provide machine hooks for creating references and accessing memory
This commit is contained in:
parent
5b7185794f
commit
ce9cd15ce6
7 changed files with 103 additions and 32 deletions
|
@ -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
|
||||
|
|
|
@ -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<Self::PointerTag>, 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<Self::PointerTag, Self::AllocExtra>,
|
||||
_ptr: Pointer<Self::PointerTag>,
|
||||
_size: Size,
|
||||
_access: MemoryAccess,
|
||||
) -> EvalResult<'tcx> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Hook for performing extra checks when memory gets deallocated.
|
||||
#[inline]
|
||||
fn memory_deallocated(
|
||||
_alloc: &mut Allocation<Self::PointerTag, Self::AllocExtra>,
|
||||
_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,
|
||||
|
|
|
@ -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<T> {
|
||||
|
@ -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;
|
||||
|
|
|
@ -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};
|
||||
|
||||
|
|
|
@ -144,17 +144,6 @@ impl<Tag> MemPlace<Tag> {
|
|||
// 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<Tag> {
|
||||
// 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<AllocId, (MemoryKind<M::MemoryKinds>, Allocation<Tag, M::AllocExtra>)>,
|
||||
{
|
||||
/// 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<M::PointerTag>> {
|
||||
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.
|
||||
|
|
|
@ -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)?;
|
||||
}
|
||||
|
||||
|
|
|
@ -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))?,
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue