blob: d5219dfbf237e8593b13f0f4452f69210ecff619 [file]
// 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 ssacompile
import (
"cmd/compile/internal/ssa"
"cmd/compile/internal/ssa/block"
"cmd/internal/src"
)
// deadcode removes dead code from f.
func deadcode(f *ssa.Func) {
// deadcode after regalloc is forbidden for now. Regalloc
// doesn't quite generate legal SSA which will lead to some
// required moves being eliminated. See the comment at the
// top of regalloc.go for details.
if f.RegAlloc != nil {
f.Fatalf("deadcode after regalloc")
}
// Find reachable blocks.
reachable := ssa.ReachableBlocks(f)
// Get rid of edges from dead to live code.
for _, b := range f.Blocks {
if reachable[b.ID] {
continue
}
for i := 0; i < len(b.Succs); {
e := b.Succs[i]
if reachable[e.B.ID] {
b.RemoveEdge(i)
} else {
i++
}
}
}
// Get rid of dead edges from live code.
for _, b := range f.Blocks {
if !reachable[b.ID] {
continue
}
if b.Kind != block.BlockFirst {
continue
}
b.RemoveEdge(1)
b.Kind = block.BlockPlain
b.Likely = ssa.BranchUnknown
}
// Splice out any copies introduced during dead block removal.
copyelim(f)
// Find live values.
live, order := ssa.LiveValues(f, reachable)
defer func() { f.Cache.FreeBoolSlice(live) }()
defer func() { f.Cache.FreeValueSlice(order) }()
// Remove dead & duplicate entries from namedValues map.
s := f.NewSparseSet(f.NumValues())
defer f.RetSparseSet(s)
i := 0
for _, name := range f.Names {
j := 0
s.Clear()
values := f.NamedValues[name]
for _, v := range values {
if live[v.ID] && !s.Contains(v.ID) {
values[j] = v
j++
s.Add(v.ID)
}
}
if j == 0 {
delete(f.NamedValues, name)
} else {
f.Names[i] = name
i++
for k := len(values) - 1; k >= j; k-- {
values[k] = nil
}
f.NamedValues[name] = values[:j]
}
}
clear(f.Names[i:])
f.Names = f.Names[:i]
pendingLines := f.CachedLineStarts // Holds statement boundaries that need to be moved to a new value/block
pendingLines.Clear()
// Unlink values and conserve statement boundaries
for i, b := range f.Blocks {
if !reachable[b.ID] {
// TODO what if control is statement boundary? Too late here.
b.ResetControls()
}
for _, v := range b.Values {
if !live[v.ID] {
v.ResetArgs()
if v.Pos.IsStmt() == src.PosIsStmt && reachable[b.ID] {
pendingLines.Set(v.Pos, int32(i)) // TODO could be more than one pos for a line
}
}
}
}
// Find new homes for lost lines -- require earliest in data flow with same line that is also in same block
for i := len(order) - 1; i >= 0; i-- {
w := order[i]
if j, ok := pendingLines.Get(w.Pos); ok && f.Blocks[j] == w.Block {
w.Pos = w.Pos.WithIsStmt()
pendingLines.Remove(w.Pos)
}
}
// Any boundary that failed to match a live value can move to a block end
pendingLines.ForeachEntry(func(j int32, l uint, bi int32) {
b := f.Blocks[bi]
if b.Pos.Line() == l && b.Pos.FileIndex() == j {
b.Pos = b.Pos.WithIsStmt()
}
})
// Remove dead values from blocks' value list. Return dead
// values 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.FreeValue(v)
}
}
b.TruncateValues(i)
}
// Remove unreachable blocks. Return dead blocks 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.FreeBlock(b)
}
}
// zero remainder to help GC
clear(f.Blocks[i:])
f.Blocks = f.Blocks[:i]
}