Skip to content

Conversation

AndyAyersMS
Copy link
Member

Conditional escape analysis is triggered by a GDV guarded call to GetEnumerator. On the fast path this call is devirtualized and inlined and exposes one or more enumerator object allocations. The more case allows for collections to do optimizations like returning a special enumerator object for an empty collection.

When there is more than one enumerator object allocation under the GDV guard, ensure that only the allocation that was analyzed for conditional escape is stack allocated; the remaining enumerators will end up on the "slow path" where enumeration happens via interface calls, and so must be heap objects.

Fixes #111922.

Conditional escape analysis is triggered by a GDV guarded call to
`GetEnumerator`. On the fast path this call is devirtualized and inlined
and exposes one or more enumerator object allocations. This allows
for collections to do special-case optimizations like returning a static
enumerator object for an empty collection.

When there is more than one enumerator object allocation under the GDV guard,
ensure that only the allocation that was analyzed for conditional escape is stack
allocated; the remaining enumerators will end up on the "slow path" where enumeration
happens via interface calls, and so must be heap objects.

Fixes dotnet#111922.
@Copilot Copilot AI review requested due to automatic review settings July 30, 2025 16:36
@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 30, 2025
Copy link
Contributor

@Copilot 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 fixes an issue in the JIT's conditional escape analysis where multiple enumerator object allocations under a GDV (Guarded Devirtualization) guard could incorrectly be stack allocated. The fix ensures that only the analyzed allocation is stack allocated while others remain heap allocated for proper interface call handling.

Key changes:

  • Enhanced CloneInfo structure to track GDV guard and definition blocks
  • Added logic to prevent stack allocation of enumerator objects on "slow paths"
  • Included regression test to validate the fix

Reviewed Changes

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

File Description
src/coreclr/jit/objectalloc.h Added fields to CloneInfo for tracking GDV guard and definition blocks
src/coreclr/jit/objectalloc.cpp Implemented logic to detect slow path allocations and prevent their stack allocation
src/tests/JIT/opt/ObjectStackAllocation/Runtime_111922v2.csproj Test project configuration for regression test
src/tests/JIT/opt/ObjectStackAllocation/Runtime_111922v2.cs Regression test reproducing the fixed issue

Copy link
Contributor

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

@AndyAyersMS
Copy link
Member Author

FYI @dotnet/jit-contrib
@amanasifkhalid PTAL

No diffs expected. The libraries tests only occasionally tier up the method that exposes this problem. The attached repro case was reliable on linux x64.

Co-authored-by: Copilot <[email protected]>
@AndyAyersMS
Copy link
Member Author

AndyAyersMS commented Jul 30, 2025

Graphically the situation is like this: BB14 is the GDV guard block; the colored boxes are the possible def values of the enumerator var. All these paths join down below at the def block (not shown).

The green allocation is the one we want to stack allocate, the yellow is an allocated array enumerator; the red is the empty static array enumerator, and the blue is the residual call to GetEnumerator for other collection types.

After CEA cloning, the green path will become the "fast" path where all the subsequent GDV checks on the enumerator succeed, and all enumerator methods are inlined.

The yellow/red/blue paths merged into the "slow" path where the enumerator methods are interface calls; along those paths all the enumerator objects must be on the heap.

This fix forces the yellow allocation to the heap.

image

@AndyAyersMS
Copy link
Member Author

Have some asserts to investigate...

the DFS tree. Such sites will not be in an enumerator GDV hammock
so can be exempted from checking.
Copy link
Contributor

@amanasifkhalid amanasifkhalid left a comment

Choose a reason for hiding this comment

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

LGTM

@AndyAyersMS AndyAyersMS merged commit 61126df into dotnet:main Jul 31, 2025
114 of 116 checks passed
@github-actions github-actions bot locked and limited conversation to collaborators Aug 31, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
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.

System.Collections.Test: Assert failure '!CREATE_CHECK_STRING(bSmallObjectHeapPtr || bLargeObjectHeapPtr)'
2 participants