[go] cmd/compile: match canonical constant position in Sub(Add) rules

2 views
Skip to first unread message

Gilbert Morgan (Gerrit)

unread,
Aug 5, 2026, 1:33:26 PM (7 days ago) Aug 5
to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

Gilbert Morgan has uploaded the change for review

Commit message

cmd/compile: match canonical constant position in Sub(Add) rules

In generic.rules, the Sub(Add) reassociation rules for x - (C + z) and
(C + z) - x matched Add nodes with the constant as the second operand
(e.g., Add64 z i:(Const64)).

Because commutative SSA operators canonicalize constants to the first
operand, those rules never triggered, preventing constant-sinking
across chained expressions such as ((1000 - x) + y) - z - 10.

Updating the patterns to match canonical order (AddXX i:(ConstXX) z)
enables constant folding across intervening variables.

Fixes #37508
Change-Id: If389db36e4c8b35f040cd470c735c1ed5a05f984

Change diff

diff --git a/src/cmd/compile/internal/ssa/_gen/generic.rules b/src/cmd/compile/internal/ssa/_gen/generic.rules
index f307b81..c2868d6 100644
--- a/src/cmd/compile/internal/ssa/_gen/generic.rules
+++ b/src/cmd/compile/internal/ssa/_gen/generic.rules
@@ -1254,11 +1254,11 @@
(Sub16 x (Sub16 i:(Const16 <t>) z)) && (z.Op != OpConst16 && x.Op != OpConst16) => (Sub16 (Add16 <t> x z) i)
(Sub8 x (Sub8 i:(Const8 <t>) z)) && (z.Op != OpConst8 && x.Op != OpConst8) => (Sub8 (Add8 <t> x z) i)

-// x - (z + C) -> x + (-z - C) -> (x - z) - C
-(Sub64 x (Add64 z i:(Const64 <t>))) && (z.Op != OpConst64 && x.Op != OpConst64) => (Sub64 (Sub64 <t> x z) i)
-(Sub32 x (Add32 z i:(Const32 <t>))) && (z.Op != OpConst32 && x.Op != OpConst32) => (Sub32 (Sub32 <t> x z) i)
-(Sub16 x (Add16 z i:(Const16 <t>))) && (z.Op != OpConst16 && x.Op != OpConst16) => (Sub16 (Sub16 <t> x z) i)
-(Sub8 x (Add8 z i:(Const8 <t>))) && (z.Op != OpConst8 && x.Op != OpConst8) => (Sub8 (Sub8 <t> x z) i)
+// x - (C + z) -> x + (-z - C) -> (x - z) - C
+(Sub64 x (Add64 i:(Const64 <t>) z)) && (z.Op != OpConst64 && x.Op != OpConst64) => (Sub64 (Sub64 <t> x z) i)
+(Sub32 x (Add32 i:(Const32 <t>) z)) && (z.Op != OpConst32 && x.Op != OpConst32) => (Sub32 (Sub32 <t> x z) i)
+(Sub16 x (Add16 i:(Const16 <t>) z)) && (z.Op != OpConst16 && x.Op != OpConst16) => (Sub16 (Sub16 <t> x z) i)
+(Sub8 x (Add8 i:(Const8 <t>) z)) && (z.Op != OpConst8 && x.Op != OpConst8) => (Sub8 (Sub8 <t> x z) i)

// (C - z) - x -> C - (z + x)
(Sub64 (Sub64 i:(Const64 <t>) z) x) && (z.Op != OpConst64 && x.Op != OpConst64) => (Sub64 i (Add64 <t> z x))
@@ -1266,11 +1266,11 @@
(Sub16 (Sub16 i:(Const16 <t>) z) x) && (z.Op != OpConst16 && x.Op != OpConst16) => (Sub16 i (Add16 <t> z x))
(Sub8 (Sub8 i:(Const8 <t>) z) x) && (z.Op != OpConst8 && x.Op != OpConst8) => (Sub8 i (Add8 <t> z x))

