JumpThreading: don't give up on phis in non-ambiguous preds - #126711

Closed
EgorBo wants to merge 1 commit into
dotnet:mainfrom
EgorBo:rbo-fix
Closed

JumpThreading: don't give up on phis in non-ambiguous preds#126711
EgorBo wants to merge 1 commit into
dotnet:mainfrom
EgorBo:rbo-fix

Conversation

@EgorBo

Copy link
Copy Markdown
Member

testing a fix for #126703

CopilotAI review requested due to automatic review settings April 9, 2026 14:54
@github-actionsgithub-actionsBot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Apr 9, 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.

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

Updates CoreCLR JIT redundant-branch/jump-threading logic to avoid prematurely bailing out of PHI-based threading when PHI “problem” cases exist but all predecessors can be fully classified, addressing the missed optimization opportunity described in #126703.

Changes:

  • Extend optJumpThreadCheck with an optional out-parameter to report (instead of immediately rejecting) problematic PHIs during PHI-based jump threading.
  • In optJumpThreadPhi, defer the “problematic PHI” bail-out until after predecessor classification and only bail when ambiguous predecessors remain.
  • Update the JIT compiler interface in compiler.h to match the new optJumpThreadCheck signature.

Reviewed changes

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

FileDescription
src/coreclr/jit/redundantbranchopts.cppAdds deferred handling for problematic PHIs during PHI-based jump threading; bails only when ambiguous preds remain.
src/coreclr/jit/compiler.hUpdates optJumpThreadCheck declaration to include an optional out-parameter.

Comment on lines +1060 to +1062
// pHasGlobalPhiUse - optional output; when non-null, global phi uses
// are reported here instead of causing an immediate bail-out,
// letting the caller decide (e.g. allow threading when every

CopilotAIApr 9, 2026

Copy link

Choose a reason for hiding this comment

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

The pHasGlobalPhiUse out-parameter is also set for promoted-field PHIs (lvIsStructField), not just “global phi uses” as the name/docstring implies. Consider renaming this to something like pHasProblematicPhi (or splitting promoted-field vs global-use flags) and updating the comment so callers don’t misinterpret what it indicates.

Suggested change
// pHasGlobalPhiUse - optional output; when non-null, global phi uses
// are reported here instead of causing an immediate bail-out,
// letting the caller decide (e.g. allow threading when every
// pHasGlobalPhiUse - optional output; when non-null, PHI cases that are
// problematic for jump threading are reported here instead
// of causing an immediate bail-out. This currently includes
// global phi uses and promoted-field PHIs, letting the
// caller decide (e.g. allow threading when every

Copilot uses AI. Check for mistakes.
Comment on lines 1416 to 1422
// First see if block is eligible for threading.
// Defer the global-phi bail-out: when every predecessor is fully classified
// the block becomes unreachable after threading, so global phi uses are safe.
//
const bool check = optJumpThreadCheck(block, /* domBlock*/ nullptr);
bool hasGlobalPhiUse = false;
const bool check = optJumpThreadCheck(block, /* domBlock*/ nullptr, &hasGlobalPhiUse);
if (!check)

CopilotAIApr 9, 2026

Copy link

Choose a reason for hiding this comment

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

hasGlobalPhiUse is used as a general “problematic PHI present” indicator (it’s set for promoted-field PHIs too), but the surrounding comment and later bail-out message talk only about “global phi”. Please align the naming/messages with the actual semantics (e.g., mention promoted-field PHIs here as well, or rename the flag).

Copilot uses AI. Check for mistakes.
Comment on lines +1650 to +1662
// If there are ambiguous predecessors and a global phi use, we cannot
// safely thread because the block would remain reachable (from the
// ambiguous preds) with a broken PHI.
//
// When there are NO ambiguous preds, all preds are redirected,
// the block becomes unreachable, and global phi uses are harmless.
//
if (hasGlobalPhiUse && (jti.m_numAmbiguousPreds > 0))
{
JITDUMP(FMT_BB " has global phi and %u ambiguous pred(s); no phi-based threading\n",
block->bbNum, jti.m_numAmbiguousPreds);
return false;
}

CopilotAIApr 9, 2026

Copy link

Choose a reason for hiding this comment

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

