github.com/cloudwego/frugal@v0.1.15/internal/atm/ssa/dominator.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  /** This is an implementation of the Lengauer-Tarjan algorithm described in
    18   *  https://doi.org/10.1145%2F357062.357071
    19   */
    20  
    21  package ssa
    22  
    23  import (
    24      `sort`
    25  
    26      `github.com/cloudwego/frugal/internal/rt`
    27      `github.com/oleiade/lane`
    28  )
    29  
    30  type _LtNode struct {
    31      semi     int
    32      node     *BasicBlock
    33      dom      *_LtNode
    34      label    *_LtNode
    35      parent   *_LtNode
    36      ancestor *_LtNode
    37      pred     []*_LtNode
    38      bucket   map[*_LtNode]struct{}
    39  }
    40  
    41  type _LengauerTarjan struct {
    42      nodes  []*_LtNode
    43      vertex map[int]int
    44  }
    45  
    46  func newLengauerTarjan() *_LengauerTarjan {
    47      return &_LengauerTarjan {
    48          vertex: make(map[int]int),
    49      }
    50  }
    51  
    52  func (self *_LengauerTarjan) dfs(bb *BasicBlock) {
    53      i := len(self.nodes)
    54      self.vertex[bb.Id] = i
    55  
    56      /* create a new node */
    57      p := &_LtNode {
    58          semi   : i,
    59          node   : bb,
    60          bucket : make(map[*_LtNode]struct{}),
    61      }
    62  
    63      /* add to node list */
    64      p.label = p
    65      self.nodes = append(self.nodes, p)
    66  
    67      /* get it's successors iterator */
    68      tr := bb.Term
    69      it := tr.Successors()
    70  
    71      /* traverse the successors */
    72      for it.Next() {
    73          w := it.Block()
    74          idx, ok := self.vertex[w.Id]
    75  
    76          /* not visited yet */
    77          if !ok {
    78              self.dfs(w)
    79              idx = self.vertex[w.Id]
    80              self.nodes[idx].parent = p
    81          }
    82  
    83          /* add predecessors */
    84          q := self.nodes[idx]
    85          q.pred = append(q.pred, p)
    86      }
    87  }
    88  
    89  func (self *_LengauerTarjan) eval(p *_LtNode) *_LtNode {
    90      if p.ancestor == nil {
    91          return p
    92      } else {
    93          self.compress(p)
    94          return p.label
    95      }
    96  }
    97  
    98  func (self *_LengauerTarjan) link(p *_LtNode, q *_LtNode) {
    99      q.ancestor = p
   100  }
   101  
   102  func (self *_LengauerTarjan) relable(p *_LtNode) {
   103      if p.label.semi > p.ancestor.label.semi {
   104          p.label = p.ancestor.label
   105      }
   106  }
   107  
   108  func (self *_LengauerTarjan) compress(p *_LtNode) {
   109      if p.ancestor.ancestor != nil {
   110          self.compress(p.ancestor)
   111          self.relable(p)
   112          p.ancestor = p.ancestor.ancestor
   113      }
   114  }
   115  
   116  type _NodeDepth struct {
   117      d  int
   118      bb int
   119  }
   120  
   121  func updateDominatorTree(cfg *CFG) {
   122      rt.MapClear(cfg.DominatedBy)
   123      rt.MapClear(cfg.DominatorOf)
   124  
   125      /* Step 1: Carry out a depth-first search of the problem graph. Number the vertices
   126       * from 1 to n as they are reached during the search. Initialize the variables used
   127       * in succeeding steps. */
   128      lt := newLengauerTarjan()
   129      lt.dfs(cfg.Root)
   130  
   131      /* perform Step 2 and Step 3 for every node */
   132      for i := len(lt.nodes) - 1; i > 0; i-- {
   133          p := lt.nodes[i]
   134          q := (*_LtNode)(nil)
   135  
   136          /* Step 2: Compute the semidominators of all vertices by applying Theorem 4.
   137           * Carry out the computation vertex by vertex in decreasing order by number. */
   138          for _, v := range p.pred {
   139              q = lt.eval(v)
   140              p.semi = minint(p.semi, q.semi)
   141          }
   142  
   143          /* link the ancestor */
   144          lt.link(p.parent, p)
   145          lt.nodes[p.semi].bucket[p] = struct{}{}
   146  
   147          /* Step 3: Implicitly define the immediate dominator of each vertex by applying Corollary 1 */
   148          for v := range p.parent.bucket {
   149              if q = lt.eval(v); q.semi < v.semi {
   150                  v.dom = q
   151              } else {
   152                  v.dom = p.parent
   153              }
   154          }
   155  
   156          /* clear the bucket */
   157          for v := range p.parent.bucket {
   158              delete(p.parent.bucket, v)
   159          }
   160      }
   161  
   162      /* Step 4: Explicitly define the immediate dominator of each vertex, carrying out the
   163       * computation vertex by vertex in increasing order by number. */
   164      for _, p := range lt.nodes[1:] {
   165          if p.dom.node.Id != lt.nodes[p.semi].node.Id {
   166              p.dom = p.dom.dom
   167          }
   168      }
   169  
   170      /* map the dominator relationship */
   171      for _, p := range lt.nodes[1:] {
   172          cfg.DominatedBy[p.node.Id] = p.dom.node
   173          cfg.DominatorOf[p.dom.node.Id] = append(cfg.DominatorOf[p.dom.node.Id], p.node)
   174      }
   175  
   176      /* sort the dominators */
   177      for _, p := range cfg.DominatorOf {
   178          sort.Slice(p, func(i int, j int) bool {
   179              return p[i].Id < p[j].Id
   180          })
   181      }
   182  }
   183  
   184  func updateDominatorDepth(cfg *CFG) {
   185      r := cfg.Root.Id
   186      q := lane.NewQueue()
   187  
   188      /* add the root node */
   189      q.Enqueue(_NodeDepth { bb: r })
   190      rt.MapClear(cfg.Depth)
   191  
   192      /* calculate depth for every block */
   193      for !q.Empty() {
   194          d := q.Dequeue().(_NodeDepth)
   195          cfg.Depth[d.bb] = d.d
   196  
   197          /* add all the dominated nodes */
   198          for _, p := range cfg.DominatorOf[d.bb] {
   199              q.Enqueue(_NodeDepth {
   200                  d  : d.d + 1,
   201                  bb : p.Id,
   202              })
   203          }
   204      }
   205  }
   206  
   207  func updateDominatorFrontier(cfg *CFG) {
   208      r := cfg.Root
   209      q := lane.NewQueue()
   210  
   211      /* add the root node */
   212      q.Enqueue(r)
   213      rt.MapClear(cfg.DominanceFrontier)
   214  
   215      /* calculate dominance frontier for every block */
   216      for !q.Empty() {
   217          k := q.Dequeue().(*BasicBlock)
   218          addImmediateDominated(cfg.DominatorOf, k, q)
   219          computeDominanceFrontier(cfg.DominatorOf, k, cfg.DominanceFrontier)
   220      }
   221  }
   222  
   223  func isStrictlyDominates(dom map[int][]*BasicBlock, p *BasicBlock, q *BasicBlock) bool {
   224      for _, v := range dom[p.Id] { if v != p && (v == q || isStrictlyDominates(dom, v, q)) { return true } }
   225      return false
   226  }
   227  
   228  func addImmediateDominated(dom map[int][]*BasicBlock, node *BasicBlock, q *lane.Queue) {
   229      for _, p := range dom[node.Id] {
   230          q.Enqueue(p)
   231      }
   232  }
   233  
   234  func computeDominanceFrontier(dom map[int][]*BasicBlock, node *BasicBlock, dfm map[int][]*BasicBlock) []*BasicBlock {
   235      var it IrSuccessors
   236      var df map[*BasicBlock]struct{}
   237  
   238      /* check for cached values */
   239      if v, ok := dfm[node.Id]; ok {
   240          return v
   241      }
   242  
   243      /* get the successor iterator */
   244      it = node.Term.Successors()
   245      df = make(map[*BasicBlock]struct{})
   246  
   247      /* local(X) = set of successors of X that X does not immediately dominate */
   248      for it.Next() {
   249          if y := it.Block(); !isStrictlyDominates(dom, node, y) {
   250              df[y] = struct{}{}
   251          }
   252      }
   253  
   254      /* df(X) = union of local(X) and ( union of up(K) for all K that are children of X ) */
   255      for _, k := range dom[node.Id] {
   256          for _, y := range computeDominanceFrontier(dom, k, dfm) {
   257              if !isStrictlyDominates(dom, node, y) {
   258                  df[y] = struct{}{}
   259              }
   260          }
   261      }
   262  
   263      /* convert to slice */
   264      nb := len(df)
   265      ret := make([]*BasicBlock, 0, nb)
   266  
   267      /* extract all the keys */
   268      for bb := range df {
   269          ret = append(ret, bb)
   270      }
   271  
   272      /* sort by ID */
   273      sort.Slice(ret, func(i int, j int) bool {
   274          return ret[i].Id < ret[j].Id
   275      })
   276  
   277      /* add to cache */
   278      dfm[node.Id] = ret
   279      return ret
   280  }