Auto merge of #122459 - Nadrieril:sort-eq, r=oli-obk
match lowering: sort `Eq` candidates in the failure case too This is a slight tweak to MIR gen of matches. Take a match like: ```rust match (s, flag) { ("a", _) if foo() => 1, ("b", true) => 2, ("a", false) => 3, (_, true) => 4, _ => 5, } ``` If we switch on `s == "a"`, the first candidate matches, and we learn almost nothing about the second candidate. So there's a choice: 1. (what we do today) stop sorting candidates, keep the "b" case grouped with everything below. This could allow us to be clever here and test on `flag == true` next. 2. (what this PR does) sort "b" into the failure case. The "b" will be alone (fewer opportunities for picking a good test), but that means the two "a" cases require a single test. Today, we aren't clever in which tests we pick, so this is an unambiguous win. In a future where we pick tests better, idk. Grouping tests as much as possible feels like a generally good strategy. This was proposed in https://github.com/rust-lang/rust/issues/29623 (9 years ago :D)
This commit is contained in:
commit
5baf1e13f5
@ -650,12 +650,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(#29623): return `Some(1)` when the values are different.
|
||||
(TestKind::Eq { value: test_val, .. }, TestCase::Constant { value: case_val })
|
||||
if test_val == case_val =>
|
||||
{
|
||||
fully_matched = true;
|
||||
Some(TestBranch::Success)
|
||||
(TestKind::Eq { value: test_val, .. }, TestCase::Constant { value: case_val }) => {
|
||||
if test_val == case_val {
|
||||
fully_matched = true;
|
||||
Some(TestBranch::Success)
|
||||
} else {
|
||||
fully_matched = false;
|
||||
Some(TestBranch::Failure)
|
||||
}
|
||||
}
|
||||
|
||||
(
|
||||
|
@ -0,0 +1,118 @@
|
||||
// MIR for `constant_eq` after SimplifyCfg-initial
|
||||
|
||||
fn constant_eq(_1: &str, _2: bool) -> u32 {
|
||||
debug s => _1;
|
||||
debug b => _2;
|
||||
let mut _0: u32;
|
||||
let mut _3: (&str, bool);
|
||||
let mut _4: &str;
|
||||
let mut _5: bool;
|
||||
let mut _6: bool;
|
||||
let mut _7: bool;
|
||||
let mut _8: &&str;
|
||||
let mut _9: &bool;
|
||||
let mut _10: bool;
|
||||
|
||||
bb0: {
|
||||
StorageLive(_3);
|
||||
StorageLive(_4);
|
||||
_4 = _1;
|
||||
StorageLive(_5);
|
||||
_5 = _2;
|
||||
_3 = (move _4, move _5);
|
||||
StorageDead(_5);
|
||||
StorageDead(_4);
|
||||
PlaceMention(_3);
|
||||
_7 = <str as PartialEq>::eq((_3.0: &str), const "a") -> [return: bb11, unwind: bb19];
|
||||
}
|
||||
|
||||
bb1: {
|
||||
switchInt((_3.1: bool)) -> [0: bb2, otherwise: bb3];
|
||||
}
|
||||
|
||||
bb2: {
|
||||
_0 = const 5_u32;
|
||||
goto -> bb18;
|
||||
}
|
||||
|
||||
bb3: {
|
||||
falseEdge -> [real: bb17, imaginary: bb2];
|
||||
}
|
||||
|
||||
bb4: {
|
||||
falseEdge -> [real: bb12, imaginary: bb9];
|
||||
}
|
||||
|
||||
bb5: {
|
||||
switchInt((_3.1: bool)) -> [0: bb1, otherwise: bb6];
|
||||
}
|
||||
|
||||
bb6: {
|
||||
falseEdge -> [real: bb16, imaginary: bb3];
|
||||
}
|
||||
|
||||
bb7: {
|
||||
_6 = <str as PartialEq>::eq((_3.0: &str), const "b") -> [return: bb10, unwind: bb19];
|
||||
}
|
||||
|
||||
bb8: {
|
||||
switchInt((_3.1: bool)) -> [0: bb1, otherwise: bb9];
|
||||
}
|
||||
|
||||
bb9: {
|
||||
falseEdge -> [real: bb15, imaginary: bb6];
|
||||
}
|
||||
|
||||
bb10: {
|
||||
switchInt(move _6) -> [0: bb1, otherwise: bb8];
|
||||
}
|
||||
|
||||
bb11: {
|
||||
switchInt(move _7) -> [0: bb7, otherwise: bb4];
|
||||
}
|
||||
|
||||
bb12: {
|
||||
_8 = &fake (_3.0: &str);
|
||||
_9 = &fake (_3.1: bool);
|
||||
StorageLive(_10);
|
||||
_10 = const true;
|
||||
switchInt(move _10) -> [0: bb14, otherwise: bb13];
|
||||
}
|
||||
|
||||
bb13: {
|
||||
StorageDead(_10);
|
||||
FakeRead(ForMatchGuard, _8);
|
||||
FakeRead(ForMatchGuard, _9);
|
||||
_0 = const 1_u32;
|
||||
goto -> bb18;
|
||||
}
|
||||
|
||||
bb14: {
|
||||
StorageDead(_10);
|
||||
falseEdge -> [real: bb5, imaginary: bb9];
|
||||
}
|
||||
|
||||
bb15: {
|
||||
_0 = const 2_u32;
|
||||
goto -> bb18;
|
||||
}
|
||||
|
||||
bb16: {
|
||||
_0 = const 3_u32;
|
||||
goto -> bb18;
|
||||
}
|
||||
|
||||
bb17: {
|
||||
_0 = const 4_u32;
|
||||
goto -> bb18;
|
||||
}
|
||||
|
||||
bb18: {
|
||||
StorageDead(_3);
|
||||
return;
|
||||
}
|
||||
|
||||
bb19 (cleanup): {
|
||||
resume;
|
||||
}
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
// MIR for `disjoint_ranges` after SimplifyCfg-initial
|
||||
|
||||
fn disjoint_ranges(_1: i32, _2: bool) -> u32 {
|
||||
debug x => _1;
|
||||
debug b => _2;
|
||||
let mut _0: u32;
|
||||
let mut _3: bool;
|
||||
let mut _4: bool;
|
||||
let mut _5: bool;
|
||||
let mut _6: bool;
|
||||
let mut _7: &i32;
|
||||
let mut _8: bool;
|
||||
|
||||
bb0: {
|
||||
PlaceMention(_1);
|
||||
_5 = Le(const 0_i32, _1);
|
||||
switchInt(move _5) -> [0: bb3, otherwise: bb8];
|
||||
}
|
||||
|
||||
bb1: {
|
||||
_0 = const 3_u32;
|
||||
goto -> bb14;
|
||||
}
|
||||
|
||||
bb2: {
|
||||
falseEdge -> [real: bb9, imaginary: bb4];
|
||||
}
|
||||
|
||||
bb3: {
|
||||
_3 = Le(const 10_i32, _1);
|
||||
switchInt(move _3) -> [0: bb5, otherwise: bb7];
|
||||
}
|
||||
|
||||
bb4: {
|
||||
falseEdge -> [real: bb12, imaginary: bb6];
|
||||
}
|
||||
|
||||
bb5: {
|
||||
switchInt(_1) -> [4294967295: bb6, otherwise: bb1];
|
||||
}
|
||||
|
||||
bb6: {
|
||||
falseEdge -> [real: bb13, imaginary: bb1];
|
||||
}
|
||||
|
||||
bb7: {
|
||||
_4 = Le(_1, const 20_i32);
|
||||
switchInt(move _4) -> [0: bb5, otherwise: bb4];
|
||||
}
|
||||
|
||||
bb8: {
|
||||
_6 = Lt(_1, const 10_i32);
|
||||
switchInt(move _6) -> [0: bb3, otherwise: bb2];
|
||||
}
|
||||
|
||||
bb9: {
|
||||
_7 = &fake _1;
|
||||
StorageLive(_8);
|
||||
_8 = _2;
|
||||
switchInt(move _8) -> [0: bb11, otherwise: bb10];
|
||||
}
|
||||
|
||||
bb10: {
|
||||
StorageDead(_8);
|
||||
FakeRead(ForMatchGuard, _7);
|
||||
_0 = const 0_u32;
|
||||
goto -> bb14;
|
||||
}
|
||||
|
||||
bb11: {
|
||||
StorageDead(_8);
|
||||
falseEdge -> [real: bb1, imaginary: bb4];
|
||||
}
|
||||
|
||||
bb12: {
|
||||
_0 = const 1_u32;
|
||||
goto -> bb14;
|
||||
}
|
||||
|
||||
bb13: {
|
||||
_0 = const 2_u32;
|
||||
goto -> bb14;
|
||||
}
|
||||
|
||||
bb14: {
|
||||
return;
|
||||
}
|
||||
}
|
41
tests/mir-opt/building/match/sort_candidates.rs
Normal file
41
tests/mir-opt/building/match/sort_candidates.rs
Normal file
@ -0,0 +1,41 @@
|
||||
// Check specific cases of sorting candidates in match lowering.
|
||||
#![feature(exclusive_range_pattern)]
|
||||
|
||||
// EMIT_MIR sort_candidates.constant_eq.SimplifyCfg-initial.after.mir
|
||||
fn constant_eq(s: &str, b: bool) -> u32 {
|
||||
// Check that we only test "a" once
|
||||
|
||||
// CHECK-LABEL: fn constant_eq(
|
||||
// CHECK: bb0: {
|
||||
// CHECK: [[a:_.*]] = const "a";
|
||||
// CHECK-NOT: {{_.*}} = const "a";
|
||||
match (s, b) {
|
||||
("a", _) if true => 1,
|
||||
("b", true) => 2,
|
||||
("a", true) => 3,
|
||||
(_, true) => 4,
|
||||
_ => 5,
|
||||
}
|
||||
}
|
||||
|
||||
// EMIT_MIR sort_candidates.disjoint_ranges.SimplifyCfg-initial.after.mir
|
||||
fn disjoint_ranges(x: i32, b: bool) -> u32 {
|
||||
// When `(0..=10).contains(x) && !b`, we should jump to the last arm without testing the two
|
||||
// other candidates.
|
||||
|
||||
// CHECK-LABEL: fn disjoint_ranges(
|
||||
// CHECK: debug b => _2;
|
||||
// CHECK: bb0: {
|
||||
// CHECK: switchInt(_2) -> [0: [[jump:bb.*]], otherwise: {{bb.*}}];
|
||||
// CHECK: [[jump]]: {
|
||||
// CHECK-NEXT: _0 = const 3_u32;
|
||||
// CHECK-NEXT: return;
|
||||
match x {
|
||||
0..10 if b => 0,
|
||||
10..=20 => 1,
|
||||
-1 => 2,
|
||||
_ => 3,
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -1,106 +0,0 @@
|
||||
// MIR for `main` after SimplifyCfg-initial
|
||||
|
||||
fn main() -> () {
|
||||
let mut _0: ();
|
||||
let _1: i32;
|
||||
let _3: i32;
|
||||
let mut _4: bool;
|
||||
let mut _5: bool;
|
||||
let mut _6: bool;
|
||||
let mut _7: bool;
|
||||
let mut _8: &i32;
|
||||
let mut _9: bool;
|
||||
scope 1 {
|
||||
debug x => _1;
|
||||
let _2: bool;
|
||||
scope 2 {
|
||||
debug b => _2;
|
||||
}
|
||||
}
|
||||
|
||||
bb0: {
|
||||
StorageLive(_1);
|
||||
_1 = const 3_i32;
|
||||
FakeRead(ForLet(None), _1);
|
||||
StorageLive(_2);
|
||||
_2 = const true;
|
||||
FakeRead(ForLet(None), _2);
|
||||
StorageLive(_3);
|
||||
PlaceMention(_1);
|
||||
_6 = Le(const 0_i32, _1);
|
||||
switchInt(move _6) -> [0: bb3, otherwise: bb8];
|
||||
}
|
||||
|
||||
bb1: {
|
||||
_3 = const 3_i32;
|
||||
goto -> bb14;
|
||||
}
|
||||
|
||||
bb2: {
|
||||
falseEdge -> [real: bb9, imaginary: bb4];
|
||||
}
|
||||
|
||||
bb3: {
|
||||
_4 = Le(const 10_i32, _1);
|
||||
switchInt(move _4) -> [0: bb5, otherwise: bb7];
|
||||
}
|
||||
|
||||
bb4: {
|
||||
falseEdge -> [real: bb12, imaginary: bb6];
|
||||
}
|
||||
|
||||
bb5: {
|
||||
switchInt(_1) -> [4294967295: bb6, otherwise: bb1];
|
||||
}
|
||||
|
||||
bb6: {
|
||||
falseEdge -> [real: bb13, imaginary: bb1];
|
||||
}
|
||||
|
||||
bb7: {
|
||||
_5 = Le(_1, const 20_i32);
|
||||
switchInt(move _5) -> [0: bb5, otherwise: bb4];
|
||||
}
|
||||
|
||||
bb8: {
|
||||
_7 = Lt(_1, const 10_i32);
|
||||
switchInt(move _7) -> [0: bb3, otherwise: bb2];
|
||||
}
|
||||
|
||||
bb9: {
|
||||
_8 = &fake _1;
|
||||
StorageLive(_9);
|
||||
_9 = _2;
|
||||
switchInt(move _9) -> [0: bb11, otherwise: bb10];
|
||||
}
|
||||
|
||||
bb10: {
|
||||
StorageDead(_9);
|
||||
FakeRead(ForMatchGuard, _8);
|
||||
_3 = const 0_i32;
|
||||
goto -> bb14;
|
||||
}
|
||||
|
||||
bb11: {
|
||||
StorageDead(_9);
|
||||
falseEdge -> [real: bb1, imaginary: bb4];
|
||||
}
|
||||
|
||||
bb12: {
|
||||
_3 = const 1_i32;
|
||||
goto -> bb14;
|
||||
}
|
||||
|
||||
bb13: {
|
||||
_3 = const 2_i32;
|
||||
goto -> bb14;
|
||||
}
|
||||
|
||||
bb14: {
|
||||
StorageDead(_3);
|
||||
_0 = const ();
|
||||
StorageDead(_2);
|
||||
StorageDead(_1);
|
||||
return;
|
||||
}
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
// skip-filecheck
|
||||
// Make sure redundant testing paths in `match` expressions are sorted out.
|
||||
|
||||
#![feature(exclusive_range_pattern)]
|
||||
|
||||
// EMIT_MIR match_test.main.SimplifyCfg-initial.after.mir
|
||||
fn main() {
|
||||
let x = 3;
|
||||
let b = true;
|
||||
|
||||
// When `(0..=10).contains(x) && !b`, we should jump to the last arm
|
||||
// without testing two other candidates.
|
||||
match x {
|
||||
0..10 if b => 0,
|
||||
10..=20 => 1,
|
||||
-1 => 2,
|
||||
_ => 3,
|
||||
};
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user