Skip to content

Commit

Permalink
Merge pull request rust-lang#18 from oli-obk/hold_the_state
Browse files Browse the repository at this point in the history
Hold the state
  • Loading branch information
solson authored Jun 10, 2016
2 parents 66a812f + 05eaa52 commit 1186a7d
Show file tree
Hide file tree
Showing 9 changed files with 565 additions and 267 deletions.
3 changes: 2 additions & 1 deletion benches/miri_helper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ extern crate test;

use self::miri::interpreter;
use self::rustc::session::Session;
use self::rustc_driver::{driver, CompilerCalls};
use self::rustc_driver::{driver, CompilerCalls, Compilation};
use std::cell::RefCell;
use std::rc::Rc;
use std::env::var;
Expand All @@ -35,6 +35,7 @@ impl<'a> CompilerCalls<'a> for MiriCompilerCalls<'a> {

let bencher = self.0.clone();

control.after_analysis.stop = Compilation::Stop;
control.after_analysis.callback = Box::new(move |state| {
state.session.abort_if_errors();
bencher.borrow_mut().iter(|| {
Expand Down
589 changes: 325 additions & 264 deletions src/interpreter.rs → src/interpreter/mod.rs

Large diffs are not rendered by default.

189 changes: 189 additions & 0 deletions src/interpreter/stepper.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
use super::{
FnEvalContext,
CachedMir,
TerminatorTarget,
ConstantId,
GlobalEvalContext,
ConstantKind,
};
use error::EvalResult;
use rustc::mir::repr as mir;
use rustc::ty::{subst, self};
use rustc::hir::def_id::DefId;
use rustc::mir::visit::{Visitor, LvalueContext};
use syntax::codemap::Span;
use std::rc::Rc;
use memory::Pointer;

pub struct Stepper<'fncx, 'a: 'fncx, 'b: 'a + 'mir, 'mir: 'fncx, 'tcx: 'b>{
fncx: &'fncx mut FnEvalContext<'a, 'b, 'mir, 'tcx>,

// 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>)>,
}

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 {
fncx: fncx,
constants: Vec::new(),
}
}

fn statement(&mut self, stmt: &mir::Statement<'tcx>) -> EvalResult<()> {
trace!("{:?}", stmt);
let mir::StatementKind::Assign(ref lvalue, ref rvalue) = stmt.kind;
let result = self.fncx.eval_assignment(lvalue, rvalue);
self.fncx.maybe_report(result)?;
self.fncx.frame_mut().stmt += 1;
Ok(())
}

fn terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> EvalResult<()> {
// after a terminator we go to a new block
self.fncx.frame_mut().stmt = 0;
let term = {
trace!("{:?}", terminator.kind);
let result = self.fncx.eval_terminator(terminator);
self.fncx.maybe_report(result)?
};
match term {
TerminatorTarget::Return => {
self.fncx.pop_stack_frame();
},
TerminatorTarget::Block |
TerminatorTarget::Call => trace!("// {:?}", self.fncx.frame().next_block),
}
Ok(())
}

// returns true as long as there are more things to do
pub fn step(&mut self) -> EvalResult<bool> {
if self.fncx.stack.is_empty() {
return Ok(false);
}

let block = self.fncx.frame().next_block;
let stmt = self.fncx.frame().stmt;
let mir = self.fncx.mir();
let basic_block = mir.basic_block_data(block);

if let Some(ref stmt) = basic_block.statements.get(stmt) {
assert!(self.constants.is_empty());
ConstantExtractor {
span: stmt.span,
substs: self.fncx.substs(),
def_id: self.fncx.frame().def_id,
gecx: self.fncx.gecx,
constants: &mut self.constants,
mir: &mir,
}.visit_statement(block, stmt);
if self.constants.is_empty() {
self.statement(stmt)?;
} else {
self.extract_constants()?;
}
return Ok(true);
}

let terminator = basic_block.terminator();
assert!(self.constants.is_empty());
ConstantExtractor {
span: terminator.span,
substs: self.fncx.substs(),
def_id: self.fncx.frame().def_id,
gecx: self.fncx.gecx,
constants: &mut self.constants,
mir: &mir,
}.visit_terminator(block, terminator);
if self.constants.is_empty() {
self.terminator(terminator)?;
} else {
self.extract_constants()?;
}
Ok(true)
}

fn extract_constants(&mut self) -> EvalResult<()> {
assert!(!self.constants.is_empty());
for (cid, span, return_ptr, mir) in self.constants.drain(..) {
trace!("queuing a constant");
self.fncx.push_stack_frame(cid.def_id, span, mir, cid.substs, Some(return_ptr));
}
// self.step() can't be "done", so it can't return false
assert!(self.step()?);
Ok(())
}
}

struct ConstantExtractor<'a, 'b: 'mir, 'mir: 'a, 'tcx: 'b> {
span: Span,
// FIXME: directly push the new stackframes instead of doing this intermediate caching
constants: &'a mut Vec<(ConstantId<'tcx>, Span, Pointer, CachedMir<'mir, 'tcx>)>,
gecx: &'a mut GlobalEvalContext<'b, 'tcx>,
mir: &'a mir::Mir<'tcx>,
def_id: DefId,
substs: &'tcx subst::Substs<'tcx>,
}

