Skip to content

Remove NativeAOT debug handle tagging - #129034

Merged
MichalStrehovsky merged 5 commits into
mainfrom
fix/jit-cross-target-handle-bits
Jun 15, 2026
Merged

Remove NativeAOT debug handle tagging#129034
MichalStrehovsky merged 5 commits into
mainfrom
fix/jit-cross-target-handle-bits

Conversation

@MichalStrehovsky

@MichalStrehovskyMichalStrehovsky commented Jun 5, 2026

Copy link
Copy Markdown
Member

Remove the debug-only high-bit tag from NativeAOT JIT interface handles. This avoids rejecting valid handles on targets where the tag bit is not preserved.

Also re-enables the affected regression coverage by removing the NativeAOT workaround/skip from the existing tests.

Cc @EgorBo

MichalStrehovskyand others added 2 commits June 5, 2026 14:27
Ensure RyuJIT keeps host-sized handle values when cross-targeting from 64-bit to 32-bit builds.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings June 5, 2026 05:34
@github-actionsgithub-actionsBot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Jun 5, 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.

@github-actions

This comment has been minimized.

CopilotAI 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.

Pull request overview

This PR adjusts RyuJIT’s constant/handle plumbing so that, in cross-target scenarios (e.g., 64-bit host compiling 32-bit target), handle values are treated as host-pointer-sized where required, avoiding truncation of the upper bits during constant import/propagation and preventing incorrect folding.

Changes:

  • Update static-readonly constant import to request sizeof(ssize_t) bytes for TYP_REF so gtNewGenericCon(TYP_REF, ...) reads a complete host-sized handle.
  • Tighten/extend handle constant preservation in JIT assertion propagation and constant bashing so handle VNs don’t lose upper bits on 32-bit targets.
  • Re-enable/strengthen coverage by removing test workarounds/skips that were masking the issue.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Show a summary per file
FileDescription
src/tests/Regressions/coreclr/15647/interfacestatics.ilprojRemoves TrimMode workaround so the test exercises the problematic optimization again.
src/tests/JIT/Methodical/jitinterface/bug603649.csRe-enables the test by removing the ActiveIssue skip.
src/coreclr/tools/Common/JitInterface/CorInfoImpl.csAdds a DEBUG invariant check to catch truncated/invalid handles early.
src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.csAdjusts getStaticFieldContent object-handle buffer handling for cross-target correctness.
src/coreclr/jit/importer.cppRequests host-sized bytes for TYP_REF static-readonly constants to avoid truncation.
src/coreclr/jit/compiler.hppAvoids incorrect debug-time “fits in int32” assertions for GC-typed constants.
src/coreclr/jit/assertionprop.cppPreserves host-sized handle constants/printing when VNs represent handles.

Comment threadsrc/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs Outdated
Comment threadsrc/coreclr/jit/importer.cpp Outdated
@github-actions

This comment has been minimized.

@MichalStrehovsky

Copy link
Copy Markdown
MemberAuthor

/azp run runtime-nativeaot-outerloop

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

@EgorBo

Copy link
Copy Markdown
Member

I assume CI failures are related? e.g.

2026-06-08T07:03:43.0458339Z Assertion failed.
2026-06-08T07:03:43.0465478Z (s_handleHighBitSet & (nint)handle) != 0
2026-06-08T07:03:43.0470117Z at Internal.JitInterface.CorInfoImpl.HandleToObject(Void* handle) in /_/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs:line 773
2026-06-08T07:03:43.0474499Z at Internal.JitInterface.CorInfoImpl.recordRelocation(Void* location, Void* locationRW, Void* target, CorInfoReloc fRelocType, Int32 addlD

@MichalStrehovsky

Copy link
Copy Markdown
MemberAuthor

I assume CI failures are related? e.g.

I only checked x86, so arm32 still not working is not completely surprising. Before I spend time setting up environment so that copilot can have a go at it, does the change so far make sense? I see the issue is now assigned to me... but I'm not a JIT dev, just someone who prompted GPT to make things compile with the added assert in CorInfoImpl.cs and GPT did its thing.

@EgorBo

Copy link
Copy Markdown
Member

I assume CI failures are related? e.g.

I only checked x86, so arm32 still not working is not completely surprising. Before I spend time setting up environment so that copilot can have a go at it, does the change so far make sense? I see the issue is now assigned to me... but I'm not a JIT dev, just someone who prompted GPT to make things compile with the added assert in CorInfoImpl.cs and GPT did its thing.

I can take a look tomorrow. I'm not sure I fully understand the problem so will have to look deeply. Meanwhole sent a PR to clean up one of the offender from your PR - #129144

@MichalStrehovsky

Copy link
Copy Markdown
MemberAuthor

@copilot undo all the non-test changes. Instead of those, delete the s_handleHighBitSet constant.

Co-authored-by: MichalStrehovsky <13110571+MichalStrehovsky@users.noreply.github.com>
CopilotAI review requested due to automatic review settings June 10, 2026 22:30
@MichalStrehovsky
MichalStrehovsky removed the request for review from CopilotJune 10, 2026 22:30

CopilotAI commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

@copilot undo all the non-test changes. Instead of those, delete the s_handleHighBitSet constant.

Done in 11abc8f: the prior non-test JIT/NativeAOT changes were reverted, and s_handleHighBitSet plus its tagging/masking uses were removed. The remaining net PR diff is the handle-tag removal plus the existing test updates.

CopilotAI changed the title Preserve JIT handle bits in cross-target buildsRemove NativeAOT debug handle taggingJun 10, 2026
@MichalStrehovsky

Copy link
Copy Markdown
MemberAuthor

/azp run runtime-nativeaot-outerloop

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

CopilotAI review requested due to automatic review settings June 12, 2026 02:18
@MichalStrehovsky

Copy link
Copy Markdown
MemberAuthor

/azp run runtime-nativeaot-outerloop

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

CopilotAI 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.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.

@github-actions

Copy link
Copy Markdown
Contributor

🤖 Copilot Code Review — PR #129034

Note

This review was generated by Copilot.

Holistic Assessment

Motivation: The problem is real and well-documented in #122013. The debug-only s_handleHighBitSet poison (0x4000000000000000 on 64-bit hosts) was applied to NativeAOT JIT interface handles but never validated on return. In cross-target builds (64-bit host → 32-bit target), the JIT truncates nint-sized handles to int-sized target pointers, losing the high bit. This causes handle equality mismatches that lead to incorrect constant folding and wrong codegen. Multiple maintainers (jkotas, EgorBo, SingleAccretion) explicitly agreed in #122013 that deleting the poison is the right fix.

Approach: Correct and minimal — remove the unused debug poison at the source (VM side) rather than attempting to patch every JIT truncation site. The test workarounds are properly cleaned up in the same PR.

Summary: ✅ LGTM. This is a clean, focused, maintainer-agreed bug fix. The handle scheme (handleBase = 0x420000, handleMultiplier = 8) already guarantees handles fit within 32-bit range, so removing the tag makes the (int)handle cast in HandleToObject correct for cross-target builds. No blocking issues found.


Detailed Findings

✅ Correctness — Handle tag removal is safe

The s_handleHighBitSet tag was added in PR #41126 as a debug aid for cross-bitness compilation but was never validated — no assertion ever checked that the bit survived round-tripping through the JIT. Meanwhile, the tag actively caused bugs: when the JIT truncated handles to target pointer size, the poisoned bit was lost, causing two handles for the same object to compare as unequal. Removing the tag eliminates this entire class of bug without losing any diagnostic value.

The remaining handle arithmetic in HandleToObject ((int)handle - handleBase) / handleMultiplier) and ObjectToHandleUnchecked (handleMultiplier * _handleToObject.Count + handleBase) is correct — handleBase is 0x420000 and handle values grow linearly from there, well within 32-bit range.

✅ Test changes — Workarounds correctly removed

  • bug603649.cs: The [ActiveIssue("...#122013", ...IsNativeAot, ...Is32)] skip is correctly removed since the root cause is fixed.
  • interfacestatics.ilproj: The <TrimMode>partial</TrimMode> workaround (which hid the bug by preventing the compiler from constant-folding the problematic handles) is correctly removed. The comment on RequiresProcessIsolation is updated to reflect its actual purpose (needed for CLRTestTargetUnsupported).

💡 Observation — Remaining issue #122013 reference in codebase

There are no remaining references to issue #122013 as an ActiveIssue or workaround in the codebase (the grep match in CgStress1.cs is coincidental test data). The issue can be closed once this PR merges.

✅ Scope and focus — PR is well-scoped

The PR was iterated from a larger multi-site JIT fix down to this minimal VM-side-only change based on reviewer feedback. The current diff is the cleanest possible fix: 3 files, +1/−15 lines, all directly related to the bug.

