293 lines
12 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-03 15:48:56 +02:00
use rustc::hir::def_id::DefId;
use rustc::hir;
2016-06-03 15:48:56 +02:00
use rustc::mir::visit::{Visitor, LvalueContext};
use rustc::mir;
2017-06-02 21:00:35 -04:00
use rustc::traits::Reveal;
2016-12-18 23:31:23 -08:00
use rustc::ty::layout::Layout;
use rustc::ty::{subst, self};
use error::{EvalResult, EvalError};
use eval_context::{EvalContext, StackPopCleanup};
use lvalue::{Global, GlobalId, Lvalue};
use value::{Value, PrimVal};
use syntax::codemap::Span;
2017-07-13 17:25:17 +02:00
use syntax::ast::Mutability;
2016-06-01 17:05:20 +02:00
impl<'a, 'tcx> EvalContext<'a, 'tcx> {
pub fn inc_step_counter_and_check_limit(&mut self, n: u64) -> EvalResult<'tcx> {
self.steps_remaining = self.steps_remaining.saturating_sub(n);
if self.steps_remaining > 0 {
Ok(())
} else {
Err(EvalError::ExecutionTimeLimitReached)
}
}
/// Returns true as long as there are more things to do.
pub fn step(&mut self) -> EvalResult<'tcx, bool> {
self.inc_step_counter_and_check_limit(1)?;
if self.stack.is_empty() {
return Ok(false);
2016-06-03 15:48:56 +02:00
}
let cur_frame = self.cur_frame();
self.memory.set_cur_frame(cur_frame);
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
2016-09-13 13:03:42 +02:00
if let Some(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,
2017-03-21 13:53:55 +01:00
instance: self.frame().instance,
ecx: self,
mir,
2016-07-05 10:47:10 +02:00
new_constants: &mut new,
2017-01-16 18:45:30 -08:00
}.visit_statement(block, stmt, mir::Location { block, statement_index: stmt_id });
2017-05-25 22:38:07 -07:00
// if ConstantExtractor added new frames, we don't execute anything here
// but await the next call to step
2016-07-05 10:47:10 +02:00
if new? == 0 {
self.statement(stmt)?;
2016-06-03 15:48:56 +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,
2017-03-21 13:53:55 +01:00
instance: self.frame().instance,
ecx: self,
mir,
2016-07-05 10:47:10 +02:00
new_constants: &mut new,
2017-01-16 18:45:30 -08:00
}.visit_terminator(block, terminator, mir::Location { block, statement_index: stmt_id });
2017-05-25 22:38:07 -07:00
// if ConstantExtractor added new frames, we don't execute anything here
// but await the next call to step
2016-07-05 10:47:10 +02:00
if new? == 0 {
self.terminator(terminator)?;
2016-06-03 15:48:56 +02:00
}
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::StatementKind::*;
2016-08-27 01:44:46 -06:00
match stmt.kind {
Assign(ref lvalue, ref rvalue) => self.eval_rvalue_into_lvalue(rvalue, lvalue)?,
2016-12-18 23:31:23 -08:00
SetDiscriminant { ref lvalue, variant_index } => {
let dest = self.eval_lvalue(lvalue)?;
let dest_ty = self.lvalue_ty(lvalue);
let dest_layout = self.type_layout(dest_ty)?;
match *dest_layout {
Layout::General { discr, .. } => {
2017-07-20 16:05:14 +02:00
let discr_size = discr.size().bytes();
let dest_ptr = self.force_allocation(dest)?.to_ptr()?;
self.memory.write_uint(dest_ptr, variant_index as u128, discr_size)?
2016-12-18 23:31:23 -08:00
}
Layout::RawNullablePointer { nndiscr, .. } => {
if variant_index as u64 != nndiscr {
2017-07-04 14:26:27 +02:00
self.write_null(dest, dest_ty)?;
2016-12-18 23:31:23 -08:00
}
}
2017-07-20 16:05:14 +02:00
Layout::StructWrappedNullablePointer { nndiscr, ref discrfield, .. } => {
if variant_index as u64 != nndiscr {
let (offset, ty) = self.nonnull_offset_and_ty(dest_ty, nndiscr, discrfield)?;
let nonnull = self.force_allocation(dest)?.to_ptr()?.offset(offset.bytes(), &self)?;
2017-07-20 16:05:14 +02:00
trace!("struct wrapped nullable pointer type: {}", ty);
// only the pointer part of a fat pointer is used for this space optimization
let discr_size = self.type_size(ty)?.expect("bad StructWrappedNullablePointer discrfield");
self.memory.write_uint(nonnull, 0, discr_size)?;
}
},
2016-12-18 23:31:23 -08:00
_ => bug!("SetDiscriminant on {} represented as {:#?}", dest_ty, dest_layout),
}
}
2016-08-27 01:44:46 -06:00
// Mark locals as dead or alive.
StorageLive(ref lvalue) | StorageDead(ref lvalue)=> {
let (frame, local) = match self.eval_lvalue(lvalue)? {
Lvalue::Local{ frame, local } if self.cur_frame() == frame => (frame, local),
2017-06-02 06:53:52 +02:00
_ => return Err(EvalError::Unimplemented("Storage annotations must refer to locals of the topmost stack frame.".to_owned())) // FIXME maybe this should get its own error type
};
let old_val = match stmt.kind {
StorageLive(_) => self.stack[frame].storage_live(local)?,
StorageDead(_) => self.stack[frame].storage_dead(local)?,
_ => bug!("We already checked that we are a storage stmt")
};
self.deallocate_local(old_val)?;
}
2016-09-21 23:16:31 -06:00
// Validity checks. Not yet implemented.
Validate(_, _) => {}
2017-06-20 09:52:58 +02:00
// Just a borrowck thing
2017-06-20 19:35:46 +09:00
EndRegion(..) => {}
2016-09-21 23:16:31 -06:00
// Defined to do nothing. These are added by optimization passes, to avoid changing the
// size of MIR constantly.
Nop => {}
2017-02-24 10:39:55 +01:00
InlineAsm { .. } => return Err(EvalError::InlineAsm),
2016-08-27 01:44:46 -06:00
}
self.frame_mut().stmt += 1;
Ok(())
}
fn terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> EvalResult<'tcx> {
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_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: &'tcx mir::Mir<'tcx>,
2017-03-21 13:53:55 +01:00
instance: ty::Instance<'tcx>,
2016-07-05 10:47:10 +02:00
new_constants: &'a mut EvalResult<'tcx, u64>,
}
impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> {
2017-02-10 03:24:05 -08:00
fn global_item(
&mut self,
def_id: DefId,
substs: &'tcx subst::Substs<'tcx>,
span: Span,
2017-07-13 17:25:17 +02:00
mutability: Mutability,
2017-02-10 03:24:05 -08:00
) {
2017-03-21 13:53:55 +01:00
let instance = self.ecx.resolve_associated_const(def_id, substs);
let cid = GlobalId { instance, promoted: None };
if self.ecx.globals.contains_key(&cid) {
2016-06-03 17:41:36 +02:00
return;
}
if self.ecx.tcx.has_attr(def_id, "linkage") {
trace!("Initializing an extern global with NULL");
2017-07-13 17:25:17 +02:00
self.ecx.globals.insert(cid, Global::initialized(self.ecx.tcx.type_of(def_id), Value::ByVal(PrimVal::Bytes(0)), mutability));
return;
}
2016-07-05 10:47:10 +02:00
self.try(|this| {
2017-03-21 13:53:55 +01:00
let mir = this.ecx.load_mir(instance.def)?;
this.ecx.globals.insert(cid, Global::uninitialized(mir.return_ty));
2017-07-13 17:25:17 +02:00
let internally_mutable = !mir.return_ty.is_freeze(
this.ecx.tcx,
2017-06-02 21:00:35 -04:00
ty::ParamEnv::empty(Reveal::All),
span);
2017-07-13 17:25:17 +02:00
let mutability = if mutability == Mutability::Mutable || internally_mutable {
Mutability::Mutable
} else {
Mutability::Immutable
};
let cleanup = StackPopCleanup::MarkStatic(mutability);
2017-02-07 07:02:45 -08:00
let name = ty::tls::with(|tcx| tcx.item_path_str(def_id));
trace!("pushing stack frame for global: {}", name);
2017-02-10 03:24:05 -08:00
this.ecx.push_stack_frame(
2017-03-21 13:53:55 +01:00
instance,
2017-02-10 03:24:05 -08:00
span,
mir,
2017-06-19 13:29:04 +02:00
Lvalue::Global(cid),
2017-02-10 03:24:05 -08:00
cleanup,
)
2016-07-05 10:47:10 +02:00
});
}
fn try<F: FnOnce(&mut Self) -> EvalResult<'tcx>>(&mut self, f: F) {
2016-07-05 10:47:10 +02:00
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 } => {
2017-07-13 17:25:17 +02:00
self.global_item(def_id, substs, constant.span, Mutability::Immutable);
2016-06-03 15:48:56 +02:00
},
mir::Literal::Promoted { index } => {
let cid = GlobalId {
2017-03-21 13:53:55 +01:00
instance: self.instance,
2016-10-21 11:54:38 +02:00
promoted: Some(index),
2016-06-03 17:41:36 +02:00
};
if self.ecx.globals.contains_key(&cid) {
2016-06-03 15:48:56 +02:00
return;
}
let mir = &self.mir.promoted[index];
2016-07-05 10:47:10 +02:00
self.try(|this| {
2017-03-21 13:53:55 +01:00
let ty = this.ecx.monomorphize(mir.return_ty, this.instance.substs);
this.ecx.globals.insert(cid, Global::uninitialized(ty));
2017-02-07 07:02:45 -08:00
trace!("pushing stack frame for {:?}", index);
2017-03-21 13:53:55 +01:00
this.ecx.push_stack_frame(this.instance,
constant.span,
mir,
2017-06-19 13:29:04 +02:00
Lvalue::Global(cid),
2017-07-13 17:25:17 +02:00
StackPopCleanup::MarkStatic(Mutability::Immutable),
)
2016-07-05 10:47:10 +02:00
});
}
}
}
2016-09-21 23:16:31 -06:00
fn visit_lvalue(
&mut self,
lvalue: &mir::Lvalue<'tcx>,
context: LvalueContext<'tcx>,
location: mir::Location
) {
self.super_lvalue(lvalue, context, location);
if let mir::Lvalue::Static(ref static_) = *lvalue {
let def_id = static_.def_id;
let substs = self.ecx.tcx.intern_substs(&[]);
2016-06-03 15:48:56 +02:00
let span = self.span;
2017-01-29 15:21:24 +08:00
if let Some(node_item) = self.ecx.tcx.hir.get_if_local(def_id) {
if let hir::map::Node::NodeItem(&hir::Item { ref node, .. }) = node_item {
if let hir::ItemStatic(_, m, _) = *node {
2017-07-13 17:25:17 +02:00
self.global_item(def_id, substs, span, if m == hir::MutMutable { Mutability::Mutable } else { Mutability::Immutable });
return;
} else {
bug!("static def id doesn't point to static");
}
} else {
bug!("static def id doesn't point to item");
}
} else {
let def = self.ecx.tcx.describe_def(def_id).expect("static not found");
if let hir::def::Def::Static(_, mutable) = def {
2017-07-13 17:25:17 +02:00
self.global_item(def_id, substs, span, if mutable { Mutability::Mutable } else { Mutability::Immutable });
} else {
bug!("static found but isn't a static: {:?}", def);
}
}
}
}
2016-06-01 17:05:20 +02:00
}