-
Notifications
You must be signed in to change notification settings - Fork 352
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
Hold the state #18
Changes from all 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 8398781
remove one layer of indirection when interpreting const/static/main f…
oli-obk 5a8b0ab
don't clone the MIR Rc in every iteration
oli-obk 2405c81
stepwise interpretation
oli-obk 77e1ec2
style: spaces between functions
oli-obk 0c269a5
rename `iterator` module to `stepper`
oli-obk 5211178
note that not all literal items are function pointers
oli-obk 6ac64f1
also step through promoteds, constants and statics
oli-obk 97bc1d4
add a const fn test
oli-obk 38ae352
remove a debug message that snuck into the commit
oli-obk 05f829c
merge the three stacks in the interpreter
oli-obk cc1ca73
jit interpretation of constants
oli-obk f995db9
store the current block in the frame
oli-obk 346560b
factor out the statement index into the stackframe
oli-obk 02eed64
update documentation
oli-obk 4743842
move constants stack to stackframe
oli-obk dc85b11
stop producing executable files in the root folder during benchmarks...
oli-obk 4c833a5
globally cache statics and promoteds
oli-obk 4d44a97
move some methods from FnEvalContext to GlobalEvalContext
oli-obk f42be6d
move load_mir to the global eval context
oli-obk c881cf1
clippy nits
oli-obk 1f27d3f
don't cache the MIR in the Stepper
oli-obk 6b939bb
rebase leftovers
oli-obk 8b25bc8
directly push stackframes for constants when they are encountered
oli-obk 3de30e3
no more function pointers
oli-obk 3868a62
put `ConstantId`'s common fields into a struct
oli-obk 240f0c0
improve fn argument naming
oli-obk 2178961
improve the docs of ConstantId
oli-obk cbbf58b
the statement/terminator has already been computed, don't do it again
oli-obk 59d858a
refactor away the closures and `Event` enum
oli-obk 225a6a2
we already have the constant's type, no need to recompute from the de…
oli-obk 040a501
make sure globals that yield function pointers aren't treated like fu…
oli-obk 05eaa52
rename `static_item` to `global_item`
oli-obk File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,6 +6,7 @@ | |
filling_drop, | ||
question_mark, | ||
rustc_private, | ||
pub_restricted, | ||
)] | ||
|
||
// From rustc. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() {} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is cloning an entire
Mir
structure... we should try to find a way around that.