Generated by Code Review for issue #129034 · ● 3.3M ·

@MichalStrehovsky

Copy link
Copy Markdown
MemberAuthor

/ba-g the crypto crash is #129339

@MichalStrehovsky
MichalStrehovsky merged commit ca65f7d into mainJun 15, 2026
135 of 144 checks passed
@MichalStrehovsky
MichalStrehovsky deleted the fix/jit-cross-target-handle-bits branch June 15, 2026 21:14
eiriktsarpalis pushed a commit that referenced this pull request Jul 15, 2026
Remove the debug-only high-bit tag from NativeAOT JIT interface handles.
This avoids rejecting valid handles on targets where the tag bit is not
preserved.
Also re-enables the affected regression coverage by removing the
NativeAOT workaround/skip from the existing tests.
Cc @EgorBo
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: MichalStrehovsky <13110571+MichalStrehovsky@users.noreply.github.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jul 18, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-CodeGen-coreclrCLR 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.

6 participants

@MichalStrehovsky@EgorBo@jkotas@JulieLeeMSFT
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Remove NativeAOT debug handle tagging by MichalStrehovsky · Pull Request #129034 · dotnet/runtime · GitHub
Skip to content

Remove NativeAOT debug handle tagging - #129034

Merged
MichalStrehovsky merged 5 commits into
mainfrom
fix/jit-cross-target-handle-bits
Jun 15, 2026
Merged

Remove NativeAOT debug handle tagging#129034
MichalStrehovsky merged 5 commits into
mainfrom
fix/jit-cross-target-handle-bits

Conversation

@MichalStrehovsky

@MichalStrehovskyMichalStrehovsky commented Jun 5, 2026

Copy link
Copy Markdown
Member

Remove the debug-only high-bit tag from NativeAOT JIT interface handles. This avoids rejecting valid handles on targets where the tag bit is not preserved.

Also re-enables the affected regression coverage by removing the NativeAOT workaround/skip from the existing tests.

Cc @EgorBo

MichalStrehovskyand others added 2 commits June 5, 2026 14:27
Ensure RyuJIT keeps host-sized handle values when cross-targeting from 64-bit to 32-bit builds.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings June 5, 2026 05:34
@github-actionsgithub-actionsBot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Jun 5, 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.

@github-actions

This comment has been minimized.

CopilotAI 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.

Pull request overview

This PR adjusts RyuJIT’s constant/handle plumbing so that, in cross-target scenarios (e.g., 64-bit host compiling 32-bit target), handle values are treated as host-pointer-sized where required, avoiding truncation of the upper bits during constant import/propagation and preventing incorrect folding.

Changes:

  • Update static-readonly constant import to request sizeof(ssize_t) bytes for TYP_REF so gtNewGenericCon(TYP_REF, ...) reads a complete host-sized handle.
  • Tighten/extend handle constant preservation in JIT assertion propagation and constant bashing so handle VNs don’t lose upper bits on 32-bit targets.
  • Re-enable/strengthen coverage by removing test workarounds/skips that were masking the issue.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Show a summary per file
FileDescription
src/tests/Regressions/coreclr/15647/interfacestatics.ilprojRemoves TrimMode workaround so the test exercises the problematic optimization again.
src/tests/JIT/Methodical/jitinterface/bug603649.csRe-enables the test by removing the ActiveIssue skip.
src/coreclr/tools/Common/JitInterface/CorInfoImpl.csAdds a DEBUG invariant check to catch truncated/invalid handles early.
src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.csAdjusts getStaticFieldContent object-handle buffer handling for cross-target correctness.
src/coreclr/jit/importer.cppRequests host-sized bytes for TYP_REF static-readonly constants to avoid truncation.
src/coreclr/jit/compiler.hppAvoids incorrect debug-time “fits in int32” assertions for GC-typed constants.
src/coreclr/jit/assertionprop.cppPreserves host-sized handle constants/printing when VNs represent handles.

Comment threadsrc/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs Outdated
Comment threadsrc/coreclr/jit/importer.cpp Outdated
@github-actions

This comment has been minimized.

@MichalStrehovsky

Copy link
Copy Markdown
MemberAuthor

/azp run runtime-nativeaot-outerloop

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

@EgorBo

Copy link
Copy Markdown
Member

I assume CI failures are related? e.g.

2026-06-08T07:03:43.0458339Z Assertion failed.
2026-06-08T07:03:43.0465478Z (s_handleHighBitSet & (nint)handle) != 0
2026-06-08T07:03:43.0470117Z at Internal.JitInterface.CorInfoImpl.HandleToObject(Void* handle) in /_/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs:line 773
2026-06-08T07:03:43.0474499Z at Internal.JitInterface.CorInfoImpl.recordRelocation(Void* location, Void* locationRW, Void* target, CorInfoReloc fRelocType, Int32 addlD

@MichalStrehovsky

Copy link
Copy Markdown
MemberAuthor

I assume CI failures are related? e.g.

I only checked x86, so arm32 still not working is not completely surprising. Before I spend time setting up environment so that copilot can have a go at it, does the change so far make sense? I see the issue is now assigned to me... but I'm not a JIT dev, just someone who prompted GPT to make things compile with the added assert in CorInfoImpl.cs and GPT did its thing.

@EgorBo

Copy link
Copy Markdown
Member

I assume CI failures are related? e.g.

I only checked x86, so arm32 still not working is not completely surprising. Before I spend time setting up environment so that copilot can have a go at it, does the change so far make sense? I see the issue is now assigned to me... but I'm not a JIT dev, just someone who prompted GPT to make things compile with the added assert in CorInfoImpl.cs and GPT did its thing.

I can take a look tomorrow. I'm not sure I fully understand the problem so will have to look deeply. Meanwhole sent a PR to clean up one of the offender from your PR - #129144

@MichalStrehovsky

Copy link
Copy Markdown
MemberAuthor

@copilot undo all the non-test changes. Instead of those, delete the s_handleHighBitSet constant.

Co-authored-by: MichalStrehovsky <13110571+MichalStrehovsky@users.noreply.github.com>
CopilotAI review requested due to automatic review settings June 10, 2026 22:30
@MichalStrehovsky
MichalStrehovsky removed the request for review from CopilotJune 10, 2026 22:30

CopilotAI commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

@copilot undo all the non-test changes. Instead of those, delete the s_handleHighBitSet constant.

Done in 11abc8f: the prior non-test JIT/NativeAOT changes were reverted, and s_handleHighBitSet plus its tagging/masking uses were removed. The remaining net PR diff is the handle-tag removal plus the existing test updates.

CopilotAI changed the title Preserve JIT handle bits in cross-target buildsRemove NativeAOT debug handle taggingJun 10, 2026
@MichalStrehovsky

Copy link
Copy Markdown
MemberAuthor

/azp run runtime-nativeaot-outerloop

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

CopilotAI review requested due to automatic review settings June 12, 2026 02:18
@MichalStrehovsky

Copy link
Copy Markdown
MemberAuthor

/azp run runtime-nativeaot-outerloop

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

CopilotAI 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.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.

@github-actions

Copy link
Copy Markdown
Contributor

🤖 Copilot Code Review — PR #129034

Note

This review was generated by Copilot.

Holistic Assessment

Motivation: The problem is real and well-documented in #122013. The debug-only s_handleHighBitSet poison (0x4000000000000000 on 64-bit hosts) was applied to NativeAOT JIT interface handles but never validated on return. In cross-target builds (64-bit host → 32-bit target), the JIT truncates nint-sized handles to int-sized target pointers, losing the high bit. This causes handle equality mismatches that lead to incorrect constant folding and wrong codegen. Multiple maintainers (jkotas, EgorBo, SingleAccretion) explicitly agreed in #122013 that deleting the poison is the right fix.

Approach: Correct and minimal — remove the unused debug poison at the source (VM side) rather than attempting to patch every JIT truncation site. The test workarounds are properly cleaned up in the same PR.

Summary: ✅ LGTM. This is a clean, focused, maintainer-agreed bug fix. The handle scheme (handleBase = 0x420000, handleMultiplier = 8) already guarantees handles fit within 32-bit range, so removing the tag makes the (int)handle cast in HandleToObject correct for cross-target builds. No blocking issues found.


Detailed Findings

✅ Correctness — Handle tag removal is safe

The s_handleHighBitSet tag was added in PR #41126 as a debug aid for cross-bitness compilation but was never validated — no assertion ever checked that the bit survived round-tripping through the JIT. Meanwhile, the tag actively caused bugs: when the JIT truncated handles to target pointer size, the poisoned bit was lost, causing two handles for the same object to compare as unequal. Removing the tag eliminates this entire class of bug without losing any diagnostic value.

The remaining handle arithmetic in HandleToObject ((int)handle - handleBase) / handleMultiplier) and ObjectToHandleUnchecked (handleMultiplier * _handleToObject.Count + handleBase) is correct — handleBase is 0x420000 and handle values grow linearly from there, well within 32-bit range.

