cmd/compile: combine x*n - y*n into (x-y)*n

Do the similar thing to CL 55143 to reduce IMUL.

Change-Id: I1bd38f618058e3cd74fac181f003610ea13f2294
Reviewed-on: https://go-review.googlesource.com/56252
Run-TryBot: Emmanuel Odeke <emm.odeke@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
This commit is contained in:
Cholerae Hu 2017-08-17 15:06:42 +08:00 committed by Keith Randall
parent 03c3bb5f84
commit fb165eaffd
4 changed files with 576 additions and 26 deletions

View file

@ -831,6 +831,20 @@ var linuxAMD64Tests = []*asmTest{
}`,
pos: []string{"\tADDQ\t[$]19", "\tIMULQ"}, // (a+19)*n
},
{
`
func mul4(n int) int {
return 23*n - 9*n
}`,
[]string{"\tIMULQ\t[$]14"}, // 14*n
},
{
`
func mul5(a, n int) int {
return a*n - 19*n
}`,
[]string{"\tADDQ\t[$]-19", "\tIMULQ"}, // (a-19)*n
},
// see issue 19595.
// We want to merge load+op in f58, but not in f59.
@ -1150,6 +1164,20 @@ var linux386Tests = []*asmTest{
`,
pos: []string{"TEXT\t.*, [$]0-4"},
},
{
`
func mul3(n int) int {
return 23*n - 9*n
}`,
[]string{"\tIMULL\t[$]14"}, // 14*n
},
{
`
func mul4(a, n int) int {
return n*a - a*19
}`,
[]string{"\tADDL\t[$]-19", "\tIMULL"}, // (n-19)*a
},
}
var linuxS390XTests = []*asmTest{

View file

@ -328,6 +328,12 @@
(Add16 <t> (Mul16 x y) (Mul16 x z)) -> (Mul16 x (Add16 <t> y z))
(Add8 <t> (Mul8 x y) (Mul8 x z)) -> (Mul8 x (Add8 <t> y z))
// Rewrite x*y - x*z to x*(y-z)
(Sub64 <t> (Mul64 x y) (Mul64 x z)) -> (Mul64 x (Sub64 <t> y z))
(Sub32 <t> (Mul32 x y) (Mul32 x z)) -> (Mul32 x (Sub32 <t> y z))
(Sub16 <t> (Mul16 x y) (Mul16 x z)) -> (Mul16 x (Sub16 <t> y z))
(Sub8 <t> (Mul8 x y) (Mul8 x z)) -> (Mul8 x (Sub8 <t> y z))
// 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

@ -23796,6 +23796,126 @@ func rewriteValuegeneric_OpSub16_0(v *Value) bool {
v.AddArg(x)
return true
}
// match: (Sub16 <t> (Mul16 x y) (Mul16 x z))
// cond:
// result: (Mul16 x (Sub16 <t> y z))
for {
t := v.Type
_ = v.Args[1]
v_0 := v.Args[0]
if v_0.Op != OpMul16 {
break
}
_ = v_0.Args[1]
x := v_0.Args[0]
y := v_0.Args[1]
v_1 := v.Args[1]
if v_1.Op != OpMul16 {
break
}
_ = v_1.Args[1]
if x != v_1.Args[0] {
break
}
z := v_1.Args[1]
v.reset(OpMul16)
v.AddArg(x)
v0 := b.NewValue0(v.Pos, OpSub16, t)
v0.AddArg(y)
v0.AddArg(z)
v.AddArg(v0)
return true
}
// match: (Sub16 <t> (Mul16 y x) (Mul16 x z))
// cond:
// result: (Mul16 x (Sub16 <t> y z))
for {
t := v.Type
_ = v.Args[1]
v_0 := v.Args[0]
if v_0.Op != OpMul16 {
break
}
_ = v_0.Args[1]
y := v_0.Args[0]
x := v_0.Args[1]
v_1 := v.Args[1]
if v_1.Op != OpMul16 {
break
}
_ = v_1.Args[1]
if x != v_1.Args[0] {
break
}
z := v_1.Args[1]
v.reset(OpMul16)
v.AddArg(x)
v0 := b.NewValue0(v.Pos, OpSub16, t)
v0.AddArg(y)
v0.AddArg(z)
v.AddArg(v0)
return true
}
// match: (Sub16 <t> (Mul16 x y) (Mul16 z x))
// cond:
// result: (Mul16 x (Sub16 <t> y z))
for {
t := v.Type
_ = v.Args[1]
v_0 := v.Args[0]
if v_0.Op != OpMul16 {
break
}
_ = v_0.Args[1]
x := v_0.Args[0]
y := v_0.Args[1]
v_1 := v.Args[1]
if v_1.Op != OpMul16 {
break
}
_ = v_1.Args[1]
z := v_1.Args[0]
if x != v_1.Args[1] {
break
}
v.reset(OpMul16)
v.AddArg(x)
v0 := b.NewValue0(v.Pos, OpSub16, t)
v0.AddArg(y)
v0.AddArg(z)
v.AddArg(v0)
return true
}
// match: (Sub16 <t> (Mul16 y x) (Mul16 z x))
// cond:
// result: (Mul16 x (Sub16 <t> y z))
for {
t := v.Type
_ = v.Args[1]
v_0 := v.Args[0]
if v_0.Op != OpMul16 {
break
}
_ = v_0.Args[1]
y := v_0.Args[0]
x := v_0.Args[1]
v_1 := v.Args[1]
if v_1.Op != OpMul16 {
break
}
_ = v_1.Args[1]
z := v_1.Args[0]
if x != v_1.Args[1] {
break
}
v.reset(OpMul16)
v.AddArg(x)
v0 := b.NewValue0(v.Pos, OpSub16, t)
v0.AddArg(y)
v0.AddArg(z)
v.AddArg(v0)
return true
}
// match: (Sub16 x x)
// cond:
// result: (Const16 [0])
@ -23869,6 +23989,11 @@ func rewriteValuegeneric_OpSub16_0(v *Value) bool {
v.AddArg(x)
return true
}
return false
}
func rewriteValuegeneric_OpSub16_10(v *Value) bool {
b := v.Block
_ = b
// match: (Sub16 (Add16 y x) y)
// cond:
// result: x
@ -23977,11 +24102,6 @@ func rewriteValuegeneric_OpSub16_0(v *Value) bool {
v.AddArg(x)
return true
}
return false
}
func rewriteValuegeneric_OpSub16_10(v *Value) bool {
b := v.Block
_ = b
// match: (Sub16 (Const16 <t> [c]) (Sub16 (Const16 <t> [d]) x))
// cond:
// result: (Add16 (Const16 <t> [int64(int16(c-d))]) x)
@ -24060,6 +24180,126 @@ func rewriteValuegeneric_OpSub32_0(v *Value) bool {
v.AddArg(x)
return true
}
// match: (Sub32 <t> (Mul32 x y) (Mul32 x z))
// cond:
// result: (Mul32 x (Sub32 <t> y z))
for {
t := v.Type
_ = v.Args[1]
v_0 := v.Args[0]
if v_0.Op != OpMul32 {
break
}
_ = v_0.Args[1]
x := v_0.Args[0]
y := v_0.Args[1]
v_1 := v.Args[1]
if v_1.Op != OpMul32 {
break
}
_ = v_1.Args[1]
if x != v_1.Args[0] {
break
}
z := v_1.Args[1]
v.reset(OpMul32)
v.AddArg(x)
v0 := b.NewValue0(v.Pos, OpSub32, t)
v0.AddArg(y)
v0.AddArg(z)
v.AddArg(v0)
return true
}
// match: (Sub32 <t> (Mul32 y x) (Mul32 x z))
// cond:
// result: (Mul32 x (Sub32 <t> y z))
for {
t := v.Type
_ = v.Args[1]
v_0 := v.Args[0]
if v_0.Op != OpMul32 {
break
}
_ = v_0.Args[1]
y := v_0.Args[0]
x := v_0.Args[1]
v_1 := v.Args[1]
if v_1.Op != OpMul32 {
break
}
_ = v_1.Args[1]
if x != v_1.Args[0] {
break
}
z := v_1.Args[1]
v.reset(OpMul32)
v.AddArg(x)
v0 := b.NewValue0(v.Pos, OpSub32, t)
v0.AddArg(y)
v0.AddArg(z)
v.AddArg(v0)
return true
}
// match: (Sub32 <t> (Mul32 x y) (Mul32 z x))
// cond:
// result: (Mul32 x (Sub32 <t> y z))
for {
t := v.Type
_ = v.Args[1]
v_0 := v.Args[0]
if v_0.Op != OpMul32 {
break
}
_ = v_0.Args[1]
x := v_0.Args[0]
y := v_0.Args[1]
v_1 := v.Args[1]
if v_1.Op != OpMul32 {
break
}
_ = v_1.Args[1]
z := v_1.Args[0]
if x != v_1.Args[1] {
break
}
v.reset(OpMul32)
v.AddArg(x)
v0 := b.NewValue0(v.Pos, OpSub32, t)
v0.AddArg(y)
v0.AddArg(z)
v.AddArg(v0)
return true
}
// match: (Sub32 <t> (Mul32 y x) (Mul32 z x))
// cond:
// result: (Mul32 x (Sub32 <t> y z))
for {
t := v.Type
_ = v.Args[1]
v_0 := v.Args[0]
if v_0.Op != OpMul32 {
break
}
_ = v_0.Args[1]
y := v_0.Args[0]
x := v_0.Args[1]
v_1 := v.Args[1]
if v_1.Op != OpMul32 {
break
}
_ = v_1.Args[1]
z := v_1.Args[0]
if x != v_1.Args[1] {
break
}
v.reset(OpMul32)
v.AddArg(x)
v0 := b.NewValue0(v.Pos, OpSub32, t)
v0.AddArg(y)
v0.AddArg(z)
v.AddArg(v0)
return true
}
// match: (Sub32 x x)
// cond:
// result: (Const32 [0])
@ -24133,6 +24373,11 @@ func rewriteValuegeneric_OpSub32_0(v *Value) bool {
v.AddArg(x)
return true
}
return false
}
func rewriteValuegeneric_OpSub32_10(v *Value) bool {
b := v.Block
_ = b
// match: (Sub32 (Add32 y x) y)
// cond:
// result: x
@ -24241,11 +24486,6 @@ func rewriteValuegeneric_OpSub32_0(v *Value) bool {
v.AddArg(x)
return true
}
return false
}
func rewriteValuegeneric_OpSub32_10(v *Value) bool {
b := v.Block
_ = b
// match: (Sub32 (Const32 <t> [c]) (Sub32 (Const32 <t> [d]) x))
// cond:
// result: (Add32 (Const32 <t> [int64(int32(c-d))]) x)
@ -24364,6 +24604,126 @@ func rewriteValuegeneric_OpSub64_0(v *Value) bool {
v.AddArg(x)
return true
}
// match: (Sub64 <t> (Mul64 x y) (Mul64 x z))
// cond:
// result: (Mul64 x (Sub64 <t> y z))
for {
t := v.Type
_ = v.Args[1]
v_0 := v.Args[0]
if v_0.Op != OpMul64 {
break
}
_ = v_0.Args[1]
x := v_0.Args[0]
y := v_0.Args[1]
v_1 := v.Args[1]
if v_1.Op != OpMul64 {
break
}
_ = v_1.Args[1]
if x != v_1.Args[0] {
break
}
z := v_1.Args[1]
v.reset(OpMul64)
v.AddArg(x)
v0 := b.NewValue0(v.Pos, OpSub64, t)
v0.AddArg(y)
v0.AddArg(z)
v.AddArg(v0)
return true
}
// match: (Sub64 <t> (Mul64 y x) (Mul64 x z))
// cond:
// result: (Mul64 x (Sub64 <t> y z))
for {
t := v.Type
_ = v.Args[1]
v_0 := v.Args[0]
if v_0.Op != OpMul64 {
break
}
_ = v_0.Args[1]
y := v_0.Args[0]
x := v_0.Args[1]
v_1 := v.Args[1]
if v_1.Op != OpMul64 {
break
}
_ = v_1.Args[1]
if x != v_1.Args[0] {
break
}
z := v_1.Args[1]
v.reset(OpMul64)
v.AddArg(x)
v0 := b.NewValue0(v.Pos, OpSub64, t)
v0.AddArg(y)
v0.AddArg(z)
v.AddArg(v0)
return true
}
// match: (Sub64 <t> (Mul64 x y) (Mul64 z x))
// cond:
// result: (Mul64 x (Sub64 <t> y z))
for {
t := v.Type
_ = v.Args[1]
v_0 := v.Args[0]
if v_0.Op != OpMul64 {
break
}
_ = v_0.Args[1]
x := v_0.Args[0]
y := v_0.Args[1]
v_1 := v.Args[1]
if v_1.Op != OpMul64 {
break
}
_ = v_1.Args[1]
z := v_1.Args[0]
if x != v_1.Args[1] {
break
}
v.reset(OpMul64)
v.AddArg(x)
v0 := b.NewValue0(v.Pos, OpSub64, t)
v0.AddArg(y)
v0.AddArg(z)
v.AddArg(v0)
return true
}
// match: (Sub64 <t> (Mul64 y x) (Mul64 z x))
// cond:
// result: (Mul64 x (Sub64 <t> y z))
for {
t := v.Type
_ = v.Args[1]
v_0 := v.Args[0]
if v_0.Op != OpMul64 {
break
}
_ = v_0.Args[1]
y := v_0.Args[0]
x := v_0.Args[1]
v_1 := v.Args[1]
if v_1.Op != OpMul64 {
break
}
_ = v_1.Args[1]
z := v_1.Args[0]
if x != v_1.Args[1] {
break
}
v.reset(OpMul64)
v.AddArg(x)
v0 := b.NewValue0(v.Pos, OpSub64, t)
v0.AddArg(y)
v0.AddArg(z)
v.AddArg(v0)
return true
}
// match: (Sub64 x x)
// cond:
// result: (Const64 [0])
@ -24437,6 +24797,11 @@ func rewriteValuegeneric_OpSub64_0(v *Value) bool {
v.AddArg(x)
return true
}
return false
}
func rewriteValuegeneric_OpSub64_10(v *Value) bool {
b := v.Block
_ = b
// match: (Sub64 (Add64 y x) y)
// cond:
// result: x
@ -24545,11 +24910,6 @@ func rewriteValuegeneric_OpSub64_0(v *Value) bool {
v.AddArg(x)
return true
}
return false
}
func rewriteValuegeneric_OpSub64_10(v *Value) bool {
b := v.Block
_ = b
// match: (Sub64 (Const64 <t> [c]) (Sub64 (Const64 <t> [d]) x))
// cond:
// result: (Add64 (Const64 <t> [c-d]) x)
@ -24668,6 +25028,126 @@ func rewriteValuegeneric_OpSub8_0(v *Value) bool {
v.AddArg(x)
return true
}
// match: (Sub8 <t> (Mul8 x y) (Mul8 x z))
// cond:
// result: (Mul8 x (Sub8 <t> y z))
for {
t := v.Type
_ = v.Args[1]
v_0 := v.Args[0]
if v_0.Op != OpMul8 {
break
}
_ = v_0.Args[1]
x := v_0.Args[0]
y := v_0.Args[1]
v_1 := v.Args[1]
if v_1.Op != OpMul8 {
break
}
_ = v_1.Args[1]
if x != v_1.Args[0] {
break
}
z := v_1.Args[1]
v.reset(OpMul8)
v.AddArg(x)
v0 := b.NewValue0(v.Pos, OpSub8, t)
v0.AddArg(y)
v0.AddArg(z)
v.AddArg(v0)
return true
}
// match: (Sub8 <t> (Mul8 y x) (Mul8 x z))
// cond:
// result: (Mul8 x (Sub8 <t> y z))
for {
t := v.Type
_ = v.Args[1]
v_0 := v.Args[0]
if v_0.Op != OpMul8 {
break
}
_ = v_0.Args[1]
y := v_0.Args[0]
x := v_0.Args[1]
v_1 := v.Args[1]
if v_1.Op != OpMul8 {
break
}
_ = v_1.Args[1]
if x != v_1.Args[0] {
break
}
z := v_1.Args[1]
v.reset(OpMul8)
v.AddArg(x)
v0 := b.NewValue0(v.Pos, OpSub8, t)
v0.AddArg(y)
v0.AddArg(z)
v.AddArg(v0)
return true
}
// match: (Sub8 <t> (Mul8 x y) (Mul8 z x))
// cond:
// result: (Mul8 x (Sub8 <t> y z))
for {
t := v.Type
_ = v.Args[1]
v_0 := v.Args[0]
if v_0.Op != OpMul8 {
break
}
_ = v_0.Args[1]
x := v_0.Args[0]
y := v_0.Args[1]
v_1 := v.Args[1]
if v_1.Op != OpMul8 {
break
}
_ = v_1.Args[1]
z := v_1.Args[0]
if x != v_1.Args[1] {
break
}
v.reset(OpMul8)
v.AddArg(x)
v0 := b.NewValue0(v.Pos, OpSub8, t)
v0.AddArg(y)
v0.AddArg(z)
v.AddArg(v0)
return true
}
// match: (Sub8 <t> (Mul8 y x) (Mul8 z x))
// cond:
// result: (Mul8 x (Sub8 <t> y z))
for {
t := v.Type
_ = v.Args[1]
v_0 := v.Args[0]
if v_0.Op != OpMul8 {
break
}
_ = v_0.Args[1]
y := v_0.Args[0]
x := v_0.Args[1]
v_1 := v.Args[1]
if v_1.Op != OpMul8 {
break
}
_ = v_1.Args[1]
z := v_1.Args[0]
if x != v_1.Args[1] {
break
}
v.reset(OpMul8)
v.AddArg(x)
v0 := b.NewValue0(v.Pos, OpSub8, t)
v0.AddArg(y)
v0.AddArg(z)
v.AddArg(v0)
return true
}
// match: (Sub8 x x)
// cond:
// result: (Const8 [0])
@ -24741,6 +25221,11 @@ func rewriteValuegeneric_OpSub8_0(v *Value) bool {
v.AddArg(x)
return true
}
return false
}
func rewriteValuegeneric_OpSub8_10(v *Value) bool {
b := v.Block
_ = b
// match: (Sub8 (Add8 y x) y)
// cond:
// result: x
@ -24849,11 +25334,6 @@ func rewriteValuegeneric_OpSub8_0(v *Value) bool {
v.AddArg(x)
return true
}
return false
}
func rewriteValuegeneric_OpSub8_10(v *Value) bool {
b := v.Block
_ = b
// match: (Sub8 (Const8 <t> [c]) (Sub8 (Const8 <t> [d]) x))
// cond:
// result: (Add8 (Const8 <t> [int64(int8(c-d))]) x)

View file

@ -24,7 +24,7 @@ import "fmt"
// if a8 != b8 {
// // print error msg and panic
// }
func makeMergeTest(m1, m2, k int, size string) string {
func makeMergeAddTest(m1, m2, k int, size string) string {
model := " a" + size + ", b" + size
model += fmt.Sprintf(" = %%d*n%s + %%d*(n%s+%%d), (%%d+%%d)*n%s + (%%d*%%d)", size, size, size)
@ -32,7 +32,39 @@ func makeMergeTest(m1, m2, k int, size string) string {
test := fmt.Sprintf(model, m1, m2, k, m1, m2, m2, k)
test += fmt.Sprintf(`
if a%s != b%s {
fmt.Printf("MergeTest(%d, %d, %d, %s) failed\n")
fmt.Printf("MergeAddTest(%d, %d, %d, %s) failed\n")
fmt.Printf("%%d != %%d\n", a%s, b%s)
panic("FAIL")
}
`, size, size, m1, m2, k, size, size, size)
return test + "\n"
}
// Check that expressions like (c*n - d*(n+k)) get correctly merged by
// the compiler into (c-d)*n - d*k (with c-d and d*k computed at
// compile time).
//
// The merging is performed by a combination of the multiplication
// merge rules
// (c*n - d*n) -> (c-d)*n
// and the distributive multiplication rules
// c * (d-x) -> c*d - c*x
// Generate a MergeTest that looks like this:
//
// a8, b8 = m1*n8 - m2*(n8+k), (m1-m2)*n8 - m2*k
// if a8 != b8 {
// // print error msg and panic
// }
func makeMergeSubTest(m1, m2, k int, size string) string {
model := " a" + size + ", b" + size
model += fmt.Sprintf(" = %%d*n%s - %%d*(n%s+%%d), (%%d-%%d)*n%s - (%%d*%%d)", size, size, size)
test := fmt.Sprintf(model, m1, m2, k, m1, m2, m2, k)
test += fmt.Sprintf(`
if a%s != b%s {
fmt.Printf("MergeSubTest(%d, %d, %d, %s) failed\n")
fmt.Printf("%%d != %%d\n", a%s, b%s)
panic("FAIL")
}
@ -42,10 +74,14 @@ func makeMergeTest(m1, m2, k int, size string) string {
func makeAllSizes(m1, m2, k int) string {
var tests string
tests += makeMergeTest(m1, m2, k, "8")
tests += makeMergeTest(m1, m2, k, "16")
tests += makeMergeTest(m1, m2, k, "32")
tests += makeMergeTest(m1, m2, k, "64")
tests += makeMergeAddTest(m1, m2, k, "8")
tests += makeMergeAddTest(m1, m2, k, "16")
tests += makeMergeAddTest(m1, m2, k, "32")
tests += makeMergeAddTest(m1, m2, k, "64")
tests += makeMergeSubTest(m1, m2, k, "8")
tests += makeMergeSubTest(m1, m2, k, "16")
tests += makeMergeSubTest(m1, m2, k, "32")
tests += makeMergeSubTest(m1, m2, k, "64")
tests += "\n"
return tests
}