Skip to content

Comments

Allow range check cloning for BBJ_RETURN blocks#124705

Open
Copilot wants to merge 5 commits intomainfrom
copilot/optimize-array-access-range-checks
Open

Allow range check cloning for BBJ_RETURN blocks#124705
Copilot wants to merge 5 commits intomainfrom
copilot/optimize-array-access-range-checks

Conversation

Copy link
Contributor

Copilot AI commented Feb 21, 2026

Diffs (size regression from cloned blocks, but pure PerfScore improvement).

Description

optRangeCheckCloning skips the last statement of all terminator blocks via block->HasTerminator() && (stmt == block->lastStmt()). This means bounds checks in return statements are never considered for cloning:

static int ArrayAccess(int[] abcd)
{
    // Four redundant range checks emitted; only abcd[3] check is needed
    return abcd[0] + abcd[1] + abcd[2] + abcd[3];
}

Changes:

  • Scanning phase: Allow processing the last statement of BBJ_RETURN blocks, guarded by #ifndef JIT32_GCENCODER (x86 epilogue limit) and block != genReturnBB (shared return block)
  • Clone phase: When fastpathBb is BBJ_RETURN and lastStmt is its last statement, skip the tail split — both fast and fallback paths become BBJ_RETURN directly (no diamond/join block needed)
  • Test: Added ReturnBlockRangeCheckCloning covering constant-index, variable-offset, and span access patterns in return statements, plus out-of-range exception verification
Original prompt

This section details on the original issue you should resolve

<issue_title>Consecutive range checks are not combined</issue_title>
<issue_description>### Description

Consider the following code (inspired by dotnet/csharplang#4082 (comment)):

static int ArrayAccess(int[] abcd)
{
    return abcd[0] + abcd[1] + abcd[2] + abcd[3];
}

Note that it accesses the array multiple times at increasing indexes, which I think is a relatively common pattern. This results in one range check for every array access:

L0000: mov eax, [ecx+4]
L0003: test eax, eax
L0005: je short L0025
L0007: mov edx, [ecx+8]
L000a: cmp eax, 1
L000d: jbe short L0025
L000f: add edx, [ecx+0xc]
L0012: cmp eax, 2
L0015: jbe short L0025
L0017: add edx, [ecx+0x10]
L001a: cmp eax, 3
L001d: jbe short L0025
L001f: mov eax, edx
L0021: add eax, [ecx+0x14]
L0024: ret
L0025: call 0x71efa060
L002a: int3

This is annoying, because only the check for abcd[3] is necessary, since there are no side-effects between the array accesses. Even though these range checks are likely to be well predicted by the CPU, I think it would be nice if they could be elided by the JIT.

This may very well be a duplicate, but I couldn't find it. Or it may not be worth tracking, in which case, feel free to close.

Configuration

Current SharpLab: Core CLR 7.0.22.51805 on x86

Regression?

Not that I know of.
</issue_description>

<agent_instructions>Fix codegen for #79490 via optRangeCheckCloning. optRangeCheckCloning currently gives up on BBJ_RETURN blocks with checks inside it (if it has just one statement for example) via block->HasTerminator() && (stmt == block->lastStmt()) check. We need to allow it for BBJ_RETURN and in case of BBJ_RETURN we can just duplicate return blocks without the diamond shape (but don't or when JIT32_GCENCODER is defined or genReturnBB matches return see fgFoldCondToReturnBlock for example). Add some test under src\tests\JIT\opt</agent_instructions>

Comments on the Issue (you are @copilot in this section)

@ Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch See info in [area-owners.md](https://github.com/dotnet/runtime/blob/main/docs/area-owners.md) if you want to be subscribed.
Issue Details

Description

Consider the following code (inspired by dotnet/csharplang#4082 (comment)):

static int ArrayAccess(int[] abcd)
{
    return abcd[0] + abcd[1] + abcd[2] + abcd[3];
}

Note that it accesses the array multiple times at increasing indexes, which I think is a relatively common pattern. This results in one range check for every array access:

L0000: mov eax, [ecx+4]
L0003: test eax, eax
L0005: je short L0025
L0007: mov edx, [ecx+8]
L000a: cmp eax, 1
L000d: jbe short L0025
L000f: add edx, [ecx+0xc]
L0012: cmp eax, 2
L0015: jbe short L0025
L0017: add edx, [ecx+0x10]
L001a: cmp eax, 3
L001d: jbe short L0025
L001f: mov eax, edx
L0021: add eax, [ecx+0x14]
L0024: ret
L0025: call 0x71efa060
L002a: int3

This is annoying, because only the check for abcd[3] is necessary, since there are no side-effects between the array accesses. Even though these range checks are likely to be well predicted by the CPU, I think it would be nice if they could be elided by the JIT.

This may very well be a duplicate, but I couldn't find it. Or it may not be worth tracking, in which case, feel free to close.

Configuration

Current SharpLab: Core CLR 7.0.22.51805 on x86

Regression?

Not that I know of.

Author: svick
Assignees: -
Labels:

tenet-performance, area-CodeGen-coreclr, untriaged

Milestone: -
@jakobbotsch This is the same underlying issue as #12549 but this one is easier findable so let's keep both open. @MihaZupan A (possibly similar) variant of this with an extra offset: [sharplab](https://sharplab.io/#v2:EYLgxg9gTgpgtADwGwBYA0AXEBDAzgWwB8BiAOwFcAbS7YSmAAgBMBLXW+gWACgeABAMwM+AJgYAVGLgwBhGrlw8A3g1UMea4UL4BGJAx...

💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

@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 Feb 21, 2026
Copilot AI and others added 2 commits February 21, 2026 17:31
Co-authored-by: EgorBo <523221+EgorBo@users.noreply.github.com>
Co-authored-by: EgorBo <523221+EgorBo@users.noreply.github.com>
Copilot AI changed the title [WIP] Optimize consecutive range checks in array access Allow range check cloning for BBJ_RETURN blocks Feb 21, 2026
Copilot AI requested a review from EgorBo February 21, 2026 17:39
@EgorBo EgorBo marked this pull request as ready for review February 21, 2026 17:55
Copilot AI review requested due to automatic review settings February 21, 2026 17:55
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This pull request enables range check cloning optimization for BBJ_RETURN blocks, addressing issue #79490 where consecutive array/span accesses in return statements resulted in redundant bounds checks. The optimization allows the JIT to clone return blocks into fast and slow paths, eliminating redundant bounds checks in the common case.

Changes:

  • Modified scanning phase to process bounds checks in BBJ_RETURN block return statements (guarded by JIT32_GCENCODER and genReturnBB checks)
  • Updated clone phase to handle BBJ_RETURN blocks without creating a diamond/join shape - both fast and fallback paths become BBJ_RETURN directly
  • Added comprehensive test covering constant-index, variable-offset, and span access patterns with exception verification

Reviewed changes

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

File Description
src/coreclr/jit/rangecheckcloning.cpp Implements BBJ_RETURN block range check cloning with conditional splitting logic and updated edge/weight handling
src/tests/JIT/opt/RangeChecks/ReturnBlockRangeCheckCloning.cs Test file verifying range check cloning works for array/span accesses in return statements
src/tests/JIT/opt/RangeChecks/ReturnBlockRangeCheckCloning.csproj Test project configuration following established patterns

EgorBo and others added 2 commits February 21, 2026 23:38
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
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.

Consecutive range checks are not combined

2 participants