diff options
| author | Nicholas Nethercote <n.nethercote@gmail.com> | 2022-10-20 18:59:07 +1100 |
|---|---|---|
| committer | Nicholas Nethercote <n.nethercote@gmail.com> | 2022-10-31 10:16:39 +1100 |
| commit | 003a3f8cd3ea4594e2c2bc89c05562a202868ce0 (patch) | |
| tree | eded7bafa2d9a51eff572a3510d175c3a494191b /compiler/rustc_middle/src | |
| parent | f42b6fa7cad0d221b0c5407dca70b085784b1b5e (diff) | |
| download | rust-003a3f8cd3ea4594e2c2bc89c05562a202868ce0.tar.gz rust-003a3f8cd3ea4594e2c2bc89c05562a202868ce0.zip | |
Use `br` instead of `switch` in more cases.
`codegen_switchint_terminator` already uses `br` instead of `switch`
when there is one normal target plus the `otherwise` target. But there's
another common case with two normal targets and an `otherwise` target
that points to an empty unreachable BB. This comes up a lot when
switching on the tags of enums that use niches.
The pattern looks like this:
```
bb1: ; preds = %bb6
%3 = load i8, ptr %_2, align 1, !range !9, !noundef !4
%4 = sub i8 %3, 2
%5 = icmp eq i8 %4, 0
%_6 = select i1 %5, i64 0, i64 1
switch i64 %_6, label %bb3 [
i64 0, label %bb4
i64 1, label %bb2
]
bb3: ; preds = %bb1
unreachable
```
This commit adds code to convert the `switch` to a `br`:
```
bb1: ; preds = %bb6
%3 = load i8, ptr %_2, align 1, !range !9, !noundef !4
%4 = sub i8 %3, 2
%5 = icmp eq i8 %4, 0
%_6 = select i1 %5, i64 0, i64 1
%6 = icmp eq i64 %_6, 0
br i1 %6, label %bb4, label %bb2
bb3: ; No predecessors!
unreachable
```
This has a surprisingly large effect on compile times, with reductions
of 5% on debug builds of some crates. The reduction is all due to LLVM
taking less time. Maybe LLVM is just much better at handling `br` than
`switch`.
The resulting code is still suboptimal.
- The `icmp`, `select`, `icmp` sequence is silly, converting an `i1` to an `i64`
and back to an `i1`. But with the current code structure it's hard to avoid,
and LLVM will easily clean it up, in opt builds at least.
- `bb3` is usually now truly dead code (though not always, so it can't
be removed universally).
Diffstat (limited to 'compiler/rustc_middle/src')
| -rw-r--r-- | compiler/rustc_middle/src/mir/mod.rs | 5 |
1 files changed, 5 insertions, 0 deletions
diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 79db35a764a..068daaadbda 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -1186,6 +1186,11 @@ impl<'tcx> BasicBlockData<'tcx> { pub fn visitable(&self, index: usize) -> &dyn MirVisitable<'tcx> { if index < self.statements.len() { &self.statements[index] } else { &self.terminator } } + + /// Does the block have no statements and an unreachable terminator? + pub fn is_empty_unreachable(&self) -> bool { + self.statements.is_empty() && matches!(self.terminator().kind, TerminatorKind::Unreachable) + } } impl<O> AssertKind<O> { |
