about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_mir_transform/src/lib.rs5
-rw-r--r--compiler/rustc_mir_transform/src/simplify_branches.rs19
-rw-r--r--compiler/rustc_mir_transform/src/uninhabited_enum_branching.rs104
-rw-r--r--compiler/rustc_mir_transform/src/unreachable_prop.rs201
-rw-r--r--tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-abort.diff16
-rw-r--r--tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-unwind.diff28
-rw-r--r--tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-abort.mir21
-rw-r--r--tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-unwind.mir21
-rw-r--r--tests/mir-opt/pre-codegen/duplicate_switch_targets.ub_if_b.PreCodegen.after.mir11
-rw-r--r--tests/mir-opt/separate_const_switch.identity.SeparateConstSwitch.diff6
-rw-r--r--tests/mir-opt/uninhabited_enum_branching.byref.UninhabitedEnumBranching.diff (renamed from tests/mir-opt/uninhabited_enum_branching2.main.UninhabitedEnumBranching.diff)26
-rw-r--r--tests/mir-opt/uninhabited_enum_branching.custom_discriminant.UninhabitedEnumBranching.diff48
-rw-r--r--tests/mir-opt/uninhabited_enum_branching.main.SimplifyCfg-after-uninhabited-enum-branching.after.mir60
-rw-r--r--tests/mir-opt/uninhabited_enum_branching.rs70
-rw-r--r--tests/mir-opt/uninhabited_enum_branching.simple.UninhabitedEnumBranching.diff (renamed from tests/mir-opt/uninhabited_enum_branching.main.UninhabitedEnumBranching.diff)39
-rw-r--r--tests/mir-opt/uninhabited_enum_branching2.main.SimplifyCfg-after-uninhabited-enum-branching.after.mir85
-rw-r--r--tests/mir-opt/uninhabited_enum_branching2.rs35
-rw-r--r--tests/mir-opt/uninhabited_fallthrough_elimination.keep_fallthrough.UninhabitedEnumBranching.diff6
-rw-r--r--tests/mir-opt/unreachable.as_match.UnreachablePropagation.panic-abort.diff45
-rw-r--r--tests/mir-opt/unreachable.as_match.UnreachablePropagation.panic-unwind.diff45
-rw-r--r--tests/mir-opt/unreachable.if_let.UnreachablePropagation.panic-abort.diff (renamed from tests/mir-opt/unreachable.main.UnreachablePropagation.panic-abort.diff)35
-rw-r--r--tests/mir-opt/unreachable.if_let.UnreachablePropagation.panic-unwind.diff (renamed from tests/mir-opt/unreachable.main.UnreachablePropagation.panic-unwind.diff)35
-rw-r--r--tests/mir-opt/unreachable.rs50
-rw-r--r--tests/mir-opt/unreachable_diverging.main.UnreachablePropagation.panic-abort.diff6
-rw-r--r--tests/mir-opt/unreachable_diverging.main.UnreachablePropagation.panic-unwind.diff6
-rw-r--r--tests/mir-opt/unreachable_diverging.rs20
26 files changed, 573 insertions, 470 deletions
diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs
index dc35381fe22..bf5f0ca7cbd 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/simplify_branches.rs b/compiler/rustc_mir_transform/src/simplify_branches.rs
index b508cd1c9cc..1f0e605c3b8 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/compiler/rustc_mir_transform/src/uninhabited_enum_branching.rs b/compiler/rustc_mir_transform/src/uninhabited_enum_branching.rs
index cb028a92d49..98f67e18a8d 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<BasicBlockData<'tcx>> {
-    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/compiler/rustc_mir_transform/src/unreachable_prop.rs b/compiler/rustc_mir_transform/src/unreachable_prop.rs
index ea7aafd866b..919e8d6a234 100644
--- a/compiler/rustc_mir_transform/src/unreachable_prop.rs
+++ b/compiler/rustc_mir_transform/src/unreachable_prop.rs
@@ -2,11 +2,13 @@
 //! 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_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;
 
@@ -21,106 +23,133 @@ 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);
             }
         }
 
+        if !tcx
+            .consider_optimizing(|| format!("UnreachablePropagation {:?} ", body.source.def_id()))
+        {
+            return;
+        }
+
+        patch.apply(body);
+
         // 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();
         }
+    }
+}
 
-        let replaced = !replacements.is_empty();
+/// Return whether the current terminator is fully unreachable.
+fn remove_successors_from_switch<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    bb: BasicBlock,
+    unreachable_blocks: &FxHashSet<BasicBlock>,
+    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)));
+    };
 
