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

implement float_to_int_unchecked #1325

Merged
merged 7 commits into from
Apr 18, 2020
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
72 changes: 70 additions & 2 deletions src/shims/intrinsics.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use std::iter;
use std::convert::TryFrom;

use rustc_ast::ast::FloatTy;
use rustc_middle::{mir, ty};
use rustc_apfloat::Float;
use rustc_target::abi::{Align, LayoutOf};
use rustc_apfloat::{Float, Round};
use rustc_target::abi::{Align, LayoutOf, Size};

use crate::*;

Expand Down Expand Up @@ -279,6 +280,22 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
this.write_scalar(Scalar::from_u64(f.powi(i).to_bits()), dest)?;
}

"float_to_int_unchecked" => {
let val = this.read_immediate(args[0])?;

let res = match val.layout.ty.kind {
ty::Float(FloatTy::F32) => {
this.float_to_int_unchecked(val.to_scalar()?.to_f32()?, dest.layout.ty)?
}
ty::Float(FloatTy::F64) => {
this.float_to_int_unchecked(val.to_scalar()?.to_f64()?, dest.layout.ty)?
}
_ => bug!("`float_to_int_unchecked` called with non-float input type {:?}", val.layout.ty),
};

this.write_scalar(res, dest)?;
}

// Atomic operations
#[rustfmt::skip]
| "atomic_load"
Expand Down Expand Up @@ -491,4 +508,55 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
this.go_to_block(ret);
Ok(())
}

fn float_to_int_unchecked<F>(
&self,
f: F,
dest_ty: ty::Ty<'tcx>,
) -> InterpResult<'tcx, Scalar<Tag>>
where
F: Float + Into<Scalar<Tag>>
{
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 width = t.bit_width().unwrap_or_else(|| this.pointer_size().bits());
let res = f.to_u128(usize::try_from(width).unwrap());
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::from_bits(width))
} else {
// `f` was not representable in this integer type.
throw_ub_format!(
"`float_to_int_unchecked` intrinsic called on {} which cannot be represented in target type `{:?}`",
f, dest_ty,
);
}
}
// Signed
ty::Int(t) => {
let width = t.bit_width().unwrap_or_else(|| this.pointer_size().bits());
let res = f.to_i128(usize::try_from(width).unwrap());
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::from_bits(width))
} else {
// `f` was not representable in this integer type.
throw_ub_format!(
"`float_to_int_unchecked` intrinsic called on {} which cannot be represented in target type `{:?}`",
f, dest_ty,
);
}
}
// Nothing else
_ => bug!("`float_to_int_unchecked` called with non-int output type {:?}", dest_ty),
})
}
}
3 changes: 1 addition & 2 deletions tests/compile-fail/intrinsics/copy_overlapping.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
//error-pattern: copy_nonoverlapping called on overlapping ranges
#![feature(intrinsics)]

// Directly call intrinsic to avoid debug assertions in libstd
Expand All @@ -11,6 +10,6 @@ fn main() {
unsafe {
let a = data.as_mut_ptr();
let b = a.wrapping_offset(1) as *mut _;
copy_nonoverlapping(a, b, 2);
copy_nonoverlapping(a, b, 2); //~ ERROR copy_nonoverlapping called on overlapping ranges
}
}
3 changes: 1 addition & 2 deletions tests/compile-fail/intrinsics/copy_unaligned.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
//error-pattern: accessing memory with alignment 1, but alignment 2 is required
#![feature(intrinsics)]

// Directly call intrinsic to avoid debug assertions in libstd
Expand All @@ -10,5 +9,5 @@ fn main() {
let mut data = [0u16; 8];
let ptr = (&mut data[0] as *mut u16 as *mut u8).wrapping_add(1) as *mut u16;
// Even copying 0 elements to something unaligned should error
unsafe { copy_nonoverlapping(&data[5], ptr, 0); }
unsafe { copy_nonoverlapping(&data[5], ptr, 0); } //~ ERROR accessing memory with alignment 1, but alignment 2 is required
}
10 changes: 10 additions & 0 deletions tests/compile-fail/intrinsics/float_to_int_32_inf1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#![feature(intrinsics)]

// Directly call intrinsic to avoid debug assertions in libstd
extern "rust-intrinsic" {
fn float_to_int_unchecked<Float: Copy, Int: Copy>(value: Float) -> Int;
}

fn main() {
unsafe { float_to_int_unchecked::<f32, i32>(f32::INFINITY); } //~ ERROR: cannot be represented in target type `i32`
}
10 changes: 10 additions & 0 deletions tests/compile-fail/intrinsics/float_to_int_32_infneg1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#![feature(intrinsics)]

// Directly call intrinsic to avoid debug assertions in libstd
extern "rust-intrinsic" {
fn float_to_int_unchecked<Float: Copy, Int: Copy>(value: Float) -> Int;
}

