cmd/compile: canonicalize x+x into x<<1 in generic.rules

The canonical way to multiply by 2 is x<<1, this is what other
generic rules expect.

It is slower than x+x but arches rule can turn x<<1 back into x+x,
as this avoids adding many special cases for rules optimizing shifts
to also search x+x as x<<1.

Change-Id: I249c60cd2643db2e2a3503f3934211f80fb2912a
Reviewed-on: https://go-review.googlesource.com/c/go/+/774060
Reviewed-by: Keith Randall <khr@google.com>
Reviewed-by: Keith Randall <khr@golang.org>
LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Jorropo <jorropo.pgm@gmail.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
This commit is contained in:
Jorropo 2026-05-05 01:08:30 +02:00 committed by Gopher Robot
parent f133609b75
commit 74c35fca7a
3 changed files with 80 additions and 21 deletions

View file

@ -407,6 +407,11 @@
(Sub(64|32|16|8) <t> (Mul(64|32|16|8) x y) (Mul(64|32|16|8) x z))
=> (Mul(64|32|16|8) x (Sub(64|32|16|8) <t> y z))
// Canonicalize x+x to x << 1.
// This is often slower since most CPUs have more adders than shifters, but it can enable other optimizations.
// Arches who care about this like AMD64 convert x << 1 back to x+x in their arch-specific rules which is useful anyhow.
(Add(64|32|16|8) x x) => (Lsh(64|32|16|8)x64 x (Const64 <types.Types[types.TUINT64]> [1]))
// rewrite shifts of 8/16/32 bit consts into 64 bit consts to reduce
// the number of the other rewrite rules for const shifts
(Lsh64x32 <t> x (Const32 [c])) => (Lsh64x64 x (Const64 <t> [int64(uint32(c))]))

View file

@ -553,6 +553,19 @@ func rewriteValuegeneric_OpAdd16(v *Value) bool {
}
break
}
// match: (Add16 x x)
// result: (Lsh16x64 x (Const64 <types.Types[types.TUINT64]> [1]))
for {
x := v_0
if x != v_1 {
break
}
v.reset(OpLsh16x64)
v0 := b.NewValue0(v.Pos, OpConst64, types.Types[types.TUINT64])
v0.AuxInt = int64ToAuxInt(1)
v.AddArg2(x, v0)
return true
}
// match: (Add16 (Const16 [0]) x)
// result: x
for {
@ -1166,6 +1179,19 @@ func rewriteValuegeneric_OpAdd32(v *Value) bool {
}
break
}
// match: (Add32 x x)
// result: (Lsh32x64 x (Const64 <types.Types[types.TUINT64]> [1]))
for {
x := v_0
if x != v_1 {
break
}
v.reset(OpLsh32x64)
v0 := b.NewValue0(v.Pos, OpConst64, types.Types[types.TUINT64])
v0.AuxInt = int64ToAuxInt(1)
v.AddArg2(x, v0)
return true
}
// match: (Add32 (Const32 [0]) x)
// result: x
for {
@ -1806,6 +1832,19 @@ func rewriteValuegeneric_OpAdd64(v *Value) bool {
}
break
}
// match: (Add64 x x)
// result: (Lsh64x64 x (Const64 <types.Types[types.TUINT64]> [1]))
for {
x := v_0
if x != v_1 {
break
}
v.reset(OpLsh64x64)
v0 := b.NewValue0(v.Pos, OpConst64, types.Types[types.TUINT64])
v0.AuxInt = int64ToAuxInt(1)
v.AddArg2(x, v0)
return true
}
// match: (Add64 (Const64 [0]) x)
// result: x
for {
@ -2484,6 +2523,19 @@ func rewriteValuegeneric_OpAdd8(v *Value) bool {
}
break
}
// match: (Add8 x x)
// result: (Lsh8x64 x (Const64 <types.Types[types.TUINT64]> [1]))
for {
x := v_0
if x != v_1 {
break
}
v.reset(OpLsh8x64)
v0 := b.NewValue0(v.Pos, OpConst64, types.Types[types.TUINT64])
v0.AuxInt = int64ToAuxInt(1)
v.AddArg2(x, v0)
return true
}
// match: (Add8 (Const8 [0]) x)
// result: x
for {

View file

@ -120,47 +120,49 @@ func rshConst64x32(v int64) int64 {
}
func lshConst32x1Add(x int32) int32 {
// amd64:"SHLL [$]2"
// loong64:"SLL [$]2"
// riscv64:"SLLI [$]2"
// ppc64x:"SLW [$]2" -"ADD"
// amd64:-"ADD" "SHLL [$]2"
// loong64:-"ADD" "SLL [$]2"
// riscv64:-"ADD" "SLLI [$]2"
// ppc64x:-"ADD" "SLW [$]2"
return (x + x) << 1
}
func lshConst64x1Add(x int64) int64 {
// amd64:"SHLQ [$]2"
// loong64:"SLLV [$]2"
// riscv64:"SLLI [$]2"
// ppc64x:"SLD [$]2" -"ADD"
// amd64:-"ADD" "SHLQ [$]2"
// loong64:-"ADD" "SLLV [$]2"
// riscv64:-"ADD" "SLLI [$]2"
// ppc64x:-"ADD" "SLD [$]2"
return (x + x) << 1
}
func lshConst32x2Add(x int32) int32 {
// amd64:"SHLL [$]3"
// loong64:"SLL [$]3"
// riscv64:"SLLI [$]3"
// ppc64x:"SLW [$]3" -"ADD"
// amd64:-"ADD" "SHLL [$]3"
// loong64:-"ADD" "SLL [$]3"
// riscv64:-"ADD" "SLLI [$]3"
// ppc64x:-"ADD" "SLW [$]3"
return (x + x) << 2
}
func lshConst64x2Add(x int64) int64 {
// amd64:"SHLQ [$]3"
// loong64:"SLLV [$]3"
// riscv64:"SLLI [$]3"
// ppc64x:"SLD [$]3" -"ADD"
// amd64:-"ADD" "SHLQ [$]3"
// loong64:-"ADD" "SLLV [$]3"
// riscv64:-"ADD" "SLLI [$]3"
// ppc64x:-"ADD" "SLD [$]3"
return (x + x) << 2
}
func lshConst32x31Add(x int32) int32 {
// loong64:-"SLL " "MOVV R0"
// riscv64:-"SLLI" "MOV [$]0"
// ppc64x:"ADD" "SLW [$]31" -"SLW [$]32"
// amd64:-"ADD" -"SHL" "XORL AX, AX"
// loong64:-"ADD" -"SLL " "MOVV R0"
// riscv64:-"ADD" -"SLLI" "MOV [$]0"
// ppc64x:-"ADD" -"SLW" "MOVD [$]0"
return (x + x) << 31
}
func lshConst64x63Add(x int64) int64 {
// loong64:-"SLLV" "MOVV R0"
// riscv64:-"SLLI" "MOV [$]0"
// amd64:-"ADD" -"SHL" "XORL AX, AX"
// loong64:-"ADD" -"SLLV" "MOVV R0"
// riscv64:-"ADD" -"SLLI" "MOV [$]0"
return (x + x) << 63
}