|
1 | 1 | use std::iter;
|
2 | 2 | use std::convert::TryFrom;
|
3 | 3 |
|
| 4 | +use rustc_ast::ast::FloatTy; |
4 | 5 | use rustc_middle::{mir, ty};
|
5 |
| -use rustc_apfloat::Float; |
6 |
| -use rustc_target::abi::{Align, LayoutOf}; |
| 6 | +use rustc_apfloat::{Float, FloatConvert, Round, ieee::{Double, Single}}; |
| 7 | +use rustc_target::abi::{Align, LayoutOf, Size}; |
7 | 8 |
|
8 | 9 | use crate::*;
|
9 | 10 |
|
@@ -279,6 +280,22 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
279 | 280 | this.write_scalar(Scalar::from_u64(f.powi(i).to_bits()), dest)?;
|
280 | 281 | }
|
281 | 282 |
|
| 283 | + "float_to_int_unchecked" => { |
| 284 | + let val = this.read_immediate(args[0])?; |
| 285 | + |
| 286 | + let res = match val.layout.ty.kind { |
| 287 | + ty::Float(FloatTy::F32) => { |
| 288 | + this.float_to_int_unchecked(val.to_scalar()?.to_f32()?, dest.layout.ty)? |
| 289 | + } |
| 290 | + ty::Float(FloatTy::F64) => { |
| 291 | + this.float_to_int_unchecked(val.to_scalar()?.to_f64()?, dest.layout.ty)? |
| 292 | + } |
| 293 | + _ => bug!("`float_to_int_unchecked` called with non-float input type {:?}", val.layout.ty), |
| 294 | + }; |
| 295 | + |
| 296 | + this.write_scalar(res, dest)?; |
| 297 | + } |
| 298 | + |
282 | 299 | // Atomic operations
|
283 | 300 | #[rustfmt::skip]
|
284 | 301 | | "atomic_load"
|
@@ -491,4 +508,55 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
491 | 508 | this.go_to_block(ret);
|
492 | 509 | Ok(())
|
493 | 510 | }
|
| 511 | + |
| 512 | + fn float_to_int_unchecked<F>( |
| 513 | + &self, |
| 514 | + f: F, |
| 515 | + dest_ty: ty::Ty<'tcx>, |
| 516 | + ) -> InterpResult<'tcx, Scalar<Tag>> |
| 517 | + where |
| 518 | + F: Float + Into<Scalar<Tag>> + FloatConvert<Single> + FloatConvert<Double>, |
| 519 | + { |
| 520 | + let this = self.eval_context_ref(); |
| 521 | + |
| 522 | + // Step 1: cut off the fractional part of `f`. The result of this is |
| 523 | + // guaranteed to be precisely representable in IEEE floats. |
| 524 | + let f = f.round_to_integral(Round::TowardZero).value; |
| 525 | + |
| 526 | + // Step 2: Cast the truncated float to the target integer type and see if we lose any information in this step. |
| 527 | + Ok(match dest_ty.kind { |
| 528 | + // Unsigned |
| 529 | + ty::Uint(t) => { |
| 530 | + let width = t.bit_width().unwrap_or_else(|| this.pointer_size().bits()); |
| 531 | + let res = f.to_u128(usize::try_from(width).unwrap()); |
| 532 | + if res.status.is_empty() { |
| 533 | + // No status flags means there was no further rounding or other loss of precision. |
| 534 | + Scalar::from_uint(res.value, Size::from_bits(width)) |
| 535 | + } else { |
| 536 | + // `f` was not representable in this integer type. |
| 537 | + throw_ub_format!( |
| 538 | + "`float_to_int_unchecked` intrinsic called on {} which cannot be represented in target type `{:?}`", |
| 539 | + f, dest_ty, |
| 540 | + ); |
| 541 | + } |
| 542 | + } |
| 543 | + // Signed |
| 544 | + ty::Int(t) => { |
| 545 | + let width = t.bit_width().unwrap_or_else(|| this.pointer_size().bits()); |
| 546 | + let res = f.to_i128(usize::try_from(width).unwrap()); |
| 547 | + if res.status.is_empty() { |
| 548 | + // No status flags means there was no further rounding or other loss of precision. |
| 549 | + Scalar::from_int(res.value, Size::from_bits(width)) |
| 550 | + } else { |
| 551 | + // `f` was not representable in this integer type. |
| 552 | + throw_ub_format!( |
| 553 | + "`float_to_int_unchecked` intrinsic called on {} which cannot be represented in target type `{:?}`", |
| 554 | + f, dest_ty, |
| 555 | + ); |
| 556 | + } |
| 557 | + } |
| 558 | + // Nothing else |
| 559 | + _ => bug!("`float_to_int_unchecked` called with non-int output type {:?}", dest_ty), |
| 560 | + }) |
| 561 | + } |
494 | 562 | }
|
0 commit comments