github.com/cloudwego/frugal@v0.1.15/internal/atm/pgen/pgen_abi_amd64.go (about) 1 /* 2 * Copyright 2022 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 pgen 18 19 import ( 20 `fmt` 21 `math` 22 `unsafe` 23 24 `github.com/cloudwego/iasm/x86_64` 25 `github.com/cloudwego/frugal/internal/atm/abi` 26 `github.com/cloudwego/frugal/internal/atm/hir` 27 `github.com/cloudwego/frugal/internal/atm/rtx` 28 `github.com/cloudwego/frugal/internal/rt` 29 ) 30 31 type _SwapPair struct { 32 rs hir.Register 33 rd hir.Register 34 rr x86_64.Register64 35 } 36 37 type _CodeGenExtension struct { 38 rets []_SwapPair 39 } 40 41 /** Prologue & Epilogue **/ 42 43 func (self *CodeGen) abiPrologue(p *x86_64.Program) { 44 for i, v := range self.ctxt.desc.Args { 45 if v.InRegister { 46 p.MOVQ(v.Reg, self.ctxt.argv(i)) 47 } 48 } 49 } 50 51 func (self *CodeGen) abiEpilogue(p *x86_64.Program) { 52 for _, v := range self.abix.rets { 53 p.XCHGQ(self.r(v.rs), v.rr) 54 self.regs[v.rs], self.regs[v.rd] = self.regs[v.rd], self.regs[v.rs] 55 } 56 } 57 58 /** Stack Growing **/ 59 60 func (self *CodeGen) abiStackGrow(p *x86_64.Program) { 61 self.internalSpillArgs(p) 62 p.MOVQ(uintptr(rtx.F_morestack_noctxt), R12) 63 p.CALLQ(R12) 64 self.internalUnspillArgs(p) 65 } 66 67 func (self *CodeGen) internalSpillArgs(p *x86_64.Program) { 68 for _, v := range self.ctxt.desc.Args { 69 if v.InRegister { 70 p.MOVQ(v.Reg, Ptr(RSP, int32(v.Mem) +abi.PtrSize)) 71 } 72 } 73 } 74 75 func (self *CodeGen) internalUnspillArgs(p *x86_64.Program) { 76 for _, v := range self.ctxt.desc.Args { 77 if v.InRegister { 78 p.MOVQ(Ptr(RSP, int32(v.Mem) +abi.PtrSize), v.Reg) 79 } 80 } 81 } 82 83 /** Reserved Register Management **/ 84 85 func (self *CodeGen) abiSaveReserved(p *x86_64.Program) { 86 for rr := range self.ctxt.regr { 87 p.MOVQ(rr, self.ctxt.rslot(rr)) 88 } 89 } 90 91 func (self *CodeGen) abiLoadReserved(p *x86_64.Program) { 92 for rr := range self.ctxt.regr { 93 p.MOVQ(self.ctxt.rslot(rr), rr) 94 } 95 } 96 97 func (self *CodeGen) abiSpillReserved(p *x86_64.Program) { 98 for rr := range self.ctxt.regr { 99 if lr := self.rindex(rr); lr != nil { 100 p.MOVQ(rr, self.ctxt.slot(lr)) 101 } 102 } 103 } 104 105 func (self *CodeGen) abiRestoreReserved(p *x86_64.Program) { 106 for rr := range self.ctxt.regr { 107 if lr := self.rindex(rr); lr != nil { 108 p.MOVQ(self.ctxt.slot(lr), rr) 109 } 110 } 111 } 112 113 /** Argument & Return Value Management **/ 114 115 func (self *CodeGen) abiLoadInt(p *x86_64.Program, i int, d hir.GenericRegister) { 116 p.MOVQ(self.ctxt.argv(i), self.r(d)) 117 } 118 119 func (self *CodeGen) abiLoadPtr(p *x86_64.Program, i int, d hir.PointerRegister) { 120 p.MOVQ(self.ctxt.argv(i), self.r(d)) 121 } 122 123 func (self *CodeGen) abiStoreInt(p *x86_64.Program, s hir.GenericRegister, i int) { 124 self.internalStoreRet(p, s, i) 125 } 126 127 func (self *CodeGen) abiStorePtr(p *x86_64.Program, s hir.PointerRegister, i int) { 128 self.internalStoreRet(p, s, i) 129 } 130 131 func (self *CodeGen) internalStoreRet(p *x86_64.Program, s hir.Register, i int) { 132 var r hir.Register 133 var m abi.Parameter 134 135 /* if return with stack, store directly */ 136 if m = self.ctxt.desc.Rets[i]; !m.InRegister { 137 p.MOVQ(self.r(s), self.ctxt.retv(i)) 138 return 139 } 140 141 /* check if the value is the very register required for return */ 142 if self.r(s) == m.Reg { 143 return 144 } 145 146 /* if return with free registers, simply overwrite with new value */ 147 if r = self.rindex(m.Reg); r == nil { 148 p.MOVQ(self.r(s), m.Reg) 149 return 150 } 151 152 /* if not, mark the register to store later */ 153 self.abix.rets = append(self.abix.rets, _SwapPair { 154 rs: s, 155 rd: r, 156 rr: m.Reg, 157 }) 158 } 159 160 /** Memory Zeroing **/ 161 162 func (self *CodeGen) abiBlockZero(p *x86_64.Program, pd hir.PointerRegister, nb int64) { 163 var dp int32 164 var rd x86_64.Register64 165 166 /* check for block size */ 167 if nb <= 0 || nb > math.MaxInt32 { 168 panic("abiBlockZero: invalid block size") 169 } 170 171 /* use XMM for larger blocks */ 172 if nb >= 16 { 173 p.PXOR(XMM15, XMM15) 174 } 175 176 /* use loops to reduce the code length */ 177 if rd = self.r(pd); nb >= 128 { 178 r := x86_64.CreateLabel("loop") 179 t := x86_64.CreateLabel("begin") 180 181 /* setup the zeroing loop, use 8x loop for more efficient pipelining */ 182 p.MOVQ (rd, RDI) 183 p.MOVL (nb / 128, EAX) 184 p.JMP (t) 185 p.Link (r) 186 p.ADDQ (128, RDI) 187 p.Link (t) 188 189 /* generate the zeroing instructions */ 190 for i := int32(0); i < 8; i++ { 191 p.MOVDQU(XMM15, Ptr(RDI, i * 16)) 192 } 193 194 /* decrease & check loop counter */ 195 p.SUBL (1, EAX) 196 p.JNZ (r) 197 198 /* replace the register */ 199 rd = RDI 200 nb %= 128 201 } 202 203 /* clear every 16-byte block */ 204 for nb >= 16 { 205 p.MOVDQU(XMM15, Ptr(rd, dp)) 206 dp += 16 207 nb -= 16 208 } 209 210 /* only 1 byte left */ 211 if nb == 1 { 212 p.MOVB(0, Ptr(rd, dp)) 213 return 214 } 215 216 /* still bytes need to be zeroed */ 217 if nb != 0 { 218 p.XORL(EAX, EAX) 219 } 220 221 /* clear every 8-byte block */ 222 if nb >= 8 { 223 p.MOVQ(RAX, Ptr(rd, dp)) 224 dp += 8 225 nb -= 8 226 } 227 228 /* clear every 4-byte block */ 229 if nb >= 8 { 230 p.MOVL(EAX, Ptr(rd, dp)) 231 dp += 4 232 nb -= 4 233 } 234 235 /* clear every 2-byte block */ 236 if nb >= 2 { 237 p.MOVW(AX, Ptr(rd, dp)) 238 dp += 2 239 nb -= 2 240 } 241 242 /* last byte */ 243 if nb > 0 { 244 p.MOVB(AL, Ptr(rd, dp)) 245 } 246 } 247 248 /** Function & Method Call **/ 249 250 var argumentOrder = [6]x86_64.Register64 { 251 RDI, 252 RSI, 253 RDX, 254 RCX, 255 R8, 256 R9, 257 } 258 259 var argumentRegisters = map[x86_64.Register64]bool { 260 RDI : true, 261 RSI : true, 262 RDX : true, 263 RCX : true, 264 R8 : true, 265 R9 : true, 266 } 267 268 var reservedRegisters = map[x86_64.Register64]bool { 269 RBX: true, 270 R12: true, 271 R13: true, 272 R14: true, 273 R15: true, 274 } 275 276 func ri2reg(ri uint8) hir.Register { 277 if ri & hir.ArgPointer == 0 { 278 return hir.GenericRegister(ri & hir.ArgMask) 279 } else { 280 return hir.PointerRegister(ri & hir.ArgMask) 281 } 282 } 283 284 func checkfp(fp unsafe.Pointer) uintptr { 285 if fp == nil { 286 panic("checkfp: nil function") 287 } else { 288 return uintptr(fp) 289 } 290 } 291 292 func checkptr(ri uint8, arg abi.Parameter) bool { 293 return arg.IsPointer() == ((ri & hir.ArgPointer) != 0) 294 } 295 296 func (self *CodeGen) abiCallGo(p *x86_64.Program, v *hir.Ir) { 297 self.internalCallFunction(p, v, nil, func(fp *hir.CallHandle) { 298 p.MOVQ(checkfp(fp.Func), R12) 299 p.CALLQ(R12) 300 }) 301 } 302 303 func (self *CodeGen) abiCallNative(p *x86_64.Program, v *hir.Ir) { 304 rv := hir.Register(nil) 305 fp := hir.LookupCall(v.Iv) 306 307 /* native function can have at most 1 return value */ 308 if v.Rn > 1 { 309 panic("abiCallNative: native function can only have at most 1 return value") 310 } 311 312 /* passing arguments on stack is currently not implemented */ 313 if int(v.An) > len(argumentOrder) { 314 panic("abiCallNative: not implemented: passing arguments on stack for native functions") 315 } 316 317 /* save all the allocated registers (except reserved registers) before function call */ 318 for _, lr := range self.ctxt.regs { 319 if rr := self.r(lr); !reservedRegisters[rr] { 320 p.MOVQ(rr, self.ctxt.slot(lr)) 321 } 322 } 323 324 /* load all the parameters */ 325 for i := 0; i < int(v.An); i++ { 326 rr := ri2reg(v.Ar[i]) 327 rd := argumentOrder[i] 328 329 /* check for zero source and spilled arguments */ 330 if rr.Z() { 331 p.XORL(x86_64.Register32(rd), x86_64.Register32(rd)) 332 } else if rs := self.r(rr); argumentRegisters[rs] { 333 p.MOVQ(self.ctxt.slot(rr), rd) 334 } else { 335 p.MOVQ(rs, rd) 336 } 337 } 338 339 /* call the function */ 340 p.MOVQ(checkfp(fp.Func), RAX) 341 p.CALLQ(RAX) 342 343 /* store the result */ 344 if v.Rn != 0 { 345 if rv = ri2reg(v.Rr[0]); !rv.Z() { 346 p.MOVQ(RAX, self.r(rv)) 347 } 348 } 349 350 /* restore all the allocated registers (except reserved registers and result) after function call */ 351 for _, lr := range self.ctxt.regs { 352 if rr := self.r(lr); (lr != rv) && !reservedRegisters[rr] { 353 p.MOVQ(self.ctxt.slot(lr), rr) 354 } 355 } 356 } 357 358 func (self *CodeGen) abiCallMethod(p *x86_64.Program, v *hir.Ir) { 359 self.internalCallFunction(p, v, v.Pd, func(fp *hir.CallHandle) { 360 p.MOVQ(self.ctxt.slot(v.Ps), R12) 361 p.CALLQ(Ptr(R12, int32(rt.GoItabFuncBase) + int32(fp.Slot) * abi.PtrSize)) 362 }) 363 } 364 365 func (self *CodeGen) internalSetArg(p *x86_64.Program, ri uint8, arg abi.Parameter, clobberSet map[x86_64.Register64]bool) { 366 if !checkptr(ri, arg) { 367 panic("internalSetArg: passing arguments in different kind of registers") 368 } else if !arg.InRegister { 369 self.internalSetStack(p, ri2reg(ri), arg) 370 } else { 371 self.internalSetRegister(p, ri2reg(ri), arg, clobberSet) 372 } 373 } 374 375 func (self *CodeGen) internalSetStack(p *x86_64.Program, rr hir.Register, arg abi.Parameter) { 376 if rr.Z() { 377 p.MOVQ(0, Ptr(RSP, int32(arg.Mem))) 378 } else { 379 p.MOVQ(self.r(rr), Ptr(RSP, int32(arg.Mem))) 380 } 381 } 382 383 func (self *CodeGen) internalSetRegister(p *x86_64.Program, rr hir.Register, arg abi.Parameter, clobberSet map[x86_64.Register64]bool) { 384 if rr.Z() { 385 p.XORL(x86_64.Register32(arg.Reg), x86_64.Register32(arg.Reg)) 386 } else if lr := self.r(rr); clobberSet[lr] { 387 p.MOVQ(self.ctxt.slot(rr), arg.Reg) 388 } else if clobberSet[arg.Reg] = true; self.rindex(arg.Reg) != nil { 389 p.MOVQ(self.ctxt.slot(rr), arg.Reg) 390 } else { 391 p.MOVQ(lr, arg.Reg) 392 } 393 } 394 395 func (self *CodeGen) internalCallFunction(p *x86_64.Program, v *hir.Ir, this hir.Register, makeFuncCall func(fp *hir.CallHandle)) { 396 ac := 0 397 fp := hir.LookupCall(v.Iv) 398 fv := abi.ABI.GetLayout(fp.Id) 399 rm := make(map[hir.Register]int32) 400 cs := make(map[x86_64.Register64]bool) 401 402 /* find the function */ 403 if fv == nil { 404 panic(fmt.Sprintf("internalCallFunction: invalid function ID: %d", v.Iv)) 405 } 406 407 /* "this" is an implicit argument, so exclude from argument count */ 408 if this != nil { 409 ac = 1 410 } 411 412 /* check for argument and return value count */ 413 if int(v.Rn) != len(fv.Rets) || int(v.An) != len(fv.Args) - ac { 414 panic("internalCallFunction: argument or return value count mismatch") 415 } 416 417 /* save all the allocated registers before function call */ 418 for _, lr := range self.ctxt.regs { 419 p.MOVQ(self.r(lr), self.ctxt.slot(lr)) 420 } 421 422 /* load all the arguments */ 423 for i, vv := range fv.Args { 424 if i == 0 && this != nil { 425 self.internalSetArg(p, this.A(), vv, cs) 426 } else { 427 self.internalSetArg(p, v.Ar[i - ac], vv, cs) 428 } 429 } 430 431 /* call the function with reserved registers restored */ 432 self.abiLoadReserved(p) 433 makeFuncCall(fp) 434 self.abiSaveReserved(p) 435 436 /* if the function returns a value with a used register, spill it on stack */ 437 for i, retv := range fv.Rets { 438 if rr := ri2reg(v.Rr[i]); !rr.Z() { 439 if !retv.InRegister { 440 rm[rr] = int32(retv.Mem) 441 } else if self.rindex(retv.Reg) != nil { 442 p.MOVQ(retv.Reg, self.ctxt.slot(rr)) 443 } 444 } 445 } 446 447 /* save all the non-spilled arguments */ 448 for i, retv := range fv.Rets { 449 if rr := ri2reg(v.Rr[i]); !rr.Z() { 450 if retv.InRegister && self.rindex(retv.Reg) == nil { 451 rm[rr] = -1 452 p.MOVQ(retv.Reg, self.r(rr)) 453 } 454 } 455 } 456 457 /* restore all the allocated registers (except return values) after function call */ 458 for _, lr := range self.ctxt.regs { 459 if _, ok := rm[lr]; !ok { 460 p.MOVQ(self.ctxt.slot(lr), self.r(lr)) 461 } 462 } 463 464 /* store all the stack-based return values */ 465 for rr, mem := range rm { 466 if mem != -1 { 467 p.MOVQ(Ptr(RSP, mem), self.r(rr)) 468 } 469 } 470 }