GoPLS Viewer

Home|gopls/go/ssa/dom.go
1// Copyright 2013 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 defines algorithms related to dominance.
8
9// Dominator tree construction ----------------------------------------
10//
11// We use the algorithm described in Lengauer & Tarjan. 1979.  A fast
12// algorithm for finding dominators in a flowgraph.
13// http://doi.acm.org/10.1145/357062.357071
14//
15// We also apply the optimizations to SLT described in Georgiadis et
16// al, Finding Dominators in Practice, JGAA 2006,
17// http://jgaa.info/accepted/2006/GeorgiadisTarjanWerneck2006.10.1.pdf
18// to avoid the need for buckets of size > 1.
19
20import (
21    "bytes"
22    "fmt"
23    "math/big"
24    "os"
25    "sort"
26)
27
28// Idom returns the block that immediately dominates b:
29// its parent in the dominator tree, if any.
30// Neither the entry node (b.Index==0) nor recover node
31// (b==b.Parent().Recover()) have a parent.
32func (b *BasicBlockIdom() *BasicBlock { return b.dom.idom }
33
34// Dominees returns the list of blocks that b immediately dominates:
35// its children in the dominator tree.
36func (b *BasicBlockDominees() []*BasicBlock { return b.dom.children }
37
38// Dominates reports whether b dominates c.
39func (b *BasicBlockDominates(c *BasicBlockbool {
40    return b.dom.pre <= c.dom.pre && c.dom.post <= b.dom.post
41}
42
43type byDomPreorder []*BasicBlock
44
45func (a byDomPreorderLen() int           { return len(a) }
46func (a byDomPreorderSwap(ij int)      { a[i], a[j] = a[j], a[i] }
47func (a byDomPreorderLess(ij intbool { return a[i].dom.pre < a[j].dom.pre }
48
49// DomPreorder returns a new slice containing the blocks of f in
50// dominator tree preorder.
51func (f *FunctionDomPreorder() []*BasicBlock {
52    n := len(f.Blocks)
53    order := make(byDomPreordern)
54    copy(orderf.Blocks)
55    sort.Sort(order)
56    return order
57}
58
59// domInfo contains a BasicBlock's dominance information.
60type domInfo struct {
61    idom      *BasicBlock   // immediate dominator (parent in domtree)
62    children  []*BasicBlock // nodes immediately dominated by this one
63    prepost int32         // pre- and post-order numbering within domtree
64}
65
66// ltState holds the working state for Lengauer-Tarjan algorithm
67// (during which domInfo.pre is repurposed for CFG DFS preorder number).
68type ltState struct {
69    // Each slice is indexed by b.Index.
70    sdom     []*BasicBlock // b's semidominator
71    parent   []*BasicBlock // b's parent in DFS traversal of CFG
72    ancestor []*BasicBlock // b's ancestor with least sdom
73}
74
75// dfs implements the depth-first search part of the LT algorithm.
76func (lt *ltStatedfs(v *BasicBlocki int32preorder []*BasicBlockint32 {
77    preorder[i] = v
78    v.dom.pre = i // For now: DFS preorder of spanning tree of CFG
79    i++
80    lt.sdom[v.Index] = v
81    lt.link(nilv)
82    for _w := range v.Succs {
83        if lt.sdom[w.Index] == nil {
84            lt.parent[w.Index] = v
85            i = lt.dfs(wipreorder)
86        }
87    }
88    return i
89}
90
91// eval implements the EVAL part of the LT algorithm.
92func (lt *ltStateeval(v *BasicBlock) *BasicBlock {
93    // TODO(adonovan): opt: do path compression per simple LT.
94    u := v
95    for ; lt.ancestor[v.Index] != nilv = lt.ancestor[v.Index] {
96        if lt.sdom[v.Index].dom.pre < lt.sdom[u.Index].dom.pre {
97            u = v
98        }
99    }
100    return u
101}
102
103// link implements the LINK part of the LT algorithm.
104func (lt *ltStatelink(vw *BasicBlock) {
105    lt.ancestor[w.Index] = v
106}
107
108// buildDomTree computes the dominator tree of f using the LT algorithm.
109// Precondition: all blocks are reachable (e.g. optimizeBlocks has been run).
110func buildDomTree(f *Function) {
111    // The step numbers refer to the original LT paper; the
112    // reordering is due to Georgiadis.
113
114    // Clear any previous domInfo.
115    for _b := range f.Blocks {
116        b.dom = domInfo{}
117    }
118
119    n := len(f.Blocks)
120    // Allocate space for 5 contiguous [n]*BasicBlock arrays:
121    // sdom, parent, ancestor, preorder, buckets.
122    space := make([]*BasicBlock5*n)
123    lt := ltState{
124        sdom:     space[0:n],
125        parent:   space[n : 2*n],
126        ancestorspace[2*n : 3*n],
127    }
128
129    // Step 1.  Number vertices by depth-first preorder.
130    preorder := space[3*n : 4*n]
131    root := f.Blocks[0]
132    prenum := lt.dfs(root0preorder)
133    recover := f.Recover
134    if recover != nil {
135        lt.dfs(recoverprenumpreorder)
136    }
137
138    buckets := space[4*n : 5*n]
139    copy(bucketspreorder)
140
141    // In reverse preorder...
142    for i := int32(n) - 1i > 0i-- {
143        w := preorder[i]
144
145        // Step 3. Implicitly define the immediate dominator of each node.
146        for v := buckets[i]; v != wv = buckets[v.dom.pre] {
147            u := lt.eval(v)
148            if lt.sdom[u.Index].dom.pre < i {
149                v.dom.idom = u
150            } else {
151                v.dom.idom = w
152            }
153        }
154
155        // Step 2. Compute the semidominators of all nodes.
156        lt.sdom[w.Index] = lt.parent[w.Index]
157        for _v := range w.Preds {
158            u := lt.eval(v)
159            if lt.sdom[u.Index].dom.pre < lt.sdom[w.Index].dom.pre {
160                lt.sdom[w.Index] = lt.sdom[u.Index]
161            }
162        }
163
164        lt.link(lt.parent[w.Index], w)
165
166        if lt.parent[w.Index] == lt.sdom[w.Index] {
167            w.dom.idom = lt.parent[w.Index]
168        } else {
169            buckets[i] = buckets[lt.sdom[w.Index].dom.pre]
170            buckets[lt.sdom[w.Index].dom.pre] = w
171        }
172    }
173
174    // The final 'Step 3' is now outside the loop.
175    for v := buckets[0]; v != rootv = buckets[v.dom.pre] {
176        v.dom.idom = root
177    }
178
179    // Step 4. Explicitly define the immediate dominator of each
180    // node, in preorder.
181    for _w := range preorder[1:] {
182        if w == root || w == recover {
183            w.dom.idom = nil
184        } else {
185            if w.dom.idom != lt.sdom[w.Index] {
186                w.dom.idom = w.dom.idom.dom.idom
187            }
188            // Calculate Children relation as inverse of Idom.
189            w.dom.idom.dom.children = append(w.dom.idom.dom.childrenw)
190        }
191    }
192
193    prepost := numberDomTree(root00)
194    if recover != nil {
195        numberDomTree(recoverprepost)
196    }
197
198    // printDomTreeDot(os.Stderr, f)        // debugging
199    // printDomTreeText(os.Stderr, root, 0) // debugging
200
201    if f.Prog.mode&SanityCheckFunctions != 0 {
202        sanityCheckDomTree(f)
203    }
204}
205
206// numberDomTree sets the pre- and post-order numbers of a depth-first
207// traversal of the dominator tree rooted at v.  These are used to
208// answer dominance queries in constant time.
209func numberDomTree(v *BasicBlockprepost int32) (int32int32) {
210    v.dom.pre = pre
211    pre++
212    for _child := range v.dom.children {
213        prepost = numberDomTree(childprepost)
214    }
215    v.dom.post = post
216    post++
217    return prepost
218}
219
220// Testing utilities ----------------------------------------
221
222// sanityCheckDomTree checks the correctness of the dominator tree
223// computed by the LT algorithm by comparing against the dominance
224// relation computed by a naive Kildall-style forward dataflow
225// analysis (Algorithm 10.16 from the "Dragon" book).
226func sanityCheckDomTree(f *Function) {
227    n := len(f.Blocks)
228
229    // D[i] is the set of blocks that dominate f.Blocks[i],
230    // represented as a bit-set of block indices.
231    D := make([]big.Intn)
232
233    one := big.NewInt(1)
234
235    // all is the set of all blocks; constant.
236    var all big.Int
237    all.Set(one).Lsh(&alluint(n)).Sub(&allone)
238
239    // Initialization.
240    for ib := range f.Blocks {
241        if i == 0 || b == f.Recover {
242            // A root is dominated only by itself.
243            D[i].SetBit(&D[0], 01)
244        } else {
245            // All other blocks are (initially) dominated
246            // by every block.
247            D[i].Set(&all)
248        }
249    }
250
251    // Iteration until fixed point.
252    for changed := truechanged; {
253        changed = false
254        for ib := range f.Blocks {
255            if i == 0 || b == f.Recover {
256                continue
257            }
258            // Compute intersection across predecessors.
259            var x big.Int
260            x.Set(&all)
261            for _pred := range b.Preds {
262                x.And(&x, &D[pred.Index])
263            }
264            x.SetBit(&xi1// a block always dominates itself.
265            if D[i].Cmp(&x) != 0 {
266                D[i].Set(&x)
267                changed = true
268            }
269        }
270    }
271
272    // Check the entire relation.  O(n^2).
273    // The Recover block (if any) must be treated specially so we skip it.
274    ok := true
275    for i := 0i < ni++ {
276        for j := 0j < nj++ {
277            bc := f.Blocks[i], f.Blocks[j]
278            if c == f.Recover {
279                continue
280            }
281            actual := b.Dominates(c)
282            expected := D[j].Bit(i) == 1
283            if actual != expected {
284                fmt.Fprintf(os.Stderr"dominates(%s, %s)==%t, want %t\n"bcactualexpected)
285                ok = false
286            }
287        }
288    }
289
290    preorder := f.DomPreorder()
291    for _b := range f.Blocks {
292        if got := preorder[b.dom.pre]; got != b {
293            fmt.Fprintf(os.Stderr"preorder[%d]==%s, want %s\n"b.dom.pregotb)
294            ok = false
295        }
296    }
297
298    if !ok {
299        panic("sanityCheckDomTree failed for " + f.String())
300    }
301
302}
303
304// Printing functions ----------------------------------------
305
306// printDomTreeText prints the dominator tree as text, using indentation.
307func printDomTreeText(buf *bytes.Bufferv *BasicBlockindent int) {
308    fmt.Fprintf(buf"%*s%s\n"4*indent""v)
309    for _child := range v.dom.children {
310        printDomTreeText(bufchildindent+1)
311    }
312}
313
314// printDomTreeDot prints the dominator tree of f in AT&T GraphViz
315// (.dot) format.
316func printDomTreeDot(buf *bytes.Bufferf *Function) {
317    fmt.Fprintln(buf"//"f)
318    fmt.Fprintln(buf"digraph domtree {")
319    for ib := range f.Blocks {
320        v := b.dom
321        fmt.Fprintf(buf"\tn%d [label=\"%s (%d, %d)\",shape=\"rectangle\"];\n"v.prebv.prev.post)
322        // TODO(adonovan): improve appearance of edges
323        // belonging to both dominator tree and CFG.
324
325        // Dominator tree edge.
326        if i != 0 {
327            fmt.Fprintf(buf"\tn%d -> n%d [style=\"solid\",weight=100];\n"v.idom.dom.prev.pre)
328        }
329        // CFG edges.
330        for _pred := range b.Preds {
331            fmt.Fprintf(buf"\tn%d -> n%d [style=\"dotted\",weight=0];\n"pred.dom.prev.pre)
332        }
333    }
334    fmt.Fprintln(buf"}")
335}
336
MembersX
buildDomTree.lt
numberDomTree.post
sanityCheckDomTree.changed
printDomTreeDot
printDomTreeDot.buf
BasicBlock.Idom.b
BasicBlock.Dominates
ltState.eval.v
buildDomTree.prenum
sanityCheckDomTree.BlockStmt.j
printDomTreeText.indent
BasicBlock.Idom
byDomPreorder.Swap.a
buildDomTree.RangeStmt_3698.b
printDomTreeDot.RangeStmt_8938.b
domInfo.pre
ltState.dfs
ltState.eval.u
ltState.link
sanityCheckDomTree.BlockStmt.RangeStmt_7297.BlockStmt.RangeStmt_7460.pred
sanityCheckDomTree.BlockStmt.BlockStmt.actual
BasicBlock.Dominees
Function.DomPreorder
ltState.eval
domInfo
ltState.dfs.lt
ltState.eval.lt
ltState.link.w
buildDomTree.f
byDomPreorder
byDomPreorder.Less.i
byDomPreorder.Less.j
numberDomTree
sanityCheckDomTree.f
printDomTreeText.RangeStmt_8643.child
sanityCheckDomTree.i
sanityCheckDomTree.preorder
byDomPreorder.Swap.j
Function.DomPreorder.f
sanityCheckDomTree.D
byDomPreorder.Swap.i
buildDomTree.space
sanityCheckDomTree.all
printDomTreeDot.f
BasicBlock.Dominates.b
buildDomTree.recover
numberDomTree.RangeStmt_6178.child
buildDomTree.n
printDomTreeDot.RangeStmt_8938.BlockStmt.RangeStmt_9330.pred
Function.DomPreorder.n
ltState.link.v
buildDomTree
buildDomTree.pre
numberDomTree.v
sanityCheckDomTree.one
sanityCheckDomTree.BlockStmt.RangeStmt_7297.i
printDomTreeText
byDomPreorder.Len
Function.DomPreorder.order
buildDomTree.BlockStmt.RangeStmt_4715.v
printDomTreeDot.RangeStmt_8938.i
ltState.parent
ltState.dfs.i
ltState.dfs.RangeStmt_2798.w
buildDomTree.RangeStmt_5313.w
BasicBlock.Dominees.b
domInfo.post
ltState.sdom
buildDomTree.BlockStmt.BlockStmt.u
sanityCheckDomTree.RangeStmt_6972.i
sanityCheckDomTree.ok
printDomTreeText.buf
byDomPreorder.Less.a
domInfo.idom
domInfo.children
byDomPreorder.Swap
sanityCheckDomTree.BlockStmt.RangeStmt_7297.b
sanityCheckDomTree
sanityCheckDomTree.RangeStmt_8145.b
printDomTreeDot.RangeStmt_8938.BlockStmt.v
byDomPreorder.Len.a
ltState
ltState.dfs.v
buildDomTree.post
sanityCheckDomTree.n
sanityCheckDomTree.BlockStmt.RangeStmt_7297.BlockStmt.x
BasicBlock.Dominates.c
byDomPreorder.Less
buildDomTree.BlockStmt.RangeStmt_4715.BlockStmt.u
numberDomTree.pre
sanityCheckDomTree.RangeStmt_6972.b
printDomTreeText.v
ltState.ancestor
ltState.dfs.preorder
ltState.link.lt
Members
X