about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRalf Jung <post@ralfj.de>2025-09-22 14:49:13 +0200
committerRalf Jung <post@ralfj.de>2025-09-22 14:52:35 +0200
commit37de09fed92a2814d93faff6e789c34d2a18718e (patch)
treea159efdea74cd14698703ca51ae7e29159dd3de2
parenta25896bc2785b234c8b093a25b7f7df34798aea8 (diff)
downloadrust-37de09fed92a2814d93faff6e789c34d2a18718e.tar.gz
rust-37de09fed92a2814d93faff6e789c34d2a18718e.zip
share the check_nondet helper as well
-rw-r--r--src/tools/miri/tests/pass/float.rs194
-rw-r--r--src/tools/miri/tests/pass/intrinsics/fmuladd_nondeterministic.rs103
-rw-r--r--src/tools/miri/tests/utils/mod.rs15
3 files changed, 138 insertions, 174 deletions
diff --git a/src/tools/miri/tests/pass/float.rs b/src/tools/miri/tests/pass/float.rs
index 3ce5ea8356b..8570addeedd 100644
--- a/src/tools/miri/tests/pass/float.rs
+++ b/src/tools/miri/tests/pass/float.rs
@@ -8,12 +8,16 @@
 #![allow(internal_features)]
 #![allow(unnecessary_transmutes)]
 
+#[path = "../utils/mod.rs"]
+mod utils;
 use std::any::type_name;
 use std::cmp::min;
 use std::fmt::{Debug, Display, LowerHex};
 use std::hint::black_box;
 use std::{f32, f64};
 
+use utils::check_nondet;
+
 /// Compare the two floats, allowing for $ulp many ULPs of error.
 ///
 /// ULP means "Units in the Last Place" or "Units of Least Precision".
@@ -1429,29 +1433,14 @@ fn test_fmuladd() {
 
 /// `min` and `max` on equal arguments are non-deterministic.
 fn test_min_max_nondet() {
-    /// Ensure that if we call the closure often enough, we see both `true` and `false.`
-    #[track_caller]
-    fn ensure_both(f: impl Fn() -> bool) {
-        let rounds = 32;
-        let first = f();
-        for _ in 1..rounds {
-            if f() != first {
-                // We saw two different values!
-                return;
-            }
-        }
-        // We saw the same thing N times.
-        panic!("expected non-determinism, got {rounds} times the same result: {first:?}");
-    }
-
-    ensure_both(|| f16::min(0.0, -0.0).is_sign_positive());
-    ensure_both(|| f16::max(0.0, -0.0).is_sign_positive());
-    ensure_both(|| f32::min(0.0, -0.0).is_sign_positive());
-    ensure_both(|| f32::max(0.0, -0.0).is_sign_positive());
-    ensure_both(|| f64::min(0.0, -0.0).is_sign_positive());
-    ensure_both(|| f64::max(0.0, -0.0).is_sign_positive());
-    ensure_both(|| f128::min(0.0, -0.0).is_sign_positive());
-    ensure_both(|| f128::max(0.0, -0.0).is_sign_positive());
+    check_nondet(|| f16::min(0.0, -0.0).is_sign_positive());
+    check_nondet(|| f16::max(0.0, -0.0).is_sign_positive());
+    check_nondet(|| f32::min(0.0, -0.0).is_sign_positive());
+    check_nondet(|| f32::max(0.0, -0.0).is_sign_positive());
+    check_nondet(|| f64::min(0.0, -0.0).is_sign_positive());
+    check_nondet(|| f64::max(0.0, -0.0).is_sign_positive());
+    check_nondet(|| f128::min(0.0, -0.0).is_sign_positive());
+    check_nondet(|| f128::max(0.0, -0.0).is_sign_positive());
 }
 
 fn test_non_determinism() {
@@ -1461,35 +1450,20 @@ fn test_non_determinism() {
     };
     use std::{f32, f64};
 
-    /// Ensure that the operation is non-deterministic
-    #[track_caller]
-    fn ensure_nondet<T: PartialEq + std::fmt::Debug>(f: impl Fn() -> T) {
-        let rounds = 16;
-        let first = f();
-        for _ in 1..rounds {
-            if f() != first {
-                // We saw two different values!
-                return;
-            }
-        }
-        // We saw the same thing N times.
-        panic!("expected non-determinism, got {rounds} times the same result: {first:?}");
-    }
-
     macro_rules! test_operations_f {
         ($a:expr, $b:expr) => {
-            ensure_nondet(|| fadd_algebraic($a, $b));
-            ensure_nondet(|| fsub_algebraic($a, $b));
-            ensure_nondet(|| fmul_algebraic($a, $b));
-            ensure_nondet(|| fdiv_algebraic($a, $b));
-            ensure_nondet(|| frem_algebraic($a, $b));
+            check_nondet(|| fadd_algebraic($a, $b));
+            check_nondet(|| fsub_algebraic($a, $b));
+            check_nondet(|| fmul_algebraic($a, $b));
+            check_nondet(|| fdiv_algebraic($a, $b));
+            check_nondet(|| frem_algebraic($a, $b));
 
             unsafe {
-                ensure_nondet(|| fadd_fast($a, $b));
-                ensure_nondet(|| fsub_fast($a, $b));
-                ensure_nondet(|| fmul_fast($a, $b));
-                ensure_nondet(|| fdiv_fast($a, $b));
-                ensure_nondet(|| frem_fast($a, $b));
+                check_nondet(|| fadd_fast($a, $b));
+                check_nondet(|| fsub_fast($a, $b));
+                check_nondet(|| fmul_fast($a, $b));
+                check_nondet(|| fdiv_fast($a, $b));
+                check_nondet(|| frem_fast($a, $b));
             }
         };
     }
@@ -1499,70 +1473,70 @@ fn test_non_determinism() {
     }
     pub fn test_operations_f32(a: f32, b: f32) {
         test_operations_f!(a, b);
-        ensure_nondet(|| a.powf(b));
-        ensure_nondet(|| a.powi(2));
-        ensure_nondet(|| a.log(b));
-        ensure_nondet(|| a.exp());
-        ensure_nondet(|| 10f32.exp2());
-        ensure_nondet(|| f32::consts::E.ln());
-        ensure_nondet(|| 10f32.log10());
-        ensure_nondet(|| 8f32.log2());
-        ensure_nondet(|| 1f32.ln_1p());
-        ensure_nondet(|| 27.0f32.cbrt());
-        ensure_nondet(|| 3.0f32.hypot(4.0f32));
-        ensure_nondet(|| 1f32.sin());
-        ensure_nondet(|| 1f32.cos());
+        check_nondet(|| a.powf(b));
+        check_nondet(|| a.powi(2));
+        check_nondet(|| a.log(b));
+        check_nondet(|| a.exp());
+        check_nondet(|| 10f32.exp2());
+        check_nondet(|| f32::consts::E.ln());
+        check_nondet(|| 10f32.log10());
+        check_nondet(|| 8f32.log2());
+        check_nondet(|| 1f32.ln_1p());
+        check_nondet(|| 27.0f32.cbrt());
+        check_nondet(|| 3.0f32.hypot(4.0f32));
+        check_nondet(|| 1f32.sin());
+        check_nondet(|| 1f32.cos());
         // On i686-pc-windows-msvc , these functions are implemented by calling the `f64` version,
         // which means the little rounding errors Miri introduces are discarded by the cast down to
         // `f32`. Just skip the test for them.
         if !cfg!(all(target_os = "windows", target_env = "msvc", target_arch = "x86")) {
-            ensure_nondet(|| 1.0f32.tan());
-            ensure_nondet(|| 1.0f32.asin());
-            ensure_nondet(|| 5.0f32.acos());
-            ensure_nondet(|| 1.0f32.atan());
-            ensure_nondet(|| 1.0f32.atan2(2.0f32));
-            ensure_nondet(|| 1.0f32.sinh());
-            ensure_nondet(|| 1.0f32.cosh());
-            ensure_nondet(|| 1.0f32.tanh());
+            check_nondet(|| 1.0f32.tan());
+            check_nondet(|| 1.0f32.asin());
+            check_nondet(|| 5.0f32.acos());
+            check_nondet(|| 1.0f32.atan());
+            check_nondet(|| 1.0f32.atan2(2.0f32));
+            check_nondet(|| 1.0f32.sinh());
+            check_nondet(|| 1.0f32.cosh());
+            check_nondet(|| 1.0f32.tanh());
         }
-        ensure_nondet(|| 1.0f32.asinh());
-        ensure_nondet(|| 2.0f32.acosh());
-        ensure_nondet(|| 0.5f32.atanh());
-        ensure_nondet(|| 5.0f32.gamma());
-        ensure_nondet(|| 5.0f32.ln_gamma());
-        ensure_nondet(|| 5.0f32.erf());
-        ensure_nondet(|| 5.0f32.erfc());
+        check_nondet(|| 1.0f32.asinh());
+        check_nondet(|| 2.0f32.acosh());
+        check_nondet(|| 0.5f32.atanh());
+        check_nondet(|| 5.0f32.gamma());
+        check_nondet(|| 5.0f32.ln_gamma());
+        check_nondet(|| 5.0f32.erf());
+        check_nondet(|| 5.0f32.erfc());
     }
     pub fn test_operations_f64(a: f64, b: f64) {
         test_operations_f!(a, b);
-        ensure_nondet(|| a.powf(b));
-        ensure_nondet(|| a.powi(2));
-        ensure_nondet(|| a.log(b));
-        ensure_nondet(|| a.exp());
-        ensure_nondet(|| 50f64.exp2());
-        ensure_nondet(|| 3f64.ln());
-        ensure_nondet(|| f64::consts::E.log10());
-        ensure_nondet(|| f64::consts::E.log2());
-        ensure_nondet(|| 1f64.ln_1p());
-        ensure_nondet(|| 27.0f64.cbrt());
-        ensure_nondet(|| 3.0f64.hypot(4.0f64));
-        ensure_nondet(|| 1f64.sin());
-        ensure_nondet(|| 1f64.cos());
-        ensure_nondet(|| 1.0f64.tan());
-        ensure_nondet(|| 1.0f64.asin());
-        ensure_nondet(|| 5.0f64.acos());
-        ensure_nondet(|| 1.0f64.atan());
-        ensure_nondet(|| 1.0f64.atan2(2.0f64));
-        ensure_nondet(|| 1.0f64.sinh());
-        ensure_nondet(|| 1.0f64.cosh());
-        ensure_nondet(|| 1.0f64.tanh());
-        ensure_nondet(|| 1.0f64.asinh());
-        ensure_nondet(|| 3.0f64.acosh());
-        ensure_nondet(|| 0.5f64.atanh());
-        ensure_nondet(|| 5.0f64.gamma());
-        ensure_nondet(|| 5.0f64.ln_gamma());
-        ensure_nondet(|| 5.0f64.erf());
-        ensure_nondet(|| 5.0f64.erfc());
+        check_nondet(|| a.powf(b));
+        check_nondet(|| a.powi(2));
+        check_nondet(|| a.log(b));
+        check_nondet(|| a.exp());
+        check_nondet(|| 50f64.exp2());
+        check_nondet(|| 3f64.ln());
+        check_nondet(|| f64::consts::E.log10());
+        check_nondet(|| f64::consts::E.log2());
+        check_nondet(|| 1f64.ln_1p());
+        check_nondet(|| 27.0f64.cbrt());
+        check_nondet(|| 3.0f64.hypot(4.0f64));
+        check_nondet(|| 1f64.sin());
+        check_nondet(|| 1f64.cos());
+        check_nondet(|| 1.0f64.tan());
+        check_nondet(|| 1.0f64.asin());
+        check_nondet(|| 5.0f64.acos());
+        check_nondet(|| 1.0f64.atan());
+        check_nondet(|| 1.0f64.atan2(2.0f64));
+        check_nondet(|| 1.0f64.sinh());
+        check_nondet(|| 1.0f64.cosh());
+        check_nondet(|| 1.0f64.tanh());
+        check_nondet(|| 1.0f64.asinh());
+        check_nondet(|| 3.0f64.acosh());
+        check_nondet(|| 0.5f64.atanh());
+        check_nondet(|| 5.0f64.gamma());
+        check_nondet(|| 5.0f64.ln_gamma());
+        check_nondet(|| 5.0f64.erf());
+        check_nondet(|| 5.0f64.erfc());
     }
     pub fn test_operations_f128(a: f128, b: f128) {
         test_operations_f!(a, b);
@@ -1574,15 +1548,15 @@ fn test_non_determinism() {
     test_operations_f128(25., 18.);
 
     // SNaN^0 = (1 | NaN)
-    ensure_nondet(|| f32::powf(SNAN_F32, 0.0).is_nan());
-    ensure_nondet(|| f64::powf(SNAN_F64, 0.0).is_nan());
+    check_nondet(|| f32::powf(SNAN_F32, 0.0).is_nan());
+    check_nondet(|| f64::powf(SNAN_F64, 0.0).is_nan());
 
     // 1^SNaN = (1 | NaN)
-    ensure_nondet(|| f32::powf(1.0, SNAN_F32).is_nan());
-    ensure_nondet(|| f64::powf(1.0, SNAN_F64).is_nan());
+    check_nondet(|| f32::powf(1.0, SNAN_F32).is_nan());
+    check_nondet(|| f64::powf(1.0, SNAN_F64).is_nan());
 
     // same as powf (keep it consistent):
     // x^SNaN = (1 | NaN)
-    ensure_nondet(|| f32::powi(SNAN_F32, 0).is_nan());
-    ensure_nondet(|| f64::powi(SNAN_F64, 0).is_nan());
+    check_nondet(|| f32::powi(SNAN_F32, 0).is_nan());
+    check_nondet(|| f64::powi(SNAN_F64, 0).is_nan());
 }
diff --git a/src/tools/miri/tests/pass/intrinsics/fmuladd_nondeterministic.rs b/src/tools/miri/tests/pass/intrinsics/fmuladd_nondeterministic.rs
index b688405c4b1..401b2911f80 100644
--- a/src/tools/miri/tests/pass/intrinsics/fmuladd_nondeterministic.rs
+++ b/src/tools/miri/tests/pass/intrinsics/fmuladd_nondeterministic.rs
@@ -3,73 +3,48 @@ use std::intrinsics::simd::simd_relaxed_fma;
 use std::intrinsics::{fmuladdf32, fmuladdf64};
 use std::simd::prelude::*;
 
-fn ensure_both_happen(f: impl Fn() -> bool) -> bool {
-    let mut saw_true = false;
-    let mut saw_false = false;
-    for _ in 0..50 {
-        let b = f();
-        if b {
-            saw_true = true;
-        } else {
-            saw_false = true;
-        }
-        if saw_true && saw_false {
-            return true;
-        }
-    }
-    false
-}
+#[path = "../../utils/mod.rs"]
+mod utils;
+use utils::check_nondet;
 
 fn main() {
-    assert!(
-        ensure_both_happen(|| {
-            let a = std::hint::black_box(0.1_f64);
-            let b = std::hint::black_box(0.2);
-            let c = std::hint::black_box(-a * b);
-            // It is unspecified whether the following operation is fused or not. The
-            // following evaluates to 0.0 if unfused, and nonzero (-1.66e-18) if fused.
-            let x = unsafe { fmuladdf64(a, b, c) };
-            x == 0.0
-        }),
-        "`fmuladdf64` failed to be evaluated as both fused and unfused"
-    );
+    check_nondet(|| {
+        let a = std::hint::black_box(0.1_f64);
+        let b = std::hint::black_box(0.2);
+        let c = std::hint::black_box(-a * b);
+        // It is unspecified whether the following operation is fused or not. The
+        // following evaluates to 0.0 if unfused, and nonzero (-1.66e-18) if fused.
+        let x = unsafe { fmuladdf64(a, b, c) };
+        x == 0.0
+    });
 
-    assert!(
-        ensure_both_happen(|| {
-            let a = std::hint::black_box(0.1_f32);
-            let b = std::hint::black_box(0.2);
-            let c = std::hint::black_box(-a * b);
-            // It is unspecified whether the following operation is fused or not. The
-            // following evaluates to 0.0 if unfused, and nonzero (-8.1956386e-10) if fused.
-            let x = unsafe { fmuladdf32(a, b, c) };
-            x == 0.0
-        }),
-        "`fmuladdf32` failed to be evaluated as both fused and unfused"
-    );
+    check_nondet(|| {
+        let a = std::hint::black_box(0.1_f32);
+        let b = std::hint::black_box(0.2);
+        let c = std::hint::black_box(-a * b);
+        // It is unspecified whether the following operation is fused or not. The
+        // following evaluates to 0.0 if unfused, and nonzero (-8.1956386e-10) if fused.
+        let x = unsafe { fmuladdf32(a, b, c) };
+        x == 0.0
+    });
 
-    assert!(
-        ensure_both_happen(|| {
-            let a = f32x4::splat(std::hint::black_box(0.1));
-            let b = f32x4::splat(std::hint::black_box(0.2));
-            let c = std::hint::black_box(-a * b);
-            let x = unsafe { simd_relaxed_fma(a, b, c) };
-            // Whether we fuse or not is a per-element decision, so sometimes these should be
-            // the same and sometimes not.
-            x[0] == x[1]
-        }),
-        "`simd_relaxed_fma` failed to be evaluated as both fused and unfused"
-    );
+    check_nondet(|| {
+        let a = f32x4::splat(std::hint::black_box(0.1));
+        let b = f32x4::splat(std::hint::black_box(0.2));
+        let c = std::hint::black_box(-a * b);
+        let x = unsafe { simd_relaxed_fma(a, b, c) };
+        // Whether we fuse or not is a per-element decision, so sometimes these should be
+        // the same and sometimes not.
+        x[0] == x[1]
+    });
 
-    assert!(
-        ensure_both_happen(|| {
-            let a = f64x4::splat(std::hint::black_box(0.1));
-            let b = f64x4::splat(std::hint::black_box(0.2));
-            let c = std::hint::black_box(-a * b);
-            let x = unsafe { simd_relaxed_fma(a, b, c) };
-            // Whether we fuse or not is a per-element decision, so sometimes these should be
-            // the same and sometimes not.
-            x[0] == x[1]
-        }),
-        "`simd_relaxed_fma` failed to be evaluated as both fused and unfused"
-    );
+    check_nondet(|| {
+        let a = f64x4::splat(std::hint::black_box(0.1));
+        let b = f64x4::splat(std::hint::black_box(0.2));
+        let c = std::hint::black_box(-a * b);
+        let x = unsafe { simd_relaxed_fma(a, b, c) };
+        // Whether we fuse or not is a per-element decision, so sometimes these should be
+        // the same and sometimes not.
+        x[0] == x[1]
+    });
 }
diff --git a/src/tools/miri/tests/utils/mod.rs b/src/tools/miri/tests/utils/mod.rs
index 459fea404ea..37f99962163 100644
--- a/src/tools/miri/tests/utils/mod.rs
+++ b/src/tools/miri/tests/utils/mod.rs
@@ -51,3 +51,18 @@ pub fn check_all_outcomes<T: Eq + std::hash::Hash + std::fmt::Debug>(
     }
     unreachable!()
 }
+
+/// Check that the operation is non-deterministic
+#[track_caller]
+pub fn check_nondet<T: PartialEq + std::fmt::Debug>(f: impl Fn() -> T) {
+    let rounds = 50;
+    let first = f();
+    for _ in 1..rounds {
+        if f() != first {
+            // We saw two different values!
+            return;
+        }
+    }
+    // We saw the same thing N times.
+    panic!("expected non-determinism, got {rounds} times the same result: {first:?}");
+}