cmd/compile: add more generic rewrite rules to reassociate (op (op y C) x|C)

With this patch, opt pass can expose more obvious constant-folding
opportunites.

Example:
func test(i int) int {return (i+8)-(i+4)}

The previous version:
  MOVD	"".i(FP), R0
  ADD	$8, R0, R1
  ADD	$4, R0, R0
  SUB	R0, R1, R0
  MOVD	R0, "".~r1+8(FP)
  RET	(R30)

The optimized version:
  MOVD	$4, R0
  MOVD	R0, "".~r1+8(FP)
  RET	(R30)

This patch removes some existing reassociation rules, such as "x+(z-C)",
because the current generic rewrite rules will canonicalize "x-const"
to "x+(-const)", making "x+(z-C)" equal to "x+(z+(-C))".

This patch also adds test cases.

Change-Id: I857108ba0b5fcc18a879eeab38e2551bc4277797
Reviewed-on: https://go-review.googlesource.com/c/go/+/237137
Run-TryBot: Keith Randall <khr@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
diff --git a/src/cmd/compile/internal/ssa/gen/generic.rules b/src/cmd/compile/internal/ssa/gen/generic.rules
index 2d39d27..f7e6bbe 100644
--- a/src/cmd/compile/internal/ssa/gen/generic.rules
+++ b/src/cmd/compile/internal/ssa/gen/generic.rules
@@ -1807,6 +1807,8 @@
 // invariant that pointers must stay within the pointed-to object,
 // we can't pull part of a pointer computation above the AddPtr.
 // See issue 37881.
+// Note: we don't need to handle any (x-C) cases because we already rewrite
+// (x-C) to (x+(-C)).
 
 // x + (C + z) -> C + (x + z)
 (Add64 (Add64 i:(Const64 <t>) z) x) && (z.Op != OpConst64 && x.Op != OpConst64) => (Add64 i (Add64 <t> z x))
@@ -1820,23 +1822,29 @@
 (Add16 (Sub16 i:(Const16 <t>) z) x) && (z.Op != OpConst16 && x.Op != OpConst16) => (Add16 i (Sub16 <t> x z))
 (Add8  (Sub8  i:(Const8  <t>) z) x) && (z.Op != OpConst8  && x.Op != OpConst8)  => (Add8  i (Sub8  <t> x z))
 
-// x + (z - C) -> (x + z) - C
-(Add64 (Sub64 z i:(Const64 <t>)) x) && (z.Op != OpConst64 && x.Op != OpConst64) => (Sub64 (Add64 <t> x z) i)
-(Add32 (Sub32 z i:(Const32 <t>)) x) && (z.Op != OpConst32 && x.Op != OpConst32) => (Sub32 (Add32 <t> x z) i)
-(Add16 (Sub16 z i:(Const16 <t>)) x) && (z.Op != OpConst16 && x.Op != OpConst16) => (Sub16 (Add16 <t> x z) i)
-(Add8  (Sub8  z i:(Const8  <t>)) x) && (z.Op != OpConst8  && x.Op != OpConst8)  => (Sub8  (Add8  <t> x z) i)
-
 // x - (C - z) -> x + (z - C) -> (x + z) - C
 (Sub64 x (Sub64 i:(Const64 <t>) z)) && (z.Op != OpConst64 && x.Op != OpConst64) => (Sub64 (Add64 <t> x z) i)
 (Sub32 x (Sub32 i:(Const32 <t>) z)) && (z.Op != OpConst32 && x.Op != OpConst32) => (Sub32 (Add32 <t> x z) i)
 (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 + (C - z) -> (x - z) + C
-(Sub64 x (Sub64 z i:(Const64 <t>))) && (z.Op != OpConst64 && x.Op != OpConst64) => (Add64 i (Sub64 <t> x z))
-(Sub32 x (Sub32 z i:(Const32 <t>))) && (z.Op != OpConst32 && x.Op != OpConst32) => (Add32 i (Sub32 <t> x z))
-(Sub16 x (Sub16 z i:(Const16 <t>))) && (z.Op != OpConst16 && x.Op != OpConst16) => (Add16 i (Sub16 <t> x z))
-(Sub8  x (Sub8  z i:(Const8  <t>))) && (z.Op != OpConst8  && x.Op != OpConst8)  => (Add8  i (Sub8  <t> x z))
+// 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)
+
+// (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))
+(Sub32 (Sub32 i:(Const32 <t>) z) x) && (z.Op != OpConst32 && x.Op != OpConst32) => (Sub32 i (Add32 <t> z x))
+(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))
 
 // 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))
@@ -1856,6 +1864,12 @@
 (Xor16 (Xor16 i:(Const16 <t>) z) x) && (z.Op != OpConst16 && x.Op != OpConst16) => (Xor16 i (Xor16 <t> z x))
 (Xor8  (Xor8  i:(Const8  <t>) z) x) && (z.Op != OpConst8  && x.Op != OpConst8)  => (Xor8  i (Xor8  <t> z x))
 
+// x * (D * z) = D * (x * z)
+(Mul64 (Mul64 i:(Const64 <t>) z) x) && (z.Op != OpConst64 && x.Op != OpConst64) => (Mul64 i (Mul64 <t> x z))
+(Mul32 (Mul32 i:(Const32 <t>) z) x) && (z.Op != OpConst32 && x.Op != OpConst32) => (Mul32 i (Mul32 <t> x z))
+(Mul16 (Mul16 i:(Const16 <t>) z) x) && (z.Op != OpConst16 && x.Op != OpConst16) => (Mul16 i (Mul16 <t> x z))
+(Mul8  (Mul8  i:(Const8  <t>) z) x) && (z.Op != OpConst8  && x.Op != OpConst8)  => (Mul8  i (Mul8  <t> x z))
+
 // C + (D + x) -> (C + D) + x
 (Add64 (Const64 <t> [c]) (Add64 (Const64 <t> [d]) x)) => (Add64 (Const64 <t> [c+d]) x)
 (Add32 (Const32 <t> [c]) (Add32 (Const32 <t> [d]) x)) => (Add32 (Const32 <t> [c+d]) x)
