Skip to content

match lowering: Separate the bool case from other integers in TestKind #121750

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 7 additions & 10 deletions compiler/rustc_mir_build/src/build/matches/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1091,21 +1091,18 @@ enum TestKind<'tcx> {
variants: BitSet<VariantIdx>,
},

/// Test what value an integer, `bool`, or `char` has.
/// Test what value an integer or `char` has.
SwitchInt {
/// The type of the value that we're testing.
switch_ty: Ty<'tcx>,
/// The (ordered) set of values that we test for.
///
/// For integers and `char`s we create a branch to each of the values in
/// `options`, as well as an "otherwise" branch for all other values, even
/// in the (rare) case that `options` is exhaustive.
///
/// For `bool` we always generate two edges, one for `true` and one for
/// `false`.
/// We create a branch to each of the values in `options`, as well as an "otherwise" branch
/// for all other values, even in the (rare) case that `options` is exhaustive.
options: FxIndexMap<Const<'tcx>, u128>,
},

/// Test what value a `bool` has.
If,

/// Test for equality with value, possibly after an unsizing coercion to
/// `ty`,
Eq {
Expand Down Expand Up @@ -1611,7 +1608,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// a test like SwitchInt, we may want to add cases based on the candidates that are
// available
match test.kind {
TestKind::SwitchInt { switch_ty: _, ref mut options } => {
TestKind::SwitchInt { ref mut options } => {
for candidate in candidates.iter() {
if !self.add_cases_to_switch(&match_place, candidate, options) {
break;
Expand Down
77 changes: 38 additions & 39 deletions compiler/rustc_mir_build/src/build/matches/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
TestKind::Switch { adt_def, variants: BitSet::new_empty(adt_def.variants().len()) }
}

TestCase::Constant { .. } if match_pair.pattern.ty.is_bool() => TestKind::If,

TestCase::Constant { .. } if is_switch_ty(match_pair.pattern.ty) => {
// For integers, we use a `SwitchInt` match, which allows
// us to handle more cases.
TestKind::SwitchInt {
switch_ty: match_pair.pattern.ty,

// these maps are empty to start; cases are
// added below in add_cases_to_switch
options: Default::default(),
Expand Down Expand Up @@ -182,31 +182,26 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
);
}

TestKind::SwitchInt { switch_ty, ref options } => {
let terminator = if *switch_ty.kind() == ty::Bool {
assert!(!options.is_empty() && options.len() <= 2);
let [first_bb, second_bb] = *target_blocks else {
bug!("`TestKind::SwitchInt` on `bool` should have two targets")
};
let (true_bb, false_bb) = match options[0] {
1 => (first_bb, second_bb),
0 => (second_bb, first_bb),
v => span_bug!(test.span, "expected boolean value but got {:?}", v),
};
TerminatorKind::if_(Operand::Copy(place), true_bb, false_bb)
} else {
// The switch may be inexhaustive so we have a catch all block
debug_assert_eq!(options.len() + 1, target_blocks.len());
let otherwise_block = *target_blocks.last().unwrap();
let switch_targets = SwitchTargets::new(
options.values().copied().zip(target_blocks),
otherwise_block,
);
TerminatorKind::SwitchInt {
discr: Operand::Copy(place),
targets: switch_targets,
}
TestKind::SwitchInt { ref options } => {
// The switch may be inexhaustive so we have a catch-all block
debug_assert_eq!(options.len() + 1, target_blocks.len());
let otherwise_block = *target_blocks.last().unwrap();
let switch_targets = SwitchTargets::new(
options.values().copied().zip(target_blocks),
otherwise_block,
);
let terminator = TerminatorKind::SwitchInt {
discr: Operand::Copy(place),
targets: switch_targets,
};
self.cfg.terminate(block, self.source_info(match_start_span), terminator);
}

TestKind::If => {
let [false_bb, true_bb] = *target_blocks else {
bug!("`TestKind::If` should have two targets")
};
let terminator = TerminatorKind::if_(Operand::Copy(place), true_bb, false_bb);
self.cfg.terminate(block, self.source_info(match_start_span), terminator);
}

Expand Down Expand Up @@ -585,14 +580,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
//
// FIXME(#29623) we could use PatKind::Range to rule
// things out here, in some cases.
(TestKind::SwitchInt { switch_ty: _, options }, TestCase::Constant { value })
(TestKind::SwitchInt { options }, TestCase::Constant { value })
if is_switch_ty(match_pair.pattern.ty) =>
{
fully_matched = true;
let index = options.get_index_of(value).unwrap();
Some(index)
}
(TestKind::SwitchInt { switch_ty: _, options }, TestCase::Range(range)) => {
(TestKind::SwitchInt { options }, TestCase::Range(range)) => {
fully_matched = false;
let not_contained =
self.values_not_contained_in_range(&*range, options).unwrap_or(false);
Expand All @@ -608,6 +603,18 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
None
}

(&TestKind::If, TestCase::Constant { value }) => {
fully_matched = true;
let value = value.try_eval_bool(self.tcx, self.param_env).unwrap_or_else(|| {
span_bug!(test.span, "expected boolean value but got {value:?}")
});
Some(value as usize)
}
(&TestKind::If, _) => {
fully_matched = false;
None
}

(
&TestKind::Len { len: test_len, op: BinOp::Eq },
&TestCase::Slice { len, variable_length },
Expand Down Expand Up @@ -755,29 +762,21 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
impl Test<'_> {
pub(super) fn targets(&self) -> usize {
match self.kind {
TestKind::Eq { .. } | TestKind::Range(_) | TestKind::Len { .. } => 2,
TestKind::Eq { .. } | TestKind::Range(_) | TestKind::Len { .. } | TestKind::If => 2,
TestKind::Switch { adt_def, .. } => {
// While the switch that we generate doesn't test for all
// variants, we have a target for each variant and the
// otherwise case, and we make sure that all of the cases not
// specified have the same block.
adt_def.variants().len() + 1
}
TestKind::SwitchInt { switch_ty, ref options, .. } => {
if switch_ty.is_bool() {
// `bool` is special cased in `perform_test` to always
// branch to two blocks.
2
} else {
options.len() + 1
}
}
TestKind::SwitchInt { ref options } => options.len() + 1,
}
}
}

fn is_switch_ty(ty: Ty<'_>) -> bool {
ty.is_integral() || ty.is_char() || ty.is_bool()
ty.is_integral() || ty.is_char()
}

fn trait_method<'tcx>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,11 @@
}

bb2: {
- switchInt((_2.0: bool)) -> [0: bb4, otherwise: bb3];
- switchInt((_2.0: bool)) -> [0: bb3, otherwise: bb4];
+ switchInt((_2.0: bool)) -> [0: bb3, otherwise: bb17];
}

bb3: {
- falseEdge -> [real: bb20, imaginary: bb4];
- }
-
- bb4: {
StorageLive(_15);
_15 = (_2.1: bool);
StorageLive(_16);
Expand All @@ -59,16 +55,19 @@
+ goto -> bb16;
}

bb4: {
- falseEdge -> [real: bb20, imaginary: bb3];
- }
-
- bb5: {
- falseEdge -> [real: bb13, imaginary: bb3];
- falseEdge -> [real: bb13, imaginary: bb4];
- }
-
- bb6: {
- falseEdge -> [real: bb8, imaginary: bb5];
- }
-
- bb7: {
+ bb4: {
_0 = const 1_i32;
- drop(_7) -> [return: bb18, unwind: bb25];
+ drop(_7) -> [return: bb15, unwind: bb22];
Expand Down Expand Up @@ -184,7 +183,7 @@
StorageDead(_12);
StorageDead(_8);
StorageDead(_6);
- falseEdge -> [real: bb2, imaginary: bb3];
- falseEdge -> [real: bb2, imaginary: bb4];
+ goto -> bb2;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,11 @@
}

bb2: {
- switchInt((_2.0: bool)) -> [0: bb4, otherwise: bb3];
- switchInt((_2.0: bool)) -> [0: bb3, otherwise: bb4];
+ switchInt((_2.0: bool)) -> [0: bb3, otherwise: bb17];
}

bb3: {
- falseEdge -> [real: bb20, imaginary: bb4];
- }
-
- bb4: {
StorageLive(_15);
_15 = (_2.1: bool);
StorageLive(_16);
Expand All @@ -59,16 +55,19 @@
+ goto -> bb16;
}

bb4: {
- falseEdge -> [real: bb20, imaginary: bb3];
- }
-
- bb5: {
- falseEdge -> [real: bb13, imaginary: bb3];
- falseEdge -> [real: bb13, imaginary: bb4];
- }
-
- bb6: {
- falseEdge -> [real: bb8, imaginary: bb5];
- }
-
- bb7: {
+ bb4: {
_0 = const 1_i32;
- drop(_7) -> [return: bb18, unwind: bb25];
+ drop(_7) -> [return: bb15, unwind: bb22];
Expand Down Expand Up @@ -184,7 +183,7 @@
StorageDead(_12);
StorageDead(_8);
StorageDead(_6);
- falseEdge -> [real: bb2, imaginary: bb3];
- falseEdge -> [real: bb2, imaginary: bb4];
+ goto -> bb2;
}

Expand Down