1
Fork 0

Allow machines to create new memory kinds

This commit is contained in:
Oliver Schneider 2017-07-28 16:48:43 +02:00
parent 7ed706d09c
commit adfea61665
No known key found for this signature in database
GPG key ID: A69F8D225B3AD7D9
10 changed files with 131 additions and 62 deletions

View file

@ -15,6 +15,8 @@ use super::{
MemoryExt,
};
use super::memory::Kind;
pub trait EvalContextExt<'tcx> {
fn call_c_abi(
&mut self,
@ -110,7 +112,7 @@ impl<'a, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'tcx, super::Evaluator>
self.write_null(dest, dest_ty)?;
} else {
let align = self.memory.pointer_size();
let ptr = self.memory.allocate(size, align, Kind::C)?;
let ptr = self.memory.allocate(size, align, Kind::C.into())?;
self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?;
}
}
@ -118,7 +120,7 @@ impl<'a, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'tcx, super::Evaluator>
"free" => {
let ptr = args[0].into_ptr(&mut self.memory)?;
if !ptr.is_null()? {
self.memory.deallocate(ptr.to_ptr()?, None, Kind::C)?;
self.memory.deallocate(ptr.to_ptr()?, None, Kind::C.into())?;
}
}
@ -242,7 +244,7 @@ impl<'a, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'tcx, super::Evaluator>
}
if let Some(old) = success {
if let Some(var) = old {
self.memory.deallocate(var, None, Kind::Env)?;
self.memory.deallocate(var, None, Kind::Env.into())?;
}
self.write_null(dest, dest_ty)?;
} else {
@ -265,12 +267,12 @@ impl<'a, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'tcx, super::Evaluator>
}
if let Some((name, value)) = new {
// +1 for the null terminator
let value_copy = self.memory.allocate((value.len() + 1) as u64, 1, Kind::Env)?;
let value_copy = self.memory.allocate((value.len() + 1) as u64, 1, Kind::Env.into())?;
self.memory.write_bytes(value_copy.into(), &value)?;
let trailing_zero_ptr = value_copy.offset(value.len() as u64, &self)?.into();
self.memory.write_bytes(trailing_zero_ptr, &[0])?;
if let Some(var) = self.machine_data.env_vars.insert(name.to_owned(), value_copy) {
self.memory.deallocate(var, None, Kind::Env)?;
self.memory.deallocate(var, None, Kind::Env.into())?;
}
self.write_null(dest, dest_ty)?;
} else {
@ -491,7 +493,7 @@ impl<'a, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'tcx, super::Evaluator>
if !align.is_power_of_two() {
return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(align));
}
let ptr = self.memory.allocate(size, align, Kind::Rust)?;
let ptr = self.memory.allocate(size, align, Kind::Rust.into())?;
self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?;
}
"alloc::heap::::__rust_alloc_zeroed" => {
@ -503,7 +505,7 @@ impl<'a, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'tcx, super::Evaluator>
if !align.is_power_of_two() {
return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(align));
}
let ptr = self.memory.allocate(size, align, Kind::Rust)?;
let ptr = self.memory.allocate(size, align, Kind::Rust.into())?;
self.memory.write_repeat(ptr.into(), 0, size)?;
self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?;
}
@ -517,7 +519,7 @@ impl<'a, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'tcx, super::Evaluator>
if !align.is_power_of_two() {
return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(align));
}
self.memory.deallocate(ptr, Some((old_size, align)), Kind::Rust)?;
self.memory.deallocate(ptr, Some((old_size, align)), Kind::Rust.into())?;
}
"alloc::heap::::__rust_realloc" => {
let ptr = args[0].into_ptr(&mut self.memory)?.to_ptr()?;
@ -534,7 +536,7 @@ impl<'a, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'tcx, super::Evaluator>
if !new_align.is_power_of_two() {
return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(new_align));
}
let new_ptr = self.memory.reallocate(ptr, old_size, old_align, new_size, new_align, Kind::Rust)?;
let new_ptr = self.memory.reallocate(ptr, old_size, old_align, new_size, new_align, Kind::Rust.into())?;
self.write_primval(dest, PrimVal::Ptr(new_ptr), dest_ty)?;
}

