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 }