expose a minimal API and use it in the binary

上级 3b804942
...@@ -7,11 +7,21 @@ ...@@ -7,11 +7,21 @@
extern crate rustc_driver; extern crate rustc_driver;
extern crate env_logger; extern crate env_logger;
extern crate log_settings; extern crate log_settings;
extern crate log; extern crate syntax;
#[macro_use] extern crate log;
use miri::interpreter; use miri::{
GlobalEvalContext,
CachedMir,
step,
EvalError,
Frame,
};
use rustc::session::Session; use rustc::session::Session;
use rustc_driver::{driver, CompilerCalls}; use rustc_driver::{driver, CompilerCalls};
use rustc::ty::{TyCtxt, subst};
use rustc::mir::mir_map::MirMap;
use rustc::hir::def_id::DefId;
struct MiriCompilerCalls; struct MiriCompilerCalls;
...@@ -25,13 +35,82 @@ fn build_controller( ...@@ -25,13 +35,82 @@ fn build_controller(
control.after_analysis.callback = Box::new(|state| { control.after_analysis.callback = Box::new(|state| {
state.session.abort_if_errors(); state.session.abort_if_errors();
interpreter::interpret_start_points(state.tcx.unwrap(), state.mir_map.unwrap()); interpret_start_points(state.tcx.unwrap(), state.mir_map.unwrap());
}); });
control control
} }
} }
fn interpret_start_points<'a, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
mir_map: &MirMap<'tcx>,
) {
let initial_indentation = ::log_settings::settings().indentation;
for (&id, mir) in &mir_map.map {
for attr in tcx.map.attrs(id) {
use syntax::attr::AttrMetaMethods;
if attr.check_name("miri_run") {
let item = tcx.map.expect_item(id);
::log_settings::settings().indentation = initial_indentation;
debug!("Interpreting: {}", item.name);
let mut gecx = GlobalEvalContext::new(tcx, mir_map);
let substs = tcx.mk_substs(subst::Substs::empty());
let return_ptr = gecx.alloc_ret_ptr(mir.return_ty, substs);
gecx.push_stack_frame(tcx.map.local_def_id(id), mir.span, CachedMir::Ref(mir), substs, return_ptr);
loop { match (step(&mut gecx), return_ptr) {
(Ok(true), _) => {},
(Ok(false), Some(ptr)) => if log_enabled!(::log::LogLevel::Debug) {
gecx.memory().dump(ptr.alloc_id);
break;
},
(Ok(false), None) => {
warn!("diverging function returned");
break;
},
// FIXME: diverging functions can end up here in some future miri
(Err(e), _) => {
report(tcx, &gecx, e);
break;
},
} }
}
}
}
}
fn report(tcx: TyCtxt, gecx: &GlobalEvalContext, e: EvalError) {
let frame = gecx.stack().last().expect("stackframe was empty");
let block = frame.mir.basic_block_data(frame.next_block);
let span = if frame.stmt < block.statements.len() {
block.statements[frame.stmt].span
} else {
block.terminator().span
};
let mut err = tcx.sess.struct_span_err(span, &e.to_string());
for &Frame { def_id, substs, span, .. } in gecx.stack().iter().rev() {
// FIXME(solson): Find a way to do this without this Display impl hack.
use rustc::util::ppaux;
use std::fmt;
struct Instance<'tcx>(DefId, &'tcx subst::Substs<'tcx>);
impl<'tcx> fmt::Display for Instance<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
ppaux::parameterized(f, self.1, self.0, ppaux::Ns::Value, &[],
|tcx| tcx.lookup_item_type(self.0).generics)
}
}
err.span_note(span, &format!("inside call to {}", Instance(def_id, substs)));
}
err.emit();
}
#[miri_run] #[miri_run]
fn main() { fn main() {
init_logger(); init_logger();
......
...@@ -24,6 +24,10 @@ ...@@ -24,6 +24,10 @@
mod stepper; mod stepper;
pub fn step<'fncx, 'a: 'fncx, 'tcx: 'a>(gecx: &'fncx mut GlobalEvalContext<'a, 'tcx>) -> EvalResult<bool> {
stepper::Stepper::new(gecx).step()
}
pub struct GlobalEvalContext<'a, 'tcx: 'a> { pub struct GlobalEvalContext<'a, 'tcx: 'a> {
/// The results of the type checker, from rustc. /// The results of the type checker, from rustc.
tcx: TyCtxt<'a, 'tcx, 'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>,
...@@ -45,38 +49,38 @@ pub struct GlobalEvalContext<'a, 'tcx: 'a> { ...@@ -45,38 +49,38 @@ pub struct GlobalEvalContext<'a, 'tcx: 'a> {
} }
/// A stack frame. /// A stack frame.
struct Frame<'a, 'tcx: 'a> { pub struct Frame<'a, 'tcx: 'a> {
/// The def_id of the current function /// The def_id of the current function
def_id: DefId, pub def_id: DefId,
/// The span of the call site /// The span of the call site
span: codemap::Span, pub span: codemap::Span,
/// type substitutions for the current function invocation /// type substitutions for the current function invocation
substs: &'tcx Substs<'tcx>, pub substs: &'tcx Substs<'tcx>,
/// The MIR for the function called on this frame. /// The MIR for the function called on this frame.
mir: CachedMir<'a, 'tcx>, pub mir: CachedMir<'a, 'tcx>,
/// The block that is currently executed (or will be executed after the above call stacks return) /// The block that is currently executed (or will be executed after the above call stacks return)
next_block: mir::BasicBlock, pub next_block: mir::BasicBlock,
/// A pointer for writing the return value of the current call if it's not a diverging call. /// A pointer for writing the return value of the current call if it's not a diverging call.
return_ptr: Option<Pointer>, pub return_ptr: Option<Pointer>,
/// The list of locals for the current function, stored in order as /// The list of locals for the current function, stored in order as
/// `[arguments..., variables..., temporaries...]`. The variables begin at `self.var_offset` /// `[arguments..., variables..., temporaries...]`. The variables begin at `self.var_offset`
/// and the temporaries at `self.temp_offset`. /// and the temporaries at `self.temp_offset`.
locals: Vec<Pointer>, pub locals: Vec<Pointer>,
/// The offset of the first variable in `self.locals`. /// The offset of the first variable in `self.locals`.
var_offset: usize, pub var_offset: usize,
/// The offset of the first temporary in `self.locals`. /// The offset of the first temporary in `self.locals`.
temp_offset: usize, pub temp_offset: usize,
/// The index of the currently evaluated statment /// The index of the currently evaluated statment
stmt: usize, pub stmt: usize,
} }
#[derive(Copy, Clone, Debug, Eq, PartialEq)] #[derive(Copy, Clone, Debug, Eq, PartialEq)]
...@@ -94,7 +98,7 @@ enum LvalueExtra { ...@@ -94,7 +98,7 @@ enum LvalueExtra {
} }
#[derive(Clone)] #[derive(Clone)]
enum CachedMir<'mir, 'tcx: 'mir> { pub enum CachedMir<'mir, 'tcx: 'mir> {
Ref(&'mir mir::Mir<'tcx>), Ref(&'mir mir::Mir<'tcx>),
Owned(Rc<mir::Mir<'tcx>>) Owned(Rc<mir::Mir<'tcx>>)
} }
...@@ -120,7 +124,7 @@ enum ConstantKind { ...@@ -120,7 +124,7 @@ enum ConstantKind {
} }
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 { pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir_map: &'a MirMap<'tcx>) -> Self {
GlobalEvalContext { GlobalEvalContext {
tcx: tcx, tcx: tcx,
mir_map: mir_map, mir_map: mir_map,
...@@ -135,7 +139,7 @@ fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir_map: &'a MirMap<'tcx>) -> Self { ...@@ -135,7 +139,7 @@ fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir_map: &'a MirMap<'tcx>) -> Self {
} }
} }
fn alloc_ret_ptr(&mut self, output_ty: ty::FnOutput<'tcx>, substs: &'tcx Substs<'tcx>) -> Option<Pointer> { pub fn alloc_ret_ptr(&mut self, output_ty: ty::FnOutput<'tcx>, substs: &'tcx Substs<'tcx>) -> Option<Pointer> {
match output_ty { match output_ty {
ty::FnConverging(ty) => { ty::FnConverging(ty) => {
let size = self.type_size(ty, substs); let size = self.type_size(ty, substs);
...@@ -145,6 +149,14 @@ fn alloc_ret_ptr(&mut self, output_ty: ty::FnOutput<'tcx>, substs: &'tcx Substs< ...@@ -145,6 +149,14 @@ fn alloc_ret_ptr(&mut self, output_ty: ty::FnOutput<'tcx>, substs: &'tcx Substs<
} }
} }
pub fn memory(&self) -> &Memory {
&self.memory
}
pub fn stack(&self) -> &[Frame] {
&self.stack
}
// TODO(solson): Try making const_to_primval instead. // TODO(solson): Try making const_to_primval instead.
fn const_to_ptr(&mut self, const_val: &const_val::ConstVal) -> EvalResult<Pointer> { fn const_to_ptr(&mut self, const_val: &const_val::ConstVal) -> EvalResult<Pointer> {
use rustc::middle::const_val::ConstVal::*; use rustc::middle::const_val::ConstVal::*;
...@@ -308,34 +320,7 @@ fn type_layout(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> &'tcx Layout ...@@ -308,34 +320,7 @@ fn type_layout(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> &'tcx Layout
}) })
} }
#[inline(never)] pub fn push_stack_frame(&mut self, def_id: DefId, span: codemap::Span, mir: CachedMir<'a, 'tcx>, substs: &'tcx Substs<'tcx>,
#[cold]
fn report(&self, e: EvalError) {
let stmt = self.frame().stmt;
let block = self.basic_block();
let span = if stmt < block.statements.len() {
block.statements[stmt].span
} else {
block.terminator().span
};
let mut err = self.tcx.sess.struct_span_err(span, &e.to_string());
for &Frame{ def_id, substs, span, .. } in self.stack.iter().rev() {
// FIXME(solson): Find a way to do this without this Display impl hack.
use rustc::util::ppaux;
use std::fmt;
struct Instance<'tcx>(DefId, &'tcx Substs<'tcx>);
impl<'tcx> fmt::Display for Instance<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
ppaux::parameterized(f, self.1, self.0, ppaux::Ns::Value, &[],
|tcx| tcx.lookup_item_type(self.0).generics)
}
}
err.span_note(span, &format!("inside call to {}", Instance(def_id, substs)));
}
err.emit();
}
fn push_stack_frame(&mut self, def_id: DefId, span: codemap::Span, mir: CachedMir<'a, 'tcx>, substs: &'tcx Substs<'tcx>,
return_ptr: Option<Pointer>) return_ptr: Option<Pointer>)
{ {
let arg_tys = mir.arg_decls.iter().map(|a| a.ty); let arg_tys = mir.arg_decls.iter().map(|a| a.ty);
...@@ -1387,48 +1372,6 @@ pub fn get_impl_method<'a, 'tcx>( ...@@ -1387,48 +1372,6 @@ pub fn get_impl_method<'a, 'tcx>(
} }
} }
pub fn interpret_start_points<'a, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
mir_map: &MirMap<'tcx>,
) {
let initial_indentation = ::log_settings::settings().indentation;
for (&id, mir) in &mir_map.map {
for attr in tcx.map.attrs(id) {
use syntax::attr::AttrMetaMethods;
if attr.check_name("miri_run") {
let item = tcx.map.expect_item(id);
::log_settings::settings().indentation = initial_indentation;
debug!("Interpreting: {}", item.name);
let mut gecx = GlobalEvalContext::new(tcx, mir_map);
let substs = tcx.mk_substs(subst::Substs::empty());
let return_ptr = gecx.alloc_ret_ptr(mir.return_ty, substs);
gecx.push_stack_frame(tcx.map.local_def_id(id), mir.span, CachedMir::Ref(mir), substs, return_ptr);
loop { match (stepper::step(&mut gecx), return_ptr) {
(Ok(true), _) => {},
(Ok(false), Some(ptr)) => if log_enabled!(::log::LogLevel::Debug) {
gecx.memory.dump(ptr.alloc_id);
break;
},
(Ok(false), None) => {
warn!("diverging function returned");
break;
},
// FIXME: diverging functions can end up here in some future miri
(Err(e), _) => {
gecx.report(e);
break;
},
} }
}
}
}
}
// TODO(solson): Upstream these methods into rustc::ty::layout. // TODO(solson): Upstream these methods into rustc::ty::layout.
trait IntegerExt { trait IntegerExt {
......
...@@ -12,16 +12,12 @@ ...@@ -12,16 +12,12 @@
use syntax::codemap::Span; use syntax::codemap::Span;
use std::rc::Rc; use std::rc::Rc;
struct Stepper<'fncx, 'a: 'fncx, 'tcx: 'a>{ pub(super) struct Stepper<'fncx, 'a: 'fncx, 'tcx: 'a>{
gecx: &'fncx mut GlobalEvalContext<'a, 'tcx>, gecx: &'fncx mut GlobalEvalContext<'a, 'tcx>,
} }
pub fn step<'fncx, 'a: 'fncx, 'tcx: 'a>(gecx: &'fncx mut GlobalEvalContext<'a, 'tcx>) -> EvalResult<bool> {
Stepper::new(gecx).step()
}
impl<'fncx, 'a, 'tcx> Stepper<'fncx, 'a, 'tcx> { impl<'fncx, 'a, 'tcx> Stepper<'fncx, 'a, 'tcx> {
fn new(gecx: &'fncx mut GlobalEvalContext<'a, 'tcx>) -> Self { pub(super) fn new(gecx: &'fncx mut GlobalEvalContext<'a, 'tcx>) -> Self {
Stepper { Stepper {
gecx: gecx, gecx: gecx,
} }
...@@ -47,7 +43,7 @@ fn terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> EvalResult<()> { ...@@ -47,7 +43,7 @@ fn terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> EvalResult<()> {
} }
// returns true as long as there are more things to do // returns true as long as there are more things to do
fn step(&mut self) -> EvalResult<bool> { pub(super) fn step(&mut self) -> EvalResult<bool> {
if self.gecx.stack.is_empty() { if self.gecx.stack.is_empty() {
return Ok(false); return Ok(false);
} }
......
...@@ -20,6 +20,20 @@ ...@@ -20,6 +20,20 @@
extern crate byteorder; extern crate byteorder;
mod error; mod error;
pub mod interpreter; mod interpreter;
mod memory; mod memory;
mod primval; mod primval;
pub use error::{
EvalError,
EvalResult,
};
pub use interpreter::{
GlobalEvalContext,
step,
Frame,
CachedMir,
};
pub use memory::Memory;
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册