Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/librustc/dep_graph/mod.rs
Original file line number Diff line number Diff line change
@@ -70,6 +70,7 @@ pub enum DepNode {
IntrinsicCheck(DefId),
MatchCheck(DefId),
MirMapConstruction(DefId),
MirPrintPass,
BorrowCheck(DefId),
RvalueCheck(DefId),
Reachability,
12 changes: 0 additions & 12 deletions src/librustc/mir/mir_map.rs
Original file line number Diff line number Diff line change
@@ -10,19 +10,7 @@

use util::nodemap::NodeMap;
use mir::repr::Mir;
use mir::transform::MirPass;
use middle::ty;

pub struct MirMap<'tcx> {
pub map: NodeMap<Mir<'tcx>>,
}

impl<'tcx> MirMap<'tcx> {
pub fn run_passes(&mut self, passes: &mut [Box<MirPass>], tcx: &ty::ctxt<'tcx>) {
for (_, ref mut mir) in &mut self.map {
for pass in &mut *passes {
pass.run_on_mir(mir, tcx)
}
}
}
}
2 changes: 1 addition & 1 deletion src/librustc/mir/repr.rs
Original file line number Diff line number Diff line change
@@ -204,7 +204,7 @@ impl Debug for BasicBlock {
}

///////////////////////////////////////////////////////////////////////////
// BasicBlock and Terminator
// BasicBlockData and Terminator

#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
pub struct BasicBlockData<'tcx> {
54 changes: 52 additions & 2 deletions src/librustc/mir/transform.rs
Original file line number Diff line number Diff line change
@@ -9,8 +9,58 @@
// except according to those terms.

use mir::repr::Mir;
use mir::mir_map::MirMap;
use middle::ty::ctxt;

pub trait MirPass {
fn run_on_mir<'tcx>(&mut self, mir: &mut Mir<'tcx>, tcx: &ctxt<'tcx>);
/// Contains various metadata about the pass.
pub trait Pass {
// Possibly also `fn name()` and `fn should_run(Session)` etc.
}

/// Pass which inspects the whole MirMap.
pub trait MirMapPass<'tcx>: Pass {
fn run_pass(&mut self, tcx: &ctxt<'tcx>, map: &mut MirMap<'tcx>);
}

/// Pass which only inspects MIR of distinct functions.
pub trait MirPass<'tcx>: Pass {
fn run_pass(&mut self, tcx: &ctxt<'tcx>, mir: &mut Mir<'tcx>);
}

impl<'tcx, T: MirPass<'tcx>> MirMapPass<'tcx> for T {
fn run_pass(&mut self, tcx: &ctxt<'tcx>, map: &mut MirMap<'tcx>) {
for (_, mir) in &mut map.map {
MirPass::run_pass(self, tcx, mir);
}
}
}

/// A manager for MIR passes.
pub struct Passes {
passes: Vec<Box<for<'tcx> MirMapPass<'tcx>>>
}

impl Passes {
pub fn new() -> Passes {
let passes = Passes {
passes: Vec::new()
};
passes
}

pub fn run_passes<'tcx>(&mut self, tcx: &ctxt<'tcx>, map: &mut MirMap<'tcx>) {
for pass in &mut self.passes {
pass.run_pass(tcx, map);
}
}

pub fn push_pass(&mut self, pass: Box<for<'a> MirMapPass<'a>>) {
self.passes.push(pass);
}
}

impl ::std::iter::Extend<Box<for<'a> MirMapPass<'a>>> for Passes {
fn extend<I: IntoIterator<Item=Box<for <'a> MirMapPass<'a>>>>(&mut self, it: I) {
self.passes.extend(it);
}
}
6 changes: 3 additions & 3 deletions src/librustc/session/mod.rs
Original file line number Diff line number Diff line change
@@ -13,7 +13,7 @@ use middle::cstore::CrateStore;
use middle::dependency_format;
use session::search_paths::PathKind;
use util::nodemap::{NodeMap, FnvHashMap};
use mir::transform::MirPass;
use mir;