fn main() {
unsafe { float_to_int_unchecked::<f32, i32>(f32::NEG_INFINITY); } //~ ERROR: cannot be represented in target type `i32`
}
10 changes: 10 additions & 0 deletions tests/compile-fail/intrinsics/float_to_int_32_nan.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#![feature(intrinsics)]

// Directly call intrinsic to avoid debug assertions in libstd
extern "rust-intrinsic" {
fn float_to_int_unchecked<Float: Copy, Int: Copy>(value: Float) -> Int;
}

fn main() {
unsafe { float_to_int_unchecked::<f32, u32>(f32::NAN); } //~ ERROR: cannot be represented in target type `u32`
}
10 changes: 10 additions & 0 deletions tests/compile-fail/intrinsics/float_to_int_32_nanneg.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#![feature(intrinsics)]

// Directly call intrinsic to avoid debug assertions in libstd
extern "rust-intrinsic" {
fn float_to_int_unchecked<Float: Copy, Int: Copy>(value: Float) -> Int;
}

fn main() {
unsafe { float_to_int_unchecked::<f32, u32>(-f32::NAN); } //~ ERROR: cannot be represented in target type `u32`
}
10 changes: 10 additions & 0 deletions tests/compile-fail/intrinsics/float_to_int_32_neg.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#![feature(intrinsics)]

// Directly call intrinsic to avoid debug assertions in libstd
extern "rust-intrinsic" {
fn float_to_int_unchecked<Float: Copy, Int: Copy>(value: Float) -> Int;
}

fn main() {
unsafe { float_to_int_unchecked::<f32, u32>(-1.000000001f32); } //~ ERROR: cannot be represented in target type `u32`
}
10 changes: 10 additions & 0 deletions tests/compile-fail/intrinsics/float_to_int_32_too_big1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#![feature(intrinsics)]

// Directly call intrinsic to avoid debug assertions in libstd
extern "rust-intrinsic" {
fn float_to_int_unchecked<Float: Copy, Int: Copy>(value: Float) -> Int;
}

fn main() {
unsafe { float_to_int_unchecked::<f32, i32>(2147483648.0f32); } //~ ERROR: cannot be represented in target type `i32`
}
10 changes: 10 additions & 0 deletions tests/compile-fail/intrinsics/float_to_int_32_too_big2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#![feature(intrinsics)]

// Directly call intrinsic to avoid debug assertions in libstd
extern "rust-intrinsic" {
fn float_to_int_unchecked<Float: Copy, Int: Copy>(value: Float) -> Int;
}

fn main() {
unsafe { float_to_int_unchecked::<f32, u32>((u32::MAX-127) as f32); } //~ ERROR: cannot be represented in target type `u32`
}
10 changes: 10 additions & 0 deletions tests/compile-fail/intrinsics/float_to_int_32_too_small1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#![feature(intrinsics)]

// Directly call intrinsic to avoid debug assertions in libstd
extern "rust-intrinsic" {
fn float_to_int_unchecked<Float: Copy, Int: Copy>(value: Float) -> Int;
}

fn main() {
unsafe { float_to_int_unchecked::<f32, i32>(-2147483904.0f32); } //~ ERROR: cannot be represented in target type `i32`
}
10 changes: 10 additions & 0 deletions tests/compile-fail/intrinsics/float_to_int_64_inf1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#![feature(intrinsics)]

// Directly call intrinsic to avoid debug assertions in libstd
extern "rust-intrinsic" {
fn float_to_int_unchecked<Float: Copy, Int: Copy>(value: Float) -> Int;
}

fn main() {
unsafe { float_to_int_unchecked::<f64, u128>(f64::INFINITY); } //~ ERROR: cannot be represented in target type `u128`
}
10 changes: 10 additions & 0 deletions tests/compile-fail/intrinsics/float_to_int_64_infneg1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#![feature(intrinsics)]

// Directly call intrinsic to avoid debug assertions in libstd
extern "rust-intrinsic" {
fn float_to_int_unchecked<Float: Copy, Int: Copy>(value: Float) -> Int;
}

fn main() {
unsafe { float_to_int_unchecked::<f64, u128>(f64::NEG_INFINITY); } //~ ERROR: cannot be represented in target type `u128`
}
10 changes: 10 additions & 0 deletions tests/compile-fail/intrinsics/float_to_int_64_infneg2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#![feature(intrinsics)]

// Directly call intrinsic to avoid debug assertions in libstd
extern "rust-intrinsic" {
fn float_to_int_unchecked<Float: Copy, Int: Copy>(value: Float) -> Int;
}

fn main() {
unsafe { float_to_int_unchecked::<f64, i128>(f64::NEG_INFINITY); } //~ ERROR: cannot be represented in target type `i128`
}
10 changes: 10 additions & 0 deletions tests/compile-fail/intrinsics/float_to_int_64_nan.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#![feature(intrinsics)]

