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 Jan 23, 2024
1 parent 8b94152 commit 5617d16
Show file tree
Hide file tree
Showing 14 changed files with 419 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
22 changes: 16 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,29 @@ 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 {
#[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: [[OR_COND:%.*]] = icmp ult i32 [[TMP1]], 70
// CHECK-NEXT: [[TMP2:%.*]] = icmp eq i32 [[TMP0]], 153
// CHECK-NEXT: [[SPEC_SELECT:%.*]] = or i1 [[OR_COND]], [[TMP2]]
// 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,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,53 @@
- // MIR for `otherwise_t3` before UninhabitedEnumBranching
+ // MIR for `otherwise_t3` after UninhabitedEnumBranching

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

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

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,48 @@
- // MIR for `otherwise_t4` before UninhabitedEnumBranching
+ // MIR for `otherwise_t4` after UninhabitedEnumBranching

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

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

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

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

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

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

Loading

0 comments on commit 5617d16

Please sign in to comment.