Skip to content

Commit dda0eef

Browse files
authored
Merge pull request #4599 from RalfJung/nondet
share non-det test helpers and increase iteration counts
2 parents 164a501 + 84437ed commit dda0eef

File tree

5 files changed

+183
-234
lines changed

5 files changed

+183
-234
lines changed

src/tools/miri/tests/pass/0weak_memory/weak.rs

Lines changed: 4 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ use std::sync::atomic::Ordering::*;
1313
use std::sync::atomic::{AtomicUsize, fence};
1414
use std::thread::spawn;
1515

16+
#[path = "../../utils/mod.rs"]
17+
mod utils;
18+
use utils::check_all_outcomes;
19+
1620
#[allow(dead_code)]
1721
#[derive(Copy, Clone)]
1822
struct EvilSend<T>(pub T);
@@ -33,35 +37,6 @@ fn spin_until(loc: &AtomicUsize, val: usize) -> usize {
3337
val
3438
}
3539

36-
/// Check that the function produces the intended set of outcomes.
37-
#[track_caller]
38-
fn check_all_outcomes<T: Eq + std::hash::Hash + std::fmt::Debug>(
39-
expected: impl IntoIterator<Item = T>,
40-
generate: impl Fn() -> T,
41-
) {
42-
use std::collections::HashSet;
43-
44-
let expected: HashSet<T> = HashSet::from_iter(expected);
45-
let mut seen = HashSet::new();
46-
// Let's give it N times as many tries as we are expecting values.
47-
let tries = expected.len() * 16;
48-
for i in 0..tries {
49-
let val = generate();
50-
assert!(expected.contains(&val), "got an unexpected value: {val:?}");
51-
seen.insert(val);
52-
if i > tries / 2 && expected.len() == seen.len() {
53-
// We saw everything and we did quite a few tries, let's avoid wasting time.
54-
return;
55-
}
56-
}
57-
// Let's see if we saw them all.
58-
for val in expected {
59-
if !seen.contains(&val) {
60-
panic!("did not get value that should be possible: {val:?}");
61-
}
62-
}
63-
}
64-
6540
fn relaxed() {
6641
check_all_outcomes([0, 1, 2], || {
6742
let x = static_atomic(0);

src/tools/miri/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
}

src/tools/miri/tests/pass/float_nan.rs

Lines changed: 6 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55
use std::fmt;
66
use std::hint::black_box;
77

8+
#[path = "../utils/mod.rs"]
9+
mod utils;
10+
use utils::check_all_outcomes;
11+
812
fn ldexp(a: f64, b: i32) -> f64 {
913
extern "C" {
1014
fn ldexp(x: f64, n: i32) -> f64;
@@ -26,35 +30,6 @@ enum NaNKind {
2630
}
2731
use NaNKind::*;
2832

29-
/// Check that the function produces the intended set of outcomes.
30-
#[track_caller]
31-
fn check_all_outcomes<T: Eq + std::hash::Hash + fmt::Display>(
32-
expected: impl IntoIterator<Item = T>,
33-
generate: impl Fn() -> T,
34-
) {
35-
use std::collections::HashSet;
36-
37-
let expected: HashSet<T> = HashSet::from_iter(expected);
38-
let mut seen = HashSet::new();
39-
// Let's give it N times as many tries as we are expecting values.
40-
let tries = expected.len() * 12;
41-
for i in 0..tries {
42-
let val = generate();
43-
assert!(expected.contains(&val), "got an unexpected value: {val}");
44-
seen.insert(val);
45-
if i > tries / 2 && expected.len() == seen.len() {
46-
// We saw everything and we did quite a few tries, let's avoid wasting time.
47-
return;
48-
}
49-
}
50-
// Let's see if we saw them all.
51-
for val in expected {
52-
if !seen.contains(&val) {
53-
panic!("did not get value that should be possible: {val}");
54-
}
55-
}
56-
}
57-
5833
// -- f32 support
5934
#[repr(C)]
6035
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
@@ -81,7 +56,7 @@ const F32_EXP: u32 = 8; // 8 bits of exponent
8156
const F32_MANTISSA: u32 = F32_SIGN_BIT - F32_EXP;
8257
const F32_NAN_PAYLOAD: u32 = F32_MANTISSA - 1;
8358

84-
impl fmt::Display for F32 {
59+
impl fmt::Debug for F32 {
8560
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
8661
// Alaways show raw bits.
8762
write!(f, "0x{:08x} ", self.0)?;
@@ -154,7 +129,7 @@ const F64_EXP: u32 = 11; // 11 bits of exponent
154129
const F64_MANTISSA: u32 = F64_SIGN_BIT - F64_EXP;
155130
const F64_NAN_PAYLOAD: u32 = F64_MANTISSA - 1;
156131

157-
impl fmt::Display for F64 {
132+
impl fmt::Debug for F64 {
158133
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
159134
// Alaways show raw bits.
160135
write!(f, "0x{:08x} ", self.0)?;

0 commit comments

Comments
 (0)