also step through promoteds, constants and statics

This commit is contained in:
Oliver Schneider 2016-06-02 17:05:17 +02:00
parent 5211178377
commit 6ac64f19af
No known key found for this signature in database
GPG Key ID: 56D6EEA0FC67AC46
4 changed files with 163 additions and 39 deletions

View File

@ -20,6 +20,8 @@ use error::{EvalError, EvalResult};
use memory::{Memory, Pointer};
use primval::{self, PrimVal};
use std::collections::HashMap;
mod stepper;
struct GlobalEvalContext<'a, 'tcx: 'a> {
@ -45,6 +47,9 @@ struct GlobalEvalContext<'a, 'tcx: 'a> {
/// * Function DefIds and Substs to print proper substituted function names.
/// * Spans pointing to specific function calls in the source.
name_stack: Vec<(DefId, &'tcx Substs<'tcx>, codemap::Span)>,
/// Precomputed statics and constants
statics: DefIdMap<Pointer>,
}
struct FnEvalContext<'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> {
@ -88,6 +93,9 @@ struct Frame<'a, 'tcx: 'a> {
/// The offset of the first temporary in `self.locals`.
temp_offset: usize,
/// List of precomputed promoted constants
promoted: HashMap<usize, Pointer>,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
@ -122,6 +130,13 @@ enum TerminatorTarget {
Return,
}
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
enum ConstantId {
Promoted { index: usize },
Static { def_id: DefId },
}
impl<'a, 'tcx> GlobalEvalContext<'a, 'tcx> {
fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir_map: &'a MirMap<'tcx>) -> Self {
GlobalEvalContext {
@ -135,10 +150,11 @@ impl<'a, 'tcx> GlobalEvalContext<'a, 'tcx> {
.expect("Session::target::uint_type was usize")/8),
substs_stack: Vec::new(),
name_stack: Vec::new(),
statics: DefIdMap(),
}
}
fn call(&mut self, mir: &mir::Mir<'tcx>) -> EvalResult<Option<Pointer>> {
fn call(&mut self, mir: &mir::Mir<'tcx>, def_id: DefId) -> EvalResult<Option<Pointer>> {
let mut nested_fecx = FnEvalContext::new(self);
let return_ptr = match mir.return_ty {
@ -150,6 +166,7 @@ impl<'a, 'tcx> GlobalEvalContext<'a, 'tcx> {
};
let substs = nested_fecx.substs();
nested_fecx.name_stack.push((def_id, substs, mir.span));
nested_fecx.push_stack_frame(CachedMir::Ref(mir), substs, return_ptr);
nested_fecx.run()?;
Ok(return_ptr)
@ -193,9 +210,9 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> {
loop {
match stepper.step()? {
Assignment(statement) => trace!("{:?}", statement),
Terminator(terminator) => {
trace!("{:?}", terminator.kind);
Assignment => trace!("{:?}", stepper.stmt()),
Terminator => {
trace!("{:?}", stepper.term().kind);
continue 'outer;
},
Done => return Ok(()),
@ -230,6 +247,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> {
locals: locals,
var_offset: num_args,
temp_offset: num_args + num_vars,
promoted: HashMap::new(),
});
}
@ -983,13 +1001,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> {
match *literal {
Value { ref value } => Ok(self.const_to_ptr(value)?),
Item { .. } => Err(EvalError::Unimplemented(format!("literal items (e.g. mentions of function items) are unimplemented"))),
Promoted { index } => {
// TODO(solson): Mark constants and statics as read-only and cache their
// values.
let current_mir = self.mir();
let mir = &current_mir.promoted[index];
self.gecx.call(mir).map(Option::unwrap)
}
Promoted { index } => Ok(*self.frame().promoted.get(&index).expect("a promoted constant hasn't been precomputed")),
}
}
}
@ -1004,11 +1016,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> {
Var(i) => self.frame().locals[self.frame().var_offset + i as usize],
Temp(i) => self.frame().locals[self.frame().temp_offset + i as usize],
Static(def_id) => {
// TODO(solson): Mark constants and statics as read-only and cache their values.
let mir = self.load_mir(def_id);
self.gecx.call(&mir)?.unwrap()
}
Static(def_id) => *self.gecx.statics.get(&def_id).expect("static should have been cached"),
Projection(ref proj) => {
let base = self.eval_lvalue(&proj.base)?;
@ -1412,7 +1420,7 @@ pub fn interpret_start_points<'a, 'tcx>(
debug!("Interpreting: {}", item.name);
let mut gecx = GlobalEvalContext::new(tcx, mir_map);
match gecx.call(mir) {
match gecx.call(mir, tcx.map.local_def_id(id)) {
Ok(Some(return_ptr)) => if log_enabled!(::log::LogLevel::Debug) {
gecx.memory.dump(return_ptr.alloc_id);
},

View File

@ -2,49 +2,61 @@ use super::{
FnEvalContext,
CachedMir,
TerminatorTarget,
ConstantId,
};
use error::EvalResult;
use rustc::mir::repr as mir;
use rustc::ty::{self, subst};
use rustc::mir::visit::Visitor;
use syntax::codemap::Span;
use memory::Pointer;
use std::rc::Rc;
pub enum Event<'a, 'tcx: 'a> {
Assignment(&'a mir::Statement<'tcx>),
Terminator(&'a mir::Terminator<'tcx>),
pub enum Event {
Assignment,
Terminator,
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,
stmt: usize,
// a stack of statement positions
stmt: Vec<usize>,
mir: CachedMir<'mir, 'tcx>,
process: fn (&mut Stepper<'fncx, 'a, 'b, 'mir, 'tcx>) -> EvalResult<()>,
// a stack of constants
constants: Vec<Vec<(ConstantId, Span)>>,
}
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 {
Stepper {
let mut stepper = Stepper {
block: fncx.frame().next_block,
mir: fncx.mir(),
fncx: fncx,
stmt: 0,
stmt: vec![0],
process: Self::dummy,
}
constants: Vec::new(),
};
stepper.extract_constants();
stepper
}
fn dummy(&mut self) -> EvalResult<()> { Ok(()) }
fn statement(&mut self) -> EvalResult<()> {
let block_data = self.mir.basic_block_data(self.block);
let stmt = &block_data.statements[self.stmt];
let stmt = &block_data.statements[*self.stmt.last().unwrap()];
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)?;
self.stmt += 1;
*self.stmt.last_mut().unwrap() += 1;
Ok(())
}
fn terminator(&mut self) -> EvalResult<()> {
self.stmt = 0;
*self.stmt.last_mut().unwrap() = 0;
let term = {
let block_data = self.mir.basic_block_data(self.block);
let terminator = block_data.terminator();
@ -58,6 +70,9 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx
TerminatorTarget::Return => {
self.fncx.pop_stack_frame();
self.fncx.name_stack.pop();
self.stmt.pop();
assert!(self.constants.last().unwrap().is_empty());
self.constants.pop();
if !self.fncx.stack.is_empty() {
self.block = self.fncx.frame().next_block;
self.mir = self.fncx.mir();
@ -66,12 +81,24 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx
TerminatorTarget::Call => {
self.block = self.fncx.frame().next_block;
self.mir = self.fncx.mir();
self.stmt.push(0);
self.extract_constants();
},
}
Ok(())
}
pub fn step<'step>(&'step mut self) -> EvalResult<Event<'step, 'tcx>> {
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> {
(self.process)(self)?;
if self.fncx.stack.is_empty() {
@ -80,18 +107,97 @@ impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx
return Ok(Event::Done);
}
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
println!("{}, {}, {}", self.fncx.stack.len(), self.fncx.name_stack.len(), self.fncx.substs_stack.len());
let def_id = self.fncx.name_stack.last().unwrap().0;
self.fncx.name_stack.push((def_id, substs, span));
self.fncx.push_stack_frame(CachedMir::Owned(Rc::new(mir)), substs, Some(return_ptr));
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());
self.fncx.name_stack.push((def_id, substs, span));
self.fncx.push_stack_frame(mir, substs, Some(return_ptr));
self.stmt.push(0);
self.constants.push(Vec::new());
self.block = self.fncx.frame().next_block;
self.mir = self.fncx.mir();
},
None => {},
}
let basic_block = self.mir.basic_block_data(self.block);
if let Some(stmt) = basic_block.statements.get(self.stmt) {
if basic_block.statements.len() > *self.stmt.last().unwrap() {
self.process = Self::statement;
return Ok(Event::Assignment(&stmt));
return Ok(Event::Assignment);
}
self.process = Self::terminator;
Ok(Event::Terminator(basic_block.terminator()))
Ok(Event::Terminator)
}
/// returns the basic block index of the currently processed block
pub fn block(&self) -> mir::BasicBlock {
self.block
}
/// 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));
}
}
}

View File

@ -1,16 +1,12 @@
#![feature(custom_attribute)]
#![allow(dead_code, unused_attributes)]
//error-pattern:literal items (e.g. mentions of function items) are unimplemented
//error-pattern:static should have been cached
static mut X: usize = 5;
#[miri_run]
fn static_mut() {
unsafe {
X = 6;
assert_eq!(X, 6);
}
fn failed_assertions() {
assert_eq!(5, 6);
}
fn main() {}

14
tests/run-pass/bug.rs Normal file
View File

@ -0,0 +1,14 @@
#![feature(custom_attribute)]
#![allow(dead_code, unused_attributes)]
static mut X: usize = 5;
#[miri_run]
fn static_mut() {
unsafe {
X = 6;
assert_eq!(X, 6);
}
}
fn main() {}