-        for (bb, terminator_kind) in replacements {
-            if !tcx.consider_optimizing(|| {
-                format!("UnreachablePropagation {:?} ", body.source.def_id())
-            }) {
-                break;
-            }
+    let otherwise = targets.otherwise();
+    let otherwise_unreachable = is_unreachable(otherwise);
 
-            body.basic_blocks_mut()[bb].terminator_mut().kind = terminator_kind;
+    let reachable_iter = targets.iter().filter(|&(value, bb)| {
+        let is_unreachable = is_unreachable(bb);
+        // We remove this target from the switch, so record the inequality using `Assume`.
+        if is_unreachable && !otherwise_unreachable {
+            add_assumption(BinOp::Ne, value);
         }
-
-        if replaced {
-            simplify::remove_dead_blocks(body);
+        !is_unreachable
+    });
+
+    let new_targets = SwitchTargets::new(reachable_iter, otherwise);
+
+    let num_targets = new_targets.all_targets().len();
+    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 }
         }
-    }
-}
-
-fn remove_successors<'tcx, F>(
-    terminator_kind: &TerminatorKind<'tcx>,
-    is_unreachable: F,
-) -> Option<TerminatorKind<'tcx>>
-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;
-                }
-
-                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;
-            }
+        _ if num_targets == targets.all_targets().len() => {
+            // Nothing has changed.
+            return false;
         }
-        _ => return None,
+        _ => 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 018b6c1ee95..2a36ad9230e 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,7 @@
 +         debug self => _2;
 +         let mut _3: &std::option::Option<T>;
 +         let mut _4: isize;
++         let mut _5: bool;
 +         scope 2 {
 +             debug val => _0;
 +         }
@@ -29,18 +30,17 @@
           StorageLive(_2);
           _2 = move _1;
 -         _0 = Option::<T>::unwrap_unchecked(move _2) -> [return: bb1, unwind unreachable];
