From f32cf8e4b025eee84aa3ec690966fa4e737a7522 Mon Sep 17 00:00:00 2001 From: Jorropo Date: Fri, 4 Jul 2025 09:26:38 +0200 Subject: [PATCH] cmd/compile: learn transitive proofs for safe unsigned subs I've split this into it's own CL to make git bisect more effective. Change-Id: I436ff21a3e2362b3924de25a458534eb9947e013 Reviewed-on: https://go-review.googlesource.com/c/go/+/685821 Reviewed-by: Keith Randall LUCI-TryBot-Result: Go LUCI Reviewed-by: Keith Randall Reviewed-by: Michael Knyszek Auto-Submit: Michael Knyszek --- src/cmd/compile/internal/ssa/prove.go | 15 +++++++ test/prove.go | 61 +++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) diff --git a/src/cmd/compile/internal/ssa/prove.go b/src/cmd/compile/internal/ssa/prove.go index cf045629cb..0c0581690f 100644 --- a/src/cmd/compile/internal/ssa/prove.go +++ b/src/cmd/compile/internal/ssa/prove.go @@ -2164,6 +2164,10 @@ func signedAddOverflowsOrUnderflows(a, b int64, t *types.Type) bool { } } +func unsignedSubUnderflows(a, b uint64) bool { + return a < b +} + func addLocalFacts(ft *factsTable, b *Block) { // Propagate constant ranges among values in this block. // We do this before the second loop so that we have the @@ -2226,6 +2230,17 @@ func addLocalFacts(ft *factsTable, b *Block) { } ft.update(b, v, v.Args[0], signed, r) } + case OpSub64, OpSub32, OpSub16, OpSub8: + x := ft.limits[v.Args[0].ID] + y := ft.limits[v.Args[1].ID] + if !unsignedSubUnderflows(x.umin, y.umax) { + r := lt + if !y.nonzero() { + r |= eq + } + ft.update(b, v, v.Args[0], unsigned, r) + } + // FIXME: we could also do signed facts but the overflow checks are much trickier and I don't need it yet. case OpAnd64, OpAnd32, OpAnd16, OpAnd8: ft.update(b, v, v.Args[0], unsigned, lt|eq) ft.update(b, v, v.Args[1], unsigned, lt|eq) diff --git a/test/prove.go b/test/prove.go index a8a9ce1ce4..8daa812e76 100644 --- a/test/prove.go +++ b/test/prove.go @@ -2241,6 +2241,67 @@ func transitiveProofsThroughOverflowingSignedAddNegative(x, y, z int64) { } } +func transitiveProofsThroughNonOverflowingUnsignedSub(x, y, z uint64) { + x |= 0xfff + y &= 0xfff + + a := x - y + if a < z { + return + } + + if x < z { // ERROR "Disproved Less64U$" + return + } + if y < z { + return + } + if a == x { + return + } + if a == y { + return + } + + y |= 1 + a = x - y + if a == x { // ERROR "Disproved Eq64$" + return + } + if a == y { + return + } +} + +func transitiveProofsThroughOverflowingUnsignedSub(x, y, z uint64) { + a := x - y + if a < z { + return + } + + if x < z { + return + } + if y < z { + return + } + if a == x { + return + } + if a == y { + return + } + + y |= 1 + a = x - y + if a == x { + return + } + if a == y { + return + } +} + //go:noinline func useInt(a int) { } -- 2.51.0