@@ -1868,24 +1882,18 @@
 (Add16 (Const16 <t> [c]) (Sub16 (Const16 <t> [d]) x)) => (Sub16 (Const16 <t> [c+d]) x)
 (Add8  (Const8  <t> [c]) (Sub8  (Const8  <t> [d]) x)) => (Sub8  (Const8  <t> [c+d]) x)
 
-// C + (x - D) -> (C - D) + x
-(Add64 (Const64 <t> [c]) (Sub64 x (Const64 <t> [d]))) => (Add64 (Const64 <t> [c-d]) x)
-(Add32 (Const32 <t> [c]) (Sub32 x (Const32 <t> [d]))) => (Add32 (Const32 <t> [c-d]) x)
-(Add16 (Const16 <t> [c]) (Sub16 x (Const16 <t> [d]))) => (Add16 (Const16 <t> [c-d]) x)
-(Add8  (Const8  <t> [c]) (Sub8  x (Const8  <t> [d]))) => (Add8  (Const8  <t> [c-d]) x)
-
-// C - (x - D) -> (C + D) - x
-(Sub64 (Const64 <t> [c]) (Sub64 x (Const64 <t> [d]))) => (Sub64 (Const64 <t> [c+d]) x)
-(Sub32 (Const32 <t> [c]) (Sub32 x (Const32 <t> [d]))) => (Sub32 (Const32 <t> [c+d]) x)
-(Sub16 (Const16 <t> [c]) (Sub16 x (Const16 <t> [d]))) => (Sub16 (Const16 <t> [c+d]) x)
-(Sub8  (Const8  <t> [c]) (Sub8  x (Const8  <t> [d]))) => (Sub8  (Const8  <t> [c+d]) x)
-
 // C - (D - x) -> (C - D) + x
 (Sub64 (Const64 <t> [c]) (Sub64 (Const64 <t> [d]) x)) => (Add64 (Const64 <t> [c-d]) x)
 (Sub32 (Const32 <t> [c]) (Sub32 (Const32 <t> [d]) x)) => (Add32 (Const32 <t> [c-d]) x)
 (Sub16 (Const16 <t> [c]) (Sub16 (Const16 <t> [d]) x)) => (Add16 (Const16 <t> [c-d]) x)
 (Sub8  (Const8  <t> [c]) (Sub8  (Const8  <t> [d]) x)) => (Add8  (Const8  <t> [c-d]) x)
 
+// C - (D + x) -> (C - D) - x
+(Sub64 (Const64 <t> [c]) (Add64 (Const64 <t> [d]) x)) => (Sub64 (Const64 <t> [c-d]) x)
+(Sub32 (Const32 <t> [c]) (Add32 (Const32 <t> [d]) x)) => (Sub32 (Const32 <t> [c-d]) x)
+(Sub16 (Const16 <t> [c]) (Add16 (Const16 <t> [d]) x)) => (Sub16 (Const16 <t> [c-d]) x)
+(Sub8  (Const8  <t> [c]) (Add8  (Const8  <t> [d]) x)) => (Sub8  (Const8  <t> [c-d]) x)
+
 // C & (D & x) -> (C & D) & x
 (And64 (Const64 <t> [c]) (And64 (Const64 <t> [d]) x)) => (And64 (Const64 <t> [c&d]) x)
 (And32 (Const32 <t> [c]) (And32 (Const32 <t> [d]) x)) => (And32 (Const32 <t> [c&d]) x)
diff --git a/src/cmd/compile/internal/ssa/rewritegeneric.go b/src/cmd/compile/internal/ssa/rewritegeneric.go
index 68e49f4..180e48b 100644
--- a/src/cmd/compile/internal/ssa/rewritegeneric.go
+++ b/src/cmd/compile/internal/ssa/rewritegeneric.go
@@ -588,33 +588,6 @@
 		}
 		break
 	}
-	// match: (Add16 (Sub16 z i:(Const16 <t>)) x)
-	// cond: (z.Op != OpConst16 && x.Op != OpConst16)
-	// result: (Sub16 (Add16 <t> x z) i)
-	for {
-		for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
-			if v_0.Op != OpSub16 {
-				continue
-			}
-			_ = v_0.Args[1]
-			z := v_0.Args[0]
-			i := v_0.Args[1]
-			if i.Op != OpConst16 {
-				continue
-			}
-			t := i.Type
-			x := v_1
-			if !(z.Op != OpConst16 && x.Op != OpConst16) {
-				continue
-			}
-			v.reset(OpSub16)
-			v0 := b.NewValue0(v.Pos, OpAdd16, t)
-			v0.AddArg2(x, z)
-			v.AddArg2(v0, i)
-			return true
-		}
-		break
-	}
 	// match: (Add16 (Const16 <t> [c]) (Add16 (Const16 <t> [d]) x))
 	// result: (Add16 (Const16 <t> [c+d]) x)
 	for {
@@ -671,33 +644,6 @@
 		}
 		break
 	}
