@@ -7,12 +7,13 @@ use rand::Rng;
77use rustc_abi:: Size ;
88use rustc_apfloat:: { Float , Round } ;
99use rustc_middle:: mir;
10- use rustc_middle:: ty:: { self , FloatTy } ;
10+ use rustc_middle:: ty:: { self , FloatTy , ScalarInt } ;
1111use rustc_span:: { Symbol , sym} ;
1212
1313use self :: atomic:: EvalContextExt as _;
1414use self :: helpers:: { ToHost , ToSoft , check_intrinsic_arg_count} ;
1515use self :: simd:: EvalContextExt as _;
16+ use crate :: math:: apply_random_float_error_ulp;
1617use crate :: * ;
1718
1819impl < ' tcx > EvalContextExt < ' tcx > for crate :: MiriInterpCx < ' tcx > { }
@@ -206,10 +207,26 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
206207 this. write_scalar ( res, dest) ?;
207208 }
208209
210+ "sqrtf32" => {
211+ let [ f] = check_intrinsic_arg_count ( args) ?;
212+ let f = this. read_scalar ( f) ?. to_f32 ( ) ?;
213+ // Sqrt is specified to be fully precise.
214+ let res = math:: sqrt ( f) ;
215+ let res = this. adjust_nan ( res, & [ f] ) ;
216+ this. write_scalar ( res, dest) ?;
217+ }
218+ "sqrtf64" => {
219+ let [ f] = check_intrinsic_arg_count ( args) ?;
220+ let f = this. read_scalar ( f) ?. to_f64 ( ) ?;
221+ // Sqrt is specified to be fully precise.
222+ let res = math:: sqrt ( f) ;
223+ let res = this. adjust_nan ( res, & [ f] ) ;
224+ this. write_scalar ( res, dest) ?;
225+ }
226+
209227 #[ rustfmt:: skip]
210228 | "sinf32"
211229 | "cosf32"
212- | "sqrtf32"
213230 | "expf32"
214231 | "exp2f32"
215232 | "logf32"
@@ -218,26 +235,33 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
218235 => {
219236 let [ f] = check_intrinsic_arg_count ( args) ?;
220237 let f = this. read_scalar ( f) ?. to_f32 ( ) ?;
221- // Using host floats except for sqrt (but it's fine, these operations do not have
238+ // Using host floats (but it's fine, these operations do not have
222239 // guaranteed precision).
240+ let host = f. to_host ( ) ;
223241 let res = match intrinsic_name {
224- "sinf32" => f. to_host ( ) . sin ( ) . to_soft ( ) ,
225- "cosf32" => f. to_host ( ) . cos ( ) . to_soft ( ) ,
226- "sqrtf32" => math:: sqrt ( f) ,
227- "expf32" => f. to_host ( ) . exp ( ) . to_soft ( ) ,
228- "exp2f32" => f. to_host ( ) . exp2 ( ) . to_soft ( ) ,
229- "logf32" => f. to_host ( ) . ln ( ) . to_soft ( ) ,
230- "log10f32" => f. to_host ( ) . log10 ( ) . to_soft ( ) ,
231- "log2f32" => f. to_host ( ) . log2 ( ) . to_soft ( ) ,
242+ "sinf32" => host. sin ( ) ,
243+ "cosf32" => host. cos ( ) ,
244+ "expf32" => host. exp ( ) ,
245+ "exp2f32" => host. exp2 ( ) ,
246+ "logf32" => host. ln ( ) ,
247+ "log10f32" => host. log10 ( ) ,
248+ "log2f32" => host. log2 ( ) ,
232249 _ => bug ! ( ) ,
233250 } ;
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 (
255+ this,
256+ res,
257+ 4 , // log2(16)
258+ ) ;
234259 let res = this. adjust_nan ( res, & [ f] ) ;
235260 this. write_scalar ( res, dest) ?;
236261 }
237262 #[ rustfmt:: skip]
238263 | "sinf64"
239264 | "cosf64"
240- | "sqrtf64"
241265 | "expf64"
242266 | "exp2f64"
243267 | "logf64"
@@ -246,19 +270,27 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
246270 => {
247271 let [ f] = check_intrinsic_arg_count ( args) ?;
248272 let f = this. read_scalar ( f) ?. to_f64 ( ) ?;
249- // Using host floats except for sqrt (but it's fine, these operations do not have
273+ // Using host floats (but it's fine, these operations do not have
250274 // guaranteed precision).
275+ let host = f. to_host ( ) ;
251276 let res = match intrinsic_name {
252- "sinf64" => f. to_host ( ) . sin ( ) . to_soft ( ) ,
253- "cosf64" => f. to_host ( ) . cos ( ) . to_soft ( ) ,
254- "sqrtf64" => math:: sqrt ( f) ,
255- "expf64" => f. to_host ( ) . exp ( ) . to_soft ( ) ,
256- "exp2f64" => f. to_host ( ) . exp2 ( ) . to_soft ( ) ,
257- "logf64" => f. to_host ( ) . ln ( ) . to_soft ( ) ,
258- "log10f64" => f. to_host ( ) . log10 ( ) . to_soft ( ) ,
259- "log2f64" => f. to_host ( ) . log2 ( ) . to_soft ( ) ,
277+ "sinf64" => host. sin ( ) ,
278+ "cosf64" => host. cos ( ) ,
279+ "expf64" => host. exp ( ) ,
280+ "exp2f64" => host. exp2 ( ) ,
281+ "logf64" => host. ln ( ) ,
282+ "log10f64" => host. log10 ( ) ,
283+ "log2f64" => host. log2 ( ) ,
260284 _ => bug ! ( ) ,
261285 } ;
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 (
290+ this,
291+ res,
292+ 4 , // log2(16)
293+ ) ;
262294 let res = this. adjust_nan ( res, & [ f] ) ;
263295 this. write_scalar ( res, dest) ?;
264296 }
@@ -316,6 +348,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
316348 }
317349
318350 "powf32" => {
351+ // FIXME: apply random relative error but without altering behaviour of powf
319352 let [ f1, f2] = check_intrinsic_arg_count ( args) ?;
320353 let f1 = this. read_scalar ( f1) ?. to_f32 ( ) ?;
321354 let f2 = this. read_scalar ( f2) ?. to_f32 ( ) ?;
@@ -325,6 +358,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
325358 this. write_scalar ( res, dest) ?;
326359 }
327360 "powf64" => {
361+ // FIXME: apply random relative error but without altering behaviour of powf
328362 let [ f1, f2] = check_intrinsic_arg_count ( args) ?;
329363 let f1 = this. read_scalar ( f1) ?. to_f64 ( ) ?;
330364 let f2 = this. read_scalar ( f2) ?. to_f64 ( ) ?;
@@ -335,6 +369,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
335369 }
336370
337371 "powif32" => {
372+ // FIXME: apply random relative error but without altering behaviour of powi
338373 let [ f, i] = check_intrinsic_arg_count ( args) ?;
339374 let f = this. read_scalar ( f) ?. to_f32 ( ) ?;
340375 let i = this. read_scalar ( i) ?. to_i32 ( ) ?;
@@ -344,6 +379,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
344379 this. write_scalar ( res, dest) ?;
345380 }
346381 "powif64" => {
382+ // FIXME: apply random relative error but without altering behaviour of powi
347383 let [ f, i] = check_intrinsic_arg_count ( args) ?;
348384 let f = this. read_scalar ( f) ?. to_f64 ( ) ?;
349385 let i = this. read_scalar ( i) ?. to_i32 ( ) ?;
@@ -372,7 +408,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
372408 _ => bug ! ( ) ,
373409 } ;
374410 let res = this. binary_op ( op, & a, & b) ?;
375- // `binary_op` already called `generate_nan` if necessary.
411+ // `binary_op` already called `generate_nan` if needed.
412+ // Apply a relative error of 16ULP to simulate non-deterministic precision loss
413+ // due to optimizations.
414+ let res = apply_random_float_error_to_imm ( this, res, 4 /* log2(16) */ ) ?;
376415 this. write_immediate ( * res, dest) ?;
377416 }
378417
@@ -418,11 +457,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
418457 _ => { }
419458 }
420459 let res = this. binary_op ( op, & a, & b) ?;
460+ // This cannot be a NaN so we also don't have to apply any non-determinism.
461+ // (Also, `binary_op` already called `generate_nan` if needed.)
421462 if !float_finite ( & res) ? {
422463 throw_ub_format ! ( "`{intrinsic_name}` intrinsic produced non-finite value as result" ) ;
423464 }
424- // This cannot be a NaN so we also don't have to apply any non-determinism.
425- // (Also, `binary_op` already called `generate_nan` if needed.)
465+ // Apply a relative error of 16ULP to simulate non-deterministic precision loss
466+ // due to optimizations.
467+ let res = apply_random_float_error_to_imm ( this, res, 4 /* log2(16) */ ) ?;
426468 this. write_immediate ( * res, dest) ?;
427469 }
428470
@@ -455,3 +497,26 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
455497 interp_ok ( EmulateItemResult :: NeedsReturn )
456498 }
457499}
500+
501+ /// Applies a random 16ULP floating point error to `val` and returns the new value.
502+ /// Will fail if `val` is not a floating point number.
503+ fn apply_random_float_error_to_imm < ' tcx > (
504+ ecx : & mut MiriInterpCx < ' tcx > ,
505+ val : ImmTy < ' tcx > ,
506+ ulp_exponent : u32 ,
507+ ) -> InterpResult < ' tcx , ImmTy < ' tcx > > {
508+ let scalar = val. to_scalar_int ( ) ?;
509+ let res: ScalarInt = match val. layout . ty . kind ( ) {
510+ ty:: Float ( FloatTy :: F16 ) =>
511+ apply_random_float_error_ulp ( ecx, scalar. to_f16 ( ) , ulp_exponent) . into ( ) ,
512+ ty:: Float ( FloatTy :: F32 ) =>
513+ apply_random_float_error_ulp ( ecx, scalar. to_f32 ( ) , ulp_exponent) . into ( ) ,
514+ ty:: Float ( FloatTy :: F64 ) =>
515+ apply_random_float_error_ulp ( ecx, scalar. to_f64 ( ) , ulp_exponent) . into ( ) ,
516+ ty:: Float ( FloatTy :: F128 ) =>
517+ apply_random_float_error_ulp ( ecx, scalar. to_f128 ( ) , ulp_exponent) . into ( ) ,
518+ _ => bug ! ( "intrinsic called with non-float input type" ) ,
519+ } ;
520+
521+ interp_ok ( ImmTy :: from_scalar_int ( res, val. layout ) )
522+ }
0 commit comments