@@ -3,6 +3,7 @@ use rustc_middle::mir;
33use rustc_middle:: ty:: { self , FloatTy } ;
44
55use self :: helpers:: { ToHost , ToSoft } ;
6+ use self :: math:: { HostFloatOperation , IeeeExt } ;
67use super :: check_intrinsic_arg_count;
78use crate :: * ;
89
@@ -20,6 +21,42 @@ fn sqrt<'tcx, F: Float + FloatConvert<F> + Into<Scalar>>(
2021 this. write_scalar ( res, dest)
2122}
2223
24+ #[ derive( Debug , Clone , Copy ) ]
25+ enum HostFloatUnaryOp {
26+ Sin ,
27+ Cos ,
28+ Exp ,
29+ Exp2 ,
30+ Log ,
31+ Log10 ,
32+ Log2 ,
33+ }
34+
35+ fn is_host_unary_float_op ( intrinsic_name : & str ) -> Option < ( FloatTy , HostFloatUnaryOp ) > {
36+ let ( op, ty) = intrinsic_name. rsplit_once ( 'f' ) ?;
37+
38+ let float_ty = match ty {
39+ "16" => FloatTy :: F16 ,
40+ "32" => FloatTy :: F32 ,
41+ "64" => FloatTy :: F64 ,
42+ "128" => FloatTy :: F128 ,
43+ _ => return None ,
44+ } ;
45+
46+ let host_float_op = match op {
47+ "sin" => HostFloatUnaryOp :: Sin ,
48+ "cos" => HostFloatUnaryOp :: Cos ,
49+ "exp" => HostFloatUnaryOp :: Exp ,
50+ "exp2" => HostFloatUnaryOp :: Exp2 ,
51+ "log" => HostFloatUnaryOp :: Log ,
52+ "log10" => HostFloatUnaryOp :: Log10 ,
53+ "log2" => HostFloatUnaryOp :: Log2 ,
54+ _ => return None ,
55+ } ;
56+
57+ Some ( ( float_ty, host_float_op) )
58+ }
59+
2360impl < ' tcx > EvalContextExt < ' tcx > for crate :: MiriInterpCx < ' tcx > { }
2461pub trait EvalContextExt < ' tcx > : crate :: MiriInterpCxExt < ' tcx > {
2562 fn emulate_math_intrinsic (
@@ -108,94 +145,84 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
108145 }
109146
110147 // Operations that need host floats.
111- #[ rustfmt:: skip]
112- | "sinf32"
113- | "cosf32"
114- | "expf32"
115- | "exp2f32"
116- | "logf32"
117- | "log10f32"
118- | "log2f32"
119- => {
120- let [ f] = check_intrinsic_arg_count ( args) ?;
121- let f = this. read_scalar ( f) ?. to_f32 ( ) ?;
148+ _ if let Some ( ( float_ty, host_float_op) ) = is_host_unary_float_op ( intrinsic_name) => {
149+ use rustc_apfloat:: ieee:: { IeeeFloat , Semantics } ;
122150
123- let res = math:: fixed_float_value ( this, intrinsic_name, & [ f] ) . unwrap_or_else ( || {
124- // Using host floats (but it's fine, these operations do not have
125- // guaranteed precision).
126- let host = f. to_host ( ) ;
127- let res = match intrinsic_name {
128- "sinf32" => host. sin ( ) ,
129- "cosf32" => host. cos ( ) ,
130- "expf32" => host. exp ( ) ,
131- "exp2f32" => host. exp2 ( ) ,
132- "logf32" => host. ln ( ) ,
133- "log10f32" => host. log10 ( ) ,
134- "log2f32" => host. log2 ( ) ,
135- _ => bug ! ( ) ,
136- } ;
137- let res = res. to_soft ( ) ;
151+ fn eval_host_float_operation < ' tcx , S : Semantics > (
152+ this : & mut MiriInterpCx < ' tcx > ,
153+ f : IeeeFloat < S > ,
154+ intrinsic_name : & str ,
155+ host_float_op : HostFloatUnaryOp ,
156+ dest : & MPlaceTy < ' tcx > ,
157+ ) -> InterpResult < ' tcx , ( ) >
158+ where
159+ IeeeFloat < S > : HostFloatOperation + IeeeExt + Float ,
160+ IeeeFloat < S > : Into < rustc_const_eval:: interpret:: Scalar < machine:: Provenance > > ,
161+ {
162+ use HostFloatOperation ;
138163
139- // Apply a relative error of 4ULP to introduce some non-determinism
140- // simulating imprecise implementations and optimizations.
141- let res = math:: apply_random_float_error_ulp (
142- this,
143- res,
144- 4 ,
145- ) ;
146-
147- // Clamp the result to the guaranteed range of this function according to the C standard,
148- // if any.
149- math:: clamp_float_value ( intrinsic_name, res)
150- } ) ;
151- let res = this. adjust_nan ( res, & [ f] ) ;
152- this. write_scalar ( res, dest) ?;
153- }
164+ let res =
165+ math:: fixed_float_value ( this, intrinsic_name, & [ f] ) . unwrap_or_else ( || {
166+ // Using host floats (but it's fine, these operations do not have
167+ // guaranteed precision).
168+ let res = match host_float_op {
169+ HostFloatUnaryOp :: Sin => HostFloatOperation :: host_sin ( f) ,
170+ HostFloatUnaryOp :: Cos => HostFloatOperation :: host_cos ( f) ,
171+ HostFloatUnaryOp :: Exp => HostFloatOperation :: host_exp ( f) ,
172+ HostFloatUnaryOp :: Exp2 => HostFloatOperation :: host_exp2 ( f) ,
173+ HostFloatUnaryOp :: Log => HostFloatOperation :: host_log ( f) ,
174+ HostFloatUnaryOp :: Log10 => HostFloatOperation :: host_log10 ( f) ,
175+ HostFloatUnaryOp :: Log2 => HostFloatOperation :: host_log2 ( f) ,
176+ } ;
177+
178+ // Apply a relative error of 4ULP to introduce some non-determinism
179+ // simulating imprecise implementations and optimizations.
180+ let res = math:: apply_random_float_error_ulp ( this, res, 4 ) ;
181+
182+ // Clamp the result to the guaranteed range of this function according to the C standard,
183+ // if any.
184+ math:: clamp_float_value ( intrinsic_name, res)
185+ } ) ;
186+
187+ let res = this. adjust_nan ( res, & [ f] ) ;
188+ this. write_scalar ( res, dest)
189+ }
154190
155- #[ rustfmt:: skip]
156- | "sinf64"
157- | "cosf64"
158- | "expf64"
159- | "exp2f64"
160- | "logf64"
161- | "log10f64"
162- | "log2f64"
163- => {
164191 let [ f] = check_intrinsic_arg_count ( args) ?;
165- let f = this. read_scalar ( f) ?. to_f64 ( ) ?;
192+ match float_ty {
193+ FloatTy :: F16 => {
194+ let f = this. read_scalar ( f) ?. to_f16 ( ) ?;
195+ eval_host_float_operation ( this, f, intrinsic_name, host_float_op, dest) ?;
196+ }
197+ FloatTy :: F32 => {
198+ let f = this. read_scalar ( f) ?. to_f32 ( ) ?;
199+ eval_host_float_operation ( this, f, intrinsic_name, host_float_op, dest) ?;
200+ }
201+ FloatTy :: F64 => {
202+ let f = this. read_scalar ( f) ?. to_f64 ( ) ?;
203+ eval_host_float_operation ( this, f, intrinsic_name, host_float_op, dest) ?;
204+ }
205+ FloatTy :: F128 => todo ! ( "f128" ) , // FIXME(f128)
206+ } ;
207+ }
166208
167- let res = math:: fixed_float_value ( this, intrinsic_name, & [ f] ) . unwrap_or_else ( || {
168- // Using host floats (but it's fine, these operations do not have
169- // guaranteed precision).
170- let host = f. to_host ( ) ;
171- let res = match intrinsic_name {
172- "sinf64" => host. sin ( ) ,
173- "cosf64" => host. cos ( ) ,
174- "expf64" => host. exp ( ) ,
175- "exp2f64" => host. exp2 ( ) ,
176- "logf64" => host. ln ( ) ,
177- "log10f64" => host. log10 ( ) ,
178- "log2f64" => host. log2 ( ) ,
179- _ => bug ! ( ) ,
180- } ;
181- let res = res. to_soft ( ) ;
209+ "powf16" => {
210+ let [ f1, f2] = check_intrinsic_arg_count ( args) ?;
211+ let f1 = this. read_scalar ( f1) ?. to_f16 ( ) ?;
212+ let f2 = this. read_scalar ( f2) ?. to_f16 ( ) ?;
182213
183- // Apply a relative error of 4ULP to introduce some non-determinism
184- // simulating imprecise implementations and optimizations.
185- let res = math:: apply_random_float_error_ulp (
186- this,
187- res,
188- 4 ,
189- ) ;
190-
191- // Clamp the result to the guaranteed range of this function according to the C standard,
192- // if any.
193- math:: clamp_float_value ( intrinsic_name, res)
194- } ) ;
195- let res = this. adjust_nan ( res, & [ f] ) ;
214+ let res =
215+ math:: fixed_float_value ( this, intrinsic_name, & [ f1, f2] ) . unwrap_or_else ( || {
216+ // Using host floats (but it's fine, this operation does not have guaranteed precision).
217+ let res = f1. to_host ( ) . powf ( f2. to_host ( ) ) . to_soft ( ) ;
218+
219+ // Apply a relative error of 4ULP to introduce some non-determinism
220+ // simulating imprecise implementations and optimizations.
221+ math:: apply_random_float_error_ulp ( this, res, 4 )
222+ } ) ;
223+ let res = this. adjust_nan ( res, & [ f1, f2] ) ;
196224 this. write_scalar ( res, dest) ?;
197225 }
198-
199226 "powf32" => {
200227 let [ f1, f2] = check_intrinsic_arg_count ( args) ?;
201228 let f1 = this. read_scalar ( f1) ?. to_f32 ( ) ?;
@@ -231,6 +258,22 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
231258 this. write_scalar ( res, dest) ?;
232259 }
233260
261+ "powif16" => {
262+ let [ f, i] = check_intrinsic_arg_count ( args) ?;
263+ let f = this. read_scalar ( f) ?. to_f16 ( ) ?;
264+ let i = this. read_scalar ( i) ?. to_i32 ( ) ?;
265+
266+ let res = math:: fixed_powi_value ( this, f, i) . unwrap_or_else ( || {
267+ // Using host floats (but it's fine, this operation does not have guaranteed precision).
268+ let res = f. to_host ( ) . powi ( i) . to_soft ( ) ;
269+
270+ // Apply a relative error of 4ULP to introduce some non-determinism
271+ // simulating imprecise implementations and optimizations.
272+ math:: apply_random_float_error_ulp ( this, res, 4 )
273+ } ) ;
274+ let res = this. adjust_nan ( res, & [ f] ) ;
275+ this. write_scalar ( res, dest) ?;
276+ }
234277 "powif32" => {
235278 let [ f, i] = check_intrinsic_arg_count ( args) ?;
236279 let f = this. read_scalar ( f) ?. to_f32 ( ) ?;
0 commit comments