-	// match: (Add16 (Const16 <t> [c]) (Sub16 x (Const16 <t> [d])))
-	// result: (Add16 (Const16 <t> [c-d]) x)
-	for {
-		for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
-			if v_0.Op != OpConst16 {
-				continue
-			}
-			t := v_0.Type
-			c := auxIntToInt16(v_0.AuxInt)
-			if v_1.Op != OpSub16 {
-				continue
-			}
-			_ = v_1.Args[1]
-			x := v_1.Args[0]
-			v_1_1 := v_1.Args[1]
-			if v_1_1.Op != OpConst16 || v_1_1.Type != t {
-				continue
-			}
-			d := auxIntToInt16(v_1_1.AuxInt)
-			v.reset(OpAdd16)
-			v0 := b.NewValue0(v.Pos, OpConst16, t)
-			v0.AuxInt = int16ToAuxInt(c - d)
-			v.AddArg2(v0, x)
-			return true
-		}
-		break
-	}
 	return false
 }
 func rewriteValuegeneric_OpAdd32(v *Value) bool {
@@ -841,33 +787,6 @@
 		}
 		break
 	}
-	// match: (Add32 (Sub32 z i:(Const32 <t>)) x)
-	// cond: (z.Op != OpConst32 && x.Op != OpConst32)
-	// result: (Sub32 (Add32 <t> x z) i)
-	for {
-		for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
-			if v_0.Op != OpSub32 {
-				continue
-			}
-			_ = v_0.Args[1]
-			z := v_0.Args[0]
-			i := v_0.Args[1]
-			if i.Op != OpConst32 {
-				continue
-			}
-			t := i.Type
-			x := v_1
-			if !(z.Op != OpConst32 && x.Op != OpConst32) {
-				continue
-			}
-			v.reset(OpSub32)
-			v0 := b.NewValue0(v.Pos, OpAdd32, t)
-			v0.AddArg2(x, z)
-			v.AddArg2(v0, i)
-			return true
-		}
-		break
-	}
 	// match: (Add32 (Const32 <t> [c]) (Add32 (Const32 <t> [d]) x))
 	// result: (Add32 (Const32 <t> [c+d]) x)
 	for {
@@ -924,33 +843,6 @@
 		}
 		break
 	}
-	// match: (Add32 (Const32 <t> [c]) (Sub32 x (Const32 <t> [d])))
-	// result: (Add32 (Const32 <t> [c-d]) x)
-	for {
-		for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
-			if v_0.Op != OpConst32 {
-				continue
-			}
-			t := v_0.Type
-			c := auxIntToInt32(v_0.AuxInt)
-			if v_1.Op != OpSub32 {
-				continue
-			}
-			_ = v_1.Args[1]
-			x := v_1.Args[0]
-			v_1_1 := v_1.Args[1]
-			if v_1_1.Op != OpConst32 || v_1_1.Type != t {
-				continue
-			}
-			d := auxIntToInt32(v_1_1.AuxInt)
-			v.reset(OpAdd32)
-			v0 := b.NewValue0(v.Pos, OpConst32, t)
-			v0.AuxInt = int32ToAuxInt(c - d)
-			v.AddArg2(v0, x)
-			return true
-		}
-		break
-	}
 	return false
 }
 func rewriteValuegeneric_OpAdd32F(v *Value) bool {
@@ -1121,33 +1013,6 @@
 		}
 		break
 	}
-	// match: (Add64 (Sub64 z i:(Const64 <t>)) x)
-	// cond: (z.Op != OpConst64 && x.Op != OpConst64)
-	// result: (Sub64 (Add64 <t> x z) i)
-	for {
-		for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
-			if v_0.Op != OpSub64 {
-				continue
-			}
-			_ = v_0.Args[1]
-			z := v_0.Args[0]
-			i := v_0.Args[1]
-			if i.Op != OpConst64 {
-				continue
-			}
-			t := i.Type
-			x := v_1
-			if !(z.Op != OpConst64 && x.Op != OpConst64) {
-				continue
-			}
-			v.reset(OpSub64)
-			v0 := b.NewValue0(v.Pos, OpAdd64, t)
-			v0.AddArg2(x, z)
-			v.AddArg2(v0, i)
-			return true
-		}
-		break
-	}
 	// match: (Add64 (Const64 <t> [c]) (Add64 (Const64 <t> [d]) x))
 	// result: (Add64 (Const64 <t> [c+d]) x)
 	for {
@@ -1204,33 +1069,6 @@
 		}
 		break
 	}
-	// match: (Add64 (Const64 <t> [c]) (Sub64 x (Const64 <t> [d])))
-	// result: (Add64 (Const64 <t> [c-d]) x)
-	for {
-		for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
-			if v_0.Op != OpConst64 {
-				continue
-			}
-			t := v_0.Type
-			c := auxIntToInt64(v_0.AuxInt)
-			if v_1.Op != OpSub64 {
-				continue
-			}
-			_ = v_1.Args[1]
-			x := v_1.Args[0]
-			v_1_1 := v_1.Args[1]
-			if v_1_1.Op != OpConst64 || v_1_1.Type != t {
-				continue
-			}
-			d := auxIntToInt64(v_1_1.AuxInt)
-			v.reset(OpAdd64)
-			v0 := b.NewValue0(v.Pos, OpConst64, t)
-			v0.AuxInt = int64ToAuxInt(c - d)
-			v.AddArg2(v0, x)
-			return true
-		}
-		break
-	}
 	return false
 }
 func rewriteValuegeneric_OpAdd64F(v *Value) bool {
@@ -1401,33 +1239,6 @@
 		}
 		break
 	}
