Skip to content

RyuJIT's loop cloning optimization has questionable CQ and a bug #4922

@mikedn

Description

@mikedn

The following method

    static void Copy(int[] src, int[] dst, int length)
    {
        for (int i = 0; i < length; i++)
            dst[i] = src[i];
    }

generates

G_M64735_IG01:
       4883EC28             sub      rsp, 40
G_M64735_IG02:
       33C0                 xor      eax, eax

; check if length is > 0, ok
       4585C0               test     r8d, r8d
       0F8E82000000         jle      G_M64735_IG06

; check if src and dst are non-null
; not needed because we already know that at least one loop iteration will be executed
       4885C9               test     rcx, rcx
       410F95C1             setne    r9b
       450FB6C9             movzx    r9, r9b
       4885D2               test     rdx, rdx
       410F95C2             setne    r10b
       450FB6D2             movzx    r10, r10b
       4523CA               and      r9d, r10d
       4585C9               test     r9d, r9d
       7445                 je       SHORT G_M64735_IG05

G_M64735_IG03:
; hoisted bound checks, ok
       44394208             cmp      dword ptr [rdx+8], r8d
       410F9DC1             setge    r9b
       450FB6C9             movzx    r9, r9b
       44394108             cmp      dword ptr [rcx+8], r8d
       410F9DC2             setge    r10b
       450FB6D2             movzx    r10, r10b

; check if length is >= 0, dubious
; this doesn't seem right, unlike the rest of the code this checks r8 instead of r8d
; and the length was already checked at the start of the method
       4D85C0               test     r8, r8
       410F9DC3             setge    r11b
       450FB6DB             movzx    r11, r11b
       4523D3               and      r10d, r11d

       4523CA               and      r9d, r10d
       4585C9               test     r9d, r9d
       7417                 je       SHORT G_M64735_IG05

G_M64735_IG04:
       4C63C8               movsxd   r9d, eax
       468B548910           mov      r10d, dword ptr [rcx+4*r9+16]
       4689548A10           mov      dword ptr [rdx+4*r9+16], r10d
       FFC0                 inc      eax
       413BC0               cmp      eax, r8d
       7CEC                 jl       SHORT G_M64735_IG04
; it's unfortunate that the loop version that is normally executed 
; is the one requiring a jmp, IG04 and IG05 should be reversed
       EB20                 jmp      SHORT G_M64735_IG06

G_M64735_IG05:
       3B4108               cmp      eax, dword ptr [rcx+8]
       731F                 jae      SHORT G_M64735_IG07
       4C63C8               movsxd   r9d, eax
       468B548910           mov      r10d, dword ptr [rcx+4*r9+16]
       3B4208               cmp      eax, dword ptr [rdx+8]
       7312                 jae      SHORT G_M64735_IG07
       4689548A10           mov      dword ptr [rdx+4*r9+16], r10d
       FFC0                 inc      eax
       413BC0               cmp      eax, r8d
       7CE2                 jl       SHORT G_M64735_IG05

G_M64735_IG06:
       4883C428             add      rsp, 40
       C3                   ret

G_M64735_IG07:
       E8376AD45E           call     CORINFO_HELP_RNGCHKFAIL
       CC                   int3

@JanielS speculated in #4921 that the null checks may be there so the NullReferenceException will appears as if it was thrown from inside the loop instead of being thrown form hoisted code. But if you pass a null array to this method the debugger will point to the i < length loop condition when the exception is thrown, not to dst[i] = src[i].

Metadata

Metadata

Assignees

Labels

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

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions