merge the RemoveDeadBlocks pass into the SimplifyCfg pass
This commit is contained in:
parent
bb4b3fb7f9
commit
e9003c5574
@ -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);
|
||||
});
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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 <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.
|
||||
|
||||
//! 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()];
|
||||
}
|
||||
}
|
||||
}
|
@ -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()];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user