-	// match: (Add8 (Sub8 z i:(Const8 <t>)) x)
-	// cond: (z.Op != OpConst8 && x.Op != OpConst8)
-	// result: (Sub8 (Add8 <t> x z) i)
-	for {
-		for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
-			if v_0.Op != OpSub8 {
-				continue
-			}
-			_ = v_0.Args[1]
-			z := v_0.Args[0]
-			i := v_0.Args[1]
-			if i.Op != OpConst8 {
-				continue
-			}
-			t := i.Type
-			x := v_1
-			if !(z.Op != OpConst8 && x.Op != OpConst8) {
-				continue
-			}
-			v.reset(OpSub8)
-			v0 := b.NewValue0(v.Pos, OpAdd8, t)
-			v0.AddArg2(x, z)
-			v.AddArg2(v0, i)
-			return true
-		}
-		break
-	}
 	// match: (Add8 (Const8 <t> [c]) (Add8 (Const8 <t> [d]) x))
 	// result: (Add8 (Const8 <t> [c+d]) x)
 	for {
@@ -1484,33 +1295,6 @@
 		}
 		break
 	}
-	// match: (Add8 (Const8 <t> [c]) (Sub8 x (Const8 <t> [d])))
-	// result: (Add8 (Const8 <t> [c-d]) x)
-	for {
-		for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
-			if v_0.Op != OpConst8 {
-				continue
-			}
-			t := v_0.Type
-			c := auxIntToInt8(v_0.AuxInt)
-			if v_1.Op != OpSub8 {
-				continue
-			}
-			_ = v_1.Args[1]
-			x := v_1.Args[0]
-			v_1_1 := v_1.Args[1]
-			if v_1_1.Op != OpConst8 || v_1_1.Type != t {
-				continue
-			}
-			d := auxIntToInt8(v_1_1.AuxInt)
-			v.reset(OpAdd8)
-			v0 := b.NewValue0(v.Pos, OpConst8, t)
-			v0.AuxInt = int8ToAuxInt(c - d)
-			v.AddArg2(v0, x)
-			return true
-		}
-		break
-	}
 	return false
 }
 func rewriteValuegeneric_OpAddPtr(v *Value) bool {
@@ -13922,6 +13706,37 @@
 		}
 		break
 	}
+	// match: (Mul16 (Mul16 i:(Const16 <t>) z) x)
+	// cond: (z.Op != OpConst16 && x.Op != OpConst16)
+	// result: (Mul16 i (Mul16 <t> x z))
+	for {
+		for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+			if v_0.Op != OpMul16 {
+				continue
+			}
+			_ = v_0.Args[1]
+			v_0_0 := v_0.Args[0]
+			v_0_1 := v_0.Args[1]
+			for _i1 := 0; _i1 <= 1; _i1, v_0_0, v_0_1 = _i1+1, v_0_1, v_0_0 {
+				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
+				}
+				v.reset(OpMul16)
+				v0 := b.NewValue0(v.Pos, OpMul16, t)
+				v0.AddArg2(x, z)
+				v.AddArg2(i, v0)
+				return true
+			}
+		}
+		break
+	}
 	// match: (Mul16 (Const16 <t> [c]) (Mul16 (Const16 <t> [d]) x))
 	// result: (Mul16 (Const16 <t> [c*d]) x)
 	for {
@@ -14098,6 +13913,37 @@
 		}
 		break
 	}
+	// match: (Mul32 (Mul32 i:(Const32 <t>) z) x)
+	// cond: (z.Op != OpConst32 && x.Op != OpConst32)
+	// result: (Mul32 i (Mul32 <t> x z))
+	for {
+		for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+			if v_0.Op != OpMul32 {
+				continue
+			}
+			_ = v_0.Args[1]
+			v_0_0 := v_0.Args[0]
+			v_0_1 := v_0.Args[1]
+			for _i1 := 0; _i1 <= 1; _i1, v_0_0, v_0_1 = _i1+1, v_0_1, v_0_0 {
+				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
+				}
+				v.reset(OpMul32)
+				v0 := b.NewValue0(v.Pos, OpMul32, t)
+				v0.AddArg2(x, z)
+				v.AddArg2(i, v0)
+				return true
+			}
+		}
+		break
+	}
 	// match: (Mul32 (Const32 <t> [c]) (Mul32 (Const32 <t> [d]) x))
 	// result: (Mul32 (Const32 <t> [c*d]) x)
 	for {
@@ -14342,6 +14188,37 @@
 		}
 		break
 	}
+	// match: (Mul64 (Mul64 i:(Const64 <t>) z) x)
+	// cond: (z.Op != OpConst64 && x.Op != OpConst64)
+	// result: (Mul64 i (Mul64 <t> x z))
+	for {
+		for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+			if v_0.Op != OpMul64 {
+				continue
+			}
+			_ = v_0.Args[1]
+			v_0_0 := v_0.Args[0]
+			v_0_1 := v_0.Args[1]
+			for _i1 := 0; _i1 <= 1; _i1, v_0_0, v_0_1 = _i1+1, v_0_1, v_0_0 {
+				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
+				}
+				v.reset(OpMul64)
+				v0 := b.NewValue0(v.Pos, OpMul64, t)
+				v0.AddArg2(x, z)
+				v.AddArg2(i, v0)
+				return true
+			}
+		}
+		break
+	}
 	// match: (Mul64 (Const64 <t> [c]) (Mul64 (Const64 <t> [d]) x))
 	// result: (Mul64 (Const64 <t> [c*d]) x)
 	for {
@@ -14552,6 +14429,37 @@
 		}
 		break
 	}
