@@ -7,12 +7,13 @@ use rand::Rng;
7
7
use rustc_abi:: Size ;
8
8
use rustc_apfloat:: { Float , Round } ;
9
9
use rustc_middle:: mir;
10
- use rustc_middle:: ty:: { self , FloatTy } ;
10
+ use rustc_middle:: ty:: { self , FloatTy , ScalarInt } ;
11
11
use rustc_span:: { Symbol , sym} ;
12
12
13
13
use self :: atomic:: EvalContextExt as _;
14
14
use self :: helpers:: { ToHost , ToSoft , check_intrinsic_arg_count} ;
15
15
use self :: simd:: EvalContextExt as _;
16
+ use crate :: math:: apply_random_float_error_ulp;
16
17
use crate :: * ;
17
18
18
19
impl < ' tcx > EvalContextExt < ' tcx > for crate :: MiriInterpCx < ' tcx > { }
@@ -206,10 +207,26 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
206
207
this. write_scalar ( res, dest) ?;
207
208
}
208
209
210
+ "sqrtf32" => {
211
+ let [ f] = check_intrinsic_arg_count ( args) ?;
212
+ let f = this. read_scalar ( f) ?. to_f32 ( ) ?;
213
+ // Sqrt is specified to be fully precise.
214
+ let res = math:: sqrt ( f) ;
215
+ let res = this. adjust_nan ( res, & [ f] ) ;
216
+ this. write_scalar ( res, dest) ?;
217
+ }
218
+ "sqrtf64" => {
219
+ let [ f] = check_intrinsic_arg_count ( args) ?;
220
+ let f = this. read_scalar ( f) ?. to_f64 ( ) ?;
221
+ // Sqrt is specified to be fully precise.
222
+ let res = math:: sqrt ( f) ;
223
+ let res = this. adjust_nan ( res, & [ f] ) ;
224
+ this. write_scalar ( res, dest) ?;
225
+ }
226
+
209
227
#[ rustfmt:: skip]
210
228
| "sinf32"
211
229
| "cosf32"
212
- | "sqrtf32"
213
230
| "expf32"
214
231
| "exp2f32"
215
232
| "logf32"
@@ -218,26 +235,33 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
218
235
=> {
219
236
let [ f] = check_intrinsic_arg_count ( args) ?;
220
237
let f = this. read_scalar ( f) ?. to_f32 ( ) ?;
221
- // Using host floats except for sqrt (but it's fine, these operations do not have
238
+ // Using host floats (but it's fine, these operations do not have
222
239
// guaranteed precision).
240
+ let host = f. to_host ( ) ;
223
241
let res = match intrinsic_name {
224
- "sinf32" => f. to_host ( ) . sin ( ) . to_soft ( ) ,
225
- "cosf32" => f. to_host ( ) . cos ( ) . to_soft ( ) ,
226
- "sqrtf32" => math:: sqrt ( f) ,
227
- "expf32" => f. to_host ( ) . exp ( ) . to_soft ( ) ,
228
- "exp2f32" => f. to_host ( ) . exp2 ( ) . to_soft ( ) ,
229
- "logf32" => f. to_host ( ) . ln ( ) . to_soft ( ) ,
230
- "log10f32" => f. to_host ( ) . log10 ( ) . to_soft ( ) ,
231
- "log2f32" => f. to_host ( ) . log2 ( ) . to_soft ( ) ,
242
+ "sinf32" => host. sin ( ) ,
243
+ "cosf32" => host. cos ( ) ,
244
+ "expf32" => host. exp ( ) ,
245
+ "exp2f32" => host. exp2 ( ) ,
246
+ "logf32" => host. ln ( ) ,
247
+ "log10f32" => host. log10 ( ) ,
248
+ "log2f32" => host. log2 ( ) ,
232
249
_ => bug ! ( ) ,
233
250
} ;
251
+ let res = res. to_soft ( ) ;
252
+ // Apply a relative error of 16ULP to introduce some non-determinism
253
+ // simulating imprecise implementations and optimizations.
254
+ let res = apply_random_float_error_ulp (
255
+ this,
256
+ res,
257
+ 4 , // log2(16)
258
+ ) ;
234
259
let res = this. adjust_nan ( res, & [ f] ) ;
235
260
this. write_scalar ( res, dest) ?;
236
261
}
237
262
#[ rustfmt:: skip]
238
263
| "sinf64"
239
264
| "cosf64"
240
- | "sqrtf64"
241
265
| "expf64"
242
266
| "exp2f64"
243
267
| "logf64"
@@ -246,19 +270,27 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
246
270
=> {
247
271
let [ f] = check_intrinsic_arg_count ( args) ?;
248
272
let f = this. read_scalar ( f) ?. to_f64 ( ) ?;
249
- // Using host floats except for sqrt (but it's fine, these operations do not have
273
+ // Using host floats (but it's fine, these operations do not have
250
274
// guaranteed precision).
275
+ let host = f. to_host ( ) ;
251
276
let res = match intrinsic_name {
252
- "sinf64" => f. to_host ( ) . sin ( ) . to_soft ( ) ,
253
- "cosf64" => f. to_host ( ) . cos ( ) . to_soft ( ) ,
254
- "sqrtf64" => math:: sqrt ( f) ,
255
- "expf64" => f. to_host ( ) . exp ( ) . to_soft ( ) ,
256
- "exp2f64" => f. to_host ( ) . exp2 ( ) . to_soft ( ) ,
257
- "logf64" => f. to_host ( ) . ln ( ) . to_soft ( ) ,
258
- "log10f64" => f. to_host ( ) . log10 ( ) . to_soft ( ) ,
259
- "log2f64" => f. to_host ( ) . log2 ( ) . to_soft ( ) ,
277
+ "sinf64" => host. sin ( ) ,
278
+ "cosf64" => host. cos ( ) ,
279
+ "expf64" => host. exp ( ) ,
280
+ "exp2f64" => host. exp2 ( ) ,
281
+ "logf64" => host. ln ( ) ,
282
+ "log10f64" => host. log10 ( ) ,
283
+ "log2f64" => host. log2 ( ) ,
260
284
_ => bug ! ( ) ,
261
285
} ;
286
+ let res = res. to_soft ( ) ;
287
+ // Apply a relative error of 16ULP to introduce some non-determinism
288
+ // simulating imprecise implementations and optimizations.
289
+ let res = apply_random_float_error_ulp (
290
+ this,
291
+ res,
292
+ 4 , // log2(16)
293
+ ) ;
262
294
let res = this. adjust_nan ( res, & [ f] ) ;
263
295
this. write_scalar ( res, dest) ?;
264
296
}
@@ -316,6 +348,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
316
348
}
317
349
318
350
"powf32" => {
351
+ // FIXME: apply random relative error but without altering behaviour of powf
319
352
let [ f1, f2] = check_intrinsic_arg_count ( args) ?;
320
353
let f1 = this. read_scalar ( f1) ?. to_f32 ( ) ?;
321
354
let f2 = this. read_scalar ( f2) ?. to_f32 ( ) ?;
@@ -325,6 +358,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
325
358
this. write_scalar ( res, dest) ?;
326
359
}
327
360
"powf64" => {
361
+ // FIXME: apply random relative error but without altering behaviour of powf
328
362
let [ f1, f2] = check_intrinsic_arg_count ( args) ?;
329
363
let f1 = this. read_scalar ( f1) ?. to_f64 ( ) ?;
330
364
let f2 = this. read_scalar ( f2) ?. to_f64 ( ) ?;
@@ -335,6 +369,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
335
369
}
336
370
337
371
"powif32" => {
372
+ // FIXME: apply random relative error but without altering behaviour of powi
338
373
let [ f, i] = check_intrinsic_arg_count ( args) ?;
339
374
let f = this. read_scalar ( f) ?. to_f32 ( ) ?;
340
375
let i = this. read_scalar ( i) ?. to_i32 ( ) ?;
@@ -344,6 +379,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
344
379
this. write_scalar ( res, dest) ?;
345
380
}
346
381
"powif64" => {
382
+ // FIXME: apply random relative error but without altering behaviour of powi
347
383
let [ f, i] = check_intrinsic_arg_count ( args) ?;
348
384
let f = this. read_scalar ( f) ?. to_f64 ( ) ?;
349
385
let i = this. read_scalar ( i) ?. to_i32 ( ) ?;
@@ -372,7 +408,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
372
408
_ => bug ! ( ) ,
373
409
} ;
374
410
let res = this. binary_op ( op, & a, & b) ?;
375
- // `binary_op` already called `generate_nan` if necessary.
411
+ // `binary_op` already called `generate_nan` if needed.
412
+ // Apply a relative error of 16ULP to simulate non-deterministic precision loss
413
+ // due to optimizations.
414
+ let res = apply_random_float_error_to_imm ( this, res, 4 /* log2(16) */ ) ?;
376
415
this. write_immediate ( * res, dest) ?;
377
416
}
378
417
@@ -418,11 +457,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
418
457
_ => { }
419
458
}
420
459
let res = this. binary_op ( op, & a, & b) ?;
460
+ // This cannot be a NaN so we also don't have to apply any non-determinism.
461
+ // (Also, `binary_op` already called `generate_nan` if needed.)
421
462
if !float_finite ( & res) ? {
422
463
throw_ub_format ! ( "`{intrinsic_name}` intrinsic produced non-finite value as result" ) ;
423
464
}
424
- // This cannot be a NaN so we also don't have to apply any non-determinism.
425
- // (Also, `binary_op` already called `generate_nan` if needed.)
465
+ // Apply a relative error of 16ULP to simulate non-deterministic precision loss
466
+ // due to optimizations.
467
+ let res = apply_random_float_error_to_imm ( this, res, 4 /* log2(16) */ ) ?;
426
468
this. write_immediate ( * res, dest) ?;
427
469
}
428
470
@@ -455,3 +497,26 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
455
497
interp_ok ( EmulateItemResult :: NeedsReturn )
456
498
}
457
499
}
500
+
501
+ /// Applies a random 16ULP floating point error to `val` and returns the new value.
502
+ /// Will fail if `val` is not a floating point number.
503
+ fn apply_random_float_error_to_imm < ' tcx > (
504
+ ecx : & mut MiriInterpCx < ' tcx > ,
505
+ val : ImmTy < ' tcx > ,
506
+ ulp_exponent : u32 ,
507
+ ) -> InterpResult < ' tcx , ImmTy < ' tcx > > {
508
+ let scalar = val. to_scalar_int ( ) ?;
509
+ let res: ScalarInt = match val. layout . ty . kind ( ) {
510
+ ty:: Float ( FloatTy :: F16 ) =>
511
+ apply_random_float_error_ulp ( ecx, scalar. to_f16 ( ) , ulp_exponent) . into ( ) ,
512
+ ty:: Float ( FloatTy :: F32 ) =>
513
+ apply_random_float_error_ulp ( ecx, scalar. to_f32 ( ) , ulp_exponent) . into ( ) ,
514
+ ty:: Float ( FloatTy :: F64 ) =>
515
+ apply_random_float_error_ulp ( ecx, scalar. to_f64 ( ) , ulp_exponent) . into ( ) ,
516
+ ty:: Float ( FloatTy :: F128 ) =>
517
+ apply_random_float_error_ulp ( ecx, scalar. to_f128 ( ) , ulp_exponent) . into ( ) ,
518
+ _ => bug ! ( "intrinsic called with non-float input type" ) ,
519
+ } ;
520
+
521
+ interp_ok ( ImmTy :: from_scalar_int ( res, val. layout ) )
522
+ }
0 commit comments