From: Meng Zhuo Date: Fri, 14 Nov 2025 04:47:35 +0000 (+0800) Subject: cmd/compile: fold negation into multiplication X-Git-Tag: go1.26rc1~285 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=2cdcc4150bc577e0b40a9cedaaa7c8301f2860cd;p=gostls13.git cmd/compile: fold negation into multiplication goos: linux goarch: riscv64 pkg: cmd/compile/internal/test cpu: Spacemit(R) X60 │ /root/mul.base.log │ /root/mul.new.log │ │ sec/op │ sec/op vs base │ MulNeg 6.426µ ± 0% 4.501µ ± 0% -29.96% (p=0.000 n=10) Mul2Neg 9.000µ ± 0% 6.431µ ± 0% -28.54% (p=0.000 n=10) Mul2 1.263µ ± 0% 1.263µ ± 0% ~ (p=1.000 n=10) MulNeg2 1.577µ ± 0% 1.577µ ± 0% ~ (p=0.211 n=10) geomean 3.276µ 2.756µ -15.89% goos: linux goarch: amd64 pkg: cmd/compile/internal/test cpu: AMD EPYC 7532 32-Core Processor │ /root/base │ /root/new │ │ sec/op │ sec/op vs base │ MulNeg 691.9n ± 1% 319.4n ± 0% -53.83% (p=0.000 n=10) Mul2Neg 630.0n ± 0% 629.6n ± 0% -0.07% (p=0.000 n=10) Mul2 438.1n ± 0% 438.1n ± 0% ~ (p=0.728 n=10) MulNeg2 439.3n ± 0% 439.4n ± 0% ~ (p=0.656 n=10) geomean 538.2n 443.6n -17.58% Change-Id: Ice8e6c8d1e8e3009ba8a0b1b689205174e199019 Reviewed-on: https://go-review.googlesource.com/c/go/+/720180 Reviewed-by: abner chenc Reviewed-by: Keith Randall Reviewed-by: Junyang Shao Reviewed-by: Joel Sing Reviewed-by: Keith Randall LUCI-TryBot-Result: Go LUCI Auto-Submit: Keith Randall --- diff --git a/src/cmd/compile/internal/ssa/_gen/LOONG64.rules b/src/cmd/compile/internal/ssa/_gen/LOONG64.rules index 9691296043..2beba0b1c5 100644 --- a/src/cmd/compile/internal/ssa/_gen/LOONG64.rules +++ b/src/cmd/compile/internal/ssa/_gen/LOONG64.rules @@ -743,9 +743,6 @@ (MULV x (MOVVconst [c])) && canMulStrengthReduce(config, c) => {mulStrengthReduce(v, x, c)} -(MULV (NEGV x) (MOVVconst [c])) => (MULV x (MOVVconst [-c])) -(MULV (NEGV x) (NEGV y)) => (MULV x y) - (ADDV x0 x1:(SLLVconst [c] y)) && x1.Uses == 1 && c > 0 && c <= 4 => (ADDshiftLLV x0 y [c]) // fold constant in ADDshift op diff --git a/src/cmd/compile/internal/ssa/_gen/generic.rules b/src/cmd/compile/internal/ssa/_gen/generic.rules index 6efead03ad..e09cd31c31 100644 --- a/src/cmd/compile/internal/ssa/_gen/generic.rules +++ b/src/cmd/compile/internal/ssa/_gen/generic.rules @@ -195,6 +195,11 @@ // Convert x * -1 to -x. (Mul(8|16|32|64) (Const(8|16|32|64) [-1]) x) => (Neg(8|16|32|64) x) +// Convert -x * c to x * -c +(Mul(8|16|32|64) (Const(8|16|32|64) [c]) (Neg(8|16|32|64) x)) => (Mul(8|16|32|64) x (Const(8|16|32|64) [-c])) + +(Mul(8|16|32|64) (Neg(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)) @@ -2228,4 +2233,4 @@ (Neq(64|32|16) (SignExt8to(64|32|16) (CvtBoolToUint8 x)) (Const(64|32|16) [0])) => x (Neq(64|32|16) (SignExt8to(64|32|16) (CvtBoolToUint8 x)) (Const(64|32|16) [1])) => (Not x) (Eq(64|32|16) (SignExt8to(64|32|16) (CvtBoolToUint8 x)) (Const(64|32|16) [1])) => x -(Eq(64|32|16) (SignExt8to(64|32|16) (CvtBoolToUint8 x)) (Const(64|32|16) [0])) => (Not x) \ No newline at end of file +(Eq(64|32|16) (SignExt8to(64|32|16) (CvtBoolToUint8 x)) (Const(64|32|16) [0])) => (Not x) diff --git a/src/cmd/compile/internal/ssa/rewriteLOONG64.go b/src/cmd/compile/internal/ssa/rewriteLOONG64.go index 4262d4e0fb..bf2dd114a9 100644 --- a/src/cmd/compile/internal/ssa/rewriteLOONG64.go +++ b/src/cmd/compile/internal/ssa/rewriteLOONG64.go @@ -5866,7 +5866,6 @@ func rewriteValueLOONG64_OpLOONG64MULV(v *Value) bool { v_0 := v.Args[0] b := v.Block config := b.Func.Config - typ := &b.Func.Config.Types // match: (MULV _ (MOVVconst [0])) // result: (MOVVconst [0]) for { @@ -5911,44 +5910,6 @@ func rewriteValueLOONG64_OpLOONG64MULV(v *Value) bool { } break } - // match: (MULV (NEGV x) (MOVVconst [c])) - // result: (MULV x (MOVVconst [-c])) - for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpLOONG64NEGV { - continue - } - x := v_0.Args[0] - if v_1.Op != OpLOONG64MOVVconst { - continue - } - c := auxIntToInt64(v_1.AuxInt) - v.reset(OpLOONG64MULV) - v0 := b.NewValue0(v.Pos, OpLOONG64MOVVconst, typ.UInt64) - v0.AuxInt = int64ToAuxInt(-c) - v.AddArg2(x, v0) - return true - } - break - } - // match: (MULV (NEGV x) (NEGV y)) - // result: (MULV x y) - for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - if v_0.Op != OpLOONG64NEGV { - continue - } - x := v_0.Args[0] - if v_1.Op != OpLOONG64NEGV { - continue - } - y := v_1.Args[0] - v.reset(OpLOONG64MULV) - v.AddArg2(x, y) - return true - } - break - } // match: (MULV (MOVVconst [c]) (MOVVconst [d])) // result: (MOVVconst [c*d]) for { diff --git a/src/cmd/compile/internal/ssa/rewritegeneric.go b/src/cmd/compile/internal/ssa/rewritegeneric.go index 2428f17947..1621153b43 100644 --- a/src/cmd/compile/internal/ssa/rewritegeneric.go +++ b/src/cmd/compile/internal/ssa/rewritegeneric.go @@ -16786,6 +16786,45 @@ func rewriteValuegeneric_OpMul16(v *Value) bool { } break } + // match: (Mul16 (Const16 [c]) (Neg16 x)) + // result: (Mul16 x (Const16 [-c])) + 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 != OpNeg16 { + continue + } + x := v_1.Args[0] + v.reset(OpMul16) + v0 := b.NewValue0(v.Pos, OpConst16, t) + v0.AuxInt = int16ToAuxInt(-c) + v.AddArg2(x, v0) + return true + } + break + } + // match: (Mul16 (Neg16 x) (Neg16 y)) + // result: (Mul16 x y) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpNeg16 { + continue + } + x := v_0.Args[0] + if v_1.Op != OpNeg16 { + continue + } + y := v_1.Args[0] + v.reset(OpMul16) + v.AddArg2(x, y) + return true + } + break + } // match: (Mul16 (Const16 [c]) (Add16 (Const16 [d]) x)) // cond: !isPowerOfTwo(c) // result: (Add16 (Const16 [c*d]) (Mul16 (Const16 [c]) x)) @@ -16997,6 +17036,45 @@ func rewriteValuegeneric_OpMul32(v *Value) bool { } break } + // match: (Mul32 (Const32 [c]) (Neg32 x)) + // result: (Mul32 x (Const32 [-c])) + 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 != OpNeg32 { + continue + } + x := v_1.Args[0] + v.reset(OpMul32) + v0 := b.NewValue0(v.Pos, OpConst32, t) + v0.AuxInt = int32ToAuxInt(-c) + v.AddArg2(x, v0) + return true + } + break + } + // match: (Mul32 (Neg32 x) (Neg32 y)) + // result: (Mul32 x y) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpNeg32 { + continue + } + x := v_0.Args[0] + if v_1.Op != OpNeg32 { + continue + } + y := v_1.Args[0] + v.reset(OpMul32) + v.AddArg2(x, y) + return true + } + break + } // match: (Mul32 (Const32 [c]) (Add32 (Const32 [d]) x)) // cond: !isPowerOfTwo(c) // result: (Add32 (Const32 [c*d]) (Mul32 (Const32 [c]) x)) @@ -17369,6 +17447,45 @@ func rewriteValuegeneric_OpMul64(v *Value) bool { } break } + // match: (Mul64 (Const64 [c]) (Neg64 x)) + // result: (Mul64 x (Const64 [-c])) + 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 != OpNeg64 { + continue + } + x := v_1.Args[0] + v.reset(OpMul64) + v0 := b.NewValue0(v.Pos, OpConst64, t) + v0.AuxInt = int64ToAuxInt(-c) + v.AddArg2(x, v0) + return true + } + break + } + // match: (Mul64 (Neg64 x) (Neg64 y)) + // result: (Mul64 x y) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpNeg64 { + continue + } + x := v_0.Args[0] + if v_1.Op != OpNeg64 { + continue + } + y := v_1.Args[0] + v.reset(OpMul64) + v.AddArg2(x, y) + return true + } + break + } // match: (Mul64 (Const64 [c]) (Add64 (Const64 [d]) x)) // cond: !isPowerOfTwo(c) // result: (Add64 (Const64 [c*d]) (Mul64 (Const64 [c]) x)) @@ -17741,6 +17858,45 @@ func rewriteValuegeneric_OpMul8(v *Value) bool { } break } + // match: (Mul8 (Const8 [c]) (Neg8 x)) + // result: (Mul8 x (Const8 [-c])) + 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 != OpNeg8 { + continue + } + x := v_1.Args[0] + v.reset(OpMul8) + v0 := b.NewValue0(v.Pos, OpConst8, t) + v0.AuxInt = int8ToAuxInt(-c) + v.AddArg2(x, v0) + return true + } + break + } + // match: (Mul8 (Neg8 x) (Neg8 y)) + // result: (Mul8 x y) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpNeg8 { + continue + } + x := v_0.Args[0] + if v_1.Op != OpNeg8 { + continue + } + y := v_1.Args[0] + v.reset(OpMul8) + v.AddArg2(x, y) + return true + } + break + } // match: (Mul8 (Const8 [c]) (Add8 (Const8 [d]) x)) // cond: !isPowerOfTwo(c) // result: (Add8 (Const8 [c*d]) (Mul8 (Const8 [c]) x)) diff --git a/test/codegen/arithmetic.go b/test/codegen/arithmetic.go index 42d5d2ef65..6b2c5529e1 100644 --- a/test/codegen/arithmetic.go +++ b/test/codegen/arithmetic.go @@ -318,13 +318,19 @@ func MergeMuls5(a, n int) int { // Multiplications folded negation func FoldNegMul(a int) int { - // loong64:"SUBVU" "ALSLV [$]2" "ALSLV [$]1" - return (-a) * 11 + // amd64:"IMUL3Q [$]-11" -"NEGQ" + // arm64:"MOVD [$]-11" "MUL" -"NEG" + // loong64:"ALSLV [$]2" "SUBVU" "ALSLV [$]4" + // riscv64:"MOV [$]-11" "MUL" -"NEG" + return -a * 11 } func Fold2NegMul(a, b int) int { + // amd64:"IMULQ" -"NEGQ" + // arm64:"MUL" -"NEG" // loong64:"MULV" -"SUBVU R[0-9], R0," - return (-a) * (-b) + // riscv64:"MUL" -"NEG" + return -a * -b } // -------------- //