From da8a388918e65712e03d51df90d88b0284527df6 Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Sun, 31 Jan 2016 20:25:17 +0200 Subject: [PATCH 1/5] Reuse MIR visitors for EraseRegions pass --- src/librustc_mir/mir_map.rs | 2 +- src/librustc_mir/transform/erase_regions.rs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/librustc_mir/mir_map.rs b/src/librustc_mir/mir_map.rs index 0e641bf679d11..f6fa924fa38c7 100644 --- a/src/librustc_mir/mir_map.rs +++ b/src/librustc_mir/mir_map.rs @@ -22,7 +22,7 @@ extern crate rustc_front; use build; use graphviz; use pretty; -use transform::simplify_cfg; +use transform::{simplify_cfg, MirPass}; use rustc::dep_graph::DepNode; use rustc::mir::repr::Mir; use hair::cx::Cx; diff --git a/src/librustc_mir/transform/erase_regions.rs b/src/librustc_mir/transform/erase_regions.rs index b927ab489f100..eeeb37e1c22fe 100644 --- a/src/librustc_mir/transform/erase_regions.rs +++ b/src/librustc_mir/transform/erase_regions.rs @@ -58,13 +58,13 @@ impl<'a, 'tcx> EraseRegionsVisitor<'a, 'tcx> { } } -impl MirPass for EraseRegions { - fn run_on_mir<'tcx>(&mut self, mir: &mut Mir<'tcx>, tcx: &ty::ctxt<'tcx>) { - EraseRegionsVisitor::new(tcx).visit_mir(mir); +impl<'a, 'tcx> MirPass<'tcx> for EraseRegions<'a, 'tcx> { + fn run_on_mir(&mut self, mir: &mut Mir<'tcx>) { + self.visit_mir(mir); } } -impl<'a, 'tcx> MutVisitor<'tcx> for EraseRegionsVisitor<'a, 'tcx> { +impl<'a, 'tcx> MutVisitor<'tcx> for EraseRegions<'a, 'tcx> { fn visit_mir(&mut self, mir: &mut Mir<'tcx>) { self.erase_regions_return_ty(&mut mir.return_ty); self.erase_regions_tys(mir.var_decls.iter_mut().map(|d| &mut d.ty)); From c7286d9a606ee81c273b701cfebbe9dffe051b1b Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Fri, 5 Feb 2016 18:34:07 +0200 Subject: [PATCH 2/5] Add MIR pass manager and tier-ed passes. Tiered passes allow to avoid some of the introspection of MIR map for passes which do not need it and perhaps improve performance of the more-restricted passes (i.e. BlockPasses could be run in parallel regardless of function for which the block belongs). It also could allow to e.g. run dead block removal only after passes which modify the CFG graph and not after those which only change blocks. --- src/librustc/mir/mir_map.rs | 12 ---- src/librustc/mir/transform.rs | 76 ++++++++++++++++++++- src/librustc/session/mod.rs | 6 +- src/librustc_driver/driver.rs | 27 ++++---- src/librustc_mir/mir_map.rs | 6 +- src/librustc_mir/transform/erase_regions.rs | 27 +++++--- src/librustc_mir/transform/simplify_cfg.rs | 16 +++-- src/librustc_plugin/registry.rs | 6 +- src/test/auxiliary/dummy_mir_pass.rs | 16 +++-- 9 files changed, 133 insertions(+), 59 deletions(-) diff --git a/src/librustc/mir/mir_map.rs b/src/librustc/mir/mir_map.rs index aa24f6d897969..1a34699aff491 100644 --- a/src/librustc/mir/mir_map.rs +++ b/src/librustc/mir/mir_map.rs @@ -10,19 +10,7 @@ use util::nodemap::NodeMap; use mir::repr::Mir; -use mir::transform::MirPass; -use middle::ty; pub struct MirMap<'tcx> { pub map: NodeMap>, } - -impl<'tcx> MirMap<'tcx> { - pub fn run_passes(&mut self, passes: &mut [Box], tcx: &ty::ctxt<'tcx>) { - for (_, ref mut mir) in &mut self.map { - for pass in &mut *passes { - pass.run_on_mir(mir, tcx) - } - } - } -} diff --git a/src/librustc/mir/transform.rs b/src/librustc/mir/transform.rs index 0f18d8d282144..df5e0d5f1a0db 100644 --- a/src/librustc/mir/transform.rs +++ b/src/librustc/mir/transform.rs @@ -8,9 +8,79 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use mir::repr::Mir; +use mir::repr::{Mir, BasicBlockData, BasicBlock}; +use mir::mir_map::MirMap; use middle::ty::ctxt; -pub trait MirPass { - fn run_on_mir<'tcx>(&mut self, mir: &mut Mir<'tcx>, tcx: &ctxt<'tcx>); +/// Contains various metadata about the pass. +pub trait Pass { + /// Ordering of the pass. Lower value runs the pass earlier. + fn priority(&self) -> usize; + // Possibly also `fn name()` and `fn should_run(Session)` etc. +} + +/// Pass which inspects the whole MirMap. +pub trait MirMapPass: Pass { + fn run_pass<'tcx>(&mut self, tcx: &ctxt<'tcx>, map: &mut MirMap<'tcx>); +} + +/// Pass which only inspects MIR of distinct functions. +pub trait MirPass: Pass { + fn run_pass<'tcx>(&mut self, tcx: &ctxt<'tcx>, mir: &mut Mir<'tcx>); +} + +/// Pass which only inspects basic blocks in MIR. +/// +/// Invariant: The blocks are considered to be fully self-contained for the purposes of this pass – +/// the pass may not change the list of successors of the block or apply any transformations to +/// blocks based on the information collected during earlier runs of the pass. +pub trait MirBlockPass: Pass { + fn run_pass<'tcx>(&mut self, tcx: &ctxt<'tcx>, bb: BasicBlock, data: &mut BasicBlockData<'tcx>); +} + +impl MirPass for T { + fn run_pass<'tcx>(&mut self, tcx: &ctxt<'tcx>, mir: &mut Mir<'tcx>) { + for (i, basic_block) in mir.basic_blocks.iter_mut().enumerate() { + MirBlockPass::run_pass(self, tcx, BasicBlock::new(i), basic_block); + } + } +} + +impl MirMapPass for T { + fn run_pass<'tcx>(&mut self, tcx: &ctxt<'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> +} + +impl Passes { + pub fn new() -> Passes { + let passes = Passes { + passes: Vec::new() + }; + passes + } + + pub fn run_passes<'tcx>(&mut self, tcx: &ctxt<'tcx>, map: &mut MirMap<'tcx>) { + self.passes.sort_by_key(|e| e.priority()); + for pass in &mut self.passes { + pass.run_pass(tcx, map); + } + } + + pub fn push_pass(&mut self, pass: Box) { + self.passes.push(pass); + } +} + +impl ::std::iter::Extend> for Passes { + fn extend>>(&mut self, it: I) { + self.passes.extend(it); + } } diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index d2f8d3f09fd7c..473b2793d77b6 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; 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::transform::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 6eede3070b27a..01a65eb471324 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -23,6 +23,7 @@ use rustc::middle; use rustc::util::common::time; use rustc::util::nodemap::NodeSet; use rustc_borrowck as borrowck; +use rustc_mir::transform; use rustc_resolve as resolve; use rustc_metadata::macro_import; use rustc_metadata::creader::LocalCrateReader; @@ -561,8 +562,8 @@ 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.plugin_attributes.borrow_mut() = attributes.clone(); + sess.mir_passes.borrow_mut().extend(mir_passes); })); // Lint plugins are registered; now we can process command line flags. @@ -846,12 +847,18 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, let mut mir_map = time(time_passes, - "MIR dump", + "MIR build", || 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 mut passes = sess.mir_passes.borrow_mut(); + // Push all the built-in passes. + passes.push_pass(box transform::simplify_cfg::SimplifyCfg); + passes.push_pass(box transform::erase_regions::EraseRegions); + // And run everything. + passes.run_passes(tcx, &mut mir_map); + }); time(time_passes, "liveness checking", @@ -907,10 +914,9 @@ 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. +/// Run the translation phase to LLVM, after which the MIR and analysis can be discarded. pub fn phase_4_translate_to_llvm<'tcx>(tcx: &ty::ctxt<'tcx>, - mut mir_map: MirMap<'tcx>, + mir_map: MirMap<'tcx>, analysis: ty::CrateAnalysis) -> trans::CrateTranslation { let time_passes = tcx.sess.time_passes(); @@ -919,11 +925,6 @@ pub fn phase_4_translate_to_llvm<'tcx>(tcx: &ty::ctxt<'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", move || trans::trans_crate(tcx, &mir_map, analysis)) diff --git a/src/librustc_mir/mir_map.rs b/src/librustc_mir/mir_map.rs index f6fa924fa38c7..a41d58ef7fb70 100644 --- a/src/librustc_mir/mir_map.rs +++ b/src/librustc_mir/mir_map.rs @@ -22,13 +22,11 @@ extern crate rustc_front; use build; use graphviz; use pretty; -use transform::{simplify_cfg, MirPass}; 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; @@ -147,9 +145,7 @@ impl<'a, 'm, 'tcx> Visitor<'tcx> for InnerDump<'a,'m,'tcx> { 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) => { - simplify_cfg::SimplifyCfg::new().run_on_mir(&mut mir, self.tcx); - + Ok(mir) => { let meta_item_list = self.attr .iter() .flat_map(|a| a.meta_item_list()) diff --git a/src/librustc_mir/transform/erase_regions.rs b/src/librustc_mir/transform/erase_regions.rs index eeeb37e1c22fe..72ea064880267 100644 --- a/src/librustc_mir/transform/erase_regions.rs +++ b/src/librustc_mir/transform/erase_regions.rs @@ -16,18 +16,33 @@ use rustc::middle::ty; use rustc::mir::repr::*; use rustc::mir::visit::MutVisitor; use rustc::mir::mir_map::MirMap; -use rustc::mir::transform::MirPass; +use rustc::mir::transform::{MirPass, Pass}; pub fn erase_regions<'tcx>(tcx: &ty::ctxt<'tcx>, mir_map: &mut MirMap<'tcx>) { let mut eraser = EraseRegions; for (_, mir) in &mut mir_map.map { - eraser.run_on_mir(mir, tcx); + eraser.run_pass(tcx, mir); } } pub struct EraseRegions; +impl MirPass for EraseRegions { + fn run_pass<'tcx>(&mut self, tcx: &ty::ctxt<'tcx>, mir: &mut Mir<'tcx>) { + EraseRegionsVisitor::new(tcx).visit_mir(mir); + } + +} + +impl Pass for EraseRegions { + fn priority(&self) -> usize { + // We want this pass to run as late as possible in transformation chain, so we give it a + // very high priority number. + !50 + } +} + struct EraseRegionsVisitor<'a, 'tcx: 'a> { tcx: &'a ty::ctxt<'tcx>, } @@ -58,13 +73,7 @@ impl<'a, 'tcx> EraseRegionsVisitor<'a, 'tcx> { } } -impl<'a, 'tcx> MirPass<'tcx> for EraseRegions<'a, 'tcx> { - fn run_on_mir(&mut self, mir: &mut Mir<'tcx>) { - self.visit_mir(mir); - } -} - -impl<'a, 'tcx> MutVisitor<'tcx> for EraseRegions<'a, 'tcx> { +impl<'a, 'tcx> MutVisitor<'tcx> for EraseRegionsVisitor<'a, 'tcx> { fn visit_mir(&mut self, mir: &mut Mir<'tcx>) { self.erase_regions_return_ty(&mut mir.return_ty); self.erase_regions_tys(mir.var_decls.iter_mut().map(|d| &mut d.ty)); diff --git a/src/librustc_mir/transform/simplify_cfg.rs b/src/librustc_mir/transform/simplify_cfg.rs index 17c5b5f7c3cff..415b8412bc4c2 100644 --- a/src/librustc_mir/transform/simplify_cfg.rs +++ b/src/librustc_mir/transform/simplify_cfg.rs @@ -9,17 +9,14 @@ // except according to those terms. use rustc::middle::const_eval::ConstVal; +use rustc::middle::ty; use rustc::mir::repr::*; use transform::util; -use rustc::mir::transform::MirPass; +use rustc::mir::transform::{MirPass, Pass}; pub struct SimplifyCfg; impl SimplifyCfg { - pub fn new() -> SimplifyCfg { - SimplifyCfg - } - fn remove_dead_blocks(&self, mir: &mut Mir) { let mut seen = vec![false; mir.basic_blocks.len()]; @@ -83,6 +80,7 @@ impl SimplifyCfg { changed } + // FIXME: This transformation should be interleaved with the constant-propagation pass. fn simplify_branches(&self, mir: &mut Mir) -> bool { let mut changed = false; @@ -119,7 +117,7 @@ impl SimplifyCfg { } impl MirPass for SimplifyCfg { - fn run_on_mir<'tcx>(&mut self, mir: &mut Mir<'tcx>, _: &::rustc::middle::ty::ctxt<'tcx>) { + fn run_pass<'tcx>(&mut self, _: &ty::ctxt<'tcx>, mir: &mut Mir<'tcx>) { let mut changed = true; while changed { changed = self.simplify_branches(mir); @@ -130,3 +128,9 @@ impl MirPass for SimplifyCfg { mir.basic_blocks.shrink_to_fit(); } } + +impl Pass for SimplifyCfg { + fn priority(&self) -> usize { + 50 + } +} diff --git a/src/librustc_plugin/registry.rs b/src/librustc_plugin/registry.rs index a51fd58db88dd..b07afb222a265 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; 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>, #[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) { self.mir_passes.push(pass); } diff --git a/src/test/auxiliary/dummy_mir_pass.rs b/src/test/auxiliary/dummy_mir_pass.rs index df9ea4ab012e7..1b2d01687ccd0 100644 --- a/src/test/auxiliary/dummy_mir_pass.rs +++ b/src/test/auxiliary/dummy_mir_pass.rs @@ -18,8 +18,8 @@ extern crate rustc_front; extern crate rustc_plugin; extern crate syntax; -use rustc::mir::transform::MirPass; -use rustc::mir::repr::{Mir, Literal}; +use rustc::mir::transform::{self, MirBlockPass}; +use rustc::mir::repr::{BasicBlock, BasicBlockData, Literal}; use rustc::mir::visit::MutVisitor; use rustc::middle::ty; use rustc::middle::const_eval::ConstVal; @@ -30,9 +30,15 @@ use syntax::attr; struct Pass; -impl MirPass for Pass { - fn run_on_mir<'tcx>(&mut self, mir: &mut Mir<'tcx>, tcx: &ty::ctxt<'tcx>) { - Visitor.visit_mir(mir) +impl transform::Pass for Pass { + fn priority(&self) -> usize { + 1000 + } +} + +impl MirBlockPass for Pass { + fn run_pass<'tcx>(&mut self, tcx: &ty::ctxt<'tcx>, bb: BasicBlock, bbd: &mut BasicBlockData) { + Visitor.visit_basic_block_data(bb, bbd) } } From af69f95ac0dbdff22d4d8b7d20c429745898b52f Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Sat, 6 Feb 2016 14:23:39 +0200 Subject: [PATCH 3/5] Split SimplifyCfg pass into smaller passes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously SimplifyCfg pass was unecessarily interleaving many different functions – actual simplification, dead block removal and compaction. This patch splits off dead block removal and compaction into their own passes. --- src/librustc/mir/repr.rs | 2 +- src/librustc_driver/driver.rs | 1 + src/librustc_mir/transform/simplify_cfg.rs | 126 ++++++++++++++++----- 3 files changed, 101 insertions(+), 28 deletions(-) diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs index 783c58469a1b3..808854b701d98 100644 --- a/src/librustc/mir/repr.rs +++ b/src/librustc/mir/repr.rs @@ -204,7 +204,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_driver/driver.rs b/src/librustc_driver/driver.rs index 01a65eb471324..0172c74139ede 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -856,6 +856,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, // Push all the built-in passes. passes.push_pass(box transform::simplify_cfg::SimplifyCfg); passes.push_pass(box transform::erase_regions::EraseRegions); + passes.push_pass(box transform::simplify_cfg::CompactMir); // And run everything. passes.run_passes(tcx, &mut mir_map); }); diff --git a/src/librustc_mir/transform/simplify_cfg.rs b/src/librustc_mir/transform/simplify_cfg.rs index 415b8412bc4c2..f87dd87555cea 100644 --- a/src/librustc_mir/transform/simplify_cfg.rs +++ b/src/librustc_mir/transform/simplify_cfg.rs @@ -11,32 +11,31 @@ use rustc::middle::const_eval::ConstVal; use rustc::middle::ty; use rustc::mir::repr::*; +use rustc::mir::visit::MutVisitor; use transform::util; use rustc::mir::transform::{MirPass, Pass}; pub struct SimplifyCfg; -impl SimplifyCfg { - fn remove_dead_blocks(&self, mir: &mut Mir) { - let mut seen = vec![false; mir.basic_blocks.len()]; - - // These blocks are always required. - seen[START_BLOCK.index()] = true; - seen[END_BLOCK.index()] = true; +impl Pass for SimplifyCfg { + fn priority(&self) -> usize { + 50 + } +} - let mut worklist = vec![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; - worklist.push(*succ); - } - } +impl MirPass for SimplifyCfg { + fn run_pass<'tcx>(&mut self, tcx: &ty::ctxt<'tcx>, mir: &mut Mir<'tcx>) { + let mut dbr_pass = DeadBlockRemoval::new(self); + let mut changed = true; + while changed { + changed = self.simplify_branches(mir); + changed |= self.remove_goto_chains(mir); + dbr_pass.run_pass(tcx, mir); } - - util::retain_basic_blocks(mir, &seen); } +} +impl SimplifyCfg { 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 @@ -116,21 +115,94 @@ impl SimplifyCfg { } } -impl MirPass for SimplifyCfg { +/// Remove all the unreachable blocks. +/// +/// You want to schedule this pass just after any pass which might introduce unreachable blocks. +/// This pass is very cheap and might improve the run-time of other not-so-cheap passes. +pub struct DeadBlockRemoval(usize); + +impl DeadBlockRemoval { + fn new(run_after: &Pass) -> DeadBlockRemoval { + DeadBlockRemoval(run_after.priority() + 1) + } +} + +impl Pass for DeadBlockRemoval { + fn priority(&self) -> usize { + self.0 + } +} + +impl MirPass for DeadBlockRemoval { fn run_pass<'tcx>(&mut self, _: &ty::ctxt<'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); + let mut seen = vec![false; mir.basic_blocks.len()]; + + // These blocks are always required. + seen[START_BLOCK.index()] = true; + seen[END_BLOCK.index()] = true; + + let mut worklist = vec![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; + worklist.push(*succ); + } + } } - // FIXME: Should probably be moved into some kind of pass manager - mir.basic_blocks.shrink_to_fit(); + + util::retain_basic_blocks(mir, &seen); } } -impl Pass for SimplifyCfg { +/// Reduce the memory allocated by a MIR graph. +pub struct CompactMir; + +impl Pass for CompactMir { fn priority(&self) -> usize { - 50 + // We want this pass to run very late, so we give it a very high priority number. + !10 + } +} + +impl MirPass for CompactMir { + fn run_pass<'tcx>(&mut self, _: &ty::ctxt<'tcx>, mir: &mut Mir<'tcx>) { + self.visit_mir(mir); + } +} + +impl<'tcx> MutVisitor<'tcx> for CompactMir { + fn visit_mir(&mut self, mir: &mut Mir<'tcx>) { + mir.basic_blocks.shrink_to_fit(); + mir.var_decls.shrink_to_fit(); + mir.arg_decls.shrink_to_fit(); + mir.temp_decls.shrink_to_fit(); + self.super_mir(mir); + } + + fn visit_basic_block_data(&mut self, b: BasicBlock, data: &mut BasicBlockData) { + data.statements.shrink_to_fit(); + self.super_basic_block_data(b, data); + } + + fn visit_terminator(&mut self, b: BasicBlock, terminator: &mut Terminator) { + match *terminator { + Terminator::Switch { ref mut targets, .. } => targets.shrink_to_fit(), + Terminator::SwitchInt { ref mut values, ref mut targets, .. } => { + values.shrink_to_fit(); + targets.shrink_to_fit(); + }, + Terminator::Call { ref mut args, .. } => args.shrink_to_fit(), + _ => {/* nothing to do */} + } + self.super_terminator(b, terminator); + } + + fn visit_rvalue(&mut self, rvalue: &mut Rvalue) { + match *rvalue { + Rvalue::Aggregate(_, ref mut operands) => operands.shrink_to_fit(), + _ => { /* nothing to do */ } + } + self.super_rvalue(rvalue); } } From 363ac0fbb22f78cdd86cb29a17ef021c41e8a50d Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Sun, 7 Feb 2016 20:25:54 +0200 Subject: [PATCH 4/5] Factor out MIR printer into a pass --- src/librustc/dep_graph/mod.rs | 1 + src/librustc_driver/driver.rs | 1 + src/librustc_mir/mir_map.rs | 60 ++---------------------- src/librustc_mir/transform/mod.rs | 1 + src/librustc_mir/transform/print.rs | 73 +++++++++++++++++++++++++++++ 5 files changed, 81 insertions(+), 55 deletions(-) create mode 100644 src/librustc_mir/transform/print.rs diff --git a/src/librustc/dep_graph/mod.rs b/src/librustc/dep_graph/mod.rs index faf97f5808e36..551518ee0c1f7 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), + MirPrintPass, BorrowCheck(DefId), RvalueCheck(DefId), Reachability, diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 0172c74139ede..b3e69132dea65 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -856,6 +856,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, // Push all the built-in passes. passes.push_pass(box transform::simplify_cfg::SimplifyCfg); passes.push_pass(box transform::erase_regions::EraseRegions); + passes.push_pass(box transform::print::MirPrint); passes.push_pass(box transform::simplify_cfg::CompactMir); // And run everything. passes.run_passes(tcx, &mut mir_map); diff --git a/src/librustc_mir/mir_map.rs b/src/librustc_mir/mir_map.rs index a41d58ef7fb70..c2c19a4dcb5bc 100644 --- a/src/librustc_mir/mir_map.rs +++ b/src/librustc_mir/mir_map.rs @@ -8,24 +8,15 @@ // 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")]` +//! An pass that builds the MIR for each item and stores it into the map. extern crate syntax; extern crate rustc_front; use build; -use graphviz; -use pretty; use rustc::dep_graph::DepNode; use rustc::mir::repr::Mir; use hair::cx::Cx; -use std::fs::File; use rustc::mir::mir_map::MirMap; use rustc::middle::infer; @@ -133,55 +124,14 @@ 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 = match fk { + intravisit::FnKind::Closure => vec![closure_self_ty(&self.tcx, id, body.id)], + _ => 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(mir) => { - 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/mod.rs b/src/librustc_mir/transform/mod.rs index 41ea4469734f7..3ec12d84da3ad 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -10,4 +10,5 @@ pub mod simplify_cfg; pub mod erase_regions; +pub mod print; mod util; diff --git a/src/librustc_mir/transform/print.rs b/src/librustc_mir/transform/print.rs new file mode 100644 index 0000000000000..8fdbcc14fe085 --- /dev/null +++ b/src/librustc_mir/transform/print.rs @@ -0,0 +1,73 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! This pass handles the `#[rustc_mir(*)]` attributes and prints the contents. +//! +//! The attribute formats that are currently accepted are: +//! +//! - `#[rustc_mir(graphviz="file.gv")]` +//! - `#[rustc_mir(pretty="file.mir")]` +use graphviz; +use pretty; + +use syntax::attr::AttrMetaMethods; +use rustc::middle::ty; +use rustc::dep_graph::DepNode; +use rustc::mir::mir_map::MirMap; +use rustc::mir::transform::{MirMapPass, Pass}; + +use std::fs::File; + +pub struct MirPrint; + +impl Pass for MirPrint { + fn priority(&self) -> usize { + // This is a late pass, because we usually want to see the Mir post-passes. + !100 + } +} + +impl MirMapPass for MirPrint { + fn run_pass<'tcx>(&mut self, tcx: &ty::ctxt<'tcx>, map: &mut MirMap<'tcx>) { + let _task = tcx.map.dep_graph.in_task(DepNode::MirPrintPass); + for (node_id, mir) in &map.map { + for attr in tcx.map.attrs(*node_id) { + if !attr.check_name("rustc_mir") { + continue + } + for arg in attr.meta_item_list().iter().flat_map(|e| *e) { + if arg.check_name("graphviz") || arg.check_name("pretty") { + let filename = if let Some(p) = arg.value_str() { + p + } else { + tcx.sess.span_err(arg.span, + &format!("{} attribute requires a path", arg.name()) + ); + continue + }; + let result = File::create(&*filename).and_then(|ref mut output| { + if arg.check_name("graphviz") { + graphviz::write_mir_graphviz(&mir, output) + } else { + pretty::write_mir_pretty(&mir, output) + } + }); + + if let Err(e) = result { + tcx.sess.span_err(arg.span, + &format!("Error writing MIR {} output to `{}`: {}", + arg.name(), filename, e)); + } + } + } + } + } + } +} From bd4ddf38156ef9a9feee592c29c0fe31ab7a0d84 Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Tue, 16 Feb 2016 00:20:05 +0200 Subject: [PATCH 5/5] Address comments --- src/librustc/mir/transform.rs | 42 ++++++--------------- src/librustc_mir/transform/erase_regions.rs | 9 +---- src/librustc_mir/transform/print.rs | 8 +--- src/librustc_mir/transform/simplify_cfg.rs | 32 ++++------------ src/librustc_plugin/registry.rs | 4 +- 5 files changed, 25 insertions(+), 70 deletions(-) diff --git a/src/librustc/mir/transform.rs b/src/librustc/mir/transform.rs index df5e0d5f1a0db..89cbc2130e094 100644 --- a/src/librustc/mir/transform.rs +++ b/src/librustc/mir/transform.rs @@ -8,46 +8,27 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use mir::repr::{Mir, BasicBlockData, BasicBlock}; +use mir::repr::Mir; use mir::mir_map::MirMap; use middle::ty::ctxt; /// Contains various metadata about the pass. pub trait Pass { - /// Ordering of the pass. Lower value runs the pass earlier. - fn priority(&self) -> usize; // Possibly also `fn name()` and `fn should_run(Session)` etc. } /// Pass which inspects the whole MirMap. -pub trait MirMapPass: Pass { - fn run_pass<'tcx>(&mut self, tcx: &ctxt<'tcx>, map: &mut MirMap<'tcx>); +pub trait MirMapPass<'tcx>: Pass { + fn run_pass(&mut self, tcx: &ctxt<'tcx>, map: &mut MirMap<'tcx>); } /// Pass which only inspects MIR of distinct functions. -pub trait MirPass: Pass { - fn run_pass<'tcx>(&mut self, tcx: &ctxt<'tcx>, mir: &mut Mir<'tcx>); +pub trait MirPass<'tcx>: Pass { + fn run_pass(&mut self, tcx: &ctxt<'tcx>, mir: &mut Mir<'tcx>); } -/// Pass which only inspects basic blocks in MIR. -/// -/// Invariant: The blocks are considered to be fully self-contained for the purposes of this pass – -/// the pass may not change the list of successors of the block or apply any transformations to -/// blocks based on the information collected during earlier runs of the pass. -pub trait MirBlockPass: Pass { - fn run_pass<'tcx>(&mut self, tcx: &ctxt<'tcx>, bb: BasicBlock, data: &mut BasicBlockData<'tcx>); -} - -impl MirPass for T { - fn run_pass<'tcx>(&mut self, tcx: &ctxt<'tcx>, mir: &mut Mir<'tcx>) { - for (i, basic_block) in mir.basic_blocks.iter_mut().enumerate() { - MirBlockPass::run_pass(self, tcx, BasicBlock::new(i), basic_block); - } - } -} - -impl MirMapPass for T { - fn run_pass<'tcx>(&mut self, tcx: &ctxt<'tcx>, map: &mut MirMap<'tcx>) { +impl<'tcx, T: MirPass<'tcx>> MirMapPass<'tcx> for T { + fn run_pass(&mut self, tcx: &ctxt<'tcx>, map: &mut MirMap<'tcx>) { for (_, mir) in &mut map.map { MirPass::run_pass(self, tcx, mir); } @@ -56,7 +37,7 @@ impl MirMapPass for T { /// A manager for MIR passes. pub struct Passes { - passes: Vec> + passes: Vec MirMapPass<'tcx>>> } impl Passes { @@ -68,19 +49,18 @@ impl Passes { } pub fn run_passes<'tcx>(&mut self, tcx: &ctxt<'tcx>, map: &mut MirMap<'tcx>) { - self.passes.sort_by_key(|e| e.priority()); for pass in &mut self.passes { pass.run_pass(tcx, map); } } - pub fn push_pass(&mut self, pass: Box) { + pub fn push_pass(&mut self, pass: Box MirMapPass<'a>>) { self.passes.push(pass); } } -impl ::std::iter::Extend> for Passes { - fn extend>>(&mut self, it: I) { +impl ::std::iter::Extend MirMapPass<'a>>> for Passes { + fn extend MirMapPass<'a>>>>(&mut self, it: I) { self.passes.extend(it); } } diff --git a/src/librustc_mir/transform/erase_regions.rs b/src/librustc_mir/transform/erase_regions.rs index 72ea064880267..506e8189581ad 100644 --- a/src/librustc_mir/transform/erase_regions.rs +++ b/src/librustc_mir/transform/erase_regions.rs @@ -28,19 +28,14 @@ pub fn erase_regions<'tcx>(tcx: &ty::ctxt<'tcx>, mir_map: &mut MirMap<'tcx>) { pub struct EraseRegions; -impl MirPass for EraseRegions { - fn run_pass<'tcx>(&mut self, tcx: &ty::ctxt<'tcx>, mir: &mut Mir<'tcx>) { +impl<'tcx> MirPass<'tcx> for EraseRegions { + fn run_pass(&mut self, tcx: &ty::ctxt<'tcx>, mir: &mut Mir<'tcx>) { EraseRegionsVisitor::new(tcx).visit_mir(mir); } } impl Pass for EraseRegions { - fn priority(&self) -> usize { - // We want this pass to run as late as possible in transformation chain, so we give it a - // very high priority number. - !50 - } } struct EraseRegionsVisitor<'a, 'tcx: 'a> { diff --git a/src/librustc_mir/transform/print.rs b/src/librustc_mir/transform/print.rs index 8fdbcc14fe085..9e1f6939a44cf 100644 --- a/src/librustc_mir/transform/print.rs +++ b/src/librustc_mir/transform/print.rs @@ -28,14 +28,10 @@ use std::fs::File; pub struct MirPrint; impl Pass for MirPrint { - fn priority(&self) -> usize { - // This is a late pass, because we usually want to see the Mir post-passes. - !100 - } } -impl MirMapPass for MirPrint { - fn run_pass<'tcx>(&mut self, tcx: &ty::ctxt<'tcx>, map: &mut MirMap<'tcx>) { +impl<'tcx> MirMapPass<'tcx> for MirPrint { + fn run_pass(&mut self, tcx: &ty::ctxt<'tcx>, map: &mut MirMap<'tcx>) { let _task = tcx.map.dep_graph.in_task(DepNode::MirPrintPass); for (node_id, mir) in &map.map { for attr in tcx.map.attrs(*node_id) { diff --git a/src/librustc_mir/transform/simplify_cfg.rs b/src/librustc_mir/transform/simplify_cfg.rs index f87dd87555cea..a49a85dc4350f 100644 --- a/src/librustc_mir/transform/simplify_cfg.rs +++ b/src/librustc_mir/transform/simplify_cfg.rs @@ -18,14 +18,11 @@ use rustc::mir::transform::{MirPass, Pass}; pub struct SimplifyCfg; impl Pass for SimplifyCfg { - fn priority(&self) -> usize { - 50 - } } -impl MirPass for SimplifyCfg { - fn run_pass<'tcx>(&mut self, tcx: &ty::ctxt<'tcx>, mir: &mut Mir<'tcx>) { - let mut dbr_pass = DeadBlockRemoval::new(self); +impl<'tcx> MirPass<'tcx> for SimplifyCfg { + fn run_pass(&mut self, tcx: &ty::ctxt<'tcx>, mir: &mut Mir<'tcx>) { + let mut dbr_pass = DeadBlockRemoval; let mut changed = true; while changed { changed = self.simplify_branches(mir); @@ -119,22 +116,13 @@ impl SimplifyCfg { /// /// You want to schedule this pass just after any pass which might introduce unreachable blocks. /// This pass is very cheap and might improve the run-time of other not-so-cheap passes. -pub struct DeadBlockRemoval(usize); - -impl DeadBlockRemoval { - fn new(run_after: &Pass) -> DeadBlockRemoval { - DeadBlockRemoval(run_after.priority() + 1) - } -} +pub struct DeadBlockRemoval; impl Pass for DeadBlockRemoval { - fn priority(&self) -> usize { - self.0 - } } -impl MirPass for DeadBlockRemoval { - fn run_pass<'tcx>(&mut self, _: &ty::ctxt<'tcx>, mir: &mut Mir<'tcx>) { +impl<'tcx> MirPass<'tcx> for DeadBlockRemoval { + fn run_pass(&mut self, _: &ty::ctxt<'tcx>, mir: &mut Mir<'tcx>) { let mut seen = vec![false; mir.basic_blocks.len()]; // These blocks are always required. @@ -159,14 +147,10 @@ impl MirPass for DeadBlockRemoval { pub struct CompactMir; impl Pass for CompactMir { - fn priority(&self) -> usize { - // We want this pass to run very late, so we give it a very high priority number. - !10 - } } -impl MirPass for CompactMir { - fn run_pass<'tcx>(&mut self, _: &ty::ctxt<'tcx>, mir: &mut Mir<'tcx>) { +impl<'tcx> MirPass<'tcx> for CompactMir { + fn run_pass(&mut self, _: &ty::ctxt<'tcx>, mir: &mut Mir<'tcx>) { self.visit_mir(mir); } } diff --git a/src/librustc_plugin/registry.rs b/src/librustc_plugin/registry.rs index b07afb222a265..730504a6178ef 100644 --- a/src/librustc_plugin/registry.rs +++ b/src/librustc_plugin/registry.rs @@ -56,7 +56,7 @@ pub struct Registry<'a> { pub late_lint_passes: Vec, #[doc(hidden)] - pub mir_passes: Vec>, + pub mir_passes: Vec mir::transform::MirMapPass<'p>>>, #[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 mir::transform::MirMapPass<'p>>) { self.mir_passes.push(pass); }