From 9c200435a9ea2a7c6d651eab5b3ef90ae4a1efe7 Mon Sep 17 00:00:00 2001 From: dianqk Date: Sat, 5 Apr 2025 20:52:30 +0800 Subject: [PATCH 1/4] mir-opt: Use one MirPatch in MatchBranchSimplification --- .../rustc_mir_transform/src/match_branches.rs | 39 +++++++------------ 1 file changed, 14 insertions(+), 25 deletions(-) diff --git a/compiler/rustc_mir_transform/src/match_branches.rs b/compiler/rustc_mir_transform/src/match_branches.rs index 0d9d0368d3729..a234da8ba2dca 100644 --- a/compiler/rustc_mir_transform/src/match_branches.rs +++ b/compiler/rustc_mir_transform/src/match_branches.rs @@ -19,32 +19,32 @@ impl<'tcx> crate::MirPass<'tcx> for MatchBranchSimplification { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let typing_env = body.typing_env(tcx); - let mut should_cleanup = false; - for i in 0..body.basic_blocks.len() { - let bbs = &*body.basic_blocks; - let bb_idx = BasicBlock::from_usize(i); - match bbs[bb_idx].terminator().kind { + let mut apply_patch = false; + let mut patch = MirPatch::new(body); + for (bb, data) in body.basic_blocks.iter_enumerated() { + match data.terminator().kind { TerminatorKind::SwitchInt { discr: ref _discr @ (Operand::Copy(_) | Operand::Move(_)), ref targets, .. // We require that the possible target blocks don't contain this block. - } if !targets.all_targets().contains(&bb_idx) => {} + } if !targets.all_targets().contains(&bb) => {} // Only optimize switch int statements _ => continue, }; - if SimplifyToIf.simplify(tcx, body, bb_idx, typing_env).is_some() { - should_cleanup = true; + if SimplifyToIf.simplify(tcx, body, &mut patch, bb, typing_env).is_some() { + apply_patch = true; continue; } - if SimplifyToExp::default().simplify(tcx, body, bb_idx, typing_env).is_some() { - should_cleanup = true; + if SimplifyToExp::default().simplify(tcx, body, &mut patch, bb, typing_env).is_some() { + apply_patch = true; continue; } } - if should_cleanup { + if apply_patch { + patch.apply(body); simplify_cfg(body); } } @@ -61,7 +61,8 @@ trait SimplifyMatch<'tcx> { fn simplify( &mut self, tcx: TyCtxt<'tcx>, - body: &mut Body<'tcx>, + body: &Body<'tcx>, + patch: &mut MirPatch<'tcx>, switch_bb_idx: BasicBlock, typing_env: ty::TypingEnv<'tcx>, ) -> Option<()> { @@ -74,8 +75,6 @@ trait SimplifyMatch<'tcx> { let discr_ty = discr.ty(body.local_decls(), tcx); self.can_simplify(tcx, targets, typing_env, bbs, discr_ty)?; - let mut patch = MirPatch::new(body); - // Take ownership of items now that we know we can optimize. let discr = discr.clone(); @@ -88,19 +87,9 @@ trait SimplifyMatch<'tcx> { let parent_end = Location { block: switch_bb_idx, statement_index }; patch.add_statement(parent_end, StatementKind::StorageLive(discr_local)); patch.add_assign(parent_end, Place::from(discr_local), Rvalue::Use(discr)); - self.new_stmts( - tcx, - targets, - typing_env, - &mut patch, - parent_end, - bbs, - discr_local, - discr_ty, - ); + self.new_stmts(tcx, targets, typing_env, patch, parent_end, bbs, discr_local, discr_ty); patch.add_statement(parent_end, StatementKind::StorageDead(discr_local)); patch.patch_terminator(switch_bb_idx, bbs[first].terminator().kind.clone()); - patch.apply(body); Some(()) } From 4fa4272a72053b1edfafb72484d36381aad4ba7a Mon Sep 17 00:00:00 2001 From: dianqk Date: Sun, 6 Apr 2025 14:37:42 +0800 Subject: [PATCH 2/4] mir-opt: run MatchBranchSimplification after GVN --- compiler/rustc_mir_transform/src/lib.rs | 3 +-- ...l_unsigned_smaller.Inline.panic-abort.diff | 14 +++++------ ..._unsigned_smaller.Inline.panic-unwind.diff | 14 +++++------ ..._shr_signed_bigger.Inline.panic-abort.diff | 14 +++++------ ...shr_signed_bigger.Inline.panic-unwind.diff | 14 +++++------ ...d.unwrap_unchecked.Inline.panic-abort.diff | 16 ++++++------- ....unwrap_unchecked.Inline.panic-unwind.diff | 16 ++++++------- ...hecked.InstSimplify-after-simplifycfg.diff | 18 +++++++------- ...e_75439.foo.MatchBranchSimplification.diff | 24 ++++++------------- ....foo.SimplifyLocals-final.panic-abort.diff | 5 ++-- ...foo.SimplifyLocals-final.panic-unwind.diff | 5 ++-- 11 files changed, 67 insertions(+), 76 deletions(-) diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index dfd07f0fb1681..0de8af3e2a4a3 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -700,8 +700,6 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { // Now, we need to shrink the generated MIR. &ref_prop::ReferencePropagation, &sroa::ScalarReplacementOfAggregates, - &match_branches::MatchBranchSimplification, - // inst combine is after MatchBranchSimplification to clean up Ne(_1, false) &multiple_return_terminators::MultipleReturnTerminators, // After simplifycfg, it allows us to discover new opportunities for peephole // optimizations. @@ -710,6 +708,7 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { &dead_store_elimination::DeadStoreElimination::Initial, &gvn::GVN, &simplify::SimplifyLocals::AfterGVN, + &match_branches::MatchBranchSimplification, &dataflow_const_prop::DataflowConstProp, &single_use_consts::SingleUseConsts, &o1(simplify_branches::SimplifyConstCondition::AfterConstProp), diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-abort.diff b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-abort.diff index f36157a762c27..39ba480d20330 100644 --- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-abort.diff +++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-abort.diff @@ -8,9 +8,9 @@ let mut _3: u16; let mut _4: u32; + scope 1 (inlined core::num::::unchecked_shl) { -+ let mut _5: bool; -+ let _6: (); ++ let _5: (); + scope 2 (inlined core::ub_checks::check_language_ub) { ++ let mut _6: bool; + scope 3 (inlined core::ub_checks::check_language_ub::runtime) { + } + } @@ -22,20 +22,20 @@ StorageLive(_4); _4 = copy _2; - _0 = core::num::::unchecked_shl(move _3, move _4) -> [return: bb1, unwind unreachable]; -+ StorageLive(_6); + StorageLive(_5); -+ _5 = UbChecks(); -+ switchInt(move _5) -> [0: bb2, otherwise: bb1]; ++ StorageLive(_6); ++ _6 = UbChecks(); ++ switchInt(copy _6) -> [0: bb2, otherwise: bb1]; } bb1: { -+ _6 = core::num::::unchecked_shl::precondition_check(copy _4) -> [return: bb2, unwind unreachable]; ++ _5 = core::num::::unchecked_shl::precondition_check(copy _4) -> [return: bb2, unwind unreachable]; + } + + bb2: { -+ StorageDead(_5); + _0 = ShlUnchecked(copy _3, copy _4); + StorageDead(_6); ++ StorageDead(_5); StorageDead(_4); StorageDead(_3); return; diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-unwind.diff b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-unwind.diff index be1b066c6c1b4..5a758d3574060 100644 --- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-unwind.diff +++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.panic-unwind.diff @@ -8,9 +8,9 @@ let mut _3: u16; let mut _4: u32; + scope 1 (inlined core::num::::unchecked_shl) { -+ let mut _5: bool; -+ let _6: (); ++ let _5: (); + scope 2 (inlined core::ub_checks::check_language_ub) { ++ let mut _6: bool; + scope 3 (inlined core::ub_checks::check_language_ub::runtime) { + } + } @@ -22,20 +22,20 @@ StorageLive(_4); _4 = copy _2; - _0 = core::num::::unchecked_shl(move _3, move _4) -> [return: bb1, unwind continue]; -+ StorageLive(_6); + StorageLive(_5); -+ _5 = UbChecks(); -+ switchInt(move _5) -> [0: bb2, otherwise: bb1]; ++ StorageLive(_6); ++ _6 = UbChecks(); ++ switchInt(copy _6) -> [0: bb2, otherwise: bb1]; } bb1: { -+ _6 = core::num::::unchecked_shl::precondition_check(copy _4) -> [return: bb2, unwind unreachable]; ++ _5 = core::num::::unchecked_shl::precondition_check(copy _4) -> [return: bb2, unwind unreachable]; + } + + bb2: { -+ StorageDead(_5); + _0 = ShlUnchecked(copy _3, copy _4); + StorageDead(_6); ++ StorageDead(_5); StorageDead(_4); StorageDead(_3); return; diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.Inline.panic-abort.diff b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.Inline.panic-abort.diff index 360687f3c4e7f..a0caf141f2d06 100644 --- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.Inline.panic-abort.diff +++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.Inline.panic-abort.diff @@ -8,9 +8,9 @@ let mut _3: i64; let mut _4: u32; + scope 1 (inlined core::num::::unchecked_shr) { -+ let mut _5: bool; -+ let _6: (); ++ let _5: (); + scope 2 (inlined core::ub_checks::check_language_ub) { ++ let mut _6: bool; + scope 3 (inlined core::ub_checks::check_language_ub::runtime) { + } + } @@ -22,20 +22,20 @@ StorageLive(_4); _4 = copy _2; - _0 = core::num::::unchecked_shr(move _3, move _4) -> [return: bb1, unwind unreachable]; -+ StorageLive(_6); + StorageLive(_5); -+ _5 = UbChecks(); -+ switchInt(move _5) -> [0: bb2, otherwise: bb1]; ++ StorageLive(_6); ++ _6 = UbChecks(); ++ switchInt(copy _6) -> [0: bb2, otherwise: bb1]; } bb1: { -+ _6 = core::num::::unchecked_shr::precondition_check(copy _4) -> [return: bb2, unwind unreachable]; ++ _5 = core::num::::unchecked_shr::precondition_check(copy _4) -> [return: bb2, unwind unreachable]; + } + + bb2: { -+ StorageDead(_5); + _0 = ShrUnchecked(copy _3, copy _4); + StorageDead(_6); ++ StorageDead(_5); StorageDead(_4); StorageDead(_3); return; diff --git a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.Inline.panic-unwind.diff b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.Inline.panic-unwind.diff index 986df55df0372..633089e7b2a28 100644 --- a/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.Inline.panic-unwind.diff +++ b/tests/mir-opt/inline/unchecked_shifts.unchecked_shr_signed_bigger.Inline.panic-unwind.diff @@ -8,9 +8,9 @@ let mut _3: i64; let mut _4: u32; + scope 1 (inlined core::num::::unchecked_shr) { -+ let mut _5: bool; -+ let _6: (); ++ let _5: (); + scope 2 (inlined core::ub_checks::check_language_ub) { ++ let mut _6: bool; + scope 3 (inlined core::ub_checks::check_language_ub::runtime) { + } + } @@ -22,20 +22,20 @@ StorageLive(_4); _4 = copy _2; - _0 = core::num::::unchecked_shr(move _3, move _4) -> [return: bb1, unwind continue]; -+ StorageLive(_6); + StorageLive(_5); -+ _5 = UbChecks(); -+ switchInt(move _5) -> [0: bb2, otherwise: bb1]; ++ StorageLive(_6); ++ _6 = UbChecks(); ++ switchInt(copy _6) -> [0: bb2, otherwise: bb1]; } bb1: { -+ _6 = core::num::::unchecked_shr::precondition_check(copy _4) -> [return: bb2, unwind unreachable]; ++ _5 = core::num::::unchecked_shr::precondition_check(copy _4) -> [return: bb2, unwind unreachable]; + } + + bb2: { -+ StorageDead(_5); + _0 = ShrUnchecked(copy _3, copy _4); + StorageDead(_6); ++ StorageDead(_5); StorageDead(_4); StorageDead(_3); return; diff --git a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-abort.diff b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-abort.diff index 28878736ed7cd..a5986a4315a2d 100644 --- a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-abort.diff +++ b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-abort.diff @@ -10,9 +10,9 @@ + scope 2 { + } + scope 3 (inlined unreachable_unchecked) { -+ let mut _4: bool; -+ let _5: (); ++ let _4: (); + scope 4 (inlined core::ub_checks::check_language_ub) { ++ let mut _5: bool; + scope 5 (inlined core::ub_checks::check_language_ub::runtime) { + } + } @@ -24,7 +24,7 @@ _2 = move _1; - _0 = Option::::unwrap_unchecked(move _2) -> [return: bb1, unwind unreachable]; + StorageLive(_3); -+ StorageLive(_5); ++ StorageLive(_4); + _3 = discriminant(_2); + switchInt(move _3) -> [0: bb2, 1: bb3, otherwise: bb1]; } @@ -34,15 +34,15 @@ + } + + bb2: { -+ StorageLive(_4); -+ _4 = UbChecks(); -+ assume(copy _4); -+ _5 = unreachable_unchecked::precondition_check() -> [return: bb1, unwind unreachable]; ++ StorageLive(_5); ++ _5 = UbChecks(); ++ assume(copy _5); ++ _4 = unreachable_unchecked::precondition_check() -> [return: bb1, unwind unreachable]; + } + + bb3: { + _0 = move ((_2 as Some).0: T); -+ StorageDead(_5); ++ StorageDead(_4); + StorageDead(_3); StorageDead(_2); return; diff --git a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-unwind.diff b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-unwind.diff index 27b6bb6a5bb23..12b03a6b6d921 100644 --- a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-unwind.diff +++ b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-unwind.diff @@ -10,9 +10,9 @@ + scope 2 { + } + scope 3 (inlined unreachable_unchecked) { -+ let mut _4: bool; -+ let _5: (); ++ let _4: (); + scope 4 (inlined core::ub_checks::check_language_ub) { ++ let mut _5: bool; + scope 5 (inlined core::ub_checks::check_language_ub::runtime) { + } + } @@ -24,7 +24,7 @@ _2 = move _1; - _0 = Option::::unwrap_unchecked(move _2) -> [return: bb1, unwind: bb2]; + StorageLive(_3); -+ StorageLive(_5); ++ StorageLive(_4); + _3 = discriminant(_2); + switchInt(move _3) -> [0: bb2, 1: bb3, otherwise: bb1]; } @@ -38,15 +38,15 @@ - bb2 (cleanup): { - resume; + bb2: { -+ StorageLive(_4); -+ _4 = UbChecks(); -+ assume(copy _4); -+ _5 = unreachable_unchecked::precondition_check() -> [return: bb1, unwind unreachable]; ++ StorageLive(_5); ++ _5 = UbChecks(); ++ assume(copy _5); ++ _4 = unreachable_unchecked::precondition_check() -> [return: bb1, unwind unreachable]; + } + + bb3: { + _0 = move ((_2 as Some).0: T); -+ StorageDead(_5); ++ StorageDead(_4); + StorageDead(_3); + StorageDead(_2); + return; diff --git a/tests/mir-opt/instsimplify/ub_check.unwrap_unchecked.InstSimplify-after-simplifycfg.diff b/tests/mir-opt/instsimplify/ub_check.unwrap_unchecked.InstSimplify-after-simplifycfg.diff index 5fee9a6733dd0..82353a2d5404a 100644 --- a/tests/mir-opt/instsimplify/ub_check.unwrap_unchecked.InstSimplify-after-simplifycfg.diff +++ b/tests/mir-opt/instsimplify/ub_check.unwrap_unchecked.InstSimplify-after-simplifycfg.diff @@ -10,9 +10,9 @@ scope 2 { } scope 3 (inlined unreachable_unchecked) { - let mut _4: bool; - let _5: (); + let _4: (); scope 4 (inlined core::ub_checks::check_language_ub) { + let mut _5: bool; scope 5 (inlined core::ub_checks::check_language_ub::runtime) { } } @@ -23,7 +23,7 @@ StorageLive(_2); _2 = copy _1; StorageLive(_3); - StorageLive(_5); + StorageLive(_4); _3 = discriminant(_2); switchInt(move _3) -> [0: bb2, 1: bb3, otherwise: bb1]; } @@ -33,16 +33,16 @@ } bb2: { - StorageLive(_4); -- _4 = UbChecks(); -+ _4 = const false; - assume(copy _4); - _5 = unreachable_unchecked::precondition_check() -> [return: bb1, unwind unreachable]; + StorageLive(_5); +- _5 = UbChecks(); ++ _5 = const false; + assume(copy _5); + _4 = unreachable_unchecked::precondition_check() -> [return: bb1, unwind unreachable]; } bb3: { _0 = move ((_2 as Some).0: i32); - StorageDead(_5); + StorageDead(_4); StorageDead(_3); StorageDead(_2); return; diff --git a/tests/mir-opt/issues/issue_75439.foo.MatchBranchSimplification.diff b/tests/mir-opt/issues/issue_75439.foo.MatchBranchSimplification.diff index 1ab9be9665221..d8eace98d556e 100644 --- a/tests/mir-opt/issues/issue_75439.foo.MatchBranchSimplification.diff +++ b/tests/mir-opt/issues/issue_75439.foo.MatchBranchSimplification.diff @@ -5,23 +5,18 @@ debug bytes => _1; let mut _0: std::option::Option<[u8; 4]>; let _2: [u32; 4]; - let mut _3: [u8; 16]; - let mut _5: [u8; 4]; - let mut _6: u32; + let mut _4: [u8; 4]; scope 1 { debug dwords => _2; scope 2 { - debug ip => _4; - let _4: u32; + debug ip => _3; + let _3: u32; } } bb0: { StorageLive(_2); - StorageLive(_3); - _3 = copy _1; - _2 = move _3 as [u32; 4] (Transmute); - StorageDead(_3); + _2 = copy _1 as [u32; 4] (Transmute); switchInt(copy _2[0 of 4]) -> [0: bb1, otherwise: bb4]; } @@ -34,15 +29,10 @@ } bb3: { + _3 = copy _2[3 of 4]; StorageLive(_4); - _4 = copy _2[3 of 4]; - StorageLive(_5); - StorageLive(_6); - _6 = copy _4; - _5 = move _6 as [u8; 4] (Transmute); - StorageDead(_6); - _0 = Option::<[u8; 4]>::Some(move _5); - StorageDead(_5); + _4 = copy _3 as [u8; 4] (Transmute); + _0 = Option::<[u8; 4]>::Some(move _4); StorageDead(_4); goto -> bb5; } diff --git a/tests/mir-opt/simplify_locals_fixedpoint.foo.SimplifyLocals-final.panic-abort.diff b/tests/mir-opt/simplify_locals_fixedpoint.foo.SimplifyLocals-final.panic-abort.diff index c363dfcbf70bb..ff1bc58524bc2 100644 --- a/tests/mir-opt/simplify_locals_fixedpoint.foo.SimplifyLocals-final.panic-abort.diff +++ b/tests/mir-opt/simplify_locals_fixedpoint.foo.SimplifyLocals-final.panic-abort.diff @@ -8,6 +8,9 @@ let mut _3: std::option::Option; let mut _4: isize; let mut _5: isize; +- let mut _7: bool; +- let mut _8: u8; +- let mut _9: bool; scope 1 { debug a => _6; let _6: u8; @@ -32,9 +35,7 @@ } bb2: { - StorageLive(_6); _6 = copy (((_1.0: std::option::Option) as Some).0: u8); - StorageDead(_6); goto -> bb3; } diff --git a/tests/mir-opt/simplify_locals_fixedpoint.foo.SimplifyLocals-final.panic-unwind.diff b/tests/mir-opt/simplify_locals_fixedpoint.foo.SimplifyLocals-final.panic-unwind.diff index 895b0067d2e63..2c289c664754a 100644 --- a/tests/mir-opt/simplify_locals_fixedpoint.foo.SimplifyLocals-final.panic-unwind.diff +++ b/tests/mir-opt/simplify_locals_fixedpoint.foo.SimplifyLocals-final.panic-unwind.diff @@ -8,6 +8,9 @@ let mut _3: std::option::Option; let mut _4: isize; let mut _5: isize; +- let mut _7: bool; +- let mut _8: u8; +- let mut _9: bool; scope 1 { debug a => _6; let _6: u8; @@ -32,9 +35,7 @@ } bb2: { - StorageLive(_6); _6 = copy (((_1.0: std::option::Option) as Some).0: u8); - StorageDead(_6); goto -> bb3; } From 42118389d387e65ab6dbac780c3a48383dc05e45 Mon Sep 17 00:00:00 2001 From: dianqk Date: Sun, 6 Apr 2025 13:33:28 +0800 Subject: [PATCH 3/4] mir-opt: ignore dead store stmts in MatchBranchSimplification --- .../src/dead_store_elimination.rs | 46 ++++++++++-- .../rustc_mir_transform/src/match_branches.rs | 70 +++++++++++++++++-- compiler/rustc_mir_transform/src/simplify.rs | 10 +-- ...ranches.bar.MatchBranchSimplification.diff | 2 +- ...tch_u8_i8_2.MatchBranchSimplification.diff | 10 +-- ..._dead_store.MatchBranchSimplification.diff | 47 +++++++++++++ ...ailed_len_1.MatchBranchSimplification.diff | 2 +- ...ailed_len_2.MatchBranchSimplification.diff | 2 +- tests/mir-opt/matches_reduce_branches.rs | 46 +++++++++++- ...py.enum_clone_as_copy.PreCodegen.after.mir | 38 +++------- tests/mir-opt/pre-codegen/clone_as_copy.rs | 4 +- .../try_identity.old.PreCodegen.after.mir | 29 ++------ 12 files changed, 226 insertions(+), 80 deletions(-) create mode 100644 tests/mir-opt/matches_reduce_branches.match_u8_i8_dead_store.MatchBranchSimplification.diff diff --git a/compiler/rustc_mir_transform/src/dead_store_elimination.rs b/compiler/rustc_mir_transform/src/dead_store_elimination.rs index eea2b0990d730..059387652903c 100644 --- a/compiler/rustc_mir_transform/src/dead_store_elimination.rs +++ b/compiler/rustc_mir_transform/src/dead_store_elimination.rs @@ -12,6 +12,7 @@ //! will still not cause any further changes. //! +use rustc_index::IndexVec; use rustc_middle::bug; use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::*; @@ -24,17 +25,49 @@ use rustc_mir_dataflow::impls::{ use crate::util::is_within_packed; +pub(super) enum ModifyBasicBlocks<'tcx, 'a> { + Direct(&'a mut Body<'tcx>), + BasicBlocks(&'a Body<'tcx>, &'a mut IndexVec>), +} + +impl<'tcx, 'a> ModifyBasicBlocks<'tcx, 'a> { + pub(super) fn body(&self) -> &Body<'tcx> { + match self { + ModifyBasicBlocks::Direct(body) => body, + ModifyBasicBlocks::BasicBlocks(body, _) => body, + } + } + + pub(super) fn bbs(&mut self) -> &mut IndexVec> { + match self { + ModifyBasicBlocks::Direct(body) => body.basic_blocks.as_mut_preserves_cfg(), + ModifyBasicBlocks::BasicBlocks(_, bbs) => bbs, + } + } +} + /// Performs the optimization on the body /// /// The `borrowed` set must be a `DenseBitSet` of all the locals that are ever borrowed in this /// body. It can be generated via the [`borrowed_locals`] function. -fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { +pub(super) fn eliminate<'tcx>( + tcx: TyCtxt<'tcx>, + mut modify_basic_blocks: ModifyBasicBlocks<'tcx, '_>, + ignore_debuginfo: bool, + arg_copy_to_move: bool, +) { + let body = modify_basic_blocks.body(); let borrowed_locals = borrowed_locals(body); // If the user requests complete debuginfo, mark the locals that appear in it as live, so // we don't remove assignments to them. - let mut always_live = debuginfo_locals(body); - always_live.union(&borrowed_locals); + let always_live = if ignore_debuginfo { + borrowed_locals.clone() + } else { + let mut always_live = debuginfo_locals(body); + always_live.union(&borrowed_locals); + always_live + }; let mut live = MaybeTransitiveLiveLocals::new(&always_live) .iterate_to_fixpoint(tcx, body, None) @@ -46,7 +79,8 @@ fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let mut patch = Vec::new(); for (bb, bb_data) in traversal::preorder(body) { - if let TerminatorKind::Call { ref args, .. } = bb_data.terminator().kind { + if arg_copy_to_move && let TerminatorKind::Call { ref args, .. } = bb_data.terminator().kind + { let loc = Location { block: bb, statement_index: bb_data.statements.len() }; // Position ourselves between the evaluation of `args` and the write to `destination`. @@ -113,7 +147,7 @@ fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { return; } - let bbs = body.basic_blocks.as_mut_preserves_cfg(); + let bbs = modify_basic_blocks.bbs(); for Location { block, statement_index } in patch { bbs[block].statements[statement_index].make_nop(); } @@ -145,7 +179,7 @@ impl<'tcx> crate::MirPass<'tcx> for DeadStoreElimination { } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - eliminate(tcx, body); + eliminate(tcx, ModifyBasicBlocks::Direct(body), false, true); } fn is_required(&self) -> bool { diff --git a/compiler/rustc_mir_transform/src/match_branches.rs b/compiler/rustc_mir_transform/src/match_branches.rs index a234da8ba2dca..85a8afcbeb1d9 100644 --- a/compiler/rustc_mir_transform/src/match_branches.rs +++ b/compiler/rustc_mir_transform/src/match_branches.rs @@ -1,14 +1,17 @@ use std::iter; use rustc_abi::Integer; -use rustc_index::IndexSlice; +use rustc_index::{IndexSlice, IndexVec}; +use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::*; use rustc_middle::ty::layout::{IntegerExt, TyAndLayout}; use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt}; use tracing::instrument; use super::simplify::simplify_cfg; +use crate::dead_store_elimination::{self, ModifyBasicBlocks}; use crate::patch::MirPatch; +use crate::simplify::strip_nops; pub(super) struct MatchBranchSimplification; @@ -20,6 +23,17 @@ impl<'tcx> crate::MirPass<'tcx> for MatchBranchSimplification { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let typing_env = body.typing_env(tcx); let mut apply_patch = false; + let mut bbs = body.basic_blocks.clone(); + let bbs = bbs.as_mut_preserves_cfg(); + // We can ignore the dead store statements when merging branches. + dead_store_elimination::eliminate( + tcx, + ModifyBasicBlocks::BasicBlocks(body, bbs), + true, + false, + ); + eliminate_unused_storage_mark(body, bbs); + strip_nops(bbs.as_mut_slice()); let mut patch = MirPatch::new(body); for (bb, data) in body.basic_blocks.iter_enumerated() { match data.terminator().kind { @@ -33,11 +47,17 @@ impl<'tcx> crate::MirPass<'tcx> for MatchBranchSimplification { _ => continue, }; - if SimplifyToIf.simplify(tcx, body, &mut patch, bb, typing_env).is_some() { + if SimplifyToIf + .simplify(tcx, body, bbs.as_slice(), &mut patch, bb, typing_env) + .is_some() + { apply_patch = true; continue; } - if SimplifyToExp::default().simplify(tcx, body, &mut patch, bb, typing_env).is_some() { + if SimplifyToExp::default() + .simplify(tcx, body, bbs.as_slice(), &mut patch, bb, typing_env) + .is_some() + { apply_patch = true; continue; } @@ -62,11 +82,11 @@ trait SimplifyMatch<'tcx> { &mut self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>, + bbs: &IndexSlice>, patch: &mut MirPatch<'tcx>, switch_bb_idx: BasicBlock, typing_env: ty::TypingEnv<'tcx>, ) -> Option<()> { - let bbs = &body.basic_blocks; let (discr, targets) = match bbs[switch_bb_idx].terminator().kind { TerminatorKind::SwitchInt { ref discr, ref targets, .. } => (discr, targets), _ => unreachable!(), @@ -83,7 +103,7 @@ trait SimplifyMatch<'tcx> { let discr_local = patch.new_temp(discr_ty, source_info.span); let (_, first) = targets.iter().next().unwrap(); - let statement_index = bbs[switch_bb_idx].statements.len(); + let statement_index = body.basic_blocks[switch_bb_idx].statements.len(); let parent_end = Location { block: switch_bb_idx, statement_index }; patch.add_statement(parent_end, StatementKind::StorageLive(discr_local)); patch.add_assign(parent_end, Place::from(discr_local), Rvalue::Use(discr)); @@ -526,3 +546,43 @@ impl<'tcx> SimplifyMatch<'tcx> for SimplifyToExp { } } } + +struct EliminateUnusedStorageMark { + storage_live_locals: IndexVec>, +} + +impl<'tcx> Visitor<'tcx> for EliminateUnusedStorageMark { + fn visit_local(&mut self, local: Local, _: visit::PlaceContext, _: Location) { + self.storage_live_locals[local] = None; + } +} + +fn eliminate_unused_storage_mark<'tcx>( + body: &Body<'tcx>, + basic_blocks: &mut IndexVec>, +) { + for (bb, data) in basic_blocks.iter_enumerated_mut() { + let mut unused_storage_mark = EliminateUnusedStorageMark { + storage_live_locals: IndexVec::from_elem_n(None, body.local_decls.len()), + }; + for stmt_index in 0..data.statements.len() { + let loc = Location { block: bb, statement_index: stmt_index }; + match data.statements[stmt_index].kind { + StatementKind::StorageLive(local) => { + unused_storage_mark.storage_live_locals[local] = Some(stmt_index); + } + StatementKind::StorageDead(local) + if let Some(live_stmt_index) = + unused_storage_mark.storage_live_locals[local] => + { + data.statements[live_stmt_index].make_nop(); + data.statements[stmt_index].make_nop(); + unused_storage_mark.storage_live_locals[local] = None; + } + _ => { + unused_storage_mark.visit_statement(&data.statements[stmt_index], loc); + } + } + } + } +} diff --git a/compiler/rustc_mir_transform/src/simplify.rs b/compiler/rustc_mir_transform/src/simplify.rs index 84905f4a400f3..92dfea4190d33 100644 --- a/compiler/rustc_mir_transform/src/simplify.rs +++ b/compiler/rustc_mir_transform/src/simplify.rs @@ -116,7 +116,7 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> { } fn simplify(mut self) { - self.strip_nops(); + strip_nops(self.basic_blocks); // Vec of the blocks that should be merged. We store the indices here, instead of the // statements itself to avoid moving the (relatively) large statements twice. @@ -276,11 +276,11 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> { terminator.kind = TerminatorKind::Goto { target: first_succ }; true } +} - fn strip_nops(&mut self) { - for blk in self.basic_blocks.iter_mut() { - blk.statements.retain(|stmt| !matches!(stmt.kind, StatementKind::Nop)) - } +pub(super) fn strip_nops(basic_blocks: &mut IndexSlice>) { + for blk in basic_blocks.iter_mut() { + blk.statements.retain(|stmt| !matches!(stmt.kind, StatementKind::Nop)) } } diff --git a/tests/mir-opt/matches_reduce_branches.bar.MatchBranchSimplification.diff b/tests/mir-opt/matches_reduce_branches.bar.MatchBranchSimplification.diff index 597d93926f169..69cdb451fa754 100644 --- a/tests/mir-opt/matches_reduce_branches.bar.MatchBranchSimplification.diff +++ b/tests/mir-opt/matches_reduce_branches.bar.MatchBranchSimplification.diff @@ -45,7 +45,7 @@ + _3 = Eq(copy _11, const 7_i32); _4 = const false; _5 = const true; - _6 = (); +- _6 = (); - goto -> bb3; - } - diff --git a/tests/mir-opt/matches_reduce_branches.match_u8_i8_2.MatchBranchSimplification.diff b/tests/mir-opt/matches_reduce_branches.match_u8_i8_2.MatchBranchSimplification.diff index f14b3af9660aa..d97cf629b1c4e 100644 --- a/tests/mir-opt/matches_reduce_branches.match_u8_i8_2.MatchBranchSimplification.diff +++ b/tests/mir-opt/matches_reduce_branches.match_u8_i8_2.MatchBranchSimplification.diff @@ -33,11 +33,7 @@ - bb2: { - _2 = const -1_i8; - _3 = const -1_i8; -+ StorageLive(_8); -+ _8 = move _5; -+ _2 = copy _8 as i8 (IntToInt); -+ _3 = copy _8 as i8 (IntToInt); - _4 = (); +- _4 = (); - goto -> bb6; - } - @@ -63,6 +59,10 @@ - } - - bb6: { ++ StorageLive(_8); ++ _8 = move _5; ++ _2 = copy _8 as i8 (IntToInt); ++ _3 = copy _8 as i8 (IntToInt); + StorageDead(_8); StorageDead(_4); StorageLive(_6); diff --git a/tests/mir-opt/matches_reduce_branches.match_u8_i8_dead_store.MatchBranchSimplification.diff b/tests/mir-opt/matches_reduce_branches.match_u8_i8_dead_store.MatchBranchSimplification.diff new file mode 100644 index 0000000000000..8874333c5893d --- /dev/null +++ b/tests/mir-opt/matches_reduce_branches.match_u8_i8_dead_store.MatchBranchSimplification.diff @@ -0,0 +1,47 @@ +- // MIR for `match_u8_i8_dead_store` before MatchBranchSimplification ++ // MIR for `match_u8_i8_dead_store` after MatchBranchSimplification + + fn match_u8_i8_dead_store(_1: EnumAu8) -> i8 { + let mut _0: i8; + let mut _2: u8; ++ let mut _3: u8; + + bb0: { + _2 = discriminant(_1); +- switchInt(copy _2) -> [0: bb1, 127: bb2, 128: bb3, 255: bb4, otherwise: bb5]; +- } +- +- bb1: { +- _0 = const 0_i8; +- goto -> bb6; +- } +- +- bb2: { +- _0 = const 1_i8; +- _0 = const i8::MAX; +- goto -> bb6; +- } +- +- bb3: { +- _0 = const i8::MIN; +- goto -> bb6; +- } +- +- bb4: { +- _0 = const -1_i8; +- goto -> bb6; +- } +- +- bb5: { +- unreachable; +- } +- +- bb6: { ++ StorageLive(_3); ++ _3 = copy _2; ++ _0 = copy _3 as i8 (IntToInt); ++ StorageDead(_3); + return; + } + } + diff --git a/tests/mir-opt/matches_reduce_branches.match_u8_i8_failed_len_1.MatchBranchSimplification.diff b/tests/mir-opt/matches_reduce_branches.match_u8_i8_failed_len_1.MatchBranchSimplification.diff index a1d58424ecd1a..027ace9e4256e 100644 --- a/tests/mir-opt/matches_reduce_branches.match_u8_i8_failed_len_1.MatchBranchSimplification.diff +++ b/tests/mir-opt/matches_reduce_branches.match_u8_i8_failed_len_1.MatchBranchSimplification.diff @@ -17,7 +17,7 @@ bb2: { _0 = const i8::MAX; - _0 = const i8::MAX; + _0 = Add(copy _0, const 0_i8); goto -> bb6; } diff --git a/tests/mir-opt/matches_reduce_branches.match_u8_i8_failed_len_2.MatchBranchSimplification.diff b/tests/mir-opt/matches_reduce_branches.match_u8_i8_failed_len_2.MatchBranchSimplification.diff index 6c4ade1b6ca96..e97704df48743 100644 --- a/tests/mir-opt/matches_reduce_branches.match_u8_i8_failed_len_2.MatchBranchSimplification.diff +++ b/tests/mir-opt/matches_reduce_branches.match_u8_i8_failed_len_2.MatchBranchSimplification.diff @@ -27,7 +27,7 @@ bb4: { _0 = const -1_i8; - _0 = const -1_i8; + _0 = Add(copy _0, const 0_i8); goto -> bb6; } diff --git a/tests/mir-opt/matches_reduce_branches.rs b/tests/mir-opt/matches_reduce_branches.rs index 3372ae2f2a614..54db1fcaaa334 100644 --- a/tests/mir-opt/matches_reduce_branches.rs +++ b/tests/mir-opt/matches_reduce_branches.rs @@ -237,7 +237,7 @@ fn match_u8_i8_failed_len_1(i: EnumAu8) -> i8 { } bb2 = { RET = 127; - RET = 127; + RET = RET + 0; Goto(ret) } bb3 = { @@ -289,6 +289,50 @@ fn match_u8_i8_failed_len_2(i: EnumAu8) -> i8 { } bb4 = { RET = -1; + RET = RET + 0; + Goto(ret) + } + unreachable_bb = { + Unreachable() + } + ret = { + Return() + } + } +} + +// EMIT_MIR matches_reduce_branches.match_u8_i8_dead_store.MatchBranchSimplification.diff +#[custom_mir(dialect = "built")] +fn match_u8_i8_dead_store(i: EnumAu8) -> i8 { + // CHECK-LABEL: fn match_u8_i8_dead_store( + // CHECK-NOT: switchInt + // CHECK: IntToInt + // CHECK: return + mir! { + { + let a = Discriminant(i); + match a { + 0 => bb1, + 127 => bb2, + 128 => bb3, + 255 => bb4, + _ => unreachable_bb, + } + } + bb1 = { + RET = 0; + Goto(ret) + } + bb2 = { + RET = 1; // This a dead store statement. + RET = 127; + Goto(ret) + } + bb3 = { + RET = -128; + Goto(ret) + } + bb4 = { RET = -1; Goto(ret) } diff --git a/tests/mir-opt/pre-codegen/clone_as_copy.enum_clone_as_copy.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/clone_as_copy.enum_clone_as_copy.PreCodegen.after.mir index 9f88e1961ec88..b36475008efa9 100644 --- a/tests/mir-opt/pre-codegen/clone_as_copy.enum_clone_as_copy.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/clone_as_copy.enum_clone_as_copy.PreCodegen.after.mir @@ -5,11 +5,12 @@ fn enum_clone_as_copy(_1: &Enum1) -> Enum1 { let mut _0: Enum1; scope 1 (inlined ::clone) { debug self => _1; - let mut _2: isize; + let _2: &AllCopy; let mut _3: &AllCopy; - let mut _4: &NestCopy; + let _4: &NestCopy; + let mut _5: &NestCopy; scope 2 { - debug __self_0 => _3; + debug __self_0 => _2; scope 6 (inlined ::clone) { debug self => _3; } @@ -17,10 +18,10 @@ fn enum_clone_as_copy(_1: &Enum1) -> Enum1 { scope 3 { debug __self_0 => _4; scope 4 (inlined ::clone) { - debug self => _4; - let _5: &AllCopy; + debug self => _5; + let _6: &AllCopy; scope 5 (inlined ::clone) { - debug self => _5; + debug self => _6; } } } @@ -30,33 +31,14 @@ fn enum_clone_as_copy(_1: &Enum1) -> Enum1 { StorageLive(_2); StorageLive(_3); StorageLive(_4); - _2 = discriminant((*_1)); - switchInt(move _2) -> [0: bb1, 1: bb2, otherwise: bb4]; - } - - bb1: { - _3 = &(((*_1) as A).0: AllCopy); - _0 = copy (*_1); - goto -> bb3; - } - - bb2: { - _4 = &(((*_1) as B).0: NestCopy); StorageLive(_5); - _5 = &((((*_1) as B).0: NestCopy).1: AllCopy); - StorageDead(_5); + StorageLive(_6); _0 = copy (*_1); - goto -> bb3; - } - - bb3: { + StorageDead(_6); + StorageDead(_5); StorageDead(_4); StorageDead(_3); StorageDead(_2); return; } - - bb4: { - unreachable; - } } diff --git a/tests/mir-opt/pre-codegen/clone_as_copy.rs b/tests/mir-opt/pre-codegen/clone_as_copy.rs index f5ff1854d387d..c5ab43002560c 100644 --- a/tests/mir-opt/pre-codegen/clone_as_copy.rs +++ b/tests/mir-opt/pre-codegen/clone_as_copy.rs @@ -32,12 +32,12 @@ fn clone_as_copy(v: &NestCopy) -> NestCopy { v.clone() } -// FIXME: We can merge into exactly one assignment statement. +// We can merge into exactly one assignment statement. // EMIT_MIR clone_as_copy.enum_clone_as_copy.PreCodegen.after.mir fn enum_clone_as_copy(v: &Enum1) -> Enum1 { // CHECK-LABEL: fn enum_clone_as_copy( // CHECK-NOT: = Enum1:: // CHECK: _0 = copy (*_1); - // CHECK: _0 = copy (*_1); + // CHECK-NOT: _0 = copy (*_1); v.clone() } diff --git a/tests/mir-opt/pre-codegen/try_identity.old.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/try_identity.old.PreCodegen.after.mir index 889e80d26e1cc..f7b6cbc855f59 100644 --- a/tests/mir-opt/pre-codegen/try_identity.old.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/try_identity.old.PreCodegen.after.mir @@ -3,38 +3,17 @@ fn old(_1: Result) -> Result { debug x => _1; let mut _0: std::result::Result; - let mut _2: isize; - let _3: T; - let _4: E; + let _2: T; + let _3: E; scope 1 { - debug v => _3; + debug v => _2; } scope 2 { - debug e => _4; + debug e => _3; } bb0: { - _2 = discriminant(_1); - switchInt(move _2) -> [0: bb1, 1: bb2, otherwise: bb4]; - } - - bb1: { - _3 = copy ((_1 as Ok).0: T); - _0 = copy _1; - goto -> bb3; - } - - bb2: { - _4 = copy ((_1 as Err).0: E); _0 = copy _1; - goto -> bb3; - } - - bb3: { return; } - - bb4: { - unreachable; - } } From 768b800605cdacd5dab08b268770c19c3dbe3aa1 Mon Sep 17 00:00:00 2001 From: dianqk Date: Wed, 9 Apr 2025 22:16:49 +0800 Subject: [PATCH 4/4] mir-opt: Do not eliminate dead statements that are being used by other BBs during merging Since we will not eliminate dead statements in other BBs, they may still use the dead statements within the merged BB. --- .../rustc_mir_dataflow/src/impls/liveness.rs | 50 +++++++++++++------ .../src/dead_store_elimination.rs | 5 +- .../rustc_mir_transform/src/match_branches.rs | 1 + ...tore_failed.MatchBranchSimplification.diff | 45 +++++++++++++++++ tests/mir-opt/matches_reduce_branches.rs | 9 ++++ 5 files changed, 94 insertions(+), 16 deletions(-) create mode 100644 tests/mir-opt/matches_reduce_branches.match_dead_store_failed.MatchBranchSimplification.diff diff --git a/compiler/rustc_mir_dataflow/src/impls/liveness.rs b/compiler/rustc_mir_dataflow/src/impls/liveness.rs index 6ec1b03a34e68..0db3435d489f5 100644 --- a/compiler/rustc_mir_dataflow/src/impls/liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/liveness.rs @@ -3,8 +3,9 @@ use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceC use rustc_middle::mir::{ self, CallReturnPlaces, Local, Location, Place, StatementKind, TerminatorEdges, }; +use rustc_middle::ty::TyCtxt; -use crate::{Analysis, Backward, GenKill}; +use crate::{Analysis, Backward, GenKill, ResultsCursor}; /// A [live-variable dataflow analysis][liveness]. /// @@ -203,21 +204,42 @@ impl DefUse { /// This is basically written for dead store elimination and nothing else. /// /// All of the caveats of `MaybeLiveLocals` apply. -pub struct MaybeTransitiveLiveLocals<'a> { +pub struct MaybeTransitiveLiveLocals<'tcx, 'mir, 'a> { always_live: &'a DenseBitSet, + live: Option>, } -impl<'a> MaybeTransitiveLiveLocals<'a> { +impl<'tcx, 'mir, 'a> MaybeTransitiveLiveLocals<'tcx, 'mir, 'a> { /// The `always_alive` set is the set of locals to which all stores should unconditionally be /// considered live. + /// The `may_live_in_other_bbs` indicates that, when analyzing the current statement, + /// statements in other basic blocks are assumed to be live. /// /// This should include at least all locals that are ever borrowed. - pub fn new(always_live: &'a DenseBitSet) -> Self { - MaybeTransitiveLiveLocals { always_live } + pub fn new( + always_live: &'a DenseBitSet, + may_live_in_other_bbs: bool, + tcx: TyCtxt<'tcx>, + body: &'mir mir::Body<'tcx>, + ) -> Self { + let live = if may_live_in_other_bbs { + Some(MaybeLiveLocals.iterate_to_fixpoint(tcx, body, None).into_results_cursor(body)) + } else { + None + }; + MaybeTransitiveLiveLocals { always_live, live } + } + + fn live_on(&mut self, place: Place<'_>, location: Location) -> bool { + let Some(live) = &mut self.live else { + return false; + }; + live.seek_before_primary_effect(location); + live.get().contains(place.local) } } -impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> { +impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'tcx, '_, 'a> { type Domain = DenseBitSet; type Direction = Backward; @@ -256,14 +278,14 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> { | StatementKind::BackwardIncompatibleDropHint { .. } | StatementKind::Nop => None, }; - if let Some(destination) = destination { - if !destination.is_indirect() - && !state.contains(destination.local) - && !self.always_live.contains(destination.local) - { - // This store is dead - return; - } + if let Some(destination) = destination + && !destination.is_indirect() + && !state.contains(destination.local) + && !self.always_live.contains(destination.local) + && !self.live_on(destination, location) + { + // This store is dead + return; } TransferFunction(state).visit_statement(statement, location); } diff --git a/compiler/rustc_mir_transform/src/dead_store_elimination.rs b/compiler/rustc_mir_transform/src/dead_store_elimination.rs index 059387652903c..69234bbc60813 100644 --- a/compiler/rustc_mir_transform/src/dead_store_elimination.rs +++ b/compiler/rustc_mir_transform/src/dead_store_elimination.rs @@ -55,6 +55,7 @@ pub(super) fn eliminate<'tcx>( mut modify_basic_blocks: ModifyBasicBlocks<'tcx, '_>, ignore_debuginfo: bool, arg_copy_to_move: bool, + may_live_in_other_bbs: bool, ) { let body = modify_basic_blocks.body(); let borrowed_locals = borrowed_locals(body); @@ -69,7 +70,7 @@ pub(super) fn eliminate<'tcx>( always_live }; - let mut live = MaybeTransitiveLiveLocals::new(&always_live) + let mut live = MaybeTransitiveLiveLocals::new(&always_live, may_live_in_other_bbs, tcx, body) .iterate_to_fixpoint(tcx, body, None) .into_results_cursor(body); @@ -179,7 +180,7 @@ impl<'tcx> crate::MirPass<'tcx> for DeadStoreElimination { } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - eliminate(tcx, ModifyBasicBlocks::Direct(body), false, true); + eliminate(tcx, ModifyBasicBlocks::Direct(body), false, true, false); } fn is_required(&self) -> bool { diff --git a/compiler/rustc_mir_transform/src/match_branches.rs b/compiler/rustc_mir_transform/src/match_branches.rs index 85a8afcbeb1d9..30cb0ff8348d8 100644 --- a/compiler/rustc_mir_transform/src/match_branches.rs +++ b/compiler/rustc_mir_transform/src/match_branches.rs @@ -31,6 +31,7 @@ impl<'tcx> crate::MirPass<'tcx> for MatchBranchSimplification { ModifyBasicBlocks::BasicBlocks(body, bbs), true, false, + true, ); eliminate_unused_storage_mark(body, bbs); strip_nops(bbs.as_mut_slice()); diff --git a/tests/mir-opt/matches_reduce_branches.match_dead_store_failed.MatchBranchSimplification.diff b/tests/mir-opt/matches_reduce_branches.match_dead_store_failed.MatchBranchSimplification.diff new file mode 100644 index 0000000000000..7ae225c0d317a --- /dev/null +++ b/tests/mir-opt/matches_reduce_branches.match_dead_store_failed.MatchBranchSimplification.diff @@ -0,0 +1,45 @@ +- // MIR for `match_dead_store_failed` before MatchBranchSimplification ++ // MIR for `match_dead_store_failed` after MatchBranchSimplification + + fn match_dead_store_failed(_1: bool) -> () { + debug b => _1; + let mut _0: (); + let _2: (&str,); + let mut _3: &str; + let mut _4: bool; + let _5: &str; + scope 1 { + debug _b => _2; + } + + bb0: { + StorageLive(_2); + StorageLive(_3); + StorageLive(_4); + _4 = copy _1; + switchInt(move _4) -> [0: bb2, otherwise: bb1]; + } + + bb1: { + _3 = const " "; + goto -> bb3; + } + + bb2: { + StorageLive(_5); + _5 = const ""; + _3 = &(*_5); + StorageDead(_5); + goto -> bb3; + } + + bb3: { + StorageDead(_4); + _2 = (move _3,); + StorageDead(_3); + _0 = const (); + StorageDead(_2); + return; + } + } + diff --git a/tests/mir-opt/matches_reduce_branches.rs b/tests/mir-opt/matches_reduce_branches.rs index 54db1fcaaa334..40d1d8e491003 100644 --- a/tests/mir-opt/matches_reduce_branches.rs +++ b/tests/mir-opt/matches_reduce_branches.rs @@ -672,6 +672,15 @@ fn match_i128_u128(i: EnumAi128) -> u128 { } } +// We cannot merge these branches unless all the dead statements are eliminated. +// EMIT_MIR matches_reduce_branches.match_dead_store_failed.MatchBranchSimplification.diff +pub fn match_dead_store_failed(b: bool) { + // CHECK-LABEL: fn match_dead_store_failed( + // CHECK: switchInt + // CHECK: return + let _b = (if b { " " } else { "" },); +} + fn main() { let _ = foo(None); let _ = foo(Some(()));