github.com/cloudwego/frugal@v0.1.15/internal/atm/ssa/block.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 ssa
    18  
    19  import (
    20      `fmt`
    21  
    22      `github.com/cloudwego/frugal/internal/atm/abi`
    23      `github.com/cloudwego/frugal/internal/atm/hir`
    24      `github.com/cloudwego/frugal/internal/atm/rtx`
    25  )
    26  
    27  var _MemSize = [...]uint8 {
    28      hir.OP_lb: 1,
    29      hir.OP_lw: 2,
    30      hir.OP_ll: 4,
    31      hir.OP_lq: 8,
    32      hir.OP_sb: 1,
    33      hir.OP_sw: 2,
    34      hir.OP_sl: 4,
    35      hir.OP_sq: 8,
    36  }
    37  
    38  var _UnaryOps = [...]IrUnaryOp {
    39      hir.OP_swapw : IrOpSwap16,
    40      hir.OP_swapl : IrOpSwap32,
    41      hir.OP_swapq : IrOpSwap64,
    42      hir.OP_sxlq  : IrOpSx32to64,
    43  }
    44  
    45  var _BinaryOps = [...]IrBinaryOp {
    46      hir.OP_add  : IrOpAdd,
    47      hir.OP_sub  : IrOpSub,
    48      hir.OP_addi : IrOpAdd,
    49      hir.OP_muli : IrOpMul,
    50      hir.OP_andi : IrOpAnd,
    51      hir.OP_xori : IrOpXor,
    52      hir.OP_shri : IrOpShr,
    53  }
    54  
    55  var _ConstnessMap = [...]Constness {
    56      hir.Const    : Const,
    57      hir.Volatile : Volatile,
    58  }
    59  
    60  type BasicBlock struct {
    61      Id   int
    62      Phi  []*IrPhi
    63      Ins  []IrNode
    64      Pred []*BasicBlock
    65      Term IrTerminator
    66  }
    67  
    68  func (self *BasicBlock) String() string {
    69      return fmt.Sprintf("bb_%d", self.Id)
    70  }
    71  
    72  func (self *BasicBlock) addPred(p *BasicBlock) {
    73      for _, b := range self.Pred { if b == p { return } }
    74      self.Pred = append(self.Pred, p)
    75  }
    76  
    77  func (self *BasicBlock) addInstr(p *hir.Ir) {
    78      switch p.Op {
    79          default: {
    80              panic("invalid instruction: " + p.Disassemble(nil))
    81          }
    82  
    83          /* no operation */
    84          case hir.OP_nop: {
    85              break
    86          }
    87  
    88          /* ptr(Pr) -> Pd */
    89          case hir.OP_ip: {
    90              self.Ins = append(
    91                  self.Ins,
    92                  &IrConstPtr {
    93                      P: p.Pr,
    94                      R: Rv(p.Pd),
    95                      M: _ConstnessMap[p.Constness()],
    96                  },
    97              )
    98          }
    99  
   100          /* *(Ps + Iv) -> Rx */
   101          case hir.OP_lb, hir.OP_lw, hir.OP_ll, hir.OP_lq: {
   102              self.Ins = append(
   103                  self.Ins,
   104                  &IrConstInt {
   105                      R: Tr(0),
   106                      V: p.Iv,
   107                  },
   108                  &IrLEA {
   109                      R   : Pr(0),
   110                      Mem : Rv(p.Ps),
   111                      Off : Tr(0),
   112                  },
   113                  &IrLoad {
   114                      R    : Rv(p.Rx),
   115                      Mem  : Pr(0),
   116                      Size : _MemSize[p.Op],
   117                  },
   118              )
   119          }
   120  
   121          /* *(Ps + Iv) -> Pd */
   122          case hir.OP_lp: {
   123              self.Ins = append(
   124                  self.Ins,
   125                  &IrConstInt {
   126                      R: Tr(0),
   127                      V: p.Iv,
   128                  },
   129                  &IrLEA {
   130                      R   : Pr(0),
   131                      Mem : Rv(p.Ps),
   132                      Off : Tr(0),
   133                  },
   134                  &IrLoad {
   135                      R    : Rv(p.Pd),
   136                      Mem  : Pr(0),
   137                      Size : abi.PtrSize,
   138                  },
   139              )
   140          }
   141  
   142          /* Rx -> *(Pd + Iv) */
   143          case hir.OP_sb, hir.OP_sw, hir.OP_sl, hir.OP_sq: {
   144              self.Ins = append(
   145                  self.Ins,
   146                  &IrConstInt {
   147                      R: Tr(0),
   148                      V: p.Iv,
   149                  },
   150                  &IrLEA {
   151                      R   : Pr(0),
   152                      Mem : Rv(p.Pd),
   153                      Off : Tr(0),
   154                  },
   155                  &IrStore {
   156                      R    : Rv(p.Rx),
   157                      Mem  : Pr(0),
   158                      Size : _MemSize[p.Op],
   159                  },
   160              )
   161          }
   162  
   163          /* Ps -> *(Pd + Iv) */
   164          case hir.OP_sp: {
   165              self.Ins = append(
   166                  self.Ins,
   167                  &IrConstInt {
   168                      R: Tr(0),
   169                      V: p.Iv,
   170                  },
   171                  &IrLEA {
   172                      R   : Pr(0),
   173                      Mem : Rv(p.Pd),
   174                      Off : Tr(0),
   175                  },
   176                  &IrConstPtr {
   177                      R: Pr(1),
   178                      P: rtx.V_pWriteBarrier,
   179                      M: Volatile,
   180                  },
   181                  &IrConstPtr {
   182                      R: Pr(2),
   183                      P: rtx.F_gcWriteBarrier,
   184                  },
   185                  &IrWriteBarrier {
   186                      R   : Rv(p.Ps),
   187                      M   : Pr(0),
   188                      Fn  : Pr(2),
   189                      Var : Pr(1),
   190                  },
   191              )
   192          }
   193  
   194          /* arg[Iv] -> Rx */
   195          case hir.OP_ldaq: {
   196              self.Ins = append(
   197                  self.Ins,
   198                  &IrLoadArg {
   199                      R: Rv(p.Rx),
   200                      I: int(p.Iv),
   201                  },
   202              )
   203          }
   204  
   205          /* arg[Iv] -> Pd */
   206          case hir.OP_ldap: {
   207              self.Ins = append(
   208                  self.Ins,
   209                  &IrLoadArg {
   210                      R: Rv(p.Pd),
   211                      I: int(p.Iv),
   212                  },
   213              )
   214          }
   215  
   216          /* Ps + Rx -> Pd */
   217          case hir.OP_addp: {
   218              self.Ins = append(
   219                  self.Ins,
   220                  &IrLEA {
   221                      R   : Rv(p.Pd),
   222                      Mem : Rv(p.Ps),
   223                      Off : Rv(p.Rx),
   224                  },
   225              )
   226          }
   227  
   228          /* Ps - Rx -> Pd */
   229          case hir.OP_subp: {
   230              self.Ins = append(
   231                  self.Ins,
   232                  &IrUnaryExpr {
   233                      R  : Tr(0),
   234                      V  : Rv(p.Rx),
   235                      Op : IrOpNegate,
   236                  },
   237                  &IrLEA {
   238                      R   : Rv(p.Pd),
   239                      Mem : Rv(p.Ps),
   240                      Off : Tr(0),
   241                  },
   242              )
   243          }
   244  
   245          /* Ps + Iv -> Pd */
   246          case hir.OP_addpi: {
   247              self.Ins = append(
   248                  self.Ins,
   249                  &IrConstInt {
   250                      R: Tr(0),
   251                      V: p.Iv,
   252                  },
   253                  &IrLEA {
   254                      R   : Rv(p.Pd),
   255                      Mem : Rv(p.Ps),
   256                      Off : Tr(0),
   257                  },
   258              )
   259          }
   260  
   261          /* Rx ± Ry -> Rz */
   262          case hir.OP_add, hir.OP_sub: {
   263              self.Ins = append(
   264                  self.Ins,
   265                  &IrBinaryExpr {
   266                      R  : Rv(p.Rz),
   267                      X  : Rv(p.Rx),
   268                      Y  : Rv(p.Ry),
   269                      Op : _BinaryOps[p.Op],
   270                  },
   271              )
   272          }
   273  
   274          /* Ry & (1 << (Rx % PTR_BITS)) != 0 -> Rz
   275           * Ry |= 1 << (Rx % PTR_BITS) */
   276          case hir.OP_bts: {
   277              self.Ins = append(
   278                  self.Ins,
   279                  &IrBitTestSet {
   280                      T: Rv(p.Rz),
   281                      S: Rv(p.Ry),
   282                      X: Rv(p.Ry),
   283                      Y: Rv(p.Rx),
   284                  },
   285              )
   286          }
   287  
   288          /* Rx {+,*,&,^,>>} Iv -> Ry */
   289          case hir.OP_addi, hir.OP_muli, hir.OP_andi, hir.OP_xori, hir.OP_shri: {
   290              self.Ins = append(
   291                  self.Ins,
   292                  &IrConstInt {
   293                      R: Tr(0),
   294                      V: p.Iv,
   295                  },
   296                  &IrBinaryExpr {
   297                      R  : Rv(p.Ry),
   298                      X  : Rv(p.Rx),
   299                      Y  : Tr(0),
   300                      Op : _BinaryOps[p.Op],
   301                  },
   302              )
   303          }
   304  
   305          /* Rx | (1 << Iv) -> Ry */
   306          case hir.OP_bsi: {
   307              self.Ins = append(
   308                  self.Ins,
   309                  &IrConstInt {
   310                      R: Tr(0),
   311                      V: 1 << p.Iv,
   312                  },
   313                  &IrBinaryExpr {
   314                      R  : Rv(p.Ry),
   315                      X  : Rv(p.Rx),
   316                      Y  : Tr(0),
   317                      Op : IrOpOr,
   318                  },
   319              )
   320          }
   321  
   322          /* {bswap{16/32/64}/sign_extend_32_to_64}(Rx) -> Ry */
   323          case hir.OP_swapw, hir.OP_swapl, hir.OP_swapq, hir.OP_sxlq: {
   324              self.Ins = append(
   325                  self.Ins,
   326                  &IrUnaryExpr {
   327                      R  : Rv(p.Ry),
   328                      V  : Rv(p.Rx),
   329                      Op : _UnaryOps[p.Op],
   330                  },
   331              )
   332          }
   333  
   334          /* memset(Pd, 0, Iv) */
   335          case hir.OP_bzero: {
   336              r := Rv(p.Pd)
   337              d := uintptr(0)
   338              n := uintptr(p.Iv)
   339  
   340              /* call memory zero for large blocks */
   341              for ; n >= rtx.MaxZeroSize; n -= rtx.MaxZeroSize {
   342                  self.zeroBlock(r, d, rtx.MaxZeroSize)
   343                  d += rtx.MaxZeroSize
   344              }
   345  
   346              /* call memory zero for smaller blocks */
   347              if n >= rtx.ZeroStep {
   348                  self.zeroBlock(r, d, n)
   349                  d += n / rtx.ZeroStep * rtx.ZeroStep
   350                  n %= rtx.ZeroStep
   351              }
   352  
   353              /* use scalar code for remaining bytes */
   354              for _, v := range []uintptr { 8, 4, 2, 1 } {
   355                  for n >= v {
   356                      self.zeroUnit(r, d, v)
   357                      d += v
   358                      n -= v
   359                  }
   360              }
   361          }
   362  
   363          /* memcpy(Pd, Ps, Rx) */
   364          case hir.OP_bcopy: {
   365              self.Ins = append(
   366                  self.Ins,
   367                  &IrConstPtr {
   368                      R: Pr(0),
   369                      P: rtx.F_memmove,
   370                  },
   371                  &IrCallFunc {
   372                      R    : Pr(0),
   373                      In   : []Reg { Rv(p.Pd), Rv(p.Ps), Rv(p.Rx) },
   374                      Func : rtx.S_memmove,
   375                  },
   376              )
   377          }
   378  
   379          /* C subroutine calls */
   380          case hir.OP_ccall: {
   381              self.Ins = append(
   382                  self.Ins,
   383                  &IrConstPtr {
   384                      R: Pr(0),
   385                      P: hir.LookupCall(p.Iv).Func,
   386                  },
   387                  &IrCallNative {
   388                      R   : Pr(0),
   389                      In  : ri2regs(p.Ar[:p.An]),
   390                      Out : ri2regz(p.Rr[:p.Rn]),
   391                  },
   392              )
   393          }
   394  
   395          /* Go subroutine calls */
   396          case hir.OP_gcall: {
   397              self.Ins = append(
   398                  self.Ins,
   399                  &IrConstPtr {
   400                      R: Pr(0),
   401                      P: hir.LookupCall(p.Iv).Func,
   402                  },
   403                  &IrCallFunc {
   404                      R    : Pr(0),
   405                      In   : ri2regs(p.Ar[:p.An]),
   406                      Out  : ri2regs(p.Rr[:p.Rn]),
   407                      Func : abi.ABI.GetLayout(hir.LookupCall(p.Iv).Id),
   408                  },
   409              )
   410          }
   411  
   412          /* interface method calls */
   413          case hir.OP_icall: {
   414              self.Ins = append(
   415                  self.Ins,
   416                  &IrCallMethod {
   417                      T    : Rv(p.Ps),
   418                      V    : Rv(p.Pd),
   419                      In   : ri2regs(p.Ar[:p.An]),
   420                      Out  : ri2regs(p.Rr[:p.Rn]),
   421                      Slot : hir.LookupCall(p.Iv).Slot,
   422                      Func : abi.ABI.GetLayout(hir.LookupCall(p.Iv).Id),
   423                  },
   424              )
   425          }
   426  
   427          /* trigger a debugger breakpoint */
   428          case hir.OP_break: {
   429              self.Ins = append(
   430                  self.Ins,
   431                  new(IrBreakpoint),
   432              )
   433          }
   434      }
   435  }
   436  
   437  func (self *BasicBlock) zeroUnit(r Reg, d uintptr, n uintptr) {
   438      self.Ins = append(self.Ins,
   439          &IrConstInt {
   440              R: Tr(0),
   441              V: int64(d),
   442          },
   443          &IrLEA {
   444              R   : Pr(0),
   445              Mem : r,
   446              Off : Tr(0),
   447          },
   448          &IrStore {
   449              R    : Rz,
   450              Mem  : Pr(0),
   451              Size : uint8(n),
   452          },
   453      )
   454  }
   455  
   456  func (self *BasicBlock) zeroBlock(r Reg, d uintptr, n uintptr) {
   457      self.Ins = append(self.Ins,
   458          &IrConstInt {
   459              R: Tr(0),
   460              V: int64(d),
   461          },
   462          &IrLEA {
   463              R   : Pr(0),
   464              Mem : r,
   465              Off : Tr(0),
   466          },
   467          &IrConstPtr {
   468              R: Pr(1),
   469              P: rtx.MemZero.ForSize(n),
   470          },
   471          &IrCallNative {
   472              R  : Pr(1),
   473              In : []Reg { Pr(0) },
   474          },
   475      )
   476  }
   477  
   478  func (self *BasicBlock) termReturn(p *hir.Ir) {
   479      self.Term = &IrReturn {
   480          R: ri2regs(p.Rr[:p.Rn]),
   481      }
   482  }
   483  
   484  func (self *BasicBlock) termBranch(to *BasicBlock) {
   485      to.addPred(self)
   486      self.Term = &IrSwitch { Ln: IrLikely(to) }
   487  }
   488  
   489  func (self *BasicBlock) termCondition(p *hir.Ir, t *BasicBlock, f *BasicBlock) {
   490      var cmp IrBinaryOp
   491      var lhs hir.Register
   492      var rhs hir.Register
   493  
   494      /* check for OpCode */
   495      switch p.Op {
   496          case hir.OP_beq  : cmp, lhs, rhs = IrCmpEq  , p.Rx, p.Ry
   497          case hir.OP_bne  : cmp, lhs, rhs = IrCmpNe  , p.Rx, p.Ry
   498          case hir.OP_blt  : cmp, lhs, rhs = IrCmpLt  , p.Rx, p.Ry
   499          case hir.OP_bltu : cmp, lhs, rhs = IrCmpLtu , p.Rx, p.Ry
   500          case hir.OP_bgeu : cmp, lhs, rhs = IrCmpGeu , p.Rx, p.Ry
   501          case hir.OP_beqp : cmp, lhs, rhs = IrCmpEq  , p.Ps, p.Pd
   502          case hir.OP_bnep : cmp, lhs, rhs = IrCmpNe  , p.Ps, p.Pd
   503          default          : panic("invalid branch: " + p.Disassemble(nil))
   504      }
   505  
   506      /* construct the instruction */
   507      ins := &IrBinaryExpr {
   508          R  : Tr(0),
   509          X  : Rv(lhs),
   510          Y  : Rv(rhs),
   511          Op : cmp,
   512      }
   513  
   514      /* add predecessors */
   515      t.addPred(self)
   516      f.addPred(self)
   517  
   518      /* create branch targets */
   519      to := IrUnlikely(t)
   520      br := IrUnlikely(f)
   521  
   522      /* assign the correct likeliness */
   523      if p.Likeliness() == hir.Likely {
   524          to.Likeliness = Likely
   525      } else {
   526          br.Likeliness = Likely
   527      }
   528  
   529      /* attach to the block */
   530      self.Ins = append(self.Ins, ins)
   531      self.Term = &IrSwitch { V: Tr(0), Ln: to, Br: map[int32]*IrBranch { 0: br } }
   532  }