about summary refs log tree commit diff
path: root/src/num.rs
diff options
context:
space:
mode:
authorbjorn3 <bjorn3@users.noreply.github.com>2022-03-25 20:24:47 +0100
committerbjorn3 <bjorn3@users.noreply.github.com>2022-03-25 20:25:11 +0100
commit3c030e2425bb1fdb165ac87797076072ec991970 (patch)
tree886f139db21d2f436586812bf834f2d72b4a7185 /src/num.rs
parentf3d97cce279fd2372aafec3761791b4110d70bf5 (diff)
downloadrust-3c030e2425bb1fdb165ac87797076072ec991970.tar.gz
rust-3c030e2425bb1fdb165ac87797076072ec991970.zip
Fix NaN handling of simd float min and max operations
Diffstat (limited to 'src/num.rs')
-rw-r--r--src/num.rs18
1 files changed, 18 insertions, 0 deletions
diff --git a/src/num.rs b/src/num.rs
index 545d390e269..4ce8adb182e 100644
--- a/src/num.rs
+++ b/src/num.rs
@@ -420,3 +420,21 @@ pub(crate) fn codegen_ptr_binop<'tcx>(
         CValue::by_val(fx.bcx.ins().bint(types::I8, res), fx.layout_of(fx.tcx.types.bool))
     }
 }
+
+// In Rust floating point min and max don't propagate NaN. In Cranelift they do however.
+// For this reason it is necessary to use `a.is_nan() ? b : (a >= b ? b : a)` for `minnumf*`
+// and `a.is_nan() ? b : (a <= b ? b : a)` for `maxnumf*`. NaN checks are done by comparing
+// a float against itself. Only in case of NaN is it not equal to itself.
+pub(crate) fn codegen_float_min(fx: &mut FunctionCx<'_, '_, '_>, a: Value, b: Value) -> Value {
+    let a_is_nan = fx.bcx.ins().fcmp(FloatCC::NotEqual, a, a);
+    let a_ge_b = fx.bcx.ins().fcmp(FloatCC::GreaterThanOrEqual, a, b);
+    let temp = fx.bcx.ins().select(a_ge_b, b, a);
+    fx.bcx.ins().select(a_is_nan, b, temp)
+}
+
+pub(crate) fn codegen_float_max(fx: &mut FunctionCx<'_, '_, '_>, a: Value, b: Value) -> Value {
+    let a_is_nan = fx.bcx.ins().fcmp(FloatCC::NotEqual, a, a);
+    let a_le_b = fx.bcx.ins().fcmp(FloatCC::LessThanOrEqual, a, b);
+    let temp = fx.bcx.ins().select(a_le_b, b, a);
+    fx.bcx.ins().select(a_is_nan, b, temp)
+}