From a26e966307226a62ad1e09247038182eb53053ab Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 28 Apr 2017 19:29:16 -0400 Subject: [PATCH] convert the `inline` pass to use the new multi result This involves changing various details about that system, though the basic shape remains the same. --- src/librustc/mir/transform.rs | 72 +++++++++---- src/librustc/ty/maps.rs | 96 +++++++++++------ src/librustc_driver/driver.rs | 2 +- src/librustc_mir/callgraph.rs | 13 +-- src/librustc_mir/transform/dump_mir.rs | 20 ++-- src/librustc_mir/transform/inline.rs | 90 ++++++++-------- src/librustc_mir/transform/interprocedural.rs | 100 ++++++++++++++++++ src/librustc_mir/transform/mod.rs | 35 ++++-- 8 files changed, 311 insertions(+), 117 deletions(-) create mode 100644 src/librustc_mir/transform/interprocedural.rs diff --git a/src/librustc/mir/transform.rs b/src/librustc/mir/transform.rs index 80a4d9a9ff1..9718098aac8 100644 --- a/src/librustc/mir/transform.rs +++ b/src/librustc/mir/transform.rs @@ -13,6 +13,8 @@ use hir::map::DefPathData; use mir::{Mir, Promoted}; use ty::TyCtxt; +use ty::maps::Multi; +use ty::steal::Steal; use std::cell::Ref; use std::rc::Rc; use syntax::ast::NodeId; @@ -70,15 +72,6 @@ pub fn item_id(&self) -> NodeId { } } -/// Various information about pass. -pub trait Pass { - fn name<'a>(&'a self) -> Cow<'a, str> { - default_name::() - } - - fn run_pass<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>); -} - /// Generates a default name for the pass based on the name of the /// type `T`. pub fn default_name() -> Cow<'static, str> { @@ -97,8 +90,20 @@ pub trait MirCtxt<'a, 'tcx: 'a> { fn suite(&self) -> MirSuite; fn pass_num(&self) -> MirPassIndex; fn source(&self) -> MirSource; + + // Get a read-only view on the MIR of this def-id from the + // previous pass. fn read_previous_mir(&self) -> Ref<'tcx, Mir<'tcx>>; + + // Steal the MIR of this def-id from the previous pass; any future + // attempt to access the MIR from the previous pass is a bug. fn steal_previous_mir(&self) -> Mir<'tcx>; + + // Same as `read_previous_mir()`, but for any def-id you want. + fn read_previous_mir_of(&self, def_id: DefId) -> Ref<'tcx, Mir<'tcx>>; + + // Same as `steal_previous_mir()`, but for any def-id you want. + fn steal_previous_mir_of(&self, def_id: DefId) -> Mir<'tcx>; } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] @@ -116,17 +121,35 @@ pub trait MirCtxt<'a, 'tcx: 'a> { /// `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). +/// want to try and access it). If the pass is interprocedural, then +/// the hook will be invoked once per output. pub trait PassHook { fn on_mir_pass<'a, 'tcx: 'a>(&self, mir_cx: &MirCtxt<'a, 'tcx>, - mir: Option<&Mir<'tcx>>); + mir: Option<(DefId, &Mir<'tcx>)>); } -/// A streamlined trait that you can implement to create a pass; the -/// pass will be invoked to process the MIR with the given `def_id`. -/// This lets you do things before we fetch the MIR itself. You may -/// prefer `MirPass`. +/// The full suite of types that identifies a particular +/// application of a pass to a def-id. +pub type PassId = (MirSuite, MirPassIndex, DefId); + +/// The most generic sort of MIR pass. You only want to implement this +/// rather general trait if you are doing an interprocedural pass that +/// may inspect and affect the MIR of many def-ids. Otherwise, prefer +/// the more steamlined `DefIdPass` or `MirPass`. +pub trait Pass { + fn name<'a>(&'a self) -> Cow<'a, str> { + default_name::() + } + + fn run_pass<'a, 'tcx: 'a>(&self, mir_cx: &MirCtxt<'a, 'tcx>) + -> Multi>>; +} + +/// A streamlined trait that you can implement to create an +/// intraprocedural pass; the pass will be invoked to process the MIR +/// with the given `def_id`. This lets you do things before we fetch +/// the MIR itself. You may prefer `MirPass`, which is even more streamlined. pub trait DefIdPass { fn name<'a>(&'a self) -> Cow<'a, str> { default_name::() @@ -135,10 +158,21 @@ fn name<'a>(&'a self) -> Cow<'a, str> { fn run_pass<'a, 'tcx: 'a>(&self, mir_cx: &MirCtxt<'a, 'tcx>) -> Mir<'tcx>; } +impl Pass for T { + fn name<'a>(&'a self) -> Cow<'a, str> { + DefIdPass::name(self) + } + + fn run_pass<'a, 'tcx: 'a>(&self, mir_cx: &MirCtxt<'a, 'tcx>) + -> Multi>> { + Multi::from(mir_cx.tcx().alloc_steal_mir(DefIdPass::run_pass(self, mir_cx))) + } +} + /// A streamlined trait that you can implement to create a pass; the /// pass will be named after the type, and it will consist of a main /// loop that goes over each available MIR and applies `run_pass`. -pub trait MirPass: DepGraphSafe { +pub trait MirPass { fn name<'a>(&'a self) -> Cow<'a, str> { default_name::() } @@ -174,7 +208,7 @@ fn run_pass<'a, 'tcx: 'a>(&self, mir_cx: &MirCtxt<'a, 'tcx>) -> Mir<'tcx> { #[derive(Clone)] pub struct Passes { pass_hooks: Vec>, - suites: Vec>>, + suites: Vec>>, } /// The number of "pass suites" that we have: @@ -202,7 +236,7 @@ pub fn new() -> Passes { } /// Pushes a built-in pass. - pub fn push_pass(&mut self, suite: MirSuite, pass: T) { + pub fn push_pass(&mut self, suite: MirSuite, pass: T) { self.suites[suite.0].push(Rc::new(pass)); } @@ -215,7 +249,7 @@ pub fn len_passes(&self, suite: MirSuite) -> usize { self.suites[suite.0].len() } - pub fn pass(&self, suite: MirSuite, pass: MirPassIndex) -> &DefIdPass { + pub fn pass(&self, suite: MirSuite, pass: MirPassIndex) -> &Pass { &*self.suites[suite.0][pass.0] } diff --git a/src/librustc/ty/maps.rs b/src/librustc/ty/maps.rs index dff58d78f82..3541a19fe6b 100644 --- a/src/librustc/ty/maps.rs +++ b/src/librustc/ty/maps.rs @@ -24,9 +24,9 @@ use ty::subst::Substs; use util::nodemap::{DefIdSet, NodeSet}; -use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::indexed_vec::IndexVec; use std::cell::{RefCell, RefMut}; +use std::option; use std::fmt::Debug; use std::hash::Hash; use std::iter::{self, Once}; @@ -34,10 +34,11 @@ use std::collections::BTreeMap; use std::ops::Deref; use std::rc::Rc; +use std::vec; use syntax_pos::{Span, DUMMY_SP}; use syntax::symbol::Symbol; -trait Key: Clone + Hash + Eq + Debug { +pub trait Key: Clone + Hash + Eq + Debug { fn map_crate(&self) -> CrateNum; fn default_span(&self, tcx: TyCtxt) -> Span; } @@ -163,27 +164,61 @@ fn from_cycle_error<'a>(_: TyCtxt<'a, 'tcx, 'tcx>) -> Self { trait IntoKeyValues { type KeyValues: IntoIterator; - fn into_key_values(tcx: TyCtxt, key: &K, value: Self) -> Self::KeyValues; + fn into_key_values(key: &K, value: Self) -> Self::KeyValues; } impl IntoKeyValues for V { type KeyValues = Once<(K, V)>; - fn into_key_values(_: TyCtxt, key: &K, value: Self) -> Self::KeyValues { + fn into_key_values(key: &K, value: Self) -> Self::KeyValues { iter::once((key.clone(), value)) } } -impl IntoKeyValues for FxHashMap { - type KeyValues = Self; +/// Return type for a multi-query, which is a query which may (if it +/// chooses) return more than one (key, value) pair. Construct a +/// `Multi` using `Multi::from(...)`. +pub struct Multi { + single: Option, + map: Vec<(K, V)>, +} - fn into_key_values(tcx: TyCtxt, key: &K, value: Self) -> Self { - if !value.contains_key(key) { - span_bug!(key.default_span(tcx), - "multi-generation function for `{:?}` did not generate a value for `{:?}`", - key, key) +impl Multi { + pub fn iter<'a>(&'a self, key: &'a K) -> impl Iterator + 'a { + self.single.iter() + .map(move |v| (key, v)) + .chain(self.map.iter().map(move |&(ref k, ref v)| (k, v))) + } +} + +/// Construct a `Multi` from a single value. +impl From for Multi { + fn from(value: V) -> Self { + Multi { + single: Some(value), + map: vec![], } - value + } +} + +/// Construct a `Multi` from a hashmap of (K, V) pairs. +impl From> for Multi { + fn from(value: Vec<(K, V)>) -> Self { + Multi { + single: None, + map: value + } + } +} + +impl IntoKeyValues for Multi { + type KeyValues = iter::Chain, vec::IntoIter<(K, V)>>; + + fn into_key_values(key: &K, value: Self) -> Self::KeyValues { + value.single + .map(|v| (key.clone(), v)) + .into_iter() + .chain(value.map) } } @@ -469,7 +504,7 @@ fn try_get_with(tcx: TyCtxt<'a, $tcx, 'lcx>, { let map = &mut *tcx.maps.$name.borrow_mut(); - for (k, v) in IntoKeyValues::<$K, $V>::into_key_values(tcx, &key, result) { + for (k, v) in IntoKeyValues::<$K, $V>::into_key_values(&key, result) { map.insert(k, v); } } @@ -545,16 +580,6 @@ impl<$tcx> Copy for Providers<$tcx> {} impl<$tcx> Clone for Providers<$tcx> { fn clone(&self) -> Self { *self } } - - impl<$tcx> Default for Providers<$tcx> { - fn default() -> Self { - $(fn $name<'a, $tcx>(_: TyCtxt<'a, $tcx, $tcx>, key: $K) -> $V { - bug!("tcx.maps.{}({:?}) unsupported by its crate", - stringify!($name), key); - })* - Providers { $($name),* } - } - } } } @@ -642,34 +667,43 @@ macro_rules! define_provider_struct { // Final state: (tcx: $tcx:tt, input: (), - output: ($($output:tt)*)) => { + output: ($(([$name:ident] [$K:ty] [$R:ty]))*)) => { pub struct Providers<$tcx> { - $($output)* + $(pub $name: for<'a> fn(TyCtxt<'a, $tcx, $tcx>, $K) -> $R,)* + } + + impl<$tcx> Default for Providers<$tcx> { + fn default() -> Self { + $(fn $name<'a, $tcx>(_: TyCtxt<'a, $tcx, $tcx>, key: $K) -> $R { + bug!("tcx.maps.{}({:?}) unsupported by its crate", + stringify!($name), key); + })* + Providers { $($name),* } + } } }; // Something ready to shift: (tcx: $tcx:tt, - ready: ([$name:ident] [$K:ty] [$R:ty]), + ready: ($name:tt $K:tt $V:tt), input: $input:tt, output: ($($output:tt)*)) => { define_provider_struct! { tcx: $tcx, input: $input, - output: ($($output)* - pub $name: for<'a> fn(TyCtxt<'a, $tcx, $tcx>, $K) -> $R,) + output: ($($output)* ($name $K $V)) } }; // The `multi` modifier indicates a **multiquery**, in which case - // the function returns a `FxHashMap` instead of just a value + // the function returns a `Multi` instead of just a value // `V`. (tcx: $tcx:tt, input: (([multi $($other_modifiers:tt)*] $name:tt [$K:ty] [$V:ty]) $($input:tt)*), output: $output:tt) => { define_provider_struct! { tcx: $tcx, - ready: ($name [$K] [FxHashMap<$K,$V>]), + ready: ($name [$K] [Multi<$K,$V>]), input: ($($input)*), output: $output } @@ -778,7 +812,7 @@ pub struct Providers<$tcx> { /// 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_suite` provider -- if you are using it /// manually, you're doing it wrong. - [] mir_pass: mir_pass((MirSuite, MirPassIndex, DefId)) -> &'tcx Steal>, + [multi] mir_pass: mir_pass((MirSuite, MirPassIndex, DefId)) -> &'tcx Steal>, /// 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. diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 9b11d168e00..cd6b5f62edd 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -929,7 +929,7 @@ macro_rules! try_with_f { passes.push_pass(MIR_OPTIMIZED, mir::transform::simplify::SimplifyCfg::new("elaborate-drops")); // No lifetime analysis based on borrowing can be done from here on out. - // passes.push_pass(MIR_OPTIMIZED, mir::transform::inline::Inline); // TODO re-enable + passes.push_pass(MIR_OPTIMIZED, mir::transform::inline::Inline); passes.push_pass(MIR_OPTIMIZED, mir::transform::instcombine::InstCombine); passes.push_pass(MIR_OPTIMIZED, mir::transform::deaggregator::Deaggregator); passes.push_pass(MIR_OPTIMIZED, mir::transform::copy_prop::CopyPropagation); diff --git a/src/librustc_mir/callgraph.rs b/src/librustc_mir/callgraph.rs index 961e202ba1c..ef271d8b836 100644 --- a/src/librustc_mir/callgraph.rs +++ b/src/librustc_mir/callgraph.rs @@ -22,21 +22,21 @@ use rustc::util::nodemap::DefIdMap; +use transform::interprocedural::InterproceduralCx; + pub struct CallGraph { node_map: DefIdMap, graph: graph::Graph } impl CallGraph { - // FIXME: allow for construction of a callgraph that inspects - // cross-crate MIRs if available. - pub fn build<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>) -> CallGraph { + pub fn build<'a, 'mir, 'tcx>(cx: &mut InterproceduralCx<'a, 'mir, 'tcx>) -> CallGraph { let mut callgraph = CallGraph { node_map: DefIdMap(), graph: graph::Graph::new() }; - for &def_id in tcx.mir_keys(LOCAL_CRATE).iter() { + for &def_id in cx.tcx.mir_keys(LOCAL_CRATE).iter() { let idx = callgraph.add_node(def_id); let mut call_visitor = CallVisitor { @@ -44,8 +44,9 @@ pub fn build<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>) -> CallGraph { graph: &mut callgraph }; - let mir = tcx.item_mir(def_id); - call_visitor.visit_mir(&mir); + if let Some(mir) = cx.ensure_mir_and_read(def_id) { + call_visitor.visit_mir(mir); + } } callgraph diff --git a/src/librustc_mir/transform/dump_mir.rs b/src/librustc_mir/transform/dump_mir.rs index c00817f9179..b895a215585 100644 --- a/src/librustc_mir/transform/dump_mir.rs +++ b/src/librustc_mir/transform/dump_mir.rs @@ -15,10 +15,11 @@ use std::fs::File; use std::io; +use rustc::hir::def_id::DefId; +use rustc::mir::Mir; +use rustc::mir::transform::{DefIdPass, MirCtxt, MirSource, PassHook}; use rustc::session::config::{OutputFilenames, OutputType}; use rustc::ty::TyCtxt; -use rustc::mir::Mir; -use rustc::mir::transform::{DefIdPass, PassHook, MirCtxt}; use util as mir_util; pub struct Marker(pub &'static str); @@ -48,19 +49,26 @@ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { impl PassHook for DumpMir { fn on_mir_pass<'a, 'tcx: 'a>(&self, - mir_cx: &MirCtxt<'a, 'tcx>, - mir: Option<&Mir<'tcx>>) + mir_cx: &MirCtxt<'a, 'tcx>, + mir: Option<(DefId, &Mir<'tcx>)>) { let tcx = mir_cx.tcx(); let suite = mir_cx.suite(); let pass_num = mir_cx.pass_num(); let pass = tcx.mir_passes.pass(suite, pass_num); let name = &pass.name(); - let source = mir_cx.source(); + let source = match mir { + None => mir_cx.source(), + Some((def_id, _)) => { + let id = tcx.hir.as_local_node_id(def_id) + .expect("mir source requires local def-id"); + MirSource::from_node(tcx, id) + } + }; if mir_util::dump_enabled(tcx, name, source) { let previous_mir; let mir_to_dump = match mir { - Some(m) => m, + Some((_, m)) => m, None => { previous_mir = mir_cx.read_previous_mir(); &*previous_mir diff --git a/src/librustc_mir/transform/inline.rs b/src/librustc_mir/transform/inline.rs index 6eda2f5abb9..e10a91c6ec2 100644 --- a/src/librustc_mir/transform/inline.rs +++ b/src/librustc_mir/transform/inline.rs @@ -18,20 +18,22 @@ use rustc::dep_graph::DepNode; use rustc::mir::*; -use rustc::mir::transform::{MirSource, Pass}; +use rustc::mir::transform::{MirCtxt, MirSource, Pass, PassId}; use rustc::mir::visit::*; use rustc::traits; use rustc::ty::{self, Ty, TyCtxt}; +use rustc::ty::maps::Multi; +use rustc::ty::steal::Steal; use rustc::ty::subst::{Subst,Substs}; use rustc::util::nodemap::{DefIdSet}; use super::simplify::{remove_dead_blocks, CfgSimplifier}; -use std::cell::{Ref, RefCell}; use syntax::{attr}; use syntax::abi::Abi; use callgraph; +use transform::interprocedural::InterproceduralCx; const DEFAULT_THRESHOLD: usize = 50; const HINT_THRESHOLD: usize = 100; @@ -44,25 +46,29 @@ pub struct Inline; impl Pass for Inline { - fn run_pass<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) { - if tcx.sess.opts.debugging_opts.mir_opt_level < 2 { return; } + fn run_pass<'a, 'tcx: 'a>(&self, mir_cx: &MirCtxt<'a, 'tcx>) + -> Multi>> { + let tcx = mir_cx.tcx(); + if tcx.sess.opts.debugging_opts.mir_opt_level < 2 { + return Multi::from(tcx.alloc_steal_mir(mir_cx.steal_previous_mir())); + } - let _ignore = tcx.dep_graph.in_ignore(); + let mut cx = InterproceduralCx::new(mir_cx); - let callgraph = callgraph::CallGraph::build(tcx); + let callgraph = callgraph::CallGraph::build(&mut cx); - let mut inliner = Inliner { - tcx: tcx, - }; + let mut inliner = Inliner { tcx }; for scc in callgraph.scc_iter() { - inliner.inline_scc(&callgraph, &scc); + inliner.inline_scc(&mut cx, &callgraph, &scc); } + + Multi::from(cx.into_local_mirs()) } } -struct Inliner<'a, 'tcx: 'a> { - tcx: TyCtxt<'a, 'tcx, 'tcx>, +struct Inliner<'mir, 'tcx: 'mir> { + tcx: TyCtxt<'mir, 'tcx, 'tcx>, } #[derive(Copy, Clone)] @@ -74,16 +80,12 @@ struct CallSite<'tcx> { location: SourceInfo, } -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 { +impl<'mir, 'tcx> Inliner<'mir, 'tcx> { + fn inline_scc<'a>(&mut self, + cx: &mut InterproceduralCx<'a, 'mir, 'tcx>, + callgraph: &callgraph::CallGraph, + scc: &[graph::NodeIndex]) -> bool { + let tcx = self.tcx; let mut callsites = Vec::new(); let mut in_scc = DefIdSet(); @@ -93,14 +95,14 @@ fn inline_scc(&mut self, callgraph: &callgraph::CallGraph, scc: &[graph::NodeInd let def_id = callgraph.def_id(node); // Don't inspect functions from other crates - let id = if let Some(id) = self.tcx.hir.as_local_node_id(def_id) { + let id = if let Some(id) = tcx.hir.as_local_node_id(def_id) { id } else { continue; }; - let src = MirSource::from_node(self.tcx, id); + let src = MirSource::from_node(tcx, id); if let MirSource::Fn(_) = src { - if let Some(mir) = self.tcx.maybe_item_mir(def_id) { + if let Some(mir) = cx.ensure_mir_and_read(def_id) { for (bb, bb_data) in mir.basic_blocks().iter_enumerated() { // Don't inline calls that are in cleanup blocks. if bb_data.is_cleanup { continue; } @@ -151,27 +153,27 @@ fn inline_scc(&mut self, callgraph: &callgraph::CallGraph, scc: &[graph::NodeInd let callsite = callsites[csi]; csi += 1; - let _task = self.tcx.dep_graph.in_task(DepNode::Mir(callsite.caller)); - self.tcx.dep_graph.write(DepNode::Mir(callsite.caller)); + let _task = tcx.dep_graph.in_task(DepNode::Mir(callsite.caller)); + tcx.dep_graph.write(DepNode::Mir(callsite.caller)); let callee_mir = { - if let Some(callee_mir) = self.maybe_item_mir(callsite.callee) { + if let Some(callee_mir) = cx.ensure_mir_and_read(callsite.callee) { if !self.should_inline(callsite, &callee_mir) { continue; } - callee_mir.subst(self.tcx, callsite.substs) + callee_mir.subst(tcx, callsite.substs) } else { continue; } }; - let mut caller_mir = self.mir(callsite.caller).borrow_mut(); + let caller_mir = cx.mir_mut(callsite.caller); let start = caller_mir.basic_blocks().len(); - if !self.inline_call(callsite, &mut caller_mir, callee_mir) { + if !self.inline_call(callsite, caller_mir, callee_mir) { continue; } @@ -216,21 +218,23 @@ fn inline_scc(&mut self, callgraph: &callgraph::CallGraph, scc: &[graph::NodeInd // Simplify functions we inlined into. for def_id in inlined_into { - let _task = self.tcx.dep_graph.in_task(DepNode::Mir(def_id)); - self.tcx.dep_graph.write(DepNode::Mir(def_id)); + let _task = tcx.dep_graph.in_task(DepNode::Mir(def_id)); + tcx.dep_graph.write(DepNode::Mir(def_id)); - let mut caller_mir = self.mir(def_id).borrow_mut(); + let caller_mir = cx.mir_mut(def_id); debug!("Running simplify cfg on {:?}", def_id); - CfgSimplifier::new(&mut caller_mir).simplify(); - remove_dead_blocks(&mut caller_mir); + CfgSimplifier::new(caller_mir).simplify(); + remove_dead_blocks(caller_mir); } changed } - fn should_inline(&self, callsite: CallSite<'tcx>, - callee_mir: &'a Mir<'tcx>) -> bool { - + fn should_inline(&self, + callsite: CallSite<'tcx>, + callee_mir: &Mir<'tcx>) + -> bool + { let tcx = self.tcx; // Don't inline closures that have captures @@ -382,10 +386,10 @@ fn should_inline(&self, callsite: CallSite<'tcx>, } } - - fn inline_call(&self, callsite: CallSite<'tcx>, - caller_mir: &mut Mir<'tcx>, mut callee_mir: Mir<'tcx>) -> bool { - + fn inline_call(&self, + callsite: CallSite<'tcx>, + caller_mir: &mut Mir<'tcx>, + mut callee_mir: Mir<'tcx>) -> bool { // Don't inline a function into itself if callsite.caller == callsite.callee { return false; } diff --git a/src/librustc_mir/transform/interprocedural.rs b/src/librustc_mir/transform/interprocedural.rs new file mode 100644 index 00000000000..02d2b357e04 --- /dev/null +++ b/src/librustc_mir/transform/interprocedural.rs @@ -0,0 +1,100 @@ +// Copyright 2016 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. + +use rustc::hir::def_id::DefId; +use rustc::mir::Mir; +use rustc::mir::transform::{MirCtxt, PassId}; +use rustc::ty::steal::Steal; +use rustc::ty::TyCtxt; +use rustc_data_structures::fx::FxHashMap; + +/// When writing inter-procedural analyses etc, we need to read (and +/// steal) the MIR for a number of def-ids at once, not all of which +/// are local. This little cache code attempts to remember what you've +/// stolen and so forth. It is more of a placeholder meant to get +/// inlining up and going again, and is probably going to need heavy +/// revision as we scale up to more interesting optimizations. +pub struct InterproceduralCx<'a, 'mir: 'a, 'tcx: 'mir> { + pub tcx: TyCtxt<'mir, 'tcx, 'tcx>, + pub mir_cx: &'a MirCtxt<'mir, 'tcx>, + local_cache: FxHashMap>, +} + +impl<'a, 'mir, 'tcx> InterproceduralCx<'a, 'mir, 'tcx> { + pub fn new(mir_cx: &'a MirCtxt<'mir, 'tcx>) -> Self { + InterproceduralCx { + mir_cx, + tcx: mir_cx.tcx(), + local_cache: FxHashMap::default(), + } + } + + pub fn into_local_mirs(self) -> Vec<(PassId, &'tcx Steal>)> { + let tcx = self.tcx; + let suite = self.mir_cx.suite(); + let pass_num = self.mir_cx.pass_num(); + self.local_cache.into_iter() + .map(|(def_id, mir)| { + let mir = tcx.alloc_steal_mir(mir); + ((suite, pass_num, def_id), mir) + }) + .collect() + } + + /// Ensures that the mir for `def_id` is available, if it can be + /// made available. + pub fn ensure_mir(&mut self, def_id: DefId) { + if def_id.is_local() { + self.ensure_mir_and_read(def_id); + } + } + + /// Ensures that the mir for `def_id` is available and returns it if possible; + /// returns `None` if this is a cross-crate MIR that is not + /// available from metadata. + pub fn ensure_mir_and_read(&mut self, def_id: DefId) -> Option<&Mir<'tcx>> { + if def_id.is_local() { + Some(self.mir_mut(def_id)) + } else { + self.tcx.maybe_item_mir(def_id) + } + } + + /// True if the local cache contains MIR for `def-id`. + pub fn contains_mir(&self, def_id: DefId) -> bool { + if def_id.is_local() { + self.local_cache.contains_key(&def_id) + } else { + self.tcx.is_item_mir_available(def_id) + } + } + + /// Reads the MIR for `def-id`. If the MIR is local, this will + /// panic if you have not previously invoked `ensure_mir`. + pub fn mir(&self, def_id: DefId) -> Option<&Mir<'tcx>> { + if def_id.is_local() { + match self.local_cache.get(&def_id) { + Some(p) => Some(p), + None => { + panic!("MIR for local def-id `{:?}` not previously ensured", def_id) + } + } + } else { + self.tcx.maybe_item_mir(def_id) + } + } + + pub fn mir_mut(&mut self, def_id: DefId) -> &mut Mir<'tcx> { + assert!(def_id.is_local(), "cannot get mutable mir of remote entry"); + let mir_cx = self.mir_cx; + self.local_cache.entry(def_id) + .or_insert_with(|| mir_cx.steal_previous_mir_of(def_id)) + } +} diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index d30495efab5..81af7c23960 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -10,10 +10,10 @@ use rustc::hir::def_id::DefId; use rustc::mir::Mir; -use rustc::mir::transform::{MirCtxt, MirPassIndex, MirSuite, MirSource, MIR_OPTIMIZED}; +use rustc::mir::transform::{MirCtxt, MirPassIndex, MirSuite, MirSource, MIR_OPTIMIZED, PassId}; use rustc::ty::steal::Steal; use rustc::ty::TyCtxt; -use rustc::ty::maps::Providers; +use rustc::ty::maps::{Multi, Providers}; use std::cell::Ref; pub mod simplify_branches; @@ -29,6 +29,7 @@ pub mod instcombine; pub mod copy_prop; pub mod inline; +pub mod interprocedural; pub fn provide(providers: &mut Providers) { self::qualify_consts::provide(providers); @@ -57,7 +58,7 @@ fn mir_suite<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, fn mir_pass<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, (suite, pass_num, def_id): (MirSuite, MirPassIndex, DefId)) - -> &'tcx Steal> + -> Multi>> { let passes = &tcx.mir_passes; let pass = passes.pass(suite, pass_num); @@ -69,11 +70,15 @@ fn mir_pass<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let mir = pass.run_pass(&mir_ctxt); + let key = &(suite, pass_num, def_id); for hook in passes.hooks() { - hook.on_mir_pass(&mir_ctxt, Some(&mir)); + for (&(_, _, k), v) in mir.iter(key) { + let v = &v.borrow(); + hook.on_mir_pass(&mir_ctxt, Some((k, v))); + } } - tcx.alloc_steal_mir(mir) + mir } struct MirCtxtImpl<'a, 'tcx: 'a> { @@ -107,24 +112,32 @@ fn source(&self) -> MirSource { } fn read_previous_mir(&self) -> Ref<'tcx, Mir<'tcx>> { - self.previous_mir().borrow() + self.previous_mir(self.def_id).borrow() } fn steal_previous_mir(&self) -> Mir<'tcx> { - self.previous_mir().steal() + self.previous_mir(self.def_id).steal() + } + + fn read_previous_mir_of(&self, def_id: DefId) -> Ref<'tcx, Mir<'tcx>> { + self.previous_mir(def_id).borrow() + } + + fn steal_previous_mir_of(&self, def_id: DefId) -> Mir<'tcx> { + self.previous_mir(def_id).steal() } } impl<'a, 'tcx> MirCtxtImpl<'a, 'tcx> { - fn previous_mir(&self) -> &'tcx Steal> { + fn previous_mir(&self, def_id: DefId) -> &'tcx Steal> { let MirSuite(suite) = self.suite; let MirPassIndex(pass_num) = self.pass_num; if pass_num > 0 { - self.tcx.mir_pass((MirSuite(suite), MirPassIndex(pass_num - 1), self.def_id)) + self.tcx.mir_pass((MirSuite(suite), MirPassIndex(pass_num - 1), def_id)) } else if suite > 0 { - self.tcx.mir_suite((MirSuite(suite - 1), self.def_id)) + self.tcx.mir_suite((MirSuite(suite - 1), def_id)) } else { - self.tcx.mir_build(self.def_id) + self.tcx.mir_build(def_id) } } } -- 2.44.0