Skip to content

Commit

Permalink
retool MIR passes completely
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
nikomatsakis committed May 2, 2017
1 parent f23a7bc commit 2b32cb9
Show file tree
Hide file tree
Showing 13 changed files with 345 additions and 185 deletions.
140 changes: 65 additions & 75 deletions src/librustc/mir/transform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -90,12 +90,37 @@ pub fn default_name<T: ?Sized>() -> 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<Mir<'tcx>>;
}

#[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
Expand All @@ -107,21 +132,7 @@ pub trait DefIdPass {
default_name::<Self>()
}

fn run_pass<'a, 'tcx>(&self,
tcx: TyCtxt<'a, 'tcx, 'tcx>,
def_id: DefId);
}

impl<T: DefIdPass> 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<Mir<'tcx>>;
}

/// A streamlined trait that you can implement to create a pass; the
Expand All @@ -138,42 +149,32 @@ 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<T: MirPass> 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<Mir<'tcx>> {
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
}
}

/// A manager for MIR passes.
#[derive(Clone)]
pub struct Passes {
pass_hooks: Vec<Rc<PassHook>>,
sets: Vec<PassSet>,
}

#[derive(Clone)]
struct PassSet {
passes: Vec<Rc<DefIdPass>>,
sets: Vec<Vec<Rc<DefIdPass>>>,
}

/// The number of "pass sets" that we have:
Expand All @@ -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<T: DefIdPass + 'static>(&mut self, set: usize, pass: T) {
self.sets[set].passes.push(Rc::new(pass));
pub fn push_pass<T: DefIdPass + 'static>(&mut self, set: MirPassSet, pass: T) {
self.sets[set.0].push(Rc::new(pass));
}

/// Pushes a pass hook.
pub fn push_hook<T: PassHook + 'static>(&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<PassHook>] {
&self.pass_hooks
}
}
68 changes: 59 additions & 9 deletions src/librustc/ty/maps.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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])*
Expand Down Expand Up @@ -542,15 +573,6 @@ define_maps! { <'tcx>
/// Methods in these implementations don't need to be exported.
[] inherent_impls: InherentImpls(DefId) -> Rc<Vec<DefId>>,

/// 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<mir::Mir<'tcx>>,

/// 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.
Expand All @@ -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<mir::Mir<'tcx>>,

/// 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<mir::Mir<'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_pass_set` provider -- if you are using it
/// manually, you're doing it wrong.
[] mir_pass: mir_pass((MirPassSet, MirPassIndex, DefId)) -> &'tcx RefCell<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.
[] optimized_mir: Mir(DefId) -> &'tcx RefCell<mir::Mir<'tcx>>,

/// Records the type of each closure. The def ID is the ID of the
/// expression defining the closure.
[] closure_kind: ItemSignature(DefId) -> ty::ClosureKind,
Expand Down Expand Up @@ -658,3 +700,11 @@ fn const_eval_dep_node((def_id, _): (DefId, &Substs)) -> DepNode<DefId> {
fn mir_keys(_: CrateNum) -> DepNode<DefId> {
DepNode::MirKeys
}

fn mir_pass_set((_pass_set, def_id): (MirPassSet, DefId)) -> DepNode<DefId> {
DepNode::Mir(def_id)
}

fn mir_pass((_pass_set, _pass_num, def_id): (MirPassSet, MirPassIndex, DefId)) -> DepNode<DefId> {
DepNode::Mir(def_id)
}
16 changes: 12 additions & 4 deletions src/librustc/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2323,18 +2323,26 @@ 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.
pub fn instance_mir(self, instance: ty::InstanceDef<'gcx>)
-> 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()
}
}
}

Expand Down
19 changes: 0 additions & 19 deletions src/librustc_driver/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down Expand Up @@ -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",
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_metadata/cstore_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
});
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_mir/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Loading

0 comments on commit 2b32cb9

Please sign in to comment.