Commit 102436e8 authored by Shenghou Ma's avatar Shenghou Ma Committed by Minux Ma

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: default avatarShenghou Ma <minux@golang.org>
Reviewed-on: https://go-review.googlesource.com/9570Reviewed-by: default avatarDave Cheney <dave@cheney.net>
Reviewed-by: default avatarKeith Randall <khr@golang.org>
parent 62ea2c90
...@@ -17,6 +17,7 @@ var F32to64 = f32to64 ...@@ -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 ...@@ -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 @@ ...@@ -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 ( ...@@ -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 { ...@@ -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
} }
// 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(),
}
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