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,6 @@ 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} ;
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 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 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,14 +326,15 @@ 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 ( this, res, 4 )
340- } ) ;
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 ( this, res, 4 )
337+ } ) ;
341338 let res = this. adjust_nan ( res, & [ f1, f2] ) ;
342339 this. write_scalar ( res, dest) ?;
343340 }
@@ -346,14 +343,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
346343 let f1 = this. read_scalar ( f1) ?. to_f64 ( ) ?;
347344 let f2 = this. read_scalar ( f2) ?. to_f64 ( ) ?;
348345
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 ( ) ;
346+ let res =
347+ math:: fixed_float_value ( this, intrinsic_name, & [ f1, f2] ) . unwrap_or_else ( || {
348+ // Using host floats (but it's fine, this operation does not have guaranteed precision).
349+ let res = f1. to_host ( ) . powf ( f2. to_host ( ) ) . to_soft ( ) ;
352350
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- } ) ;
351+ // Apply a relative error of 4ULP to introduce some non-determinism
352+ // simulating imprecise implementations and optimizations.
353+ math :: apply_random_float_error_ulp ( this, res, 4 )
354+ } ) ;
357355 let res = this. adjust_nan ( res, & [ f1, f2] ) ;
358356 this. write_scalar ( res, dest) ?;
359357 }
@@ -363,13 +361,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
363361 let f = this. read_scalar ( f) ?. to_f32 ( ) ?;
364362 let i = this. read_scalar ( i) ?. to_i32 ( ) ?;
365363
366- let res = fixed_powi_float_value ( this, f, i) . unwrap_or_else ( || {
364+ let res = math :: fixed_powi_value ( this, f, i) . unwrap_or_else ( || {
367365 // Using host floats (but it's fine, this operation does not have guaranteed precision).
368366 let res = f. to_host ( ) . powi ( i) . to_soft ( ) ;
369367
370368 // Apply a relative error of 4ULP to introduce some non-determinism
371369 // simulating imprecise implementations and optimizations.
372- apply_random_float_error_ulp ( this, res, 4 )
370+ math :: apply_random_float_error_ulp ( this, res, 4 )
373371 } ) ;
374372 let res = this. adjust_nan ( res, & [ f] ) ;
375373 this. write_scalar ( res, dest) ?;
@@ -379,13 +377,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
379377 let f = this. read_scalar ( f) ?. to_f64 ( ) ?;
380378 let i = this. read_scalar ( i) ?. to_i32 ( ) ?;
381379
382- let res = fixed_powi_float_value ( this, f, i) . unwrap_or_else ( || {
380+ let res = math :: fixed_powi_value ( this, f, i) . unwrap_or_else ( || {
383381 // Using host floats (but it's fine, this operation does not have guaranteed precision).
384382 let res = f. to_host ( ) . powi ( i) . to_soft ( ) ;
385383
386384 // Apply a relative error of 4ULP to introduce some non-determinism
387385 // simulating imprecise implementations and optimizations.
388- apply_random_float_error_ulp ( this, res, 4 )
386+ math :: apply_random_float_error_ulp ( this, res, 4 )
389387 } ) ;
390388 let res = this. adjust_nan ( res, & [ f] ) ;
391389 this. write_scalar ( res, dest) ?;
@@ -440,7 +438,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
440438 }
441439 // Apply a relative error of 4ULP to simulate non-deterministic precision loss
442440 // due to optimizations.
443- let res = crate :: math:: apply_random_float_error_to_imm ( this, res, 4 ) ?;
441+ let res = math:: apply_random_float_error_to_imm ( this, res, 4 ) ?;
444442 this. write_immediate ( * res, dest) ?;
445443 }
446444
@@ -477,108 +475,3 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
477475 interp_ok ( EmulateItemResult :: NeedsReturn )
478476 }
479477}
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