about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorRalf Jung <post@ralfj.de>2017-07-26 23:43:13 -0700
committerRalf Jung <post@ralfj.de>2017-07-26 23:45:22 -0700
commit4672cb7bde348a3096bf1e6125d3cff98bf52b4d (patch)
tree09714eb63933ba0d82be5ac86d9dccd181a9ddb8 /src
parentf906c5458c43d0666dd9af37f44cd46a190f0506 (diff)
downloadrust-4672cb7bde348a3096bf1e6125d3cff98bf52b4d.tar.gz
rust-4672cb7bde348a3096bf1e6125d3cff98bf52b4d.zip
make force_allocation handle packed ByValPair
Diffstat (limited to 'src')
-rw-r--r--src/librustc_mir/interpret/eval_context.rs75
-rw-r--r--src/librustc_mir/interpret/lvalue.rs2
-rw-r--r--src/librustc_mir/interpret/memory.rs12
-rw-r--r--src/librustc_mir/interpret/step.rs10
-rw-r--r--src/librustc_mir/interpret/terminator/mod.rs13
5 files changed, 71 insertions, 41 deletions
diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs
index 331ae7e248b..5913ff168fc 100644
--- a/src/librustc_mir/interpret/eval_context.rs
+++ b/src/librustc_mir/interpret/eval_context.rs
@@ -609,7 +609,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
                                     let operand_ty = self.operand_ty(operand);
                                     assert_eq!(self.type_size(operand_ty)?, Some(0));
                                 }
-                                let (offset, ty) = self.nonnull_offset_and_ty(dest_ty, nndiscr, discrfield)?;
+                                let (offset, ty, _packed) = self.nonnull_offset_and_ty(dest_ty, nndiscr, discrfield)?;
+                                // TODO: The packed flag is ignored
 
                                 // FIXME(solson)
                                 let dest = self.force_allocation(dest)?.to_ptr()?;
@@ -702,7 +703,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
                     LvalueExtra::DowncastVariant(..) =>
                         bug!("attempted to take a reference to an enum downcast lvalue"),
                 };
-
                 self.write_value(val, dest, dest_ty)?;
             }
 
@@ -826,7 +826,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
         ty: Ty<'tcx>,
         nndiscr: u64,
         discrfield: &[u32],
-    ) -> EvalResult<'tcx, (Size, Ty<'tcx>)> {
+    ) -> EvalResult<'tcx, (Size, Ty<'tcx>, bool)> {
         // Skip the constant 0 at the start meant for LLVM GEP and the outer non-null variant
         let path = discrfield.iter().skip(2).map(|&i| i as usize);
 
@@ -849,16 +849,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
         mut offset: Size,
         mut ty: Ty<'tcx>,
         path: I,
-    ) -> EvalResult<'tcx, (Size, Ty<'tcx>)> {
+    ) -> EvalResult<'tcx, (Size, Ty<'tcx>, bool)> {
         // Skip the initial 0 intended for LLVM GEP.
+        let mut packed = false;
         for field_index in path {
             let field_offset = self.get_field_offset(ty, field_index)?;
             trace!("field_path_offset_and_ty: {}, {}, {:?}, {:?}", field_index, ty, field_offset, offset);
-            ty = self.get_field_ty(ty, field_index)?;
+            let field_ty = self.get_field_ty(ty, field_index)?;
+            ty = field_ty.0;
+            packed = packed || field_ty.1;
             offset = offset.checked_add(field_offset, &self.tcx.data_layout).unwrap();
         }
 
-        Ok((offset, ty))
+        Ok((offset, ty, packed))
     }
     fn get_fat_field(&self, pointee_ty: Ty<'tcx>, field_index: usize) -> EvalResult<'tcx, Ty<'tcx>> {
         match (field_index, &self.tcx.struct_tail(pointee_ty).sty) {
@@ -870,33 +873,46 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
         }
     }
 
