Source file src/cmd/compile/internal/ssa/sparsetree.go
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 import ( 8 "fmt" 9 "strings" 10 ) 11 12 type SparseTreeNode struct { 13 child *Block 14 sibling *Block 15 parent *Block 16 17 // Every block has 6 numbers associated with it: 18 // entry-1, entry, entry+1, exit-1, and exit, exit+1. 19 // entry and exit are conceptually the top of the block (phi functions) 20 // entry+1 and exit-1 are conceptually the bottom of the block (ordinary defs) 21 // entry-1 and exit+1 are conceptually "just before" the block (conditions flowing in) 22 // 23 // This simplifies life if we wish to query information about x 24 // when x is both an input to and output of a block. 25 entry, exit int32 26 } 27 28 func (s *SparseTreeNode) String() string { 29 return fmt.Sprintf("[%d,%d]", s.entry, s.exit) 30 } 31 32 func (s *SparseTreeNode) Entry() int32 { 33 return s.entry 34 } 35 36 func (s *SparseTreeNode) Exit() int32 { 37 return s.exit 38 } 39 40 const ( 41 // When used to lookup up definitions in a sparse tree, 42 // these adjustments to a block's entry (+adjust) and 43 // exit (-adjust) numbers allow a distinction to be made 44 // between assignments (typically branch-dependent 45 // conditionals) occurring "before" the block (e.g., as inputs 46 // to the block and its phi functions), "within" the block, 47 // and "after" the block. 48 AdjustBefore = -1 // defined before phi 49 AdjustWithin = 0 // defined by phi 50 AdjustAfter = 1 // defined within block 51 ) 52 53 // A SparseTree is a tree of Blocks. 54 // It allows rapid ancestor queries, 55 // such as whether one block dominates another. 56 type SparseTree []SparseTreeNode 57 58 // newSparseTree creates a SparseTree from a block-to-parent map (array indexed by Block.ID). 59 // The children of a given node are in reverse postorder. 60 // This has the nice property that for a given tree walk, the source block of all 61 // non-retreating edges are visited before their destination block. 62 func newSparseTree(f *Func, parentOf []*Block) SparseTree { 63 po := f.postorder() 64 t := make(SparseTree, f.NumBlocks()) 65 for _, b := range po { 66 n := &t[b.ID] 67 if p := parentOf[b.ID]; p != nil { 68 n.parent = p 69 n.sibling = t[p.ID].child 70 t[p.ID].child = b 71 } 72 } 73 t.numberBlock(f.Entry, 1) 74 return t 75 } 76 77 // treestructure provides a string description of the dominator 78 // tree and flow structure of block b and all blocks that it 79 // dominates. 80 func (t SparseTree) treestructure(b *Block) string { 81 return t.treestructure1(b, 0) 82 } 83 func (t SparseTree) treestructure1(b *Block, i int) string { 84 s := "\n" + strings.Repeat("\t", i) + b.String() + "->[" 85 for i, e := range b.Succs { 86 if i > 0 { 87 s += "," 88 } 89 s += e.b.String() 90 } 91 s += "]" 92 if c0 := t[b.ID].child; c0 != nil { 93 s += "(" 94 for c := c0; c != nil; c = t[c.ID].sibling { 95 if c != c0 { 96 s += " " 97 } 98 s += t.treestructure1(c, i+1) 99 } 100 s += ")" 101 } 102 return s 103 } 104 105 // numberBlock assigns entry and exit numbers for b and b's 106 // children in an in-order walk from a gappy sequence, where n 107 // is the first number not yet assigned or reserved. N should 108 // be larger than zero. For each entry and exit number, the 109 // values one larger and smaller are reserved to indicate 110 // "strictly above" and "strictly below". numberBlock returns 111 // the smallest number not yet assigned or reserved (i.e., the 112 // exit number of the last block visited, plus two, because 113 // last.exit+1 is a reserved value.) 114 // 115 // examples: 116 // 117 // single node tree Root, call with n=1 118 // entry=2 Root exit=5; returns 7 119 // 120 // two node tree, Root->Child, call with n=1 121 // entry=2 Root exit=11; returns 13 122 // entry=5 Child exit=8 123 // 124 // three node tree, Root->(Left, Right), call with n=1 125 // entry=2 Root exit=17; returns 19 126 // entry=5 Left exit=8; entry=11 Right exit=14 127 // 128 // This is the in-order sequence of assigned and reserved numbers 129 // for the last example: 130 // root left left right right root 131 // 1 2e 3 | 4 5e 6 | 7 8x 9 | 10 11e 12 | 13 14x 15 | 16 17x 18 132 133 func (t SparseTree) numberBlock(b *Block, n int32) int32 { 134 // reserve n for entry-1, assign n+1 to entry 135 n++ 136 t[b.ID].entry = n 137 // reserve n+1 for entry+1, n+2 is next free number 138 n += 2 139 for c := t[b.ID].child; c != nil; c = t[c.ID].sibling { 140 n = t.numberBlock(c, n) // preserves n = next free number 141 } 142 // reserve n for exit-1, assign n+1 to exit 143 n++ 144 t[b.ID].exit = n 145 // reserve n+1 for exit+1, n+2 is next free number, returned. 146 return n + 2 147 } 148 149 // Sibling returns a sibling of x in the dominator tree (i.e., 150 // a node with the same immediate dominator) or nil if there 151 // are no remaining siblings in the arbitrary but repeatable 152 // order chosen. Because the Child-Sibling order is used 153 // to assign entry and exit numbers in the treewalk, those 154 // numbers are also consistent with this order (i.e., 155 // Sibling(x) has entry number larger than x's exit number). 156 func (t SparseTree) Sibling(x *Block) *Block { 157 return t[x.ID].sibling 158 } 159 160 // Child returns a child of x in the dominator tree, or 161 // nil if there are none. The choice of first child is 162 // arbitrary but repeatable. 163 func (t SparseTree) Child(x *Block) *Block { 164 return t[x.ID].child 165 } 166 167 // Parent returns the parent of x in the dominator tree, or 168 // nil if x is the function's entry. 169 func (t SparseTree) Parent(x *Block) *Block { 170 return t[x.ID].parent 171 } 172 173 // IsAncestorEq reports whether x is an ancestor of or equal to y. 174 func (t SparseTree) IsAncestorEq(x, y *Block) bool { 175 if x == y { 176 return true 177 } 178 xx := &t[x.ID] 179 yy := &t[y.ID] 180 return xx.entry <= yy.entry && yy.exit <= xx.exit 181 } 182 183 // isAncestor reports whether x is a strict ancestor of y. 184 func (t SparseTree) isAncestor(x, y *Block) bool { 185 if x == y { 186 return false 187 } 188 xx := &t[x.ID] 189 yy := &t[y.ID] 190 return xx.entry < yy.entry && yy.exit < xx.exit 191 } 192 193 // domorder returns a value for dominator-oriented sorting. 194 // Block domination does not provide a total ordering, 195 // but domorder two has useful properties. 196 // 1. If domorder(x) > domorder(y) then x does not dominate y. 197 // 2. If domorder(x) < domorder(y) and domorder(y) < domorder(z) and x does not dominate y, 198 // then x does not dominate z. 199 // 200 // Property (1) means that blocks sorted by domorder always have a maximal dominant block first. 201 // Property (2) allows searches for dominated blocks to exit early. 202 func (t SparseTree) domorder(x *Block) int32 { 203 // Here is an argument that entry(x) provides the properties documented above. 204 // 205 // Entry and exit values are assigned in a depth-first dominator tree walk. 206 // For all blocks x and y, one of the following holds: 207 // 208 // (x-dom-y) x dominates y => entry(x) < entry(y) < exit(y) < exit(x) 209 // (y-dom-x) y dominates x => entry(y) < entry(x) < exit(x) < exit(y) 210 // (x-then-y) neither x nor y dominates the other and x walked before y => entry(x) < exit(x) < entry(y) < exit(y) 211 // (y-then-x) neither x nor y dominates the other and y walked before y => entry(y) < exit(y) < entry(x) < exit(x) 212 // 213 // entry(x) > entry(y) eliminates case x-dom-y. This provides property (1) above. 214 // 215 // For property (2), assume entry(x) < entry(y) and entry(y) < entry(z) and x does not dominate y. 216 // entry(x) < entry(y) allows cases x-dom-y and x-then-y. 217 // But by supposition, x does not dominate y. So we have x-then-y. 218 // 219 // For contradiction, assume x dominates z. 220 // Then entry(x) < entry(z) < exit(z) < exit(x). 221 // But we know x-then-y, so entry(x) < exit(x) < entry(y) < exit(y). 222 // Combining those, entry(x) < entry(z) < exit(z) < exit(x) < entry(y) < exit(y). 223 // By supposition, entry(y) < entry(z), which allows cases y-dom-z and y-then-z. 224 // y-dom-z requires entry(y) < entry(z), but we have entry(z) < entry(y). 225 // y-then-z requires exit(y) < entry(z), but we have entry(z) < exit(y). 226 // We have a contradiction, so x does not dominate z, as required. 227 return t[x.ID].entry 228 } 229