github.com/cloudwego/frugal@v0.1.15/internal/atm/ssa/pass_tdce.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  // TDCE removes trivial dead-code such as unused register definations from CFG.
    20  type TDCE struct{}
    21  
    22  func (TDCE) Apply(cfg *CFG) {
    23      for {
    24          done := true
    25          decl := make(map[Reg]struct{})
    26  
    27          /* Phase 1: Mark all the definations */
    28          cfg.PostOrder().ForEach(func(bb *BasicBlock) {
    29              var ok bool
    30              var defs IrDefinitions
    31  
    32              /* mark all definations in Phi nodes */
    33              for _, v := range bb.Phi {
    34                  for _, r := range v.Definitions() {
    35                      decl[*r] = struct{}{}
    36                  }
    37              }
    38  
    39              /* mark all definations in instructions if any */
    40              for _, v := range bb.Ins {
    41                  if defs, ok = v.(IrDefinitions); ok {
    42                      for _, r := range defs.Definitions() {
    43                          decl[*r] = struct{}{}
    44                      }
    45                  }
    46              }
    47  
    48              /* mark all definations in terminators if any */
    49              if defs, ok = bb.Term.(IrDefinitions); ok {
    50                  for _, r := range defs.Definitions() {
    51                      decl[*r] = struct{}{}
    52                  }
    53              }
    54          })
    55  
    56          /* Phase 2: Find all register usages */
    57          cfg.PostOrder().ForEach(func(bb *BasicBlock) {
    58              var ok bool
    59              var use IrUsages
    60  
    61              /* mark all usages in Phi nodes */
    62              for _, v := range bb.Phi {
    63                  for _, r := range v.Usages() {
    64                      delete(decl, *r)
    65                  }
    66              }
    67  
    68              /* mark all usages in instructions if any */
    69              for _, v := range bb.Ins {
    70                  if use, ok = v.(IrUsages); ok {
    71                      for _, r := range use.Usages() {
    72                          delete(decl, *r)
    73                      }
    74                  }
    75              }
    76  
    77              /* mark usages in the terminator if any */
    78              if use, ok = bb.Term.(IrUsages); ok {
    79                  for _, r := range use.Usages() {
    80                      delete(decl, *r)
    81                  }
    82              }
    83          })
    84  
    85          /* Phase 3: Remove all unused declarations */
    86          cfg.PostOrder().ForEach(func(bb *BasicBlock) {
    87              var ok bool
    88              var defs IrDefinitions
    89  
    90              /* replace unused Phi assigments with zero registers */
    91              for _, v := range bb.Phi {
    92                  for _, r := range v.Definitions() {
    93                      if _, ok = decl[*r]; ok && r.Kind() != K_zero {
    94                          *r, done = r.Zero(), false
    95                      }
    96                  }
    97              }
    98  
    99              /* replace unused instruction assigments with zero registers */
   100              for _, v := range bb.Ins {
   101                  if defs, ok = v.(IrDefinitions); ok {
   102                      for _, r := range defs.Definitions() {
   103                          if _, ok = decl[*r]; ok && r.Kind() != K_zero {
   104                              *r, done = r.Zero(), false
   105                          }
   106                      }
   107                  }
   108              }
   109  
   110              /* replace unused terminator assigments with zero registers */
   111              if defs, ok = bb.Term.(IrDefinitions); ok {
   112                  for _, r := range defs.Definitions() {
   113                      if _, ok = decl[*r]; ok && r.Kind() != K_zero {
   114                          *r, done = r.Zero(), false
   115                      }
   116                  }
   117              }
   118          })
   119  
   120          /* Phase 4: Remove the entire defination if it's all zeros */
   121          cfg.PostOrder().ForEach(func(bb *BasicBlock) {
   122              phi, ins := bb.Phi, bb.Ins
   123              bb.Phi, bb.Ins = bb.Phi[:0], bb.Ins[:0]
   124  
   125              /* remove Phi nodes that don't have any effects */
   126              for _, v := range phi {
   127                  for _, r := range v.Definitions() {
   128                      if r.Kind() != K_zero {
   129                          bb.Phi = append(bb.Phi, v)
   130                          break
   131                      }
   132                  }
   133              }
   134  
   135              /* remove instructions that don't have any effects */
   136              for _, v := range ins {
   137                  if _, ok := v.(IrImpure); ok {
   138                      bb.Ins = append(bb.Ins, v)
   139                  } else if d, ok := v.(IrDefinitions); !ok {
   140                      bb.Ins = append(bb.Ins, v)
   141                  } else {
   142                      for _, r := range d.Definitions() {
   143                          if r.Kind() != K_zero {
   144                              bb.Ins = append(bb.Ins, v)
   145                              break
   146                          }
   147                      }
   148                  }
   149              }
   150          })
   151  
   152          /* no more modifications */
   153          if done {
   154              break
   155          }
   156      }
   157  }