@@ -8,7 +8,7 @@ use std::ops::Neg;
88use rand:: Rng ;
99use rustc_abi:: Size ;
1010use rustc_apfloat:: ieee:: { Double , IeeeFloat , Semantics , Single } ;
11- use rustc_apfloat:: { self , Category , Float , Round } ;
11+ use rustc_apfloat:: { self , Float , Round } ;
1212use rustc_middle:: mir;
1313use rustc_middle:: ty:: { self , FloatTy , ScalarInt } ;
1414use rustc_span:: { Symbol , sym} ;
@@ -19,6 +19,63 @@ use self::simd::EvalContextExt as _;
1919use crate :: math:: { IeeeExt , apply_random_float_error_ulp} ;
2020use crate :: * ;
2121
22+ macro_rules! pow_impl {
23+ ( $this: expr, powf( $f1: expr, $f2: expr) ) => { {
24+ let host1 = $f1. to_host( ) ;
25+ let host2 = $f2. to_host( ) ;
26+
27+ let fixed_res = match ( host1, host2) {
28+ // 1^y = 1 for any y even a NaN.
29+ ( one @ 1.0 , _) => Some ( one) ,
30+
31+ // (-1)^(±INF) = 1
32+ ( -1.0 , exp) if exp. is_infinite( ) => Some ( 1.0 ) ,
33+
34+ // x^(±0) = 1 for any x, even a NaN
35+ ( _, 0.0 ) => Some ( 1.0 ) ,
36+
37+ _ => None ,
38+ } ;
39+ fixed_res. map_or_else( || {
40+ // Using host floats (but it's fine, this operation does not have guaranteed precision).
41+ let res = host1. powf( host2) . to_soft( ) ;
42+ // Apply a relative error of 4ULP to introduce some non-determinism
43+ // simulating imprecise implementations and optimizations.
44+ apply_random_float_error_ulp(
45+ $this, res, 2 , // log2(4)
46+ )
47+ } , ToSoft :: to_soft)
48+ } } ;
49+ ( $this: expr, powi( $f: expr, $i: expr) ) => { {
50+ let host = $f. to_host( ) ;
51+ let exp = $i;
52+
53+ let fixed_res = match ( host, exp) {
54+ // ±0^x = ±0 with x an odd integer.
55+ ( zero @ 0.0 , x) if x % 2 != 0 => Some ( zero) , // preserve sign of zero.
56+
57+ // ±0^x = +0 with x an even integer.
58+ ( 0.0 , x) if x % 2 == 0 => Some ( 0.0 ) ,
59+
60+ // x^0 = 1:
61+ // Standard specifies we can only fix to 1 if x is is not a Signaling NaN.
62+ // TODO: How to do this in normal rust?
63+ ( _, 0 ) /* if !f.is_signaling() */ => Some ( 1.0 ) ,
64+
65+ _ => None ,
66+ } ;
67+ fixed_res. map_or_else( || {
68+ // Using host floats (but it's fine, this operation does not have guaranteed precision).
69+ let res = host. powi( exp) . to_soft( ) ;
70+ // Apply a relative error of 4ULP to introduce some non-determinism
71+ // simulating imprecise implementations and optimizations.
72+ apply_random_float_error_ulp(
73+ $this, res, 2 , // log2(4)
74+ )
75+ } , ToSoft :: to_soft)
76+ } } ;
77+ }
78+
2279impl < ' tcx > EvalContextExt < ' tcx > for crate :: MiriInterpCx < ' tcx > { }
2380pub trait EvalContextExt < ' tcx > : crate :: MiriInterpCxExt < ' tcx > {
2481 fn call_intrinsic (
@@ -385,28 +442,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
385442 let f1 = this. read_scalar ( f1) ?. to_f32 ( ) ?;
386443 let f2 = this. read_scalar ( f2) ?. to_f32 ( ) ?;
387444
388- let fixed_res = match ( f1. category ( ) , f2. category ( ) ) {
389- // 1^y = 1 for any y, even a NaN.
390- ( Category :: Normal , _) if f1 == 1.0f32 . to_soft ( ) => Some ( 1.0f32 . to_soft ( ) ) ,
391-
392- // (-1)^(±INF) = 1
393- ( Category :: Normal , Category :: Infinity ) if f1 == ( -1.0f32 ) . to_soft ( ) =>
394- Some ( 1.0f32 . to_soft ( ) ) ,
395-
396- // x^(±0) = 1 for any x, even a NaN
397- ( _, Category :: Zero ) => Some ( 1.0f32 . to_soft ( ) ) ,
398-
399- _ => None ,
400- } ;
401- let res = fixed_res. unwrap_or_else ( || {
402- // Using host floats (but it's fine, this operation does not have guaranteed precision).
403- let res = f1. to_host ( ) . powf ( f2. to_host ( ) ) . to_soft ( ) ;
404- // Apply a relative error of 4ULP to introduce some non-determinism
405- // simulating imprecise implementations and optimizations.
406- apply_random_float_error_ulp (
407- this, res, 2 , // log2(4)
408- )
409- } ) ;
445+ let res = pow_impl ! ( this, powf( f1, f2) ) ;
410446 let res = this. adjust_nan ( res, & [ f1, f2] ) ;
411447 this. write_scalar ( res, dest) ?;
412448 }
@@ -415,30 +451,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
415451 let f1 = this. read_scalar ( f1) ?. to_f64 ( ) ?;
416452 let f2 = this. read_scalar ( f2) ?. to_f64 ( ) ?;
417453
418- let fixed_res = match ( f1. category ( ) , f2. category ( ) ) {
419- // 1^y = 1 for any y even a NaN.
420- ( Category :: Normal , _) if f1 == 1.0f64 . to_soft ( ) => Some ( 1.0f64 . to_soft ( ) ) ,
421-
422- // (-1)^(±INF) = 1
423- ( Category :: Normal , Category :: Infinity ) if f1 == ( -1.0f64 ) . to_soft ( ) =>
424- Some ( 1.0f64 . to_soft ( ) ) ,
425-
426- // x^(±0) = 1 for any x, even a NaN
427- ( _, Category :: Zero ) => Some ( 1.0f64 . to_soft ( ) ) ,
428-
429- // TODO: pow has a lot of "edge" cases which mostly result in ±0 or ±INF
430- // do we have to catch them all?
431- _ => None ,
432- } ;
433- let res = fixed_res. unwrap_or_else ( || {
434- // Using host floats (but it's fine, this operation does not have guaranteed precision).
435- let res = f1. to_host ( ) . powf ( f2. to_host ( ) ) . to_soft ( ) ;
436- // Apply a relative error of 4ULP to introduce some non-determinism
437- // simulating imprecise implementations and optimizations.
438- apply_random_float_error_ulp (
439- this, res, 2 , // log2(4)
440- )
441- } ) ;
454+ let res = pow_impl ! ( this, powf( f1, f2) ) ;
442455 let res = this. adjust_nan ( res, & [ f1, f2] ) ;
443456 this. write_scalar ( res, dest) ?;
444457 }
@@ -448,27 +461,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
448461 let f = this. read_scalar ( f) ?. to_f32 ( ) ?;
449462 let i = this. read_scalar ( i) ?. to_i32 ( ) ?;
450463
451- let fixed_res = match ( f. category ( ) , i) {
452- // Standard specifies we can only fix to 1 if input is is not a Signaling NaN.
453- ( _, 0 ) if !f. is_signaling ( ) => Some ( 1.0f32 . to_soft ( ) ) ,
454-
455- // TODO: Isn't this done by the implementation? And ULP error on 0.0 doesn't have an effect
456- ( Category :: Zero , x) if x % 2 == 0 => Some ( 0.0f32 . to_soft ( ) ) ,
457-
458- // ±0^x = ±0 with x an odd integer.
459- ( Category :: Zero , x) if x % 2 != 0 => Some ( f) , // preserve sign of zero.
460- _ => None ,
461- } ;
462- let res = fixed_res. unwrap_or_else ( || {
463- // Using host floats (but it's fine, this operation does not have guaranteed precision).
464- let res = f. to_host ( ) . powi ( i) . to_soft ( ) ;
465-
466- // Apply a relative error of 4ULP to introduce some non-determinism
467- // simulating imprecise implementations and optimizations.
468- apply_random_float_error_ulp (
469- this, res, 2 , // log2(4)
470- )
471- } ) ;
464+ let res = pow_impl ! ( this, powi( f, i) ) ;
472465 let res = this. adjust_nan ( res, & [ f] ) ;
473466 this. write_scalar ( res, dest) ?;
474467 }
@@ -477,28 +470,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
477470 let f = this. read_scalar ( f) ?. to_f64 ( ) ?;
478471 let i = this. read_scalar ( i) ?. to_i32 ( ) ?;
479472
480- let fixed_res = match ( f. category ( ) , i) {
481- // Standard specifies we can only fix to 1 if input is is not a Signaling NaN.
482- ( _, 0 ) if !f. is_signaling ( ) => Some ( 1.0f64 . to_soft ( ) ) ,
483-
484- // ±0^x = 0 with x an even integer.
485- // TODO: Isn't this done by the implementation itself?
486- ( Category :: Zero , x) if x % 2 == 0 => Some ( 0.0f64 . to_soft ( ) ) ,
487-
488- // ±0^x = ±0 with x an odd integer.
489- ( Category :: Zero , x) if x % 2 != 0 => Some ( f) , // preserve sign of zero.
490- _ => None ,
491- } ;
492- let res = fixed_res. unwrap_or_else ( || {
493- // Using host floats (but it's fine, this operation does not have guaranteed precision).
494- let res = f. to_host ( ) . powi ( i) . to_soft ( ) ;
495-
496- // Apply a relative error of 4ULP to introduce some non-determinism
497- // simulating imprecise implementations and optimizations.
498- apply_random_float_error_ulp (
499- this, res, 2 , // log2(4)
500- )
501- } ) ;
473+ let res = pow_impl ! ( this, powi( f, i) ) ;
502474 let res = this. adjust_nan ( res, & [ f] ) ;
503475 this. write_scalar ( res, dest) ?;
504476 }
0 commit comments