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 eea2b0990d730..69234bbc60813 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,19 +25,52 @@ 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, + may_live_in_other_bbs: 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 mut live = MaybeTransitiveLiveLocals::new(&always_live) + 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, may_live_in_other_bbs, tcx, body) .iterate_to_fixpoint(tcx, body, None) .into_results_cursor(body); @@ -46,7 +80,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 +148,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 +180,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, false); } fn is_required(&self) -> bool { 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/compiler/rustc_mir_transform/src/match_branches.rs b/compiler/rustc_mir_transform/src/match_branches.rs index 0d9d0368d3729..30cb0ff8348d8 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; @@ -19,32 +22,50 @@ 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 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, + true, + ); + 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 { 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, bbs.as_slice(), &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, bbs.as_slice(), &mut patch, bb, typing_env) + .is_some() + { + apply_patch = true; continue; } } - if should_cleanup { + if apply_patch { + patch.apply(body); simplify_cfg(body); } } @@ -61,11 +82,12 @@ trait SimplifyMatch<'tcx> { fn simplify( &mut self, tcx: TyCtxt<'tcx>, - body: &mut Body<'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!(), @@ -74,8 +96,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(); @@ -84,23 +104,13 @@ 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)); - 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(()) } @@ -537,3 +547,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/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/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_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.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..40d1d8e491003 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) } @@ -628,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(())); 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; - } } 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; }