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