* [PATCH v2 1/4] softfloat: silence sNaN for conversions to/from floatx80
2020-05-04 23:36 [PATCH v2 0/4] softfloat: fix floatx80 emulation bugs Joseph Myers
@ 2020-05-04 23:37 ` Joseph Myers
2020-05-04 23:38 ` [PATCH v2 2/4] softfloat: fix floatx80 pseudo-denormal addition / subtraction Joseph Myers
` (3 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Joseph Myers @ 2020-05-04 23:37 UTC (permalink / raw)
To: qemu-devel, aurelien, peter.maydell, alex.bennee, pbonzini, rth,
ehabkost
Conversions between IEEE floating-point formats should convert
signaling NaNs to quiet NaNs. Most of those in QEMU's softfloat code
do so, but those for floatx80 fail to. Fix those conversions to
silence signaling NaNs as well.
Signed-off-by: Joseph Myers <joseph@codesourcery.com>
---
fpu/softfloat.c | 24 +++++++---
tests/tcg/i386/test-i386-snan-convert.c | 63 +++++++++++++++++++++++++
2 files changed, 81 insertions(+), 6 deletions(-)
create mode 100644 tests/tcg/i386/test-i386-snan-convert.c
diff --git a/fpu/softfloat.c b/fpu/softfloat.c
index ae6ba71854..ac116c70b8 100644
--- a/fpu/softfloat.c
+++ b/fpu/softfloat.c
@@ -4498,7 +4498,9 @@ floatx80 float32_to_floatx80(float32 a, float_status *status)
aSign = extractFloat32Sign( a );
if ( aExp == 0xFF ) {
if (aSig) {
- return commonNaNToFloatx80(float32ToCommonNaN(a, status), status);
+ floatx80 res = commonNaNToFloatx80(float32ToCommonNaN(a, status),
+ status);
+ return floatx80_silence_nan(res, status);
}
return packFloatx80(aSign,
floatx80_infinity_high,
@@ -5016,7 +5018,9 @@ floatx80 float64_to_floatx80(float64 a, float_status *status)
aSign = extractFloat64Sign( a );
if ( aExp == 0x7FF ) {
if (aSig) {
- return commonNaNToFloatx80(float64ToCommonNaN(a, status), status);
+ floatx80 res = commonNaNToFloatx80(float64ToCommonNaN(a, status),
+ status);
+ return floatx80_silence_nan(res, status);
}
return packFloatx80(aSign,
floatx80_infinity_high,
@@ -5618,7 +5622,9 @@ float32 floatx80_to_float32(floatx80 a, float_status *status)
aSign = extractFloatx80Sign( a );
if ( aExp == 0x7FFF ) {
if ( (uint64_t) ( aSig<<1 ) ) {
- return commonNaNToFloat32(floatx80ToCommonNaN(a, status), status);
+ float32 res = commonNaNToFloat32(floatx80ToCommonNaN(a, status),
+ status);
+ return float32_silence_nan(res, status);
}
return packFloat32( aSign, 0xFF, 0 );
}
@@ -5650,7 +5656,9 @@ float64 floatx80_to_float64(floatx80 a, float_status *status)
aSign = extractFloatx80Sign( a );
if ( aExp == 0x7FFF ) {
if ( (uint64_t) ( aSig<<1 ) ) {
- return commonNaNToFloat64(floatx80ToCommonNaN(a, status), status);
+ float64 res = commonNaNToFloat64(floatx80ToCommonNaN(a, status),
+ status);
+ return float64_silence_nan(res, status);
}
return packFloat64( aSign, 0x7FF, 0 );
}
@@ -5681,7 +5689,9 @@ float128 floatx80_to_float128(floatx80 a, float_status *status)
aExp = extractFloatx80Exp( a );
aSign = extractFloatx80Sign( a );
if ( ( aExp == 0x7FFF ) && (uint64_t) ( aSig<<1 ) ) {
- return commonNaNToFloat128(floatx80ToCommonNaN(a, status), status);
+ float128 res = commonNaNToFloat128(floatx80ToCommonNaN(a, status),
+ status);
+ return float128_silence_nan(res, status);
}
shift128Right( aSig<<1, 0, 16, &zSig0, &zSig1 );
return packFloat128( aSign, aExp, zSig0, zSig1 );
@@ -6959,7 +6969,9 @@ floatx80 float128_to_floatx80(float128 a, float_status *status)
aSign = extractFloat128Sign( a );
if ( aExp == 0x7FFF ) {
if ( aSig0 | aSig1 ) {
- return commonNaNToFloatx80(float128ToCommonNaN(a, status), status);
+ floatx80 res = commonNaNToFloatx80(float128ToCommonNaN(a, status),
+ status);
+ return floatx80_silence_nan(res, status);
}
return packFloatx80(aSign, floatx80_infinity_high,
floatx80_infinity_low);
diff --git a/tests/tcg/i386/test-i386-snan-convert.c b/tests/tcg/i386/test-i386-snan-convert.c
new file mode 100644
index 0000000000..ed6d535ce2
--- /dev/null
+++ b/tests/tcg/i386/test-i386-snan-convert.c
@@ -0,0 +1,63 @@
+/* Test conversions of signaling NaNs to and from long double. */
+
+#include <stdint.h>
+#include <stdio.h>
+
+volatile float f_res;
+volatile double d_res;
+volatile long double ld_res;
+
+volatile float f_snan = __builtin_nansf("");
+volatile double d_snan = __builtin_nans("");
+volatile long double ld_snan = __builtin_nansl("");
+
+int issignaling_f(float x)
+{
+ union { float f; uint32_t u; } u = { .f = x };
+ return (u.u & 0x7fffffff) > 0x7f800000 && (u.u & 0x400000) == 0;
+}
+
+int issignaling_d(double x)
+{
+ union { double d; uint64_t u; } u = { .d = x };
+ return (((u.u & UINT64_C(0x7fffffffffffffff)) >
+ UINT64_C(0x7ff0000000000000)) &&
+ (u.u & UINT64_C(0x8000000000000)) == 0);
+}
+
+int issignaling_ld(long double x)
+{
+ union {
+ long double ld;
+ struct { uint64_t sig; uint16_t sign_exp; } s;
+ } u = { .ld = x };
+ return ((u.s.sign_exp & 0x7fff) == 0x7fff &&
+ (u.s.sig >> 63) != 0 &&
+ (u.s.sig & UINT64_C(0x4000000000000000)) == 0);
+}
+
+int main(void)
+{
+ int ret = 0;
+ ld_res = f_snan;
+ if (issignaling_ld(ld_res)) {
+ printf("FAIL: float -> long double\n");
+ ret = 1;
+ }
+ ld_res = d_snan;
+ if (issignaling_ld(ld_res)) {
+ printf("FAIL: double -> long double\n");
+ ret = 1;
+ }
+ f_res = ld_snan;
+ if (issignaling_d(f_res)) {
+ printf("FAIL: long double -> float\n");
+ ret = 1;
+ }
+ d_res = ld_snan;
+ if (issignaling_d(d_res)) {
+ printf("FAIL: long double -> double\n");
+ ret = 1;
+ }
+ return ret;
+}
--
2.17.1
--
Joseph S. Myers
joseph@codesourcery.com
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v2 2/4] softfloat: fix floatx80 pseudo-denormal addition / subtraction
2020-05-04 23:36 [PATCH v2 0/4] softfloat: fix floatx80 emulation bugs Joseph Myers
2020-05-04 23:37 ` [PATCH v2 1/4] softfloat: silence sNaN for conversions to/from floatx80 Joseph Myers
@ 2020-05-04 23:38 ` Joseph Myers
2020-05-04 23:39 ` [PATCH v2 3/4] softfloat: fix floatx80 pseudo-denormal comparisons Joseph Myers
` (2 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Joseph Myers @ 2020-05-04 23:38 UTC (permalink / raw)
To: qemu-devel, aurelien, peter.maydell, alex.bennee, pbonzini, rth,
ehabkost
The softfloat function addFloatx80Sigs, used for addition of values
with the same sign and subtraction of values with opposite sign, fails
to handle the case where the two values both have biased exponent zero
and there is a carry resulting from adding the significands, which can
occur if one or both values are pseudo-denormals (biased exponent
zero, explicit integer bit 1). Add a check for that case, so making
the results match those seen on x86 hardware for pseudo-denormals.
Signed-off-by: Joseph Myers <joseph@codesourcery.com>
---
fpu/softfloat.c | 6 ++++++
tests/tcg/i386/test-i386-pseudo-denormal.c | 24 ++++++++++++++++++++++
2 files changed, 30 insertions(+)
create mode 100644 tests/tcg/i386/test-i386-pseudo-denormal.c
diff --git a/fpu/softfloat.c b/fpu/softfloat.c
index ac116c70b8..6094d267b5 100644
--- a/fpu/softfloat.c
+++ b/fpu/softfloat.c
@@ -5866,6 +5866,12 @@ static floatx80 addFloatx80Sigs(floatx80 a, floatx80 b, flag zSign,
zSig1 = 0;
zSig0 = aSig + bSig;
if ( aExp == 0 ) {
+ if ((aSig | bSig) & UINT64_C(0x8000000000000000) && zSig0 < aSig) {
+ /* At least one of the values is a pseudo-denormal,
+ * and there is a carry out of the result. */
+ zExp = 1;
+ goto shiftRight1;
+ }
if (zSig0 == 0) {
return packFloatx80(zSign, 0, 0);
}
diff --git a/tests/tcg/i386/test-i386-pseudo-denormal.c b/tests/tcg/i386/test-i386-pseudo-denormal.c
new file mode 100644
index 0000000000..cfa2a500b0
--- /dev/null
+++ b/tests/tcg/i386/test-i386-pseudo-denormal.c
@@ -0,0 +1,24 @@
+/* Test pseudo-denormal operations. */
+
+#include <stdint.h>
+#include <stdio.h>
+
+union u {
+ struct { uint64_t sig; uint16_t sign_exp; } s;
+ long double ld;
+};
+
+volatile union u ld_pseudo_m16382 = { .s = { UINT64_C(1) << 63, 0 } };
+
+volatile long double ld_res;
+
+int main(void)
+{
+ int ret = 0;
+ ld_res = ld_pseudo_m16382.ld + ld_pseudo_m16382.ld;
+ if (ld_res != 0x1p-16381L) {
+ printf("FAIL: pseudo-denormal add\n");
+ ret = 1;
+ }
+ return ret;
+}
--
2.17.1
--
Joseph S. Myers
joseph@codesourcery.com
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v2 3/4] softfloat: fix floatx80 pseudo-denormal comparisons
2020-05-04 23:36 [PATCH v2 0/4] softfloat: fix floatx80 emulation bugs Joseph Myers
2020-05-04 23:37 ` [PATCH v2 1/4] softfloat: silence sNaN for conversions to/from floatx80 Joseph Myers
2020-05-04 23:38 ` [PATCH v2 2/4] softfloat: fix floatx80 pseudo-denormal addition / subtraction Joseph Myers
@ 2020-05-04 23:39 ` Joseph Myers
2020-05-04 23:40 ` [PATCH v2 4/4] softfloat: fix floatx80 pseudo-denormal round to integer Joseph Myers
2020-05-05 17:58 ` [PATCH v2 0/4] softfloat: fix floatx80 emulation bugs Richard Henderson
4 siblings, 0 replies; 6+ messages in thread
From: Joseph Myers @ 2020-05-04 23:39 UTC (permalink / raw)
To: qemu-devel, aurelien, peter.maydell, alex.bennee, pbonzini, rth,
ehabkost
The softfloat floatx80 comparisons fail to allow for pseudo-denormals,
which should compare equal to corresponding values with biased
exponent 1 rather than 0. Add an adjustment for that case when
comparing numbers with the same sign.
Note that this fix only changes floatx80_compare_internal, not the
other more specific comparison operations. That is the only
comparison function for floatx80 used in the i386 port, which is the
only supported port with these pseudo-denormal semantics.
Signed-off-by: Joseph Myers <joseph@codesourcery.com>
---
fpu/softfloat.c | 5 +++++
tests/tcg/i386/test-i386-pseudo-denormal.c | 4 ++++
2 files changed, 9 insertions(+)
diff --git a/fpu/softfloat.c b/fpu/softfloat.c
index 6094d267b5..8e9c714e6f 100644
--- a/fpu/softfloat.c
+++ b/fpu/softfloat.c
@@ -7966,6 +7966,11 @@ static inline int floatx80_compare_internal(floatx80 a, floatx80 b,
return 1 - (2 * aSign);
}
} else {
+ /* Normalize pseudo-denormals before comparison. */
+ if ((a.high & 0x7fff) == 0 && a.low & UINT64_C(0x8000000000000000))
+ ++a.high;
+ if ((b.high & 0x7fff) == 0 && b.low & UINT64_C(0x8000000000000000))
+ ++b.high;
if (a.low == b.low && a.high == b.high) {
return float_relation_equal;
} else {
diff --git a/tests/tcg/i386/test-i386-pseudo-denormal.c b/tests/tcg/i386/test-i386-pseudo-denormal.c
index cfa2a500b0..acf2b9cf03 100644
--- a/tests/tcg/i386/test-i386-pseudo-denormal.c
+++ b/tests/tcg/i386/test-i386-pseudo-denormal.c
@@ -20,5 +20,9 @@ int main(void)
printf("FAIL: pseudo-denormal add\n");
ret = 1;
}
+ if (ld_pseudo_m16382.ld != 0x1p-16382L) {
+ printf("FAIL: pseudo-denormal compare\n");
+ ret = 1;
+ }
return ret;
}
--
2.17.1
--
Joseph S. Myers
joseph@codesourcery.com
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v2 4/4] softfloat: fix floatx80 pseudo-denormal round to integer
2020-05-04 23:36 [PATCH v2 0/4] softfloat: fix floatx80 emulation bugs Joseph Myers
` (2 preceding siblings ...)
2020-05-04 23:39 ` [PATCH v2 3/4] softfloat: fix floatx80 pseudo-denormal comparisons Joseph Myers
@ 2020-05-04 23:40 ` Joseph Myers
2020-05-05 17:58 ` [PATCH v2 0/4] softfloat: fix floatx80 emulation bugs Richard Henderson
4 siblings, 0 replies; 6+ messages in thread
From: Joseph Myers @ 2020-05-04 23:40 UTC (permalink / raw)
To: qemu-devel, aurelien, peter.maydell, alex.bennee, pbonzini, rth,
ehabkost
The softfloat function floatx80_round_to_int incorrectly handles the
case of a pseudo-denormal where only the high bit of the significand
is set, ignoring that bit (treating the number as an exact zero)
rather than treating the number as an alternative representation of
+/- 2^-16382 (which may round to +/- 1 depending on the rounding mode)
as hardware does. Fix this check (simplifying the code in the
process).
Signed-off-by: Joseph Myers <joseph@codesourcery.com>
---
fpu/softfloat.c | 2 +-
tests/tcg/i386/test-i386-pseudo-denormal.c | 10 ++++++++++
2 files changed, 11 insertions(+), 1 deletion(-)
diff --git a/fpu/softfloat.c b/fpu/softfloat.c
index 8e9c714e6f..e29b07542a 100644
--- a/fpu/softfloat.c
+++ b/fpu/softfloat.c
@@ -5741,7 +5741,7 @@ floatx80 floatx80_round_to_int(floatx80 a, float_status *status)
}
if ( aExp < 0x3FFF ) {
if ( ( aExp == 0 )
- && ( (uint64_t) ( extractFloatx80Frac( a )<<1 ) == 0 ) ) {
+ && ( (uint64_t) ( extractFloatx80Frac( a ) ) == 0 ) ) {
return a;
}
status->float_exception_flags |= float_flag_inexact;
diff --git a/tests/tcg/i386/test-i386-pseudo-denormal.c b/tests/tcg/i386/test-i386-pseudo-denormal.c
index acf2b9cf03..00d510cf4a 100644
--- a/tests/tcg/i386/test-i386-pseudo-denormal.c
+++ b/tests/tcg/i386/test-i386-pseudo-denormal.c
@@ -14,6 +14,7 @@ volatile long double ld_res;
int main(void)
{
+ short cw;
int ret = 0;
ld_res = ld_pseudo_m16382.ld + ld_pseudo_m16382.ld;
if (ld_res != 0x1p-16381L) {
@@ -24,5 +25,14 @@ int main(void)
printf("FAIL: pseudo-denormal compare\n");
ret = 1;
}
+ /* Set round-upward. */
+ __asm__ volatile ("fnstcw %0" : "=m" (cw));
+ cw = (cw & ~0xc00) | 0x800;
+ __asm__ volatile ("fldcw %0" : : "m" (cw));
+ __asm__ ("frndint" : "=t" (ld_res) : "0" (ld_pseudo_m16382.ld));
+ if (ld_res != 1.0L) {
+ printf("FAIL: pseudo-denormal round-to-integer\n");
+ ret = 1;
+ }
return ret;
}
--
2.17.1
--
Joseph S. Myers
joseph@codesourcery.com
^ permalink raw reply related [flat|nested] 6+ messages in thread