From: Keith Randall Date: Tue, 30 Jun 2020 22:59:40 +0000 (-0700) Subject: cmd/compile: invalidate zero-use values during rewrite X-Git-Tag: go1.16beta1~1176 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=cdc635547fc436dc49c91ddb172b0e101febd3d7;p=gostls13.git cmd/compile: invalidate zero-use values during rewrite This helps remove uses that aren't needed any more. That in turn helps other rules with Uses==1 conditions fire. Update #39918 Change-Id: I68635b675472f1d59e59604e4d34b949a0016533 Reviewed-on: https://go-review.googlesource.com/c/go/+/249463 Run-TryBot: Keith Randall TryBot-Result: Gobot Gobot Reviewed-by: Josh Bleecher Snyder --- diff --git a/src/cmd/compile/internal/ssa/decompose.go b/src/cmd/compile/internal/ssa/decompose.go index 6e72e3825c..ab27ba85ae 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 ab0fa803bf..f332b2e028 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 6e91fd7da3..128e614175 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 fb35691296..3df9ad24f9 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 4b578b133b..8db4334fef 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 7ead0ff300..7fc33772d3 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 +}