Auto merge of #61673 - RalfJung:miri-no-hard-float, r=eddyb,oli-obk

Miri: convert to/from apfloat instead of host floats

Cc @oli-obk @eddyb
This commit is contained in:
bors 2019-06-11 08:15:12 +00:00
commit 912d22e369
5 changed files with 154 additions and 124 deletions

View File

@ -1,5 +1,6 @@
use std::fmt;
use rustc_macros::HashStable;
use rustc_apfloat::{Float, ieee::{Double, Single}};
use crate::ty::{Ty, InferConst, ParamConst, layout::{HasDataLayout, Size}, subst::SubstsRef};
use crate::ty::PlaceholderConst;
@ -131,6 +132,20 @@ impl<Tag> fmt::Display for Scalar<Tag> {
}
}
impl<Tag> From<Single> for Scalar<Tag> {
#[inline(always)]
fn from(f: Single) -> Self {
Scalar::from_f32(f)
}
}
impl<Tag> From<Double> for Scalar<Tag> {
#[inline(always)]
fn from(f: Double) -> Self {
Scalar::from_f64(f)
}
}
impl<'tcx> Scalar<()> {
#[inline(always)]
fn check_data(data: u128, size: u8) {
@ -279,6 +294,26 @@ impl<'tcx, Tag> Scalar<Tag> {
Scalar::Raw { data: i, size: size.bytes() as u8 }
}
#[inline]
pub fn from_u8(i: u8) -> Self {
Scalar::Raw { data: i as u128, size: 1 }
}
#[inline]
pub fn from_u16(i: u16) -> Self {
Scalar::Raw { data: i as u128, size: 2 }
}
#[inline]
pub fn from_u32(i: u32) -> Self {
Scalar::Raw { data: i as u128, size: 4 }
}
#[inline]
pub fn from_u64(i: u64) -> Self {
Scalar::Raw { data: i as u128, size: 8 }
}
#[inline]
pub fn from_int(i: impl Into<i128>, size: Size) -> Self {
let i = i.into();
@ -292,13 +327,15 @@ impl<'tcx, Tag> Scalar<Tag> {
}
#[inline]
pub fn from_f32(f: f32) -> Self {
Scalar::Raw { data: f.to_bits() as u128, size: 4 }
pub fn from_f32(f: Single) -> Self {
// We trust apfloat to give us properly truncated data.
Scalar::Raw { data: f.to_bits(), size: 4 }
}
#[inline]
pub fn from_f64(f: f64) -> Self {
Scalar::Raw { data: f.to_bits() as u128, size: 8 }
pub fn from_f64(f: Double) -> Self {
// We trust apfloat to give us properly truncated data.
Scalar::Raw { data: f.to_bits(), size: 8 }
}
#[inline]
@ -427,13 +464,15 @@ impl<'tcx, Tag> Scalar<Tag> {
}
#[inline]
pub fn to_f32(self) -> InterpResult<'static, f32> {
Ok(f32::from_bits(self.to_u32()?))
pub fn to_f32(self) -> InterpResult<'static, Single> {
// Going through `u32` to check size and truncation.
Ok(Single::from_bits(self.to_u32()? as u128))
}
#[inline]
pub fn to_f64(self) -> InterpResult<'static, f64> {
Ok(f64::from_bits(self.to_u64()?))
pub fn to_f64(self) -> InterpResult<'static, Double> {
// Going through `u64` to check size and truncation.
Ok(Double::from_bits(self.to_u64()? as u128))
}
}
@ -517,12 +556,12 @@ impl<'tcx, Tag> ScalarMaybeUndef<Tag> {
}
#[inline(always)]
pub fn to_f32(self) -> InterpResult<'tcx, f32> {
pub fn to_f32(self) -> InterpResult<'tcx, Single> {
self.not_undef()?.to_f32()
}
#[inline(always)]
pub fn to_f64(self) -> InterpResult<'tcx, f64> {
pub fn to_f64(self) -> InterpResult<'tcx, Double> {
self.not_undef()?.to_f64()
}

View File