+	// match: (Mul8 (Mul8 i:(Const8 <t>) z) x)
+	// cond: (z.Op != OpConst8 && x.Op != OpConst8)
+	// result: (Mul8 i (Mul8 <t> x z))
+	for {
+		for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+			if v_0.Op != OpMul8 {
+				continue
+			}
+			_ = v_0.Args[1]
+			v_0_0 := v_0.Args[0]
+			v_0_1 := v_0.Args[1]
+			for _i1 := 0; _i1 <= 1; _i1, v_0_0, v_0_1 = _i1+1, v_0_1, v_0_0 {
+				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
+				}
+				v.reset(OpMul8)
+				v0 := b.NewValue0(v.Pos, OpMul8, t)
+				v0.AddArg2(x, z)
+				v.AddArg2(i, v0)
+				return true
+			}
+		}
+		break
+	}
 	// match: (Mul8 (Const8 <t> [c]) (Mul8 (Const8 <t> [d]) x))
 	// result: (Mul8 (Const8 <t> [c*d]) x)
 	for {
@@ -22432,53 +22340,86 @@
 		v.AddArg2(v0, i)
 		return true
 	}
-	// match: (Sub16 x (Sub16 z i:(Const16 <t>)))
+	// match: (Sub16 x (Add16 z i:(Const16 <t>)))
 	// cond: (z.Op != OpConst16 && x.Op != OpConst16)
-	// result: (Add16 i (Sub16 <t> x z))
+	// result: (Sub16 (Sub16 <t> x z) i)
 	for {
 		x := v_0
-		if v_1.Op != OpSub16 {
+		if v_1.Op != OpAdd16 {
 			break
 		}
 		_ = v_1.Args[1]
-		z := v_1.Args[0]
-		i := v_1.Args[1]
+		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
+			if i.Op != OpConst16 {
+				continue
+			}
+			t := i.Type
+			if !(z.Op != OpConst16 && x.Op != OpConst16) {
+				continue
+			}
+			v.reset(OpSub16)
+			v0 := b.NewValue0(v.Pos, OpSub16, t)
+			v0.AddArg2(x, z)
+			v.AddArg2(v0, i)
+			return true
+		}
+		break
+	}
+	// match: (Sub16 (Sub16 i:(Const16 <t>) z) x)
+	// cond: (z.Op != OpConst16 && x.Op != OpConst16)
+	// result: (Sub16 i (Add16 <t> z x))
+	for {
+		if v_0.Op != OpSub16 {
+			break
+		}
+		z := v_0.Args[1]
+		i := v_0.Args[0]
 		if i.Op != OpConst16 {
 			break
 		}
 		t := i.Type
+		x := v_1
 		if !(z.Op != OpConst16 && x.Op != OpConst16) {
 			break
 		}
-		v.reset(OpAdd16)
-		v0 := b.NewValue0(v.Pos, OpSub16, t)
-		v0.AddArg2(x, z)
+		v.reset(OpSub16)
+		v0 := b.NewValue0(v.Pos, OpAdd16, t)
+		v0.AddArg2(z, x)
 		v.AddArg2(i, v0)
 		return true
 	}
-	// match: (Sub16 (Const16 <t> [c]) (Sub16 x (Const16 <t> [d])))
-	// result: (Sub16 (Const16 <t> [c+d]) x)
+	// match: (Sub16 (Add16 z i:(Const16 <t>)) x)
+	// cond: (z.Op != OpConst16 && x.Op != OpConst16)
+	// result: (Add16 i (Sub16 <t> z x))
 	for {
-		if v_0.Op != OpConst16 {
+		if v_0.Op != OpAdd16 {
 			break
 		}
-		t := v_0.Type
-		c := auxIntToInt16(v_0.AuxInt)
-		if v_1.Op != OpSub16 {
-			break
+		_ = v_0.Args[1]
+		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
+			if i.Op != OpConst16 {
+				continue
+			}
+			t := i.Type
+			x := v_1
+			if !(z.Op != OpConst16 && x.Op != OpConst16) {
+				continue
+			}
+			v.reset(OpAdd16)
+			v0 := b.NewValue0(v.Pos, OpSub16, t)
+			v0.AddArg2(z, x)
+			v.AddArg2(i, v0)
+			return true
 		}
-		_ = v_1.Args[1]
-		x := v_1.Args[0]
-		v_1_1 := v_1.Args[1]
-		if v_1_1.Op != OpConst16 || v_1_1.Type != t {
-			break
-		}
-		d := auxIntToInt16(v_1_1.AuxInt)
-		v.reset(OpSub16)
-		v0 := b.NewValue0(v.Pos, OpConst16, t)
-		v0.AuxInt = int16ToAuxInt(c + d)
-		v.AddArg2(v0, x)
-		return true
+		break
 	}
 	// match: (Sub16 (Const16 <t> [c]) (Sub16 (Const16 <t> [d]) x))
 	// result: (Add16 (Const16 <t> [c-d]) x)
@@ -22503,6 +22444,34 @@
 		v.AddArg2(v0, x)
 		return true
 	}
+	// match: (Sub16 (Const16 <t> [c]) (Add16 (Const16 <t> [d]) x))
+	// result: (Sub16 (Const16 <t> [c-d]) x)
+	for {
+		if v_0.Op != OpConst16 {
+			break
+		}
+		t := v_0.Type
+		c := auxIntToInt16(v_0.AuxInt)
+		if v_1.Op != OpAdd16 {
+			break
+		}
+		_ = v_1.Args[1]
+		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 {
+			if v_1_0.Op != OpConst16 || v_1_0.Type != t {
+				continue
+			}
+			d := auxIntToInt16(v_1_0.AuxInt)
+			x := v_1_1
+			v.reset(OpSub16)
+			v0 := b.NewValue0(v.Pos, OpConst16, t)
+			v0.AuxInt = int16ToAuxInt(c - d)
+			v.AddArg2(v0, x)
+			return true
+		}
+		break
+	}
 	return false
 }
 func rewriteValuegeneric_OpSub32(v *Value) bool {
@@ -22650,53 +22619,86 @@
 		v.AddArg2(v0, i)
 		return true
 	}
