From 24af44143749c864d9e7d7f5d7d60c02e6662b2a Mon Sep 17 00:00:00 2001 From: Jorropo Date: Sun, 26 Oct 2025 18:38:00 +0100 Subject: [PATCH] cmd/compile: rewrite proved multiplies by 0 or 1 into CondSelect Updates #76056 Change-Id: I64fe631ab381c74f902f877392530d7cc91860ab Reviewed-on: https://go-review.googlesource.com/c/go/+/715044 Reviewed-by: Michael Knyszek Auto-Submit: Jorropo Reviewed-by: Keith Randall Reviewed-by: Keith Randall LUCI-TryBot-Result: Go LUCI --- src/cmd/compile/internal/ssa/branchelim.go | 3 +- src/cmd/compile/internal/ssa/prove.go | 51 +++++++++++++++++++++- test/prove.go | 13 ++++++ 3 files changed, 65 insertions(+), 2 deletions(-) diff --git a/src/cmd/compile/internal/ssa/branchelim.go b/src/cmd/compile/internal/ssa/branchelim.go index 7b0a7ae8ec..8c411b541d 100644 --- a/src/cmd/compile/internal/ssa/branchelim.go +++ b/src/cmd/compile/internal/ssa/branchelim.go @@ -70,7 +70,8 @@ func branchelim(f *Func) { } func canCondSelect(v *Value, arch string, loadAddr *sparseSet) bool { - if loadAddr.contains(v.ID) { + if loadAddr != nil && // prove calls this on some multiplies and doesn't take care of loadAddrs + loadAddr.contains(v.ID) { // The result of the soon-to-be conditional move is used to compute a load address. // We want to avoid generating a conditional move in this case // because the load address would now be data-dependent on the condition. diff --git a/src/cmd/compile/internal/ssa/prove.go b/src/cmd/compile/internal/ssa/prove.go index e38dd4a838..d1920b00dd 100644 --- a/src/cmd/compile/internal/ssa/prove.go +++ b/src/cmd/compile/internal/ssa/prove.go @@ -2646,11 +2646,23 @@ var bytesizeToConst = [...]Op{ 32 / 8: OpConst32, 64 / 8: OpConst64, } +var bytesizeToNeq = [...]Op{ + 8 / 8: OpNeq8, + 16 / 8: OpNeq16, + 32 / 8: OpNeq32, + 64 / 8: OpNeq64, +} +var bytesizeToAnd = [...]Op{ + 8 / 8: OpAnd8, + 16 / 8: OpAnd16, + 32 / 8: OpAnd32, + 64 / 8: OpAnd64, +} // simplifyBlock simplifies some constant values in b and evaluates // branches to non-uniquely dominated successors of b. func simplifyBlock(sdom SparseTree, ft *factsTable, b *Block) { - for _, v := range b.Values { + for iv, v := range b.Values { switch v.Op { case OpSlicemask: // Replace OpSlicemask operations in b with constants where possible. @@ -2757,6 +2769,43 @@ func simplifyBlock(sdom SparseTree, ft *factsTable, b *Block) { b.Func.Warnl(v.Pos, "Proved %v does not need fix-up", v.Op) } } + case OpMul64, OpMul32, OpMul16, OpMul8: + x := v.Args[0] + xl := ft.limits[x.ID] + y := v.Args[1] + yl := ft.limits[y.ID] + switch xOne, yOne := xl.umax <= 1, yl.umax <= 1; { + case xOne && yOne: + v.Op = bytesizeToAnd[v.Type.Size()] + if b.Func.pass.debug > 0 { + b.Func.Warnl(v.Pos, "Rewrote Mul %v into And", v) + } + case yOne && b.Func.Config.haveCondSelect: + x, y = y, x + fallthrough + case xOne && b.Func.Config.haveCondSelect: + if !canCondSelect(v, b.Func.Config.arch, nil) { + break + } + zero := b.Func.constVal(bytesizeToConst[v.Type.Size()], v.Type, 0, true) + ft.initLimitForNewValue(zero) + check := b.NewValue2(v.Pos, bytesizeToNeq[v.Type.Size()], types.Types[types.TBOOL], zero, x) + ft.initLimitForNewValue(check) + v.reset(OpCondSelect) + v.AddArg3(y, zero, check) + + // FIXME: workaround for go.dev/issues/76060 + // we need to schedule the Neq before the CondSelect even tho + // scheduling is meaningless until we reach the schedule pass. + if b.Values[len(b.Values)-1] != check { + panic("unreachable; failed sanity check, new value isn't at the end of the block") + } + b.Values[iv], b.Values[len(b.Values)-1] = b.Values[len(b.Values)-1], b.Values[iv] + + if b.Func.pass.debug > 0 { + b.Func.Warnl(v.Pos, "Rewrote Mul %v into CondSelect; %v is bool", v, x) + } + } } // Fold provable constant results. // Helps in cases where we reuse a value after branching on its equality. diff --git a/test/prove.go b/test/prove.go index 31f8d711f1..4892d7b49c 100644 --- a/test/prove.go +++ b/test/prove.go @@ -2501,6 +2501,19 @@ func issue75144ifNot(a, b []uint64) bool { return false } +func mulIntoAnd(a, b uint) uint { + if a > 1 || b > 1 { + return 0 + } + return a * b // ERROR "Rewrote Mul v[0-9]+ into And$" +} +func mulIntoCondSelect(a, b uint) uint { + if a > 1 { + return 0 + } + return a * b // ERROR "Rewrote Mul v[0-9]+ into CondSelect" +} + //go:noinline func useInt(a int) { } -- 2.52.0