Skip to content

JIT: Fix CEA cloning when enumerator local is reassigned from an untracked source#127079

Merged
EgorBo merged 5 commits into
dotnet:mainfrom
EgorBo:fix-127075-cea-enumerator-reuse
Jun 2, 2026
Merged

JIT: Fix CEA cloning when enumerator local is reassigned from an untracked source#127079
EgorBo merged 5 commits into
dotnet:mainfrom
EgorBo:fix-127075-cea-enumerator-reuse

Conversation

@EgorBo
Copy link
Copy Markdown
Member

@EgorBo EgorBo commented Apr 17, 2026

Fixes #127075.

In the conditional escape analysis pipeline for GDV-guarded enumerator allocations, CheckForGuardedAllocationOrCopy only records stores whose data is GT_ALLOCOBJ, GT_LCL_VAR, or GT_BOX. Stores from other sources (e.g. a virtual call that did not devirtualize) were silently ignored, so a second def of a shared enumerator local was never recorded.

CheckCanClone therefore saw only a single def and proceeded to clone, reusing the same stack-allocated enumerator slot for both loops. The second loop's fast-path guard still passed (method table slot retains the first constructor's write), so it iterated using the first enumerator's _endIndex and _array, producing wrong results for [.. head, .. Tail]-style collection expressions under Tier1+PGO.

The fix records such stores as appearances so CheckCanClone detects multiple defs and bails out of unsafe cloning.

Copilot AI review requested due to automatic review settings April 17, 2026 12:17
@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 Apr 17, 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.

Copy link
Copy Markdown
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

Fixes a JIT conditional escape analysis (CEA) miscompile by ensuring “unrecognized” stores to tracked GDV-guarded enumerator locals are recorded as appearances, so cloning is rejected when the enumerator local is multiply-defined.

Changes:

  • Extend ObjectAllocator::CheckForGuardedAllocationOrCopy to record appearances for stores from previously-untracked sources into tracked enumerator locals (preventing unsafe cloning).
  • Add a JIT regression test that exercises [.. head, .. tail] collection expression spread under Tiered Compilation + PGO.

Reviewed changes

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

File Description
src/coreclr/jit/objectalloc.cpp Records unrecognized enumerator-local defs as appearances so CheckCanClone detects multiple defs and bails out.
src/tests/JIT/Regression/JitBlue/Runtime_127075/Runtime_127075.csproj Configures isolated test execution with Tiered Compilation + Tiered PGO (+ QuickJitForLoops).
src/tests/JIT/Regression/JitBlue/Runtime_127075/Runtime_127075.cs Adds regression coverage for [.. head, .. tail] spread scenario that previously produced wrong results.

Comment thread src/tests/JIT/Regression/JitBlue/Runtime_127075/Runtime_127075.cs Outdated
Comment thread src/coreclr/jit/objectalloc.cpp Outdated
Copilot AI review requested due to automatic review settings April 17, 2026 20:18
Copy link
Copy Markdown
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

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

Comment thread src/coreclr/jit/objectalloc.cpp Outdated
Comment thread src/tests/JIT/Regression/JitBlue/Runtime_127075/Runtime_127075.cs
…cognized source

In the conditional escape analysis (CEA) pipeline for GDV-guarded

enumerator allocations, CheckForGuardedAllocationOrCopy only recorded

stores whose data was GT_ALLOCOBJ, GT_LCL_VAR, or GT_BOX. Stores whose

data was something else (e.g. a non-devirtualized virtual call) were

silently ignored, so a reassignment of the enumerator local in a second

spread loop was never recorded as a def.

CheckCanClone therefore saw the local as single-def and proceeded to

clone, renaming all uses of the local in the cloned region to a new

local that held the first allocation. The second loop's body ended up

iterating over the first source array.

Record such stores as appearances so the multiple-defs check fires and

we bail out of unsafe cloning. Skip null/zero constant stores: the

inliner emits these to clear dead inlinee gc-ref temps and treating

them as real defs would pessimize unrelated methods.

Fixes dotnet#127075
Copy link
Copy Markdown
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

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

Comment thread src/coreclr/jit/objectalloc.cpp Outdated
… source

Direct I[] -> IReadOnlyCollection<I> assignment lets Tier1+PGO devirt the

second GetEnumerator() into an inlined SZGenericArrayEnumerator allocation,

so the buggy code path (where the second store is a non-devirt virtual call

into the shared enumerator local) never executes and the test passes even

against the broken JIT. Storing a collection literal directly to the

interface type forces Roslyn's <>z__ReadOnlyArray<I> wrapper, whose

GetEnumerator() remains a virtual call and exercises the fix.
@EgorBo EgorBo force-pushed the fix-127075-cea-enumerator-reuse branch from 3fc42d1 to 3901ce4 Compare June 1, 2026 19:27
Copilot AI review requested due to automatic review settings June 1, 2026 19:32
Copy link
Copy Markdown
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

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

Comment thread src/coreclr/jit/objectalloc.cpp Outdated
@EgorBo EgorBo marked this pull request as ready for review June 2, 2026 12:24
Copilot AI review requested due to automatic review settings June 2, 2026 12:24
Copy link
Copy Markdown
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

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

@EgorBo
Copy link
Copy Markdown
Member Author

EgorBo commented Jun 2, 2026

PTAL @AndyAyersMS bug in the EA for iterators, we might want to backport it. No diffs cc @dotnet/jit-contrib

PS: I checked that the added tests fails if I remove the fix on CI.

@EgorBo EgorBo requested a review from AndyAyersMS June 2, 2026 15:37
Copy link
Copy Markdown
Member

@AndyAyersMS AndyAyersMS left a comment

Choose a reason for hiding this comment

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

Thanks for fixing this.

Yes, we should backport.

@EgorBo
Copy link
Copy Markdown
Member Author

EgorBo commented Jun 2, 2026

/ba-g unrelated CSharpMissingShebangInFileBasedProgram failure

@EgorBo EgorBo merged commit b3db78f into dotnet:main Jun 2, 2026
136 of 143 checks passed
@EgorBo
Copy link
Copy Markdown
Member Author

EgorBo commented Jun 2, 2026

/backport to release/10.0

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 2, 2026

Started backporting to release/10.0 (link to workflow run)

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.

JIT Tier1 miscompiles [.. array, .. collection] — second copy loop is dead (NET 10.0.6)

3 participants