Skip to content

Commit

Permalink
Auto merge of #61673 - RalfJung:miri-no-hard-float, r=eddyb,oli-obk
Browse files Browse the repository at this point in the history
Miri: convert to/from apfloat instead of host floats

Cc @oli-obk @eddyb
  • Loading branch information
bors committed Jun 11, 2019
2 parents 9d9c7ad + 8dfc8db commit 912d22e
Show file tree
Hide file tree
Showing 5 changed files with 154 additions and 124 deletions.
59 changes: 49 additions & 10 deletions src/librustc/mir/interpret/value.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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();
Expand All @@ -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]
Expand Down Expand Up @@ -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))
}
}

Expand Down Expand Up @@ -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()
}

Expand Down
8 changes: 8 additions & 0 deletions src/librustc/ty/sty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
11 changes: 5 additions & 6 deletions src/librustc_mir/hair/constant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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| {
Expand All @@ -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))
}
97 changes: 45 additions & 52 deletions src/librustc_mir/interpret/cast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down Expand Up @@ -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>,
Expand All @@ -135,23 +135,36 @@ 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),
}
}
}
}

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)
Expand All @@ -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 => {
Expand All @@ -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),
}
}

Expand Down
Loading

0 comments on commit 912d22e

Please sign in to comment.