about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
authorest31 <MTest31@outlook.com>2020-09-30 17:46:18 +0200
committerest31 <MTest31@outlook.com>2020-09-30 17:48:26 +0200
commit7f5008c8293a4d1eb3e4557a36a6bfdef34de284 (patch)
tree728d353ca824250abd9606cec36a4f0183b2979c /compiler
parentc6e4db620a7d2f569f11dcab627430921ea8aacf (diff)
Backport LLVM apfloat commit to rustc_apfloat
Backports LLVM commit: https://github.com/llvm/llvm-project/commit/e34bd1e0b03d20a506ada156d87e1b3a96d82fa2

Fixes #69532
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_apfloat/src/ieee.rs12
-rw-r--r--compiler/rustc_apfloat/tests/ieee.rs9
2 files changed, 21 insertions, 0 deletions
diff --git a/compiler/rustc_apfloat/src/ieee.rs b/compiler/rustc_apfloat/src/ieee.rs
index e3d941cad7a..aafd6dfb89a 100644
--- a/compiler/rustc_apfloat/src/ieee.rs
+++ b/compiler/rustc_apfloat/src/ieee.rs
@@ -1511,6 +1511,18 @@ impl<S: Semantics, T: Semantics> FloatConvert<IeeeFloat<T>> for IeeeFloat<S> {
                 sig::set_bit(&mut r.sig, T::PRECISION - 1);
             }
 
+            // If we are truncating NaN, it is possible that we shifted out all of the
+            // set bits in a signalling NaN payload. But NaN must remain NaN, so some
+            // bit in the significand must be set (otherwise it is Inf).
+            // This can only happen with sNaN. Set the 1st bit after the quiet bit,
+            // so that we still have an sNaN.
+            if r.sig[0] == 0 {
+                assert!(shift < 0, "Should not lose NaN payload on extend");
+                assert!(T::PRECISION >= 3, "Unexpectedly narrow significand");
+                assert!(*loses_info, "Missing payload should have set lost info");
+                sig::set_bit(&mut r.sig, T::PRECISION - 3);
+            }
+
             // gcc forces the Quiet bit on, which means (float)(double)(float_sNan)
             // does not give you back the same bits. This is dubious, and we
             // don't currently do it. You're really supposed to get
diff --git a/compiler/rustc_apfloat/tests/ieee.rs b/compiler/rustc_apfloat/tests/ieee.rs
index 2d8bb7d1e8e..0f3c99fba9e 100644
--- a/compiler/rustc_apfloat/tests/ieee.rs
+++ b/compiler/rustc_apfloat/tests/ieee.rs
@@ -567,6 +567,15 @@ fn fma() {
 }
 
 #[test]
+fn issue_69532() {
+    let f = Double::from_bits(0x7FF0_0000_0000_0001u64 as u128);
+    let mut loses_info = false;
+    let r: Single = f.convert(&mut loses_info).value;
+    assert!(loses_info);
+    assert!(r.is_nan());
+}
+
+#[test]
 fn min_num() {
     let f1 = Double::from_f64(1.0);
     let f2 = Double::from_f64(2.0);