Remove the deduplicate_blocks pass

This commit is contained in:
Michael Goulet 2025-02-09 18:57:22 +00:00
parent a26e97be88
commit a6dcfe3af4
5 changed files with 0 additions and 414 deletions

View file

@ -1,195 +0,0 @@
//! This pass finds basic blocks that are completely equal,
//! and replaces all uses with just one of them.
use std::collections::hash_map::Entry;
use std::hash::{Hash, Hasher};
use std::iter;
use rustc_data_structures::fx::FxHashMap;
use rustc_middle::mir::visit::MutVisitor;
use rustc_middle::mir::*;
use rustc_middle::ty::TyCtxt;
use tracing::debug;
use super::simplify::simplify_cfg;
pub(super) struct DeduplicateBlocks;
impl<'tcx> crate::MirPass<'tcx> for DeduplicateBlocks {
fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
sess.mir_opt_level() >= 4
}
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
debug!("Running DeduplicateBlocks on `{:?}`", body.source);
let duplicates = find_duplicates(body);
let has_opts_to_apply = !duplicates.is_empty();
if has_opts_to_apply {
let mut opt_applier = OptApplier { tcx, duplicates };
opt_applier.visit_body(body);
simplify_cfg(body);
}
}
fn is_required(&self) -> bool {
false
}
}
struct OptApplier<'tcx> {
tcx: TyCtxt<'tcx>,
duplicates: FxHashMap<BasicBlock, BasicBlock>,
}
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() {
if let Some(replacement) = self.duplicates.get(target) {
debug!("SUCCESS: Replacing: `{:?}` with `{:?}`", target, replacement);
*target = *replacement;
}
}
self.super_terminator(terminator, location);
}
}
fn find_duplicates(body: &Body<'_>) -> FxHashMap<BasicBlock, BasicBlock> {
let mut duplicates = FxHashMap::default();
let bbs_to_go_through =
body.basic_blocks.iter_enumerated().filter(|(_, bbd)| !bbd.is_cleanup).count();
let mut same_hashes =
FxHashMap::with_capacity_and_hasher(bbs_to_go_through, Default::default());
// Go through the basic blocks backwards. This means that in case of duplicates,
// we can use the basic block with the highest index as the replacement for all lower ones.
// For example, if bb1, bb2 and bb3 are duplicates, we will first insert bb3 in same_hashes.
// Then we will see that bb2 is a duplicate of bb3,
// and insert bb2 with the replacement bb3 in the duplicates list.
// When we see bb1, we see that it is a duplicate of bb3, and therefore insert it in the
// duplicates list with replacement bb3.
// When the duplicates are removed, we will end up with only bb3.
for (bb, bbd) in body.basic_blocks.iter_enumerated().rev().filter(|(_, bbd)| !bbd.is_cleanup) {
// Basic blocks can get really big, so to avoid checking for duplicates in basic blocks
// that are unlikely to have duplicates, we stop early. The early bail number has been
// found experimentally by eprintln while compiling the crates in the rustc-perf suite.
if bbd.statements.len() > 10 {
continue;
}
let to_hash = BasicBlockHashable { basic_block_data: bbd };
let entry = same_hashes.entry(to_hash);
match entry {
Entry::Occupied(occupied) => {
// The basic block was already in the hashmap, which means we have a duplicate
let value = *occupied.get();
debug!("Inserting {:?} -> {:?}", bb, value);
duplicates.try_insert(bb, value).expect("key was already inserted");
}
Entry::Vacant(vacant) => {
vacant.insert(bb);
}
}
}
duplicates
}
struct BasicBlockHashable<'a, 'tcx> {
basic_block_data: &'a BasicBlockData<'tcx>,
}
impl Hash for BasicBlockHashable<'_, '_> {
fn hash<H: Hasher>(&self, state: &mut H) {
hash_statements(state, self.basic_block_data.statements.iter());
// Note that since we only hash the kind, we lose span information if we deduplicate the
// blocks.
self.basic_block_data.terminator().kind.hash(state);
}
}
impl Eq for BasicBlockHashable<'_, '_> {}
impl PartialEq for BasicBlockHashable<'_, '_> {
fn eq(&self, other: &Self) -> bool {
self.basic_block_data.statements.len() == other.basic_block_data.statements.len()
&& &self.basic_block_data.terminator().kind == &other.basic_block_data.terminator().kind
&& iter::zip(&self.basic_block_data.statements, &other.basic_block_data.statements)
.all(|(x, y)| statement_eq(&x.kind, &y.kind))
}
}
fn hash_statements<'a, 'tcx, H: Hasher>(
hasher: &mut H,
iter: impl Iterator<Item = &'a Statement<'tcx>>,
) where
'tcx: 'a,
{
for stmt in iter {
statement_hash(hasher, &stmt.kind);
}
}
fn statement_hash<H: Hasher>(hasher: &mut H, stmt: &StatementKind<'_>) {
match stmt {
StatementKind::Assign(box (place, rvalue)) => {
place.hash(hasher);
rvalue_hash(hasher, rvalue)
}
x => x.hash(hasher),
};
}
fn rvalue_hash<H: Hasher>(hasher: &mut H, rvalue: &Rvalue<'_>) {
match rvalue {
Rvalue::Use(op) => operand_hash(hasher, op),
x => x.hash(hasher),
};
}
fn operand_hash<H: Hasher>(hasher: &mut H, operand: &Operand<'_>) {
match operand {
Operand::Constant(box ConstOperand { user_ty: _, const_, span: _ }) => const_.hash(hasher),
x => x.hash(hasher),
};
}
fn statement_eq<'tcx>(lhs: &StatementKind<'tcx>, rhs: &StatementKind<'tcx>) -> bool {
let res = match (lhs, rhs) {
(
StatementKind::Assign(box (place, rvalue)),
StatementKind::Assign(box (place2, rvalue2)),
) => place == place2 && rvalue_eq(rvalue, rvalue2),
(x, y) => x == y,
};
debug!("statement_eq lhs: `{:?}` rhs: `{:?}` result: {:?}", lhs, rhs, res);
res
}
fn rvalue_eq<'tcx>(lhs: &Rvalue<'tcx>, rhs: &Rvalue<'tcx>) -> bool {
let res = match (lhs, rhs) {
(Rvalue::Use(op1), Rvalue::Use(op2)) => operand_eq(op1, op2),
(x, y) => x == y,
};
debug!("rvalue_eq lhs: `{:?}` rhs: `{:?}` result: {:?}", lhs, rhs, res);
res
}
fn operand_eq<'tcx>(lhs: &Operand<'tcx>, rhs: &Operand<'tcx>) -> bool {
let res = match (lhs, rhs) {
(
Operand::Constant(box ConstOperand { user_ty: _, const_, span: _ }),
Operand::Constant(box ConstOperand { user_ty: _, const_: const2, span: _ }),
) => const_ == const2,
(x, y) => x == y,
};
debug!("operand_eq lhs: `{:?}` rhs: `{:?}` result: {:?}", lhs, rhs, res);
res
}

