cmd/compile: ssa rewrite x-(x&y) => x&^y

This allows to rewrite:
  AND BX, AX
  SUB AX, BX

Into (with GOAMD64=v3):
  ANDN AX, BX, AX

Which is twice as fast, and smaller.

Into (without GOAMD64=v3):
  NOT BX
  AND AX, BX

Which if AX's dependency isn't resolved yet but BX is in most cases twice as
fast because now the NOT BX and AX's depency can execute in parallel.
In other cases, it has negligible positive impact.

Other instructions set will also benefit from the pipelining advantage
And also benefit from the smaller ANDN instruction if they have something
similar.

Help reduce ChaCha20 and Poly1305 to #52563

Change-Id: I0a0a3fe553b857e13c61fbf52f9765377615a898
Reviewed-on: https://go-review.googlesource.com/c/go/+/786700
Reviewed-by: Keith Randall <khr@golang.org>
Reviewed-by: Keith Randall <khr@google.com>
Auto-Submit: Jorropo <jorropo.pgm@gmail.com>
LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Carlos Amedee <carlos@golang.org>
This commit is contained in:
Jorropo 2022-05-03 12:24:25 +02:00 committed by Gopher Robot
parent e798729d8c
commit 1008f900f6
5 changed files with 162 additions and 0 deletions

View File

@ -1602,6 +1602,7 @@
// CPUID feature: BMI1.
(AND(Q|L) x (NOT(Q|L) y)) && buildcfg.GOAMD64 >= 3 => (ANDN(Q|L) x y)
(SUB(Q|L) x (AND(Q|L) x y)) && buildcfg.GOAMD64 >= 3 => (ANDN(Q|L) x y)
(AND(Q|L) x (NEG(Q|L) x)) && buildcfg.GOAMD64 >= 3 => (BLSI(Q|L) x)
(XOR(Q|L) x (ADD(Q|L)const [-1] x)) && buildcfg.GOAMD64 >= 3 => (BLSMSK(Q|L) x)
(AND(Q|L) <t> x (ADD(Q|L)const [-1] x)) && buildcfg.GOAMD64 >= 3 => (Select0 <t> (BLSR(Q|L) x))

View File

@ -708,6 +708,9 @@
(Or(64|32|16|8) x (Or(64|32|16|8) x y)) => (Or(64|32|16|8) x y)
(Xor(64|32|16|8) x (Xor(64|32|16|8) x y)) => y
// x-(x&y) == x&^y
(Sub(64|32|16|8) x (And(64|32|16|8) <t> x y)) => (And(64|32|16|8) x (Com(64|32|16|8) <t> y))
// Fold comparisons with numeric bounds
(Less(64|32|16|8)U _ (Const(64|32|16|8) [0])) => (ConstBool [false])
(Leq(64|32|16|8)U (Const(64|32|16|8) [0]) _) => (ConstBool [true])

View File

@ -40193,6 +40193,31 @@ func rewriteValueAMD64_OpAMD64SUBL(v *Value) bool {
v.AddArg3(x, ptr, mem)
return true
}
// match: (SUBL x (ANDL x y))
// cond: buildcfg.GOAMD64 >= 3
// result: (ANDNL x y)
for {
x := v_0
if v_1.Op != OpAMD64ANDL {
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 x != v_1_0 {
continue
}
y := v_1_1
if !(buildcfg.GOAMD64 >= 3) {
continue
}
v.reset(OpAMD64ANDNL)
v.AddArg2(x, y)
return true
}
break
}
return false
}
func rewriteValueAMD64_OpAMD64SUBLconst(v *Value) bool {
@ -40435,6 +40460,31 @@ func rewriteValueAMD64_OpAMD64SUBQ(v *Value) bool {
v.AddArg3(x, ptr, mem)
return true
}
// match: (SUBQ x (ANDQ x y))
// cond: buildcfg.GOAMD64 >= 3
// result: (ANDNQ x y)
for {
x := v_0
if v_1.Op != OpAMD64ANDQ {
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 x != v_1_0 {
continue
}
y := v_1_1
if !(buildcfg.GOAMD64 >= 3) {
continue
}
v.reset(OpAMD64ANDNQ)
v.AddArg2(x, y)
return true
}
break
}
return false
}
func rewriteValueAMD64_OpAMD64SUBQborrow(v *Value) bool {

View File

@ -35967,6 +35967,30 @@ func rewriteValuegeneric_OpSub16(v *Value) bool {
}
break
}
// match: (Sub16 x (And16 <t> x y))
// result: (And16 x (Com16 <t> y))
for {
x := v_0
if v_1.Op != OpAnd16 {
break
}
t := v_1.Type
_ = 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 x != v_1_0 {
continue
}
y := v_1_1
v.reset(OpAnd16)
v0 := b.NewValue0(v.Pos, OpCom16, t)
v0.AddArg(y)
v.AddArg2(x, v0)
return true
}
break
}
// match: (Sub16 (Add16 x y) x)
// result: y
for {
@ -36340,6 +36364,30 @@ func rewriteValuegeneric_OpSub32(v *Value) bool {
}
break
}
// match: (Sub32 x (And32 <t> x y))
// result: (And32 x (Com32 <t> y))
for {
x := v_0
if v_1.Op != OpAnd32 {
break
}
t := v_1.Type
_ = 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 x != v_1_0 {
continue
}
y := v_1_1
v.reset(OpAnd32)
v0 := b.NewValue0(v.Pos, OpCom32, t)
v0.AddArg(y)
v.AddArg2(x, v0)
return true
}
break
}
// match: (Sub32 (Add32 x y) x)
// result: y
for {
@ -36737,6 +36785,30 @@ func rewriteValuegeneric_OpSub64(v *Value) bool {
}
break
}
// match: (Sub64 x (And64 <t> x y))
// result: (And64 x (Com64 <t> y))
for {
x := v_0
if v_1.Op != OpAnd64 {
break
}
t := v_1.Type
_ = 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 x != v_1_0 {
continue
}
y := v_1_1
v.reset(OpAnd64)
v0 := b.NewValue0(v.Pos, OpCom64, t)
v0.AddArg(y)
v.AddArg2(x, v0)
return true
}
break
}
// match: (Sub64 (Add64 x y) x)
// result: y
for {
@ -37134,6 +37206,30 @@ func rewriteValuegeneric_OpSub8(v *Value) bool {
}
break
}
// match: (Sub8 x (And8 <t> x y))
// result: (And8 x (Com8 <t> y))
for {
x := v_0
if v_1.Op != OpAnd8 {
break
}
t := v_1.Type
_ = 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 x != v_1_0 {
continue
}
y := v_1_1
v.reset(OpAnd8)
v0 := b.NewValue0(v.Pos, OpCom8, t)
v0.AddArg(y)
v.AddArg2(x, v0)
return true
}
break
}
// match: (Sub8 (Add8 x y) x)
// result: y
for {

View File

@ -207,3 +207,15 @@ func shlrx32_load(x []uint32, i int, s uint32) uint32 {
s = x[i+1] << s
return s
}
func XSubXandYAndn64(x, y uint64) uint64 {
// amd64/v1:"ANDQ" "NOTQ" -"SUBQ"
// amd64/v3:"ANDNQ" -"SUBQ" -"ANDQ"
return x - (x & y)
}
func XSubXandYAndn32(x, y uint32) uint32 {
// amd64/v1:"ANDL" "NOTL" -"SUBL"
// amd64/v3:"ANDNL" -"SUBL" -"ANDL"
return x - (x & y)
}