✅ Test changes — Workarounds correctly removed

  • bug603649.cs: The [ActiveIssue("...#122013", ...IsNativeAot, ...Is32)] skip is correctly removed since the root cause is fixed.
  • interfacestatics.ilproj: The <TrimMode>partial</TrimMode> workaround (which hid the bug by preventing the compiler from constant-folding the problematic handles) is correctly removed. The comment on RequiresProcessIsolation is updated to reflect its actual purpose (needed for CLRTestTargetUnsupported).

💡 Observation — Remaining issue #122013 reference in codebase

There are no remaining references to issue #122013 as an ActiveIssue or workaround in the codebase (the grep match in CgStress1.cs is coincidental test data). The issue can be closed once this PR merges.

✅ Scope and focus — PR is well-scoped

The PR was iterated from a larger multi-site JIT fix down to this minimal VM-side-only change based on reviewer feedback. The current diff is the cleanest possible fix: 3 files, +1/−15 lines, all directly related to the bug.

Generated by Code Review for issue #129034 · ● 3.3M ·

@MichalStrehovsky

Copy link
Copy Markdown
MemberAuthor

/ba-g the crypto crash is #129339

@MichalStrehovsky
MichalStrehovsky merged commit ca65f7d into mainJun 15, 2026
135 of 144 checks passed
@MichalStrehovsky
MichalStrehovsky deleted the fix/jit-cross-target-handle-bits branch June 15, 2026 21:14
eiriktsarpalis pushed a commit that referenced this pull request Jul 15, 2026
Remove the debug-only high-bit tag from NativeAOT JIT interface handles.
This avoids rejecting valid handles on targets where the tag bit is not
preserved.
Also re-enables the affected regression coverage by removing the
NativeAOT workaround/skip from the existing tests.
Cc @EgorBo
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: MichalStrehovsky <13110571+MichalStrehovsky@users.noreply.github.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jul 18, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-CodeGen-coreclrCLR 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.

6 participants

@MichalStrehovsky@EgorBo@jkotas@JulieLeeMSFT
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Remove NativeAOT debug handle tagging by MichalStrehovsky · Pull Request #129034 · dotnet/runtime · GitHub
Skip to content

Remove NativeAOT debug handle tagging - #129034

Merged
MichalStrehovsky merged 5 commits into
mainfrom
fix/jit-cross-target-handle-bits
Jun 15, 2026
Merged

Remove NativeAOT debug handle tagging#129034
MichalStrehovsky merged 5 commits into
mainfrom
fix/jit-cross-target-handle-bits

Conversation

@MichalStrehovsky

@MichalStrehovskyMichalStrehovsky commented Jun 5, 2026

Copy link
Copy Markdown
Member

Remove the debug-only high-bit tag from NativeAOT JIT interface handles. This avoids rejecting valid handles on targets where the tag bit is not preserved.

Also re-enables the affected regression coverage by removing the NativeAOT workaround/skip from the existing tests.

Cc @EgorBo

MichalStrehovskyand others added 2 commits June 5, 2026 14:27
Ensure RyuJIT keeps host-sized handle values when cross-targeting from 64-bit to 32-bit builds.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings June 5, 2026 05:34
@github-actionsgithub-actionsBot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Jun 5, 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.

@github-actions

This comment has been minimized.

CopilotAI 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.

Pull request overview

This PR adjusts RyuJIT’s constant/handle plumbing so that, in cross-target scenarios (e.g., 64-bit host compiling 32-bit target), handle values are treated as host-pointer-sized where required, avoiding truncation of the upper bits during constant import/propagation and preventing incorrect folding.

Changes:

  • Update static-readonly constant import to request sizeof(ssize_t) bytes for TYP_REF so gtNewGenericCon(TYP_REF, ...) reads a complete host-sized handle.
  • Tighten/extend handle constant preservation in JIT assertion propagation and constant bashing so handle VNs don’t lose upper bits on 32-bit targets.
  • Re-enable/strengthen coverage by removing test workarounds/skips that were masking the issue.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Show a summary per file
FileDescription
src/tests/Regressions/coreclr/15647/interfacestatics.ilprojRemoves TrimMode workaround so the test exercises the problematic optimization again.
src/tests/JIT/Methodical/jitinterface/bug603649.csRe-enables the test by removing the ActiveIssue skip.
src/coreclr/tools/Common/JitInterface/CorInfoImpl.csAdds a DEBUG invariant check to catch truncated/invalid handles early.
src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.csAdjusts getStaticFieldContent object-handle buffer handling for cross-target correctness.
src/coreclr/jit/importer.cppRequests host-sized bytes for TYP_REF static-readonly constants to avoid truncation.
src/coreclr/jit/compiler.hppAvoids incorrect debug-time “fits in int32” assertions for GC-typed constants.
src/coreclr/jit/assertionprop.cppPreserves host-sized handle constants/printing when VNs represent handles.

Comment threadsrc/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs Outdated
Comment threadsrc/coreclr/jit/importer.cpp Outdated
@github-actions

This comment has been minimized.

@MichalStrehovsky

Copy link
Copy Markdown
MemberAuthor

/azp run runtime-nativeaot-outerloop

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

@EgorBo

Copy link
Copy Markdown
Member

I assume CI failures are related? e.g.

2026-06-08T07:03:43.0458339Z Assertion failed.
2026-06-08T07:03:43.0465478Z (s_handleHighBitSet & (nint)handle) != 0
2026-06-08T07:03:43.0470117Z at Internal.JitInterface.CorInfoImpl.HandleToObject(Void* handle) in /_/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs:line 773
2026-06-08T07:03:43.0474499Z at Internal.JitInterface.CorInfoImpl.recordRelocation(Void* location, Void* locationRW, Void* target, CorInfoReloc fRelocType, Int32 addlD

@MichalStrehovsky

Copy link
Copy Markdown
MemberAuthor

I assume CI failures are related? e.g.

I only checked x86, so arm32 still not working is not completely surprising. Before I spend time setting up environment so that copilot can have a go at it, does the change so far make sense? I see the issue is now assigned to me... but I'm not a JIT dev, just someone who prompted GPT to make things compile with the added assert in CorInfoImpl.cs and GPT did its thing.

@EgorBo

Copy link
Copy Markdown
Member

I assume CI failures are related? e.g.

I only checked x86, so arm32 still not working is not completely surprising. Before I spend time setting up environment so that copilot can have a go at it, does the change so far make sense? I see the issue is now assigned to me... but I'm not a JIT dev, just someone who prompted GPT to make things compile with the added assert in CorInfoImpl.cs and GPT did its thing.

I can take a look tomorrow. I'm not sure I fully understand the problem so will have to look deeply. Meanwhole sent a PR to clean up one of the offender from your PR - #129144

@MichalStrehovsky

Copy link
Copy Markdown
MemberAuthor

@copilot undo all the non-test changes. Instead of those, delete the s_handleHighBitSet constant.

Co-authored-by: MichalStrehovsky <13110571+MichalStrehovsky@users.noreply.github.com>
CopilotAI review requested due to automatic review settings June 10, 2026 22:30
@MichalStrehovsky
MichalStrehovsky removed the request for review from CopilotJune 10, 2026 22:30

CopilotAI commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

@copilot undo all the non-test changes. Instead of those, delete the s_handleHighBitSet constant.

Done in 11abc8f: the prior non-test JIT/NativeAOT changes were reverted, and s_handleHighBitSet plus its tagging/masking uses were removed. The remaining net PR diff is the handle-tag removal plus the existing test updates.

CopilotAI changed the title Preserve JIT handle bits in cross-target buildsRemove NativeAOT debug handle taggingJun 10, 2026
@MichalStrehovsky

Copy link
Copy Markdown
MemberAuthor

/azp run runtime-nativeaot-outerloop

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

CopilotAI review requested due to automatic review settings June 12, 2026 02:18
@MichalStrehovsky

Copy link
Copy Markdown
MemberAuthor

/azp run runtime-nativeaot-outerloop

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

CopilotAI 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.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.

@github-actions

Copy link
Copy Markdown
Contributor

🤖 Copilot Code Review — PR #129034

Note

This review was generated by Copilot.

Holistic Assessment

Motivation: The problem is real and well-documented in #122013. The debug-only s_handleHighBitSet poison (0x4000000000000000 on 64-bit hosts) was applied to NativeAOT JIT interface handles but never validated on return. In cross-target builds (64-bit host → 32-bit target), the JIT truncates nint-sized handles to int-sized target pointers, losing the high bit. This causes handle equality mismatches that lead to incorrect constant folding and wrong codegen. Multiple maintainers (jkotas, EgorBo, SingleAccretion) explicitly agreed in #122013 that deleting the poison is the right fix.

Approach: Correct and minimal — remove the unused debug poison at the source (VM side) rather than attempting to patch every JIT truncation site. The test workarounds are properly cleaned up in the same PR.

Summary: ✅ LGTM. This is a clean, focused, maintainer-agreed bug fix. The handle scheme (handleBase = 0x420000, handleMultiplier = 8) already guarantees handles fit within 32-bit range, so removing the tag makes the (int)handle cast in HandleToObject correct for cross-target builds. No blocking issues found.


Detailed Findings

✅ Correctness — Handle tag removal is safe

The s_handleHighBitSet tag was added in PR #41126 as a debug aid for cross-bitness compilation but was never validated — no assertion ever checked that the bit survived round-tripping through the JIT. Meanwhile, the tag actively caused bugs: when the JIT truncated handles to target pointer size, the poisoned bit was lost, causing two handles for the same object to compare as unequal. Removing the tag eliminates this entire class of bug without losing any diagnostic value.

The remaining handle arithmetic in HandleToObject ((int)handle - handleBase) / handleMultiplier) and ObjectToHandleUnchecked (handleMultiplier * _handleToObject.Count + handleBase) is correct — handleBase is 0x420000 and handle values grow linearly from there, well within 32-bit range.

