Skip to content

JIT: Elide transitive array bounds checks#130588

Open
EgorBo wants to merge 4 commits into
dotnet:mainfrom
EgorBo:egorbo/jit-elide-transitive-bounds-check
Open

JIT: Elide transitive array bounds checks#130588
EgorBo wants to merge 4 commits into
dotnet:mainfrom
EgorBo:egorbo/jit-elide-transitive-bounds-check

Conversation

@EgorBo

@EgorBo EgorBo commented Jul 12, 2026

Copy link
Copy Markdown
Member

Closes #121795

static int Test(int[] items, int size, int index)
{
    if ((uint)size <= (uint)items.Length && (uint)index < (uint)size)
    {
        return items[index];
    }
    return 0;
}
 G_M24430_IG01:
-       sub      rsp, 40
 
 G_M24430_IG06:
-       cmp      r8d, r10d
-       jae      SHORT G_M24430_IG08
        mov      eax, r8d
        mov      eax, dword ptr [rcx+4*rax+0x10]
 
-G_M24430_IG08:
-       call     CORINFO_HELP_RNGCHKFAIL
-       int3
-
-; Total bytes of code: 48
+; Total bytes of code: 23

Diffs

Copilot AI review requested due to automatic review settings July 12, 2026 21:47
@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 12, 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.

Copilot AI left a comment

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.

Pull request overview

This PR extends JIT assertion propagation + range analysis so it can chain certain unsigned relations involving checked-bound VNs (e.g., size u<= items.Length) to prove a subsequent index relation (e.g., index u< size) and thereby elide the transitive array bounds check for items[index].

Changes:

  • Teach optCreateJTrueBoundsAssertion to create/selectively admit unsigned VN-to-VN and checked-bound assertions that can participate in chaining.
  • Update RangeCheck::MergeEdgeAssertionsWorker to (a) accept LE_UN checked-bound assertions and (b) derive bounds through transitive unsigned VN relations while preserving checked-bound-shaped limits.
  • Add a JIT opt test covering the “index < size <= length” pattern and asserting no CORINFO_HELP_RNGCHKFAIL appears in the generated code.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
src/tests/JIT/opt/RangeChecks/ElidedBoundsChecks.cs Adds a new regression/opt-pattern method and invocation to validate transitive unsigned bounds-check elimination.
src/coreclr/jit/rangecheck.cpp Enables transitive reasoning across unsigned relations (with guardrails) and preserves checked-bound limits when following VN chains.
src/coreclr/jit/assertionprop.cpp Broadens assertion creation to support the unsigned checked-bound chaining needed by the rangecheck optimization, with heuristics to limit table pressure.

Comment on lines +273 to +276
// Bounds-check coalescing: 4 constant indices, same length VN.
int[] arr4 = new int[] { 10, 20, 30, 40 };
if (IndexLessThanSizeLessThanOrEqualToLength(arr4, 3, 2) != 30)
return 0;
Comment thread src/coreclr/jit/assertionprop.cpp Outdated
Comment on lines +1794 to +1796
// Restrict optional unsigned relation assertions to small methods, where they cannot crowd out
// later bounds-check assertions in an already pressured assertion table.
const bool canCreateUnsignedRelation = optMaxAssertionCount == 64;
Copilot AI review requested due to automatic review settings July 12, 2026 22:20
@EgorBo EgorBo force-pushed the egorbo/jit-elide-transitive-bounds-check branch from 8e84a81 to d67fb6b Compare July 12, 2026 22:20

Copilot AI left a comment

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.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

Comment thread src/coreclr/jit/rangecheck.cpp Outdated
Comment on lines +1572 to +1582
budget--;
Range otherRange = GetRangeFromAssertionsWorker(comp, otherVN, assertions, budget, visited);
if (!otherRange.IsConstantRange())
int nextBudget = budget;
Range otherRange = GetRangeFromAssertionsWorker(comp, otherVN, assertions, nextBudget, visited);

// Preserve checked-bound-shaped limits while following a chain such as
// "index u< size" and "size u<= array.Length".
if (canUseCheckedBounds)
{
MergeEdgeAssertionsWorker(comp, otherVN, preferredBoundVN, assertions, &otherRange, canUseCheckedBounds,
nextBudget, visited);
}
EgorBo and others added 3 commits July 14, 2026 02:58
Track bounds-check index VNs and let range analysis propagate symbolic bounds through unsigned VN comparisons. Preserve the distinction between index operands and non-negative length bounds.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b997c5bf-c48e-4c43-af7f-3062fc691e31
Restrict symbolic unsigned bound propagation to unresolved ranges and non-strict intermediate array-length bounds. Keep legacy length-only assertion consumers from treating marked index VNs as lengths.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b997c5bf-c48e-4c43-af7f-3062fc691e31
Keep length and index VN markers separate, and recognize the exact short-circuit predecessor pattern at bounds-check assertion propagation. Do not infer non-negativity from checked-bound membership.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b997c5bf-c48e-4c43-af7f-3062fc691e31
Copilot AI review requested due to automatic review settings July 14, 2026 02:13
@EgorBo EgorBo force-pushed the egorbo/jit-elide-transitive-bounds-check branch from d67fb6b to 19159f8 Compare July 14, 2026 02:13

Copilot AI left a comment

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.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

Comment thread src/coreclr/jit/assertionprop.cpp Outdated
Comment thread src/coreclr/jit/valuenum.cpp
Comment thread src/coreclr/jit/assertionprop.cpp Outdated
When an index has an unsigned upper-bound assertion against an intermediate VN, resolve that VN through assertions using the current preferred array-length bound. Keep existing unsigned-bound recognition precedence and require explicit non-negativity facts.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b997c5bf-c48e-4c43-af7f-3062fc691e31
Copilot AI review requested due to automatic review settings July 14, 2026 02:26

Copilot AI left a comment

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.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Comment on lines +1531 to +1532
if (boundRange.LowerLimit().IsConstant() && (boundRange.LowerLimit().GetConstant() >= 0) &&
boundRange.UpperLimit().IsBinOpArray() && (boundRange.UpperLimit().vn == preferredBoundVN))
Comment on lines +1519 to +1525
// Current assertion is "normalLclVN u< boundVN". Get boundVN's range using our preferred bound.
else if (canUseCheckedBounds && pRange->LowerLimit().IsUnknown() && pRange->UpperLimit().IsUnknown() &&
curAssertion.KindIs(Compiler::OAK_LT_UN) &&
(curAssertion.GetOp1().GetVN() == normalLclVN) &&
curAssertion.GetOp2().KindIs(Compiler::O2K_VN_ADD_CNS) &&
(curAssertion.GetOp2().GetVN() != preferredBoundVN) && (curAssertion.GetOp2().GetCns() == 0) &&
(budget > 0))
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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Range check not eliminated after sequential comparisons

2 participants