Skip to content

Perf: JIT sometimes does not elide duplicate checks across inlined methods #69080

@GrabYourPitchforks

Description

@GrabYourPitchforks

If two methods which call Span<T>.IsEmpty are both inlined into the caller, JIT will not properly deduplicate the second check. See repro and codegen below.

Repro code

public class C
{
    public static void M(ReadOnlySpan<Char> s)
    {
        if (s.StartsWith('\"') && s.EndsWith('\"'))
        {
            Console.WriteLine("Quoted!");
        }
    }
}

public static class Extensions
{
    public static bool StartsWith(this ReadOnlySpan<Char> span, char c)
    {
        if (span.IsEmpty) { return false; }
        if (span[0] == c) { return true; }
        return false;
    }

    public static bool EndsWith(this ReadOnlySpan<Char> span, char c)
    {
        if (span.IsEmpty) { return false; }
        nuint localLength = (uint)span.Length;
        if (Unsafe.Add(ref MemoryMarshal.GetReference(span), (nint)localLength - 1) == c) { return true; }
        return false;
    }
}
; Method ConsoleApp7.C:M(System.ReadOnlySpan`1[Char])
G_M60782_IG01:
       sub      rsp, 40
						;; size=4 bbWeight=1    PerfScore 0.25

G_M60782_IG02:
       mov      rax, bword ptr [rcx]
       mov      rdx, rax
       mov      ecx, dword ptr [rcx+8]
       mov      r8d, ecx
       test     r8d, r8d
       je       SHORT G_M60782_IG04
						;; size=17 bbWeight=1    PerfScore 5.75

G_M60782_IG03:
       cmp      word  ptr [rdx], 34
       jne      SHORT G_M60782_IG04
       test     ecx, ecx  ; <-- ** duplicate span.IsEmpty check **
       je       SHORT G_M60782_IG04
       mov      ecx, ecx
       cmp      word  ptr [rax+2*rcx-2], 34
       jne      SHORT G_M60782_IG04
       mov      rcx, 0xD1FFAB1E      ; "Quoted!"
       mov      rcx, gword ptr [rcx]
       call     [System.Console:WriteLine(System.String)]
						;; size=39 bbWeight=0.50 PerfScore 7.38

G_M60782_IG04:
       nop      
						;; size=1 bbWeight=1    PerfScore 0.25

G_M60782_IG05:
       add      rsp, 40
       ret      
						;; size=5 bbWeight=1    PerfScore 1.25
; Total bytes of code: 66

Interestingly, if the signatures of both extension methods are changed from this to in this, the second check is correctly elided.

G_M60782_IG02:
       mov      rax, bword ptr [rcx]
       mov      ecx, dword ptr [rcx+8]
       test     ecx, ecx
       je       SHORT G_M60782_IG04
						;; size=10 bbWeight=1    PerfScore 5.25

G_M60782_IG03:
       cmp      word  ptr [rax], 34
       jne      SHORT G_M60782_IG04
       mov      ecx, ecx
       cmp      word  ptr [rax+2*rcx-2], 34
       jne      SHORT G_M60782_IG04
       mov      rcx, 0xD1FFAB1E      ; "Quoted!"
       mov      rcx, gword ptr [rcx]
       call     [System.Console:WriteLine(System.String)]

category:performance
theme:inlining
skill-level:intermediate
cost:medium
impact:small

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