1+ use rustc_apfloat:: ieee:: { DoubleS , HalfS , IeeeFloat , Semantics , SingleS } ;
12use rustc_apfloat:: { self , Float , FloatConvert , Round } ;
23use rustc_middle:: mir;
34use rustc_middle:: ty:: { self , FloatTy } ;
45
5- use self :: helpers :: { ToHost , ToSoft } ;
6+ use self :: math :: { HostFloatOperation , HostUnaryFloatOp , IeeeExt , host_unary_float_op } ;
67use super :: check_intrinsic_arg_count;
78use crate :: * ;
89
@@ -12,12 +13,82 @@ fn sqrt<'tcx, F: Float + FloatConvert<F> + Into<Scalar>>(
1213 dest : & MPlaceTy < ' tcx > ,
1314) -> InterpResult < ' tcx > {
1415 let [ f] = check_intrinsic_arg_count ( args) ?;
15- let f = this. read_scalar ( f) ?;
16- let f: F = f. to_float ( ) ?;
17- // Sqrt is specified to be fully precise.
18- let res = math:: sqrt ( f) ;
16+ math:: sqrt_op :: < F > ( this, f, dest)
17+ }
18+
19+ /// Determine which float operation on which type this is.
20+ fn is_host_unary_float_op ( intrinsic_name : & str ) -> Option < ( FloatTy , HostUnaryFloatOp ) > {
21+ let ( op, ty) = intrinsic_name. rsplit_once ( 'f' ) ?;
22+
23+ let float_ty = match ty {
24+ "16" => FloatTy :: F16 ,
25+ "32" => FloatTy :: F32 ,
26+ "64" => FloatTy :: F64 ,
27+ "128" => FloatTy :: F128 ,
28+ _ => return None ,
29+ } ;
30+
31+ let host_float_op = match op {
32+ "sin" => HostUnaryFloatOp :: Sin ,
33+ "cos" => HostUnaryFloatOp :: Cos ,
34+ "exp" => HostUnaryFloatOp :: Exp ,
35+ "exp2" => HostUnaryFloatOp :: Exp2 ,
36+ "log" => HostUnaryFloatOp :: Log ,
37+ "log10" => HostUnaryFloatOp :: Log10 ,
38+ "log2" => HostUnaryFloatOp :: Log2 ,
39+ _ => return None ,
40+ } ;
41+
42+ Some ( ( float_ty, host_float_op) )
43+ }
44+
45+ fn pow_intrinsic < ' tcx , S : Semantics > (
46+ this : & mut MiriInterpCx < ' tcx > ,
47+ args : & [ OpTy < ' tcx > ] ,
48+ dest : & MPlaceTy < ' tcx > ,
49+ ) -> InterpResult < ' tcx , ( ) >
50+ where
51+ IeeeFloat < S > : HostFloatOperation + IeeeExt + Float + Into < Scalar > ,
52+ {
53+ let [ f1, f2] = check_intrinsic_arg_count ( args) ?;
54+ let f1: IeeeFloat < S > = this. read_scalar ( f1) ?. to_float ( ) ?;
55+ let f2: IeeeFloat < S > = this. read_scalar ( f2) ?. to_float ( ) ?;
56+
57+ let res = math:: fixed_float_value ( this, "pow" , & [ f1, f2] ) . unwrap_or_else ( || {
58+ // Using host floats (but it's fine, this operation does not have guaranteed precision).
59+ let res = f1. host_powf ( f2) ;
60+
61+ // Apply a relative error of 4ULP to introduce some non-determinism
62+ // simulating imprecise implementations and optimizations.
63+ math:: apply_random_float_error_ulp ( this, res, 4 )
64+ } ) ;
65+ let res = this. adjust_nan ( res, & [ f1, f2] ) ;
66+ this. write_scalar ( res, dest) ?;
67+ interp_ok ( ( ) )
68+ }
69+ fn powi_intrinsic < ' tcx , S : Semantics > (
70+ this : & mut MiriInterpCx < ' tcx > ,
71+ args : & [ OpTy < ' tcx > ] ,
72+ dest : & MPlaceTy < ' tcx > ,
73+ ) -> InterpResult < ' tcx , ( ) >
74+ where
75+ IeeeFloat < S > : HostFloatOperation + IeeeExt + Float + Into < Scalar > ,
76+ {
77+ let [ f, i] = check_intrinsic_arg_count ( args) ?;
78+ let f: IeeeFloat < S > = this. read_scalar ( f) ?. to_float ( ) ?;
79+ let i = this. read_scalar ( i) ?. to_i32 ( ) ?;
80+
81+ let res = math:: fixed_powi_value ( this, f, i) . unwrap_or_else ( || {
82+ // Using host floats (but it's fine, this operation does not have guaranteed precision).
83+ let res = f. host_powi ( i) ;
84+
85+ // Apply a relative error of 4ULP to introduce some non-determinism
86+ // simulating imprecise implementations and optimizations.
87+ math:: apply_random_float_error_ulp ( this, res, 4 )
88+ } ) ;
1989 let res = this. adjust_nan ( res, & [ f] ) ;
20- this. write_scalar ( res, dest)
90+ this. write_scalar ( res, dest) ?;
91+ interp_ok ( ( ) )
2192}
2293
2394impl < ' tcx > EvalContextExt < ' tcx > for crate :: MiriInterpCx < ' tcx > { }
@@ -108,161 +179,23 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
108179 }
109180
110181 // Operations that need host floats.
111- #[ rustfmt:: skip]
112- | "sinf32"
113- | "cosf32"
114- | "expf32"
115- | "exp2f32"
116- | "logf32"
117- | "log10f32"
118- | "log2f32"
119- => {
182+ _ if let Some ( ( float_ty, op) ) = is_host_unary_float_op ( intrinsic_name) => {
120183 let [ f] = check_intrinsic_arg_count ( args) ?;
121- let f = this. read_scalar ( f) ?. to_f32 ( ) ?;
122-
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 ( ) ;
138-
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- }
154-
155- #[ rustfmt:: skip]
156- | "sinf64"
157- | "cosf64"
158- | "expf64"
159- | "exp2f64"
160- | "logf64"
161- | "log10f64"
162- | "log2f64"
163- => {
164- let [ f] = check_intrinsic_arg_count ( args) ?;
165- let f = this. read_scalar ( f) ?. to_f64 ( ) ?;
166-
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 ( ) ;
182-
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] ) ;
196- this. write_scalar ( res, dest) ?;
197- }
198-
199- "powf32" => {
200- let [ f1, f2] = check_intrinsic_arg_count ( args) ?;
201- let f1 = this. read_scalar ( f1) ?. to_f32 ( ) ?;
202- let f2 = this. read_scalar ( f2) ?. to_f32 ( ) ?;
203-
204- let res =
205- math:: fixed_float_value ( this, intrinsic_name, & [ f1, f2] ) . unwrap_or_else ( || {
206- // Using host floats (but it's fine, this operation does not have guaranteed precision).
207- let res = f1. to_host ( ) . powf ( f2. to_host ( ) ) . to_soft ( ) ;
208-
209- // Apply a relative error of 4ULP to introduce some non-determinism
210- // simulating imprecise implementations and optimizations.
211- math:: apply_random_float_error_ulp ( this, res, 4 )
212- } ) ;
213- let res = this. adjust_nan ( res, & [ f1, f2] ) ;
214- this. write_scalar ( res, dest) ?;
215- }
216- "powf64" => {
217- let [ f1, f2] = check_intrinsic_arg_count ( args) ?;
218- let f1 = this. read_scalar ( f1) ?. to_f64 ( ) ?;
219- let f2 = this. read_scalar ( f2) ?. to_f64 ( ) ?;
220-
221- let res =
222- math:: fixed_float_value ( this, intrinsic_name, & [ f1, f2] ) . unwrap_or_else ( || {
223- // Using host floats (but it's fine, this operation does not have guaranteed precision).
224- let res = f1. to_host ( ) . powf ( f2. to_host ( ) ) . to_soft ( ) ;
225-
226- // Apply a relative error of 4ULP to introduce some non-determinism
227- // simulating imprecise implementations and optimizations.
228- math:: apply_random_float_error_ulp ( this, res, 4 )
229- } ) ;
230- let res = this. adjust_nan ( res, & [ f1, f2] ) ;
231- this. write_scalar ( res, dest) ?;
232- }
233-
234- "powif32" => {
235- let [ f, i] = check_intrinsic_arg_count ( args) ?;
236- let f = this. read_scalar ( f) ?. to_f32 ( ) ?;
237- let i = this. read_scalar ( i) ?. to_i32 ( ) ?;
238-
239- let res = math:: fixed_powi_value ( this, f, i) . unwrap_or_else ( || {
240- // Using host floats (but it's fine, this operation does not have guaranteed precision).
241- let res = f. to_host ( ) . powi ( i) . to_soft ( ) ;
242-
243- // Apply a relative error of 4ULP to introduce some non-determinism
244- // simulating imprecise implementations and optimizations.
245- math:: apply_random_float_error_ulp ( this, res, 4 )
246- } ) ;
247- let res = this. adjust_nan ( res, & [ f] ) ;
248- this. write_scalar ( res, dest) ?;
184+ match float_ty {
185+ FloatTy :: F16 => host_unary_float_op :: < HalfS > ( this, f, op, dest) ?,
186+ FloatTy :: F32 => host_unary_float_op :: < SingleS > ( this, f, op, dest) ?,
187+ FloatTy :: F64 => host_unary_float_op :: < DoubleS > ( this, f, op, dest) ?,
188+ FloatTy :: F128 => todo ! ( "f128" ) , // FIXME(f128)
189+ } ;
249190 }
250- "powif64" => {
251- let [ f, i] = check_intrinsic_arg_count ( args) ?;
252- let f = this. read_scalar ( f) ?. to_f64 ( ) ?;
253- let i = this. read_scalar ( i) ?. to_i32 ( ) ?;
254191
255- let res = math :: fixed_powi_value ( this, f , i ) . unwrap_or_else ( || {
256- // Using host floats (but it's fine, this operation does not have guaranteed precision).
257- let res = f . to_host ( ) . powi ( i ) . to_soft ( ) ;
192+ "powf16" => pow_intrinsic :: < HalfS > ( this, args , dest ) ? ,
193+ "powf32" => pow_intrinsic :: < SingleS > ( this , args , dest ) ? ,
194+ "powf64" => pow_intrinsic :: < DoubleS > ( this , args , dest ) ? ,
258195
259- // Apply a relative error of 4ULP to introduce some non-determinism
260- // simulating imprecise implementations and optimizations.
261- math:: apply_random_float_error_ulp ( this, res, 4 )
262- } ) ;
263- let res = this. adjust_nan ( res, & [ f] ) ;
264- this. write_scalar ( res, dest) ?;
265- }
196+ "powif16" => powi_intrinsic :: < HalfS > ( this, args, dest) ?,
197+ "powif32" => powi_intrinsic :: < SingleS > ( this, args, dest) ?,
198+ "powif64" => powi_intrinsic :: < DoubleS > ( this, args, dest) ?,
266199
267200 _ => return interp_ok ( EmulateItemResult :: NotSupported ) ,
268201 }
0 commit comments