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
+}