impl<'a, 'b, 'mir, 'tcx> ConstantExtractor<'a, 'b, 'mir, 'tcx> {
fn global_item(&mut self, def_id: DefId, substs: &'tcx subst::Substs<'tcx>, span: Span) {
let cid = ConstantId {
def_id: def_id,
substs: substs,
kind: ConstantKind::Global,
};
if self.gecx.statics.contains_key(&cid) {
return;
}
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);
self.constants.push((cid, span, ptr, mir));
}
}

impl<'a, 'b, 'mir, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'mir, 'tcx> {
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 { def_id, substs } => {
if let ty::TyFnDef(..) = constant.ty.sty {
// 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`
} else {
self.global_item(def_id, substs, constant.span);
}
},
mir::Literal::Promoted { index } => {
let cid = ConstantId {
def_id: self.def_id,
substs: self.substs,
kind: ConstantKind::Promoted(index),
};
if self.gecx.statics.contains_key(&cid) {
return;
}
let mir = self.mir.promoted[index].clone();
let return_ty = mir.return_ty;
let return_ptr = self.gecx.alloc_ret_ptr(return_ty, cid.substs).expect("there's no such thing as an unreachable static");
let mir = CachedMir::Owned(Rc::new(mir));
self.gecx.statics.insert(cid.clone(), return_ptr);
self.constants.push((cid, constant.span, return_ptr, mir));
}
}
}

fn visit_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>, context: LvalueContext) {
self.super_lvalue(lvalue, context);
if let mir::Lvalue::Static(def_id) = *lvalue {
let substs = self.gecx.tcx.mk_substs(subst::Substs::empty());
let span = self.span;
self.global_item(def_id, substs, span);
}
}
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
filling_drop,
question_mark,
rustc_private,
pub_restricted,
)]

// From rustc.
Expand Down
2 changes: 1 addition & 1 deletion src/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ impl Memory {
alloc.bytes.extend(iter::repeat(0).take(amount));
alloc.undef_mask.grow(amount, false);
} else if size > new_size {
return Err(EvalError::Unimplemented(format!("unimplemented allocation relocation")));
return Err(EvalError::Unimplemented(format!("unimplemented allocation relocation (from {} to {})", size, new_size)));
// alloc.bytes.truncate(new_size);
// alloc.undef_mask.len = new_size;
// TODO: potentially remove relocations
Expand Down
12 changes: 12 additions & 0 deletions tests/compile-fail/unimplemented.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#![feature(custom_attribute)]
#![allow(dead_code, unused_attributes)]

//error-pattern:unimplemented: mentions of function items


#[miri_run]
fn failed_assertions() {
assert_eq!(5, 6);
}

fn main() {}
14 changes: 14 additions & 0 deletions tests/run-pass/bug.rs
Original file line number Diff line number Diff line change
@@ -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() {}
11 changes: 10 additions & 1 deletion tests/run-pass/calls.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#![feature(custom_attribute)]
#![feature(custom_attribute, const_fn)]
#![allow(dead_code, unused_attributes)]

#[miri_run]
Expand Down Expand Up @@ -33,6 +33,15 @@ fn cross_crate_fn_call() -> i64 {
if 1i32.is_positive() { 1 } else { 0 }
}

const fn foo(i: i64) -> i64 { *&i + 1 }

#[miri_run]
fn const_fn_call() -> i64 {
let x = 5 + foo(5);
assert_eq!(x, 11);
x
}

#[miri_run]
fn main() {
assert_eq!(call(), 2);
Expand Down
11 changes: 11 additions & 0 deletions tests/run-pass/constants.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#![feature(custom_attribute)]
#![allow(dead_code, unused_attributes)]

const A: usize = *&5;

#[miri_run]
fn foo() -> usize {
A
}

fn main() {}

0 comments on commit 1186a7d

Please sign in to comment.