✅ Test changes — Workarounds correctly removed

  • bug603649.cs: The [ActiveIssue("...#122013", ...IsNativeAot, ...Is32)] skip is correctly removed since the root cause is fixed.
  • interfacestatics.ilproj: The <TrimMode>partial</TrimMode> workaround (which hid the bug by preventing the compiler from constant-folding the problematic handles) is correctly removed. The comment on RequiresProcessIsolation is updated to reflect its actual purpose (needed for CLRTestTargetUnsupported).

💡 Observation — Remaining issue #122013 reference in codebase

There are no remaining references to issue #122013 as an ActiveIssue or workaround in the codebase (the grep match in CgStress1.cs is coincidental test data). The issue can be closed once this PR merges.

✅ Scope and focus — PR is well-scoped

The PR was iterated from a larger multi-site JIT fix down to this minimal VM-side-only change based on reviewer feedback. The current diff is the cleanest possible fix: 3 files, +1/−15 lines, all directly related to the bug.

Generated by Code Review for issue #129034 · ● 3.3M ·

@MichalStrehovsky

Copy link
Copy Markdown
MemberAuthor

/ba-g the crypto crash is #129339

@MichalStrehovsky
MichalStrehovsky merged commit ca65f7d into mainJun 15, 2026
135 of 144 checks passed
@MichalStrehovsky
MichalStrehovsky deleted the fix/jit-cross-target-handle-bits branch June 15, 2026 21:14
eiriktsarpalis pushed a commit that referenced this pull request Jul 15, 2026
Remove the debug-only high-bit tag from NativeAOT JIT interface handles.
This avoids rejecting valid handles on targets where the tag bit is not
preserved.
Also re-enables the affected regression coverage by removing the
NativeAOT workaround/skip from the existing tests.
Cc @EgorBo
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: MichalStrehovsky <13110571+MichalStrehovsky@users.noreply.github.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jul 18, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-CodeGen-coreclrCLR 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.

6 participants

@MichalStrehovsky@EgorBo@jkotas@JulieLeeMSFT
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Remove NativeAOT debug handle tagging by MichalStrehovsky · Pull Request #129034 · dotnet/runtime · GitHub
Skip to content

Remove NativeAOT debug handle tagging - #129034

Merged
MichalStrehovsky merged 5 commits into
mainfrom
fix/jit-cross-target-handle-bits
Jun 15, 2026
Merged

Remove NativeAOT debug handle tagging#129034
MichalStrehovsky merged 5 commits into
mainfrom
fix/jit-cross-target-handle-bits

Conversation

@MichalStrehovsky

@MichalStrehovskyMichalStrehovsky commented Jun 5, 2026

Copy link
Copy Markdown
Member

Remove the debug-only high-bit tag from NativeAOT JIT interface handles. This avoids rejecting valid handles on targets where the tag bit is not preserved.

Also re-enables the affected regression coverage by removing the NativeAOT workaround/skip from the existing tests.

Cc @EgorBo

MichalStrehovskyand others added 2 commits June 5, 2026 14:27
Ensure RyuJIT keeps host-sized handle values when cross-targeting from 64-bit to 32-bit builds.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings June 5, 2026 05:34
@github-actionsgithub-actionsBot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Jun 5, 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.

@github-actions

This comment has been minimized.

CopilotAI 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.

Pull request overview

This PR adjusts RyuJIT’s constant/handle plumbing so that, in cross-target scenarios (e.g., 64-bit host compiling 32-bit target), handle values are treated as host-pointer-sized where required, avoiding truncation of the upper bits during constant import/propagation and preventing incorrect folding.

Changes:

  • Update static-readonly constant import to request sizeof(ssize_t) bytes for TYP_REF so gtNewGenericCon(TYP_REF, ...) reads a complete host-sized handle.
  • Tighten/extend handle constant preservation in JIT assertion propagation and constant bashing so handle VNs don’t lose upper bits on 32-bit targets.
  • Re-enable/strengthen coverage by removing test workarounds/skips that were masking the issue.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Show a summary per file
FileDescription
src/tests/Regressions/coreclr/15647/interfacestatics.ilprojRemoves TrimMode workaround so the test exercises the problematic optimization again.
src/tests/JIT/Methodical/jitinterface/bug603649.csRe-enables the test by removing the ActiveIssue skip.
src/coreclr/tools/Common/JitInterface/CorInfoImpl.csAdds a DEBUG invariant check to catch truncated/invalid handles early.
src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.csAdjusts getStaticFieldContent object-handle buffer handling for cross-target correctness.
src/coreclr/jit/importer.cppRequests host-sized bytes for TYP_REF static-readonly constants to avoid truncation.
src/coreclr/jit/compiler.hppAvoids incorrect debug-time “fits in int32” assertions for GC-typed constants.
src/coreclr/jit/assertionprop.cppPreserves host-sized handle constants/printing when VNs represent handles.

Comment threadsrc/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs Outdated
Comment threadsrc/coreclr/jit/importer.cpp Outdated
@github-actions

This comment has been minimized.

@MichalStrehovsky

Copy link
Copy Markdown
MemberAuthor

/azp run runtime-nativeaot-outerloop

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

@EgorBo

Copy link
Copy Markdown
Member

I assume CI failures are related? e.g.