use syntax::ast::{NodeId, NodeIdAssigner, Name};
use syntax::codemap::{Span, MultiSpan};
@@ -60,7 +60,7 @@ pub struct Session {
pub lint_store: RefCell<lint::LintStore>,
pub lints: RefCell<NodeMap<Vec<(lint::LintId, Span, String)>>>,
pub plugin_llvm_passes: RefCell<Vec<String>>,
pub plugin_mir_passes: RefCell<Vec<Box<MirPass>>>,
pub mir_passes: RefCell<mir::transform::Passes>,
pub plugin_attributes: RefCell<Vec<(String, AttributeType)>>,
pub crate_types: RefCell<Vec<config::CrateType>>,
pub dependency_formats: RefCell<dependency_format::Dependencies>,
@@ -477,7 +477,7 @@ pub fn build_session_(sopts: config::Options,
lint_store: RefCell::new(lint::LintStore::new()),
lints: RefCell::new(NodeMap()),
plugin_llvm_passes: RefCell::new(Vec::new()),
plugin_mir_passes: RefCell::new(Vec::new()),
mir_passes: RefCell::new(mir::transform::Passes::new()),
plugin_attributes: RefCell::new(Vec::new()),
crate_types: RefCell::new(Vec::new()),
dependency_formats: RefCell::new(FnvHashMap()),
29 changes: 16 additions & 13 deletions src/librustc_driver/driver.rs
Original file line number Diff line number Diff line change
@@ -23,6 +23,7 @@ use rustc::middle;
use rustc::util::common::time;
use rustc::util::nodemap::NodeSet;
use rustc_borrowck as borrowck;
use rustc_mir::transform;
use rustc_resolve as resolve;
use rustc_metadata::macro_import;
use rustc_metadata::creader::LocalCrateReader;
@@ -561,8 +562,8 @@ pub fn phase_2_configure_and_expand(sess: &Session,
}

*sess.plugin_llvm_passes.borrow_mut() = llvm_passes;
*sess.plugin_mir_passes.borrow_mut() = mir_passes;
*sess.plugin_attributes.borrow_mut() = attributes.clone();
sess.mir_passes.borrow_mut().extend(mir_passes);
}));

// Lint plugins are registered; now we can process command line flags.
@@ -846,12 +847,20 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,

let mut mir_map =
time(time_passes,
"MIR dump",
"MIR build",
|| mir::mir_map::build_mir_for_crate(tcx));

time(time_passes,
"MIR passes",
|| mir_map.run_passes(&mut sess.plugin_mir_passes.borrow_mut(), tcx));

time(time_passes, "MIR passes", || {
let mut passes = sess.mir_passes.borrow_mut();
// Push all the built-in passes.
passes.push_pass(box transform::simplify_cfg::SimplifyCfg);
passes.push_pass(box transform::erase_regions::EraseRegions);
passes.push_pass(box transform::print::MirPrint);
passes.push_pass(box transform::simplify_cfg::CompactMir);
// And run everything.
passes.run_passes(tcx, &mut mir_map);
});

time(time_passes,
"liveness checking",
@@ -907,10 +916,9 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
})
}

/// Run the translation phase to LLVM, after which the AST and analysis can
/// be discarded.
/// Run the translation phase to LLVM, after which the MIR and analysis can be discarded.
pub fn phase_4_translate_to_llvm<'tcx>(tcx: &ty::ctxt<'tcx>,
mut mir_map: MirMap<'tcx>,
mir_map: MirMap<'tcx>,
analysis: ty::CrateAnalysis)
-> trans::CrateTranslation {
let time_passes = tcx.sess.time_passes();
@@ -919,11 +927,6 @@ pub fn phase_4_translate_to_llvm<'tcx>(tcx: &ty::ctxt<'tcx>,
"resolving dependency formats",
|| dependency_format::calculate(&tcx.sess));

time(time_passes,
"erasing regions from MIR",
|| mir::transform::erase_regions::erase_regions(tcx, &mut mir_map));

// Option dance to work around the lack of stack once closures.
time(time_passes,
"translation",
move || trans::trans_crate(tcx, &mir_map, analysis))
64 changes: 5 additions & 59 deletions src/librustc_mir/mir_map.rs
Original file line number Diff line number Diff line change
@@ -8,27 +8,16 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! An experimental pass that scources for `#[rustc_mir]` attributes,
//! builds the resulting MIR, and dumps it out into a file for inspection.
//!
//! The attribute formats that are currently accepted are:
//!
//! - `#[rustc_mir(graphviz="file.gv")]`
//! - `#[rustc_mir(pretty="file.mir")]`
//! An pass that builds the MIR for each item and stores it into the map.
extern crate syntax;
extern crate rustc_front;

use build;
use graphviz;
use pretty;
use transform::simplify_cfg;
use rustc::dep_graph::DepNode;
use rustc::mir::repr::Mir;
use hair::cx::Cx;
use std::fs::File;

