github.com/tidwall/go@v0.0.0-20170415222209-6694a6888b7d/src/cmd/compile/internal/ssa/sparsetree.go (about) 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 func newSparseTree(f *Func, parentOf []*Block) SparseTree { 60 t := make(SparseTree, f.NumBlocks()) 61 for _, b := range f.Blocks { 62 n := &t[b.ID] 63 if p := parentOf[b.ID]; p != nil { 64 n.parent = p 65 n.sibling = t[p.ID].child 66 t[p.ID].child = b 67 } 68 } 69 t.numberBlock(f.Entry, 1) 70 return t 71 } 72 73 // treestructure provides a string description of the dominator 74 // tree and flow structure of block b and all blocks that it 75 // dominates. 76 func (t SparseTree) treestructure(b *Block) string { 77 return t.treestructure1(b, 0) 78 } 79 func (t SparseTree) treestructure1(b *Block, i int) string { 80 s := "\n" + strings.Repeat("\t", i) + b.String() + "->[" 81 for i, e := range b.Succs { 82 if i > 0 { 83 s = s + "," 84 } 85 s = s + e.b.String() 86 } 87 s += "]" 88 if c0 := t[b.ID].child; c0 != nil { 89 s += "(" 90 for c := c0; c != nil; c = t[c.ID].sibling { 91 if c != c0 { 92 s += " " 93 } 94 s += t.treestructure1(c, i+1) 95 } 96 s += ")" 97 } 98 return s 99 } 100 101 // numberBlock assigns entry and exit numbers for b and b's 102 // children in an in-order walk from a gappy sequence, where n 103 // is the first number not yet assigned or reserved. N should 104 // be larger than zero. For each entry and exit number, the 105 // values one larger and smaller are reserved to indicate 106 // "strictly above" and "strictly below". numberBlock returns 107 // the smallest number not yet assigned or reserved (i.e., the 108 // exit number of the last block visited, plus two, because 109 // last.exit+1 is a reserved value.) 110 // 111 // examples: 112 // 113 // single node tree Root, call with n=1 114 // entry=2 Root exit=5; returns 7 115 // 116 // two node tree, Root->Child, call with n=1 117 // entry=2 Root exit=11; returns 13 118 // entry=5 Child exit=8 119 // 120 // three node tree, Root->(Left, Right), call with n=1 121 // entry=2 Root exit=17; returns 19 122 // entry=5 Left exit=8; entry=11 Right exit=14 123 // 124 // This is the in-order sequence of assigned and reserved numbers 125 // for the last example: 126 // root left left right right root 127 // 1 2e 3 | 4 5e 6 | 7 8x 9 | 10 11e 12 | 13 14x 15 | 16 17x 18 128 129 func (t SparseTree) numberBlock(b *Block, n int32) int32 { 130 // reserve n for entry-1, assign n+1 to entry 131 n++ 132 t[b.ID].entry = n 133 // reserve n+1 for entry+1, n+2 is next free number 134 n += 2 135 for c := t[b.ID].child; c != nil; c = t[c.ID].sibling { 136 n = t.numberBlock(c, n) // preserves n = next free number 137 } 138 // reserve n for exit-1, assign n+1 to exit 139 n++ 140 t[b.ID].exit = n 141 // reserve n+1 for exit+1, n+2 is next free number, returned. 142 return n + 2 143 } 144 145 // Sibling returns a sibling of x in the dominator tree (i.e., 146 // a node with the same immediate dominator) or nil if there 147 // are no remaining siblings in the arbitrary but repeatable 148 // order chosen. Because the Child-Sibling order is used 149 // to assign entry and exit numbers in the treewalk, those 150 // numbers are also consistent with this order (i.e., 151 // Sibling(x) has entry number larger than x's exit number). 152 func (t SparseTree) Sibling(x *Block) *Block { 153 return t[x.ID].sibling 154 } 155 156 // Child returns a child of x in the dominator tree, or 157 // nil if there are none. The choice of first child is 158 // arbitrary but repeatable. 159 func (t SparseTree) Child(x *Block) *Block { 160 return t[x.ID].child 161 } 162 163 // isAncestorEq reports whether x is an ancestor of or equal to y. 164 func (t SparseTree) isAncestorEq(x, y *Block) bool { 165 if x == y { 166 return true 167 } 168 xx := &t[x.ID] 169 yy := &t[y.ID] 170 return xx.entry <= yy.entry && yy.exit <= xx.exit 171 } 172 173 // isAncestor reports whether x is a strict ancestor of y. 174 func (t SparseTree) isAncestor(x, y *Block) bool { 175 if x == y { 176 return false 177 } 178 xx := &t[x.ID] 179 yy := &t[y.ID] 180 return xx.entry < yy.entry && yy.exit < xx.exit 181 } 182 183 // domorder returns a value for dominator-oriented sorting. 184 // Block domination does not provide a total ordering, 185 // but domorder two has useful properties. 186 // (1) If domorder(x) > domorder(y) then x does not dominate y. 187 // (2) If domorder(x) < domorder(y) and domorder(y) < domorder(z) and x does not dominate y, 188 // then x does not dominate z. 189 // Property (1) means that blocks sorted by domorder always have a maximal dominant block first. 190 // Property (2) allows searches for dominated blocks to exit early. 191 func (t SparseTree) domorder(x *Block) int32 { 192 // Here is an argument that entry(x) provides the properties documented above. 193 // 194 // Entry and exit values are assigned in a depth-first dominator tree walk. 195 // For all blocks x and y, one of the following holds: 196 // 197 // (x-dom-y) x dominates y => entry(x) < entry(y) < exit(y) < exit(x) 198 // (y-dom-x) y dominates x => entry(y) < entry(x) < exit(x) < exit(y) 199 // (x-then-y) neither x nor y dominates the other and x walked before y => entry(x) < exit(x) < entry(y) < exit(y) 200 // (y-then-x) neither x nor y dominates the other and y walked before y => entry(y) < exit(y) < entry(x) < exit(x) 201 // 202 // entry(x) > entry(y) eliminates case x-dom-y. This provides property (1) above. 203 // 204 // For property (2), assume entry(x) < entry(y) and entry(y) < entry(z) and x does not dominate y. 205 // entry(x) < entry(y) allows cases x-dom-y and x-then-y. 206 // But by supposition, x does not dominate y. So we have x-then-y. 207 // 208 // For contractidion, assume x dominates z. 209 // Then entry(x) < entry(z) < exit(z) < exit(x). 210 // But we know x-then-y, so entry(x) < exit(x) < entry(y) < exit(y). 211 // Combining those, entry(x) < entry(z) < exit(z) < exit(x) < entry(y) < exit(y). 212 // By supposition, entry(y) < entry(z), which allows cases y-dom-z and y-then-z. 213 // y-dom-z requires entry(y) < entry(z), but we have entry(z) < entry(y). 214 // y-then-z requires exit(y) < entry(z), but we have entry(z) < exit(y). 215 // We have a contradiction, so x does not dominate z, as required. 216 return t[x.ID].entry 217 }