github.com/cloudwego/frugal@v0.1.15/internal/atm/ssa/pass_compact_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 ssa
    18  
    19  import (
    20      `github.com/cloudwego/frugal/internal/atm/abi`
    21  )
    22  
    23  type _MemAddr struct {
    24      m Mem
    25      n uint8
    26  }
    27  
    28  func memaddr(m Mem, n uint8) _MemAddr {
    29      return _MemAddr { m, n }
    30  }
    31  
    32  type _MemTree struct {
    33      next *_MemTree
    34      mems map[Mem]Reg
    35  }
    36  
    37  func (self *_MemTree) add(mm Mem, rr Reg) {
    38      if _, ok := self.mems[mm]; ok {
    39          panic("memtree: memory operand conflict: " + mm.String())
    40      } else {
    41          self.mems[mm] = rr
    42      }
    43  }
    44  
    45  func (self *_MemTree) find(mm Mem) (Reg, bool) {
    46      if rr, ok := self.mems[mm]; ok {
    47          return rr, true
    48      } else if self.next == nil {
    49          return 0, false
    50      } else {
    51          return self.next.find(mm)
    52      }
    53  }
    54  
    55  func (self *_MemTree) derive() *_MemTree {
    56      return &_MemTree {
    57          next: self,
    58          mems: make(map[Mem]Reg),
    59      }
    60  }
    61  
    62  // Compaction is like Fusion, but it performs the reverse action to reduce redundant operations.
    63  type Compaction struct{}
    64  
    65  func (self Compaction) dfs(cfg *CFG, bb *BasicBlock, mems *_MemTree, next *bool) {
    66      id := bb.Id
    67      vv := make(map[_MemAddr]Reg)
    68  
    69      /* check every instructions, reuse memory addresses as much as possible */
    70      for i, v := range bb.Ins {
    71          switch p := v.(type) {
    72              default: {
    73                  break
    74              }
    75  
    76              /* load effective address */
    77              case *IrAMD64_LEA: {
    78                  mems.add(p.M, p.R)
    79              }
    80  
    81              /* lea {mem}, %r0; movx {mem}, %r1 --> lea {mem}, %r0; movx (%r0), %r1 */
    82              case *IrAMD64_MOV_load: {
    83                  if m, ok := mems.find(p.M); ok {
    84                      p.M, *next = Ptr(m, 0), true
    85                  } else if _, ok = vv[memaddr(p.M, p.N)]; !ok {
    86                      vv[memaddr(p.M, p.N)] = p.R
    87                  }
    88              }
    89  
    90              /* movx {mem}, %r0; movbex {mem}, %r1 --> movx {mem}, %r0; bswapx %r0, %r1
    91               * leax {mem}, %r0; movbex {mem}, %r1 --> leax {mem}, %r0; movbex (%r0), %r1 */
    92              case *IrAMD64_MOV_load_be: {
    93                  if m, ok := mems.find(p.M); ok {
    94                      p.M, *next = Ptr(m, 0), true
    95                  } else if r, ok := vv[memaddr(p.M, p.N)]; ok {
    96                      bb.Ins[i], *next = &IrAMD64_BSWAP { R: p.R, V: r, N: p.N }, true
    97                  }
    98              }
    99  
   100              /* lea {mem}, %r0; movx %r1, {mem} --> lea {mem}, %r0; movx %r1, (%r0) */
   101              case *IrAMD64_MOV_store_r: {
   102                  if m, ok := mems.find(p.M); ok {
   103                      p.M, *next = Ptr(m, 0), true
   104                      delete(vv, memaddr(p.M, p.N))
   105                  }
   106              }
   107  
   108              /* lea {mem}, %r0; movx {imm}, {mem} --> lea {mem}, %r0; movx {imm}, (%r0) */
   109              case *IrAMD64_MOV_store_i: {
   110                  if m, ok := mems.find(p.M); ok {
   111                      p.M, *next = Ptr(m, 0), true
   112                      delete(vv, memaddr(p.M, p.N))
   113                  }
   114              }
   115  
   116              /* lea {mem}, %r0; movx {ptr}, {mem} --> lea {mem}, %r0; movx {ptr}, (%r0) */
   117              case *IrAMD64_MOV_store_p: {
   118                  if m, ok := mems.find(p.M); ok {
   119                      p.M, *next = Ptr(m, 0), true
   120                      delete(vv, memaddr(p.M, abi.PtrSize))
   121                  }
   122              }
   123  
   124              /* lea {mem}, %r0; movbex %r1, {mem} --> lea {mem}, %r0; movbex %r1, (%r0) */
   125              case *IrAMD64_MOV_store_be: {
   126                  if m, ok := mems.find(p.M); ok {
   127                      p.M, *next = Ptr(m, 0), true
   128                      delete(vv, memaddr(p.M, p.N))
   129                  }
   130              }
   131  
   132              /* movx {mem}, %r0; cmpx %r1, {mem} --> movx {mem}, %r0; cmpx %r1, %r0
   133               * leaq {mem}, %r0; cmpx %r1, {mem} --> leaq {mem}, %r0; cmpx %r1, (%r0) */
   134              case *IrAMD64_CMPQ_rm: {
   135                  if m, ok := mems.find(p.Y); ok {
   136                      p.Y, *next = Ptr(m, 0), true
   137                  } else if v, ok := vv[memaddr(p.Y, p.N)]; ok {
   138                      bb.Ins[i], *next = &IrAMD64_CMPQ_rr { X: p.X, Y: v, Op: p.Op }, true
   139                  }
   140              }
   141  
   142              /* movx {mem}, %r0; cmpx {mem}, %r1 --> movx {mem}, %r0; cmpx %r0, %r1
   143               * leaq {mem}, %r0; cmpx {mem}, %r1 --> leaq {mem}, %r0; cmpx (%r0), %r1 */
   144              case *IrAMD64_CMPQ_mr: {
   145                  if m, ok := mems.find(p.X); ok {
   146                      p.X, *next = Ptr(m, 0), true
   147                  } else if r, ok := vv[memaddr(p.X, p.N)]; ok {
   148                      bb.Ins[i], *next = &IrAMD64_CMPQ_rr { X: r, Y: p.Y, Op: p.Op }, true
   149                  }
   150              }
   151  
   152              /* movx {mem}, %r0; cmpx {mem}, {imm} --> movx {mem}, %r0; cmpx %r0, {imm}
   153               * leaq {mem}, %r0; cmpx {mem}, {imm} --> leaq {mem}, %r0; cmpx (%r0), {imm} */
   154              case *IrAMD64_CMPQ_mi: {
   155                  if m, ok := mems.find(p.X); ok {
   156                      p.X, *next = Ptr(m, 0), true
   157                  } else if r, ok := vv[memaddr(p.X, p.N)]; ok {
   158                      bb.Ins[i], *next = &IrAMD64_CMPQ_ri { X: r, Y: p.Y, Op: p.Op }, true
   159                  }
   160              }
   161  
   162              /* movx {mem}, %r0; cmpx {mem}, {ptr} --> movx {mem}, %r0; cmpx %r0, {ptr}
   163               * leaq {mem}, %r0; cmpx {mem}, {ptr} --> leaq {mem}, %r0; cmpx (%r0), {ptr} */
   164              case *IrAMD64_CMPQ_mp: {
   165                  if m, ok := mems.find(p.X); ok {
   166                      p.X, *next = Ptr(m, 0), true
   167                  } else if r, ok := vv[memaddr(p.X, abi.PtrSize)]; ok {
   168                      bb.Ins[i], *next = &IrAMD64_CMPQ_rp { X: r, Y: p.Y, Op: p.Op }, true
   169                  }
   170              }
   171  
   172              /* movx {mem}, %r0; cmpx {imm}, {mem} --> movx {mem}, %r0; cmpx {imm}, %r0
   173               * leaq {mem}, %r0; cmpx {imm}, {mem} --> leaq {mem}, %r0; cmpx {imm}, (%r0) */
   174              case *IrAMD64_CMPQ_im: {
   175                  if m, ok := mems.find(p.Y); ok {
   176                      p.Y, *next = Ptr(m, 0), true
   177                  } else if r, ok := vv[memaddr(p.Y, p.N)]; ok {
   178                      bb.Ins[i], *next = &IrAMD64_CMPQ_ir { X: p.X, Y: r, Op: p.Op }, true
   179                  }
   180              }
   181  
   182              /* movx {mem}, %r0; cmpx {ptr}, {mem} --> movx {mem}, %r0; cmpx %r0, {ptr}
   183               * leaq {mem}, %r0; cmpx {ptr}, {mem} --> leaq {mem}, %r0; cmpx (%r0), {ptr} */
   184              case *IrAMD64_CMPQ_pm: {
   185                  if m, ok := mems.find(p.Y); ok {
   186                      p.Y, *next = Ptr(m, 0), true
   187                  } else if r, ok := vv[memaddr(p.Y, abi.PtrSize)]; ok {
   188                      bb.Ins[i], *next = &IrAMD64_CMPQ_pr { X: p.X, Y: r, Op: p.Op }, true
   189                  }
   190              }
   191          }
   192      }
   193  
   194      /* check for terminators */
   195      switch p := bb.Term.(type) {
   196          default: {
   197              break
   198          }
   199  
   200          /* movx {mem}, %r0; cmpx %r1, {mem}; jcc --> movx {mem}, %r0; cmpx %r1, %r0; jcc
   201           * leaq {mem}, %r0; cmpx %r1, {mem}; jcc --> leaq {mem}, %r0; cmpx %r1, (%r0); jcc */
   202          case *IrAMD64_Jcc_rm: {
   203              if m, ok := mems.find(p.Y); ok {
   204                  p.Y, *next = Ptr(m, 0), true
   205              } else if r, ok := vv[memaddr(p.Y, p.N)]; ok {
   206                  bb.Term, *next = &IrAMD64_Jcc_rr { X: p.X, Y: r, To: p.To, Ln: p.Ln, Op: p.Op }, true
   207              }
   208          }
   209  
   210          /* movx {mem}, %r0; cmpx {mem}, %r1; jcc --> movx {mem}, %r0; cmpx %r0, %r1; jcc
   211           * leaq {mem}, %r0; cmpx {mem}, %r1; jcc --> leaq {mem}, %r0; cmpx (%r0), %r1; jcc */
   212          case *IrAMD64_Jcc_mr: {
   213              if m, ok := mems.find(p.X); ok {
   214                  p.X, *next = Ptr(m, 0), true
   215              } else if r, ok := vv[memaddr(p.X, p.N)]; ok {
   216                  bb.Term, *next = &IrAMD64_Jcc_rr { X: r, Y: p.Y, To: p.To, Ln: p.Ln, Op: p.Op }, true
   217              }
   218          }
   219  
   220          /* movx {mem}, %r0; cmpx {mem}, {imm}; jcc --> movx {mem}, %r0; cmpx %r0, {imm}; jcc
   221           * leaq {mem}, %r0; cmpx {mem}, {imm}; jcc --> leaq {mem}, %r0; cmpx (%r0), {imm}; jcc */
   222          case *IrAMD64_Jcc_mi: {
   223              if m, ok := mems.find(p.X); ok {
   224                  p.X, *next = Ptr(m, 0), true
   225              } else if r, ok := vv[memaddr(p.X, p.N)]; ok {
   226                  bb.Term, *next = &IrAMD64_Jcc_ri { X: r, Y: p.Y, To: p.To, Ln: p.Ln, Op: p.Op }, true
   227              }
   228          }
   229  
   230          /* movx {mem}, %r0; cmpx {mem}, {ptr}; jcc --> movx {mem}, %r0; cmpx %r0, {ptr}; jcc
   231           * leaq {mem}, %r0; cmpx {mem}, {ptr}; jcc --> leaq {mem}, %r0; cmpx (%r0), {ptr}; jcc */
   232          case *IrAMD64_Jcc_mp: {
   233              if m, ok := mems.find(p.X); ok {
   234                  p.X, *next = Ptr(m, 0), true
   235              } else if r, ok := vv[memaddr(p.X, abi.PtrSize)]; ok {
   236                  bb.Term, *next = &IrAMD64_Jcc_rp { X: r, Y: p.Y, To: p.To, Ln: p.Ln, Op: p.Op }, true
   237              }
   238          }
   239  
   240          /* movx {mem}, %r0; cmpx {imm}, {mem}; jcc --> movx {mem}, %r0; cmpx {imm}, %r0; jcc
   241           * leaq {mem}, %r0; cmpx {imm}, {mem}; jcc --> leaq {mem}, %r0; cmpx {imm}, (%r0); jcc */
   242          case *IrAMD64_Jcc_im: {
   243              if m, ok := mems.find(p.Y); ok {
   244                  p.Y, *next = Ptr(m, 0), true
   245              } else if r, ok := vv[memaddr(p.Y, p.N)]; ok {
   246                  bb.Term, *next = &IrAMD64_Jcc_ir { X: p.X, Y: r, To: p.To, Ln: p.Ln, Op: p.Op }, true
   247              }
   248          }
   249  
   250          /* movx {mem}, %r0; cmpx {ptr}, {mem}; jcc --> movx {mem}, %r0; cmpx %r0, {ptr}; jcc
   251           * leaq {mem}, %r0; cmpx {ptr}, {mem}; jcc --> leaq {mem}, %r0; cmpx (%r0), {ptr}; jcc */
   252          case *IrAMD64_Jcc_pm: {
   253              if m, ok := mems.find(p.Y); ok {
   254                  p.Y, *next = Ptr(m, 0), true
   255              } else if r, ok := vv[memaddr(p.Y, abi.PtrSize)]; ok {
   256                  bb.Term, *next = &IrAMD64_Jcc_pr { X: p.X, Y: r, To: p.To, Ln: p.Ln, Op: p.Op }, true
   257              }
   258          }
   259      }
   260  
   261      /* DFS the dominator tree */
   262      for _, p := range cfg.DominatorOf[id] {
   263          self.dfs(cfg, p, mems.derive(), next)
   264      }
   265  }
   266  
   267  func (self Compaction) Apply(cfg *CFG) {
   268      for next := true; next; {
   269          next = false
   270          self.dfs(cfg, cfg.Root, (*_MemTree).derive(nil), &next)
   271      }
   272  }