src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

dom.go (9672B)


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