Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor float_to_int_checked to remove its generic parameter and reduce code duplication a bit #3185

Merged
merged 1 commit into from
Nov 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 57 additions & 47 deletions 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/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/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/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(_)));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
assert!(matches!(dest.layout.field(this, 0).ty.kind(), ty::Int(_)));
// 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)
RalfJung marked this conversation as resolved.
Show resolved Hide resolved
});
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/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