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;
1310use rustc_middle:: ty:: { self , FloatTy } ;
@@ -16,7 +13,7 @@ use rustc_span::{Symbol, sym};
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} ;
16+ use crate :: math:: apply_random_float_error_ulp;
2017use crate :: * ;
2118
2219/// Check that the number of args is what we expect.
@@ -209,7 +206,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
209206 let [ f] = check_intrinsic_arg_count ( args) ?;
210207 let f = this. read_scalar ( f) ?. to_f32 ( ) ?;
211208
212- let res = fixed_float_value ( this, intrinsic_name, & [ f] ) . unwrap_or_else ( || {
209+ let res = math :: fixed_float_value ( this, intrinsic_name, & [ f] ) . unwrap_or_else ( || {
213210 // Using host floats (but it's fine, these operations do not have
214211 // guaranteed precision).
215212 let host = f. to_host ( ) ;
@@ -227,15 +224,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
227224
228225 // Apply a relative error of 4ULP to introduce some non-determinism
229226 // simulating imprecise implementations and optimizations.
230- let res = apply_random_float_error_ulp (
227+ let res = math :: apply_random_float_error_ulp (
231228 this,
232229 res,
233230 4 ,
234231 ) ;
235232
236233 // Clamp the result to the guaranteed range of this function according to the C standard,
237234 // if any.
238- clamp_float_value ( intrinsic_name, res)
235+ math :: clamp_float_value ( intrinsic_name, res)
239236 } ) ;
240237 let res = this. adjust_nan ( res, & [ f] ) ;
241238 this. write_scalar ( res, dest) ?;
@@ -253,7 +250,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
253250 let [ f] = check_intrinsic_arg_count ( args) ?;
254251 let f = this. read_scalar ( f) ?. to_f64 ( ) ?;
255252
256- let res = fixed_float_value ( this, intrinsic_name, & [ f] ) . unwrap_or_else ( || {
253+ let res = math :: fixed_float_value ( this, intrinsic_name, & [ f] ) . unwrap_or_else ( || {
257254 // Using host floats (but it's fine, these operations do not have
258255 // guaranteed precision).
259256 let host = f. to_host ( ) ;
@@ -271,15 +268,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
271268
272269 // Apply a relative error of 4ULP to introduce some non-determinism
273270 // simulating imprecise implementations and optimizations.
274- let res = apply_random_float_error_ulp (
271+ let res = math :: apply_random_float_error_ulp (
275272 this,
276273 res,
277274 4 ,
278275 ) ;
279276
280277 // Clamp the result to the guaranteed range of this function according to the C standard,
281278 // if any.
282- clamp_float_value ( intrinsic_name, res)
279+ math :: clamp_float_value ( intrinsic_name, res)
283280 } ) ;
284281 let res = this. adjust_nan ( res, & [ f] ) ;
285282 this. write_scalar ( res, dest) ?;
@@ -330,14 +327,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
330327 let f1 = this. read_scalar ( f1) ?. to_f32 ( ) ?;
331328 let f2 = this. read_scalar ( f2) ?. to_f32 ( ) ?;
332329
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 ( ) ;
330+ let res =
331+ math:: fixed_float_value ( this, intrinsic_name, & [ f1, f2] ) . unwrap_or_else ( || {
332+ // Using host floats (but it's fine, this operation does not have guaranteed precision).
333+ let res = f1. to_host ( ) . powf ( f2. to_host ( ) ) . to_soft ( ) ;
336334
337- // Apply a relative error of 4ULP to introduce some non-determinism
338- // simulating imprecise implementations and optimizations.
339- apply_random_float_error_ulp ( this, res, 4 )
340- } ) ;
335+ // Apply a relative error of 4ULP to introduce some non-determinism
336+ // simulating imprecise implementations and optimizations.
337+ math :: apply_random_float_error_ulp ( this, res, 4 )
338+ } ) ;
341339 let res = this. adjust_nan ( res, & [ f1, f2] ) ;
342340 this. write_scalar ( res, dest) ?;
343341 }
@@ -346,14 +344,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
346344 let f1 = this. read_scalar ( f1) ?. to_f64 ( ) ?;
347345 let f2 = this. read_scalar ( f2) ?. to_f64 ( ) ?;
348346
349- let res = 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 ( ) ;
347+ let res =
348+ math:: fixed_float_value ( this, intrinsic_name, & [ f1, f2] ) . unwrap_or_else ( || {
349+ // Using host floats (but it's fine, this operation does not have guaranteed precision).
350+ let res = f1. to_host ( ) . powf ( f2. to_host ( ) ) . to_soft ( ) ;
352351
353- // Apply a relative error of 4ULP to introduce some non-determinism
354- // simulating imprecise implementations and optimizations.
355- apply_random_float_error_ulp ( this, res, 4 )
356- } ) ;
352+ // Apply a relative error of 4ULP to introduce some non-determinism
353+ // simulating imprecise implementations and optimizations.
354+ math :: apply_random_float_error_ulp ( this, res, 4 )
355+ } ) ;
357356 let res = this. adjust_nan ( res, & [ f1, f2] ) ;
358357 this. write_scalar ( res, dest) ?;
359358 }
@@ -363,7 +362,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
363362 let f = this. read_scalar ( f) ?. to_f32 ( ) ?;
364363 let i = this. read_scalar ( i) ?. to_i32 ( ) ?;
365364
366- let res = fixed_powi_float_value ( this, f, i) . unwrap_or_else ( || {
365+ let res = math :: fixed_powi_float_value ( this, f, i) . unwrap_or_else ( || {
367366 // Using host floats (but it's fine, this operation does not have guaranteed precision).
368367 let res = f. to_host ( ) . powi ( i) . to_soft ( ) ;
369368
@@ -379,13 +378,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
379378 let f = this. read_scalar ( f) ?. to_f64 ( ) ?;
380379 let i = this. read_scalar ( i) ?. to_i32 ( ) ?;
381380
382- let res = fixed_powi_float_value ( this, f, i) . unwrap_or_else ( || {
381+ let res = math :: fixed_powi_float_value ( this, f, i) . unwrap_or_else ( || {
383382 // Using host floats (but it's fine, this operation does not have guaranteed precision).
384383 let res = f. to_host ( ) . powi ( i) . to_soft ( ) ;
385384
386385 // Apply a relative error of 4ULP to introduce some non-determinism
387386 // simulating imprecise implementations and optimizations.
388- apply_random_float_error_ulp ( this, res, 4 )
387+ math :: apply_random_float_error_ulp ( this, res, 4 )
389388 } ) ;
390389 let res = this. adjust_nan ( res, & [ f] ) ;
391390 this. write_scalar ( res, dest) ?;
@@ -440,7 +439,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
440439 }
441440 // Apply a relative error of 4ULP to simulate non-deterministic precision loss
442441 // due to optimizations.
443- let res = crate :: math:: apply_random_float_error_to_imm ( this, res, 4 ) ?;
442+ let res = math:: apply_random_float_error_to_imm ( this, res, 4 ) ?;
444443 this. write_immediate ( * res, dest) ?;
445444 }
446445
@@ -477,108 +476,3 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
477476 interp_ok ( EmulateItemResult :: NeedsReturn )
478477 }
479478}
480-
481- /// For the intrinsics:
482- /// - sinf32, sinf64
483- /// - cosf32, cosf64
484- /// - expf32, expf64, exp2f32, exp2f64
485- /// - logf32, logf64, log2f32, log2f64, log10f32, log10f64
486- /// - powf32, powf64
487- ///
488- /// # Return
489- ///
490- /// Returns `Some(output)` if the `intrinsic` results in a defined fixed `output` specified in the C standard
491- /// (specifically, C23 annex F.10) when given `args` as arguments. Outputs that are unaffected by a relative error
492- /// (such as INF and zero) are not handled here, they are assumed to be handled by the underlying
493- /// implementation. Returns `None` if no specific value is guaranteed.
494- ///
495- /// # Note
496- ///
497- /// For `powf*` operations of the form:
498- ///
499- /// - `(SNaN)^(±0)`
500- /// - `1^(SNaN)`
501- ///
502- /// The result is implementation-defined:
503- /// - musl returns for both `1.0`
504- /// - glibc returns for both `NaN`
505- ///
506- /// This discrepancy exists because SNaN handling is not consistently defined across platforms,
507- /// and the C standard leaves behavior for SNaNs unspecified.
508- ///
509- /// Miri chooses to adhere to both implementations and returns either one of them non-deterministically.
510- fn fixed_float_value < S : Semantics > (
511- ecx : & mut MiriInterpCx < ' _ > ,
512- intrinsic_name : & str ,
513- args : & [ IeeeFloat < S > ] ,
514- ) -> Option < IeeeFloat < S > > {
515- let one = IeeeFloat :: < S > :: one ( ) ;
516- Some ( match ( intrinsic_name, args) {
517- // cos(+- 0) = 1
518- ( "cosf32" | "cosf64" , [ input] ) if input. is_zero ( ) => one,
519-
520- // e^0 = 1
521- ( "expf32" | "expf64" | "exp2f32" | "exp2f64" , [ input] ) if input. is_zero ( ) => one,
522-
523- // (-1)^(±INF) = 1
524- ( "powf32" | "powf64" , [ base, exp] ) if * base == -one && exp. is_infinite ( ) => one,
525-
526- // 1^y = 1 for any y, even a NaN
527- ( "powf32" | "powf64" , [ base, exp] ) if * base == one => {
528- let rng = ecx. machine . rng . get_mut ( ) ;
529- // SNaN exponents get special treatment: they might return 1, or a NaN.
530- let return_nan = exp. is_signaling ( ) && ecx. machine . float_nondet && rng. random ( ) ;
531- // Handle both the musl and glibc cases non-deterministically.
532- if return_nan { ecx. generate_nan ( args) } else { one }
533- }
534-
535- // x^(±0) = 1 for any x, even a NaN
536- ( "powf32" | "powf64" , [ base, exp] ) if exp. is_zero ( ) => {
537- let rng = ecx. machine . rng . get_mut ( ) ;
538- // SNaN bases get special treatment: they might return 1, or a NaN.
539- let return_nan = base. is_signaling ( ) && ecx. machine . float_nondet && rng. random ( ) ;
540- // Handle both the musl and glibc cases non-deterministically.
541- if return_nan { ecx. generate_nan ( args) } else { one }
542- }
543-
544- // There are a lot of cases for fixed outputs according to the C Standard, but these are
545- // mainly INF or zero which are not affected by the applied error.
546- _ => return None ,
547- } )
548- }
549-
550- /// Returns `Some(output)` if `powi` (called `pown` in C) results in a fixed value specified in the
551- /// C standard (specifically, C23 annex F.10.4.6) when doing `base^exp`. Otherwise, returns `None`.
552- fn fixed_powi_float_value < S : Semantics > (
553- ecx : & mut MiriInterpCx < ' _ > ,
554- base : IeeeFloat < S > ,
555- exp : i32 ,
556- ) -> Option < IeeeFloat < S > > {
557- Some ( match exp {
558- 0 => {
559- let one = IeeeFloat :: < S > :: one ( ) ;
560- let rng = ecx. machine . rng . get_mut ( ) ;
561- let return_nan = ecx. machine . float_nondet && rng. random ( ) && base. is_signaling ( ) ;
562- // For SNaN treatment, we are consistent with `powf`above.
563- // (We wouldn't have two, unlike powf all implementations seem to agree for powi,
564- // but for now we are maximally conservative.)
565- if return_nan { ecx. generate_nan ( & [ base] ) } else { one }
566- }
567-
568- _ => return None ,
569- } )
570- }
571-
572- /// Given an floating-point operation and a floating-point value, clamps the result to the output
573- /// range of the given operation.
574- fn clamp_float_value < S : Semantics > ( intrinsic_name : & str , val : IeeeFloat < S > ) -> IeeeFloat < S > {
575- match intrinsic_name {
576- // sin and cos: [-1, 1]
577- "sinf32" | "cosf32" | "sinf64" | "cosf64" =>
578- val. clamp ( IeeeFloat :: < S > :: one ( ) . neg ( ) , IeeeFloat :: < S > :: one ( ) ) ,
579- // exp: [0, +INF]
580- "expf32" | "exp2f32" | "expf64" | "exp2f64" =>
581- IeeeFloat :: < S > :: maximum ( val, IeeeFloat :: < S > :: ZERO ) ,
582- _ => val,
583- }
584- }
0 commit comments