summaryrefslogtreecommitdiff
path: root/vendor/github.com/bytedance/sonic/internal/jit/assembler_amd64.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/bytedance/sonic/internal/jit/assembler_amd64.go')
-rw-r--r--vendor/github.com/bytedance/sonic/internal/jit/assembler_amd64.go269
1 files changed, 269 insertions, 0 deletions
diff --git a/vendor/github.com/bytedance/sonic/internal/jit/assembler_amd64.go b/vendor/github.com/bytedance/sonic/internal/jit/assembler_amd64.go
new file mode 100644
index 000000000..00e6009db
--- /dev/null
+++ b/vendor/github.com/bytedance/sonic/internal/jit/assembler_amd64.go
@@ -0,0 +1,269 @@
+/*
+ * Copyright 2021 ByteDance Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jit
+
+import (
+ `encoding/binary`
+ `strconv`
+ `strings`
+ `sync`
+
+ `github.com/bytedance/sonic/loader`
+ `github.com/bytedance/sonic/internal/rt`
+ `github.com/twitchyliquid64/golang-asm/obj`
+ `github.com/twitchyliquid64/golang-asm/obj/x86`
+)
+
+const (
+ _LB_jump_pc = "_jump_pc_"
+)
+
+type BaseAssembler struct {
+ i int
+ f func()
+ c []byte
+ o sync.Once
+ pb *Backend
+ xrefs map[string][]*obj.Prog
+ labels map[string]*obj.Prog
+ pendings map[string][]*obj.Prog
+}
+
+/** Instruction Encoders **/
+
+var _NOPS = [][16]byte {
+ {0x90}, // NOP
+ {0x66, 0x90}, // 66 NOP
+ {0x0f, 0x1f, 0x00}, // NOP DWORD ptr [EAX]
+ {0x0f, 0x1f, 0x40, 0x00}, // NOP DWORD ptr [EAX + 00H]
+ {0x0f, 0x1f, 0x44, 0x00, 0x00}, // NOP DWORD ptr [EAX + EAX*1 + 00H]
+ {0x66, 0x0f, 0x1f, 0x44, 0x00, 0x00}, // 66 NOP DWORD ptr [EAX + EAX*1 + 00H]
+ {0x0f, 0x1f, 0x80, 0x00, 0x00, 0x00, 0x00}, // NOP DWORD ptr [EAX + 00000000H]
+ {0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00}, // NOP DWORD ptr [EAX + EAX*1 + 00000000H]
+ {0x66, 0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00}, // 66 NOP DWORD ptr [EAX + EAX*1 + 00000000H]
+}
+
+func (self *BaseAssembler) NOP() *obj.Prog {
+ p := self.pb.New()
+ p.As = obj.ANOP
+ self.pb.Append(p)
+ return p
+}
+
+func (self *BaseAssembler) NOPn(n int) {
+ for i := len(_NOPS); i > 0 && n > 0; i-- {
+ for ; n >= i; n -= i {
+ self.Byte(_NOPS[i - 1][:i]...)
+ }
+ }
+}
+
+func (self *BaseAssembler) Byte(v ...byte) {
+ for ; len(v) >= 8; v = v[8:] { self.From("QUAD", Imm(rt.Get64(v))) }
+ for ; len(v) >= 4; v = v[4:] { self.From("LONG", Imm(int64(rt.Get32(v)))) }
+ for ; len(v) >= 2; v = v[2:] { self.From("WORD", Imm(int64(rt.Get16(v)))) }
+ for ; len(v) >= 1; v = v[1:] { self.From("BYTE", Imm(int64(v[0]))) }
+}
+
+func (self *BaseAssembler) Mark(pc int) {
+ self.i++
+ self.Link(_LB_jump_pc + strconv.Itoa(pc))
+}
+
+func (self *BaseAssembler) Link(to string) {
+ var p *obj.Prog
+ var v []*obj.Prog
+
+ /* placeholder substitution */
+ if strings.Contains(to, "{n}") {
+ to = strings.ReplaceAll(to, "{n}", strconv.Itoa(self.i))
+ }
+
+ /* check for duplications */
+ if _, ok := self.labels[to]; ok {
+ panic("label " + to + " has already been linked")
+ }
+
+ /* get the pending links */
+ p = self.NOP()
+ v = self.pendings[to]
+
+ /* patch all the pending jumps */
+ for _, q := range v {
+ q.To.Val = p
+ }
+
+ /* mark the label as resolved */
+ self.labels[to] = p
+ delete(self.pendings, to)
+}
+
+func (self *BaseAssembler) Xref(pc int, d int64) {
+ self.Sref(_LB_jump_pc + strconv.Itoa(pc), d)
+}
+
+func (self *BaseAssembler) Sref(to string, d int64) {
+ p := self.pb.New()
+ p.As = x86.ALONG
+ p.From = Imm(-d)
+
+ /* placeholder substitution */
+ if strings.Contains(to, "{n}") {
+ to = strings.ReplaceAll(to, "{n}", strconv.Itoa(self.i))
+ }
+
+ /* record the patch point */
+ self.pb.Append(p)
+ self.xrefs[to] = append(self.xrefs[to], p)
+}
+
+func (self *BaseAssembler) Xjmp(op string, to int) {
+ self.Sjmp(op, _LB_jump_pc + strconv.Itoa(to))
+}
+
+func (self *BaseAssembler) Sjmp(op string, to string) {
+ p := self.pb.New()
+ p.As = As(op)
+
+ /* placeholder substitution */
+ if strings.Contains(to, "{n}") {
+ to = strings.ReplaceAll(to, "{n}", strconv.Itoa(self.i))
+ }
+
+ /* check for backward jumps */
+ if v, ok := self.labels[to]; ok {
+ p.To.Val = v
+ } else {
+ self.pendings[to] = append(self.pendings[to], p)
+ }
+
+ /* mark as a branch, and add to instruction buffer */
+ p.To.Type = obj.TYPE_BRANCH
+ self.pb.Append(p)
+}
+
+func (self *BaseAssembler) Rjmp(op string, to obj.Addr) {
+ p := self.pb.New()
+ p.To = to
+ p.As = As(op)
+ self.pb.Append(p)
+}
+
+func (self *BaseAssembler) From(op string, val obj.Addr) {
+ p := self.pb.New()
+ p.As = As(op)
+ p.From = val
+ self.pb.Append(p)
+}
+
+func (self *BaseAssembler) Emit(op string, args ...obj.Addr) {
+ p := self.pb.New()
+ p.As = As(op)
+ self.assignOperands(p, args)
+ self.pb.Append(p)
+}
+
+func (self *BaseAssembler) assignOperands(p *obj.Prog, args []obj.Addr) {
+ switch len(args) {
+ case 0 :
+ case 1 : p.To = args[0]
+ case 2 : p.To, p.From = args[1], args[0]
+ case 3 : p.To, p.From, p.RestArgs = args[2], args[0], args[1:2]
+ case 4 : p.To, p.From, p.RestArgs = args[2], args[3], args[:2]
+ default : panic("invalid operands")
+ }
+}
+
+/** Assembler Helpers **/
+
+func (self *BaseAssembler) Size() int {
+ self.build()
+ return len(self.c)
+}
+
+func (self *BaseAssembler) Init(f func()) {
+ self.i = 0
+ self.f = f
+ self.c = nil
+ self.o = sync.Once{}
+}
+
+var jitLoader = loader.Loader{
+ Name: "sonic.jit.",
+ File: "github.com/bytedance/sonic/jit.go",
+ Options: loader.Options{
+ NoPreempt: true,
+ },
+}
+
+func (self *BaseAssembler) Load(name string, frameSize int, argSize int, argStackmap []bool, localStackmap []bool) loader.Function {
+ self.build()
+ return jitLoader.LoadOne(self.c, name, frameSize, argSize, argStackmap, localStackmap)
+}
+
+/** Assembler Stages **/
+
+func (self *BaseAssembler) init() {
+ self.pb = newBackend("amd64")
+ self.xrefs = map[string][]*obj.Prog{}
+ self.labels = map[string]*obj.Prog{}
+ self.pendings = map[string][]*obj.Prog{}
+}
+
+func (self *BaseAssembler) build() {
+ self.o.Do(func() {
+ self.init()
+ self.f()
+ self.validate()
+ self.assemble()
+ self.resolve()
+ self.release()
+ })
+}
+
+func (self *BaseAssembler) release() {
+ self.pb.Release()
+ self.pb = nil
+ self.xrefs = nil
+ self.labels = nil
+ self.pendings = nil
+}
+
+func (self *BaseAssembler) resolve() {
+ for s, v := range self.xrefs {
+ for _, prog := range v {
+ if prog.As != x86.ALONG {
+ panic("invalid RIP relative reference")
+ } else if p, ok := self.labels[s]; !ok {
+ panic("links are not fully resolved: " + s)
+ } else {
+ off := prog.From.Offset + p.Pc - prog.Pc
+ binary.LittleEndian.PutUint32(self.c[prog.Pc:], uint32(off))
+ }
+ }
+ }
+}
+
+func (self *BaseAssembler) validate() {
+ for key := range self.pendings {
+ panic("links are not fully resolved: " + key)
+ }
+}
+
+func (self *BaseAssembler) assemble() {
+ self.c = self.pb.Assemble()
+}