@ -2035,6 +2035,14 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> {
}
}
#[inline]
pub fn is_fn_ptr(&self) -> bool {
match self.sty {
FnPtr(_) => true,
_ => false,
}
}
pub fn is_impl_trait(&self) -> bool {
match self.sty {
Opaque(..) => true,

View File

@ -69,8 +69,7 @@ fn parse_float<'tcx>(
) -> Result<ConstValue<'tcx>, ()> {
let num = num.as_str();
use rustc_apfloat::ieee::{Single, Double};
use rustc_apfloat::Float;
let (data, size) = match fty {
let scalar = match fty {
ast::FloatTy::F32 => {
num.parse::<f32>().map_err(|_| ())?;
let mut f = num.parse::<Single>().unwrap_or_else(|e| {
@ -79,19 +78,19 @@ fn parse_float<'tcx>(
if neg {
f = -f;
}
(f.to_bits(), 4)
Scalar::from_f32(f)
}
ast::FloatTy::F64 => {
num.parse::<f64>().map_err(|_| ())?;
let mut f = num.parse::<Double>().unwrap_or_else(|e| {
panic!("apfloat::ieee::Single failed to parse `{}`: {:?}", num, e)
panic!("apfloat::ieee::Double failed to parse `{}`: {:?}", num, e)
});
if neg {
f = -f;
}
(f.to_bits(), 8)
Scalar::from_f64(f)
}
};
Ok(ConstValue::Scalar(Scalar::from_uint(data, Size::from_bytes(size))))
Ok(ConstValue::Scalar(scalar))
}

View File

@ -5,11 +5,11 @@ use syntax::ast::{FloatTy, IntTy, UintTy};
use syntax::symbol::sym;
use rustc_apfloat::ieee::{Single, Double};
use rustc_apfloat::{Float, FloatConvert};
use rustc::mir::interpret::{
Scalar, InterpResult, Pointer, PointerArithmetic, InterpError,
};
use rustc::mir::CastKind;
use rustc_apfloat::Float;
use super::{InterpretCx, Machine, PlaceTy, OpTy, Immediate};
@ -126,7 +126,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M>
Ok(())
}
pub(super) fn cast_scalar(
fn cast_scalar(
&self,
val: Scalar<M::PointerTag>,
src_layout: TyLayout<'tcx>,
@ -135,12 +135,24 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M>
use rustc::ty::TyKind::*;
trace!("Casting {:?}: {:?} to {:?}", val, src_layout.ty, dest_layout.ty);
match val.to_bits_or_ptr(src_layout.size, self) {
Err(ptr) => self.cast_from_ptr(ptr, dest_layout.ty),
Ok(data) => {
match src_layout.ty.sty {
Float(fty) => self.cast_from_float(data, fty, dest_layout.ty),
_ => self.cast_from_int(data, src_layout, dest_layout),
match src_layout.ty.sty {
// Floating point
Float(FloatTy::F32) => self.cast_from_float(val.to_f32()?, dest_layout.ty),
Float(FloatTy::F64) => self.cast_from_float(val.to_f64()?, dest_layout.ty),
// Integer(-like), including fn ptr casts and casts from enums that
// are represented as integers (this excludes univariant enums, which
// are handled in `cast` directly).
_ => {
assert!(
src_layout.ty.is_bool() || src_layout.ty.is_char() ||
src_layout.ty.is_enum() || src_layout.ty.is_integral() ||
src_layout.ty.is_unsafe_ptr() || src_layout.ty.is_fn_ptr() ||
src_layout.ty.is_region_ptr(),
"Unexpected cast from type {:?}", src_layout.ty
);
match val.to_bits_or_ptr(src_layout.size, self) {
Err(ptr) => self.cast_from_ptr(ptr, dest_layout.ty),
Ok(data) => self.cast_from_int(data, src_layout, dest_layout),
}
}
}
@ -148,10 +160,11 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M>
fn cast_from_int(
&self,
v: u128,
v: u128, // raw bits
src_layout: TyLayout<'tcx>,
dest_layout: TyLayout<'tcx>,
) -> InterpResult<'tcx, Scalar<M::PointerTag>> {
// Let's make sure v is sign-extended *if* it has a signed type.
let signed = src_layout.abi.is_signed();
let v = if signed {
self.sign_extend(v, src_layout)
@ -166,21 +179,17 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M>
Ok(Scalar::from_uint(v, dest_layout.size))
}
Float(FloatTy::F32) if signed => Ok(Scalar::from_uint(
Single::from_i128(v as i128).value.to_bits(),
Size::from_bits(32)
Float(FloatTy::F32) if signed => Ok(Scalar::from_f32(
Single::from_i128(v as i128).value
)),
Float(FloatTy::F64) if signed => Ok(Scalar::from_uint(
Double::from_i128(v as i128).value.to_bits(),
Size::from_bits(64)
Float(FloatTy::F64) if signed => Ok(Scalar::from_f64(
Double::from_i128(v as i128).value
)),
Float(FloatTy::F32) => Ok(Scalar::from_uint(
Single::from_u128(v).value.to_bits(),
Size::from_bits(32)
Float(FloatTy::F32) => Ok(Scalar::from_f32(
Single::from_u128(v).value
)),
Float(FloatTy::F64) => Ok(Scalar::from_uint(
Double::from_u128(v).value.to_bits(),
Size::from_bits(64)
Float(FloatTy::F64) => Ok(Scalar::from_f64(
Double::from_u128(v).value
)),
Char => {
@ -194,52 +203,36 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M>
}
}
fn cast_from_float(
fn cast_from_float<F>(
&self,
bits: u128,
fty: FloatTy,
f: F,
dest_ty: Ty<'tcx>
) -> InterpResult<'tcx, Scalar<M::PointerTag>> {
) -> InterpResult<'tcx, Scalar<M::PointerTag>>
where F: Float + Into<Scalar<M::PointerTag>> + FloatConvert<Single> + FloatConvert<Double>
{
use rustc::ty::TyKind::*;
use rustc_apfloat::FloatConvert;
match dest_ty.sty {
// float -> uint
Uint(t) => {
let width = t.bit_width().unwrap_or_else(|| self.pointer_size().bits() as usize);
let v = match fty {
FloatTy::F32 => Single::from_bits(bits).to_u128(width).value,
FloatTy::F64 => Double::from_bits(bits).to_u128(width).value,
};
let v = f.to_u128(width).value;
// This should already fit the bit width
Ok(Scalar::from_uint(v, Size::from_bits(width as u64)))
},
// float -> int
Int(t) => {
let width = t.bit_width().unwrap_or_else(|| self.pointer_size().bits() as usize);
let v = match fty {
FloatTy::F32 => Single::from_bits(bits).to_i128(width).value,
FloatTy::F64 => Double::from_bits(bits).to_i128(width).value,
};
let v = f.to_i128(width).value;
Ok(Scalar::from_int(v, Size::from_bits(width as u64)))
},
// f64 -> f32
Float(FloatTy::F32) if fty == FloatTy::F64 => {
Ok(Scalar::from_uint(
Single::to_bits(Double::from_bits(bits).convert(&mut false).value),
Size::from_bits(32),
))
},
// f32 -> f64
Float(FloatTy::F64) if fty == FloatTy::F32 => {
Ok(Scalar::from_uint(
Double::to_bits(Single::from_bits(bits).convert(&mut false).value),
Size::from_bits(64),
))
},
// identity cast
Float(FloatTy:: F64) => Ok(Scalar::from_uint(bits, Size::from_bits(64))),
Float(FloatTy:: F32) => Ok(Scalar::from_uint(bits, Size::from_bits(32))),
_ => err!(Unimplemented(format!("float to {:?} cast", dest_ty))),
// float -> f32
Float(FloatTy::F32) =>
Ok(Scalar::from_f32(f.convert(&mut false).value)),
// float -> f64
Float(FloatTy::F64) =>
Ok(Scalar::from_f64(f.convert(&mut false).value)),
// That's it.
_ => bug!("invalid float to {:?} cast", dest_ty),
}
}

View File

@ -1,7 +1,6 @@
use rustc::mir;
use rustc::ty::{self, layout::{Size, TyLayout}};
use rustc::ty::{self, layout::TyLayout};
use syntax::ast::FloatTy;
use rustc_apfloat::ieee::{Double, Single};
use rustc_apfloat::Float;
use rustc::mir::interpret::{InterpResult, Scalar};
@ -43,7 +42,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M>
bin_op: mir::BinOp,
l: char,
r: char,
) -> InterpResult<'tcx, (Scalar<M::PointerTag>, bool)> {
) -> (Scalar<M::PointerTag>, bool) {
use rustc::mir::BinOp::*;
let res = match bin_op {
@ -55,7 +54,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M>
Ge => l >= r,
_ => bug!("Invalid operation on char: {:?}", bin_op),
};
return Ok((Scalar::from_bool(res), false));
return (Scalar::from_bool(res), false);
}
fn binary_bool_op(
@ -63,7 +62,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M>
bin_op: mir::BinOp,
l: bool,
r: bool,
) -> InterpResult<'tcx, (Scalar<M::PointerTag>, bool)> {
) -> (Scalar<M::PointerTag>, bool) {
use rustc::mir::BinOp::*;
let res = match bin_op {
@ -78,46 +77,32 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M>
BitXor => l ^ r,
_ => bug!("Invalid operation on bool: {:?}", bin_op),
};
return Ok((Scalar::from_bool(res), false));
return (Scalar::from_bool(res), false);
}
fn binary_float_op(
fn binary_float_op<F: Float + Into<Scalar<M::PointerTag>>>(
&self,
bin_op: mir::BinOp,
fty: FloatTy,
// passing in raw bits
l: u128,
r: u128,
) -> InterpResult<'tcx, (Scalar<M::PointerTag>, bool)> {
l: F,
r: F,
) -> (Scalar<M::PointerTag>, bool) {
use rustc::mir::BinOp::*;
macro_rules! float_math {
($ty:path, $size:expr) => {{
let l = <$ty>::from_bits(l);
let r = <$ty>::from_bits(r);
let bitify = |res: ::rustc_apfloat::StatusAnd<$ty>|
Scalar::from_uint(res.value.to_bits(), Size::from_bytes($size));
let val = match bin_op {
Eq => Scalar::from_bool(l == r),
Ne => Scalar::from_bool(l != r),
Lt => Scalar::from_bool(l < r),
Le => Scalar::from_bool(l <= r),
Gt => Scalar::from_bool(l > r),
Ge => Scalar::from_bool(l >= r),
Add => bitify(l + r),
Sub => bitify(l - r),
Mul => bitify(l * r),
Div => bitify(l / r),
Rem => bitify(l % r),
_ => bug!("invalid float op: `{:?}`", bin_op),
};
return Ok((val, false));
}};
}
match fty {
FloatTy::F32 => float_math!(Single, 4),
FloatTy::F64 => float_math!(Double, 8),
}
let val = match bin_op {
Eq => Scalar::from_bool(l == r),
Ne => Scalar::from_bool(l != r),
Lt => Scalar::from_bool(l < r),
Le => Scalar::from_bool(l <= r),
Gt => Scalar::from_bool(l > r),
Ge => Scalar::from_bool(l >= r),
Add => (l + r).value.into(),
Sub => (l - r).value.into(),
Mul => (l * r).value.into(),
Div => (l / r).value.into(),
Rem => (l % r).value.into(),
_ => bug!("invalid float op: `{:?}`", bin_op),
};
return (val, false);
}
fn binary_int_op(
@ -286,28 +271,35 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M>
match left.layout.ty.sty {
ty::Char => {
assert_eq!(left.layout.ty, right.layout.ty);
let left = left.to_scalar()?.to_char()?;
let right = right.to_scalar()?.to_char()?;
self.binary_char_op(bin_op, left, right)
let left = left.to_scalar()?;
let right = right.to_scalar()?;
Ok(self.binary_char_op(bin_op, left.to_char()?, right.to_char()?))
}
ty::Bool => {
assert_eq!(left.layout.ty, right.layout.ty);
let left = left.to_scalar()?.to_bool()?;
let right = right.to_scalar()?.to_bool()?;
self.binary_bool_op(bin_op, left, right)
let left = left.to_scalar()?;
let right = right.to_scalar()?;
Ok(self.binary_bool_op(bin_op, left.to_bool()?, right.to_bool()?))
}
ty::Float(fty) => {
assert_eq!(left.layout.ty, right.layout.ty);
let left = left.to_bits()?;
let right = right.to_bits()?;
self.binary_float_op(bin_op, fty, left, right)
let left = left.to_scalar()?;
let right = right.to_scalar()?;
Ok(match fty {
FloatTy::F32 => self.binary_float_op(bin_op, left.to_f32()?, right.to_f32()?),
FloatTy::F64 => self.binary_float_op(bin_op, left.to_f64()?, right.to_f64()?),
})
}
_ => {
// Must be integer(-like) types. Don't forget about == on fn pointers.
assert!(left.layout.ty.is_integral() || left.layout.ty.is_unsafe_ptr() ||
left.layout.ty.is_fn());
assert!(right.layout.ty.is_integral() || right.layout.ty.is_unsafe_ptr() ||
right.layout.ty.is_fn());
assert!(
left.layout.ty.is_integral() ||
left.layout.ty.is_unsafe_ptr() || left.layout.ty.is_fn_ptr(),
"Unexpected LHS type {:?} for BinOp {:?}", left.layout.ty, bin_op);
assert!(
right.layout.ty.is_integral() ||
right.layout.ty.is_unsafe_ptr() || right.layout.ty.is_fn_ptr(),
"Unexpected RHS type {:?} for BinOp {:?}", right.layout.ty, bin_op);
// Handle operations that support pointer values
if left.to_scalar_ptr()?.is_ptr() ||
@ -346,13 +338,12 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M>
Ok(Scalar::from_bool(res))
}
ty::Float(fty) => {
let val = val.to_bits(layout.size)?;
let res = match (un_op, fty) {
(Neg, FloatTy::F32) => Single::to_bits(-Single::from_bits(val)),
(Neg, FloatTy::F64) => Double::to_bits(-Double::from_bits(val)),
(Neg, FloatTy::F32) => Scalar::from_f32(-val.to_f32()?),
(Neg, FloatTy::F64) => Scalar::from_f64(-val.to_f64()?),
_ => bug!("Invalid float op {:?}", un_op)
};
Ok(Scalar::from_uint(res, layout.size))
Ok(res)
}
_ => {
assert!(layout.ty.is_integral());