Skip to content

Commit 064357f

Browse files
apply requested changes of reviewer
1 parent 8c88114 commit 064357f

File tree

4 files changed

+184
-160
lines changed

4 files changed

+184
-160
lines changed

src/intrinsics/mod.rs

Lines changed: 45 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@ use rand::Rng;
77
use rustc_abi::Size;
88
use rustc_apfloat::{Float, Round};
99
use rustc_middle::mir;
10-
use rustc_middle::ty::{self, FloatTy};
10+
use rustc_middle::ty::{self, FloatTy, ScalarInt};
1111
use rustc_span::{Symbol, sym};
1212

1313
use self::atomic::EvalContextExt as _;
1414
use self::helpers::{ToHost, ToSoft, check_intrinsic_arg_count};
1515
use self::simd::EvalContextExt as _;
16-
use crate::math::{apply_random_float_error, ulp_err_scale};
16+
use crate::math::apply_random_float_error_ulp;
1717
use crate::*;
1818

1919
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
@@ -248,11 +248,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
248248
"log2f32" => host.log2(),
249249
_ => bug!(),
250250
};
251-
// Apply a relative error of 16ULP to simulate non-determinism
252-
let res = apply_random_float_error(
251+
let res = res.to_soft();
252+
// Apply a relative error of 16ULP to introduce some non-determinism
253+
// simulating imprecise implementations and optimizations.
254+
let res = apply_random_float_error_ulp(
253255
this,
254-
res.to_soft(),
255-
ulp_err_scale::<rustc_apfloat::ieee::Single>(4)
256+
res,
257+
4
256258
);
257259
let res = this.adjust_nan(res, &[f]);
258260
this.write_scalar(res, dest)?;
@@ -281,11 +283,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
281283
"log2f64" => host.log2(),
282284
_ => bug!(),
283285
};
284-
// Apply a relative error of 16ULP to simulate non-determinism
285-
let res = apply_random_float_error(
286+
let res = res.to_soft();
287+
// Apply a relative error of 16ULP to introduce some non-determinism
288+
// simulating imprecise implementations and optimizations.
289+
let res = apply_random_float_error_ulp(
286290
this,
287-
res.to_soft(),
288-
ulp_err_scale::<rustc_apfloat::ieee::Double>(4));
291+
res,
292+
4
293+
);
289294
let res = this.adjust_nan(res, &[f]);
290295
this.write_scalar(res, dest)?;
291296
}
@@ -398,29 +403,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
398403
"frem_algebraic" => mir::BinOp::Rem,
399404
_ => bug!(),
400405
};
406+
// `binary_op` already called `generate_nan` if needed.
401407
let res = this.binary_op(op, &a, &b)?;
402-
// `binary_op` already called `generate_nan` if needed
403-
// Apply a relative error of 16ULP to simulate non-determinism
404-
fn apply_error_and_write<'tcx, F: Float + Into<Scalar> >(
405-
ecx: &mut MiriInterpCx<'tcx>,
406-
res: F,
407-
dest: &MPlaceTy<'tcx>
408-
) -> InterpResult<'tcx> {
409-
let res = apply_random_float_error(
410-
ecx,
411-
res,
412-
ulp_err_scale::<F>(4)
413-
);
414-
ecx.write_scalar(res, dest)
415-
}
416-
let scalar = res.to_scalar_int()?;
417-
match res.layout.ty.kind(){
418-
ty::Float(FloatTy::F16) => apply_error_and_write(this, scalar.to_f16(), dest),
419-
ty::Float(FloatTy::F32) => apply_error_and_write(this, scalar.to_f32(), dest),
420-
ty::Float(FloatTy::F64) => apply_error_and_write(this, scalar.to_f64(), dest),
421-
ty::Float(FloatTy::F128) => apply_error_and_write(this, scalar.to_f128(), dest),
422-
_ => bug!("`{intrinsic_name}` intrinsic called with non-float input type")
423-
}?;
408+
// Apply a relative error of 16ULP to simulate non-deterministic precision loss
409+
// due to optimizations.
410+
let res = apply_random_float_error_to_imm(this, res)?;
411+
this.write_immediate(*res, dest)?;
424412
}
425413

