From ed27cb0f493f79234cde209fa2d1c44597b89be7 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sat, 22 Jul 2023 15:34:54 +0000 Subject: [PATCH 1/6] Reorder passes. --- compiler/rustc_mir_transform/src/lib.rs | 5 ++-- .../src/unreachable_prop.rs | 7 +---- ...t_switch.identity.SeparateConstSwitch.diff | 6 +---- ...in.UnreachablePropagation.panic-abort.diff | 27 ++++++++++--------- ...n.UnreachablePropagation.panic-unwind.diff | 27 ++++++++++--------- tests/mir-opt/unreachable.rs | 18 ++++++++++++- tests/mir-opt/unreachable_diverging.rs | 18 ++++++++++++- 7 files changed, 67 insertions(+), 41 deletions(-) diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index dc35381fe22bf..bf5f0ca7cbd23 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -568,10 +568,11 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { &[ &check_alignment::CheckAlignment, &lower_slice_len::LowerSliceLenCalls, // has to be done before inlining, otherwise actual call will be almost always inlined. Also simple, so can just do first - &unreachable_prop::UnreachablePropagation, + &inline::Inline, + // Substitutions during inlining may introduce switch on enums with uninhabited branches. &uninhabited_enum_branching::UninhabitedEnumBranching, + &unreachable_prop::UnreachablePropagation, &o1(simplify::SimplifyCfg::AfterUninhabitedEnumBranching), - &inline::Inline, &remove_storage_markers::RemoveStorageMarkers, &remove_zsts::RemoveZsts, &normalize_array_len::NormalizeArrayLen, // has to run after `slice::len` lowering diff --git a/compiler/rustc_mir_transform/src/unreachable_prop.rs b/compiler/rustc_mir_transform/src/unreachable_prop.rs index ea7aafd866b15..c7e22dfe842d7 100644 --- a/compiler/rustc_mir_transform/src/unreachable_prop.rs +++ b/compiler/rustc_mir_transform/src/unreachable_prop.rs @@ -2,7 +2,6 @@ //! when all of their successors are unreachable. This is achieved through a //! post-order traversal of the blocks. -use crate::simplify; use crate::MirPass; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_middle::mir::*; @@ -52,8 +51,6 @@ impl MirPass<'_> for UnreachablePropagation { body.basic_blocks_mut()[bb].statements.clear(); } - let replaced = !replacements.is_empty(); - for (bb, terminator_kind) in replacements { if !tcx.consider_optimizing(|| { format!("UnreachablePropagation {:?} ", body.source.def_id()) @@ -64,9 +61,7 @@ impl MirPass<'_> for UnreachablePropagation { body.basic_blocks_mut()[bb].terminator_mut().kind = terminator_kind; } - if replaced { - simplify::remove_dead_blocks(body); - } + // Do not remove dead blocks, let `SimplifyCfg` do it. } } diff --git a/tests/mir-opt/separate_const_switch.identity.SeparateConstSwitch.diff b/tests/mir-opt/separate_const_switch.identity.SeparateConstSwitch.diff index 491db551a7d2b..fe4b33001fc4c 100644 --- a/tests/mir-opt/separate_const_switch.identity.SeparateConstSwitch.diff +++ b/tests/mir-opt/separate_const_switch.identity.SeparateConstSwitch.diff @@ -52,7 +52,7 @@ StorageLive(_10); StorageLive(_11); _9 = discriminant(_1); - switchInt(move _9) -> [0: bb7, 1: bb5, otherwise: bb6]; + switchInt(move _9) -> [0: bb6, 1: bb5, otherwise: bb3]; } bb1: { @@ -92,10 +92,6 @@ } bb6: { - unreachable; - } - - bb7: { _10 = ((_1 as Ok).0: i32); _3 = ControlFlow::, i32>::Continue(move _10); goto -> bb1; diff --git a/tests/mir-opt/unreachable.main.UnreachablePropagation.panic-abort.diff b/tests/mir-opt/unreachable.main.UnreachablePropagation.panic-abort.diff index eb5a0c39b0b2f..aec22e0328cbd 100644 --- a/tests/mir-opt/unreachable.main.UnreachablePropagation.panic-abort.diff +++ b/tests/mir-opt/unreachable.main.UnreachablePropagation.panic-abort.diff @@ -24,8 +24,7 @@ bb1: { _2 = discriminant(_1); -- switchInt(move _2) -> [1: bb2, otherwise: bb6]; -+ switchInt(move _2) -> [1: bb2, otherwise: bb3]; + switchInt(move _2) -> [1: bb2, otherwise: bb6]; } bb2: { @@ -36,29 +35,31 @@ - StorageLive(_6); - _6 = const true; - switchInt(move _6) -> [0: bb4, otherwise: bb3]; -- } -- -- bb3: { ++ unreachable; + } + + bb3: { - _4 = const 21_i32; - _5 = const (); - goto -> bb5; -- } -- -- bb4: { ++ unreachable; + } + + bb4: { - _4 = const 42_i32; - _5 = const (); - goto -> bb5; -- } -- -- bb5: { ++ unreachable; + } + + bb5: { - StorageDead(_6); - StorageDead(_5); - StorageLive(_7); unreachable; } -- bb6: { -+ bb3: { + bb6: { _0 = const (); StorageDead(_1); return; diff --git a/tests/mir-opt/unreachable.main.UnreachablePropagation.panic-unwind.diff b/tests/mir-opt/unreachable.main.UnreachablePropagation.panic-unwind.diff index 906dce9819fdc..b2e8ab95d946f 100644 --- a/tests/mir-opt/unreachable.main.UnreachablePropagation.panic-unwind.diff +++ b/tests/mir-opt/unreachable.main.UnreachablePropagation.panic-unwind.diff @@ -24,8 +24,7 @@ bb1: { _2 = discriminant(_1); -- switchInt(move _2) -> [1: bb2, otherwise: bb6]; -+ switchInt(move _2) -> [1: bb2, otherwise: bb3]; + switchInt(move _2) -> [1: bb2, otherwise: bb6]; } bb2: { @@ -36,29 +35,31 @@ - StorageLive(_6); - _6 = const true; - switchInt(move _6) -> [0: bb4, otherwise: bb3]; -- } -- -- bb3: { ++ unreachable; + } + + bb3: { - _4 = const 21_i32; - _5 = const (); - goto -> bb5; -- } -- -- bb4: { ++ unreachable; + } + + bb4: { - _4 = const 42_i32; - _5 = const (); - goto -> bb5; -- } -- -- bb5: { ++ unreachable; + } + + bb5: { - StorageDead(_6); - StorageDead(_5); - StorageLive(_7); unreachable; } -- bb6: { -+ bb3: { + bb6: { _0 = const (); StorageDead(_1); return; diff --git a/tests/mir-opt/unreachable.rs b/tests/mir-opt/unreachable.rs index 5c0df09b752ea..9e8b0600b19eb 100644 --- a/tests/mir-opt/unreachable.rs +++ b/tests/mir-opt/unreachable.rs @@ -1,5 +1,6 @@ -// skip-filecheck +// unit-test: UnreachablePropagation // EMIT_MIR_FOR_EACH_PANIC_STRATEGY + enum Empty {} fn empty() -> Option { @@ -8,6 +9,21 @@ fn empty() -> Option { // EMIT_MIR unreachable.main.UnreachablePropagation.diff fn main() { + // CHECK-LABEL: fn main( + // CHECK: bb0: { + // CHECK: {{_.*}} = empty() + // CHECK: bb1: { + // CHECK: switchInt({{.*}}) -> [1: bb2, otherwise: bb6]; + // CHECK: bb2: { + // CHECK: unreachable; + // CHECK: bb3: { + // CHECK: unreachable; + // CHECK: bb4: { + // CHECK: unreachable; + // CHECK: bb5: { + // CHECK: unreachable; + // CHECK: bb6: { + // CHECK: return; if let Some(_x) = empty() { let mut _y; diff --git a/tests/mir-opt/unreachable_diverging.rs b/tests/mir-opt/unreachable_diverging.rs index 3713bcaea1694..95ea6cb00f9ea 100644 --- a/tests/mir-opt/unreachable_diverging.rs +++ b/tests/mir-opt/unreachable_diverging.rs @@ -1,5 +1,6 @@ -// skip-filecheck +// unit-test: UnreachablePropagation // EMIT_MIR_FOR_EACH_PANIC_STRATEGY + pub enum Empty {} fn empty() -> Option { @@ -12,6 +13,21 @@ fn loop_forever() { // EMIT_MIR unreachable_diverging.main.UnreachablePropagation.diff fn main() { + // CHECK-LABEL: fn main( + // CHECK: bb0: { + // CHECK: {{_.*}} = empty() + // CHECK: bb1: { + // CHECK: switchInt({{.*}}) -> [1: bb2, otherwise: bb6]; + // CHECK: bb2: { + // CHECK: switchInt({{.*}}) -> [0: bb4, otherwise: bb3]; + // CHECK: bb3: { + // CHECK: {{_.*}} = loop_forever() + // CHECK: bb4: { + // CHECK: unreachable; + // CHECK: bb5: { + // CHECK: unreachable; + // CHECK: bb6: { + // CHECK: return; let x = true; if let Some(bomb) = empty() { if x { From c748ac1f11c2e711cf205bdf262d3b4c1b9de405 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sat, 22 Jul 2023 15:34:42 +0000 Subject: [PATCH 2/6] Replace SwitchInt to unreachable by an assumption. --- .../src/unreachable_prop.rs | 199 ++++++++++-------- ...d.unwrap_unchecked.Inline.panic-abort.diff | 12 +- ....unwrap_unchecked.Inline.panic-unwind.diff | 12 +- ...unchecked.PreCodegen.after.panic-abort.mir | 18 +- ...nchecked.PreCodegen.after.panic-unwind.mir | 18 +- ...witch_targets.ub_if_b.PreCodegen.after.mir | 8 +- ...after-uninhabited-enum-branching.after.mir | 21 +- ...in.UnreachablePropagation.panic-abort.diff | 7 +- ...n.UnreachablePropagation.panic-unwind.diff | 7 +- tests/mir-opt/unreachable.rs | 12 +- ...in.UnreachablePropagation.panic-abort.diff | 6 +- ...n.UnreachablePropagation.panic-unwind.diff | 6 +- tests/mir-opt/unreachable_diverging.rs | 4 +- 13 files changed, 210 insertions(+), 120 deletions(-) diff --git a/compiler/rustc_mir_transform/src/unreachable_prop.rs b/compiler/rustc_mir_transform/src/unreachable_prop.rs index c7e22dfe842d7..d25c6d471cd2a 100644 --- a/compiler/rustc_mir_transform/src/unreachable_prop.rs +++ b/compiler/rustc_mir_transform/src/unreachable_prop.rs @@ -3,9 +3,12 @@ //! post-order traversal of the blocks. use crate::MirPass; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::FxHashSet; +use rustc_middle::mir::interpret::Scalar; +use rustc_middle::mir::patch::MirPatch; use rustc_middle::mir::*; -use rustc_middle::ty::TyCtxt; +use rustc_middle::ty::{self, TyCtxt}; +use rustc_target::abi::Size; pub struct UnreachablePropagation; @@ -20,102 +23,134 @@ impl MirPass<'_> for UnreachablePropagation { } fn run_pass<'tcx>(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + let mut patch = MirPatch::new(body); let mut unreachable_blocks = FxHashSet::default(); - let mut replacements = FxHashMap::default(); for (bb, bb_data) in traversal::postorder(body) { let terminator = bb_data.terminator(); - if terminator.kind == TerminatorKind::Unreachable { - unreachable_blocks.insert(bb); - } else { - let is_unreachable = |succ: BasicBlock| unreachable_blocks.contains(&succ); - let terminator_kind_opt = remove_successors(&terminator.kind, is_unreachable); - - if let Some(terminator_kind) = terminator_kind_opt { - if terminator_kind == TerminatorKind::Unreachable { - unreachable_blocks.insert(bb); - } - replacements.insert(bb, terminator_kind); + let is_unreachable = match &terminator.kind { + TerminatorKind::Unreachable => true, + // This will unconditionally run into an unreachable and is therefore unreachable as well. + TerminatorKind::Goto { target } if unreachable_blocks.contains(target) => { + patch.patch_terminator(bb, TerminatorKind::Unreachable); + true + } + // Try to remove unreachable targets from the switch. + TerminatorKind::SwitchInt { .. } => { + remove_successors_from_switch(tcx, bb, &unreachable_blocks, body, &mut patch) } + _ => false, + }; + if is_unreachable { + unreachable_blocks.insert(bb); } } - // We do want do keep some unreachable blocks, but make them empty. - for bb in unreachable_blocks { - if !tcx.consider_optimizing(|| { - format!("UnreachablePropagation {:?} ", body.source.def_id()) - }) { - break; - } - - body.basic_blocks_mut()[bb].statements.clear(); + if !tcx + .consider_optimizing(|| format!("UnreachablePropagation {:?} ", body.source.def_id())) + { + return; } - for (bb, terminator_kind) in replacements { - if !tcx.consider_optimizing(|| { - format!("UnreachablePropagation {:?} ", body.source.def_id()) - }) { - break; - } + patch.apply(body); - body.basic_blocks_mut()[bb].terminator_mut().kind = terminator_kind; + // We do want do keep some unreachable blocks, but make them empty. + for bb in unreachable_blocks { + body.basic_blocks_mut()[bb].statements.clear(); } - - // Do not remove dead blocks, let `SimplifyCfg` do it. } } -fn remove_successors<'tcx, F>( - terminator_kind: &TerminatorKind<'tcx>, - is_unreachable: F, -) -> Option> -where - F: Fn(BasicBlock) -> bool, -{ - let terminator = match terminator_kind { - // This will unconditionally run into an unreachable and is therefore unreachable as well. - TerminatorKind::Goto { target } if is_unreachable(*target) => TerminatorKind::Unreachable, - TerminatorKind::SwitchInt { targets, discr } => { - let otherwise = targets.otherwise(); - - // If all targets are unreachable, we can be unreachable as well. - if targets.all_targets().iter().all(|bb| is_unreachable(*bb)) { - TerminatorKind::Unreachable - } else if is_unreachable(otherwise) { - // If there are multiple targets, don't delete unreachable branches (like an unreachable otherwise) - // unless otherwise is unreachable, in which case deleting a normal branch causes it to be merged with - // the otherwise, keeping its unreachable. - // This looses information about reachability causing worse codegen. - // For example (see tests/codegen/match-optimizes-away.rs) - // - // pub enum Two { A, B } - // pub fn identity(x: Two) -> Two { - // match x { - // Two::A => Two::A, - // Two::B => Two::B, - // } - // } - // - // This generates a `switchInt() -> [0: 0, 1: 1, otherwise: unreachable]`, which allows us or LLVM to - // turn it into just `x` later. Without the unreachable, such a transformation would be illegal. - // If the otherwise branch is unreachable, we can delete all other unreachable targets, as they will - // still point to the unreachable and therefore not lose reachability information. - let reachable_iter = targets.iter().filter(|(_, bb)| !is_unreachable(*bb)); - - let new_targets = SwitchTargets::new(reachable_iter, otherwise); - - // No unreachable branches were removed. - if new_targets.all_targets().len() == targets.all_targets().len() { - return None; - } +/// Return whether the current terminator is fully unreachable. +fn remove_successors_from_switch<'tcx>( + tcx: TyCtxt<'tcx>, + bb: BasicBlock, + unreachable_blocks: &FxHashSet, + body: &Body<'tcx>, + patch: &mut MirPatch<'tcx>, +) -> bool { + let terminator = body.basic_blocks[bb].terminator(); + let TerminatorKind::SwitchInt { discr, targets } = &terminator.kind else { bug!() }; + let source_info = terminator.source_info; + let location = body.terminator_loc(bb); + + let is_unreachable = |bb| unreachable_blocks.contains(&bb); + + // If there are multiple targets, we want to keep information about reachability for codegen. + // For example (see tests/codegen/match-optimizes-away.rs) + // + // pub enum Two { A, B } + // pub fn identity(x: Two) -> Two { + // match x { + // Two::A => Two::A, + // Two::B => Two::B, + // } + // } + // + // This generates a `switchInt() -> [0: 0, 1: 1, otherwise: unreachable]`, which allows us or LLVM to + // turn it into just `x` later. Without the unreachable, such a transformation would be illegal. + // + // In order to preserve this information, we record reachable and unreachable targets as + // `Assume` statements in MIR. + + let discr_ty = discr.ty(body, tcx); + let discr_size = Size::from_bits(match discr_ty.kind() { + ty::Uint(uint) => uint.normalize(tcx.sess.target.pointer_width).bit_width().unwrap(), + ty::Int(int) => int.normalize(tcx.sess.target.pointer_width).bit_width().unwrap(), + ty::Char => 32, + ty::Bool => 1, + other => bug!("unhandled type: {:?}", other), + }); + + let mut add_assumption = |binop, value| { + let local = patch.new_temp(tcx.types.bool, source_info.span); + let value = Operand::Constant(Box::new(ConstOperand { + span: source_info.span, + user_ty: None, + const_: Const::from_scalar(tcx, Scalar::from_uint(value, discr_size), discr_ty), + })); + let cmp = Rvalue::BinaryOp(binop, Box::new((discr.to_copy(), value))); + patch.add_assign(location, local.into(), cmp); + + let assume = NonDivergingIntrinsic::Assume(Operand::Move(local.into())); + patch.add_statement(location, StatementKind::Intrinsic(Box::new(assume))); + }; - TerminatorKind::SwitchInt { discr: discr.clone(), targets: new_targets } - } else { - // If the otherwise branch is reachable, we don't want to delete any unreachable branches. - return None; - } + let reachable_iter = targets.iter().filter(|&(value, bb)| { + let is_unreachable = is_unreachable(bb); + if is_unreachable { + // We remove this target from the switch, so record the inequality using `Assume`. + add_assumption(BinOp::Ne, value); + false + } else { + true + } + }); + + let otherwise = targets.otherwise(); + let new_targets = SwitchTargets::new(reachable_iter, otherwise); + + let num_targets = new_targets.all_targets().len(); + let otherwise_unreachable = is_unreachable(otherwise); + let fully_unreachable = num_targets == 1 && otherwise_unreachable; + + let terminator = match (num_targets, otherwise_unreachable) { + // If all targets are unreachable, we can be unreachable as well. + (1, true) => TerminatorKind::Unreachable, + (1, false) => TerminatorKind::Goto { target: otherwise }, + (2, true) => { + // All targets are unreachable except one. Record the equality, and make it a goto. + let (value, target) = new_targets.iter().next().unwrap(); + add_assumption(BinOp::Eq, value); + TerminatorKind::Goto { target } } - _ => return None, + _ if num_targets == targets.all_targets().len() => { + // Nothing has changed. + return false; + } + _ => TerminatorKind::SwitchInt { discr: discr.clone(), targets: new_targets }, }; - Some(terminator) + + patch.patch_terminator(bb, terminator); + fully_unreachable } 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 018b6c1ee958c..14a8b22657f67 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 @@ -16,6 +16,7 @@ + scope 5 (inlined unreachable_unchecked) { + scope 6 { + scope 7 (inlined unreachable_unchecked::runtime) { ++ let _5: !; + } + } + } @@ -31,16 +32,23 @@ - _0 = Option::::unwrap_unchecked(move _2) -> [return: bb1, unwind unreachable]; + StorageLive(_3); + StorageLive(_4); ++ StorageLive(_5); + _4 = discriminant(_2); -+ switchInt(move _4) -> [1: bb2, otherwise: bb1]; ++ switchInt(move _4) -> [0: bb1, 1: bb3, otherwise: bb2]; } bb1: { -+ unreachable; ++ assume(const false); ++ _5 = core::panicking::panic_nounwind(const "unsafe precondition(s) violated: hint::unreachable_unchecked must never be reached") -> unwind unreachable; + } + + bb2: { ++ unreachable; ++ } ++ ++ bb3: { + _0 = move ((_2 as Some).0: T); ++ StorageDead(_5); + StorageDead(_4); + StorageDead(_3); StorageDead(_2); 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 47845758a3f61..a6901ca089267 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 @@ -16,6 +16,7 @@ + scope 5 (inlined unreachable_unchecked) { + scope 6 { + scope 7 (inlined unreachable_unchecked::runtime) { ++ let _5: !; + } + } + } @@ -31,20 +32,27 @@ - _0 = Option::::unwrap_unchecked(move _2) -> [return: bb1, unwind: bb2]; + StorageLive(_3); + StorageLive(_4); ++ StorageLive(_5); + _4 = discriminant(_2); -+ switchInt(move _4) -> [1: bb2, otherwise: bb1]; ++ switchInt(move _4) -> [0: bb1, 1: bb3, otherwise: bb2]; } bb1: { - StorageDead(_2); - return; -+ unreachable; ++ assume(const false); ++ _5 = core::panicking::panic_nounwind(const "unsafe precondition(s) violated: hint::unreachable_unchecked must never be reached") -> unwind unreachable; } - bb2 (cleanup): { - resume; + bb2: { ++ unreachable; ++ } ++ ++ bb3: { + _0 = move ((_2 as Some).0: T); ++ StorageDead(_5); + StorageDead(_4); + StorageDead(_3); + StorageDead(_2); diff --git a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-abort.mir b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-abort.mir index 392f085bd4d1d..37a2d28e0c48c 100644 --- a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-abort.mir @@ -6,7 +6,7 @@ fn unwrap_unchecked(_1: Option) -> T { scope 1 (inlined #[track_caller] Option::::unwrap_unchecked) { debug self => _1; let mut _2: isize; - let mut _3: &std::option::Option; + let mut _4: &std::option::Option; scope 2 { debug val => _0; } @@ -14,30 +14,36 @@ fn unwrap_unchecked(_1: Option) -> T { scope 5 (inlined unreachable_unchecked) { scope 6 { scope 7 (inlined unreachable_unchecked::runtime) { + let _3: !; } } } } scope 4 (inlined Option::::is_some) { - debug self => _3; + debug self => _4; } } bb0: { - StorageLive(_3); + StorageLive(_4); StorageLive(_2); _2 = discriminant(_1); - switchInt(move _2) -> [1: bb1, otherwise: bb2]; + switchInt(move _2) -> [0: bb1, 1: bb2, otherwise: bb3]; } bb1: { + assume(const false); + _3 = core::panicking::panic_nounwind(const "unsafe precondition(s) violated: hint::unreachable_unchecked must never be reached") -> unwind unreachable; + } + + bb2: { _0 = move ((_1 as Some).0: T); StorageDead(_2); - StorageDead(_3); + StorageDead(_4); return; } - bb2: { + bb3: { unreachable; } } diff --git a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-unwind.mir index 392f085bd4d1d..37a2d28e0c48c 100644 --- a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-unwind.mir @@ -6,7 +6,7 @@ fn unwrap_unchecked(_1: Option) -> T { scope 1 (inlined #[track_caller] Option::::unwrap_unchecked) { debug self => _1; let mut _2: isize; - let mut _3: &std::option::Option; + let mut _4: &std::option::Option; scope 2 { debug val => _0; } @@ -14,30 +14,36 @@ fn unwrap_unchecked(_1: Option) -> T { scope 5 (inlined unreachable_unchecked) { scope 6 { scope 7 (inlined unreachable_unchecked::runtime) { + let _3: !; } } } } scope 4 (inlined Option::::is_some) { - debug self => _3; + debug self => _4; } } bb0: { - StorageLive(_3); + StorageLive(_4); StorageLive(_2); _2 = discriminant(_1); - switchInt(move _2) -> [1: bb1, otherwise: bb2]; + switchInt(move _2) -> [0: bb1, 1: bb2, otherwise: bb3]; } bb1: { + assume(const false); + _3 = core::panicking::panic_nounwind(const "unsafe precondition(s) violated: hint::unreachable_unchecked must never be reached") -> unwind unreachable; + } + + bb2: { _0 = move ((_1 as Some).0: T); StorageDead(_2); - StorageDead(_3); + StorageDead(_4); return; } - bb2: { + bb3: { unreachable; } } diff --git a/tests/mir-opt/pre-codegen/duplicate_switch_targets.ub_if_b.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/duplicate_switch_targets.ub_if_b.PreCodegen.after.mir index 65d71199aaeb0..d5d5253a8b783 100644 --- a/tests/mir-opt/pre-codegen/duplicate_switch_targets.ub_if_b.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/duplicate_switch_targets.ub_if_b.PreCodegen.after.mir @@ -7,13 +7,14 @@ fn ub_if_b(_1: Thing) -> Thing { scope 1 (inlined unreachable_unchecked) { scope 2 { scope 3 (inlined unreachable_unchecked::runtime) { + let _3: !; } } } bb0: { _2 = discriminant(_1); - switchInt(move _2) -> [0: bb1, otherwise: bb2]; + switchInt(move _2) -> [0: bb1, 1: bb2, otherwise: bb3]; } bb1: { @@ -22,6 +23,11 @@ fn ub_if_b(_1: Thing) -> Thing { } bb2: { + assume(const false); + _3 = core::panicking::panic_nounwind(const "unsafe precondition(s) violated: hint::unreachable_unchecked must never be reached") -> unwind unreachable; + } + + bb3: { unreachable; } } diff --git a/tests/mir-opt/uninhabited_enum_branching.main.SimplifyCfg-after-uninhabited-enum-branching.after.mir b/tests/mir-opt/uninhabited_enum_branching.main.SimplifyCfg-after-uninhabited-enum-branching.after.mir index 474f43104bb43..1ee44e48c8ec8 100644 --- a/tests/mir-opt/uninhabited_enum_branching.main.SimplifyCfg-after-uninhabited-enum-branching.after.mir +++ b/tests/mir-opt/uninhabited_enum_branching.main.SimplifyCfg-after-uninhabited-enum-branching.after.mir @@ -11,16 +11,15 @@ fn main() -> () { let mut _7: Test2; let mut _8: isize; let _9: &str; + let mut _10: bool; bb0: { StorageLive(_1); StorageLive(_2); _2 = Test1::C; _3 = discriminant(_2); - switchInt(move _3) -> [2: bb1, otherwise: bb2]; - } - - bb1: { + _10 = Eq(_3, const 2_isize); + assume(move _10); StorageLive(_5); _5 = const "C"; _1 = &(*_5); @@ -31,27 +30,27 @@ fn main() -> () { StorageLive(_7); _7 = Test2::D; _8 = discriminant(_7); - switchInt(move _8) -> [4: bb4, 5: bb3, otherwise: bb2]; + switchInt(move _8) -> [4: bb3, 5: bb2, otherwise: bb1]; } - bb2: { + bb1: { unreachable; } - bb3: { + bb2: { StorageLive(_9); _9 = const "E"; _6 = &(*_9); StorageDead(_9); - goto -> bb5; + goto -> bb4; } - bb4: { + bb3: { _6 = const "D"; - goto -> bb5; + goto -> bb4; } - bb5: { + bb4: { StorageDead(_7); StorageDead(_6); _0 = const (); diff --git a/tests/mir-opt/unreachable.main.UnreachablePropagation.panic-abort.diff b/tests/mir-opt/unreachable.main.UnreachablePropagation.panic-abort.diff index aec22e0328cbd..6f4faefce145e 100644 --- a/tests/mir-opt/unreachable.main.UnreachablePropagation.panic-abort.diff +++ b/tests/mir-opt/unreachable.main.UnreachablePropagation.panic-abort.diff @@ -8,6 +8,8 @@ let _5: (); let mut _6: bool; let mut _7: !; ++ let mut _8: bool; ++ let mut _9: bool; scope 1 { debug _x => _3; let _3: Empty; @@ -24,7 +26,10 @@ bb1: { _2 = discriminant(_1); - switchInt(move _2) -> [1: bb2, otherwise: bb6]; +- switchInt(move _2) -> [1: bb2, otherwise: bb6]; ++ _9 = Ne(_2, const 1_isize); ++ assume(move _9); ++ goto -> bb6; } bb2: { diff --git a/tests/mir-opt/unreachable.main.UnreachablePropagation.panic-unwind.diff b/tests/mir-opt/unreachable.main.UnreachablePropagation.panic-unwind.diff index b2e8ab95d946f..5bacb42bed3f2 100644 --- a/tests/mir-opt/unreachable.main.UnreachablePropagation.panic-unwind.diff +++ b/tests/mir-opt/unreachable.main.UnreachablePropagation.panic-unwind.diff @@ -8,6 +8,8 @@ let _5: (); let mut _6: bool; let mut _7: !; ++ let mut _8: bool; ++ let mut _9: bool; scope 1 { debug _x => _3; let _3: Empty; @@ -24,7 +26,10 @@ bb1: { _2 = discriminant(_1); - switchInt(move _2) -> [1: bb2, otherwise: bb6]; +- switchInt(move _2) -> [1: bb2, otherwise: bb6]; ++ _9 = Ne(_2, const 1_isize); ++ assume(move _9); ++ goto -> bb6; } bb2: { diff --git a/tests/mir-opt/unreachable.rs b/tests/mir-opt/unreachable.rs index 9e8b0600b19eb..3d934ace261e7 100644 --- a/tests/mir-opt/unreachable.rs +++ b/tests/mir-opt/unreachable.rs @@ -13,15 +13,17 @@ fn main() { // CHECK: bb0: { // CHECK: {{_.*}} = empty() // CHECK: bb1: { - // CHECK: switchInt({{.*}}) -> [1: bb2, otherwise: bb6]; + // CHECK: [[ne:_.*]] = Ne({{.*}}, const 1_isize); + // CHECK-NEXT: assume(move [[ne]]); + // CHECK-NEXT: goto -> bb6; // CHECK: bb2: { - // CHECK: unreachable; + // CHECK-NEXT: unreachable; // CHECK: bb3: { - // CHECK: unreachable; + // CHECK-NEXT: unreachable; // CHECK: bb4: { - // CHECK: unreachable; + // CHECK-NEXT: unreachable; // CHECK: bb5: { - // CHECK: unreachable; + // CHECK-NEXT: unreachable; // CHECK: bb6: { // CHECK: return; if let Some(_x) = empty() { diff --git a/tests/mir-opt/unreachable_diverging.main.UnreachablePropagation.panic-abort.diff b/tests/mir-opt/unreachable_diverging.main.UnreachablePropagation.panic-abort.diff index 713757ce6e080..11d7924e7360e 100644 --- a/tests/mir-opt/unreachable_diverging.main.UnreachablePropagation.panic-abort.diff +++ b/tests/mir-opt/unreachable_diverging.main.UnreachablePropagation.panic-abort.diff @@ -9,6 +9,7 @@ let _5: (); let mut _6: bool; let mut _7: !; ++ let mut _8: bool; scope 1 { debug x => _1; scope 2 { @@ -35,7 +36,10 @@ StorageLive(_5); StorageLive(_6); _6 = _1; - switchInt(move _6) -> [0: bb4, otherwise: bb3]; +- switchInt(move _6) -> [0: bb4, otherwise: bb3]; ++ _8 = Ne(_6, const false); ++ assume(move _8); ++ goto -> bb3; } bb3: { diff --git a/tests/mir-opt/unreachable_diverging.main.UnreachablePropagation.panic-unwind.diff b/tests/mir-opt/unreachable_diverging.main.UnreachablePropagation.panic-unwind.diff index a0479fb91304b..df6f5609fbfa9 100644 --- a/tests/mir-opt/unreachable_diverging.main.UnreachablePropagation.panic-unwind.diff +++ b/tests/mir-opt/unreachable_diverging.main.UnreachablePropagation.panic-unwind.diff @@ -9,6 +9,7 @@ let _5: (); let mut _6: bool; let mut _7: !; ++ let mut _8: bool; scope 1 { debug x => _1; scope 2 { @@ -35,7 +36,10 @@ StorageLive(_5); StorageLive(_6); _6 = _1; - switchInt(move _6) -> [0: bb4, otherwise: bb3]; +- switchInt(move _6) -> [0: bb4, otherwise: bb3]; ++ _8 = Ne(_6, const false); ++ assume(move _8); ++ goto -> bb3; } bb3: { diff --git a/tests/mir-opt/unreachable_diverging.rs b/tests/mir-opt/unreachable_diverging.rs index 95ea6cb00f9ea..b1df6f85262de 100644 --- a/tests/mir-opt/unreachable_diverging.rs +++ b/tests/mir-opt/unreachable_diverging.rs @@ -19,7 +19,9 @@ fn main() { // CHECK: bb1: { // CHECK: switchInt({{.*}}) -> [1: bb2, otherwise: bb6]; // CHECK: bb2: { - // CHECK: switchInt({{.*}}) -> [0: bb4, otherwise: bb3]; + // CHECK: [[ne:_.*]] = Ne({{.*}}, const false); + // CHECK: assume(move [[ne]]); + // CHECK: goto -> bb3; // CHECK: bb3: { // CHECK: {{_.*}} = loop_forever() // CHECK: bb4: { From 0b13e636f54e1762016ffc8ffbfd43285a99b8ab Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sat, 22 Jul 2023 15:34:22 +0000 Subject: [PATCH 3/6] Simplify assume of a constant. --- .../src/simplify_branches.rs | 19 +++++++++- ...d.unwrap_unchecked.Inline.panic-abort.diff | 25 ++++++------- ....unwrap_unchecked.Inline.panic-unwind.diff | 37 +++++++++---------- ...unchecked.PreCodegen.after.panic-abort.mir | 32 +++++++--------- ...nchecked.PreCodegen.after.panic-unwind.mir | 32 +++++++--------- ...witch_targets.ub_if_b.PreCodegen.after.mir | 20 +++------- 6 files changed, 80 insertions(+), 85 deletions(-) diff --git a/compiler/rustc_mir_transform/src/simplify_branches.rs b/compiler/rustc_mir_transform/src/simplify_branches.rs index b508cd1c9cc53..1f0e605c3b858 100644 --- a/compiler/rustc_mir_transform/src/simplify_branches.rs +++ b/compiler/rustc_mir_transform/src/simplify_branches.rs @@ -16,8 +16,25 @@ impl<'tcx> MirPass<'tcx> for SimplifyConstCondition { } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + trace!("Running SimplifyConstCondition on {:?}", body.source); let param_env = tcx.param_env_reveal_all_normalized(body.source.def_id()); - for block in body.basic_blocks_mut() { + 'blocks: for block in body.basic_blocks_mut() { + for stmt in block.statements.iter_mut() { + if let StatementKind::Intrinsic(box ref intrinsic) = stmt.kind + && let NonDivergingIntrinsic::Assume(discr) = intrinsic + && let Operand::Constant(ref c) = discr + && let Some(constant) = c.const_.try_eval_bool(tcx, param_env) + { + if constant { + stmt.make_nop(); + } else { + block.statements.clear(); + block.terminator_mut().kind = TerminatorKind::Unreachable; + continue 'blocks; + } + } + } + let terminator = block.terminator_mut(); terminator.kind = match terminator.kind { TerminatorKind::SwitchInt { 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 14a8b22657f67..4ca7a2c4294ff 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 @@ -9,6 +9,8 @@ + debug self => _2; + let mut _3: &std::option::Option; + let mut _4: isize; ++ let mut _5: bool; ++ let mut _6: bool; + scope 2 { + debug val => _0; + } @@ -16,7 +18,6 @@ + scope 5 (inlined unreachable_unchecked) { + scope 6 { + scope 7 (inlined unreachable_unchecked::runtime) { -+ let _5: !; + } + } + } @@ -30,24 +31,20 @@ StorageLive(_2); _2 = move _1; - _0 = Option::::unwrap_unchecked(move _2) -> [return: bb1, unwind unreachable]; +- } +- +- bb1: { + StorageLive(_3); + StorageLive(_4); + StorageLive(_5); ++ StorageLive(_6); + _4 = discriminant(_2); -+ switchInt(move _4) -> [0: bb1, 1: bb3, otherwise: bb2]; - } - - bb1: { -+ assume(const false); -+ _5 = core::panicking::panic_nounwind(const "unsafe precondition(s) violated: hint::unreachable_unchecked must never be reached") -> unwind unreachable; -+ } -+ -+ bb2: { -+ unreachable; -+ } -+ -+ bb3: { ++ _5 = Ne(_4, const 0_isize); ++ assume(move _5); ++ _6 = Eq(_4, const 1_isize); ++ assume(move _6); + _0 = move ((_2 as Some).0: T); ++ StorageDead(_6); + StorageDead(_5); + StorageDead(_4); + StorageDead(_3); 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 a6901ca089267..caabb7ea46358 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 @@ -9,6 +9,8 @@ + debug self => _2; + let mut _3: &std::option::Option; + let mut _4: isize; ++ let mut _5: bool; ++ let mut _6: bool; + scope 2 { + debug val => _0; + } @@ -16,7 +18,6 @@ + scope 5 (inlined unreachable_unchecked) { + scope 6 { + scope 7 (inlined unreachable_unchecked::runtime) { -+ let _5: !; + } + } + } @@ -30,33 +31,29 @@ StorageLive(_2); _2 = move _1; - _0 = Option::::unwrap_unchecked(move _2) -> [return: bb1, unwind: bb2]; +- } +- +- bb1: { + StorageLive(_3); + StorageLive(_4); + StorageLive(_5); ++ StorageLive(_6); + _4 = discriminant(_2); -+ switchInt(move _4) -> [0: bb1, 1: bb3, otherwise: bb2]; - } - - bb1: { -- StorageDead(_2); -- return; -+ assume(const false); -+ _5 = core::panicking::panic_nounwind(const "unsafe precondition(s) violated: hint::unreachable_unchecked must never be reached") -> unwind unreachable; - } - -- bb2 (cleanup): { -- resume; -+ bb2: { -+ unreachable; -+ } -+ -+ bb3: { ++ _5 = Ne(_4, const 0_isize); ++ assume(move _5); ++ _6 = Eq(_4, const 1_isize); ++ assume(move _6); + _0 = move ((_2 as Some).0: T); ++ StorageDead(_6); + StorageDead(_5); + StorageDead(_4); + StorageDead(_3); -+ StorageDead(_2); -+ return; + StorageDead(_2); + return; +- } +- +- bb2 (cleanup): { +- resume; } } diff --git a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-abort.mir b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-abort.mir index 37a2d28e0c48c..521266925e0d4 100644 --- a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-abort.mir @@ -6,7 +6,9 @@ fn unwrap_unchecked(_1: Option) -> T { scope 1 (inlined #[track_caller] Option::::unwrap_unchecked) { debug self => _1; let mut _2: isize; - let mut _4: &std::option::Option; + let mut _3: bool; + let mut _4: bool; + let mut _5: &std::option::Option; scope 2 { debug val => _0; } @@ -14,36 +16,30 @@ fn unwrap_unchecked(_1: Option) -> T { scope 5 (inlined unreachable_unchecked) { scope 6 { scope 7 (inlined unreachable_unchecked::runtime) { - let _3: !; } } } } scope 4 (inlined Option::::is_some) { - debug self => _4; + debug self => _5; } } bb0: { - StorageLive(_4); + StorageLive(_5); StorageLive(_2); + StorageLive(_3); + StorageLive(_4); _2 = discriminant(_1); - switchInt(move _2) -> [0: bb1, 1: bb2, otherwise: bb3]; - } - - bb1: { - assume(const false); - _3 = core::panicking::panic_nounwind(const "unsafe precondition(s) violated: hint::unreachable_unchecked must never be reached") -> unwind unreachable; - } - - bb2: { + _3 = Ne(_2, const 0_isize); + assume(move _3); + _4 = Eq(_2, const 1_isize); + assume(move _4); _0 = move ((_1 as Some).0: T); - StorageDead(_2); StorageDead(_4); + StorageDead(_3); + StorageDead(_2); + StorageDead(_5); return; } - - bb3: { - unreachable; - } } diff --git a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-unwind.mir index 37a2d28e0c48c..521266925e0d4 100644 --- a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-unwind.mir @@ -6,7 +6,9 @@ fn unwrap_unchecked(_1: Option) -> T { scope 1 (inlined #[track_caller] Option::::unwrap_unchecked) { debug self => _1; let mut _2: isize; - let mut _4: &std::option::Option; + let mut _3: bool; + let mut _4: bool; + let mut _5: &std::option::Option; scope 2 { debug val => _0; } @@ -14,36 +16,30 @@ fn unwrap_unchecked(_1: Option) -> T { scope 5 (inlined unreachable_unchecked) { scope 6 { scope 7 (inlined unreachable_unchecked::runtime) { - let _3: !; } } } } scope 4 (inlined Option::::is_some) { - debug self => _4; + debug self => _5; } } bb0: { - StorageLive(_4); + StorageLive(_5); StorageLive(_2); + StorageLive(_3); + StorageLive(_4); _2 = discriminant(_1); - switchInt(move _2) -> [0: bb1, 1: bb2, otherwise: bb3]; - } - - bb1: { - assume(const false); - _3 = core::panicking::panic_nounwind(const "unsafe precondition(s) violated: hint::unreachable_unchecked must never be reached") -> unwind unreachable; - } - - bb2: { + _3 = Ne(_2, const 0_isize); + assume(move _3); + _4 = Eq(_2, const 1_isize); + assume(move _4); _0 = move ((_1 as Some).0: T); - StorageDead(_2); StorageDead(_4); + StorageDead(_3); + StorageDead(_2); + StorageDead(_5); return; } - - bb3: { - unreachable; - } } diff --git a/tests/mir-opt/pre-codegen/duplicate_switch_targets.ub_if_b.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/duplicate_switch_targets.ub_if_b.PreCodegen.after.mir index d5d5253a8b783..fe7beadc8188c 100644 --- a/tests/mir-opt/pre-codegen/duplicate_switch_targets.ub_if_b.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/duplicate_switch_targets.ub_if_b.PreCodegen.after.mir @@ -4,30 +4,22 @@ fn ub_if_b(_1: Thing) -> Thing { debug t => _1; let mut _0: Thing; let mut _2: isize; + let mut _3: bool; + let mut _4: bool; scope 1 (inlined unreachable_unchecked) { scope 2 { scope 3 (inlined unreachable_unchecked::runtime) { - let _3: !; } } } bb0: { _2 = discriminant(_1); - switchInt(move _2) -> [0: bb1, 1: bb2, otherwise: bb3]; - } - - bb1: { + _3 = Ne(_2, const 1_isize); + assume(move _3); + _4 = Eq(_2, const 0_isize); + assume(move _4); _0 = move _1; return; } - - bb2: { - assume(const false); - _3 = core::panicking::panic_nounwind(const "unsafe precondition(s) violated: hint::unreachable_unchecked must never be reached") -> unwind unreachable; - } - - bb3: { - unreachable; - } } From 096196d5b0f8035d461b9611e32c18d9696fe7e1 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sat, 22 Jul 2023 16:59:13 +0000 Subject: [PATCH 4/6] Refactor UninhabitedEnumBranching to mark targets unreachable. --- .../src/uninhabited_enum_branching.rs | 104 ++++++++---------- ...after-uninhabited-enum-branching.after.mir | 8 +- ...anching.main.UninhabitedEnumBranching.diff | 9 +- ...after-uninhabited-enum-branching.after.mir | 12 ++ ...nching2.main.UninhabitedEnumBranching.diff | 8 +- ..._fallthrough.UninhabitedEnumBranching.diff | 6 +- 6 files changed, 84 insertions(+), 63 deletions(-) diff --git a/compiler/rustc_mir_transform/src/uninhabited_enum_branching.rs b/compiler/rustc_mir_transform/src/uninhabited_enum_branching.rs index cb028a92d49b2..98f67e18a8d0e 100644 --- a/compiler/rustc_mir_transform/src/uninhabited_enum_branching.rs +++ b/compiler/rustc_mir_transform/src/uninhabited_enum_branching.rs @@ -3,8 +3,7 @@ use crate::MirPass; use rustc_data_structures::fx::FxHashSet; use rustc_middle::mir::{ - BasicBlockData, Body, Local, Operand, Rvalue, StatementKind, SwitchTargets, Terminator, - TerminatorKind, + BasicBlockData, Body, Local, Operand, Rvalue, StatementKind, Terminator, TerminatorKind, }; use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::ty::{Ty, TyCtxt}; @@ -30,17 +29,20 @@ fn get_switched_on_type<'tcx>( let terminator = block_data.terminator(); // Only bother checking blocks which terminate by switching on a local. - if let Some(local) = get_discriminant_local(&terminator.kind) - && let [.., stmt_before_term] = &block_data.statements[..] - && let StatementKind::Assign(box (l, Rvalue::Discriminant(place))) = stmt_before_term.kind + let local = get_discriminant_local(&terminator.kind)?; + + let stmt_before_term = block_data.statements.last()?; + + if let StatementKind::Assign(box (l, Rvalue::Discriminant(place))) = stmt_before_term.kind && l.as_local() == Some(local) - && let ty = place.ty(body, tcx).ty - && ty.is_enum() { - Some(ty) - } else { - None + let ty = place.ty(body, tcx).ty; + if ty.is_enum() { + return Some(ty); + } } + + None } fn variant_discriminants<'tcx>( @@ -67,28 +69,6 @@ fn variant_discriminants<'tcx>( } } -/// Ensures that the `otherwise` branch leads to an unreachable bb, returning `None` if so and a new -/// bb to use as the new target if not. -fn ensure_otherwise_unreachable<'tcx>( - body: &Body<'tcx>, - targets: &SwitchTargets, -) -> Option> { - let otherwise = targets.otherwise(); - let bb = &body.basic_blocks[otherwise]; - if bb.terminator().kind == TerminatorKind::Unreachable - && bb.statements.iter().all(|s| matches!(&s.kind, StatementKind::StorageDead(_))) - { - return None; - } - - let mut new_block = BasicBlockData::new(Some(Terminator { - source_info: bb.terminator().source_info, - kind: TerminatorKind::Unreachable, - })); - new_block.is_cleanup = bb.is_cleanup; - Some(new_block) -} - impl<'tcx> MirPass<'tcx> for UninhabitedEnumBranching { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { sess.mir_opt_level() > 0 @@ -97,13 +77,16 @@ impl<'tcx> MirPass<'tcx> for UninhabitedEnumBranching { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { trace!("UninhabitedEnumBranching starting for {:?}", body.source); - for bb in body.basic_blocks.indices() { + let mut removable_switchs = Vec::new(); + + for (bb, bb_data) in body.basic_blocks.iter_enumerated() { trace!("processing block {:?}", bb); - let Some(discriminant_ty) = get_switched_on_type(&body.basic_blocks[bb], tcx, body) - else { + if bb_data.is_cleanup { continue; - }; + } + + let Some(discriminant_ty) = get_switched_on_type(&bb_data, tcx, body) else { continue }; let layout = tcx.layout_of( tcx.param_env_reveal_all_normalized(body.source.def_id()).and(discriminant_ty), @@ -117,31 +100,38 @@ impl<'tcx> MirPass<'tcx> for UninhabitedEnumBranching { trace!("allowed_variants = {:?}", allowed_variants); - if let TerminatorKind::SwitchInt { targets, .. } = - &mut body.basic_blocks_mut()[bb].terminator_mut().kind - { - let mut new_targets = SwitchTargets::new( - targets.iter().filter(|(val, _)| allowed_variants.contains(val)), - targets.otherwise(), - ); - - if new_targets.iter().count() == allowed_variants.len() { - if let Some(updated) = ensure_otherwise_unreachable(body, &new_targets) { - let new_otherwise = body.basic_blocks_mut().push(updated); - *new_targets.all_targets_mut().last_mut().unwrap() = new_otherwise; - } - } + let terminator = bb_data.terminator(); + let TerminatorKind::SwitchInt { targets, .. } = &terminator.kind else { bug!() }; - if let TerminatorKind::SwitchInt { targets, .. } = - &mut body.basic_blocks_mut()[bb].terminator_mut().kind - { - *targets = new_targets; + let mut reachable_count = 0; + for (index, (val, _)) in targets.iter().enumerate() { + if allowed_variants.contains(&val) { + reachable_count += 1; } else { - unreachable!() + removable_switchs.push((bb, index)); } - } else { - unreachable!() } + + if reachable_count == allowed_variants.len() { + removable_switchs.push((bb, targets.iter().count())); + } + } + + if removable_switchs.is_empty() { + return; + } + + let new_block = BasicBlockData::new(Some(Terminator { + source_info: body.basic_blocks[removable_switchs[0].0].terminator().source_info, + kind: TerminatorKind::Unreachable, + })); + let unreachable_block = body.basic_blocks.as_mut().push(new_block); + + for (bb, index) in removable_switchs { + let bb = &mut body.basic_blocks.as_mut()[bb]; + let terminator = bb.terminator_mut(); + let TerminatorKind::SwitchInt { targets, .. } = &mut terminator.kind else { bug!() }; + targets.all_targets_mut()[index] = unreachable_block; } } } diff --git a/tests/mir-opt/uninhabited_enum_branching.main.SimplifyCfg-after-uninhabited-enum-branching.after.mir b/tests/mir-opt/uninhabited_enum_branching.main.SimplifyCfg-after-uninhabited-enum-branching.after.mir index 1ee44e48c8ec8..1df3b74b3e7d9 100644 --- a/tests/mir-opt/uninhabited_enum_branching.main.SimplifyCfg-after-uninhabited-enum-branching.after.mir +++ b/tests/mir-opt/uninhabited_enum_branching.main.SimplifyCfg-after-uninhabited-enum-branching.after.mir @@ -12,14 +12,20 @@ fn main() -> () { let mut _8: isize; let _9: &str; let mut _10: bool; + let mut _11: bool; + let mut _12: bool; bb0: { StorageLive(_1); StorageLive(_2); _2 = Test1::C; _3 = discriminant(_2); - _10 = Eq(_3, const 2_isize); + _10 = Ne(_3, const 0_isize); assume(move _10); + _11 = Ne(_3, const 1_isize); + assume(move _11); + _12 = Eq(_3, const 2_isize); + assume(move _12); StorageLive(_5); _5 = const "C"; _1 = &(*_5); diff --git a/tests/mir-opt/uninhabited_enum_branching.main.UninhabitedEnumBranching.diff b/tests/mir-opt/uninhabited_enum_branching.main.UninhabitedEnumBranching.diff index 9db95abec34c8..5b107f80ce80b 100644 --- a/tests/mir-opt/uninhabited_enum_branching.main.UninhabitedEnumBranching.diff +++ b/tests/mir-opt/uninhabited_enum_branching.main.UninhabitedEnumBranching.diff @@ -19,7 +19,7 @@ _2 = Test1::C; _3 = discriminant(_2); - switchInt(move _3) -> [0: bb3, 1: bb4, 2: bb1, otherwise: bb2]; -+ switchInt(move _3) -> [2: bb1, otherwise: bb2]; ++ switchInt(move _3) -> [0: bb9, 1: bb9, 2: bb1, otherwise: bb9]; } bb1: { @@ -54,7 +54,8 @@ StorageLive(_7); _7 = Test2::D; _8 = discriminant(_7); - switchInt(move _8) -> [4: bb7, 5: bb6, otherwise: bb2]; +- switchInt(move _8) -> [4: bb7, 5: bb6, otherwise: bb2]; ++ switchInt(move _8) -> [4: bb7, 5: bb6, otherwise: bb9]; } bb6: { @@ -75,6 +76,10 @@ StorageDead(_6); _0 = const (); return; ++ } ++ ++ bb9: { ++ unreachable; } } diff --git a/tests/mir-opt/uninhabited_enum_branching2.main.SimplifyCfg-after-uninhabited-enum-branching.after.mir b/tests/mir-opt/uninhabited_enum_branching2.main.SimplifyCfg-after-uninhabited-enum-branching.after.mir index 9c0c5d18917ae..06375b3ffaebf 100644 --- a/tests/mir-opt/uninhabited_enum_branching2.main.SimplifyCfg-after-uninhabited-enum-branching.after.mir +++ b/tests/mir-opt/uninhabited_enum_branching2.main.SimplifyCfg-after-uninhabited-enum-branching.after.mir @@ -15,6 +15,10 @@ fn main() -> () { let _11: &str; let _12: &str; let _13: &str; + let mut _14: bool; + let mut _15: bool; + let mut _16: bool; + let mut _17: bool; scope 1 { debug plop => _1; } @@ -29,6 +33,10 @@ fn main() -> () { StorageLive(_4); _4 = &(_1.1: Test1); _5 = discriminant((*_4)); + _16 = Ne(_5, const 0_isize); + assume(move _16); + _17 = Ne(_5, const 1_isize); + assume(move _17); switchInt(move _5) -> [2: bb3, 3: bb1, otherwise: bb2]; } @@ -57,6 +65,10 @@ fn main() -> () { StorageDead(_3); StorageLive(_9); _10 = discriminant((_1.1: Test1)); + _14 = Ne(_10, const 0_isize); + assume(move _14); + _15 = Ne(_10, const 1_isize); + assume(move _15); switchInt(move _10) -> [2: bb6, 3: bb5, otherwise: bb2]; } diff --git a/tests/mir-opt/uninhabited_enum_branching2.main.UninhabitedEnumBranching.diff b/tests/mir-opt/uninhabited_enum_branching2.main.UninhabitedEnumBranching.diff index 12ce6505af948..165421acd6916 100644 --- a/tests/mir-opt/uninhabited_enum_branching2.main.UninhabitedEnumBranching.diff +++ b/tests/mir-opt/uninhabited_enum_branching2.main.UninhabitedEnumBranching.diff @@ -31,7 +31,7 @@ _4 = &(_1.1: Test1); _5 = discriminant((*_4)); - switchInt(move _5) -> [0: bb3, 1: bb4, 2: bb5, 3: bb1, otherwise: bb2]; -+ switchInt(move _5) -> [2: bb5, 3: bb1, otherwise: bb2]; ++ switchInt(move _5) -> [0: bb12, 1: bb12, 2: bb5, 3: bb1, otherwise: bb12]; } bb1: { @@ -73,7 +73,7 @@ StorageLive(_9); _10 = discriminant((_1.1: Test1)); - switchInt(move _10) -> [0: bb8, 1: bb9, 2: bb10, 3: bb7, otherwise: bb2]; -+ switchInt(move _10) -> [2: bb10, 3: bb7, otherwise: bb2]; ++ switchInt(move _10) -> [0: bb12, 1: bb12, 2: bb10, 3: bb7, otherwise: bb12]; } bb7: { @@ -110,6 +110,10 @@ _0 = const (); StorageDead(_1); return; ++ } ++ ++ bb12: { ++ unreachable; } } diff --git a/tests/mir-opt/uninhabited_fallthrough_elimination.keep_fallthrough.UninhabitedEnumBranching.diff b/tests/mir-opt/uninhabited_fallthrough_elimination.keep_fallthrough.UninhabitedEnumBranching.diff index 498e1e20f8a79..79948139f888d 100644 --- a/tests/mir-opt/uninhabited_fallthrough_elimination.keep_fallthrough.UninhabitedEnumBranching.diff +++ b/tests/mir-opt/uninhabited_fallthrough_elimination.keep_fallthrough.UninhabitedEnumBranching.diff @@ -9,7 +9,7 @@ bb0: { _2 = discriminant(_1); - switchInt(move _2) -> [0: bb2, 1: bb3, otherwise: bb1]; -+ switchInt(move _2) -> [1: bb3, otherwise: bb1]; ++ switchInt(move _2) -> [0: bb5, 1: bb3, otherwise: bb1]; } bb1: { @@ -29,6 +29,10 @@ bb4: { return; ++ } ++ ++ bb5: { ++ unreachable; } } From cb918904fe693e93c679d4c3417cfe7776f8ebe2 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sun, 24 Sep 2023 08:27:42 +0000 Subject: [PATCH 5/6] Only emit `!=` assumptions if the otherwise target is reachable. --- .../src/unreachable_prop.rs | 13 +++--- ...d.unwrap_unchecked.Inline.panic-abort.diff | 7 +-- ....unwrap_unchecked.Inline.panic-unwind.diff | 7 +-- ...unchecked.PreCodegen.after.panic-abort.mir | 15 +++---- ...nchecked.PreCodegen.after.panic-unwind.mir | 15 +++---- ...witch_targets.ub_if_b.PreCodegen.after.mir | 5 +-- ...ch.UnreachablePropagation.panic-abort.diff | 45 +++++++++++++++++++ ...h.UnreachablePropagation.panic-unwind.diff | 45 +++++++++++++++++++ ...t.UnreachablePropagation.panic-abort.diff} | 11 +++-- ....UnreachablePropagation.panic-unwind.diff} | 11 +++-- tests/mir-opt/unreachable.rs | 32 +++++++++++-- 11 files changed, 148 insertions(+), 58 deletions(-) create mode 100644 tests/mir-opt/unreachable.as_match.UnreachablePropagation.panic-abort.diff create mode 100644 tests/mir-opt/unreachable.as_match.UnreachablePropagation.panic-unwind.diff rename tests/mir-opt/{unreachable.main.UnreachablePropagation.panic-abort.diff => unreachable.if_let.UnreachablePropagation.panic-abort.diff} (86%) rename tests/mir-opt/{unreachable.main.UnreachablePropagation.panic-unwind.diff => unreachable.if_let.UnreachablePropagation.panic-unwind.diff} (86%) diff --git a/compiler/rustc_mir_transform/src/unreachable_prop.rs b/compiler/rustc_mir_transform/src/unreachable_prop.rs index d25c6d471cd2a..919e8d6a2340f 100644 --- a/compiler/rustc_mir_transform/src/unreachable_prop.rs +++ b/compiler/rustc_mir_transform/src/unreachable_prop.rs @@ -116,22 +116,21 @@ fn remove_successors_from_switch<'tcx>( patch.add_statement(location, StatementKind::Intrinsic(Box::new(assume))); }; + let otherwise = targets.otherwise(); + let otherwise_unreachable = is_unreachable(otherwise); + let reachable_iter = targets.iter().filter(|&(value, bb)| { let is_unreachable = is_unreachable(bb); - if is_unreachable { - // We remove this target from the switch, so record the inequality using `Assume`. + // We remove this target from the switch, so record the inequality using `Assume`. + if is_unreachable && !otherwise_unreachable { add_assumption(BinOp::Ne, value); - false - } else { - true } + !is_unreachable }); - let otherwise = targets.otherwise(); let new_targets = SwitchTargets::new(reachable_iter, otherwise); let num_targets = new_targets.all_targets().len(); - let otherwise_unreachable = is_unreachable(otherwise); let fully_unreachable = num_targets == 1 && otherwise_unreachable; let terminator = match (num_targets, otherwise_unreachable) { 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 4ca7a2c4294ff..2a36ad9230e4b 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,7 +10,6 @@ + let mut _3: &std::option::Option; + let mut _4: isize; + let mut _5: bool; -+ let mut _6: bool; + scope 2 { + debug val => _0; + } @@ -37,14 +36,10 @@ + StorageLive(_3); + StorageLive(_4); + StorageLive(_5); -+ StorageLive(_6); + _4 = discriminant(_2); -+ _5 = Ne(_4, const 0_isize); ++ _5 = Eq(_4, const 1_isize); + assume(move _5); -+ _6 = Eq(_4, const 1_isize); -+ assume(move _6); + _0 = move ((_2 as Some).0: T); -+ StorageDead(_6); + StorageDead(_5); + StorageDead(_4); + StorageDead(_3); 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 caabb7ea46358..14c8c671d3fe2 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,7 +10,6 @@ + let mut _3: &std::option::Option; + let mut _4: isize; + let mut _5: bool; -+ let mut _6: bool; + scope 2 { + debug val => _0; + } @@ -37,14 +36,10 @@ + StorageLive(_3); + StorageLive(_4); + StorageLive(_5); -+ StorageLive(_6); + _4 = discriminant(_2); -+ _5 = Ne(_4, const 0_isize); ++ _5 = Eq(_4, const 1_isize); + assume(move _5); -+ _6 = Eq(_4, const 1_isize); -+ assume(move _6); + _0 = move ((_2 as Some).0: T); -+ StorageDead(_6); + StorageDead(_5); + StorageDead(_4); + StorageDead(_3); diff --git a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-abort.mir b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-abort.mir index 521266925e0d4..aeb93bd334fe6 100644 --- a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-abort.mir @@ -7,8 +7,7 @@ fn unwrap_unchecked(_1: Option) -> T { debug self => _1; let mut _2: isize; let mut _3: bool; - let mut _4: bool; - let mut _5: &std::option::Option; + let mut _4: &std::option::Option; scope 2 { debug val => _0; } @@ -21,25 +20,21 @@ fn unwrap_unchecked(_1: Option) -> T { } } scope 4 (inlined Option::::is_some) { - debug self => _5; + debug self => _4; } } bb0: { - StorageLive(_5); + StorageLive(_4); StorageLive(_2); StorageLive(_3); - StorageLive(_4); _2 = discriminant(_1); - _3 = Ne(_2, const 0_isize); + _3 = Eq(_2, const 1_isize); assume(move _3); - _4 = Eq(_2, const 1_isize); - assume(move _4); _0 = move ((_1 as Some).0: T); - StorageDead(_4); StorageDead(_3); StorageDead(_2); - StorageDead(_5); + StorageDead(_4); return; } } diff --git a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-unwind.mir index 521266925e0d4..aeb93bd334fe6 100644 --- a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-unwind.mir @@ -7,8 +7,7 @@ fn unwrap_unchecked(_1: Option) -> T { debug self => _1; let mut _2: isize; let mut _3: bool; - let mut _4: bool; - let mut _5: &std::option::Option; + let mut _4: &std::option::Option; scope 2 { debug val => _0; } @@ -21,25 +20,21 @@ fn unwrap_unchecked(_1: Option) -> T { } } scope 4 (inlined Option::::is_some) { - debug self => _5; + debug self => _4; } } bb0: { - StorageLive(_5); + StorageLive(_4); StorageLive(_2); StorageLive(_3); - StorageLive(_4); _2 = discriminant(_1); - _3 = Ne(_2, const 0_isize); + _3 = Eq(_2, const 1_isize); assume(move _3); - _4 = Eq(_2, const 1_isize); - assume(move _4); _0 = move ((_1 as Some).0: T); - StorageDead(_4); StorageDead(_3); StorageDead(_2); - StorageDead(_5); + StorageDead(_4); return; } } diff --git a/tests/mir-opt/pre-codegen/duplicate_switch_targets.ub_if_b.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/duplicate_switch_targets.ub_if_b.PreCodegen.after.mir index fe7beadc8188c..0114309dbb58e 100644 --- a/tests/mir-opt/pre-codegen/duplicate_switch_targets.ub_if_b.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/duplicate_switch_targets.ub_if_b.PreCodegen.after.mir @@ -5,7 +5,6 @@ fn ub_if_b(_1: Thing) -> Thing { let mut _0: Thing; let mut _2: isize; let mut _3: bool; - let mut _4: bool; scope 1 (inlined unreachable_unchecked) { scope 2 { scope 3 (inlined unreachable_unchecked::runtime) { @@ -15,10 +14,8 @@ fn ub_if_b(_1: Thing) -> Thing { bb0: { _2 = discriminant(_1); - _3 = Ne(_2, const 1_isize); + _3 = Eq(_2, const 0_isize); assume(move _3); - _4 = Eq(_2, const 0_isize); - assume(move _4); _0 = move _1; return; } diff --git a/tests/mir-opt/unreachable.as_match.UnreachablePropagation.panic-abort.diff b/tests/mir-opt/unreachable.as_match.UnreachablePropagation.panic-abort.diff new file mode 100644 index 0000000000000..f6e594ffac765 --- /dev/null +++ b/tests/mir-opt/unreachable.as_match.UnreachablePropagation.panic-abort.diff @@ -0,0 +1,45 @@ +- // MIR for `as_match` before UnreachablePropagation ++ // MIR for `as_match` after UnreachablePropagation + + fn as_match() -> () { + let mut _0: (); + let mut _1: std::option::Option; + let mut _2: isize; + let _3: Empty; + let mut _4: !; ++ let mut _5: bool; + scope 1 { + debug _x => _3; + } + + bb0: { + StorageLive(_1); + _1 = empty() -> [return: bb1, unwind unreachable]; + } + + bb1: { + _2 = discriminant(_1); +- switchInt(move _2) -> [0: bb4, 1: bb2, otherwise: bb3]; ++ _5 = Eq(_2, const 0_isize); ++ assume(move _5); ++ goto -> bb4; + } + + bb2: { +- StorageLive(_3); +- _3 = move ((_1 as Some).0: Empty); +- StorageLive(_4); + unreachable; + } + + bb3: { + unreachable; + } + + bb4: { + _0 = const (); + StorageDead(_1); + return; + } + } + diff --git a/tests/mir-opt/unreachable.as_match.UnreachablePropagation.panic-unwind.diff b/tests/mir-opt/unreachable.as_match.UnreachablePropagation.panic-unwind.diff new file mode 100644 index 0000000000000..2813d64672e6a --- /dev/null +++ b/tests/mir-opt/unreachable.as_match.UnreachablePropagation.panic-unwind.diff @@ -0,0 +1,45 @@ +- // MIR for `as_match` before UnreachablePropagation ++ // MIR for `as_match` after UnreachablePropagation + + fn as_match() -> () { + let mut _0: (); + let mut _1: std::option::Option; + let mut _2: isize; + let _3: Empty; + let mut _4: !; ++ let mut _5: bool; + scope 1 { + debug _x => _3; + } + + bb0: { + StorageLive(_1); + _1 = empty() -> [return: bb1, unwind continue]; + } + + bb1: { + _2 = discriminant(_1); +- switchInt(move _2) -> [0: bb4, 1: bb2, otherwise: bb3]; ++ _5 = Eq(_2, const 0_isize); ++ assume(move _5); ++ goto -> bb4; + } + + bb2: { +- StorageLive(_3); +- _3 = move ((_1 as Some).0: Empty); +- StorageLive(_4); + unreachable; + } + + bb3: { + unreachable; + } + + bb4: { + _0 = const (); + StorageDead(_1); + return; + } + } + diff --git a/tests/mir-opt/unreachable.main.UnreachablePropagation.panic-abort.diff b/tests/mir-opt/unreachable.if_let.UnreachablePropagation.panic-abort.diff similarity index 86% rename from tests/mir-opt/unreachable.main.UnreachablePropagation.panic-abort.diff rename to tests/mir-opt/unreachable.if_let.UnreachablePropagation.panic-abort.diff index 6f4faefce145e..61959732720e0 100644 --- a/tests/mir-opt/unreachable.main.UnreachablePropagation.panic-abort.diff +++ b/tests/mir-opt/unreachable.if_let.UnreachablePropagation.panic-abort.diff @@ -1,7 +1,7 @@ -- // MIR for `main` before UnreachablePropagation -+ // MIR for `main` after UnreachablePropagation +- // MIR for `if_let` before UnreachablePropagation ++ // MIR for `if_let` after UnreachablePropagation - fn main() -> () { + fn if_let() -> () { let mut _0: (); let mut _1: std::option::Option; let mut _2: isize; @@ -9,7 +9,6 @@ let mut _6: bool; let mut _7: !; + let mut _8: bool; -+ let mut _9: bool; scope 1 { debug _x => _3; let _3: Empty; @@ -27,8 +26,8 @@ bb1: { _2 = discriminant(_1); - switchInt(move _2) -> [1: bb2, otherwise: bb6]; -+ _9 = Ne(_2, const 1_isize); -+ assume(move _9); ++ _8 = Ne(_2, const 1_isize); ++ assume(move _8); + goto -> bb6; } diff --git a/tests/mir-opt/unreachable.main.UnreachablePropagation.panic-unwind.diff b/tests/mir-opt/unreachable.if_let.UnreachablePropagation.panic-unwind.diff similarity index 86% rename from tests/mir-opt/unreachable.main.UnreachablePropagation.panic-unwind.diff rename to tests/mir-opt/unreachable.if_let.UnreachablePropagation.panic-unwind.diff index 5bacb42bed3f2..476e2f5599449 100644 --- a/tests/mir-opt/unreachable.main.UnreachablePropagation.panic-unwind.diff +++ b/tests/mir-opt/unreachable.if_let.UnreachablePropagation.panic-unwind.diff @@ -1,7 +1,7 @@ -- // MIR for `main` before UnreachablePropagation -+ // MIR for `main` after UnreachablePropagation +- // MIR for `if_let` before UnreachablePropagation ++ // MIR for `if_let` after UnreachablePropagation - fn main() -> () { + fn if_let() -> () { let mut _0: (); let mut _1: std::option::Option; let mut _2: isize; @@ -9,7 +9,6 @@ let mut _6: bool; let mut _7: !; + let mut _8: bool; -+ let mut _9: bool; scope 1 { debug _x => _3; let _3: Empty; @@ -27,8 +26,8 @@ bb1: { _2 = discriminant(_1); - switchInt(move _2) -> [1: bb2, otherwise: bb6]; -+ _9 = Ne(_2, const 1_isize); -+ assume(move _9); ++ _8 = Ne(_2, const 1_isize); ++ assume(move _8); + goto -> bb6; } diff --git a/tests/mir-opt/unreachable.rs b/tests/mir-opt/unreachable.rs index 3d934ace261e7..5b96681d9df58 100644 --- a/tests/mir-opt/unreachable.rs +++ b/tests/mir-opt/unreachable.rs @@ -7,9 +7,9 @@ fn empty() -> Option { None } -// EMIT_MIR unreachable.main.UnreachablePropagation.diff -fn main() { - // CHECK-LABEL: fn main( +// EMIT_MIR unreachable.if_let.UnreachablePropagation.diff +fn if_let() { + // CHECK-LABEL: fn if_let( // CHECK: bb0: { // CHECK: {{_.*}} = empty() // CHECK: bb1: { @@ -38,3 +38,29 @@ fn main() { match _x { } } } + +// EMIT_MIR unreachable.as_match.UnreachablePropagation.diff +fn as_match() { + // CHECK-LABEL: fn as_match( + // CHECK: bb0: { + // CHECK: {{_.*}} = empty() + // CHECK: bb1: { + // CHECK: [[eq:_.*]] = Eq({{.*}}, const 0_isize); + // CHECK-NEXT: assume(move [[eq]]); + // CHECK-NEXT: goto -> bb4; + // CHECK: bb2: { + // CHECK-NEXT: unreachable; + // CHECK: bb3: { + // CHECK-NEXT: unreachable; + // CHECK: bb4: { + // CHECK: return; + match empty() { + None => {} + Some(_x) => match _x {} + } +} + +fn main() { + if_let(); + as_match(); +} From ae2e21114becdd27b426baaa385a90aca0cfa412 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sat, 21 Oct 2023 13:45:25 +0000 Subject: [PATCH 6/6] FileCheck uninhabited_enum_branching. --- ...ching.byref.UninhabitedEnumBranching.diff} | 18 ++-- ...discriminant.UninhabitedEnumBranching.diff | 48 +++++++++ ...after-uninhabited-enum-branching.after.mir | 65 ------------- tests/mir-opt/uninhabited_enum_branching.rs | 70 +++++++++++-- ...hing.simple.UninhabitedEnumBranching.diff} | 38 +------- ...after-uninhabited-enum-branching.after.mir | 97 ------------------- tests/mir-opt/uninhabited_enum_branching2.rs | 35 ------- 7 files changed, 126 insertions(+), 245 deletions(-) rename tests/mir-opt/{uninhabited_enum_branching2.main.UninhabitedEnumBranching.diff => uninhabited_enum_branching.byref.UninhabitedEnumBranching.diff} (87%) create mode 100644 tests/mir-opt/uninhabited_enum_branching.custom_discriminant.UninhabitedEnumBranching.diff delete mode 100644 tests/mir-opt/uninhabited_enum_branching.main.SimplifyCfg-after-uninhabited-enum-branching.after.mir rename tests/mir-opt/{uninhabited_enum_branching.main.UninhabitedEnumBranching.diff => uninhabited_enum_branching.simple.UninhabitedEnumBranching.diff} (52%) delete mode 100644 tests/mir-opt/uninhabited_enum_branching2.main.SimplifyCfg-after-uninhabited-enum-branching.after.mir delete mode 100644 tests/mir-opt/uninhabited_enum_branching2.rs diff --git a/tests/mir-opt/uninhabited_enum_branching2.main.UninhabitedEnumBranching.diff b/tests/mir-opt/uninhabited_enum_branching.byref.UninhabitedEnumBranching.diff similarity index 87% rename from tests/mir-opt/uninhabited_enum_branching2.main.UninhabitedEnumBranching.diff rename to tests/mir-opt/uninhabited_enum_branching.byref.UninhabitedEnumBranching.diff index 165421acd6916..7919450cdc52a 100644 --- a/tests/mir-opt/uninhabited_enum_branching2.main.UninhabitedEnumBranching.diff +++ b/tests/mir-opt/uninhabited_enum_branching.byref.UninhabitedEnumBranching.diff @@ -1,12 +1,12 @@ -- // MIR for `main` before UninhabitedEnumBranching -+ // MIR for `main` after UninhabitedEnumBranching +- // MIR for `byref` before UninhabitedEnumBranching ++ // MIR for `byref` after UninhabitedEnumBranching - fn main() -> () { + fn byref() -> () { let mut _0: (); let _1: Plop; - let mut _2: Test1; + let mut _2: Test3; let _3: &str; - let mut _4: &Test1; + let mut _4: &Test3; let mut _5: isize; let _6: &str; let _7: &str; @@ -23,12 +23,12 @@ bb0: { StorageLive(_1); StorageLive(_2); - _2 = Test1::C; - _1 = Plop { xx: const 51_u32, test1: move _2 }; + _2 = Test3::C; + _1 = Plop { xx: const 51_u32, test3: move _2 }; StorageDead(_2); StorageLive(_3); StorageLive(_4); - _4 = &(_1.1: Test1); + _4 = &(_1.1: Test3); _5 = discriminant((*_4)); - switchInt(move _5) -> [0: bb3, 1: bb4, 2: bb5, 3: bb1, otherwise: bb2]; + switchInt(move _5) -> [0: bb12, 1: bb12, 2: bb5, 3: bb1, otherwise: bb12]; @@ -71,7 +71,7 @@ StorageDead(_4); StorageDead(_3); StorageLive(_9); - _10 = discriminant((_1.1: Test1)); + _10 = discriminant((_1.1: Test3)); - switchInt(move _10) -> [0: bb8, 1: bb9, 2: bb10, 3: bb7, otherwise: bb2]; + switchInt(move _10) -> [0: bb12, 1: bb12, 2: bb10, 3: bb7, otherwise: bb12]; } diff --git a/tests/mir-opt/uninhabited_enum_branching.custom_discriminant.UninhabitedEnumBranching.diff b/tests/mir-opt/uninhabited_enum_branching.custom_discriminant.UninhabitedEnumBranching.diff new file mode 100644 index 0000000000000..5e15298a78c12 --- /dev/null +++ b/tests/mir-opt/uninhabited_enum_branching.custom_discriminant.UninhabitedEnumBranching.diff @@ -0,0 +1,48 @@ +- // MIR for `custom_discriminant` before UninhabitedEnumBranching ++ // MIR for `custom_discriminant` after UninhabitedEnumBranching + + fn custom_discriminant() -> () { + let mut _0: (); + let _1: &str; + let mut _2: Test2; + let mut _3: isize; + let _4: &str; + + bb0: { + StorageLive(_1); + StorageLive(_2); + _2 = Test2::D; + _3 = discriminant(_2); +- switchInt(move _3) -> [4: bb3, 5: bb1, otherwise: bb2]; ++ switchInt(move _3) -> [4: bb3, 5: bb1, otherwise: bb5]; + } + + bb1: { + StorageLive(_4); + _4 = const "E"; + _1 = &(*_4); + StorageDead(_4); + goto -> bb4; + } + + bb2: { + unreachable; + } + + bb3: { + _1 = const "D"; + goto -> bb4; + } + + bb4: { + StorageDead(_2); + StorageDead(_1); + _0 = const (); + return; ++ } ++ ++ bb5: { ++ unreachable; + } + } + diff --git a/tests/mir-opt/uninhabited_enum_branching.main.SimplifyCfg-after-uninhabited-enum-branching.after.mir b/tests/mir-opt/uninhabited_enum_branching.main.SimplifyCfg-after-uninhabited-enum-branching.after.mir deleted file mode 100644 index 1df3b74b3e7d9..0000000000000 --- a/tests/mir-opt/uninhabited_enum_branching.main.SimplifyCfg-after-uninhabited-enum-branching.after.mir +++ /dev/null @@ -1,65 +0,0 @@ -// MIR for `main` after SimplifyCfg-after-uninhabited-enum-branching - -fn main() -> () { - let mut _0: (); - let _1: &str; - let mut _2: Test1; - let mut _3: isize; - let _4: &str; - let _5: &str; - let _6: &str; - let mut _7: Test2; - let mut _8: isize; - let _9: &str; - let mut _10: bool; - let mut _11: bool; - let mut _12: bool; - - bb0: { - StorageLive(_1); - StorageLive(_2); - _2 = Test1::C; - _3 = discriminant(_2); - _10 = Ne(_3, const 0_isize); - assume(move _10); - _11 = Ne(_3, const 1_isize); - assume(move _11); - _12 = Eq(_3, const 2_isize); - assume(move _12); - StorageLive(_5); - _5 = const "C"; - _1 = &(*_5); - StorageDead(_5); - StorageDead(_2); - StorageDead(_1); - StorageLive(_6); - StorageLive(_7); - _7 = Test2::D; - _8 = discriminant(_7); - switchInt(move _8) -> [4: bb3, 5: bb2, otherwise: bb1]; - } - - bb1: { - unreachable; - } - - bb2: { - StorageLive(_9); - _9 = const "E"; - _6 = &(*_9); - StorageDead(_9); - goto -> bb4; - } - - bb3: { - _6 = const "D"; - goto -> bb4; - } - - bb4: { - StorageDead(_7); - StorageDead(_6); - _0 = const (); - return; - } -} diff --git a/tests/mir-opt/uninhabited_enum_branching.rs b/tests/mir-opt/uninhabited_enum_branching.rs index 96ae84eca5093..60389117b1614 100644 --- a/tests/mir-opt/uninhabited_enum_branching.rs +++ b/tests/mir-opt/uninhabited_enum_branching.rs @@ -1,11 +1,11 @@ -// skip-filecheck -enum Empty { } +// unit-test: UninhabitedEnumBranching +enum Empty {} // test matching an enum with uninhabited variants enum Test1 { A(Empty), B(Empty), - C + C, } // test an enum where the discriminants don't match the variant indexes @@ -15,17 +15,75 @@ enum Test2 { E = 5, } -// EMIT_MIR uninhabited_enum_branching.main.UninhabitedEnumBranching.diff -// EMIT_MIR uninhabited_enum_branching.main.SimplifyCfg-after-uninhabited-enum-branching.after.mir -fn main() { +// test matching an enum with uninhabited variants and multiple inhabited +enum Test3 { + A(Empty), + B(Empty), + C, + D, +} + +struct Plop { + xx: u32, + test3: Test3, +} + +// EMIT_MIR uninhabited_enum_branching.simple.UninhabitedEnumBranching.diff +fn simple() { + // CHECK-LABEL: fn simple( + // CHECK: [[discr:_.*]] = discriminant( + // CHECK: switchInt(move [[discr]]) -> [0: [[unreachable:bb.*]], 1: [[unreachable]], 2: bb1, otherwise: [[unreachable]]]; + // CHECK: [[unreachable]]: { + // CHECK-NEXT: unreachable; match Test1::C { Test1::A(_) => "A(Empty)", Test1::B(_) => "B(Empty)", Test1::C => "C", }; +} +// EMIT_MIR uninhabited_enum_branching.custom_discriminant.UninhabitedEnumBranching.diff +fn custom_discriminant() { + // CHECK-LABEL: fn custom_discriminant( + // CHECK: [[discr:_.*]] = discriminant( + // CHECK: switchInt(move [[discr]]) -> [4: bb3, 5: bb1, otherwise: bb5]; + // CHECK: bb5: { + // CHECK-NEXT: unreachable; match Test2::D { Test2::D => "D", Test2::E => "E", }; } + +// EMIT_MIR uninhabited_enum_branching.byref.UninhabitedEnumBranching.diff +fn byref() { + // CHECK-LABEL: fn byref( + let plop = Plop { xx: 51, test3: Test3::C }; + + // CHECK: [[ref_discr:_.*]] = discriminant((* + // CHECK: switchInt(move [[ref_discr]]) -> [0: [[unreachable:bb.*]], 1: [[unreachable]], 2: bb5, 3: bb1, otherwise: [[unreachable]]]; + match &plop.test3 { + Test3::A(_) => "A(Empty)", + Test3::B(_) => "B(Empty)", + Test3::C => "C", + Test3::D => "D", + }; + + // CHECK: [[discr:_.*]] = discriminant( + // CHECK: switchInt(move [[discr]]) -> [0: [[unreachable]], 1: [[unreachable]], 2: bb10, 3: bb7, otherwise: [[unreachable]]]; + match plop.test3 { + Test3::A(_) => "A(Empty)", + Test3::B(_) => "B(Empty)", + Test3::C => "C", + Test3::D => "D", + }; + + // CHECK: [[unreachable]]: { + // CHECK-NEXT: unreachable; +} + +fn main() { + simple(); + custom_discriminant(); + byref(); +} diff --git a/tests/mir-opt/uninhabited_enum_branching.main.UninhabitedEnumBranching.diff b/tests/mir-opt/uninhabited_enum_branching.simple.UninhabitedEnumBranching.diff similarity index 52% rename from tests/mir-opt/uninhabited_enum_branching.main.UninhabitedEnumBranching.diff rename to tests/mir-opt/uninhabited_enum_branching.simple.UninhabitedEnumBranching.diff index 5b107f80ce80b..410db79802edc 100644 --- a/tests/mir-opt/uninhabited_enum_branching.main.UninhabitedEnumBranching.diff +++ b/tests/mir-opt/uninhabited_enum_branching.simple.UninhabitedEnumBranching.diff @@ -1,17 +1,13 @@ -- // MIR for `main` before UninhabitedEnumBranching -+ // MIR for `main` after UninhabitedEnumBranching +- // MIR for `simple` before UninhabitedEnumBranching ++ // MIR for `simple` after UninhabitedEnumBranching - fn main() -> () { + fn simple() -> () { let mut _0: (); let _1: &str; let mut _2: Test1; let mut _3: isize; let _4: &str; let _5: &str; - let _6: &str; - let mut _7: Test2; - let mut _8: isize; - let _9: &str; bb0: { StorageLive(_1); @@ -19,7 +15,7 @@ _2 = Test1::C; _3 = discriminant(_2); - switchInt(move _3) -> [0: bb3, 1: bb4, 2: bb1, otherwise: bb2]; -+ switchInt(move _3) -> [0: bb9, 1: bb9, 2: bb1, otherwise: bb9]; ++ switchInt(move _3) -> [0: bb6, 1: bb6, 2: bb1, otherwise: bb6]; } bb1: { @@ -50,35 +46,11 @@ bb5: { StorageDead(_2); StorageDead(_1); - StorageLive(_6); - StorageLive(_7); - _7 = Test2::D; - _8 = discriminant(_7); -- switchInt(move _8) -> [4: bb7, 5: bb6, otherwise: bb2]; -+ switchInt(move _8) -> [4: bb7, 5: bb6, otherwise: bb9]; - } - - bb6: { - StorageLive(_9); - _9 = const "E"; - _6 = &(*_9); - StorageDead(_9); - goto -> bb8; - } - - bb7: { - _6 = const "D"; - goto -> bb8; - } - - bb8: { - StorageDead(_7); - StorageDead(_6); _0 = const (); return; + } + -+ bb9: { ++ bb6: { + unreachable; } } diff --git a/tests/mir-opt/uninhabited_enum_branching2.main.SimplifyCfg-after-uninhabited-enum-branching.after.mir b/tests/mir-opt/uninhabited_enum_branching2.main.SimplifyCfg-after-uninhabited-enum-branching.after.mir deleted file mode 100644 index 06375b3ffaebf..0000000000000 --- a/tests/mir-opt/uninhabited_enum_branching2.main.SimplifyCfg-after-uninhabited-enum-branching.after.mir +++ /dev/null @@ -1,97 +0,0 @@ -// MIR for `main` after SimplifyCfg-after-uninhabited-enum-branching - -fn main() -> () { - let mut _0: (); - let _1: Plop; - let mut _2: Test1; - let _3: &str; - let mut _4: &Test1; - let mut _5: isize; - let _6: &str; - let _7: &str; - let _8: &str; - let _9: &str; - let mut _10: isize; - let _11: &str; - let _12: &str; - let _13: &str; - let mut _14: bool; - let mut _15: bool; - let mut _16: bool; - let mut _17: bool; - scope 1 { - debug plop => _1; - } - - bb0: { - StorageLive(_1); - StorageLive(_2); - _2 = Test1::C; - _1 = Plop { xx: const 51_u32, test1: move _2 }; - StorageDead(_2); - StorageLive(_3); - StorageLive(_4); - _4 = &(_1.1: Test1); - _5 = discriminant((*_4)); - _16 = Ne(_5, const 0_isize); - assume(move _16); - _17 = Ne(_5, const 1_isize); - assume(move _17); - switchInt(move _5) -> [2: bb3, 3: bb1, otherwise: bb2]; - } - - bb1: { - StorageLive(_8); - _8 = const "D"; - _3 = &(*_8); - StorageDead(_8); - goto -> bb4; - } - - bb2: { - unreachable; - } - - bb3: { - StorageLive(_7); - _7 = const "C"; - _3 = &(*_7); - StorageDead(_7); - goto -> bb4; - } - - bb4: { - StorageDead(_4); - StorageDead(_3); - StorageLive(_9); - _10 = discriminant((_1.1: Test1)); - _14 = Ne(_10, const 0_isize); - assume(move _14); - _15 = Ne(_10, const 1_isize); - assume(move _15); - switchInt(move _10) -> [2: bb6, 3: bb5, otherwise: bb2]; - } - - bb5: { - StorageLive(_13); - _13 = const "D"; - _9 = &(*_13); - StorageDead(_13); - goto -> bb7; - } - - bb6: { - StorageLive(_12); - _12 = const "C"; - _9 = &(*_12); - StorageDead(_12); - goto -> bb7; - } - - bb7: { - StorageDead(_9); - _0 = const (); - StorageDead(_1); - return; - } -} diff --git a/tests/mir-opt/uninhabited_enum_branching2.rs b/tests/mir-opt/uninhabited_enum_branching2.rs deleted file mode 100644 index 751f2ae01f8ba..0000000000000 --- a/tests/mir-opt/uninhabited_enum_branching2.rs +++ /dev/null @@ -1,35 +0,0 @@ -// skip-filecheck -enum Empty { } - -// test matching an enum with uninhabited variants -enum Test1 { - A(Empty), - B(Empty), - C, - D, -} - -struct Plop { - xx: u32, - test1: Test1, -} - -// EMIT_MIR uninhabited_enum_branching2.main.UninhabitedEnumBranching.diff -// EMIT_MIR uninhabited_enum_branching2.main.SimplifyCfg-after-uninhabited-enum-branching.after.mir -fn main() { - let plop = Plop { xx: 51, test1: Test1::C }; - - match &plop.test1 { - Test1::A(_) => "A(Empty)", - Test1::B(_) => "B(Empty)", - Test1::C => "C", - Test1::D => "D", - }; - - match plop.test1 { - Test1::A(_) => "A(Empty)", - Test1::B(_) => "B(Empty)", - Test1::C => "C", - Test1::D => "D", - }; -}