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 , check_intrinsic_arg_count} ;
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
2219impl < ' tcx > EvalContextExt < ' tcx > for crate :: MiriInterpCx < ' tcx > { }
@@ -191,7 +188,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
191188 let [ f] = check_intrinsic_arg_count ( args) ?;
192189 let f = this. read_scalar ( f) ?. to_f32 ( ) ?;
193190
194- let res = fixed_float_value ( this, intrinsic_name, & [ f] ) . unwrap_or_else ( || {
191+ let res = math :: fixed_float_value ( this, intrinsic_name, & [ f] ) . unwrap_or_else ( || {
195192 // Using host floats (but it's fine, these operations do not have
196193 // guaranteed precision).
197194 let host = f. to_host ( ) ;
@@ -209,15 +206,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
209206
210207 // Apply a relative error of 4ULP to introduce some non-determinism
211208 // simulating imprecise implementations and optimizations.
212- let res = apply_random_float_error_ulp (
209+ let res = math :: apply_random_float_error_ulp (
213210 this,
214211 res,
215212 2 , // log2(4)
216213 ) ;
217214
218215 // Clamp the result to the guaranteed range of this function according to the C standard,
219216 // if any.
220- clamp_float_value ( intrinsic_name, res)
217+ math :: clamp_float_value ( intrinsic_name, res)
221218 } ) ;
222219 let res = this. adjust_nan ( res, & [ f] ) ;
223220 this. write_scalar ( res, dest) ?;
@@ -235,7 +232,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
235232 let [ f] = check_intrinsic_arg_count ( args) ?;
236233 let f = this. read_scalar ( f) ?. to_f64 ( ) ?;
237234
238- let res = fixed_float_value ( this, intrinsic_name, & [ f] ) . unwrap_or_else ( || {
235+ let res = math :: fixed_float_value ( this, intrinsic_name, & [ f] ) . unwrap_or_else ( || {
239236 // Using host floats (but it's fine, these operations do not have
240237 // guaranteed precision).
241238 let host = f. to_host ( ) ;
@@ -253,15 +250,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
253250
254251 // Apply a relative error of 4ULP to introduce some non-determinism
255252 // simulating imprecise implementations and optimizations.
256- let res = apply_random_float_error_ulp (
253+ let res = math :: apply_random_float_error_ulp (
257254 this,
258255 res,
259256 2 , // log2(4)
260257 ) ;
261258
262259 // Clamp the result to the guaranteed range of this function according to the C standard,
263260 // if any.
264- clamp_float_value ( intrinsic_name, res)
261+ math :: clamp_float_value ( intrinsic_name, res)
265262 } ) ;
266263 let res = this. adjust_nan ( res, & [ f] ) ;
267264 this. write_scalar ( res, dest) ?;
@@ -312,16 +309,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
312309 let f1 = this. read_scalar ( f1) ?. to_f32 ( ) ?;
313310 let f2 = this. read_scalar ( f2) ?. to_f32 ( ) ?;
314311
315- let res = fixed_float_value ( this, intrinsic_name, & [ f1, f2] ) . unwrap_or_else ( || {
316- // Using host floats (but it's fine, this operation does not have guaranteed precision).
317- let res = f1. to_host ( ) . powf ( f2. to_host ( ) ) . to_soft ( ) ;
312+ let res =
313+ math:: fixed_float_value ( this, intrinsic_name, & [ f1, f2] ) . unwrap_or_else ( || {
314+ // Using host floats (but it's fine, this operation does not have guaranteed precision).
315+ let res = f1. to_host ( ) . powf ( f2. to_host ( ) ) . to_soft ( ) ;
318316
319- // Apply a relative error of 4ULP to introduce some non-determinism
320- // simulating imprecise implementations and optimizations.
321- apply_random_float_error_ulp (
322- this, res, 2 , // log2(4)
323- )
324- } ) ;
317+ // Apply a relative error of 4ULP to introduce some non-determinism
318+ // simulating imprecise implementations and optimizations.
319+ math :: apply_random_float_error_ulp (
320+ this, res, 2 , // log2(4)
321+ )
322+ } ) ;
325323 let res = this. adjust_nan ( res, & [ f1, f2] ) ;
326324 this. write_scalar ( res, dest) ?;
327325 }
@@ -330,16 +328,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
330328 let f1 = this. read_scalar ( f1) ?. to_f64 ( ) ?;
331329 let f2 = this. read_scalar ( f2) ?. to_f64 ( ) ?;
332330
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 ( ) ;
331+ let res =
332+ math:: fixed_float_value ( this, intrinsic_name, & [ f1, f2] ) . unwrap_or_else ( || {
333+ // Using host floats (but it's fine, this operation does not have guaranteed precision).
334+ let res = f1. to_host ( ) . powf ( f2. to_host ( ) ) . to_soft ( ) ;
336335
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- } ) ;
336+ // Apply a relative error of 4ULP to introduce some non-determinism
337+ // simulating imprecise implementations and optimizations.
338+ math :: apply_random_float_error_ulp (
339+ this, res, 2 , // log2(4)
340+ )
341+ } ) ;
343342 let res = this. adjust_nan ( res, & [ f1, f2] ) ;
344343 this. write_scalar ( res, dest) ?;
345344 }
@@ -349,7 +348,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
349348 let f = this. read_scalar ( f) ?. to_f32 ( ) ?;
350349 let i = this. read_scalar ( i) ?. to_i32 ( ) ?;
351350
352- let res = fixed_powi_float_value ( this, f, i) . unwrap_or_else ( || {
351+ let res = math :: fixed_powi_float_value ( this, f, i) . unwrap_or_else ( || {
353352 // Using host floats (but it's fine, this operation does not have guaranteed precision).
354353 let res = f. to_host ( ) . powi ( i) . to_soft ( ) ;
355354
@@ -367,13 +366,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
367366 let f = this. read_scalar ( f) ?. to_f64 ( ) ?;
368367 let i = this. read_scalar ( i) ?. to_i32 ( ) ?;
369368
370- let res = fixed_powi_float_value ( this, f, i) . unwrap_or_else ( || {
369+ let res = math :: fixed_powi_float_value ( this, f, i) . unwrap_or_else ( || {
371370 // Using host floats (but it's fine, this operation does not have guaranteed precision).
372371 let res = f. to_host ( ) . powi ( i) . to_soft ( ) ;
373372
374373 // Apply a relative error of 4ULP to introduce some non-determinism
375374 // simulating imprecise implementations and optimizations.
376- apply_random_float_error_ulp (
375+ math :: apply_random_float_error_ulp (
377376 this, res, 2 , // log2(4)
378377 )
379378 } ) ;
@@ -430,7 +429,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
430429 }
431430 // Apply a relative error of 4ULP to simulate non-deterministic precision loss
432431 // due to optimizations.
433- let res = apply_random_float_error_to_imm ( this, res, 2 /* log2(4) */ ) ?;
432+ let res = math :: apply_random_float_error_to_imm ( this, res, 2 /* log2(4) */ ) ?;
434433 this. write_immediate ( * res, dest) ?;
435434 }
436435
@@ -467,133 +466,3 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
467466 interp_ok ( EmulateItemResult :: NeedsReturn )
468467 }
469468}
470-
471- /// Applies a random ULP floating point error to `val` and returns the new value.
472- /// So if you want an X ULP error, `ulp_exponent` should be log2(X).
473- ///
474- /// Will fail if `val` is not a floating point number.
475- fn apply_random_float_error_to_imm < ' tcx > (
476- ecx : & mut MiriInterpCx < ' tcx > ,
477- val : ImmTy < ' tcx > ,
478- ulp_exponent : u32 ,
479- ) -> InterpResult < ' tcx , ImmTy < ' tcx > > {
480- let scalar = val. to_scalar_int ( ) ?;
481- let res: ScalarInt = match val. layout . ty . kind ( ) {
482- ty:: Float ( FloatTy :: F16 ) =>
483- apply_random_float_error_ulp ( ecx, scalar. to_f16 ( ) , ulp_exponent) . into ( ) ,
484- ty:: Float ( FloatTy :: F32 ) =>
485- apply_random_float_error_ulp ( ecx, scalar. to_f32 ( ) , ulp_exponent) . into ( ) ,
486- ty:: Float ( FloatTy :: F64 ) =>
487- apply_random_float_error_ulp ( ecx, scalar. to_f64 ( ) , ulp_exponent) . into ( ) ,
488- ty:: Float ( FloatTy :: F128 ) =>
489- apply_random_float_error_ulp ( ecx, scalar. to_f128 ( ) , ulp_exponent) . into ( ) ,
490- _ => bug ! ( "intrinsic called with non-float input type" ) ,
491- } ;
492-
493- interp_ok ( ImmTy :: from_scalar_int ( res, val. layout ) )
494- }
495-
496- /// For the intrinsics:
497- /// - sinf32, sinf64
498- /// - cosf32, cosf64
499- /// - expf32, expf64, exp2f32, exp2f64
500- /// - logf32, logf64, log2f32, log2f64, log10f32, log10f64
501- /// - powf32, powf64
502- ///
503- /// # Return
504- ///
505- /// Returns `Some(output)` if the `intrinsic` results in a defined fixed `output` specified in the C standard
506- /// (specifically, C23 annex F.10) when given `args` as arguments. Outputs that are unaffected by a relative error
507- /// (such as INF and zero) are not handled here, they are assumed to be handled by the underlying
508- /// implementation. Returns `None` if no specific value is guaranteed.
509- ///
510- /// # Note
511- ///
512- /// For `powf*` operations of the form:
513- ///
514- /// - `(SNaN)^(±0)`
515- /// - `1^(SNaN)`
516- ///
517- /// The result is implementation-defined:
518- /// - musl returns for both `1.0`
519- /// - glibc returns for both `NaN`
520- ///
521- /// This discrepancy exists because SNaN handling is not consistently defined across platforms,
522- /// and the C standard leaves behavior for SNaNs unspecified.
523- ///
524- /// Miri chooses to adhere to both implementations and returns either one of them non-deterministically.
525- fn fixed_float_value < S : Semantics > (
526- ecx : & mut MiriInterpCx < ' _ > ,
527- intrinsic_name : & str ,
528- args : & [ IeeeFloat < S > ] ,
529- ) -> Option < IeeeFloat < S > > {
530- let one = IeeeFloat :: < S > :: one ( ) ;
531- Some ( match ( intrinsic_name, args) {
532- // cos(+- 0) = 1
533- ( "cosf32" | "cosf64" , [ input] ) if input. is_zero ( ) => one,
534-
535- // e^0 = 1
536- ( "expf32" | "expf64" | "exp2f32" | "exp2f64" , [ input] ) if input. is_zero ( ) => one,
537-
538- // (-1)^(±INF) = 1
539- ( "powf32" | "powf64" , [ base, exp] ) if * base == -one && exp. is_infinite ( ) => one,
540-
541- // 1^y = 1 for any y, even a NaN
542- ( "powf32" | "powf64" , [ base, exp] ) if * base == one => {
543- let rng = ecx. machine . rng . get_mut ( ) ;
544- // SNaN exponents get special treatment: they might return 1, or a NaN.
545- let return_nan = exp. is_signaling ( ) && ecx. machine . float_nondet && rng. random ( ) ;
546- // Handle both the musl and glibc cases non-deterministically.
547- if return_nan { ecx. generate_nan ( args) } else { one }
548- }
549-
550- // x^(±0) = 1 for any x, even a NaN
551- ( "powf32" | "powf64" , [ base, exp] ) if exp. is_zero ( ) => {
552- let rng = ecx. machine . rng . get_mut ( ) ;
553- // SNaN bases get special treatment: they might return 1, or a NaN.
554- let return_nan = base. is_signaling ( ) && ecx. machine . float_nondet && rng. random ( ) ;
555- // Handle both the musl and glibc cases non-deterministically.
556- if return_nan { ecx. generate_nan ( args) } else { one }
557- }
558-
559- // There are a lot of cases for fixed outputs according to the C Standard, but these are
560- // mainly INF or zero which are not affected by the applied error.
561- _ => return None ,
562- } )
563- }
564-
565- /// Returns `Some(output)` if `powi` (called `pown` in C) results in a fixed value specified in the
566- /// C standard (specifically, C23 annex F.10.4.6) when doing `base^exp`. Otherwise, returns `None`.
567- fn fixed_powi_float_value < S : Semantics > (
568- ecx : & mut MiriInterpCx < ' _ > ,
569- base : IeeeFloat < S > ,
570- exp : i32 ,
571- ) -> Option < IeeeFloat < S > > {
572- Some ( match exp {
573- 0 => {
574- let one = IeeeFloat :: < S > :: one ( ) ;
575- let rng = ecx. machine . rng . get_mut ( ) ;
576- let return_nan = ecx. machine . float_nondet && rng. random ( ) && base. is_signaling ( ) ;
577- // For SNaN treatment, we are consistent with `powf`above.
578- // (We wouldn't have two, unlike powf all implementations seem to agree for powi,
579- // but for now we are maximally conservative.)
580- if return_nan { ecx. generate_nan ( & [ base] ) } else { one }
581- }
582-
583- _ => return None ,
584- } )
585- }
586-
587- /// Given an floating-point operation and a floating-point value, clamps the result to the output
588- /// range of the given operation.
589- fn clamp_float_value < S : Semantics > ( intrinsic_name : & str , val : IeeeFloat < S > ) -> IeeeFloat < S > {
590- match intrinsic_name {
591- // sin and cos: [-1, 1]
592- "sinf32" | "cosf32" | "sinf64" | "cosf64" =>
593- val. clamp ( IeeeFloat :: < S > :: one ( ) . neg ( ) , IeeeFloat :: < S > :: one ( ) ) ,
594- // exp: [0, +INF]
595- "expf32" | "exp2f32" | "expf64" | "exp2f64" =>
596- IeeeFloat :: < S > :: maximum ( val, IeeeFloat :: < S > :: ZERO ) ,
597- _ => val,
598- }
599- }
0 commit comments