426414
#[rustfmt::skip]
@@ -464,33 +452,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
464452
),
465453
_ => {}
466454
}
455+
// This cannot be a NaN so we also don't have to apply any non-determinism.
456+
// (Also, `binary_op` already called `generate_nan` if needed.)
467457
let res = this.binary_op(op, &a, &b)?;
468458
if !float_finite(&res)? {
469459
throw_ub_format!("`{intrinsic_name}` intrinsic produced non-finite value as result");
470460
}
471-
// Apply a relative error of 16ULP to simulate non-determinism
472-
fn apply_error_and_write<'tcx, F: Float + Into<Scalar> >(
473-
ecx: &mut MiriInterpCx<'tcx>,
474-
res: F,
475-
dest: &MPlaceTy<'tcx>
476-
) -> InterpResult<'tcx> {
477-
let res = apply_random_float_error(
478-
ecx,
479-
res,
480-
ulp_err_scale::<F>(4)
481-
);
482-
ecx.write_scalar(res, dest)
483-
}
484-
// This cannot be a NaN so we also don't have to apply any non-determinism.
485-
// (Also, `binary_op` already called `generate_nan` if needed.)
486-
let scalar = res.to_scalar_int()?;
487-
match res.layout.ty.kind(){
488-
ty::Float(FloatTy::F16) => apply_error_and_write(this, scalar.to_f16(), dest),
489-
ty::Float(FloatTy::F32) => apply_error_and_write(this, scalar.to_f32(), dest),
490-
ty::Float(FloatTy::F64) => apply_error_and_write(this, scalar.to_f64(), dest),
491-
ty::Float(FloatTy::F128) => apply_error_and_write(this, scalar.to_f128(), dest),
492-
_ => bug!("`{intrinsic_name}` intrinsic called with non-float input type")
493-
}?;
461+
// Apply a relative error of 16ULP to simulate non-deterministic precision loss
462+
// due to optimizations.
463+
let res = apply_random_float_error_to_imm(this, res)?;
464+
this.write_immediate(*res, dest)?;
494465
}
495466

496467
"float_to_int_unchecked" => {
@@ -522,3 +493,22 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
522493
interp_ok(EmulateItemResult::NeedsReturn)
523494
}
524495
}
496+
497+
/// Applies a random 16ULP floating point error to `val` and returns the new value.
498+
/// Will fail if `val` is not a floating point number.
499+
fn apply_random_float_error_to_imm<'tcx>(
500+
ecx: &mut MiriInterpCx<'tcx>,
501+
val: ImmTy<'tcx>,
502+
) -> InterpResult<'tcx, ImmTy<'tcx>> {
503+
let scalar = val.to_scalar_int()?;
504+
let layout = val.layout;
505+
let res: ScalarInt = match val.layout.ty.kind() {
506+
ty::Float(FloatTy::F16) => apply_random_float_error_ulp(ecx, scalar.to_f16(), 4).into(),
507+
ty::Float(FloatTy::F32) => apply_random_float_error_ulp(ecx, scalar.to_f32(), 4).into(),
508+
ty::Float(FloatTy::F64) => apply_random_float_error_ulp(ecx, scalar.to_f64(), 4).into(),
509+
ty::Float(FloatTy::F128) => apply_random_float_error_ulp(ecx, scalar.to_f128(), 4).into(),
510+
_ => bug!("intrinsic called with non-float input type"),
511+
};
512+
513+
interp_ok(ImmTy::from_scalar_int(res, layout))
514+
}

src/math.rs

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,6 @@ use rand::Rng as _;
22
use rustc_apfloat::Float as _;
33
use rustc_apfloat::ieee::IeeeFloat;
44

5-
/// Creates an error scale to pass to `apply_random_float_error` when applying a 2^n ULP error.
6-
/// For example, if you want to apply a 16 ULP error, you should pass 4, because 2^4 = 16.
7-
pub(crate) fn ulp_err_scale<F: rustc_apfloat::Float>(n: u32) -> i32 {
8-
let n = i32::try_from(n)
9-
.expect("`err_scale_for_ulp`: exponent is too large to create an error scale");
10-
// we know this fits
11-
let prec = i32::try_from(F::PRECISION).unwrap();
12-
-(prec - n - 1)
13-
}
14-
155
/// Disturbes a floating-point result by a relative error in the range (-2^scale, 2^scale).
166
///
177
/// For a 2^N ULP error, you can use an `err_scale` of `-(F::PRECISION - 1 - N)`.
@@ -37,6 +27,27 @@ pub(crate) fn apply_random_float_error<F: rustc_apfloat::Float>(
3727
(val * (F::from_u128(1).value + err).value).value
3828
}
3929

