Bounds checks not eliminated when compared to Min
@EgorBo is already working on this.
Since May 30, 2025.
Assessment
This issue has not been assessed yet.
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.
- Dominant language
- C#
- Stars
- 18.3k
- Forks
- 5.6k
- PR merge metrics
- PR metrics pending
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from dotnet/runtime
-
agentic-workflows untriaged
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
-
area-System.Reflection blocking-clean-ci-optional Known Build Error os-mac-os-x untriaged
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
-
area-CodeGen-coreclr untriaged
Difficulty 1/5 Under an hour Newbie friendliness 92/100
-
agentic-workflows untriaged
Difficulty 1/5 Under an hour Newbie friendliness 78/100
-
area-VM-meta-mono untriaged
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 86/100
-
:watch: Not Triaged 11.0 fundamentals/subsvc
Difficulty 2/5 1-3 hours Newbie friendliness 92/100
dotnet/AspNetCore.Docs#37699 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
SubtitleEdit/subtitleedit#15108 · 1 comment ·
-
area/docs-content Bug pulumi/docs
Difficulty 1/5 1-3 hours Newbie friendliness 94/100
-
Create parent directories only after the containment check in InstallHelper.TryExtractToDirectory Open
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
PowerShell/PSResourceGet#2056 ·