+-     }
+- 
+-     bb1: {
 +         StorageLive(_3);
 +         StorageLive(_4);
++         StorageLive(_5);
 +         _4 = discriminant(_2);
-+         switchInt(move _4) -> [1: bb2, otherwise: bb1];
-      }
-  
-      bb1: {
-+         unreachable;
-+     }
-+ 
-+     bb2: {
++         _5 = Eq(_4, const 1_isize);
++         assume(move _5);
 +         _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 47845758a3f..14c8c671d3f 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,7 @@
 +         debug self => _2;
 +         let mut _3: &std::option::Option<T>;
 +         let mut _4: isize;
++         let mut _5: bool;
 +         scope 2 {
 +             debug val => _0;
 +         }
@@ -29,26 +30,25 @@
           StorageLive(_2);
           _2 = move _1;
 -         _0 = Option::<T>::unwrap_unchecked(move _2) -> [return: bb1, unwind: bb2];
+-     }
+- 
+-     bb1: {
 +         StorageLive(_3);
 +         StorageLive(_4);
++         StorageLive(_5);
 +         _4 = discriminant(_2);
-+         switchInt(move _4) -> [1: bb2, otherwise: bb1];
-      }
-  
-      bb1: {
--         StorageDead(_2);
--         return;
-+         unreachable;
-      }
-  
--     bb2 (cleanup): {
--         resume;
-+     bb2: {
++         _5 = Eq(_4, const 1_isize);
++         assume(move _5);
 +         _0 = move ((_2 as Some).0: T);
++         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 392f085bd4d..aeb93bd334f 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,8 @@ fn unwrap_unchecked(_1: Option<T>) -> T {
     scope 1 (inlined #[track_caller] Option::<T>::unwrap_unchecked) {
         debug self => _1;
         let mut _2: isize;
-        let mut _3: &std::option::Option<T>;
+        let mut _3: bool;
+        let mut _4: &std::option::Option<T>;
         scope 2 {
             debug val => _0;
         }
@@ -19,25 +20,21 @@ fn unwrap_unchecked(_1: Option<T>) -> T {
             }
         }
         scope 4 (inlined Option::<T>::is_some) {
-            debug self => _3;
+            debug self => _4;
         }
     }
 
     bb0: {
-        StorageLive(_3);
+        StorageLive(_4);
         StorageLive(_2);
+        StorageLive(_3);
         _2 = discriminant(_1);
-        switchInt(move _2) -> [1: bb1, otherwise: bb2];
-    }
-
-    bb1: {
+        _3 = Eq(_2, const 1_isize);
+        assume(move _3);
         _0 = move ((_1 as Some).0: T);
-        StorageDead(_2);
         StorageDead(_3);
+        StorageDead(_2);
+        StorageDead(_4);
         return;
     }
-
-    bb2: {
-        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 392f085bd4d..aeb93bd334f 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,8 @@ fn unwrap_unchecked(_1: Option<T>) -> T {
     scope 1 (inlined #[track_caller] Option::<T>::unwrap_unchecked) {
         debug self => _1;
         let mut _2: isize;
-        let mut _3: &std::option::Option<T>;
+        let mut _3: bool;
+        let mut _4: &std::option::Option<T>;
         scope 2 {
             debug val => _0;
         }
@@ -19,25 +20,21 @@ fn unwrap_unchecked(_1: Option<T>) -> T {
             }
         }
         scope 4 (inlined Option::<T>::is_some) {
-            debug self => _3;
+            debug self => _4;
         }
     }
 
     bb0: {
-        StorageLive(_3);
+        StorageLive(_4);
         StorageLive(_2);
+        StorageLive(_3);
         _2 = discriminant(_1);
-        switchInt(move _2) -> [1: bb1, otherwise: bb2];
-    }
-
-    bb1: {
+        _3 = Eq(_2, const 1_isize);
+        assume(move _3);
         _0 = move ((_1 as Some).0: T);
-        StorageDead(_2);
         StorageDead(_3);
+        StorageDead(_2);
+        StorageDead(_4);
         return;
     }
-
-    bb2: {
-        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 65d71199aae..0114309dbb5 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,6 +4,7 @@ fn ub_if_b(_1: Thing) -> Thing {
     debug t => _1;
     let mut _0: Thing;
     let mut _2: isize;
+    let mut _3: bool;
     scope 1 (inlined unreachable_unchecked) {
         scope 2 {
             scope 3 (inlined unreachable_unchecked::runtime) {
@@ -13,15 +14,9 @@ fn ub_if_b(_1: Thing) -> Thing {
 
     bb0: {
         _2 = discriminant(_1);
-        switchInt(move _2) -> [0: bb1, otherwise: bb2];
-    }
-
-    bb1: {
+        _3 = Eq(_2, const 0_isize);
+        assume(move _3);
         _0 = move _1;
         return;
     }
-
-    bb2: {
-        unreachable;
-    }
 }
diff --git a/tests/mir-opt/separate_const_switch.identity.SeparateConstSwitch.diff b/tests/mir-opt/separate_const_switch.identity.SeparateConstSwitch.diff
index 491db551a7d..fe4b33001fc 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::<Result<Infallible, i32>, i32>::Continue(move _10);
           goto -> bb1;
diff --git a/tests/mir-opt/uninhabited_enum_branching2.main.UninhabitedEnumBranching.diff b/tests/mir-opt/uninhabited_enum_branching.byref.UninhabitedEnumBranching.diff
index 12ce6505af9..7919450cdc5 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,15 +23,15 @@
       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) -> [2: bb5, 3: bb1, otherwise: bb2];
++         switchInt(move _5) -> [0: bb12, 1: bb12, 2: bb5, 3: bb1, otherwise: bb12];
       }
   
       bb1: {
@@ -71,9 +71,9 @@
           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) -> [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_enum_branching.custom_discriminant.UninhabitedEnumBranching.diff b/tests/mir-opt/uninhabited_enum_branching.custom_discriminant.UninhabitedEnumBranching.diff
new file mode 100644
index 00000000000..5e15298a78c
--- /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 474f43104bb..00000000000
--- a/tests/mir-opt/uninhabited_enum_branching.main.SimplifyCfg-after-uninhabited-enum-branching.after.mir
+++ /dev/null
@@ -1,60 +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;
-
-    bb0: {
-        StorageLive(_1);
-        StorageLive(_2);
-        _2 = Test1::C;
-        _3 = discriminant(_2);
-        switchInt(move _3) -> [2: bb1, otherwise: bb2];
-    }
-
-    bb1: {
-        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: bb4, 5: bb3, otherwise: bb2];
-    }
-
-    bb2: {
-        unreachable;
-    }
-
-    bb3: {
-        StorageLive(_9);
-        _9 = const "E";
-        _6 = &(*_9);
-        StorageDead(_9);
-        goto -> bb5;
-    }
-
-    bb4: {
-        _6 = const "D";
-        goto -> bb5;
-    }
-
-    bb5: {
-        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 96ae84eca50..60389117b16 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
index 9db95abec34..410db79802e 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) -> [2: bb1, otherwise: bb2];
++         switchInt(move _3) -> [0: bb6, 1: bb6, 2: bb1, otherwise: bb6];
       }
   
       bb1: {
@@ -50,31 +46,12 @@
       bb5: {
           StorageDead(_2);
           StorageDead(_1);
-          StorageLive(_6);
-          StorageLive(_7);
-          _7 = Test2::D;
-          _8 = discriminant(_7);
-          switchInt(move _8) -> [4: bb7, 5: bb6, otherwise: bb2];
-      }
-  
-      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;
++     }
++ 
++     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 9c0c5d18917..00000000000
--- a/tests/mir-opt/uninhabited_enum_branching2.main.SimplifyCfg-after-uninhabited-enum-branching.after.mir
+++ /dev/null
@@ -1,85 +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;
-    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));
-        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));
-        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 751f2ae01f8..00000000000
--- 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",
-    };
-}
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 498e1e20f8a..79948139f88 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;
       }
   }
   
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 00000000000..f6e594ffac7
--- /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<Empty>;
+      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 00000000000..2813d64672e
--- /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<Empty>;
+      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
index eb5a0c39b0b..61959732720 100644
--- a/tests/mir-opt/unreachable.main.UnreachablePropagation.panic-abort.diff
+++ b/tests/mir-opt/unreachable.if_let.UnreachablePropagation.panic-abort.diff
@@ -1,13 +1,14 @@
-- // 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<Empty>;
       let mut _2: isize;
       let _5: ();
       let mut _6: bool;
       let mut _7: !;