-	// match: (Sub32 x (Sub32 z i:(Const32 <t>)))
+	// match: (Sub32 x (Add32 z i:(Const32 <t>)))
 	// cond: (z.Op != OpConst32 && x.Op != OpConst32)
-	// result: (Add32 i (Sub32 <t> x z))
+	// result: (Sub32 (Sub32 <t> x z) i)
 	for {
 		x := v_0
-		if v_1.Op != OpSub32 {
+		if v_1.Op != OpAdd32 {
 			break
 		}
 		_ = v_1.Args[1]
-		z := v_1.Args[0]
-		i := v_1.Args[1]
+		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
+			if i.Op != OpConst32 {
+				continue
+			}
+			t := i.Type
+			if !(z.Op != OpConst32 && x.Op != OpConst32) {
+				continue
+			}
+			v.reset(OpSub32)
+			v0 := b.NewValue0(v.Pos, OpSub32, t)
+			v0.AddArg2(x, z)
+			v.AddArg2(v0, i)
+			return true
+		}
+		break
+	}
+	// match: (Sub32 (Sub32 i:(Const32 <t>) z) x)
+	// cond: (z.Op != OpConst32 && x.Op != OpConst32)
+	// result: (Sub32 i (Add32 <t> z x))
+	for {
+		if v_0.Op != OpSub32 {
+			break
+		}
+		z := v_0.Args[1]
+		i := v_0.Args[0]
 		if i.Op != OpConst32 {
 			break
 		}
 		t := i.Type
+		x := v_1
 		if !(z.Op != OpConst32 && x.Op != OpConst32) {
 			break
 		}
-		v.reset(OpAdd32)
-		v0 := b.NewValue0(v.Pos, OpSub32, t)
-		v0.AddArg2(x, z)
+		v.reset(OpSub32)
+		v0 := b.NewValue0(v.Pos, OpAdd32, t)
+		v0.AddArg2(z, x)
 		v.AddArg2(i, v0)
 		return true
 	}
-	// match: (Sub32 (Const32 <t> [c]) (Sub32 x (Const32 <t> [d])))
-	// result: (Sub32 (Const32 <t> [c+d]) x)
+	// match: (Sub32 (Add32 z i:(Const32 <t>)) x)
+	// cond: (z.Op != OpConst32 && x.Op != OpConst32)
+	// result: (Add32 i (Sub32 <t> z x))
 	for {
-		if v_0.Op != OpConst32 {
+		if v_0.Op != OpAdd32 {
 			break
 		}
-		t := v_0.Type
-		c := auxIntToInt32(v_0.AuxInt)
-		if v_1.Op != OpSub32 {
-			break
+		_ = v_0.Args[1]
+		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
+			if i.Op != OpConst32 {
+				continue
+			}
+			t := i.Type
+			x := v_1
+			if !(z.Op != OpConst32 && x.Op != OpConst32) {
+				continue
+			}
+			v.reset(OpAdd32)
+			v0 := b.NewValue0(v.Pos, OpSub32, t)
+			v0.AddArg2(z, x)
+			v.AddArg2(i, v0)
+			return true
 		}
-		_ = v_1.Args[1]
-		x := v_1.Args[0]
-		v_1_1 := v_1.Args[1]
-		if v_1_1.Op != OpConst32 || v_1_1.Type != t {
-			break
-		}
-		d := auxIntToInt32(v_1_1.AuxInt)
-		v.reset(OpSub32)
-		v0 := b.NewValue0(v.Pos, OpConst32, t)
-		v0.AuxInt = int32ToAuxInt(c + d)
-		v.AddArg2(v0, x)
-		return true
+		break
 	}
 	// match: (Sub32 (Const32 <t> [c]) (Sub32 (Const32 <t> [d]) x))
 	// result: (Add32 (Const32 <t> [c-d]) x)
@@ -22721,6 +22723,34 @@
 		v.AddArg2(v0, x)
 		return true
 	}
+	// match: (Sub32 (Const32 <t> [c]) (Add32 (Const32 <t> [d]) x))
+	// result: (Sub32 (Const32 <t> [c-d]) x)
+	for {
+		if v_0.Op != OpConst32 {
+			break
+		}
+		t := v_0.Type
+		c := auxIntToInt32(v_0.AuxInt)
+		if v_1.Op != OpAdd32 {
+			break
+		}
+		_ = v_1.Args[1]
+		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 {
+			if v_1_0.Op != OpConst32 || v_1_0.Type != t {
+				continue
+			}
+			d := auxIntToInt32(v_1_0.AuxInt)
+			x := v_1_1
+			v.reset(OpSub32)
+			v0 := b.NewValue0(v.Pos, OpConst32, t)
+			v0.AuxInt = int32ToAuxInt(c - d)
+			v.AddArg2(v0, x)
+			return true
+		}
+		break
+	}
 	return false
 }
 func rewriteValuegeneric_OpSub32F(v *Value) bool {
@@ -22892,53 +22922,86 @@
 		v.AddArg2(v0, i)
 		return true
 	}
-	// match: (Sub64 x (Sub64 z i:(Const64 <t>)))
+	// match: (Sub64 x (Add64 z i:(Const64 <t>)))
 	// cond: (z.Op != OpConst64 && x.Op != OpConst64)
