3
3
mod atomic;
4
4
mod simd;
5
5
6
- use std:: ops:: Neg ;
7
-
8
6
use rand:: Rng ;
9
7
use rustc_abi:: Size ;
10
- use rustc_apfloat:: ieee:: { IeeeFloat , Semantics } ;
11
8
use rustc_apfloat:: { self , Float , Round } ;
12
9
use rustc_middle:: mir;
13
10
use rustc_middle:: ty:: { self , FloatTy } ;
@@ -16,7 +13,7 @@ use rustc_span::{Symbol, sym};
16
13
use self :: atomic:: EvalContextExt as _;
17
14
use self :: helpers:: { ToHost , ToSoft } ;
18
15
use self :: simd:: EvalContextExt as _;
19
- use crate :: math:: { IeeeExt , apply_random_float_error_ulp} ;
16
+ use crate :: math:: apply_random_float_error_ulp;
20
17
use crate :: * ;
21
18
22
19
/// Check that the number of args is what we expect.
@@ -209,7 +206,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
209
206
let [ f] = check_intrinsic_arg_count ( args) ?;
210
207
let f = this. read_scalar ( f) ?. to_f32 ( ) ?;
211
208
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 ( || {
213
210
// Using host floats (but it's fine, these operations do not have
214
211
// guaranteed precision).
215
212
let host = f. to_host ( ) ;
@@ -227,15 +224,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
227
224
228
225
// Apply a relative error of 4ULP to introduce some non-determinism
229
226
// simulating imprecise implementations and optimizations.
230
- let res = apply_random_float_error_ulp (
227
+ let res = math :: apply_random_float_error_ulp (
231
228
this,
232
229
res,
233
230
4 ,
234
231
) ;
235
232
236
233
// Clamp the result to the guaranteed range of this function according to the C standard,
237
234
// if any.
238
- clamp_float_value ( intrinsic_name, res)
235
+ math :: clamp_float_value ( intrinsic_name, res)
239
236
} ) ;
240
237
let res = this. adjust_nan ( res, & [ f] ) ;
241
238
this. write_scalar ( res, dest) ?;
@@ -253,7 +250,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
253
250
let [ f] = check_intrinsic_arg_count ( args) ?;
254
251
let f = this. read_scalar ( f) ?. to_f64 ( ) ?;
255
252
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 ( || {
257
254
// Using host floats (but it's fine, these operations do not have
258
255
// guaranteed precision).
259
256
let host = f. to_host ( ) ;
@@ -271,15 +268,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
271
268
272
269
// Apply a relative error of 4ULP to introduce some non-determinism
273
270
// simulating imprecise implementations and optimizations.
274
- let res = apply_random_float_error_ulp (
271
+ let res = math :: apply_random_float_error_ulp (
275
272
this,
276
273
res,
277
274
4 ,
278
275
) ;
279
276
280
277
// Clamp the result to the guaranteed range of this function according to the C standard,
281
278
// if any.
282
- clamp_float_value ( intrinsic_name, res)
279
+ math :: clamp_float_value ( intrinsic_name, res)
283
280
} ) ;
284
281
let res = this. adjust_nan ( res, & [ f] ) ;
285
282
this. write_scalar ( res, dest) ?;
@@ -330,14 +327,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
330
327
let f1 = this. read_scalar ( f1) ?. to_f32 ( ) ?;
331
328
let f2 = this. read_scalar ( f2) ?. to_f32 ( ) ?;
332
329
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 ( ) ;
336
334
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
+ } ) ;
341
339
let res = this. adjust_nan ( res, & [ f1, f2] ) ;
342
340
this. write_scalar ( res, dest) ?;
343
341
}
@@ -346,14 +344,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
346
344
let f1 = this. read_scalar ( f1) ?. to_f64 ( ) ?;
347
345
let f2 = this. read_scalar ( f2) ?. to_f64 ( ) ?;
348
346
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 ( ) ;
352
351
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
+ } ) ;
357
356
let res = this. adjust_nan ( res, & [ f1, f2] ) ;
358
357
this. write_scalar ( res, dest) ?;
359
358
}
@@ -363,7 +362,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
363
362
let f = this. read_scalar ( f) ?. to_f32 ( ) ?;
364
363
let i = this. read_scalar ( i) ?. to_i32 ( ) ?;
365
364
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 ( || {
367
366
// Using host floats (but it's fine, this operation does not have guaranteed precision).
368
367
let res = f. to_host ( ) . powi ( i) . to_soft ( ) ;
369
368
@@ -379,13 +378,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
379
378
let f = this. read_scalar ( f) ?. to_f64 ( ) ?;
380
379
let i = this. read_scalar ( i) ?. to_i32 ( ) ?;
381
380
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 ( || {
383
382
// Using host floats (but it's fine, this operation does not have guaranteed precision).
384
383
let res = f. to_host ( ) . powi ( i) . to_soft ( ) ;
385
384
386
385
// Apply a relative error of 4ULP to introduce some non-determinism
387
386
// simulating imprecise implementations and optimizations.
388
- apply_random_float_error_ulp ( this, res, 4 )
387
+ math :: apply_random_float_error_ulp ( this, res, 4 )
389
388
} ) ;
390
389
let res = this. adjust_nan ( res, & [ f] ) ;
391
390
this. write_scalar ( res, dest) ?;
@@ -440,7 +439,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
440
439
}
441
440
// Apply a relative error of 4ULP to simulate non-deterministic precision loss
442
441
// 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 ) ?;
444
443
this. write_immediate ( * res, dest) ?;
445
444
}
446
445
@@ -477,108 +476,3 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
477
476
interp_ok ( EmulateItemResult :: NeedsReturn )
478
477
}
479
478
}
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