Skip to content

JIT does not transform switch expressions where every arm returns a constant into lookup tables #114041

@neon-sunset

Description

@neon-sunset

Description

Given a simple method which maps one integer to another

static int MapInteger(int value)
{
    return value switch
    {
        0 => 101,
        1 => 99,
        2 => 3100,
        3 => 502,
        4 => 777,
        _ => -1
    };
}

.NET currently produces a jump table with mov immediate + jmp to epilog arms:

Program:MapInteger(int):int (FullOpts):
       push     rbp
       mov      rbp, rsp
G_M9365_IG02:  ;; offset=0x0004
       cmp      edi, 4
       ja       SHORT G_M9365_IG09
       mov      eax, edi
       lea      rcx, [reloc @RWD00]
       mov      ecx, dword ptr [rcx+4*rax]
       lea      rdx, G_M9365_IG02
       add      rcx, rdx
       jmp      rcx
       mov      eax, 777
       jmp      SHORT G_M9365_IG10
       mov      eax, 502
       jmp      SHORT G_M9365_IG10
       mov      eax, 0xC1C
       jmp      SHORT G_M9365_IG10
       mov      eax, 99
       jmp      SHORT G_M9365_IG10
       mov      eax, 101
       jmp      SHORT G_M9365_IG10
G_M9365_IG09:  ;; offset=0x0044
       mov      eax, -1
G_M9365_IG10:  ;; offset=0x0049
       pop      rbp
       ret      
RWD00  	dd	00000039h ; case G_M9365_IG08
       	dd	00000032h ; case G_M9365_IG07
       	dd	0000002Bh ; case G_M9365_IG06
       	dd	00000024h ; case G_M9365_IG05
       	dd	0000001Dh ; case G_M9365_IG04

This is a fairly common pattern which can be compiled to a plain lookup table.
For comparison, this is what Rustc/LLVM emits for Rust:

fn map_integer(value: i32) -> i32 {
    match value {
        0 => 101,
        1 => 99,
        2 => 3100,
        3 => 502,
        4 => 777,
        _ => -1
    }
}
map_integer:
        mov     eax, -1
        cmp     edi, 4
        ja      .LBB0_2
        mov     eax, edi
        lea     rcx, [rip + .Lswitch.table.map_integer]
        mov     eax, dword ptr [rcx + 4*rax]
.LBB0_2:
        ret

.Lswitch.table.map_integer:
        .long   101
        .long   99
        .long   3100
        .long   502
        .long   777

Configuration

Whatever .NET 10 commit Godbolt currently points to

Regression?

No

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMItenet-performancePerformance related issue

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions