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