2016-06-01 17:05:20 +02:00
|
|
|
use super::{
|
|
|
|
FnEvalContext,
|
|
|
|
CachedMir,
|
|
|
|
TerminatorTarget,
|
2016-06-02 17:05:17 +02:00
|
|
|
ConstantId,
|
2016-06-08 11:11:08 +02:00
|
|
|
GlobalEvalContext,
|
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:16:09 +02:00
|
|
|
use rustc::ty::subst;
|
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-08 11:11:33 +02:00
|
|
|
use memory::Pointer;
|
2016-06-01 17:05:20 +02:00
|
|
|
|
|
|
|
pub struct Stepper<'fncx, 'a: 'fncx, 'b: 'a + 'mir, 'mir: 'fncx, 'tcx: 'b>{
|
|
|
|
fncx: &'fncx mut FnEvalContext<'a, 'b, 'mir, 'tcx>,
|
2016-06-08 11:11:33 +02:00
|
|
|
|
|
|
|
// 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>)>,
|
2016-06-01 17:05:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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 {
|
2016-06-03 15:48:56 +02:00
|
|
|
Stepper {
|
2016-06-01 17:05:20 +02:00
|
|
|
fncx: fncx,
|
2016-06-08 11:11:33 +02:00
|
|
|
constants: Vec::new(),
|
2016-06-03 15:48:56 +02:00
|
|
|
}
|
2016-06-01 17:05:20 +02:00
|
|
|
}
|
2016-06-01 19:01:40 +02:00
|
|
|
|
2016-06-09 10:52:45 +02:00
|
|
|
fn statement(&mut self, stmt: &mir::Statement<'tcx>) -> EvalResult<()> {
|
|
|
|
trace!("{:?}", stmt);
|
2016-06-01 17:05:20 +02:00
|
|
|
let mir::StatementKind::Assign(ref lvalue, ref rvalue) = stmt.kind;
|
|
|
|
let result = self.fncx.eval_assignment(lvalue, rvalue);
|
2016-06-08 11:11:08 +02:00
|
|
|
self.fncx.maybe_report(result)?;
|
2016-06-03 16:57:47 +02:00
|
|
|
self.fncx.frame_mut().stmt += 1;
|
2016-06-01 17:05:20 +02:00
|
|
|
Ok(())
|
|
|
|
}
|
2016-06-01 19:01:40 +02:00
|
|
|
|
2016-06-09 10:52:45 +02:00
|
|
|
fn terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> EvalResult<()> {
|
2016-06-03 16:57:47 +02:00
|
|
|
// after a terminator we go to a new block
|
|
|
|
self.fncx.frame_mut().stmt = 0;
|
2016-06-01 17:05:20 +02:00
|
|
|
let term = {
|
2016-06-09 10:52:45 +02:00
|
|
|
trace!("{:?}", terminator.kind);
|
2016-06-01 17:05:20 +02:00
|
|
|
let result = self.fncx.eval_terminator(terminator);
|
2016-06-08 11:11:08 +02:00
|
|
|
self.fncx.maybe_report(result)?
|
2016-06-01 17:05:20 +02:00
|
|
|
};
|
|
|
|
match term {
|
|
|
|
TerminatorTarget::Return => {
|
|
|
|
self.fncx.pop_stack_frame();
|
|
|
|
},
|
2016-06-09 10:52:45 +02:00
|
|
|
TerminatorTarget::Block |
|
|
|
|
TerminatorTarget::Call => trace!("// {:?}", self.fncx.frame().next_block),
|
2016-06-01 17:05:20 +02:00
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
2016-06-01 19:01:40 +02:00
|
|
|
|
2016-06-08 11:34:56 +02:00
|
|
|
// returns true as long as there are more things to do
|
2016-06-09 10:52:45 +02:00
|
|
|
pub fn step(&mut self) -> EvalResult<bool> {
|
2016-06-03 15:48:56 +02:00
|
|
|
if self.fncx.stack.is_empty() {
|
2016-06-09 10:52:45 +02:00
|
|
|
return Ok(false);
|
2016-06-03 15:48:56 +02:00
|
|
|
}
|
|
|
|
|
2016-06-03 16:51:51 +02:00
|
|
|
let block = self.fncx.frame().next_block;
|
2016-06-03 16:57:47 +02:00
|
|
|
let stmt = self.fncx.frame().stmt;
|
2016-06-08 10:17:26 +02:00
|
|
|
let mir = self.fncx.mir();
|
|
|
|
let basic_block = mir.basic_block_data(block);
|
2016-06-01 17:05:20 +02:00
|
|
|
|
2016-06-03 16:57:47 +02:00
|
|
|
if let Some(ref stmt) = basic_block.statements.get(stmt) {
|
2016-06-08 11:11:33 +02:00
|
|
|
assert!(self.constants.is_empty());
|
2016-06-03 15:48:56 +02:00
|
|
|
ConstantExtractor {
|
|
|
|
span: stmt.span,
|
2016-06-08 11:11:33 +02:00
|
|
|
substs: self.fncx.substs(),
|
|
|
|
def_id: self.fncx.frame().def_id,
|
2016-06-06 15:22:33 +02:00
|
|
|
gecx: self.fncx.gecx,
|
2016-06-08 11:11:33 +02:00
|
|
|
constants: &mut self.constants,
|
|
|
|
mir: &mir,
|
2016-06-03 16:51:51 +02:00
|
|
|
}.visit_statement(block, stmt);
|
2016-06-08 11:11:33 +02:00
|
|
|
if self.constants.is_empty() {
|
2016-06-09 10:52:45 +02:00
|
|
|
self.statement(stmt)?;
|
2016-06-03 15:48:56 +02:00
|
|
|
} else {
|
2016-06-09 10:52:45 +02:00
|
|
|
self.extract_constants()?;
|
2016-06-03 15:48:56 +02:00
|
|
|
}
|
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-06-08 11:11:33 +02:00
|
|
|
assert!(self.constants.is_empty());
|
2016-06-03 15:48:56 +02:00
|
|
|
ConstantExtractor {
|
|
|
|
span: terminator.span,
|
2016-06-08 11:11:33 +02:00
|
|
|
substs: self.fncx.substs(),
|
|
|
|
def_id: self.fncx.frame().def_id,
|
2016-06-06 15:22:33 +02:00
|
|
|
gecx: self.fncx.gecx,
|
2016-06-08 11:11:33 +02:00
|
|
|
constants: &mut self.constants,
|
|
|
|
mir: &mir,
|
2016-06-03 16:51:51 +02:00
|
|
|
}.visit_terminator(block, terminator);
|
2016-06-08 11:11:33 +02:00
|
|
|
if self.constants.is_empty() {
|
2016-06-09 10:52:45 +02:00
|
|
|
self.terminator(terminator)?;
|
2016-06-03 15:48:56 +02:00
|
|
|
} else {
|
2016-06-09 10:52:45 +02:00
|
|
|
self.extract_constants()?;
|
2016-06-03 15:48:56 +02:00
|
|
|
}
|
2016-06-09 10:52:45 +02:00
|
|
|
Ok(true)
|
2016-06-01 17:05:20 +02:00
|
|
|
}
|
2016-06-02 17:05:17 +02:00
|
|
|
|
2016-06-09 10:52:45 +02:00
|
|
|
fn extract_constants(&mut self) -> EvalResult<()> {
|
2016-06-08 11:11:33 +02:00
|
|
|
assert!(!self.constants.is_empty());
|
|
|
|
for (cid, span, return_ptr, mir) in self.constants.drain(..) {
|
2016-06-09 10:52:45 +02:00
|
|
|
trace!("queuing a constant");
|
2016-06-08 11:46:37 +02:00
|
|
|
self.fncx.push_stack_frame(cid.def_id, span, mir, cid.substs, Some(return_ptr));
|
2016-06-08 11:11:33 +02:00
|
|
|
}
|
2016-06-09 10:52:45 +02:00
|
|
|
// self.step() can't be "done", so it can't return false
|
|
|
|
assert!(self.step()?);
|
|
|
|
Ok(())
|
2016-06-02 17:05:17 +02:00
|
|
|
}
|
2016-06-03 15:48:56 +02:00
|
|
|
}
|
2016-06-02 17:05:17 +02:00
|
|
|
|
2016-06-06 15:22:33 +02:00
|
|
|
struct ConstantExtractor<'a, 'b: 'mir, 'mir: 'a, 'tcx: 'b> {
|
2016-06-03 15:48:56 +02:00
|
|
|
span: Span,
|
2016-06-08 12:47:24 +02:00
|
|
|
// FIXME: directly push the new stackframes instead of doing this intermediate caching
|
2016-06-08 11:11:33 +02:00
|
|
|
constants: &'a mut Vec<(ConstantId<'tcx>, Span, Pointer, CachedMir<'mir, 'tcx>)>,
|
2016-06-06 15:22:33 +02:00
|
|
|
gecx: &'a mut GlobalEvalContext<'b, 'tcx>,
|
2016-06-08 11:11:33 +02:00
|
|
|
mir: &'a mir::Mir<'tcx>,
|
|
|
|
def_id: DefId,
|
|
|
|
substs: &'tcx subst::Substs<'tcx>,
|
2016-06-02 17:05:17 +02:00
|
|
|
}
|
|
|
|
|
2016-06-06 15:22:33 +02:00
|
|
|
impl<'a, 'b, 'mir, 'tcx> ConstantExtractor<'a, 'b, 'mir, 'tcx> {
|
2016-06-03 17:41:36 +02:00
|
|
|
fn static_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-06 15:22:33 +02:00
|
|
|
if self.gecx.statics.contains_key(&cid) {
|
2016-06-03 17:41:36 +02:00
|
|
|
return;
|
|
|
|
}
|
2016-06-06 15:22:33 +02:00
|
|
|
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);
|
2016-06-08 11:11:33 +02:00
|
|
|
self.constants.push((cid, span, ptr, mir));
|
2016-06-03 15:48:56 +02:00
|
|
|
}
|
2016-06-02 17:05:17 +02:00
|
|
|
}
|
|
|
|
|
2016-06-06 15:22:33 +02:00
|
|
|
impl<'a, 'b, 'mir, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'mir, 'tcx> {
|
2016-06-02 17:05:17 +02:00
|
|
|
fn visit_constant(&mut self, constant: &mir::Constant<'tcx>) {
|
|
|
|
self.super_constant(constant);
|
|
|
|
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:16:09 +02:00
|
|
|
if constant.ty.is_fn() {
|
|
|
|
// No need to do anything here, even if function pointers are implemented,
|
|
|
|
// 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-03 17:41:36 +02:00
|
|
|
self.static_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-06 15:22:33 +02:00
|
|
|
if self.gecx.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-06-08 11:46:37 +02:00
|
|
|
let return_ptr = self.gecx.alloc_ret_ptr(return_ty, cid.substs).expect("there's no such thing as an unreachable static");
|
2016-06-03 15:48:56 +02:00
|
|
|
let mir = CachedMir::Owned(Rc::new(mir));
|
2016-06-06 15:22:33 +02:00
|
|
|
self.gecx.statics.insert(cid.clone(), return_ptr);
|
2016-06-08 11:11:33 +02:00
|
|
|
self.constants.push((cid, constant.span, return_ptr, mir));
|
2016-06-02 17:05:17 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-03 15:48:56 +02:00
|
|
|
fn visit_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>, context: LvalueContext) {
|
|
|
|
self.super_lvalue(lvalue, context);
|
|
|
|
if let mir::Lvalue::Static(def_id) = *lvalue {
|
2016-06-06 15:22:33 +02:00
|
|
|
let substs = self.gecx.tcx.mk_substs(subst::Substs::empty());
|
2016-06-03 15:48:56 +02:00
|
|
|
let span = self.span;
|
2016-06-03 17:41:36 +02:00
|
|
|
self.static_item(def_id, substs, span);
|
2016-06-02 17:05:17 +02:00
|
|
|
}
|
|
|
|
}
|
2016-06-01 17:05:20 +02:00
|
|
|
}
|