Add checked float-to-int helper function
This commit is contained in:
parent
a45f181eb0
commit
fe35dd1afc
@ -13,11 +13,11 @@
|
||||
use rustc_middle::mir;
|
||||
use rustc_middle::ty::{
|
||||
self,
|
||||
layout::{LayoutOf, TyAndLayout},
|
||||
List, TyCtxt,
|
||||
layout::{IntegerExt as _, LayoutOf, TyAndLayout},
|
||||
List, Ty, TyCtxt,
|
||||
};
|
||||
use rustc_span::{def_id::CrateNum, sym, Span, Symbol};
|
||||
use rustc_target::abi::{Align, FieldIdx, FieldsShape, Size, Variants};
|
||||
use rustc_target::abi::{Align, FieldIdx, FieldsShape, Integer, Size, Variants};
|
||||
use rustc_target::spec::abi::Abi;
|
||||
|
||||
use rand::RngCore;
|
||||
@ -1011,6 +1011,65 @@ fn item_link_name(&self, def_id: DefId) -> Symbol {
|
||||
None => tcx.item_name(def_id),
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts `f` to integer type `dest_ty` after rounding with mode `round`.
|
||||
/// Returns `None` if `f` is NaN or out of range.
|
||||
fn float_to_int_checked<F>(
|
||||
&self,
|
||||
f: F,
|
||||
dest_ty: Ty<'tcx>,
|
||||
round: rustc_apfloat::Round,
|
||||
) -> Option<Scalar<Provenance>>
|
||||
where
|
||||
F: rustc_apfloat::Float + Into<Scalar<Provenance>>,
|
||||
{
|
||||
let this = self.eval_context_ref();
|
||||
|
||||
match dest_ty.kind() {
|
||||
// Unsigned
|
||||
ty::Uint(t) => {
|
||||
let size = Integer::from_uint_ty(this, *t).size();
|
||||
let res = f.to_u128_r(size.bits_usize(), round, &mut false);
|
||||
if res.status.intersects(
|
||||
rustc_apfloat::Status::INVALID_OP
|
||||
| rustc_apfloat::Status::OVERFLOW
|
||||
| rustc_apfloat::Status::UNDERFLOW,
|
||||
) {
|
||||
// Floating point value is NaN (flagged with INVALID_OP) or outside the range
|
||||
// of values of the integer type (flagged with OVERFLOW or UNDERFLOW).
|
||||
None
|
||||
} else {
|
||||
// Floating point value can be represented by the integer type after rounding.
|
||||
// The INEXACT flag is ignored on purpose to allow rounding.
|
||||
Some(Scalar::from_uint(res.value, size))
|
||||
}
|
||||
}
|
||||
// Signed
|
||||
ty::Int(t) => {
|
||||
let size = Integer::from_int_ty(this, *t).size();
|
||||
let res = f.to_i128_r(size.bits_usize(), round, &mut false);
|
||||
if res.status.intersects(
|
||||
rustc_apfloat::Status::INVALID_OP
|
||||
| rustc_apfloat::Status::OVERFLOW
|
||||
| rustc_apfloat::Status::UNDERFLOW,
|
||||
) {
|
||||
// Floating point value is NaN (flagged with INVALID_OP) or outside the range
|
||||
// of values of the integer type (flagged with OVERFLOW or UNDERFLOW).
|
||||
None
|
||||
} else {
|
||||
// Floating point value can be represented by the integer type after rounding.
|
||||
// The INEXACT flag is ignored on purpose to allow rounding.
|
||||
Some(Scalar::from_int(res.value, size))
|
||||
}
|
||||
}
|
||||
// Nothing else
|
||||
_ =>
|
||||
span_bug!(
|
||||
this.cur_span(),
|
||||
"attempted float-to-int conversion with non-int output type {dest_ty:?}"
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
|
||||
|
@ -6,12 +6,12 @@
|
||||
use log::trace;
|
||||
|
||||
use rustc_apfloat::{Float, Round};
|
||||
use rustc_middle::ty::layout::{IntegerExt, LayoutOf};
|
||||
use rustc_middle::ty::layout::LayoutOf;
|
||||
use rustc_middle::{
|
||||
mir,
|
||||
ty::{self, FloatTy, Ty},
|
||||
};
|
||||
use rustc_target::abi::{Integer, Size};
|
||||
use rustc_target::abi::Size;
|
||||
|
||||
use crate::*;
|
||||
use atomic::EvalContextExt as _;
|
||||
@ -393,47 +393,10 @@ fn float_to_int_unchecked<F>(
|
||||
F: Float + Into<Scalar<Provenance>>,
|
||||
{
|
||||
let this = self.eval_context_ref();
|
||||
|
||||
// Step 1: cut off the fractional part of `f`. The result of this is
|
||||
// guaranteed to be precisely representable in IEEE floats.
|
||||
let f = f.round_to_integral(Round::TowardZero).value;
|
||||
|
||||
// Step 2: Cast the truncated float to the target integer type and see if we lose any information in this step.
|
||||
Ok(match dest_ty.kind() {
|
||||
// Unsigned
|
||||
ty::Uint(t) => {
|
||||
let size = Integer::from_uint_ty(this, *t).size();
|
||||
let res = f.to_u128(size.bits_usize());
|
||||
if res.status.is_empty() {
|
||||
// No status flags means there was no further rounding or other loss of precision.
|
||||
Scalar::from_uint(res.value, size)
|
||||
} else {
|
||||
// `f` was not representable in this integer type.
|
||||
throw_ub_format!(
|
||||
"`float_to_int_unchecked` intrinsic called on {f} which cannot be represented in target type `{dest_ty:?}`",
|
||||
);
|
||||
}
|
||||
}
|
||||
// Signed
|
||||
ty::Int(t) => {
|
||||
let size = Integer::from_int_ty(this, *t).size();
|
||||
let res = f.to_i128(size.bits_usize());
|
||||
if res.status.is_empty() {
|
||||
// No status flags means there was no further rounding or other loss of precision.
|
||||
Scalar::from_int(res.value, size)
|
||||
} else {
|
||||
// `f` was not representable in this integer type.
|
||||
throw_ub_format!(
|
||||
"`float_to_int_unchecked` intrinsic called on {f} which cannot be represented in target type `{dest_ty:?}`",
|
||||
);
|
||||
}
|
||||
}
|
||||
// Nothing else
|
||||
_ =>
|
||||
span_bug!(
|
||||
this.cur_span(),
|
||||
"`float_to_int_unchecked` called with non-int output type {dest_ty:?}"
|
||||
),
|
||||
})
|
||||
Ok(this
|
||||
.float_to_int_checked(f, dest_ty, Round::TowardZero)
|
||||
.ok_or_else(|| err_ub_format!(
|
||||
"`float_to_int_unchecked` intrinsic called on {f} which cannot be represented in target type `{dest_ty:?}`",
|
||||
))?)
|
||||
}
|
||||
}
|
||||
|
@ -195,24 +195,12 @@ fn emulate_x86_sse_intrinsic(
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let mut exact = false;
|
||||
let cvt = op.to_i128_r(32, rnd, &mut exact);
|
||||
let res = if cvt.status.intersects(
|
||||
rustc_apfloat::Status::INVALID_OP
|
||||
| rustc_apfloat::Status::OVERFLOW
|
||||
| rustc_apfloat::Status::UNDERFLOW,
|
||||
) {
|
||||
// Input is NaN (flagged with INVALID_OP) or does not fit
|
||||
// in an i32 (flagged with OVERFLOW or UNDERFLOW), fallback
|
||||
// to minimum acording to SSE semantics. The INEXACT flag
|
||||
// is ignored on purpose because rounding can happen during
|
||||
// float-to-int conversion.
|
||||
i32::MIN
|
||||
} else {
|
||||
i32::try_from(cvt.value).unwrap()
|
||||
};
|
||||
let res = this.float_to_int_checked(op, dest.layout.ty, rnd).unwrap_or_else(|| {
|
||||
// Fallback to minimum acording to SSE semantics.
|
||||
Scalar::from_i32(i32::MIN)
|
||||
});
|
||||
|
||||
this.write_scalar(Scalar::from_i32(res), dest)?;
|
||||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
// Use to implement _mm_cvtss_si64 and _mm_cvttss_si64.
|
||||
// Converts the first component of `op` from f32 to i64.
|
||||
@ -232,24 +220,12 @@ fn emulate_x86_sse_intrinsic(
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let mut exact = false;
|
||||
let cvt = op.to_i128_r(64, rnd, &mut exact);
|
||||
let res = if cvt.status.intersects(
|
||||
rustc_apfloat::Status::INVALID_OP
|
||||
| rustc_apfloat::Status::OVERFLOW
|
||||
| rustc_apfloat::Status::UNDERFLOW,
|
||||
) {
|
||||
// Input is NaN (flagged with INVALID_OP) or does not fit
|
||||
// in an i64 (flagged with OVERFLOW or UNDERFLOW), fallback
|
||||
// to minimum acording to SSE semantics. The INEXACT flag
|
||||
// is ignored on purpose because rounding can happen during
|
||||
// float-to-int conversion.
|
||||
i64::MIN
|
||||
} else {
|
||||
i64::try_from(cvt.value).unwrap()
|
||||
};
|
||||
let res = this.float_to_int_checked(op, dest.layout.ty, rnd).unwrap_or_else(|| {
|
||||
// Fallback to minimum acording to SSE semantics.
|
||||
Scalar::from_i64(i64::MIN)
|
||||
});
|
||||
|
||||
this.write_scalar(Scalar::from_i64(res), dest)?;
|
||||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
// Used to implement the _mm_cvtsi32_ss function.
|
||||
// Converts `right` from i32 to f32. Returns a SIMD vector with
|
||||
|
@ -1,8 +1,8 @@
|
||||
error: Undefined Behavior: `float_to_int_unchecked` intrinsic called on -1 which cannot be represented in target type `u128`
|
||||
error: Undefined Behavior: `float_to_int_unchecked` intrinsic called on -1.0000000000000999 which cannot be represented in target type `u128`
|
||||
--> $DIR/float_to_int_64_neg.rs:LL:CC
|
||||
|
|
||||
LL | float_to_int_unchecked::<f64, u128>(-1.0000000000001f64);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `float_to_int_unchecked` intrinsic called on -1 which cannot be represented in target type `u128`
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `float_to_int_unchecked` intrinsic called on -1.0000000000000999 which cannot be represented in target type `u128`
|
||||
|
|
||||
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
|
||||
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
|
||||
|
Loading…
Reference in New Issue
Block a user