mirror of
https://github.com/golang/go
synced 2024-11-23 20:30:04 -07:00
runtime: fix software FP regs corruption when emulating SQRT on ARM
When emulating ARM FSQRT instruction, the sqrt function itself should not use any floating point arithmetics, otherwise it will clobber the user software FP registers. Fortunately, the sqrt function only uses floating point instructions to test for corner cases, so it's easy to make that function does all it job using pure integer arithmetic only. I've verified that after this change, runtime.stepflt and runtime.sqrt doesn't contain any call to _sfloat. (Perhaps we should add //go:nosfloat to make the compiler enforce this?) Fixes #10641. Change-Id: Ida4742c49000fae4fea4649f28afde630ce4c576 Signed-off-by: Shenghou Ma <minux@golang.org> Reviewed-on: https://go-review.googlesource.com/9570 Reviewed-by: Dave Cheney <dave@cheney.net> Reviewed-by: Keith Randall <khr@golang.org>
This commit is contained in:
parent
62ea2c9093
commit
102436e800
@ -17,6 +17,7 @@ var F32to64 = f32to64
|
|||||||
var Fcmp64 = fcmp64
|
var Fcmp64 = fcmp64
|
||||||
var Fintto64 = fintto64
|
var Fintto64 = fintto64
|
||||||
var F64toint = f64toint
|
var F64toint = f64toint
|
||||||
|
var Sqrt = sqrt
|
||||||
|
|
||||||
var Entersyscall = entersyscall
|
var Entersyscall = entersyscall
|
||||||
var Exitsyscall = exitsyscall
|
var Exitsyscall = exitsyscall
|
||||||
|
@ -437,8 +437,7 @@ stage3: // regd, regm are 4bit variables
|
|||||||
break
|
break
|
||||||
|
|
||||||
case 0xeeb10bc0: // D[regd] = sqrt D[regm]
|
case 0xeeb10bc0: // D[regd] = sqrt D[regm]
|
||||||
uval = float64bits(sqrt(float64frombits(fgetd(regm))))
|
fputd(regd, sqrt(fgetd(regm)))
|
||||||
fputd(regd, uval)
|
|
||||||
|
|
||||||
if fptrace > 0 {
|
if fptrace > 0 {
|
||||||
print("*** D[", regd, "] = sqrt D[", regm, "] ", hex(m.freghi[regd]), "-", hex(m.freglo[regd]), "\n")
|
print("*** D[", regd, "] = sqrt D[", regm, "] ", hex(m.freghi[regd]), "-", hex(m.freglo[regd]), "\n")
|
||||||
|
@ -3,11 +3,12 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// Copy of math/sqrt.go, here for use by ARM softfloat.
|
// Copy of math/sqrt.go, here for use by ARM softfloat.
|
||||||
|
// Modified to not use any floating point arithmetic so
|
||||||
|
// that we don't clobber any floating-point registers
|
||||||
|
// while emulating the sqrt instruction.
|
||||||
|
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import "unsafe"
|
|
||||||
|
|
||||||
// The original C code and the long comment below are
|
// The original C code and the long comment below are
|
||||||
// from FreeBSD's /usr/src/lib/msun/src/e_sqrt.c and
|
// from FreeBSD's /usr/src/lib/msun/src/e_sqrt.c and
|
||||||
// came with this notice. The go code is a simplified
|
// came with this notice. The go code is a simplified
|
||||||
@ -89,21 +90,30 @@ const (
|
|||||||
float64Mask = 0x7FF
|
float64Mask = 0x7FF
|
||||||
float64Shift = 64 - 11 - 1
|
float64Shift = 64 - 11 - 1
|
||||||
float64Bias = 1023
|
float64Bias = 1023
|
||||||
|
float64NaN = 0x7FF8000000000001
|
||||||
|
float64Inf = 0x7FF0000000000000
|
||||||
maxFloat64 = 1.797693134862315708145274237317043567981e+308 // 2**1023 * (2**53 - 1) / 2**52
|
maxFloat64 = 1.797693134862315708145274237317043567981e+308 // 2**1023 * (2**53 - 1) / 2**52
|
||||||
)
|
)
|
||||||
|
|
||||||
func float64bits(f float64) uint64 { return *(*uint64)(unsafe.Pointer(&f)) }
|
// isnanu returns whether ix represents a NaN floating point number.
|
||||||
func float64frombits(b uint64) float64 { return *(*float64)(unsafe.Pointer(&b)) }
|
func isnanu(ix uint64) bool {
|
||||||
|
exp := (ix >> float64Shift) & float64Mask
|
||||||
|
sig := ix << (64 - float64Shift) >> (64 - float64Shift)
|
||||||
|
return exp == float64Mask && sig != 0
|
||||||
|
}
|
||||||
|
|
||||||
func sqrt(x float64) float64 {
|
func sqrt(ix uint64) uint64 {
|
||||||
// special cases
|
// special cases
|
||||||
switch {
|
switch {
|
||||||
case x == 0 || x != x || x > maxFloat64:
|
case ix == 0 || ix == 1<<63: // x == 0
|
||||||
return x
|
return ix
|
||||||
case x < 0:
|
case isnanu(ix): // x != x
|
||||||
return nan()
|
return ix
|
||||||
|
case ix&(1<<63) != 0: // x < 0
|
||||||
|
return float64NaN
|
||||||
|
case ix == float64Inf: // x > MaxFloat
|
||||||
|
return ix
|
||||||
}
|
}
|
||||||
ix := float64bits(x)
|
|
||||||
// normalize x
|
// normalize x
|
||||||
exp := int((ix >> float64Shift) & float64Mask)
|
exp := int((ix >> float64Shift) & float64Mask)
|
||||||
if exp == 0 { // subnormal x
|
if exp == 0 { // subnormal x
|
||||||
@ -139,5 +149,5 @@ func sqrt(x float64) float64 {
|
|||||||
q += q & 1 // round according to extra bit
|
q += q & 1 // round according to extra bit
|
||||||
}
|
}
|
||||||
ix = q>>1 + uint64(exp-1+float64Bias)<<float64Shift // significand + biased exponent
|
ix = q>>1 + uint64(exp-1+float64Bias)<<float64Shift // significand + biased exponent
|
||||||
return float64frombits(ix)
|
return ix
|
||||||
}
|
}
|
||||||
|
85
src/runtime/sqrt_test.go
Normal file
85
src/runtime/sqrt_test.go
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
// Copyright 2015 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.
|
||||||
|
|
||||||
|
// A copy of Sqrt tests from the math package to test the
|
||||||
|
// purely integer arithmetic implementaiton in sqrt.go.
|
||||||
|
|
||||||
|
package runtime_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"runtime"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func SqrtRT(x float64) float64 {
|
||||||
|
return math.Float64frombits(runtime.Sqrt(math.Float64bits(x)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSqrt(t *testing.T) {
|
||||||
|
for i := 0; i < len(vf); i++ {
|
||||||
|
a := math.Abs(vf[i])
|
||||||
|
if f := SqrtRT(a); sqrt[i] != f {
|
||||||
|
t.Errorf("Sqrt(%g) = %g, want %g", a, f, sqrt[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i := 0; i < len(vfsqrtSC); i++ {
|
||||||
|
if f := SqrtRT(vfsqrtSC[i]); !alike(sqrtSC[i], f) {
|
||||||
|
t.Errorf("Sqrt(%g) = %g, want %g", vfsqrtSC[i], f, sqrtSC[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func alike(a, b float64) bool {
|
||||||
|
switch {
|
||||||
|
case math.IsNaN(a) && math.IsNaN(b):
|
||||||
|
return true
|
||||||
|
case a == b:
|
||||||
|
return math.Signbit(a) == math.Signbit(b)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var vf = []float64{
|
||||||
|
4.9790119248836735e+00,
|
||||||
|
7.7388724745781045e+00,
|
||||||
|
-2.7688005719200159e-01,
|
||||||
|
-5.0106036182710749e+00,
|
||||||
|
9.6362937071984173e+00,
|
||||||
|
2.9263772392439646e+00,
|
||||||
|
5.2290834314593066e+00,
|
||||||
|
2.7279399104360102e+00,
|
||||||
|
1.8253080916808550e+00,
|
||||||
|
-8.6859247685756013e+00,
|
||||||
|
}
|
||||||
|
|
||||||
|
var sqrt = []float64{
|
||||||
|
2.2313699659365484748756904e+00,
|
||||||
|
2.7818829009464263511285458e+00,
|
||||||
|
5.2619393496314796848143251e-01,
|
||||||
|
2.2384377628763938724244104e+00,
|
||||||
|
3.1042380236055381099288487e+00,
|
||||||
|
1.7106657298385224403917771e+00,
|
||||||
|
2.286718922705479046148059e+00,
|
||||||
|
1.6516476350711159636222979e+00,
|
||||||
|
1.3510396336454586262419247e+00,
|
||||||
|
2.9471892997524949215723329e+00,
|
||||||
|
}
|
||||||
|
|
||||||
|
var vfsqrtSC = []float64{
|
||||||
|
math.Inf(-1),
|
||||||
|
-math.Pi,
|
||||||
|
math.Copysign(0, -1),
|
||||||
|
0,
|
||||||
|
math.Inf(1),
|
||||||
|
math.NaN(),
|
||||||
|
}
|
||||||
|
var sqrtSC = []float64{
|
||||||
|
math.NaN(),
|
||||||
|
math.NaN(),
|
||||||
|
math.Copysign(0, -1),
|
||||||
|
0,
|
||||||
|
math.Inf(1),
|
||||||
|
math.NaN(),
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user