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  }