From 2b32cb90c72d90c722d56324ca0ea9f748ebf4e1 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 27 Apr 2017 16:48:48 -0400 Subject: [PATCH] retool MIR passes completely The new setup is as follows. There is a pipeline of MIR passes that each run **per def-id** to optimize a particular function. You are intended to request MIR at whatever stage you need it. At the moment, there is only one stage you can request: - `optimized_mir(def_id)` This yields the final product. Internally, it pulls the MIR for the given def-id through a series of steps. Right now, these are still using an "interned ref-cell" but they are intended to "steal" from one another: - `mir_build` -- performs the initial construction for local MIR - `mir_pass_set` -- performs a suite of optimizations and transformations - `mir_pass` -- an individual optimization within a suite So, to construct the optimized MIR, we invoke: mir_pass_set((MIR_OPTIMIZED, def_id)) which will build up the final MIR. --- src/librustc/mir/transform.rs | 140 +++++++++---------- src/librustc/ty/maps.rs | 68 +++++++-- src/librustc/ty/mod.rs | 16 ++- src/librustc_driver/driver.rs | 19 --- src/librustc_metadata/cstore_impl.rs | 2 +- src/librustc_mir/lib.rs | 2 +- src/librustc_mir/mir_map.rs | 14 +- src/librustc_mir/transform/dump_mir.rs | 56 ++++---- src/librustc_mir/transform/inline.rs | 15 +- src/librustc_mir/transform/mod.rs | 106 ++++++++++++++ src/librustc_mir/transform/qualify_consts.rs | 42 +++--- src/librustc_mir/util/mod.rs | 2 +- src/librustc_mir/util/pretty.rs | 48 ++++--- 13 files changed, 345 insertions(+), 185 deletions(-) diff --git a/src/librustc/mir/transform.rs b/src/librustc/mir/transform.rs index 8ecfbfdb5c3ef..9fab6564d9b00 100644 --- a/src/librustc/mir/transform.rs +++ b/src/librustc/mir/transform.rs @@ -9,13 +9,13 @@ // except according to those terms. use hir; -use hir::def_id::{DefId, LOCAL_CRATE}; +use hir::def_id::DefId; use hir::map::DefPathData; use mir::{Mir, Promoted}; use ty::TyCtxt; +use std::cell::{Ref, RefCell}; use std::rc::Rc; use syntax::ast::NodeId; -use util::common::time; use std::borrow::Cow; @@ -90,12 +90,37 @@ pub fn default_name() -> Cow<'static, str> { } } +/// Gives you access to various bits of state during your MIR pass. +pub trait MirCtxt<'a, 'tcx: 'a> { + fn tcx(&self) -> TyCtxt<'a, 'tcx, 'tcx>; + fn def_id(&self) -> DefId; + fn pass_set(&self) -> MirPassSet; + fn pass_num(&self) -> MirPassIndex; + fn source(&self) -> MirSource; + fn read_previous_mir(&self) -> Ref<'tcx, Mir<'tcx>>; + fn steal_previous_mir(&self) -> &'tcx RefCell>; +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct MirPassSet(pub usize); + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct MirPassIndex(pub usize); + +/// A pass hook is invoked both before and after each pass executes. +/// This is primarily used to dump MIR for debugging. +/// +/// You can tell whether this is before or after by inspecting the +/// `mir` parameter -- before the pass executes, it will be `None` (in +/// which case you can inspect the MIR from previous pass by executing +/// `mir_cx.read_previous_mir()`); after the pass executes, it will be +/// `Some()` with the result of the pass (in which case the output +/// from the previous pass is most likely stolen, so you would not +/// want to try and access it). pub trait PassHook { - fn on_mir_pass<'a, 'tcx>(&self, - tcx: TyCtxt<'a, 'tcx, 'tcx>, - pass_name: &str, - pass_num: usize, - is_after: bool); + fn on_mir_pass<'a, 'tcx: 'a>(&self, + mir_cx: &MirCtxt<'a, 'tcx>, + mir: Option<&Mir<'tcx>>); } /// A streamlined trait that you can implement to create a pass; the @@ -107,21 +132,7 @@ pub trait DefIdPass { default_name::() } - fn run_pass<'a, 'tcx>(&self, - tcx: TyCtxt<'a, 'tcx, 'tcx>, - def_id: DefId); -} - -impl Pass for T { - fn name<'a>(&'a self) -> Cow<'a, str> { - DefIdPass::name(self) - } - - fn run_pass<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) { - for &def_id in tcx.mir_keys(LOCAL_CRATE).iter() { - DefIdPass::run_pass(self, tcx, def_id); - } - } + fn run_pass<'a, 'tcx: 'a>(&self, mir_cx: &MirCtxt<'a, 'tcx>) -> &'tcx RefCell>; } /// A streamlined trait that you can implement to create a pass; the @@ -138,29 +149,24 @@ pub trait MirPass: DepGraphSafe { mir: &mut Mir<'tcx>); } -fn for_each_assoc_mir<'a, 'tcx, OP>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - def_id: DefId, - mut op: OP) - where OP: FnMut(MirSource, &mut Mir<'tcx>) -{ - let id = tcx.hir.as_local_node_id(def_id).expect("mir source requires local def-id"); - let source = MirSource::from_node(tcx, id); - let mir = &mut tcx.mir(def_id).borrow_mut(); - op(source, mir); - - for (promoted_index, promoted_mir) in mir.promoted.iter_enumerated_mut() { - let promoted_source = MirSource::Promoted(id, promoted_index); - op(promoted_source, promoted_mir); - } -} - impl DefIdPass for T { fn name<'a>(&'a self) -> Cow<'a, str> { MirPass::name(self) } - fn run_pass<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) { - for_each_assoc_mir(tcx, def_id, |src, mir| MirPass::run_pass(self, tcx, src, mir)); + fn run_pass<'a, 'tcx: 'a>(&self, mir_cx: &MirCtxt<'a, 'tcx>) -> &'tcx RefCell> { + let tcx = mir_cx.tcx(); + let source = mir_cx.source(); + let mir = mir_cx.steal_previous_mir(); + MirPass::run_pass(self, tcx, source, &mut mir.borrow_mut()); + + let item_id = source.item_id(); + for (promoted_index, promoted_mir) in mir.borrow_mut().promoted.iter_enumerated_mut() { + let promoted_source = MirSource::Promoted(item_id, promoted_index); + MirPass::run_pass(self, tcx, promoted_source, promoted_mir); + } + + mir } } @@ -168,12 +174,7 @@ impl DefIdPass for T { #[derive(Clone)] pub struct Passes { pass_hooks: Vec>, - sets: Vec, -} - -#[derive(Clone)] -struct PassSet { - passes: Vec>, + sets: Vec>>, } /// The number of "pass sets" that we have: @@ -184,52 +185,41 @@ struct PassSet { pub const MIR_PASS_SETS: usize = 3; /// Run the passes we need to do constant qualification and evaluation. -pub const MIR_CONST: usize = 0; +pub const MIR_CONST: MirPassSet = MirPassSet(0); /// Run the passes we need to consider the MIR validated and ready for borrowck etc. -pub const MIR_VALIDATED: usize = 1; +pub const MIR_VALIDATED: MirPassSet = MirPassSet(1); /// Run the passes we need to consider the MIR *optimized*. -pub const MIR_OPTIMIZED: usize = 2; +pub const MIR_OPTIMIZED: MirPassSet = MirPassSet(2); impl<'a, 'tcx> Passes { pub fn new() -> Passes { Passes { pass_hooks: Vec::new(), - sets: (0..MIR_PASS_SETS).map(|_| PassSet { passes: Vec::new() }).collect(), - } - } - - pub fn run_passes(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, set_index: usize) { - let set = &self.sets[set_index]; - - let start_num: usize = self.sets[..set_index].iter().map(|s| s.passes.len()).sum(); - - // NB: passes are numbered from 1, since "construction" is zero. - for (pass, pass_num) in set.passes.iter().zip(start_num + 1..) { - for hook in &self.pass_hooks { - hook.on_mir_pass(tcx, &pass.name(), pass_num, false); - } - - time(tcx.sess.time_passes(), &*pass.name(), || { - for &def_id in tcx.mir_keys(LOCAL_CRATE).iter() { - pass.run_pass(tcx, def_id); - } - }); - - for hook in &self.pass_hooks { - hook.on_mir_pass(tcx, &pass.name(), pass_num, true); - } + sets: (0..MIR_PASS_SETS).map(|_| Vec::new()).collect(), } } /// Pushes a built-in pass. - pub fn push_pass(&mut self, set: usize, pass: T) { - self.sets[set].passes.push(Rc::new(pass)); + pub fn push_pass(&mut self, set: MirPassSet, pass: T) { + self.sets[set.0].push(Rc::new(pass)); } /// Pushes a pass hook. pub fn push_hook(&mut self, hook: T) { self.pass_hooks.push(Rc::new(hook)); } + + pub fn len_passes(&self, set: MirPassSet) -> usize { + self.sets[set.0].len() + } + + pub fn pass(&self, set: MirPassSet, pass: MirPassIndex) -> &DefIdPass { + &*self.sets[set.0][pass.0] + } + + pub fn hooks(&self) -> &[Rc] { + &self.pass_hooks + } } diff --git a/src/librustc/ty/maps.rs b/src/librustc/ty/maps.rs index a9deb52efb68a..dc70dcc81162f 100644 --- a/src/librustc/ty/maps.rs +++ b/src/librustc/ty/maps.rs @@ -16,6 +16,7 @@ use middle::const_val; use middle::privacy::AccessLevels; use middle::region::RegionMaps; use mir; +use mir::transform::{MirPassSet, MirPassIndex}; use session::CompileResult; use ty::{self, CrateInherentImpls, Ty, TyCtxt}; use ty::item_path; @@ -101,6 +102,24 @@ impl<'tcx> Key for (DefId, &'tcx Substs<'tcx>) { } } +impl Key for (MirPassSet, DefId) { + fn map_crate(&self) -> CrateNum { + self.1.map_crate() + } + fn default_span(&self, tcx: TyCtxt) -> Span { + self.1.default_span(tcx) + } +} + +impl Key for (MirPassSet, MirPassIndex, DefId) { + fn map_crate(&self) -> CrateNum { + self.2.map_crate() + } + fn default_span(&self, tcx: TyCtxt) -> Span { + self.2.default_span(tcx) + } +} + trait Value<'tcx>: Sized { fn from_cycle_error<'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Self; } @@ -318,6 +337,18 @@ impl<'tcx> QueryDescription for queries::is_item_mir_available<'tcx> { } } +impl<'tcx> QueryDescription for queries::mir_pass_set<'tcx> { + fn describe(_: TyCtxt, (pass_set, _): (MirPassSet, DefId)) -> String { + format!("MIR passes #{}.*", pass_set.0) + } +} + +impl<'tcx> QueryDescription for queries::mir_pass<'tcx> { + fn describe(_: TyCtxt, (pass_set, pass_num, _): (MirPassSet, MirPassIndex, DefId)) -> String { + format!("MIR pass #{}.{}", pass_set.0, pass_num.0) + } +} + macro_rules! define_maps { (<$tcx:tt> $($(#[$attr:meta])* @@ -542,15 +573,6 @@ define_maps! { <'tcx> /// Methods in these implementations don't need to be exported. [] inherent_impls: InherentImpls(DefId) -> Rc>, - /// Maps from the def-id of a function/method or const/static - /// to its MIR. Mutation is done at an item granularity to - /// allow MIR optimization passes to function and still - /// access cross-crate MIR (e.g. inlining or const eval). - /// - /// Note that cross-crate MIR appears to be always borrowed - /// (in the `RefCell` sense) to prevent accidental mutation. - [] mir: Mir(DefId) -> &'tcx RefCell>, - /// Set of all the def-ids in this crate that have MIR associated with /// them. This includes all the body owners, but also things like struct /// constructors. @@ -561,6 +583,26 @@ define_maps! { <'tcx> /// the value isn't known except to the pass itself. [] mir_const_qualif: Mir(DefId) -> u8, + /// Performs the initial MIR construction. You almost certainly do not + /// want to use this query, because its output is intended to be stolen + /// immediately by the MIR passes below. Consider `optimized_mir` instead. + [] mir_build: Mir(DefId) -> &'tcx RefCell>, + + /// Fetch the MIR for a given def-id after the given set of passes has ben + /// applied to it. This is mostly an "intermediate" query. Normally, you would + /// prefer to use `optimized_mir(def_id)`, which will fetch the MIR after all + /// optimizations and so forth. + [] mir_pass_set: mir_pass_set((MirPassSet, DefId)) -> &'tcx RefCell>, + + /// Fetch the MIR for a given def-id after a given pass has been executed. This is + /// **only** intended to be used by the `mir_pass_set` provider -- if you are using it + /// manually, you're doing it wrong. + [] mir_pass: mir_pass((MirPassSet, MirPassIndex, DefId)) -> &'tcx RefCell>, + + /// MIR after our optimization passes have run. This is MIR that is ready + /// for trans. This is also the only query that can fetch non-local MIR, at present. + [] optimized_mir: Mir(DefId) -> &'tcx RefCell>, + /// Records the type of each closure. The def ID is the ID of the /// expression defining the closure. [] closure_kind: ItemSignature(DefId) -> ty::ClosureKind, @@ -658,3 +700,11 @@ fn const_eval_dep_node((def_id, _): (DefId, &Substs)) -> DepNode { fn mir_keys(_: CrateNum) -> DepNode { DepNode::MirKeys } + +fn mir_pass_set((_pass_set, def_id): (MirPassSet, DefId)) -> DepNode { + DepNode::Mir(def_id) +} + +fn mir_pass((_pass_set, _pass_num, def_id): (MirPassSet, MirPassIndex, DefId)) -> DepNode { + DepNode::Mir(def_id) +} diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 5e5dbdf20c04c..cf66c83800d36 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -2323,9 +2323,9 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { } } - /// Given the did of an item, returns its MIR, borrowed immutably. + /// Given the did of an item, returns its (optimized) MIR, borrowed immutably. pub fn item_mir(self, did: DefId) -> Ref<'gcx, Mir<'gcx>> { - self.mir(did).borrow() + self.optimized_mir(did).borrow() } /// Return the possibly-auto-generated MIR of a (DefId, Subst) pair. @@ -2333,8 +2333,16 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { -> Ref<'gcx, Mir<'gcx>> { match instance { - ty::InstanceDef::Item(did) if true => self.item_mir(did), - _ => self.mir_shims(instance).borrow(), + ty::InstanceDef::Item(did) => { + self.item_mir(did) + } + ty::InstanceDef::Intrinsic(..) | + ty::InstanceDef::FnPtrShim(..) | + ty::InstanceDef::Virtual(..) | + ty::InstanceDef::ClosureOnceShim { .. } | + ty::InstanceDef::DropGlue(..) => { + self.mir_shims(instance).borrow() + } } } diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 4c984559428bf..9b11d168e0077 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -1005,11 +1005,6 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, mir_stats::print_mir_stats(tcx, "PRE CLEANUP MIR STATS"); } - time(time_passes, "MIR cleanup and validation", || { - tcx.mir_passes.run_passes(tcx, MIR_CONST); - tcx.mir_passes.run_passes(tcx, MIR_VALIDATED); - }); - time(time_passes, "borrow checking", || borrowck::check_crate(tcx)); @@ -1058,20 +1053,6 @@ pub fn phase_4_translate_to_llvm<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, "resolving dependency formats", || dependency_format::calculate(&tcx.sess)); - if tcx.sess.opts.debugging_opts.mir_stats { - mir_stats::print_mir_stats(tcx, "PRE OPTIMISATION MIR STATS"); - } - - // Run the passes that transform the MIR into a more suitable form for translation to LLVM - // code. - time(time_passes, "MIR optimisations", || { - tcx.mir_passes.run_passes(tcx, MIR_OPTIMIZED); - }); - - if tcx.sess.opts.debugging_opts.mir_stats { - mir_stats::print_mir_stats(tcx, "POST OPTIMISATION MIR STATS"); - } - let translation = time(time_passes, "translation", diff --git a/src/librustc_metadata/cstore_impl.rs b/src/librustc_metadata/cstore_impl.rs index f5a8accea2803..4ecce3cc132fe 100644 --- a/src/librustc_metadata/cstore_impl.rs +++ b/src/librustc_metadata/cstore_impl.rs @@ -95,7 +95,7 @@ provide! { <'tcx> tcx, def_id, cdata bug!("coerce_unsized_info: `{:?}` is missing its info", def_id); }) } - mir => { + optimized_mir => { let mir = cdata.maybe_get_item_mir(tcx, def_id.index).unwrap_or_else(|| { bug!("get_item_mir: missing MIR for `{:?}`", def_id) }); diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index 8b55cdf06d208..cf6a50563a08c 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -59,5 +59,5 @@ use rustc::ty::maps::Providers; pub fn provide(providers: &mut Providers) { mir_map::provide(providers); shim::provide(providers); - transform::qualify_consts::provide(providers); + transform::provide(providers); } diff --git a/src/librustc_mir/mir_map.rs b/src/librustc_mir/mir_map.rs index 6aa7089e7a906..46f7c34c06e8d 100644 --- a/src/librustc_mir/mir_map.rs +++ b/src/librustc_mir/mir_map.rs @@ -52,8 +52,11 @@ pub fn build_mir_for_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { } pub fn provide(providers: &mut Providers) { - providers.mir = build_mir; - providers.mir_keys = mir_keys; + *providers = Providers { + mir_build, + mir_keys, + ..*providers + }; } fn mir_keys<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, krate: CrateNum) @@ -95,8 +98,7 @@ fn mir_keys<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, krate: CrateNum) Rc::new(set) } -fn build_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) - -> &'tcx RefCell> { +fn mir_build<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx RefCell> { let id = tcx.hir.as_local_node_id(def_id).unwrap(); let unsupported = || { span_bug!(tcx.hir.span(id), "can't build MIR for {:?}", def_id); @@ -192,7 +194,7 @@ fn build_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) mem::transmute::>(mir) }; - mir_util::dump_mir(tcx, 0, "mir_map", &0, src, &mir); + mir_util::dump_mir(tcx, None, "mir_map", &0, src, &mir); tcx.alloc_mir(mir) }) @@ -251,7 +253,7 @@ fn create_constructor_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, mem::transmute::>(mir) }; - mir_util::dump_mir(tcx, 0, "mir_map", &0, src, &mir); + mir_util::dump_mir(tcx, None, "mir_map", &0, src, &mir); tcx.alloc_mir(mir) }) diff --git a/src/librustc_mir/transform/dump_mir.rs b/src/librustc_mir/transform/dump_mir.rs index 62b480494c3ec..a76ba8a8b688c 100644 --- a/src/librustc_mir/transform/dump_mir.rs +++ b/src/librustc_mir/transform/dump_mir.rs @@ -11,14 +11,15 @@ //! This pass just dumps MIR at a specified point. use std::borrow::Cow; +use std::cell::RefCell; use std::fmt; use std::fs::File; use std::io; -use rustc::hir::def_id::{DefId, LOCAL_CRATE}; use rustc::session::config::{OutputFilenames, OutputType}; use rustc::ty::TyCtxt; -use rustc::mir::transform::{DefIdPass, Pass, PassHook, MirSource}; +use rustc::mir::Mir; +use rustc::mir::transform::{DefIdPass, PassHook, MirCtxt}; use util as mir_util; pub struct Marker(pub &'static str); @@ -28,8 +29,8 @@ impl DefIdPass for Marker { Cow::Borrowed(self.0) } - fn run_pass<'a, 'tcx>(&self, _: TyCtxt<'a, 'tcx, 'tcx>, _: DefId) { - // no-op + fn run_pass<'a, 'tcx: 'a>(&self, mir_cx: &MirCtxt<'a, 'tcx>) -> &'tcx RefCell> { + mir_cx.steal_previous_mir() } } @@ -47,30 +48,31 @@ impl fmt::Display for Disambiguator { pub struct DumpMir; impl PassHook for DumpMir { - fn on_mir_pass<'a, 'tcx>( - &self, - tcx: TyCtxt<'a, 'tcx, 'tcx>, - pass_name: &str, - pass_num: usize, - is_after: bool) + fn on_mir_pass<'a, 'tcx: 'a>(&self, + mir_cx: &MirCtxt<'a, 'tcx>, + mir: Option<&Mir<'tcx>>) { - // No dump filters enabled. - if tcx.sess.opts.debugging_opts.dump_mir.is_none() { - return; - } - - for &def_id in tcx.mir_keys(LOCAL_CRATE).iter() { - let id = tcx.hir.as_local_node_id(def_id).unwrap(); - let source = MirSource::from_node(tcx, id); - let mir = tcx.item_mir(def_id); - mir_util::dump_mir( - tcx, - pass_num, - &pass_name, - &Disambiguator { is_after }, - source, - &mir - ); + let tcx = mir_cx.tcx(); + let pass_set = mir_cx.pass_set(); + let pass_num = mir_cx.pass_num(); + let pass = tcx.mir_passes.pass(pass_set, pass_num); + let name = &pass.name(); + let source = mir_cx.source(); + if mir_util::dump_enabled(tcx, name, source) { + let previous_mir; + let mir_to_dump = match mir { + Some(m) => m, + None => { + previous_mir = mir_cx.read_previous_mir(); + &*previous_mir + } + }; + mir_util::dump_mir(tcx, + Some((pass_set, pass_num)), + name, + &Disambiguator { is_after: mir.is_some() }, + source, + mir_to_dump); } } } diff --git a/src/librustc_mir/transform/inline.rs b/src/librustc_mir/transform/inline.rs index b0a067063dd60..6eda2f5abb9d8 100644 --- a/src/librustc_mir/transform/inline.rs +++ b/src/librustc_mir/transform/inline.rs @@ -27,6 +27,7 @@ use rustc::util::nodemap::{DefIdSet}; use super::simplify::{remove_dead_blocks, CfgSimplifier}; +use std::cell::{Ref, RefCell}; use syntax::{attr}; use syntax::abi::Abi; @@ -74,6 +75,14 @@ struct CallSite<'tcx> { } impl<'a, 'tcx> Inliner<'a, 'tcx> { + fn maybe_item_mir(&mut self, _def_id: DefId) -> Option>> { + panic!() // TODO -- hook up inline into the system + } + + fn mir(&mut self, _def_id: DefId) -> &'tcx RefCell> { + panic!() // TODO -- hook up inline into the system + } + fn inline_scc(&mut self, callgraph: &callgraph::CallGraph, scc: &[graph::NodeIndex]) -> bool { let mut callsites = Vec::new(); let mut in_scc = DefIdSet(); @@ -146,7 +155,7 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> { self.tcx.dep_graph.write(DepNode::Mir(callsite.caller)); let callee_mir = { - if let Some(callee_mir) = self.tcx.maybe_item_mir(callsite.callee) { + if let Some(callee_mir) = self.maybe_item_mir(callsite.callee) { if !self.should_inline(callsite, &callee_mir) { continue; } @@ -158,7 +167,7 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> { }; - let mut caller_mir = self.tcx.mir(callsite.caller).borrow_mut(); + let mut caller_mir = self.mir(callsite.caller).borrow_mut(); let start = caller_mir.basic_blocks().len(); @@ -210,7 +219,7 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> { let _task = self.tcx.dep_graph.in_task(DepNode::Mir(def_id)); self.tcx.dep_graph.write(DepNode::Mir(def_id)); - let mut caller_mir = self.tcx.mir(def_id).borrow_mut(); + let mut caller_mir = self.mir(def_id).borrow_mut(); debug!("Running simplify cfg on {:?}", def_id); CfgSimplifier::new(&mut caller_mir).simplify(); diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index cbd054a72499b..68070ded12d98 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -8,6 +8,14 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use rustc::hir::def_id::DefId; +use rustc::mir::Mir; +use rustc::mir::transform::{MirCtxt, MirPassIndex, MirPassSet, MirSource, MIR_OPTIMIZED}; +use rustc::ty::TyCtxt; +use rustc::ty::maps::Providers; +use std::cell::{Ref, RefCell}; +use std::mem; + pub mod simplify_branches; pub mod simplify; pub mod erase_regions; @@ -21,3 +29,101 @@ pub mod deaggregator; pub mod instcombine; pub mod copy_prop; pub mod inline; + +pub fn provide(providers: &mut Providers) { + self::qualify_consts::provide(providers); + *providers = Providers { + optimized_mir, + mir_pass_set, + mir_pass, + ..*providers + }; +} + +fn optimized_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx RefCell> { + let mir = tcx.mir_pass_set((MIR_OPTIMIZED, def_id)); + + // "lock" the ref cell into read mode; after this point, + // there ought to be no more changes to the MIR. + mem::drop(mir.borrow()); + + mir +} + +fn mir_pass_set<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + (pass_set, def_id): (MirPassSet, DefId)) + -> &'tcx RefCell> +{ + let passes = &tcx.mir_passes; + let len = passes.len_passes(pass_set); + assert!(len > 0, "no passes in {:?}", pass_set); + tcx.mir_pass((pass_set, MirPassIndex(len - 1), def_id)) +} + +fn mir_pass<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + (pass_set, pass_num, def_id): (MirPassSet, MirPassIndex, DefId)) + -> &'tcx RefCell> +{ + let passes = &tcx.mir_passes; + let pass = passes.pass(pass_set, pass_num); + let mir_ctxt = MirCtxtImpl { tcx, pass_num, pass_set, def_id }; + + for hook in passes.hooks() { + hook.on_mir_pass(&mir_ctxt, None); + } + + let mir = pass.run_pass(&mir_ctxt); + + for hook in passes.hooks() { + hook.on_mir_pass(&mir_ctxt, Some(&mir.borrow())); + } + + mir +} + +struct MirCtxtImpl<'a, 'tcx: 'a> { + tcx: TyCtxt<'a, 'tcx, 'tcx>, + pass_num: MirPassIndex, + pass_set: MirPassSet, + def_id: DefId +} + +impl<'a, 'tcx> MirCtxt<'a, 'tcx> for MirCtxtImpl<'a, 'tcx> { + fn tcx(&self) -> TyCtxt<'a, 'tcx, 'tcx> { + self.tcx + } + + fn pass_set(&self) -> MirPassSet { + self.pass_set + } + + fn pass_num(&self) -> MirPassIndex { + self.pass_num + } + + fn def_id(&self) -> DefId { + self.def_id + } + + fn source(&self) -> MirSource { + let id = self.tcx.hir.as_local_node_id(self.def_id) + .expect("mir source requires local def-id"); + MirSource::from_node(self.tcx, id) + } + + fn read_previous_mir(&self) -> Ref<'tcx, Mir<'tcx>> { + self.steal_previous_mir().borrow() + } + + fn steal_previous_mir(&self) -> &'tcx RefCell> { + let MirPassSet(pass_set) = self.pass_set; + let MirPassIndex(pass_num) = self.pass_num; + if pass_num > 0 { + self.tcx.mir_pass((MirPassSet(pass_set), MirPassIndex(pass_num - 1), self.def_id)) + } else if pass_set > 0 { + self.tcx.mir_pass_set((MirPassSet(pass_set - 1), self.def_id)) + } else { + self.tcx.mir_build(self.def_id) + } + } +} diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index 80ec83fcaefb0..cb002acf009d1 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -16,7 +16,6 @@ use rustc_data_structures::bitvec::BitVector; use rustc_data_structures::indexed_vec::{IndexVec, Idx}; -use rustc::dep_graph::DepNode; use rustc::hir; use rustc::hir::map as hir_map; use rustc::hir::def_id::DefId; @@ -27,13 +26,14 @@ use rustc::ty::cast::CastTy; use rustc::ty::maps::Providers; use rustc::mir::*; use rustc::mir::traversal::ReversePostorder; -use rustc::mir::transform::{DefIdPass, MirSource}; +use rustc::mir::transform::{DefIdPass, MirCtxt, MirSource, MIR_CONST}; use rustc::mir::visit::{LvalueContext, Visitor}; use rustc::middle::lang_items; use syntax::abi::Abi; use syntax::feature_gate::UnstableFeatures; use syntax_pos::{Span, DUMMY_SP}; +use std::cell::RefCell; use std::fmt; use std::usize; @@ -925,7 +925,7 @@ pub fn provide(providers: &mut Providers) { fn qualify_const_item<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> u8 { - let mir = &tcx.item_mir(def_id); + let mir = &tcx.mir_pass_set((MIR_CONST, def_id)).borrow(); if mir.return_ty.references_error() { return Qualif::NOT_CONST.bits(); } @@ -940,30 +940,32 @@ fn qualify_const_item<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, pub struct QualifyAndPromoteConstants; impl DefIdPass for QualifyAndPromoteConstants { - fn run_pass<'a, 'tcx>(&self, - tcx: TyCtxt<'a, 'tcx, 'tcx>, - def_id: DefId) - { - let _task = tcx.dep_graph.in_task(DepNode::Mir(def_id)); - let id = tcx.hir.as_local_node_id(def_id).unwrap(); - let src = MirSource::from_node(tcx, id); - - if let MirSource::Const(_) = src { - tcx.mir_const_qualif(def_id); - return; - } - - let mir = &mut tcx.mir(def_id).borrow_mut(); - tcx.dep_graph.write(DepNode::Mir(def_id)); + fn run_pass<'a, 'tcx: 'a>(&self, mir_cx: &MirCtxt<'a, 'tcx>) -> &'tcx RefCell> { + let tcx = mir_cx.tcx(); + match mir_cx.source() { + MirSource::Const(_) => { + // Ensure that we compute the `mir_const_qualif` for + // constants at this point, before we do any further + // optimization (and before we steal the previous + // MIR). + tcx.mir_const_qualif(mir_cx.def_id()); + mir_cx.steal_previous_mir() + } - self.run_pass(tcx, src, mir); + src => { + let mir = mir_cx.steal_previous_mir(); + self.run_pass(tcx, src, &mut mir.borrow_mut()); + mir + } + } } } impl<'a, 'tcx> QualifyAndPromoteConstants { fn run_pass(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, - src: MirSource, mir: &mut Mir<'tcx>) { + src: MirSource, + mir: &mut Mir<'tcx>) { let id = src.item_id(); let def_id = tcx.hir.local_def_id(id); let mode = match src { diff --git a/src/librustc_mir/util/mod.rs b/src/librustc_mir/util/mod.rs index cafc5bca76acd..4386bab38c039 100644 --- a/src/librustc_mir/util/mod.rs +++ b/src/librustc_mir/util/mod.rs @@ -15,6 +15,6 @@ pub mod patch; mod graphviz; mod pretty; -pub use self::pretty::{dump_mir, write_mir_pretty}; +pub use self::pretty::{dump_enabled, dump_mir, write_mir_pretty}; pub use self::graphviz::{write_mir_graphviz}; pub use self::graphviz::write_node_label as write_graphviz_node_label; diff --git a/src/librustc_mir/util/pretty.rs b/src/librustc_mir/util/pretty.rs index 6c637f2b2a9d6..e8fc70b74bcb7 100644 --- a/src/librustc_mir/util/pretty.rs +++ b/src/librustc_mir/util/pretty.rs @@ -11,7 +11,7 @@ use rustc::hir; use rustc::hir::def_id::{DefId, LOCAL_CRATE}; use rustc::mir::*; -use rustc::mir::transform::MirSource; +use rustc::mir::transform::{MirPassSet, MirPassIndex, MirSource}; use rustc::ty::TyCtxt; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::indexed_vec::{Idx}; @@ -39,29 +39,18 @@ const ALIGN: usize = 40; /// that can appear in the pass-name or the `item_path_str` for the given /// node-id. If any one of the substrings match, the data is dumped out. pub fn dump_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - pass_num: usize, + pass_num: Option<(MirPassSet, MirPassIndex)>, pass_name: &str, disambiguator: &Display, source: MirSource, mir: &Mir<'tcx>) { - let filters = match tcx.sess.opts.debugging_opts.dump_mir { - None => return, - Some(ref filters) => filters, - }; - let node_id = source.item_id(); - let node_path = tcx.item_path_str(tcx.hir.local_def_id(node_id)); - let is_matched = - filters.split("&") - .any(|filter| { - filter == "all" || - pass_name.contains(filter) || - node_path.contains(filter) - }); - if !is_matched { + if !dump_enabled(tcx, pass_name, source) { return; } - dump_matched_mir_node(tcx, pass_num, pass_name, &node_path, disambiguator, source, mir); + let node_path = tcx.item_path_str(tcx.hir.local_def_id(source.item_id())); + dump_matched_mir_node(tcx, pass_num, pass_name, &node_path, + disambiguator, source, mir); for (index, promoted_mir) in mir.promoted.iter_enumerated() { let promoted_source = MirSource::Promoted(source.item_id(), index); dump_matched_mir_node(tcx, pass_num, pass_name, &node_path, disambiguator, @@ -69,8 +58,26 @@ pub fn dump_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } } +pub fn dump_enabled<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + pass_name: &str, + source: MirSource) + -> bool { + let filters = match tcx.sess.opts.debugging_opts.dump_mir { + None => return false, + Some(ref filters) => filters, + }; + let node_id = source.item_id(); + let node_path = tcx.item_path_str(tcx.hir.local_def_id(node_id)); + filters.split("&") + .any(|filter| { + filter == "all" || + pass_name.contains(filter) || + node_path.contains(filter) + }) +} + fn dump_matched_mir_node<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - pass_num: usize, + pass_num: Option<(MirPassSet, MirPassIndex)>, pass_name: &str, node_path: &str, disambiguator: &Display, @@ -84,7 +91,10 @@ fn dump_matched_mir_node<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let pass_num = if tcx.sess.opts.debugging_opts.dump_mir_exclude_pass_number { format!("") } else { - format!(".{:03}", pass_num) + match pass_num { + None => format!(".-------"), + Some((pass_set, pass_num)) => format!(".{:03}-{:03}", pass_set.0, pass_num.0), + } }; let mut file_path = PathBuf::new();