++     let mut _8: bool;
       scope 1 {
           debug _x => _3;
           let _3: Empty;
@@ -25,7 +26,9 @@
       bb1: {
           _2 = discriminant(_1);
 -         switchInt(move _2) -> [1: bb2, otherwise: bb6];
-+         switchInt(move _2) -> [1: bb2, otherwise: bb3];
++         _8 = Ne(_2, const 1_isize);
++         assume(move _8);
++         goto -> bb6;
       }
   
       bb2: {
@@ -36,29 +39,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.if_let.UnreachablePropagation.panic-unwind.diff
index 906dce9819f..476e2f55994 100644
--- a/tests/mir-opt/unreachable.main.UnreachablePropagation.panic-unwind.diff
+++ b/tests/mir-opt/unreachable.if_let.UnreachablePropagation.panic-unwind.diff
@@ -1,13 +1,14 @@
-- // 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<Empty>;
       let mut _2: isize;
       let _5: ();
       let mut _6: bool;
       let mut _7: !;
++     let mut _8: bool;
       scope 1 {
           debug _x => _3;
           let _3: Empty;
@@ -25,7 +26,9 @@
       bb1: {
           _2 = discriminant(_1);
 -         switchInt(move _2) -> [1: bb2, otherwise: bb6];
-+         switchInt(move _2) -> [1: bb2, otherwise: bb3];
++         _8 = Ne(_2, const 1_isize);
++         assume(move _8);
++         goto -> bb6;
       }
   
       bb2: {
@@ -36,29 +39,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 5c0df09b752..5b96681d9df 100644
--- a/tests/mir-opt/unreachable.rs
+++ b/tests/mir-opt/unreachable.rs
@@ -1,13 +1,31 @@
-// skip-filecheck
+// unit-test: UnreachablePropagation
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
+
 enum Empty {}
 
 fn empty() -> Option<Empty> {
     None
 }
 
-// EMIT_MIR unreachable.main.UnreachablePropagation.diff
-fn main() {
+// EMIT_MIR unreachable.if_let.UnreachablePropagation.diff
+fn if_let() {
+    // CHECK-LABEL: fn if_let(
+    // CHECK: bb0: {
+    // CHECK: {{_.*}} = empty()
+    // CHECK: bb1: {
+    // CHECK: [[ne:_.*]] = Ne({{.*}}, const 1_isize);
+    // CHECK-NEXT: assume(move [[ne]]);
+    // CHECK-NEXT: goto -> bb6;
+    // CHECK: bb2: {
+    // CHECK-NEXT: unreachable;
+    // CHECK: bb3: {
+    // CHECK-NEXT: unreachable;
+    // CHECK: bb4: {
+    // CHECK-NEXT: unreachable;
+    // CHECK: bb5: {
+    // CHECK-NEXT: unreachable;
+    // CHECK: bb6: {
+    // CHECK: return;
     if let Some(_x) = empty() {
         let mut _y;
 
@@ -20,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();
+}
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 713757ce6e0..11d7924e736 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 a0479fb9130..df6f5609fbf 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 3713bcaea16..b1df6f85262 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<Empty> {
@@ -12,6 +13,23 @@ 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: [[ne:_.*]] = Ne({{.*}}, const false);
+    // CHECK: assume(move [[ne]]);
+    // CHECK: goto -> 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 {