Skip to content

Eliminate bound checks for "% arr.Length" when possible#130156

Open
henriquewr wants to merge 20 commits into
dotnet:mainfrom
henriquewr:boundCheckMod
Open

Eliminate bound checks for "% arr.Length" when possible#130156
henriquewr wants to merge 20 commits into
dotnet:mainfrom
henriquewr:boundCheckMod

Conversation

@henriquewr

@henriquewr henriquewr commented Jul 3, 2026

Copy link
Copy Markdown

fixes #130155

Pattern [index % arr.Length]:

static int Test(int index, int len)
{
    if (index >= 0)
    {
        var arr = new int[len];

        return arr[index % arr.Length];
    }

    return 1234;
}

went from

; Method BoundCheckTests.Program:Test(int,int):int (FullOpts)
G_M000_IG01:                ;; offset=0x0000
       push     rbx
       sub      rsp, 32
       mov      ebx, ecx

G_M000_IG02:                ;; offset=0x0007
       test     ebx, ebx
       jge      SHORT G_M000_IG05

G_M000_IG03:                ;; offset=0x000B
       mov      eax, 0x4D2

G_M000_IG04:                ;; offset=0x0010
       add      rsp, 32
       pop      rbx
       ret      

G_M000_IG05:                ;; offset=0x0016
       movsxd   rdx, edx
       mov      rcx, 0x7FFD39B6A7D0
       call     CORINFO_HELP_NEWARR_1_VC
       mov      rcx, rax
       mov      r8d, dword ptr [rcx+0x08]
       mov      eax, ebx
       cdq      
       idiv     edx:eax, r8d
       cmp      edx, r8d
       jae      SHORT G_M000_IG07
       mov      eax, edx
       mov      eax, dword ptr [rcx+4*rax+0x10]

G_M000_IG06:                ;; offset=0x0040
       add      rsp, 32
       pop      rbx
       ret      

G_M000_IG07:                ;; offset=0x0046
       call     CORINFO_HELP_RNGCHKFAIL
       int3     
; Total bytes of code: 76

to

; Method BoundCheckTests.Program:Test(int,int):int (FullOpts)
G_M50457_IG01:  ;; offset=0x0000
       push     rbx
       sub      rsp, 32
       mov      ebx, ecx
						;; size=7 bbWeight=1 PerfScore 1.50

G_M50457_IG02:  ;; offset=0x0007
       test     ebx, ebx
       jge      SHORT G_M50457_IG05
						;; size=4 bbWeight=1 PerfScore 1.25

G_M50457_IG03:  ;; offset=0x000B
       mov      eax, 0x4D2
						;; size=5 bbWeight=0.50 PerfScore 0.12

G_M50457_IG04:  ;; offset=0x0010
       add      rsp, 32
       pop      rbx
       ret      
						;; size=6 bbWeight=0.50 PerfScore 0.88

G_M50457_IG05:  ;; offset=0x0016
       movsxd   rdx, edx
       mov      rcx, 0x7FFD2E8773C0      ; int[]
       call     CORINFO_HELP_NEWARR_1_VC
       mov      rcx, rax
       mov      r8d, dword ptr [rcx+0x08]
       mov      eax, ebx
       xor      edx, edx
       div      edx:eax, r8d
       mov      eax, edx
       mov      eax, dword ptr [rcx+4*rax+0x10]
						;; size=38 bbWeight=0.50 PerfScore 15.75

G_M50457_IG06:  ;; offset=0x003C
       add      rsp, 32
       pop      rbx
       ret      
						;; size=6 bbWeight=0.50 PerfScore 0.88
; Total bytes of code: 66

@github-actions github-actions Bot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Jul 3, 2026
@dotnet-policy-service dotnet-policy-service Bot added the community-contribution Indicates that the PR has been added by a community member label Jul 3, 2026
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch
See info in area-owners.md if you want to be subscribed.

@henriquewr henriquewr marked this pull request as draft July 3, 2026 01:18
@henriquewr henriquewr marked this pull request as ready for review July 3, 2026 02:38
Comment thread src/coreclr/jit/rangecheck.h Outdated
return Range(Limit(Limit::keConstant, 0), Limit(Limit::keConstant, rightLimit));
}

return Range(Limit(Limit::keConstant, 0), Limit(Limit::keUnknown));

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way of representing the maximum value possible? (limit is int32 in the ctor)
Im not sure how ranges above 32 bits are representable

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rangecheck is 32bit limited.

@EgorBo

EgorBo commented Jul 3, 2026

Copy link
Copy Markdown
Member

CI failures are related

@EgorBo EgorBo marked this pull request as draft July 5, 2026 13:01
}

// "CheckedBnd <relop> X"
if (!isUnsignedRelop && vnStore->IsVNCheckedBound(op1VN))

@henriquewr henriquewr Jul 10, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I know adding asserts isn't free
these asserts might be taking the place of a more useful one ((uint)index <= (uint)arr.Length and (uint)arr.Length >= (uint)index)

Is doing this really a good idea?

(Also, I don't really know how often the asserts table gets full)

@EgorBo

EgorBo commented Jul 10, 2026

Copy link
Copy Markdown
Member

TBH, I am not sure I understand what you're doing, or rather this looks like a typical AI contribution given how AI loves to solve problems b in the jit codebase by introducing new loops over entites vs integrating into existing structures.

I don't think "arr.Length % X" makes any sense in real code, "X % arrLen" does make sense, and it's supported but only for unsigned mods, it seems it should be possible to extend that logic to signed mods as well with the help of assertions, should be a very trivial change, not +300 LOC of very risky changes in an area that has a poor test coverage.

@henriquewr

henriquewr commented Jul 10, 2026

Copy link
Copy Markdown
Author

loops over entites vs integrating into existing structures.

I could be wrong, but i think you are talking about the (ugly) while loops, as far as I searched in the codebase, thats the only way of knowing if an assert is know to be applicable

I don't think "arr.Length % X" makes any sense in real code

Agree, i dont really think thats the mosty useful thing, but since signed operators already has all the assertions needed (<=, >=, >, <) I just implemented anyway

should be a very trivial change, not +300 LOC

yeah, most of the code is the ugly loop for assertions to support the not so useful length % index pattern (removing the support for this would remove around 120 loc, including the risky modifications on optCreateJTrueBoundsAssertion)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI community-contribution Indicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Eliminate bound checks for "% arr.Length" when possible

3 participants