-	// result: (Add64 i (Sub64 <t> x z))
+	// result: (Sub64 (Sub64 <t> x z) i)
 	for {
 		x := v_0
-		if v_1.Op != OpSub64 {
+		if v_1.Op != OpAdd64 {
 			break
 		}
 		_ = v_1.Args[1]
-		z := v_1.Args[0]
-		i := v_1.Args[1]
+		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
+			if i.Op != OpConst64 {
+				continue
+			}
+			t := i.Type
+			if !(z.Op != OpConst64 && x.Op != OpConst64) {
+				continue
+			}
+			v.reset(OpSub64)
+			v0 := b.NewValue0(v.Pos, OpSub64, t)
+			v0.AddArg2(x, z)
+			v.AddArg2(v0, i)
+			return true
+		}
+		break
+	}
+	// match: (Sub64 (Sub64 i:(Const64 <t>) z) x)
+	// cond: (z.Op != OpConst64 && x.Op != OpConst64)
+	// result: (Sub64 i (Add64 <t> z x))
+	for {
+		if v_0.Op != OpSub64 {
+			break
+		}
+		z := v_0.Args[1]
+		i := v_0.Args[0]
 		if i.Op != OpConst64 {
 			break
 		}
 		t := i.Type
+		x := v_1
 		if !(z.Op != OpConst64 && x.Op != OpConst64) {
 			break
 		}
-		v.reset(OpAdd64)
-		v0 := b.NewValue0(v.Pos, OpSub64, t)
-		v0.AddArg2(x, z)
+		v.reset(OpSub64)
+		v0 := b.NewValue0(v.Pos, OpAdd64, t)
+		v0.AddArg2(z, x)
 		v.AddArg2(i, v0)
 		return true
 	}
-	// match: (Sub64 (Const64 <t> [c]) (Sub64 x (Const64 <t> [d])))
-	// result: (Sub64 (Const64 <t> [c+d]) x)
+	// match: (Sub64 (Add64 z i:(Const64 <t>)) x)
+	// cond: (z.Op != OpConst64 && x.Op != OpConst64)
+	// result: (Add64 i (Sub64 <t> z x))
 	for {
-		if v_0.Op != OpConst64 {
+		if v_0.Op != OpAdd64 {
 			break
 		}
-		t := v_0.Type
-		c := auxIntToInt64(v_0.AuxInt)
-		if v_1.Op != OpSub64 {
-			break
+		_ = v_0.Args[1]
+		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
+			if i.Op != OpConst64 {
+				continue
+			}
+			t := i.Type
+			x := v_1
+			if !(z.Op != OpConst64 && x.Op != OpConst64) {
+				continue
+			}
+			v.reset(OpAdd64)
+			v0 := b.NewValue0(v.Pos, OpSub64, t)
+			v0.AddArg2(z, x)
+			v.AddArg2(i, v0)
+			return true
 		}
-		_ = v_1.Args[1]
-		x := v_1.Args[0]
-		v_1_1 := v_1.Args[1]
-		if v_1_1.Op != OpConst64 || v_1_1.Type != t {
-			break
-		}
-		d := auxIntToInt64(v_1_1.AuxInt)
-		v.reset(OpSub64)
-		v0 := b.NewValue0(v.Pos, OpConst64, t)
-		v0.AuxInt = int64ToAuxInt(c + d)
-		v.AddArg2(v0, x)
-		return true
+		break
 	}
 	// match: (Sub64 (Const64 <t> [c]) (Sub64 (Const64 <t> [d]) x))
 	// result: (Add64 (Const64 <t> [c-d]) x)
@@ -22963,6 +23026,34 @@
 		v.AddArg2(v0, x)
 		return true
 	}
+	// match: (Sub64 (Const64 <t> [c]) (Add64 (Const64 <t> [d]) x))
+	// result: (Sub64 (Const64 <t> [c-d]) x)
+	for {
+		if v_0.Op != OpConst64 {
+			break
+		}
+		t := v_0.Type
+		c := auxIntToInt64(v_0.AuxInt)
+		if v_1.Op != OpAdd64 {
+			break
+		}
+		_ = v_1.Args[1]
+		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 {
+			if v_1_0.Op != OpConst64 || v_1_0.Type != t {
+				continue
+			}
+			d := auxIntToInt64(v_1_0.AuxInt)
+			x := v_1_1
+			v.reset(OpSub64)
+			v0 := b.NewValue0(v.Pos, OpConst64, t)
+			v0.AuxInt = int64ToAuxInt(c - d)
+			v.AddArg2(v0, x)
+			return true
+		}
+		break
+	}
 	return false
 }
 func rewriteValuegeneric_OpSub64F(v *Value) bool {
@@ -23134,53 +23225,86 @@
 		v.AddArg2(v0, i)
 		return true
 	}
-	// match: (Sub8 x (Sub8 z i:(Const8 <t>)))
+	// match: (Sub8 x (Add8 z i:(Const8 <t>)))
 	// cond: (z.Op != OpConst8 && x.Op != OpConst8)
-	// result: (Add8 i (Sub8 <t> x z))
+	// result: (Sub8 (Sub8 <t> x z) i)
 	for {
 		x := v_0
-		if v_1.Op != OpSub8 {
+		if v_1.Op != OpAdd8 {
 			break
 		}
 		_ = v_1.Args[1]
-		z := v_1.Args[0]
-		i := v_1.Args[1]
+		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
+			if i.Op != OpConst8 {
+				continue
+			}
+			t := i.Type
+			if !(z.Op != OpConst8 && x.Op != OpConst8) {
+				continue
+			}
+			v.reset(OpSub8)
+			v0 := b.NewValue0(v.Pos, OpSub8, t)
+			v0.AddArg2(x, z)
+			v.AddArg2(v0, i)
+			return true
+		}
+		break
+	}
+	// match: (Sub8 (Sub8 i:(Const8 <t>) z) x)
+	// cond: (z.Op != OpConst8 && x.Op != OpConst8)
+	// result: (Sub8 i (Add8 <t> z x))
+	for {
+		if v_0.Op != OpSub8 {
+			break
+		}
+		z := v_0.Args[1]
+		i := v_0.Args[0]
 		if i.Op != OpConst8 {
 			break
 		}
 		t := i.Type
+		x := v_1
 		if !(z.Op != OpConst8 && x.Op != OpConst8) {
 			break
 		}
-		v.reset(OpAdd8)
-		v0 := b.NewValue0(v.Pos, OpSub8, t)
-		v0.AddArg2(x, z)
+		v.reset(OpSub8)
+		v0 := b.NewValue0(v.Pos, OpAdd8, t)
+		v0.AddArg2(z, x)
 		v.AddArg2(i, v0)
 		return true
 	}
