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