github.com/corona10/go@v0.0.0-20180224231303-7a218942be57/src/cmd/compile/internal/ssa/deadcode.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 // findlive returns the reachable blocks and live values in f. 8 func findlive(f *Func) (reachable []bool, live []bool) { 9 reachable = ReachableBlocks(f) 10 live = liveValues(f, reachable) 11 return 12 } 13 14 // ReachableBlocks returns the reachable blocks in f. 15 func ReachableBlocks(f *Func) []bool { 16 reachable := make([]bool, f.NumBlocks()) 17 reachable[f.Entry.ID] = true 18 p := []*Block{f.Entry} // stack-like worklist 19 for len(p) > 0 { 20 // Pop a reachable block 21 b := p[len(p)-1] 22 p = p[:len(p)-1] 23 // Mark successors as reachable 24 s := b.Succs 25 if b.Kind == BlockFirst { 26 s = s[:1] 27 } 28 for _, e := range s { 29 c := e.b 30 if int(c.ID) >= len(reachable) { 31 f.Fatalf("block %s >= f.NumBlocks()=%d?", c, len(reachable)) 32 } 33 if !reachable[c.ID] { 34 reachable[c.ID] = true 35 p = append(p, c) // push 36 } 37 } 38 } 39 return reachable 40 } 41 42 // liveValues returns the live values in f. 43 // reachable is a map from block ID to whether the block is reachable. 44 func liveValues(f *Func, reachable []bool) []bool { 45 live := make([]bool, f.NumValues()) 46 47 // After regalloc, consider all values to be live. 48 // See the comment at the top of regalloc.go and in deadcode for details. 49 if f.RegAlloc != nil { 50 for i := range live { 51 live[i] = true 52 } 53 return live 54 } 55 56 // Find all live values 57 var q []*Value // stack-like worklist of unscanned values 58 59 // Starting set: all control values of reachable blocks are live. 60 // Calls are live (because callee can observe the memory state). 61 for _, b := range f.Blocks { 62 if !reachable[b.ID] { 63 continue 64 } 65 if v := b.Control; v != nil && !live[v.ID] { 66 live[v.ID] = true 67 q = append(q, v) 68 } 69 for _, v := range b.Values { 70 if (opcodeTable[v.Op].call || opcodeTable[v.Op].hasSideEffects) && !live[v.ID] { 71 live[v.ID] = true 72 q = append(q, v) 73 } 74 if v.Type.IsVoid() && !live[v.ID] { 75 // The only Void ops are nil checks. We must keep these. 76 live[v.ID] = true 77 q = append(q, v) 78 } 79 } 80 } 81 82 // Compute transitive closure of live values. 83 for len(q) > 0 { 84 // pop a reachable value 85 v := q[len(q)-1] 86 q = q[:len(q)-1] 87 for i, x := range v.Args { 88 if v.Op == OpPhi && !reachable[v.Block.Preds[i].b.ID] { 89 continue 90 } 91 if !live[x.ID] { 92 live[x.ID] = true 93 q = append(q, x) // push 94 } 95 } 96 } 97 98 return live 99 } 100 101 // deadcode removes dead code from f. 102 func deadcode(f *Func) { 103 // deadcode after regalloc is forbidden for now. Regalloc 104 // doesn't quite generate legal SSA which will lead to some 105 // required moves being eliminated. See the comment at the 106 // top of regalloc.go for details. 107 if f.RegAlloc != nil { 108 f.Fatalf("deadcode after regalloc") 109 } 110 111 // Find reachable blocks. 112 reachable := ReachableBlocks(f) 113 114 // Get rid of edges from dead to live code. 115 for _, b := range f.Blocks { 116 if reachable[b.ID] { 117 continue 118 } 119 for i := 0; i < len(b.Succs); { 120 e := b.Succs[i] 121 if reachable[e.b.ID] { 122 b.removeEdge(i) 123 } else { 124 i++ 125 } 126 } 127 } 128 129 // Get rid of dead edges from live code. 130 for _, b := range f.Blocks { 131 if !reachable[b.ID] { 132 continue 133 } 134 if b.Kind != BlockFirst { 135 continue 136 } 137 b.removeEdge(1) 138 b.Kind = BlockPlain 139 b.Likely = BranchUnknown 140 } 141 142 // Splice out any copies introduced during dead block removal. 143 copyelim(f) 144 145 // Find live values. 146 live := liveValues(f, reachable) 147 148 // Remove dead & duplicate entries from namedValues map. 149 s := f.newSparseSet(f.NumValues()) 150 defer f.retSparseSet(s) 151 i := 0 152 for _, name := range f.Names { 153 j := 0 154 s.clear() 155 values := f.NamedValues[name] 156 for _, v := range values { 157 if live[v.ID] && !s.contains(v.ID) { 158 values[j] = v 159 j++ 160 s.add(v.ID) 161 } 162 } 163 if j == 0 { 164 delete(f.NamedValues, name) 165 } else { 166 f.Names[i] = name 167 i++ 168 for k := len(values) - 1; k >= j; k-- { 169 values[k] = nil 170 } 171 f.NamedValues[name] = values[:j] 172 } 173 } 174 for k := len(f.Names) - 1; k >= i; k-- { 175 f.Names[k] = LocalSlot{} 176 } 177 f.Names = f.Names[:i] 178 179 // Unlink values. 180 for _, b := range f.Blocks { 181 if !reachable[b.ID] { 182 b.SetControl(nil) 183 } 184 for _, v := range b.Values { 185 if !live[v.ID] { 186 v.resetArgs() 187 } 188 } 189 } 190 191 // Remove dead values from blocks' value list. Return dead 192 // values to the allocator. 193 for _, b := range f.Blocks { 194 i := 0 195 for _, v := range b.Values { 196 if live[v.ID] { 197 b.Values[i] = v 198 i++ 199 } else { 200 f.freeValue(v) 201 } 202 } 203 // aid GC 204 tail := b.Values[i:] 205 for j := range tail { 206 tail[j] = nil 207 } 208 b.Values = b.Values[:i] 209 } 210 211 // Remove unreachable blocks. Return dead blocks to allocator. 212 i = 0 213 for _, b := range f.Blocks { 214 if reachable[b.ID] { 215 f.Blocks[i] = b 216 i++ 217 } else { 218 if len(b.Values) > 0 { 219 b.Fatalf("live values in unreachable block %v: %v", b, b.Values) 220 } 221 f.freeBlock(b) 222 } 223 } 224 // zero remainder to help GC 225 tail := f.Blocks[i:] 226 for j := range tail { 227 tail[j] = nil 228 } 229 f.Blocks = f.Blocks[:i] 230 } 231 232 // removeEdge removes the i'th outgoing edge from b (and 233 // the corresponding incoming edge from b.Succs[i].b). 234 func (b *Block) removeEdge(i int) { 235 e := b.Succs[i] 236 c := e.b 237 j := e.i 238 239 // Adjust b.Succs 240 b.removeSucc(i) 241 242 // Adjust c.Preds 243 c.removePred(j) 244 245 // Remove phi args from c's phis. 246 n := len(c.Preds) 247 for _, v := range c.Values { 248 if v.Op != OpPhi { 249 continue 250 } 251 v.Args[j].Uses-- 252 v.Args[j] = v.Args[n] 253 v.Args[n] = nil 254 v.Args = v.Args[:n] 255 phielimValue(v) 256 // Note: this is trickier than it looks. Replacing 257 // a Phi with a Copy can in general cause problems because 258 // Phi and Copy don't have exactly the same semantics. 259 // Phi arguments always come from a predecessor block, 260 // whereas copies don't. This matters in loops like: 261 // 1: x = (Phi y) 262 // y = (Add x 1) 263 // goto 1 264 // If we replace Phi->Copy, we get 265 // 1: x = (Copy y) 266 // y = (Add x 1) 267 // goto 1 268 // (Phi y) refers to the *previous* value of y, whereas 269 // (Copy y) refers to the *current* value of y. 270 // The modified code has a cycle and the scheduler 271 // will barf on it. 272 // 273 // Fortunately, this situation can only happen for dead 274 // code loops. We know the code we're working with is 275 // not dead, so we're ok. 276 // Proof: If we have a potential bad cycle, we have a 277 // situation like this: 278 // x = (Phi z) 279 // y = (op1 x ...) 280 // z = (op2 y ...) 281 // Where opX are not Phi ops. But such a situation 282 // implies a cycle in the dominator graph. In the 283 // example, x.Block dominates y.Block, y.Block dominates 284 // z.Block, and z.Block dominates x.Block (treating 285 // "dominates" as reflexive). Cycles in the dominator 286 // graph can only happen in an unreachable cycle. 287 } 288 }