diff options
| author | Ralf Jung <post@ralfj.de> | 2018-11-14 16:00:52 +0100 |
|---|---|---|
| committer | Ralf Jung <post@ralfj.de> | 2018-11-25 10:49:43 +0100 |
| commit | 4c090fe310e32300bd6c6a0610f3f23366ea1445 (patch) | |
| tree | 78dc95a77e2a18faee155751a62901e4956c4211 | |
| parent | 37961dbd2d84a29be49007a5770b96e296f5f349 (diff) | |
| download | rust-4c090fe310e32300bd6c6a0610f3f23366ea1445.tar.gz rust-4c090fe310e32300bd6c6a0610f3f23366ea1445.zip | |
bring back MemoryExtra
| -rw-r--r-- | src/librustc/mir/interpret/allocation.rs | 250 | ||||
| -rw-r--r-- | src/librustc/ty/context.rs | 2 | ||||
| -rw-r--r-- | src/librustc_mir/const_eval.rs | 1 | ||||
| -rw-r--r-- | src/librustc_mir/interpret/machine.rs | 7 | ||||
| -rw-r--r-- | src/librustc_mir/interpret/memory.rs | 22 | ||||
| -rw-r--r-- | src/librustc_mir/interpret/place.rs | 4 |
6 files changed, 179 insertions, 107 deletions
diff --git a/src/librustc/mir/interpret/allocation.rs b/src/librustc/mir/interpret/allocation.rs index c612d6ad1bb..b1e92ef4b51 100644 --- a/src/librustc/mir/interpret/allocation.rs +++ b/src/librustc/mir/interpret/allocation.rs @@ -53,6 +53,94 @@ pub struct Allocation<Tag=(),Extra=()> { pub extra: Extra, } + +pub trait AllocationExtra<Tag, MemoryExtra>: ::std::fmt::Debug + Clone { + /// Hook to initialize the extra data when an allocation gets crated. + fn memory_allocated( + _size: Size, + _memory_extra: &MemoryExtra + ) -> EvalResult<'tcx, Self>; + + /// Hook for performing extra checks on a memory read access. + /// + /// Takes read-only access to the allocation so we can keep all the memory read + /// operations take `&self`. Use a `RefCell` in `AllocExtra` if you + /// need to mutate. + #[inline(always)] + fn memory_read( + _alloc: &Allocation<Tag, Self>, + _ptr: Pointer<Tag>, + _size: Size, + ) -> EvalResult<'tcx> { + Ok(()) + } + + /// Hook for performing extra checks on a memory write access. + #[inline(always)] + fn memory_written( + _alloc: &mut Allocation<Tag, Self>, + _ptr: Pointer<Tag>, + _size: Size, + ) -> EvalResult<'tcx> { + Ok(()) + } + + /// Hook for performing extra checks on a memory deallocation. + /// `size` will be the size of the allocation. + #[inline(always)] + fn memory_deallocated( + _alloc: &mut Allocation<Tag, Self>, + _ptr: Pointer<Tag>, + _size: Size, + ) -> EvalResult<'tcx> { + Ok(()) + } +} + +impl AllocationExtra<(), ()> for () { + #[inline(always)] + fn memory_allocated( + _size: Size, + _memory_extra: &() + ) -> EvalResult<'tcx, Self> { + Ok(()) + } +} + +impl<Tag, Extra> Allocation<Tag, Extra> { + /// Creates a read-only allocation initialized by the given bytes + pub fn from_bytes(slice: &[u8], align: Align, extra: Extra) -> Self { + let mut undef_mask = UndefMask::new(Size::ZERO); + undef_mask.grow(Size::from_bytes(slice.len() as u64), true); + Self { + bytes: slice.to_owned(), + relocations: Relocations::new(), + undef_mask, + align, + mutability: Mutability::Immutable, + extra, + } + } + + pub fn from_byte_aligned_bytes(slice: &[u8], extra: Extra) -> Self { + Allocation::from_bytes(slice, Align::from_bytes(1).unwrap(), extra) + } + + pub fn undef(size: Size, align: Align, extra: Extra) -> Self { + assert_eq!(size.bytes() as usize as u64, size.bytes()); + Allocation { + bytes: vec![0; size.bytes() as usize], + relocations: Relocations::new(), + undef_mask: UndefMask::new(size), + align, + mutability: Mutability::Mutable, + extra, + } + } +} + +impl<'tcx> ::serialize::UseSpecializedDecodable for &'tcx Allocation {} + /// Alignment and bounds checks impl<'tcx, Tag, Extra> Allocation<Tag, Extra> { /// Check if the pointer is "in-bounds". Notice that a pointer pointing at the end @@ -81,7 +169,7 @@ impl<'tcx, Tag, Extra> Allocation<Tag, Extra> { } /// Byte accessors -impl<'tcx, Tag: Copy, Extra: AllocationExtra<Tag>> Allocation<Tag, Extra> { +impl<'tcx, Tag: Copy, Extra> Allocation<Tag, Extra> { /// The last argument controls whether we error out when there are undefined /// or pointer bytes. You should never call this, call `get_bytes` or /// `get_bytes_with_undef_and_ptr` instead, @@ -89,13 +177,16 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra<Tag>> Allocation<Tag, Extra> { /// This function also guarantees that the resulting pointer will remain stable /// even when new allocations are pushed to the `HashMap`. `copy_repeatedly` relies /// on that. - fn get_bytes_internal( + fn get_bytes_internal<MemoryExtra>( &self, cx: &impl HasDataLayout, ptr: Pointer<Tag>, size: Size, check_defined_and_ptr: bool, - ) -> EvalResult<'tcx, &[u8]> { + ) -> EvalResult<'tcx, &[u8]> + // FIXME: Working around https://github.com/rust-lang/rust/issues/56209 + where Extra: AllocationExtra<Tag, MemoryExtra> + { self.check_bounds(cx, ptr, size)?; if check_defined_and_ptr { @@ -115,35 +206,44 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra<Tag>> Allocation<Tag, Extra> { } #[inline] - pub fn get_bytes( + pub fn get_bytes<MemoryExtra>( &self, cx: &impl HasDataLayout, ptr: Pointer<Tag>, size: Size, - ) -> EvalResult<'tcx, &[u8]> { + ) -> EvalResult<'tcx, &[u8]> + // FIXME: Working around https://github.com/rust-lang/rust/issues/56209 + where Extra: AllocationExtra<Tag, MemoryExtra> + { self.get_bytes_internal(cx, ptr, size, true) } /// It is the caller's responsibility to handle undefined and pointer bytes. /// However, this still checks that there are no relocations on the *edges*. #[inline] - pub fn get_bytes_with_undef_and_ptr( + pub fn get_bytes_with_undef_and_ptr<MemoryExtra>( &self, cx: &impl HasDataLayout, ptr: Pointer<Tag>, size: Size, - ) -> EvalResult<'tcx, &[u8]> { + ) -> EvalResult<'tcx, &[u8]> + // FIXME: Working around https://github.com/rust-lang/rust/issues/56209 + where Extra: AllocationExtra<Tag, MemoryExtra> + { self.get_bytes_internal(cx, ptr, size, false) } /// Just calling this already marks everything as defined and removes relocations, /// so be sure to actually put data there! - pub fn get_bytes_mut( + pub fn get_bytes_mut<MemoryExtra>( &mut self, cx: &impl HasDataLayout, ptr: Pointer<Tag>, size: Size, - ) -> EvalResult<'tcx, &mut [u8]> { + ) -> EvalResult<'tcx, &mut [u8]> + // FIXME: Working around https://github.com/rust-lang/rust/issues/56209 + where Extra: AllocationExtra<Tag, MemoryExtra> + { assert_ne!(size.bytes(), 0, "0-sized accesses should never even get a `Pointer`"); self.check_bounds(cx, ptr, size)?; @@ -160,14 +260,17 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra<Tag>> Allocation<Tag, Extra> { } /// Reading and writing -impl<'tcx, Tag: Copy, Extra: AllocationExtra<Tag>> Allocation<Tag, Extra> { +impl<'tcx, Tag: Copy, Extra> Allocation<Tag, Extra> { /// Reads bytes until a `0` is encountered. Will error if the end of the allocation is reached /// before a `0` is found. - pub fn read_c_str( + pub fn read_c_str<MemoryExtra>( &self, cx: &impl HasDataLayout, ptr: Pointer<Tag>, - ) -> EvalResult<'tcx, &[u8]> { + ) -> EvalResult<'tcx, &[u8]> + // FIXME: Working around https://github.com/rust-lang/rust/issues/56209 + where Extra: AllocationExtra<Tag, MemoryExtra> + { assert_eq!(ptr.offset.bytes() as usize as u64, ptr.offset.bytes()); let offset = ptr.offset.bytes() as usize; match self.bytes[offset..].iter().position(|&c| c == 0) { @@ -184,13 +287,16 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra<Tag>> Allocation<Tag, Extra> { /// Validates that `ptr.offset` and `ptr.offset + size` do not point to the middle of a /// relocation. If `allow_ptr_and_undef` is `false`, also enforces that the memory in the /// given range contains neither relocations nor undef bytes. - pub fn check_bytes( + pub fn check_bytes<MemoryExtra>( &self, cx: &impl HasDataLayout, ptr: Pointer<Tag>, size: Size, allow_ptr_and_undef: bool, - ) -> EvalResult<'tcx> { + ) -> EvalResult<'tcx> + // FIXME: Working around https://github.com/rust-lang/rust/issues/56209 + where Extra: AllocationExtra<Tag, MemoryExtra> + { // Check bounds and relocations on the edges self.get_bytes_with_undef_and_ptr(cx, ptr, size)?; // Check undef and ptr @@ -204,25 +310,31 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra<Tag>> Allocation<Tag, Extra> { /// Writes `src` to the memory starting at `ptr.offset`. /// /// Will do bounds checks on the allocation. - pub fn write_bytes( + pub fn write_bytes<MemoryExtra>( &mut self, cx: &impl HasDataLayout, ptr: Pointer<Tag>, src: &[u8], - ) -> EvalResult<'tcx> { + ) -> EvalResult<'tcx> + // FIXME: Working around https://github.com/rust-lang/rust/issues/56209 + where Extra: AllocationExtra<Tag, MemoryExtra> + { let bytes = self.get_bytes_mut(cx, ptr, Size::from_bytes(src.len() as u64))?; bytes.clone_from_slice(src); Ok(()) } /// Sets `count` bytes starting at `ptr.offset` with `val`. Basically `memset`. - pub fn write_repeat( + pub fn write_repeat<MemoryExtra>( &mut self, cx: &impl HasDataLayout, ptr: Pointer<Tag>, val: u8, count: Size - ) -> EvalResult<'tcx> { + ) -> EvalResult<'tcx> + // FIXME: Working around https://github.com/rust-lang/rust/issues/56209 + where Extra: AllocationExtra<Tag, MemoryExtra> + { let bytes = self.get_bytes_mut(cx, ptr, count)?; for b in bytes { *b = val; @@ -238,12 +350,15 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra<Tag>> Allocation<Tag, Extra> { /// being valid for ZSTs /// /// Note: This function does not do *any* alignment checks, you need to do these before calling - pub fn read_scalar( + pub fn read_scalar<MemoryExtra>( &self, cx: &impl HasDataLayout, ptr: Pointer<Tag>, size: Size - ) -> EvalResult<'tcx, ScalarMaybeUndef<Tag>> { + ) -> EvalResult<'tcx, ScalarMaybeUndef<Tag>> + // FIXME: Working around https://github.com/rust-lang/rust/issues/56209 + where Extra: AllocationExtra<Tag, MemoryExtra> + { // get_bytes_unchecked tests relocation edges let bytes = self.get_bytes_with_undef_and_ptr(cx, ptr, size)?; // Undef check happens *after* we established that the alignment is correct. @@ -273,11 +388,14 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra<Tag>> Allocation<Tag, Extra> { } /// Note: This function does not do *any* alignment checks, you need to do these before calling - pub fn read_ptr_sized( + pub fn read_ptr_sized<MemoryExtra>( &self, cx: &impl HasDataLayout, ptr: Pointer<Tag>, - ) -> EvalResult<'tcx, ScalarMaybeUndef<Tag>> { + ) -> EvalResult<'tcx, ScalarMaybeUndef<Tag>> + // FIXME: Working around https://github.com/rust-lang/rust/issues/56209 + where Extra: AllocationExtra<Tag, MemoryExtra> + { self.read_scalar(cx, ptr, cx.data_layout().pointer_size) } @@ -289,13 +407,16 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra<Tag>> Allocation<Tag, Extra> { /// being valid for ZSTs /// /// Note: This function does not do *any* alignment checks, you need to do these before calling - pub fn write_scalar( + pub fn write_scalar<MemoryExtra>( &mut self, cx: &impl HasDataLayout, ptr: Pointer<Tag>, val: ScalarMaybeUndef<Tag>, type_size: Size, - ) -> EvalResult<'tcx> { + ) -> EvalResult<'tcx> + // FIXME: Working around https://github.com/rust-lang/rust/issues/56209 + where Extra: AllocationExtra<Tag, MemoryExtra> + { let val = match val { ScalarMaybeUndef::Scalar(scalar) => scalar, ScalarMaybeUndef::Undef => return self.mark_definedness(ptr, type_size, false), @@ -336,12 +457,15 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra<Tag>> Allocation<Tag, Extra> { } /// Note: This function does not do *any* alignment checks, you need to do these before calling - pub fn write_ptr_sized( + pub fn write_ptr_sized<MemoryExtra>( &mut self, cx: &impl HasDataLayout, ptr: Pointer<Tag>, val: ScalarMaybeUndef<Tag> - ) -> EvalResult<'tcx> { + ) -> EvalResult<'tcx> + // FIXME: Working around https://github.com/rust-lang/rust/issues/56209 + where Extra: AllocationExtra<Tag, MemoryExtra> + { let ptr_size = cx.data_layout().pointer_size; self.write_scalar(cx, ptr.into(), val, ptr_size) } @@ -465,79 +589,7 @@ impl<'tcx, Tag, Extra> Allocation<Tag, Extra> { } } -pub trait AllocationExtra<Tag>: ::std::fmt::Debug + Default + Clone { - /// Hook for performing extra checks on a memory read access. - /// - /// Takes read-only access to the allocation so we can keep all the memory read - /// operations take `&self`. Use a `RefCell` in `AllocExtra` if you - /// need to mutate. - #[inline] - fn memory_read( - _alloc: &Allocation<Tag, Self>, - _ptr: Pointer<Tag>, - _size: Size, - ) -> EvalResult<'tcx> { - Ok(()) - } - - /// Hook for performing extra checks on a memory write access. - #[inline] - fn memory_written( - _alloc: &mut Allocation<Tag, Self>, - _ptr: Pointer<Tag>, - _size: Size, - ) -> EvalResult<'tcx> { - Ok(()) - } - - /// Hook for performing extra checks on a memory deallocation. - /// `size` will be the size of the allocation. - #[inline] - fn memory_deallocated( - _alloc: &mut Allocation<Tag, Self>, - _ptr: Pointer<Tag>, - _size: Size, - ) -> EvalResult<'tcx> { - Ok(()) - } -} - -impl AllocationExtra<()> for () {} - -impl<Tag, Extra: Default> Allocation<Tag, Extra> { - /// Creates a read-only allocation initialized by the given bytes - pub fn from_bytes(slice: &[u8], align: Align) -> Self { - let mut undef_mask = UndefMask::new(Size::ZERO); - undef_mask.grow(Size::from_bytes(slice.len() as u64), true); - Self { - bytes: slice.to_owned(), - relocations: Relocations::new(), - undef_mask, - align, - mutability: Mutability::Immutable, - extra: Extra::default(), - } - } - - pub fn from_byte_aligned_bytes(slice: &[u8]) -> Self { - Allocation::from_bytes(slice, Align::from_bytes(1).unwrap()) - } - - pub fn undef(size: Size, align: Align) -> Self { - assert_eq!(size.bytes() as usize as u64, size.bytes()); - Allocation { - bytes: vec![0; size.bytes() as usize], - relocations: Relocations::new(), - undef_mask: UndefMask::new(size), - align, - mutability: Mutability::Mutable, - extra: Extra::default(), - } - } -} - -impl<'tcx> ::serialize::UseSpecializedDecodable for &'tcx Allocation {} - +/// Relocations #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, RustcEncodable, RustcDecodable)] pub struct Relocations<Tag=(), Id=AllocId>(SortedMap<Size, (Tag, Id)>); diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index 1b947c276f3..25ef6ddc005 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -1055,7 +1055,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { /// Allocates a byte or string literal for `mir::interpret`, read-only pub fn allocate_bytes(self, bytes: &[u8]) -> interpret::AllocId { // create an allocation that just contains these bytes - let alloc = interpret::Allocation::from_byte_aligned_bytes(bytes); + let alloc = interpret::Allocation::from_byte_aligned_bytes(bytes, ()); let alloc = self.intern_const_alloc(alloc); self.alloc_map.lock().allocate(alloc) } diff --git a/src/librustc_mir/const_eval.rs b/src/librustc_mir/const_eval.rs index 1bc3b322717..73c0f8fff7a 100644 --- a/src/librustc_mir/const_eval.rs +++ b/src/librustc_mir/const_eval.rs @@ -353,6 +353,7 @@ impl<'a, 'mir, 'tcx> interpret::Machine<'a, 'mir, 'tcx> for CompileTimeInterpreter<'a, 'mir, 'tcx> { type MemoryKinds = !; + type MemoryExtra = (); type AllocExtra = (); type PointerTag = (); diff --git a/src/librustc_mir/interpret/machine.rs b/src/librustc_mir/interpret/machine.rs index 57640dc48f1..481c46cb510 100644 --- a/src/librustc_mir/interpret/machine.rs +++ b/src/librustc_mir/interpret/machine.rs @@ -77,8 +77,13 @@ pub trait Machine<'a, 'mir, 'tcx>: Sized { /// The `default()` is used for pointers to consts, statics, vtables and functions. type PointerTag: ::std::fmt::Debug + Default + Copy + Eq + Hash + 'static; + /// Extra data stored in memory. A reference to this is available when `AllocExtra` + /// gets initialized, so you can e.g. have an `Rc` here if there is global state you + /// need access to in the `AllocExtra` hooks. + type MemoryExtra: Default; + /// Extra data stored in every allocation. - type AllocExtra: AllocationExtra<Self::PointerTag>; + type AllocExtra: AllocationExtra<Self::PointerTag, Self::MemoryExtra>; /// Memory's allocation map type MemoryMap: diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs index c673b57a66f..99bf93c8a9b 100644 --- a/src/librustc_mir/interpret/memory.rs +++ b/src/librustc_mir/interpret/memory.rs @@ -73,6 +73,9 @@ pub struct Memory<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'a, 'mir, 'tcx>> { /// that do not exist any more. dead_alloc_map: FxHashMap<AllocId, (Size, Align)>, + /// Extra data added by the machine. + pub extra: M::MemoryExtra, + /// Lets us implement `HasDataLayout`, which is awfully convenient. pub(super) tcx: TyCtxtAt<'a, 'tcx, 'tcx>, } @@ -88,13 +91,19 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> HasDataLayout // FIXME: Really we shouldn't clone memory, ever. Snapshot machinery should instead // carefully copy only the reachable parts. -impl<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'a, 'mir, 'tcx>> - Clone for Memory<'a, 'mir, 'tcx, M> +impl<'a, 'mir, 'tcx, M> + Clone +for + Memory<'a, 'mir, 'tcx, M> +where + M: Machine<'a, 'mir, 'tcx, PointerTag=(), AllocExtra=(), MemoryExtra=()>, + M::MemoryMap: AllocMap<AllocId, (MemoryKind<M::MemoryKinds>, Allocation)>, { fn clone(&self) -> Self { Memory { alloc_map: self.alloc_map.clone(), dead_alloc_map: self.dead_alloc_map.clone(), + extra: (), tcx: self.tcx, } } @@ -103,8 +112,9 @@ impl<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'a, 'mir, 'tcx>> impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { pub fn new(tcx: TyCtxtAt<'a, 'tcx, 'tcx>) -> Self { Memory { - alloc_map: Default::default(), + alloc_map: M::MemoryMap::default(), dead_alloc_map: FxHashMap::default(), + extra: M::MemoryExtra::default(), tcx, } } @@ -133,7 +143,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { align: Align, kind: MemoryKind<M::MemoryKinds>, ) -> EvalResult<'tcx, Pointer> { - Ok(Pointer::from(self.allocate_with(Allocation::undef(size, align), kind)?)) + let extra = AllocationExtra::memory_allocated(size, &self.extra)?; + Ok(Pointer::from(self.allocate_with(Allocation::undef(size, align, extra), kind)?)) } pub fn reallocate( @@ -601,7 +612,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { /// Interning (for CTFE) impl<'a, 'mir, 'tcx, M> Memory<'a, 'mir, 'tcx, M> where - M: Machine<'a, 'mir, 'tcx, PointerTag=(), AllocExtra=()>, + M: Machine<'a, 'mir, 'tcx, PointerTag=(), AllocExtra=(), MemoryExtra=()>, + // FIXME: Working around https://github.com/rust-lang/rust/issues/24159 M::MemoryMap: AllocMap<AllocId, (MemoryKind<M::MemoryKinds>, Allocation)>, { /// mark an allocation as static and initialized, either mutable or not diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs index 0fd1a993cbd..1b47530eaec 100644 --- a/src/librustc_mir/interpret/place.rs +++ b/src/librustc_mir/interpret/place.rs @@ -295,10 +295,12 @@ impl<'tcx, Tag: ::std::fmt::Debug> PlaceTy<'tcx, Tag> { // separating the pointer tag for `impl Trait`, see https://github.com/rust-lang/rust/issues/54385 impl<'a, 'mir, 'tcx, Tag, M> EvalContext<'a, 'mir, 'tcx, M> where + // FIXME: Working around https://github.com/rust-lang/rust/issues/54385 Tag: ::std::fmt::Debug+Default+Copy+Eq+Hash+'static, M: Machine<'a, 'mir, 'tcx, PointerTag=Tag>, + // FIXME: Working around https://github.com/rust-lang/rust/issues/24159 M::MemoryMap: AllocMap<AllocId, (MemoryKind<M::MemoryKinds>, Allocation<Tag, M::AllocExtra>)>, - M::AllocExtra: AllocationExtra<Tag>, + M::AllocExtra: AllocationExtra<Tag, M::MemoryExtra>, { /// Take a value, which represents a (thin or fat) reference, and make it a place. /// Alignment is just based on the type. This is the inverse of `MemPlace::to_ref()`. |
