Merge pull request #60 from oli-obk/dst
some more cleanups getting rid of intermediate allocations and bad fat ptr assumptions
This commit is contained in:
commit
10ab168db9
9 changed files with 430 additions and 202 deletions
|
@ -8,7 +8,7 @@ use primval::PrimVal;
|
|||
use memory::Pointer;
|
||||
|
||||
use rustc::ty::Ty;
|
||||
use syntax::ast;
|
||||
use syntax::ast::{self, IntTy, UintTy};
|
||||
|
||||
impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||
pub(super) fn cast_primval(&self, val: PrimVal, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> {
|
||||
|
@ -37,6 +37,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
ty::TyRef(..) |
|
||||
ty::TyRawPtr(_) => Ok(Ptr(ptr)),
|
||||
ty::TyFnPtr(_) => Ok(FnPtr(ptr)),
|
||||
ty::TyInt(IntTy::I8) => Ok(I8(ptr.to_int()? as i8)),
|
||||
ty::TyInt(IntTy::I16) => Ok(I16(ptr.to_int()? as i16)),
|
||||
ty::TyInt(IntTy::I32) => Ok(I32(ptr.to_int()? as i32)),
|
||||
ty::TyInt(IntTy::I64) => Ok(I64(ptr.to_int()? as i64)),
|
||||
ty::TyUint(UintTy::U8) => Ok(U8(ptr.to_int()? as u8)),
|
||||
ty::TyUint(UintTy::U16) => Ok(U16(ptr.to_int()? as u16)),
|
||||
ty::TyUint(UintTy::U32) => Ok(U32(ptr.to_int()? as u32)),
|
||||
ty::TyUint(UintTy::U64) => Ok(U64(ptr.to_int()? as u64)),
|
||||
_ => Err(EvalError::Unimplemented(format!("ptr to {:?} cast", ty))),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
@ -217,45 +204,45 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn str_to_value(&mut self, s: &str) -> EvalResult<'tcx, Value> {
|
||||
// FIXME: cache these allocs
|
||||
let ptr = self.memory.allocate(s.len(), 1)?;
|
||||
self.memory.write_bytes(ptr, s.as_bytes())?;
|
||||
self.memory.freeze(ptr.alloc_id)?;
|
||||
Ok(Value::ByValPair(PrimVal::Ptr(ptr), self.target_usize_primval(s.len() as u64)))
|
||||
}
|
||||
|
||||
fn const_to_value(&mut self, const_val: &ConstVal) -> EvalResult<'tcx, Value> {
|
||||
use rustc::middle::const_val::ConstVal::*;
|
||||
use rustc_const_math::{ConstInt, ConstIsize, ConstUsize, ConstFloat};
|
||||
|
||||
let primval = match *const_val {
|
||||
Integral(ConstInt::I8(i)) => Value::ByVal(PrimVal::I8(i)),
|
||||
Integral(ConstInt::U8(i)) => Value::ByVal(PrimVal::U8(i)),
|
||||
Integral(ConstInt::I8(i)) => PrimVal::I8(i),
|
||||
Integral(ConstInt::U8(i)) => PrimVal::U8(i),
|
||||
Integral(ConstInt::Isize(ConstIsize::Is16(i))) |
|
||||
Integral(ConstInt::I16(i)) => Value::ByVal(PrimVal::I16(i)),
|
||||
Integral(ConstInt::I16(i)) => PrimVal::I16(i),
|
||||
Integral(ConstInt::Usize(ConstUsize::Us16(i))) |
|
||||
Integral(ConstInt::U16(i)) => Value::ByVal(PrimVal::U16(i)),
|
||||
Integral(ConstInt::U16(i)) => PrimVal::U16(i),
|
||||
Integral(ConstInt::Isize(ConstIsize::Is32(i))) |
|
||||
Integral(ConstInt::I32(i)) => Value::ByVal(PrimVal::I32(i)),
|
||||
Integral(ConstInt::I32(i)) => PrimVal::I32(i),
|
||||
Integral(ConstInt::Usize(ConstUsize::Us32(i))) |
|
||||
Integral(ConstInt::U32(i)) => Value::ByVal(PrimVal::U32(i)),
|
||||
Integral(ConstInt::U32(i)) => PrimVal::U32(i),
|
||||
Integral(ConstInt::Isize(ConstIsize::Is64(i))) |
|
||||
Integral(ConstInt::I64(i)) => Value::ByVal(PrimVal::I64(i)),
|
||||
Integral(ConstInt::I64(i)) => PrimVal::I64(i),
|
||||
Integral(ConstInt::Usize(ConstUsize::Us64(i))) |
|
||||
Integral(ConstInt::U64(i)) => Value::ByVal(PrimVal::U64(i)),
|
||||
Float(ConstFloat::F32(f)) => Value::ByVal(PrimVal::F32(f)),
|
||||
Float(ConstFloat::F64(f)) => Value::ByVal(PrimVal::F64(f)),
|
||||
Bool(b) => Value::ByVal(PrimVal::Bool(b)),
|
||||
Char(c) => Value::ByVal(PrimVal::Char(c)),
|
||||
Integral(ConstInt::U64(i)) => PrimVal::U64(i),
|
||||
Float(ConstFloat::F32(f)) => PrimVal::F32(f),
|
||||
Float(ConstFloat::F64(f)) => PrimVal::F64(f),
|
||||
Bool(b) => PrimVal::Bool(b),
|
||||
Char(c) => PrimVal::Char(c),
|
||||
|
||||
Str(ref s) => {
|
||||
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)
|
||||
)
|
||||
}
|
||||
Str(ref s) => return self.str_to_value(s),
|
||||
|
||||
ByteStr(ref bs) => {
|
||||
let ptr = self.memory.allocate(bs.len(), 1)?;
|
||||
self.memory.write_bytes(ptr, bs)?;
|
||||
self.memory.freeze(ptr.alloc_id)?;
|
||||
Value::ByVal(PrimVal::Ptr(ptr))
|
||||
PrimVal::Ptr(ptr)
|
||||
}
|
||||
|
||||
Struct(_) => unimplemented!(),
|
||||
|
@ -271,7 +258,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
bug!("uninferred constants only exist before typeck"),
|
||||
};
|
||||
|
||||
Ok(primval)
|
||||
Ok(Value::ByVal(primval))
|
||||
}
|
||||
|
||||
fn type_is_sized(&self, ty: Ty<'tcx>) -> bool {
|
||||
|
@ -578,30 +565,25 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
Len(ref lvalue) => {
|
||||
let src = self.eval_lvalue(lvalue)?;
|
||||
let ty = self.lvalue_ty(lvalue);
|
||||
let len = match ty.sty {
|
||||
ty::TyArray(_, n) => n as u64,
|
||||
ty::TySlice(_) => if let LvalueExtra::Length(n) = src.extra {
|
||||
n
|
||||
match ty.sty {
|
||||
ty::TyArray(_, n) => self.memory.write_usize(dest, n as u64)?,
|
||||
ty::TySlice(_) => if let LvalueExtra::Length(len) = src.extra {
|
||||
self.memory.write_usize(dest, len)?;
|
||||
} else {
|
||||
bug!("Rvalue::Len of a slice given non-slice pointer: {:?}", src);
|
||||
},
|
||||
_ => bug!("Rvalue::Len expected array or slice, got {:?}", ty),
|
||||
};
|
||||
self.memory.write_usize(dest, len)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ref(_, _, ref lvalue) => {
|
||||
let lv = self.eval_lvalue(lvalue)?;
|
||||
let (ptr, extra) = self.get_fat_ptr(dest);
|
||||
self.memory.write_ptr(ptr, lv.ptr)?;
|
||||
match lv.extra {
|
||||
let lvalue = self.eval_lvalue(lvalue)?;
|
||||
self.memory.write_ptr(dest, lvalue.ptr)?;
|
||||
let extra_ptr = dest.offset(self.memory.pointer_size() as isize);
|
||||
match lvalue.extra {
|
||||
LvalueExtra::None => {},
|
||||
LvalueExtra::Length(len) => {
|
||||
self.memory.write_usize(extra, len)?;
|
||||
}
|
||||
LvalueExtra::Vtable(ptr) => {
|
||||
self.memory.write_ptr(extra, ptr)?;
|
||||
},
|
||||
LvalueExtra::Length(len) => self.memory.write_usize(extra_ptr, len)?,
|
||||
LvalueExtra::Vtable(ptr) => self.memory.write_ptr(extra_ptr, ptr)?,
|
||||
LvalueExtra::DowncastVariant(..) =>
|
||||
bug!("attempted to take a reference to an enum downcast lvalue"),
|
||||
}
|
||||
|
@ -618,13 +600,11 @@ 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)?;
|
||||
let src_pointee_ty = pointee_type(src_ty).unwrap();
|
||||
let dest_pointee_ty = pointee_type(dest_ty).unwrap();
|
||||
|
||||
|
@ -633,21 +613,28 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
|
||||
match (&src_pointee_ty.sty, &dest_pointee_ty.sty) {
|
||||
(&ty::TyArray(_, length), &ty::TySlice(_)) => {
|
||||
self.memory.write_usize(extra, length as u64)?;
|
||||
let ptr = src.read_ptr(&self.memory)?;
|
||||
self.memory.write_ptr(dest, ptr)?;
|
||||
let ptr_size = self.memory.pointer_size() as isize;
|
||||
let dest_extra = dest.offset(ptr_size);
|
||||
self.memory.write_usize(dest_extra, length as u64)?;
|
||||
}
|
||||
(&ty::TyTrait(_), &ty::TyTrait(_)) => {
|
||||
// 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)?;
|
||||
self.write_value(src, dest, dest_ty)?;
|
||||
},
|
||||
(_, &ty::TyTrait(ref data)) => {
|
||||
let trait_ref = data.principal.with_self_ty(self.tcx, src_pointee_ty);
|
||||
let trait_ref = self.tcx.erase_regions(&trait_ref);
|
||||
let vtable = self.get_vtable(trait_ref)?;
|
||||
self.memory.write_ptr(extra, vtable)?;
|
||||
let ptr = src.read_ptr(&self.memory)?;
|
||||
|
||||
self.memory.write_ptr(dest, ptr)?;
|
||||
let ptr_size = self.memory.pointer_size() as isize;
|
||||
let dest_extra = dest.offset(ptr_size);
|
||||
self.memory.write_ptr(dest_extra, vtable)?;
|
||||
},
|
||||
|
||||
_ => bug!("invalid unsizing {:?} -> {:?}", src_ty, dest_ty),
|
||||
|
@ -655,25 +642,31 @@ 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::ByValPair(data, meta), true) => {
|
||||
self.memory.write_primval(dest, data)?;
|
||||
self.memory.write_primval(dest.offset(ptr_size as isize), meta)?;
|
||||
},
|
||||
(Value::ByValPair(data, _), false) => {
|
||||
self.memory.write_primval(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 +682,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 +772,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);
|
||||
|
@ -863,6 +848,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
use rustc::mir::repr::ProjectionElem::*;
|
||||
match proj.elem {
|
||||
Field(field, field_ty) => {
|
||||
let field_ty = self.monomorphize(field_ty, self.substs());
|
||||
use rustc::ty::layout::Layout::*;
|
||||
let variant = match *base_layout {
|
||||
Univariant { ref variant, .. } => variant,
|
||||
|
@ -883,15 +869,21 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
|
||||
let offset = variant.field_offset(field.index()).bytes();
|
||||
let ptr = base.ptr.offset(offset as isize);
|
||||
match (&field_ty.sty, base.extra) {
|
||||
(&ty::TyStr, extra @ LvalueExtra::Length(_)) |
|
||||
(&ty::TySlice(_), extra @ LvalueExtra::Length(_)) |
|
||||
(&ty::TyTrait(_), extra @ LvalueExtra::Vtable(_)) => return Ok(Lvalue {
|
||||
trace!("{:?}", base);
|
||||
trace!("{:?}", field_ty);
|
||||
if self.type_is_sized(field_ty) {
|
||||
ptr
|
||||
} else {
|
||||
match base.extra {
|
||||
LvalueExtra::None => bug!("expected fat pointer"),
|
||||
LvalueExtra::DowncastVariant(..) => bug!("Rust doesn't support unsized fields in enum variants"),
|
||||
LvalueExtra::Vtable(_) |
|
||||
LvalueExtra::Length(_) => {},
|
||||
}
|
||||
return Ok(Lvalue {
|
||||
ptr: ptr,
|
||||
extra: extra,
|
||||
}),
|
||||
(&ty::TyTrait(_), _) => bug!("trait field without vtable"),
|
||||
_ => ptr,
|
||||
extra: base.extra,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -912,21 +904,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
},
|
||||
|
||||
Deref => {
|
||||
let pointee_ty = pointee_type(base_ty).expect("Deref of non-pointer");
|
||||
let pointee_ty = self.tcx.struct_tail(pointee_ty);
|
||||
let ptr = self.memory.read_ptr(base.ptr)?;
|
||||
let extra = match pointee_ty.sty {
|
||||
ty::TySlice(_) | ty::TyStr => {
|
||||
let (_, extra) = self.get_fat_ptr(base.ptr);
|
||||
let len = self.memory.read_usize(extra)?;
|
||||
LvalueExtra::Length(len)
|
||||
}
|
||||
ty::TyTrait(_) => {
|
||||
let (_, extra) = self.get_fat_ptr(base.ptr);
|
||||
let vtable = self.memory.read_ptr(extra)?;
|
||||
LvalueExtra::Vtable(vtable)
|
||||
},
|
||||
_ => LvalueExtra::None,
|
||||
use primval::PrimVal::*;
|
||||
use interpreter::value::Value::*;
|
||||
let (ptr, extra) = match self.read_value(base.ptr, base_ty)? {
|
||||
ByValPair(Ptr(ptr), Ptr(vptr)) => (ptr, LvalueExtra::Vtable(vptr)),
|
||||
ByValPair(Ptr(ptr), n) => (ptr, LvalueExtra::Length(n.expect_uint("slice length"))),
|
||||
ByVal(Ptr(ptr)) => (ptr, LvalueExtra::None),
|
||||
_ => bug!("can't deref non pointer types"),
|
||||
};
|
||||
return Ok(Lvalue { ptr: ptr, extra: extra });
|
||||
}
|
||||
|
@ -937,8 +921,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)
|
||||
}
|
||||
|
||||
|
@ -951,12 +936,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
Ok(Lvalue { ptr: ptr, extra: LvalueExtra::None })
|
||||
}
|
||||
|
||||
fn get_fat_ptr(&self, ptr: Pointer) -> (Pointer, Pointer) {
|
||||
assert_eq!(layout::FAT_PTR_ADDR, 0);
|
||||
assert_eq!(layout::FAT_PTR_EXTRA, 1);
|
||||
(ptr, ptr.offset(self.memory.pointer_size() as isize))
|
||||
}
|
||||
|
||||
fn lvalue_ty(&self, lvalue: &mir::Lvalue<'tcx>) -> Ty<'tcx> {
|
||||
self.monomorphize(lvalue.ty(&self.mir(), self.tcx).to_ty(self.tcx), self.substs())
|
||||
}
|
||||
|
@ -974,7 +953,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),
|
||||
|
||||
|
@ -986,15 +965,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
Ok(ptr)
|
||||
}
|
||||
|
||||
Value::ByValPair(primval1, primval2) => {
|
||||
Value::ByValPair(a, b) => {
|
||||
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)?;
|
||||
let ptr_size = self.memory.pointer_size() as isize;
|
||||
self.memory.write_primval(ptr, a)?;
|
||||
self.memory.write_primval(ptr.offset(ptr_size), b)?;
|
||||
Ok(ptr)
|
||||
}
|
||||
}
|
||||
|
@ -1002,11 +979,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
|
||||
fn value_to_primval(&mut self, value: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> {
|
||||
match value {
|
||||
Value::ByRef(ptr) => self.read_primval(ptr, ty),
|
||||
Value::ByRef(ptr) => match self.read_value(ptr, ty)? {
|
||||
Value::ByRef(_) => bug!("read_value can't result in `ByRef`"),
|
||||
Value::ByVal(primval) => Ok(primval),
|
||||
Value::ByValPair(..) => bug!("value_to_primval can't work with fat pointers"),
|
||||
},
|
||||
|
||||
// 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"),
|
||||
Value::ByValPair(..) => bug!("value_to_primval can't work with fat pointers"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1019,18 +1000,24 @@ 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(())
|
||||
Value::ByValPair(a, b) => {
|
||||
self.memory.write_primval(dest, a)?;
|
||||
let layout = self.type_layout(dest_ty);
|
||||
let offset = match *layout {
|
||||
Layout::Univariant { .. } => {
|
||||
bug!("I don't think this can ever happen until we have custom fat pointers");
|
||||
//variant.field_offset(1).bytes() as isize
|
||||
},
|
||||
Layout::FatPointer { .. } => self.memory.pointer_size() as isize,
|
||||
_ => bug!("tried to write value pair of non-fat pointer type: {:?}", layout),
|
||||
};
|
||||
let extra_dest = dest.offset(offset);
|
||||
self.memory.write_primval(extra_dest, b)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_primval(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> {
|
||||
fn read_value(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> {
|
||||
use syntax::ast::{IntTy, UintTy, FloatTy};
|
||||
let val = match &ty.sty {
|
||||
&ty::TyBool => PrimVal::Bool(self.memory.read_bool(ptr)?),
|
||||
|
@ -1061,12 +1048,22 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
PrimVal::FnPtr(self.memory.create_fn_ptr(def_id, substs, fn_ty))
|
||||
},
|
||||
&ty::TyFnPtr(_) => self.memory.read_ptr(ptr).map(PrimVal::FnPtr)?,
|
||||
&ty::TyBox(ty) |
|
||||
&ty::TyRef(_, ty::TypeAndMut { ty, .. }) |
|
||||
&ty::TyRawPtr(ty::TypeAndMut { ty, .. }) => {
|
||||
let p = self.memory.read_ptr(ptr)?;
|
||||
if self.type_is_sized(ty) {
|
||||
PrimVal::Ptr(self.memory.read_ptr(ptr)?)
|
||||
PrimVal::Ptr(p)
|
||||
} else {
|
||||
bug!("primitive read of fat pointer type: {:?}", ty);
|
||||
// FIXME: extract the offset to the tail field for `Box<(i64, i32, [u8])>`
|
||||
let extra = ptr.offset(self.memory.pointer_size() as isize);
|
||||
let extra = match self.tcx.struct_tail(ty).sty {
|
||||
ty::TyTrait(..) => PrimVal::Ptr(self.memory.read_ptr(extra)?),
|
||||
ty::TySlice(..) |
|
||||
ty::TyStr => self.target_usize_primval(self.memory.read_usize(extra)?),
|
||||
_ => bug!("unsized primval ptr read from {:?}", ty),
|
||||
};
|
||||
return Ok(Value::ByValPair(PrimVal::Ptr(p), extra));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1091,7 +1088,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
|
||||
_ => bug!("primitive read of non-primitive type: {:?}", ty),
|
||||
};
|
||||
Ok(val)
|
||||
Ok(Value::ByVal(val))
|
||||
}
|
||||
|
||||
fn frame(&self) -> &Frame<'a, 'tcx> {
|
||||
|
|
|
@ -2,12 +2,13 @@ use rustc::hir::def_id::DefId;
|
|||
use rustc::mir::repr as mir;
|
||||
use rustc::ty::layout::Layout;
|
||||
use rustc::ty::subst::Substs;
|
||||
use rustc::ty;
|
||||
use rustc::ty::{self, 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(
|
||||
|
@ -16,14 +17,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
substs: &'tcx Substs<'tcx>,
|
||||
args: &[mir::Operand<'tcx>],
|
||||
dest: Pointer,
|
||||
dest_ty: Ty<'tcx>,
|
||||
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 +37,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 +58,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 +136,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.write_value(args_ptrs[1], ptr, ty)?;
|
||||
}
|
||||
|
||||
"needs_drop" => {
|
||||
|
@ -129,10 +148,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 +168,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())?;
|
||||
}
|
||||
|
||||
|
@ -182,14 +200,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
let (size, _) = self.size_and_align_of_dst(ty, args_ptrs[0])?;
|
||||
self.memory.write_uint(dest, size, pointer_size)?;
|
||||
}
|
||||
// FIXME: wait for eval_operand_to_ptr to be gone
|
||||
/*
|
||||
"type_name" => {
|
||||
let ty = substs.type_at(0);
|
||||
let ty_name = ty.to_string();
|
||||
let s = self.str_to_value(&ty_name)?;
|
||||
self.memory.write_ptr(dest, s)?;
|
||||
}*/
|
||||
self.write_value(s, dest, dest_ty)?;
|
||||
}
|
||||
"type_id" => {
|
||||
let ty = substs.type_at(0);
|
||||
let n = self.tcx.type_id_hash(ty);
|
||||
|
@ -198,7 +214,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
|
||||
"transmute" => {
|
||||
let ty = substs.type_at(0);
|
||||
self.move_(args_ptrs[0], dest, ty)?;
|
||||
self.write_value(args_ptrs[0], dest, ty)?;
|
||||
}
|
||||
|
||||
"try" => unimplemented!(),
|
||||
|
@ -207,14 +223,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.write_value(args_ptrs[1], dest, ty)?;
|
||||
}
|
||||
|
||||
name => return Err(EvalError::Unimplemented(format!("unimplemented intrinsic: {}", name))),
|
||||
|
@ -229,7 +245,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 +322,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 +332,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),
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
@ -164,7 +165,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
let ty = fn_ty.sig.0.output;
|
||||
let layout = self.type_layout(ty);
|
||||
let (ret, target) = destination.unwrap();
|
||||
self.call_intrinsic(def_id, substs, arg_operands, ret, layout)?;
|
||||
self.call_intrinsic(def_id, substs, arg_operands, ret, ty, layout)?;
|
||||
self.goto_block(target);
|
||||
Ok(())
|
||||
}
|
||||
|
@ -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))?;
|
||||
|
|
53
src/interpreter/value.rs
Normal file
53
src/interpreter/value.rs
Normal file
|
@ -0,0 +1,53 @@
|
|||
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),
|
||||
ByValPair(PrimVal, 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),
|
||||
ByValPair(..) => unimplemented!(),
|
||||
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)),
|
||||
ByValPair(_, PrimVal::Ptr(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)),
|
||||
ByValPair(_, PrimVal::U8(len)) => Ok(len as u64),
|
||||
ByValPair(_, PrimVal::U16(len)) => Ok(len as u64),
|
||||
ByValPair(_, PrimVal::U32(len)) => Ok(len as u64),
|
||||
ByValPair(_, PrimVal::U64(len)) => Ok(len),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -55,6 +55,13 @@ impl Pointer {
|
|||
pub fn points_to_zst(&self) -> bool {
|
||||
self.alloc_id == ZST_ALLOC_ID
|
||||
}
|
||||
pub fn to_int<'tcx>(&self) -> EvalResult<'tcx, usize> {
|
||||
if self.points_to_zst() {
|
||||
Ok(self.offset)
|
||||
} else {
|
||||
Err(EvalError::ReadPointerAsBytes)
|
||||
}
|
||||
}
|
||||
pub fn from_int(i: usize) -> Self {
|
||||
Pointer {
|
||||
alloc_id: ZST_ALLOC_ID,
|
||||
|
|
|
@ -32,7 +32,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 +44,19 @@ impl PrimVal {
|
|||
U16(u) => u as u64,
|
||||
U32(u) => u as u64,
|
||||
U64(u) => u,
|
||||
Ptr(ptr) => ptr.to_int().expect("non abstract ptr") 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().expect("non abstract ptr") as i64,
|
||||
_ => bug!("{}", error_msg),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -105,6 +105,7 @@ fn compile_test() {
|
|||
writeln!(stderr.lock(), "FAILED with exit code {:?}", output.status.code()).unwrap();
|
||||
writeln!(stderr.lock(), "stdout: \n {}", std::str::from_utf8(&output.stdout).unwrap()).unwrap();
|
||||
writeln!(stderr.lock(), "stderr: \n {}", output_err).unwrap();
|
||||
panic!("failed to run test");
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
|
|
134
tests/run-pass/dst-struct.rs
Normal file
134
tests/run-pass/dst-struct.rs
Normal file
|
@ -0,0 +1,134 @@
|
|||
// Copyright 2014 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.
|
||||
|
||||
|
||||
#![allow(unused_features)]
|
||||
#![feature(box_syntax)]
|
||||
|
||||
struct Fat<T: ?Sized> {
|
||||
f1: isize,
|
||||
f2: &'static str,
|
||||
ptr: T
|
||||
}
|
||||
|
||||
// x is a fat pointer
|
||||
fn foo(x: &Fat<[isize]>) {
|
||||
let y = &x.ptr;
|
||||
assert_eq!(x.ptr.len(), 3);
|
||||
assert_eq!(y[0], 1);
|
||||
assert_eq!(x.ptr[1], 2);
|
||||
assert_eq!(x.f1, 5);
|
||||
assert_eq!(x.f2, "some str");
|
||||
}
|
||||
|
||||
fn foo2<T:ToBar>(x: &Fat<[T]>) {
|
||||
let y = &x.ptr;
|
||||
let bar = Bar;
|
||||
assert_eq!(x.ptr.len(), 3);
|
||||
assert_eq!(y[0].to_bar(), bar);
|
||||
assert_eq!(x.ptr[1].to_bar(), bar);
|
||||
assert_eq!(x.f1, 5);
|
||||
assert_eq!(x.f2, "some str");
|
||||
}
|
||||
|
||||
fn foo3(x: &Fat<Fat<[isize]>>) {
|
||||
let y = &x.ptr.ptr;
|
||||
assert_eq!(x.f1, 5);
|
||||
assert_eq!(x.f2, "some str");
|
||||
assert_eq!(x.ptr.f1, 8);
|
||||
assert_eq!(x.ptr.f2, "deep str");
|
||||
assert_eq!(x.ptr.ptr.len(), 3);
|
||||
assert_eq!(y[0], 1);
|
||||
assert_eq!(x.ptr.ptr[1], 2);
|
||||
}
|
||||
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
struct Bar;
|
||||
|
||||
trait ToBar {
|
||||
fn to_bar(&self) -> Bar;
|
||||
}
|
||||
|
||||
impl ToBar for Bar {
|
||||
fn to_bar(&self) -> Bar {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
// With a vec of ints.
|
||||
let f1 = Fat { f1: 5, f2: "some str", ptr: [1, 2, 3] };
|
||||
foo(&f1);
|
||||
let f2 = &f1;
|
||||
foo(f2);
|
||||
let f3: &Fat<[isize]> = f2;
|
||||
foo(f3);
|
||||
let f4: &Fat<[isize]> = &f1;
|
||||
foo(f4);
|
||||
let f5: &Fat<[isize]> = &Fat { f1: 5, f2: "some str", ptr: [1, 2, 3] };
|
||||
foo(f5);
|
||||
|
||||
// With a vec of Bars.
|
||||
let bar = Bar;
|
||||
let f1 = Fat { f1: 5, f2: "some str", ptr: [bar, bar, bar] };
|
||||
foo2(&f1);
|
||||
let f2 = &f1;
|
||||
foo2(f2);
|
||||
let f3: &Fat<[Bar]> = f2;
|
||||
foo2(f3);
|
||||
let f4: &Fat<[Bar]> = &f1;
|
||||
foo2(f4);
|
||||
let f5: &Fat<[Bar]> = &Fat { f1: 5, f2: "some str", ptr: [bar, bar, bar] };
|
||||
foo2(f5);
|
||||
|
||||
// Assignment.
|
||||
let f5: &mut Fat<[isize]> = &mut Fat { f1: 5, f2: "some str", ptr: [1, 2, 3] };
|
||||
f5.ptr[1] = 34;
|
||||
assert_eq!(f5.ptr[0], 1);
|
||||
assert_eq!(f5.ptr[1], 34);
|
||||
assert_eq!(f5.ptr[2], 3);
|
||||
|
||||
// Zero size vec.
|
||||
let f5: &Fat<[isize]> = &Fat { f1: 5, f2: "some str", ptr: [] };
|
||||
assert!(f5.ptr.is_empty());
|
||||
let f5: &Fat<[Bar]> = &Fat { f1: 5, f2: "some str", ptr: [] };
|
||||
assert!(f5.ptr.is_empty());
|
||||
|
||||
// Deeply nested.
|
||||
let f1 = Fat { f1: 5, f2: "some str", ptr: Fat { f1: 8, f2: "deep str", ptr: [1, 2, 3]} };
|
||||
foo3(&f1);
|
||||
let f2 = &f1;
|
||||
foo3(f2);
|
||||
let f3: &Fat<Fat<[isize]>> = f2;
|
||||
foo3(f3);
|
||||
let f4: &Fat<Fat<[isize]>> = &f1;
|
||||
foo3(f4);
|
||||
let f5: &Fat<Fat<[isize]>> =
|
||||
&Fat { f1: 5, f2: "some str", ptr: Fat { f1: 8, f2: "deep str", ptr: [1, 2, 3]} };
|
||||
foo3(f5);
|
||||
|
||||
// Box.
|
||||
let f1 = Box::new([1, 2, 3]);
|
||||
assert_eq!((*f1)[1], 2);
|
||||
let f2: Box<[isize]> = f1;
|
||||
assert_eq!((*f2)[1], 2);
|
||||
|
||||
// Nested Box.
|
||||
let f1 : Box<Fat<[isize; 3]>> = box Fat { f1: 5, f2: "some str", ptr: [1, 2, 3] };
|
||||
foo(&*f1);
|
||||
let f2 : Box<Fat<[isize]>> = f1;
|
||||
foo(&*f2);
|
||||
|
||||
// FIXME (#22405): Replace `Box::new` with `box` here when/if possible.
|
||||
let f3 : Box<Fat<[isize]>> =
|
||||
Box::<Fat<[_; 3]>>::new(Fat { f1: 5, f2: "some str", ptr: [1, 2, 3] });
|
||||
foo(&*f3);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue