github.com/miolini/go@v0.0.0-20160405192216-fca68c8cb408/src/cmd/compile/internal/ssa/dom.go (about) 1 // Copyright 2015 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package ssa 6 7 // mark values 8 const ( 9 notFound = 0 // block has not been discovered yet 10 notExplored = 1 // discovered and in queue, outedges not processed yet 11 explored = 2 // discovered and in queue, outedges processed 12 done = 3 // all done, in output ordering 13 ) 14 15 // This file contains code to compute the dominator tree 16 // of a control-flow graph. 17 18 // postorder computes a postorder traversal ordering for the 19 // basic blocks in f. Unreachable blocks will not appear. 20 func postorder(f *Func) []*Block { 21 mark := make([]byte, f.NumBlocks()) 22 23 // result ordering 24 var order []*Block 25 26 // stack of blocks 27 var s []*Block 28 s = append(s, f.Entry) 29 mark[f.Entry.ID] = notExplored 30 for len(s) > 0 { 31 b := s[len(s)-1] 32 switch mark[b.ID] { 33 case explored: 34 // Children have all been visited. Pop & output block. 35 s = s[:len(s)-1] 36 mark[b.ID] = done 37 order = append(order, b) 38 case notExplored: 39 // Children have not been visited yet. Mark as explored 40 // and queue any children we haven't seen yet. 41 mark[b.ID] = explored 42 for _, c := range b.Succs { 43 if mark[c.ID] == notFound { 44 mark[c.ID] = notExplored 45 s = append(s, c) 46 } 47 } 48 default: 49 b.Fatalf("bad stack state %v %d", b, mark[b.ID]) 50 } 51 } 52 return order 53 } 54 55 type linkedBlocks func(*Block) []*Block 56 57 const nscratchslices = 8 58 59 // experimentally, functions with 512 or fewer blocks account 60 // for 75% of memory (size) allocation for dominator computation 61 // in make.bash. 62 const minscratchblocks = 512 63 64 func (cfg *Config) scratchBlocksForDom(maxBlockID int) (a, b, c, d, e, f, g, h []ID) { 65 tot := maxBlockID * nscratchslices 66 scratch := cfg.domblockstore 67 if len(scratch) < tot { 68 // req = min(1.5*tot, nscratchslices*minscratchblocks) 69 // 50% padding allows for graph growth in later phases. 70 req := (tot * 3) >> 1 71 if req < nscratchslices*minscratchblocks { 72 req = nscratchslices * minscratchblocks 73 } 74 scratch = make([]ID, req) 75 cfg.domblockstore = scratch 76 } else { 77 // Clear as much of scratch as we will (re)use 78 scratch = scratch[0:tot] 79 for i := range scratch { 80 scratch[i] = 0 81 } 82 } 83 84 a = scratch[0*maxBlockID : 1*maxBlockID] 85 b = scratch[1*maxBlockID : 2*maxBlockID] 86 c = scratch[2*maxBlockID : 3*maxBlockID] 87 d = scratch[3*maxBlockID : 4*maxBlockID] 88 e = scratch[4*maxBlockID : 5*maxBlockID] 89 f = scratch[5*maxBlockID : 6*maxBlockID] 90 g = scratch[6*maxBlockID : 7*maxBlockID] 91 h = scratch[7*maxBlockID : 8*maxBlockID] 92 93 return 94 } 95 96 // dfs performs a depth first search over the blocks starting at the set of 97 // blocks in the entries list (in arbitrary order). dfnum contains a mapping 98 // from block id to an int indicating the order the block was reached or 99 // notFound if the block was not reached. order contains a mapping from dfnum 100 // to block. 101 func (f *Func) dfs(entries []*Block, succFn linkedBlocks, dfnum, order, parent []ID) (fromID []*Block) { 102 maxBlockID := entries[0].Func.NumBlocks() 103 104 fromID = make([]*Block, maxBlockID) 105 106 for _, entry := range entries[0].Func.Blocks { 107 eid := entry.ID 108 if fromID[eid] != nil { 109 panic("Colliding entry IDs") 110 } 111 fromID[eid] = entry 112 } 113 114 n := ID(0) 115 s := make([]*Block, 0, 256) 116 for _, entry := range entries { 117 if dfnum[entry.ID] != notFound { 118 continue // already found from a previous entry 119 } 120 s = append(s, entry) 121 parent[entry.ID] = entry.ID 122 for len(s) > 0 { 123 node := s[len(s)-1] 124 s = s[:len(s)-1] 125 126 n++ 127 for _, w := range succFn(node) { 128 // if it has a dfnum, we've already visited it 129 if dfnum[w.ID] == notFound { 130 s = append(s, w) 131 parent[w.ID] = node.ID 132 dfnum[w.ID] = notExplored 133 } 134 } 135 dfnum[node.ID] = n 136 order[n] = node.ID 137 } 138 } 139 140 return 141 } 142 143 // dominators computes the dominator tree for f. It returns a slice 144 // which maps block ID to the immediate dominator of that block. 145 // Unreachable blocks map to nil. The entry block maps to nil. 146 func dominators(f *Func) []*Block { 147 preds := func(b *Block) []*Block { return b.Preds } 148 succs := func(b *Block) []*Block { return b.Succs } 149 150 //TODO: benchmark and try to find criteria for swapping between 151 // dominatorsSimple and dominatorsLT 152 return f.dominatorsLT([]*Block{f.Entry}, preds, succs) 153 } 154 155 // postDominators computes the post-dominator tree for f. 156 func postDominators(f *Func) []*Block { 157 preds := func(b *Block) []*Block { return b.Preds } 158 succs := func(b *Block) []*Block { return b.Succs } 159 160 if len(f.Blocks) == 0 { 161 return nil 162 } 163 164 // find the exit blocks 165 var exits []*Block 166 for _, b := range f.Blocks { 167 switch b.Kind { 168 case BlockExit, BlockRet, BlockRetJmp, BlockCall, BlockCheck: 169 exits = append(exits, b) 170 } 171 } 172 173 // infinite loop with no exit 174 if exits == nil { 175 return make([]*Block, f.NumBlocks()) 176 } 177 return f.dominatorsLT(exits, succs, preds) 178 } 179 180 // dominatorsLt runs Lengauer-Tarjan to compute a dominator tree starting at 181 // entry and using predFn/succFn to find predecessors/successors to allow 182 // computing both dominator and post-dominator trees. 183 func (f *Func) dominatorsLT(entries []*Block, predFn linkedBlocks, succFn linkedBlocks) []*Block { 184 // Based on Lengauer-Tarjan from Modern Compiler Implementation in C - 185 // Appel with optimizations from Finding Dominators in Practice - 186 // Georgiadis 187 188 maxBlockID := entries[0].Func.NumBlocks() 189 190 dfnum, vertex, parent, semi, samedom, ancestor, best, bucket := f.Config.scratchBlocksForDom(maxBlockID) 191 192 // dfnum := make([]ID, maxBlockID) // conceptually int32, but punning for allocation purposes. 193 // vertex := make([]ID, maxBlockID) 194 // parent := make([]ID, maxBlockID) 195 196 // semi := make([]ID, maxBlockID) 197 // samedom := make([]ID, maxBlockID) 198 // ancestor := make([]ID, maxBlockID) 199 // best := make([]ID, maxBlockID) 200 // bucket := make([]ID, maxBlockID) 201 202 // Step 1. Carry out a depth first search of the problem graph. Number 203 // the vertices from 1 to n as they are reached during the search. 204 fromID := f.dfs(entries, succFn, dfnum, vertex, parent) 205 206 idom := make([]*Block, maxBlockID) 207 208 // Step 2. Compute the semidominators of all vertices by applying 209 // Theorem 4. Carry out the computation vertex by vertex in decreasing 210 // order by number. 211 for i := maxBlockID - 1; i > 0; i-- { 212 w := vertex[i] 213 if w == 0 { 214 continue 215 } 216 217 if dfnum[w] == notFound { 218 // skip unreachable node 219 continue 220 } 221 222 // Step 3. Implicitly define the immediate dominator of each 223 // vertex by applying Corollary 1. (reordered) 224 for v := bucket[w]; v != 0; v = bucket[v] { 225 u := eval(v, ancestor, semi, dfnum, best) 226 if semi[u] == semi[v] { 227 idom[v] = fromID[w] // true dominator 228 } else { 229 samedom[v] = u // v has same dominator as u 230 } 231 } 232 233 p := parent[w] 234 s := p // semidominator 235 236 var sp ID 237 // calculate the semidominator of w 238 for _, v := range predFn(fromID[w]) { 239 if dfnum[v.ID] == notFound { 240 // skip unreachable predecessor 241 continue 242 } 243 244 if dfnum[v.ID] <= dfnum[w] { 245 sp = v.ID 246 } else { 247 sp = semi[eval(v.ID, ancestor, semi, dfnum, best)] 248 } 249 250 if dfnum[sp] < dfnum[s] { 251 s = sp 252 } 253 } 254 255 // link 256 ancestor[w] = p 257 best[w] = w 258 259 semi[w] = s 260 if semi[s] != parent[s] { 261 bucket[w] = bucket[s] 262 bucket[s] = w 263 } 264 } 265 266 // Final pass of step 3 267 for v := bucket[0]; v != 0; v = bucket[v] { 268 idom[v] = fromID[bucket[0]] 269 } 270 271 // Step 4. Explicitly define the immediate dominator of each vertex, 272 // carrying out the computation vertex by vertex in increasing order by 273 // number. 274 for i := 1; i < maxBlockID-1; i++ { 275 w := vertex[i] 276 if w == 0 { 277 continue 278 } 279 // w has the same dominator as samedom[w] 280 if samedom[w] != 0 { 281 idom[w] = idom[samedom[w]] 282 } 283 } 284 return idom 285 } 286 287 // eval function from LT paper with path compression 288 func eval(v ID, ancestor []ID, semi []ID, dfnum []ID, best []ID) ID { 289 a := ancestor[v] 290 if ancestor[a] != 0 { 291 bid := eval(a, ancestor, semi, dfnum, best) 292 ancestor[v] = ancestor[a] 293 if dfnum[semi[bid]] < dfnum[semi[best[v]]] { 294 best[v] = bid 295 } 296 } 297 return best[v] 298 } 299 300 // dominators computes the dominator tree for f. It returns a slice 301 // which maps block ID to the immediate dominator of that block. 302 // Unreachable blocks map to nil. The entry block maps to nil. 303 func dominatorsSimple(f *Func) []*Block { 304 // A simple algorithm for now 305 // Cooper, Harvey, Kennedy 306 idom := make([]*Block, f.NumBlocks()) 307 308 // Compute postorder walk 309 post := postorder(f) 310 311 // Make map from block id to order index (for intersect call) 312 postnum := make([]int, f.NumBlocks()) 313 for i, b := range post { 314 postnum[b.ID] = i 315 } 316 317 // Make the entry block a self-loop 318 idom[f.Entry.ID] = f.Entry 319 if postnum[f.Entry.ID] != len(post)-1 { 320 f.Fatalf("entry block %v not last in postorder", f.Entry) 321 } 322 323 // Compute relaxation of idom entries 324 for { 325 changed := false 326 327 for i := len(post) - 2; i >= 0; i-- { 328 b := post[i] 329 var d *Block 330 for _, p := range b.Preds { 331 if idom[p.ID] == nil { 332 continue 333 } 334 if d == nil { 335 d = p 336 continue 337 } 338 d = intersect(d, p, postnum, idom) 339 } 340 if d != idom[b.ID] { 341 idom[b.ID] = d 342 changed = true 343 } 344 } 345 if !changed { 346 break 347 } 348 } 349 // Set idom of entry block to nil instead of itself. 350 idom[f.Entry.ID] = nil 351 return idom 352 } 353 354 // intersect finds the closest dominator of both b and c. 355 // It requires a postorder numbering of all the blocks. 356 func intersect(b, c *Block, postnum []int, idom []*Block) *Block { 357 // TODO: This loop is O(n^2). See BenchmarkNilCheckDeep*. 358 for b != c { 359 if postnum[b.ID] < postnum[c.ID] { 360 b = idom[b.ID] 361 } else { 362 c = idom[c.ID] 363 } 364 } 365 return b 366 }