github.com/stingnevermore/go@v0.0.0-20180120041312-3810f5bfed72/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 type markKind uint8 9 10 const ( 11 notFound markKind = 0 // block has not been discovered yet 12 notExplored markKind = 1 // discovered and in queue, outedges not processed yet 13 explored markKind = 2 // discovered and in queue, outedges processed 14 done markKind = 3 // all done, in output ordering 15 ) 16 17 // This file contains code to compute the dominator tree 18 // of a control-flow graph. 19 20 // postorder computes a postorder traversal ordering for the 21 // basic blocks in f. Unreachable blocks will not appear. 22 func postorder(f *Func) []*Block { 23 return postorderWithNumbering(f, []int32{}) 24 } 25 26 type blockAndIndex struct { 27 b *Block 28 index int // index is the number of successor edges of b that have already been explored. 29 } 30 31 // postorderWithNumbering provides a DFS postordering. 32 // This seems to make loop-finding more robust. 33 func postorderWithNumbering(f *Func, ponums []int32) []*Block { 34 mark := make([]markKind, f.NumBlocks()) 35 36 // result ordering 37 var order []*Block 38 39 // stack of blocks and next child to visit 40 var s []blockAndIndex 41 s = append(s, blockAndIndex{b: f.Entry}) 42 mark[f.Entry.ID] = explored 43 for len(s) > 0 { 44 tos := len(s) - 1 45 x := s[tos] 46 b := x.b 47 i := x.index 48 if i < len(b.Succs) { 49 s[tos].index++ 50 bb := b.Succs[i].Block() 51 if mark[bb.ID] == notFound { 52 mark[bb.ID] = explored 53 s = append(s, blockAndIndex{b: bb}) 54 } 55 } else { 56 s = s[:tos] 57 if len(ponums) > 0 { 58 ponums[b.ID] = int32(len(order)) 59 } 60 order = append(order, b) 61 } 62 } 63 return order 64 } 65 66 type linkedBlocks func(*Block) []Edge 67 68 const nscratchslices = 7 69 70 // experimentally, functions with 512 or fewer blocks account 71 // for 75% of memory (size) allocation for dominator computation 72 // in make.bash. 73 const minscratchblocks = 512 74 75 func (cache *Cache) scratchBlocksForDom(maxBlockID int) (a, b, c, d, e, f, g []ID) { 76 tot := maxBlockID * nscratchslices 77 scratch := cache.domblockstore 78 if len(scratch) < tot { 79 // req = min(1.5*tot, nscratchslices*minscratchblocks) 80 // 50% padding allows for graph growth in later phases. 81 req := (tot * 3) >> 1 82 if req < nscratchslices*minscratchblocks { 83 req = nscratchslices * minscratchblocks 84 } 85 scratch = make([]ID, req) 86 cache.domblockstore = scratch 87 } else { 88 // Clear as much of scratch as we will (re)use 89 scratch = scratch[0:tot] 90 for i := range scratch { 91 scratch[i] = 0 92 } 93 } 94 95 a = scratch[0*maxBlockID : 1*maxBlockID] 96 b = scratch[1*maxBlockID : 2*maxBlockID] 97 c = scratch[2*maxBlockID : 3*maxBlockID] 98 d = scratch[3*maxBlockID : 4*maxBlockID] 99 e = scratch[4*maxBlockID : 5*maxBlockID] 100 f = scratch[5*maxBlockID : 6*maxBlockID] 101 g = scratch[6*maxBlockID : 7*maxBlockID] 102 103 return 104 } 105 106 func dominators(f *Func) []*Block { 107 preds := func(b *Block) []Edge { return b.Preds } 108 succs := func(b *Block) []Edge { return b.Succs } 109 110 //TODO: benchmark and try to find criteria for swapping between 111 // dominatorsSimple and dominatorsLT 112 return f.dominatorsLTOrig(f.Entry, preds, succs) 113 } 114 115 // dominatorsLTOrig runs Lengauer-Tarjan to compute a dominator tree starting at 116 // entry and using predFn/succFn to find predecessors/successors to allow 117 // computing both dominator and post-dominator trees. 118 func (f *Func) dominatorsLTOrig(entry *Block, predFn linkedBlocks, succFn linkedBlocks) []*Block { 119 // Adapted directly from the original TOPLAS article's "simple" algorithm 120 121 maxBlockID := entry.Func.NumBlocks() 122 semi, vertex, label, parent, ancestor, bucketHead, bucketLink := f.Cache.scratchBlocksForDom(maxBlockID) 123 124 // This version uses integers for most of the computation, 125 // to make the work arrays smaller and pointer-free. 126 // fromID translates from ID to *Block where that is needed. 127 fromID := make([]*Block, maxBlockID) 128 for _, v := range f.Blocks { 129 fromID[v.ID] = v 130 } 131 idom := make([]*Block, maxBlockID) 132 133 // Step 1. Carry out a depth first search of the problem graph. Number 134 // the vertices from 1 to n as they are reached during the search. 135 n := f.dfsOrig(entry, succFn, semi, vertex, label, parent) 136 137 for i := n; i >= 2; i-- { 138 w := vertex[i] 139 140 // step2 in TOPLAS paper 141 for _, e := range predFn(fromID[w]) { 142 v := e.b 143 if semi[v.ID] == 0 { 144 // skip unreachable predecessor 145 // not in original, but we're using existing pred instead of building one. 146 continue 147 } 148 u := evalOrig(v.ID, ancestor, semi, label) 149 if semi[u] < semi[w] { 150 semi[w] = semi[u] 151 } 152 } 153 154 // add w to bucket[vertex[semi[w]]] 155 // implement bucket as a linked list implemented 156 // in a pair of arrays. 157 vsw := vertex[semi[w]] 158 bucketLink[w] = bucketHead[vsw] 159 bucketHead[vsw] = w 160 161 linkOrig(parent[w], w, ancestor) 162 163 // step3 in TOPLAS paper 164 for v := bucketHead[parent[w]]; v != 0; v = bucketLink[v] { 165 u := evalOrig(v, ancestor, semi, label) 166 if semi[u] < semi[v] { 167 idom[v] = fromID[u] 168 } else { 169 idom[v] = fromID[parent[w]] 170 } 171 } 172 } 173 // step 4 in toplas paper 174 for i := ID(2); i <= n; i++ { 175 w := vertex[i] 176 if idom[w].ID != vertex[semi[w]] { 177 idom[w] = idom[idom[w].ID] 178 } 179 } 180 181 return idom 182 } 183 184 // dfs performs a depth first search over the blocks starting at entry block 185 // (in arbitrary order). This is a de-recursed version of dfs from the 186 // original Tarjan-Lengauer TOPLAS article. It's important to return the 187 // same values for parent as the original algorithm. 188 func (f *Func) dfsOrig(entry *Block, succFn linkedBlocks, semi, vertex, label, parent []ID) ID { 189 n := ID(0) 190 s := make([]*Block, 0, 256) 191 s = append(s, entry) 192 193 for len(s) > 0 { 194 v := s[len(s)-1] 195 s = s[:len(s)-1] 196 // recursing on v 197 198 if semi[v.ID] != 0 { 199 continue // already visited 200 } 201 n++ 202 semi[v.ID] = n 203 vertex[n] = v.ID 204 label[v.ID] = v.ID 205 // ancestor[v] already zero 206 for _, e := range succFn(v) { 207 w := e.b 208 // if it has a dfnum, we've already visited it 209 if semi[w.ID] == 0 { 210 // yes, w can be pushed multiple times. 211 s = append(s, w) 212 parent[w.ID] = v.ID // keep overwriting this till it is visited. 213 } 214 } 215 } 216 return n 217 } 218 219 // compressOrig is the "simple" compress function from LT paper 220 func compressOrig(v ID, ancestor, semi, label []ID) { 221 if ancestor[ancestor[v]] != 0 { 222 compressOrig(ancestor[v], ancestor, semi, label) 223 if semi[label[ancestor[v]]] < semi[label[v]] { 224 label[v] = label[ancestor[v]] 225 } 226 ancestor[v] = ancestor[ancestor[v]] 227 } 228 } 229 230 // evalOrig is the "simple" eval function from LT paper 231 func evalOrig(v ID, ancestor, semi, label []ID) ID { 232 if ancestor[v] == 0 { 233 return v 234 } 235 compressOrig(v, ancestor, semi, label) 236 return label[v] 237 } 238 239 func linkOrig(v, w ID, ancestor []ID) { 240 ancestor[w] = v 241 } 242 243 // dominators computes the dominator tree for f. It returns a slice 244 // which maps block ID to the immediate dominator of that block. 245 // Unreachable blocks map to nil. The entry block maps to nil. 246 func dominatorsSimple(f *Func) []*Block { 247 // A simple algorithm for now 248 // Cooper, Harvey, Kennedy 249 idom := make([]*Block, f.NumBlocks()) 250 251 // Compute postorder walk 252 post := f.postorder() 253 254 // Make map from block id to order index (for intersect call) 255 postnum := make([]int, f.NumBlocks()) 256 for i, b := range post { 257 postnum[b.ID] = i 258 } 259 260 // Make the entry block a self-loop 261 idom[f.Entry.ID] = f.Entry 262 if postnum[f.Entry.ID] != len(post)-1 { 263 f.Fatalf("entry block %v not last in postorder", f.Entry) 264 } 265 266 // Compute relaxation of idom entries 267 for { 268 changed := false 269 270 for i := len(post) - 2; i >= 0; i-- { 271 b := post[i] 272 var d *Block 273 for _, e := range b.Preds { 274 p := e.b 275 if idom[p.ID] == nil { 276 continue 277 } 278 if d == nil { 279 d = p 280 continue 281 } 282 d = intersect(d, p, postnum, idom) 283 } 284 if d != idom[b.ID] { 285 idom[b.ID] = d 286 changed = true 287 } 288 } 289 if !changed { 290 break 291 } 292 } 293 // Set idom of entry block to nil instead of itself. 294 idom[f.Entry.ID] = nil 295 return idom 296 } 297 298 // intersect finds the closest dominator of both b and c. 299 // It requires a postorder numbering of all the blocks. 300 func intersect(b, c *Block, postnum []int, idom []*Block) *Block { 301 // TODO: This loop is O(n^2). It used to be used in nilcheck, 302 // see BenchmarkNilCheckDeep*. 303 for b != c { 304 if postnum[b.ID] < postnum[c.ID] { 305 b = idom[b.ID] 306 } else { 307 c = idom[c.ID] 308 } 309 } 310 return b 311 }