View file

@ -32,6 +32,7 @@ mod fn_call;
mod operator;
mod intrinsic;
mod helpers;
mod memory;
use fn_call::EvalContextExt as MissingFnsEvalContextExt;
use operator::EvalContextExt as OperatorEvalContextExt;
@ -278,6 +279,7 @@ impl<'a, 'tcx: 'a> MemoryExt<'tcx> for Memory<'a, 'tcx, Evaluator> {
impl<'tcx> Machine<'tcx> for Evaluator {
type Data = EvaluatorData;
type MemoryData = MemoryData<'tcx>;
type MemoryKinds = memory::Kind;
/// Returns Ok() when the function was handled, fail otherwise
fn eval_fn_call<'a>(
@ -313,4 +315,28 @@ impl<'tcx> Machine<'tcx> for Evaluator {
) -> EvalResult<'tcx, Option<(PrimVal, bool)>> {
ecx.ptr_op(bin_op, left, left_ty, right, right_ty)
}
fn mark_static_initialized(m: memory::Kind) -> EvalResult<'tcx> {
use memory::Kind::*;
match m {
// FIXME: This could be allowed, but not for env vars set during miri execution
Env => Err(EvalError::Unimplemented("statics can't refer to env vars".to_owned())),
_ => Ok(()),
}
}
fn box_alloc<'a>(
ecx: &mut EvalContext<'a, 'tcx, Self>,
ty: ty::Ty<'tcx>,
) -> EvalResult<'tcx, PrimVal> {
let size = ecx.type_size(ty)?.expect("box only works with sized types");
let align = ecx.type_align(ty)?;
if size == 0 {
Ok(PrimVal::Bytes(align.into()))
} else {
ecx.memory
.allocate(size, align, Kind::Machine(memory::Kind::Rust))
.map(PrimVal::Ptr)
}
}
}

16
miri/memory.rs Normal file
View file

@ -0,0 +1,16 @@
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Kind {
/// Error if deallocated any other way than `rust_deallocate`
Rust,
/// Error if deallocated any other way than `free`
C,
/// Part of env var emulation
Env,
}
impl Into<::rustc_miri::interpret::Kind<Kind>> for Kind {
fn into(self) -> ::rustc_miri::interpret::Kind<Kind> {
::rustc_miri::interpret::Kind::Machine(self)
}
}

View file

@ -128,6 +128,7 @@ impl Error for ConstEvalError {
impl<'tcx> super::Machine<'tcx> for CompileTimeFunctionEvaluator {
type Data = ();
type MemoryData = ();
type MemoryKinds = !;
fn eval_fn_call<'a>(
ecx: &mut EvalContext<'a, 'tcx, Self>,
instance: ty::Instance<'tcx>,
@ -185,4 +186,15 @@ impl<'tcx> super::Machine<'tcx> for CompileTimeFunctionEvaluator {
) -> EvalResult<'tcx, Option<(PrimVal, bool)>> {
Err(ConstEvalError::NeedsRfc("Pointer arithmetic or comparison".to_string()).into())
}
fn mark_static_initialized(m: !) -> EvalResult<'tcx> {
m
}
fn box_alloc<'a>(
_ecx: &mut EvalContext<'a, 'tcx, Self>,
_ty: ty::Ty<'tcx>,
) -> EvalResult<'tcx, PrimVal> {
Err(ConstEvalError::NeedsRfc("Heap allocations via `box` keyword".to_string()).into())
}
}

View file

