github.com/gocuntian/go@v0.0.0-20160610041250-fee02d270bf8/src/cmd/compile/internal/gc/sparselocatephifunctions.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 gc 6 7 import ( 8 "cmd/compile/internal/ssa" 9 "fmt" 10 "math" 11 ) 12 13 // sparseDefState contains a Go map from ONAMEs (*Node) to sparse definition trees, and 14 // a search helper for the CFG's dominator tree in which those definitions are embedded. 15 // Once initialized, given a use of an ONAME within a block, the ssa definition for 16 // that ONAME can be discovered in time roughly proportional to the log of the number 17 // of SSA definitions of that ONAME (thus avoiding pathological quadratic behavior for 18 // very large programs). The helper contains state (a dominator tree numbering) common 19 // to all the sparse definition trees, as well as some necessary data obtained from 20 // the ssa package. 21 // 22 // This algorithm has improved asymptotic complexity, but the constant factor is 23 // rather large and thus it is only preferred for very large inputs containing 24 // 1000s of blocks and variables. 25 type sparseDefState struct { 26 helper *ssa.SparseTreeHelper // contains one copy of information needed to do sparse mapping 27 defmapForOname map[*Node]*onameDefs // for each ONAME, its definition set (normal and phi) 28 } 29 30 // onameDefs contains a record of definitions (ordinary and implied phi function) for a single OName. 31 // stm is the set of definitions for the OName. 32 // firstdef and lastuse are postorder block numberings that 33 // conservatively bracket the entire lifetime of the OName. 34 type onameDefs struct { 35 stm *ssa.SparseTreeMap 36 // firstdef and lastuse define an interval in the postorder numbering 37 // that is guaranteed to include the entire lifetime of an ONAME. 38 // In the postorder numbering, math.MaxInt32 is before anything, 39 // and 0 is after-or-equal all exit nodes and infinite loops. 40 firstdef int32 // the first definition of this ONAME *in the postorder numbering* 41 lastuse int32 // the last use of this ONAME *in the postorder numbering* 42 } 43 44 // defsFor finds or creates-and-inserts-in-map the definition information 45 // (sparse tree and live range) for a given OName. 46 func (m *sparseDefState) defsFor(n *Node) *onameDefs { 47 d := m.defmapForOname[n] 48 if d != nil { 49 return d 50 } 51 // Reminder: firstdef/lastuse are postorder indices, not block indices, 52 // so these default values define an empty interval, not the entire one. 53 d = &onameDefs{stm: m.helper.NewTree(), firstdef: 0, lastuse: math.MaxInt32} 54 m.defmapForOname[n] = d 55 return d 56 } 57 58 // Insert adds a definition at b (with specified before/within/after adjustment) 59 // to sparse tree onameDefs. The lifetime is extended as necessary. 60 func (m *sparseDefState) Insert(tree *onameDefs, b *ssa.Block, adjust int32) { 61 bponum := m.helper.Ponums[b.ID] 62 if bponum > tree.firstdef { 63 tree.firstdef = bponum 64 } 65 tree.stm.Insert(b, adjust, b, m.helper) 66 } 67 68 // Use updates tree to record a use within b, extending the lifetime as necessary. 69 func (m *sparseDefState) Use(tree *onameDefs, b *ssa.Block) { 70 bponum := m.helper.Ponums[b.ID] 71 if bponum < tree.lastuse { 72 tree.lastuse = bponum 73 } 74 } 75 76 // locatePotentialPhiFunctions finds all the places where phi functions 77 // will be inserted into a program and records those and ordinary definitions 78 // in a "map" (not a Go map) that given an OName and use site, returns the 79 // SSA definition for that OName that will reach the use site (that is, 80 // the use site's nearest def/phi site in the dominator tree.) 81 func (s *state) locatePotentialPhiFunctions(fn *Node) *sparseDefState { 82 // s.config.SparsePhiCutoff() is compared with product of numblocks and numvalues, 83 // if product is smaller than cutoff, use old non-sparse method. 84 // cutoff == 0 implies all sparse 85 // cutoff == uint(-1) implies all non-sparse 86 if uint64(s.f.NumValues())*uint64(s.f.NumBlocks()) < s.config.SparsePhiCutoff() { 87 return nil 88 } 89 90 helper := ssa.NewSparseTreeHelper(s.f) 91 po := helper.Po // index by block.ID to obtain postorder # of block. 92 trees := make(map[*Node]*onameDefs) 93 dm := &sparseDefState{defmapForOname: trees, helper: helper} 94 95 // Process params, taking note of their special lifetimes 96 b := s.f.Entry 97 for _, n := range fn.Func.Dcl { 98 switch n.Class { 99 case PPARAM, PPARAMOUT: 100 t := dm.defsFor(n) 101 dm.Insert(t, b, ssa.AdjustBefore) // define param at entry block 102 if n.Class == PPARAMOUT { 103 dm.Use(t, po[0]) // Explicitly use PPARAMOUT at very last block 104 } 105 default: 106 } 107 } 108 109 // Process memory variable. 110 t := dm.defsFor(&memVar) 111 dm.Insert(t, b, ssa.AdjustBefore) // define memory at entry block 112 dm.Use(t, po[0]) // Explicitly use memory at last block 113 114 // Next load the map w/ basic definitions for ONames recorded per-block 115 // Iterate over po to avoid unreachable blocks. 116 for i := len(po) - 1; i >= 0; i-- { 117 b := po[i] 118 m := s.defvars[b.ID] 119 for n := range m { // no specified order, but per-node trees are independent. 120 t := dm.defsFor(n) 121 dm.Insert(t, b, ssa.AdjustWithin) 122 } 123 } 124 125 // Find last use of each variable 126 for _, v := range s.fwdRefs { 127 b := v.Block 128 name := v.Aux.(*Node) 129 t := dm.defsFor(name) 130 dm.Use(t, b) 131 } 132 133 for _, t := range trees { 134 // iterating over names in the outer loop 135 for change := true; change; { 136 change = false 137 for i := t.firstdef; i >= t.lastuse; i-- { 138 // Iterating in reverse of post-order reduces number of 'change' iterations; 139 // all possible forward flow goes through each time. 140 b := po[i] 141 // Within tree t, would a use at b require a phi function to ensure a single definition? 142 // TODO: perhaps more efficient to record specific use sites instead of range? 143 if len(b.Preds) < 2 { 144 continue // no phi possible 145 } 146 phi := t.stm.Find(b, ssa.AdjustWithin, helper) // Look for defs in earlier block or AdjustBefore in this one. 147 if phi != nil && phi.(*ssa.Block) == b { 148 continue // has a phi already in this block. 149 } 150 var defseen interface{} 151 // Do preds see different definitions? if so, need a phi function. 152 for _, e := range b.Preds { 153 p := e.Block() 154 dm.Use(t, p) // always count phi pred as "use"; no-op except for loop edges, which matter. 155 x := t.stm.Find(p, ssa.AdjustAfter, helper) // Look for defs reaching or within predecessors. 156 if defseen == nil { 157 defseen = x 158 } 159 if defseen != x || x == nil { // TODO: too conservative at loops, does better if x == nil -> continue 160 // Need to insert a phi function here because predecessors's definitions differ. 161 change = true 162 // Phi insertion is at AdjustBefore, visible with find in same block at AdjustWithin or AdjustAfter. 163 dm.Insert(t, b, ssa.AdjustBefore) 164 break 165 } 166 } 167 } 168 } 169 } 170 return dm 171 } 172 173 // FindBetterDefiningBlock tries to find a better block for a definition of OName name 174 // reaching (or within) p than p itself. If it cannot, it returns p instead. 175 // This aids in more efficient location of phi functions, since it can skip over 176 // branch code that might contain a definition of name if it actually does not. 177 func (m *sparseDefState) FindBetterDefiningBlock(name *Node, p *ssa.Block) *ssa.Block { 178 if m == nil { 179 return p 180 } 181 t := m.defmapForOname[name] 182 // For now this is fail-soft, since the old algorithm still works using the unimproved block. 183 if t == nil { 184 return p 185 } 186 x := t.stm.Find(p, ssa.AdjustAfter, m.helper) 187 if x == nil { 188 return p 189 } 190 b := x.(*ssa.Block) 191 if b == nil { 192 return p 193 } 194 return b 195 } 196 197 func (d *onameDefs) String() string { 198 return fmt.Sprintf("onameDefs:first=%d,last=%d,tree=%s", d.firstdef, d.lastuse, d.stm.String()) 199 }