From 811b874716210f0e4135cbaa489f8b2857bbb031 Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Fri, 26 Feb 2016 18:05:50 +0200 Subject: [PATCH 1/4] Add Pass manager for MIR --- src/librustc/dep_graph/mod.rs | 1 + src/librustc/mir/mir_map.rs | 22 ------ src/librustc/mir/repr.rs | 2 +- src/librustc/mir/transform.rs | 63 ++++++++++++++++- src/librustc/session/mod.rs | 6 +- src/librustc_driver/driver.rs | 28 +++++--- src/librustc_mir/mir_map.rs | 61 ++-------------- src/librustc_mir/transform/erase_regions.rs | 18 +++-- src/librustc_mir/transform/mod.rs | 2 +- src/librustc_mir/transform/no_landing_pads.rs | 13 ++-- ...r_dead_blocks.rs => remove_dead_blocks.rs} | 69 ++++++++++--------- src/librustc_mir/transform/simplify_cfg.rs | 56 +++------------ src/librustc_mir/transform/type_check.rs | 37 +++++----- src/librustc_plugin/registry.rs | 6 +- src/test/auxiliary/dummy_mir_pass.rs | 9 +-- 15 files changed, 179 insertions(+), 214 deletions(-) rename src/librustc_mir/transform/{clear_dead_blocks.rs => remove_dead_blocks.rs} (53%) diff --git a/src/librustc/dep_graph/mod.rs b/src/librustc/dep_graph/mod.rs index 2fad161652f..3a04b48bb83 100644 --- a/src/librustc/dep_graph/mod.rs +++ b/src/librustc/dep_graph/mod.rs @@ -70,6 +70,7 @@ pub enum DepNode { IntrinsicCheck(DefId), MatchCheck(DefId), MirMapConstruction(DefId), + MirPasses, BorrowCheck(DefId), RvalueCheck(DefId), Reachability, diff --git a/src/librustc/mir/mir_map.rs b/src/librustc/mir/mir_map.rs index 933621b765f..1a34699aff4 100644 --- a/src/librustc/mir/mir_map.rs +++ b/src/librustc/mir/mir_map.rs @@ -8,31 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use dep_graph::DepNode; use util::nodemap::NodeMap; use mir::repr::Mir; -use mir::transform::MirPass; -use middle::ty::{self, TyCtxt}; -use middle::infer; pub struct MirMap<'tcx> { pub map: NodeMap>, } - -impl<'tcx> MirMap<'tcx> { - pub fn run_passes(&mut self, passes: &mut [Box], tcx: &TyCtxt<'tcx>) { - if passes.is_empty() { return; } - - for (&id, mir) in &mut self.map { - let did = tcx.map.local_def_id(id); - let _task = tcx.dep_graph.in_task(DepNode::MirMapConstruction(did)); - - let param_env = ty::ParameterEnvironment::for_item(tcx, id); - let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, Some(param_env)); - - for pass in &mut *passes { - pass.run_on_mir(mir, &infcx) - } - } - } -} diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs index ce7b1ceb355..348b496112c 100644 --- a/src/librustc/mir/repr.rs +++ b/src/librustc/mir/repr.rs @@ -207,7 +207,7 @@ impl Debug for BasicBlock { } /////////////////////////////////////////////////////////////////////////// -// BasicBlock and Terminator +// BasicBlockData and Terminator #[derive(Clone, Debug, RustcEncodable, RustcDecodable)] pub struct BasicBlockData<'tcx> { diff --git a/src/librustc/mir/transform.rs b/src/librustc/mir/transform.rs index cc417f5a99e..5f09fe042a5 100644 --- a/src/librustc/mir/transform.rs +++ b/src/librustc/mir/transform.rs @@ -8,9 +8,66 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use mir::mir_map::MirMap; use mir::repr::Mir; -use middle::infer::InferCtxt; +use middle::ty::TyCtxt; -pub trait MirPass { - fn run_on_mir<'a, 'tcx>(&mut self, mir: &mut Mir<'tcx>, infcx: &InferCtxt<'a, 'tcx>); +/// Various information about pass. +pub trait Pass { + // fn name() for printouts of various sorts? + // fn should_run(Session) to check if pass should run? +} + +/// A pass which inspects the whole MirMap. +pub trait MirMapPass<'tcx>: Pass { + fn run_pass(&mut self, cx: &TyCtxt<'tcx>, map: &mut MirMap<'tcx>); +} + +pub trait MirPass<'tcx>: Pass { + fn run_pass(&mut self, cx: &TyCtxt<'tcx>, map: &mut Mir<'tcx>); +} + +impl<'tcx, T: MirPass<'tcx>> MirMapPass<'tcx> for T { + fn run_pass(&mut self, tcx: &TyCtxt<'tcx>, map: &mut MirMap<'tcx>) { + for (_, mir) in &mut map.map { + MirPass::run_pass(self, tcx, mir); + } + } +} + +/// A manager for MIR passes. +pub struct Passes { + passes: Vec MirMapPass<'tcx>>>, + plugin_passes: Vec MirMapPass<'tcx>>> +} + +impl Passes { + pub fn new() -> Passes { + let passes = Passes { + passes: Vec::new(), + plugin_passes: Vec::new() + }; + passes + } + + pub fn run_passes<'tcx>(&mut self, pcx: &TyCtxt<'tcx>, map: &mut MirMap<'tcx>) { + for pass in &mut self.plugin_passes { + pass.run_pass(pcx, map); + } + for pass in &mut self.passes { + pass.run_pass(pcx, map); + } + } + + /// Pushes a built-in pass. + pub fn push_pass(&mut self, pass: Box MirMapPass<'a>>) { + self.passes.push(pass); + } +} + +/// Copies the plugin passes. +impl ::std::iter::Extend MirMapPass<'a>>> for Passes { + fn extend MirMapPass<'a>>>>(&mut self, it: I) { + self.plugin_passes.extend(it); + } } diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index d2f8d3f09fd..b198eda1812 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -13,7 +13,7 @@ use middle::cstore::CrateStore; use middle::dependency_format; use session::search_paths::PathKind; use util::nodemap::{NodeMap, FnvHashMap}; -use mir::transform::MirPass; +use mir::transform as mir_pass; use syntax::ast::{NodeId, NodeIdAssigner, Name}; use syntax::codemap::{Span, MultiSpan}; @@ -60,7 +60,7 @@ pub struct Session { pub lint_store: RefCell, pub lints: RefCell>>, pub plugin_llvm_passes: RefCell>, - pub plugin_mir_passes: RefCell>>, + pub mir_passes: RefCell, pub plugin_attributes: RefCell>, pub crate_types: RefCell>, pub dependency_formats: RefCell, @@ -477,7 +477,7 @@ pub fn build_session_(sopts: config::Options, lint_store: RefCell::new(lint::LintStore::new()), lints: RefCell::new(NodeMap()), plugin_llvm_passes: RefCell::new(Vec::new()), - plugin_mir_passes: RefCell::new(Vec::new()), + mir_passes: RefCell::new(mir_pass::Passes::new()), plugin_attributes: RefCell::new(Vec::new()), crate_types: RefCell::new(Vec::new()), dependency_formats: RefCell::new(FnvHashMap()), diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 9c1be4c9f2f..d6bbb22c203 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use rustc::dep_graph::DepGraph; +use rustc::dep_graph::{DepGraph, DepNode}; use rustc::front; use rustc::front::map as hir_map; use rustc_mir as mir; @@ -561,7 +561,7 @@ pub fn phase_2_configure_and_expand(sess: &Session, } *sess.plugin_llvm_passes.borrow_mut() = llvm_passes; - *sess.plugin_mir_passes.borrow_mut() = mir_passes; + sess.mir_passes.borrow_mut().extend(mir_passes); *sess.plugin_attributes.borrow_mut() = attributes.clone(); })); @@ -861,9 +861,20 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, "MIR dump", || mir::mir_map::build_mir_for_crate(tcx)); - time(time_passes, - "MIR passes", - || mir_map.run_passes(&mut sess.plugin_mir_passes.borrow_mut(), tcx)); + time(time_passes, "MIR passes", || { + let _task = tcx.dep_graph.in_task(DepNode::MirPasses); + let mut passes = sess.mir_passes.borrow_mut(); + // Push all the built-in passes. + passes.push_pass(box mir::transform::remove_dead_blocks::RemoveDeadBlocks); + passes.push_pass(box mir::transform::type_check::TypeckMir); + passes.push_pass(box mir::transform::simplify_cfg::SimplifyCfg); + // Late passes + passes.push_pass(box mir::transform::no_landing_pads::NoLandingPads); + passes.push_pass(box mir::transform::remove_dead_blocks::RemoveDeadBlocks); + passes.push_pass(box mir::transform::erase_regions::EraseRegions); + // And run everything. + passes.run_passes(tcx, &mut mir_map); + }); time(time_passes, "borrow checking", @@ -912,9 +923,8 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, } /// Run the translation phase to LLVM, after which the AST and analysis can -/// be discarded. pub fn phase_4_translate_to_llvm<'tcx>(tcx: &TyCtxt<'tcx>, - mut mir_map: MirMap<'tcx>, + mir_map: MirMap<'tcx>, analysis: ty::CrateAnalysis) -> trans::CrateTranslation { let time_passes = tcx.sess.time_passes(); @@ -923,10 +933,6 @@ pub fn phase_4_translate_to_llvm<'tcx>(tcx: &TyCtxt<'tcx>, "resolving dependency formats", || dependency_format::calculate(&tcx.sess)); - time(time_passes, - "erasing regions from MIR", - || mir::transform::erase_regions::erase_regions(tcx, &mut mir_map)); - // Option dance to work around the lack of stack once closures. time(time_passes, "translation", diff --git a/src/librustc_mir/mir_map.rs b/src/librustc_mir/mir_map.rs index 96828628888..400035a15de 100644 --- a/src/librustc_mir/mir_map.rs +++ b/src/librustc_mir/mir_map.rs @@ -20,16 +20,10 @@ extern crate syntax; extern crate rustc_front; use build; -use graphviz; -use pretty; -use transform::{clear_dead_blocks, simplify_cfg, type_check}; -use transform::{no_landing_pads}; use rustc::dep_graph::DepNode; use rustc::mir::repr::Mir; use hair::cx::Cx; -use std::fs::File; -use rustc::mir::transform::MirPass; use rustc::mir::mir_map::MirMap; use rustc::middle::infer; use rustc::middle::region::CodeExtentData; @@ -136,61 +130,16 @@ impl<'a, 'm, 'tcx> Visitor<'tcx> for InnerDump<'a,'m,'tcx> { body: &'tcx hir::Block, span: Span, id: ast::NodeId) { - let (prefix, implicit_arg_tys) = match fk { - intravisit::FnKind::Closure => - (format!("{}-", id), vec![closure_self_ty(&self.tcx, id, body.id)]), - _ => - (format!(""), vec![]), + let implicit_arg_tys = if let intravisit::FnKind::Closure = fk { + vec![closure_self_ty(&self.tcx, id, body.id)] + } else { + vec![] }; let param_env = ty::ParameterEnvironment::for_item(self.tcx, id); - let infcx = infer::new_infer_ctxt(self.tcx, &self.tcx.tables, Some(param_env)); - match build_mir(Cx::new(&infcx), implicit_arg_tys, id, span, decl, body) { - Ok(mut mir) => { - clear_dead_blocks::ClearDeadBlocks::new().run_on_mir(&mut mir, &infcx); - type_check::TypeckMir::new().run_on_mir(&mut mir, &infcx); - no_landing_pads::NoLandingPads.run_on_mir(&mut mir, &infcx); - if self.tcx.sess.opts.mir_opt_level > 0 { - simplify_cfg::SimplifyCfg::new().run_on_mir(&mut mir, &infcx); - } - let meta_item_list = self.attr - .iter() - .flat_map(|a| a.meta_item_list()) - .flat_map(|l| l.iter()); - for item in meta_item_list { - if item.check_name("graphviz") || item.check_name("pretty") { - match item.value_str() { - Some(s) => { - let filename = format!("{}{}", prefix, s); - let result = File::create(&filename).and_then(|ref mut output| { - if item.check_name("graphviz") { - graphviz::write_mir_graphviz(&mir, output) - } else { - pretty::write_mir_pretty(&mir, output) - } - }); - - if let Err(e) = result { - self.tcx.sess.span_fatal( - item.span, - &format!("Error writing MIR {} results to `{}`: {}", - item.name(), filename, e)); - } - } - None => { - self.tcx.sess.span_err( - item.span, - &format!("{} attribute requires a path", item.name())); - } - } - } - } - - let previous = self.map.map.insert(id, mir); - assert!(previous.is_none()); - } + Ok(mir) => assert!(self.map.map.insert(id, mir).is_none()), Err(ErrorReported) => {} } diff --git a/src/librustc_mir/transform/erase_regions.rs b/src/librustc_mir/transform/erase_regions.rs index 53d88709add..2bab0168c52 100644 --- a/src/librustc_mir/transform/erase_regions.rs +++ b/src/librustc_mir/transform/erase_regions.rs @@ -15,13 +15,7 @@ use rustc::middle::ty::{self, TyCtxt}; use rustc::mir::repr::*; use rustc::mir::visit::MutVisitor; -use rustc::mir::mir_map::MirMap; - -pub fn erase_regions<'tcx>(tcx: &TyCtxt<'tcx>, mir_map: &mut MirMap<'tcx>) { - for (_, mir) in &mut mir_map.map { - EraseRegionsVisitor::new(tcx).visit_mir(mir); - } -} +use rustc::mir::transform::{MirPass, Pass}; struct EraseRegionsVisitor<'a, 'tcx: 'a> { tcx: &'a TyCtxt<'tcx>, @@ -123,3 +117,13 @@ impl<'a, 'tcx> MutVisitor<'tcx> for EraseRegionsVisitor<'a, 'tcx> { self.super_constant(constant); } } + +pub struct EraseRegions; + +impl Pass for EraseRegions {} + +impl<'tcx> MirPass<'tcx> for EraseRegions { + fn run_pass(&mut self, tcx: &TyCtxt<'tcx>, mir: &mut Mir<'tcx>) { + EraseRegionsVisitor::new(tcx).visit_mir(mir); + } +} diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index adca68114fd..57690caeccb 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -pub mod clear_dead_blocks; +pub mod remove_dead_blocks; pub mod simplify_cfg; pub mod erase_regions; pub mod no_landing_pads; diff --git a/src/librustc_mir/transform/no_landing_pads.rs b/src/librustc_mir/transform/no_landing_pads.rs index e2c93bd4e87..c08f45a8ec4 100644 --- a/src/librustc_mir/transform/no_landing_pads.rs +++ b/src/librustc_mir/transform/no_landing_pads.rs @@ -11,10 +11,10 @@ //! This pass removes the unwind branch of all the terminators when the no-landing-pads option is //! specified. -use rustc::middle::infer; +use rustc::middle::ty::TyCtxt; use rustc::mir::repr::*; use rustc::mir::visit::MutVisitor; -use rustc::mir::transform::MirPass; +use rustc::mir::transform::{Pass, MirPass}; pub struct NoLandingPads; @@ -40,11 +40,12 @@ impl<'tcx> MutVisitor<'tcx> for NoLandingPads { } } -impl MirPass for NoLandingPads { - fn run_on_mir<'a, 'tcx>(&mut self, mir: &mut Mir<'tcx>, - infcx: &infer::InferCtxt<'a, 'tcx>) { - if infcx.tcx.sess.no_landing_pads() { +impl<'tcx> MirPass<'tcx> for NoLandingPads { + fn run_pass(&mut self, tcx: &TyCtxt<'tcx>, mir: &mut Mir<'tcx>) { + if tcx.sess.no_landing_pads() { self.visit_mir(mir); } } } + +impl Pass for NoLandingPads {} diff --git a/src/librustc_mir/transform/clear_dead_blocks.rs b/src/librustc_mir/transform/remove_dead_blocks.rs similarity index 53% rename from src/librustc_mir/transform/clear_dead_blocks.rs rename to src/librustc_mir/transform/remove_dead_blocks.rs index b35d8c08f5d..308e257c20d 100644 --- a/src/librustc_mir/transform/clear_dead_blocks.rs +++ b/src/librustc_mir/transform/remove_dead_blocks.rs @@ -32,50 +32,55 @@ //! this pass just replaces the blocks with empty "return" blocks //! and does not renumber anything. -use rustc::middle::infer; +use rustc_data_structures::bitvec::BitVector; +use rustc::middle::ty::TyCtxt; use rustc::mir::repr::*; -use rustc::mir::transform::MirPass; +use rustc::mir::transform::{Pass, MirPass}; -pub struct ClearDeadBlocks; - -impl ClearDeadBlocks { - pub fn new() -> ClearDeadBlocks { - ClearDeadBlocks - } - - fn clear_dead_blocks(&self, mir: &mut Mir) { - let mut seen = vec![false; mir.basic_blocks.len()]; +pub struct RemoveDeadBlocks; +impl<'tcx> MirPass<'tcx> for RemoveDeadBlocks { + fn run_pass(&mut self, _: &TyCtxt<'tcx>, mir: &mut Mir<'tcx>) { + let mut seen = BitVector::new(mir.basic_blocks.len()); // These blocks are always required. - seen[START_BLOCK.index()] = true; - seen[END_BLOCK.index()] = true; + seen.insert(START_BLOCK.index()); + seen.insert(END_BLOCK.index()); - let mut worklist = vec![START_BLOCK]; + let mut worklist = Vec::with_capacity(4); + worklist.push(START_BLOCK); while let Some(bb) = worklist.pop() { for succ in mir.basic_block_data(bb).terminator().successors().iter() { - if !seen[succ.index()] { - seen[succ.index()] = true; + if seen.insert(succ.index()) { worklist.push(*succ); } } } + retain_basic_blocks(mir, &seen); + } +} - for (n, (block, seen)) in mir.basic_blocks.iter_mut().zip(seen).enumerate() { - if !seen { - info!("clearing block #{}: {:?}", n, block); - *block = BasicBlockData { - statements: vec![], - terminator: Some(Terminator::Return), - is_cleanup: false - }; - } +impl Pass for RemoveDeadBlocks {} + +/// Mass removal of basic blocks to keep the ID-remapping cheap. +fn retain_basic_blocks(mir: &mut Mir, keep: &BitVector) { + let num_blocks = mir.basic_blocks.len(); + + let mut replacements: Vec<_> = (0..num_blocks).map(BasicBlock::new).collect(); + let mut used_blocks = 0; + for alive_index in keep.iter() { + replacements[alive_index] = BasicBlock::new(used_blocks); + if alive_index != used_blocks { + // Swap the next alive block data with the current available slot. Since alive_index is + // non-decreasing this is a valid operation. + mir.basic_blocks.swap(alive_index, used_blocks); + } + used_blocks += 1; + } + mir.basic_blocks.truncate(used_blocks); + + for bb in mir.all_basic_blocks() { + for target in mir.basic_block_data_mut(bb).terminator_mut().successors_mut() { + *target = replacements[target.index()]; } } } - -impl MirPass for ClearDeadBlocks { - fn run_on_mir<'a, 'tcx>(&mut self, mir: &mut Mir<'tcx>, _: &infer::InferCtxt<'a, 'tcx>) - { - self.clear_dead_blocks(mir); - } -} diff --git a/src/librustc_mir/transform/simplify_cfg.rs b/src/librustc_mir/transform/simplify_cfg.rs index 785e6db57a5..85f36df7ced 100644 --- a/src/librustc_mir/transform/simplify_cfg.rs +++ b/src/librustc_mir/transform/simplify_cfg.rs @@ -8,11 +8,12 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use rustc_data_structures::bitvec::BitVector; use rustc::middle::const_eval::ConstVal; -use rustc::middle::infer; +use rustc::middle::ty::TyCtxt; use rustc::mir::repr::*; -use rustc::mir::transform::MirPass; +use rustc::mir::transform::{MirPass, Pass}; + +use super::remove_dead_blocks::RemoveDeadBlocks; pub struct SimplifyCfg; @@ -21,26 +22,7 @@ impl SimplifyCfg { SimplifyCfg } - fn remove_dead_blocks(&self, mir: &mut Mir) { - let mut seen = BitVector::new(mir.basic_blocks.len()); - // These blocks are always required. - seen.insert(START_BLOCK.index()); - seen.insert(END_BLOCK.index()); - - let mut worklist = Vec::with_capacity(4); - worklist.push(START_BLOCK); - while let Some(bb) = worklist.pop() { - for succ in mir.basic_block_data(bb).terminator().successors().iter() { - if seen.insert(succ.index()) { - worklist.push(*succ); - } - } - } - retain_basic_blocks(mir, &seen); - } - fn remove_goto_chains(&self, mir: &mut Mir) -> bool { - // Find the target at the end of the jump chain, return None if there is a loop fn final_target(mir: &Mir, mut target: BasicBlock) -> Option { // Keep track of already seen blocks to detect loops @@ -118,39 +100,17 @@ impl SimplifyCfg { } } -impl MirPass for SimplifyCfg { - fn run_on_mir<'a, 'tcx>(&mut self, mir: &mut Mir<'tcx>, _: &infer::InferCtxt<'a, 'tcx>) { +impl<'tcx> MirPass<'tcx> for SimplifyCfg { + fn run_pass(&mut self, tcx: &TyCtxt<'tcx>, mir: &mut Mir<'tcx>) { let mut changed = true; while changed { changed = self.simplify_branches(mir); changed |= self.remove_goto_chains(mir); - self.remove_dead_blocks(mir); + RemoveDeadBlocks.run_pass(tcx, mir); } // FIXME: Should probably be moved into some kind of pass manager mir.basic_blocks.shrink_to_fit(); } } -/// Mass removal of basic blocks to keep the ID-remapping cheap. -fn retain_basic_blocks(mir: &mut Mir, keep: &BitVector) { - let num_blocks = mir.basic_blocks.len(); - - let mut replacements: Vec<_> = (0..num_blocks).map(BasicBlock::new).collect(); - let mut used_blocks = 0; - for alive_index in keep.iter() { - replacements[alive_index] = BasicBlock::new(used_blocks); - if alive_index != used_blocks { - // Swap the next alive block data with the current available slot. Since alive_index is - // non-decreasing this is a valid operation. - mir.basic_blocks.swap(alive_index, used_blocks); - } - used_blocks += 1; - } - mir.basic_blocks.truncate(used_blocks); - - for bb in mir.all_basic_blocks() { - for target in mir.basic_block_data_mut(bb).terminator_mut().successors_mut() { - *target = replacements[target.index()]; - } - } -} +impl Pass for SimplifyCfg {} diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs index bf22c7b0b8b..21b3566418c 100644 --- a/src/librustc_mir/transform/type_check.rs +++ b/src/librustc_mir/transform/type_check.rs @@ -16,8 +16,9 @@ use rustc::middle::traits; use rustc::middle::ty::{self, Ty, TyCtxt}; use rustc::middle::ty::fold::TypeFoldable; use rustc::mir::repr::*; +use rustc::mir::mir_map::MirMap; use rustc::mir::tcx::LvalueTy; -use rustc::mir::transform::MirPass; +use rustc::mir::transform::{MirMapPass, Pass}; use rustc::mir::visit::{self, Visitor}; use syntax::codemap::{Span, DUMMY_SP}; @@ -572,27 +573,29 @@ impl TypeckMir { } } -impl MirPass for TypeckMir { - fn run_on_mir<'a, 'tcx>(&mut self, mir: &mut Mir<'tcx>, infcx: &InferCtxt<'a, 'tcx>) - { - if infcx.tcx.sess.err_count() > 0 { +impl<'tcx> MirMapPass<'tcx> for TypeckMir { + fn run_pass(&mut self, tcx: &TyCtxt<'tcx>, map: &mut MirMap<'tcx>) { + if tcx.sess.err_count() > 0 { // compiling a broken program can obviously result in a // broken MIR, so try not to report duplicate errors. return; } - - let mut checker = TypeChecker::new(infcx); - - { - let mut verifier = TypeVerifier::new(&mut checker, mir); - verifier.visit_mir(mir); - if verifier.errors_reported { - // don't do further checks to avoid ICEs - return; + for (&id, mir) in &mut map.map { + let param_env = ty::ParameterEnvironment::for_item(tcx, id); + let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, Some(param_env)); + let mut checker = TypeChecker::new(&infcx); + { + let mut verifier = TypeVerifier::new(&mut checker, mir); + verifier.visit_mir(mir); + if verifier.errors_reported { + // don't do further checks to avoid ICEs + continue; + } } + checker.typeck_mir(mir); + checker.verify_obligations(mir); } - - checker.typeck_mir(mir); - checker.verify_obligations(mir); } } + +impl Pass for TypeckMir {} diff --git a/src/librustc_plugin/registry.rs b/src/librustc_plugin/registry.rs index a51fd58db88..3cfd6a76dda 100644 --- a/src/librustc_plugin/registry.rs +++ b/src/librustc_plugin/registry.rs @@ -13,7 +13,7 @@ use rustc::lint::{EarlyLintPassObject, LateLintPassObject, LintId, Lint}; use rustc::session::Session; -use rustc::mir::transform::MirPass; +use rustc::mir::transform::MirMapPass; use syntax::ext::base::{SyntaxExtension, NamedSyntaxExtension, NormalTT}; use syntax::ext::base::{IdentTT, MultiModifier, MultiDecorator}; @@ -56,7 +56,7 @@ pub struct Registry<'a> { pub late_lint_passes: Vec, #[doc(hidden)] - pub mir_passes: Vec>, + pub mir_passes: Vec MirMapPass<'pcx>>>, #[doc(hidden)] pub lint_groups: HashMap<&'static str, Vec>, @@ -141,7 +141,7 @@ impl<'a> Registry<'a> { } /// Register a MIR pass - pub fn register_mir_pass(&mut self, pass: Box) { + pub fn register_mir_pass(&mut self, pass: Box MirMapPass<'pcx>>) { self.mir_passes.push(pass); } diff --git a/src/test/auxiliary/dummy_mir_pass.rs b/src/test/auxiliary/dummy_mir_pass.rs index 16ef965e0db..aec5165f6cf 100644 --- a/src/test/auxiliary/dummy_mir_pass.rs +++ b/src/test/auxiliary/dummy_mir_pass.rs @@ -18,17 +18,18 @@ extern crate rustc_front; extern crate rustc_plugin; extern crate syntax; -use rustc::mir::transform::MirPass; +use rustc::mir::transform::{self, MirPass}; use rustc::mir::repr::{Mir, Literal}; use rustc::mir::visit::MutVisitor; -use rustc::middle::infer::InferCtxt; +use rustc::middle::ty; use rustc::middle::const_eval::ConstVal; use rustc_plugin::Registry; struct Pass; -impl MirPass for Pass { - fn run_on_mir<'a, 'tcx>(&mut self, mir: &mut Mir<'tcx>, _: &InferCtxt<'a, 'tcx>) { +impl transform::Pass for Pass {} +impl<'tcx> MirPass<'tcx> for Pass { + fn run_pass(&mut self, _: &ty::ctxt<'tcx>, mir: &mut Mir<'tcx>) { Visitor.visit_mir(mir) } } From 27d91d73f9cc535921a2ff214f09578df7855ffa Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Fri, 4 Mar 2016 14:52:22 +0200 Subject: [PATCH 2/4] Address comments --- src/librustc/dep_graph/mod.rs | 3 ++- src/librustc/mir/transform.rs | 4 +++- src/librustc_driver/driver.rs | 3 +-- src/librustc_mir/transform/type_check.rs | 2 ++ 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/librustc/dep_graph/mod.rs b/src/librustc/dep_graph/mod.rs index 3a04b48bb83..b4167285636 100644 --- a/src/librustc/dep_graph/mod.rs +++ b/src/librustc/dep_graph/mod.rs @@ -10,6 +10,7 @@ use self::thread::{DepGraphThreadData, DepMessage}; use middle::def_id::DefId; +use syntax::ast::NodeId; use middle::ty::TyCtxt; use rustc_front::hir; use rustc_front::intravisit::Visitor; @@ -70,7 +71,7 @@ pub enum DepNode { IntrinsicCheck(DefId), MatchCheck(DefId), MirMapConstruction(DefId), - MirPasses, + MirTypeck(NodeId), BorrowCheck(DefId), RvalueCheck(DefId), Reachability, diff --git a/src/librustc/mir/transform.rs b/src/librustc/mir/transform.rs index 5f09fe042a5..d15ef724d59 100644 --- a/src/librustc/mir/transform.rs +++ b/src/librustc/mir/transform.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use dep_graph::DepNode; use mir::mir_map::MirMap; use mir::repr::Mir; use middle::ty::TyCtxt; @@ -23,8 +24,9 @@ pub trait MirMapPass<'tcx>: Pass { fn run_pass(&mut self, cx: &TyCtxt<'tcx>, map: &mut MirMap<'tcx>); } +/// A pass which inspects Mir of functions in isolation. pub trait MirPass<'tcx>: Pass { - fn run_pass(&mut self, cx: &TyCtxt<'tcx>, map: &mut Mir<'tcx>); + fn run_pass(&mut self, cx: &TyCtxt<'tcx>, mir: &mut Mir<'tcx>); } impl<'tcx, T: MirPass<'tcx>> MirMapPass<'tcx> for T { diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index d6bbb22c203..ff2f079f345 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use rustc::dep_graph::{DepGraph, DepNode}; +use rustc::dep_graph::DepGraph; use rustc::front; use rustc::front::map as hir_map; use rustc_mir as mir; @@ -862,7 +862,6 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, || mir::mir_map::build_mir_for_crate(tcx)); time(time_passes, "MIR passes", || { - let _task = tcx.dep_graph.in_task(DepNode::MirPasses); let mut passes = sess.mir_passes.borrow_mut(); // Push all the built-in passes. passes.push_pass(box mir::transform::remove_dead_blocks::RemoveDeadBlocks); diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs index 21b3566418c..0d942f35654 100644 --- a/src/librustc_mir/transform/type_check.rs +++ b/src/librustc_mir/transform/type_check.rs @@ -11,6 +11,7 @@ //! This pass type-checks the MIR to ensure it is not broken. #![allow(unreachable_code)] +use rustc::dep_graph::DepNode; use rustc::middle::infer::{self, InferCtxt}; use rustc::middle::traits; use rustc::middle::ty::{self, Ty, TyCtxt}; @@ -581,6 +582,7 @@ impl<'tcx> MirMapPass<'tcx> for TypeckMir { return; } for (&id, mir) in &mut map.map { + let _task = tcx.dep_graph.in_task(DepNode::MirTypeck(id)); let param_env = ty::ParameterEnvironment::for_item(tcx, id); let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, Some(param_env)); let mut checker = TypeChecker::new(&infcx); From e30ff067567fc1c607e12601649e6b2539085a91 Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Mon, 7 Mar 2016 14:07:07 +0200 Subject: [PATCH 3/4] Change MirPass to also take NodeId --- src/librustc/mir/transform.rs | 8 ++-- src/librustc_mir/transform/erase_regions.rs | 3 +- src/librustc_mir/transform/no_landing_pads.rs | 3 +- .../transform/remove_dead_blocks.rs | 3 +- src/librustc_mir/transform/simplify_cfg.rs | 5 ++- src/librustc_mir/transform/type_check.rs | 39 +++++++++---------- src/test/auxiliary/dummy_mir_pass.rs | 4 +- 7 files changed, 34 insertions(+), 31 deletions(-) diff --git a/src/librustc/mir/transform.rs b/src/librustc/mir/transform.rs index d15ef724d59..afcb5b95631 100644 --- a/src/librustc/mir/transform.rs +++ b/src/librustc/mir/transform.rs @@ -8,10 +8,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use dep_graph::DepNode; use mir::mir_map::MirMap; use mir::repr::Mir; use middle::ty::TyCtxt; +use syntax::ast::NodeId; /// Various information about pass. pub trait Pass { @@ -26,13 +26,13 @@ pub trait MirMapPass<'tcx>: Pass { /// A pass which inspects Mir of functions in isolation. pub trait MirPass<'tcx>: Pass { - fn run_pass(&mut self, cx: &TyCtxt<'tcx>, mir: &mut Mir<'tcx>); + fn run_pass(&mut self, cx: &TyCtxt<'tcx>, id: NodeId, mir: &mut Mir<'tcx>); } impl<'tcx, T: MirPass<'tcx>> MirMapPass<'tcx> for T { fn run_pass(&mut self, tcx: &TyCtxt<'tcx>, map: &mut MirMap<'tcx>) { - for (_, mir) in &mut map.map { - MirPass::run_pass(self, tcx, mir); + for (&id, mir) in &mut map.map { + MirPass::run_pass(self, tcx, id, mir); } } } diff --git a/src/librustc_mir/transform/erase_regions.rs b/src/librustc_mir/transform/erase_regions.rs index 2bab0168c52..1920bd552ec 100644 --- a/src/librustc_mir/transform/erase_regions.rs +++ b/src/librustc_mir/transform/erase_regions.rs @@ -16,6 +16,7 @@ use rustc::middle::ty::{self, TyCtxt}; use rustc::mir::repr::*; use rustc::mir::visit::MutVisitor; use rustc::mir::transform::{MirPass, Pass}; +use syntax::ast::NodeId; struct EraseRegionsVisitor<'a, 'tcx: 'a> { tcx: &'a TyCtxt<'tcx>, @@ -123,7 +124,7 @@ pub struct EraseRegions; impl Pass for EraseRegions {} impl<'tcx> MirPass<'tcx> for EraseRegions { - fn run_pass(&mut self, tcx: &TyCtxt<'tcx>, mir: &mut Mir<'tcx>) { + fn run_pass(&mut self, tcx: &TyCtxt<'tcx>, _: NodeId, mir: &mut Mir<'tcx>) { EraseRegionsVisitor::new(tcx).visit_mir(mir); } } diff --git a/src/librustc_mir/transform/no_landing_pads.rs b/src/librustc_mir/transform/no_landing_pads.rs index c08f45a8ec4..9caee36e44a 100644 --- a/src/librustc_mir/transform/no_landing_pads.rs +++ b/src/librustc_mir/transform/no_landing_pads.rs @@ -15,6 +15,7 @@ use rustc::middle::ty::TyCtxt; use rustc::mir::repr::*; use rustc::mir::visit::MutVisitor; use rustc::mir::transform::{Pass, MirPass}; +use syntax::ast::NodeId; pub struct NoLandingPads; @@ -41,7 +42,7 @@ impl<'tcx> MutVisitor<'tcx> for NoLandingPads { } impl<'tcx> MirPass<'tcx> for NoLandingPads { - fn run_pass(&mut self, tcx: &TyCtxt<'tcx>, mir: &mut Mir<'tcx>) { + fn run_pass(&mut self, tcx: &TyCtxt<'tcx>, _: NodeId, mir: &mut Mir<'tcx>) { if tcx.sess.no_landing_pads() { self.visit_mir(mir); } diff --git a/src/librustc_mir/transform/remove_dead_blocks.rs b/src/librustc_mir/transform/remove_dead_blocks.rs index 308e257c20d..4513aeef86d 100644 --- a/src/librustc_mir/transform/remove_dead_blocks.rs +++ b/src/librustc_mir/transform/remove_dead_blocks.rs @@ -36,11 +36,12 @@ use rustc_data_structures::bitvec::BitVector; use rustc::middle::ty::TyCtxt; use rustc::mir::repr::*; use rustc::mir::transform::{Pass, MirPass}; +use syntax::ast::NodeId; pub struct RemoveDeadBlocks; impl<'tcx> MirPass<'tcx> for RemoveDeadBlocks { - fn run_pass(&mut self, _: &TyCtxt<'tcx>, mir: &mut Mir<'tcx>) { + fn run_pass(&mut self, _: &TyCtxt<'tcx>, _: NodeId, mir: &mut Mir<'tcx>) { let mut seen = BitVector::new(mir.basic_blocks.len()); // These blocks are always required. seen.insert(START_BLOCK.index()); diff --git a/src/librustc_mir/transform/simplify_cfg.rs b/src/librustc_mir/transform/simplify_cfg.rs index 85f36df7ced..84410bdc57c 100644 --- a/src/librustc_mir/transform/simplify_cfg.rs +++ b/src/librustc_mir/transform/simplify_cfg.rs @@ -12,6 +12,7 @@ use rustc::middle::const_eval::ConstVal; use rustc::middle::ty::TyCtxt; use rustc::mir::repr::*; use rustc::mir::transform::{MirPass, Pass}; +use syntax::ast::NodeId; use super::remove_dead_blocks::RemoveDeadBlocks; @@ -101,12 +102,12 @@ impl SimplifyCfg { } impl<'tcx> MirPass<'tcx> for SimplifyCfg { - fn run_pass(&mut self, tcx: &TyCtxt<'tcx>, mir: &mut Mir<'tcx>) { + fn run_pass(&mut self, tcx: &TyCtxt<'tcx>, id: NodeId, mir: &mut Mir<'tcx>) { let mut changed = true; while changed { changed = self.simplify_branches(mir); changed |= self.remove_goto_chains(mir); - RemoveDeadBlocks.run_pass(tcx, mir); + RemoveDeadBlocks.run_pass(tcx, id, mir); } // FIXME: Should probably be moved into some kind of pass manager mir.basic_blocks.shrink_to_fit(); diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs index 0d942f35654..9924a4f1ad9 100644 --- a/src/librustc_mir/transform/type_check.rs +++ b/src/librustc_mir/transform/type_check.rs @@ -14,16 +14,15 @@ use rustc::dep_graph::DepNode; use rustc::middle::infer::{self, InferCtxt}; use rustc::middle::traits; -use rustc::middle::ty::{self, Ty, TyCtxt}; use rustc::middle::ty::fold::TypeFoldable; +use rustc::middle::ty::{self, Ty, TyCtxt}; use rustc::mir::repr::*; -use rustc::mir::mir_map::MirMap; use rustc::mir::tcx::LvalueTy; -use rustc::mir::transform::{MirMapPass, Pass}; +use rustc::mir::transform::{MirPass, Pass}; use rustc::mir::visit::{self, Visitor}; - -use syntax::codemap::{Span, DUMMY_SP}; use std::fmt; +use syntax::ast::NodeId; +use syntax::codemap::{Span, DUMMY_SP}; macro_rules! span_mirbug { ($context:expr, $elem:expr, $($message:tt)*) => ({ @@ -574,29 +573,27 @@ impl TypeckMir { } } -impl<'tcx> MirMapPass<'tcx> for TypeckMir { - fn run_pass(&mut self, tcx: &TyCtxt<'tcx>, map: &mut MirMap<'tcx>) { +impl<'tcx> MirPass<'tcx> for TypeckMir { + fn run_pass(&mut self, tcx: &TyCtxt<'tcx>, id: NodeId, mir: &mut Mir<'tcx>) { if tcx.sess.err_count() > 0 { // compiling a broken program can obviously result in a // broken MIR, so try not to report duplicate errors. return; } - for (&id, mir) in &mut map.map { - let _task = tcx.dep_graph.in_task(DepNode::MirTypeck(id)); - let param_env = ty::ParameterEnvironment::for_item(tcx, id); - let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, Some(param_env)); - let mut checker = TypeChecker::new(&infcx); - { - let mut verifier = TypeVerifier::new(&mut checker, mir); - verifier.visit_mir(mir); - if verifier.errors_reported { - // don't do further checks to avoid ICEs - continue; - } + let _task = tcx.dep_graph.in_task(DepNode::MirTypeck(id)); + let param_env = ty::ParameterEnvironment::for_item(tcx, id); + let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, Some(param_env)); + let mut checker = TypeChecker::new(&infcx); + { + let mut verifier = TypeVerifier::new(&mut checker, mir); + verifier.visit_mir(mir); + if verifier.errors_reported { + // don't do further checks to avoid ICEs + return; } - checker.typeck_mir(mir); - checker.verify_obligations(mir); } + checker.typeck_mir(mir); + checker.verify_obligations(mir); } } diff --git a/src/test/auxiliary/dummy_mir_pass.rs b/src/test/auxiliary/dummy_mir_pass.rs index aec5165f6cf..89101fe709d 100644 --- a/src/test/auxiliary/dummy_mir_pass.rs +++ b/src/test/auxiliary/dummy_mir_pass.rs @@ -25,11 +25,13 @@ use rustc::middle::ty; use rustc::middle::const_eval::ConstVal; use rustc_plugin::Registry; +use syntax::ast::NodeId; + struct Pass; impl transform::Pass for Pass {} impl<'tcx> MirPass<'tcx> for Pass { - fn run_pass(&mut self, _: &ty::ctxt<'tcx>, mir: &mut Mir<'tcx>) { + fn run_pass(&mut self, _: &ty::TyCtxt<'tcx>, _: NodeId, mir: &mut Mir<'tcx>) { Visitor.visit_mir(mir) } } From bdc176ef6b3909270c187235af657a89e4f2aeec Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Sat, 12 Mar 2016 19:07:00 +0200 Subject: [PATCH 4/4] Implement --unpretty mir-cfg for graphviz output Also change output for --unpretty mir to output function names in a prettier way. --- src/librustc_driver/pretty.rs | 57 +++++++++++++++++------------------ src/librustc_mir/graphviz.rs | 47 ++++++++++++++++------------- src/librustc_mir/pretty.rs | 25 +++++++++------ 3 files changed, 69 insertions(+), 60 deletions(-) diff --git a/src/librustc_driver/pretty.rs b/src/librustc_driver/pretty.rs index eb4668e6abb..e716c3e82b0 100644 --- a/src/librustc_driver/pretty.rs +++ b/src/librustc_driver/pretty.rs @@ -31,6 +31,7 @@ use rustc_resolve as resolve; use rustc_metadata::cstore::CStore; use rustc_mir::pretty::write_mir_pretty; +use rustc_mir::graphviz::write_mir_graphviz; use syntax::ast::{self, BlockCheckMode}; use syntax::codemap; @@ -44,6 +45,7 @@ use graphviz as dot; use std::fs::File; use std::io::{self, Write}; +use std::iter; use std::option; use std::path::PathBuf; use std::str::FromStr; @@ -80,6 +82,7 @@ pub enum PpMode { PpmHir(PpSourceMode), PpmFlowGraph(PpFlowGraphMode), PpmMir, + PpmMirCFG, } pub fn parse_pretty(sess: &Session, @@ -100,6 +103,7 @@ pub fn parse_pretty(sess: &Session, ("hir,identified", true) => PpmHir(PpmIdentified), ("hir,typed", true) => PpmHir(PpmTyped), ("mir", true) => PpmMir, + ("mir-cfg", true) => PpmMirCFG, ("flowgraph", true) => PpmFlowGraph(PpFlowGraphMode::Default), ("flowgraph,unlabelled", true) => PpmFlowGraph(PpFlowGraphMode::UnlabelledEdges), _ => { @@ -574,6 +578,7 @@ fn needs_ast_map(ppm: &PpMode, opt_uii: &Option) -> bool { PpmSource(PpmExpandedHygiene) | PpmHir(_) | PpmMir | + PpmMirCFG | PpmFlowGraph(_) => true, PpmSource(PpmTyped) => panic!("invalid state"), } @@ -590,6 +595,7 @@ fn needs_expansion(ppm: &PpMode) -> bool { PpmSource(PpmExpandedHygiene) | PpmHir(_) | PpmMir | + PpmMirCFG | PpmFlowGraph(_) => true, PpmSource(PpmTyped) => panic!("invalid state"), } @@ -807,9 +813,15 @@ pub fn pretty_print_input(sess: Session, }) } - (PpmMir, None) => { - debug!("pretty printing MIR for whole crate"); - let ast_map = ast_map.expect("--unpretty mir missing ast_map"); + (pp_type@PpmMir, uii) | (pp_type@PpmMirCFG, uii) => { + let ast_map = ast_map.expect("--unpretty missing ast_map"); + let nodeid = if let Some(uii) = uii { + debug!("pretty printing MIR for {:?}", uii); + Some(uii.to_one_node_id("--unpretty", &sess, &ast_map)) + } else { + debug!("pretty printing MIR for whole crate"); + None + }; abort_on_err(driver::phase_3_run_analysis_passes(&sess, &cstore, ast_map, @@ -818,38 +830,25 @@ pub fn pretty_print_input(sess: Session, resolve::MakeGlobMap::No, |tcx, mir_map, _, _| { if let Some(mir_map) = mir_map { - for (nodeid, mir) in &mir_map.map { - try!(writeln!(out, "MIR for {}", tcx.map.node_to_string(*nodeid))); - try!(write_mir_pretty(mir, &mut out)); + if let Some(nodeid) = nodeid { + let mir = mir_map.map.get(&nodeid).unwrap_or_else(|| { + sess.fatal(&format!("no MIR map entry for node {}", nodeid)) + }); + try!(match pp_type { + PpmMir => write_mir_pretty(tcx, iter::once((&nodeid, mir)), &mut out), + _ => write_mir_graphviz(tcx, iter::once((&nodeid, mir)), &mut out) + }); + } else { + try!(match pp_type { + PpmMir => write_mir_pretty(tcx, mir_map.map.iter(), &mut out), + _ => write_mir_graphviz(tcx, mir_map.map.iter(), &mut out) + }); } } Ok(()) }), &sess) } - (PpmMir, Some(uii)) => { - debug!("pretty printing MIR for {:?}", uii); - let ast_map = ast_map.expect("--unpretty mir missing ast_map"); - let nodeid = uii.to_one_node_id("--unpretty", &sess, &ast_map); - - abort_on_err(driver::phase_3_run_analysis_passes(&sess, - &cstore, - ast_map, - &arenas, - &id, - resolve::MakeGlobMap::No, - |tcx, mir_map, _, _| { - if let Some(mir_map) = mir_map { - try!(writeln!(out, "MIR for {}", tcx.map.node_to_string(nodeid))); - let mir = mir_map.map.get(&nodeid).unwrap_or_else(|| { - sess.fatal(&format!("no MIR map entry for node {}", nodeid)) - }); - try!(write_mir_pretty(mir, &mut out)); - } - Ok(()) - }), &sess) - } - (PpmFlowGraph(mode), opt_uii) => { debug!("pretty printing flow graph for {:?}", opt_uii); let uii = opt_uii.unwrap_or_else(|| { diff --git a/src/librustc_mir/graphviz.rs b/src/librustc_mir/graphviz.rs index 1b8fe650558..f705c0591b5 100644 --- a/src/librustc_mir/graphviz.rs +++ b/src/librustc_mir/graphviz.rs @@ -13,30 +13,34 @@ use rustc::mir::repr::*; use rustc::middle::ty; use std::fmt::Debug; use std::io::{self, Write}; +use syntax::ast::NodeId; -/// Write a graphviz DOT graph for the given MIR. -pub fn write_mir_graphviz(mir: &Mir, w: &mut W) -> io::Result<()> { - try!(writeln!(w, "digraph Mir {{")); +/// Write a graphviz DOT graph of a list of MIRs. +pub fn write_mir_graphviz<'a, 't, W, I>(tcx: &ty::TyCtxt<'t>, iter: I, w: &mut W) -> io::Result<()> +where W: Write, I: Iterator)> { + for (&nodeid, mir) in iter { + try!(writeln!(w, "digraph Mir_{} {{", nodeid)); - // Global graph properties - try!(writeln!(w, r#" graph [fontname="monospace"];"#)); - try!(writeln!(w, r#" node [fontname="monospace"];"#)); - try!(writeln!(w, r#" edge [fontname="monospace"];"#)); + // Global graph properties + try!(writeln!(w, r#" graph [fontname="monospace"];"#)); + try!(writeln!(w, r#" node [fontname="monospace"];"#)); + try!(writeln!(w, r#" edge [fontname="monospace"];"#)); - // Graph label - try!(write_graph_label(mir, w)); + // Graph label + try!(write_graph_label(tcx, nodeid, mir, w)); - // Nodes - for block in mir.all_basic_blocks() { - try!(write_node(block, mir, w)); + // Nodes + for block in mir.all_basic_blocks() { + try!(write_node(block, mir, w)); + } + + // Edges + for source in mir.all_basic_blocks() { + try!(write_edges(source, mir, w)); + } + try!(writeln!(w, "}}")) } - - // Edges - for source in mir.all_basic_blocks() { - try!(write_edges(source, mir, w)); - } - - writeln!(w, "}}") + Ok(()) } /// Write a graphviz DOT node for the given basic block. @@ -84,8 +88,9 @@ fn write_edges(source: BasicBlock, mir: &Mir, w: &mut W) -> io::Result /// Write the graphviz DOT label for the overall graph. This is essentially a block of text that /// will appear below the graph, showing the type of the `fn` this MIR represents and the types of /// all the variables and temporaries. -fn write_graph_label(mir: &Mir, w: &mut W) -> io::Result<()> { - try!(write!(w, " label=(tcx: &ty::TyCtxt, nid: NodeId, mir: &Mir, w: &mut W) +-> io::Result<()> { + try!(write!(w, " label=(mir: &Mir, w: &mut W) -> io::Result<()> { - try!(write_mir_intro(mir, w)); - - // Nodes - for block in mir.all_basic_blocks() { - try!(write_basic_block(block, mir, w)); +pub fn write_mir_pretty<'a, 't, W, I>(tcx: &ty::TyCtxt<'t>, iter: I, w: &mut W) -> io::Result<()> +where W: Write, I: Iterator)> { + for (&nodeid, mir) in iter { + try!(write_mir_intro(tcx, nodeid, mir, w)); + // Nodes + for block in mir.all_basic_blocks() { + try!(write_basic_block(block, mir, w)); + } + try!(writeln!(w, "}}")) } - - writeln!(w, "}}") + Ok(()) } /// Write out a human-readable textual representation for the given basic block. @@ -46,8 +49,10 @@ fn write_basic_block(block: BasicBlock, mir: &Mir, w: &mut W) -> io::R /// Write out a human-readable textual representation of the MIR's `fn` type and the types of its /// local variables (both user-defined bindings and compiler temporaries). -fn write_mir_intro(mir: &Mir, w: &mut W) -> io::Result<()> { - try!(write!(w, "fn(")); +fn write_mir_intro(tcx: &ty::TyCtxt, nid: NodeId, mir: &Mir, w: &mut W) +-> io::Result<()> { + + try!(write!(w, "fn {}(", tcx.map.path_to_string(nid))); // fn argument types. for (i, arg) in mir.arg_decls.iter().enumerate() {