use rustc::mir::transform::MirPass;
use rustc::mir::mir_map::MirMap;
use rustc::middle::infer;
use rustc::middle::region::CodeExtentData;
@@ -135,57 +124,14 @@ impl<'a, 'm, 'tcx> Visitor<'tcx> for InnerDump<'a,'m,'tcx> {
body: &'tcx hir::Block,
span: Span,
id: ast::NodeId) {
let (prefix, implicit_arg_tys) = match fk {
intravisit::FnKind::Closure =>
(format!("{}-", id), vec![closure_self_ty(&self.tcx, id, body.id)]),
_ =>
(format!(""), vec![]),
let implicit_arg_tys = match fk {
intravisit::FnKind::Closure => vec![closure_self_ty(&self.tcx, id, body.id)],
_ => vec![],
};

let param_env = ty::ParameterEnvironment::for_item(self.tcx, id);

let infcx = infer::new_infer_ctxt(self.tcx, &self.tcx.tables, Some(param_env));

match build_mir(Cx::new(&infcx), implicit_arg_tys, id, span, decl, body) {
Ok(mut mir) => {
simplify_cfg::SimplifyCfg::new().run_on_mir(&mut mir, self.tcx);

let meta_item_list = self.attr
.iter()
.flat_map(|a| a.meta_item_list())
.flat_map(|l| l.iter());
for item in meta_item_list {
if item.check_name("graphviz") || item.check_name("pretty") {
match item.value_str() {
Some(s) => {
let filename = format!("{}{}", prefix, s);
let result = File::create(&filename).and_then(|ref mut output| {
if item.check_name("graphviz") {
graphviz::write_mir_graphviz(&mir, output)
} else {
pretty::write_mir_pretty(&mir, output)
}
});

if let Err(e) = result {
self.tcx.sess.span_fatal(
item.span,
&format!("Error writing MIR {} results to `{}`: {}",
item.name(), filename, e));
}
}
None => {
self.tcx.sess.span_err(
item.span,
&format!("{} attribute requires a path", item.name()));
}
}
}
}

let previous = self.map.map.insert(id, mir);
assert!(previous.is_none());
}
Ok(mir) => assert!(self.map.map.insert(id, mir).is_none()),
Err(ErrorReported) => {}
}

20 changes: 12 additions & 8 deletions src/librustc_mir/transform/erase_regions.rs
Original file line number Diff line number Diff line change
@@ -16,18 +16,28 @@ use rustc::middle::ty;
use rustc::mir::repr::*;
use rustc::mir::visit::MutVisitor;
use rustc::mir::mir_map::MirMap;
use rustc::mir::transform::MirPass;
use rustc::mir::transform::{MirPass, Pass};

pub fn erase_regions<'tcx>(tcx: &ty::ctxt<'tcx>, mir_map: &mut MirMap<'tcx>) {
let mut eraser = EraseRegions;

for (_, mir) in &mut mir_map.map {
eraser.run_on_mir(mir, tcx);
eraser.run_pass(tcx, mir);
}
}

pub struct EraseRegions;

impl<'tcx> MirPass<'tcx> for EraseRegions {
fn run_pass(&mut self, tcx: &ty::ctxt<'tcx>, mir: &mut Mir<'tcx>) {
EraseRegionsVisitor::new(tcx).visit_mir(mir);
}

}

impl Pass for EraseRegions {
}

struct EraseRegionsVisitor<'a, 'tcx: 'a> {
tcx: &'a ty::ctxt<'tcx>,
}
@@ -58,12 +68,6 @@ impl<'a, 'tcx> EraseRegionsVisitor<'a, 'tcx> {
}
}

impl MirPass for EraseRegions {
fn run_on_mir<'tcx>(&mut self, mir: &mut Mir<'tcx>, tcx: &ty::ctxt<'tcx>) {
EraseRegionsVisitor::new(tcx).visit_mir(mir);
}
}