-// (z + C) -x -> C + (z - x)
-(Sub64 (Add64 z i:(Const64 <t>)) x) && (z.Op != OpConst64 && x.Op != OpConst64) => (Add64 i (Sub64 <t> z x))
-(Sub32 (Add32 z i:(Const32 <t>)) x) && (z.Op != OpConst32 && x.Op != OpConst32) => (Add32 i (Sub32 <t> z x))
-(Sub16 (Add16 z i:(Const16 <t>)) x) && (z.Op != OpConst16 && x.Op != OpConst16) => (Add16 i (Sub16 <t> z x))
-(Sub8 (Add8 z i:(Const8 <t>)) x) && (z.Op != OpConst8 && x.Op != OpConst8) => (Add8 i (Sub8 <t> z x))
+// (C + z) - x -> C + (z - x)
+(Sub64 (Add64 i:(Const64 <t>) z) x) && (z.Op != OpConst64 && x.Op != OpConst64) => (Add64 i (Sub64 <t> z x))
+(Sub32 (Add32 i:(Const32 <t>) z) x) && (z.Op != OpConst32 && x.Op != OpConst32) => (Add32 i (Sub32 <t> z x))
+(Sub16 (Add16 i:(Const16 <t>) z) x) && (z.Op != OpConst16 && x.Op != OpConst16) => (Add16 i (Sub16 <t> z x))
+(Sub8 (Add8 i:(Const8 <t>) z) x) && (z.Op != OpConst8 && x.Op != OpConst8) => (Add8 i (Sub8 <t> z x))

// x & (C & z) -> C & (x & z)
(And64 (And64 i:(Const64 <t>) z) x) && (z.Op != OpConst64 && x.Op != OpConst64) => (And64 i (And64 <t> z x))
diff --git a/src/cmd/compile/internal/ssa/rewritegeneric.go b/src/cmd/compile/internal/ssa/rewritegeneric.go
index 4908350..5b3ebd4 100644
--- a/src/cmd/compile/internal/ssa/rewritegeneric.go
+++ b/src/cmd/compile/internal/ssa/rewritegeneric.go
@@ -36123,7 +36123,7 @@
v.AddArg2(v0, i)
return true
}
- // match: (Sub16 x (Add16 z i:(Const16 <t>)))
+ // match: (Sub16 x (Add16 i:(Const16 <t>) z))
// cond: (z.Op != OpConst16 && x.Op != OpConst16)
// result: (Sub16 (Sub16 <t> x z) i)
for {
@@ -36135,12 +36135,12 @@
v_1_0 := v_1.Args[0]
v_1_1 := v_1.Args[1]
for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 {
- z := v_1_0
- i := v_1_1
+ i := v_1_0
if i.Op != OpConst16 {
continue
}
t := i.Type
+ z := v_1_1
if !(z.Op != OpConst16 && x.Op != OpConst16) {
continue
}
@@ -36175,7 +36175,7 @@
v.AddArg2(i, v0)
return true
}
- // match: (Sub16 (Add16 z i:(Const16 <t>)) x)
+ // match: (Sub16 (Add16 i:(Const16 <t>) z) x)
// cond: (z.Op != OpConst16 && x.Op != OpConst16)
// result: (Add16 i (Sub16 <t> z x))
for {
@@ -36186,12 +36186,12 @@
v_0_0 := v_0.Args[0]
v_0_1 := v_0.Args[1]
for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 {
- z := v_0_0
- i := v_0_1
+ i := v_0_0
if i.Op != OpConst16 {
continue
}
t := i.Type
+ z := v_0_1
x := v_1
if !(z.Op != OpConst16 && x.Op != OpConst16) {
continue
@@ -36496,7 +36496,7 @@
v.AddArg2(v0, i)
return true
}
- // match: (Sub32 x (Add32 z i:(Const32 <t>)))
+ // match: (Sub32 x (Add32 i:(Const32 <t>) z))
// cond: (z.Op != OpConst32 && x.Op != OpConst32)
// result: (Sub32 (Sub32 <t> x z) i)
for {
@@ -36508,12 +36508,12 @@
v_1_0 := v_1.Args[0]
v_1_1 := v_1.Args[1]
for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 {
- z := v_1_0
- i := v_1_1
+ i := v_1_0
if i.Op != OpConst32 {
continue
}
t := i.Type
+ z := v_1_1
if !(z.Op != OpConst32 && x.Op != OpConst32) {
continue
}
@@ -36548,7 +36548,7 @@
v.AddArg2(i, v0)
return true
}
- // match: (Sub32 (Add32 z i:(Const32 <t>)) x)
+ // match: (Sub32 (Add32 i:(Const32 <t>) z) x)
// cond: (z.Op != OpConst32 && x.Op != OpConst32)
// result: (Add32 i (Sub32 <t> z x))
for {
@@ -36559,12 +36559,12 @@
v_0_0 := v_0.Args[0]
v_0_1 := v_0.Args[1]
for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 {
- z := v_0_0
- i := v_0_1
+ i := v_0_0
if i.Op != OpConst32 {
continue
}
t := i.Type
+ z := v_0_1
x := v_1
if !(z.Op != OpConst32 && x.Op != OpConst32) {
continue
@@ -36893,7 +36893,7 @@
v.AddArg2(v0, i)
return true
}
- // match: (Sub64 x (Add64 z i:(Const64 <t>)))
+ // match: (Sub64 x (Add64 i:(Const64 <t>) z))
// cond: (z.Op != OpConst64 && x.Op != OpConst64)
// result: (Sub64 (Sub64 <t> x z) i)
for {
@@ -36905,12 +36905,12 @@
v_1_0 := v_1.Args[0]
v_1_1 := v_1.Args[1]
for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 {
- z := v_1_0
- i := v_1_1
+ i := v_1_0
if i.Op != OpConst64 {
continue
}
t := i.Type
+ z := v_1_1
if !(z.Op != OpConst64 && x.Op != OpConst64) {
continue
}
@@ -36945,7 +36945,7 @@
v.AddArg2(i, v0)
return true
}
- // match: (Sub64 (Add64 z i:(Const64 <t>)) x)
+ // match: (Sub64 (Add64 i:(Const64 <t>) z) x)
// cond: (z.Op != OpConst64 && x.Op != OpConst64)
// result: (Add64 i (Sub64 <t> z x))
for {
@@ -36956,12 +36956,12 @@
v_0_0 := v_0.Args[0]
v_0_1 := v_0.Args[1]
for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 {
- z := v_0_0
- i := v_0_1
+ i := v_0_0
if i.Op != OpConst64 {
continue
}
t := i.Type
+ z := v_0_1
x := v_1
if !(z.Op != OpConst64 && x.Op != OpConst64) {
continue
@@ -37325,7 +37325,7 @@
v.AddArg2(v0, i)
return true
}
- // match: (Sub8 x (Add8 z i:(Const8 <t>)))
+ // match: (Sub8 x (Add8 i:(Const8 <t>) z))
// cond: (z.Op != OpConst8 && x.Op != OpConst8)
// result: (Sub8 (Sub8 <t> x z) i)
for {
@@ -37337,12 +37337,12 @@
v_1_0 := v_1.Args[0]
v_1_1 := v_1.Args[1]
for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 {
- z := v_1_0
- i := v_1_1
+ i := v_1_0
if i.Op != OpConst8 {
continue
}
t := i.Type
+ z := v_1_1
if !(z.Op != OpConst8 && x.Op != OpConst8) {
continue
}
@@ -37377,7 +37377,7 @@
v.AddArg2(i, v0)
return true
}
- // match: (Sub8 (Add8 z i:(Const8 <t>)) x)
+ // match: (Sub8 (Add8 i:(Const8 <t>) z) x)
// cond: (z.Op != OpConst8 && x.Op != OpConst8)
// result: (Add8 i (Sub8 <t> z x))
for {
@@ -37388,12 +37388,12 @@
v_0_0 := v_0.Args[0]
v_0_1 := v_0.Args[1]
for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 {
- z := v_0_0
- i := v_0_1
+ i := v_0_0
if i.Op != OpConst8 {
continue
}
t := i.Type
+ z := v_0_1
x := v_1
if !(z.Op != OpConst8 && x.Op != OpConst8) {
continue
diff --git a/test/codegen/algebraic_simps.go b/test/codegen/algebraic_simps.go
new file mode 100644
index 0000000..efed618
--- /dev/null
+++ b/test/codegen/algebraic_simps.go
@@ -0,0 +1,42 @@
+// asmcheck
+
+// Copyright 2026 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 codegen
+
+// This file contains codegen tests for SSA algebraic simplifications
+// and addition/subtraction constant reassociation (issue #37508).
+
+//go:noinline
+func issue37508(x, y, z, abc int64) int64 {
+ // amd64: `(ADDQ\s*[$]948|LEAQ\s*948)` -`ADDQ\s*[$]1000` -`SUBQ\s*[$]10` -`SUBQ\s*[$]42` -`SUBQ\s*[$]52`
+ // arm64: `(ADD\s*[$]948|SUB\s*[$]-948)` -`ADD\s*[$]1000` -`SUB\s*[$]10` -`SUB\s*[$]42` -`SUB\s*[$]52`
+ // riscv64: `(ADDI\s*[$]948|ADDIW\s*[$]948)` -`ADDI\s*[$]1000` -`ADDI\s*[$]-10` -`ADDI\s*[$]-42` -`ADDI\s*[$]-52`
+ return (((((1000 - x) + y) - z) - 10) + abc) - 42
+}
+
+//go:noinline
+func subAddReassoc(z, x int64) int64 {
+ // amd64: `(ADDQ\s*[$]60|LEAQ\s*60)` -`ADDQ\s*[$]100` -`SUBQ\s*[$]40`
+ // arm64: `ADD\s*[$]60` -`ADD\s*[$]100` -`SUB\s*[$]40`
+ // riscv64: `ADDI\s*[$]60` -`ADDI\s*[$]100` -`ADDI\s*[$]-40`
+ return (100 + z) - x - 40
+}
+
+//go:noinline
+func subSubReassoc(z, x int64) int64 {
+ // amd64: `(ADDQ\s*[$]-300|LEAQ\s*-300)` -`ADDQ\s*[$]500` -`SUBQ\s*[$]200`
+ // arm64: `SUB\s*[$]300` -`ADD\s*[$]500` -`SUB\s*[$]200`
+ // riscv64: `ADDI\s*[$]-300` -`ADDI\s*[$]500` -`ADDI\s*[$]-200`
+ return 500 - z - x - 200
+}
+
+//go:noinline
+func subAddInv(x, z int64) int64 {
+ // amd64: `(ADDQ\s*[$]-100|LEAQ\s*-100)` -`ADDQ\s*[$]150` -`SUBQ\s*[$]50`
+ // arm64: `SUB\s*[$]100` -`ADD\s*[$]150` -`SUB\s*[$]50`
+ // riscv64: `ADDI\s*[$]-100` -`ADDI\s*[$]150` -`ADDI\s*[$]-50`
+ return x - (150 + z) + 50
+}

Change information

Files:
  • M src/cmd/compile/internal/ssa/_gen/generic.rules
  • M src/cmd/compile/internal/ssa/rewritegeneric.go
  • A test/codegen/algebraic_simps.go
Change size: M
Delta: 3 files changed, 76 insertions(+), 34 deletions(-)
Open in Gerrit

Related details

Attention set is empty
Submit Requirements:
  • requirement is not satisfiedCode-Review
  • requirement satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
  • requirement is not satisfiedTryBots-Pass
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: newchange
Gerrit-Project: go
Gerrit-Branch: master
Gerrit-Change-Id: If389db36e4c8b35f040cd470c735c1ed5a05f984
Gerrit-Change-Number: 810940
Gerrit-PatchSet: 1
Gerrit-Owner: Gilbert Morgan <gilber...@google.com>
unsatisfied_requirement
satisfied_requirement
open
diffy

Keith Randall (Gerrit)

unread,
Aug 6, 2026, 2:14:13 PM (6 days ago) Aug 6
to Gilbert Morgan, goph...@pubsubhelper.golang.org, Martin Möhrmann, Keith Randall, Gopher Robot, golang-co...@googlegroups.com
Attention needed from Gilbert Morgan and Martin Möhrmann

Keith Randall added 1 comment

Patchset-level comments
File-level comment, Patchset 1 (Latest):
Keith Randall . unresolved

This looks unnecessary. All the test examples already work on tip? Maybe 37508 is already fixed.

Open in Gerrit

Related details

Attention is currently required from:
  • Gilbert Morgan
  • Martin Möhrmann
Submit Requirements:
    • requirement is not satisfiedCode-Review
    • requirement is not satisfiedNo-Unresolved-Comments
    • requirement is not satisfiedReview-Enforcement
    • requirement is not satisfiedTryBots-Pass
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: comment
    Gerrit-Project: go
    Gerrit-Branch: master
    Gerrit-Change-Id: If389db36e4c8b35f040cd470c735c1ed5a05f984
    Gerrit-Change-Number: 810940
    Gerrit-PatchSet: 1
    Gerrit-Owner: Gilbert Morgan <gilber...@google.com>
    Gerrit-Reviewer: Keith Randall <k...@golang.org>
    Gerrit-Reviewer: Martin Möhrmann <moeh...@google.com>
    Gerrit-CC: Gopher Robot <go...@golang.org>
    Gerrit-Attention: Gilbert Morgan <gilber...@google.com>
    Gerrit-Attention: Martin Möhrmann <moeh...@google.com>
    Gerrit-Comment-Date: Thu, 06 Aug 2026 18:14:08 +0000
    Gerrit-HasComments: Yes
    Gerrit-Has-Labels: No
    unsatisfied_requirement
    open
    diffy

    Gilbert Morgan (Gerrit)

    unread,
    Aug 11, 2026, 11:51:41 PM (4 hours ago) Aug 11
    to goph...@pubsubhelper.golang.org, Martin Möhrmann, Keith Randall, Gopher Robot, golang-co...@googlegroups.com
    Attention needed from Keith Randall and Martin Möhrmann

    Gilbert Morgan added 1 comment

    Patchset-level comments
    Keith Randall . resolved

    This looks unnecessary. All the test examples already work on tip? Maybe 37508 is already fixed.

    Gilbert Morgan

    Understood. I will abandon this commit.

    Open in Gerrit

    Related details

    Attention is currently required from:
    • Keith Randall
    • Martin Möhrmann
    Submit Requirements:
      • requirement is not satisfiedCode-Review
      • requirement satisfiedNo-Unresolved-Comments
      • requirement is not satisfiedReview-Enforcement
      • requirement is not satisfiedTryBots-Pass
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: comment
      Gerrit-Project: go
      Gerrit-Branch: master
      Gerrit-Change-Id: If389db36e4c8b35f040cd470c735c1ed5a05f984
      Gerrit-Change-Number: 810940
      Gerrit-PatchSet: 1
      Gerrit-Owner: Gilbert Morgan <gilber...@google.com>
      Gerrit-Reviewer: Keith Randall <k...@golang.org>
      Gerrit-Reviewer: Martin Möhrmann <moeh...@google.com>
      Gerrit-CC: Gopher Robot <go...@golang.org>
      Gerrit-Attention: Keith Randall <k...@golang.org>
      Gerrit-Attention: Martin Möhrmann <moeh...@google.com>
      Gerrit-Comment-Date: Wed, 12 Aug 2026 03:51:34 +0000
      Gerrit-HasComments: Yes
      Gerrit-Has-Labels: No
      Comment-In-Reply-To: Keith Randall <k...@golang.org>
      unsatisfied_requirement
      satisfied_requirement
      open
      diffy

      Gilbert Morgan (Gerrit)

      unread,
      Aug 11, 2026, 11:51:44 PM (4 hours ago) Aug 11
      to goph...@pubsubhelper.golang.org, Martin Möhrmann, Keith Randall, Gopher Robot, golang-co...@googlegroups.com

      Gilbert Morgan abandoned this change.

      View Change

      Abandoned

      Gilbert Morgan abandoned this change

      Related details

      Attention set is empty
      Submit Requirements:
      • requirement is not satisfiedCode-Review
      • requirement satisfiedNo-Unresolved-Comments
      • requirement is not satisfiedReview-Enforcement
      • requirement is not satisfiedTryBots-Pass
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: abandon
      unsatisfied_requirement
      satisfied_requirement
      open
      diffy
      Reply all
      Reply to author
      Forward
      0 new messages