Skip to content

Weird Match Statement Codegen With Byte Strings #103073

@SE2Dev

Description

@SE2Dev

It seems that with the following snippet of code results in some rather strange codegen:

pub enum MyEnum {
    A,
    B,
    C,
    D,
}

impl MyEnum {
    pub fn try_from_matched(value: [u8; 4]) -> Result<Self, ()> {
        match &value {
            b"ABCD" => Ok(Self::A),
            b"EFGH" => Ok(Self::B),
            b"IJKL" => Ok(Self::C),
            b"MNOP" => Ok(Self::D),
            _ => Err(()),
        }
    }    
}

The following is produced with -C opt-level=2:

example::MyEnum::try_from_matched:
        lea     edx, [rdi - 65]
        rol     dl, 6
        cmp     dl, 3
        ja      .LBB0_1
        mov     ecx, edi
        shr     ecx, 8
        mov     eax, edi
        shr     eax, 16
        shr     edi, 24
        movzx   edx, dl
        lea     rsi, [rip + .LJTI0_0]
        movsxd  rdx, dword ptr [rsi + 4*rdx]
        add     rdx, rsi
        jmp     rdx
.LBB0_3:
        xor     cl, 66
        xor     al, 67
        or      al, cl
        xor     dil, 68
        or      dil, al
        setne   al
        shl     al, 2
        ret
.LBB0_1:
        mov     al, 4
        ret
.LBB0_4:
        xor     cl, 70
        xor     al, 71
        or      al, cl
        xor     dil, 72
        xor     ecx, ecx
        or      dil, al
        setne   cl
        lea     eax, [rcx + 2*rcx]
        add     eax, 1
        ret
.LBB0_5:
        xor     cl, 74
        xor     al, 75
        or      al, cl
        xor     dil, 76
        or      dil, al
        setne   al
        add     al, al
        add     al, 2
        ret
.LBB0_6:
        xor     cl, 78
        xor     al, 79
        or      al, cl
        xor     dil, 80
        or      dil, al
        sete    cl
        mov     al, 4
        sub     al, cl
        ret
.LJTI0_0:
        .long   .LBB0_3-.LJTI0_0
        .long   .LBB0_4-.LJTI0_0
        .long   .LBB0_5-.LJTI0_0
        .long   .LBB0_6-.LJTI0_0

I would expect something like this to be produced instead (specifically with optimizations enabled):

example::MyEnum::try_from_matched:
        cmp     edi, 1145258561
        je      .LBB0_1
        cmp     edi, 1280002633
        je      .LBB0_5
        cmp     edi, 1212630597
        jne     .LBB0_7
        mov     al, 1
        ret
.LBB0_1:
        xor     eax, eax
        ret
.LBB0_5:
        mov     al, 2
        ret
.LBB0_7:
        cmp     edi, 1347374669
        sete    cl
        mov     al, 4
        sub     al, cl
        ret

An example is available here: https://rust.godbolt.org/z/EEKr43rP8

For what it's worth, at -C opt-level=0 it becomes apparent that the if/else version uses PartialEq while the match version doesn't. I suspect that this is resulting in the optimizations for the two examples being processed differently.

Meta

rustc --version --verbose:

1.64.0 (also affects nightly and others)

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-bugCategory: This is a bug.I-slowIssue: Problems and improvements with respect to performance of generated code.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions