Skip to content

Commit

Permalink
Auto merge of rust-lang#3185 - eduardosm:float_to_int_checked-generic…
Browse files Browse the repository at this point in the history
…, r=RalfJung

Refactor `float_to_int_checked` to remove its generic parameter and reduce code duplication a bit
  • Loading branch information
bors committed Nov 24, 2023
2 parents 2a1e0ce + 2855024 commit 44aceb5
Show file tree
Hide file tree
Showing 31 changed files with 185 additions and 205 deletions.
104 changes: 57 additions & 47 deletions src/tools/miri/src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,18 @@ use std::time::Duration;

use log::trace;

use rustc_apfloat::ieee::{Double, Single};
use rustc_hir::def::{DefKind, Namespace};
use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX};
use rustc_index::IndexVec;
use rustc_middle::mir;
use rustc_middle::ty::{
self,
layout::{IntegerExt as _, LayoutOf, TyAndLayout},
IntTy, Ty, TyCtxt, UintTy,
layout::{LayoutOf, TyAndLayout},
FloatTy, IntTy, Ty, TyCtxt, UintTy,
};
use rustc_span::{def_id::CrateNum, sym, Span, Symbol};
use rustc_target::abi::{Align, FieldIdx, FieldsShape, Integer, Size, Variants};
use rustc_target::abi::{Align, FieldIdx, FieldsShape, Size, Variants};
use rustc_target::spec::abi::Abi;

use rand::RngCore;
Expand Down Expand Up @@ -986,65 +987,74 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
}
}

/// Converts `f` to integer type `dest_ty` after rounding with mode `round`.
/// Converts `src` from floating point 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>(
fn float_to_int_checked(
&self,
f: F,
src: &ImmTy<'tcx, Provenance>,
cast_to: TyAndLayout<'tcx>,
round: rustc_apfloat::Round,
) -> Option<ImmTy<'tcx, Provenance>>
where
F: rustc_apfloat::Float + Into<Scalar<Provenance>>,
{
) -> InterpResult<'tcx, Option<ImmTy<'tcx, Provenance>>> {
let this = self.eval_context_ref();

let val = match cast_to.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).
return None;
} else {
// Floating point value can be represented by the integer type after rounding.
// The INEXACT flag is ignored on purpose to allow rounding.
Scalar::from_uint(res.value, size)
fn float_to_int_inner<'tcx, F: rustc_apfloat::Float>(
this: &MiriInterpCx<'_, 'tcx>,
src: F,
cast_to: TyAndLayout<'tcx>,
round: rustc_apfloat::Round,
) -> (Scalar<Provenance>, rustc_apfloat::Status) {
let int_size = cast_to.layout.size;
match cast_to.ty.kind() {
// Unsigned
ty::Uint(_) => {
let res = src.to_u128_r(int_size.bits_usize(), round, &mut false);
(Scalar::from_uint(res.value, int_size), res.status)
}
}
// 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).
return None;
} else {
// Floating point value can be represented by the integer type after rounding.
// The INEXACT flag is ignored on purpose to allow rounding.
Scalar::from_int(res.value, size)
// Signed
ty::Int(_) => {
let res = src.to_i128_r(int_size.bits_usize(), round, &mut false);
(Scalar::from_int(res.value, int_size), res.status)
}
// Nothing else
_ =>
span_bug!(
this.cur_span(),
"attempted float-to-int conversion with non-int output type {}",
cast_to.ty,
),
}
}

let (val, status) = match src.layout.ty.kind() {
// f32
ty::Float(FloatTy::F32) =>
float_to_int_inner::<Single>(this, src.to_scalar().to_f32()?, cast_to, round),
// f64
ty::Float(FloatTy::F64) =>
float_to_int_inner::<Double>(this, src.to_scalar().to_f64()?, cast_to, round),
// Nothing else
_ =>
span_bug!(
this.cur_span(),
"attempted float-to-int conversion with non-int output type {}",
cast_to.ty,
"attempted float-to-int conversion with non-float input type {}",
src.layout.ty,
),
};
Some(ImmTy::from_scalar(val, cast_to))

if 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).
Ok(None)
} else {
// Floating point value can be represented by the integer type after rounding.
// The INEXACT flag is ignored on purpose to allow rounding.
Ok(Some(ImmTy::from_scalar(val, cast_to)))
}
}

/// Returns an integer type that is twice wide as `ty`
Expand Down
38 changes: 8 additions & 30 deletions src/tools/miri/src/shims/intrinsics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -365,36 +365,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
let [val] = check_arg_count(args)?;
let val = this.read_immediate(val)?;

let res = match val.layout.ty.kind() {
ty::Float(FloatTy::F32) => {
let f = val.to_scalar().to_f32()?;
this
.float_to_int_checked(f, dest.layout, Round::TowardZero)
.ok_or_else(|| {
err_ub_format!(
"`float_to_int_unchecked` intrinsic called on {f} which cannot be represented in target type `{:?}`",
dest.layout.ty
)
})?
}
ty::Float(FloatTy::F64) => {
let f = val.to_scalar().to_f64()?;
this
.float_to_int_checked(f, dest.layout, Round::TowardZero)
.ok_or_else(|| {
err_ub_format!(
"`float_to_int_unchecked` intrinsic called on {f} which cannot be represented in target type `{:?}`",
dest.layout.ty
)
})?
}
_ =>
span_bug!(
this.cur_span(),
"`float_to_int_unchecked` called with non-float input type {:?}",
val.layout.ty
),
};
let res = this
.float_to_int_checked(&val, dest.layout, Round::TowardZero)?
.ok_or_else(|| {
err_ub_format!(
"`float_to_int_unchecked` intrinsic called on {val} which cannot be represented in target type `{:?}`",
dest.layout.ty
)
})?;

this.write_immediate(*res, dest)?;
}
Expand Down
17 changes: 3 additions & 14 deletions src/tools/miri/src/shims/intrinsics/simd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -447,22 +447,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
(ty::Float(_), ty::Int(_) | ty::Uint(_)) if safe_cast =>
this.float_to_float_or_int(&op, dest.layout)?,
// Float-to-int in unchecked mode
(ty::Float(FloatTy::F32), ty::Int(_) | ty::Uint(_)) if unsafe_cast => {
let f = op.to_scalar().to_f32()?;
this.float_to_int_checked(f, dest.layout, Round::TowardZero)
(ty::Float(_), ty::Int(_) | ty::Uint(_)) if unsafe_cast => {
this.float_to_int_checked(&op, dest.layout, Round::TowardZero)?
.ok_or_else(|| {
err_ub_format!(
"`simd_cast` intrinsic called on {f} which cannot be represented in target type `{:?}`",
dest.layout.ty
)
})?
}
(ty::Float(FloatTy::F64), ty::Int(_) | ty::Uint(_)) if unsafe_cast => {
let f = op.to_scalar().to_f64()?;
this.float_to_int_checked(f, dest.layout, Round::TowardZero)
.ok_or_else(|| {
err_ub_format!(
"`simd_cast` intrinsic called on {f} which cannot be represented in target type `{:?}`",
"`simd_cast` intrinsic called on {op} which cannot be represented in target type `{:?}`",
dest.layout.ty
)
})?
Expand Down
39 changes: 38 additions & 1 deletion src/tools/miri/src/shims/x86/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use rustc_middle::mir;
use rustc_middle::{mir, ty};
use rustc_span::Symbol;
use rustc_target::abi::Size;
use rustc_target::spec::abi::Abi;
Expand Down Expand Up @@ -331,6 +331,43 @@ fn bin_op_simd_float_all<'tcx, F: rustc_apfloat::Float>(
Ok(())
}

/// Converts each element of `op` from floating point to signed integer.
///
/// When the input value is NaN or out of range, fall back to minimum value.
///
/// If `op` has more elements than `dest`, extra elements are ignored. If `op`
/// has less elements than `dest`, the rest is filled with zeros.
fn convert_float_to_int<'tcx>(
this: &mut crate::MiriInterpCx<'_, 'tcx>,
op: &OpTy<'tcx, Provenance>,
rnd: rustc_apfloat::Round,
dest: &PlaceTy<'tcx, Provenance>,
) -> InterpResult<'tcx, ()> {
let (op, op_len) = this.operand_to_simd(op)?;
let (dest, dest_len) = this.place_to_simd(dest)?;

// Output must be *signed* integers.
assert!(matches!(dest.layout.field(this, 0).ty.kind(), ty::Int(_)));

for i in 0..op_len.min(dest_len) {
let op = this.read_immediate(&this.project_index(&op, i)?)?;
let dest = this.project_index(&dest, i)?;

let res = this.float_to_int_checked(&op, dest.layout, rnd)?.unwrap_or_else(|| {
// Fallback to minimum acording to SSE/AVX semantics.
ImmTy::from_int(dest.layout.size.signed_int_min(), dest.layout)
});
this.write_immediate(*res, &dest)?;
}
// Fill remainder with zeros
for i in op_len..dest_len {
let dest = this.project_index(&dest, i)?;
this.write_scalar(Scalar::from_int(0, dest.layout.size), &dest)?;
}

Ok(())
}

/// Horizontaly performs `which` operation on adjacent values of
/// `left` and `right` SIMD vectors and stores the result in `dest`.
fn horizontal_bin_op<'tcx>(
Expand Down
4 changes: 2 additions & 2 deletions src/tools/miri/src/shims/x86/sse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
let [op] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
let (op, _) = this.operand_to_simd(op)?;

let op = this.read_scalar(&this.project_index(&op, 0)?)?.to_f32()?;
let op = this.read_immediate(&this.project_index(&op, 0)?)?;

let rnd = match unprefixed_name {
// "current SSE rounding mode", assume nearest
Expand All @@ -180,7 +180,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
_ => unreachable!(),
};

let res = this.float_to_int_checked(op, dest.layout, rnd).unwrap_or_else(|| {
let res = this.float_to_int_checked(&op, dest.layout, rnd)?.unwrap_or_else(|| {
// Fallback to minimum acording to SSE semantics.
ImmTy::from_int(dest.layout.size.signed_int_min(), dest.layout)
});
Expand Down
Loading

0 comments on commit 44aceb5

Please sign in to comment.