2016-06-01 17:05:20 +02:00
|
|
|
use super::{
|
|
|
|
FnEvalContext,
|
|
|
|
CachedMir,
|
|
|
|
TerminatorTarget,
|
2016-06-02 17:05:17 +02:00
|
|
|
ConstantId,
|
2016-06-01 17:05:20 +02:00
|
|
|
};
|
|
|
|
use error::EvalResult;
|
|
|
|
use rustc::mir::repr as mir;
|
2016-06-02 17:05:17 +02:00
|
|
|
use rustc::ty::{self, subst};
|
|
|
|
use rustc::mir::visit::Visitor;
|
|
|
|
use syntax::codemap::Span;
|
|
|
|
use memory::Pointer;
|
|
|
|
use std::rc::Rc;
|
2016-06-01 17:05:20 +02:00
|
|
|
|
2016-06-02 17:05:17 +02:00
|
|
|
pub enum Event {
|
|
|
|
Assignment,
|
|
|
|
Terminator,
|
2016-06-01 17:05:20 +02:00
|
|
|
Done,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct Stepper<'fncx, 'a: 'fncx, 'b: 'a + 'mir, 'mir: 'fncx, 'tcx: 'b>{
|
|
|
|
fncx: &'fncx mut FnEvalContext<'a, 'b, 'mir, 'tcx>,
|
|
|
|
block: mir::BasicBlock,
|
2016-06-02 17:05:17 +02:00
|
|
|
// a stack of statement positions
|
|
|
|
stmt: Vec<usize>,
|
2016-06-01 17:05:20 +02:00
|
|
|
mir: CachedMir<'mir, 'tcx>,
|
|
|
|
process: fn (&mut Stepper<'fncx, 'a, 'b, 'mir, 'tcx>) -> EvalResult<()>,
|
2016-06-02 17:05:17 +02:00
|
|
|
// a stack of constants
|
|
|
|
constants: Vec<Vec<(ConstantId, Span)>>,
|
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-02 17:05:17 +02:00
|
|
|
let mut stepper = Stepper {
|
2016-06-01 17:05:20 +02:00
|
|
|
block: fncx.frame().next_block,
|
|
|
|
mir: fncx.mir(),
|
|
|
|
fncx: fncx,
|
2016-06-02 17:05:17 +02:00
|
|
|
stmt: vec![0],
|
2016-06-01 17:05:20 +02:00
|
|
|
process: Self::dummy,
|
2016-06-02 17:05:17 +02:00
|
|
|
constants: Vec::new(),
|
|
|
|
};
|
|
|
|
stepper.extract_constants();
|
|
|
|
stepper
|
2016-06-01 17:05:20 +02:00
|
|
|
}
|
2016-06-01 19:01:40 +02:00
|
|
|
|
2016-06-01 17:05:20 +02:00
|
|
|
fn dummy(&mut self) -> EvalResult<()> { Ok(()) }
|
2016-06-01 19:01:40 +02:00
|
|
|
|
2016-06-01 17:05:20 +02:00
|
|
|
fn statement(&mut self) -> EvalResult<()> {
|
|
|
|
let block_data = self.mir.basic_block_data(self.block);
|
2016-06-02 17:05:17 +02:00
|
|
|
let stmt = &block_data.statements[*self.stmt.last().unwrap()];
|
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);
|
|
|
|
self.fncx.maybe_report(stmt.span, result)?;
|
2016-06-02 17:05:17 +02:00
|
|
|
*self.stmt.last_mut().unwrap() += 1;
|
2016-06-01 17:05:20 +02:00
|
|
|
Ok(())
|
|
|
|
}
|
2016-06-01 19:01:40 +02:00
|
|
|
|
2016-06-01 17:05:20 +02:00
|
|
|
fn terminator(&mut self) -> EvalResult<()> {
|
2016-06-02 17:05:17 +02:00
|
|
|
*self.stmt.last_mut().unwrap() = 0;
|
2016-06-01 17:05:20 +02:00
|
|
|
let term = {
|
|
|
|
let block_data = self.mir.basic_block_data(self.block);
|
|
|
|
let terminator = block_data.terminator();
|
|
|
|
let result = self.fncx.eval_terminator(terminator);
|
|
|
|
self.fncx.maybe_report(terminator.span, result)?
|
|
|
|
};
|
|
|
|
match term {
|
|
|
|
TerminatorTarget::Block(block) => {
|
|
|
|
self.block = block;
|
|
|
|
},
|
|
|
|
TerminatorTarget::Return => {
|
|
|
|
self.fncx.pop_stack_frame();
|
2016-06-02 17:05:17 +02:00
|
|
|
self.stmt.pop();
|
|
|
|
assert!(self.constants.last().unwrap().is_empty());
|
|
|
|
self.constants.pop();
|
2016-06-01 17:05:20 +02:00
|
|
|
if !self.fncx.stack.is_empty() {
|
|
|
|
self.block = self.fncx.frame().next_block;
|
|
|
|
self.mir = self.fncx.mir();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
TerminatorTarget::Call => {
|
|
|
|
self.block = self.fncx.frame().next_block;
|
|
|
|
self.mir = self.fncx.mir();
|
2016-06-02 17:05:17 +02:00
|
|
|
self.stmt.push(0);
|
|
|
|
self.extract_constants();
|
2016-06-01 17:05:20 +02:00
|
|
|
},
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
2016-06-01 19:01:40 +02:00
|
|
|
|
2016-06-02 17:05:17 +02:00
|
|
|
fn alloc(&mut self, ty: ty::FnOutput<'tcx>) -> Pointer {
|
|
|
|
match ty {
|
|
|
|
ty::FnConverging(ty) => {
|
|
|
|
let size = self.fncx.type_size(ty);
|
|
|
|
self.fncx.memory.allocate(size)
|
|
|
|
}
|
|
|
|
ty::FnDiverging => panic!("there's no such thing as an unreachable static"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn step(&mut self) -> EvalResult<Event> {
|
2016-06-01 17:05:20 +02:00
|
|
|
(self.process)(self)?;
|
|
|
|
|
|
|
|
if self.fncx.stack.is_empty() {
|
|
|
|
// fuse the iterator
|
|
|
|
self.process = Self::dummy;
|
|
|
|
return Ok(Event::Done);
|
|
|
|
}
|
|
|
|
|
2016-06-02 17:05:17 +02:00
|
|
|
match self.constants.last_mut().unwrap().pop() {
|
|
|
|
Some((ConstantId::Promoted { index }, span)) => {
|
|
|
|
trace!("adding promoted constant {}", index);
|
|
|
|
let mir = self.mir.promoted[index].clone();
|
|
|
|
let return_ptr = self.alloc(mir.return_ty);
|
|
|
|
self.fncx.frame_mut().promoted.insert(index, return_ptr);
|
|
|
|
let substs = self.fncx.substs();
|
|
|
|
// FIXME: somehow encode that this is a promoted constant's frame
|
2016-06-02 18:21:32 +02:00
|
|
|
let def_id = self.fncx.frame().def_id;
|
|
|
|
self.fncx.push_stack_frame(def_id, span, CachedMir::Owned(Rc::new(mir)), substs, Some(return_ptr));
|
2016-06-02 17:05:17 +02:00
|
|
|
self.stmt.push(0);
|
|
|
|
self.constants.push(Vec::new());
|
|
|
|
self.block = self.fncx.frame().next_block;
|
|
|
|
self.mir = self.fncx.mir();
|
|
|
|
},
|
|
|
|
Some((ConstantId::Static { def_id }, span)) => {
|
|
|
|
trace!("adding static {:?}", def_id);
|
|
|
|
let mir = self.fncx.load_mir(def_id);
|
|
|
|
let return_ptr = self.alloc(mir.return_ty);
|
|
|
|
self.fncx.gecx.statics.insert(def_id, return_ptr);
|
|
|
|
let substs = self.fncx.tcx.mk_substs(subst::Substs::empty());
|
2016-06-02 18:21:32 +02:00
|
|
|
self.fncx.push_stack_frame(def_id, span, mir, substs, Some(return_ptr));
|
2016-06-02 17:05:17 +02:00
|
|
|
self.stmt.push(0);
|
|
|
|
self.constants.push(Vec::new());
|
|
|
|
self.block = self.fncx.frame().next_block;
|
|
|
|
self.mir = self.fncx.mir();
|
|
|
|
},
|
|
|
|
None => {},
|
|
|
|
}
|
|
|
|
|
2016-06-01 17:05:20 +02:00
|
|
|
let basic_block = self.mir.basic_block_data(self.block);
|
|
|
|
|
2016-06-02 17:05:17 +02:00
|
|
|
if basic_block.statements.len() > *self.stmt.last().unwrap() {
|
2016-06-01 17:05:20 +02:00
|
|
|
self.process = Self::statement;
|
2016-06-02 17:05:17 +02:00
|
|
|
return Ok(Event::Assignment);
|
2016-06-01 17:05:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
self.process = Self::terminator;
|
2016-06-02 17:05:17 +02:00
|
|
|
Ok(Event::Terminator)
|
2016-06-01 17:05:20 +02:00
|
|
|
}
|
2016-06-02 17:05:17 +02:00
|
|
|
|
|
|
|
/// returns the basic block index of the currently processed block
|
2016-06-01 17:05:20 +02:00
|
|
|
pub fn block(&self) -> mir::BasicBlock {
|
|
|
|
self.block
|
|
|
|
}
|
2016-06-02 17:05:17 +02:00
|
|
|
|
|
|
|
/// returns the statement that will be processed next
|
|
|
|
pub fn stmt(&self) -> &mir::Statement {
|
|
|
|
let block_data = self.mir.basic_block_data(self.block);
|
|
|
|
&block_data.statements[*self.stmt.last().unwrap()]
|
|
|
|
}
|
|
|
|
|
|
|
|
/// returns the terminator of the current block
|
|
|
|
pub fn term(&self) -> &mir::Terminator {
|
|
|
|
let block_data = self.mir.basic_block_data(self.block);
|
|
|
|
block_data.terminator()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn extract_constants(&mut self) {
|
|
|
|
let mut extractor = ConstantExtractor {
|
|
|
|
constants: Vec::new(),
|
|
|
|
};
|
|
|
|
extractor.visit_mir(&self.mir);
|
|
|
|
self.constants.push(extractor.constants);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct ConstantExtractor {
|
|
|
|
constants: Vec<(ConstantId, Span)>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'tcx> Visitor<'tcx> for ConstantExtractor {
|
|
|
|
fn visit_constant(&mut self, constant: &mir::Constant<'tcx>) {
|
|
|
|
self.super_constant(constant);
|
|
|
|
match constant.literal {
|
|
|
|
// already computed by rustc
|
|
|
|
mir::Literal::Value { .. } => {}
|
|
|
|
mir::Literal::Item { .. } => {}, // FIXME: unimplemented
|
|
|
|
mir::Literal::Promoted { index } => {
|
|
|
|
self.constants.push((ConstantId::Promoted { index: index }, constant.span));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn visit_statement(&mut self, block: mir::BasicBlock, stmt: &mir::Statement<'tcx>) {
|
|
|
|
self.super_statement(block, stmt);
|
|
|
|
if let mir::StatementKind::Assign(mir::Lvalue::Static(def_id), _) = stmt.kind {
|
|
|
|
self.constants.push((ConstantId::Static { def_id: def_id }, stmt.span));
|
|
|
|
}
|
|
|
|
}
|
2016-06-01 17:05:20 +02:00
|
|
|
}
|