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