Skip to content

Commit 1186a7d

Browse files
authored
Merge pull request rust-lang#18 from oli-obk/hold_the_state
Hold the state
2 parents 66a812f + 05eaa52 commit 1186a7d

File tree

9 files changed

+565
-267
lines changed

9 files changed

+565
-267
lines changed

benches/miri_helper.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ extern crate test;
1010

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

3636
let bencher = self.0.clone();
3737

38+
control.after_analysis.stop = Compilation::Stop;
3839
control.after_analysis.callback = Box::new(move |state| {
3940
state.session.abort_if_errors();
4041
bencher.borrow_mut().iter(|| {

src/interpreter.rs renamed to src/interpreter/mod.rs

+325-264
Large diffs are not rendered by default.

src/interpreter/stepper.rs

+189
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
use super::{
2+
FnEvalContext,
3+
CachedMir,
4+
TerminatorTarget,
5+
ConstantId,
6+
GlobalEvalContext,
7+
ConstantKind,
8+
};
9+
use error::EvalResult;
10+
use rustc::mir::repr as mir;
11+
use rustc::ty::{subst, self};
12+
use rustc::hir::def_id::DefId;
13+
use rustc::mir::visit::{Visitor, LvalueContext};
14+
use syntax::codemap::Span;
15+
use std::rc::Rc;
16+
use memory::Pointer;
17+
18+
pub struct Stepper<'fncx, 'a: 'fncx, 'b: 'a + 'mir, 'mir: 'fncx, 'tcx: 'b>{
19+
fncx: &'fncx mut FnEvalContext<'a, 'b, 'mir, 'tcx>,
20+
21+
// a cache of the constants to be computed before the next statement/terminator
22+
// this is an optimization, so we don't have to allocate a new vector for every statement
23+
constants: Vec<(ConstantId<'tcx>, Span, Pointer, CachedMir<'mir, 'tcx>)>,
24+
}
25+
26+
impl<'fncx, 'a, 'b: 'a + 'mir, 'mir, 'tcx: 'b> Stepper<'fncx, 'a, 'b, 'mir, 'tcx> {
27+
pub(super) fn new(fncx: &'fncx mut FnEvalContext<'a, 'b, 'mir, 'tcx>) -> Self {
28+
Stepper {
29+
fncx: fncx,
30+
constants: Vec::new(),
31+
}
32+
}
33+
34+
fn statement(&mut self, stmt: &mir::Statement<'tcx>) -> EvalResult<()> {
35+
trace!("{:?}", stmt);
36+
let mir::StatementKind::Assign(ref lvalue, ref rvalue) = stmt.kind;
37+
let result = self.fncx.eval_assignment(lvalue, rvalue);
38+
self.fncx.maybe_report(result)?;
39+
self.fncx.frame_mut().stmt += 1;
40+
Ok(())
41+
}
42+
43+
fn terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> EvalResult<()> {
44+
// after a terminator we go to a new block
45+
self.fncx.frame_mut().stmt = 0;
46+
let term = {
47+
trace!("{:?}", terminator.kind);
48+
let result = self.fncx.eval_terminator(terminator);
49+
self.fncx.maybe_report(result)?
50+
};
51+
match term {
52+
TerminatorTarget::Return => {
53+
self.fncx.pop_stack_frame();
54+
},
55+
TerminatorTarget::Block |
56+
TerminatorTarget::Call => trace!("// {:?}", self.fncx.frame().next_block),
57+
}
58+
Ok(())
59+
}
60+
61+
// returns true as long as there are more things to do
62+
pub fn step(&mut self) -> EvalResult<bool> {
63+
if self.fncx.stack.is_empty() {
64+
return Ok(false);
65+
}
66+
67+
let block = self.fncx.frame().next_block;
68+
let stmt = self.fncx.frame().stmt;
69+
let mir = self.fncx.mir();
70+
let basic_block = mir.basic_block_data(block);
71+
72+
if let Some(ref stmt) = basic_block.statements.get(stmt) {
73+
assert!(self.constants.is_empty());
74+
ConstantExtractor {
75+
span: stmt.span,
76+
substs: self.fncx.substs(),
77+
def_id: self.fncx.frame().def_id,
78+
gecx: self.fncx.gecx,
79+
constants: &mut self.constants,
80+
mir: &mir,
81+
}.visit_statement(block, stmt);
82+
if self.constants.is_empty() {
83+
self.statement(stmt)?;
84+
} else {
85+
self.extract_constants()?;
86+
}
87+
return Ok(true);
88+
}
89+
90+
let terminator = basic_block.terminator();
91+
assert!(self.constants.is_empty());
92+
ConstantExtractor {
93+
span: terminator.span,
94+
substs: self.fncx.substs(),
95+
def_id: self.fncx.frame().def_id,
96+
gecx: self.fncx.gecx,
97+
constants: &mut self.constants,
98+
mir: &mir,
99+
}.visit_terminator(block, terminator);
100+
if self.constants.is_empty() {
101+
self.terminator(terminator)?;
102+
} else {
103+
self.extract_constants()?;
104+
}
105+
Ok(true)
106+
}
107+
108+
fn extract_constants(&mut self) -> EvalResult<()> {
109+
assert!(!self.constants.is_empty());
110+
for (cid, span, return_ptr, mir) in self.constants.drain(..) {
111+
trace!("queuing a constant");
112+
self.fncx.push_stack_frame(cid.def_id, span, mir, cid.substs, Some(return_ptr));
113+
}
114+
// self.step() can't be "done", so it can't return false
115+
assert!(self.step()?);
116+
Ok(())
117+
}
118+
}
119+
120+
struct ConstantExtractor<'a, 'b: 'mir, 'mir: 'a, 'tcx: 'b> {
121+
span: Span,
122+
// FIXME: directly push the new stackframes instead of doing this intermediate caching
123+
constants: &'a mut Vec<(ConstantId<'tcx>, Span, Pointer, CachedMir<'mir, 'tcx>)>,
124+
gecx: &'a mut GlobalEvalContext<'b, 'tcx>,
125+
mir: &'a mir::Mir<'tcx>,
126+
def_id: DefId,
127+
substs: &'tcx subst::Substs<'tcx>,
128+
}
129+
130+
impl<'a, 'b, 'mir, 'tcx> ConstantExtractor<'a, 'b, 'mir, 'tcx> {
131+
fn global_item(&mut self, def_id: DefId, substs: &'tcx subst::Substs<'tcx>, span: Span) {
132+
let cid = ConstantId {
133+
def_id: def_id,
134+
substs: substs,
135+
kind: ConstantKind::Global,
136+
};
137+
if self.gecx.statics.contains_key(&cid) {
138+
return;
139+
}
140+
let mir = self.gecx.load_mir(def_id);
141+
let ptr = self.gecx.alloc_ret_ptr(mir.return_ty, substs).expect("there's no such thing as an unreachable static");
142+
self.gecx.statics.insert(cid.clone(), ptr);
143+
self.constants.push((cid, span, ptr, mir));
144+
}
145+
}
146+
147+
impl<'a, 'b, 'mir, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'mir, 'tcx> {
148+
fn visit_constant(&mut self, constant: &mir::Constant<'tcx>) {
149+
self.super_constant(constant);
150+
match constant.literal {
151+
// already computed by rustc
152+
mir::Literal::Value { .. } => {}
153+
mir::Literal::Item { def_id, substs } => {
154+
if let ty::TyFnDef(..) = constant.ty.sty {
155+
// No need to do anything here, even if function pointers are implemented,
156+
// because the type is the actual function, not the signature of the function.
157+
// Thus we can simply create a zero sized allocation in `evaluate_operand`
158+
} else {
159+
self.global_item(def_id, substs, constant.span);
160+
}
161+
},
162+
mir::Literal::Promoted { index } => {
163+
let cid = ConstantId {
164+
def_id: self.def_id,
165+
substs: self.substs,
166+
kind: ConstantKind::Promoted(index),
167+
};
168+
if self.gecx.statics.contains_key(&cid) {
169+
return;
170+
}
171+
let mir = self.mir.promoted[index].clone();
172+
let return_ty = mir.return_ty;
173+
let return_ptr = self.gecx.alloc_ret_ptr(return_ty, cid.substs).expect("there's no such thing as an unreachable static");
174+
let mir = CachedMir::Owned(Rc::new(mir));
175+
self.gecx.statics.insert(cid.clone(), return_ptr);
176+
self.constants.push((cid, constant.span, return_ptr, mir));
177+
}
178+
}
179+
}
180+
181+
fn visit_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>, context: LvalueContext) {
182+
self.super_lvalue(lvalue, context);
183+
if let mir::Lvalue::Static(def_id) = *lvalue {
184+
let substs = self.gecx.tcx.mk_substs(subst::Substs::empty());
185+
let span = self.span;
186+
self.global_item(def_id, substs, span);
187+
}
188+
}
189+
}

src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
filling_drop,
77
question_mark,
88
rustc_private,
9+
pub_restricted,
910
)]
1011

1112
// From rustc.

src/memory.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ impl Memory {
8888
alloc.bytes.extend(iter::repeat(0).take(amount));
8989
alloc.undef_mask.grow(amount, false);
9090
} else if size > new_size {
91-
return Err(EvalError::Unimplemented(format!("unimplemented allocation relocation")));
91+
return Err(EvalError::Unimplemented(format!("unimplemented allocation relocation (from {} to {})", size, new_size)));
9292
// alloc.bytes.truncate(new_size);
9393
// alloc.undef_mask.len = new_size;
9494
// TODO: potentially remove relocations

tests/compile-fail/unimplemented.rs

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#![feature(custom_attribute)]
2+
#![allow(dead_code, unused_attributes)]
3+
4+
//error-pattern:unimplemented: mentions of function items
5+
6+
7+
#[miri_run]
8+
fn failed_assertions() {
9+
assert_eq!(5, 6);
10+
}
11+
12+
fn main() {}

tests/run-pass/bug.rs

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#![feature(custom_attribute)]
2+
#![allow(dead_code, unused_attributes)]
3+
4+
static mut X: usize = 5;
5+
6+
#[miri_run]
7+
fn static_mut() {
8+
unsafe {
9+
X = 6;
10+
assert_eq!(X, 6);
11+
}
12+
}
13+
14+
fn main() {}

tests/run-pass/calls.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#![feature(custom_attribute)]
1+
#![feature(custom_attribute, const_fn)]
22
#![allow(dead_code, unused_attributes)]
33

44
#[miri_run]
@@ -33,6 +33,15 @@ fn cross_crate_fn_call() -> i64 {
3333
if 1i32.is_positive() { 1 } else { 0 }
3434
}
3535

36+
const fn foo(i: i64) -> i64 { *&i + 1 }
37+
38+
#[miri_run]
39+
fn const_fn_call() -> i64 {
40+
let x = 5 + foo(5);
41+
assert_eq!(x, 11);
42+
x
43+
}
44+
3645
#[miri_run]
3746
fn main() {
3847
assert_eq!(call(), 2);

tests/run-pass/constants.rs

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#![feature(custom_attribute)]
2+
#![allow(dead_code, unused_attributes)]
3+
4+
const A: usize = *&5;
5+
6+
#[miri_run]
7+
fn foo() -> usize {
8+
A
9+
}
10+
11+
fn main() {}

0 commit comments

Comments
 (0)