Skip to content

[release/10.0] JIT: Fix CEA cloning when enumerator local is reassigned from an untracked source#128909

Open
github-actions[bot] wants to merge 4 commits into
release/10.0from
backport/pr-127079-to-release/10.0
Open

[release/10.0] JIT: Fix CEA cloning when enumerator local is reassigned from an untracked source#128909
github-actions[bot] wants to merge 4 commits into
release/10.0from
backport/pr-127079-to-release/10.0

Conversation

@github-actions
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot commented Jun 2, 2026

Backport of #127079 to release/10.0

/cc @EgorBo

Customer Impact

  • Customer reported
  • Found internally

Customer-reported (#127075). Silent data loss: a C# collection expression spread with two sources, [.. a, .. b], miscompiles under Tier1 + PGO. The result array has the correct length but the tail elements coming from the second source are left at their default value (null for reference types) because the second copy loop becomes dead code.

A bug in JIT's escape analysis.

Regression

  • Yes
  • No

Introduced in .NET 10

Testing

Added a standalone JIT regression test (src/tests/JIT/Regression/JitBlue/Runtime_127075) that reproduces the wrong codegen under Tier1 + PGO.

Risk

Low

EgorBo added 4 commits June 2, 2026 17:30
…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 #127075
… 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.
@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 Jun 2, 2026
@EgorBo EgorBo requested a review from AndyAyersMS June 2, 2026 17:52
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.

2 participants