Skip to content

Bounds checks not eliminated when compared to Min #116161

Open
@stephentoub

Description

@stephentoub

Repro:

internal class Program
{
    private static void Main(string[] args) { }

    private static int Get1(int i, int size, int[] items)
    {
        if ((uint)i < (uint)size && (uint)i < (uint)items.Length)
        {
            return items[i];
        }

        return -1;
    }

    private static int Get2(int i, int size, int[] items)
    {
        if ((uint)i < Math.Min((uint)size, (uint)items.Length))
        {
            return items[i];
        }

        return -1;
    }

    private static int Get3(int i, int size, int[] items)
    {
        if ((uint)i < ((uint)size < (uint)items.Length ? (uint)size : (uint)items.Length))
        {
            return items[i];
        }

        return -1;
    }
}

Get1 eliminates the bounds check, but incurs both comparisons against size and items.Length, resulting in two cmp/ja pairs:

; Method Program:Get1(int,int,int[]):int (FullOpts)
G_M000_IG01:                ;; offset=0x0000

G_M000_IG02:                ;; offset=0x0000
       cmp      ecx, edx
       jae      SHORT G_M000_IG04

G_M000_IG03:                ;; offset=0x0004
       mov      eax, dword ptr [r8+0x08]
       cmp      eax, ecx
       ja       SHORT G_M000_IG06

G_M000_IG04:                ;; offset=0x000C
       mov      eax, -1

G_M000_IG05:                ;; offset=0x0011
       ret      

G_M000_IG06:                ;; offset=0x0012
       mov      eax, ecx
       mov      eax, dword ptr [r8+4*rax+0x10]

G_M000_IG07:                ;; offset=0x0019
       ret      
; Total bytes of code: 26

Get2 and Get3 both combine the size/items.Length checks via a cmov, but incur a bounds check, resulting in a cmov/cmp/jb and cmp/jae:

; Method Program:Get3(int,int,int[]):int (FullOpts)
G_M000_IG01:                ;; offset=0x0000
       sub      rsp, 40

G_M000_IG02:                ;; offset=0x0004
       mov      r10d, dword ptr [r8+0x08]
       cmp      r10d, edx
       cmovbe   edx, r10d
       cmp      ecx, edx
       jb       SHORT G_M000_IG05

G_M000_IG03:                ;; offset=0x0013
       mov      eax, -1

G_M000_IG04:                ;; offset=0x0018
       add      rsp, 40
       ret      

G_M000_IG05:                ;; offset=0x001D
       cmp      ecx, r10d
       jae      SHORT G_M000_IG07
       mov      eax, ecx
       mov      eax, dword ptr [r8+4*rax+0x10]

G_M000_IG06:                ;; offset=0x0029
       add      rsp, 40
       ret      

G_M000_IG07:                ;; offset=0x002E
       call     CORINFO_HELP_RNGCHKFAIL
       int3     
; Total bytes of code: 52

Seems if the JIT could recognize that the bounds check could be eliminated, then Get2/Get3 could eliminate the extra cmp/jae.

Metadata

Metadata

Assignees

Labels

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

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions