1
Fork 0

provide machine hooks for creating references and accessing memory

This commit is contained in:
Ralf Jung 2018-10-16 14:50:07 +02:00
parent 5b7185794f
commit ce9cd15ce6
7 changed files with 103 additions and 32 deletions

View file

@ -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

View file

@ -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,

View file

@ -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;

View file

@ -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};

View file

@ -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.

View file

@ -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)?;
}

View file

@ -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))?,
};