1
Fork 0

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

View file

@ -2,49 +2,61 @@ use super::{
FnEvalContext, FnEvalContext,
CachedMir, CachedMir,
TerminatorTarget, TerminatorTarget,
ConstantId,
}; };
use error::EvalResult; use error::EvalResult;
use rustc::mir::repr as mir; 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> { pub enum Event {
Assignment(&'a mir::Statement<'tcx>), Assignment,
Terminator(&'a mir::Terminator<'tcx>), Terminator,
Done, Done,
} }
pub struct Stepper<'fncx, 'a: 'fncx, 'b: 'a + 'mir, 'mir: 'fncx, 'tcx: 'b>{ pub struct Stepper<'fncx, 'a: 'fncx, 'b: 'a + 'mir, 'mir: 'fncx, 'tcx: 'b>{
fncx: &'fncx mut FnEvalContext<'a, 'b, 'mir, 'tcx>, fncx: &'fncx mut FnEvalContext<'a, 'b, 'mir, 'tcx>,
block: mir::BasicBlock, block: mir::BasicBlock,
stmt: usize, // a stack of statement positions
stmt: Vec<usize>,
mir: CachedMir<'mir, 'tcx>, mir: CachedMir<'mir, 'tcx>,
process: fn (&mut Stepper<'fncx, 'a, 'b, 'mir, 'tcx>) -> EvalResult<()>, 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> { 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 { pub(super) fn new(fncx: &'fncx mut FnEvalContext<'a, 'b, 'mir, 'tcx>) -> Self {
Stepper { let mut stepper = Stepper {
block: fncx.frame().next_block, block: fncx.frame().next_block,
mir: fncx.mir(), mir: fncx.mir(),
fncx: fncx, fncx: fncx,
stmt: 0, stmt: vec![0],
process: Self::dummy, process: Self::dummy,
} constants: Vec::new(),
};
stepper.extract_constants();
stepper
} }
fn dummy(&mut self) -> EvalResult<()> { Ok(()) } fn dummy(&mut self) -> EvalResult<()> { Ok(()) }
fn statement(&mut self) -> EvalResult<()> { fn statement(&mut self) -> EvalResult<()> {
let block_data = self.mir.basic_block_data(self.block); 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 mir::StatementKind::Assign(ref lvalue, ref rvalue) = stmt.kind;
let result = self.fncx.eval_assignment(lvalue, rvalue); let result = self.fncx.eval_assignment(lvalue, rvalue);
self.fncx.maybe_report(stmt.span, result)?; self.fncx.maybe_report(stmt.span, result)?;
self.stmt += 1; *self.stmt.last_mut().unwrap() += 1;
Ok(()) Ok(())
} }
fn terminator(&mut self) -> EvalResult<()> { fn terminator(&mut self) -> EvalResult<()> {
self.stmt = 0; *self.stmt.last_mut().unwrap() = 0;
let term = { let term = {
let block_data = self.mir.basic_block_data(self.block); let block_data = self.mir.basic_block_data(self.block);
let terminator = block_data.terminator(); 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 => { TerminatorTarget::Return => {
self.fncx.pop_stack_frame(); self.fncx.pop_stack_frame();
self.fncx.name_stack.pop(); self.fncx.name_stack.pop();
self.stmt.pop();
assert!(self.constants.last().unwrap().is_empty());
self.constants.pop();
if !self.fncx.stack.is_empty() { if !self.fncx.stack.is_empty() {
self.block = self.fncx.frame().next_block; self.block = self.fncx.frame().next_block;
self.mir = self.fncx.mir(); 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 => { TerminatorTarget::Call => {
self.block = self.fncx.frame().next_block; self.block = self.fncx.frame().next_block;
self.mir = self.fncx.mir(); self.mir = self.fncx.mir();
self.stmt.push(0);
self.extract_constants();
}, },
} }
Ok(()) 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)?; (self.process)(self)?;
if self.fncx.stack.is_empty() { 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); 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); 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; self.process = Self::statement;
return Ok(Event::Assignment(&stmt)); return Ok(Event::Assignment);
} }
self.process = Self::terminator; 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 { pub fn block(&self) -> mir::BasicBlock {
self.block 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)] #![feature(custom_attribute)]
#![allow(dead_code, unused_attributes)] #![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] #[miri_run]
fn static_mut() { fn failed_assertions() {
unsafe { assert_eq!(5, 6);
X = 6;
assert_eq!(X, 6);
}
} }
fn main() {} 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() {}