2015-08-18 17:59:21 -04:00
|
|
|
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
|
|
|
|
// file at the top-level directory of this distribution and at
|
|
|
|
// http://rust-lang.org/COPYRIGHT.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
// except according to those terms.
|
|
|
|
|
|
|
|
//! An experimental pass that scources for `#[rustc_mir]` attributes,
|
|
|
|
//! builds the resulting MIR, and dumps it out into a file for inspection.
|
|
|
|
//!
|
|
|
|
//! The attribute formats that are currently accepted are:
|
|
|
|
//!
|
|
|
|
//! - `#[rustc_mir(graphviz="file.gv")]`
|
|
|
|
//! - `#[rustc_mir(pretty="file.mir")]`
|
|
|
|
|
|
|
|
extern crate syntax;
|
|
|
|
extern crate rustc;
|
|
|
|
extern crate rustc_front;
|
|
|
|
|
|
|
|
use build;
|
2015-12-18 19:29:03 -06:00
|
|
|
use graphviz;
|
2015-12-29 20:06:19 -06:00
|
|
|
use pretty;
|
2016-01-31 20:25:17 +02:00
|
|
|
use transform::{simplify_cfg, MirPass};
|
2015-12-22 16:35:02 -05:00
|
|
|
use rustc::dep_graph::DepNode;
|
2015-11-19 16:37:34 +01:00
|
|
|
use rustc::mir::repr::Mir;
|
2015-11-09 20:54:17 -05:00
|
|
|
use hair::cx::Cx;
|
2015-08-18 17:59:21 -04:00
|
|
|
use std::fs::File;
|
|
|
|
|
|
|
|
use self::rustc::middle::infer;
|
|
|
|
use self::rustc::middle::region::CodeExtentData;
|
|
|
|
use self::rustc::middle::ty::{self, Ty};
|
|
|
|
use self::rustc::util::common::ErrorReported;
|
2015-10-06 11:41:31 -04:00
|
|
|
use self::rustc::util::nodemap::NodeMap;
|
2015-08-18 17:59:21 -04:00
|
|
|
use self::rustc_front::hir;
|
2015-11-17 17:51:44 -05:00
|
|
|
use self::rustc_front::intravisit::{self, Visitor};
|
2015-08-18 17:59:21 -04:00
|
|
|
use self::syntax::ast;
|
2015-09-14 21:58:20 +12:00
|
|
|
use self::syntax::attr::AttrMetaMethods;
|
2015-08-18 17:59:21 -04:00
|
|
|
use self::syntax::codemap::Span;
|
|
|
|
|
2015-10-06 12:35:53 -04:00
|
|
|
pub type MirMap<'tcx> = NodeMap<Mir<'tcx>>;
|
|
|
|
|
2015-10-07 14:37:42 +02:00
|
|
|
pub fn build_mir_for_crate<'tcx>(tcx: &ty::ctxt<'tcx>) -> MirMap<'tcx> {
|
2015-10-06 11:41:31 -04:00
|
|
|
let mut map = NodeMap();
|
2015-10-06 12:35:53 -04:00
|
|
|
{
|
2015-10-07 14:37:42 +02:00
|
|
|
let mut dump = OuterDump {
|
|
|
|
tcx: tcx,
|
|
|
|
map: &mut map,
|
|
|
|
};
|
2015-12-22 16:35:02 -05:00
|
|
|
tcx.visit_all_items_in_krate(DepNode::MirMapConstruction, &mut dump);
|
2015-10-06 12:35:53 -04:00
|
|
|
}
|
|
|
|
map
|
2015-08-18 17:59:21 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// OuterDump -- walks a crate, looking for fn items and methods to build MIR from
|
|
|
|
|
2015-10-07 14:37:42 +02:00
|
|
|
struct OuterDump<'a, 'tcx: 'a> {
|
2015-08-18 17:59:21 -04:00
|
|
|
tcx: &'a ty::ctxt<'tcx>,
|
2015-10-06 12:35:53 -04:00
|
|
|
map: &'a mut MirMap<'tcx>,
|
2015-08-18 17:59:21 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, 'tcx> OuterDump<'a, 'tcx> {
|
2015-10-06 11:41:31 -04:00
|
|
|
fn visit_mir<OP>(&mut self, attributes: &'a [ast::Attribute], mut walk_op: OP)
|
2015-10-07 14:37:42 +02:00
|
|
|
where OP: for<'m> FnMut(&mut InnerDump<'a, 'm, 'tcx>)
|
2015-08-18 17:59:21 -04:00
|
|
|
{
|
2015-10-07 14:37:42 +02:00
|
|
|
let mut closure_dump = InnerDump {
|
|
|
|
tcx: self.tcx,
|
|
|
|
attr: None,
|
|
|
|
map: &mut *self.map,
|
|
|
|
};
|
2015-08-18 17:59:21 -04:00
|
|
|
for attr in attributes {
|
|
|
|
if attr.check_name("rustc_mir") {
|
2015-10-06 10:48:11 -04:00
|
|
|
closure_dump.attr = Some(attr);
|
2015-08-18 17:59:21 -04:00
|
|
|
}
|
|
|
|
}
|
2015-10-06 10:48:11 -04:00
|
|
|
walk_op(&mut closure_dump);
|
2015-08-18 17:59:21 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-11-17 17:51:44 -05:00
|
|
|
impl<'a, 'tcx> Visitor<'tcx> for OuterDump<'a, 'tcx> {
|
2015-08-18 17:59:21 -04:00
|
|
|
fn visit_item(&mut self, item: &'tcx hir::Item) {
|
2015-11-17 17:51:44 -05:00
|
|
|
self.visit_mir(&item.attrs, |c| intravisit::walk_item(c, item));
|
|
|
|
intravisit::walk_item(self, item);
|
2015-08-18 17:59:21 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem) {
|
|
|
|
match trait_item.node {
|
|
|
|
hir::MethodTraitItem(_, Some(_)) => {
|
2015-11-17 17:51:44 -05:00
|
|
|
self.visit_mir(&trait_item.attrs, |c| intravisit::walk_trait_item(c, trait_item));
|
2015-08-18 17:59:21 -04:00
|
|
|
}
|
2015-10-06 11:41:31 -04:00
|
|
|
hir::MethodTraitItem(_, None) |
|
|
|
|
hir::ConstTraitItem(..) |
|
2015-10-07 14:37:42 +02:00
|
|
|
hir::TypeTraitItem(..) => {}
|
2015-08-18 17:59:21 -04:00
|
|
|
}
|
2015-11-17 17:51:44 -05:00
|
|
|
intravisit::walk_trait_item(self, trait_item);
|
2015-08-18 17:59:21 -04:00
|
|
|
}
|
2015-10-06 11:41:31 -04:00
|
|
|
|
|
|
|
fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem) {
|
|
|
|
match impl_item.node {
|
2015-11-12 15:57:51 +01:00
|
|
|
hir::ImplItemKind::Method(..) => {
|
2015-11-17 17:51:44 -05:00
|
|
|
self.visit_mir(&impl_item.attrs, |c| intravisit::walk_impl_item(c, impl_item));
|
2015-10-06 11:41:31 -04:00
|
|
|
}
|
2015-11-12 15:57:51 +01:00
|
|
|
hir::ImplItemKind::Const(..) | hir::ImplItemKind::Type(..) => {}
|
2015-10-06 11:41:31 -04:00
|
|
|
}
|
2015-11-17 17:51:44 -05:00
|
|
|
intravisit::walk_impl_item(self, impl_item);
|
2015-10-06 11:41:31 -04:00
|
|
|
}
|
2015-08-18 17:59:21 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// InnerDump -- dumps MIR for a single fn and its contained closures
|
|
|
|
|
2015-10-07 14:37:42 +02:00
|
|
|
struct InnerDump<'a, 'm, 'tcx: 'a + 'm> {
|
2015-08-18 17:59:21 -04:00
|
|
|
tcx: &'a ty::ctxt<'tcx>,
|
2015-10-06 12:35:53 -04:00
|
|
|
map: &'m mut MirMap<'tcx>,
|
2015-09-14 21:58:20 +12:00
|
|
|
attr: Option<&'a ast::Attribute>,
|
2015-08-18 17:59:21 -04:00
|
|
|
}
|
|
|
|
|
2015-11-17 17:51:44 -05:00
|
|
|
impl<'a, 'm, 'tcx> Visitor<'tcx> for InnerDump<'a,'m,'tcx> {
|
2015-10-06 11:41:31 -04:00
|
|
|
fn visit_trait_item(&mut self, _: &'tcx hir::TraitItem) {
|
2015-11-17 17:51:44 -05:00
|
|
|
// ignore methods; the outer dump will call us for them independently
|
2015-10-06 11:41:31 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
fn visit_impl_item(&mut self, _: &'tcx hir::ImplItem) {
|
2015-11-17 17:51:44 -05:00
|
|
|
// ignore methods; the outer dump will call us for them independently
|
2015-10-06 11:41:31 -04:00
|
|
|
}
|
|
|
|
|
2015-08-18 17:59:21 -04:00
|
|
|
fn visit_fn(&mut self,
|
2015-11-17 17:51:44 -05:00
|
|
|
fk: intravisit::FnKind<'tcx>,
|
2015-08-18 17:59:21 -04:00
|
|
|
decl: &'tcx hir::FnDecl,
|
|
|
|
body: &'tcx hir::Block,
|
|
|
|
span: Span,
|
|
|
|
id: ast::NodeId) {
|
|
|
|
let (prefix, implicit_arg_tys) = match fk {
|
2015-11-17 17:51:44 -05:00
|
|
|
intravisit::FnKind::Closure =>
|
2015-08-18 17:59:21 -04:00
|
|
|
(format!("{}-", id), vec![closure_self_ty(&self.tcx, id, body.id)]),
|
|
|
|
_ =>
|
|
|
|
(format!(""), vec![]),
|
|
|
|
};
|
|
|
|
|
2015-10-07 14:37:42 +02:00
|
|
|
let param_env = ty::ParameterEnvironment::for_item(self.tcx, id);
|
2015-08-18 17:59:21 -04:00
|
|
|
|
2015-12-22 19:51:29 -05:00
|
|
|
let infcx = infer::new_infer_ctxt(self.tcx, &self.tcx.tables, Some(param_env));
|
2015-08-18 17:59:21 -04:00
|
|
|
|
|
|
|
match build_mir(Cx::new(&infcx), implicit_arg_tys, id, span, decl, body) {
|
2015-11-10 21:38:36 +01:00
|
|
|
Ok(mut mir) => {
|
|
|
|
simplify_cfg::SimplifyCfg::new().run_on_mir(&mut mir);
|
|
|
|
|
2015-10-07 14:37:42 +02:00
|
|
|
let meta_item_list = self.attr
|
|
|
|
.iter()
|
|
|
|
.flat_map(|a| a.meta_item_list())
|
|
|
|
.flat_map(|l| l.iter());
|
2015-08-18 17:59:21 -04:00
|
|
|
for item in meta_item_list {
|
2015-12-29 20:06:19 -06:00
|
|
|
if item.check_name("graphviz") || item.check_name("pretty") {
|
2015-08-18 17:59:21 -04:00
|
|
|
match item.value_str() {
|
|
|
|
Some(s) => {
|
2015-12-29 20:06:19 -06:00
|
|
|
let filename = format!("{}{}", prefix, s);
|
|
|
|
let result = File::create(&filename).and_then(|ref mut output| {
|
|
|
|
if item.check_name("graphviz") {
|
2015-12-18 19:29:03 -06:00
|
|
|
graphviz::write_mir_graphviz(&mir, output)
|
2015-12-29 20:06:19 -06:00
|
|
|
} else {
|
|
|
|
pretty::write_mir_pretty(&mir, output)
|
2015-08-18 17:59:21 -04:00
|
|
|
}
|
2015-12-29 20:06:19 -06:00
|
|
|
});
|
|
|
|
|
|
|
|
if let Err(e) = result {
|
|
|
|
self.tcx.sess.span_fatal(
|
|
|
|
item.span,
|
|
|
|
&format!("Error writing MIR {} results to `{}`: {}",
|
|
|
|
item.name(), filename, e));
|
2015-08-18 17:59:21 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
None => {
|
|
|
|
self.tcx.sess.span_err(
|
|
|
|
item.span,
|
2015-12-29 20:06:19 -06:00
|
|
|
&format!("{} attribute requires a path", item.name()));
|
2015-08-18 17:59:21 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-10-06 11:41:31 -04:00
|
|
|
|
|
|
|
let previous = self.map.insert(id, mir);
|
|
|
|
assert!(previous.is_none());
|
2015-08-18 17:59:21 -04:00
|
|
|
}
|
2015-10-07 14:37:42 +02:00
|
|
|
Err(ErrorReported) => {}
|
2015-08-18 17:59:21 -04:00
|
|
|
}
|
|
|
|
|
2015-11-17 17:51:44 -05:00
|
|
|
intravisit::walk_fn(self, fk, decl, body, span);
|
2015-08-18 17:59:21 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-21 17:14:25 -04:00
|
|
|
fn build_mir<'a,'tcx:'a>(cx: Cx<'a,'tcx>,
|
|
|
|
implicit_arg_tys: Vec<Ty<'tcx>>,
|
|
|
|
fn_id: ast::NodeId,
|
|
|
|
span: Span,
|
|
|
|
decl: &'tcx hir::FnDecl,
|
|
|
|
body: &'tcx hir::Block)
|
|
|
|
-> Result<Mir<'tcx>, ErrorReported> {
|
|
|
|
// fetch the fully liberated fn signature (that is, all bound
|
|
|
|
// types/lifetimes replaced)
|
|
|
|
let fn_sig = match cx.tcx().tables.borrow().liberated_fn_sigs.get(&fn_id) {
|
|
|
|
Some(f) => f.clone(),
|
|
|
|
None => {
|
|
|
|
cx.tcx().sess.span_bug(span,
|
|
|
|
&format!("no liberated fn sig for {:?}", fn_id));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let arguments =
|
|
|
|
decl.inputs
|
|
|
|
.iter()
|
|
|
|
.enumerate()
|
|
|
|
.map(|(index, arg)| {
|
2015-11-09 20:54:17 -05:00
|
|
|
(fn_sig.inputs[index], &*arg.pat)
|
2015-10-21 17:14:25 -04:00
|
|
|
})
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
let parameter_scope =
|
|
|
|
cx.tcx().region_maps.lookup_code_extent(
|
|
|
|
CodeExtentData::ParameterScope { fn_id: fn_id, body_id: body.id });
|
|
|
|
Ok(build::construct(cx,
|
|
|
|
span,
|
|
|
|
implicit_arg_tys,
|
|
|
|
arguments,
|
|
|
|
parameter_scope,
|
|
|
|
fn_sig.output,
|
|
|
|
body))
|
2015-08-18 17:59:21 -04:00
|
|
|
}
|
|
|
|
|
2015-10-07 14:37:42 +02:00
|
|
|
fn closure_self_ty<'a, 'tcx>(tcx: &ty::ctxt<'tcx>,
|
|
|
|
closure_expr_id: ast::NodeId,
|
|
|
|
body_id: ast::NodeId)
|
|
|
|
-> Ty<'tcx> {
|
2015-08-18 17:59:21 -04:00
|
|
|
let closure_ty = tcx.node_id_to_type(closure_expr_id);
|
|
|
|
|
|
|
|
// We're just hard-coding the idea that the signature will be
|
|
|
|
// &self or &mut self and hence will have a bound region with
|
|
|
|
// number 0, hokey.
|
2015-10-07 14:37:42 +02:00
|
|
|
let region = ty::Region::ReFree(ty::FreeRegion {
|
|
|
|
scope: tcx.region_maps.item_extent(body_id),
|
|
|
|
bound_region: ty::BoundRegion::BrAnon(0),
|
|
|
|
});
|
|
|
|
let region = tcx.mk_region(region);
|
2015-08-18 17:59:21 -04:00
|
|
|
|
2015-09-02 16:11:32 -04:00
|
|
|
match tcx.closure_kind(tcx.map.local_def_id(closure_expr_id)) {
|
2015-08-18 17:59:21 -04:00
|
|
|
ty::ClosureKind::FnClosureKind =>
|
|
|
|
tcx.mk_ref(region,
|
|
|
|
ty::TypeAndMut { ty: closure_ty,
|
|
|
|
mutbl: hir::MutImmutable }),
|
|
|
|
ty::ClosureKind::FnMutClosureKind =>
|
|
|
|
tcx.mk_ref(region,
|
|
|
|
ty::TypeAndMut { ty: closure_ty,
|
|
|
|
mutbl: hir::MutMutable }),
|
|
|
|
ty::ClosureKind::FnOnceClosureKind =>
|
|
|
|
closure_ty
|
|
|
|
}
|
|
|
|
}
|