gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

assembler_amd64.go (7747B)


      1 /*
      2  * Copyright 2021 ByteDance Inc.
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *     http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package jit
     18 
     19 import (
     20     `encoding/binary`
     21     `strconv`
     22     `strings`
     23     `sync`
     24 
     25     `github.com/bytedance/sonic/loader`
     26     `github.com/bytedance/sonic/internal/rt`
     27     `github.com/twitchyliquid64/golang-asm/obj`
     28     `github.com/twitchyliquid64/golang-asm/obj/x86`
     29 )
     30 
     31 const (
     32     _LB_jump_pc = "_jump_pc_"
     33 )
     34 
     35 type BaseAssembler struct {
     36     i        int
     37     f        func()
     38     c        []byte
     39     o        sync.Once
     40     pb       *Backend
     41     xrefs    map[string][]*obj.Prog
     42     labels   map[string]*obj.Prog
     43     pendings map[string][]*obj.Prog
     44 }
     45 
     46 /** Instruction Encoders **/
     47 
     48 var _NOPS = [][16]byte {
     49     {0x90},                                                     // NOP
     50     {0x66, 0x90},                                               // 66 NOP
     51     {0x0f, 0x1f, 0x00},                                         // NOP DWORD ptr [EAX]
     52     {0x0f, 0x1f, 0x40, 0x00},                                   // NOP DWORD ptr [EAX + 00H]
     53     {0x0f, 0x1f, 0x44, 0x00, 0x00},                             // NOP DWORD ptr [EAX + EAX*1 + 00H]
     54     {0x66, 0x0f, 0x1f, 0x44, 0x00, 0x00},                       // 66 NOP DWORD ptr [EAX + EAX*1 + 00H]
     55     {0x0f, 0x1f, 0x80, 0x00, 0x00, 0x00, 0x00},                 // NOP DWORD ptr [EAX + 00000000H]
     56     {0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00},           // NOP DWORD ptr [EAX + EAX*1 + 00000000H]
     57     {0x66, 0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00},     // 66 NOP DWORD ptr [EAX + EAX*1 + 00000000H]
     58 }
     59 
     60 func (self *BaseAssembler) NOP() *obj.Prog {
     61     p := self.pb.New()
     62     p.As = obj.ANOP
     63     self.pb.Append(p)
     64     return p
     65 }
     66 
     67 func (self *BaseAssembler) NOPn(n int) {
     68     for i := len(_NOPS); i > 0 && n > 0; i-- {
     69         for ; n >= i; n -= i {
     70             self.Byte(_NOPS[i - 1][:i]...)
     71         }
     72     }
     73 }
     74 
     75 func (self *BaseAssembler) StorePtr(ptr int64, to obj.Addr, tmp obj.Addr) {
     76     if (to.Type != obj.TYPE_MEM) || (tmp.Type != obj.TYPE_REG) {
     77         panic("must store imm to memory, tmp must be register")
     78     }
     79     if (ptr >> 32) != 0 {
     80         self.Emit("MOVQ", Imm(ptr), tmp)
     81         self.Emit("MOVQ", tmp, to)
     82     } else {
     83         self.Emit("MOVQ", Imm(ptr), to);
     84     }
     85 }
     86 
     87 func (self *BaseAssembler) Byte(v ...byte) {
     88     for ; len(v) >= 8; v = v[8:] { self.From("QUAD", Imm(rt.Get64(v))) }
     89     for ; len(v) >= 4; v = v[4:] { self.From("LONG", Imm(int64(rt.Get32(v)))) }
     90     for ; len(v) >= 2; v = v[2:] { self.From("WORD", Imm(int64(rt.Get16(v)))) }
     91     for ; len(v) >= 1; v = v[1:] { self.From("BYTE", Imm(int64(v[0]))) }
     92 }
     93 
     94 func (self *BaseAssembler) Mark(pc int) {
     95     self.i++
     96     self.Link(_LB_jump_pc + strconv.Itoa(pc))
     97 }
     98 
     99 func (self *BaseAssembler) Link(to string) {
    100     var p *obj.Prog
    101     var v []*obj.Prog
    102 
    103     /* placeholder substitution */
    104     if strings.Contains(to, "{n}") {
    105         to = strings.ReplaceAll(to, "{n}", strconv.Itoa(self.i))
    106     }
    107 
    108     /* check for duplications */
    109     if _, ok := self.labels[to]; ok {
    110         panic("label " + to + " has already been linked")
    111     }
    112 
    113     /* get the pending links */
    114     p = self.NOP()
    115     v = self.pendings[to]
    116 
    117     /* patch all the pending jumps */
    118     for _, q := range v {
    119         q.To.Val = p
    120     }
    121 
    122     /* mark the label as resolved */
    123     self.labels[to] = p
    124     delete(self.pendings, to)
    125 }
    126 
    127 func (self *BaseAssembler) Xref(pc int, d int64) {
    128     self.Sref(_LB_jump_pc + strconv.Itoa(pc), d)
    129 }
    130 
    131 func (self *BaseAssembler) Sref(to string, d int64) {
    132     p := self.pb.New()
    133     p.As = x86.ALONG
    134     p.From = Imm(-d)
    135 
    136     /* placeholder substitution */
    137     if strings.Contains(to, "{n}") {
    138         to = strings.ReplaceAll(to, "{n}", strconv.Itoa(self.i))
    139     }
    140 
    141     /* record the patch point */
    142     self.pb.Append(p)
    143     self.xrefs[to] = append(self.xrefs[to], p)
    144 }
    145 
    146 func (self *BaseAssembler) Xjmp(op string, to int) {
    147     self.Sjmp(op, _LB_jump_pc + strconv.Itoa(to))
    148 }
    149 
    150 func (self *BaseAssembler) Sjmp(op string, to string) {
    151     p := self.pb.New()
    152     p.As = As(op)
    153 
    154     /* placeholder substitution */
    155     if strings.Contains(to, "{n}") {
    156         to = strings.ReplaceAll(to, "{n}", strconv.Itoa(self.i))
    157     }
    158 
    159     /* check for backward jumps */
    160     if v, ok := self.labels[to]; ok {
    161         p.To.Val = v
    162     } else {
    163         self.pendings[to] = append(self.pendings[to], p)
    164     }
    165 
    166     /* mark as a branch, and add to instruction buffer */
    167     p.To.Type = obj.TYPE_BRANCH
    168     self.pb.Append(p)
    169 }
    170 
    171 func (self *BaseAssembler) Rjmp(op string, to obj.Addr) {
    172     p := self.pb.New()
    173     p.To = to
    174     p.As = As(op)
    175     self.pb.Append(p)
    176 }
    177 
    178 func (self *BaseAssembler) From(op string, val obj.Addr) {
    179     p := self.pb.New()
    180     p.As = As(op)
    181     p.From = val
    182     self.pb.Append(p)
    183 }
    184 
    185 func (self *BaseAssembler) Emit(op string, args ...obj.Addr) {
    186     p := self.pb.New()
    187     p.As = As(op)
    188     self.assignOperands(p, args)
    189     self.pb.Append(p)
    190 }
    191 
    192 func (self *BaseAssembler) assignOperands(p *obj.Prog, args []obj.Addr) {
    193     switch len(args) {
    194         case 0  :
    195         case 1  : p.To                     = args[0]
    196         case 2  : p.To, p.From             = args[1], args[0]
    197         case 3  : p.To, p.From, p.RestArgs = args[2], args[0], args[1:2]
    198         case 4  : p.To, p.From, p.RestArgs = args[2], args[3], args[:2]
    199         default : panic("invalid operands")
    200     }
    201 }
    202 
    203 /** Assembler Helpers **/
    204 
    205 func (self *BaseAssembler) Size() int {
    206     self.build()
    207     return len(self.c)
    208 }
    209 
    210 func (self *BaseAssembler) Init(f func()) {
    211     self.i = 0
    212     self.f = f
    213     self.c = nil
    214     self.o = sync.Once{}
    215 }
    216 
    217 var jitLoader = loader.Loader{
    218     Name: "sonic.jit.",
    219     File: "github.com/bytedance/sonic/jit.go",
    220     Options: loader.Options{
    221         NoPreempt: true,
    222     },
    223 }
    224 
    225 func (self *BaseAssembler) Load(name string, frameSize int, argSize int, argStackmap []bool, localStackmap []bool) loader.Function {
    226     self.build()
    227     return jitLoader.LoadOne(self.c, name, frameSize, argSize, argStackmap, localStackmap)
    228 }
    229 
    230 /** Assembler Stages **/
    231 
    232 func (self *BaseAssembler) init() {
    233     self.pb       = newBackend("amd64")
    234     self.xrefs    = map[string][]*obj.Prog{}
    235     self.labels   = map[string]*obj.Prog{}
    236     self.pendings = map[string][]*obj.Prog{}
    237 }
    238 
    239 func (self *BaseAssembler) build() {
    240     self.o.Do(func() {
    241         self.init()
    242         self.f()
    243         self.validate()
    244         self.assemble()
    245         self.resolve()
    246         self.release()
    247     })
    248 }
    249 
    250 func (self *BaseAssembler) release() {
    251     self.pb.Release()
    252     self.pb = nil
    253     self.xrefs = nil
    254     self.labels = nil
    255     self.pendings = nil
    256 }
    257 
    258 func (self *BaseAssembler) resolve() {
    259     for s, v := range self.xrefs {
    260         for _, prog := range v {
    261             if prog.As != x86.ALONG {
    262                 panic("invalid RIP relative reference")
    263             } else if p, ok := self.labels[s]; !ok {
    264                 panic("links are not fully resolved: " + s)
    265             } else {
    266                 off := prog.From.Offset + p.Pc - prog.Pc
    267                 binary.LittleEndian.PutUint32(self.c[prog.Pc:], uint32(off))
    268             }
    269         }
    270     }
    271 }
    272 
    273 func (self *BaseAssembler) validate() {
    274     for key := range self.pendings {
    275         panic("links are not fully resolved: " + key)
    276     }
    277 }
    278 
    279 func (self *BaseAssembler) assemble() {
    280     self.c = self.pb.Assemble()
    281 }