// Directly call intrinsic to avoid debug assertions in libstd
extern "rust-intrinsic" {
fn float_to_int_unchecked<Float: Copy, Int: Copy>(value: Float) -> Int;
}

fn main() {
unsafe { float_to_int_unchecked::<f64, u32>(f64::NAN); } //~ ERROR: cannot be represented in target type `u32`
}
10 changes: 10 additions & 0 deletions tests/compile-fail/intrinsics/float_to_int_64_neg.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#![feature(intrinsics)]

// Directly call intrinsic to avoid debug assertions in libstd
extern "rust-intrinsic" {
fn float_to_int_unchecked<Float: Copy, Int: Copy>(value: Float) -> Int;
}

fn main() {
unsafe { float_to_int_unchecked::<f64, u128>(-1.0000000000001f64); } //~ ERROR: cannot be represented in target type `u128`
}
10 changes: 10 additions & 0 deletions tests/compile-fail/intrinsics/float_to_int_64_too_big1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#![feature(intrinsics)]

// Directly call intrinsic to avoid debug assertions in libstd
extern "rust-intrinsic" {
fn float_to_int_unchecked<Float: Copy, Int: Copy>(value: Float) -> Int;
}

fn main() {
unsafe { float_to_int_unchecked::<f64, i32>(2147483648.0f64); } //~ ERROR: cannot be represented in target type `i32`
}
10 changes: 10 additions & 0 deletions tests/compile-fail/intrinsics/float_to_int_64_too_big2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#![feature(intrinsics)]

// Directly call intrinsic to avoid debug assertions in libstd
extern "rust-intrinsic" {
fn float_to_int_unchecked<Float: Copy, Int: Copy>(value: Float) -> Int;
}

fn main() {
unsafe { float_to_int_unchecked::<f64, i64>(9223372036854775808.0f64); } //~ ERROR: cannot be represented in target type `i64`
}
10 changes: 10 additions & 0 deletions tests/compile-fail/intrinsics/float_to_int_64_too_big3.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#![feature(intrinsics)]

// Directly call intrinsic to avoid debug assertions in libstd
extern "rust-intrinsic" {
fn float_to_int_unchecked<Float: Copy, Int: Copy>(value: Float) -> Int;
}

fn main() {
unsafe { float_to_int_unchecked::<f64, u64>(18446744073709551616.0f64); } //~ ERROR: cannot be represented in target type `u64`
}
10 changes: 10 additions & 0 deletions tests/compile-fail/intrinsics/float_to_int_64_too_big4.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#![feature(intrinsics)]

// Directly call intrinsic to avoid debug assertions in libstd
extern "rust-intrinsic" {
fn float_to_int_unchecked<Float: Copy, Int: Copy>(value: Float) -> Int;
}

fn main() {
unsafe { float_to_int_unchecked::<f64, u128>(u128::MAX as f64); } //~ ERROR: cannot be represented in target type `u128`
}
10 changes: 10 additions & 0 deletions tests/compile-fail/intrinsics/float_to_int_64_too_big5.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#![feature(intrinsics)]

// Directly call intrinsic to avoid debug assertions in libstd
extern "rust-intrinsic" {
fn float_to_int_unchecked<Float: Copy, Int: Copy>(value: Float) -> Int;
}

fn main() {
unsafe { float_to_int_unchecked::<f64, i128>(240282366920938463463374607431768211455.0f64); } //~ ERROR: cannot be represented in target type `i128`
}
10 changes: 10 additions & 0 deletions tests/compile-fail/intrinsics/float_to_int_64_too_small1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#![feature(intrinsics)]

// Directly call intrinsic to avoid debug assertions in libstd
extern "rust-intrinsic" {
fn float_to_int_unchecked<Float: Copy, Int: Copy>(value: Float) -> Int;
}

fn main() {
unsafe { float_to_int_unchecked::<f64, i32>(-2147483649.0f64); } //~ ERROR: cannot be represented in target type `i32`
}
10 changes: 10 additions & 0 deletions tests/compile-fail/intrinsics/float_to_int_64_too_small2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#![feature(intrinsics)]

// Directly call intrinsic to avoid debug assertions in libstd
extern "rust-intrinsic" {
fn float_to_int_unchecked<Float: Copy, Int: Copy>(value: Float) -> Int;
}

fn main() {
unsafe { float_to_int_unchecked::<f64, i64>(-9223372036854777856.0f64); } //~ ERROR: cannot be represented in target type `i64`
}
10 changes: 10 additions & 0 deletions tests/compile-fail/intrinsics/float_to_int_64_too_small3.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#![feature(intrinsics)]

// Directly call intrinsic to avoid debug assertions in libstd
extern "rust-intrinsic" {
fn float_to_int_unchecked<Float: Copy, Int: Copy>(value: Float) -> Int;
}

fn main() {
unsafe { float_to_int_unchecked::<f64, i128>(-240282366920938463463374607431768211455.0f64); } //~ ERROR: cannot be represented in target type `i128`
}