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 }