]> git.lizzy.rs Git - rust.git/commitdiff
implement packed struct field access
authorOliver Schneider <git-spam-no-reply9815368754983@oli-obk.de>
Mon, 30 Jan 2017 08:44:52 +0000 (09:44 +0100)
committerOliver Schneider <git-spam-no-reply9815368754983@oli-obk.de>
Mon, 30 Jan 2017 08:44:52 +0000 (09:44 +0100)
src/eval_context.rs
src/lib.rs
src/lvalue.rs
src/memory.rs
src/step.rs
tests/run-pass/packed_struct.rs [new file with mode: 0644]

index ce44e34f9ed9e0c102509b1ed1da6098d1b61e8d..87f4cb9e5dd3cf680b8c63606cc13439886f47fd 100644 (file)
@@ -443,6 +443,10 @@ pub(super) fn eval_rvalue_into_lvalue(
                 match *dest_layout {
                     Univariant { ref variant, .. } => {
                         let offsets = variant.offsets.iter().map(|s| s.bytes());
+                        if variant.packed {
+                            let ptr = self.force_allocation(dest)?.to_ptr_and_extra().0;
+                            self.memory.mark_packed(ptr, variant.stride().bytes());
+                        }
                         self.assign_fields(dest, offsets, operands)?;
                     }
 
@@ -460,6 +464,10 @@ pub(super) fn eval_rvalue_into_lvalue(
                         if let mir::AggregateKind::Adt(adt_def, variant, _, _) = *kind {
                             let discr_val = adt_def.variants[variant].disr_val.to_u128_unchecked();
                             let discr_size = discr.size().bytes();
+                            if variants[variant].packed {
+                                let ptr = self.force_allocation(dest)?.to_ptr_and_extra().0;
+                                self.memory.mark_packed(ptr, variants[variant].stride().bytes());
+                            }
 
                             self.assign_discr_and_fields(
                                 dest,
@@ -496,6 +504,10 @@ pub(super) fn eval_rvalue_into_lvalue(
 
                     StructWrappedNullablePointer { nndiscr, ref nonnull, ref discrfield, .. } => {
                         if let mir::AggregateKind::Adt(_, variant, _, _) = *kind {
+                            if nonnull.packed {
+                                let ptr = self.force_allocation(dest)?.to_ptr_and_extra().0;
+                                self.memory.mark_packed(ptr, nonnull.stride().bytes());
+                            }
                             if nndiscr == variant as u64 {
                                 let offsets = nonnull.offsets.iter().map(|s| s.bytes());
                                 self.assign_fields(dest, offsets, operands)?;
index 47ed4fd0cebfb2559c08b7a93cd4705d8283eed8..2908e31a4931ca6f5919b87aa15c56664e96af2a 100644 (file)
@@ -5,6 +5,7 @@
     i128_type,
     pub_restricted,
     rustc_private,
+    collections_bound,
 )]
 
 // From rustc.
index 7bd6cc3d989e15c966c7f39af7b3c1df27b843bc..64c01cc2fc4ea78d264dc837473f7f5783819ed2 100644 (file)
@@ -173,13 +173,15 @@ fn eval_lvalue_projection(
                 let field = field.index();
 
                 use rustc::ty::layout::Layout::*;
-                let offset = match *base_layout {
-                    Univariant { ref variant, .. } => variant.offsets[field],
+                let (offset, packed) = match *base_layout {
+                    Univariant { ref variant, .. } => {
+                        (variant.offsets[field], variant.packed)
+                    },
 
                     General { ref variants, .. } => {
                         if let LvalueExtra::DowncastVariant(variant_idx) = base_extra {
                             // +1 for the discriminant, which is field 0
-                            variants[variant_idx].offsets[field + 1]
+                            (variants[variant_idx].offsets[field + 1], variants[variant_idx].packed)
                         } else {
                             bug!("field access on enum had no variant index");
                         }
@@ -191,7 +193,7 @@ fn eval_lvalue_projection(
                     }
 
                     StructWrappedNullablePointer { ref nonnull, .. } => {
-                        nonnull.offsets[field]
+                        (nonnull.offsets[field], nonnull.packed)
                     }
 
                     UntaggedUnion { .. } => return Ok(base),
@@ -200,12 +202,17 @@ fn eval_lvalue_projection(
                         let field = field as u64;
                         assert!(field < count);
                         let elem_size = element.size(&self.tcx.data_layout).bytes();
-                        Size::from_bytes(field * elem_size)
+                        (Size::from_bytes(field * elem_size), false)
                     }
 
                     _ => bug!("field access on non-product type: {:?}", base_layout),
                 };
 
+                if packed {
+                    let size = self.type_size(field_ty)?.expect("packed struct must be sized");
+                    self.memory.mark_packed(base_ptr, size);
+                }
+
                 let ptr = base_ptr.offset(offset.bytes());
                 let extra = if self.type_is_sized(field_ty) {
                     LvalueExtra::None
index 79a1af50ad1f22276455df3637e04568c6ddc2b6..43bb0e23ba1108fe7e2f22ad1486c721748158c2 100644 (file)
@@ -1,5 +1,5 @@
 use byteorder::{ReadBytesExt, WriteBytesExt, LittleEndian, BigEndian};
-use std::collections::{btree_map, BTreeMap, HashMap, HashSet, VecDeque};
+use std::collections::{btree_map, BTreeMap, HashMap, HashSet, VecDeque, BTreeSet};
 use std::{fmt, iter, ptr, mem, io};
 
 use rustc::hir::def_id::DefId;
@@ -120,6 +120,8 @@ pub struct Memory<'a, 'tcx> {
     function_alloc_cache: HashMap<FunctionDefinition<'tcx>, AllocId>,
     next_id: AllocId,
     pub layout: &'a TargetDataLayout,
+    /// List of memory regions containing packed structures
+    packed: BTreeSet<Entry>,
 }
 
 const ZST_ALLOC_ID: AllocId = AllocId(0);
@@ -135,6 +137,7 @@ pub fn new(layout: &'a TargetDataLayout, max_memory: u64) -> Self {
             layout,
             memory_size: max_memory,
             memory_usage: 0,
+            packed: BTreeSet::new(),
         }
     }
 
@@ -280,8 +283,16 @@ pub fn endianess(&self) -> layout::Endian {
         self.layout.endian
     }
 
-    pub fn check_align(&self, ptr: Pointer, align: u64) -> EvalResult<'tcx, ()> {
+    pub fn check_align(&self, ptr: Pointer, align: u64, len: u64) -> EvalResult<'tcx, ()> {
         let alloc = self.get(ptr.alloc_id)?;
+        // check whether the memory was marked as aligned
+        let start = Entry(ptr.alloc_id, 0, ptr.offset + len);
+        let end = Entry(ptr.alloc_id, ptr.offset + len, 0);
+        for &Entry(_, start, end) in self.packed.range(start..end) {
+            if start <= ptr.offset && (ptr.offset + len) <= end {
+                return Ok(());
+            }
+        }
         if alloc.align < align {
             return Err(EvalError::AlignmentCheckFailed {
                 has: alloc.align,
@@ -297,8 +308,19 @@ pub fn check_align(&self, ptr: Pointer, align: u64) -> EvalResult<'tcx, ()> {
             })
         }
     }
+
+    pub(crate) fn mark_packed(&mut self, ptr: Pointer, len: u64) {
+        self.packed.insert(Entry(ptr.alloc_id, ptr.offset, ptr.offset + len));
+    }
+
+    pub(crate) fn clear_packed(&mut self) {
+        self.packed.clear();
+    }
 }
 
+#[derive(Eq, PartialEq, Ord, PartialOrd)]
+struct Entry(AllocId, u64, u64);
+
 /// Allocation accessors
 impl<'a, 'tcx> Memory<'a, 'tcx> {
     pub fn get(&self, id: AllocId) -> EvalResult<'tcx, &Allocation> {
@@ -451,7 +473,7 @@ fn get_bytes(&self, ptr: Pointer, size: u64, align: u64) -> EvalResult<'tcx, &[u
         if size == 0 {
             return Ok(&[]);
         }
-        self.check_align(ptr, align)?;
+        self.check_align(ptr, align, size)?;
         if self.relocations(ptr, size)?.count() != 0 {
             return Err(EvalError::ReadPointerAsBytes);
         }
@@ -463,7 +485,7 @@ fn get_bytes_mut(&mut self, ptr: Pointer, size: u64, align: u64) -> EvalResult<'
         if size == 0 {
             return Ok(&mut []);
         }
-        self.check_align(ptr, align)?;
+        self.check_align(ptr, align, size)?;
         self.clear_relocations(ptr, size)?;
         self.mark_definedness(ptr, size, true)?;
         self.get_bytes_unchecked_mut(ptr, size)
index 9b4914f6831b10a66e78471ea76fc573bf05216a..53a3aefed849f8ac41a7c95050cbd35eb2f845d8 100644 (file)
@@ -28,6 +28,7 @@ pub fn inc_step_counter_and_check_limit(&mut self, n: u64) -> EvalResult<'tcx, (
 
     /// Returns true as long as there are more things to do.
     pub fn step(&mut self) -> EvalResult<'tcx, bool> {
+        self.memory.clear_packed();
         self.inc_step_counter_and_check_limit(1)?;
         if self.stack.is_empty() {
             return Ok(false);
diff --git a/tests/run-pass/packed_struct.rs b/tests/run-pass/packed_struct.rs
new file mode 100644 (file)
index 0000000..93aecbb
--- /dev/null
@@ -0,0 +1,14 @@
+#[repr(packed)]
+struct S {
+    a: i32,
+    b: i64,
+}
+
+fn main() {
+    let x = S {
+        a: 42,
+        b: 99,
+    };
+    assert_eq!(x.a, 42);
+    assert_eq!(x.b, 99);
+}