View file

@ -135,7 +135,6 @@ declare_passes! {
Initial,
Final
};
mod deduplicate_blocks : DeduplicateBlocks;
mod deref_separator : Derefer;
mod dest_prop : DestinationPropagation;
pub mod dump_mir : Marker;
@ -700,7 +699,6 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
&nrvo::RenameReturnPlace,
&simplify::SimplifyLocals::Final,
&multiple_return_terminators::MultipleReturnTerminators,
&deduplicate_blocks::DeduplicateBlocks,
&large_enums::EnumSizeOpt { discrepancy: 128 },
// Some cleanup necessary at least for LLVM and potentially other codegen backends.
&add_call_guards::CriticalCallEdges,

View file

@ -1,100 +0,0 @@
- // MIR for `is_line_doc_comment_2` before DeduplicateBlocks
+ // MIR for `is_line_doc_comment_2` after DeduplicateBlocks
fn is_line_doc_comment_2(_1: &str) -> bool {
debug s => _1;
let mut _0: bool;
let mut _2: &[u8];
let mut _3: &str;
let mut _4: usize;
let mut _5: usize;
let mut _6: bool;
let mut _7: usize;
let mut _8: usize;
let mut _9: bool;
bb0: {
StorageLive(_2);
StorageLive(_3);
_3 = &(*_1);
_2 = core::str::<impl str>::as_bytes(move _3) -> [return: bb1, unwind unreachable];
}
bb1: {
StorageDead(_3);
_4 = Len((*_2));
_5 = const 4_usize;
_6 = Ge(move _4, move _5);
switchInt(move _6) -> [0: bb2, otherwise: bb3];
}
bb2: {
_7 = Len((*_2));
_8 = const 3_usize;
_9 = Ge(move _7, move _8);
- switchInt(move _9) -> [0: bb7, otherwise: bb8];
+ switchInt(move _9) -> [0: bb11, otherwise: bb7];
}
bb3: {
switchInt(copy (*_2)[0 of 4]) -> [47: bb4, otherwise: bb2];
}
bb4: {
switchInt(copy (*_2)[1 of 4]) -> [47: bb5, otherwise: bb2];
}
bb5: {
switchInt(copy (*_2)[2 of 4]) -> [47: bb6, otherwise: bb2];
}
bb6: {
- switchInt(copy (*_2)[3 of 4]) -> [47: bb13, otherwise: bb2];
+ switchInt(copy (*_2)[3 of 4]) -> [47: bb11, otherwise: bb2];
}
bb7: {
- _0 = const false;
- goto -> bb14;
+ switchInt(copy (*_2)[0 of 3]) -> [47: bb8, otherwise: bb11];
}
bb8: {
- switchInt(copy (*_2)[0 of 3]) -> [47: bb9, otherwise: bb7];
+ switchInt(copy (*_2)[1 of 3]) -> [47: bb9, otherwise: bb11];
}
bb9: {
- switchInt(copy (*_2)[1 of 3]) -> [47: bb10, otherwise: bb7];
+ switchInt(copy (*_2)[2 of 3]) -> [47: bb10, 33: bb10, otherwise: bb11];
}
bb10: {
- switchInt(copy (*_2)[2 of 3]) -> [47: bb12, 33: bb11, otherwise: bb7];
- }
-
- bb11: {
_0 = const true;
- goto -> bb14;
+ goto -> bb12;
}
- bb12: {
- _0 = const true;
- goto -> bb14;
- }
-
- bb13: {
+ bb11: {
_0 = const false;
- goto -> bb14;
+ goto -> bb12;
}
- bb14: {
+ bb12: {
StorageDead(_2);
return;
}
}

View file

@ -1,100 +0,0 @@
- // MIR for `is_line_doc_comment_2` before DeduplicateBlocks
+ // MIR for `is_line_doc_comment_2` after DeduplicateBlocks
fn is_line_doc_comment_2(_1: &str) -> bool {
debug s => _1;
let mut _0: bool;
let mut _2: &[u8];
let mut _3: &str;
let mut _4: usize;
let mut _5: usize;
let mut _6: bool;
let mut _7: usize;
let mut _8: usize;
let mut _9: bool;
bb0: {
StorageLive(_2);
StorageLive(_3);
_3 = &(*_1);
_2 = core::str::<impl str>::as_bytes(move _3) -> [return: bb1, unwind continue];
}
bb1: {
StorageDead(_3);
_4 = Len((*_2));
_5 = const 4_usize;
_6 = Ge(move _4, move _5);
switchInt(move _6) -> [0: bb2, otherwise: bb3];
}
bb2: {
_7 = Len((*_2));
_8 = const 3_usize;
_9 = Ge(move _7, move _8);
- switchInt(move _9) -> [0: bb7, otherwise: bb8];
+ switchInt(move _9) -> [0: bb11, otherwise: bb7];
}
bb3: {
switchInt(copy (*_2)[0 of 4]) -> [47: bb4, otherwise: bb2];
}
bb4: {
switchInt(copy (*_2)[1 of 4]) -> [47: bb5, otherwise: bb2];
}
bb5: {
switchInt(copy (*_2)[2 of 4]) -> [47: bb6, otherwise: bb2];
}
bb6: {
- switchInt(copy (*_2)[3 of 4]) -> [47: bb13, otherwise: bb2];
+ switchInt(copy (*_2)[3 of 4]) -> [47: bb11, otherwise: bb2];
}
bb7: {
- _0 = const false;
- goto -> bb14;
+ switchInt(copy (*_2)[0 of 3]) -> [47: bb8, otherwise: bb11];
}
bb8: {
- switchInt(copy (*_2)[0 of 3]) -> [47: bb9, otherwise: bb7];
+ switchInt(copy (*_2)[1 of 3]) -> [47: bb9, otherwise: bb11];
}
bb9: {
- switchInt(copy (*_2)[1 of 3]) -> [47: bb10, otherwise: bb7];
+ switchInt(copy (*_2)[2 of 3]) -> [47: bb10, 33: bb10, otherwise: bb11];
}
bb10: {
- switchInt(copy (*_2)[2 of 3]) -> [47: bb12, 33: bb11, otherwise: bb7];
- }
-
- bb11: {
_0 = const true;
- goto -> bb14;
+ goto -> bb12;
}
- bb12: {
- _0 = const true;
- goto -> bb14;
- }
-
- bb13: {
+ bb11: {
_0 = const false;
- goto -> bb14;
+ goto -> bb12;
}
- bb14: {
+ bb12: {
StorageDead(_2);
return;
}
}

View file

@ -1,17 +0,0 @@
// skip-filecheck
// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
//@ test-mir-pass: DeduplicateBlocks
// EMIT_MIR deduplicate_blocks.is_line_doc_comment_2.DeduplicateBlocks.diff
pub const fn is_line_doc_comment_2(s: &str) -> bool {
match s.as_bytes() {
[b'/', b'/', b'/', b'/', ..] => false,
[b'/', b'/', b'/', ..] => true,
[b'/', b'/', b'!', ..] => true,
_ => false,
}
}
fn main() {
is_line_doc_comment_2("asd");
}