about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJakob Degen <jakob@degen.com>2021-12-11 21:32:08 -0500
committerJakob Degen <jakob.e.degen@gmail.com>2022-02-18 09:14:21 -0500
commit3a5c07889586564017bc0d88826a48aecf721600 (patch)
treed53038bf30cb2b5899f3d26eb8cd2dedc5369e70
parent30b3f35c420694a4f24e5a4df00f06073f4f3a37 (diff)
downloadrust-3a5c07889586564017bc0d88826a48aecf721600.tar.gz
rust-3a5c07889586564017bc0d88826a48aecf721600.zip
Extend uninhabited match branch optimization to also work on fallthrough.
The `uninhabited_enum_branch` miropt now also checks whether the fallthrough
case is inhabited, and if not will ensure that it points to an unreachable
block.
-rw-r--r--compiler/rustc_mir_transform/src/uninhabited_enum_branching.rs42
1 files changed, 39 insertions, 3 deletions
diff --git a/compiler/rustc_mir_transform/src/uninhabited_enum_branching.rs b/compiler/rustc_mir_transform/src/uninhabited_enum_branching.rs
index cda9ba9dcc8..7133724d07d 100644
--- a/compiler/rustc_mir_transform/src/uninhabited_enum_branching.rs
+++ b/compiler/rustc_mir_transform/src/uninhabited_enum_branching.rs
@@ -3,7 +3,8 @@
 use crate::MirPass;
 use rustc_data_structures::stable_set::FxHashSet;
 use rustc_middle::mir::{
-    BasicBlockData, Body, Local, Operand, Rvalue, StatementKind, SwitchTargets, TerminatorKind,
+    BasicBlockData, Body, Local, Operand, Rvalue, StatementKind, SwitchTargets, Terminator,
+    TerminatorKind,
 };
 use rustc_middle::ty::layout::TyAndLayout;
 use rustc_middle::ty::{Ty, TyCtxt};
@@ -71,6 +72,28 @@ 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
@@ -99,12 +122,25 @@ impl<'tcx> MirPass<'tcx> for UninhabitedEnumBranching {
             if let TerminatorKind::SwitchInt { targets, .. } =
                 &mut body.basic_blocks_mut()[bb].terminator_mut().kind
             {
-                let new_targets = SwitchTargets::new(
+                let mut new_targets = SwitchTargets::new(
                     targets.iter().filter(|(val, _)| allowed_variants.contains(val)),
                     targets.otherwise(),
                 );
 
-                *targets = new_targets;
+                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;
+                    }
+                }
+
+                if let TerminatorKind::SwitchInt { targets, .. } =
+                    &mut body.basic_blocks_mut()[bb].terminator_mut().kind
+                {
+                    *targets = new_targets;
+                } else {
+                    unreachable!()
+                }
             } else {
                 unreachable!()
             }