2026-06-08T07:03:43.0458339Z Assertion failed.
2026-06-08T07:03:43.0465478Z (s_handleHighBitSet & (nint)handle) != 0
2026-06-08T07:03:43.0470117Z at Internal.JitInterface.CorInfoImpl.HandleToObject(Void* handle) in /_/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs:line 773
2026-06-08T07:03:43.0474499Z at Internal.JitInterface.CorInfoImpl.recordRelocation(Void* location, Void* locationRW, Void* target, CorInfoReloc fRelocType, Int32 addlD

@MichalStrehovsky

Copy link
Copy Markdown
MemberAuthor

I assume CI failures are related? e.g.

I only checked x86, so arm32 still not working is not completely surprising. Before I spend time setting up environment so that copilot can have a go at it, does the change so far make sense? I see the issue is now assigned to me... but I'm not a JIT dev, just someone who prompted GPT to make things compile with the added assert in CorInfoImpl.cs and GPT did its thing.

@EgorBo

Copy link
Copy Markdown
Member

I assume CI failures are related? e.g.

I only checked x86, so arm32 still not working is not completely surprising. Before I spend time setting up environment so that copilot can have a go at it, does the change so far make sense? I see the issue is now assigned to me... but I'm not a JIT dev, just someone who prompted GPT to make things compile with the added assert in CorInfoImpl.cs and GPT did its thing.

I can take a look tomorrow. I'm not sure I fully understand the problem so will have to look deeply. Meanwhole sent a PR to clean up one of the offender from your PR - #129144

@MichalStrehovsky

Copy link
Copy Markdown
MemberAuthor

@copilot undo all the non-test changes. Instead of those, delete the s_handleHighBitSet constant.

Co-authored-by: MichalStrehovsky <13110571+MichalStrehovsky@users.noreply.github.com>
CopilotAI review requested due to automatic review settings June 10, 2026 22:30
@MichalStrehovsky
MichalStrehovsky removed the request for review from CopilotJune 10, 2026 22:30

CopilotAI commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

@copilot undo all the non-test changes. Instead of those, delete the s_handleHighBitSet constant.

Done in 11abc8f: the prior non-test JIT/NativeAOT changes were reverted, and s_handleHighBitSet plus its tagging/masking uses were removed. The remaining net PR diff is the handle-tag removal plus the existing test updates.

CopilotAI changed the title Preserve JIT handle bits in cross-target buildsRemove NativeAOT debug handle taggingJun 10, 2026
@MichalStrehovsky

Copy link
Copy Markdown
MemberAuthor

/azp run runtime-nativeaot-outerloop

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

CopilotAI review requested due to automatic review settings June 12, 2026 02:18
@MichalStrehovsky

Copy link
Copy Markdown
MemberAuthor

/azp run runtime-nativeaot-outerloop

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

CopilotAI 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.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.

@github-actions

Copy link
Copy Markdown
Contributor

🤖 Copilot Code Review — PR #129034

Note

This review was generated by Copilot.

Holistic Assessment

Motivation: The problem is real and well-documented in #122013. The debug-only s_handleHighBitSet poison (0x4000000000000000 on 64-bit hosts) was applied to NativeAOT JIT interface handles but never validated on return. In cross-target builds (64-bit host → 32-bit target), the JIT truncates nint-sized handles to int-sized target pointers, losing the high bit. This causes handle equality mismatches that lead to incorrect constant folding and wrong codegen. Multiple maintainers (jkotas, EgorBo, SingleAccretion) explicitly agreed in #122013 that deleting the poison is the right fix.

Approach: Correct and minimal — remove the unused debug poison at the source (VM side) rather than attempting to patch every JIT truncation site. The test workarounds are properly cleaned up in the same PR.

Summary: ✅ LGTM. This is a clean, focused, maintainer-agreed bug fix. The handle scheme (handleBase = 0x420000, handleMultiplier = 8) already guarantees handles fit within 32-bit range, so removing the tag makes the (int)handle cast in HandleToObject correct for cross-target builds. No blocking issues found.


Detailed Findings

✅ Correctness — Handle tag removal is safe

The s_handleHighBitSet tag was added in PR #41126 as a debug aid for cross-bitness compilation but was never validated — no assertion ever checked that the bit survived round-tripping through the JIT. Meanwhile, the tag actively caused bugs: when the JIT truncated handles to target pointer size, the poisoned bit was lost, causing two handles for the same object to compare as unequal. Removing the tag eliminates this entire class of bug without losing any diagnostic value.

The remaining handle arithmetic in HandleToObject ((int)handle - handleBase) / handleMultiplier) and ObjectToHandleUnchecked (handleMultiplier * _handleToObject.Count + handleBase) is correct — handleBase is 0x420000 and handle values grow linearly from there, well within 32-bit range.

✅ Test changes — Workarounds correctly removed

  • bug603649.cs: The [ActiveIssue("...#122013", ...IsNativeAot, ...Is32)] skip is correctly removed since the root cause is fixed.
  • interfacestatics.ilproj: The <TrimMode>partial</TrimMode> workaround (which hid the bug by preventing the compiler from constant-folding the problematic handles) is correctly removed. The comment on RequiresProcessIsolation is updated to reflect its actual purpose (needed for CLRTestTargetUnsupported).

💡 Observation — Remaining issue #122013 reference in codebase

There are no remaining references to issue #122013 as an ActiveIssue or workaround in the codebase (the grep match in CgStress1.cs is coincidental test data). The issue can be closed once this PR merges.

✅ Scope and focus — PR is well-scoped

The PR was iterated from a larger multi-site JIT fix down to this minimal VM-side-only change based on reviewer feedback. The current diff is the cleanest possible fix: 3 files, +1/−15 lines, all directly related to the bug.

Generated by Code Review for issue #129034 · ● 3.3M ·

@MichalStrehovsky

Copy link
Copy Markdown
MemberAuthor

/ba-g the crypto crash is #129339

@MichalStrehovsky
MichalStrehovsky merged commit ca65f7d into mainJun 15, 2026
135 of 144 checks passed
@MichalStrehovsky
MichalStrehovsky deleted the fix/jit-cross-target-handle-bits branch June 15, 2026 21:14
eiriktsarpalis pushed a commit that referenced this pull request Jul 15, 2026
Remove the debug-only high-bit tag from NativeAOT JIT interface handles.
This avoids rejecting valid handles on targets where the tag bit is not
preserved.
Also re-enables the affected regression coverage by removing the
NativeAOT workaround/skip from the existing tests.
Cc @EgorBo
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: MichalStrehovsky <13110571+MichalStrehovsky@users.noreply.github.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jul 18, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-CodeGen-coreclrCLR 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.

6 participants

@MichalStrehovsky@EgorBo@jkotas@JulieLeeMSFT
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' Remove NativeAOT debug handle tagging by MichalStrehovsky · Pull Request #129034 · dotnet/runtime · GitHub
Skip to content

Remove NativeAOT debug handle tagging - #129034

Merged
MichalStrehovsky merged 5 commits into
mainfrom
fix/jit-cross-target-handle-bits
Jun 15, 2026
Merged

Remove NativeAOT debug handle tagging#129034
MichalStrehovsky merged 5 commits into
mainfrom
fix/jit-cross-target-handle-bits

Conversation

@MichalStrehovsky

@MichalStrehovskyMichalStrehovsky commented Jun 5, 2026

Copy link
Copy Markdown
Member

Remove the debug-only high-bit tag from NativeAOT JIT interface handles. This avoids rejecting valid handles on targets where the tag bit is not preserved.

Also re-enables the affected regression coverage by removing the NativeAOT workaround/skip from the existing tests.

Cc @EgorBo

MichalStrehovskyand others added 2 commits June 5, 2026 14:27
Ensure RyuJIT keeps host-sized handle values when cross-targeting from 64-bit to 32-bit builds.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings June 5, 2026 05:34
@github-actionsgithub-actionsBot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Jun 5, 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.

@github-actions

This comment has been minimized.

CopilotAI 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.

Pull request overview

This PR adjusts RyuJIT’s constant/handle plumbing so that, in cross-target scenarios (e.g., 64-bit host compiling 32-bit target), handle values are treated as host-pointer-sized where required, avoiding truncation of the upper bits during constant import/propagation and preventing incorrect folding.

Changes:

  • Update static-readonly constant import to request sizeof(ssize_t) bytes for TYP_REF so gtNewGenericCon(TYP_REF, ...) reads a complete host-sized handle.
  • Tighten/extend handle constant preservation in JIT assertion propagation and constant bashing so handle VNs don’t lose upper bits on 32-bit targets.
  • Re-enable/strengthen coverage by removing test workarounds/skips that were masking the issue.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Show a summary per file
FileDescription
src/tests/Regressions/coreclr/15647/interfacestatics.ilprojRemoves TrimMode workaround so the test exercises the problematic optimization again.
src/tests/JIT/Methodical/jitinterface/bug603649.csRe-enables the test by removing the ActiveIssue skip.
src/coreclr/tools/Common/JitInterface/CorInfoImpl.csAdds a DEBUG invariant check to catch truncated/invalid handles early.
src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.csAdjusts getStaticFieldContent object-handle buffer handling for cross-target correctness.
src/coreclr/jit/importer.cppRequests host-sized bytes for TYP_REF static-readonly constants to avoid truncation.
src/coreclr/jit/compiler.hppAvoids incorrect debug-time “fits in int32” assertions for GC-typed constants.
src/coreclr/jit/assertionprop.cppPreserves host-sized handle constants/printing when VNs represent handles.

Comment threadsrc/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs Outdated
Comment threadsrc/coreclr/jit/importer.cpp Outdated
@github-actions

This comment has been minimized.

@MichalStrehovsky

Copy link
Copy Markdown
MemberAuthor

/azp run runtime-nativeaot-outerloop

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

@EgorBo

Copy link
Copy Markdown
Member

I assume CI failures are related? e.g.