-	// match: (Sub8 (Const8 <t> [c]) (Sub8 x (Const8 <t> [d])))
-	// result: (Sub8 (Const8 <t> [c+d]) x)
+	// match: (Sub8 (Add8 z i:(Const8 <t>)) x)
+	// cond: (z.Op != OpConst8 && x.Op != OpConst8)
+	// result: (Add8 i (Sub8 <t> z x))
 	for {
-		if v_0.Op != OpConst8 {
+		if v_0.Op != OpAdd8 {
 			break
 		}
-		t := v_0.Type
-		c := auxIntToInt8(v_0.AuxInt)
-		if v_1.Op != OpSub8 {
-			break
+		_ = v_0.Args[1]
+		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
+			if i.Op != OpConst8 {
+				continue
+			}
+			t := i.Type
+			x := v_1
+			if !(z.Op != OpConst8 && x.Op != OpConst8) {
+				continue
+			}
+			v.reset(OpAdd8)
+			v0 := b.NewValue0(v.Pos, OpSub8, t)
+			v0.AddArg2(z, x)
+			v.AddArg2(i, v0)
+			return true
 		}
-		_ = v_1.Args[1]
-		x := v_1.Args[0]
-		v_1_1 := v_1.Args[1]
-		if v_1_1.Op != OpConst8 || v_1_1.Type != t {
-			break
-		}
-		d := auxIntToInt8(v_1_1.AuxInt)
-		v.reset(OpSub8)
-		v0 := b.NewValue0(v.Pos, OpConst8, t)
-		v0.AuxInt = int8ToAuxInt(c + d)
-		v.AddArg2(v0, x)
-		return true
+		break
 	}
 	// match: (Sub8 (Const8 <t> [c]) (Sub8 (Const8 <t> [d]) x))
 	// result: (Add8 (Const8 <t> [c-d]) x)
@@ -23205,6 +23329,34 @@
 		v.AddArg2(v0, x)
 		return true
 	}
+	// match: (Sub8 (Const8 <t> [c]) (Add8 (Const8 <t> [d]) x))
+	// result: (Sub8 (Const8 <t> [c-d]) x)
+	for {
+		if v_0.Op != OpConst8 {
+			break
+		}
+		t := v_0.Type
+		c := auxIntToInt8(v_0.AuxInt)
+		if v_1.Op != OpAdd8 {
+			break
+		}
+		_ = v_1.Args[1]
+		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 {
+			if v_1_0.Op != OpConst8 || v_1_0.Type != t {
+				continue
+			}
+			d := auxIntToInt8(v_1_0.AuxInt)
+			x := v_1_1
+			v.reset(OpSub8)
+			v0 := b.NewValue0(v.Pos, OpConst8, t)
+			v0.AuxInt = int8ToAuxInt(c - d)
+			v.AddArg2(v0, x)
+			return true
+		}
+		break
+	}
 	return false
 }
 func rewriteValuegeneric_OpTrunc16to8(v *Value) bool {
diff --git a/test/codegen/arithmetic.go b/test/codegen/arithmetic.go
index 45fdb68..afd4d66 100644
--- a/test/codegen/arithmetic.go
+++ b/test/codegen/arithmetic.go
@@ -462,7 +462,6 @@
 	return a, b, c
 }
 
-
 // Divide -> shift rules usually require fixup for negative inputs.
 // If the input is non-negative, make sure the fixup is eliminated.
 func divInt(v int64) int64 {
@@ -472,3 +471,33 @@
 	// amd64:-`.*SARQ.*63,`, -".*SHRQ", ".*SARQ.*[$]9,"
 	return v / 512
 }
+
+// The reassociate rules "x - (z + C) -> (x - z) - C" and
+// "(z + C) -x -> C + (z - x)" can optimize the following cases.
+func constantFold1(i0, j0, i1, j1, i2, j2, i3, j3 int) (int, int, int, int) {
+	// arm64:"SUB","ADD\t[$]2"
+	r0 := (i0 + 3) - (j0 + 1)
+	// arm64:"SUB","SUB\t[$]4"
+	r1 := (i1 - 3) - (j1 + 1)
+	// arm64:"SUB","ADD\t[$]4"
+	r2 := (i2 + 3) - (j2 - 1)
+	// arm64:"SUB","SUB\t[$]2"
+	r3 := (i3 - 3) - (j3 - 1)
+	return r0, r1, r2, r3
+}
+
+// The reassociate rules "x - (z + C) -> (x - z) - C" and
+// "(C - z) - x -> C - (z + x)" can optimize the following cases.
+func constantFold2(i0, j0, i1, j1 int) (int, int) {
+	// arm64:"ADD","MOVD\t[$]2","SUB"
+	r0 := (3 - i0) - (j0 + 1)
+	// arm64:"ADD","MOVD\t[$]4","SUB"
+	r1 := (3 - i1) - (j1 - 1)
+	return r0, r1
+}
+
+func constantFold3(i, j int) int {
+	// arm64: "MOVD\t[$]30","MUL",-"ADD",-"LSL"
+	r := (5 * i) * (6 * j)
+	return r
+}