Merge dead bb pruning and unreachable bb deduplication.

This commit is contained in:
Camille GILLOT 2024-01-07 14:59:59 +00:00
parent 9522993b03
commit 4071572cb4
19 changed files with 129 additions and 161 deletions

View file

@ -51,7 +51,7 @@ impl<'tcx> MirPass<'tcx> for ConstGoto {
// if we applied optimizations, we potentially have some cfg to cleanup to
// make it easier for further passes
if should_simplify {
simplify_cfg(tcx, body);
simplify_cfg(body);
simplify_locals(body, tcx);
}
}

View file

@ -25,7 +25,7 @@ impl<'tcx> MirPass<'tcx> for DeduplicateBlocks {
if has_opts_to_apply {
let mut opt_applier = OptApplier { tcx, duplicates };
opt_applier.visit_body(body);
simplify_cfg(tcx, body);
simplify_cfg(body);
}
}
}

View file

@ -212,7 +212,7 @@ impl<'tcx> MirPass<'tcx> for EarlyOtherwiseBranch {
// Since this optimization adds new basic blocks and invalidates others,
// clean up the cfg to make it nicer for other passes
if should_cleanup {
simplify_cfg(tcx, body);
simplify_cfg(body);
}
}
}

View file

@ -15,7 +15,7 @@ use rustc_target::abi::FieldIdx;
use rustc_target::spec::abi::Abi;
use crate::cost_checker::CostChecker;
use crate::simplify::{remove_dead_blocks, CfgSimplifier};
use crate::simplify::simplify_cfg;
use crate::util;
use std::iter;
use std::ops::{Range, RangeFrom};
@ -56,8 +56,7 @@ impl<'tcx> MirPass<'tcx> for Inline {
let _guard = span.enter();
if inline(tcx, body) {
debug!("running simplify cfg on {:?}", body.source);
CfgSimplifier::new(body).simplify();
remove_dead_blocks(body);
simplify_cfg(body);
deref_finder(tcx, body);
}
}

View file

@ -174,7 +174,7 @@ impl<'tcx> MirPass<'tcx> for MatchBranchSimplification {
}
if should_cleanup {
simplify_cfg(tcx, body);
simplify_cfg(body);
}
}
}

View file

@ -38,7 +38,7 @@ impl<'tcx> MirPass<'tcx> for RemoveUnneededDrops {
// if we applied optimizations, we potentially have some cfg to cleanup to
// make it easier for further passes
if should_simplify {
simplify_cfg(tcx, body);
simplify_cfg(body);
}
}
}

View file

@ -50,11 +50,11 @@ impl<'tcx> MirPass<'tcx> for SeparateConstSwitch {
sess.mir_opt_level() >= 2 && sess.opts.unstable_opts.unsound_mir_opts
}
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
fn run_pass(&self, _: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
// If execution did something, applying a simplification layer
// helps later passes optimize the copy away.
if separate_const_switch(body) > 0 {
super::simplify::simplify_cfg(tcx, body);
super::simplify::simplify_cfg(body);
}
}
}

View file

