rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
96.78k stars 12.5k forks source link

Missing MIR optimization: Replace matches with loads if possible #88793

Open pcwalton opened 3 years ago

pcwalton commented 3 years ago

Many times, people write matches on enums that are really loads from a table. It'd be nice if we could codegen them as such.

Here's an example, at https://godbolt.org/z/4P9v61an7:

pub enum Char {
    A,
    B,
    C,
    D,
    E,
    F,
    G,
    H,
    I,
    J,
    K,
    L,
}

pub fn to_str(val: Char) -> &'static str {
    match val {
        Char::A => "A",
        Char::B => "B",
        Char::C => "C",
        Char::D => "D",
        Char::E => "E",
        Char::F => "F",
        Char::G => "G",
        Char::H => "H",
        Char::I => "I",
        Char::J => "J",
        Char::K => "K",
        Char::L => "L",
    }
}

The codegen here has a lot to be desired:

example::to_str:
        lea     rax, [rip + .L__unnamed_1]
        movzx   ecx, dil
        lea     rdx, [rip + .LJTI0_0]
        movsxd  rcx, dword ptr [rdx + 4*rcx]
        add     rcx, rdx
        jmp     rcx
.LBB0_1:
        lea     rax, [rip + .L__unnamed_2]
        mov     edx, 1
        ret
.LBB0_2:
        lea     rax, [rip + .L__unnamed_3]
        mov     edx, 1
        ret
.LBB0_3:
        lea     rax, [rip + .L__unnamed_4]
        mov     edx, 1
        ret
.LBB0_4:
        lea     rax, [rip + .L__unnamed_5]
        mov     edx, 1
        ret
.LBB0_5:
        lea     rax, [rip + .L__unnamed_6]
        mov     edx, 1
        ret
.LBB0_6:
        lea     rax, [rip + .L__unnamed_7]
        mov     edx, 1
        ret
.LBB0_7:
        lea     rax, [rip + .L__unnamed_8]
        mov     edx, 1
        ret
.LBB0_8:
        lea     rax, [rip + .L__unnamed_9]
        mov     edx, 1
        ret
.LBB0_9:
        lea     rax, [rip + .L__unnamed_10]
        mov     edx, 1
        ret
.LBB0_10:
        lea     rax, [rip + .L__unnamed_11]
        mov     edx, 1
        ret
.LBB0_11:
        lea     rax, [rip + .L__unnamed_12]
.LBB0_12:
        mov     edx, 1
        ret
.LJTI0_0:
        .long   .LBB0_12-.LJTI0_0
        .long   .LBB0_1-.LJTI0_0
        .long   .LBB0_2-.LJTI0_0
        .long   .LBB0_3-.LJTI0_0
        .long   .LBB0_4-.LJTI0_0
        .long   .LBB0_5-.LJTI0_0
        .long   .LBB0_6-.LJTI0_0
        .long   .LBB0_7-.LJTI0_0
        .long   .LBB0_8-.LJTI0_0
        .long   .LBB0_9-.LJTI0_0
        .long   .LBB0_10-.LJTI0_0
        .long   .LBB0_11-.LJTI0_0

.L__unnamed_12:
        .byte   76

.L__unnamed_11:
        .byte   75

.L__unnamed_10:
        .byte   74

.L__unnamed_9:
        .byte   73

.L__unnamed_8:
        .byte   72

.L__unnamed_7:
        .byte   71

.L__unnamed_6:
        .byte   70

.L__unnamed_5:
        .byte   69

.L__unnamed_4:
        .byte   68

.L__unnamed_3:
        .byte   67

.L__unnamed_2:
        .byte   66

.L__unnamed_1:
        .byte   65

Deriving Debug can cause poor codegen too: https://godbolt.org/z/xnexGxo8e

There's some discussion on Twitter from LLVM folks that suggests this would be best as an MIR optzn: https://twitter.com/pcwalton/status/1436036809603960835

glandium commented 3 years ago

It's worth noting that clang is doing better at equivalent C++ code. https://godbolt.org/z/zqT1z36es

pcwalton commented 3 years ago

Wonder if this is a pass ordering issue then.

pcwalton commented 3 years ago

There's LLVM code that already tries to do this. I guess it's not running on Rust for some reason? https://llvm.org/doxygen/SimplifyCFG_8cpp_source.html#l05786

pcwalton commented 3 years ago

I think I have an LLVM fix. Will post a patch upstream.

mati865 commented 2 years ago

Should this issues be relabelled as A-LLVM?