This change is intended to unlock a specific codegen improvement (issue #126703), but there’s no regression test exercising the TrailingZeroCount(x) + sentinel compare pattern. Consider adding a JIT disasm-check test (see existing HasDisasmCheck tests under src/tests/JIT/opt/Compares/) that asserts the extra compare/cmove is eliminated for the idx != -1 case.

Copilot uses AI. Check for mistakes.
@AndyAyersMS

Copy link
Copy Markdown
Member

I think this may run into issues like #76636 and #76507, where downstream phases now make bad inferences.

@EgorBo

Copy link
Copy Markdown
MemberAuthor

I think this may run into issues like #76636 and #76507, where downstream phases now make bad inferences.

Sure, just wanted to estimate diffs, feel free to take over

@EgorBoEgorBo closed this Apr 9, 2026
@AndyAyersMS

Copy link
Copy Markdown
Member

We may be able to do something like the following:

  • if there is a phi def in the jump thread block with a small number of (global) uses, and no phi uses, then
    • search successor blocks to see if we can find all the global uses
    • if we can find them all, and the successors with uses are join-free, we can rewrite the uses during jump threading

This will catch the example here as there is just one global use, it's in one of the successors, and that successor is join-free.

If we can't account for local vs global uses accurately, we would also need to scan the jump thread block and count up its uses.

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.

3 participants

@EgorBo@AndyAyersMS
, '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" + '
Skip to content

JumpThreading: don't give up on phis in non-ambiguous preds - #126711

Closed
EgorBo wants to merge 1 commit into
dotnet:mainfrom
EgorBo:rbo-fix
Closed

JumpThreading: don't give up on phis in non-ambiguous preds#126711
EgorBo wants to merge 1 commit into
dotnet:mainfrom
EgorBo:rbo-fix

Conversation

@EgorBo

Copy link
Copy Markdown
Member

testing a fix for #126703

CopilotAI review requested due to automatic review settings April 9, 2026 14:54
@github-actionsgithub-actionsBot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Apr 9, 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.

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

Updates CoreCLR JIT redundant-branch/jump-threading logic to avoid prematurely bailing out of PHI-based threading when PHI “problem” cases exist but all predecessors can be fully classified, addressing the missed optimization opportunity described in #126703.

Changes:

  • Extend optJumpThreadCheck with an optional out-parameter to report (instead of immediately rejecting) problematic PHIs during PHI-based jump threading.
  • In optJumpThreadPhi, defer the “problematic PHI” bail-out until after predecessor classification and only bail when ambiguous predecessors remain.
  • Update the JIT compiler interface in compiler.h to match the new optJumpThreadCheck signature.

Reviewed changes

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

FileDescription
src/coreclr/jit/redundantbranchopts.cppAdds deferred handling for problematic PHIs during PHI-based jump threading; bails only when ambiguous preds remain.
src/coreclr/jit/compiler.hUpdates optJumpThreadCheck declaration to include an optional out-parameter.

Comment on lines +1060 to +1062
// pHasGlobalPhiUse - optional output; when non-null, global phi uses
// are reported here instead of causing an immediate bail-out,
// letting the caller decide (e.g. allow threading when every

CopilotAIApr 9, 2026

Copy link

Choose a reason for hiding this comment

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

The pHasGlobalPhiUse out-parameter is also set for promoted-field PHIs (lvIsStructField), not just “global phi uses” as the name/docstring implies. Consider renaming this to something like pHasProblematicPhi (or splitting promoted-field vs global-use flags) and updating the comment so callers don’t misinterpret what it indicates.

Suggested change
// pHasGlobalPhiUse - optional output; when non-null, global phi uses
// are reported here instead of causing an immediate bail-out,
// letting the caller decide (e.g. allow threading when every
// pHasGlobalPhiUse - optional output; when non-null, PHI cases that are
// problematic for jump threading are reported here instead
// of causing an immediate bail-out. This currently includes
// global phi uses and promoted-field PHIs, letting the
// caller decide (e.g. allow threading when every

Copilot uses AI. Check for mistakes.
Comment on lines 1416 to 1422
// First see if block is eligible for threading.
// Defer the global-phi bail-out: when every predecessor is fully classified
// the block becomes unreachable after threading, so global phi uses are safe.
//
const bool check = optJumpThreadCheck(block, /* domBlock*/ nullptr);
bool hasGlobalPhiUse = false;
const bool check = optJumpThreadCheck(block, /* domBlock*/ nullptr, &hasGlobalPhiUse);
if (!check)

CopilotAIApr 9, 2026

Copy link

Choose a reason for hiding this comment

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

hasGlobalPhiUse is used as a general “problematic PHI present” indicator (it’s set for promoted-field PHIs too), but the surrounding comment and later bail-out message talk only about “global phi”. Please align the naming/messages with the actual semantics (e.g., mention promoted-field PHIs here as well, or rename the flag).

Copilot uses AI. Check for mistakes.
Comment on lines +1650 to +1662
// If there are ambiguous predecessors and a global phi use, we cannot
// safely thread because the block would remain reachable (from the
// ambiguous preds) with a broken PHI.
//
// When there are NO ambiguous preds, all preds are redirected,
// the block becomes unreachable, and global phi uses are harmless.
//
if (hasGlobalPhiUse && (jti.m_numAmbiguousPreds > 0))
{
JITDUMP(FMT_BB " has global phi and %u ambiguous pred(s); no phi-based threading\n",
block->bbNum, jti.m_numAmbiguousPreds);
return false;
}

CopilotAIApr 9, 2026

Copy link

Choose a reason for hiding this comment

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

This change is intended to unlock a specific codegen improvement (issue #126703), but there’s no regression test exercising the TrailingZeroCount(x) + sentinel compare pattern. Consider adding a JIT disasm-check test (see existing HasDisasmCheck tests under src/tests/JIT/opt/Compares/) that asserts the extra compare/cmove is eliminated for the idx != -1 case.

Copilot uses AI. Check for mistakes.
@AndyAyersMS

Copy link
Copy Markdown
Member

I think this may run into issues like #76636 and #76507, where downstream phases now make bad inferences.

@EgorBo

Copy link
Copy Markdown
MemberAuthor

I think this may run into issues like #76636 and #76507, where downstream phases now make bad inferences.

Sure, just wanted to estimate diffs, feel free to take over

@EgorBoEgorBo closed this Apr 9, 2026
@AndyAyersMS

Copy link
Copy Markdown
Member

We may be able to do something like the following:

  • if there is a phi def in the jump thread block with a small number of (global) uses, and no phi uses, then
    • search successor blocks to see if we can find all the global uses
    • if we can find them all, and the successors with uses are join-free, we can rewrite the uses during jump threading

This will catch the example here as there is just one global use, it's in one of the successors, and that successor is join-free.

If we can't account for local vs global uses accurately, we would also need to scan the jump thread block and count up its uses.

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.

3 participants

@EgorBo@AndyAyersMS
, '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('^' + ".*" + '
Skip to content

JumpThreading: don't give up on phis in non-ambiguous preds - #126711

Closed
EgorBo wants to merge 1 commit into
dotnet:mainfrom
EgorBo:rbo-fix
Closed

JumpThreading: don't give up on phis in non-ambiguous preds#126711
EgorBo wants to merge 1 commit into
dotnet:mainfrom
EgorBo:rbo-fix

Conversation

@EgorBo

Copy link
Copy Markdown
Member

testing a fix for #126703

CopilotAI review requested due to automatic review settings April 9, 2026 14:54
@github-actionsgithub-actionsBot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Apr 9, 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.

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

Updates CoreCLR JIT redundant-branch/jump-threading logic to avoid prematurely bailing out of PHI-based threading when PHI “problem” cases exist but all predecessors can be fully classified, addressing the missed optimization opportunity described in #126703.

Changes:

  • Extend optJumpThreadCheck with an optional out-parameter to report (instead of immediately rejecting) problematic PHIs during PHI-based jump threading.
  • In optJumpThreadPhi, defer the “problematic PHI” bail-out until after predecessor classification and only bail when ambiguous predecessors remain.
  • Update the JIT compiler interface in compiler.h to match the new optJumpThreadCheck signature.

Reviewed changes

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

FileDescription
src/coreclr/jit/redundantbranchopts.cppAdds deferred handling for problematic PHIs during PHI-based jump threading; bails only when ambiguous preds remain.
src/coreclr/jit/compiler.hUpdates optJumpThreadCheck declaration to include an optional out-parameter.

Comment on lines +1060 to +1062
// pHasGlobalPhiUse - optional output; when non-null, global phi uses
// are reported here instead of causing an immediate bail-out,
// letting the caller decide (e.g. allow threading when every

CopilotAIApr 9, 2026

Copy link

Choose a reason for hiding this comment

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

The pHasGlobalPhiUse out-parameter is also set for promoted-field PHIs (lvIsStructField), not just “global phi uses” as the name/docstring implies. Consider renaming this to something like pHasProblematicPhi (or splitting promoted-field vs global-use flags) and updating the comment so callers don’t misinterpret what it indicates.

Suggested change
// pHasGlobalPhiUse - optional output; when non-null, global phi uses
// are reported here instead of causing an immediate bail-out,
// letting the caller decide (e.g. allow threading when every
// pHasGlobalPhiUse - optional output; when non-null, PHI cases that are
// problematic for jump threading are reported here instead
// of causing an immediate bail-out. This currently includes
// global phi uses and promoted-field PHIs, letting the
// caller decide (e.g. allow threading when every

Copilot uses AI. Check for mistakes.
Comment on lines 1416 to 1422
// First see if block is eligible for threading.
// Defer the global-phi bail-out: when every predecessor is fully classified
// the block becomes unreachable after threading, so global phi uses are safe.
//
const bool check = optJumpThreadCheck(block, /* domBlock*/ nullptr);
bool hasGlobalPhiUse = false;
const bool check = optJumpThreadCheck(block, /* domBlock*/ nullptr, &hasGlobalPhiUse);
if (!check)

CopilotAIApr 9, 2026

Copy link

Choose a reason for hiding this comment

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

hasGlobalPhiUse is used as a general “problematic PHI present” indicator (it’s set for promoted-field PHIs too), but the surrounding comment and later bail-out message talk only about “global phi”. Please align the naming/messages with the actual semantics (e.g., mention promoted-field PHIs here as well, or rename the flag).

Copilot uses AI. Check for mistakes.
Comment on lines +1650 to +1662
// If there are ambiguous predecessors and a global phi use, we cannot
// safely thread because the block would remain reachable (from the
// ambiguous preds) with a broken PHI.
//
// When there are NO ambiguous preds, all preds are redirected,
// the block becomes unreachable, and global phi uses are harmless.
//
if (hasGlobalPhiUse && (jti.m_numAmbiguousPreds > 0))
{
JITDUMP(FMT_BB " has global phi and %u ambiguous pred(s); no phi-based threading\n",
block->bbNum, jti.m_numAmbiguousPreds);
return false;
}

CopilotAIApr 9, 2026

Copy link

Choose a reason for hiding this comment

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

This change is intended to unlock a specific codegen improvement (issue #126703), but there’s no regression test exercising the TrailingZeroCount(x) + sentinel compare pattern. Consider adding a JIT disasm-check test (see existing HasDisasmCheck tests under src/tests/JIT/opt/Compares/) that asserts the extra compare/cmove is eliminated for the idx != -1 case.

Copilot uses AI. Check for mistakes.
@AndyAyersMS

Copy link
Copy Markdown
Member

I think this may run into issues like #76636 and #76507, where downstream phases now make bad inferences.

@EgorBo

Copy link
Copy Markdown
MemberAuthor

I think this may run into issues like #76636 and #76507, where downstream phases now make bad inferences.

Sure, just wanted to estimate diffs, feel free to take over

@EgorBoEgorBo closed this Apr 9, 2026
@AndyAyersMS

Copy link
Copy Markdown
Member

We may be able to do something like the following:

  • if there is a phi def in the jump thread block with a small number of (global) uses, and no phi uses, then
    • search successor blocks to see if we can find all the global uses
    • if we can find them all, and the successors with uses are join-free, we can rewrite the uses during jump threading

This will catch the example here as there is just one global use, it's in one of the successors, and that successor is join-free.

If we can't account for local vs global uses accurately, we would also need to scan the jump thread block and count up its uses.

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.

3 participants

@EgorBo@AndyAyersMS
, '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('^' + ".*" + '
Skip to content

JumpThreading: don't give up on phis in non-ambiguous preds - #126711

Closed
EgorBo wants to merge 1 commit into
dotnet:mainfrom
EgorBo:rbo-fix
Closed

JumpThreading: don't give up on phis in non-ambiguous preds#126711
EgorBo wants to merge 1 commit into
dotnet:mainfrom
EgorBo:rbo-fix

Conversation

@EgorBo

Copy link
Copy Markdown
Member

testing a fix for #126703

CopilotAI review requested due to automatic review settings April 9, 2026 14:54
@github-actionsgithub-actionsBot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Apr 9, 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.

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

Updates CoreCLR JIT redundant-branch/jump-threading logic to avoid prematurely bailing out of PHI-based threading when PHI “problem” cases exist but all predecessors can be fully classified, addressing the missed optimization opportunity described in #126703.

Changes:

  • Extend optJumpThreadCheck with an optional out-parameter to report (instead of immediately rejecting) problematic PHIs during PHI-based jump threading.
  • In optJumpThreadPhi, defer the “problematic PHI” bail-out until after predecessor classification and only bail when ambiguous predecessors remain.
  • Update the JIT compiler interface in compiler.h to match the new optJumpThreadCheck signature.

Reviewed changes

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

FileDescription
src/coreclr/jit/redundantbranchopts.cppAdds deferred handling for problematic PHIs during PHI-based jump threading; bails only when ambiguous preds remain.
src/coreclr/jit/compiler.hUpdates optJumpThreadCheck declaration to include an optional out-parameter.

Comment on lines +1060 to +1062
// pHasGlobalPhiUse - optional output; when non-null, global phi uses
// are reported here instead of causing an immediate bail-out,
// letting the caller decide (e.g. allow threading when every

CopilotAIApr 9, 2026

Copy link

Choose a reason for hiding this comment

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

The pHasGlobalPhiUse out-parameter is also set for promoted-field PHIs (lvIsStructField), not just “global phi uses” as the name/docstring implies. Consider renaming this to something like pHasProblematicPhi (or splitting promoted-field vs global-use flags) and updating the comment so callers don’t misinterpret what it indicates.

Suggested change
// pHasGlobalPhiUse - optional output; when non-null, global phi uses
// are reported here instead of causing an immediate bail-out,
// letting the caller decide (e.g. allow threading when every
// pHasGlobalPhiUse - optional output; when non-null, PHI cases that are
// problematic for jump threading are reported here instead
// of causing an immediate bail-out. This currently includes
// global phi uses and promoted-field PHIs, letting the
// caller decide (e.g. allow threading when every

Copilot uses AI. Check for mistakes.
Comment on lines 1416 to 1422
// First see if block is eligible for threading.
// Defer the global-phi bail-out: when every predecessor is fully classified
// the block becomes unreachable after threading, so global phi uses are safe.
//
const bool check = optJumpThreadCheck(block, /* domBlock*/ nullptr);
bool hasGlobalPhiUse = false;
const bool check = optJumpThreadCheck(block, /* domBlock*/ nullptr, &hasGlobalPhiUse);
if (!check)

CopilotAIApr 9, 2026

Copy link

Choose a reason for hiding this comment

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

hasGlobalPhiUse is used as a general “problematic PHI present” indicator (it’s set for promoted-field PHIs too), but the surrounding comment and later bail-out message talk only about “global phi”. Please align the naming/messages with the actual semantics (e.g., mention promoted-field PHIs here as well, or rename the flag).

Copilot uses AI. Check for mistakes.
Comment on lines +1650 to +1662
// If there are ambiguous predecessors and a global phi use, we cannot
// safely thread because the block would remain reachable (from the
// ambiguous preds) with a broken PHI.
//
// When there are NO ambiguous preds, all preds are redirected,
// the block becomes unreachable, and global phi uses are harmless.
//
if (hasGlobalPhiUse && (jti.m_numAmbiguousPreds > 0))
{
JITDUMP(FMT_BB " has global phi and %u ambiguous pred(s); no phi-based threading\n",
block->bbNum, jti.m_numAmbiguousPreds);
return false;
}

CopilotAIApr 9, 2026

Copy link

Choose a reason for hiding this comment

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

This change is intended to unlock a specific codegen improvement (issue #126703), but there’s no regression test exercising the TrailingZeroCount(x) + sentinel compare pattern. Consider adding a JIT disasm-check test (see existing HasDisasmCheck tests under src/tests/JIT/opt/Compares/) that asserts the extra compare/cmove is eliminated for the idx != -1 case.

Copilot uses AI. Check for mistakes.
@AndyAyersMS

Copy link
Copy Markdown
Member

I think this may run into issues like #76636 and #76507, where downstream phases now make bad inferences.

@EgorBo

Copy link
Copy Markdown
MemberAuthor

I think this may run into issues like #76636 and #76507, where downstream phases now make bad inferences.

Sure, just wanted to estimate diffs, feel free to take over

@EgorBoEgorBo closed this Apr 9, 2026
@AndyAyersMS

Copy link
Copy Markdown
Member

We may be able to do something like the following:

  • if there is a phi def in the jump thread block with a small number of (global) uses, and no phi uses, then
    • search successor blocks to see if we can find all the global uses
    • if we can find them all, and the successors with uses are join-free, we can rewrite the uses during jump threading

This will catch the example here as there is just one global use, it's in one of the successors, and that successor is join-free.

If we can't account for local vs global uses accurately, we would also need to scan the jump thread block and count up its uses.

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.

3 participants

@EgorBo@AndyAyersMS
, '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" + '
Skip to content

JumpThreading: don't give up on phis in non-ambiguous preds - #126711

Closed
EgorBo wants to merge 1 commit into
dotnet:mainfrom
EgorBo:rbo-fix
Closed

JumpThreading: don't give up on phis in non-ambiguous preds#126711
EgorBo wants to merge 1 commit into
dotnet:mainfrom
EgorBo:rbo-fix

Conversation

@EgorBo

Copy link
Copy Markdown
Member

testing a fix for #126703

CopilotAI review requested due to automatic review settings April 9, 2026 14:54
@github-actionsgithub-actionsBot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Apr 9, 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.

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

Updates CoreCLR JIT redundant-branch/jump-threading logic to avoid prematurely bailing out of PHI-based threading when PHI “problem” cases exist but all predecessors can be fully classified, addressing the missed optimization opportunity described in #126703.

Changes:

  • Extend optJumpThreadCheck with an optional out-parameter to report (instead of immediately rejecting) problematic PHIs during PHI-based jump threading.
  • In optJumpThreadPhi, defer the “problematic PHI” bail-out until after predecessor classification and only bail when ambiguous predecessors remain.
  • Update the JIT compiler interface in compiler.h to match the new optJumpThreadCheck signature.

Reviewed changes

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

FileDescription
src/coreclr/jit/redundantbranchopts.cppAdds deferred handling for problematic PHIs during PHI-based jump threading; bails only when ambiguous preds remain.
src/coreclr/jit/compiler.hUpdates optJumpThreadCheck declaration to include an optional out-parameter.

Comment on lines +1060 to +1062
// pHasGlobalPhiUse - optional output; when non-null, global phi uses
// are reported here instead of causing an immediate bail-out,
// letting the caller decide (e.g. allow threading when every

CopilotAIApr 9, 2026

Copy link

Choose a reason for hiding this comment

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

The pHasGlobalPhiUse out-parameter is also set for promoted-field PHIs (lvIsStructField), not just “global phi uses” as the name/docstring implies. Consider renaming this to something like pHasProblematicPhi (or splitting promoted-field vs global-use flags) and updating the comment so callers don’t misinterpret what it indicates.

Suggested change
// pHasGlobalPhiUse - optional output; when non-null, global phi uses
// are reported here instead of causing an immediate bail-out,
// letting the caller decide (e.g. allow threading when every
// pHasGlobalPhiUse - optional output; when non-null, PHI cases that are
// problematic for jump threading are reported here instead
// of causing an immediate bail-out. This currently includes
// global phi uses and promoted-field PHIs, letting the
// caller decide (e.g. allow threading when every

Copilot uses AI. Check for mistakes.
Comment on lines 1416 to 1422
// First see if block is eligible for threading.
// Defer the global-phi bail-out: when every predecessor is fully classified
// the block becomes unreachable after threading, so global phi uses are safe.
//
const bool check = optJumpThreadCheck(block, /* domBlock*/ nullptr);
bool hasGlobalPhiUse = false;
const bool check = optJumpThreadCheck(block, /* domBlock*/ nullptr, &hasGlobalPhiUse);
if (!check)

CopilotAIApr 9, 2026

Copy link

Choose a reason for hiding this comment

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

hasGlobalPhiUse is used as a general “problematic PHI present” indicator (it’s set for promoted-field PHIs too), but the surrounding comment and later bail-out message talk only about “global phi”. Please align the naming/messages with the actual semantics (e.g., mention promoted-field PHIs here as well, or rename the flag).

Copilot uses AI. Check for mistakes.
Comment on lines +1650 to +1662
// If there are ambiguous predecessors and a global phi use, we cannot
// safely thread because the block would remain reachable (from the
// ambiguous preds) with a broken PHI.
//
// When there are NO ambiguous preds, all preds are redirected,
// the block becomes unreachable, and global phi uses are harmless.
//
if (hasGlobalPhiUse && (jti.m_numAmbiguousPreds > 0))
{
JITDUMP(FMT_BB " has global phi and %u ambiguous pred(s); no phi-based threading\n",
block->bbNum, jti.m_numAmbiguousPreds);
return false;
}

CopilotAIApr 9, 2026

Copy link

Choose a reason for hiding this comment

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

This change is intended to unlock a specific codegen improvement (issue #126703), but there’s no regression test exercising the TrailingZeroCount(x) + sentinel compare pattern. Consider adding a JIT disasm-check test (see existing HasDisasmCheck tests under src/tests/JIT/opt/Compares/) that asserts the extra compare/cmove is eliminated for the idx != -1 case.

Copilot uses AI. Check for mistakes.
@AndyAyersMS

Copy link
Copy Markdown
Member

I think this may run into issues like #76636 and #76507, where downstream phases now make bad inferences.

@EgorBo

Copy link
Copy Markdown
MemberAuthor

I think this may run into issues like #76636 and #76507, where downstream phases now make bad inferences.

Sure, just wanted to estimate diffs, feel free to take over

@EgorBoEgorBo closed this Apr 9, 2026
@AndyAyersMS

Copy link
Copy Markdown
Member

We may be able to do something like the following:

  • if there is a phi def in the jump thread block with a small number of (global) uses, and no phi uses, then
    • search successor blocks to see if we can find all the global uses
    • if we can find them all, and the successors with uses are join-free, we can rewrite the uses during jump threading

This will catch the example here as there is just one global use, it's in one of the successors, and that successor is join-free.

If we can't account for local vs global uses accurately, we would also need to scan the jump thread block and count up its uses.

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.

3 participants

@EgorBo@AndyAyersMS
, '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('^' + ".*" + '
Skip to content

JumpThreading: don't give up on phis in non-ambiguous preds - #126711

Closed
EgorBo wants to merge 1 commit into
dotnet:mainfrom
EgorBo:rbo-fix
Closed

JumpThreading: don't give up on phis in non-ambiguous preds#126711
EgorBo wants to merge 1 commit into
dotnet:mainfrom
EgorBo:rbo-fix

Conversation

@EgorBo

Copy link
Copy Markdown
Member

testing a fix for #126703

CopilotAI review requested due to automatic review settings April 9, 2026 14:54
@github-actionsgithub-actionsBot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Apr 9, 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.

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

Updates CoreCLR JIT redundant-branch/jump-threading logic to avoid prematurely bailing out of PHI-based threading when PHI “problem” cases exist but all predecessors can be fully classified, addressing the missed optimization opportunity described in #126703.

Changes:

  • Extend optJumpThreadCheck with an optional out-parameter to report (instead of immediately rejecting) problematic PHIs during PHI-based jump threading.
  • In optJumpThreadPhi, defer the “problematic PHI” bail-out until after predecessor classification and only bail when ambiguous predecessors remain.
  • Update the JIT compiler interface in compiler.h to match the new optJumpThreadCheck signature.

Reviewed changes

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

FileDescription
src/coreclr/jit/redundantbranchopts.cppAdds deferred handling for problematic PHIs during PHI-based jump threading; bails only when ambiguous preds remain.
src/coreclr/jit/compiler.hUpdates optJumpThreadCheck declaration to include an optional out-parameter.

Comment on lines +1060 to +1062
// pHasGlobalPhiUse - optional output; when non-null, global phi uses
// are reported here instead of causing an immediate bail-out,
// letting the caller decide (e.g. allow threading when every

CopilotAIApr 9, 2026

Copy link

Choose a reason for hiding this comment

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

The pHasGlobalPhiUse out-parameter is also set for promoted-field PHIs (lvIsStructField), not just “global phi uses” as the name/docstring implies. Consider renaming this to something like pHasProblematicPhi (or splitting promoted-field vs global-use flags) and updating the comment so callers don’t misinterpret what it indicates.

Suggested change
// pHasGlobalPhiUse - optional output; when non-null, global phi uses
// are reported here instead of causing an immediate bail-out,
// letting the caller decide (e.g. allow threading when every
// pHasGlobalPhiUse - optional output; when non-null, PHI cases that are
// problematic for jump threading are reported here instead
// of causing an immediate bail-out. This currently includes
// global phi uses and promoted-field PHIs, letting the
// caller decide (e.g. allow threading when every

Copilot uses AI. Check for mistakes.
Comment on lines 1416 to 1422
// First see if block is eligible for threading.
// Defer the global-phi bail-out: when every predecessor is fully classified
// the block becomes unreachable after threading, so global phi uses are safe.
//
const bool check = optJumpThreadCheck(block, /* domBlock*/ nullptr);
bool hasGlobalPhiUse = false;
const bool check = optJumpThreadCheck(block, /* domBlock*/ nullptr, &hasGlobalPhiUse);
if (!check)

CopilotAIApr 9, 2026

Copy link

Choose a reason for hiding this comment

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

hasGlobalPhiUse is used as a general “problematic PHI present” indicator (it’s set for promoted-field PHIs too), but the surrounding comment and later bail-out message talk only about “global phi”. Please align the naming/messages with the actual semantics (e.g., mention promoted-field PHIs here as well, or rename the flag).

Copilot uses AI. Check for mistakes.
Comment on lines +1650 to +1662
// If there are ambiguous predecessors and a global phi use, we cannot
// safely thread because the block would remain reachable (from the
// ambiguous preds) with a broken PHI.
//
// When there are NO ambiguous preds, all preds are redirected,
// the block becomes unreachable, and global phi uses are harmless.
//
if (hasGlobalPhiUse && (jti.m_numAmbiguousPreds > 0))
{
JITDUMP(FMT_BB " has global phi and %u ambiguous pred(s); no phi-based threading\n",
block->bbNum, jti.m_numAmbiguousPreds);
return false;
}

CopilotAIApr 9, 2026

Copy link

Choose a reason for hiding this comment

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

This change is intended to unlock a specific codegen improvement (issue #126703), but there’s no regression test exercising the TrailingZeroCount(x) + sentinel compare pattern. Consider adding a JIT disasm-check test (see existing HasDisasmCheck tests under src/tests/JIT/opt/Compares/) that asserts the extra compare/cmove is eliminated for the idx != -1 case.

Copilot uses AI. Check for mistakes.
@AndyAyersMS

Copy link
Copy Markdown
Member

I think this may run into issues like #76636 and #76507, where downstream phases now make bad inferences.

@EgorBo

Copy link
Copy Markdown
MemberAuthor

I think this may run into issues like #76636 and #76507, where downstream phases now make bad inferences.

Sure, just wanted to estimate diffs, feel free to take over

@EgorBoEgorBo closed this Apr 9, 2026
@AndyAyersMS

Copy link
Copy Markdown
Member

We may be able to do something like the following:

  • if there is a phi def in the jump thread block with a small number of (global) uses, and no phi uses, then
    • search successor blocks to see if we can find all the global uses
    • if we can find them all, and the successors with uses are join-free, we can rewrite the uses during jump threading

This will catch the example here as there is just one global use, it's in one of the successors, and that successor is join-free.

If we can't account for local vs global uses accurately, we would also need to scan the jump thread block and count up its uses.

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.

3 participants

@EgorBo@AndyAyersMS
, '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); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

JumpThreading: don't give up on phis in non-ambiguous preds - #126711

Closed
EgorBo wants to merge 1 commit into
dotnet:mainfrom
EgorBo:rbo-fix
Closed

JumpThreading: don't give up on phis in non-ambiguous preds#126711
EgorBo wants to merge 1 commit into
dotnet:mainfrom
EgorBo:rbo-fix

Conversation

@EgorBo

Copy link
Copy Markdown
Member

testing a fix for #126703

CopilotAI review requested due to automatic review settings April 9, 2026 14:54
@github-actionsgithub-actionsBot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Apr 9, 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.

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

Updates CoreCLR JIT redundant-branch/jump-threading logic to avoid prematurely bailing out of PHI-based threading when PHI “problem” cases exist but all predecessors can be fully classified, addressing the missed optimization opportunity described in #126703.

Changes:

  • Extend optJumpThreadCheck with an optional out-parameter to report (instead of immediately rejecting) problematic PHIs during PHI-based jump threading.
  • In optJumpThreadPhi, defer the “problematic PHI” bail-out until after predecessor classification and only bail when ambiguous predecessors remain.
  • Update the JIT compiler interface in compiler.h to match the new optJumpThreadCheck signature.

Reviewed changes

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

FileDescription
src/coreclr/jit/redundantbranchopts.cppAdds deferred handling for problematic PHIs during PHI-based jump threading; bails only when ambiguous preds remain.
src/coreclr/jit/compiler.hUpdates optJumpThreadCheck declaration to include an optional out-parameter.

Comment on lines +1060 to +1062
// pHasGlobalPhiUse - optional output; when non-null, global phi uses
// are reported here instead of causing an immediate bail-out,
// letting the caller decide (e.g. allow threading when every

CopilotAIApr 9, 2026

Copy link

Choose a reason for hiding this comment

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

The pHasGlobalPhiUse out-parameter is also set for promoted-field PHIs (lvIsStructField), not just “global phi uses” as the name/docstring implies. Consider renaming this to something like pHasProblematicPhi (or splitting promoted-field vs global-use flags) and updating the comment so callers don’t misinterpret what it indicates.

Suggested change
// pHasGlobalPhiUse - optional output; when non-null, global phi uses
// are reported here instead of causing an immediate bail-out,
// letting the caller decide (e.g. allow threading when every
// pHasGlobalPhiUse - optional output; when non-null, PHI cases that are
// problematic for jump threading are reported here instead
// of causing an immediate bail-out. This currently includes
// global phi uses and promoted-field PHIs, letting the
// caller decide (e.g. allow threading when every

Copilot uses AI. Check for mistakes.
Comment on lines 1416 to 1422
// First see if block is eligible for threading.
// Defer the global-phi bail-out: when every predecessor is fully classified
// the block becomes unreachable after threading, so global phi uses are safe.
//
const bool check = optJumpThreadCheck(block, /* domBlock*/ nullptr);
bool hasGlobalPhiUse = false;
const bool check = optJumpThreadCheck(block, /* domBlock*/ nullptr, &hasGlobalPhiUse);
if (!check)

CopilotAIApr 9, 2026

Copy link

Choose a reason for hiding this comment

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

hasGlobalPhiUse is used as a general “problematic PHI present” indicator (it’s set for promoted-field PHIs too), but the surrounding comment and later bail-out message talk only about “global phi”. Please align the naming/messages with the actual semantics (e.g., mention promoted-field PHIs here as well, or rename the flag).

Copilot uses AI. Check for mistakes.
Comment on lines +1650 to +1662
// If there are ambiguous predecessors and a global phi use, we cannot
// safely thread because the block would remain reachable (from the
// ambiguous preds) with a broken PHI.
//
// When there are NO ambiguous preds, all preds are redirected,
// the block becomes unreachable, and global phi uses are harmless.
//
if (hasGlobalPhiUse && (jti.m_numAmbiguousPreds > 0))
{
JITDUMP(FMT_BB " has global phi and %u ambiguous pred(s); no phi-based threading\n",
block->bbNum, jti.m_numAmbiguousPreds);
return false;
}

CopilotAIApr 9, 2026

Copy link

Choose a reason for hiding this comment

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

This change is intended to unlock a specific codegen improvement (issue #126703), but there’s no regression test exercising the TrailingZeroCount(x) + sentinel compare pattern. Consider adding a JIT disasm-check test (see existing HasDisasmCheck tests under src/tests/JIT/opt/Compares/) that asserts the extra compare/cmove is eliminated for the idx != -1 case.

Copilot uses AI. Check for mistakes.
@AndyAyersMS

Copy link
Copy Markdown
Member

I think this may run into issues like #76636 and #76507, where downstream phases now make bad inferences.

@EgorBo

Copy link
Copy Markdown
MemberAuthor

I think this may run into issues like #76636 and #76507, where downstream phases now make bad inferences.

Sure, just wanted to estimate diffs, feel free to take over

@EgorBoEgorBo closed this Apr 9, 2026
@AndyAyersMS

Copy link
Copy Markdown
Member

We may be able to do something like the following:

  • if there is a phi def in the jump thread block with a small number of (global) uses, and no phi uses, then
    • search successor blocks to see if we can find all the global uses
    • if we can find them all, and the successors with uses are join-free, we can rewrite the uses during jump threading

This will catch the example here as there is just one global use, it's in one of the successors, and that successor is join-free.

If we can't account for local vs global uses accurately, we would also need to scan the jump thread block and count up its uses.

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.

3 participants

@EgorBo@AndyAyersMS
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

JumpThreading: don't give up on phis in non-ambiguous preds - #126711

Closed
EgorBo wants to merge 1 commit into
dotnet:mainfrom
EgorBo:rbo-fix
Closed

JumpThreading: don't give up on phis in non-ambiguous preds#126711
EgorBo wants to merge 1 commit into
dotnet:mainfrom
EgorBo:rbo-fix

Conversation

@EgorBo

Copy link
Copy Markdown
Member

testing a fix for #126703

CopilotAI review requested due to automatic review settings April 9, 2026 14:54
@github-actionsgithub-actionsBot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Apr 9, 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.

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

Updates CoreCLR JIT redundant-branch/jump-threading logic to avoid prematurely bailing out of PHI-based threading when PHI “problem” cases exist but all predecessors can be fully classified, addressing the missed optimization opportunity described in #126703.

Changes:

  • Extend optJumpThreadCheck with an optional out-parameter to report (instead of immediately rejecting) problematic PHIs during PHI-based jump threading.
  • In optJumpThreadPhi, defer the “problematic PHI” bail-out until after predecessor classification and only bail when ambiguous predecessors remain.
  • Update the JIT compiler interface in compiler.h to match the new optJumpThreadCheck signature.

Reviewed changes

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

FileDescription
src/coreclr/jit/redundantbranchopts.cppAdds deferred handling for problematic PHIs during PHI-based jump threading; bails only when ambiguous preds remain.
src/coreclr/jit/compiler.hUpdates optJumpThreadCheck declaration to include an optional out-parameter.

Comment on lines +1060 to +1062
// pHasGlobalPhiUse - optional output; when non-null, global phi uses
// are reported here instead of causing an immediate bail-out,
// letting the caller decide (e.g. allow threading when every

CopilotAIApr 9, 2026

Copy link

Choose a reason for hiding this comment

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

The pHasGlobalPhiUse out-parameter is also set for promoted-field PHIs (lvIsStructField), not just “global phi uses” as the name/docstring implies. Consider renaming this to something like pHasProblematicPhi (or splitting promoted-field vs global-use flags) and updating the comment so callers don’t misinterpret what it indicates.

Suggested change
// pHasGlobalPhiUse - optional output; when non-null, global phi uses
// are reported here instead of causing an immediate bail-out,
// letting the caller decide (e.g. allow threading when every
// pHasGlobalPhiUse - optional output; when non-null, PHI cases that are
// problematic for jump threading are reported here instead
// of causing an immediate bail-out. This currently includes
// global phi uses and promoted-field PHIs, letting the
// caller decide (e.g. allow threading when every

Copilot uses AI. Check for mistakes.
Comment on lines 1416 to 1422
// First see if block is eligible for threading.
// Defer the global-phi bail-out: when every predecessor is fully classified
// the block becomes unreachable after threading, so global phi uses are safe.
//
const bool check = optJumpThreadCheck(block, /* domBlock*/ nullptr);
bool hasGlobalPhiUse = false;
const bool check = optJumpThreadCheck(block, /* domBlock*/ nullptr, &hasGlobalPhiUse);
if (!check)

CopilotAIApr 9, 2026

Copy link

Choose a reason for hiding this comment

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

hasGlobalPhiUse is used as a general “problematic PHI present” indicator (it’s set for promoted-field PHIs too), but the surrounding comment and later bail-out message talk only about “global phi”. Please align the naming/messages with the actual semantics (e.g., mention promoted-field PHIs here as well, or rename the flag).

Copilot uses AI. Check for mistakes.
Comment on lines +1650 to +1662
// If there are ambiguous predecessors and a global phi use, we cannot
// safely thread because the block would remain reachable (from the
// ambiguous preds) with a broken PHI.
//
// When there are NO ambiguous preds, all preds are redirected,
// the block becomes unreachable, and global phi uses are harmless.
//
if (hasGlobalPhiUse && (jti.m_numAmbiguousPreds > 0))
{
JITDUMP(FMT_BB " has global phi and %u ambiguous pred(s); no phi-based threading\n",
block->bbNum, jti.m_numAmbiguousPreds);
return false;
}

CopilotAIApr 9, 2026

Copy link

Choose a reason for hiding this comment

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

This change is intended to unlock a specific codegen improvement (issue #126703), but there’s no regression test exercising the TrailingZeroCount(x) + sentinel compare pattern. Consider adding a JIT disasm-check test (see existing HasDisasmCheck tests under src/tests/JIT/opt/Compares/) that asserts the extra compare/cmove is eliminated for the idx != -1 case.

Copilot uses AI. Check for mistakes.
@AndyAyersMS

Copy link
Copy Markdown
Member

I think this may run into issues like #76636 and #76507, where downstream phases now make bad inferences.

@EgorBo

Copy link
Copy Markdown
MemberAuthor

I think this may run into issues like #76636 and #76507, where downstream phases now make bad inferences.

Sure, just wanted to estimate diffs, feel free to take over

@EgorBoEgorBo closed this Apr 9, 2026
@AndyAyersMS

Copy link
Copy Markdown
Member

We may be able to do something like the following:

  • if there is a phi def in the jump thread block with a small number of (global) uses, and no phi uses, then
    • search successor blocks to see if we can find all the global uses
    • if we can find them all, and the successors with uses are join-free, we can rewrite the uses during jump threading

This will catch the example here as there is just one global use, it's in one of the successors, and that successor is join-free.

If we can't account for local vs global uses accurately, we would also need to scan the jump thread block and count up its uses.

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.

3 participants

@EgorBo@AndyAyersMS