Uh oh!
There was an error while loading. Please reload this page.
JIT: fix GC holes in large stack-target struct block ops - #130352
Conversation
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). |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
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. Fixesdotnet#130286 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
f930e6d to
2df526fCompareUh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Fixes#130286. For a stack-target struct copy/zero containing GC pointers, `LowerCopyBlockStore`/`LowerInitBlockStore` correctly dropped write barriers but, above the unroll threshold, fell back to the managed `CORINFO_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: - Copy: only take the "no write barriers" shortcut when the copy is actually unrolled (single non-interruptible region); otherwise stay on the GC-aware CpObj path. - Zero: route zeroing of any GC-pointer struct (stack or heap) to the atomic pointer-sized loop instead of the helper. The copy regression was introduced by #128542. Verified: SPMI `replay -f benchmarks.run` clean; asmdiffs limited to large GC structs now using the GC-safe path; before/after codegen confirms `MEMCPY`→`BULK_WRITEBARRIER` and `MEMZERO`→zeroing loop. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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.