Uh oh!
There was an error while loading. Please reload this page.
JIT: handle adjacent no-GC regions (#115719) - #130834
Conversation
Ensure the first instruction of each non-interruptible GC range is excluded from the range. This breaks up adjacent no-GC regions, where previously only the first instruction of the first range was excluded. Fixesdotnet#115719. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
AndyAyersMS
commented
Jul 16, 2026
@EgorBo PTAL |
|
Azure Pipelines: Successfully started running 5 pipeline(s). 10 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch |
There was a problem hiding this comment.
Pull request overview
Updates CoreCLR JIT GC info encoding so that each IGF_NOGCINTERRUPT (non-interruptible / no-GC) region consistently has its first instruction treated as interruptible (except for prolog regions), even when two no-GC regions are adjacent. This improves the accuracy of interruptible range reporting used by the runtime/debugger.
Changes:
- Adjust
InterruptibleRangeReporterto considerigOffs + firstInstrSize(non-prolog) when deciding whether to emit an interruptible range. - This ensures an interruptible “gap” is emitted for the first instruction even when
igOffs == m_uninterruptibleEnd, breaking up adjacent no-GC regions.
Uh oh!
There was an error while loading. Please reload this page.
What happened in the issue? We end up defining a 0-length interruptible GC range and that does something odd? |
jakobbotsch
commented
Jul 16, 2026
/azp run runtime-coreclr gcstress0x3-gcstress0xc, runtime-coreclr gcstress-extra |
|
Azure Pipelines: Successfully started running 2 pipeline(s). |
AndyAyersMS
commented
Jul 16, 2026
We fused together adjacent no-gc regions into one big region, and the user was unable to run funceval when broken in at the start of the second one ( |
AndyAyersMS
commented
Jul 16, 2026
GC stress failures are related. |
AndyAyersMS
commented
Jul 16, 2026
We can't distinguish between a single no-gc region broken up across IGs vs multiple no-gc regions that happen to be adjacent. Looking instead at adding an explicit NOP to clearly delineate the start of a second, separate no-gc region. |
tannergooding
commented
Jul 16, 2026
Is this one worth backporting? |
AndyAyersMS
commented
Jul 16, 2026
It is fixing an impaired debugging experience. I don't know if that meets the servicing bar. The issue has been open a while now. |
tannergooding
commented
Jul 16, 2026
Personally I'd say its worth a bar check. Pretty sure I've hit this a few times and it would be nice to not have a degraded debug experience in the LTS |
AndyAyersMS
commented
Jul 16, 2026
Revising this to insert a NOP in lower instead, so that we have a clear gc safe point with appropriate debug info. |
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
AndyAyersMS
commented
Jul 16, 2026
/azp run runtime-coreclr gcstress0x3-gcstress0xc, runtime-coreclr gcstress-extra |
|
Azure Pipelines: Successfully started running 2 pipeline(s). |
AndyAyersMS
commented
Jul 17, 2026
/azp run runtime-coreclr gcstress0x3-gcstress0xc, runtime-coreclr gcstress-extra |
|
Azure Pipelines: Successfully started running 2 pipeline(s). |
AndyAyersMS
commented
Jul 18, 2026
Workflow state for the Holistic Review Orchestrator. {
"version": 5,
"last_dispatched_commit": "ec4034b1140ce1658a1cc7e6cf7941cfd93af5ab",
"last_dispatched_base_ref": "main",
"last_dispatched_base_sha": "49110af56a95223e88f9e540503c0f8a16e64e15",
"last_reviewed_commit": "ec4034b1140ce1658a1cc7e6cf7941cfd93af5ab",
"last_reviewed_base_ref": "main",
"last_reviewed_base_sha": "49110af56a95223e88f9e540503c0f8a16e64e15",
"last_recorded_worker_run_id": "29688127987",
"review_attempt_commit": "",
"review_attempt_base_ref": "",
"review_attempt_count": 0,
"max_review_attempts": 5,
"review_history_format": "holistic-review-disclosure-v1",
"review_history": [
{
"commit": "ec4034b1140ce1658a1cc7e6cf7941cfd93af5ab",
"review_id": 4730810109
}
]
} |
There was a problem hiding this comment.
Holistic Review
Motivation: Justified. Issue #115719 shows a real debugging failure where evaluation is disallowed because the debugger stops at an unsafe GC point in unoptimized code. The root cause is that adjacent no-GC regions get merged so only the very first instruction of the first region is excluded from no-GC, leaving stack-empty points between logical regions non-interruptible.
Approach: Sound. For debuggable codegen only, the change detects when a new no-GC region is being opened immediately after existing no-GC code at a stack-empty Normal IP mapping matching the current location, and inserts an interruptible INS_nop to break up the adjacent regions. It also introduces emitLastSavedIGWasNoGC tracking and renames the previously-unused emitGCDisabled() to emitLastCodeIsNoGC(), which correctly reports interruptibility of the current IG (if non-empty) or the last saved non-empty IG.
Summary: ✅ LGTM. The change is narrowly gated to compDbgCode, so codegen for optimized code is unaffected. Bookkeeping for emitLastSavedIGWasNoGC is updated consistently in emitSavIG (only for non-empty IGs), emitCreatePlaceholderIG (prolog/epilog placeholders, always no-GC), and initialized in emitBegFN. The renamed helper had no external callers, so the rename is safe. Already approved by two JIT area experts. No blocking issues found.
Detailed Findings
✅ Correctness — No-GC region bookkeeping is consistent
emitLastSavedIGWasNoGC is set from IGF_NOGCINTERRUPT only when the saved IG is non-empty (ig->igSize != 0), matching the emitCurIGsize != 0 guard in emitLastCodeIsNoGC. Placeholder IGs (prolog/epilog) are unconditionally marked no-GC, which is correct since those regions are non-interruptible. Initialization in emitBegFN prevents stale cross-method state.
✅ Scope — Debug-only, low risk
The NOP insertion is guarded by m_compiler->opts.compDbgCode, emitNoGCRequestCount == 0, a non-empty genIPmappings, and precise checks that the last mapping is a Normal, STACK_EMPTY, current-location mapping. This tightly limits the behavioral change to the intended debuggable scenario and avoids emitting spurious NOPs.
Note
This review was generated by this repository's Holistic Review agentic workflow to complement the built-in Copilot review.
Generated by Holistic Review · 62.3 AIC · ⌖ 14.7 AIC · ⊞ 10K
AndyAyersMS
commented
Jul 21, 2026
@EgorBo@jakobbotsch reworked this, take another look. GC stress in main has some known failures. |
JulieLeeMSFT
left a comment
There was a problem hiding this comment.
LGTM.
In emitDisableGC(), conditions are tight and look correct.
jakobbotsch
commented
Jul 22, 2026
I'm worried about this case here: runtime/src/coreclr/jit/codegenxarch.cpp Lines 166 to 171 in 78fd36e With this PR do we potentially end up allowing a GC in this position that has the wrong GC info described in the comment? |
AndyAyersMS
commented
Jul 22, 2026
I don't think so? The concern there is that in a That doesn't change with this PR. |
jakobbotsch
commented
Jul 23, 2026
Ah yes, I see that now |
AndyAyersMS
commented
Jul 24, 2026
/azp run runtime-coreclr gcstress0x3-gcstress0xc, runtime-coreclr gcstress-extra |
|
Azure Pipelines: Successfully started running 2 pipeline(s). |
Uh oh!
There was an error while loading. Please reload this page.
AndyAyersMS
commented
Jul 24, 2026
Merged up and GC stress is all green. |
Uh oh!
There was an error while loading. Please reload this page.
Ensure the first instruction of each non-interruptible GC range is excluded from the range. This breaks up adjacent no-GC regions, where previously only the first instruction of the first range was excluded. Fixesdotnet#115719. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Ensure the first instruction of each non-interruptible GC range is excluded from the range. This breaks up adjacent no-GC regions, where previously only the first instruction of the first range was excluded.
Fixes#115719.