Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hold the state #18

Merged
merged 33 commits into from
Jun 10, 2016
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
e73fa77
can't evaluate failed assertions yet
oli-obk May 31, 2016
8398781
remove one layer of indirection when interpreting const/static/main f…
oli-obk Jun 1, 2016
5a8b0ab
don't clone the MIR Rc in every iteration
oli-obk Jun 1, 2016
2405c81
stepwise interpretation
oli-obk Jun 1, 2016
77e1ec2
style: spaces between functions
oli-obk Jun 1, 2016
0c269a5
rename `iterator` module to `stepper`
oli-obk Jun 1, 2016
5211178
note that not all literal items are function pointers
oli-obk Jun 1, 2016
6ac64f1
also step through promoteds, constants and statics
oli-obk Jun 2, 2016
97bc1d4
add a const fn test
oli-obk Jun 2, 2016
38ae352
remove a debug message that snuck into the commit
oli-obk Jun 2, 2016
05f829c
merge the three stacks in the interpreter
oli-obk Jun 2, 2016
cc1ca73
jit interpretation of constants
oli-obk Jun 3, 2016
f995db9
store the current block in the frame
oli-obk Jun 3, 2016
346560b
factor out the statement index into the stackframe
oli-obk Jun 3, 2016
02eed64
update documentation
oli-obk Jun 3, 2016
4743842
move constants stack to stackframe
oli-obk Jun 3, 2016
dc85b11
stop producing executable files in the root folder during benchmarks...
oli-obk Jun 3, 2016
4c833a5
globally cache statics and promoteds
oli-obk Jun 3, 2016
4d44a97
move some methods from FnEvalContext to GlobalEvalContext
oli-obk Jun 8, 2016
f42be6d
move load_mir to the global eval context
oli-obk Jun 6, 2016
c881cf1
clippy nits
oli-obk Jun 6, 2016
1f27d3f
don't cache the MIR in the Stepper
oli-obk Jun 8, 2016
6b939bb
rebase leftovers
oli-obk Jun 8, 2016
8b25bc8
directly push stackframes for constants when they are encountered
oli-obk Jun 8, 2016
3de30e3
no more function pointers
oli-obk Jun 8, 2016
3868a62
put `ConstantId`'s common fields into a struct
oli-obk Jun 8, 2016
240f0c0
improve fn argument naming
oli-obk Jun 8, 2016
2178961
improve the docs of ConstantId
oli-obk Jun 8, 2016
cbbf58b
the statement/terminator has already been computed, don't do it again
oli-obk Jun 8, 2016
59d858a
refactor away the closures and `Event` enum
oli-obk Jun 9, 2016
225a6a2
we already have the constant's type, no need to recompute from the de…
oli-obk Jun 9, 2016
040a501
make sure globals that yield function pointers aren't treated like fu…
oli-obk Jun 9, 2016
05eaa52
rename `static_item` to `global_item`
oli-obk Jun 9, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 34 additions & 49 deletions src/interpreter.rs → src/interpreter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ use error::{EvalError, EvalResult};
use memory::{Memory, Pointer};
use primval::{self, PrimVal};

mod stepper;

struct GlobalEvalContext<'a, 'tcx: 'a> {
/// The results of the type checker, from rustc.
tcx: TyCtxt<'a, 'tcx, 'tcx>,
Expand Down Expand Up @@ -135,6 +137,23 @@ impl<'a, 'tcx> GlobalEvalContext<'a, 'tcx> {
name_stack: Vec::new(),
}
}

fn call(&mut self, mir: &mir::Mir<'tcx>) -> EvalResult<Option<Pointer>> {
let mut nested_fecx = FnEvalContext::new(self);

let return_ptr = match mir.return_ty {
ty::FnConverging(ty) => {
let size = nested_fecx.type_size(ty);
Some(nested_fecx.memory.allocate(size))
}
ty::FnDiverging => None,
};

let substs = nested_fecx.substs();
nested_fecx.push_stack_frame(CachedMir::Ref(mir), substs, return_ptr);
nested_fecx.run()?;
Ok(return_ptr)
}
}

impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> {
Expand Down Expand Up @@ -167,55 +186,22 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> {
}

fn run(&mut self) -> EvalResult<()> {
'outer: while !self.stack.is_empty() {
let mut current_block = self.frame().next_block;
let mut stepper = stepper::Stepper::new(self);
'outer: loop {
use self::stepper::Event::*;
trace!("// {:?}", stepper.block());

loop {
trace!("// {:?}", current_block);
let current_mir = self.mir().clone(); // Cloning a reference.
let block_data = current_mir.basic_block_data(current_block);

for stmt in &block_data.statements {
trace!("{:?}", stmt);
let mir::StatementKind::Assign(ref lvalue, ref rvalue) = stmt.kind;
let result = self.eval_assignment(lvalue, rvalue);
self.maybe_report(stmt.span, result)?;
}

let terminator = block_data.terminator();
trace!("{:?}", terminator.kind);

let result = self.eval_terminator(terminator);
match self.maybe_report(terminator.span, result)? {
TerminatorTarget::Block(block) => current_block = block,
TerminatorTarget::Return => {
self.pop_stack_frame();
self.name_stack.pop();
match stepper.step()? {
Assignment(statement) => trace!("{:?}", statement),
Terminator(terminator) => {
trace!("{:?}", terminator.kind);
continue 'outer;
}
TerminatorTarget::Call => continue 'outer,
},
Done => return Ok(()),
}
}
}

Ok(())
}

fn call_nested(&mut self, mir: &mir::Mir<'tcx>) -> EvalResult<Option<Pointer>> {
let mut nested_fecx = FnEvalContext::new(self.gecx);

let return_ptr = match mir.return_ty {
ty::FnConverging(ty) => {
let size = nested_fecx.type_size(ty);
Some(nested_fecx.memory.allocate(size))
}
ty::FnDiverging => None,
};

let substs = nested_fecx.substs();
nested_fecx.push_stack_frame(CachedMir::Ref(mir), substs, return_ptr);
nested_fecx.run()?;
Ok(return_ptr)
}

fn push_stack_frame(&mut self, mir: CachedMir<'mir, 'tcx>, substs: &'tcx Substs<'tcx>,
Expand Down Expand Up @@ -996,13 +982,13 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> {
use rustc::mir::repr::Literal::*;
match *literal {
Value { ref value } => Ok(self.const_to_ptr(value)?),
Item { .. } => unimplemented!(),
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.call_nested(mir).map(Option::unwrap)
self.gecx.call(mir).map(Option::unwrap)
}
}
}
Expand All @@ -1021,7 +1007,7 @@ impl<'a, 'b, 'mir, 'tcx> FnEvalContext<'a, 'b, 'mir, 'tcx> {
Static(def_id) => {
// TODO(solson): Mark constants and statics as read-only and cache their values.
let mir = self.load_mir(def_id);
self.call_nested(&mir)?.unwrap()
self.gecx.call(&mir)?.unwrap()
}

Projection(ref proj) => {
Expand Down Expand Up @@ -1426,10 +1412,9 @@ pub fn interpret_start_points<'a, 'tcx>(
debug!("Interpreting: {}", item.name);

let mut gecx = GlobalEvalContext::new(tcx, mir_map);
let mut fecx = FnEvalContext::new(&mut gecx);
match fecx.call_nested(mir) {
match gecx.call(mir) {
Ok(Some(return_ptr)) => if log_enabled!(::log::LogLevel::Debug) {
fecx.memory.dump(return_ptr.alloc_id);
gecx.memory.dump(return_ptr.alloc_id);
},
Ok(None) => warn!("diverging function returned"),
Err(_e) => {
Expand Down
97 changes: 97 additions & 0 deletions src/interpreter/stepper.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
use super::{
FnEvalContext,
CachedMir,
TerminatorTarget,
};
use error::EvalResult;
use rustc::mir::repr as mir;

pub enum Event<'a, 'tcx: 'a> {
Assignment(&'a mir::Statement<'tcx>),
Terminator(&'a mir::Terminator<'tcx>),
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,
mir: CachedMir<'mir, 'tcx>,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we remove this field and just use fncx.mir() all the time?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

process: fn (&mut Stepper<'fncx, 'a, 'b, 'mir, 'tcx>) -> EvalResult<()>,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we replace the function pointers with an enum? Instead of calling (self.process)(self), match over the enum and call whatever method is needed.

I'm not sure why we'd use a function pointer in the first place.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure why we'd use a function pointer in the first place.

because ... reasons ... no idea.

I did a refactoring to get rid of the Constant state (I simply push all the constants' stackframes directly, instead of pushing one, evaluating it, pushing the next one, evaluating it, ...)

}

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 {
block: fncx.frame().next_block,
mir: fncx.mir(),
fncx: fncx,
stmt: 0,
process: Self::dummy,
}
}

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 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;
Ok(())
}

fn terminator(&mut self) -> EvalResult<()> {
self.stmt = 0;
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();
self.fncx.name_stack.pop();
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();
},
}
Ok(())
}

pub fn step<'step>(&'step mut self) -> EvalResult<Event<'step, 'tcx>> {
(self.process)(self)?;

if self.fncx.stack.is_empty() {
// fuse the iterator
self.process = Self::dummy;
return Ok(Event::Done);
}

let basic_block = self.mir.basic_block_data(self.block);

if let Some(stmt) = basic_block.statements.get(self.stmt) {
self.process = Self::statement;
return Ok(Event::Assignment(&stmt));
}

self.process = Self::terminator;
Ok(Event::Terminator(basic_block.terminator()))
}

pub fn block(&self) -> mir::BasicBlock {
self.block
}
}
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
16 changes: 16 additions & 0 deletions tests/compile-fail/unimplemented.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#![feature(custom_attribute)]
#![allow(dead_code, unused_attributes)]

//error-pattern:literal items (e.g. mentions of function items) are unimplemented

static mut X: usize = 5;

#[miri_run]
fn static_mut() {
unsafe {
X = 6;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So yeah, this might fail for a different reason in the future (mutation of a static), but I'm fine with having it here so we notice when that happens.

Although it would be allowed in versions of Miri not intended for rustc const eval.

assert_eq!(X, 6);
}
}

fn main() {}