2016-06-23 00:02:47 -06:00
|
|
|
//! This module contains the `EvalContext` methods for executing a single step of the interpreter.
|
|
|
|
//!
|
|
|
|
//! The main entry point is the `step` method.
|
|
|
|
|
2016-06-01 17:05:20 +02:00
|
|
|
use super::{
|
|
|
|
CachedMir,
|
2016-06-02 17:05:17 +02:00
|
|
|
ConstantId,
|
2016-06-10 16:20:17 +02:00
|
|
|
EvalContext,
|
2016-06-08 11:46:37 +02:00
|
|
|
ConstantKind,
|
2016-06-01 17:05:20 +02:00
|
|
|
};
|
|
|
|
use error::EvalResult;
|
|
|
|
use rustc::mir::repr as mir;
|
2016-06-09 11:27:02 +02:00
|
|
|
use rustc::ty::{subst, self};
|
2016-06-03 15:48:56 +02:00
|
|
|
use rustc::hir::def_id::DefId;
|
|
|
|
use rustc::mir::visit::{Visitor, LvalueContext};
|
2016-06-02 17:05:17 +02:00
|
|
|
use syntax::codemap::Span;
|
|
|
|
use std::rc::Rc;
|
2016-06-01 17:05:20 +02:00
|
|
|
|
2016-06-23 00:02:47 -06:00
|
|
|
impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|
|
|
/// Returns true as long as there are more things to do.
|
|
|
|
pub fn step(&mut self) -> EvalResult<'tcx, bool> {
|
|
|
|
if self.stack.is_empty() {
|
2016-06-09 10:52:45 +02:00
|
|
|
return Ok(false);
|
2016-06-03 15:48:56 +02:00
|
|
|
}
|
|
|
|
|
2016-06-23 00:02:47 -06:00
|
|
|
let block = self.frame().block;
|
2016-09-06 16:04:51 +02:00
|
|
|
let stmt_id = self.frame().stmt;
|
2016-06-23 00:02:47 -06:00
|
|
|
let mir = self.mir();
|
2016-06-11 12:38:28 -06:00
|
|
|
let basic_block = &mir.basic_blocks()[block];
|
2016-06-01 17:05:20 +02:00
|
|
|
|
2016-09-06 16:04:51 +02:00
|
|
|
if let Some(ref stmt) = basic_block.statements.get(stmt_id) {
|
2016-07-05 10:47:10 +02:00
|
|
|
let mut new = Ok(0);
|
2016-06-03 15:48:56 +02:00
|
|
|
ConstantExtractor {
|
2016-06-11 12:38:28 -06:00
|
|
|
span: stmt.source_info.span,
|
2016-06-23 00:02:47 -06:00
|
|
|
substs: self.substs(),
|
|
|
|
def_id: self.frame().def_id,
|
|
|
|
ecx: self,
|
2016-06-08 11:11:33 +02:00
|
|
|
mir: &mir,
|
2016-07-05 10:47:10 +02:00
|
|
|
new_constants: &mut new,
|
2016-09-06 16:04:51 +02:00
|
|
|
}.visit_statement(block, stmt, mir::Location {
|
|
|
|
block: block,
|
|
|
|
statement_index: stmt_id,
|
|
|
|
});
|
2016-07-05 10:47:10 +02:00
|
|
|
if new? == 0 {
|
2016-06-09 10:52:45 +02:00
|
|
|
self.statement(stmt)?;
|
2016-06-03 15:48:56 +02:00
|
|
|
}
|
2016-06-28 15:06:44 +02:00
|
|
|
// if ConstantExtractor added new frames, we don't execute anything here
|
|
|
|
// but await the next call to step
|
2016-06-09 10:52:45 +02:00
|
|
|
return Ok(true);
|
2016-06-01 17:05:20 +02:00
|
|
|
}
|
|
|
|
|
2016-06-03 15:48:56 +02:00
|
|
|
let terminator = basic_block.terminator();
|
2016-07-05 10:47:10 +02:00
|
|
|
let mut new = Ok(0);
|
2016-06-03 15:48:56 +02:00
|
|
|
ConstantExtractor {
|
2016-06-11 12:38:28 -06:00
|
|
|
span: terminator.source_info.span,
|
2016-06-23 00:02:47 -06:00
|
|
|
substs: self.substs(),
|
|
|
|
def_id: self.frame().def_id,
|
|
|
|
ecx: self,
|
2016-06-08 11:11:33 +02:00
|
|
|
mir: &mir,
|
2016-07-05 10:47:10 +02:00
|
|
|
new_constants: &mut new,
|
2016-09-06 16:04:51 +02:00
|
|
|
}.visit_terminator(block, terminator, mir::Location {
|
|
|
|
block: block,
|
|
|
|
statement_index: stmt_id,
|
|
|
|
});
|
2016-07-05 10:47:10 +02:00
|
|
|
if new? == 0 {
|
2016-06-09 10:52:45 +02:00
|
|
|
self.terminator(terminator)?;
|
2016-06-03 15:48:56 +02:00
|
|
|
}
|
2016-06-28 15:06:44 +02:00
|
|
|
// if ConstantExtractor added new frames, we don't execute anything here
|
|
|
|
// but await the next call to step
|
2016-06-09 10:52:45 +02:00
|
|
|
Ok(true)
|
2016-06-01 17:05:20 +02:00
|
|
|
}
|
2016-06-23 00:02:47 -06:00
|
|
|
|
|
|
|
fn statement(&mut self, stmt: &mir::Statement<'tcx>) -> EvalResult<'tcx, ()> {
|
|
|
|
trace!("{:?}", stmt);
|
2016-08-27 01:44:46 -06:00
|
|
|
|
|
|
|
use rustc::mir::repr::StatementKind::*;
|
|
|
|
match stmt.kind {
|
|
|
|
Assign(ref lvalue, ref rvalue) => self.eval_assignment(lvalue, rvalue)?,
|
|
|
|
SetDiscriminant { .. } => unimplemented!(),
|
|
|
|
|
|
|
|
// Miri can safely ignore these. Only translation needs them.
|
|
|
|
StorageLive(_) | StorageDead(_) => {}
|
|
|
|
}
|
|
|
|
|
2016-06-23 00:02:47 -06:00
|
|
|
self.frame_mut().stmt += 1;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> EvalResult<'tcx, ()> {
|
|
|
|
// after a terminator we go to a new block
|
|
|
|
self.frame_mut().stmt = 0;
|
|
|
|
trace!("{:?}", terminator.kind);
|
|
|
|
self.eval_terminator(terminator)?;
|
|
|
|
if !self.stack.is_empty() {
|
|
|
|
trace!("// {:?}", self.frame().block);
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
2016-06-03 15:48:56 +02:00
|
|
|
}
|
2016-06-02 17:05:17 +02:00
|
|
|
|
2016-06-10 16:56:04 +02:00
|
|
|
// WARNING: make sure that any methods implemented on this type don't ever access ecx.stack
|
2016-06-09 17:24:42 +02:00
|
|
|
// 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> {
|
2016-06-03 15:48:56 +02:00
|
|
|
span: Span,
|
2016-06-10 16:56:04 +02:00
|
|
|
ecx: &'a mut EvalContext<'b, 'tcx>,
|
2016-06-08 11:11:33 +02:00
|
|
|
mir: &'a mir::Mir<'tcx>,
|
|
|
|
def_id: DefId,
|
|
|
|
substs: &'tcx subst::Substs<'tcx>,
|
2016-07-05 10:47:10 +02:00
|
|
|
new_constants: &'a mut EvalResult<'tcx, u64>,
|
2016-06-02 17:05:17 +02:00
|
|
|
}
|
|
|
|
|
2016-06-09 17:24:42 +02:00
|
|
|
impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> {
|
2016-06-09 11:27:12 +02:00
|
|
|
fn global_item(&mut self, def_id: DefId, substs: &'tcx subst::Substs<'tcx>, span: Span) {
|
2016-06-08 11:46:37 +02:00
|
|
|
let cid = ConstantId {
|
2016-06-03 15:48:56 +02:00
|
|
|
def_id: def_id,
|
|
|
|
substs: substs,
|
2016-06-08 12:35:15 +02:00
|
|
|
kind: ConstantKind::Global,
|
2016-06-03 15:48:56 +02:00
|
|
|
};
|
2016-06-10 16:56:04 +02:00
|
|
|
if self.ecx.statics.contains_key(&cid) {
|
2016-06-03 17:41:36 +02:00
|
|
|
return;
|
|
|
|
}
|
2016-06-10 16:56:04 +02:00
|
|
|
let mir = self.ecx.load_mir(def_id);
|
2016-07-05 10:47:10 +02:00
|
|
|
self.try(|this| {
|
|
|
|
let ptr = this.ecx.alloc_ret_ptr(mir.return_ty, substs)?;
|
|
|
|
this.ecx.statics.insert(cid.clone(), ptr);
|
|
|
|
this.ecx.push_stack_frame(def_id, span, mir, substs, Some(ptr))
|
|
|
|
});
|
|
|
|
}
|
|
|
|
fn try<F: FnOnce(&mut Self) -> EvalResult<'tcx, ()>>(&mut self, f: F) {
|
|
|
|
if let Ok(ref mut n) = *self.new_constants {
|
|
|
|
*n += 1;
|
|
|
|
} else {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if let Err(e) = f(self) {
|
|
|
|
*self.new_constants = Err(e);
|
|
|
|
}
|
2016-06-03 15:48:56 +02:00
|
|
|
}
|
2016-06-02 17:05:17 +02:00
|
|
|
}
|
|
|
|
|
2016-06-09 17:24:42 +02:00
|
|
|
impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> {
|
2016-09-06 16:04:51 +02:00
|
|
|
fn visit_constant(&mut self, constant: &mir::Constant<'tcx>, location: mir::Location) {
|
|
|
|
self.super_constant(constant, location);
|
2016-06-02 17:05:17 +02:00
|
|
|
match constant.literal {
|
|
|
|
// already computed by rustc
|
|
|
|
mir::Literal::Value { .. } => {}
|
2016-06-03 15:48:56 +02:00
|
|
|
mir::Literal::Item { def_id, substs } => {
|
2016-06-09 11:27:02 +02:00
|
|
|
if let ty::TyFnDef(..) = constant.ty.sty {
|
2016-06-09 10:45:06 +02:00
|
|
|
// No need to do anything here,
|
2016-06-09 11:16:09 +02:00
|
|
|
// because the type is the actual function, not the signature of the function.
|
|
|
|
// Thus we can simply create a zero sized allocation in `evaluate_operand`
|
2016-06-03 15:48:56 +02:00
|
|
|
} else {
|
2016-06-09 11:27:12 +02:00
|
|
|
self.global_item(def_id, substs, constant.span);
|
2016-06-03 15:48:56 +02:00
|
|
|
}
|
|
|
|
},
|
2016-06-02 17:05:17 +02:00
|
|
|
mir::Literal::Promoted { index } => {
|
2016-06-08 11:46:37 +02:00
|
|
|
let cid = ConstantId {
|
2016-06-08 11:11:33 +02:00
|
|
|
def_id: self.def_id,
|
|
|
|
substs: self.substs,
|
2016-06-08 11:46:37 +02:00
|
|
|
kind: ConstantKind::Promoted(index),
|
2016-06-03 17:41:36 +02:00
|
|
|
};
|
2016-06-10 16:56:04 +02:00
|
|
|
if self.ecx.statics.contains_key(&cid) {
|
2016-06-03 15:48:56 +02:00
|
|
|
return;
|
|
|
|
}
|
2016-06-08 11:11:33 +02:00
|
|
|
let mir = self.mir.promoted[index].clone();
|
2016-06-03 15:48:56 +02:00
|
|
|
let return_ty = mir.return_ty;
|
2016-07-05 10:47:10 +02:00
|
|
|
self.try(|this| {
|
|
|
|
let return_ptr = this.ecx.alloc_ret_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, Some(return_ptr))
|
|
|
|
});
|
2016-06-02 17:05:17 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-06 16:04:51 +02:00
|
|
|
fn visit_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>, context: LvalueContext, location: mir::Location) {
|
|
|
|
self.super_lvalue(lvalue, context, location);
|
2016-06-03 15:48:56 +02:00
|
|
|
if let mir::Lvalue::Static(def_id) = *lvalue {
|
2016-08-27 01:44:46 -06:00
|
|
|
let substs = subst::Substs::empty(self.ecx.tcx);
|
2016-06-03 15:48:56 +02:00
|
|
|
let span = self.span;
|
2016-06-09 11:27:12 +02:00
|
|
|
self.global_item(def_id, substs, span);
|
2016-06-02 17:05:17 +02:00
|
|
|
}
|
|
|
|
}
|
2016-06-01 17:05:20 +02:00
|
|
|
}
|