@ -27,7 +27,6 @@
//! naively generate still contains the `_a = ()` write in the unreachable block "after" the
//! return.
use rustc_data_structures::fx::FxIndexSet;
use rustc_index::{Idx, IndexSlice, IndexVec};
use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor};
use rustc_middle::mir::*;
@ -62,9 +61,8 @@ impl SimplifyCfg {
}
}
pub fn simplify_cfg<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
pub(crate) fn simplify_cfg(body: &mut Body<'_>) {
CfgSimplifier::new(body).simplify();
remove_duplicate_unreachable_blocks(tcx, body);
remove_dead_blocks(body);
// FIXME: Should probably be moved into some kind of pass manager
@ -76,9 +74,9 @@ impl<'tcx> MirPass<'tcx> for SimplifyCfg {
self.name()
}
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
fn run_pass(&self, _: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
debug!("SimplifyCfg({:?}) - simplifying {:?}", self.name(), body.source);
simplify_cfg(tcx, body);
simplify_cfg(body);
}
}
@ -289,55 +287,25 @@ pub fn simplify_duplicate_switch_targets(terminator: &mut Terminator<'_>) {
}
}
pub fn remove_duplicate_unreachable_blocks<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
struct OptApplier<'tcx> {
tcx: TyCtxt<'tcx>,
duplicates: FxIndexSet<BasicBlock>,
}
pub(crate) fn remove_dead_blocks(body: &mut Body<'_>) {
let should_deduplicate_unreachable = |bbdata: &BasicBlockData<'_>| {
// CfgSimplifier::simplify leaves behind some unreachable basic blocks without a
// terminator. Those blocks will be deleted by remove_dead_blocks, but we run just
// before then so we need to handle missing terminators.
// We also need to prevent confusing cleanup and non-cleanup blocks. In practice we
// don't emit empty unreachable cleanup blocks, so this simple check suffices.
bbdata.terminator.is_some() && bbdata.is_empty_unreachable() && !bbdata.is_cleanup
};
impl<'tcx> MutVisitor<'tcx> for OptApplier<'tcx> {
fn tcx(&self) -> TyCtxt<'tcx> {
self.tcx
}
fn visit_terminator(&mut self, terminator: &mut Terminator<'tcx>, location: Location) {
for target in terminator.successors_mut() {
// We don't have to check whether `target` is a cleanup block, because have
// entirely excluded cleanup blocks in building the set of duplicates.
if self.duplicates.contains(target) {
*target = self.duplicates[0];
}
}
simplify_duplicate_switch_targets(terminator);
self.super_terminator(terminator, location);
}
}
let unreachable_blocks = body
let reachable = traversal::reachable_as_bitset(body);
let empty_unreachable_blocks = body
.basic_blocks
.iter_enumerated()
.filter(|(_, bb)| {
// CfgSimplifier::simplify leaves behind some unreachable basic blocks without a
// terminator. Those blocks will be deleted by remove_dead_blocks, but we run just
// before then so we need to handle missing terminators.
// We also need to prevent confusing cleanup and non-cleanup blocks. In practice we
// don't emit empty unreachable cleanup blocks, so this simple check suffices.
bb.terminator.is_some() && bb.is_empty_unreachable() && !bb.is_cleanup
})
.map(|(block, _)| block)
.collect::<FxIndexSet<_>>();
.filter(|(bb, bbdata)| should_deduplicate_unreachable(bbdata) && reachable.contains(*bb))
.count();
if unreachable_blocks.len() > 1 {
OptApplier { tcx, duplicates: unreachable_blocks }.visit_body(body);
}
}
pub fn remove_dead_blocks(body: &mut Body<'_>) {
let reachable = traversal::reachable_as_bitset(body);
let num_blocks = body.basic_blocks.len();
if num_blocks == reachable.count() {
if num_blocks == reachable.count() && empty_unreachable_blocks <= 1 {
return;
}
@ -346,14 +314,28 @@ pub fn remove_dead_blocks(body: &mut Body<'_>) {
let mut replacements: Vec<_> = (0..num_blocks).map(BasicBlock::new).collect();
let mut orig_index = 0;
let mut used_index = 0;
basic_blocks.raw.retain(|_| {
let keep = reachable.contains(BasicBlock::new(orig_index));
if keep {
replacements[orig_index] = BasicBlock::new(used_index);
used_index += 1;
let mut kept_unreachable = None;
basic_blocks.raw.retain(|bbdata| {
let orig_bb = BasicBlock::new(orig_index);
if !reachable.contains(orig_bb) {
orig_index += 1;
return false;
}
let used_bb = BasicBlock::new(used_index);
if should_deduplicate_unreachable(bbdata) {
let kept_unreachable = *kept_unreachable.get_or_insert(used_bb);
if kept_unreachable != used_bb {
replacements[orig_index] = kept_unreachable;
orig_index += 1;
return false;
}
}
replacements[orig_index] = used_bb;
used_index += 1;
orig_index += 1;
keep
true
});
for block in basic_blocks {