From e9003c5574bbc90b219266a56d3e4dcd0cae59b3 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Mon, 6 Jun 2016 22:58:28 +0300 Subject: [PATCH] merge the RemoveDeadBlocks pass into the SimplifyCfg pass --- src/librustc_driver/driver.rs | 13 +-- src/librustc_mir/transform/mod.rs | 1 - .../transform/remove_dead_blocks.rs | 86 ------------------- src/librustc_mir/transform/simplify_cfg.rs | 73 +++++++++++++--- 4 files changed, 70 insertions(+), 103 deletions(-) delete mode 100644 src/librustc_mir/transform/remove_dead_blocks.rs diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index c63122948ff3a..67eb2479994e1 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -976,11 +976,10 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, time(time_passes, "MIR passes", || { let mut passes = sess.mir_passes.borrow_mut(); // Push all the built-in passes. - passes.push_pass(box mir::transform::remove_dead_blocks::RemoveDeadBlocks); + passes.push_pass(box mir::transform::simplify_cfg::SimplifyCfg::new("initial")); passes.push_pass(box mir::transform::qualify_consts::QualifyAndPromoteConstants); passes.push_pass(box mir::transform::type_check::TypeckMir); - passes.push_pass(box mir::transform::simplify_cfg::SimplifyCfg); - passes.push_pass(box mir::transform::remove_dead_blocks::RemoveDeadBlocks); + passes.push_pass(box mir::transform::simplify_cfg::SimplifyCfg::new("qualify-consts")); // And run everything. passes.run_passes(tcx, &mut mir_map); }); @@ -1047,14 +1046,18 @@ pub fn phase_4_translate_to_llvm<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, time(time_passes, "Prepare MIR codegen passes", || { let mut passes = ::rustc::mir::transform::Passes::new(); passes.push_pass(box mir::transform::no_landing_pads::NoLandingPads); - passes.push_pass(box mir::transform::remove_dead_blocks::RemoveDeadBlocks); + passes.push_pass(box mir::transform::simplify_cfg::SimplifyCfg::new("no-landing-pads")); + passes.push_pass(box mir::transform::erase_regions::EraseRegions); + passes.push_pass(box mir::transform::add_call_guards::AddCallGuards); passes.push_pass(box borrowck::ElaborateDrops); passes.push_pass(box mir::transform::no_landing_pads::NoLandingPads); - passes.push_pass(box mir::transform::simplify_cfg::SimplifyCfg); + passes.push_pass(box mir::transform::simplify_cfg::SimplifyCfg::new("elaborate-drops")); + passes.push_pass(box mir::transform::add_call_guards::AddCallGuards); passes.push_pass(box mir::transform::dump_mir::DumpMir("pre_trans")); + passes.run_passes(tcx, &mut mir_map); }); diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index 339dcdec06080..32ebbf5e936ad 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -8,7 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -pub mod remove_dead_blocks; pub mod simplify_cfg; pub mod erase_regions; pub mod no_landing_pads; diff --git a/src/librustc_mir/transform/remove_dead_blocks.rs b/src/librustc_mir/transform/remove_dead_blocks.rs deleted file mode 100644 index 44f3ce7361cf4..0000000000000 --- a/src/librustc_mir/transform/remove_dead_blocks.rs +++ /dev/null @@ -1,86 +0,0 @@ -// 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. - -//! A pass that erases the contents of dead blocks. This pass must -//! run before any analysis passes because some of the dead blocks -//! can be ill-typed. -//! -//! The main problem is that typeck lets most blocks whose end is not -//! reachable have an arbitrary return type, rather than having the -//! usual () return type (as a note, typeck's notion of reachability -//! is in fact slightly weaker than MIR CFG reachability - see #31617). -//! -//! A standard example of the situation is: -//! ```rust -//! fn example() { -//! let _a: char = { return; }; -//! } -//! ``` -//! -//! Here the block (`{ return; }`) has the return type `char`, -//! rather than `()`, but the MIR we naively generate still contains -//! the `_a = ()` write in the unreachable block "after" the return. -//! -//! As we have to run this pass even when we want to debug the MIR, -//! this pass just replaces the blocks with empty "return" blocks -//! and does not renumber anything. - -use rustc_data_structures::bitvec::BitVector; -use rustc::ty::TyCtxt; -use rustc::mir::repr::*; -use rustc::mir::transform::{Pass, MirPass, MirSource}; - -pub struct RemoveDeadBlocks; - -impl<'tcx> MirPass<'tcx> for RemoveDeadBlocks { - fn run_pass<'a>(&mut self, _: TyCtxt<'a, 'tcx, 'tcx>, - _: MirSource, mir: &mut Mir<'tcx>) { - let mut seen = BitVector::new(mir.basic_blocks.len()); - // This block is always required. - seen.insert(START_BLOCK.index()); - - let mut worklist = Vec::with_capacity(4); - worklist.push(START_BLOCK); - while let Some(bb) = worklist.pop() { - for succ in mir.basic_block_data(bb).terminator().successors().iter() { - if seen.insert(succ.index()) { - worklist.push(*succ); - } - } - } - retain_basic_blocks(mir, &seen); - } -} - -impl Pass for RemoveDeadBlocks {} - -/// Mass removal of basic blocks to keep the ID-remapping cheap. -fn retain_basic_blocks(mir: &mut Mir, keep: &BitVector) { - let num_blocks = mir.basic_blocks.len(); - - let mut replacements: Vec<_> = (0..num_blocks).map(BasicBlock::new).collect(); - let mut used_blocks = 0; - for alive_index in keep.iter() { - replacements[alive_index] = BasicBlock::new(used_blocks); - if alive_index != used_blocks { - // Swap the next alive block data with the current available slot. Since alive_index is - // non-decreasing this is a valid operation. - mir.basic_blocks.swap(alive_index, used_blocks); - } - used_blocks += 1; - } - mir.basic_blocks.truncate(used_blocks); - - for bb in mir.all_basic_blocks() { - for target in mir.basic_block_data_mut(bb).terminator_mut().successors_mut() { - *target = replacements[target.index()]; - } - } -} diff --git a/src/librustc_mir/transform/simplify_cfg.rs b/src/librustc_mir/transform/simplify_cfg.rs index 3a0055c564feb..8cce55a9beba6 100644 --- a/src/librustc_mir/transform/simplify_cfg.rs +++ b/src/librustc_mir/transform/simplify_cfg.rs @@ -8,6 +8,30 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +//! A pass that removes various redundancies in the CFG. It should be +//! called after every significant CFG modification to tidy things +//! up. +//! +//! This pass must also be run before any analysis passes because it removes +//! dead blocks, and some of these can be ill-typed. +//! +//! The cause of that is that typeck lets most blocks whose end is not +//! reachable have an arbitrary return type, rather than having the +//! usual () return type (as a note, typeck's notion of reachability +//! is in fact slightly weaker than MIR CFG reachability - see #31617). +//! +//! A standard example of the situation is: +//! ```rust +//! fn example() { +//! let _a: char = { return; }; +//! } +//! ``` +//! +//! Here the block (`{ return; }`) has the return type `char`, +//! rather than `()`, but the MIR we naively generate still contains +//! the `_a = ()` write in the unreachable block "after" the return. + + use rustc_data_structures::bitvec::BitVector; use rustc::middle::const_val::ConstVal; use rustc::ty::TyCtxt; @@ -17,30 +41,29 @@ use rustc::mir::traversal; use pretty; use std::mem; -use super::remove_dead_blocks::RemoveDeadBlocks; +pub struct SimplifyCfg<'a> { label: &'a str } -pub struct SimplifyCfg; - -impl SimplifyCfg { - pub fn new() -> SimplifyCfg { - SimplifyCfg +impl<'a> SimplifyCfg<'a> { + pub fn new(label: &'a str) -> Self { + SimplifyCfg { label: label } } } -impl<'tcx> MirPass<'tcx> for SimplifyCfg { +impl<'l, 'tcx> MirPass<'tcx> for SimplifyCfg<'l> { fn run_pass<'a>(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>, src: MirSource, mir: &mut Mir<'tcx>) { + pretty::dump_mir(tcx, "simplify_cfg", &format!("{}-before", self.label), src, mir, None); simplify_branches(mir); - RemoveDeadBlocks.run_pass(tcx, src, mir); + remove_dead_blocks(mir); merge_consecutive_blocks(mir); - RemoveDeadBlocks.run_pass(tcx, src, mir); - pretty::dump_mir(tcx, "simplify_cfg", &0, src, mir, None); + remove_dead_blocks(mir); + pretty::dump_mir(tcx, "simplify_cfg", &format!("{}-after", self.label), src, mir, None); // FIXME: Should probably be moved into some kind of pass manager mir.basic_blocks.shrink_to_fit(); } } -impl Pass for SimplifyCfg {} +impl<'l> Pass for SimplifyCfg<'l> {} fn merge_consecutive_blocks(mir: &mut Mir) { // Build the precedecessor map for the MIR @@ -202,3 +225,31 @@ fn simplify_branches(mir: &mut Mir) { } } } + +fn remove_dead_blocks(mir: &mut Mir) { + let mut seen = BitVector::new(mir.basic_blocks.len()); + for (bb, _) in traversal::preorder(mir) { + seen.insert(bb.index()); + } + + let num_blocks = mir.basic_blocks.len(); + + let mut replacements: Vec<_> = (0..num_blocks).map(BasicBlock::new).collect(); + let mut used_blocks = 0; + for alive_index in seen.iter() { + replacements[alive_index] = BasicBlock::new(used_blocks); + if alive_index != used_blocks { + // Swap the next alive block data with the current available slot. Since alive_index is + // non-decreasing this is a valid operation. + mir.basic_blocks.swap(alive_index, used_blocks); + } + used_blocks += 1; + } + mir.basic_blocks.truncate(used_blocks); + + for bb in mir.all_basic_blocks() { + for target in mir.basic_block_data_mut(bb).terminator_mut().successors_mut() { + *target = replacements[target.index()]; + } + } +}