Missed `match` optimization of riscv
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 119k
- Forks
- 16.1k
- PR merge metrics
- PR metrics pending
Description
I was code golfing different implementations for a function that "inverts" (rotates by 2) an enum with discriminants 1, 2, 4, 8 (i.e. f(1) = 4), the obvious implementation being this:
pub enum Side {
Top = 1 << 0,
Right = 1 << 1,
Bottom = 1 << 2,
Left = 1 << 3,
}
pub fn opposite_match(x: Side) -> Side {
use Side::*;
match x {
Top => Bottom,
Right => Left,
Bottom => Top,
Left => Right,
}
}
I expected to see this happen: on riscv opposite_match produces the optimal code, or at least a reasonable one.
Instead, this happened: compiler generates the following asm:
opposite_match:
addi a0, a0, -1
slli a0, a0, 24
srai a0, a0, 24
lui a1, %hi(.Lswitch.table.opposite_match)
addi a1, a1, %lo(.Lswitch.table.opposite_match)
add a0, a1, a0
lbu a0, 0(a0)
example::table_lookup::T::h07c70895e308d45b:
.ascii "\004\b\004\001\004\004\004\002"
The problem is the slli (shift left logical immediate) and srai (shift right arithmetic immediate) instructions -- those together are noop, since a0 <= 7 at that point and 7 << 24 < 1.rotate_right(1).
The underlying issue is that llvm at some point replaces indexing by u8 with indexing by u32, inserting a sext in the process; sext is not getting optimized out afterwards and is later lowered to ssli+srai. LLVM issue: https://github.com/llvm/llvm-project/issues/124841.
If you write the lookup table by hand compiler generates better asm:
pub fn table_lookup(x: Side) -> Side {
static T: [Side; 8] = [
Side::Bottom, // <--
Side::Left, // <--
Side::Bottom,
Side::Top, // <--
Side::Bottom,
Side::Bottom,
Side::Bottom,
Side::Right, // <--
];
T[x as usize - 1]
}
example::table_lookup::h8d56712109e87652:
lui a1, %hi(example::table_lookup::T::h07c70895e308d45b)
addi a1, a1, %lo(example::table_lookup::T::h07c70895e308d45b)
add a0, a1, a0
lbu a0, -1(a0)
ret
example::table_lookup::T::h07c70895e308d45b:
.ascii "\004\b\004\001\004\004\004\002"
There are no shifts and the -1 is merged into the the lbu (load byte unsigned).
Meta
rustc version:
1.86.0-nightly (2025-01-27 2f348cb7ce4063fa4eb4)
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the opposite_match and table_lookup examples and the linked Godbolt reproduction on RISC-V. Investigate the reported LLVM u8 indexing and sext optimization issue, then compare generated assembly for the two functions. Done means the redundant shift pair is eliminated from the match implementation.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- compilers, performance
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100