go/src/cmd/compile/internal/ssa/deadcode.go
Josh Bleecher Snyder 9b048527db [dev.ssa] cmd/compile/ssa: handle nested dead blocks
removePredecessor can change which blocks are live.
However, it cannot remove dead blocks from the function's
slice of blocks because removePredecessor may have been
called from within a function doing a walk of the blocks.

CL 11879 did not handle this correctly and broke the build.

To fix this, mark the block as dead but leave its actual
removal for a deadcode pass. Blocks that are dead must have
no successors, predecessors, values, or control values,
so they will generally be ignored by other passes.
To be safe, we add a deadcode pass after the opt pass,
which is the only other pass that calls removePredecessor.

Two alternatives that I considered and discarded:

(1) Make all call sites aware of the fact that removePrecessor
might make arbitrary changes to the list of blocks. This
will needlessly complicate callers.

(2) Handle the things that can go wrong in practice when
we encounter a dead-but-not-removed block. CL 11930 takes
this approach (and the tests are stolen from that CL).
However, this is just patching over the problem.

Change-Id: Icf0687b0a8148ce5e96b2988b668804411b05bd8
Reviewed-on: https://go-review.googlesource.com/12004
Reviewed-by: Todd Neal <todd@tneal.org>
Reviewed-by: Michael Matloob <michaelmatloob@gmail.com>
2015-07-11 00:08:50 +00:00

150 lines
3.2 KiB
Go

// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ssa
// deadcode removes dead code from f.
func deadcode(f *Func) {
// Find all reachable basic blocks.
reachable := make([]bool, f.NumBlocks())
reachable[f.Entry.ID] = true
p := []*Block{f.Entry} // stack-like worklist
for len(p) > 0 {
// Pop a reachable block
b := p[len(p)-1]
p = p[:len(p)-1]
// Mark successors as reachable
for _, c := range b.Succs {
if !reachable[c.ID] {
reachable[c.ID] = true
p = append(p, c) // push
}
}
}
// Find all live values
live := make([]bool, f.NumValues()) // flag to set for each live value
var q []*Value // stack-like worklist of unscanned values
// Starting set: all control values of reachable blocks are live.
for _, b := range f.Blocks {
if !reachable[b.ID] {
continue
}
if v := b.Control; v != nil && !live[v.ID] {
live[v.ID] = true
q = append(q, v)
}
}
// Compute transitive closure of live values.
for len(q) > 0 {
// pop a reachable value
v := q[len(q)-1]
q = q[:len(q)-1]
for _, x := range v.Args {
if !live[x.ID] {
live[x.ID] = true
q = append(q, x) // push
}
}
}
// Remove dead values from blocks' value list. Return dead
// value ids to the allocator.
for _, b := range f.Blocks {
i := 0
for _, v := range b.Values {
if live[v.ID] {
b.Values[i] = v
i++
} else {
f.vid.put(v.ID)
}
}
// aid GC
tail := b.Values[i:]
for j := range tail {
tail[j] = nil
}
b.Values = b.Values[:i]
}
// Remove unreachable blocks. Return dead block ids to allocator.
i := 0
for _, b := range f.Blocks {
if reachable[b.ID] {
f.Blocks[i] = b
i++
} else {
if len(b.Values) > 0 {
b.Fatalf("live values in unreachable block %v: %v", b, b.Values)
}
f.bid.put(b.ID)
}
}
// zero remainder to help GC
tail := f.Blocks[i:]
for j := range tail {
tail[j] = nil
}
f.Blocks = f.Blocks[:i]
// TODO: renumber Blocks and Values densely?
// TODO: save dead Values and Blocks for reuse? Or should we just let GC handle it?
}
// There was an edge b->c. c has been removed from b's successors.
// Fix up c to handle that fact.
func (f *Func) removePredecessor(b, c *Block) {
work := [][2]*Block{{b, c}}
for len(work) > 0 {
b, c := work[0][0], work[0][1]
work = work[1:]
// find index of b in c's predecessor list
var i int
for j, p := range c.Preds {
if p == b {
i = j
break
}
}
n := len(c.Preds) - 1
c.Preds[i] = c.Preds[n]
c.Preds[n] = nil // aid GC
c.Preds = c.Preds[:n]
// rewrite phi ops to match the new predecessor list
for _, v := range c.Values {
if v.Op != OpPhi {
continue
}
v.Args[i] = v.Args[n]
v.Args[n] = nil // aid GC
v.Args = v.Args[:n]
if n == 1 {
v.Op = OpCopy
}
}
if n == 0 {
// c is now dead--recycle its values
for _, v := range c.Values {
f.vid.put(v.ID)
}
c.Values = nil
// Also kill any successors of c now, to spare later processing.
for _, succ := range c.Succs {
work = append(work, [2]*Block{c, succ})
}
c.Succs = nil
c.Kind = BlockDead
c.Control = nil
}
}
}