about summary refs log tree commit diff
diff options
context:
space:
mode:
authorantoyo <antoyo@users.noreply.github.com>2025-02-12 13:21:04 -0500
committerGitHub <noreply@github.com>2025-02-12 13:21:04 -0500
commit7f05920a4a5d9feb97d3564f2da29ee891965a7a (patch)
tree416ad2d09e9dcdeee09a9da9b75f486d1e500738
parentcb499a29a8d26cf58e4b26ea70fd49c9486e7528 (diff)
parentc817210c70bed03133395890a09908ae06e44c94 (diff)
downloadrust-7f05920a4a5d9feb97d3564f2da29ee891965a7a.tar.gz
rust-7f05920a4a5d9feb97d3564f2da29ee891965a7a.zip
Merge pull request #627 from folkertdev/fix-unordered-compare
handle NaN in unordered comparisons
-rw-r--r--src/builder.rs45
-rw-r--r--tests/failing-ui-tests.txt1
-rw-r--r--tests/run/float.rs32
3 files changed, 76 insertions, 2 deletions
diff --git a/src/builder.rs b/src/builder.rs
index 8268819c5fa..fc2f6862c16 100644
--- a/src/builder.rs
+++ b/src/builder.rs
@@ -1284,7 +1284,50 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
     }
 
     fn fcmp(&mut self, op: RealPredicate, lhs: RValue<'gcc>, rhs: RValue<'gcc>) -> RValue<'gcc> {
-        self.context.new_comparison(self.location, op.to_gcc_comparison(), lhs, rhs)
+        // LLVM has a concept of "unordered compares", where eg ULT returns true if either the two
+        // arguments are unordered (i.e. either is NaN), or the lhs is less than the rhs. GCC does
+        // not natively have this concept, so in some cases we must manually handle NaNs
+        let must_handle_nan = match op {
+            RealPredicate::RealPredicateFalse => unreachable!(),
+            RealPredicate::RealOEQ => false,
+            RealPredicate::RealOGT => false,
+            RealPredicate::RealOGE => false,
+            RealPredicate::RealOLT => false,
+            RealPredicate::RealOLE => false,
+            RealPredicate::RealONE => false,
+            RealPredicate::RealORD => unreachable!(),
+            RealPredicate::RealUNO => unreachable!(),
+            RealPredicate::RealUEQ => false,
+            RealPredicate::RealUGT => true,
+            RealPredicate::RealUGE => true,
+            RealPredicate::RealULT => true,
+            RealPredicate::RealULE => true,
+            RealPredicate::RealUNE => false,
+            RealPredicate::RealPredicateTrue => unreachable!(),
+        };
+
+        let cmp = self.context.new_comparison(self.location, op.to_gcc_comparison(), lhs, rhs);
+
+        if must_handle_nan {
+            let is_nan = self.context.new_binary_op(
+                self.location,
+                BinaryOp::LogicalOr,
+                self.cx.bool_type,
+                // compare a value to itself to check whether it is NaN
+                self.context.new_comparison(self.location, ComparisonOp::NotEquals, lhs, lhs),
+                self.context.new_comparison(self.location, ComparisonOp::NotEquals, rhs, rhs),
+            );
+
+            self.context.new_binary_op(
+                self.location,
+                BinaryOp::LogicalOr,
+                self.cx.bool_type,
+                is_nan,
+                cmp,
+            )
+        } else {
+            cmp
+        }
     }
 
     /* Miscellaneous instructions */
diff --git a/tests/failing-ui-tests.txt b/tests/failing-ui-tests.txt
index 082958bfe1f..579ac3749e9 100644
--- a/tests/failing-ui-tests.txt
+++ b/tests/failing-ui-tests.txt
@@ -5,7 +5,6 @@ tests/ui/asm/x86_64/multiple-clobber-abi.rs
 tests/ui/functions-closures/parallel-codegen-closures.rs
 tests/ui/linkage-attr/linkage1.rs
 tests/ui/lto/dylib-works.rs
-tests/ui/numbers-arithmetic/saturating-float-casts.rs
 tests/ui/sepcomp/sepcomp-cci.rs
 tests/ui/sepcomp/sepcomp-extern.rs
 tests/ui/sepcomp/sepcomp-fns-backwards.rs
diff --git a/tests/run/float.rs b/tests/run/float.rs
new file mode 100644
index 00000000000..e0a57e6fed1
--- /dev/null
+++ b/tests/run/float.rs
@@ -0,0 +1,32 @@
+// Compiler:
+//
+// Run-time:
+//   status: 0
+
+#![feature(const_black_box)]
+
+/*
+ * Code
+ */
+
+fn main() {
+    use std::hint::black_box;
+
+    macro_rules! check {
+        ($ty:ty, $expr:expr) => {{
+            const EXPECTED: $ty = $expr;
+            assert_eq!($expr, EXPECTED);
+        }};
+    }
+
+    check!(i32, (black_box(0.0f32) as i32));
+
+    check!(u64, (black_box(f32::NAN) as u64));
+    check!(u128, (black_box(f32::NAN) as u128));
+
+    check!(i64, (black_box(f64::NAN) as i64));
+    check!(u64, (black_box(f64::NAN) as u64));
+
+    check!(i16, (black_box(f32::MIN) as i16));
+    check!(i16, (black_box(f32::MAX) as i16));
+}