Skip to content

Unnecesary discriminant checks in PartialEq/PartialOrd style code on enums #119014

Closed
@Kmeakin

Description

@Kmeakin

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>

Metadata

Metadata

Assignees

Labels

A-LLVMArea: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues.I-slowIssue: Problems and improvements with respect to performance of generated code.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions