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,6 @@ 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} ;
20
16
use crate :: * ;
21
17
22
18
/// Check that the number of args is what we expect.
@@ -209,7 +205,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
209
205
let [ f] = check_intrinsic_arg_count ( args) ?;
210
206
let f = this. read_scalar ( f) ?. to_f32 ( ) ?;
211
207
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 ( || {
213
209
// Using host floats (but it's fine, these operations do not have
214
210
// guaranteed precision).
215
211
let host = f. to_host ( ) ;
@@ -227,15 +223,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
227
223
228
224
// Apply a relative error of 4ULP to introduce some non-determinism
229
225
// simulating imprecise implementations and optimizations.
230
- let res = apply_random_float_error_ulp (
226
+ let res = math :: apply_random_float_error_ulp (
231
227
this,
232
228
res,
233
229
4 ,
234
230
) ;
235
231
236
232
// Clamp the result to the guaranteed range of this function according to the C standard,
237
233
// if any.
238
- clamp_float_value ( intrinsic_name, res)
234
+ math :: clamp_float_value ( intrinsic_name, res)
239
235
} ) ;
240
236
let res = this. adjust_nan ( res, & [ f] ) ;
241
237
this. write_scalar ( res, dest) ?;
@@ -253,7 +249,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
253
249
let [ f] = check_intrinsic_arg_count ( args) ?;
254
250
let f = this. read_scalar ( f) ?. to_f64 ( ) ?;
255
251
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 ( || {
257
253
// Using host floats (but it's fine, these operations do not have
258
254
// guaranteed precision).
259
255
let host = f. to_host ( ) ;
@@ -271,15 +267,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
271
267
272
268
// Apply a relative error of 4ULP to introduce some non-determinism
273
269
// simulating imprecise implementations and optimizations.
274
- let res = apply_random_float_error_ulp (
270
+ let res = math :: apply_random_float_error_ulp (
275
271
this,
276
272
res,
277
273
4 ,
278
274
) ;
279
275
280
276
// Clamp the result to the guaranteed range of this function according to the C standard,
281
277
// if any.
282
- clamp_float_value ( intrinsic_name, res)
278
+ math :: clamp_float_value ( intrinsic_name, res)
283
279
} ) ;
284
280
let res = this. adjust_nan ( res, & [ f] ) ;
285
281
this. write_scalar ( res, dest) ?;
@@ -330,14 +326,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
330
326
let f1 = this. read_scalar ( f1) ?. to_f32 ( ) ?;
331
327
let f2 = this. read_scalar ( f2) ?. to_f32 ( ) ?;
332
328
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 ( ) ;
336
333
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
+ } ) ;
341
338
let res = this. adjust_nan ( res, & [ f1, f2] ) ;
342
339
this. write_scalar ( res, dest) ?;
343
340
}
@@ -346,14 +343,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
346
343
let f1 = this. read_scalar ( f1) ?. to_f64 ( ) ?;
347
344
let f2 = this. read_scalar ( f2) ?. to_f64 ( ) ?;
348
345
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 ( ) ;
352
350
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
+ } ) ;
357
355
let res = this. adjust_nan ( res, & [ f1, f2] ) ;
358
356
this. write_scalar ( res, dest) ?;
359
357
}
@@ -363,13 +361,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
363
361
let f = this. read_scalar ( f) ?. to_f32 ( ) ?;
364
362
let i = this. read_scalar ( i) ?. to_i32 ( ) ?;
365
363
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 ( || {
367
365
// Using host floats (but it's fine, this operation does not have guaranteed precision).
368
366
let res = f. to_host ( ) . powi ( i) . to_soft ( ) ;
369
367
370
368
// Apply a relative error of 4ULP to introduce some non-determinism
371
369
// simulating imprecise implementations and optimizations.
372
- apply_random_float_error_ulp ( this, res, 4 )
370
+ math :: apply_random_float_error_ulp ( this, res, 4 )
373
371
} ) ;
374
372
let res = this. adjust_nan ( res, & [ f] ) ;
375
373
this. write_scalar ( res, dest) ?;
@@ -379,13 +377,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
379
377
let f = this. read_scalar ( f) ?. to_f64 ( ) ?;
380
378
let i = this. read_scalar ( i) ?. to_i32 ( ) ?;
381
379
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 ( || {
383
381
// Using host floats (but it's fine, this operation does not have guaranteed precision).
384
382
let res = f. to_host ( ) . powi ( i) . to_soft ( ) ;
385
383
386
384
// Apply a relative error of 4ULP to introduce some non-determinism
387
385
// simulating imprecise implementations and optimizations.
388
- apply_random_float_error_ulp ( this, res, 4 )
386
+ math :: apply_random_float_error_ulp ( this, res, 4 )
389
387
} ) ;
390
388
let res = this. adjust_nan ( res, & [ f] ) ;
391
389
this. write_scalar ( res, dest) ?;
@@ -440,7 +438,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
440
438
}
441
439
// Apply a relative error of 4ULP to simulate non-deterministic precision loss
442
440
// 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 ) ?;
444
442
this. write_immediate ( * res, dest) ?;
445
443
}
446
444
@@ -477,108 +475,3 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
477
475
interp_ok ( EmulateItemResult :: NeedsReturn )
478
476
}
479
477
}
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