Pattern matching on enum creates unecessary jump table #115742
Closed
Description
opened on Sep 10, 2023
I tried this code:
Say we define an AST type for expressions, each carrying a span to give its position in the original source code:
pub type ByteSpan = (u32, u32);
pub enum Expr<'core> {
Error(ByteSpan),
Int(ByteSpan, u64),
Array(ByteSpan, &'core [Self]),
Call(ByteSpan, &'core Self, &'core [Self]),
}
pub fn span(expr: &Expr) -> ByteSpan {
match expr {
Expr::Error(span, ..)
| Expr::Int(span, ..)
| Expr::Array(span, ..)
| Expr::Call(span, ..) => *span,
}
}
I expected to see this happen:
The ByteSpan
is placed at the same offset for every variant of the enum, so the final assembly for span
should be:
example::span:
mov eax, dword ptr [rdi + 4]
mov edx, dword ptr [rdi + 8]
ret
Instead, this happened:
A jump table is generated, even though every entry in the jump table is identical (godbolt):
example::span:
mov eax, dword ptr [rdi]
lea rcx, [rip + .LJTI0_0]
movsxd rax, dword ptr [rcx + 4*rax]
add rax, rcx
jmp rax
.LBB0_1:
lea rcx, [rdi + 4]
mov eax, dword ptr [rdi + 4]
mov edx, dword ptr [rcx + 4]
ret
.LJTI0_0:
.long .LBB0_1-.LJTI0_0
.long .LBB0_1-.LJTI0_0
.long .LBB0_1-.LJTI0_0
.long .LBB0_1-.LJTI0_0
If the last 2 variants are removed, the optimal code is produced. If only the last variant is removed, the code is better, but still suboptimal:
example::span:
mov eax, dword ptr [rdi]
test rax, rax
je .LBB0_2
cmp eax, 1
.LBB0_2:
lea rcx, [rdi + 4]
mov eax, dword ptr [rdi + 4]
mov edx, dword ptr [rcx + 4]
ret
Meta
rustc --version --verbose
:
rustc 1.74.0-nightly (1e746d774 2023-09-07)
binary: rustc
commit-hash: 1e746d7741d44551e9378daf13b8797322aa0b74
commit-date: 2023-09-07
host: x86_64-unknown-linux-gnu
release: 1.74.0-nightly
LLVM version: 17.0.0
Compiler returned: 0
Activity