Skip to content

Commit

Permalink
Replace the default branch with an unreachable branch If it is the la…
Browse files Browse the repository at this point in the history
…st variant
  • Loading branch information
DianQK committed Mar 7, 2024
1 parent d8b7b5b commit 08ae838
Show file tree
Hide file tree
Showing 23 changed files with 997 additions and 11 deletions.
11 changes: 11 additions & 0 deletions compiler/rustc_middle/src/mir/terminator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,17 @@ impl SwitchTargets {
pub fn target_for_value(&self, value: u128) -> BasicBlock {
self.iter().find_map(|(v, t)| (v == value).then_some(t)).unwrap_or_else(|| self.otherwise())
}

/// Adds a new target to the switch. But You cannot add an already present value.
#[inline]
pub fn add_target(&mut self, value: u128, bb: BasicBlock) {
let value = Pu128(value);
if self.values.contains(&value) {
bug!("target value {:?} already present", value);
}
self.values.push(value);
self.targets.insert(self.targets.len() - 1, bb);
}
}

pub struct SwitchTargetsIter<'a> {
Expand Down
24 changes: 18 additions & 6 deletions compiler/rustc_mir_transform/src/uninhabited_enum_branching.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ impl<'tcx> MirPass<'tcx> for UninhabitedEnumBranching {
trace!("UninhabitedEnumBranching starting for {:?}", body.source);

let mut removable_switchs = Vec::new();
let mut otherwise_is_last_variant_switchs = Vec::new();

for (bb, bb_data) in body.basic_blocks.iter_enumerated() {
trace!("processing block {:?}", bb);
Expand All @@ -92,7 +93,7 @@ impl<'tcx> MirPass<'tcx> for UninhabitedEnumBranching {
tcx.param_env_reveal_all_normalized(body.source.def_id()).and(discriminant_ty),
);

let allowed_variants = if let Ok(layout) = layout {
let mut allowed_variants = if let Ok(layout) = layout {
variant_discriminants(&layout, discriminant_ty, tcx)
} else {
continue;
Expand All @@ -103,20 +104,31 @@ impl<'tcx> MirPass<'tcx> for UninhabitedEnumBranching {
let terminator = bb_data.terminator();
let TerminatorKind::SwitchInt { targets, .. } = &terminator.kind else { bug!() };

let mut reachable_count = 0;
for (index, (val, _)) in targets.iter().enumerate() {
if allowed_variants.contains(&val) {
reachable_count += 1;
} else {
if !allowed_variants.remove(&val) {
removable_switchs.push((bb, index));
}
}

if reachable_count == allowed_variants.len() {
if allowed_variants.is_empty() {
removable_switchs.push((bb, targets.iter().count()));
} else if allowed_variants.len() == 1
&& !body.basic_blocks[targets.otherwise()].is_empty_unreachable()
{
#[allow(rustc::potential_query_instability)]
let last_variant = *allowed_variants.iter().next().unwrap();
otherwise_is_last_variant_switchs.push((bb, last_variant));
}
}

for (bb, last_variant) in otherwise_is_last_variant_switchs {
let bb_data = &mut body.basic_blocks.as_mut()[bb];
let terminator = bb_data.terminator_mut();
let TerminatorKind::SwitchInt { targets, .. } = &mut terminator.kind else { bug!() };
targets.add_target(last_variant, targets.otherwise());
removable_switchs.push((bb, targets.iter().count()));
}

if removable_switchs.is_empty() {
return;
}
Expand Down
24 changes: 24 additions & 0 deletions tests/codegen/enum/uninhabited_enum_default_branch.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//@ compile-flags: -O

#![crate_type = "lib"]

#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct Int(u32);

const A: Int = Int(201);
const B: Int = Int(270);
const C: Int = Int(153);

// CHECK-LABEL: @foo(
// CHECK-SAME: [[TMP0:%.*]])
// CHECK-NEXT: start:
// CHECK-NEXT: [[TMP1:%.*]] = add i32 [[TMP0]], -201
// CHECK-NEXT: icmp ult i32 [[TMP1]], 70
// CHECK-NEXT: icmp eq i32 [[TMP0]], 153
// CHECK-NEXT: [[SPEC_SELECT:%.*]] = or i1
// CHECK-NEXT: ret i1 [[SPEC_SELECT]]
#[no_mangle]
pub fn foo(x: Int) -> bool {
(x >= A && x <= B)
|| x == C
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
StorageLive(_6);
_6 = ((*_1).4: std::option::Option<usize>);
_7 = discriminant(_6);
switchInt(move _7) -> [1: bb4, otherwise: bb6];
switchInt(move _7) -> [1: bb4, 0: bb6, otherwise: bb9];
}

bb4: {
Expand Down Expand Up @@ -135,5 +135,9 @@
StorageDead(_6);
return;
}

bb9: {
unreachable;
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
StorageLive(_6);
_6 = ((*_1).4: std::option::Option<usize>);
_7 = discriminant(_6);
switchInt(move _7) -> [1: bb4, otherwise: bb6];
switchInt(move _7) -> [1: bb4, 0: bb6, otherwise: bb9];
}

bb4: {
Expand Down Expand Up @@ -135,5 +135,9 @@
StorageDead(_6);
return;
}

bb9: {
unreachable;
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
StorageDead(_3);
StorageDead(_2);
_5 = discriminant((_1.0: std::option::Option<u8>));
switchInt(move _5) -> [1: bb1, otherwise: bb3];
switchInt(move _5) -> [1: bb1, 0: bb3, otherwise: bb5];
}

bb1: {
Expand All @@ -46,5 +46,9 @@
StorageDead(_1);
return;
}

bb5: {
unreachable;
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
StorageDead(_3);
StorageDead(_2);
_5 = discriminant((_1.0: std::option::Option<u8>));
switchInt(move _5) -> [1: bb1, otherwise: bb3];
switchInt(move _5) -> [1: bb1, 0: bb3, otherwise: bb5];
}

bb1: {
Expand All @@ -46,5 +46,9 @@
StorageDead(_1);
return;
}

bb5: {
unreachable;
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
- // MIR for `otherwise_t1` before UninhabitedEnumBranching
+ // MIR for `otherwise_t1` after UninhabitedEnumBranching

fn otherwise_t1() -> () {
let mut _0: ();
let _1: &str;
let mut _2: Test1;
let mut _3: isize;
let _4: &str;
let _5: &str;

bb0: {
StorageLive(_1);
StorageLive(_2);
_2 = Test1::C;
_3 = discriminant(_2);
- switchInt(move _3) -> [0: bb2, 1: bb3, otherwise: bb1];
+ switchInt(move _3) -> [0: bb5, 1: bb5, 2: bb1, otherwise: bb5];
}

bb1: {
StorageLive(_5);
_5 = const "C";
_1 = &(*_5);
StorageDead(_5);
goto -> bb4;
}

bb2: {
_1 = const "A(Empty)";
goto -> bb4;
}

bb3: {
StorageLive(_4);
_4 = const "B(Empty)";
_1 = &(*_4);
StorageDead(_4);
goto -> bb4;
}

bb4: {
StorageDead(_2);
StorageDead(_1);
_0 = const ();
return;
+ }
+
+ bb5: {
+ unreachable;
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
- // MIR for `otherwise_t1` before UninhabitedEnumBranching
+ // MIR for `otherwise_t1` after UninhabitedEnumBranching

fn otherwise_t1() -> () {
let mut _0: ();
let _1: &str;
let mut _2: Test1;
let mut _3: isize;
let _4: &str;
let _5: &str;

bb0: {
StorageLive(_1);
StorageLive(_2);
_2 = Test1::C;
_3 = discriminant(_2);
- switchInt(move _3) -> [0: bb2, 1: bb3, otherwise: bb1];
+ switchInt(move _3) -> [0: bb5, 1: bb5, 2: bb1, otherwise: bb5];
}

bb1: {
StorageLive(_5);
_5 = const "C";
_1 = &(*_5);
StorageDead(_5);
goto -> bb4;
}

bb2: {
_1 = const "A(Empty)";
goto -> bb4;
}

bb3: {
StorageLive(_4);
_4 = const "B(Empty)";
_1 = &(*_4);
StorageDead(_4);
goto -> bb4;
}

bb4: {
StorageDead(_2);
StorageDead(_1);
_0 = const ();
return;
+ }
+
+ bb5: {
+ unreachable;
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
- // MIR for `otherwise_t2` before UninhabitedEnumBranching
+ // MIR for `otherwise_t2` after UninhabitedEnumBranching

fn otherwise_t2() -> () {
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: bb2, otherwise: bb1];
+ switchInt(move _3) -> [4: bb2, 5: bb1, otherwise: bb4];
}

bb1: {
StorageLive(_4);
_4 = const "E";
_1 = &(*_4);
StorageDead(_4);
goto -> bb3;
}

bb2: {
_1 = const "D";
goto -> bb3;
}

bb3: {
StorageDead(_2);
StorageDead(_1);
_0 = const ();
return;
+ }
+
+ bb4: {
+ unreachable;
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
- // MIR for `otherwise_t2` before UninhabitedEnumBranching
+ // MIR for `otherwise_t2` after UninhabitedEnumBranching

fn otherwise_t2() -> () {
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: bb2, otherwise: bb1];
+ switchInt(move _3) -> [4: bb2, 5: bb1, otherwise: bb4];
}

bb1: {
StorageLive(_4);
_4 = const "E";
_1 = &(*_4);
StorageDead(_4);
goto -> bb3;
}

bb2: {
_1 = const "D";
goto -> bb3;
}

bb3: {
StorageDead(_2);
StorageDead(_1);
_0 = const ();
return;
+ }
+
+ bb4: {
+ unreachable;
}
}

Loading

0 comments on commit 08ae838

Please sign in to comment.