github.com/mh-cbon/go@v0.0.0-20160603070303-9e112a3fe4c0/src/cmd/compile/internal/ssa/sparsetreemap.go (about) 1 // Copyright 2016 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 // A SparseTreeMap encodes a subset of nodes within a tree 10 // used for sparse-ancestor queries. 11 // 12 // Combined with a SparseTreeHelper, this supports an Insert 13 // to add a tree node to the set and a Find operation to locate 14 // the nearest tree ancestor of a given node such that the 15 // ancestor is also in the set. 16 // 17 // Given a set of blocks {B1, B2, B3} within the dominator tree, established by 18 // stm.Insert()ing B1, B2, B3, etc, a query at block B 19 // (performed with stm.Find(stm, B, adjust, helper)) 20 // will return the member of the set that is the nearest strict 21 // ancestor of B within the dominator tree, or nil if none exists. 22 // The expected complexity of this operation is the log of the size 23 // the set, given certain assumptions about sparsity (the log complexity 24 // could be guaranteed with additional data structures whose constant- 25 // factor overhead has not yet been justified.) 26 // 27 // The adjust parameter allows positioning of the insertion 28 // and lookup points within a block -- one of 29 // AdjustBefore, AdjustWithin, AdjustAfter, 30 // where lookups at AdjustWithin can find insertions at 31 // AdjustBefore in the same block, and lookups at AdjustAfter 32 // can find insertions at either AdjustBefore or AdjustWithin 33 // in the same block. (Note that this assumes a gappy numbering 34 // such that exit number or exit number is separated from its 35 // nearest neighbor by at least 3). 36 // 37 // The Sparse Tree lookup algorithm is described by 38 // Paul F. Dietz. Maintaining order in a linked list. In 39 // Proceedings of the Fourteenth Annual ACM Symposium on 40 // Theory of Computing, pages 122–127, May 1982. 41 // and by 42 // Ben Wegbreit. Faster retrieval from context trees. 43 // Communications of the ACM, 19(9):526–529, September 1976. 44 type SparseTreeMap RBTint32 45 46 // A SparseTreeHelper contains indexing and allocation data 47 // structures common to a collection of SparseTreeMaps, as well 48 // as exposing some useful control-flow-related data to other 49 // packages, such as gc. 50 type SparseTreeHelper struct { 51 Sdom []SparseTreeNode // indexed by block.ID 52 Po []*Block // exported data 53 Dom []*Block // exported data 54 Ponums []int32 // exported data 55 } 56 57 // NewSparseTreeHelper returns a SparseTreeHelper for use 58 // in the gc package, for example in phi-function placement. 59 func NewSparseTreeHelper(f *Func) *SparseTreeHelper { 60 dom := dominators(f) 61 ponums := make([]int32, f.NumBlocks()) 62 po := postorderWithNumbering(f, ponums) 63 return makeSparseTreeHelper(newSparseTree(f, dom), dom, po, ponums) 64 } 65 66 func (h *SparseTreeHelper) NewTree() *SparseTreeMap { 67 return &SparseTreeMap{} 68 } 69 70 func makeSparseTreeHelper(sdom SparseTree, dom, po []*Block, ponums []int32) *SparseTreeHelper { 71 helper := &SparseTreeHelper{Sdom: []SparseTreeNode(sdom), 72 Dom: dom, 73 Po: po, 74 Ponums: ponums, 75 } 76 return helper 77 } 78 79 // A sparseTreeMapEntry contains the data stored in a binary search 80 // data structure indexed by (dominator tree walk) entry and exit numbers. 81 // Each entry is added twice, once keyed by entry-1/entry/entry+1 and 82 // once keyed by exit+1/exit/exit-1. (there are three choices of paired indices, not 9, and they properly nest) 83 type sparseTreeMapEntry struct { 84 index *SparseTreeNode 85 block *Block // TODO: store this in a separate index. 86 data interface{} 87 } 88 89 // Insert creates a definition within b with data x. 90 // adjust indicates where in the block should be inserted: 91 // AdjustBefore means defined at a phi function (visible Within or After in the same block) 92 // AdjustWithin means defined within the block (visible After in the same block) 93 // AdjustAfter means after the block (visible within child blocks) 94 func (m *SparseTreeMap) Insert(b *Block, adjust int32, x interface{}, helper *SparseTreeHelper) { 95 rbtree := (*RBTint32)(m) 96 blockIndex := &helper.Sdom[b.ID] 97 if blockIndex.entry == 0 { 98 // assert unreachable 99 return 100 } 101 entry := &sparseTreeMapEntry{index: blockIndex, data: x} 102 right := blockIndex.exit - adjust 103 _ = rbtree.Insert(right, entry) 104 105 left := blockIndex.entry + adjust 106 _ = rbtree.Insert(left, entry) 107 } 108 109 // Find returns the definition visible from block b, or nil if none can be found. 110 // Adjust indicates where the block should be searched. 111 // AdjustBefore searches before the phi functions of b. 112 // AdjustWithin searches starting at the phi functions of b. 113 // AdjustAfter searches starting at the exit from the block, including normal within-block definitions. 114 // 115 // Note that Finds are properly nested with Inserts: 116 // m.Insert(b, a) followed by m.Find(b, a) will not return the result of the insert, 117 // but m.Insert(b, AdjustBefore) followed by m.Find(b, AdjustWithin) will. 118 // 119 // Another way to think of this is that Find searches for inputs, Insert defines outputs. 120 func (m *SparseTreeMap) Find(b *Block, adjust int32, helper *SparseTreeHelper) interface{} { 121 rbtree := (*RBTint32)(m) 122 if rbtree == nil { 123 return nil 124 } 125 blockIndex := &helper.Sdom[b.ID] 126 _, v := rbtree.Glb(blockIndex.entry + adjust) 127 for v != nil { 128 otherEntry := v.(*sparseTreeMapEntry) 129 otherIndex := otherEntry.index 130 // Two cases -- either otherIndex brackets blockIndex, 131 // or it doesn't. 132 // 133 // Note that if otherIndex and blockIndex are 134 // the same block, then the glb test only passed 135 // because the definition is "before", 136 // i.e., k == blockIndex.entry-1 137 // allowing equality is okay on the blocks check. 138 if otherIndex.exit >= blockIndex.exit { 139 // bracketed. 140 return otherEntry.data 141 } 142 // In the not-bracketed case, we could memoize the results of 143 // walking up the tree, but for now we won't. 144 // Memoize plan is to take the gap (inclusive) 145 // from otherIndex.exit+1 to blockIndex.entry-1 146 // and insert it into this or a second tree. 147 // Said tree would then need adjusting whenever 148 // an insertion occurred. 149 150 // Expectation is that per-variable tree is sparse, 151 // therefore probe siblings instead of climbing up. 152 // Note that each sibling encountered in this walk 153 // to find a defining ancestor shares that ancestor 154 // because the walk skips over the interior -- each 155 // Glb will be an exit, and the iteration is to the 156 // Glb of the entry. 157 _, v = rbtree.Glb(otherIndex.entry - 1) 158 } 159 return nil // nothing found 160 } 161 162 func (m *SparseTreeMap) String() string { 163 tree := (*RBTint32)(m) 164 return tree.String() 165 } 166 167 func (e *sparseTreeMapEntry) String() string { 168 return fmt.Sprintf("index=%v, data=%v", e.index, e.data) 169 }