Skip to content

[WIP] Fix GC hole in JIT due to if-conversion - #133905

Merged
EgorBo merged 5 commits into
mainfrom
copilot/fix-gc-hole-issue
Sep 22, 2026
Merged

EgorBo merged 5 commits into
mainfrom
copilot/fix-gc-hole-issue

Conversation

Copilot AI commented Sep 14, 2026 •

Copy link
Copy Markdown
Contributor
  • Inspect explicit-init dependencies and complete the baseline build.
  • Remove explicit-init flags and dependent logic; add focused initialization coverage.
  • Build, run targeted tests, and check codegen impact: host/cross-target Debug JIT builds and six Linux x64 test runners passed; the normal regression wrapper fails on baseline and passes with the fix. Focused reference initialization adds 2 instructions / 6 bytes; existing large-struct zeroing codegen is unchanged. The crashing GC-stress repro was not run.
  • Complete formatting, code review, and security validation: clang-format 17 and secret scans passed; manual review feedback addressed. Full jit-format and the automated review tool were unavailable; CodeQL analysis was skipped because of database-size limits.
  • Resolves JIT: (bug) GC hole. If-conversion sinks the explicit init of an untracked GC local past a safepoint, leaving an uninitialized reported GC slot #133754

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
16 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@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 Sep 14, 2026
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

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

Copilot AI and others added 2 commits September 14, 2026 19:30
Co-authored-by: EgorBo <523221+EgorBo@users.noreply.github.com>
Co-authored-by: EgorBo <523221+EgorBo@users.noreply.github.com>
@EgorBo

EgorBo commented Sep 14, 2026

Copy link
Copy Markdown
Member

@copilot remove changes to StructWithGC_Zeroing tests, no tests needed.

Co-authored-by: EgorBo <523221+EgorBo@users.noreply.github.com>

Copilot AI commented Sep 14, 2026

Copy link
Copy Markdown
Contributor Author

@copilot remove changes to StructWithGC_Zeroing tests, no tests needed.

Reverted both test files to their pre-PR versions in 735995d. The JIT fix is unchanged.

@EgorBo

EgorBo commented Sep 15, 2026

Copy link
Copy Markdown
Member

@jakobbotsch @AndyAyersMS @dotnet/jit-contrib so this PR removes hasExplicitInit entirely (with the flag) - see PR changes with "ignore whitespaces" mode.

Diffs - not that bad for non-tests collections, but still a regression. @jakobbotsch pointed out that the current change is not correct - it assumes at the SSA liveness stage we know exactly what parts of code are no-gc areas which is not correct, so presumably there might be a scenario when we have a GC local, it's not zeroed yet and we hit an unexpected gc safe point (e.g. fully interruptible part) - it will crash. Should we take it as is given it's a correctness fix?

I prototyped a fix that does the same analysis at the codegen phase that recovers the regressions, but it was a bit too complex for this PR IMO.

@EgorBo

EgorBo commented Sep 15, 2026 •

Copy link
Copy Markdown
Member

It seems that regression happen moslty in small methods where the existing opt managed to avoid any zeroing in prolog, e.g.

nuint Test(ref int value)
{
    fixed (int* p = &value)
        return (nuint)p & 1;
}

Main:

; Method WriteBarrierBenchmarks:Test(byref):nuint (FullOpts)
G_M49291_IG01:
       push     rax
G_M49291_IG02:
       mov      bword ptr [rsp], rcx
       mov      rax, rcx
       and      rax, 1
G_M49291_IG03:
       add      rsp, 8
       ret      
; Total bytes of code: 17

PR:

; Method WriteBarrierBenchmarks:Test(byref):nuint (FullOpts)
G_M49291_IG01:
       push     rax
       xor      eax, eax
       mov      qword ptr [rsp], rax
G_M49291_IG02:
       mov      bword ptr [rsp], rcx
       mov      rax, rcx
       and      rax, 1
G_M49291_IG03:
       add      rsp, 8
       ret      
; Total bytes of code: 23

(in the real world, it's likely inlined to somewhere where we already zero locals with SIMD anyway)

@AndyAyersMS

Copy link
Copy Markdown
Member

I'm a bit worried about the potential impact of this change. Wondering if we should consider something more surgical.

Is this a regression in .NET 11?

@EgorBo

EgorBo commented Sep 15, 2026

Copy link
Copy Markdown
Member

I'm a bit worried about the potential impact of this change. Wondering if we should consider something more surgical.

Is this a regression in .NET 11?

The actual bug can be fixed easily in ifConversion by checking the flag. but the assumption about no-gc between prolog and a store in SSA phase cannot be fixed surgically I believe, it needs to be moved to a later phase to be correct. I assume it's been like that for a few releases.

@jakobbotsch

Copy link
Copy Markdown
Member

I don't think I said that it wasn't correct, just that SSA's liveness is a very early point to be trying to reason about GC safepoints. I do not know of other correctness issues but I wouldn't be surprised if others exist. We run a lot of optimizations after SSA's liveness, but not that much code motion, so that's probably how we get by.

#132326 was another issue around this.

I do think we should run this particular optimization much later, probably as part of LSRA or right before it. I also wouldn't be surprised if this optimization is quite beneficial with full dataflow instead of being limited to initial basic blocks and bailing on any BBJ_COND.
Computing fgHasCycleWithoutGCSafePoint at the same later point would also make sense to me.

@EgorBo

EgorBo commented Sep 17, 2026

Copy link
Copy Markdown
Member

@copilot revert the change and fix ifconversion instead (see #133754)

@EgorBo
EgorBo marked this pull request as ready for review September 20, 2026 13:09
Copilot AI lite review requested due to automatic review settings September 20, 2026 13:09
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 5 pipeline(s).
11 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🟡 Changes recommended

The fix is for a subtle GC correctness hole and should include a focused regression test to prevent future reintroductions.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 1 Low severity

Open (1)
What changed in this PR

This PR hardens JIT if-conversion against a GC-hole scenario caused by sinking an explicit-init store past a GC safepoint, which can leave an uninitialized (but reported) GC stack slot.

Changes:

  • Prevent IfConvertTryGetElseFromJtrueBlock from treating an explicit-init store (GTF_VAR_EXPLICIT_INIT) as a movable “else” operation.
  • Add a guard/comment explaining why explicit-init sinking is unsafe in the presence of safepoints.
File Description
src/​coreclr/​jit/​ifconversion.cpp Adds a safety check to avoid sinking explicit-init stores during if-conversion.

Comment thread src/coreclr/jit/ifconversion.cpp
@EgorBo
EgorBo enabled auto-merge (squash) September 20, 2026 13:16
@EgorBo

EgorBo commented Sep 21, 2026 •

Copy link
Copy Markdown
Member

@dotnet/jit-contrib can someone else approve too, it blocks merging with "At least 1 more approval is needed from a reviewer with write access."

@EgorBo
EgorBo merged commit 46b90ca into main Sep 22, 2026
133 of 136 checks passed
@EgorBo
EgorBo deleted the copilot/fix-gc-hole-issue branch September 22, 2026 11:52
@dotnet-milestone-bot dotnet-milestone-bot Bot added this to the 12.0-preview1 milestone Sep 23, 2026
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.

JIT: (bug) GC hole. If-conversion sinks the explicit init of an untracked GC local past a safepoint, leaving an uninitialized reported GC slot

5 participants