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.
This commit is contained in:
Niko Matsakis 2017-04-28 19:29:16 -04:00
parent 1d675ce0a3
commit a26e966307
8 changed files with 311 additions and 117 deletions

View File

@ -13,6 +13,8 @@ use hir::def_id::DefId;
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 @@ impl<'a, 'tcx> MirSource {
}
}
/// Various information about pass.
pub trait Pass {
fn name<'a>(&'a self) -> Cow<'a, str> {
default_name::<Self>()
}
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<T: ?Sized>() -> 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 struct MirPassIndex(pub usize);
/// `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::<Self>()
}
fn run_pass<'a, 'tcx: 'a>(&self, mir_cx: &MirCtxt<'a, 'tcx>)
-> Multi<PassId, &'tcx Steal<Mir<'tcx>>>;
}
/// 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::<Self>()
@ -135,10 +158,21 @@ pub trait DefIdPass {
fn run_pass<'a, 'tcx: 'a>(&self, mir_cx: &MirCtxt<'a, 'tcx>) -> Mir<'tcx>;
}
impl<T: DefIdPass> 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<PassId, &'tcx Steal<Mir<'tcx>>> {
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::<Self>()
}
@ -174,7 +208,7 @@ impl<T: MirPass> DefIdPass for T {
#[derive(Clone)]
pub struct Passes {
pass_hooks: Vec<Rc<PassHook>>,
suites: Vec<Vec<Rc<DefIdPass>>>,
suites: Vec<Vec<Rc<Pass>>>,
}
/// The number of "pass suites" that we have:
@ -202,7 +236,7 @@ impl<'a, 'tcx> Passes {
}
/// Pushes a built-in pass.
pub fn push_pass<T: DefIdPass + 'static>(&mut self, suite: MirSuite, pass: T) {
pub fn push_pass<T: Pass + 'static>(&mut self, suite: MirSuite, pass: T) {
self.suites[suite.0].push(Rc::new(pass));
}
@ -215,7 +249,7 @@ impl<'a, 'tcx> Passes {
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]
}

View File

@ -24,9 +24,9 @@ use ty::steal::Steal;
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::mem;
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 @@ impl<'tcx> Value<'tcx> for ty::SymbolName {
trait IntoKeyValues<K: Key, V> {
type KeyValues: IntoIterator<Item=(K, V)>;
fn into_key_values(tcx: TyCtxt, key: &K, value: Self) -> Self::KeyValues;
fn into_key_values(key: &K, value: Self) -> Self::KeyValues;
}
impl<K: Key, V> IntoKeyValues<K, V> 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<K: Key, V> IntoKeyValues<K, V> for FxHashMap<K, V> {
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<K: Key, V> {
single: Option<V>,
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<K: Key, V> Multi<K, V> {
pub fn iter<'a>(&'a self, key: &'a K) -> impl Iterator<Item = (&'a K, &'a V)> + '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<K: Key, V> From<V> for Multi<K, V> {
fn from(value: V) -> Self {
Multi {
single: Some(value),
map: vec![],
}
value
}
}
/// Construct a `Multi` from a hashmap of (K, V) pairs.
impl<K: Key, V> From<Vec<(K, V)>> for Multi<K, V> {
fn from(value: Vec<(K, V)>) -> Self {
Multi {
single: None,
map: value
}
}
}
impl<K: Key, V> IntoKeyValues<K, V> for Multi<K, V> {
type KeyValues = iter::Chain<option::IntoIter<(K, V)>, 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 @@ macro_rules! define_maps {
{
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 @@ macro_rules! define_maps {
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<K,V>` instead of just a value
// the function returns a `Multi<K,V>` 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 @@ define_maps! { <'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<mir::Mir<'tcx>>,
[multi] mir_pass: mir_pass((MirSuite, MirPassIndex, DefId)) -> &'tcx Steal<mir::Mir<'tcx>>,
/// 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.

View File

@ -929,7 +929,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
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);

View File

@ -22,21 +22,21 @@ use rustc::ty;
use rustc::util::nodemap::DefIdMap;
use transform::interprocedural::InterproceduralCx;
pub struct CallGraph {
node_map: DefIdMap<graph::NodeIndex>,
graph: graph::Graph<DefId, ()>
}
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 @@ impl 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

View File

@ -15,10 +15,11 @@ use std::fmt;
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 @@ pub struct DumpMir;
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

View File

@ -18,20 +18,22 @@ use rustc_data_structures::graph;
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 @@ const UNKNOWN_SIZE_COST: usize = 10;
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<PassId, &'tcx Steal<Mir<'tcx>>> {
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<Ref<'tcx, Mir<'tcx>>> {
panic!() // TODO -- hook up inline into the system
}
fn mir(&mut self, _def_id: DefId) -> &'tcx RefCell<Mir<'tcx>> {
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 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> {
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 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> {
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 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> {
// 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 @@ impl<'a, 'tcx> Inliner<'a, '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; }

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<DefId, Mir<'tcx>>,
}
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<Mir<'tcx>>)> {
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))
}
}

View File

@ -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 deaggregator;
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<Mir<'tcx>>
-> Multi<PassId, &'tcx Steal<Mir<'tcx>>>
{
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 @@ impl<'a, 'tcx> MirCtxt<'a, 'tcx> for MirCtxtImpl<'a, 'tcx> {
}
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<Mir<'tcx>> {
fn previous_mir(&self, def_id: DefId) -> &'tcx Steal<Mir<'tcx>> {
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)
}
}
}