1
0
mirror of https://github.com/golang/go synced 2024-11-12 10:20:27 -07:00

cmd/compile: precompute constant square roots

If a program wants to evaluate math.Sqrt for any constant value
(for example, math.Sqrt(3)), we can replace that expression with
its evaluation (1.7320508075688772) at compile time, instead of
generating a SQRT assembly command or equivalent.

Adds tests that math.Sqrt generates the correct values. I also
compiled a short program and verified that the Sqrt expression was
replaced by a constant value in the "after opt" step.

Adds a short doc to the top of generic.rules explaining what the file
does and how other files interact with it.

Fixes #15543.

Change-Id: I6b6e63ac61cec50763a09ba581024adeee03d4fa
Reviewed-on: https://go-review.googlesource.com/27457
Reviewed-by: Josh Bleecher Snyder <josharian@gmail.com>
Reviewed-by: Keith Randall <khr@golang.org>
This commit is contained in:
Kevin Burke 2016-08-20 22:05:47 -07:00 committed by Josh Bleecher Snyder
parent d00890b5f3
commit 8e24a98abe
5 changed files with 100 additions and 3 deletions

View File

@ -103,3 +103,5 @@ func TestSlice(t *testing.T) { runTest(t, "slice.go") }
func TestNamedReturn(t *testing.T) { runTest(t, "namedReturn.go") }
func TestDuplicateLoad(t *testing.T) { runTest(t, "dupLoad.go") }
func TestSqrt(t *testing.T) { runTest(t, "sqrt_const.go") }

View File

@ -0,0 +1,59 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"fmt"
"math"
)
var tests = [...]struct {
name string
in float64 // used for error messages, not an input
got float64
want float64
}{
{"sqrt0", 0, math.Sqrt(0), 0},
{"sqrt1", 1, math.Sqrt(1), 1},
{"sqrt2", 2, math.Sqrt(2), math.Sqrt2},
{"sqrt4", 4, math.Sqrt(4), 2},
{"sqrt100", 100, math.Sqrt(100), 10},
{"sqrt101", 101, math.Sqrt(101), 10.04987562112089},
}
var nanTests = [...]struct {
name string
in float64 // used for error messages, not an input
got float64
}{
{"sqrtNaN", math.NaN(), math.Sqrt(math.NaN())},
{"sqrtNegative", -1, math.Sqrt(-1)},
{"sqrtNegInf", math.Inf(-1), math.Sqrt(math.Inf(-1))},
}
var failed = false
func main() {
for _, test := range tests {
if test.got != test.want {
fmt.Printf("%s: math.Sqrt(%f): got %f, want %f\n", test.name, test.in, test.got, test.want)
failed = true
}
}
for _, test := range nanTests {
if math.IsNaN(test.got) != true {
fmt.Printf("%s: math.Sqrt(%f): got %f, want NaN\n", test.name, test.in, test.got)
failed = true
}
}
if got := math.Sqrt(math.Inf(1)); !math.IsInf(got, 1) {
fmt.Printf("math.Sqrt(+Inf), got %f, want +Inf\n", got)
failed = true
}
if failed {
panic("failed")
}
}

View File

