diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 34a8c1b2be..82702ae437 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -7,11 +7,21 @@ extern crate rustc; extern crate rustc_driver; extern crate env_logger; extern crate log_settings; -extern crate log; +extern crate syntax; +#[macro_use] extern crate log; -use miri::interpreter; +use miri::{ + EvalContext, + CachedMir, + step, + EvalError, + Frame, +}; use rustc::session::Session; use rustc_driver::{driver, CompilerCalls}; +use rustc::ty::{TyCtxt, subst}; +use rustc::mir::mir_map::MirMap; +use rustc::hir::def_id::DefId; struct MiriCompilerCalls; @@ -25,13 +35,84 @@ impl<'a> CompilerCalls<'a> for MiriCompilerCalls { control.after_analysis.callback = Box::new(|state| { state.session.abort_if_errors(); - interpreter::interpret_start_points(state.tcx.unwrap(), state.mir_map.unwrap()); + interpret_start_points(state.tcx.unwrap(), state.mir_map.unwrap()); }); control } } + + +fn interpret_start_points<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + mir_map: &MirMap<'tcx>, +) { + let initial_indentation = ::log_settings::settings().indentation; + for (&id, mir) in &mir_map.map { + for attr in tcx.map.attrs(id) { + use syntax::attr::AttrMetaMethods; + if attr.check_name("miri_run") { + let item = tcx.map.expect_item(id); + + ::log_settings::settings().indentation = initial_indentation; + + debug!("Interpreting: {}", item.name); + + let mut ecx = EvalContext::new(tcx, mir_map); + let substs = tcx.mk_substs(subst::Substs::empty()); + let return_ptr = ecx.alloc_ret_ptr(mir.return_ty, substs); + + ecx.push_stack_frame(tcx.map.local_def_id(id), mir.span, CachedMir::Ref(mir), substs, return_ptr); + + loop { + match (step(&mut ecx), return_ptr) { + (Ok(true), _) => {}, + (Ok(false), Some(ptr)) => if log_enabled!(::log::LogLevel::Debug) { + ecx.memory().dump(ptr.alloc_id); + break; + }, + (Ok(false), None) => { + warn!("diverging function returned"); + break; + }, + // FIXME: diverging functions can end up here in some future miri + (Err(e), _) => { + report(tcx, &ecx, e); + break; + }, + } + } + } + } + } +} + +fn report(tcx: TyCtxt, ecx: &EvalContext, e: EvalError) { + let frame = ecx.stack().last().expect("stackframe was empty"); + let block = frame.mir.basic_block_data(frame.next_block); + let span = if frame.stmt < block.statements.len() { + block.statements[frame.stmt].span + } else { + block.terminator().span + }; + let mut err = tcx.sess.struct_span_err(span, &e.to_string()); + for &Frame { def_id, substs, span, .. } in ecx.stack().iter().rev() { + // FIXME(solson): Find a way to do this without this Display impl hack. + use rustc::util::ppaux; + use std::fmt; + struct Instance<'tcx>(DefId, &'tcx subst::Substs<'tcx>); + impl<'tcx> fmt::Display for Instance<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + ppaux::parameterized(f, self.1, self.0, ppaux::Ns::Value, &[], + |tcx| tcx.lookup_item_type(self.0).generics) + } + } + err.span_note(span, &format!("inside call to {}", Instance(def_id, substs))); + } + err.emit(); +} + #[miri_run] fn main() { init_logger(); diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 6c0d58f1c4..6831ab212f 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -9,7 +9,7 @@ use rustc::ty::subst::{self, Subst, Substs}; use rustc::ty::{self, Ty, TyCtxt}; use rustc::util::nodemap::DefIdMap; use std::cell::RefCell; -use std::ops::{Deref, DerefMut}; +use std::ops::Deref; use std::rc::Rc; use std::{iter, mem}; use syntax::ast; @@ -24,7 +24,11 @@ use std::collections::HashMap; mod stepper; -struct GlobalEvalContext<'a, 'tcx: 'a> { +pub fn step<'ecx, 'a: 'ecx, 'tcx: 'a>(ecx: &'ecx mut EvalContext<'a, 'tcx>) -> EvalResult { + stepper::Stepper::new(ecx).step() +} + +pub struct EvalContext<'a, 'tcx: 'a> { /// The results of the type checker, from rustc. tcx: TyCtxt<'a, 'tcx, 'tcx>, @@ -39,61 +43,44 @@ struct GlobalEvalContext<'a, 'tcx: 'a> { /// Precomputed statics, constants and promoteds statics: HashMap, Pointer>, -} - -struct FnEvalContext<'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> { - gecx: &'a mut GlobalEvalContext<'b, 'tcx>, /// The virtual call stack. - stack: Vec>, -} - -impl<'a, 'b, 'mir, 'tcx> Deref for FnEvalContext<'a, 'b, 'mir, 'tcx> { - type Target = GlobalEvalContext<'b, 'tcx>; - fn deref(&self) -> &Self::Target { - self.gecx - } -} - -impl<'a, 'b, 'mir, 'tcx> DerefMut for FnEvalContext<'a, 'b, 'mir, 'tcx> { - fn deref_mut(&mut self) -> &mut Self::Target { - self.gecx - } + stack: Vec>, } /// A stack frame. -struct Frame<'a, 'tcx: 'a> { +pub struct Frame<'a, 'tcx: 'a> { /// The def_id of the current function - def_id: DefId, + pub def_id: DefId, /// The span of the call site - span: codemap::Span, + pub span: codemap::Span, /// type substitutions for the current function invocation - substs: &'tcx Substs<'tcx>, + pub substs: &'tcx Substs<'tcx>, /// The MIR for the function called on this frame. - mir: CachedMir<'a, 'tcx>, + pub mir: CachedMir<'a, 'tcx>, /// The block that is currently executed (or will be executed after the above call stacks return) - next_block: mir::BasicBlock, + pub next_block: mir::BasicBlock, /// A pointer for writing the return value of the current call if it's not a diverging call. - return_ptr: Option, + pub return_ptr: Option, /// The list of locals for the current function, stored in order as /// `[arguments..., variables..., temporaries...]`. The variables begin at `self.var_offset` /// and the temporaries at `self.temp_offset`. - locals: Vec, + pub locals: Vec, /// The offset of the first variable in `self.locals`. - var_offset: usize, + pub var_offset: usize, /// The offset of the first temporary in `self.locals`. - temp_offset: usize, + pub temp_offset: usize, /// The index of the currently evaluated statment - stmt: usize, + pub stmt: usize, } #[derive(Copy, Clone, Debug, Eq, PartialEq)] @@ -111,23 +98,11 @@ enum LvalueExtra { } #[derive(Clone)] -enum CachedMir<'mir, 'tcx: 'mir> { +pub enum CachedMir<'mir, 'tcx: 'mir> { Ref(&'mir mir::Mir<'tcx>), Owned(Rc>) } -/// Represents the action to be taken in the main loop as a result of executing a terminator. -enum TerminatorTarget { - /// Make a local jump to the next block - Block, - - /// Start executing from the new current frame. (For function calls.) - Call, - - /// Stop executing the current frame and resume the previous frame. - Return, -} - #[derive(Clone, Debug, Eq, PartialEq, Hash)] /// Uniquely identifies a specific constant or static struct ConstantId<'tcx> { @@ -148,9 +123,9 @@ enum ConstantKind { Global, } -impl<'a, 'tcx> GlobalEvalContext<'a, 'tcx> { - fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir_map: &'a MirMap<'tcx>) -> Self { - GlobalEvalContext { +impl<'a, 'tcx> EvalContext<'a, 'tcx> { + pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir_map: &'a MirMap<'tcx>) -> Self { + EvalContext { tcx: tcx, mir_map: mir_map, mir_cache: RefCell::new(DefIdMap()), @@ -160,24 +135,11 @@ impl<'a, 'tcx> GlobalEvalContext<'a, 'tcx> { .bit_width() .expect("Session::target::uint_type was usize")/8), statics: HashMap::new(), + stack: Vec::new(), } } - fn call(&mut self, mir: &mir::Mir<'tcx>, def_id: DefId) -> EvalResult> { - let substs = self.tcx.mk_substs(subst::Substs::empty()); - let return_ptr = self.alloc_ret_ptr(mir.return_ty, substs); - - let mut nested_fecx = FnEvalContext::new(self); - - nested_fecx.push_stack_frame(def_id, mir.span, CachedMir::Ref(mir), substs, None); - - nested_fecx.frame_mut().return_ptr = return_ptr; - - nested_fecx.run()?; - Ok(return_ptr) - } - - fn alloc_ret_ptr(&mut self, output_ty: ty::FnOutput<'tcx>, substs: &'tcx Substs<'tcx>) -> Option { + pub fn alloc_ret_ptr(&mut self, output_ty: ty::FnOutput<'tcx>, substs: &'tcx Substs<'tcx>) -> Option { match output_ty { ty::FnConverging(ty) => { let size = self.type_size(ty, substs); @@ -187,6 +149,14 @@ impl<'a, 'tcx> GlobalEvalContext<'a, 'tcx> { } } + pub fn memory(&self) -> &Memory { + &self.memory + } + + pub fn stack(&self) -> &[Frame] { + &self.stack + } + // TODO(solson): Try making const_to_primval instead. fn const_to_ptr(&mut self, const_val: &const_val::ConstVal) -> EvalResult { use rustc::middle::const_val::ConstVal::*; @@ -349,57 +319,8 @@ impl<'a, 'tcx> GlobalEvalContext<'a, 'tcx> { ty.layout(&infcx).unwrap() }) } -} - -impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { - fn new(gecx: &'a mut GlobalEvalContext<'b, 'tcx>) -> Self { - FnEvalContext { - gecx: gecx, - stack: Vec::new(), - } - } - - #[inline(never)] - #[cold] - fn report(&self, e: &EvalError) { - let stmt = self.frame().stmt; - let block = self.basic_block(); - let span = if stmt < block.statements.len() { - block.statements[stmt].span - } else { - block.terminator().span - }; - let mut err = self.tcx.sess.struct_span_err(span, &e.to_string()); - for &Frame{ def_id, substs, span, .. } in self.stack.iter().rev() { - // FIXME(solson): Find a way to do this without this Display impl hack. - use rustc::util::ppaux; - use std::fmt; - struct Instance<'tcx>(DefId, &'tcx Substs<'tcx>); - impl<'tcx> fmt::Display for Instance<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - ppaux::parameterized(f, self.1, self.0, ppaux::Ns::Value, &[], - |tcx| tcx.lookup_item_type(self.0).generics) - } - } - err.span_note(span, &format!("inside call to {}", Instance(def_id, substs))); - } - err.emit(); - } - fn maybe_report(&self, r: EvalResult) -> EvalResult { - if let Err(ref e) = r { - self.report(e); - } - r - } - - fn run(&mut self) -> EvalResult<()> { - let mut stepper = stepper::Stepper::new(self); - while stepper.step()? {} - Ok(()) - } - - fn push_stack_frame(&mut self, def_id: DefId, span: codemap::Span, mir: CachedMir<'mir, 'tcx>, substs: &'tcx Substs<'tcx>, + pub fn push_stack_frame(&mut self, def_id: DefId, span: codemap::Span, mir: CachedMir<'a, 'tcx>, substs: &'tcx Substs<'tcx>, return_ptr: Option) { let arg_tys = mir.arg_decls.iter().map(|a| a.ty); @@ -411,11 +332,16 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { ::log_settings::settings().indentation += 1; + let locals: Vec = arg_tys.chain(var_tys).chain(temp_tys).map(|ty| { + let size = self.type_size(ty, substs); + self.memory.allocate(size) + }).collect(); + self.stack.push(Frame { mir: mir.clone(), next_block: mir::START_BLOCK, return_ptr: return_ptr, - locals: Vec::new(), + locals: locals, var_offset: num_args, temp_offset: num_args + num_vars, span: span, @@ -423,13 +349,6 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { substs: substs, stmt: 0, }); - - let locals: Vec = arg_tys.chain(var_tys).chain(temp_tys).map(|ty| { - let size = self.type_size(ty); - self.memory.allocate(size) - }).collect(); - - self.frame_mut().locals = locals; } fn pop_stack_frame(&mut self) { @@ -439,27 +358,25 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { } fn eval_terminator(&mut self, terminator: &mir::Terminator<'tcx>) - -> EvalResult { + -> EvalResult<()> { use rustc::mir::repr::TerminatorKind::*; - let target = match terminator.kind { - Return => TerminatorTarget::Return, + match terminator.kind { + Return => self.pop_stack_frame(), Goto { target } => { self.frame_mut().next_block = target; - TerminatorTarget::Block }, If { ref cond, targets: (then_target, else_target) } => { let cond_ptr = self.eval_operand(cond)?; let cond_val = self.memory.read_bool(cond_ptr)?; self.frame_mut().next_block = if cond_val { then_target } else { else_target }; - TerminatorTarget::Block } SwitchInt { ref discr, ref values, ref targets, .. } => { let discr_ptr = self.eval_lvalue(discr)?.to_ptr(); let discr_size = self - .type_layout(self.lvalue_ty(discr)) + .type_layout(self.lvalue_ty(discr), self.substs()) .size(&self.tcx.data_layout) .bytes() as usize; let discr_val = self.memory.read_uint(discr_ptr, discr_size)?; @@ -477,7 +394,6 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { } self.frame_mut().next_block = target_block; - TerminatorTarget::Block } Switch { ref discr, ref targets, adt_def } => { @@ -490,7 +406,6 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { match matching { Some(i) => { self.frame_mut().next_block = targets[i]; - TerminatorTarget::Block }, None => return Err(EvalError::InvalidDiscriminant), } @@ -512,7 +427,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { let name = self.tcx.item_name(def_id).as_str(); match fn_ty.sig.0.output { ty::FnConverging(ty) => { - let size = self.type_size(ty); + let size = self.type_size(ty, self.substs()); let ret = return_ptr.unwrap(); self.call_intrinsic(&name, substs, args, ret, size)? } @@ -523,7 +438,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { Abi::C => { match fn_ty.sig.0.output { ty::FnConverging(ty) => { - let size = self.type_size(ty); + let size = self.type_size(ty, self.substs()); self.call_c_abi(def_id, args, return_ptr.unwrap(), size)? } ty::FnDiverging => unimplemented!(), @@ -553,7 +468,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { let last_arg = args.last().unwrap(); let last = self.eval_operand(last_arg)?; let last_ty = self.operand_ty(last_arg); - let last_layout = self.type_layout(last_ty); + let last_layout = self.type_layout(last_ty, self.substs()); match (&last_ty.sty, last_layout) { (&ty::TyTuple(fields), &Layout::Univariant { ref variant, .. }) => { @@ -576,8 +491,6 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { let dest = self.frame().locals[i]; self.move_(src, dest, src_ty)?; } - - TerminatorTarget::Call } abi => return Err(EvalError::Unimplemented(format!("can't handle function with {:?} ABI", abi))), @@ -593,13 +506,12 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { let ty = self.lvalue_ty(value); self.drop(ptr, ty)?; self.frame_mut().next_block = target; - TerminatorTarget::Block } Resume => unimplemented!(), - }; + } - Ok(target) + Ok(()) } fn drop(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<()> { @@ -638,7 +550,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { // Filling drop. // FIXME(solson): Trait objects (with no static size) probably get filled, too. - let size = self.type_size(ty); + let size = self.type_size(ty, self.substs()); self.memory.drop_fill(ptr, size)?; Ok(()) @@ -646,7 +558,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { fn read_discriminant_value(&self, adt_ptr: Pointer, adt_ty: Ty<'tcx>) -> EvalResult { use rustc::ty::layout::Layout::*; - let adt_layout = self.type_layout(adt_ty); + let adt_layout = self.type_layout(adt_ty, self.substs()); let discr_val = match *adt_layout { General { discr, .. } | CEnum { discr, .. } => { @@ -689,7 +601,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { args: &[mir::Operand<'tcx>], dest: Pointer, dest_size: usize - ) -> EvalResult { + ) -> EvalResult<()> { let args_res: EvalResult> = args.iter() .map(|arg| self.eval_operand(arg)) .collect(); @@ -699,7 +611,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { // FIXME(solson): Handle different integer types correctly. "add_with_overflow" => { let ty = *substs.types.get(subst::FnSpace, 0); - let size = self.type_size(ty); + let size = self.type_size(ty, self.substs()); let left = self.memory.read_int(args[0], size)?; let right = self.memory.read_int(args[1], size)?; let (n, overflowed) = unsafe { @@ -713,7 +625,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { "copy_nonoverlapping" => { let elem_ty = *substs.types.get(subst::FnSpace, 0); - let elem_size = self.type_size(elem_ty); + let elem_size = self.type_size(elem_ty, self.substs()); let src = self.memory.read_ptr(args[0])?; let dest = self.memory.read_ptr(args[1])?; let count = self.memory.read_isize(args[2])?; @@ -729,7 +641,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { "forget" => { let arg_ty = *substs.types.get(subst::FnSpace, 0); - let arg_size = self.type_size(arg_ty); + let arg_size = self.type_size(arg_ty, self.substs()); self.memory.drop_fill(args[0], arg_size)?; } @@ -748,7 +660,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { // FIXME(solson): Handle different integer types correctly. "mul_with_overflow" => { let ty = *substs.types.get(subst::FnSpace, 0); - let size = self.type_size(ty); + let size = self.type_size(ty, self.substs()); let left = self.memory.read_int(args[0], size)?; let right = self.memory.read_int(args[1], size)?; let (n, overflowed) = unsafe { @@ -760,7 +672,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { "offset" => { let pointee_ty = *substs.types.get(subst::FnSpace, 0); - let pointee_size = self.type_size(pointee_ty) as isize; + let pointee_size = self.type_size(pointee_ty, self.substs()) as isize; let ptr_arg = args[0]; let offset = self.memory.read_isize(args[1])?; @@ -781,7 +693,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { // FIXME(solson): Handle different integer types correctly. Use primvals? "overflowing_sub" => { let ty = *substs.types.get(subst::FnSpace, 0); - let size = self.type_size(ty); + let size = self.type_size(ty, self.substs()); let left = self.memory.read_int(args[0], size)?; let right = self.memory.read_int(args[1], size)?; let n = left.wrapping_sub(right); @@ -790,20 +702,20 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { "size_of" => { let ty = *substs.types.get(subst::FnSpace, 0); - let size = self.type_size(ty) as u64; + let size = self.type_size(ty, self.substs()) as u64; self.memory.write_uint(dest, size, dest_size)?; } "size_of_val" => { let ty = *substs.types.get(subst::FnSpace, 0); if self.type_is_sized(ty) { - let size = self.type_size(ty) as u64; + let size = self.type_size(ty, self.substs()) as u64; self.memory.write_uint(dest, size, dest_size)?; } else { match ty.sty { ty::TySlice(_) | ty::TyStr => { let elem_ty = ty.sequence_element_type(self.tcx); - let elem_size = self.type_size(elem_ty) as u64; + let elem_size = self.type_size(elem_ty, self.substs()) as u64; let ptr_size = self.memory.pointer_size as isize; let n = self.memory.read_usize(args[0].offset(ptr_size))?; self.memory.write_uint(dest, n * elem_size, dest_size)?; @@ -826,7 +738,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { // Since we pushed no stack frame, the main loop will act // as if the call just completed and it's returning to the // current frame. - Ok(TerminatorTarget::Call) + Ok(()) } fn call_c_abi( @@ -835,7 +747,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { args: &[mir::Operand<'tcx>], dest: Pointer, dest_size: usize, - ) -> EvalResult { + ) -> EvalResult<()> { let name = self.tcx.item_name(def_id); let attrs = self.tcx.get_attrs(def_id); let link_name = match attr::first_attr_value_str_by_name(&attrs, "link_name") { @@ -888,7 +800,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { // Since we pushed no stack frame, the main loop will act // as if the call just completed and it's returning to the // current frame. - Ok(TerminatorTarget::Call) + Ok(()) } fn assign_fields>( @@ -911,7 +823,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { { let dest = self.eval_lvalue(lvalue)?.to_ptr(); let dest_ty = self.lvalue_ty(lvalue); - let dest_layout = self.type_layout(dest_ty); + let dest_layout = self.type_layout(dest_ty, self.substs()); use rustc::mir::repr::Rvalue::*; match *rvalue { @@ -951,7 +863,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { Array { .. } => { let elem_size = match dest_ty.sty { - ty::TyArray(elem_ty, _) => self.type_size(elem_ty) as u64, + ty::TyArray(elem_ty, _) => self.type_size(elem_ty, self.substs()) as u64, _ => panic!("tried to assign {:?} to non-array type {:?}", kind, dest_ty), }; @@ -1029,7 +941,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { Repeat(ref operand, _) => { let (elem_size, length) = match dest_ty.sty { - ty::TyArray(elem_ty, n) => (self.type_size(elem_ty), n), + ty::TyArray(elem_ty, n) => (self.type_size(elem_ty, self.substs()), n), _ => panic!("tried to assign array-repeat to non-array type {:?}", dest_ty), }; @@ -1070,7 +982,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { } Box(ty) => { - let size = self.type_size(ty); + let size = self.type_size(ty, self.substs()); let ptr = self.memory.allocate(size); self.memory.write_ptr(dest, ptr)?; } @@ -1164,7 +1076,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { } fn get_field_offset(&self, ty: Ty<'tcx>, field_index: usize) -> EvalResult { - let layout = self.type_layout(ty); + let layout = self.type_layout(ty, self.substs()); use rustc::ty::layout::Layout::*; match *layout { @@ -1229,13 +1141,13 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { substs: substs, kind: ConstantKind::Global, }; - *self.gecx.statics.get(&cid).expect("static should have been cached (lvalue)") + *self.statics.get(&cid).expect("static should have been cached (lvalue)") }, Projection(ref proj) => { let base = self.eval_lvalue(&proj.base)?; let base_ty = self.lvalue_ty(&proj.base); - let base_layout = self.type_layout(base_ty); + let base_layout = self.type_layout(base_ty, self.substs()); use rustc::mir::repr::ProjectionElem::*; match proj.elem { @@ -1296,7 +1208,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { Index(ref operand) => { let elem_size = match base_ty.sty { ty::TyArray(elem_ty, _) | - ty::TySlice(elem_ty) => self.type_size(elem_ty), + ty::TySlice(elem_ty) => self.type_size(elem_ty, self.substs()), _ => panic!("indexing expected an array or slice, got {:?}", base_ty), }; let n_ptr = self.eval_operand(operand)?; @@ -1313,19 +1225,15 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { } fn lvalue_ty(&self, lvalue: &mir::Lvalue<'tcx>) -> Ty<'tcx> { - self.monomorphize(self.mir().lvalue_ty(self.tcx, lvalue).to_ty(self.tcx)) + self.monomorphize(self.mir().lvalue_ty(self.tcx, lvalue).to_ty(self.tcx), self.substs()) } fn operand_ty(&self, operand: &mir::Operand<'tcx>) -> Ty<'tcx> { - self.monomorphize(self.mir().operand_ty(self.tcx, operand)) - } - - fn monomorphize(&self, ty: Ty<'tcx>) -> Ty<'tcx> { - self.gecx.monomorphize(ty, self.substs()) + self.monomorphize(self.mir().operand_ty(self.tcx, operand), self.substs()) } fn move_(&mut self, src: Pointer, dest: Pointer, ty: Ty<'tcx>) -> EvalResult<()> { - let size = self.type_size(ty); + let size = self.type_size(ty, self.substs()); self.memory.copy(src, dest, size)?; if self.type_needs_drop(ty) { self.memory.drop_fill(src, size)?; @@ -1333,14 +1241,6 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { Ok(()) } - fn type_size(&self, ty: Ty<'tcx>) -> usize { - self.gecx.type_size(ty, self.substs()) - } - - fn type_layout(&self, ty: Ty<'tcx>) -> &'tcx Layout { - self.gecx.type_layout(ty, self.substs()) - } - pub fn read_primval(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult { use syntax::ast::{IntTy, UintTy}; let val = match (self.memory.pointer_size, &ty.sty) { @@ -1380,20 +1280,15 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> { Ok(val) } - fn frame(&self) -> &Frame<'mir, 'tcx> { + fn frame(&self) -> &Frame<'a, 'tcx> { self.stack.last().expect("no call frames exist") } - fn basic_block(&self) -> &mir::BasicBlockData<'tcx> { - let frame = self.frame(); - frame.mir.basic_block_data(frame.next_block) - } - - fn frame_mut(&mut self) -> &mut Frame<'mir, 'tcx> { + fn frame_mut(&mut self) -> &mut Frame<'a, 'tcx> { self.stack.last_mut().expect("no call frames exist") } - fn mir(&self) -> CachedMir<'mir, 'tcx> { + fn mir(&self) -> CachedMir<'a, 'tcx> { self.frame().mir.clone() } @@ -1472,37 +1367,6 @@ pub fn get_impl_method<'a, 'tcx>( } } -pub fn interpret_start_points<'a, 'tcx>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, - mir_map: &MirMap<'tcx>, -) { - let initial_indentation = ::log_settings::settings().indentation; - for (&id, mir) in &mir_map.map { - for attr in tcx.map.attrs(id) { - use syntax::attr::AttrMetaMethods; - if attr.check_name("miri_run") { - let item = tcx.map.expect_item(id); - - ::log_settings::settings().indentation = initial_indentation; - - debug!("Interpreting: {}", item.name); - - let mut gecx = GlobalEvalContext::new(tcx, mir_map); - match gecx.call(mir, tcx.map.local_def_id(id)) { - Ok(Some(return_ptr)) => if log_enabled!(::log::LogLevel::Debug) { - gecx.memory.dump(return_ptr.alloc_id); - }, - Ok(None) => warn!("diverging function returned"), - Err(_e) => { - // TODO(solson): Detect whether the error was already reported or not. - // tcx.sess.err(&e.to_string()); - } - } - } - } - } -} - // TODO(solson): Upstream these methods into rustc::ty::layout. trait IntegerExt { diff --git a/src/interpreter/stepper.rs b/src/interpreter/stepper.rs index a113b8f98e..3e4816834a 100644 --- a/src/interpreter/stepper.rs +++ b/src/interpreter/stepper.rs @@ -1,9 +1,7 @@ use super::{ - FnEvalContext, CachedMir, - TerminatorTarget, ConstantId, - GlobalEvalContext, + EvalContext, ConstantKind, }; use error::EvalResult; @@ -13,138 +11,117 @@ use rustc::hir::def_id::DefId; use rustc::mir::visit::{Visitor, LvalueContext}; use syntax::codemap::Span; use std::rc::Rc; -use memory::Pointer; -pub struct Stepper<'fncx, 'a: 'fncx, 'b: 'a + 'mir, 'mir: 'fncx, 'tcx: 'b>{ - fncx: &'fncx mut FnEvalContext<'a, 'b, 'mir, 'tcx>, - - // a cache of the constants to be computed before the next statement/terminator - // this is an optimization, so we don't have to allocate a new vector for every statement - constants: Vec<(ConstantId<'tcx>, Span, Pointer, CachedMir<'mir, 'tcx>)>, +pub(super) struct Stepper<'ecx, 'a: 'ecx, 'tcx: 'a>{ + ecx: &'ecx mut EvalContext<'a, 'tcx>, } -impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx> { - pub(super) fn new(fncx: &'fncx mut FnEvalContext<'a, 'b, 'mir, 'tcx>) -> Self { +impl<'ecx, 'a, 'tcx> Stepper<'ecx, 'a, 'tcx> { + pub(super) fn new(ecx: &'ecx mut EvalContext<'a, 'tcx>) -> Self { Stepper { - fncx: fncx, - constants: Vec::new(), + ecx: ecx, } } fn statement(&mut self, stmt: &mir::Statement<'tcx>) -> EvalResult<()> { trace!("{:?}", stmt); let mir::StatementKind::Assign(ref lvalue, ref rvalue) = stmt.kind; - let result = self.fncx.eval_assignment(lvalue, rvalue); - self.fncx.maybe_report(result)?; - self.fncx.frame_mut().stmt += 1; + self.ecx.eval_assignment(lvalue, rvalue)?; + self.ecx.frame_mut().stmt += 1; Ok(()) } fn terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> EvalResult<()> { // after a terminator we go to a new block - self.fncx.frame_mut().stmt = 0; - let term = { - trace!("{:?}", terminator.kind); - let result = self.fncx.eval_terminator(terminator); - self.fncx.maybe_report(result)? - }; - match term { - TerminatorTarget::Return => { - self.fncx.pop_stack_frame(); - }, - TerminatorTarget::Block | - TerminatorTarget::Call => trace!("// {:?}", self.fncx.frame().next_block), + self.ecx.frame_mut().stmt = 0; + trace!("{:?}", terminator.kind); + self.ecx.eval_terminator(terminator)?; + if !self.ecx.stack.is_empty() { + trace!("// {:?}", self.ecx.frame().next_block); } Ok(()) } // returns true as long as there are more things to do - pub fn step(&mut self) -> EvalResult { - if self.fncx.stack.is_empty() { + pub(super) fn step(&mut self) -> EvalResult { + if self.ecx.stack.is_empty() { return Ok(false); } - let block = self.fncx.frame().next_block; - let stmt = self.fncx.frame().stmt; - let mir = self.fncx.mir(); + let block = self.ecx.frame().next_block; + let stmt = self.ecx.frame().stmt; + let mir = self.ecx.mir(); let basic_block = mir.basic_block_data(block); if let Some(ref stmt) = basic_block.statements.get(stmt) { - assert!(self.constants.is_empty()); + let current_stack = self.ecx.stack.len(); ConstantExtractor { span: stmt.span, - substs: self.fncx.substs(), - def_id: self.fncx.frame().def_id, - gecx: self.fncx.gecx, - constants: &mut self.constants, + substs: self.ecx.substs(), + def_id: self.ecx.frame().def_id, + ecx: self.ecx, mir: &mir, }.visit_statement(block, stmt); - if self.constants.is_empty() { + if current_stack == self.ecx.stack.len() { self.statement(stmt)?; } else { - self.extract_constants()?; + // ConstantExtractor added some new frames for statics/constants/promoteds + // self.step() can't be "done", so it can't return false + assert!(self.step()?); } return Ok(true); } let terminator = basic_block.terminator(); - assert!(self.constants.is_empty()); + let current_stack = self.ecx.stack.len(); ConstantExtractor { span: terminator.span, - substs: self.fncx.substs(), - def_id: self.fncx.frame().def_id, - gecx: self.fncx.gecx, - constants: &mut self.constants, + substs: self.ecx.substs(), + def_id: self.ecx.frame().def_id, + ecx: self.ecx, mir: &mir, }.visit_terminator(block, terminator); - if self.constants.is_empty() { + if current_stack == self.ecx.stack.len() { self.terminator(terminator)?; } else { - self.extract_constants()?; + // ConstantExtractor added some new frames for statics/constants/promoteds + // self.step() can't be "done", so it can't return false + assert!(self.step()?); } Ok(true) } - - fn extract_constants(&mut self) -> EvalResult<()> { - assert!(!self.constants.is_empty()); - for (cid, span, return_ptr, mir) in self.constants.drain(..) { - trace!("queuing a constant"); - self.fncx.push_stack_frame(cid.def_id, span, mir, cid.substs, Some(return_ptr)); - } - // self.step() can't be "done", so it can't return false - assert!(self.step()?); - Ok(()) - } } -struct ConstantExtractor<'a, 'b: 'mir, 'mir: 'a, 'tcx: 'b> { +// 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` +// 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, - // FIXME: directly push the new stackframes instead of doing this intermediate caching - constants: &'a mut Vec<(ConstantId<'tcx>, Span, Pointer, CachedMir<'mir, 'tcx>)>, - gecx: &'a mut GlobalEvalContext<'b, 'tcx>, + ecx: &'a mut EvalContext<'b, 'tcx>, mir: &'a mir::Mir<'tcx>, def_id: DefId, substs: &'tcx subst::Substs<'tcx>, } -impl<'a, 'b, 'mir, 'tcx> ConstantExtractor<'a, 'b, 'mir, 'tcx> { +impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> { fn global_item(&mut self, def_id: DefId, substs: &'tcx subst::Substs<'tcx>, span: Span) { let cid = ConstantId { def_id: def_id, substs: substs, kind: ConstantKind::Global, }; - if self.gecx.statics.contains_key(&cid) { + if self.ecx.statics.contains_key(&cid) { return; } - let mir = self.gecx.load_mir(def_id); - let ptr = self.gecx.alloc_ret_ptr(mir.return_ty, substs).expect("there's no such thing as an unreachable static"); - self.gecx.statics.insert(cid.clone(), ptr); - self.constants.push((cid, span, ptr, mir)); + let mir = self.ecx.load_mir(def_id); + let ptr = self.ecx.alloc_ret_ptr(mir.return_ty, substs).expect("there's no such thing as an unreachable static"); + self.ecx.statics.insert(cid.clone(), ptr); + self.ecx.push_stack_frame(def_id, span, mir, substs, Some(ptr)); } } -impl<'a, 'b, 'mir, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'mir, 'tcx> { +impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> { fn visit_constant(&mut self, constant: &mir::Constant<'tcx>) { self.super_constant(constant); match constant.literal { @@ -165,15 +142,15 @@ impl<'a, 'b, 'mir, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'mir, 'tcx> substs: self.substs, kind: ConstantKind::Promoted(index), }; - if self.gecx.statics.contains_key(&cid) { + if self.ecx.statics.contains_key(&cid) { return; } let mir = self.mir.promoted[index].clone(); let return_ty = mir.return_ty; - let return_ptr = self.gecx.alloc_ret_ptr(return_ty, cid.substs).expect("there's no such thing as an unreachable static"); + let return_ptr = self.ecx.alloc_ret_ptr(return_ty, cid.substs).expect("there's no such thing as an unreachable static"); let mir = CachedMir::Owned(Rc::new(mir)); - self.gecx.statics.insert(cid.clone(), return_ptr); - self.constants.push((cid, constant.span, return_ptr, mir)); + self.ecx.statics.insert(cid.clone(), return_ptr); + self.ecx.push_stack_frame(self.def_id, constant.span, mir, self.substs, Some(return_ptr)); } } } @@ -181,7 +158,7 @@ impl<'a, 'b, 'mir, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'mir, 'tcx> fn visit_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>, context: LvalueContext) { self.super_lvalue(lvalue, context); if let mir::Lvalue::Static(def_id) = *lvalue { - let substs = self.gecx.tcx.mk_substs(subst::Substs::empty()); + let substs = self.ecx.tcx.mk_substs(subst::Substs::empty()); let span = self.span; self.global_item(def_id, substs, span); } diff --git a/src/lib.rs b/src/lib.rs index 4e06a3ce38..c3369878f3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,6 +20,20 @@ extern crate log_settings; extern crate byteorder; mod error; -pub mod interpreter; +mod interpreter; mod memory; mod primval; + +pub use error::{ + EvalError, + EvalResult, +}; + +pub use interpreter::{ + EvalContext, + step, + Frame, + CachedMir, +}; + +pub use memory::Memory;