diff --git a/src/cmd/compile/internal/ssa/decompose.go b/src/cmd/compile/internal/ssa/decompose.go index 6e72e3825ce0a..ab27ba85ae789 100644 --- a/src/cmd/compile/internal/ssa/decompose.go +++ b/src/cmd/compile/internal/ssa/decompose.go @@ -23,9 +23,11 @@ func decomposeBuiltIn(f *Func) { } // Decompose other values - applyRewrite(f, rewriteBlockdec, rewriteValuedec) + // Note: deadcode is false because we need to keep the original + // values around so the name component resolution below can still work. + applyRewrite(f, rewriteBlockdec, rewriteValuedec, leaveDeadValues) if f.Config.RegSize == 4 { - applyRewrite(f, rewriteBlockdec64, rewriteValuedec64) + applyRewrite(f, rewriteBlockdec64, rewriteValuedec64, leaveDeadValues) } // Split up named values into their components. @@ -215,7 +217,7 @@ func decomposeInterfacePhi(v *Value) { } func decomposeArgs(f *Func) { - applyRewrite(f, rewriteBlockdecArgs, rewriteValuedecArgs) + applyRewrite(f, rewriteBlockdecArgs, rewriteValuedecArgs, removeDeadValues) } func decomposeUser(f *Func) { diff --git a/src/cmd/compile/internal/ssa/lower.go b/src/cmd/compile/internal/ssa/lower.go index ab0fa803bfdea..f332b2e028e72 100644 --- a/src/cmd/compile/internal/ssa/lower.go +++ b/src/cmd/compile/internal/ssa/lower.go @@ -7,7 +7,7 @@ package ssa // convert to machine-dependent ops func lower(f *Func) { // repeat rewrites until we find no more rewrites - applyRewrite(f, f.Config.lowerBlock, f.Config.lowerValue) + applyRewrite(f, f.Config.lowerBlock, f.Config.lowerValue, removeDeadValues) } // checkLower checks for unlowered opcodes and fails if we find one. diff --git a/src/cmd/compile/internal/ssa/opt.go b/src/cmd/compile/internal/ssa/opt.go index 6e91fd7da3533..128e6141750dd 100644 --- a/src/cmd/compile/internal/ssa/opt.go +++ b/src/cmd/compile/internal/ssa/opt.go @@ -6,5 +6,5 @@ package ssa // machine-independent optimization func opt(f *Func) { - applyRewrite(f, rewriteBlockgeneric, rewriteValuegeneric) + applyRewrite(f, rewriteBlockgeneric, rewriteValuegeneric, removeDeadValues) } diff --git a/src/cmd/compile/internal/ssa/rewrite.go b/src/cmd/compile/internal/ssa/rewrite.go index fb35691296028..3df9ad24f91a3 100644 --- a/src/cmd/compile/internal/ssa/rewrite.go +++ b/src/cmd/compile/internal/ssa/rewrite.go @@ -20,7 +20,15 @@ import ( "path/filepath" ) -func applyRewrite(f *Func, rb blockRewriter, rv valueRewriter) { +type deadValueChoice bool + +const ( + leaveDeadValues deadValueChoice = false + removeDeadValues = true +) + +// deadcode indicates that rewrite should try to remove any values that become dead. +func applyRewrite(f *Func, rb blockRewriter, rv valueRewriter, deadcode deadValueChoice) { // repeat rewrites until we find no more rewrites pendingLines := f.cachedLineStarts // Holds statement boundaries that need to be moved to a new value/block pendingLines.clear() @@ -56,6 +64,18 @@ func applyRewrite(f *Func, rb blockRewriter, rv valueRewriter) { *v0 = *v v0.Args = append([]*Value{}, v.Args...) // make a new copy, not aliasing } + if v.Uses == 0 && v.removeable() { + if v.Op != OpInvalid && deadcode == removeDeadValues { + // Reset any values that are now unused, so that we decrement + // the use count of all of its arguments. + // Not quite a deadcode pass, because it does not handle cycles. + // But it should help Uses==1 rules to fire. + v.reset(OpInvalid) + change = true + } + // No point rewriting values which aren't used. + continue + } vchange := phielimValue(v) if vchange && debug > 1 { diff --git a/src/cmd/compile/internal/ssa/softfloat.go b/src/cmd/compile/internal/ssa/softfloat.go index 4b578b133b567..8db4334fef853 100644 --- a/src/cmd/compile/internal/ssa/softfloat.go +++ b/src/cmd/compile/internal/ssa/softfloat.go @@ -72,7 +72,7 @@ func softfloat(f *Func) { if newInt64 && f.Config.RegSize == 4 { // On 32bit arch, decompose Uint64 introduced in the switch above. decomposeBuiltIn(f) - applyRewrite(f, rewriteBlockdec64, rewriteValuedec64) + applyRewrite(f, rewriteBlockdec64, rewriteValuedec64, removeDeadValues) } } diff --git a/src/cmd/compile/internal/ssa/value.go b/src/cmd/compile/internal/ssa/value.go index 7ead0ff300135..7fc33772d3a6e 100644 --- a/src/cmd/compile/internal/ssa/value.go +++ b/src/cmd/compile/internal/ssa/value.go @@ -460,3 +460,23 @@ func (v *Value) LackingPos() bool { return v.Op == OpVarDef || v.Op == OpVarKill || v.Op == OpVarLive || v.Op == OpPhi || (v.Op == OpFwdRef || v.Op == OpCopy) && v.Type == types.TypeMem } + +// removeable reports whether the value v can be removed from the SSA graph entirely +// if its use count drops to 0. +func (v *Value) removeable() bool { + if v.Type.IsVoid() { + // Void ops, like nil pointer checks, must stay. + return false + } + if v.Type.IsMemory() { + // All memory ops aren't needed here, but we do need + // to keep calls at least (because they might have + // syncronization operations we can't see). + return false + } + if v.Op.HasSideEffects() { + // These are mostly synchronization operations. + return false + } + return true +}