Open
Description
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.