dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
15.13k stars 4.7k forks source link

JIT doesn't do CSE for comparison on string.Length #108791

Open hez2010 opened 5 days ago

hez2010 commented 5 days ago

Description

Repro:

https://godbolt.org/z/1d9Phs177

using System;

class Program
{
    static char? Matcher(string b)
    {
        return b switch
        {
            "abcd" => 'a',
            "efgh" => 'b',
            "klmn" => 'c',
            "opqr" => 'd',
            _ => null
        };
    }
}

Codegen:

       push     rbp
       sub      rsp, 16
       lea      rbp, [rsp+0x10]
       xor      eax, eax
       mov      qword ptr [rbp-0x08], rax
       test     rdi, rdi
       je       SHORT G_M16319_IG04
       cmp      dword ptr [rdi+0x08], 4
       je       SHORT G_M16319_IG12
G_M16319_IG04:  ;; offset=0x001B
       test     rdi, rdi
       je       SHORT G_M16319_IG14
G_M16319_IG05:  ;; offset=0x0020
       cmp      dword ptr [rdi+0x08], 4
       jne      SHORT G_M16319_IG14
       mov      rax, 0x68006700660065
       cmp      qword ptr [rdi+0x0C], rax
       je       SHORT G_M16319_IG16
G_M16319_IG07:  ;; offset=0x0036
       cmp      dword ptr [rdi+0x08], 4
       jne      SHORT G_M16319_IG15
       mov      rax, 0x6E006D006C006B
       cmp      qword ptr [rdi+0x0C], rax
       je       SHORT G_M16319_IG17
G_M16319_IG09:  ;; offset=0x004C
       cmp      dword ptr [rdi+0x08], 4
       jne      SHORT G_M16319_IG11
       mov      rax, 0x7200710070006F
       cmp      qword ptr [rdi+0x0C], rax
       je       SHORT G_M16319_IG18
G_M16319_IG11:  ;; offset=0x0062
       xor      eax, eax
       mov      dword ptr [rbp-0x08], eax
       jmp      SHORT G_M16319_IG19
G_M16319_IG12:  ;; offset=0x0069
       mov      rax, 0x64006300620061
       cmp      qword ptr [rdi+0x0C], rax
       jne      SHORT G_M16319_IG05
       mov      byte  ptr [rbp-0x08], 1
       mov      word  ptr [rbp-0x06], 97
       jmp      SHORT G_M16319_IG19
G_M16319_IG14:  ;; offset=0x0085
       test     rdi, rdi
       jne      SHORT G_M16319_IG07
G_M16319_IG15:  ;; offset=0x008A
       test     rdi, rdi
       je       SHORT G_M16319_IG11
       jmp      SHORT G_M16319_IG09
G_M16319_IG16:  ;; offset=0x0091
       mov      byte  ptr [rbp-0x08], 1
       mov      word  ptr [rbp-0x06], 98
       jmp      SHORT G_M16319_IG19
G_M16319_IG17:  ;; offset=0x009D
       mov      byte  ptr [rbp-0x08], 1
       mov      word  ptr [rbp-0x06], 99
       jmp      SHORT G_M16319_IG19
G_M16319_IG18:  ;; offset=0x00A9
       mov      byte  ptr [rbp-0x08], 1
       mov      word  ptr [rbp-0x06], 100
G_M16319_IG19:  ;; offset=0x00B3
       mov      eax, dword ptr [rbp-0x08]
       add      rsp, 16
       pop      rbp
       ret      

The JIT is generating code to compare on string.Length with 4 again and again.

Configuration

.NET main (751b2c0225c3e174f7fa0507854d4a26d733fa76)

Regression?

No

dotnet-policy-service[bot] commented 5 days ago

Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch See info in area-owners.md if you want to be subscribed.

huoyaoyuan commented 5 days ago

Also #101184. You should have seen it before.

jakobbotsch commented 2 days ago

There is never any dominating compare against 4, hence the JIT cannot eliminate it. Similarly, there is never a dominating compare against null that the JIT can eliminate. This requires some more general jump threading / RBO, or some form of partial redundancy elimination to improve, I think.

The CSE does happen if you manually add a dominating null check on b so that the JIT can sort out all the redundancies.