@@ -6,11 +6,8 @@ mod simd;
6
6
pub use self :: atomic:: AtomicRmwOp ;
7
7
8
8
#[ rustfmt:: skip] // prevent `use` reordering
9
- use std:: ops:: Neg ;
10
-
11
9
use rand:: Rng ;
12
10
use rustc_abi:: Size ;
13
- use rustc_apfloat:: ieee:: { IeeeFloat , Semantics } ;
14
11
use rustc_apfloat:: { self , Float , Round } ;
15
12
use rustc_middle:: mir;
16
13
use rustc_middle:: ty:: { self , FloatTy } ;
@@ -19,7 +16,6 @@ use rustc_span::{Symbol, sym};
19
16
use self :: atomic:: EvalContextExt as _;
20
17
use self :: helpers:: { ToHost , ToSoft } ;
21
18
use self :: simd:: EvalContextExt as _;
22
- use crate :: math:: { IeeeExt , apply_random_float_error_ulp} ;
23
19
use crate :: * ;
24
20
25
21
/// Check that the number of args is what we expect.
@@ -212,7 +208,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
212
208
let [ f] = check_intrinsic_arg_count ( args) ?;
213
209
let f = this. read_scalar ( f) ?. to_f32 ( ) ?;
214
210
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 ( || {
216
212
// Using host floats (but it's fine, these operations do not have
217
213
// guaranteed precision).
218
214
let host = f. to_host ( ) ;
@@ -230,15 +226,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
230
226
231
227
// Apply a relative error of 4ULP to introduce some non-determinism
232
228
// simulating imprecise implementations and optimizations.
233
- let res = apply_random_float_error_ulp (
229
+ let res = math :: apply_random_float_error_ulp (
234
230
this,
235
231
res,
236
232
4 ,
237
233
) ;
238
234
239
235
// Clamp the result to the guaranteed range of this function according to the C standard,
240
236
// if any.
241
- clamp_float_value ( intrinsic_name, res)
237
+ math :: clamp_float_value ( intrinsic_name, res)
242
238
} ) ;
243
239
let res = this. adjust_nan ( res, & [ f] ) ;
244
240
this. write_scalar ( res, dest) ?;
@@ -256,7 +252,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
256
252
let [ f] = check_intrinsic_arg_count ( args) ?;
257
253
let f = this. read_scalar ( f) ?. to_f64 ( ) ?;
258
254
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 ( || {
260
256
// Using host floats (but it's fine, these operations do not have
261
257
// guaranteed precision).
262
258
let host = f. to_host ( ) ;
@@ -274,15 +270,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
274
270
275
271
// Apply a relative error of 4ULP to introduce some non-determinism
276
272
// simulating imprecise implementations and optimizations.
277
- let res = apply_random_float_error_ulp (
273
+ let res = math :: apply_random_float_error_ulp (
278
274
this,
279
275
res,
280
276
4 ,
281
277
) ;
282
278
283
279
// Clamp the result to the guaranteed range of this function according to the C standard,
284
280
// if any.
285
- clamp_float_value ( intrinsic_name, res)
281
+ math :: clamp_float_value ( intrinsic_name, res)
286
282
} ) ;
287
283
let res = this. adjust_nan ( res, & [ f] ) ;
288
284
this. write_scalar ( res, dest) ?;
@@ -333,14 +329,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
333
329
let f1 = this. read_scalar ( f1) ?. to_f32 ( ) ?;
334
330
let f2 = this. read_scalar ( f2) ?. to_f32 ( ) ?;
335
331
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 ( ) ;
339
336
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
+ } ) ;
344
341
let res = this. adjust_nan ( res, & [ f1, f2] ) ;
345
342
this. write_scalar ( res, dest) ?;
346
343
}
@@ -349,14 +346,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
349
346
let f1 = this. read_scalar ( f1) ?. to_f64 ( ) ?;
350
347
let f2 = this. read_scalar ( f2) ?. to_f64 ( ) ?;
351
348
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 ( ) ;
355
353
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
+ } ) ;
360
358
let res = this. adjust_nan ( res, & [ f1, f2] ) ;
361
359
this. write_scalar ( res, dest) ?;
362
360
}
@@ -366,13 +364,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
366
364
let f = this. read_scalar ( f) ?. to_f32 ( ) ?;
367
365
let i = this. read_scalar ( i) ?. to_i32 ( ) ?;
368
366
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 ( || {
370
368
// Using host floats (but it's fine, this operation does not have guaranteed precision).
371
369
let res = f. to_host ( ) . powi ( i) . to_soft ( ) ;
372
370
373
371
// Apply a relative error of 4ULP to introduce some non-determinism
374
372
// simulating imprecise implementations and optimizations.
375
- apply_random_float_error_ulp ( this, res, 4 )
373
+ math :: apply_random_float_error_ulp ( this, res, 4 )
376
374
} ) ;
377
375
let res = this. adjust_nan ( res, & [ f] ) ;
378
376
this. write_scalar ( res, dest) ?;
@@ -382,13 +380,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
382
380
let f = this. read_scalar ( f) ?. to_f64 ( ) ?;
383
381
let i = this. read_scalar ( i) ?. to_i32 ( ) ?;
384
382
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 ( || {
386
384
// Using host floats (but it's fine, this operation does not have guaranteed precision).
387
385
let res = f. to_host ( ) . powi ( i) . to_soft ( ) ;
388
386
389
387
// Apply a relative error of 4ULP to introduce some non-determinism
390
388
// simulating imprecise implementations and optimizations.
391
- apply_random_float_error_ulp ( this, res, 4 )
389
+ math :: apply_random_float_error_ulp ( this, res, 4 )
392
390
} ) ;
393
391
let res = this. adjust_nan ( res, & [ f] ) ;
394
392
this. write_scalar ( res, dest) ?;
@@ -443,7 +441,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
443
441
}
444
442
// Apply a relative error of 4ULP to simulate non-deterministic precision loss
445
443
// 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 ) ?;
447
445
this. write_immediate ( * res, dest) ?;
448
446
}
449
447
@@ -480,108 +478,3 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
480
478
interp_ok ( EmulateItemResult :: NeedsReturn )
481
479
}
482
480
}
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