Commit 63a72fd4 authored by Keith Randall's avatar Keith Randall

cmd/compile: strength-reduce floating point

x*2 -> x+x
x/c, c power of 2 -> x*(1/c)

Fixes #19827

Change-Id: I74c9f0b5b49b2ed26c0990314c7d1d5f9631b6f1
Reviewed-on: https://go-review.googlesource.com/39295
Run-TryBot: Keith Randall <khr@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarDavid Chase <drchase@google.com>
parent 095a62c3
......@@ -719,6 +719,35 @@ var linuxAMD64Tests = []*asmTest{
}`,
[]string{"\tADDQ\t[A-Z]"},
},
// Floating-point strength reduction
{
`
func f60(f float64) float64 {
return f * 2.0
}`,
[]string{"\tADDSD\t"},
},
{
`
func f62(f float64) float64 {
return f / 16.0
}`,
[]string{"\tMULSD\t"},
},
{
`
func f63(f float64) float64 {
return f / 0.125
}`,
[]string{"\tMULSD\t"},
},
{
`
func f64(f float64) float64 {
return f / 0.5
}`,
[]string{"\tADDSD\t"},
},
}
var linux386Tests = []*asmTest{
......
......@@ -133,3 +133,24 @@ func TestFloatConvert(t *testing.T) {
t.Errorf("cvt12 got %d, wanted 3", got)
}
}
var sinkFloat float64
func BenchmarkMul2(b *testing.B) {
for i := 0; i < b.N; i++ {
var m float64 = 1
for j := 0; j < 500; j++ {
m *= 2
}
sinkFloat = m
}
}
func BenchmarkMulNeg2(b *testing.B) {
for i := 0; i < b.N; i++ {
var m float64 = 1
for j := 0; j < 500; j++ {
m *= -2
}
sinkFloat = m
}
}
......@@ -1392,10 +1392,10 @@
(Mul32F (Const32F [f2i(-1)]) x) -> (Neg32F x)
(Mul64F x (Const64F [f2i(-1)])) -> (Neg64F x)
(Mul64F (Const64F [f2i(-1)]) x) -> (Neg64F x)
(Div32F x (Const32F [f2i(1)])) -> x
(Div64F x (Const64F [f2i(1)])) -> x
(Div32F x (Const32F [f2i(-1)])) -> (Neg32F x)
(Div64F x (Const64F [f2i(-1)])) -> (Neg32F x)
(Mul32F x (Const32F [f2i(2)])) -> (Add32F x x)
(Mul64F x (Const64F [f2i(2)])) -> (Add64F x x)
(Div32F x (Const32F <t> [c])) && reciprocalExact32(float32(i2f(c))) -> (Mul32F x (Const32F <t> [f2i(1/i2f(c))]))
(Div64F x (Const64F <t> [c])) && reciprocalExact64(i2f(c)) -> (Mul64F x (Const64F <t> [f2i(1/i2f(c))]))
(Sqrt (Const64F [c])) -> (Const64F [f2i(math.Sqrt(i2f(c)))])
......
......@@ -596,3 +596,47 @@ func isConstZero(v *Value) bool {
}
return false
}
// reciprocalExact64 reports whether 1/c is exactly representable.
func reciprocalExact64(c float64) bool {
b := math.Float64bits(c)
man := b & (1<<52 - 1)
if man != 0 {
return false // not a power of 2, denormal, or NaN
}
exp := b >> 52 & (1<<11 - 1)
// exponent bias is 0x3ff. So taking the reciprocal of a number
// changes the exponent to 0x7fe-exp.
switch exp {
case 0:
return false // ±0
case 0x7ff:
return false // ±inf
case 0x7fe:
return false // exponent is not representable
default:
return true
}
}
// reciprocalExact32 reports whether 1/c is exactly representable.
func reciprocalExact32(c float32) bool {
b := math.Float32bits(c)
man := b & (1<<23 - 1)
if man != 0 {
return false // not a power of 2, denormal, or NaN
}
exp := b >> 23 & (1<<8 - 1)
// exponent bias is 0x7f. So taking the reciprocal of a number
// changes the exponent to 0xfe-exp.
switch exp {
case 0:
return false // ±0
case 0xff:
return false // ±inf
case 0xfe:
return false // exponent is not representable
default:
return true
}
}
......@@ -3990,6 +3990,8 @@ func rewriteValuegeneric_OpDiv32(v *Value) bool {
return false
}
func rewriteValuegeneric_OpDiv32F(v *Value) bool {
b := v.Block
_ = b
// match: (Div32F (Const32F [c]) (Const32F [d]))
// cond:
// result: (Const32F [f2i(float64(i2f32(c) / i2f32(d)))])
......@@ -4008,37 +4010,25 @@ func rewriteValuegeneric_OpDiv32F(v *Value) bool {
v.AuxInt = f2i(float64(i2f32(c) / i2f32(d)))
return true
}
// match: (Div32F x (Const32F [f2i(1)]))
// cond:
// result: x
for {
x := v.Args[0]
v_1 := v.Args[1]
if v_1.Op != OpConst32F {
break
}
if v_1.AuxInt != f2i(1) {
break
}
v.reset(OpCopy)
v.Type = x.Type
v.AddArg(x)
return true
}
// match: (Div32F x (Const32F [f2i(-1)]))
// cond:
// result: (Neg32F x)
// match: (Div32F x (Const32F <t> [c]))
// cond: reciprocalExact32(float32(i2f(c)))
// result: (Mul32F x (Const32F <t> [f2i(1/i2f(c))]))
for {
x := v.Args[0]
v_1 := v.Args[1]
if v_1.Op != OpConst32F {
break
}
if v_1.AuxInt != f2i(-1) {
t := v_1.Type
c := v_1.AuxInt
if !(reciprocalExact32(float32(i2f(c)))) {
break
}
v.reset(OpNeg32F)
v.reset(OpMul32F)
v.AddArg(x)
v0 := b.NewValue0(v.Pos, OpConst32F, t)
v0.AuxInt = f2i(1 / i2f(c))
v.AddArg(v0)
return true
}
return false
......@@ -4465,6 +4455,8 @@ func rewriteValuegeneric_OpDiv64(v *Value) bool {
return false
}
func rewriteValuegeneric_OpDiv64F(v *Value) bool {
b := v.Block
_ = b
// match: (Div64F (Const64F [c]) (Const64F [d]))
// cond:
// result: (Const64F [f2i(i2f(c) / i2f(d))])
......@@ -4483,37 +4475,25 @@ func rewriteValuegeneric_OpDiv64F(v *Value) bool {
v.AuxInt = f2i(i2f(c) / i2f(d))
return true
}
// match: (Div64F x (Const64F [f2i(1)]))
// cond:
// result: x
// match: (Div64F x (Const64F <t> [c]))
// cond: reciprocalExact64(i2f(c))
// result: (Mul64F x (Const64F <t> [f2i(1/i2f(c))]))
for {
x := v.Args[0]
v_1 := v.Args[1]
if v_1.Op != OpConst64F {
break
}
if v_1.AuxInt != f2i(1) {
break
}
v.reset(OpCopy)
v.Type = x.Type
v.AddArg(x)
return true
}
// match: (Div64F x (Const64F [f2i(-1)]))
// cond:
// result: (Neg32F x)
for {
x := v.Args[0]
v_1 := v.Args[1]
if v_1.Op != OpConst64F {
break
}
if v_1.AuxInt != f2i(-1) {
t := v_1.Type
c := v_1.AuxInt
if !(reciprocalExact64(i2f(c))) {
break
}
v.reset(OpNeg32F)
v.reset(OpMul64F)
v.AddArg(x)
v0 := b.NewValue0(v.Pos, OpConst64F, t)
v0.AuxInt = f2i(1 / i2f(c))
v.AddArg(v0)
return true
}
return false
......@@ -8975,6 +8955,8 @@ func rewriteValuegeneric_OpMul32(v *Value) bool {
return false
}
func rewriteValuegeneric_OpMul32F(v *Value) bool {
b := v.Block
_ = b
// match: (Mul32F (Const32F [c]) (Const32F [d]))
// cond:
// result: (Const32F [f2i(float64(i2f32(c) * i2f32(d)))])
......@@ -9059,6 +9041,42 @@ func rewriteValuegeneric_OpMul32F(v *Value) bool {
v.AddArg(x)
return true
}
// match: (Mul32F x (Const32F [f2i(2)]))
// cond:
// result: (Add32F x x)
for {
x := v.Args[0]
v_1 := v.Args[1]
if v_1.Op != OpConst32F {
break
}
if v_1.AuxInt != f2i(2) {
break
}
v.reset(OpAdd32F)
v.AddArg(x)
v.AddArg(x)
return true
}
// match: (Mul32F x (Const32F [f2i(-2)]))
// cond:
// result: (Neg32F (Add32F <v.Type> x x))
for {
x := v.Args[0]
v_1 := v.Args[1]
if v_1.Op != OpConst32F {
break
}
if v_1.AuxInt != f2i(-2) {
break
}
v.reset(OpNeg32F)
v0 := b.NewValue0(v.Pos, OpAdd32F, v.Type)
v0.AddArg(x)
v0.AddArg(x)
v.AddArg(v0)
return true
}
return false
}
func rewriteValuegeneric_OpMul64(v *Value) bool {
......@@ -9286,6 +9304,8 @@ func rewriteValuegeneric_OpMul64(v *Value) bool {
return false
}
func rewriteValuegeneric_OpMul64F(v *Value) bool {
b := v.Block
_ = b
// match: (Mul64F (Const64F [c]) (Const64F [d]))
// cond:
// result: (Const64F [f2i(i2f(c) * i2f(d))])
......@@ -9370,6 +9390,42 @@ func rewriteValuegeneric_OpMul64F(v *Value) bool {
v.AddArg(x)
return true
}
// match: (Mul64F x (Const64F [f2i(2)]))
// cond:
// result: (Add64F x x)
for {
x := v.Args[0]
v_1 := v.Args[1]
if v_1.Op != OpConst64F {
break
}
if v_1.AuxInt != f2i(2) {
break
}
v.reset(OpAdd64F)
v.AddArg(x)
v.AddArg(x)
return true
}
// match: (Mul64F x (Const64F [f2i(-2)]))
// cond:
// result: (Neg64F (Add64F <v.Type> x x))
for {
x := v.Args[0]
v_1 := v.Args[1]
if v_1.Op != OpConst64F {
break
}
if v_1.AuxInt != f2i(-2) {
break
}
v.reset(OpNeg64F)
v0 := b.NewValue0(v.Pos, OpAdd64F, v.Type)
v0.AddArg(x)
v0.AddArg(x)
v.AddArg(v0)
return true
}
return false
}
func rewriteValuegeneric_OpMul8(v *Value) bool {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment