From ce9cd15ce637928d6b571b0c7a248ab678908c6d Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 16 Oct 2018 14:50:07 +0200 Subject: [PATCH] provide machine hooks for creating references and accessing memory --- src/librustc_mir/const_eval.rs | 11 ++++- src/librustc_mir/interpret/machine.rs | 52 +++++++++++++++++++++--- src/librustc_mir/interpret/memory.rs | 22 ++++++---- src/librustc_mir/interpret/mod.rs | 4 +- src/librustc_mir/interpret/place.rs | 36 ++++++++++------ src/librustc_mir/interpret/step.rs | 5 ++- src/librustc_mir/interpret/terminator.rs | 5 ++- 7 files changed, 103 insertions(+), 32 deletions(-) diff --git a/src/librustc_mir/const_eval.rs b/src/librustc_mir/const_eval.rs index fd9af74c5d7..baf6a4ecaa0 100644 --- a/src/librustc_mir/const_eval.rs +++ b/src/librustc_mir/const_eval.rs @@ -33,7 +33,7 @@ use rustc::mir::interpret::{ Scalar, Allocation, AllocId, ConstValue, }; use interpret::{self, - PlaceTy, MemPlace, OpTy, Operand, Value, + PlaceTy, MPlaceTy, MemPlace, OpTy, Operand, Value, EvalContext, StackPopCleanup, MemoryKind, snapshot, }; @@ -464,6 +464,15 @@ impl<'a, 'mir, 'tcx> interpret::Machine<'a, 'mir, 'tcx> &ecx.stack[..], ) } + + #[inline(always)] + fn tag_reference( + _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>, + _place: MPlaceTy<'tcx, Self::PointerTag>, + _borrow_kind: mir::BorrowKind, + ) -> EvalResult<'tcx, Self::PointerTag> { + Ok(()) + } } /// Project to a field of a (variant of a) const diff --git a/src/librustc_mir/interpret/machine.rs b/src/librustc_mir/interpret/machine.rs index 56909938df4..e30874ce7b3 100644 --- a/src/librustc_mir/interpret/machine.rs +++ b/src/librustc_mir/interpret/machine.rs @@ -16,11 +16,19 @@ use std::borrow::{Borrow, Cow}; use std::hash::Hash; use rustc::hir::def_id::DefId; -use rustc::mir::interpret::{Allocation, AllocId, EvalResult, Scalar}; use rustc::mir; -use rustc::ty::{self, layout::TyLayout, query::TyCtxtAt}; +use rustc::ty::{self, layout::{Size, TyLayout}, query::TyCtxtAt}; -use super::{EvalContext, PlaceTy, OpTy, MemoryKind}; +use super::{ + Allocation, AllocId, EvalResult, Scalar, + EvalContext, PlaceTy, OpTy, MPlaceTy, Pointer, MemoryKind, +}; + +/// Classifying memory accesses +pub enum MemoryAccess { + Read, + Write, +} /// Whether this kind of memory is allowed to leak pub trait MayLeak: Copy { @@ -160,15 +168,47 @@ pub trait Machine<'a, 'mir, 'tcx>: Sized { right_layout: TyLayout<'tcx>, ) -> EvalResult<'tcx, (Scalar, bool)>; - /// Heap allocations via the `box` keyword - /// - /// Returns a pointer to the allocated memory + /// Heap allocations via the `box` keyword. fn box_alloc( ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>, dest: PlaceTy<'tcx, Self::PointerTag>, ) -> EvalResult<'tcx>; + /// Hook for performing extra checks on a memory 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_accessed( + _alloc: &Allocation, + _ptr: Pointer, + _size: Size, + _access: MemoryAccess, + ) -> EvalResult<'tcx> { + Ok(()) + } + + /// Hook for performing extra checks when memory gets deallocated. + #[inline] + fn memory_deallocated( + _alloc: &mut Allocation, + _id: AllocId, + ) -> EvalResult<'tcx> { + Ok(()) + } + + /// Executed when evaluating the `&` operator: Creating a new reference. + /// This has the chance to adjust the tag. It is only ever called if the + /// pointer in `place` is really a pointer, not another scalar. + fn tag_reference( + ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>, + place: MPlaceTy<'tcx, Self::PointerTag>, + borrow_kind: mir::BorrowKind, + ) -> EvalResult<'tcx, Self::PointerTag>; + /// Execute a validation operation + #[inline] fn validation_op( _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>, _op: ::rustc::mir::ValidationOp, diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs index 51dd970e0bb..faa165723db 100644 --- a/src/librustc_mir/interpret/memory.rs +++ b/src/librustc_mir/interpret/memory.rs @@ -22,17 +22,16 @@ use std::borrow::Cow; use rustc::ty::{self, Instance, ParamEnv, query::TyCtxtAt}; use rustc::ty::layout::{self, Align, TargetDataLayout, Size, HasDataLayout}; -use rustc::mir::interpret::{ - Pointer, AllocId, Allocation, ConstValue, GlobalId, - EvalResult, Scalar, EvalErrorKind, AllocType, PointerArithmetic, - truncate -}; -pub use rustc::mir::interpret::{write_target_uint, read_target_uint}; +pub use rustc::mir::interpret::{truncate, write_target_uint, read_target_uint}; use rustc_data_structures::fx::{FxHashSet, FxHashMap}; use syntax::ast::Mutability; -use super::{Machine, AllocMap, MayLeak, ScalarMaybeUndef}; +use super::{ + Pointer, AllocId, Allocation, ConstValue, GlobalId, + EvalResult, Scalar, EvalErrorKind, AllocType, PointerArithmetic, + Machine, MemoryAccess, AllocMap, MayLeak, ScalarMaybeUndef, +}; #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)] pub enum MemoryKind { @@ -197,7 +196,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { return err!(DeallocateNonBasePtr); } - let (alloc_kind, alloc) = match self.alloc_map.remove(&ptr.alloc_id) { + let (alloc_kind, mut alloc) = match self.alloc_map.remove(&ptr.alloc_id) { Some(alloc) => alloc, None => { // Deallocating static memory -- always an error @@ -232,6 +231,9 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { } } + // Let the machine take some extra action + M::memory_deallocated(&mut alloc, ptr.alloc_id)?; + // Don't forget to remember size and align of this now-dead allocation let old = self.dead_alloc_map.insert( ptr.alloc_id, @@ -632,6 +634,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { } let alloc = self.get(ptr.alloc_id)?; + M::memory_accessed(alloc, ptr, size, MemoryAccess::Read)?; + assert_eq!(ptr.offset.bytes() as usize as u64, ptr.offset.bytes()); assert_eq!(size.bytes() as usize as u64, size.bytes()); let offset = ptr.offset.bytes() as usize; @@ -676,6 +680,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { self.clear_relocations(ptr, size)?; let alloc = self.get_mut(ptr.alloc_id)?; + M::memory_accessed(alloc, ptr, size, MemoryAccess::Write)?; + assert_eq!(ptr.offset.bytes() as usize as u64, ptr.offset.bytes()); assert_eq!(size.bytes() as usize as u64, size.bytes()); let offset = ptr.offset.bytes() as usize; diff --git a/src/librustc_mir/interpret/mod.rs b/src/librustc_mir/interpret/mod.rs index a174b12aaac..55037a99e01 100644 --- a/src/librustc_mir/interpret/mod.rs +++ b/src/librustc_mir/interpret/mod.rs @@ -24,6 +24,8 @@ mod traits; mod validity; mod intrinsics; +pub use rustc::mir::interpret::*; // have all the `interpret` symbols in one place: here + pub use self::eval_context::{ EvalContext, Frame, StackPopCleanup, LocalValue, }; @@ -32,7 +34,7 @@ pub use self::place::{Place, PlaceTy, MemPlace, MPlaceTy}; pub use self::memory::{Memory, MemoryKind}; -pub use self::machine::{Machine, AllocMap, MayLeak}; +pub use self::machine::{Machine, AllocMap, MemoryAccess, MayLeak}; pub use self::operand::{ScalarMaybeUndef, Value, ValTy, Operand, OpTy}; diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs index c4f01d8ce31..51406a686bc 100644 --- a/src/librustc_mir/interpret/place.rs +++ b/src/librustc_mir/interpret/place.rs @@ -144,17 +144,6 @@ impl MemPlace { // it now must be aligned. self.to_scalar_ptr_align().0.to_ptr() } - - /// Turn a mplace into a (thin or fat) pointer, as a reference, pointing to the same space. - /// This is the inverse of `ref_to_mplace`. - pub fn to_ref(self) -> Value { - // We ignore the alignment of the place here -- special handling for packed structs ends - // at the `&` operator. - match self.meta { - None => Value::Scalar(self.ptr.into()), - Some(meta) => Value::ScalarPair(self.ptr.into(), meta.into()), - } - } } impl<'tcx, Tag> MPlaceTy<'tcx, Tag> { @@ -270,9 +259,10 @@ where M::MemoryMap: AllocMap, Allocation)>, { /// 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`. + /// Alignment is just based on the type. This is the inverse of `create_ref`. pub fn ref_to_mplace( - &self, val: ValTy<'tcx, M::PointerTag> + &self, + val: ValTy<'tcx, M::PointerTag>, ) -> EvalResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { let pointee_type = val.layout.ty.builtin_deref(true).unwrap().ty; let layout = self.layout_of(pointee_type)?; @@ -286,6 +276,26 @@ where Ok(MPlaceTy { mplace, layout }) } + /// Turn a mplace into a (thin or fat) pointer, as a reference, pointing to the same space. + /// This is the inverse of `ref_to_mplace`. + pub fn create_ref( + &mut self, + place: MPlaceTy<'tcx, M::PointerTag>, + borrow_kind: mir::BorrowKind, + ) -> EvalResult<'tcx, Value> { + let ptr = match place.ptr { + Scalar::Ptr(ptr) => { + let tag = M::tag_reference(self, place, borrow_kind)?; + Scalar::Ptr(Pointer::new_with_tag(ptr.alloc_id, ptr.offset, tag)) + }, + scalar @ Scalar::Bits { .. } => scalar, + }; + Ok(match place.meta { + None => Value::Scalar(ptr.into()), + Some(meta) => Value::ScalarPair(ptr.into(), meta.into()), + }) + } + /// Offset a pointer to project to a field. Unlike place_field, this is always /// possible without allocating, so it can take &self. Also return the field's layout. /// This supports both struct and array fields. diff --git a/src/librustc_mir/interpret/step.rs b/src/librustc_mir/interpret/step.rs index d15867eacdd..cb629e8313c 100644 --- a/src/librustc_mir/interpret/step.rs +++ b/src/librustc_mir/interpret/step.rs @@ -248,9 +248,10 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> )?; } - Ref(_, _, ref place) => { + Ref(_, borrow_kind, ref place) => { let src = self.eval_place(place)?; - let val = self.force_allocation(src)?.to_ref(); + let val = self.force_allocation(src)?; + let val = self.create_ref(val, borrow_kind)?; self.write_value(val, dest)?; } diff --git a/src/librustc_mir/interpret/terminator.rs b/src/librustc_mir/interpret/terminator.rs index a339fa34ae1..bb2dc732200 100644 --- a/src/librustc_mir/interpret/terminator.rs +++ b/src/librustc_mir/interpret/terminator.rs @@ -446,7 +446,10 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> }; let arg = OpTy { - op: Operand::Immediate(place.to_ref()), + op: Operand::Immediate(self.create_ref( + place, + mir::BorrowKind::Mut { allow_two_phase_borrow: false } + )?), layout: self.layout_of(self.tcx.mk_mut_ptr(place.layout.ty))?, };