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>
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") }
--- /dev/null
+// 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")
+ }
+}
// 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
(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])
(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))])
(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))])
(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)))])
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:
}
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
// 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{}