@ -2,6 +2,19 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Simplifications that apply to all backend architectures. As an example, this
// Go source code
//
// y := 0 * x
//
// can be translated into y := 0 without losing any information, which saves a
// pointless multiplication instruction. Other .rules files in this directory
// (for example AMD64.rules) contain rules specific to the architecture in the
// filename. The rules here apply to every architecture.
//
// The code for parsing this file lives in rulegen.go; this file generates
// ssa/rewritegeneric.go.
// values are specified using the following format:
// (op <type> [auxint] {aux} arg0 arg1 ...)
// the type, aux, and auxint fields are optional
@ -46,7 +59,7 @@
(Add16 (Const16 [c]) (Const16 [d])) -> (Const16 [int64(int16(c+d))])
(Add32 (Const32 [c]) (Const32 [d])) -> (Const32 [int64(int32(c+d))])
(Add64 (Const64 [c]) (Const64 [d])) -> (Const64 [c+d])
(Add32F (Const32F [c]) (Const32F [d])) ->
(Add32F (Const32F [c]) (Const32F [d])) ->
(Const32F [f2i(float64(i2f32(c) + i2f32(d)))]) // ensure we combine the operands with 32 bit precision
(Add64F (Const64F [c]) (Const64F [d])) -> (Const64F [f2i(i2f(c) + i2f(d))])
(AddPtr <t> x (Const64 [c])) -> (OffPtr <t> x [c])
@ -55,7 +68,7 @@
(Sub16 (Const16 [c]) (Const16 [d])) -> (Const16 [int64(int16(c-d))])
(Sub32 (Const32 [c]) (Const32 [d])) -> (Const32 [int64(int32(c-d))])
(Sub64 (Const64 [c]) (Const64 [d])) -> (Const64 [c-d])
(Sub32F (Const32F [c]) (Const32F [d])) ->
(Sub32F (Const32F [c]) (Const32F [d])) ->
(Const32F [f2i(float64(i2f32(c) - i2f32(d)))])
(Sub64F (Const64F [c]) (Const64F [d])) -> (Const64F [f2i(i2f(c) - i2f(d))])
@ -63,7 +76,7 @@
(Mul16 (Const16 [c]) (Const16 [d])) -> (Const16 [int64(int16(c*d))])
(Mul32 (Const32 [c]) (Const32 [d])) -> (Const32 [int64(int32(c*d))])
(Mul64 (Const64 [c]) (Const64 [d])) -> (Const64 [c*d])
(Mul32F (Const32F [c]) (Const32F [d])) ->
(Mul32F (Const32F [c]) (Const32F [d])) ->
(Const32F [f2i(float64(i2f32(c) * i2f32(d)))])
(Mul64F (Const64F [c]) (Const64F [d])) -> (Const64F [f2i(i2f(c) * i2f(d))])
@ -860,3 +873,5 @@
(Div64F x (Const64F [f2i(1)])) -> x
(Div32F x (Const32F [f2i(-1)])) -> (Neg32F x)
(Div64F x (Const64F [f2i(-1)])) -> (Neg32F x)
(Sqrt (Const64F [c])) -> (Const64F [f2i(math.Sqrt(i2f(c)))])

View File

@ -316,6 +316,8 @@ func rewriteValuegeneric(v *Value, config *Config) bool {
return rewriteValuegeneric_OpSliceLen(v, config)
case OpSlicePtr:
return rewriteValuegeneric_OpSlicePtr(v, config)
case OpSqrt:
return rewriteValuegeneric_OpSqrt(v, config)
case OpStore:
return rewriteValuegeneric_OpStore(v, config)
case OpStringLen:
@ -9032,6 +9034,24 @@ func rewriteValuegeneric_OpSlicePtr(v *Value, config *Config) bool {
}
return false
}
func rewriteValuegeneric_OpSqrt(v *Value, config *Config) bool {
b := v.Block
_ = b
// match: (Sqrt (Const64F [c]))
// cond:
// result: (Const64F [f2i(math.Sqrt(i2f(c)))])
for {
v_0 := v.Args[0]
if v_0.Op != OpConst64F {
break
}
c := v_0.AuxInt
v.reset(OpConst64F)
v.AuxInt = f2i(math.Sqrt(i2f(c)))
return true
}
return false
}
func rewriteValuegeneric_OpStore(v *Value, config *Config) bool {
b := v.Block
_ = b

View File

@ -26,6 +26,7 @@ type Value struct {
// Auxiliary info for this value. The type of this information depends on the opcode and type.
// AuxInt is used for integer values, Aux is used for other values.
// Floats are stored in AuxInt using math.Float64bits(f).
AuxInt int64
Aux interface{}