Skip to content

Commit 73b30b6

Browse files
committed
share the check_nondet helper as well
1 parent d44fc41 commit 73b30b6

File tree

3 files changed

+138
-174
lines changed

3 files changed

+138
-174
lines changed

tests/pass/float.rs

Lines changed: 84 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,16 @@
88
#![allow(internal_features)]
99
#![allow(unnecessary_transmutes)]
1010

11+
#[path = "../utils/mod.rs"]
12+
mod utils;
1113
use std::any::type_name;
1214
use std::cmp::min;
1315
use std::fmt::{Debug, Display, LowerHex};
1416
use std::hint::black_box;
1517
use std::{f32, f64};
1618

19+
use utils::check_nondet;
20+
1721
/// Compare the two floats, allowing for $ulp many ULPs of error.
1822
///
1923
/// ULP means "Units in the Last Place" or "Units of Least Precision".
@@ -1429,29 +1433,14 @@ fn test_fmuladd() {
14291433

14301434
/// `min` and `max` on equal arguments are non-deterministic.
14311435
fn test_min_max_nondet() {
1432-
/// Ensure that if we call the closure often enough, we see both `true` and `false.`
1433-
#[track_caller]
1434-
fn ensure_both(f: impl Fn() -> bool) {
1435-
let rounds = 32;
1436-
let first = f();
1437-
for _ in 1..rounds {
1438-
if f() != first {
1439-
// We saw two different values!
1440-
return;
1441-
}
1442-
}
1443-
// We saw the same thing N times.
1444-
panic!("expected non-determinism, got {rounds} times the same result: {first:?}");
1445-
}
1446-
1447-
ensure_both(|| f16::min(0.0, -0.0).is_sign_positive());
1448-
ensure_both(|| f16::max(0.0, -0.0).is_sign_positive());
1449-
ensure_both(|| f32::min(0.0, -0.0).is_sign_positive());
1450-
ensure_both(|| f32::max(0.0, -0.0).is_sign_positive());
1451-
ensure_both(|| f64::min(0.0, -0.0).is_sign_positive());
1452-
ensure_both(|| f64::max(0.0, -0.0).is_sign_positive());
1453-
ensure_both(|| f128::min(0.0, -0.0).is_sign_positive());
1454-
ensure_both(|| f128::max(0.0, -0.0).is_sign_positive());
1436+
check_nondet(|| f16::min(0.0, -0.0).is_sign_positive());
1437+
check_nondet(|| f16::max(0.0, -0.0).is_sign_positive());
1438+
check_nondet(|| f32::min(0.0, -0.0).is_sign_positive());
1439+
check_nondet(|| f32::max(0.0, -0.0).is_sign_positive());
1440+
check_nondet(|| f64::min(0.0, -0.0).is_sign_positive());
1441+
check_nondet(|| f64::max(0.0, -0.0).is_sign_positive());
1442+
check_nondet(|| f128::min(0.0, -0.0).is_sign_positive());
1443+
check_nondet(|| f128::max(0.0, -0.0).is_sign_positive());
14551444
}
14561445

14571446
fn test_non_determinism() {
@@ -1461,35 +1450,20 @@ fn test_non_determinism() {
14611450
};
14621451
use std::{f32, f64};
14631452

1464-
/// Ensure that the operation is non-deterministic
1465-
#[track_caller]
1466-
fn ensure_nondet<T: PartialEq + std::fmt::Debug>(f: impl Fn() -> T) {
1467-
let rounds = 16;
1468-
let first = f();
1469-
for _ in 1..rounds {
1470-
if f() != first {
1471-
// We saw two different values!
1472-
return;
1473-
}
1474-
}
1475-
// We saw the same thing N times.
1476-
panic!("expected non-determinism, got {rounds} times the same result: {first:?}");
1477-
}
1478-
14791453
macro_rules! test_operations_f {
14801454
($a:expr, $b:expr) => {
1481-
ensure_nondet(|| fadd_algebraic($a, $b));
1482-
ensure_nondet(|| fsub_algebraic($a, $b));
1483-
ensure_nondet(|| fmul_algebraic($a, $b));
1484-
ensure_nondet(|| fdiv_algebraic($a, $b));
1485-
ensure_nondet(|| frem_algebraic($a, $b));
1455+
check_nondet(|| fadd_algebraic($a, $b));
1456+
check_nondet(|| fsub_algebraic($a, $b));
1457+
check_nondet(|| fmul_algebraic($a, $b));
1458+
check_nondet(|| fdiv_algebraic($a, $b));
1459+
check_nondet(|| frem_algebraic($a, $b));
14861460

14871461
unsafe {
1488-
ensure_nondet(|| fadd_fast($a, $b));
1489-
ensure_nondet(|| fsub_fast($a, $b));
1490-
ensure_nondet(|| fmul_fast($a, $b));
1491-
ensure_nondet(|| fdiv_fast($a, $b));
1492-
ensure_nondet(|| frem_fast($a, $b));
1462+
check_nondet(|| fadd_fast($a, $b));
1463+
check_nondet(|| fsub_fast($a, $b));
1464+
check_nondet(|| fmul_fast($a, $b));
1465+
check_nondet(|| fdiv_fast($a, $b));
1466+
check_nondet(|| frem_fast($a, $b));
14931467
}
14941468
};
14951469
}
@@ -1499,70 +1473,70 @@ fn test_non_determinism() {
14991473
}
15001474
pub fn test_operations_f32(a: f32, b: f32) {
15011475
test_operations_f!(a, b);
1502-
ensure_nondet(|| a.powf(b));
1503-
ensure_nondet(|| a.powi(2));
1504-
ensure_nondet(|| a.log(b));
1505-
ensure_nondet(|| a.exp());
1506-
ensure_nondet(|| 10f32.exp2());
1507-
ensure_nondet(|| f32::consts::E.ln());
1508-
ensure_nondet(|| 10f32.log10());
1509-
ensure_nondet(|| 8f32.log2());
1510-
ensure_nondet(|| 1f32.ln_1p());
1511-
ensure_nondet(|| 27.0f32.cbrt());
1512-
ensure_nondet(|| 3.0f32.hypot(4.0f32));
1513-
ensure_nondet(|| 1f32.sin());
1514-
ensure_nondet(|| 1f32.cos());
1476+
check_nondet(|| a.powf(b));
1477+
check_nondet(|| a.powi(2));
1478+
check_nondet(|| a.log(b));
1479+
check_nondet(|| a.exp());
1480+
check_nondet(|| 10f32.exp2());
1481+
check_nondet(|| f32::consts::E.ln());
1482+
check_nondet(|| 10f32.log10());
1483+
check_nondet(|| 8f32.log2());
1484+
check_nondet(|| 1f32.ln_1p());
1485+
check_nondet(|| 27.0f32.cbrt());
1486+
check_nondet(|| 3.0f32.hypot(4.0f32));
1487+
check_nondet(|| 1f32.sin());
1488+
check_nondet(|| 1f32.cos());
15151489
// On i686-pc-windows-msvc , these functions are implemented by calling the `f64` version,
15161490
// which means the little rounding errors Miri introduces are discarded by the cast down to
15171491
// `f32`. Just skip the test for them.
15181492
if !cfg!(all(target_os = "windows", target_env = "msvc", target_arch = "x86")) {
1519-
ensure_nondet(|| 1.0f32.tan());
1520-
ensure_nondet(|| 1.0f32.asin());
1521-
ensure_nondet(|| 5.0f32.acos());
1522-
ensure_nondet(|| 1.0f32.atan());
1523-
ensure_nondet(|| 1.0f32.atan2(2.0f32));
1524-
ensure_nondet(|| 1.0f32.sinh());
1525-
ensure_nondet(|| 1.0f32.cosh());
1526-
ensure_nondet(|| 1.0f32.tanh());
1493+
check_nondet(|| 1.0f32.tan());
1494+
check_nondet(|| 1.0f32.asin());
1495+
check_nondet(|| 5.0f32.acos());
1496+
check_nondet(|| 1.0f32.atan());
1497+
check_nondet(|| 1.0f32.atan2(2.0f32));
1498+
check_nondet(|| 1.0f32.sinh());
1499+
check_nondet(|| 1.0f32.cosh());
1500+
check_nondet(|| 1.0f32.tanh());
15271501
}
1528-
ensure_nondet(|| 1.0f32.asinh());
1529-
ensure_nondet(|| 2.0f32.acosh());
1530-
ensure_nondet(|| 0.5f32.atanh());
1531-
ensure_nondet(|| 5.0f32.gamma());
1532-
ensure_nondet(|| 5.0f32.ln_gamma());
1533-
ensure_nondet(|| 5.0f32.erf());
1534-
ensure_nondet(|| 5.0f32.erfc());
1502+
check_nondet(|| 1.0f32.asinh());
1503+
check_nondet(|| 2.0f32.acosh());
1504+
check_nondet(|| 0.5f32.atanh());
1505+
check_nondet(|| 5.0f32.gamma());
1506+
check_nondet(|| 5.0f32.ln_gamma());
1507+
check_nondet(|| 5.0f32.erf());
1508+
check_nondet(|| 5.0f32.erfc());
15351509
}
15361510
pub fn test_operations_f64(a: f64, b: f64) {
15371511
test_operations_f!(a, b);
1538-
ensure_nondet(|| a.powf(b));
1539-
ensure_nondet(|| a.powi(2));
1540-
ensure_nondet(|| a.log(b));
1541-
ensure_nondet(|| a.exp());
1542-
ensure_nondet(|| 50f64.exp2());
1543-
ensure_nondet(|| 3f64.ln());
1544-
ensure_nondet(|| f64::consts::E.log10());
1545-
ensure_nondet(|| f64::consts::E.log2());
1546-
ensure_nondet(|| 1f64.ln_1p());
1547-
ensure_nondet(|| 27.0f64.cbrt());
1548-
ensure_nondet(|| 3.0f64.hypot(4.0f64));
1549-
ensure_nondet(|| 1f64.sin());
1550-
ensure_nondet(|| 1f64.cos());
1551-
ensure_nondet(|| 1.0f64.tan());
1552-
ensure_nondet(|| 1.0f64.asin());
1553-
ensure_nondet(|| 5.0f64.acos());
1554-
ensure_nondet(|| 1.0f64.atan());
1555-
ensure_nondet(|| 1.0f64.atan2(2.0f64));
1556-
ensure_nondet(|| 1.0f64.sinh());
1557-
ensure_nondet(|| 1.0f64.cosh());
1558-
ensure_nondet(|| 1.0f64.tanh());
1559-
ensure_nondet(|| 1.0f64.asinh());
1560-
ensure_nondet(|| 3.0f64.acosh());
1561-
ensure_nondet(|| 0.5f64.atanh());
1562-
ensure_nondet(|| 5.0f64.gamma());
1563-
ensure_nondet(|| 5.0f64.ln_gamma());
1564-
ensure_nondet(|| 5.0f64.erf());
1565-
ensure_nondet(|| 5.0f64.erfc());
1512+
check_nondet(|| a.powf(b));
1513+
check_nondet(|| a.powi(2));
1514+
check_nondet(|| a.log(b));
1515+
check_nondet(|| a.exp());
1516+
check_nondet(|| 50f64.exp2());
1517+
check_nondet(|| 3f64.ln());
1518+
check_nondet(|| f64::consts::E.log10());
1519+
check_nondet(|| f64::consts::E.log2());
1520+
check_nondet(|| 1f64.ln_1p());
1521+
check_nondet(|| 27.0f64.cbrt());
1522+
check_nondet(|| 3.0f64.hypot(4.0f64));
1523+
check_nondet(|| 1f64.sin());
1524+
check_nondet(|| 1f64.cos());
1525+
check_nondet(|| 1.0f64.tan());
1526+
check_nondet(|| 1.0f64.asin());
1527+
check_nondet(|| 5.0f64.acos());
1528+
check_nondet(|| 1.0f64.atan());
1529+
check_nondet(|| 1.0f64.atan2(2.0f64));
1530+
check_nondet(|| 1.0f64.sinh());
1531+
check_nondet(|| 1.0f64.cosh());
1532+
check_nondet(|| 1.0f64.tanh());
1533+
check_nondet(|| 1.0f64.asinh());
1534+
check_nondet(|| 3.0f64.acosh());
1535+
check_nondet(|| 0.5f64.atanh());
1536+
check_nondet(|| 5.0f64.gamma());
1537+
check_nondet(|| 5.0f64.ln_gamma());
1538+
check_nondet(|| 5.0f64.erf());
1539+
check_nondet(|| 5.0f64.erfc());
15661540
}
15671541
pub fn test_operations_f128(a: f128, b: f128) {
15681542
test_operations_f!(a, b);
@@ -1574,15 +1548,15 @@ fn test_non_determinism() {
15741548
test_operations_f128(25., 18.);
15751549

15761550
// SNaN^0 = (1 | NaN)
1577-
ensure_nondet(|| f32::powf(SNAN_F32, 0.0).is_nan());
1578-
ensure_nondet(|| f64::powf(SNAN_F64, 0.0).is_nan());
1551+
check_nondet(|| f32::powf(SNAN_F32, 0.0).is_nan());
1552+
check_nondet(|| f64::powf(SNAN_F64, 0.0).is_nan());
15791553

15801554
// 1^SNaN = (1 | NaN)
1581-
ensure_nondet(|| f32::powf(1.0, SNAN_F32).is_nan());
1582-
ensure_nondet(|| f64::powf(1.0, SNAN_F64).is_nan());
1555+
check_nondet(|| f32::powf(1.0, SNAN_F32).is_nan());
1556+
check_nondet(|| f64::powf(1.0, SNAN_F64).is_nan());
15831557

15841558
// same as powf (keep it consistent):
15851559
// x^SNaN = (1 | NaN)
1586-
ensure_nondet(|| f32::powi(SNAN_F32, 0).is_nan());
1587-
ensure_nondet(|| f64::powi(SNAN_F64, 0).is_nan());
1560+
check_nondet(|| f32::powi(SNAN_F32, 0).is_nan());
1561+
check_nondet(|| f64::powi(SNAN_F64, 0).is_nan());
15881562
}

tests/pass/intrinsics/fmuladd_nondeterministic.rs

Lines changed: 39 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -3,73 +3,48 @@ use std::intrinsics::simd::simd_relaxed_fma;
33
use std::intrinsics::{fmuladdf32, fmuladdf64};
44
use std::simd::prelude::*;
55

6-
fn ensure_both_happen(f: impl Fn() -> bool) -> bool {
7-
let mut saw_true = false;
8-
let mut saw_false = false;
9-
for _ in 0..50 {
10-
let b = f();
11-
if b {
12-
saw_true = true;
13-
} else {
14-
saw_false = true;
15-
}
16-
if saw_true && saw_false {
17-
return true;
18-
}
19-
}
20-
false
21-
}
6+
#[path = "../../utils/mod.rs"]
7+
mod utils;
8+
use utils::check_nondet;
229

2310
fn main() {
24-
assert!(
25-
ensure_both_happen(|| {
26-
let a = std::hint::black_box(0.1_f64);
27-
let b = std::hint::black_box(0.2);
28-
let c = std::hint::black_box(-a * b);
29-
// It is unspecified whether the following operation is fused or not. The
30-
// following evaluates to 0.0 if unfused, and nonzero (-1.66e-18) if fused.
31-
let x = unsafe { fmuladdf64(a, b, c) };
32-
x == 0.0
33-
}),
34-
"`fmuladdf64` failed to be evaluated as both fused and unfused"
35-
);
11+
check_nondet(|| {
12+
let a = std::hint::black_box(0.1_f64);
13+
let b = std::hint::black_box(0.2);
14+
let c = std::hint::black_box(-a * b);
15+
// It is unspecified whether the following operation is fused or not. The
16+
// following evaluates to 0.0 if unfused, and nonzero (-1.66e-18) if fused.
17+
let x = unsafe { fmuladdf64(a, b, c) };
18+
x == 0.0
19+
});
3620

37-
assert!(
38-
ensure_both_happen(|| {
39-
let a = std::hint::black_box(0.1_f32);
40-
let b = std::hint::black_box(0.2);
41-
let c = std::hint::black_box(-a * b);
42-
// It is unspecified whether the following operation is fused or not. The
43-
// following evaluates to 0.0 if unfused, and nonzero (-8.1956386e-10) if fused.
44-
let x = unsafe { fmuladdf32(a, b, c) };
45-
x == 0.0
46-
}),
47-
"`fmuladdf32` failed to be evaluated as both fused and unfused"
48-
);
21+
check_nondet(|| {
22+
let a = std::hint::black_box(0.1_f32);
23+
let b = std::hint::black_box(0.2);
24+
let c = std::hint::black_box(-a * b);
25+
// It is unspecified whether the following operation is fused or not. The
26+
// following evaluates to 0.0 if unfused, and nonzero (-8.1956386e-10) if fused.
27+
let x = unsafe { fmuladdf32(a, b, c) };
28+
x == 0.0
29+
});
4930

50-
assert!(
51-
ensure_both_happen(|| {
52-
let a = f32x4::splat(std::hint::black_box(0.1));
53-
let b = f32x4::splat(std::hint::black_box(0.2));
54-
let c = std::hint::black_box(-a * b);
55-
let x = unsafe { simd_relaxed_fma(a, b, c) };
56-
// Whether we fuse or not is a per-element decision, so sometimes these should be
57-
// the same and sometimes not.
58-
x[0] == x[1]
59-
}),
60-
"`simd_relaxed_fma` failed to be evaluated as both fused and unfused"
61-
);
31+
check_nondet(|| {
32+
let a = f32x4::splat(std::hint::black_box(0.1));
33+
let b = f32x4::splat(std::hint::black_box(0.2));
34+
let c = std::hint::black_box(-a * b);
35+
let x = unsafe { simd_relaxed_fma(a, b, c) };
36+
// Whether we fuse or not is a per-element decision, so sometimes these should be
37+
// the same and sometimes not.
38+
x[0] == x[1]
39+
});
6240

63-
assert!(
64-
ensure_both_happen(|| {
65-
let a = f64x4::splat(std::hint::black_box(0.1));
66-
let b = f64x4::splat(std::hint::black_box(0.2));
67-
let c = std::hint::black_box(-a * b);
68-
let x = unsafe { simd_relaxed_fma(a, b, c) };
69-
// Whether we fuse or not is a per-element decision, so sometimes these should be
70-
// the same and sometimes not.
71-
x[0] == x[1]
72-
}),
73-
"`simd_relaxed_fma` failed to be evaluated as both fused and unfused"
74-
);
41+
check_nondet(|| {
42+
let a = f64x4::splat(std::hint::black_box(0.1));
43+
let b = f64x4::splat(std::hint::black_box(0.2));
44+
let c = std::hint::black_box(-a * b);
45+
let x = unsafe { simd_relaxed_fma(a, b, c) };
46+
// Whether we fuse or not is a per-element decision, so sometimes these should be
47+
// the same and sometimes not.
48+
x[0] == x[1]
49+
});
7550
}

tests/utils/mod.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,18 @@ pub fn check_all_outcomes<T: Eq + std::hash::Hash + std::fmt::Debug>(
5151
}
5252
unreachable!()
5353
}
54+
55+
/// Check that the operation is non-deterministic
56+
#[track_caller]
57+
pub fn check_nondet<T: PartialEq + std::fmt::Debug>(f: impl Fn() -> T) {
58+
let rounds = 50;
59+
let first = f();
60+
for _ in 1..rounds {
61+
if f() != first {
62+
// We saw two different values!
63+
return;
64+
}
65+
}
66+
// We saw the same thing N times.
67+
panic!("expected non-determinism, got {rounds} times the same result: {first:?}");
68+
}

0 commit comments

Comments
 (0)