impl<'a, 'tcx> MutVisitor<'tcx> for EraseRegionsVisitor<'a, 'tcx> {
fn visit_mir(&mut self, mir: &mut Mir<'tcx>) {
self.erase_regions_return_ty(&mut mir.return_ty);
1 change: 1 addition & 0 deletions src/librustc_mir/transform/mod.rs
Original file line number Diff line number Diff line change
@@ -10,4 +10,5 @@

pub mod simplify_cfg;
pub mod erase_regions;
pub mod print;
mod util;
69 changes: 69 additions & 0 deletions src/librustc_mir/transform/print.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <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.

//! This pass handles the `#[rustc_mir(*)]` attributes and prints the contents.
//!
//! The attribute formats that are currently accepted are:
//!
//! - `#[rustc_mir(graphviz="file.gv")]`
//! - `#[rustc_mir(pretty="file.mir")]`
use graphviz;
use pretty;

use syntax::attr::AttrMetaMethods;
use rustc::middle::ty;
use rustc::dep_graph::DepNode;
use rustc::mir::mir_map::MirMap;
use rustc::mir::transform::{MirMapPass, Pass};

use std::fs::File;

pub struct MirPrint;

impl Pass for MirPrint {
}

impl<'tcx> MirMapPass<'tcx> for MirPrint {
fn run_pass(&mut self, tcx: &ty::ctxt<'tcx>, map: &mut MirMap<'tcx>) {
let _task = tcx.map.dep_graph.in_task(DepNode::MirPrintPass);
for (node_id, mir) in &map.map {
for attr in tcx.map.attrs(*node_id) {
if !attr.check_name("rustc_mir") {
continue
}
for arg in attr.meta_item_list().iter().flat_map(|e| *e) {
if arg.check_name("graphviz") || arg.check_name("pretty") {
let filename = if let Some(p) = arg.value_str() {
p
} else {
tcx.sess.span_err(arg.span,
&format!("{} attribute requires a path", arg.name())
);
continue
};
let result = File::create(&*filename).and_then(|ref mut output| {
if arg.check_name("graphviz") {
graphviz::write_mir_graphviz(&mir, output)
} else {
pretty::write_mir_pretty(&mir, output)
}
});

if let Err(e) = result {
tcx.sess.span_err(arg.span,
&format!("Error writing MIR {} output to `{}`: {}",
arg.name(), filename, e));
}
}
}
}
}
}
}
120 changes: 90 additions & 30 deletions src/librustc_mir/transform/simplify_cfg.rs
Original file line number Diff line number Diff line change
@@ -9,37 +9,30 @@
// except according to those terms.

use rustc::middle::const_eval::ConstVal;
use rustc::middle::ty;
use rustc::mir::repr::*;
use rustc::mir::visit::MutVisitor;
use transform::util;
use rustc::mir::transform::MirPass;
use rustc::mir::transform::{MirPass, Pass};

pub struct SimplifyCfg;

impl SimplifyCfg {
pub fn new() -> SimplifyCfg {
SimplifyCfg
}

fn remove_dead_blocks(&self, mir: &mut Mir) {
let mut seen = vec![false; mir.basic_blocks.len()];

// These blocks are always required.
seen[START_BLOCK.index()] = true;
seen[END_BLOCK.index()] = true;
impl Pass for SimplifyCfg {
}

let mut worklist = vec![START_BLOCK];
while let Some(bb) = worklist.pop() {
for succ in mir.basic_block_data(bb).terminator().successors().iter() {
if !seen[succ.index()] {
seen[succ.index()] = true;
worklist.push(*succ);
}
}
impl<'tcx> MirPass<'tcx> for SimplifyCfg {
fn run_pass(&mut self, tcx: &ty::ctxt<'tcx>, mir: &mut Mir<'tcx>) {
let mut dbr_pass = DeadBlockRemoval;
let mut changed = true;
while changed {
changed = self.simplify_branches(mir);
changed |= self.remove_goto_chains(mir);
dbr_pass.run_pass(tcx, mir);
}

util::retain_basic_blocks(mir, &seen);
}
}

impl SimplifyCfg {
fn remove_goto_chains(&self, mir: &mut Mir) -> bool {

// Find the target at the end of the jump chain, return None if there is a loop
@@ -83,6 +76,7 @@ impl SimplifyCfg {
changed
}

// FIXME: This transformation should be interleaved with the constant-propagation pass.
fn simplify_branches(&self, mir: &mut Mir) -> bool {
let mut changed = false;

@@ -118,15 +112,81 @@ impl SimplifyCfg {
}
}

impl MirPass for SimplifyCfg {
fn run_on_mir<'tcx>(&mut self, mir: &mut Mir<'tcx>, _: &::rustc::middle::ty::ctxt<'tcx>) {
let mut changed = true;
while changed {
changed = self.simplify_branches(mir);
changed |= self.remove_goto_chains(mir);
self.remove_dead_blocks(mir);
/// Remove all the unreachable blocks.
///
/// You want to schedule this pass just after any pass which might introduce unreachable blocks.
/// This pass is very cheap and might improve the run-time of other not-so-cheap passes.
pub struct DeadBlockRemoval;

impl Pass for DeadBlockRemoval {
}

impl<'tcx> MirPass<'tcx> for DeadBlockRemoval {
fn run_pass(&mut self, _: &ty::ctxt<'tcx>, mir: &mut Mir<'tcx>) {
let mut seen = vec![false; mir.basic_blocks.len()];

// These blocks are always required.
seen[START_BLOCK.index()] = true;
seen[END_BLOCK.index()] = true;

let mut worklist = vec![START_BLOCK];
while let Some(bb) = worklist.pop() {
for succ in mir.basic_block_data(bb).terminator().successors().iter() {
if !seen[succ.index()] {
seen[succ.index()] = true;
worklist.push(*succ);
}
}
}
// FIXME: Should probably be moved into some kind of pass manager

util::retain_basic_blocks(mir, &seen);
}
}

/// Reduce the memory allocated by a MIR graph.
pub struct CompactMir;

impl Pass for CompactMir {
}

impl<'tcx> MirPass<'tcx> for CompactMir {
fn run_pass(&mut self, _: &ty::ctxt<'tcx>, mir: &mut Mir<'tcx>) {
self.visit_mir(mir);
}
}

impl<'tcx> MutVisitor<'tcx> for CompactMir {
fn visit_mir(&mut self, mir: &mut Mir<'tcx>) {
mir.basic_blocks.shrink_to_fit();
mir.var_decls.shrink_to_fit();
mir.arg_decls.shrink_to_fit();
mir.temp_decls.shrink_to_fit();
self.super_mir(mir);
}

fn visit_basic_block_data(&mut self, b: BasicBlock, data: &mut BasicBlockData) {
data.statements.shrink_to_fit();
self.super_basic_block_data(b, data);
}

fn visit_terminator(&mut self, b: BasicBlock, terminator: &mut Terminator) {
match *terminator {
Terminator::Switch { ref mut targets, .. } => targets.shrink_to_fit(),
Terminator::SwitchInt { ref mut values, ref mut targets, .. } => {
values.shrink_to_fit();
targets.shrink_to_fit();
},
Terminator::Call { ref mut args, .. } => args.shrink_to_fit(),
_ => {/* nothing to do */}
}
self.super_terminator(b, terminator);
}

fn visit_rvalue(&mut self, rvalue: &mut Rvalue) {
match *rvalue {
Rvalue::Aggregate(_, ref mut operands) => operands.shrink_to_fit(),
_ => { /* nothing to do */ }
}
self.super_rvalue(rvalue);
}
}
6 changes: 3 additions & 3 deletions src/librustc_plugin/registry.rs
Original file line number Diff line number Diff line change
@@ -13,7 +13,7 @@
use rustc::lint::{EarlyLintPassObject, LateLintPassObject, LintId, Lint};
use rustc::session::Session;

use rustc::mir::transform::MirPass;
use rustc::mir;

use syntax::ext::base::{SyntaxExtension, NamedSyntaxExtension, NormalTT};
use syntax::ext::base::{IdentTT, MultiModifier, MultiDecorator};
@@ -56,7 +56,7 @@ pub struct Registry<'a> {
pub late_lint_passes: Vec<LateLintPassObject>,

#[doc(hidden)]
pub mir_passes: Vec<Box<MirPass>>,
pub mir_passes: Vec<Box<for<'p> mir::transform::MirMapPass<'p>>>,

#[doc(hidden)]
pub lint_groups: HashMap<&'static str, Vec<LintId>>,
@@ -141,7 +141,7 @@ impl<'a> Registry<'a> {
}

/// Register a MIR pass
pub fn register_mir_pass(&mut self, pass: Box<MirPass>) {
pub fn register_mir_pass(&mut self, pass: Box<for<'p> mir::transform::MirMapPass<'p>>) {
self.mir_passes.push(pass);
}

16 changes: 11 additions & 5 deletions src/test/auxiliary/dummy_mir_pass.rs
Original file line number Diff line number Diff line change
@@ -18,8 +18,8 @@ extern crate rustc_front;
extern crate rustc_plugin;
extern crate syntax;

use rustc::mir::transform::MirPass;
use rustc::mir::repr::{Mir, Literal};
use rustc::mir::transform::{self, MirBlockPass};
use rustc::mir::repr::{BasicBlock, BasicBlockData, Literal};
use rustc::mir::visit::MutVisitor;
use rustc::middle::ty;
use rustc::middle::const_eval::ConstVal;
@@ -30,9 +30,15 @@ use syntax::attr;

struct Pass;

impl MirPass for Pass {
fn run_on_mir<'tcx>(&mut self, mir: &mut Mir<'tcx>, tcx: &ty::ctxt<'tcx>) {
Visitor.visit_mir(mir)
impl transform::Pass for Pass {
fn priority(&self) -> usize {
1000
}
}

impl MirBlockPass for Pass {
fn run_pass<'tcx>(&mut self, tcx: &ty::ctxt<'tcx>, bb: BasicBlock, bbd: &mut BasicBlockData) {
Visitor.visit_basic_block_data(bb, bbd)
}
}