github.com/bytedance/sonic@v1.11.7-0.20240517092252-d2edb31b167b/internal/jit/assembler_amd64.go (about) 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) Byte(v ...byte) { 76 for ; len(v) >= 8; v = v[8:] { self.From("QUAD", Imm(rt.Get64(v))) } 77 for ; len(v) >= 4; v = v[4:] { self.From("LONG", Imm(int64(rt.Get32(v)))) } 78 for ; len(v) >= 2; v = v[2:] { self.From("WORD", Imm(int64(rt.Get16(v)))) } 79 for ; len(v) >= 1; v = v[1:] { self.From("BYTE", Imm(int64(v[0]))) } 80 } 81 82 func (self *BaseAssembler) Mark(pc int) { 83 self.i++ 84 self.Link(_LB_jump_pc + strconv.Itoa(pc)) 85 } 86 87 func (self *BaseAssembler) Link(to string) { 88 var p *obj.Prog 89 var v []*obj.Prog 90 91 /* placeholder substitution */ 92 if strings.Contains(to, "{n}") { 93 to = strings.ReplaceAll(to, "{n}", strconv.Itoa(self.i)) 94 } 95 96 /* check for duplications */ 97 if _, ok := self.labels[to]; ok { 98 panic("label " + to + " has already been linked") 99 } 100 101 /* get the pending links */ 102 p = self.NOP() 103 v = self.pendings[to] 104 105 /* patch all the pending jumps */ 106 for _, q := range v { 107 q.To.Val = p 108 } 109 110 /* mark the label as resolved */ 111 self.labels[to] = p 112 delete(self.pendings, to) 113 } 114 115 func (self *BaseAssembler) Xref(pc int, d int64) { 116 self.Sref(_LB_jump_pc + strconv.Itoa(pc), d) 117 } 118 119 func (self *BaseAssembler) Sref(to string, d int64) { 120 p := self.pb.New() 121 p.As = x86.ALONG 122 p.From = Imm(-d) 123 124 /* placeholder substitution */ 125 if strings.Contains(to, "{n}") { 126 to = strings.ReplaceAll(to, "{n}", strconv.Itoa(self.i)) 127 } 128 129 /* record the patch point */ 130 self.pb.Append(p) 131 self.xrefs[to] = append(self.xrefs[to], p) 132 } 133 134 func (self *BaseAssembler) Xjmp(op string, to int) { 135 self.Sjmp(op, _LB_jump_pc + strconv.Itoa(to)) 136 } 137 138 func (self *BaseAssembler) Sjmp(op string, to string) { 139 p := self.pb.New() 140 p.As = As(op) 141 142 /* placeholder substitution */ 143 if strings.Contains(to, "{n}") { 144 to = strings.ReplaceAll(to, "{n}", strconv.Itoa(self.i)) 145 } 146 147 /* check for backward jumps */ 148 if v, ok := self.labels[to]; ok { 149 p.To.Val = v 150 } else { 151 self.pendings[to] = append(self.pendings[to], p) 152 } 153 154 /* mark as a branch, and add to instruction buffer */ 155 p.To.Type = obj.TYPE_BRANCH 156 self.pb.Append(p) 157 } 158 159 func (self *BaseAssembler) Rjmp(op string, to obj.Addr) { 160 p := self.pb.New() 161 p.To = to 162 p.As = As(op) 163 self.pb.Append(p) 164 } 165 166 func (self *BaseAssembler) From(op string, val obj.Addr) { 167 p := self.pb.New() 168 p.As = As(op) 169 p.From = val 170 self.pb.Append(p) 171 } 172 173 func (self *BaseAssembler) Emit(op string, args ...obj.Addr) { 174 p := self.pb.New() 175 p.As = As(op) 176 self.assignOperands(p, args) 177 self.pb.Append(p) 178 } 179 180 func (self *BaseAssembler) assignOperands(p *obj.Prog, args []obj.Addr) { 181 switch len(args) { 182 case 0 : 183 case 1 : p.To = args[0] 184 case 2 : p.To, p.From = args[1], args[0] 185 case 3 : p.To, p.From, p.RestArgs = args[2], args[0], args[1:2] 186 case 4 : p.To, p.From, p.RestArgs = args[2], args[3], args[:2] 187 default : panic("invalid operands") 188 } 189 } 190 191 /** Assembler Helpers **/ 192 193 func (self *BaseAssembler) Size() int { 194 self.build() 195 return len(self.c) 196 } 197 198 func (self *BaseAssembler) Init(f func()) { 199 self.i = 0 200 self.f = f 201 self.c = nil 202 self.o = sync.Once{} 203 } 204 205 var jitLoader = loader.Loader{ 206 Name: "sonic.jit.", 207 File: "github.com/bytedance/sonic/jit.go", 208 Options: loader.Options{ 209 NoPreempt: true, 210 }, 211 } 212 213 func (self *BaseAssembler) Load(name string, frameSize int, argSize int, argStackmap []bool, localStackmap []bool) loader.Function { 214 self.build() 215 return jitLoader.LoadOne(self.c, name, frameSize, argSize, argStackmap, localStackmap) 216 } 217 218 /** Assembler Stages **/ 219 220 func (self *BaseAssembler) init() { 221 self.pb = newBackend("amd64") 222 self.xrefs = map[string][]*obj.Prog{} 223 self.labels = map[string]*obj.Prog{} 224 self.pendings = map[string][]*obj.Prog{} 225 } 226 227 func (self *BaseAssembler) build() { 228 self.o.Do(func() { 229 self.init() 230 self.f() 231 self.validate() 232 self.assemble() 233 self.resolve() 234 self.release() 235 }) 236 } 237 238 func (self *BaseAssembler) release() { 239 self.pb.Release() 240 self.pb = nil 241 self.xrefs = nil 242 self.labels = nil 243 self.pendings = nil 244 } 245 246 func (self *BaseAssembler) resolve() { 247 for s, v := range self.xrefs { 248 for _, prog := range v { 249 if prog.As != x86.ALONG { 250 panic("invalid RIP relative reference") 251 } else if p, ok := self.labels[s]; !ok { 252 panic("links are not fully resolved: " + s) 253 } else { 254 off := prog.From.Offset + p.Pc - prog.Pc 255 binary.LittleEndian.PutUint32(self.c[prog.Pc:], uint32(off)) 256 } 257 } 258 } 259 } 260 261 func (self *BaseAssembler) validate() { 262 for key := range self.pendings { 263 panic("links are not fully resolved: " + key) 264 } 265 } 266 267 func (self *BaseAssembler) assemble() { 268 self.c = self.pb.Assemble() 269 }