github.com/bir3/gocompiler@v0.3.205/src/cmd/compile/internal/ssa/tighten.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 // tighten moves Values closer to the Blocks in which they are used. 8 // This can reduce the amount of register spilling required, 9 // if it doesn't also create more live values. 10 // A Value can be moved to any block that 11 // dominates all blocks in which it is used. 12 func tighten(f *Func) { 13 canMove := f.Cache.allocBoolSlice(f.NumValues()) 14 defer f.Cache.freeBoolSlice(canMove) 15 for _, b := range f.Blocks { 16 for _, v := range b.Values { 17 if v.Op.isLoweredGetClosurePtr() { 18 // Must stay in the entry block. 19 continue 20 } 21 switch v.Op { 22 case OpPhi, OpArg, OpArgIntReg, OpArgFloatReg, OpSelect0, OpSelect1, OpSelectN: 23 // Phis need to stay in their block. 24 // Arg must stay in the entry block. 25 // Tuple selectors must stay with the tuple generator. 26 // SelectN is typically, ultimately, a register. 27 continue 28 } 29 if v.MemoryArg() != nil { 30 // We can't move values which have a memory arg - it might 31 // make two memory values live across a block boundary. 32 continue 33 } 34 // Count arguments which will need a register. 35 narg := 0 36 for _, a := range v.Args { 37 if !a.rematerializeable() { 38 narg++ 39 } 40 } 41 if narg >= 2 && !v.Type.IsFlags() { 42 // Don't move values with more than one input, as that may 43 // increase register pressure. 44 // We make an exception for flags, as we want flag generators 45 // moved next to uses (because we only have 1 flag register). 46 continue 47 } 48 canMove[v.ID] = true 49 } 50 } 51 52 // Build data structure for fast least-common-ancestor queries. 53 lca := makeLCArange(f) 54 55 // For each moveable value, record the block that dominates all uses found so far. 56 target := f.Cache.allocBlockSlice(f.NumValues()) 57 defer f.Cache.freeBlockSlice(target) 58 59 // Grab loop information. 60 // We use this to make sure we don't tighten a value into a (deeper) loop. 61 idom := f.Idom() 62 loops := f.loopnest() 63 loops.calculateDepths() 64 65 changed := true 66 for changed { 67 changed = false 68 69 // Reset target 70 for i := range target { 71 target[i] = nil 72 } 73 74 // Compute target locations (for moveable values only). 75 // target location = the least common ancestor of all uses in the dominator tree. 76 for _, b := range f.Blocks { 77 for _, v := range b.Values { 78 for i, a := range v.Args { 79 if !canMove[a.ID] { 80 continue 81 } 82 use := b 83 if v.Op == OpPhi { 84 use = b.Preds[i].b 85 } 86 if target[a.ID] == nil { 87 target[a.ID] = use 88 } else { 89 target[a.ID] = lca.find(target[a.ID], use) 90 } 91 } 92 } 93 for _, c := range b.ControlValues() { 94 if !canMove[c.ID] { 95 continue 96 } 97 if target[c.ID] == nil { 98 target[c.ID] = b 99 } else { 100 target[c.ID] = lca.find(target[c.ID], b) 101 } 102 } 103 } 104 105 // If the target location is inside a loop, 106 // move the target location up to just before the loop head. 107 for _, b := range f.Blocks { 108 origloop := loops.b2l[b.ID] 109 for _, v := range b.Values { 110 t := target[v.ID] 111 if t == nil { 112 continue 113 } 114 targetloop := loops.b2l[t.ID] 115 for targetloop != nil && (origloop == nil || targetloop.depth > origloop.depth) { 116 t = idom[targetloop.header.ID] 117 target[v.ID] = t 118 targetloop = loops.b2l[t.ID] 119 } 120 } 121 } 122 123 // Move values to target locations. 124 for _, b := range f.Blocks { 125 for i := 0; i < len(b.Values); i++ { 126 v := b.Values[i] 127 t := target[v.ID] 128 if t == nil || t == b { 129 // v is not moveable, or is already in correct place. 130 continue 131 } 132 // Move v to the block which dominates its uses. 133 t.Values = append(t.Values, v) 134 v.Block = t 135 last := len(b.Values) - 1 136 b.Values[i] = b.Values[last] 137 b.Values[last] = nil 138 b.Values = b.Values[:last] 139 changed = true 140 i-- 141 } 142 } 143 } 144 } 145 146 // phiTighten moves constants closer to phi users. 147 // This pass avoids having lots of constants live for lots of the program. 148 // See issue 16407. 149 func phiTighten(f *Func) { 150 for _, b := range f.Blocks { 151 for _, v := range b.Values { 152 if v.Op != OpPhi { 153 continue 154 } 155 for i, a := range v.Args { 156 if !a.rematerializeable() { 157 continue // not a constant we can move around 158 } 159 if a.Block == b.Preds[i].b { 160 continue // already in the right place 161 } 162 // Make a copy of a, put in predecessor block. 163 v.SetArg(i, a.copyInto(b.Preds[i].b)) 164 } 165 } 166 } 167 }