@@ -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