about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2019-06-11 08:15:12 +0000
committerbors <bors@rust-lang.org>2019-06-11 08:15:12 +0000
commit912d22e36965d3c9f6d7f14ca18657182aa1fe54 (patch)
tree3f434240e7a09d49bcd6b88b084ddea93b71da6c
parent9d9c7ad323f95612d933106d65e3057ca9a0434f (diff)
parent8dfc8db235e205c60b56e0753996399a6f66f3e1 (diff)
downloadrust-912d22e36965d3c9f6d7f14ca18657182aa1fe54.tar.gz
rust-912d22e36965d3c9f6d7f14ca18657182aa1fe54.zip
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
-rw-r--r--src/librustc/mir/interpret/value.rs59
-rw-r--r--src/librustc/ty/sty.rs8
-rw-r--r--src/librustc_mir/hair/constant.rs11
-rw-r--r--src/librustc_mir/interpret/cast.rs97
-rw-r--r--src/librustc_mir/interpret/operator.rs103
5 files changed, 154 insertions, 124 deletions
diff --git a/src/librustc/mir/interpret/value.rs b/src/librustc/mir/interpret/value.rs
index 454a4e2111a..23c37676f36 100644
--- a/src/librustc/mir/interpret/value.rs
+++ b/src/librustc/mir/interpret/value.rs
@@ -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) {
@@ -280,6 +295,26 @@ impl<'tcx, Tag> Scalar<Tag> {
     }
 
     #[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();
         // `into` performed sign extension, we have to truncate
@@ -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()
     }
 
diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs
index ddc4bd3f9f6..5cc46b21fca 100644
--- a/src/librustc/ty/sty.rs
+++ b/src/librustc/ty/sty.rs
@@ -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,
diff --git a/src/librustc_mir/hair/constant.rs b/src/librustc_mir/hair/constant.rs
index b5604f4cb0f..37a2e79dae9 100644
--- a/src/librustc_mir/hair/constant.rs
+++ b/src/librustc_mir/hair/constant.rs
@@ -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))
 }
diff --git a/src/librustc_mir/interpret/cast.rs b/src/librustc_mir/interpret/cast.rs
index 5bc8b931ae8..6392e0996ae 100644
--- a/src/librustc_mir/interpret/cast.rs
+++ b/src/librustc_mir/interpret/cast.rs
@@ -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),
         }
     }
 
diff --git a/src/librustc_mir/interpret/operator.rs b/src/librustc_mir/interpret/operator.rs
index 4f13eeb6fa4..db7da9359de 100644
--- a/src/librustc_mir/interpret/operator.rs
+++ b/src/librustc_mir/interpret/operator.rs
@@ -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());