2026-06-08T07:03:43.0458339Z Assertion failed.
2026-06-08T07:03:43.0465478Z (s_handleHighBitSet & (nint)handle) != 0
2026-06-08T07:03:43.0470117Z at Internal.JitInterface.CorInfoImpl.HandleToObject(Void* handle) in /_/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs:line 773
2026-06-08T07:03:43.0474499Z at Internal.JitInterface.CorInfoImpl.recordRelocation(Void* location, Void* locationRW, Void* target, CorInfoReloc fRelocType, Int32 addlD

@MichalStrehovsky

Copy link
Copy Markdown
MemberAuthor

I assume CI failures are related? e.g.

I only checked x86, so arm32 still not working is not completely surprising. Before I spend time setting up environment so that copilot can have a go at it, does the change so far make sense? I see the issue is now assigned to me... but I'm not a JIT dev, just someone who prompted GPT to make things compile with the added assert in CorInfoImpl.cs and GPT did its thing.

@EgorBo

Copy link
Copy Markdown
Member

I assume CI failures are related? e.g.

I only checked x86, so arm32 still not working is not completely surprising. Before I spend time setting up environment so that copilot can have a go at it, does the change so far make sense? I see the issue is now assigned to me... but I'm not a JIT dev, just someone who prompted GPT to make things compile with the added assert in CorInfoImpl.cs and GPT did its thing.

I can take a look tomorrow. I'm not sure I fully understand the problem so will have to look deeply. Meanwhole sent a PR to clean up one of the offender from your PR - #129144

@MichalStrehovsky

Copy link
Copy Markdown
MemberAuthor

@copilot undo all the non-test changes. Instead of those, delete the s_handleHighBitSet constant.

Co-authored-by: MichalStrehovsky <13110571+MichalStrehovsky@users.noreply.github.com>
CopilotAI review requested due to automatic review settings June 10, 2026 22:30
@MichalStrehovsky
MichalStrehovsky removed the request for review from CopilotJune 10, 2026 22:30

CopilotAI commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

@copilot undo all the non-test changes. Instead of those, delete the s_handleHighBitSet constant.

Done in 11abc8f: the prior non-test JIT/NativeAOT changes were reverted, and s_handleHighBitSet plus its tagging/masking uses were removed. The remaining net PR diff is the handle-tag removal plus the existing test updates.

CopilotAI changed the title Preserve JIT handle bits in cross-target buildsRemove NativeAOT debug handle taggingJun 10, 2026
@MichalStrehovsky

Copy link
Copy Markdown
MemberAuthor

/azp run runtime-nativeaot-outerloop

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

CopilotAI review requested due to automatic review settings June 12, 2026 02:18
@MichalStrehovsky

Copy link
Copy Markdown
MemberAuthor

/azp run runtime-nativeaot-outerloop

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

CopilotAI 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.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.

@github-actions

Copy link
Copy Markdown
Contributor

🤖 Copilot Code Review — PR #129034

Note

This review was generated by Copilot.

Holistic Assessment

Motivation: The problem is real and well-documented in #122013. The debug-only s_handleHighBitSet poison (0x4000000000000000 on 64-bit hosts) was applied to NativeAOT JIT interface handles but never validated on return. In cross-target builds (64-bit host → 32-bit target), the JIT truncates nint-sized handles to int-sized target pointers, losing the high bit. This causes handle equality mismatches that lead to incorrect constant folding and wrong codegen. Multiple maintainers (jkotas, EgorBo, SingleAccretion) explicitly agreed in #122013 that deleting the poison is the right fix.

Approach: Correct and minimal — remove the unused debug poison at the source (VM side) rather than attempting to patch every JIT truncation site. The test workarounds are properly cleaned up in the same PR.

Summary: ✅ LGTM. This is a clean, focused, maintainer-agreed bug fix. The handle scheme (handleBase = 0x420000, handleMultiplier = 8) already guarantees handles fit within 32-bit range, so removing the tag makes the (int)handle cast in HandleToObject correct for cross-target builds. No blocking issues found.


Detailed Findings

✅ Correctness — Handle tag removal is safe

The s_handleHighBitSet tag was added in PR #41126 as a debug aid for cross-bitness compilation but was never validated — no assertion ever checked that the bit survived round-tripping through the JIT. Meanwhile, the tag actively caused bugs: when the JIT truncated handles to target pointer size, the poisoned bit was lost, causing two handles for the same object to compare as unequal. Removing the tag eliminates this entire class of bug without losing any diagnostic value.

The remaining handle arithmetic in HandleToObject ((int)handle - handleBase) / handleMultiplier) and ObjectToHandleUnchecked (handleMultiplier * _handleToObject.Count + handleBase) is correct — handleBase is 0x420000 and handle values grow linearly from there, well within 32-bit range.

✅ Test changes — Workarounds correctly removed

  • bug603649.cs: The [ActiveIssue("...#122013", ...IsNativeAot, ...Is32)] skip is correctly removed since the root cause is fixed.
  • interfacestatics.ilproj: The <TrimMode>partial</TrimMode> workaround (which hid the bug by preventing the compiler from constant-folding the problematic handles) is correctly removed. The comment on RequiresProcessIsolation is updated to reflect its actual purpose (needed for CLRTestTargetUnsupported).

💡 Observation — Remaining issue #122013 reference in codebase

There are no remaining references to issue #122013 as an ActiveIssue or workaround in the codebase (the grep match in CgStress1.cs is coincidental test data). The issue can be closed once this PR merges.

✅ Scope and focus — PR is well-scoped

The PR was iterated from a larger multi-site JIT fix down to this minimal VM-side-only change based on reviewer feedback. The current diff is the cleanest possible fix: 3 files, +1/−15 lines, all directly related to the bug.

Generated by Code Review for issue #129034 · ● 3.3M ·

@MichalStrehovsky

Copy link
Copy Markdown
MemberAuthor

/ba-g the crypto crash is #129339

@MichalStrehovsky
MichalStrehovsky merged commit ca65f7d into mainJun 15, 2026
135 of 144 checks passed
@MichalStrehovsky
MichalStrehovsky deleted the fix/jit-cross-target-handle-bits branch June 15, 2026 21:14
eiriktsarpalis pushed a commit that referenced this pull request Jul 15, 2026
Remove the debug-only high-bit tag from NativeAOT JIT interface handles.
This avoids rejecting valid handles on targets where the tag bit is not
preserved.
Also re-enables the affected regression coverage by removing the
NativeAOT workaround/skip from the existing tests.
Cc @EgorBo
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: MichalStrehovsky <13110571+MichalStrehovsky@users.noreply.github.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jul 18, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-CodeGen-coreclrCLR 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.

6 participants

@MichalStrehovsky@EgorBo@jkotas@JulieLeeMSFT
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Remove NativeAOT debug handle tagging by MichalStrehovsky · Pull Request #129034 · dotnet/runtime · GitHub
Skip to content

Remove NativeAOT debug handle tagging - #129034

Merged
MichalStrehovsky merged 5 commits into
mainfrom
fix/jit-cross-target-handle-bits
Jun 15, 2026
Merged

Remove NativeAOT debug handle tagging#129034
MichalStrehovsky merged 5 commits into
mainfrom
fix/jit-cross-target-handle-bits

Conversation

@MichalStrehovsky

@MichalStrehovskyMichalStrehovsky commented Jun 5, 2026

Copy link
Copy Markdown
Member

Remove the debug-only high-bit tag from NativeAOT JIT interface handles. This avoids rejecting valid handles on targets where the tag bit is not preserved.

Also re-enables the affected regression coverage by removing the NativeAOT workaround/skip from the existing tests.

Cc @EgorBo

MichalStrehovskyand others added 2 commits June 5, 2026 14:27
Ensure RyuJIT keeps host-sized handle values when cross-targeting from 64-bit to 32-bit builds.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings June 5, 2026 05:34
@github-actionsgithub-actionsBot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Jun 5, 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.

@github-actions

This comment has been minimized.

CopilotAI 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.

Pull request overview

This PR adjusts RyuJIT’s constant/handle plumbing so that, in cross-target scenarios (e.g., 64-bit host compiling 32-bit target), handle values are treated as host-pointer-sized where required, avoiding truncation of the upper bits during constant import/propagation and preventing incorrect folding.

Changes:

  • Update static-readonly constant import to request sizeof(ssize_t) bytes for TYP_REF so gtNewGenericCon(TYP_REF, ...) reads a complete host-sized handle.
  • Tighten/extend handle constant preservation in JIT assertion propagation and constant bashing so handle VNs don’t lose upper bits on 32-bit targets.
  • Re-enable/strengthen coverage by removing test workarounds/skips that were masking the issue.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Show a summary per file
FileDescription
src/tests/Regressions/coreclr/15647/interfacestatics.ilprojRemoves TrimMode workaround so the test exercises the problematic optimization again.
src/tests/JIT/Methodical/jitinterface/bug603649.csRe-enables the test by removing the ActiveIssue skip.
src/coreclr/tools/Common/JitInterface/CorInfoImpl.csAdds a DEBUG invariant check to catch truncated/invalid handles early.
src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.csAdjusts getStaticFieldContent object-handle buffer handling for cross-target correctness.
src/coreclr/jit/importer.cppRequests host-sized bytes for TYP_REF static-readonly constants to avoid truncation.
src/coreclr/jit/compiler.hppAvoids incorrect debug-time “fits in int32” assertions for GC-typed constants.
src/coreclr/jit/assertionprop.cppPreserves host-sized handle constants/printing when VNs represent handles.

