Home | History | Annotate | Download | only in ssa
      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 := f.postorder()
    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