JIT: fix GC holes in large stack-target struct block ops#130352
Open
EgorBo wants to merge 1 commit into
Open
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
This PR updates CoreCLR JIT lowering to avoid GC-safepoint helper calls for certain large struct block operations involving GC references, preventing GC from observing partially-updated GC-reference stack (and heap) locations. It also adds a JIT regression test to exercise the affected scenarios under GCStress.
Changes:
- Adjust
LowerCopyBlockStoreso stack-target GC-pointer struct copies only drop the CpObj path when the copy is actually unrolled into a single non-interruptible region. - Adjust
LowerInitBlockStoreso zeroing GC-pointer-containing structs uses the pointer-sized zeroing loop (instead of helper-based zeroing) for both stack and heap destinations. - Add a JIT regression test (
Runtime_130286) and wire it intoRegression_ro_2.csproj.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| src/coreclr/jit/lower.cpp | Changes lowering decisions for large GC-pointer block copy/zero to avoid managed helper safepoints that can expose GC holes. |
| src/tests/JIT/Regression/Regression_ro_2.csproj | Includes the new JitBlue regression test source file in the regression suite. |
| src/tests/JIT/Regression/JitBlue/Runtime_130286/Runtime_130286.cs | New regression test intended to reproduce the GC-hole scenarios (primarily under GCStress). |
LowerCopyBlockStore/LowerInitBlockStore dropped write barriers for a stack destination containing GC pointers (correct) but then, for sizes above the unroll threshold, fell back to the managed CORINFO_HELP_MEMCPY (SpanHelpers.Memmove) / CORINFO_HELP_MEMZERO (SpanHelpers.ClearWithoutReferences) helpers. Those calls are GC-safe points, so a GC triggered mid-operation could observe torn GC pointers in the partially-written, GC-reported stack destination and corrupt the heap. Only take the "no write barriers" copy shortcut when the copy is actually unrolled into a single non-interruptible region; otherwise stay on the GC-aware CpObj path. Route zeroing of any GC-pointer struct (stack or heap) to the atomic pointer-sized zeroing loop instead of the helper. Copy regression introduced by dotnet#128542. Fixes dotnet#130286 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
f930e6d to
2df526f
Compare
jakobbotsch
approved these changes
Jul 8, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #130286.
For a stack-target struct copy/zero containing GC pointers,
LowerCopyBlockStore/LowerInitBlockStorecorrectly dropped write barriers but, above the unroll threshold, fell back to the managedCORINFO_HELP_MEMCPY(Memmove) /CORINFO_HELP_MEMZERO(ClearWithoutReferences) helpers. Those are GC-safe points, so a GC mid-operation could observe torn GC pointers in the partially-written, GC-reported stack destination and corrupt the heap.Fix:
The copy regression was introduced by #128542.
Verified: SPMI
replay -f benchmarks.runclean; asmdiffs limited to large GC structs now using the GC-safe path; before/after codegen confirmsMEMCPY→BULK_WRITEBARRIERandMEMZERO→zeroing loop.