30+
/// [`apply_random_float_error`] gives instructions to apply a 2^N ULP error.
31+
/// This function implements these instructions such that applying a 2^N ULP error is less error prone.
32+
/// So for a 2^N ULP error, you would pass N as the `ulp_exponent` argument.
33+
pub(crate) fn apply_random_float_error_ulp<F: rustc_apfloat::Float>(
34+
ecx: &mut crate::MiriInterpCx<'_>,
35+
val: F,
36+
ulp_exponent: u32,
37+
) -> F {
38+
apply_random_float_error(ecx, val, ulp_exponent_err_scale::<F>(ulp_exponent))
39+
}
40+
41+
/// Creates an error scale to pass to `apply_random_float_error` when applying a 2^n ULP error.
42+
/// For example, if you want to apply a 16 ULP error, you should pass 4, because 2^4 = 16.
43+
pub(crate) fn ulp_exponent_err_scale<F: rustc_apfloat::Float>(n: u32) -> i32 {
44+
let n = i32::try_from(n)
45+
.expect("`err_scale_for_ulp`: exponent is too large to create an error scale");
46+
// we know this fits
47+
let prec = i32::try_from(F::PRECISION).unwrap();
48+
-(prec - n - 1)
49+
}
50+
4051
pub(crate) fn sqrt<S: rustc_apfloat::ieee::Semantics>(x: IeeeFloat<S>) -> IeeeFloat<S> {
4152
match x.category() {
4253
// preserve zero sign

src/shims/foreign_items.rs

Lines changed: 22 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ use rustc_target::callconv::{Conv, FnAbi};
1717
use self::helpers::{ToHost, ToSoft};
1818
use super::alloc::EvalContextExt as _;
1919
use super::backtrace::EvalContextExt as _;
20-
use crate::math::ulp_err_scale;
2120
use crate::*;
2221

2322
/// Type of dynamic symbols (for `dlsym` et al)
@@ -762,11 +761,12 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
762761
"tgammaf" => f_host.gamma(),
763762
_ => bug!(),
764763
};
765-
// Apply a relative error of 16ULP to simulate non-determinism
766-
let res = math::apply_random_float_error(
764+
// Apply a relative error of 16ULP to introduce some non-determinism
765+
// simulating imprecise implementations and optimizations.
766+
let res = math::apply_random_float_error_ulp(
767767
this,
768768
res.to_soft(),
769-
ulp_err_scale::<rustc_apfloat::ieee::Single>(4),
769+
4
770770
);
771771
let res = this.adjust_nan(res, &[f]);
772772
this.write_scalar(res, dest)?;
@@ -790,11 +790,12 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
790790
"fdimf" => f1.to_host().abs_sub(f2.to_host()).to_soft(),
791791
_ => bug!(),
792792
};
793-
// Apply a relative error of 16ULP to simulate non-determinism
794-
let res = math::apply_random_float_error(
793+
// Apply a relative error of 16ULP to introduce some non-determinism
794+
// simulating imprecise implementations and optimizations.
795+
let res = math::apply_random_float_error_ulp(
795796
this,
796797
res,
797-
ulp_err_scale::<rustc_apfloat::ieee::Single>(4),
798+
4
798799
);
799800
let res = this.adjust_nan(res, &[f1, f2]);
800801
this.write_scalar(res, dest)?;
@@ -830,11 +831,12 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
830831
"tgamma" => f_host.gamma(),
831832
_ => bug!(),
832833
};
833-
// Apply a relative error of 16ULP to simulate non-determinism
834-
let res = math::apply_random_float_error(
834+
// Apply a relative error of 16ULP to introduce some non-determinism
835+
// simulating imprecise implementations and optimizations.
836+
let res = math::apply_random_float_error_ulp(
835837
this,
836838
res.to_soft(),
837-
ulp_err_scale::<rustc_apfloat::ieee::Double>(4),
839+
4
838840
);
839841
let res = this.adjust_nan(res, &[f]);
840842
this.write_scalar(res, dest)?;
@@ -858,11 +860,12 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
858860
"fdim" => f1.to_host().abs_sub(f2.to_host()).to_soft(),
859861
_ => bug!(),
860862
};
861-
// Apply a relative error of 16ULP to simulate non-determinism
862-
let res = math::apply_random_float_error(
863+
// Apply a relative error of 16ULP to introduce some non-determinism
864+
// simulating imprecise implementations and optimizations.
865+
let res = math::apply_random_float_error_ulp(
863866
this,
864867
res,
865-
ulp_err_scale::<rustc_apfloat::ieee::Double>(4),
868+
4
866869
);
867870
let res = this.adjust_nan(res, &[f1, f2]);
868871
this.write_scalar(res, dest)?;
@@ -889,12 +892,9 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
889892
// Using host floats (but it's fine, these operations do not have guaranteed precision).
890893
let (res, sign) = x.to_host().ln_gamma();
891894
this.write_int(sign, &signp)?;
892-
// Apply a relative error of 16ULP to simulate non-determinism
893-
let res = math::apply_random_float_error(
894-
this,
895-
res.to_soft(),
896-
ulp_err_scale::<rustc_apfloat::ieee::Single>(4),
897-
);
895+
// Apply a relative error of 16ULP to introduce some non-determinism
896+
// simulating imprecise implementations and optimizations.
897+
let res = math::apply_random_float_error_ulp(this, res.to_soft(), 4);
898898
let res = this.adjust_nan(res, &[x]);
899899
this.write_scalar(res, dest)?;
900900
}
@@ -906,12 +906,9 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
906906
// Using host floats (but it's fine, these operations do not have guaranteed precision).
907907
let (res, sign) = x.to_host().ln_gamma();
908908
this.write_int(sign, &signp)?;
909-
// Apply a relative error of 16ULP to simulate non-determinism
910-
let res = math::apply_random_float_error(
911-
this,
912-
res.to_soft(),
913-
ulp_err_scale::<rustc_apfloat::ieee::Double>(4),
914-
);
909+
// Apply a relative error of 16ULP to introduce some non-determinism
910+
// simulating imprecise implementations and optimizations.
911+
let res = math::apply_random_float_error_ulp(this, res.to_soft(), 4);
915912
let res = this.adjust_nan(res, &[x]);
916913
this.write_scalar(res, dest)?;
917914
}

0 commit comments

Comments
 (0)