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
- use rustc_middle:: ty:: { self , FloatTy , ScalarInt } ;
10
+ use rustc_middle:: ty:: { self , FloatTy } ;
14
11
use rustc_span:: { Symbol , sym} ;
15
12
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
2 , // log2(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
2 , // log2(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,16 +326,17 @@ 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 (
340
- this, res, 2 , // log2(4)
341
- )
342
- } ) ;
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 (
337
+ this, res, 2 , // log2(4)
338
+ )
339
+ } ) ;
343
340
let res = this. adjust_nan ( res, & [ f1, f2] ) ;
344
341
this. write_scalar ( res, dest) ?;
345
342
}
@@ -348,16 +345,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
348
345
let f1 = this. read_scalar ( f1) ?. to_f64 ( ) ?;
349
346
let f2 = this. read_scalar ( f2) ?. to_f64 ( ) ?;
350
347
351
- let res = fixed_float_value ( this, intrinsic_name, & [ f1, f2] ) . unwrap_or_else ( || {
352
- // Using host floats (but it's fine, this operation does not have guaranteed precision).
353
- let res = f1. to_host ( ) . powf ( f2. to_host ( ) ) . to_soft ( ) ;
348
+ let res =
349
+ math:: 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 ( ) ;
354
352
355
- // Apply a relative error of 4ULP to introduce some non-determinism
356
- // simulating imprecise implementations and optimizations.
357
- apply_random_float_error_ulp (
358
- this, res, 2 , // log2(4)
359
- )
360
- } ) ;
353
+ // Apply a relative error of 4ULP to introduce some non-determinism
354
+ // simulating imprecise implementations and optimizations.
355
+ math :: apply_random_float_error_ulp (
356
+ this, res, 2 , // log2(4)
357
+ )
358
+ } ) ;
361
359
let res = this. adjust_nan ( res, & [ f1, f2] ) ;
362
360
this. write_scalar ( res, dest) ?;
363
361
}
@@ -367,13 +365,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
367
365
let f = this. read_scalar ( f) ?. to_f32 ( ) ?;
368
366
let i = this. read_scalar ( i) ?. to_i32 ( ) ?;
369
367
370
- let res = fixed_powi_float_value ( this, f, i) . unwrap_or_else ( || {
368
+ let res = math :: fixed_powi_value ( this, f, i) . unwrap_or_else ( || {
371
369
// Using host floats (but it's fine, this operation does not have guaranteed precision).
372
370
let res = f. to_host ( ) . powi ( i) . to_soft ( ) ;
373
371
374
372
// Apply a relative error of 4ULP to introduce some non-determinism
375
373
// simulating imprecise implementations and optimizations.
376
- apply_random_float_error_ulp (
374
+ math :: apply_random_float_error_ulp (
377
375
this, res, 2 , // log2(4)
378
376
)
379
377
} ) ;
@@ -385,13 +383,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
385
383
let f = this. read_scalar ( f) ?. to_f64 ( ) ?;
386
384
let i = this. read_scalar ( i) ?. to_i32 ( ) ?;
387
385
388
- let res = fixed_powi_float_value ( this, f, i) . unwrap_or_else ( || {
386
+ let res = math :: fixed_powi_value ( this, f, i) . unwrap_or_else ( || {
389
387
// Using host floats (but it's fine, this operation does not have guaranteed precision).
390
388
let res = f. to_host ( ) . powi ( i) . to_soft ( ) ;
391
389
392
390
// Apply a relative error of 4ULP to introduce some non-determinism
393
391
// simulating imprecise implementations and optimizations.
394
- apply_random_float_error_ulp (
392
+ math :: apply_random_float_error_ulp (
395
393
this, res, 2 , // log2(4)
396
394
)
397
395
} ) ;
@@ -448,7 +446,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
448
446
}
449
447
// Apply a relative error of 4ULP to simulate non-deterministic precision loss
450
448
// due to optimizations.
451
- let res = apply_random_float_error_to_imm ( this, res, 2 /* log2(4) */ ) ?;
449
+ let res = math :: apply_random_float_error_to_imm ( this, res, 2 /* log2(4) */ ) ?;
452
450
this. write_immediate ( * res, dest) ?;
453
451
}
454
452
@@ -485,133 +483,3 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
485
483
interp_ok ( EmulateItemResult :: NeedsReturn )
486
484
}
487
485
}
488
-
489
- /// Applies a random ULP floating point error to `val` and returns the new value.
490
- /// So if you want an X ULP error, `ulp_exponent` should be log2(X).
491
- ///
492
- /// Will fail if `val` is not a floating point number.
493
- fn apply_random_float_error_to_imm < ' tcx > (
494
- ecx : & mut MiriInterpCx < ' tcx > ,
495
- val : ImmTy < ' tcx > ,
496
- ulp_exponent : u32 ,
497
- ) -> InterpResult < ' tcx , ImmTy < ' tcx > > {
498
- let scalar = val. to_scalar_int ( ) ?;
499
- let res: ScalarInt = match val. layout . ty . kind ( ) {
500
- ty:: Float ( FloatTy :: F16 ) =>
501
- apply_random_float_error_ulp ( ecx, scalar. to_f16 ( ) , ulp_exponent) . into ( ) ,
502
- ty:: Float ( FloatTy :: F32 ) =>
503
- apply_random_float_error_ulp ( ecx, scalar. to_f32 ( ) , ulp_exponent) . into ( ) ,
504
- ty:: Float ( FloatTy :: F64 ) =>
505
- apply_random_float_error_ulp ( ecx, scalar. to_f64 ( ) , ulp_exponent) . into ( ) ,
506
- ty:: Float ( FloatTy :: F128 ) =>
507
- apply_random_float_error_ulp ( ecx, scalar. to_f128 ( ) , ulp_exponent) . into ( ) ,
508
- _ => bug ! ( "intrinsic called with non-float input type" ) ,
509
- } ;
510
-
511
- interp_ok ( ImmTy :: from_scalar_int ( res, val. layout ) )
512
- }
513
-
514
- /// For the intrinsics:
515
- /// - sinf32, sinf64
516
- /// - cosf32, cosf64
517
- /// - expf32, expf64, exp2f32, exp2f64
518
- /// - logf32, logf64, log2f32, log2f64, log10f32, log10f64
519
- /// - powf32, powf64
520
- ///
521
- /// # Return
522
- ///
523
- /// Returns `Some(output)` if the `intrinsic` results in a defined fixed `output` specified in the C standard
524
- /// (specifically, C23 annex F.10) when given `args` as arguments. Outputs that are unaffected by a relative error
525
- /// (such as INF and zero) are not handled here, they are assumed to be handled by the underlying
526
- /// implementation. Returns `None` if no specific value is guaranteed.
527
- ///
528
- /// # Note
529
- ///
530
- /// For `powf*` operations of the form:
531
- ///
532
- /// - `(SNaN)^(±0)`
533
- /// - `1^(SNaN)`
534
- ///
535
- /// The result is implementation-defined:
536
- /// - musl returns for both `1.0`
537
- /// - glibc returns for both `NaN`
538
- ///
539
- /// This discrepancy exists because SNaN handling is not consistently defined across platforms,
540
- /// and the C standard leaves behavior for SNaNs unspecified.
541
- ///
542
- /// Miri chooses to adhere to both implementations and returns either one of them non-deterministically.
543
- fn fixed_float_value < S : Semantics > (
544
- ecx : & mut MiriInterpCx < ' _ > ,
545
- intrinsic_name : & str ,
546
- args : & [ IeeeFloat < S > ] ,
547
- ) -> Option < IeeeFloat < S > > {
548
- let one = IeeeFloat :: < S > :: one ( ) ;
549
- Some ( match ( intrinsic_name, args) {
550
- // cos(+- 0) = 1
551
- ( "cosf32" | "cosf64" , [ input] ) if input. is_zero ( ) => one,
552
-
553
- // e^0 = 1
554
- ( "expf32" | "expf64" | "exp2f32" | "exp2f64" , [ input] ) if input. is_zero ( ) => one,
555
-
556
- // (-1)^(±INF) = 1
557
- ( "powf32" | "powf64" , [ base, exp] ) if * base == -one && exp. is_infinite ( ) => one,
558
-
559
- // 1^y = 1 for any y, even a NaN
560
- ( "powf32" | "powf64" , [ base, exp] ) if * base == one => {
561
- let rng = ecx. machine . rng . get_mut ( ) ;
562
- // SNaN exponents get special treatment: they might return 1, or a NaN.
563
- let return_nan = exp. is_signaling ( ) && ecx. machine . float_nondet && rng. random ( ) ;
564
- // Handle both the musl and glibc cases non-deterministically.
565
- if return_nan { ecx. generate_nan ( args) } else { one }
566
- }
567
-
568
- // x^(±0) = 1 for any x, even a NaN
569
- ( "powf32" | "powf64" , [ base, exp] ) if exp. is_zero ( ) => {
570
- let rng = ecx. machine . rng . get_mut ( ) ;
571
- // SNaN bases get special treatment: they might return 1, or a NaN.
572
- let return_nan = base. is_signaling ( ) && ecx. machine . float_nondet && rng. random ( ) ;
573
- // Handle both the musl and glibc cases non-deterministically.
574
- if return_nan { ecx. generate_nan ( args) } else { one }
575
- }
576
-
577
- // There are a lot of cases for fixed outputs according to the C Standard, but these are
578
- // mainly INF or zero which are not affected by the applied error.
579
- _ => return None ,
580
- } )
581
- }
582
-
583
- /// Returns `Some(output)` if `powi` (called `pown` in C) results in a fixed value specified in the
584
- /// C standard (specifically, C23 annex F.10.4.6) when doing `base^exp`. Otherwise, returns `None`.
585
- fn fixed_powi_float_value < S : Semantics > (
586
- ecx : & mut MiriInterpCx < ' _ > ,
587
- base : IeeeFloat < S > ,
588
- exp : i32 ,
589
- ) -> Option < IeeeFloat < S > > {
590
- Some ( match exp {
591
- 0 => {
592
- let one = IeeeFloat :: < S > :: one ( ) ;
593
- let rng = ecx. machine . rng . get_mut ( ) ;
594
- let return_nan = ecx. machine . float_nondet && rng. random ( ) && base. is_signaling ( ) ;
595
- // For SNaN treatment, we are consistent with `powf`above.
596
- // (We wouldn't have two, unlike powf all implementations seem to agree for powi,
597
- // but for now we are maximally conservative.)
598
- if return_nan { ecx. generate_nan ( & [ base] ) } else { one }
599
- }
600
-
601
- _ => return None ,
602
- } )
603
- }
604
-
605
- /// Given an floating-point operation and a floating-point value, clamps the result to the output
606
- /// range of the given operation.
607
- fn clamp_float_value < S : Semantics > ( intrinsic_name : & str , val : IeeeFloat < S > ) -> IeeeFloat < S > {
608
- match intrinsic_name {
609
- // sin and cos: [-1, 1]
610
- "sinf32" | "cosf32" | "sinf64" | "cosf64" =>
611
- val. clamp ( IeeeFloat :: < S > :: one ( ) . neg ( ) , IeeeFloat :: < S > :: one ( ) ) ,
612
- // exp: [0, +INF]
613
- "expf32" | "exp2f32" | "expf64" | "exp2f64" =>
614
- IeeeFloat :: < S > :: maximum ( val, IeeeFloat :: < S > :: ZERO ) ,
615
- _ => val,
616
- }
617
- }
0 commit comments