From e7d47ac33d71666863509334192a03da90d4acab Mon Sep 17 00:00:00 2001 From: Meng Zhuo Date: Tue, 18 Nov 2025 09:53:21 +0800 Subject: [PATCH] cmd/compile: simplify negative on multiplication MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit goos: linux goarch: amd64 pkg: cmd/compile/internal/test cpu: AMD EPYC 7532 32-Core Processor │ simplify_base │ simplify_new │ │ sec/op │ sec/op vs base │ SimplifyNegMul 623.0n ± 0% 319.3n ± 1% -48.75% (p=0.000 n=10) goos: linux goarch: riscv64 pkg: cmd/compile/internal/test cpu: Spacemit(R) X60 │ simplify.base │ simplify.new │ │ sec/op │ sec/op vs base │ SimplifyNegMul 10.928µ ± 0% 6.432µ ± 0% -41.14% (p=0.000 n=10) Change-Id: I1d9393cd19a0b948a5d3a512d627cdc0cf0b38be Reviewed-on: https://go-review.googlesource.com/c/go/+/721520 Reviewed-by: Keith Randall LUCI-TryBot-Result: Go LUCI Reviewed-by: Mark Freeman Auto-Submit: Keith Randall Reviewed-by: Keith Randall --- .../compile/internal/ssa/_gen/generic.rules | 4 + .../compile/internal/ssa/rewritegeneric.go | 180 ++++++++++++++++++ src/cmd/compile/internal/test/bench_test.go | 29 +++ test/codegen/arithmetic.go | 15 ++ 4 files changed, 228 insertions(+) diff --git a/src/cmd/compile/internal/ssa/_gen/generic.rules b/src/cmd/compile/internal/ssa/_gen/generic.rules index 372e89863d..e440a5d1ee 100644 --- a/src/cmd/compile/internal/ssa/_gen/generic.rules +++ b/src/cmd/compile/internal/ssa/_gen/generic.rules @@ -200,6 +200,10 @@ (Mul(8|16|32|64) (Neg(8|16|32|64) x) (Neg(8|16|32|64) y)) => (Mul(8|16|32|64) x y) +// simplify negative on mul if possible +(Neg(8|16|32|64) (Mul(8|16|32|64) x (Const(8|16|32|64) [c]))) => (Mul(8|16|32|64) x (Const(8|16|32|64) [-c])) +(Neg(8|16|32|64) (Mul(8|16|32|64) x (Neg(8|16|32|64) y))) => (Mul(8|16|32|64) x y) + // DeMorgan's Laws (And(8|16|32|64) (Com(8|16|32|64) x) (Com(8|16|32|64) y)) => (Com(8|16|32|64) (Or(8|16|32|64) x y)) (Or(8|16|32|64) (Com(8|16|32|64) x) (Com(8|16|32|64) y)) => (Com(8|16|32|64) (And(8|16|32|64) x y)) diff --git a/src/cmd/compile/internal/ssa/rewritegeneric.go b/src/cmd/compile/internal/ssa/rewritegeneric.go index f0560671ac..35dee611fc 100644 --- a/src/cmd/compile/internal/ssa/rewritegeneric.go +++ b/src/cmd/compile/internal/ssa/rewritegeneric.go @@ -18069,6 +18069,51 @@ func rewriteValuegeneric_OpNeg16(v *Value) bool { v.AuxInt = int16ToAuxInt(-c) return true } + // match: (Neg16 (Mul16 x (Const16 [c]))) + // result: (Mul16 x (Const16 [-c])) + for { + if v_0.Op != OpMul16 { + 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 { + x := v_0_0 + if v_0_1.Op != OpConst16 { + continue + } + t := v_0_1.Type + c := auxIntToInt16(v_0_1.AuxInt) + v.reset(OpMul16) + v0 := b.NewValue0(v.Pos, OpConst16, t) + v0.AuxInt = int16ToAuxInt(-c) + v.AddArg2(x, v0) + return true + } + break + } + // match: (Neg16 (Mul16 x (Neg16 y))) + // result: (Mul16 x y) + for { + if v_0.Op != OpMul16 { + 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 { + x := v_0_0 + if v_0_1.Op != OpNeg16 { + continue + } + y := v_0_1.Args[0] + v.reset(OpMul16) + v.AddArg2(x, y) + return true + } + break + } // match: (Neg16 (Sub16 x y)) // result: (Sub16 y x) for { @@ -18121,6 +18166,51 @@ func rewriteValuegeneric_OpNeg32(v *Value) bool { v.AuxInt = int32ToAuxInt(-c) return true } + // match: (Neg32 (Mul32 x (Const32 [c]))) + // result: (Mul32 x (Const32 [-c])) + for { + if v_0.Op != OpMul32 { + 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 { + x := v_0_0 + if v_0_1.Op != OpConst32 { + continue + } + t := v_0_1.Type + c := auxIntToInt32(v_0_1.AuxInt) + v.reset(OpMul32) + v0 := b.NewValue0(v.Pos, OpConst32, t) + v0.AuxInt = int32ToAuxInt(-c) + v.AddArg2(x, v0) + return true + } + break + } + // match: (Neg32 (Mul32 x (Neg32 y))) + // result: (Mul32 x y) + for { + if v_0.Op != OpMul32 { + 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 { + x := v_0_0 + if v_0_1.Op != OpNeg32 { + continue + } + y := v_0_1.Args[0] + v.reset(OpMul32) + v.AddArg2(x, y) + return true + } + break + } // match: (Neg32 (Sub32 x y)) // result: (Sub32 y x) for { @@ -18192,6 +18282,51 @@ func rewriteValuegeneric_OpNeg64(v *Value) bool { v.AuxInt = int64ToAuxInt(-c) return true } + // match: (Neg64 (Mul64 x (Const64 [c]))) + // result: (Mul64 x (Const64 [-c])) + for { + if v_0.Op != OpMul64 { + 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 { + x := v_0_0 + if v_0_1.Op != OpConst64 { + continue + } + t := v_0_1.Type + c := auxIntToInt64(v_0_1.AuxInt) + v.reset(OpMul64) + v0 := b.NewValue0(v.Pos, OpConst64, t) + v0.AuxInt = int64ToAuxInt(-c) + v.AddArg2(x, v0) + return true + } + break + } + // match: (Neg64 (Mul64 x (Neg64 y))) + // result: (Mul64 x y) + for { + if v_0.Op != OpMul64 { + 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 { + x := v_0_0 + if v_0_1.Op != OpNeg64 { + continue + } + y := v_0_1.Args[0] + v.reset(OpMul64) + v.AddArg2(x, y) + return true + } + break + } // match: (Neg64 (Sub64 x y)) // result: (Sub64 y x) for { @@ -18263,6 +18398,51 @@ func rewriteValuegeneric_OpNeg8(v *Value) bool { v.AuxInt = int8ToAuxInt(-c) return true } + // match: (Neg8 (Mul8 x (Const8 [c]))) + // result: (Mul8 x (Const8 [-c])) + for { + if v_0.Op != OpMul8 { + 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 { + x := v_0_0 + if v_0_1.Op != OpConst8 { + continue + } + t := v_0_1.Type + c := auxIntToInt8(v_0_1.AuxInt) + v.reset(OpMul8) + v0 := b.NewValue0(v.Pos, OpConst8, t) + v0.AuxInt = int8ToAuxInt(-c) + v.AddArg2(x, v0) + return true + } + break + } + // match: (Neg8 (Mul8 x (Neg8 y))) + // result: (Mul8 x y) + for { + if v_0.Op != OpMul8 { + 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 { + x := v_0_0 + if v_0_1.Op != OpNeg8 { + continue + } + y := v_0_1.Args[0] + v.reset(OpMul8) + v.AddArg2(x, y) + return true + } + break + } // match: (Neg8 (Sub8 x y)) // result: (Sub8 y x) for { diff --git a/src/cmd/compile/internal/test/bench_test.go b/src/cmd/compile/internal/test/bench_test.go index 7303f672fe..694673a8f6 100644 --- a/src/cmd/compile/internal/test/bench_test.go +++ b/src/cmd/compile/internal/test/bench_test.go @@ -145,3 +145,32 @@ func BenchmarkMul2Neg(b *testing.B) { globl = s } } + +func BenchmarkSimplifyNegMul(b *testing.B) { + x := make([]int64, 1024) + y := make([]int64, 1024) + b.ResetTimer() + for i := 0; i < b.N; i++ { + var s int64 + for i := range x { + s = -(-x[i] * y[i]) + } + globl = s + } +} + +func BenchmarkSimplifyNegDiv(b *testing.B) { + x := make([]int64, 1024) + y := make([]int64, 1024) + for i := range y { + y[i] = 42 + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + var s int64 + for i := range x { + s = -(-x[i] / y[i]) + } + globl = s + } +} diff --git a/test/codegen/arithmetic.go b/test/codegen/arithmetic.go index 9443d812dc..29764bf817 100644 --- a/test/codegen/arithmetic.go +++ b/test/codegen/arithmetic.go @@ -337,11 +337,26 @@ func Mul32(a, b int32) int64 { // arm64:"SMULL" -"MOVW" return int64(a) * int64(b) } + func Mul32U(a, b uint32) uint64 { // arm64:"UMULL" -"MOVWU" return uint64(a) * uint64(b) } +func SimplifyNegMulConst(a int) int { + // amd64:-"NEGQ" + // arm64:"MOVD [$]11" "MUL" -"NEG" + // riscv64:"MOV [$]11" "MUL" -"NEG" + return -(a * -11) +} + +func SimplifyNegMul(a, b int) int { + // amd64:-"NEGQ" + // arm64:"MUL" -"NEG" + // riscv64:"MUL" -"NEG" + return -(-a * b) +} + // -------------- // // Division // // -------------- // -- 2.52.0