-    pub fn get_field_ty(&self, ty: Ty<'tcx>, field_index: usize) -> EvalResult<'tcx, Ty<'tcx>> {
+    /// Returns the field type and whether the field is packed
+    pub fn get_field_ty(&self, ty: Ty<'tcx>, field_index: usize) -> EvalResult<'tcx, (Ty<'tcx>, bool)> {
         match ty.sty {
-            ty::TyAdt(adt_def, _) if adt_def.is_box() => self.get_fat_field(ty.boxed_ty(), field_index),
+            ty::TyAdt(adt_def, _) if adt_def.is_box() =>
+                Ok((self.get_fat_field(ty.boxed_ty(), field_index)?, false)),
             ty::TyAdt(adt_def, substs) if adt_def.is_enum() => {
                 use rustc::ty::layout::Layout::*;
                 match *self.type_layout(ty)? {
-                    RawNullablePointer { nndiscr, .. } |
-                    StructWrappedNullablePointer { nndiscr, .. } => Ok(adt_def.variants[nndiscr as usize].fields[field_index].ty(self.tcx, substs)),
+                    RawNullablePointer { nndiscr, .. } =>
+                        Ok((adt_def.variants[nndiscr as usize].fields[field_index].ty(self.tcx, substs), false)),
+                    StructWrappedNullablePointer { nndiscr, ref nonnull, .. } => {
+                        let ty = adt_def.variants[nndiscr as usize].fields[field_index].ty(self.tcx, substs);
+                        Ok((ty, nonnull.packed))
+                    },
                     _ => Err(EvalError::Unimplemented(format!("get_field_ty can't handle enum type: {:?}, {:?}", ty, ty.sty))),
                 }
             }
             ty::TyAdt(adt_def, substs) => {
-                Ok(adt_def.struct_variant().fields[field_index].ty(self.tcx, substs))
+                let variant_def = adt_def.struct_variant();
+                use rustc::ty::layout::Layout::*;
+                match *self.type_layout(ty)? {
+                    Univariant { ref variant, .. } =>
+                        Ok((variant_def.fields[field_index].ty(self.tcx, substs), variant.packed)),
+                    _ => Err(EvalError::Unimplemented(format!("get_field_ty can't handle struct type: {:?}, {:?}", ty, ty.sty))),
+                }
             }
 
-            ty::TyTuple(fields, _) => Ok(fields[field_index]),
+            ty::TyTuple(fields, _) => Ok((fields[field_index], false)),
 
             ty::TyRef(_, ref tam) |
-            ty::TyRawPtr(ref tam) => self.get_fat_field(tam.ty, field_index),
+            ty::TyRawPtr(ref tam) => Ok((self.get_fat_field(tam.ty, field_index)?, false)),
 
-            ty::TyArray(ref inner, _) => Ok(inner),
+            ty::TyArray(ref inner, _) => Ok((inner, false)),
 
             _ => Err(EvalError::Unimplemented(format!("can't handle type: {:?}, {:?}", ty, ty.sty))),
         }
     }
 
     fn get_field_offset(&self, ty: Ty<'tcx>, field_index: usize) -> EvalResult<'tcx, Size> {
+        // Also see lvalue_field in lvalue.rs, which handles more cases but needs an actual value at the given type
         let layout = self.type_layout(ty)?;
 
         use rustc::ty::layout::Layout::*;
@@ -1236,20 +1252,28 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
         ptr: MemoryPointer,
         mut ty: Ty<'tcx>
     ) -> EvalResult<'tcx> {
+        let mut packed = false;
         while self.get_field_count(ty)? == 1 {
-            ty = self.get_field_ty(ty, 0)?;
+            let field = self.get_field_ty(ty, 0)?;
+            ty = field.0;
+            packed = packed || field.1;
         }
         assert_eq!(self.get_field_count(ty)?, 2);
-        let field_0 = self.get_field_offset(ty, 0)?.bytes();
-        let field_1 = self.get_field_offset(ty, 1)?.bytes();
+        let field_0 = self.get_field_offset(ty, 0)?;
+        let field_1 = self.get_field_offset(ty, 1)?;
         let field_0_ty = self.get_field_ty(ty, 0)?;
         let field_1_ty = self.get_field_ty(ty, 1)?;
-        let field_0_size = self.type_size(field_0_ty)?.expect("pair element type must be sized");
-        let field_1_size = self.type_size(field_1_ty)?.expect("pair element type must be sized");
-        let field_0_ptr = ptr.offset(field_0, &self)?.into();
-        let field_1_ptr = ptr.offset(field_1, &self)?.into();
-        self.memory.write_primval(field_0_ptr, a, field_0_size)?;
-        self.memory.write_primval(field_1_ptr, b, field_1_size)?;
+        // The .1 components say whether the field is packed
+        assert_eq!(field_0_ty.1, field_1_ty.1, "the two fields must agree on being packed");
+        packed = packed || field_0_ty.1;
+        let field_0_size = self.type_size(field_0_ty.0)?.expect("pair element type must be sized");
+        let field_1_size = self.type_size(field_1_ty.0)?.expect("pair element type must be sized");
+        let field_0_ptr = ptr.offset(field_0.bytes(), &self)?.into();
+        let field_1_ptr = ptr.offset(field_1.bytes(), &self)?.into();
+        self.write_maybe_aligned(!packed,
+            |ectx| ectx.memory.write_primval(field_0_ptr, a, field_0_size))?;
+        self.write_maybe_aligned(!packed,
+            |ectx| ectx.memory.write_primval(field_1_ptr, b, field_1_size))?;
         Ok(())
     }
 
@@ -1529,8 +1553,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
                     return self.unsize_into_ptr(src, src_ty, dest, dest_ty, src_ty.boxed_ty(), dest_ty.boxed_ty());
                 }
                 if self.ty_to_primval_kind(src_ty).is_ok() {
-                    let sty = self.get_field_ty(src_ty, 0)?;
-                    let dty = self.get_field_ty(dest_ty, 0)?;
+                    // TODO: We ignore the packed flag here
+                    let sty = self.get_field_ty(src_ty, 0)?.0;
+                    let dty = self.get_field_ty(dest_ty, 0)?.0;
                     return self.unsize_into(src, sty, dest, dty);
                 }
                 // unsizing of generic struct with pointer fields
diff --git a/src/librustc_mir/interpret/lvalue.rs b/src/librustc_mir/interpret/lvalue.rs
index e4ab3d90a5c..da357a6d1e7 100644
--- a/src/librustc_mir/interpret/lvalue.rs
+++ b/src/librustc_mir/interpret/lvalue.rs
@@ -295,6 +295,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
 
             _ => bug!("field access on non-product type: {:?}", base_layout),
         };
