Skip to content

Commit 2a10f12

Browse files
committed
miri: add flag to suppress float non-determinism
1 parent 1c04750 commit 2a10f12

File tree

8 files changed

+31
-4
lines changed

8 files changed

+31
-4
lines changed

src/tools/miri/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,11 @@ environment variable. We first document the most relevant and most commonly used
286286
specific circumstances, but Miri's behavior will also be more stable across versions and targets.
287287
This is equivalent to `-Zmiri-fixed-schedule -Zmiri-compare-exchange-weak-failure-rate=0.0
288288
-Zmiri-address-reuse-cross-thread-rate=0.0 -Zmiri-disable-weak-memory-emulation`.
289+
* `-Zmiri-deterministic-floats` makes Miri's floating-point behavior fully deterministic. This means
290+
that operations will always return the preferred NaN, imprecise operations will not have any
291+
random error applied to them, and `min`/`max` as "maybe fused" multiply-add all behave
292+
deterministically. Note that Miri still uses host floats for some operations, so behavior can
293+
still differ depending on the host target and setup.
289294
* `-Zmiri-disable-isolation` disables host isolation. As a consequence,
290295
the program has access to host resources such as environment variables, file
291296
systems, and randomness.

src/tools/miri/src/bin/miri.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -601,6 +601,8 @@ fn main() {
601601
miri_config.collect_leak_backtraces = false;
602602
} else if arg == "-Zmiri-force-intrinsic-fallback" {
603603
miri_config.force_intrinsic_fallback = true;
604+
} else if arg == "-Zmiri-deterministic-floats" {
605+
miri_config.float_nondet = false;
604606
} else if arg == "-Zmiri-strict-provenance" {
605607
miri_config.provenance_mode = ProvenanceMode::Strict;
606608
} else if arg == "-Zmiri-permissive-provenance" {

src/tools/miri/src/eval.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,8 @@ pub struct MiriConfig {
166166
pub fixed_scheduling: bool,
167167
/// Always prefer the intrinsic fallback body over the native Miri implementation.
168168
pub force_intrinsic_fallback: bool,
169+
/// Whether floating-point operations can behave non-deterministically.
170+
pub float_nondet: bool,
169171
}
170172

171173
impl Default for MiriConfig {
@@ -205,6 +207,7 @@ impl Default for MiriConfig {
205207
address_reuse_cross_thread_rate: 0.1,
206208
fixed_scheduling: false,
207209
force_intrinsic_fallback: false,
210+
float_nondet: true,
208211
}
209212
}
210213
}

src/tools/miri/src/intrinsics/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
293293
let a = this.read_scalar(a)?.to_f32()?;
294294
let b = this.read_scalar(b)?.to_f32()?;
295295
let c = this.read_scalar(c)?.to_f32()?;
296-
let fuse: bool = this.machine.rng.get_mut().random();
296+
let fuse: bool = this.machine.float_nondet && this.machine.rng.get_mut().random();
297297
let res = if fuse {
298298
// FIXME: Using host floats, to work around https://github.com/rust-lang/rustc_apfloat/issues/11
299299
a.to_host().mul_add(b.to_host(), c.to_host()).to_soft()
@@ -308,7 +308,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
308308
let a = this.read_scalar(a)?.to_f64()?;
309309
let b = this.read_scalar(b)?.to_f64()?;
310310
let c = this.read_scalar(c)?.to_f64()?;
311-
let fuse: bool = this.machine.rng.get_mut().random();
311+
let fuse: bool = this.machine.float_nondet && this.machine.rng.get_mut().random();
312312
let res = if fuse {
313313
// FIXME: Using host floats, to work around https://github.com/rust-lang/rustc_apfloat/issues/11
314314
a.to_host().mul_add(b.to_host(), c.to_host()).to_soft()

src/tools/miri/src/intrinsics/simd.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
306306
let c = this.read_scalar(&this.project_index(&c, i)?)?;
307307
let dest = this.project_index(&dest, i)?;
308308

309-
let fuse: bool = intrinsic_name == "fma" || this.machine.rng.get_mut().random();
309+
let fuse: bool = intrinsic_name == "fma"
310+
|| (this.machine.float_nondet && this.machine.rng.get_mut().random());
310311

311312
// Works for f32 and f64.
312313
// FIXME: using host floats to work around https://github.com/rust-lang/miri/issues/2468.

src/tools/miri/src/machine.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -618,6 +618,9 @@ pub struct MiriMachine<'tcx> {
618618

619619
/// Always prefer the intrinsic fallback body over the native Miri implementation.
620620
pub force_intrinsic_fallback: bool,
621+
622+
/// Whether floating-point operations can behave non-deterministically.
623+
pub float_nondet: bool,
621624
}
622625

623626
impl<'tcx> MiriMachine<'tcx> {
@@ -778,6 +781,7 @@ impl<'tcx> MiriMachine<'tcx> {
778781
int2ptr_warned: Default::default(),
779782
mangle_internal_symbol_cache: Default::default(),
780783
force_intrinsic_fallback: config.force_intrinsic_fallback,
784+
float_nondet: config.float_nondet,
781785
}
782786
}
783787

@@ -956,6 +960,7 @@ impl VisitProvenance for MiriMachine<'_> {
956960
int2ptr_warned: _,
957961
mangle_internal_symbol_cache: _,
958962
force_intrinsic_fallback: _,
963+
float_nondet: _,
959964
} = self;
960965

961966
threads.visit_provenance(visit);

src/tools/miri/src/math.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ pub(crate) fn apply_random_float_error<F: rustc_apfloat::Float>(
1515
val: F,
1616
err_scale: i32,
1717
) -> F {
18+
if !ecx.machine.float_nondet {
19+
return val;
20+
}
21+
1822
let rng = ecx.machine.rng.get_mut();
1923
// Generate a random integer in the range [0, 2^PREC).
2024
// (When read as binary, the position of the first `1` determines the exponent,

src/tools/miri/src/operator.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
7676
}
7777

7878
fn generate_nan<F1: Float + FloatConvert<F2>, F2: Float>(&self, inputs: &[F1]) -> F2 {
79+
let this = self.eval_context_ref();
80+
if !this.machine.float_nondet {
81+
return F2::NAN;
82+
}
83+
7984
/// Make the given NaN a signaling NaN.
8085
/// Returns `None` if this would not result in a NaN.
8186
fn make_signaling<F: Float>(f: F) -> Option<F> {
@@ -89,7 +94,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
8994
if f.is_nan() { Some(f) } else { None }
9095
}
9196

92-
let this = self.eval_context_ref();
9397
let mut rand = this.machine.rng.borrow_mut();
9498
// Assemble an iterator of possible NaNs: preferred, quieting propagation, unchanged propagation.
9599
// On some targets there are more possibilities; for now we just generate those options that
@@ -118,6 +122,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
118122

119123
fn equal_float_min_max<F: Float>(&self, a: F, b: F) -> F {
120124
let this = self.eval_context_ref();
125+
if !this.machine.float_nondet {
126+
return a;
127+
}
121128
// Return one side non-deterministically.
122129
let mut rand = this.machine.rng.borrow_mut();
123130
if rand.random() { a } else { b }

0 commit comments

Comments
 (0)