diff mbox series

[1/4] softfloat: silence sNaN for conversions to/from floatx80

Message ID alpine.DEB.2.21.2005010036120.30535@digraph.polyomino.org.uk
State New
Headers show
Series softfloat: fix floatx80 emulation bugs | expand

Commit Message

Joseph Myers May 1, 2020, 12:37 a.m. UTC
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 ++++++++++++++++++------
 1 file changed, 18 insertions(+), 6 deletions(-)

Comments

Alex Bennée May 1, 2020, 3:45 p.m. UTC | #1
Joseph Myers <joseph@codesourcery.com> writes:

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

The threading from this seems to be a bit broken as it doesn't link to
the cover text. Also its a good idea to use get_maintainer.pl in your
workflow to ensure maintainers get CC'd so patches don't get lost in the
torrent that is qemu-devel.
Alex Bennée May 1, 2020, 4:17 p.m. UTC | #2
Joseph Myers <joseph@codesourcery.com> writes:

> 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.

I realised we hadn't enabled float-to-float tests for check-softfloat so
with this applied:

--8<---------------cut here---------------start------------->8---
tests/tcg: add check-softfloat-conv-f2f

Signed-off-by: Alex Bennée <alex.bennee@linaro.org>

1 file changed, 17 insertions(+), 1 deletion(-)
tests/Makefile.include | 18 +++++++++++++++++-

modified   tests/Makefile.include
@@ -687,7 +687,23 @@ test-softfloat = $(call quiet-command, \
 			(cat $2.out && exit 1;), \
 			"FLOAT TEST", $2)
 
-# Conversion Routines:
+# Conversion Routines: Float to Float
+# FIXME: f32_to_f128 (broken)
+check-softfloat-conv-f2f: $(FP_TEST_BIN)
+	$(call test-softfloat, \
+		f16_to_f32 f16_to_f64 \
+		f16_to_extF80 f16_to_f128 \
+		f32_to_f16 f32_to_f64 \
+		f32_to_extF80 \
+		f64_to_f16 f64_to_f32 \
+		f64_to_extF80 f64_to_f128 \
+		extF80_to_f16 extF80_to_f32 \
+		extF80_to_f64 extF80_to_f128 \
+		f128_to_f16 f128_to_f32 \
+		f128_to_f64 f128_to_extF80, \
+		float-to-float)
+
+# Conversion Routines: Float to Float
 # FIXME: i32_to_extF80 (broken), i64_to_extF80 (broken)
 #        ui32_to_f128 (not implemented), extF80_roundToInt (broken)
 #
--8<---------------cut here---------------end--------------->8---

I still see some failures for:

  f64_to_extF80
  f128_to_extF80

(as well as other conversions you've not touched). Ideally we want to
convert the extF80 and f128 code to use the newer FloatParts code which
is pretty robustly tested now. However the trick would be finding a way
to do that that doesn't involve break or regressing the performance for
the f16/32/64 cases. Just using a union causes all sorts of problems.

Anyway I'd settle for fixing the old style code and enabling the tests
we can.

>
> Signed-off-by: Joseph Myers <joseph@codesourcery.com>
> ---
>  fpu/softfloat.c | 24 ++++++++++++++++++------
>  1 file changed, 18 insertions(+), 6 deletions(-)
>
> 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);
> -- 
> 2.17.1
Richard Henderson May 1, 2020, 4:57 p.m. UTC | #3
On 5/1/20 9:17 AM, Alex Bennée wrote:
> Ideally we want to
> convert the extF80 and f128 code to use the newer FloatParts code which
> is pretty robustly tested now. However the trick would be finding a way
> to do that that doesn't involve break or regressing the performance for
> the f16/32/64 cases. Just using a union causes all sorts of problems.

Yep.  Doing this conversion is the only way we'll ever fix the issue that
Joseph points out for the difference between m68k and i686 fp format.

> Anyway I'd settle for fixing the old style code and enabling the tests
> we can.

Yep.


r~
Joseph Myers May 1, 2020, 5 p.m. UTC | #4
On Fri, 1 May 2020, Alex Bennée wrote:

> I still see some failures for:
> 
>   f64_to_extF80
>   f128_to_extF80

Running what I think are those tests, I see e.g.

./fp-test -s -l 1 -r all f64_to_extF80
>> Testing f64_to_extF80
768 tests total.
Errors found in f64_to_extF80:
-368.FFFF8000000FF
        => -3F68.FFFFC0000007F800 .....  expected -3F68.FFFFC00000000000 ....x

which looks like it's a test of the floatx80 format with 24-bit precision.

If that's what this is testing, then:

(a) float64_to_floatx80 would need, in 24-bit mode, to call 
roundAndPackFloatx80 rather than just packFloatx80, to get appropriate 
rounding;

(b) float128_to_floatx80 would need to use the dynamically specified 
rounding precision in its call to roundAndPackFloatx80 instead of 
hardcoded 80;

(c) but i386 instruction semantics are that a load of a double value into 
a floating-point register, in the 24-bit mode, does *not* convert the 
significand to 24-bit precision, but loads the full 53-bit-precision value 
into the register, so making such a change to float64_to_floatx80 would 
render it incorrect for i386 emulation without changes to the target/i386 
code to adjust the rounding precision used for loads;

(d) float128_to_floatx80 shouldn't actually be used by any QEMU target, 
because no supported CPU architecture has support for both formats in 
hardware (although I made my sNaN change to the conversions between them 
anyway for completeness).
diff mbox series

Patch

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);