// 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] }