github.com/cloudwego/frugal@v0.1.15/internal/atm/ssa/pass_constprop.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      `math/bits`
    22      `unsafe`
    23  
    24      `github.com/cloudwego/frugal/internal/atm/abi`
    25  )
    26  
    27  // ConstProp propagates constant through the expression tree.
    28  type ConstProp struct{}
    29  
    30  func (ConstProp) unary(v int64, op IrUnaryOp) int64 {
    31      switch op {
    32          case IrOpNegate   : return -v
    33          case IrOpSwap16   : return int64(bits.ReverseBytes16(uint16(v)))
    34          case IrOpSwap32   : return int64(bits.ReverseBytes32(uint32(v)))
    35          case IrOpSwap64   : return int64(bits.ReverseBytes64(uint64(v)))
    36          case IrOpSx32to64 : return int64(int32(v))
    37          default           : panic(fmt.Sprintf("constprop: invalid unary operator: %d", op))
    38      }
    39  }
    40  
    41  func (ConstProp) binary(x int64, y int64, op IrBinaryOp) int64 {
    42      switch op {
    43          case IrOpAdd    : return x + y
    44          case IrOpSub    : return x - y
    45          case IrOpMul    : return x * y
    46          case IrOpAnd    : return x & y
    47          case IrOpOr     : return x | y
    48          case IrOpXor    : return x ^ y
    49          case IrOpShr    : return x >> y
    50          case IrCmpEq    : if x == y { return 1 } else { return 0 }
    51          case IrCmpNe    : if x != y { return 1 } else { return 0 }
    52          case IrCmpLt    : if x <  y { return 1 } else { return 0 }
    53          case IrCmpLtu   : if uint64(x) <  uint64(y) { return 1 } else { return 0 }
    54          case IrCmpGeu   : if uint64(x) >= uint64(y) { return 1 } else { return 0 }
    55          default         : panic(fmt.Sprintf("constprop: invalid binary operator: %d", op))
    56      }
    57  }
    58  
    59  func (ConstProp) testandset(x int64, y int64) (int64, int64) {
    60      if bv := int64(1 << y); x & bv == 0 {
    61          return 0, x | bv
    62      } else {
    63          return 1, x | bv
    64      }
    65  }
    66  
    67  func (self ConstProp) Apply(cfg *CFG) {
    68      done := false
    69      consts := make(map[Reg]_ConstData)
    70  
    71      /* constant zero registers */
    72      consts[Rz] = constint(0)
    73      consts[Pn] = constptr(nil, Const)
    74  
    75      /* const adder */
    76      addconst := func(r Reg, v _ConstData) {
    77          if _, ok := consts[r]; !ok {
    78              done = false
    79              consts[r] = v
    80          }
    81      }
    82  
    83      /* evaluate const expression until no modifications were made */
    84      for !done {
    85          done = true
    86          for _, bb := range cfg.PostOrder().Reversed() {
    87              phi := make([]*IrPhi, 0, len(bb.Phi))
    88              ins := make([]IrNode, 0, len(bb.Ins))
    89              isconst := false
    90  
    91              /* check every Phi node */
    92              for _, p := range bb.Phi {
    93                  var first bool
    94                  var cdata _ConstData
    95  
    96                  /* assume it's a const */
    97                  first = true
    98                  isconst = true
    99  
   100                  /* a Phi node is a const iff all it's arguments are the same const */
   101                  for _, r := range p.V {
   102                      if *r != p.R {
   103                          if cc, ok := consts[*r]; !ok {
   104                              isconst = false
   105                              break
   106                          } else if first {
   107                              cdata = cc
   108                              first = false
   109                          } else if cdata != cc {
   110                              isconst = false
   111                              break
   112                          }
   113                      }
   114                  }
   115  
   116                  /* not constant, keep it as is */
   117                  if !isconst {
   118                      phi = append(phi, p)
   119                      continue
   120                  }
   121  
   122                  /* registers declared by Phi nodes can never be zero registers */
   123                  if p.R.Kind() == K_zero {
   124                      panic("constprop: assignment to zero registers in Phi node: " + p.String())
   125                  }
   126  
   127                  /* replace the Phi node with a Const node */
   128                  if cdata.i {
   129                      ins = append(ins, &IrConstInt { R: p.R, V: cdata.v })
   130                  } else {
   131                      ins = append(ins, &IrConstPtr { R: p.R, P: cdata.p, M: cdata.c })
   132                  }
   133  
   134                  /* mark as constant */
   135                  done = false
   136                  consts[p.R] = cdata
   137              }
   138  
   139              /* check every instructions */
   140              for _, v := range bb.Ins {
   141                  switch p := v.(type) {
   142                      default: {
   143                          ins = append(ins, p)
   144                      }
   145  
   146                      /* value loads */
   147                      case *IrLoad: {
   148                          var ok bool
   149                          var iv int64
   150                          var cc _ConstData
   151                          var pv unsafe.Pointer
   152  
   153                          /* must be constant memory address */
   154                          if cc, ok = consts[p.Mem]; !ok {
   155                              ins = append(ins, p)
   156                              break
   157                          }
   158  
   159                          /* address must be a pointer */
   160                          if cc.i {
   161                              panic(fmt.Sprintf("constprop: value load on integer value %#x: %s", cc.v, p))
   162                          }
   163  
   164                          /* do not load a volatile pointer */
   165                          if cc.c != Const {
   166                              ins = append(ins, p)
   167                              break
   168                          }
   169  
   170                          /* constant pointer */
   171                          if p.R.Ptr() {
   172                              if p.Size != abi.PtrSize {
   173                                  panic("constprop: pointer load of invalid size: " + p.String())
   174                              } else {
   175                                  pv = *(*unsafe.Pointer)(cc.p)
   176                                  ins = append(ins, &IrConstPtr { R: p.R, P: pv, M: Volatile })
   177                                  addconst(p.R, constptr(pv, Volatile))
   178                                  break
   179                              }
   180                          }
   181  
   182                          /* read the integer */
   183                          switch p.Size {
   184                              case 1  : iv = int64(*(*uint8)(cc.p))
   185                              case 2  : iv = int64(*(*uint16)(cc.p))
   186                              case 4  : iv = int64(*(*uint32)(cc.p))
   187                              case 8  : iv = int64(*(*uint64)(cc.p))
   188                              default : panic("constprop: integer load of invalid size: " + p.String())
   189                          }
   190  
   191                          /* replace the instruction */
   192                          ins = append(ins, &IrConstInt { R: p.R, V: iv })
   193                          addconst(p.R, constint(iv))
   194                      }
   195  
   196                      /* integer constant */
   197                      case *IrConstInt: {
   198                          ins = append(ins, p)
   199                          addconst(p.R, constint(p.V))
   200                      }
   201  
   202                      /* pointer constant */
   203                      case *IrConstPtr: {
   204                          ins = append(ins, p)
   205                          addconst(p.R, constptr(p.P, p.M))
   206                      }
   207  
   208                      /* pointer arithmetics */
   209                      case *IrLEA: {
   210                          if mem, ok := consts[p.Mem]; !ok {
   211                              ins = append(ins, p)
   212                          } else if off, ok := consts[p.Off]; !ok {
   213                              ins = append(ins, p)
   214                          } else if mem.i {
   215                              panic(fmt.Sprintf("constprop: pointer operation on integer value %#x: %s", mem.v, p))
   216                          } else if !off.i {
   217                              panic(fmt.Sprintf("constprop: pointer operation with pointer offset %p: %s", off.p, p))
   218                          } else {
   219                              r := addptr(mem.p, off.v)
   220                              ins = append(ins, &IrConstPtr { R: p.R, P: r, M: Volatile })
   221                              addconst(p.R, constptr(r, mem.c))
   222                          }
   223                      }
   224  
   225                      /* unary expressions */
   226                      case *IrUnaryExpr: {
   227                          if cc, ok := consts[p.V]; !ok {
   228                              ins = append(ins, p)
   229                          } else if !cc.i {
   230                              panic(fmt.Sprintf("constprop: integer operation on pointer value %p: %s", cc.p, p))
   231                          } else {
   232                              r := self.unary(cc.v, p.Op)
   233                              ins = append(ins, &IrConstInt { R: p.R, V: r })
   234                              addconst(p.R, constint(r))
   235                          }
   236                      }
   237  
   238                      /* binary expressions */
   239                      case *IrBinaryExpr: {
   240                          if x, ok := consts[p.X]; !ok {
   241                              ins = append(ins, p)
   242                          } else if y, ok := consts[p.Y]; !ok {
   243                              ins = append(ins, p)
   244                          } else if !x.i {
   245                              panic(fmt.Sprintf("constprop: integer operation on pointer value %p: %s", x.p, p))
   246                          } else if !y.i {
   247                              panic(fmt.Sprintf("constprop: integer operation on pointer value %p: %s", y.p, p))
   248                          } else {
   249                              r := self.binary(x.v, y.v, p.Op)
   250                              ins = append(ins, &IrConstInt { R: p.R, V: r })
   251                              addconst(p.R, constint(r))
   252                          }
   253                      }
   254  
   255                      /* bit test and set operation */
   256                      case *IrBitTestSet: {
   257                          if x, ok := consts[p.X]; !ok {
   258                              ins = append(ins, p)
   259                          } else if y, ok := consts[p.Y]; !ok {
   260                              ins = append(ins, p)
   261                          } else if !x.i {
   262                              panic(fmt.Sprintf("constprop: integer operation on pointer value %p: %s", x.p, p))
   263                          } else if !y.i {
   264                              panic(fmt.Sprintf("constprop: integer operation on pointer value %p: %s", y.p, p))
   265                          } else {
   266                              t, s := self.testandset(x.v, y.v)
   267                              ins = append(ins, &IrConstInt { R: p.T, V: t }, &IrConstInt { R: p.S, V: s })
   268                              addconst(p.T, constint(t))
   269                              addconst(p.S, constint(s))
   270                          }
   271                      }
   272                  }
   273              }
   274  
   275              /* rebuild the basic block */
   276              bb.Phi = phi
   277              bb.Ins = ins
   278          }
   279      }
   280  }