+        //trace!("Field {} of {:?} is at offset {}{}", field_index, base_ty, offset.bytes(),
+        //    if packed { " (packed)" } else { "" });
 
         // Do not allocate in trivial cases
         let (base_ptr, base_extra, aligned) = match base {
diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs
index 461fced3c60..591f5dc7fe8 100644
--- a/src/librustc_mir/interpret/memory.rs
+++ b/src/librustc_mir/interpret/memory.rs
@@ -1402,20 +1402,20 @@ pub(crate) trait HasMemory<'a, 'tcx> {
     fn read_maybe_aligned<F, T>(&mut self, aligned: bool, f: F) -> EvalResult<'tcx, T>
         where F: FnOnce(&mut Self) -> EvalResult<'tcx, T>
     {
-        assert!(self.memory_mut().reads_are_aligned, "Unaligned reads must not be nested");
-        self.memory_mut().reads_are_aligned = aligned;
+        let old = self.memory_mut().reads_are_aligned;
+        self.memory_mut().reads_are_aligned = old && aligned;
         let t = f(self);
-        self.memory_mut().reads_are_aligned = true;
+        self.memory_mut().reads_are_aligned = old;
         t
     }
 
     fn write_maybe_aligned<F, T>(&mut self, aligned: bool, f: F) -> EvalResult<'tcx, T>
         where F: FnOnce(&mut Self) -> EvalResult<'tcx, T>
     {
-        assert!(self.memory_mut().writes_are_aligned, "Unaligned writes must not be nested");
-        self.memory_mut().writes_are_aligned = aligned;
+        let old = self.memory_mut().writes_are_aligned;
+        self.memory_mut().writes_are_aligned = old && aligned;
         let t = f(self);
-        self.memory_mut().writes_are_aligned = true;
+        self.memory_mut().writes_are_aligned = old;
         t
     }
 }
diff --git a/src/librustc_mir/interpret/step.rs b/src/librustc_mir/interpret/step.rs
index 86e12323306..075fab36f64 100644
--- a/src/librustc_mir/interpret/step.rs
+++ b/src/librustc_mir/interpret/step.rs
@@ -11,12 +11,14 @@ use rustc::ty;
 use rustc::ty::layout::Layout;
 use rustc::ty::subst::Substs;
 
+use syntax::codemap::Span;
+use syntax::ast::Mutability;
+
 use error::{EvalResult, EvalError};
 use eval_context::{EvalContext, StackPopCleanup};
 use lvalue::{Global, GlobalId, Lvalue};
 use value::{Value, PrimVal};
-use syntax::codemap::Span;
-use syntax::ast::Mutability;
+use memory::HasMemory;
 
 impl<'a, 'tcx> EvalContext<'a, 'tcx> {
     pub fn inc_step_counter_and_check_limit(&mut self, n: u64) -> EvalResult<'tcx> {
@@ -101,12 +103,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
 
                     Layout::StructWrappedNullablePointer { nndiscr, ref discrfield, .. } => {
                         if variant_index as u64 != nndiscr {
-                            let (offset, ty) = self.nonnull_offset_and_ty(dest_ty, nndiscr, discrfield)?;
+                            let (offset, ty, packed) = self.nonnull_offset_and_ty(dest_ty, nndiscr, discrfield)?;
                             let nonnull = self.force_allocation(dest)?.to_ptr()?.offset(offset.bytes(), &self)?;
                             trace!("struct wrapped nullable pointer type: {}", ty);
                             // only the pointer part of a fat pointer is used for this space optimization
                             let discr_size = self.type_size(ty)?.expect("bad StructWrappedNullablePointer discrfield");
-                            self.memory.write_uint(nonnull, 0, discr_size)?;
+                            self.write_maybe_aligned(!packed, |ectx| ectx.memory.write_uint(nonnull, 0, discr_size))?;
                         }
                     },
 
diff --git a/src/librustc_mir/interpret/terminator/mod.rs b/src/librustc_mir/interpret/terminator/mod.rs
index 3ee6bab77e0..e5b6d371381 100644
--- a/src/librustc_mir/interpret/terminator/mod.rs
+++ b/src/librustc_mir/interpret/terminator/mod.rs
@@ -9,7 +9,7 @@ use syntax::abi::Abi;
 use error::{EvalError, EvalResult};
 use eval_context::{EvalContext, IntegerExt, StackPopCleanup, is_inhabited, self};
 use lvalue::Lvalue;
-use memory::{MemoryPointer, TlsKey, Kind};
+use memory::{MemoryPointer, TlsKey, Kind, HasMemory};
 use value::{PrimVal, Value};
 use rustc_data_structures::indexed_vec::Idx;
 use const_eval;
@@ -402,7 +402,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
                 let instance = self.memory.get_fn(fn_ptr.to_ptr()?)?;
                 let mut arg_operands = arg_operands.to_vec();
                 let ty = self.operand_ty(&arg_operands[0]);
-                let ty = self.get_field_ty(ty, 0)?;
+                let ty = self.get_field_ty(ty, 0)?.0; // TODO: packed flag is ignored
                 match arg_operands[0] {
                     mir::Operand::Consume(ref mut lval) => *lval = lval.clone().field(mir::Field::new(0), ty),
                     _ => bug!("virtual call first arg cannot be a constant"),
@@ -464,7 +464,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
         Ok(false)
     }
 
-    pub fn read_discriminant_value(&self, adt_ptr: MemoryPointer, adt_ty: Ty<'tcx>) -> EvalResult<'tcx, u128> {
+    pub fn read_discriminant_value(&mut self, adt_ptr: MemoryPointer, adt_ty: Ty<'tcx>) -> EvalResult<'tcx, u128> {
         use rustc::ty::layout::Layout::*;
         let adt_layout = self.type_layout(adt_ty)?;
         //trace!("read_discriminant_value {:#?}", adt_layout);
@@ -487,12 +487,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
             }
 
             StructWrappedNullablePointer { nndiscr, ref discrfield, .. } => {
-                let (offset, ty) = self.nonnull_offset_and_ty(adt_ty, nndiscr, discrfield)?;
-                let nonnull = adt_ptr.offset(offset.bytes(), self)?;
+                let (offset, ty, packed) = self.nonnull_offset_and_ty(adt_ty, nndiscr, discrfield)?;
+                let nonnull = adt_ptr.offset(offset.bytes(), &*self)?;
                 trace!("struct wrapped nullable pointer type: {}", ty);
                 // only the pointer part of a fat pointer is used for this space optimization
                 let discr_size = self.type_size(ty)?.expect("bad StructWrappedNullablePointer discrfield");
-                self.read_nonnull_discriminant_value(nonnull, nndiscr as u128, discr_size)?
+                self.read_maybe_aligned(!packed,
+                    |ectx| ectx.read_nonnull_discriminant_value(nonnull, nndiscr as u128, discr_size))?
             }
 
             // The discriminant_value intrinsic returns 0 for non-sum types.