@ -4,7 +4,7 @@ use rustc::mir;
use rustc::ty::{FnSig, Ty, layout};
use super::{
MemoryPointer, Kind, LockInfo, AccessKind
MemoryPointer, LockInfo, AccessKind
};
use rustc_const_math::ConstMathErr;
@ -88,8 +88,8 @@ pub enum EvalError<'tcx> {
AssumptionNotHeld,
InlineAsm,
TypeNotPrimitive(Ty<'tcx>),
ReallocatedWrongMemoryKind(Kind, Kind),
DeallocatedWrongMemoryKind(Kind, Kind),
ReallocatedWrongMemoryKind(String, String),
DeallocatedWrongMemoryKind(String, String),
ReallocateNonBasePtr,
DeallocateNonBasePtr,
IncorrectAllocationInformation,
@ -262,10 +262,10 @@ impl<'tcx> fmt::Display for EvalError<'tcx> {
write!(f, "tried to call a function with sig {} through a function pointer of type {}", sig, got),
ArrayIndexOutOfBounds(span, len, index) =>
write!(f, "index out of bounds: the len is {} but the index is {} at {:?}", len, index, span),
ReallocatedWrongMemoryKind(old, new) =>
write!(f, "tried to reallocate memory from {:?} to {:?}", old, new),
DeallocatedWrongMemoryKind(old, new) =>
write!(f, "tried to deallocate {:?} memory but gave {:?} as the kind", old, new),
ReallocatedWrongMemoryKind(ref old, ref new) =>
write!(f, "tried to reallocate memory from {} to {}", old, new),
DeallocatedWrongMemoryKind(ref old, ref new) =>
write!(f, "tried to deallocate {} memory but gave {} as the kind", old, new),
Math(span, ref err) =>
write!(f, "{:?} at {:?}", err, span),
Intrinsic(ref err) =>

View file

@ -782,17 +782,8 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
}
NullaryOp(mir::NullOp::Box, ty) => {
// FIXME(CTFE): don't allow heap allocations in const eval
// FIXME: call the `exchange_malloc` lang item if available
let size = self.type_size(ty)?.expect("box only works with sized types");
if size == 0 {
let align = self.type_align(ty)?;
self.write_primval(dest, PrimVal::Bytes(align.into()), dest_ty)?;
} else {
let align = self.type_align(ty)?;
let ptr = self.memory.allocate(size, align, MemoryKind::Rust)?;
self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?;
}
let ptr = M::box_alloc(self, ty)?;
self.write_primval(dest, ptr, dest_ty)?;
}
NullaryOp(mir::NullOp::SizeOf, ty) => {

View file

@ -21,6 +21,9 @@ pub trait Machine<'tcx>: Sized {
/// Additional data that can be accessed via the Memory
type MemoryData;
/// Additional memory kinds a machine wishes to distinguish from the builtin ones
type MemoryKinds: ::std::fmt::Debug + PartialEq + Copy + Clone;
/// Entry point to all function calls.
///
/// Returns Ok(true) when the function was handled completely
@ -61,5 +64,16 @@ pub trait Machine<'tcx>: Sized {
right: PrimVal,
right_ty: ty::Ty<'tcx>,
) -> EvalResult<'tcx, Option<(PrimVal, bool)>>;
/// Called when trying to mark machine defined `MemoryKinds` as static
fn mark_static_initialized(m: Self::MemoryKinds) -> EvalResult<'tcx>;
/// Heap allocations via the `box` keyword
///
/// Returns a pointer to the allocated memory
fn box_alloc<'a>(
ecx: &mut EvalContext<'a, 'tcx, Self>,
ty: ty::Ty<'tcx>,
) -> EvalResult<'tcx, PrimVal>;
}

View file

@ -116,7 +116,7 @@ impl fmt::Display for AllocId {
}
#[derive(Debug)]
pub struct Allocation {
pub struct Allocation<M> {
/// The actual bytes of the allocation.
/// Note that the bytes of a pointer represent the offset of the pointer
pub bytes: Vec<u8>,
@ -132,12 +132,12 @@ pub struct Allocation {
/// Use the `mark_static_initalized` method of `Memory` to ensure that an error occurs, if the memory of this
/// allocation is modified or deallocated in the future.
/// Helps guarantee that stack allocations aren't deallocated via `rust_deallocate`
pub kind: Kind,
pub kind: Kind<M>,
/// Memory regions that are locked by some function
locks: BTreeMap<MemoryRange, LockInfo>,
}
impl Allocation {
impl<M> Allocation<M> {
fn iter_locks<'a>(&'a self, offset: u64, len: u64) -> impl Iterator<Item=(&'a MemoryRange, &'a LockInfo)> + 'a {
self.locks.range(MemoryRange::range(offset, len))
.filter(move |&(range, _)| range.overlaps(offset, len))
@ -165,11 +165,7 @@ impl Allocation {
}
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Kind {
/// Error if deallocated any other way than `rust_deallocate`
Rust,
/// Error if deallocated any other way than `free`
C,
pub enum Kind<T> {
/// Error if deallocated except during a stack pop
Stack,
/// Static in the process of being initialized.
@ -179,8 +175,8 @@ pub enum Kind {
UninitializedStatic,
/// May never be deallocated
Static,
/// Part of env var emulation
Env,
/// Additional memory kinds a machine wishes to distinguish from the builtin ones
Machine(T),
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
@ -226,7 +222,7 @@ pub struct Memory<'a, 'tcx, M: Machine<'tcx>> {
pub data: M::MemoryData,
/// Actual memory allocations (arbitrary bytes, may contain pointers into other allocations).
alloc_map: HashMap<AllocId, Allocation>,
alloc_map: HashMap<AllocId, Allocation<M::MemoryKinds>>,
/// The AllocId to assign to the next new allocation. Always incremented, never gets smaller.
next_id: AllocId,
@ -285,7 +281,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
}
}
pub fn allocations(&self) -> ::std::collections::hash_map::Iter<AllocId, Allocation> {
pub fn allocations(&self) -> ::std::collections::hash_map::Iter<AllocId, Allocation<M::MemoryKinds>> {
self.alloc_map.iter()
}
@ -313,7 +309,12 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
Ok(ptr)
}
pub fn allocate(&mut self, size: u64, align: u64, kind: Kind) -> EvalResult<'tcx, MemoryPointer> {
pub fn allocate(
&mut self,
size: u64,
align: u64,
kind: Kind<M::MemoryKinds>,
) -> EvalResult<'tcx, MemoryPointer> {
assert_ne!(align, 0);
assert!(align.is_power_of_two());
@ -341,7 +342,15 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
Ok(MemoryPointer::new(id, 0))
}
pub fn reallocate(&mut self, ptr: MemoryPointer, old_size: u64, old_align: u64, new_size: u64, new_align: u64, kind: Kind) -> EvalResult<'tcx, MemoryPointer> {
pub fn reallocate(
&mut self,
ptr: MemoryPointer,
old_size: u64,
old_align: u64,
new_size: u64,
new_align: u64,
kind: Kind<M::MemoryKinds>,
) -> EvalResult<'tcx, MemoryPointer> {
use std::cmp::min;
if ptr.offset != 0 {
@ -349,7 +358,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
}
if let Ok(alloc) = self.get(ptr.alloc_id) {
if alloc.kind != kind {
return Err(EvalError::ReallocatedWrongMemoryKind(alloc.kind, kind));
return Err(EvalError::ReallocatedWrongMemoryKind(format!("{:?}", alloc.kind), format!("{:?}", kind)));
}
}
@ -361,7 +370,12 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
Ok(new_ptr)
}
pub fn deallocate(&mut self, ptr: MemoryPointer, size_and_align: Option<(u64, u64)>, kind: Kind) -> EvalResult<'tcx> {
pub fn deallocate(
&mut self,
ptr: MemoryPointer,
size_and_align: Option<(u64, u64)>,
kind: Kind<M::MemoryKinds>,
) -> EvalResult<'tcx> {
if ptr.offset != 0 {
return Err(EvalError::DeallocateNonBasePtr);
}
@ -380,7 +394,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
.map_err(|lock| EvalError::DeallocatedLockedMemory { ptr, lock })?;
if alloc.kind != kind {
return Err(EvalError::DeallocatedWrongMemoryKind(alloc.kind, kind));
return Err(EvalError::DeallocatedWrongMemoryKind(format!("{:?}", alloc.kind), format!("{:?}", kind)));
}
if let Some((size, align)) = size_and_align {
if size != alloc.bytes.len() as u64 || align != alloc.align {
@ -573,7 +587,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
/// Allocation accessors
impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
pub fn get(&self, id: AllocId) -> EvalResult<'tcx, &Allocation> {
pub fn get(&self, id: AllocId) -> EvalResult<'tcx, &Allocation<M::MemoryKinds>> {
match self.alloc_map.get(&id) {
Some(alloc) => Ok(alloc),
None => match self.functions.get(&id) {
@ -583,7 +597,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
}
}
fn get_mut_unchecked(&mut self, id: AllocId) -> EvalResult<'tcx, &mut Allocation> {
fn get_mut_unchecked(&mut self, id: AllocId) -> EvalResult<'tcx, &mut Allocation<M::MemoryKinds>> {
match self.alloc_map.get_mut(&id) {
Some(alloc) => Ok(alloc),
None => match self.functions.get(&id) {
@ -593,7 +607,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
}
}
pub fn get_mut(&mut self, id: AllocId) -> EvalResult<'tcx, &mut Allocation> {
pub fn get_mut(&mut self, id: AllocId) -> EvalResult<'tcx, &mut Allocation<M::MemoryKinds>> {
let alloc = self.get_mut_unchecked(id)?;
if alloc.mutable == Mutability::Mutable {
Ok(alloc)
@ -663,13 +677,11 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
}
let immutable = match (alloc.kind, alloc.mutable) {
(Kind::UninitializedStatic, _) => " (static in the process of initialization)",
(Kind::Static, Mutability::Mutable) => " (static mut)",
(Kind::Static, Mutability::Immutable) => " (immutable)",
(Kind::Env, _) => " (env var)",
(Kind::C, _) => " (malloc)",
(Kind::Rust, _) => " (heap)",
(Kind::Stack, _) => " (stack)",
(Kind::UninitializedStatic, _) => " (static in the process of initialization)".to_owned(),
(Kind::Static, Mutability::Mutable) => " (static mut)".to_owned(),
(Kind::Static, Mutability::Immutable) => " (immutable)".to_owned(),
(Kind::Machine(m), _) => format!(" ({:?})", m),
(Kind::Stack, _) => " (stack)".to_owned(),
};
trace!("{}({} bytes, alignment {}){}", msg, alloc.bytes.len(), alloc.align, immutable);
@ -793,17 +805,12 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
// E.g. `const Foo: &u32 = &1;` refers to the temp local that stores the `1`
Kind::Stack |
// The entire point of this function
Kind::UninitializedStatic |
// In the future const eval will allow heap allocations so we'll need to protect them
// from deallocation, too
Kind::Rust |
Kind::C => {},
Kind::UninitializedStatic => {},
Kind::Machine(m) => M::mark_static_initialized(m)?,
Kind::Static => {
trace!("mark_static_initalized: skipping already initialized static referred to by static currently being initialized");
return Ok(());
},
// FIXME: This could be allowed, but not for env vars set during miri execution
Kind::Env => return Err(EvalError::Unimplemented("statics can't refer to env vars".to_owned())),
}
*kind = Kind::Static;
*mutable = mutability;

View file

@ -2,6 +2,7 @@
i128_type,
rustc_private,
conservative_impl_trait,
never_type,
)]
// From rustc.

View file

@ -1,4 +1,4 @@
// error-pattern: tried to deallocate Stack memory but gave Rust as the kind
// error-pattern: tried to deallocate Stack memory but gave Machine(Rust) as the kind
fn main() {
let x = 42;