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/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/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/mir/transform.rs b/src/librustc/mir/transform.rs index 0f18d8d282144..89cbc2130e094 100644 --- a/src/librustc/mir/transform.rs +++ b/src/librustc/mir/transform.rs @@ -9,8 +9,58 @@ // except according to those terms. use mir::repr::Mir; +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 { + // Possibly also `fn name()` and `fn should_run(Session)` etc. +} + +/// Pass which inspects the whole MirMap. +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<'tcx>: Pass { + fn run_pass(&mut self, tcx: &ctxt<'tcx>, mir: &mut Mir<'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); + } + } +} + +/// A manager for MIR passes. +pub struct Passes { + passes: Vec MirMapPass<'tcx>>> +} + +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>) { + for pass in &mut self.passes { + pass.run_pass(tcx, map); + } + } + + pub fn push_pass(&mut self, pass: Box MirMapPass<'a>>) { + self.passes.push(pass); + } +} + +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/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..b3e69132dea65 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,20 @@ 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); + 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); + }); time(time_passes, "liveness checking", @@ -907,10 +916,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 +927,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 0e641bf679d11..c2c19a4dcb5bc 100644 --- a/src/librustc_mir/mir_map.rs +++ b/src/librustc_mir/mir_map.rs @@ -8,27 +8,16 @@ // 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 transform::simplify_cfg; 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; @@ -135,57 +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(mut mir) => { - simplify_cfg::SimplifyCfg::new().run_on_mir(&mut mir, self.tcx); - - 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 b927ab489f100..506e8189581ad 100644 --- a/src/librustc_mir/transform/erase_regions.rs +++ b/src/librustc_mir/transform/erase_regions.rs @@ -16,18 +16,28 @@ 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<'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 { +} + struct EraseRegionsVisitor<'a, 'tcx: 'a> { tcx: &'a ty::ctxt<'tcx>, } @@ -58,12 +68,6 @@ 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> MutVisitor<'tcx> for EraseRegionsVisitor<'a, 'tcx> { fn visit_mir(&mut self, mir: &mut Mir<'tcx>) { self.erase_regions_return_ty(&mut mir.return_ty); 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..9e1f6939a44cf --- /dev/null +++ b/src/librustc_mir/transform/print.rs @@ -0,0 +1,69 @@ +// 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 { +} + +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) { + 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)); + } + } + } + } + } + } +} diff --git a/src/librustc_mir/transform/simplify_cfg.rs b/src/librustc_mir/transform/simplify_cfg.rs index 17c5b5f7c3cff..a49a85dc4350f 100644 --- a/src/librustc_mir/transform/simplify_cfg.rs +++ b/src/librustc_mir/transform/simplify_cfg.rs @@ -9,37 +9,30 @@ // except according to those terms. 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; +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()]; - - // These blocks are always required. - seen[START_BLOCK.index()] = true; - seen[END_BLOCK.index()] = true; +impl Pass for SimplifyCfg { +} - 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<'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); + 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 @@ -83,6 +76,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; @@ -118,15 +112,81 @@ impl SimplifyCfg { } } -impl MirPass for SimplifyCfg { - fn run_on_mir<'tcx>(&mut self, mir: &mut Mir<'tcx>, _: &::rustc::middle::ty::ctxt<'tcx>) { - let mut changed = true; - while changed { - changed = self.simplify_branches(mir); - changed |= self.remove_goto_chains(mir); - self.remove_dead_blocks(mir); +/// 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; + +impl Pass for DeadBlockRemoval { +} + +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. + 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 + + util::retain_basic_blocks(mir, &seen); + } +} + +/// Reduce the memory allocated by a MIR graph. +pub struct CompactMir; + +impl Pass for CompactMir { +} + +impl<'tcx> MirPass<'tcx> for CompactMir { + fn run_pass(&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); } } diff --git a/src/librustc_plugin/registry.rs b/src/librustc_plugin/registry.rs index a51fd58db88dd..730504a6178ef 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 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); } 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) } }