Refactor in preparation for Value locals.

Turning locals into `Vec<Value>` will allow writing `PrimVal` results
directly into the locals array without creating `memory::Allocation`s
for every local.

This will entail passing around a generalized kind of `Lvalue` instead
of `Pointer`s for the destinations of operations. Replacing `Pointer`
with `Lvalue` is mostly done with this commit, but expanding `Lvalue`
will come later.

This commit turns every local from `Pointer` into `Value::ByRef(ptr)`.
Locals which are `Value::ByVal(prim_val)` will come in a later commit.
This commit is contained in:
Scott Olson 2016-10-14 03:31:45 -06:00
parent cb23b8d0a7
commit 5f65ee2713
7 changed files with 283 additions and 127 deletions

View File

@ -78,7 +78,7 @@ pub struct Frame<'a, 'tcx: 'a> {
/// The list of locals for the current function, stored in order as
/// `[return_ptr, arguments..., variables..., temporaries...]`.
pub locals: Vec<Pointer>,
pub locals: Vec<Value>,
////////////////////////////////////////////////////////////////////////////////
// Current position within the function
@ -93,7 +93,7 @@ pub struct Frame<'a, 'tcx: 'a> {
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
struct Lvalue {
pub struct Lvalue {
ptr: Pointer,
extra: LvalueExtra,
}
@ -157,7 +157,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}
}
pub fn alloc_ret_ptr(&mut self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> EvalResult<'tcx, Pointer> {
pub fn alloc_ptr(
&mut self,
ty: Ty<'tcx>,
substs: &'tcx Substs<'tcx>
) -> EvalResult<'tcx, Pointer> {
let size = self.type_size_with_substs(ty, substs);
let align = self.type_align_with_substs(ty, substs);
self.memory.allocate(size, align)
@ -175,7 +179,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
&self.stack
}
fn target_isize_primval(&self, n: i64) -> PrimVal {
fn isize_primval(&self, n: i64) -> PrimVal {
match self.memory.pointer_size() {
1 => PrimVal::I8(n as i8),
2 => PrimVal::I16(n as i16),
@ -185,7 +189,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}
}
fn target_usize_primval(&self, n: u64) -> PrimVal {
fn usize_primval(&self, n: u64) -> PrimVal {
match self.memory.pointer_size() {
1 => PrimVal::U8(n as u8),
2 => PrimVal::U16(n as u16),
@ -200,7 +204,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)?;
Ok(Value::ByValPair(PrimVal::Ptr(ptr), self.target_usize_primval(s.len() as u64)))
Ok(Value::ByValPair(PrimVal::Ptr(ptr), self.usize_primval(s.len() as u64)))
}
fn const_to_value(&mut self, const_val: &ConstVal) -> EvalResult<'tcx, Value> {
@ -326,19 +330,24 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
span: codemap::Span,
mir: CachedMir<'a, 'tcx>,
substs: &'tcx Substs<'tcx>,
return_ptr: Pointer,
return_lvalue: Lvalue,
return_to_block: StackPopCleanup,
) -> EvalResult<'tcx, ()> {
let local_tys = mir.local_decls.iter().map(|a| a.ty);
::log_settings::settings().indentation += 1;
// FIXME(solson)
let return_ptr = return_lvalue.to_ptr();
// directly change the first allocation (the return value) to *be* the allocation where the
// caller stores the result
let locals: EvalResult<'tcx, Vec<Pointer>> = iter::once(Ok(return_ptr)).chain(local_tys.skip(1).map(|ty| {
let locals: EvalResult<'tcx, Vec<Value>> = iter::once(Ok(Value::ByRef(return_ptr))).chain(local_tys.skip(1).map(|ty| {
let size = self.type_size_with_substs(ty, substs);
let align = self.type_align_with_substs(ty, substs);
self.memory.allocate(size, align)
// FIXME(solson)
self.memory.allocate(size, align).map(Value::ByRef)
})).collect();
self.stack.push(Frame {
@ -377,7 +386,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
op: mir::BinOp,
left: &mir::Operand<'tcx>,
right: &mir::Operand<'tcx>,
dest: Pointer,
dest: Lvalue,
dest_layout: &'tcx Layout,
) -> EvalResult<'tcx, ()> {
use rustc::ty::layout::Layout::*;
@ -387,6 +396,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
};
let overflowed = self.intrinsic_overflowing(op, left, right, dest)?;
// FIXME(solson)
let dest = dest.to_ptr();
let offset = tup_layout.offsets[1].bytes() as isize;
self.memory.write_bool(dest.offset(offset), overflowed)
}
@ -398,26 +411,29 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
op: mir::BinOp,
left: &mir::Operand<'tcx>,
right: &mir::Operand<'tcx>,
dest: Pointer,
dest: Lvalue,
) -> EvalResult<'tcx, bool> {
let left_primval = self.eval_operand_to_primval(left)?;
let right_primval = self.eval_operand_to_primval(right)?;
let (val, overflow) = primval::binary_op(op, left_primval, right_primval)?;
self.memory.write_primval(dest, val)?;
self.write_primval(dest, val)?;
Ok(overflow)
}
fn assign_fields<I: IntoIterator<Item = u64>>(
&mut self,
dest: Pointer,
dest: Lvalue,
offsets: I,
operands: &[mir::Operand<'tcx>],
) -> EvalResult<'tcx, ()> {
// FIXME(solson)
let dest = dest.to_ptr();
for (offset, operand) in offsets.into_iter().zip(operands) {
let value = self.eval_operand(operand)?;
let value_ty = self.operand_ty(operand);
let field_dest = dest.offset(offset as isize);
self.write_value(value, field_dest, value_ty)?;
self.write_value_to_ptr(value, field_dest, value_ty)?;
}
Ok(())
}
@ -431,7 +447,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
rvalue: &mir::Rvalue<'tcx>,
lvalue: &mir::Lvalue<'tcx>,
) -> EvalResult<'tcx, ()> {
let dest = self.eval_lvalue(lvalue)?.to_ptr();
let dest = self.eval_lvalue(lvalue)?;
let dest_ty = self.lvalue_ty(lvalue);
let dest_layout = self.type_layout(dest_ty);
@ -453,7 +469,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
UnaryOp(un_op, ref operand) => {
let val = self.eval_operand_to_primval(operand)?;
self.memory.write_primval(dest, primval::unary_op(un_op, val)?)?;
self.write_primval(dest, primval::unary_op(un_op, val)?)?;
}
Aggregate(ref kind, ref operands) => {
@ -478,7 +494,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
let discr_val = adt_def.variants[variant].disr_val.to_u64_unchecked();
let discr_size = discr.size().bytes() as usize;
let discr_offset = variants[variant].offsets[0].bytes() as isize;
let discr_dest = dest.offset(discr_offset);
// FIXME(solson)
let discr_dest = (dest.to_ptr()).offset(discr_offset);
self.memory.write_uint(discr_dest, discr_val, discr_size)?;
// Don't include the first offset; it's for the discriminant.
@ -500,7 +519,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
self.write_value(value, dest, value_ty)?;
} else {
assert_eq!(operands.len(), 0);
self.memory.write_isize(dest, 0)?;
let zero = self.isize_primval(0);
self.write_primval(dest, zero)?;
}
} else {
bug!("tried to assign {:?} to Layout::RawNullablePointer", kind);
@ -518,6 +538,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
assert_eq!(self.type_size(operand_ty), 0);
}
let offset = self.nonnull_offset(dest_ty, nndiscr, discrfield)?;
// FIXME(solson)
let dest = dest.to_ptr();
let dest = dest.offset(offset.bytes() as isize);
try!(self.memory.write_isize(dest, 0));
}
@ -532,6 +556,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
let val = adt_def.variants[variant].disr_val.to_u64_unchecked();
let size = discr.size().bytes() as usize;
// FIXME(solson)
let dest = dest.to_ptr();
if signed {
self.memory.write_int(dest, val as i64, size)?;
} else {
@ -553,9 +580,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
};
let elem_size = self.type_size(elem_ty);
let value = self.eval_operand(operand)?;
// FIXME(solson)
let dest = dest.to_ptr();
for i in 0..length {
let elem_dest = dest.offset((i * elem_size) as isize);
self.write_value(value, elem_dest, elem_ty)?;
self.write_value_to_ptr(value, elem_dest, elem_ty)?;
}
}
@ -563,10 +594,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
let src = self.eval_lvalue(lvalue)?;
let ty = self.lvalue_ty(lvalue);
let (_, len) = src.elem_ty_and_len(ty);
self.memory.write_usize(dest, len)?;
let len_val = self.usize_primval(len);
self.write_primval(dest, len_val)?;
}
Ref(_, _, ref lvalue) => {
// FIXME(solson)
let dest = dest.to_ptr();
let lvalue = self.eval_lvalue(lvalue)?;
self.memory.write_ptr(dest, lvalue.ptr)?;
let extra_ptr = dest.offset(self.memory.pointer_size() as isize);
@ -580,6 +615,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}
Box(ty) => {
// FIXME(solson)
let dest = dest.to_ptr();
let size = self.type_size(ty);
let align = self.type_align(ty);
let ptr = self.memory.allocate(size, align)?;
@ -587,6 +625,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}
Cast(kind, ref operand, cast_ty) => {
// FIXME(solson)
let dest = dest.to_ptr();
debug_assert_eq!(self.monomorphize(cast_ty, self.substs()), dest_ty);
use rustc::mir::repr::CastKind::*;
match kind {
@ -778,7 +819,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
fn eval_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Lvalue> {
use rustc::mir::repr::Lvalue::*;
let ptr = match *lvalue {
Local(i) => self.frame().locals[i.index()],
Local(i) => {
match self.frame().locals[i.index()] {
Value::ByRef(p) => p,
_ => bug!(),
}
}
Static(def_id) => {
let substs = subst::Substs::empty(self.tcx);
@ -910,7 +956,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
self.monomorphize(operand.ty(&self.mir(), self.tcx), self.substs())
}
fn move_(&mut self, src: Pointer, dest: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, ()> {
fn copy(&mut self, src: Pointer, dest: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, ()> {
let size = self.type_size(ty);
let align = self.type_align(ty);
self.memory.copy(src, dest, size, align)?;
@ -957,14 +1003,36 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}
}
fn write_primval(
&mut self,
dest: Lvalue,
val: PrimVal,
) -> EvalResult<'tcx, ()> {
// FIXME(solson)
let dest = dest.to_ptr();
self.memory.write_primval(dest, val)
}
fn write_value(
&mut self,
value: Value,
dest: Lvalue,
dest_ty: Ty<'tcx>,
) -> EvalResult<'tcx, ()> {
// FIXME(solson)
let dest = dest.to_ptr();
self.write_value_to_ptr(value, dest, dest_ty)
}
fn write_value_to_ptr(
&mut self,
value: Value,
dest: Pointer,
dest_ty: Ty<'tcx>
dest_ty: Ty<'tcx>,
) -> EvalResult<'tcx, ()> {
match value {
Value::ByRef(ptr) => self.move_(ptr, dest, dest_ty),
Value::ByRef(ptr) => self.copy(ptr, dest, dest_ty),
Value::ByVal(primval) => self.memory.write_primval(dest, primval),
Value::ByValPair(a, b) => {
self.memory.write_primval(dest, a)?;
@ -984,7 +1052,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}
fn read_value(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> {
use syntax::ast::{IntTy, UintTy, FloatTy};
use syntax::ast::FloatTy;
let val = match &ty.sty {
&ty::TyBool => PrimVal::Bool(self.memory.read_bool(ptr)?),
&ty::TyChar => {
@ -995,17 +1064,31 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}
}
&ty::TyInt(IntTy::I8) => PrimVal::I8(self.memory.read_int(ptr, 1)? as i8),
&ty::TyInt(IntTy::I16) => PrimVal::I16(self.memory.read_int(ptr, 2)? as i16),
&ty::TyInt(IntTy::I32) => PrimVal::I32(self.memory.read_int(ptr, 4)? as i32),
&ty::TyInt(IntTy::I64) => PrimVal::I64(self.memory.read_int(ptr, 8)? as i64),
&ty::TyUint(UintTy::U8) => PrimVal::U8(self.memory.read_uint(ptr, 1)? as u8),
&ty::TyUint(UintTy::U16) => PrimVal::U16(self.memory.read_uint(ptr, 2)? as u16),
&ty::TyUint(UintTy::U32) => PrimVal::U32(self.memory.read_uint(ptr, 4)? as u32),
&ty::TyUint(UintTy::U64) => PrimVal::U64(self.memory.read_uint(ptr, 8)? as u64),
&ty::TyInt(int_ty) => {
use syntax::ast::IntTy::*;
let size = match int_ty {
I8 => 1,
I16 => 2,
I32 => 4,
I64 => 8,
Is => self.memory.pointer_size(),
};
let n = self.memory.read_int(ptr, size)?;
PrimVal::int_with_size(n, size)
}
&ty::TyInt(IntTy::Is) => self.target_isize_primval(self.memory.read_isize(ptr)?),
&ty::TyUint(UintTy::Us) => self.target_usize_primval(self.memory.read_usize(ptr)?),
&ty::TyUint(uint_ty) => {
use syntax::ast::UintTy::*;
let size = match uint_ty {
U8 => 1,
U16 => 2,
U32 => 4,
U64 => 8,
Us => self.memory.pointer_size(),
};
let n = self.memory.read_uint(ptr, size)?;
PrimVal::uint_with_size(n, size)
}
&ty::TyFloat(FloatTy::F32) => PrimVal::F32(self.memory.read_f32(ptr)?),
&ty::TyFloat(FloatTy::F64) => PrimVal::F64(self.memory.read_f64(ptr)?),
@ -1026,7 +1109,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
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)?),
ty::TyStr => self.usize_primval(self.memory.read_usize(extra)?),
_ => bug!("unsized primval ptr read from {:?}", ty),
};
return Ok(Value::ByValPair(PrimVal::Ptr(p), extra));
@ -1036,16 +1119,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
&ty::TyAdt(..) => {
use rustc::ty::layout::Layout::*;
if let CEnum { discr, signed, .. } = *self.type_layout(ty) {
match (discr.size().bytes(), signed) {
(1, true) => PrimVal::I8(self.memory.read_int(ptr, 1)? as i8),
(2, true) => PrimVal::I16(self.memory.read_int(ptr, 2)? as i16),
(4, true) => PrimVal::I32(self.memory.read_int(ptr, 4)? as i32),
(8, true) => PrimVal::I64(self.memory.read_int(ptr, 8)? as i64),
(1, false) => PrimVal::U8(self.memory.read_uint(ptr, 1)? as u8),
(2, false) => PrimVal::U16(self.memory.read_uint(ptr, 2)? as u16),
(4, false) => PrimVal::U32(self.memory.read_uint(ptr, 4)? as u32),
(8, false) => PrimVal::U64(self.memory.read_uint(ptr, 8)? as u64),
(size, _) => bug!("CEnum discr size {}", size),
let size = discr.size().bytes() as usize;
if signed {
let n = self.memory.read_int(ptr, size)?;
PrimVal::int_with_size(n, size)
} else {
let n = self.memory.read_uint(ptr, size)?;
PrimVal::uint_with_size(n, size)
}
} else {
bug!("primitive read of non-clike enum: {:?}", ty);
@ -1100,7 +1180,7 @@ 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.
self.write_value(src, dest, dest_ty)?;
self.write_value_to_ptr(src, dest, dest_ty)?;
},
(_, &ty::TyTrait(ref data)) => {
let trait_ref = data.principal.with_self_ty(self.tcx, src_pointee_ty);
@ -1146,7 +1226,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
let src_f_ptr = src_ptr.offset(src_field_offset);
let dst_f_ptr = dest.offset(dst_field_offset);
if src_fty == dst_fty {
self.move_(src_f_ptr, dst_f_ptr, src_fty)?;
self.copy(src_f_ptr, dst_f_ptr, src_fty)?;
} else {
self.unsize_into(Value::ByRef(src_f_ptr), src_fty, dst_f_ptr, dst_fty)?;
}
@ -1161,10 +1241,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}
impl Lvalue {
fn from_ptr(ptr: Pointer) -> Self {
Lvalue { ptr: ptr, extra: LvalueExtra::None }
}
fn to_ptr(self) -> Pointer {
assert_eq!(self.extra, LvalueExtra::None);
self.ptr
}
fn elem_ty_and_len<'tcx>(self, ty: Ty<'tcx>) -> (Ty<'tcx>, u64) {
match ty.sty {
ty::TyArray(elem, n) => (elem, n as u64),
@ -1199,27 +1284,22 @@ pub fn eval_main<'a, 'tcx: 'a>(
let mir = mir_map.map.get(&def_id).expect("no mir for main function");
let mut ecx = EvalContext::new(tcx, mir_map, memory_size, stack_limit);
let substs = subst::Substs::empty(tcx);
let return_ptr = ecx.alloc_ret_ptr(mir.return_ty, substs)
let return_ptr = ecx.alloc_ptr(mir.return_ty, substs)
.expect("should at least be able to allocate space for the main function's return value");
ecx.push_stack_frame(def_id, mir.span, CachedMir::Ref(mir), substs, return_ptr, StackPopCleanup::None)
.expect("could not allocate first stack frame");
// FIXME: this is a horrible and wrong way to detect the start function, but overwriting the first two locals shouldn't do much
if mir.local_decls.len() > 2 {
// start function
let nargs = ecx.frame_mut().locals[1];
let args = ecx.frame_mut().locals[2];
// ignore errors, if the locals are too small this is not the start function
let _ = ecx.memory_mut().write_usize(nargs, 0);
let _ = ecx.memory_mut().write_usize(args, 0);
}
ecx.push_stack_frame(
def_id,
mir.span,
CachedMir::Ref(mir),
substs,
Lvalue::from_ptr(return_ptr), // FIXME(solson)
StackPopCleanup::None
).expect("could not allocate first stack frame");
for _ in 0..step_limit {
match ecx.step() {
Ok(true) => {}
Ok(false) => return,
// FIXME: diverging functions can end up here in some future miri
Err(e) => {
report(tcx, &ecx, e);
return;

View File

@ -6,6 +6,7 @@ use super::{
CachedMir,
ConstantId,
EvalContext,
Lvalue,
ConstantKind,
StackPopCleanup,
};
@ -104,7 +105,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
// WARNING: make sure that any methods implemented on this type don't ever access ecx.stack
// this includes any method that might access the stack
// basically don't call anything other than `load_mir`, `alloc_ret_ptr`, `push_stack_frame`
// basically don't call anything other than `load_mir`, `alloc_ptr`, `push_stack_frame`
// The reason for this is, that `push_stack_frame` modifies the stack out of obvious reasons
struct ConstantExtractor<'a, 'b: 'a, 'tcx: 'b> {
span: Span,
@ -127,14 +128,15 @@ impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> {
}
self.try(|this| {
let mir = this.ecx.load_mir(def_id)?;
let ptr = this.ecx.alloc_ret_ptr(mir.return_ty, substs)?;
// FIXME(solson): Don't allocate a pointer unconditionally.
let ptr = this.ecx.alloc_ptr(mir.return_ty, substs)?;
this.ecx.statics.insert(cid.clone(), ptr);
let cleanup = if immutable && !mir.return_ty.type_contents(this.ecx.tcx).interior_unsafe() {
StackPopCleanup::Freeze(ptr.alloc_id)
} else {
StackPopCleanup::None
};
this.ecx.push_stack_frame(def_id, span, mir, substs, ptr, cleanup)
this.ecx.push_stack_frame(def_id, span, mir, substs, Lvalue::from_ptr(ptr), cleanup)
});
}
fn try<F: FnOnce(&mut Self) -> EvalResult<'tcx, ()>>(&mut self, f: F) {
@ -176,14 +178,15 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> {
let mir = self.mir.promoted[index].clone();
let return_ty = mir.return_ty;
self.try(|this| {
let return_ptr = this.ecx.alloc_ret_ptr(return_ty, cid.substs)?;
// FIXME(solson): Don't allocate a pointer unconditionally.
let return_ptr = this.ecx.alloc_ptr(return_ty, cid.substs)?;
let mir = CachedMir::Owned(Rc::new(mir));
this.ecx.statics.insert(cid.clone(), return_ptr);
this.ecx.push_stack_frame(this.def_id,
constant.span,
mir,
this.substs,
return_ptr,
Lvalue::from_ptr(return_ptr),
StackPopCleanup::Freeze(return_ptr.alloc_id))
});
}

View File

@ -5,10 +5,9 @@ use rustc::ty::subst::Substs;
use rustc::ty::{self, Ty};
use error::{EvalError, EvalResult};
use memory::Pointer;
use interpreter::EvalContext;
use primval::{self, PrimVal};
use interpreter::value::Value;
use interpreter::{EvalContext, Lvalue};
use primval::{self, PrimVal};
impl<'a, 'tcx> EvalContext<'a, 'tcx> {
pub(super) fn call_intrinsic(
@ -16,7 +15,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
def_id: DefId,
substs: &'tcx Substs<'tcx>,
args: &[mir::Operand<'tcx>],
dest: Pointer,
dest: Lvalue,
dest_ty: Ty<'tcx>,
dest_layout: &'tcx Layout,
) -> EvalResult<'tcx, ()> {
@ -24,7 +23,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
.map(|arg| self.eval_operand(arg))
.collect();
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;
@ -33,15 +31,41 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
let intrinsic_name = &self.tcx.item_name(def_id).as_str()[..];
match intrinsic_name {
"add_with_overflow" => self.intrinsic_with_overflow(mir::BinOp::Add, &args[0], &args[1], dest, dest_layout)?,
"sub_with_overflow" => self.intrinsic_with_overflow(mir::BinOp::Sub, &args[0], &args[1], dest, dest_layout)?,
"mul_with_overflow" => self.intrinsic_with_overflow(mir::BinOp::Mul, &args[0], &args[1], dest, dest_layout)?,
"add_with_overflow" => {
self.intrinsic_with_overflow(
mir::BinOp::Add,
&args[0],
&args[1],
dest,
dest_layout,
)?
}
"sub_with_overflow" => {
self.intrinsic_with_overflow(
mir::BinOp::Sub,
&args[0],
&args[1],
dest,
dest_layout,
)?
}
"mul_with_overflow" => {
self.intrinsic_with_overflow(
mir::BinOp::Mul,
&args[0],
&args[1],
dest,
dest_layout,
)?
}
"arith_offset" => {
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)?;
self.write_primval(dest, PrimVal::Ptr(new_ptr))?;
}
"assume" => {
@ -72,24 +96,24 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
let elem_ty = substs.type_at(0);
let num = self.value_to_primval(args_ptrs[0], elem_ty)?;
let num = numeric_intrinsic(intrinsic_name, num);
self.memory.write_primval(dest, num)?;
self.write_primval(dest, num)?;
}
"discriminant_value" => {
let ty = substs.type_at(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)?;
self.write_primval(dest, PrimVal::U64(discr_val))?;
}
"fabsf32" => {
let f = self.value_to_primval(args_ptrs[2], f32)?.expect_f32("fabsf32 read non f32");
self.memory.write_f32(dest, f.abs())?;
self.write_primval(dest, PrimVal::F32(f.abs()))?;
}
"fabsf64" => {
let f = self.value_to_primval(args_ptrs[2], f64)?.expect_f64("fabsf64 read non f64");
self.memory.write_f64(dest, f.abs())?;
self.write_primval(dest, PrimVal::F64(f.abs()))?;
}
"fadd_fast" => {
@ -97,37 +121,47 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
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)?;
self.write_primval(dest, result.0)?;
}
"likely" |
"unlikely" |
"forget" => {}
"init" => self.memory.write_repeat(dest, 0, dest_layout.size(&self.tcx.data_layout).bytes() as usize)?,
"init" => {
// FIXME(solson)
let dest = dest.to_ptr();
let size = dest_layout.size(&self.tcx.data_layout).bytes() as usize;
self.memory.write_repeat(dest, 0, size)?;
}
"min_align_of" => {
let elem_ty = substs.type_at(0);
let elem_align = self.type_align(elem_ty);
self.memory.write_uint(dest, elem_align as u64, pointer_size)?;
let align_val = self.usize_primval(elem_align as u64);
self.write_primval(dest, align_val)?;
}
"pref_align_of" => {
let ty = substs.type_at(0);
let layout = self.type_layout(ty);
let align = layout.align(&self.tcx.data_layout).pref();
self.memory.write_uint(dest, align, pointer_size)?;
let align_val = self.usize_primval(align);
self.write_primval(dest, align_val)?;
}
"move_val_init" => {
let ty = substs.type_at(0);
let ptr = args_ptrs[0].read_ptr(&self.memory)?;
self.write_value(args_ptrs[1], ptr, ty)?;
self.write_value_to_ptr(args_ptrs[1], ptr, ty)?;
}
"needs_drop" => {
let ty = substs.type_at(0);
self.memory.write_bool(dest, self.tcx.type_needs_drop_given_env(ty, &self.tcx.empty_parameter_environment()))?;
let env = self.tcx.empty_parameter_environment();
let needs_drop = self.tcx.type_needs_drop_given_env(ty, &env);
self.write_primval(dest, PrimVal::Bool(needs_drop))?;
}
"offset" => {
@ -137,7 +171,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
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)?;
self.write_primval(dest, PrimVal::Ptr(result_ptr))?;
}
"overflowing_sub" => {
@ -155,35 +189,37 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
"powif32" => {
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))?;
self.write_primval(dest, PrimVal::F32(f.powi(i as i32)))?;
}
"powif64" => {
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))?;
self.write_primval(dest, PrimVal::F64(f.powi(i as i32)))?;
}
"sqrtf32" => {
let f = self.value_to_primval(args_ptrs[0], f32)?.expect_f32("sqrtf32 first arg not f32");
self.memory.write_f32(dest, f.sqrt())?;
self.write_primval(dest, PrimVal::F32(f.sqrt()))?;
}
"sqrtf64" => {
let f = self.value_to_primval(args_ptrs[0], f64)?.expect_f64("sqrtf64 first arg not f64");
self.memory.write_f64(dest, f.sqrt())?;
self.write_primval(dest, PrimVal::F64(f.sqrt()))?;
}
"size_of" => {
let ty = substs.type_at(0);
let size = self.type_size(ty) as u64;
self.memory.write_uint(dest, size, pointer_size)?;
let size_val = self.usize_primval(size);
self.write_primval(dest, size_val)?;
}
"size_of_val" => {
let ty = substs.type_at(0);
let (size, _) = self.size_and_align_of_dst(ty, args_ptrs[0])?;
self.memory.write_uint(dest, size, pointer_size)?;
let size_val = self.usize_primval(size);
self.write_primval(dest, size_val)?;
}
"type_name" => {
let ty = substs.type_at(0);
@ -194,7 +230,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
"type_id" => {
let ty = substs.type_at(0);
let n = self.tcx.type_id_hash(ty);
self.memory.write_uint(dest, n, 8)?;
self.write_primval(dest, PrimVal::U64(n))?;
}
"transmute" => {
@ -202,20 +238,24 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
self.write_value(args_ptrs[0], dest, ty)?;
}
"try" => unimplemented!(),
"uninit" => {
// FIXME(solson)
let dest = dest.to_ptr();
"uninit" => self.memory.mark_definedness(dest, dest_layout.size(&self.tcx.data_layout).bytes() as usize, false)?,
let size = dest_layout.size(&self.tcx.data_layout).bytes() as usize;
self.memory.mark_definedness(dest, size, false)?;
}
"volatile_load" => {
let ty = substs.type_at(0);
let ptr = args_ptrs[0].read_ptr(&self.memory)?;
self.move_(ptr, dest, ty)?;
self.write_value(Value::ByRef(ptr), dest, ty)?;
}
"volatile_store" => {
let ty = substs.type_at(0);
let dest = args_ptrs[0].read_ptr(&self.memory)?;
self.write_value(args_ptrs[1], dest, ty)?;
self.write_value_to_ptr(args_ptrs[1], dest, ty)?;
}
name => return Err(EvalError::Unimplemented(format!("unimplemented intrinsic: {}", name))),

View File

@ -12,7 +12,7 @@ use syntax::{ast, attr};
use error::{EvalError, EvalResult};
use memory::Pointer;
use primval::PrimVal;
use super::{EvalContext, IntegerExt, StackPopCleanup};
use super::{EvalContext, Lvalue, IntegerExt, StackPopCleanup};
use super::value::Value;
mod intrinsics;
@ -76,7 +76,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
Call { ref func, ref args, ref destination, .. } => {
let destination = match *destination {
Some((ref lv, target)) => Some((self.eval_lvalue(lv)?.to_ptr(), target)),
Some((ref lv, target)) => Some((self.eval_lvalue(lv)?, target)),
None => None,
};
@ -143,7 +143,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
def_id: DefId,
substs: &'tcx Substs<'tcx>,
fn_ty: &'tcx BareFnTy,
destination: Option<(Pointer, mir::BasicBlock)>,
destination: Option<(Lvalue, mir::BasicBlock)>,
arg_operands: &[mir::Operand<'tcx>],
span: Span,
) -> EvalResult<'tcx, ()> {
@ -184,15 +184,34 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
};
let mir = self.load_mir(resolved_def_id)?;
let (return_ptr, return_to_block) = match destination {
Some((ptr, block)) => (ptr, StackPopCleanup::Goto(block)),
None => (Pointer::never_ptr(), StackPopCleanup::None),
let (return_lvalue, return_to_block) = match destination {
Some((lvalue, block)) => (lvalue, StackPopCleanup::Goto(block)),
None => {
// FIXME(solson)
let lvalue = Lvalue::from_ptr(Pointer::never_ptr());
(lvalue, StackPopCleanup::None)
}
};
self.push_stack_frame(resolved_def_id, span, mir, resolved_substs, return_ptr, return_to_block)?;
self.push_stack_frame(
resolved_def_id,
span,
mir,
resolved_substs,
return_lvalue,
return_to_block
)?;
for (i, (arg_val, arg_ty)) in args.into_iter().enumerate() {
// argument start at index 1, since index 0 is reserved for the return allocation
let dest = self.frame().locals[i + 1];
// FIXME(solson)
let dest = match dest {
Value::ByRef(p) => Lvalue::from_ptr(p),
_ => bug!("all locals should be ByRef until I finish refactoring"),
};
self.write_value(arg_val, dest, arg_ty)?;
}
@ -245,7 +264,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
&mut self,
def_id: DefId,
args: &[mir::Operand<'tcx>],
dest: Pointer,
dest: Lvalue,
dest_size: usize,
) -> EvalResult<'tcx, ()> {
let name = self.tcx.item_name(def_id);
@ -269,10 +288,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
match &link_name[..] {
"__rust_allocate" => {
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 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)?;
self.write_primval(dest, PrimVal::Ptr(ptr))?;
}
"__rust_reallocate" => {
@ -280,7 +301,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
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)?;
self.write_primval(dest, PrimVal::Ptr(new_ptr))?;
}
"memcmp" => {
@ -300,7 +321,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}
};
self.memory.write_int(dest, result, dest_size)?;
self.write_primval(dest, PrimVal::int_with_size(result, dest_size))?;
}
_ => {

View File

@ -1,5 +1,5 @@
use memory::{Memory, Pointer};
use error::EvalResult;
use memory::{Memory, Pointer};
use primval::PrimVal;
/// A `Value` represents a single self-contained Rust value.
@ -11,15 +11,14 @@ use primval::PrimVal;
/// 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 {
pub 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> {
impl<'a, 'tcx: 'a> Value {
pub(super) fn read_ptr(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, Pointer> {
use self::Value::*;
match *self {
ByRef(ptr) => mem.read_ptr(ptr),
@ -30,7 +29,7 @@ impl Value {
}
}
pub(super) fn expect_vtable<'a, 'tcx: 'a>(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, Pointer> {
pub(super) fn expect_vtable(&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)),
@ -39,7 +38,7 @@ impl Value {
}
}
pub(super) fn expect_slice_len<'a, 'tcx: 'a>(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, u64> {
pub(super) fn expect_slice_len(&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)),

View File

@ -60,6 +60,28 @@ impl PrimVal {
_ => bug!("{}", error_msg),
}
}
pub fn uint_with_size(n: u64, size: usize) -> Self {
use self::PrimVal::*;
match size {
1 => U8(n as u8),
2 => U16(n as u16),
4 => U32(n as u32),
8 => U64(n),
_ => bug!("can't make uint ({}) with size {}", n, size),
}
}
pub fn int_with_size(n: i64, size: usize) -> Self {
use self::PrimVal::*;
match size {
1 => I8(n as i8),
2 => I16(n as i16),
4 => I32(n as i32),
8 => I64(n),
_ => bug!("can't make int ({}) with size {}", n, size),
}
}
}
/// returns the result of the operation and whether the operation overflowed

View File

@ -1,9 +0,0 @@
#![feature(start)]
#[start]
fn foo(nargs: isize, args: *const *const u8) -> isize {
if nargs > 0 {
assert!(unsafe{*args} as usize != 0);
}
0
}