JIT: Fix GC info for fast tailcalls with contained targets#122924
Merged
jakobbotsch merged 1 commit intodotnet:mainfrom Jan 9, 2026
Merged
JIT: Fix GC info for fast tailcalls with contained targets#122924jakobbotsch merged 1 commit intodotnet:mainfrom
jakobbotsch merged 1 commit intodotnet:mainfrom
Conversation
For fast tailcalls we consume the operands as part of generating the `GT_CALL` node, yet we do not actually use values in the registers until after generating the epilog. This is incompatible with assumptions made by the code that implements the consumption side of operands. That code assumes that the registers will be used immediately and kills any GC information/local information immediately. Normally this is not a problem. We end up with a wrong GC information view in codegen, but since the emitter uses a lazy approach to register GC information, we don't end up actually reporting the wrong GC information. However, we can see a problem because of various constructs where the emitter ends up synchronizing its GC information view with the view that codegen had -- for example if a label with GC information was created. That started happening recently with dotnet#107283 when we started allowing tailcalls out of methods with GS cookie checks. Fix the situation by remarking the base/index of contained indirections as containing GC pointers if necessary. There is already corresponding logic for arguments that does the same remarking. The fix is only needed on x64/x86 since other platforms do not support contained indirections in calls and hence the target will not usually contain any GC pointers.
Contributor
|
Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch |
This was referenced Jan 6, 2026
Open
Member
Author
|
PTAL @dotnet/jit-contrib |
Contributor
There was a problem hiding this comment.
Pull request overview
This PR fixes a GC information tracking bug for fast tailcalls with contained indirect call targets on x64/x86. When registers containing GC pointers are consumed for a contained indirection but remain live into the epilog, the GC tracking information needs to be explicitly restored after consumption. The fix follows the same pattern used for argument registers in fast tailcalls.
Key Changes:
- Added GC pointer remarking logic for base/index registers of contained indirections in fast tailcall targets
- Updated validation logic to skip all non-variable pointer registers for tailcall blocks (not just argument registers)
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| src/coreclr/jit/codegenxarch.cpp | Adds logic to remark base/index registers as GC pointers after consuming a contained indirection for fast tailcall targets, ensuring GC tracking remains correct through the epilog |
| src/coreclr/jit/codegenlinear.cpp | Changes validation logic to skip all non-variable pointer registers (not just argument registers) for blocks with tailcalls, since target registers may also be live into the epilog; updates comment to reflect this broader scope |
EgorBo
reviewed
Jan 7, 2026
EgorBo
approved these changes
Jan 7, 2026
This was referenced Jan 10, 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 subscribe to this conversation on GitHub.
Already have an account?
Sign in.
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.
For fast tailcalls we consume the operands as part of generating the
GT_CALLnode, yet we do not actually use values in the registers until after generating the epilog.This is incompatible with assumptions made by the code that implements the consumption side of operands. That code assumes that the registers will be used immediately and kills any GC information/local information immediately.
Normally this is not a problem. We end up with a wrong GC information view in codegen, but since the emitter uses a lazy approach to register GC information, we don't end up actually reporting the wrong GC information. However, we can see a problem because of various constructs where the emitter ends up synchronizing its GC information view with the view that codegen had -- for example if a label with GC information was created. That started happening recently with #107283 when we started allowing tailcalls out of methods with GS cookie checks.
Fix the situation by remarking the base/index of contained indirections as containing GC pointers if necessary. There is already corresponding logic for arguments that does the same remarking.
The fix is only needed on x64/x86 since other platforms do not support contained indirections in calls and hence the target will not usually contain any GC pointers.
Fix #122544