Comment threadsrc/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs Outdated
Comment threadsrc/coreclr/jit/importer.cpp Outdated
@github-actions

This comment has been minimized.

@MichalStrehovsky

Copy link
Copy Markdown
MemberAuthor

/azp run runtime-nativeaot-outerloop

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

@EgorBo

Copy link
Copy Markdown
Member

I assume CI failures are related? e.g.

2026-06-08T07:03:43.0458339Z Assertion failed.
2026-06-08T07:03:43.0465478Z (s_handleHighBitSet & (nint)handle) != 0
2026-06-08T07:03:43.0470117Z at Internal.JitInterface.CorInfoImpl.HandleToObject(Void* handle) in /_/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs:line 773
2026-06-08T07:03:43.0474499Z at Internal.JitInterface.CorInfoImpl.recordRelocation(Void* location, Void* locationRW, Void* target, CorInfoReloc fRelocType, Int32 addlD

@MichalStrehovsky

Copy link
Copy Markdown
MemberAuthor

I assume CI failures are related? e.g.

I only checked x86, so arm32 still not working is not completely surprising. Before I spend time setting up environment so that copilot can have a go at it, does the change so far make sense? I see the issue is now assigned to me... but I'm not a JIT dev, just someone who prompted GPT to make things compile with the added assert in CorInfoImpl.cs and GPT did its thing.

@EgorBo

Copy link
Copy Markdown
Member

I assume CI failures are related? e.g.

I only checked x86, so arm32 still not working is not completely surprising. Before I spend time setting up environment so that copilot can have a go at it, does the change so far make sense? I see the issue is now assigned to me... but I'm not a JIT dev, just someone who prompted GPT to make things compile with the added assert in CorInfoImpl.cs and GPT did its thing.

I can take a look tomorrow. I'm not sure I fully understand the problem so will have to look deeply. Meanwhole sent a PR to clean up one of the offender from your PR - #129144

@MichalStrehovsky

Copy link
Copy Markdown
MemberAuthor

@copilot undo all the non-test changes. Instead of those, delete the s_handleHighBitSet constant.

Co-authored-by: MichalStrehovsky <13110571+MichalStrehovsky@users.noreply.github.com>
CopilotAI review requested due to automatic review settings June 10, 2026 22:30
@MichalStrehovsky
MichalStrehovsky removed the request for review from CopilotJune 10, 2026 22:30

CopilotAI commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

@copilot undo all the non-test changes. Instead of those, delete the s_handleHighBitSet constant.

Done in 11abc8f: the prior non-test JIT/NativeAOT changes were reverted, and s_handleHighBitSet plus its tagging/masking uses were removed. The remaining net PR diff is the handle-tag removal plus the existing test updates.

CopilotAI changed the title Preserve JIT handle bits in cross-target buildsRemove NativeAOT debug handle taggingJun 10, 2026
@MichalStrehovsky

Copy link
Copy Markdown
MemberAuthor

/azp run runtime-nativeaot-outerloop

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

CopilotAI review requested due to automatic review settings June 12, 2026 02:18
@MichalStrehovsky

Copy link
Copy Markdown
MemberAuthor

/azp run runtime-nativeaot-outerloop

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

CopilotAI 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.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.

@github-actions

Copy link
Copy Markdown
Contributor

🤖 Copilot Code Review — PR #129034

Note

This review was generated by Copilot.

Holistic Assessment

Motivation: The problem is real and well-documented in #122013. The debug-only s_handleHighBitSet poison (0x4000000000000000 on 64-bit hosts) was applied to NativeAOT JIT interface handles but never validated on return. In cross-target builds (64-bit host → 32-bit target), the JIT truncates nint-sized handles to int-sized target pointers, losing the high bit. This causes handle equality mismatches that lead to incorrect constant folding and wrong codegen. Multiple maintainers (jkotas, EgorBo, SingleAccretion) explicitly agreed in #122013 that deleting the poison is the right fix.

Approach: Correct and minimal — remove the unused debug poison at the source (VM side) rather than attempting to patch every JIT truncation site. The test workarounds are properly cleaned up in the same PR.

Summary: ✅ LGTM. This is a clean, focused, maintainer-agreed bug fix. The handle scheme (handleBase = 0x420000, handleMultiplier = 8) already guarantees handles fit within 32-bit range, so removing the tag makes the (int)handle cast in HandleToObject correct for cross-target builds. No blocking issues found.


Detailed Findings

✅ Correctness — Handle tag removal is safe

The s_handleHighBitSet tag was added in PR #41126 as a debug aid for cross-bitness compilation but was never validated — no assertion ever checked that the bit survived round-tripping through the JIT. Meanwhile, the tag actively caused bugs: when the JIT truncated handles to target pointer size, the poisoned bit was lost, causing two handles for the same object to compare as unequal. Removing the tag eliminates this entire class of bug without losing any diagnostic value.

The remaining handle arithmetic in HandleToObject ((int)handle - handleBase) / handleMultiplier) and ObjectToHandleUnchecked (handleMultiplier * _handleToObject.Count + handleBase) is correct — handleBase is 0x420000 and handle values grow linearly from there, well within 32-bit range.

✅ Test changes — Workarounds correctly removed

  • bug603649.cs: The [ActiveIssue("...#122013", ...IsNativeAot, ...Is32)] skip is correctly removed since the root cause is fixed.
  • interfacestatics.ilproj: The <TrimMode>partial</TrimMode> workaround (which hid the bug by preventing the compiler from constant-folding the problematic handles) is correctly removed. The comment on RequiresProcessIsolation is updated to reflect its actual purpose (needed for CLRTestTargetUnsupported).

💡 Observation — Remaining issue #122013 reference in codebase

There are no remaining references to issue #122013 as an ActiveIssue or workaround in the codebase (the grep match in CgStress1.cs is coincidental test data). The issue can be closed once this PR merges.

✅ Scope and focus — PR is well-scoped

The PR was iterated from a larger multi-site JIT fix down to this minimal VM-side-only change based on reviewer feedback. The current diff is the cleanest possible fix: 3 files, +1/−15 lines, all directly related to the bug.

Generated by Code Review for issue #129034 · ● 3.3M ·

@MichalStrehovsky

Copy link
Copy Markdown
MemberAuthor

/ba-g the crypto crash is #129339

@MichalStrehovsky
MichalStrehovsky merged commit ca65f7d into mainJun 15, 2026
135 of 144 checks passed
@MichalStrehovsky
MichalStrehovsky deleted the fix/jit-cross-target-handle-bits branch June 15, 2026 21:14
eiriktsarpalis pushed a commit that referenced this pull request Jul 15, 2026
Remove the debug-only high-bit tag from NativeAOT JIT interface handles.
This avoids rejecting valid handles on targets where the tag bit is not
preserved.
Also re-enables the affected regression coverage by removing the
NativeAOT workaround/skip from the existing tests.
Cc @EgorBo
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: MichalStrehovsky <13110571+MichalStrehovsky@users.noreply.github.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jul 18, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-CodeGen-coreclrCLR 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.

6 participants

@MichalStrehovsky@EgorBo@jkotas@JulieLeeMSFT
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); Remove NativeAOT debug handle tagging by MichalStrehovsky · Pull Request #129034 · dotnet/runtime · GitHub
Skip to content

Remove NativeAOT debug handle tagging - #129034

Merged
MichalStrehovsky merged 5 commits into
mainfrom
fix/jit-cross-target-handle-bits
Jun 15, 2026
Merged

Remove NativeAOT debug handle tagging#129034
MichalStrehovsky merged 5 commits into
mainfrom
fix/jit-cross-target-handle-bits

Conversation

@MichalStrehovsky

@MichalStrehovskyMichalStrehovsky commented Jun 5, 2026

Copy link
Copy Markdown
Member

Remove the debug-only high-bit tag from NativeAOT JIT interface handles. This avoids rejecting valid handles on targets where the tag bit is not preserved.

Also re-enables the affected regression coverage by removing the NativeAOT workaround/skip from the existing tests.

Cc @EgorBo

MichalStrehovskyand others added 2 commits June 5, 2026 14:27
Ensure RyuJIT keeps host-sized handle values when cross-targeting from 64-bit to 32-bit builds.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings June 5, 2026 05:34
@github-actionsgithub-actionsBot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Jun 5, 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.

@github-actions

This comment has been minimized.

CopilotAI 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.

