@@ -6,11 +6,8 @@ mod simd;
66pub use self :: atomic:: AtomicRmwOp ;
77
88#[ rustfmt:: skip] // prevent `use` reordering
9- use std:: ops:: Neg ;
10-
119use rand:: Rng ;
1210use rustc_abi:: Size ;
13- use rustc_apfloat:: ieee:: { IeeeFloat , Semantics } ;
1411use rustc_apfloat:: { self , Float , Round } ;
1512use rustc_middle:: mir;
1613use rustc_middle:: ty:: { self , FloatTy } ;
@@ -19,7 +16,6 @@ use rustc_span::{Symbol, sym};
1916use self :: atomic:: EvalContextExt as _;
2017use self :: helpers:: { ToHost , ToSoft } ;
2118use self :: simd:: EvalContextExt as _;
22- use crate :: math:: { IeeeExt , apply_random_float_error_ulp} ;
2319use crate :: * ;
2420
2521/// Check that the number of args is what we expect.
@@ -212,7 +208,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
212208 let [ f] = check_intrinsic_arg_count ( args) ?;
213209 let f = this. read_scalar ( f) ?. to_f32 ( ) ?;
214210
215- let res = fixed_float_value ( this, intrinsic_name, & [ f] ) . unwrap_or_else ( || {
211+ let res = math :: fixed_float_value ( this, intrinsic_name, & [ f] ) . unwrap_or_else ( || {
216212 // Using host floats (but it's fine, these operations do not have
217213 // guaranteed precision).
218214 let host = f. to_host ( ) ;
@@ -230,15 +226,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
230226
231227 // Apply a relative error of 4ULP to introduce some non-determinism
232228 // simulating imprecise implementations and optimizations.
233- let res = apply_random_float_error_ulp (
229+ let res = math :: apply_random_float_error_ulp (
234230 this,
235231 res,
236232 4 ,
237233 ) ;
238234
239235 // Clamp the result to the guaranteed range of this function according to the C standard,
240236 // if any.
241- clamp_float_value ( intrinsic_name, res)
237+ math :: clamp_float_value ( intrinsic_name, res)
242238 } ) ;
243239 let res = this. adjust_nan ( res, & [ f] ) ;
244240 this. write_scalar ( res, dest) ?;
@@ -256,7 +252,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
256252 let [ f] = check_intrinsic_arg_count ( args) ?;
257253 let f = this. read_scalar ( f) ?. to_f64 ( ) ?;
258254
259- let res = fixed_float_value ( this, intrinsic_name, & [ f] ) . unwrap_or_else ( || {
255+ let res = math :: fixed_float_value ( this, intrinsic_name, & [ f] ) . unwrap_or_else ( || {
260256 // Using host floats (but it's fine, these operations do not have
261257 // guaranteed precision).
262258 let host = f. to_host ( ) ;
@@ -274,15 +270,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
274270
275271 // Apply a relative error of 4ULP to introduce some non-determinism
276272 // simulating imprecise implementations and optimizations.
277- let res = apply_random_float_error_ulp (
273+ let res = math :: apply_random_float_error_ulp (
278274 this,
279275 res,
280276 4 ,
281277 ) ;
282278
283279 // Clamp the result to the guaranteed range of this function according to the C standard,
284280 // if any.
285- clamp_float_value ( intrinsic_name, res)
281+ math :: clamp_float_value ( intrinsic_name, res)
286282 } ) ;
287283 let res = this. adjust_nan ( res, & [ f] ) ;
288284 this. write_scalar ( res, dest) ?;
@@ -333,14 +329,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
333329 let f1 = this. read_scalar ( f1) ?. to_f32 ( ) ?;
334330 let f2 = this. read_scalar ( f2) ?. to_f32 ( ) ?;
335331
336- let res = fixed_float_value ( this, intrinsic_name, & [ f1, f2] ) . unwrap_or_else ( || {
337- // Using host floats (but it's fine, this operation does not have guaranteed precision).
338- let res = f1. to_host ( ) . powf ( f2. to_host ( ) ) . to_soft ( ) ;
332+ let res =
333+ math:: 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 ( ) ;
339336
340- // Apply a relative error of 4ULP to introduce some non-determinism
341- // simulating imprecise implementations and optimizations.
342- apply_random_float_error_ulp ( this, res, 4 )
343- } ) ;
337+ // Apply a relative error of 4ULP to introduce some non-determinism
338+ // simulating imprecise implementations and optimizations.
339+ math :: apply_random_float_error_ulp ( this, res, 4 )
340+ } ) ;
344341 let res = this. adjust_nan ( res, & [ f1, f2] ) ;
345342 this. write_scalar ( res, dest) ?;
346343 }
@@ -349,14 +346,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
349346 let f1 = this. read_scalar ( f1) ?. to_f64 ( ) ?;
350347 let f2 = this. read_scalar ( f2) ?. to_f64 ( ) ?;
351348
352- let res = fixed_float_value ( this, intrinsic_name, & [ f1, f2] ) . unwrap_or_else ( || {
353- // Using host floats (but it's fine, this operation does not have guaranteed precision).
354- let res = f1. to_host ( ) . powf ( f2. to_host ( ) ) . to_soft ( ) ;
349+ let res =
350+ math:: fixed_float_value ( this, intrinsic_name, & [ f1, f2] ) . unwrap_or_else ( || {
351+ // Using host floats (but it's fine, this operation does not have guaranteed precision).
352+ let res = f1. to_host ( ) . powf ( f2. to_host ( ) ) . to_soft ( ) ;
355353
356- // Apply a relative error of 4ULP to introduce some non-determinism
357- // simulating imprecise implementations and optimizations.
358- apply_random_float_error_ulp ( this, res, 4 )
359- } ) ;
354+ // Apply a relative error of 4ULP to introduce some non-determinism
355+ // simulating imprecise implementations and optimizations.
356+ math :: apply_random_float_error_ulp ( this, res, 4 )
357+ } ) ;
360358 let res = this. adjust_nan ( res, & [ f1, f2] ) ;
361359 this. write_scalar ( res, dest) ?;
362360 }
@@ -366,13 +364,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
366364 let f = this. read_scalar ( f) ?. to_f32 ( ) ?;
367365 let i = this. read_scalar ( i) ?. to_i32 ( ) ?;
368366
369- let res = fixed_powi_float_value ( this, f, i) . unwrap_or_else ( || {
367+ let res = math :: fixed_powi_value ( this, f, i) . unwrap_or_else ( || {
370368 // Using host floats (but it's fine, this operation does not have guaranteed precision).
371369 let res = f. to_host ( ) . powi ( i) . to_soft ( ) ;
372370
373371 // Apply a relative error of 4ULP to introduce some non-determinism
374372 // simulating imprecise implementations and optimizations.
375- apply_random_float_error_ulp ( this, res, 4 )
373+ math :: apply_random_float_error_ulp ( this, res, 4 )
376374 } ) ;
377375 let res = this. adjust_nan ( res, & [ f] ) ;
378376 this. write_scalar ( res, dest) ?;
@@ -382,13 +380,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
382380 let f = this. read_scalar ( f) ?. to_f64 ( ) ?;
383381 let i = this. read_scalar ( i) ?. to_i32 ( ) ?;
384382
385- let res = fixed_powi_float_value ( this, f, i) . unwrap_or_else ( || {
383+ let res = math :: fixed_powi_value ( this, f, i) . unwrap_or_else ( || {
386384 // Using host floats (but it's fine, this operation does not have guaranteed precision).
387385 let res = f. to_host ( ) . powi ( i) . to_soft ( ) ;
388386
389387 // Apply a relative error of 4ULP to introduce some non-determinism
390388 // simulating imprecise implementations and optimizations.
391- apply_random_float_error_ulp ( this, res, 4 )
389+ math :: apply_random_float_error_ulp ( this, res, 4 )
392390 } ) ;
393391 let res = this. adjust_nan ( res, & [ f] ) ;
394392 this. write_scalar ( res, dest) ?;
@@ -443,7 +441,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
443441 }
444442 // Apply a relative error of 4ULP to simulate non-deterministic precision loss
445443 // due to optimizations.
446- let res = crate :: math:: apply_random_float_error_to_imm ( this, res, 4 ) ?;
444+ let res = math:: apply_random_float_error_to_imm ( this, res, 4 ) ?;
447445 this. write_immediate ( * res, dest) ?;
448446 }
449447
@@ -480,108 +478,3 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
480478 interp_ok ( EmulateItemResult :: NeedsReturn )
481479 }
482480}
483-
484- /// For the intrinsics:
485- /// - sinf32, sinf64
486- /// - cosf32, cosf64
487- /// - expf32, expf64, exp2f32, exp2f64
488- /// - logf32, logf64, log2f32, log2f64, log10f32, log10f64
489- /// - powf32, powf64
490- ///
491- /// # Return
492- ///
493- /// Returns `Some(output)` if the `intrinsic` results in a defined fixed `output` specified in the C standard
494- /// (specifically, C23 annex F.10) when given `args` as arguments. Outputs that are unaffected by a relative error
495- /// (such as INF and zero) are not handled here, they are assumed to be handled by the underlying
496- /// implementation. Returns `None` if no specific value is guaranteed.
497- ///
498- /// # Note
499- ///
500- /// For `powf*` operations of the form:
501- ///
502- /// - `(SNaN)^(±0)`
503- /// - `1^(SNaN)`
504- ///
505- /// The result is implementation-defined:
506- /// - musl returns for both `1.0`
507- /// - glibc returns for both `NaN`
508- ///
509- /// This discrepancy exists because SNaN handling is not consistently defined across platforms,
510- /// and the C standard leaves behavior for SNaNs unspecified.
511- ///
512- /// Miri chooses to adhere to both implementations and returns either one of them non-deterministically.
513- fn fixed_float_value < S : Semantics > (
514- ecx : & mut MiriInterpCx < ' _ > ,
515- intrinsic_name : & str ,
516- args : & [ IeeeFloat < S > ] ,
517- ) -> Option < IeeeFloat < S > > {
518- let one = IeeeFloat :: < S > :: one ( ) ;
519- Some ( match ( intrinsic_name, args) {
520- // cos(+- 0) = 1
521- ( "cosf32" | "cosf64" , [ input] ) if input. is_zero ( ) => one,
522-
523- // e^0 = 1
524- ( "expf32" | "expf64" | "exp2f32" | "exp2f64" , [ input] ) if input. is_zero ( ) => one,
525-
526- // (-1)^(±INF) = 1
527- ( "powf32" | "powf64" , [ base, exp] ) if * base == -one && exp. is_infinite ( ) => one,
528-
529- // 1^y = 1 for any y, even a NaN
530- ( "powf32" | "powf64" , [ base, exp] ) if * base == one => {
531- let rng = ecx. machine . rng . get_mut ( ) ;
532- // SNaN exponents get special treatment: they might return 1, or a NaN.
533- let return_nan = exp. is_signaling ( ) && ecx. machine . float_nondet && rng. random ( ) ;
534- // Handle both the musl and glibc cases non-deterministically.
535- if return_nan { ecx. generate_nan ( args) } else { one }
536- }
537-
538- // x^(±0) = 1 for any x, even a NaN
539- ( "powf32" | "powf64" , [ base, exp] ) if exp. is_zero ( ) => {
540- let rng = ecx. machine . rng . get_mut ( ) ;
541- // SNaN bases get special treatment: they might return 1, or a NaN.
542- let return_nan = base. is_signaling ( ) && ecx. machine . float_nondet && rng. random ( ) ;
543- // Handle both the musl and glibc cases non-deterministically.
544- if return_nan { ecx. generate_nan ( args) } else { one }
545- }
546-
547- // There are a lot of cases for fixed outputs according to the C Standard, but these are
548- // mainly INF or zero which are not affected by the applied error.
549- _ => return None ,
550- } )
551- }
552-
553- /// Returns `Some(output)` if `powi` (called `pown` in C) results in a fixed value specified in the
554- /// C standard (specifically, C23 annex F.10.4.6) when doing `base^exp`. Otherwise, returns `None`.
555- fn fixed_powi_float_value < S : Semantics > (
556- ecx : & mut MiriInterpCx < ' _ > ,
557- base : IeeeFloat < S > ,
558- exp : i32 ,
559- ) -> Option < IeeeFloat < S > > {
560- Some ( match exp {
561- 0 => {
562- let one = IeeeFloat :: < S > :: one ( ) ;
563- let rng = ecx. machine . rng . get_mut ( ) ;
564- let return_nan = ecx. machine . float_nondet && rng. random ( ) && base. is_signaling ( ) ;
565- // For SNaN treatment, we are consistent with `powf`above.
566- // (We wouldn't have two, unlike powf all implementations seem to agree for powi,
567- // but for now we are maximally conservative.)
568- if return_nan { ecx. generate_nan ( & [ base] ) } else { one }
569- }
570-
571- _ => return None ,
572- } )
573- }
574-
575- /// Given an floating-point operation and a floating-point value, clamps the result to the output
576- /// range of the given operation.
577- fn clamp_float_value < S : Semantics > ( intrinsic_name : & str , val : IeeeFloat < S > ) -> IeeeFloat < S > {
578- match intrinsic_name {
579- // sin and cos: [-1, 1]
580- "sinf32" | "cosf32" | "sinf64" | "cosf64" =>
581- val. clamp ( IeeeFloat :: < S > :: one ( ) . neg ( ) , IeeeFloat :: < S > :: one ( ) ) ,
582- // exp: [0, +INF]
583- "expf32" | "exp2f32" | "expf64" | "exp2f64" =>
584- IeeeFloat :: < S > :: maximum ( val, IeeeFloat :: < S > :: ZERO ) ,
585- _ => val,
586- }
587- }
0 commit comments