Closed
Description
I tried this code:
compiler explorer
pub enum Enum {
A(u32),
B(u32),
C(u32),
}
#[no_mangle]
pub fn f1(lhs: &Enum, rhs: &Enum) -> bool {
match (lhs, rhs) {
(Enum::A(lhs), Enum::A(rhs)) => lhs == rhs,
(Enum::B(lhs), Enum::B(rhs)) => lhs == rhs,
(Enum::C(lhs), Enum::C(rhs)) => lhs == rhs,
_ => false,
}
}
#[no_mangle]
pub fn f2(lhs: &Enum, rhs: &Enum) -> bool {
if std::mem::discriminant(lhs) != std::mem::discriminant(rhs) {
return false;
}
match (lhs, rhs) {
(Enum::A(lhs), Enum::A(rhs)) => lhs == rhs,
(Enum::B(lhs), Enum::B(rhs)) => lhs == rhs,
(Enum::C(lhs), Enum::C(rhs)) => lhs == rhs,
_ => false,
}
}
#[no_mangle]
pub fn f3(lhs: &Enum, rhs: &Enum) -> bool {
if std::mem::discriminant(lhs) != std::mem::discriminant(rhs) {
return false;
}
match (lhs, rhs) {
(Enum::A(lhs), Enum::A(rhs)) => lhs == rhs,
(Enum::B(lhs), Enum::B(rhs)) => lhs == rhs,
(Enum::C(lhs), Enum::C(rhs)) => lhs == rhs,
_ => unsafe { std::hint::unreachable_unchecked() },
}
}
Code like this is often used for implementations of PartialEq
or PartialOrd
on enums, so it is important that it be optimized well.
I expected to see this happen:
The functions f1
and f2
should produce equivalent machine code to f3
.
Instead, this happened:
f1
and f2
do not produce equivalent machine code.
f1
is missing the early return for when the discriminants do not match.
f2
unnecessarily examines the discriminant of lhs
twice!
LLVM should be able to transform f1
into f2
, and f2
into f3
Meta
rustc --version --verbose
:
rustc 1.76.0-nightly (a96d57bdb 2023-12-15)
binary: rustc
commit-hash: a96d57bdb6d2bb6d233d7d5aaefc2995ab99be01
commit-date: 2023-12-15
host: x86_64-unknown-linux-gnu
release: 1.76.0-nightly
LLVM version: 17.0.6
Backtrace
<backtrace>