rust/src/interpreter/step.rs

188 lines
6.6 KiB
Rust
Raw Normal View History

//! 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,
ConstantId,
EvalContext,
ConstantKind,
2016-06-01 17:05:20 +02:00
};
use error::EvalResult;
use rustc::mir::repr as mir;
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};
use syntax::codemap::Span;
use std::rc::Rc;
2016-06-01 17:05:20 +02: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() {
return Ok(false);
2016-06-03 15:48:56 +02:00
}
let block = self.frame().block;
let stmt_id = self.frame().stmt;
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
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,
substs: self.substs(),
def_id: self.frame().def_id,
ecx: self,
mir: &mir,
2016-07-05 10:47:10 +02:00
new_constants: &mut new,
}.visit_statement(block, stmt, mir::Location {
block: block,
statement_index: stmt_id,
});
2016-07-05 10:47:10 +02:00
if new? == 0 {
self.statement(stmt)?;
2016-06-03 15:48:56 +02:00
}
// if ConstantExtractor added new frames, we don't execute anything here
// but await the next call to step
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,
substs: self.substs(),
def_id: self.frame().def_id,
ecx: self,
mir: &mir,
2016-07-05 10:47:10 +02:00
new_constants: &mut new,
}.visit_terminator(block, terminator, mir::Location {
block: block,
statement_index: stmt_id,
});
2016-07-05 10:47:10 +02:00
if new? == 0 {
self.terminator(terminator)?;
2016-06-03 15:48:56 +02:00
}
// if ConstantExtractor added new frames, we don't execute anything here
// but await the next call to step
Ok(true)
2016-06-01 17:05:20 +02: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(_) => {}
}
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
}
// 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> {
2016-06-03 15:48:56 +02:00
span: Span,
ecx: &'a mut EvalContext<'b, 'tcx>,
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>,
}
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) {
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
};
if self.ecx.statics.contains_key(&cid) {
2016-06-03 17:41:36 +02:00
return;
}
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
}
}
impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> {
fn visit_constant(&mut self, constant: &mir::Constant<'tcx>, location: mir::Location) {
self.super_constant(constant, location);
match constant.literal {
// already computed by rustc
mir::Literal::Value { .. } => {}
2016-06-03 15:48:56 +02:00
mir::Literal::Item { def_id, substs } => {
if let ty::TyFnDef(..) = constant.ty.sty {
// No need to do anything here,
// 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
}
},
mir::Literal::Promoted { index } => {
let cid = ConstantId {
def_id: self.def_id,
substs: self.substs,
kind: ConstantKind::Promoted(index),
2016-06-03 17:41:36 +02:00
};
if self.ecx.statics.contains_key(&cid) {
2016-06-03 15:48:56 +02:00
return;
}
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))
});
}
}
}
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-01 17:05:20 +02:00
}