refactor away intermediate allocations, stage1

This commit is contained in:
Oliver Schneider 2016-09-23 10:27:14 +02:00
parent 38748fa615
commit 2282289ad5
No known key found for this signature in database
GPG Key ID: 56D6EEA0FC67AC46
8 changed files with 233 additions and 130 deletions

View File

@ -28,6 +28,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
U64(u) => self.cast_const_int(u, ty, false),
FnPtr(ptr) |
Ptr(ptr) => self.cast_ptr(ptr, ty),
VtablePtr(..) | SlicePtr(..) => unimplemented!(),
}
}

View File

@ -17,6 +17,7 @@ use syntax::codemap::{self, DUMMY_SP};
use error::{EvalError, EvalResult};
use memory::{Memory, Pointer, AllocId};
use primval::{self, PrimVal};
use self::value::Value;
use std::collections::HashMap;
@ -24,6 +25,7 @@ mod step;
mod terminator;
mod cast;
mod vtable;
mod value;
pub struct EvalContext<'a, 'tcx: 'a> {
/// The results of the type checker, from rustc.
@ -99,21 +101,6 @@ pub struct Frame<'a, 'tcx: 'a> {
pub stmt: usize,
}
/// A `Value` represents a single self-contained Rust value.
///
/// A `Value` can either refer to a block of memory inside an allocation (`ByRef`) or to a primitve
/// value held directly, outside of any allocation (`ByVal`).
///
/// For optimization of a few very common cases, there is also a representation for a pair of
/// primitive values (`ByValPair`). It allows Miri to avoid making allocations for checked binary
/// operations and fat pointers. This idea was taken from rustc's trans.
#[derive(Clone, Copy, Debug)]
enum Value {
ByRef(Pointer),
ByVal(PrimVal),
ByValPair(PrimVal, PrimVal),
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
struct Lvalue {
ptr: Pointer,
@ -245,10 +232,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
let ptr = self.memory.allocate(s.len(), 1)?;
self.memory.write_bytes(ptr, s.as_bytes())?;
self.memory.freeze(ptr.alloc_id)?;
Value::ByValPair(
PrimVal::Ptr(ptr),
self.target_usize_primval(s.len() as u64)
)
Value::ByVal(PrimVal::SlicePtr(ptr, s.len() as u64))
}
ByteStr(ref bs) => {
@ -618,13 +602,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
use rustc::mir::repr::CastKind::*;
match kind {
Unsize => {
let src = self.eval_operand_to_ptr(operand)?;
let src = self.eval_operand(operand)?;
let src_ty = self.operand_ty(operand);
let dest_ty = self.monomorphize(dest_ty, self.substs());
// FIXME: cases where dest_ty is not a fat pointer. e.g. Arc<Struct> -> Arc<Trait>
assert!(self.type_is_fat_ptr(dest_ty));
let (ptr, extra) = self.get_fat_ptr(dest);
self.move_(src, ptr, src_ty)?;
self.move_value(src, ptr, src_ty)?;
let src_pointee_ty = pointee_type(src_ty).unwrap();
let dest_pointee_ty = pointee_type(dest_ty).unwrap();
@ -639,9 +623,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
// For now, upcasts are limited to changes in marker
// traits, and hence never actually require an actual
// change to the vtable.
let (_, src_extra) = self.get_fat_ptr(src);
let src_extra = self.memory.read_ptr(src_extra)?;
self.memory.write_ptr(extra, src_extra)?;
let src_extra = src.expect_fat_ptr_extra(&self.memory)?;
self.memory.write_primval(extra, src_extra)?;
},
(_, &ty::TyTrait(ref data)) => {
let trait_ref = data.principal.with_self_ty(self.tcx, src_pointee_ty);
@ -655,25 +638,36 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}
Misc => {
let src = self.eval_operand_to_ptr(operand)?;
let src = self.eval_operand(operand)?;
let src_ty = self.operand_ty(operand);
// FIXME: dest_ty should already be monomorphized
let dest_ty = self.monomorphize(dest_ty, self.substs());
if self.type_is_fat_ptr(src_ty) {
let (data_ptr, _meta_ptr) = self.get_fat_ptr(src);
trace!("misc cast: {:?}", src);
let ptr_size = self.memory.pointer_size();
let dest_ty = self.monomorphize(dest_ty, self.substs());
if self.type_is_fat_ptr(dest_ty) {
// FIXME: add assertion that the extra part of the src_ty and
// dest_ty is of the same type
self.memory.copy(data_ptr, dest, ptr_size * 2, ptr_size)?;
} else { // cast to thin-ptr
// Cast of fat-ptr to thin-ptr is an extraction of data-ptr and
// pointer-cast of that pointer to desired pointer type.
self.memory.copy(data_ptr, dest, ptr_size, ptr_size)?;
match (src, self.type_is_fat_ptr(dest_ty)) {
(Value::ByVal(PrimVal::VtablePtr(data, meta)), true) => {
self.memory.write_ptr(dest, data)?;
self.memory.write_ptr(dest.offset(ptr_size as isize), meta)?;
},
(Value::ByVal(PrimVal::SlicePtr(data, meta)), true) => {
self.memory.write_ptr(dest, data)?;
self.memory.write_usize(dest.offset(ptr_size as isize), meta)?;
},
(Value::ByVal(PrimVal::SlicePtr(data, _)), false) |
(Value::ByVal(PrimVal::VtablePtr(data, _)), false) => {
self.memory.write_ptr(dest, data)?;
},
(Value::ByRef(ptr), true) => {
self.memory.copy(ptr, dest, ptr_size * 2, ptr_size)?;
},
(Value::ByRef(ptr), false) => {
self.memory.copy(ptr, dest, ptr_size, ptr_size)?;
},
(Value::ByVal(_), _) => bug!("expected fat ptr"),
}
} else {
// FIXME: dest_ty should already be monomorphized
let dest_ty = self.monomorphize(dest_ty, self.substs());
let src_val = self.read_primval(src, src_ty)?;
let src_val = self.value_to_primval(src, src_ty)?;
let dest_val = self.cast_primval(src_val, dest_ty)?;
self.memory.write_primval(dest, dest_val)?;
}
@ -689,8 +683,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
UnsafeFnPointer => match dest_ty.sty {
ty::TyFnPtr(unsafe_fn_ty) => {
let src = self.eval_operand_to_ptr(operand)?;
let ptr = self.memory.read_ptr(src)?;
let src = self.eval_operand(operand)?;
let ptr = src.read_ptr(&self.memory)?;
let (def_id, substs, _) = self.memory.get_fn(ptr.alloc_id)?;
let fn_ptr = self.memory.create_fn_ptr(def_id, substs, unsafe_fn_ty);
self.memory.write_ptr(dest, fn_ptr)?;
@ -779,14 +773,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}
}
// FIXME(solson): This method unnecessarily allocates and should not be necessary. We can
// remove it as soon as PrimVal can represent fat pointers.
fn eval_operand_to_ptr(&mut self, op: &mir::Operand<'tcx>) -> EvalResult<'tcx, Pointer> {
let value = self.eval_operand(op)?;
let ty = self.operand_ty(op);
self.value_to_ptr(value, ty)
}
fn eval_operand_to_primval(&mut self, op: &mir::Operand<'tcx>) -> EvalResult<'tcx, PrimVal> {
let value = self.eval_operand(op)?;
let ty = self.operand_ty(op);
@ -857,6 +843,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
Projection(ref proj) => {
let base = self.eval_lvalue(&proj.base)?;
trace!("projection base: {:?}", base);
trace!("projection: {:?}", proj.elem);
let base_ty = self.lvalue_ty(&proj.base);
let base_layout = self.type_layout(base_ty);
@ -937,8 +925,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
ty::TySlice(elem_ty) => self.type_size(elem_ty),
_ => bug!("indexing expected an array or slice, got {:?}", base_ty),
};
let n_ptr = self.eval_operand_to_ptr(operand)?;
let n = self.memory.read_usize(n_ptr)?;
let n_ptr = self.eval_operand(operand)?;
let usize = self.tcx.types.usize;
let n = self.value_to_primval(n_ptr, usize)?.expect_uint("Projection::Index expected usize");
base.ptr.offset(n as isize * elem_size as isize)
}
@ -965,6 +954,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
self.monomorphize(operand.ty(&self.mir(), self.tcx), self.substs())
}
fn move_value(&mut self, src: Value, dest: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, ()> {
match src {
Value::ByRef(ptr) => self.move_(ptr, dest, ty),
Value::ByVal(val) => self.memory.write_primval(dest, val),
}
}
fn move_(&mut self, src: Pointer, dest: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, ()> {
let size = self.type_size(ty);
let align = self.type_align(ty);
@ -974,7 +970,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
// FIXME(solson): This method unnecessarily allocates and should not be necessary. We can
// remove it as soon as PrimVal can represent fat pointers.
fn value_to_ptr(&mut self, value: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, Pointer> {
fn value_to_ptr_dont_use(&mut self, value: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, Pointer> {
match value {
Value::ByRef(ptr) => Ok(ptr),
@ -985,18 +981,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
self.memory.write_primval(ptr, primval)?;
Ok(ptr)
}
Value::ByValPair(primval1, primval2) => {
let size = self.type_size(ty);
let align = self.type_align(ty);
let ptr = self.memory.allocate(size, align)?;
// FIXME(solson): Major dangerous assumptions here. Ideally obliterate this
// function.
self.memory.write_primval(ptr, primval1)?;
self.memory.write_primval(ptr.offset((size / 2) as isize), primval2)?;
Ok(ptr)
}
}
}
@ -1006,7 +990,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
// TODO(solson): Sanity-check the primval type against the input type.
Value::ByVal(primval) => Ok(primval),
Value::ByValPair(..) => bug!("can't turn a ByValPair into a single PrimVal"),
}
}
@ -1019,14 +1002,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
match value {
Value::ByRef(ptr) => self.move_(ptr, dest, dest_ty),
Value::ByVal(primval) => self.memory.write_primval(dest, primval),
Value::ByValPair(primval1, primval2) => {
let size = self.type_size(dest_ty);
// FIXME(solson): Major dangerous assumptions here.
self.memory.write_primval(dest, primval1)?;
self.memory.write_primval(dest.offset((size / 2) as isize), primval2)?;
Ok(())
}
}
}

View File

@ -7,7 +7,8 @@ use rustc::ty;
use error::{EvalError, EvalResult};
use memory::Pointer;
use interpreter::EvalContext;
use primval;
use primval::{self, PrimVal};
use interpreter::value::Value;
impl<'a, 'tcx> EvalContext<'a, 'tcx> {
pub(super) fn call_intrinsic(
@ -18,12 +19,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
dest: Pointer,
dest_layout: &'tcx Layout,
) -> EvalResult<'tcx, ()> {
// TODO(solson): We can probably remove this _to_ptr easily.
let args_res: EvalResult<Vec<Pointer>> = args.iter()
.map(|arg| self.eval_operand_to_ptr(arg))
let args_ptrs: EvalResult<Vec<Value>> = args.iter()
.map(|arg| self.eval_operand(arg))
.collect();
let args_ptrs = args_res?;
let args_ptrs = args_ptrs?;
let pointer_size = self.memory.pointer_size();
let i32 = self.tcx.types.i32;
let isize = self.tcx.types.isize;
let usize = self.tcx.types.usize;
let f32 = self.tcx.types.f32;
let f64 = self.tcx.types.f64;
match &self.tcx.item_name(def_id).as_str()[..] {
"add_with_overflow" => self.intrinsic_with_overflow(mir::BinOp::Add, &args[0], &args[1], dest, dest_layout)?,
@ -31,14 +36,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
"mul_with_overflow" => self.intrinsic_with_overflow(mir::BinOp::Mul, &args[0], &args[1], dest, dest_layout)?,
"arith_offset" => {
let ptr = self.memory.read_ptr(args_ptrs[0])?;
let offset = self.memory.read_int(args_ptrs[1], pointer_size)?;
let ptr = args_ptrs[0].read_ptr(&self.memory)?;
let offset = self.value_to_primval(args_ptrs[1], isize)?.expect_int("arith_offset second arg not isize");
let new_ptr = ptr.offset(offset as isize);
self.memory.write_ptr(dest, new_ptr)?;
}
"assume" => {
if !self.memory.read_bool(args_ptrs[0])? {
let bool = self.tcx.types.bool;
if !self.value_to_primval(args_ptrs[0], bool)?.expect_bool("assume arg not bool") {
return Err(EvalError::AssumptionNotHeld);
}
}
@ -51,47 +57,59 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
let elem_ty = substs.type_at(0);
let elem_size = self.type_size(elem_ty);
let elem_align = self.type_align(elem_ty);
let src = self.memory.read_ptr(args_ptrs[0])?;
let dest = self.memory.read_ptr(args_ptrs[1])?;
let count = self.memory.read_isize(args_ptrs[2])?;
let src = args_ptrs[0].read_ptr(&self.memory)?;
let dest = args_ptrs[1].read_ptr(&self.memory)?;
let count = self.value_to_primval(args_ptrs[2], usize)?.expect_uint("arith_offset second arg not isize");
self.memory.copy(src, dest, count as usize * elem_size, elem_align)?;
}
"ctpop" => {
let elem_ty = substs.type_at(0);
let elem_size = self.type_size(elem_ty);
let num = self.memory.read_uint(args_ptrs[0], elem_size)?.count_ones();
let num = self.value_to_primval(args_ptrs[2], elem_ty)?.expect_int("ctpop second arg not integral");
let num = num.count_ones();
self.memory.write_uint(dest, num.into(), elem_size)?;
}
"ctlz" => {
let elem_ty = substs.type_at(0);
let elem_size = self.type_size(elem_ty);
let num = self.memory.read_uint(args_ptrs[0], elem_size)?.leading_zeros();
let num = self.value_to_primval(args_ptrs[2], elem_ty)?;
let num = match num {
PrimVal::I8(i) => i.leading_zeros(),
PrimVal::U8(i) => i.leading_zeros(),
PrimVal::I16(i) => i.leading_zeros(),
PrimVal::U16(i) => i.leading_zeros(),
PrimVal::I32(i) => i.leading_zeros(),
PrimVal::U32(i) => i.leading_zeros(),
PrimVal::I64(i) => i.leading_zeros(),
PrimVal::U64(i) => i.leading_zeros(),
_ => bug!("ctlz called with non-integer type"),
};
self.memory.write_uint(dest, num.into(), elem_size)?;
}
"discriminant_value" => {
let ty = substs.type_at(0);
let adt_ptr = self.memory.read_ptr(args_ptrs[0])?;
let adt_ptr = args_ptrs[0].read_ptr(&self.memory)?;
let discr_val = self.read_discriminant_value(adt_ptr, ty)?;
self.memory.write_uint(dest, discr_val, 8)?;
}
"fabsf32" => {
let f = self.memory.read_f32(args_ptrs[0])?;
let f = self.value_to_primval(args_ptrs[2], f32)?.expect_f32("fabsf32 read non f32");
self.memory.write_f32(dest, f.abs())?;
}
"fabsf64" => {
let f = self.memory.read_f64(args_ptrs[0])?;
let f = self.value_to_primval(args_ptrs[2], f64)?.expect_f64("fabsf64 read non f64");
self.memory.write_f64(dest, f.abs())?;
}
"fadd_fast" => {
let ty = substs.type_at(0);
let a = self.read_primval(args_ptrs[0], ty)?;
let b = self.read_primval(args_ptrs[0], ty)?;
let a = self.value_to_primval(args_ptrs[0], ty)?;
let b = self.value_to_primval(args_ptrs[0], ty)?;
let result = primval::binary_op(mir::BinOp::Add, a, b)?;
self.memory.write_primval(dest, result.0)?;
}
@ -117,8 +135,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
"move_val_init" => {
let ty = substs.type_at(0);
let ptr = self.memory.read_ptr(args_ptrs[0])?;
self.move_(args_ptrs[1], ptr, ty)?;
let ptr = args_ptrs[0].read_ptr(&self.memory)?;
self.move_value(args_ptrs[1], ptr, ty)?;
}
"needs_drop" => {
@ -129,10 +147,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
"offset" => {
let pointee_ty = substs.type_at(0);
let pointee_size = self.type_size(pointee_ty) as isize;
let ptr_arg = args_ptrs[0];
let offset = self.memory.read_isize(args_ptrs[1])?;
let offset = self.value_to_primval(args_ptrs[1], isize)?.expect_int("offset second arg not isize");
let ptr = self.memory.read_ptr(ptr_arg)?;
let ptr = args_ptrs[0].read_ptr(&self.memory)?;
let result_ptr = ptr.offset(offset as isize * pointee_size);
self.memory.write_ptr(dest, result_ptr)?;
}
@ -150,24 +167,24 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}
"powif32" => {
let f = self.memory.read_f32(args_ptrs[0])?;
let i = self.memory.read_int(args_ptrs[1], 4)?;
let f = self.value_to_primval(args_ptrs[0], f32)?.expect_f32("powif32 first arg not f32");
let i = self.value_to_primval(args_ptrs[1], i32)?.expect_int("powif32 second arg not i32");
self.memory.write_f32(dest, f.powi(i as i32))?;
}
"powif64" => {
let f = self.memory.read_f32(args_ptrs[0])?;
let i = self.memory.read_int(args_ptrs[1], 4)?;
self.memory.write_f32(dest, f.powi(i as i32))?;
let f = self.value_to_primval(args_ptrs[0], f64)?.expect_f64("powif64 first arg not f64");
let i = self.value_to_primval(args_ptrs[1], i32)?.expect_int("powif64 second arg not i32");
self.memory.write_f64(dest, f.powi(i as i32))?;
}
"sqrtf32" => {
let f = self.memory.read_f32(args_ptrs[0])?;
let f = self.value_to_primval(args_ptrs[0], f32)?.expect_f32("sqrtf32 first arg not f32");
self.memory.write_f32(dest, f.sqrt())?;
}
"sqrtf64" => {
let f = self.memory.read_f64(args_ptrs[0])?;
let f = self.value_to_primval(args_ptrs[0], f64)?.expect_f64("sqrtf64 first arg not f64");
self.memory.write_f64(dest, f.sqrt())?;
}
@ -198,7 +215,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
"transmute" => {
let ty = substs.type_at(0);
self.move_(args_ptrs[0], dest, ty)?;
self.move_value(args_ptrs[0], dest, ty)?;
}
"try" => unimplemented!(),
@ -207,14 +224,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
"volatile_load" => {
let ty = substs.type_at(0);
let ptr = self.memory.read_ptr(args_ptrs[0])?;
let ptr = args_ptrs[0].read_ptr(&self.memory)?;
self.move_(ptr, dest, ty)?;
}
"volatile_store" => {
let ty = substs.type_at(0);
let dest = self.memory.read_ptr(args_ptrs[0])?;
self.move_(args_ptrs[1], dest, ty)?;
let dest = args_ptrs[0].read_ptr(&self.memory)?;
self.move_value(args_ptrs[1], dest, ty)?;
}
name => return Err(EvalError::Unimplemented(format!("unimplemented intrinsic: {}", name))),
@ -229,7 +246,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
fn size_and_align_of_dst(
&self,
ty: ty::Ty<'tcx>,
value: Pointer,
value: Value,
) -> EvalResult<'tcx, (u64, u64)> {
let pointer_size = self.memory.pointer_size();
if self.type_is_sized(ty) {
@ -306,8 +323,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}
}
ty::TyTrait(..) => {
let (_, vtable) = self.get_fat_ptr(value);
let vtable = self.memory.read_ptr(vtable)?;
let vtable = value.expect_vtable(&self.memory)?;
// the second entry in the vtable is the dynamic size of the object.
let size = self.memory.read_usize(vtable.offset(pointer_size as isize))?;
let align = self.memory.read_usize(vtable.offset(pointer_size as isize * 2))?;
@ -317,10 +333,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
ty::TySlice(_) | ty::TyStr => {
let elem_ty = ty.sequence_element_type(self.tcx);
let elem_size = self.type_size(elem_ty) as u64;
let (_, len_ptr) = self.get_fat_ptr(value);
let n = self.memory.read_usize(len_ptr)?;
let len = value.expect_slice_len(&self.memory)?;
let align = self.type_align(elem_ty);
Ok((n * elem_size, align as u64))
Ok((len * elem_size, align as u64))
}
_ => bug!("size_of_val::<{:?}>", ty),

View File

@ -14,7 +14,8 @@ use syntax::{ast, attr};
use error::{EvalError, EvalResult};
use memory::Pointer;
use primval::PrimVal;
use super::{EvalContext, IntegerExt, StackPopCleanup, Value};
use super::{EvalContext, IntegerExt, StackPopCleanup};
use super::value::Value;
mod intrinsics;
@ -265,9 +266,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
None => name.as_str(),
};
// TODO(solson): We can probably remove this _to_ptr easily.
let args_res: EvalResult<Vec<Pointer>> = args.iter()
.map(|arg| self.eval_operand_to_ptr(arg))
let args_res: EvalResult<Vec<Value>> = args.iter()
.map(|arg| self.eval_operand(arg))
.collect();
let args = args_res?;
@ -276,26 +276,28 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
return Ok(());
}
let usize = self.tcx.types.usize;
match &link_name[..] {
"__rust_allocate" => {
let size = self.memory.read_usize(args[0])?;
let align = self.memory.read_usize(args[1])?;
let size = self.value_to_primval(args[0], usize)?.expect_uint("__rust_allocate first arg not usize");
let align = self.value_to_primval(args[1], usize)?.expect_uint("__rust_allocate second arg not usize");
let ptr = self.memory.allocate(size as usize, align as usize)?;
self.memory.write_ptr(dest, ptr)?;
}
"__rust_reallocate" => {
let ptr = self.memory.read_ptr(args[0])?;
let size = self.memory.read_usize(args[2])?;
let align = self.memory.read_usize(args[3])?;
let ptr = args[0].read_ptr(&self.memory)?;
let size = self.value_to_primval(args[2], usize)?.expect_uint("__rust_reallocate third arg not usize");
let align = self.value_to_primval(args[3], usize)?.expect_uint("__rust_reallocate fourth arg not usize");
let new_ptr = self.memory.reallocate(ptr, size as usize, align as usize)?;
self.memory.write_ptr(dest, new_ptr)?;
}
"memcmp" => {
let left = self.memory.read_ptr(args[0])?;
let right = self.memory.read_ptr(args[1])?;
let n = self.memory.read_usize(args[2])? as usize;
let left = args[0].read_ptr(&self.memory)?;
let right = args[1].read_ptr(&self.memory)?;
let n = self.value_to_primval(args[2], usize)?.expect_uint("__rust_reallocate first arg not usize") as usize;
let result = {
let left_bytes = self.memory.read_bytes(left, n)?;
@ -419,7 +421,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
// intermediate function call.
// FIXME: this is a memory leak, should probably add the pointer to the
// current stack.
let first = self.value_to_ptr(args[0].0, args[0].1)?;
let first = self.value_to_ptr_dont_use(args[0].0, args[0].1)?;
args[0].0 = Value::ByVal(PrimVal::Ptr(first));
args[0].1 = self.tcx.mk_mut_ptr(args[0].1);
}
@ -442,11 +444,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
traits::VtableObject(ref data) => {
let idx = self.tcx.get_vtable_index_of_object_method(data, def_id);
if let Some(&mut(ref mut first_arg, ref mut first_ty)) = args.get_mut(0) {
// FIXME(solson): Remove this allocating hack.
let ptr = self.value_to_ptr(*first_arg, *first_ty)?;
*first_arg = Value::ByRef(ptr);
let (_, vtable) = self.get_fat_ptr(ptr);
let vtable = self.memory.read_ptr(vtable)?;
let vtable = first_arg.expect_vtable(&self.memory)?;
let idx = idx + 3;
let offset = idx * self.memory.pointer_size();
let fn_ptr = self.memory.read_ptr(vtable.offset(offset as isize))?;

60
src/interpreter/value.rs Normal file
View File

@ -0,0 +1,60 @@
use memory::{Memory, Pointer};
use error::EvalResult;
use primval::PrimVal;
/// A `Value` represents a single self-contained Rust value.
///
/// A `Value` can either refer to a block of memory inside an allocation (`ByRef`) or to a primitve
/// value held directly, outside of any allocation (`ByVal`).
///
/// For optimization of a few very common cases, there is also a representation for a pair of
/// primitive values (`ByValPair`). It allows Miri to avoid making allocations for checked binary
/// operations and fat pointers. This idea was taken from rustc's trans.
#[derive(Clone, Copy, Debug)]
pub(super) enum Value {
ByRef(Pointer),
ByVal(PrimVal),
}
impl Value {
pub(super) fn read_ptr<'a, 'tcx: 'a>(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, Pointer> {
use self::Value::*;
match *self {
ByRef(ptr) => mem.read_ptr(ptr),
ByVal(PrimVal::Ptr(ptr)) |
ByVal(PrimVal::FnPtr(ptr)) => Ok(ptr),
ByVal(_other) => unimplemented!(),
}
}
pub(super) fn expect_vtable<'a, 'tcx: 'a>(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, Pointer> {
use self::Value::*;
match *self {
ByRef(ptr) => mem.read_ptr(ptr.offset(mem.pointer_size() as isize)),
ByVal(PrimVal::VtablePtr(_, vtable)) => Ok(vtable),
_ => unimplemented!(),
}
}
pub(super) fn expect_slice_len<'a, 'tcx: 'a>(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, u64> {
use self::Value::*;
match *self {
ByRef(ptr) => mem.read_usize(ptr.offset(mem.pointer_size() as isize)),
ByVal(PrimVal::SlicePtr(_, len)) => Ok(len),
_ => unimplemented!(),
}
}
pub(super) fn expect_fat_ptr_extra<'a, 'tcx: 'a>(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, PrimVal> {
use self::Value::*;
match (*self, mem.pointer_size()) {
(ByRef(ptr), size) => mem.read_ptr(ptr.offset(size as isize)).map(PrimVal::Ptr),
(ByVal(PrimVal::SlicePtr(_, len)), 8) => Ok(PrimVal::U64(len)),
(ByVal(PrimVal::SlicePtr(_, len)), 4) => Ok(PrimVal::U32(len as u32)),
(ByVal(PrimVal::SlicePtr(_, len)), 2) => Ok(PrimVal::U16(len as u16)),
(ByVal(PrimVal::SlicePtr(_, len)), 1) => Ok(PrimVal::U8(len as u8)),
(ByVal(PrimVal::VtablePtr(_, ptr)), _) => Ok(PrimVal::Ptr(ptr)),
_ => unimplemented!(),
}
}
}

View File

@ -55,6 +55,9 @@ impl Pointer {
pub fn points_to_zst(&self) -> bool {
self.alloc_id == ZST_ALLOC_ID
}
pub fn to_int(&self) -> usize {
self.offset
}
pub fn from_int(i: usize) -> Self {
Pointer {
alloc_id: ZST_ALLOC_ID,
@ -543,6 +546,20 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
PrimVal::F64(f) => self.write_f64(ptr, f),
PrimVal::FnPtr(p) |
PrimVal::Ptr(p) => self.write_ptr(ptr, p),
PrimVal::VtablePtr(p, v) => {
assert_eq!(layout::FAT_PTR_ADDR, 0);
assert_eq!(layout::FAT_PTR_EXTRA, 1);
self.write_ptr(ptr, p)?;
let vptr = ptr.offset(self.pointer_size() as isize);
self.write_ptr(vptr, v)
}
PrimVal::SlicePtr(p, n) => {
assert_eq!(layout::FAT_PTR_ADDR, 0);
assert_eq!(layout::FAT_PTR_EXTRA, 1);
self.write_ptr(ptr, p)?;
let nptr = ptr.offset(self.pointer_size() as isize);
self.write_usize(nptr, n)
}
}
}

View File

@ -14,6 +14,8 @@ pub enum PrimVal {
Ptr(Pointer),
FnPtr(Pointer),
VtablePtr(Pointer, Pointer),
SlicePtr(Pointer, u64),
Char(char),
F32(f32), F64(f64),
@ -32,7 +34,10 @@ macro_rules! declare_expect_fn {
impl PrimVal {
declare_expect_fn!(expect_bool, Bool, bool);
declare_expect_fn!(expect_f32, F32, f32);
declare_expect_fn!(expect_f64, F64, f64);
declare_expect_fn!(expect_fn_ptr, FnPtr, Pointer);
declare_expect_fn!(expect_ptr, Ptr, Pointer);
pub fn expect_uint(self, error_msg: &str) -> u64 {
use self::PrimVal::*;
@ -41,6 +46,19 @@ impl PrimVal {
U16(u) => u as u64,
U32(u) => u as u64,
U64(u) => u,
Ptr(ptr) => ptr.to_int() as u64,
_ => bug!("{}", error_msg),
}
}
pub fn expect_int(self, error_msg: &str) -> i64 {
use self::PrimVal::*;
match self {
I8(i) => i as i64,
I16(i) => i as i64,
I32(i) => i as i64,
I64(i) => i,
Ptr(ptr) => ptr.to_int() as i64,
_ => bug!("{}", error_msg),
}
}

View File

@ -0,0 +1,19 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::sync::Arc;
trait Foo {}
impl Foo for [u8; 2] {}
fn main() {
let _: Arc<Foo + Send> = Arc::new([3, 4]);
}