Pull request overview

This PR adjusts RyuJIT’s constant/handle plumbing so that, in cross-target scenarios (e.g., 64-bit host compiling 32-bit target), handle values are treated as host-pointer-sized where required, avoiding truncation of the upper bits during constant import/propagation and preventing incorrect folding.

Changes:

  • Update static-readonly constant import to request sizeof(ssize_t) bytes for TYP_REF so gtNewGenericCon(TYP_REF, ...) reads a complete host-sized handle.
  • Tighten/extend handle constant preservation in JIT assertion propagation and constant bashing so handle VNs don’t lose upper bits on 32-bit targets.
  • Re-enable/strengthen coverage by removing test workarounds/skips that were masking the issue.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Show a summary per file
FileDescription
src/tests/Regressions/coreclr/15647/interfacestatics.ilprojRemoves TrimMode workaround so the test exercises the problematic optimization again.
src/tests/JIT/Methodical/jitinterface/bug603649.csRe-enables the test by removing the ActiveIssue skip.
src/coreclr/tools/Common/JitInterface/CorInfoImpl.csAdds a DEBUG invariant check to catch truncated/invalid handles early.
src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.csAdjusts getStaticFieldContent object-handle buffer handling for cross-target correctness.
src/coreclr/jit/importer.cppRequests host-sized bytes for TYP_REF static-readonly constants to avoid truncation.
src/coreclr/jit/compiler.hppAvoids incorrect debug-time “fits in int32” assertions for GC-typed constants.
src/coreclr/jit/assertionprop.cppPreserves host-sized handle constants/printing when VNs represent handles.

Comment threadsrc/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs Outdated
Comment threadsrc/coreclr/jit/importer.cpp Outdated
@github-actions

This comment has been minimized.

@MichalStrehovsky

Copy link
Copy Markdown
MemberAuthor

/azp run runtime-nativeaot-outerloop

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

@EgorBo

Copy link
Copy Markdown
Member

I assume CI failures are related? e.g.

2026-06-08T07:03:43.0458339Z Assertion failed.
2026-06-08T07:03:43.0465478Z (s_handleHighBitSet & (nint)handle) != 0
2026-06-08T07:03:43.0470117Z at Internal.JitInterface.CorInfoImpl.HandleToObject(Void* handle) in /_/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs:line 773
2026-06-08T07:03:43.0474499Z at Internal.JitInterface.CorInfoImpl.recordRelocation(Void* location, Void* locationRW, Void* target, CorInfoReloc fRelocType, Int32 addlD

@MichalStrehovsky

Copy link
Copy Markdown
MemberAuthor

I assume CI failures are related? e.g.

I only checked x86, so arm32 still not working is not completely surprising. Before I spend time setting up environment so that copilot can have a go at it, does the change so far make sense? I see the issue is now assigned to me... but I'm not a JIT dev, just someone who prompted GPT to make things compile with the added assert in CorInfoImpl.cs and GPT did its thing.

@EgorBo

Copy link
Copy Markdown
Member

I assume CI failures are related? e.g.

I only checked x86, so arm32 still not working is not completely surprising. Before I spend time setting up environment so that copilot can have a go at it, does the change so far make sense? I see the issue is now assigned to me... but I'm not a JIT dev, just someone who prompted GPT to make things compile with the added assert in CorInfoImpl.cs and GPT did its thing.

I can take a look tomorrow. I'm not sure I fully understand the problem so will have to look deeply. Meanwhole sent a PR to clean up one of the offender from your PR - #129144

@MichalStrehovsky

Copy link
Copy Markdown
MemberAuthor

@copilot undo all the non-test changes. Instead of those, delete the s_handleHighBitSet constant.

Co-authored-by: MichalStrehovsky <13110571+MichalStrehovsky@users.noreply.github.com>
CopilotAI review requested due to automatic review settings June 10, 2026 22:30
@MichalStrehovsky
MichalStrehovsky removed the request for review from CopilotJune 10, 2026 22:30

CopilotAI commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

@copilot undo all the non-test changes. Instead of those, delete the s_handleHighBitSet constant.

Done in 11abc8f: the prior non-test JIT/NativeAOT changes were reverted, and s_handleHighBitSet plus its tagging/masking uses were removed. The remaining net PR diff is the handle-tag removal plus the existing test updates.

CopilotAI changed the title Preserve JIT handle bits in cross-target buildsRemove NativeAOT debug handle taggingJun 10, 2026
@MichalStrehovsky

Copy link
Copy Markdown
MemberAuthor

/azp run runtime-nativeaot-outerloop

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

CopilotAI review requested due to automatic review settings June 12, 2026 02:18
@MichalStrehovsky

Copy link
Copy Markdown
MemberAuthor

/azp run runtime-nativeaot-outerloop

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

CopilotAI 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.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.

@github-actions

Copy link
Copy Markdown
Contributor

🤖 Copilot Code Review — PR #129034

Note

This review was generated by Copilot.

Holistic Assessment

Motivation: The problem is real and well-documented in #122013. The debug-only s_handleHighBitSet poison (0x4000000000000000 on 64-bit hosts) was applied to NativeAOT JIT interface handles but never validated on return. In cross-target builds (64-bit host → 32-bit target), the JIT truncates nint-sized handles to int-sized target pointers, losing the high bit. This causes handle equality mismatches that lead to incorrect constant folding and wrong codegen. Multiple maintainers (jkotas, EgorBo, SingleAccretion) explicitly agreed in #122013 that deleting the poison is the right fix.

Approach: Correct and minimal — remove the unused debug poison at the source (VM side) rather than attempting to patch every JIT truncation site. The test workarounds are properly cleaned up in the same PR.

Summary: ✅ LGTM. This is a clean, focused, maintainer-agreed bug fix. The handle scheme (handleBase = 0x420000, handleMultiplier = 8) already guarantees handles fit within 32-bit range, so removing the tag makes the (int)handle cast in HandleToObject correct for cross-target builds. No blocking issues found.


Detailed Findings

✅ Correctness — Handle tag removal is safe

The s_handleHighBitSet tag was added in PR #41126 as a debug aid for cross-bitness compilation but was never validated — no assertion ever checked that the bit survived round-tripping through the JIT. Meanwhile, the tag actively caused bugs: when the JIT truncated handles to target pointer size, the poisoned bit was lost, causing two handles for the same object to compare as unequal. Removing the tag eliminates this entire class of bug without losing any diagnostic value.

The remaining handle arithmetic in HandleToObject ((int)handle - handleBase) / handleMultiplier) and ObjectToHandleUnchecked (handleMultiplier * _handleToObject.Count + handleBase) is correct — handleBase is 0x420000 and handle values grow linearly from there, well within 32-bit range.

✅ Test changes — Workarounds correctly removed

  • bug603649.cs: The [ActiveIssue("...#122013", ...IsNativeAot, ...Is32)] skip is correctly removed since the root cause is fixed.
  • interfacestatics.ilproj: The <TrimMode>partial</TrimMode> workaround (which hid the bug by preventing the compiler from constant-folding the problematic handles) is correctly removed. The comment on RequiresProcessIsolation is updated to reflect its actual purpose (needed for CLRTestTargetUnsupported).

💡 Observation — Remaining issue #122013 reference in codebase

There are no remaining references to issue #122013 as an ActiveIssue or workaround in the codebase (the grep match in CgStress1.cs is coincidental test data). The issue can be closed once this PR merges.

✅ Scope and focus — PR is well-scoped

The PR was iterated from a larger multi-site JIT fix down to this minimal VM-side-only change based on reviewer feedback. The current diff is the cleanest possible fix: 3 files, +1/−15 lines, all directly related to the bug.

Generated by Code Review for issue #129034 · ● 3.3M ·

@MichalStrehovsky

Copy link
Copy Markdown
MemberAuthor

/ba-g the crypto crash is #129339

@MichalStrehovsky
MichalStrehovsky merged commit ca65f7d into mainJun 15, 2026
135 of 144 checks passed
@MichalStrehovsky
MichalStrehovsky deleted the fix/jit-cross-target-handle-bits branch June 15, 2026 21:14
eiriktsarpalis pushed a commit that referenced this pull request Jul 15, 2026
Remove the debug-only high-bit tag from NativeAOT JIT interface handles.
This avoids rejecting valid handles on targets where the tag bit is not
preserved.
Also re-enables the affected regression coverage by removing the
NativeAOT workaround/skip from the existing tests.
Cc @EgorBo
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: MichalStrehovsky <13110571+MichalStrehovsky@users.noreply.github.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jul 18, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-CodeGen-coreclrCLR 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.

6 participants

@MichalStrehovsky@EgorBo@jkotas@JulieLeeMSFT