qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 0/4] softfloat: fix floatx80 emulation bugs
@ 2020-05-04 23:36 Joseph Myers
  2020-05-04 23:37 ` [PATCH v2 1/4] softfloat: silence sNaN for conversions to/from floatx80 Joseph Myers
                   ` (4 more replies)
  0 siblings, 5 replies; 6+ messages in thread
From: Joseph Myers @ 2020-05-04 23:36 UTC (permalink / raw)
  To: qemu-devel, aurelien, peter.maydell, alex.bennee, pbonzini, rth,
	ehabkost

Attempting to run the GCC and glibc testsuites for i686 under QEMU
shows up a range of bugs in the x87 floating-point emulation.  This
series fixes some bugs (found both through those testsuites and
through subsequent code inspection) that appear to be in the softfloat
code itself rather than in the target/i386 code; I intend to address
such bugs in target/i386 separately.

Note that the floatx80 code is used for both i386 and m68k emulation,
but the two variants of the floatx80 format are not entirely
compatible.  Where the code should do different things for i386 and
m68k, it consistently only does the thing that is right for i386, not
the thing that is right for m68k, and my patches (specifically, the
second and third patches) continue this, doing the things that are
right for i386 but not for m68k.

Specifically, the formats have the following differences (based on
documentation; I don't have m68k hardware to test):

* For m68k, the explicit integer bit of the significand may be either
  0 or 1 for infinities and NaNs, but for i386 it must be 1 and having
  0 there makes it an invalid encoding.

* For i386, when the biased exponent is 0, this is interpreted the
  same way as a biased exponent of 0 in an IEEE format; an explicit
  integer bit 0 means a subnormal value while an explicit integer bit
  1 means a pseudo-denormal; the integer bit has value 2^-16382, as
  for a biased exponent of 1.  For m68k, a biased exponent of 0
  results in the integer bit having value 2^-16383, so values with
  integer bit 1 are normal and those with integer bit 0 are
  subnormal.  So the least subnormal value is 2^-16445 for i386 and
  2^-16446 for m68k.  (This means that the i386 floatx80 format meets
  the IEEE definition of an extended format, which requires a certain
  relation between the largest and smallest exponents, but the m68k
  floatx80 format does not meet that definition.)

  Patches 2 and 3 in this series deal with pseudo-denormals in a way
  that is correct for i386 but not for m68k; to support the m68k
  format properly, the new code in patch 3 could simply be disabled
  for m68k, but addition / subtraction would need more complicated
  changes to be correct for m68k and just disabling the new code would
  not make it correct (likewise, various changes elsewhere in the
  softfloat code would be needed to handle the m68k semantics for
  biased exponent 0).

This second version of the patch series includes i386-specific tests
for the bugs being fixed (written to be reasonably self-contained
rather than depending on libm functionality).  Given the previous
discussion of how some existing tests for floating-point operations
that are present but not enabled fail for unrelated reasons if enabled
for floatx80, this does not do anything regarding enabling such tests.

Joseph Myers (4):
  softfloat: silence sNaN for conversions to/from floatx80
  softfloat: fix floatx80 pseudo-denormal addition / subtraction
  softfloat: fix floatx80 pseudo-denormal comparisons
  softfloat: fix floatx80 pseudo-denormal round to integer

 fpu/softfloat.c                            | 37 ++++++++++---
 tests/tcg/i386/test-i386-pseudo-denormal.c | 38 +++++++++++++
 tests/tcg/i386/test-i386-snan-convert.c    | 63 ++++++++++++++++++++++
 3 files changed, 131 insertions(+), 7 deletions(-)
 create mode 100644 tests/tcg/i386/test-i386-pseudo-denormal.c
 create mode 100644 tests/tcg/i386/test-i386-snan-convert.c

-- 
2.17.1


-- 
Joseph S. Myers
joseph@codesourcery.com


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [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

* Re: [PATCH v2 0/4] softfloat: fix floatx80 emulation bugs
  2020-05-04 23:36 [PATCH v2 0/4] softfloat: fix floatx80 emulation bugs Joseph Myers
                   ` (3 preceding siblings ...)
  2020-05-04 23:40 ` [PATCH v2 4/4] softfloat: fix floatx80 pseudo-denormal round to integer Joseph Myers
@ 2020-05-05 17:58 ` Richard Henderson
  4 siblings, 0 replies; 6+ messages in thread
From: Richard Henderson @ 2020-05-05 17:58 UTC (permalink / raw)
  To: Joseph Myers, qemu-devel, aurelien, peter.maydell, alex.bennee,
	pbonzini, rth, ehabkost

On 5/4/20 4:36 PM, Joseph Myers wrote:
> Joseph Myers (4):
>   softfloat: silence sNaN for conversions to/from floatx80
>   softfloat: fix floatx80 pseudo-denormal addition / subtraction
>   softfloat: fix floatx80 pseudo-denormal comparisons
>   softfloat: fix floatx80 pseudo-denormal round to integer

Thanks.  Queued to a local branch with some more fpu cleanups.


r~



^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2020-05-05 18:06 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
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 ` [PATCH v2 3/4] softfloat: fix floatx80 pseudo-denormal comparisons 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

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).