33mod atomic;
44mod simd;
55
6- use std:: ops:: Neg ;
7-
86use rand:: Rng ;
97use rustc_abi:: Size ;
10- use rustc_apfloat:: ieee:: { IeeeFloat , Semantics } ;
118use rustc_apfloat:: { self , Float , Round } ;
129use rustc_middle:: mir;
13- use rustc_middle:: ty:: { self , FloatTy , ScalarInt } ;
10+ use rustc_middle:: ty:: { self , FloatTy } ;
1411use rustc_span:: { Symbol , sym} ;
1512
1613use self :: atomic:: EvalContextExt as _;
1714use self :: helpers:: { ToHost , ToSoft } ;
1815use self :: simd:: EvalContextExt as _;
19- use crate :: math:: { IeeeExt , apply_random_float_error_ulp} ;
2016use crate :: * ;
2117
2218/// Check that the number of args is what we expect.
@@ -209,7 +205,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
209205 let [ f] = check_intrinsic_arg_count ( args) ?;
210206 let f = this. read_scalar ( f) ?. to_f32 ( ) ?;
211207
212- let res = fixed_float_value ( this, intrinsic_name, & [ f] ) . unwrap_or_else ( || {
208+ let res = math :: fixed_float_value ( this, intrinsic_name, & [ f] ) . unwrap_or_else ( || {
213209 // Using host floats (but it's fine, these operations do not have
214210 // guaranteed precision).
215211 let host = f. to_host ( ) ;
@@ -227,15 +223,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
227223
228224 // Apply a relative error of 4ULP to introduce some non-determinism
229225 // simulating imprecise implementations and optimizations.
230- let res = apply_random_float_error_ulp (
226+ let res = math :: apply_random_float_error_ulp (
231227 this,
232228 res,
233229 2 , // log2(4)
234230 ) ;
235231
236232 // Clamp the result to the guaranteed range of this function according to the C standard,
237233 // if any.
238- clamp_float_value ( intrinsic_name, res)
234+ math :: clamp_float_value ( intrinsic_name, res)
239235 } ) ;
240236 let res = this. adjust_nan ( res, & [ f] ) ;
241237 this. write_scalar ( res, dest) ?;
@@ -253,7 +249,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
253249 let [ f] = check_intrinsic_arg_count ( args) ?;
254250 let f = this. read_scalar ( f) ?. to_f64 ( ) ?;
255251
256- let res = fixed_float_value ( this, intrinsic_name, & [ f] ) . unwrap_or_else ( || {
252+ let res = math :: fixed_float_value ( this, intrinsic_name, & [ f] ) . unwrap_or_else ( || {
257253 // Using host floats (but it's fine, these operations do not have
258254 // guaranteed precision).
259255 let host = f. to_host ( ) ;
@@ -271,15 +267,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
271267
272268 // Apply a relative error of 4ULP to introduce some non-determinism
273269 // simulating imprecise implementations and optimizations.
274- let res = apply_random_float_error_ulp (
270+ let res = math :: apply_random_float_error_ulp (
275271 this,
276272 res,
277273 2 , // log2(4)
278274 ) ;
279275
280276 // Clamp the result to the guaranteed range of this function according to the C standard,
281277 // if any.
282- clamp_float_value ( intrinsic_name, res)
278+ math :: clamp_float_value ( intrinsic_name, res)
283279 } ) ;
284280 let res = this. adjust_nan ( res, & [ f] ) ;
285281 this. write_scalar ( res, dest) ?;
@@ -330,16 +326,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
330326 let f1 = this. read_scalar ( f1) ?. to_f32 ( ) ?;
331327 let f2 = this. read_scalar ( f2) ?. to_f32 ( ) ?;
332328
333- let res = fixed_float_value ( this, intrinsic_name, & [ f1, f2] ) . unwrap_or_else ( || {
334- // Using host floats (but it's fine, this operation does not have guaranteed precision).
335- let res = f1. to_host ( ) . powf ( f2. to_host ( ) ) . to_soft ( ) ;
329+ let res =
330+ math:: fixed_float_value ( this, intrinsic_name, & [ f1, f2] ) . unwrap_or_else ( || {
331+ // Using host floats (but it's fine, this operation does not have guaranteed precision).
332+ let res = f1. to_host ( ) . powf ( f2. to_host ( ) ) . to_soft ( ) ;
336333
337- // Apply a relative error of 4ULP to introduce some non-determinism
338- // simulating imprecise implementations and optimizations.
339- apply_random_float_error_ulp (
340- this, res, 2 , // log2(4)
341- )
342- } ) ;
334+ // Apply a relative error of 4ULP to introduce some non-determinism
335+ // simulating imprecise implementations and optimizations.
336+ math :: apply_random_float_error_ulp (
337+ this, res, 2 , // log2(4)
338+ )
339+ } ) ;
343340 let res = this. adjust_nan ( res, & [ f1, f2] ) ;
344341 this. write_scalar ( res, dest) ?;
345342 }
@@ -348,16 +345,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
348345 let f1 = this. read_scalar ( f1) ?. to_f64 ( ) ?;
349346 let f2 = this. read_scalar ( f2) ?. to_f64 ( ) ?;
350347
351- let res = fixed_float_value ( this, intrinsic_name, & [ f1, f2] ) . unwrap_or_else ( || {
352- // Using host floats (but it's fine, this operation does not have guaranteed precision).
353- let res = f1. to_host ( ) . powf ( f2. to_host ( ) ) . to_soft ( ) ;
348+ let res =
349+ math:: fixed_float_value ( this, intrinsic_name, & [ f1, f2] ) . unwrap_or_else ( || {
350+ // Using host floats (but it's fine, this operation does not have guaranteed precision).
351+ let res = f1. to_host ( ) . powf ( f2. to_host ( ) ) . to_soft ( ) ;
354352
355- // Apply a relative error of 4ULP to introduce some non-determinism
356- // simulating imprecise implementations and optimizations.
357- apply_random_float_error_ulp (
358- this, res, 2 , // log2(4)
359- )
360- } ) ;
353+ // Apply a relative error of 4ULP to introduce some non-determinism
354+ // simulating imprecise implementations and optimizations.
355+ math :: apply_random_float_error_ulp (
356+ this, res, 2 , // log2(4)
357+ )
358+ } ) ;
361359 let res = this. adjust_nan ( res, & [ f1, f2] ) ;
362360 this. write_scalar ( res, dest) ?;
363361 }
@@ -367,13 +365,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
367365 let f = this. read_scalar ( f) ?. to_f32 ( ) ?;
368366 let i = this. read_scalar ( i) ?. to_i32 ( ) ?;
369367
370- let res = fixed_powi_float_value ( this, f, i) . unwrap_or_else ( || {
368+ let res = math :: fixed_powi_value ( this, f, i) . unwrap_or_else ( || {
371369 // Using host floats (but it's fine, this operation does not have guaranteed precision).
372370 let res = f. to_host ( ) . powi ( i) . to_soft ( ) ;
373371
374372 // Apply a relative error of 4ULP to introduce some non-determinism
375373 // simulating imprecise implementations and optimizations.
376- apply_random_float_error_ulp (
374+ math :: apply_random_float_error_ulp (
377375 this, res, 2 , // log2(4)
378376 )
379377 } ) ;
@@ -385,13 +383,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
385383 let f = this. read_scalar ( f) ?. to_f64 ( ) ?;
386384 let i = this. read_scalar ( i) ?. to_i32 ( ) ?;
387385
388- let res = fixed_powi_float_value ( this, f, i) . unwrap_or_else ( || {
386+ let res = math :: fixed_powi_value ( this, f, i) . unwrap_or_else ( || {
389387 // Using host floats (but it's fine, this operation does not have guaranteed precision).
390388 let res = f. to_host ( ) . powi ( i) . to_soft ( ) ;
391389
392390 // Apply a relative error of 4ULP to introduce some non-determinism
393391 // simulating imprecise implementations and optimizations.
394- apply_random_float_error_ulp (
392+ math :: apply_random_float_error_ulp (
395393 this, res, 2 , // log2(4)
396394 )
397395 } ) ;
@@ -448,7 +446,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
448446 }
449447 // Apply a relative error of 4ULP to simulate non-deterministic precision loss
450448 // due to optimizations.
451- let res = apply_random_float_error_to_imm ( this, res, 2 /* log2(4) */ ) ?;
449+ let res = math :: apply_random_float_error_to_imm ( this, res, 2 /* log2(4) */ ) ?;
452450 this. write_immediate ( * res, dest) ?;
453451 }
454452
@@ -485,133 +483,3 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
485483 interp_ok ( EmulateItemResult :: NeedsReturn )
486484 }
487485}
488-
489- /// Applies a random ULP floating point error to `val` and returns the new value.
490- /// So if you want an X ULP error, `ulp_exponent` should be log2(X).
491- ///
492- /// Will fail if `val` is not a floating point number.
493- fn apply_random_float_error_to_imm < ' tcx > (
494- ecx : & mut MiriInterpCx < ' tcx > ,
495- val : ImmTy < ' tcx > ,
496- ulp_exponent : u32 ,
497- ) -> InterpResult < ' tcx , ImmTy < ' tcx > > {
498- let scalar = val. to_scalar_int ( ) ?;
499- let res: ScalarInt = match val. layout . ty . kind ( ) {
500- ty:: Float ( FloatTy :: F16 ) =>
501- apply_random_float_error_ulp ( ecx, scalar. to_f16 ( ) , ulp_exponent) . into ( ) ,
502- ty:: Float ( FloatTy :: F32 ) =>
503- apply_random_float_error_ulp ( ecx, scalar. to_f32 ( ) , ulp_exponent) . into ( ) ,
504- ty:: Float ( FloatTy :: F64 ) =>
505- apply_random_float_error_ulp ( ecx, scalar. to_f64 ( ) , ulp_exponent) . into ( ) ,
506- ty:: Float ( FloatTy :: F128 ) =>
507- apply_random_float_error_ulp ( ecx, scalar. to_f128 ( ) , ulp_exponent) . into ( ) ,
508- _ => bug ! ( "intrinsic called with non-float input type" ) ,
509- } ;
510-
511- interp_ok ( ImmTy :: from_scalar_int ( res, val. layout ) )
512- }
513-
514- /// For the intrinsics:
515- /// - sinf32, sinf64
516- /// - cosf32, cosf64
517- /// - expf32, expf64, exp2f32, exp2f64
518- /// - logf32, logf64, log2f32, log2f64, log10f32, log10f64
519- /// - powf32, powf64
520- ///
521- /// # Return
522- ///
523- /// Returns `Some(output)` if the `intrinsic` results in a defined fixed `output` specified in the C standard
524- /// (specifically, C23 annex F.10) when given `args` as arguments. Outputs that are unaffected by a relative error
525- /// (such as INF and zero) are not handled here, they are assumed to be handled by the underlying
526- /// implementation. Returns `None` if no specific value is guaranteed.
527- ///
528- /// # Note
529- ///
530- /// For `powf*` operations of the form:
531- ///
532- /// - `(SNaN)^(±0)`
533- /// - `1^(SNaN)`
534- ///
535- /// The result is implementation-defined:
536- /// - musl returns for both `1.0`
537- /// - glibc returns for both `NaN`
538- ///
539- /// This discrepancy exists because SNaN handling is not consistently defined across platforms,
540- /// and the C standard leaves behavior for SNaNs unspecified.
541- ///
542- /// Miri chooses to adhere to both implementations and returns either one of them non-deterministically.
543- fn fixed_float_value < S : Semantics > (
544- ecx : & mut MiriInterpCx < ' _ > ,
545- intrinsic_name : & str ,
546- args : & [ IeeeFloat < S > ] ,
547- ) -> Option < IeeeFloat < S > > {
548- let one = IeeeFloat :: < S > :: one ( ) ;
549- Some ( match ( intrinsic_name, args) {
550- // cos(+- 0) = 1
551- ( "cosf32" | "cosf64" , [ input] ) if input. is_zero ( ) => one,
552-
553- // e^0 = 1
554- ( "expf32" | "expf64" | "exp2f32" | "exp2f64" , [ input] ) if input. is_zero ( ) => one,
555-
556- // (-1)^(±INF) = 1
557- ( "powf32" | "powf64" , [ base, exp] ) if * base == -one && exp. is_infinite ( ) => one,
558-
559- // 1^y = 1 for any y, even a NaN
560- ( "powf32" | "powf64" , [ base, exp] ) if * base == one => {
561- let rng = ecx. machine . rng . get_mut ( ) ;
562- // SNaN exponents get special treatment: they might return 1, or a NaN.
563- let return_nan = exp. is_signaling ( ) && ecx. machine . float_nondet && rng. random ( ) ;
564- // Handle both the musl and glibc cases non-deterministically.
565- if return_nan { ecx. generate_nan ( args) } else { one }
566- }
567-
568- // x^(±0) = 1 for any x, even a NaN
569- ( "powf32" | "powf64" , [ base, exp] ) if exp. is_zero ( ) => {
570- let rng = ecx. machine . rng . get_mut ( ) ;
571- // SNaN bases get special treatment: they might return 1, or a NaN.
572- let return_nan = base. is_signaling ( ) && ecx. machine . float_nondet && rng. random ( ) ;
573- // Handle both the musl and glibc cases non-deterministically.
574- if return_nan { ecx. generate_nan ( args) } else { one }
575- }
576-
577- // There are a lot of cases for fixed outputs according to the C Standard, but these are
578- // mainly INF or zero which are not affected by the applied error.
579- _ => return None ,
580- } )
581- }
582-
583- /// Returns `Some(output)` if `powi` (called `pown` in C) results in a fixed value specified in the
584- /// C standard (specifically, C23 annex F.10.4.6) when doing `base^exp`. Otherwise, returns `None`.
585- fn fixed_powi_float_value < S : Semantics > (
586- ecx : & mut MiriInterpCx < ' _ > ,
587- base : IeeeFloat < S > ,
588- exp : i32 ,
589- ) -> Option < IeeeFloat < S > > {
590- Some ( match exp {
591- 0 => {
592- let one = IeeeFloat :: < S > :: one ( ) ;
593- let rng = ecx. machine . rng . get_mut ( ) ;
594- let return_nan = ecx. machine . float_nondet && rng. random ( ) && base. is_signaling ( ) ;
595- // For SNaN treatment, we are consistent with `powf`above.
596- // (We wouldn't have two, unlike powf all implementations seem to agree for powi,
597- // but for now we are maximally conservative.)
598- if return_nan { ecx. generate_nan ( & [ base] ) } else { one }
599- }
600-
601- _ => return None ,
602- } )
603- }
604-
605- /// Given an floating-point operation and a floating-point value, clamps the result to the output
606- /// range of the given operation.
607- fn clamp_float_value < S : Semantics > ( intrinsic_name : & str , val : IeeeFloat < S > ) -> IeeeFloat < S > {
608- match intrinsic_name {
609- // sin and cos: [-1, 1]
610- "sinf32" | "cosf32" | "sinf64" | "cosf64" =>
611- val. clamp ( IeeeFloat :: < S > :: one ( ) . neg ( ) , IeeeFloat :: < S > :: one ( ) ) ,
612- // exp: [0, +INF]
613- "expf32" | "exp2f32" | "expf64" | "exp2f64" =>
614- IeeeFloat :: < S > :: maximum ( val, IeeeFloat :: < S > :: ZERO ) ,
615- _ => val,
616- }
617- }
0 commit comments