Skip to content

JIT: bound dominating compares inspected by redundant branch opts - #136740

Merged
lewing merged 1 commit into
dotnet:mainfrom
lewing:lewing-jit-rbo-dominator-limit
Nov 2, 2026
Merged

lewing merged 1 commit into
dotnet:mainfrom
lewing:lewing-jit-rbo-dominator-limit

Conversation

@lewing

@lewing lewing commented Oct 30, 2026 •

Copy link
Copy Markdown
Member

optRedundantBranch walks up the dominator tree from each conditional branch. Its only bound, matchLimit = 4, counts dominating compares that are related to the current one (rii.canInfer). With a long chain of unrelated compares every relop walks all the way to the method entry, calling optRelopImpliesRelop → GetRelatedRelop → VNForFunc at each step, so the phase is O(n²) in the chain length.

This caps the total number of dominating compares inspected per relop at 128 (overridable in DEBUG via DOTNET_JitRboDominatorLimit).

Trigger

src/tests/JIT/Regression_3/DevDiv_255294: HugeMethod is 62,348 if (f) i++; on a static bool. Each f load goes through a static-base helper call that may run a cctor. Since #136106, VN treats those helpers as heap writes, so each load gets a distinct VN and no dominating compare is ever related.

Found because the new browser/wasm SuperPMI coreclr_tests collection (#136717) hit the 2-hour Helix timeout on this test.

Measurements

osx-arm64 host, release JITs, crossgen2 from current main, --parallelism:1, on generated methods with the DevDiv_255294 shape:

Method wasm before → after x64 before → after
62,335 branches 304.5 s → 5.8 s 219.6 s → 1.5 s
16,000 branches 11.7 s → 1.4 s 11.1 s → 0.9 s

SuperPMI asmdiffs (checked base vs. diff)

  • linux-x64 and linux-arm64 coreclr_tests.run, libraries.crossgen2, libraries.pmi (~2.5M contexts): zero diffs at limit 128. At limit 64 there were 2 regressions per arch (JitTest_cond32_NaN_il.Test:RunTests, JitTest_cond64_NaN_il.Test:RunTests, about +61%), so I picked 128.
  • browser/wasm libraries.crossgen2 (87,061 contexts): no real diffs. The run reports ~290 same-size binary diffs whose disassembly is identical. A base-vs-identical-base run shows the same noise (250).
  • Not covered locally: benchmarks/aspnet collections, osx-arm64 collections, and throughput (no Pin on macOS). CI's superpmi-diffs and tpdiff on this PR should cover those.

Possible follow-up (out of scope)

With the cap, wasm still takes ~5.8 s vs. 1.5 s for x64 at 62k branches, because some wasm-only phases also grow superlinearly ("Wasm control flow" 4.5 s, "Generate code" 2.9 s, "Set block weights" 0.5 s).

Note

This PR description was generated with GitHub Copilot.

optRedundantBranch walks up the dominator tree from each conditional
branch, but its only bound (matchLimit) counts dominating compares that
are related to the current one. With a long chain of unrelated compares
every relop walks to the method entry, which is quadratic in the chain
length.

Cap the total number of dominating compares inspected per relop at 128
(configurable in DEBUG via JitRboDominatorLimit).

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@github-actions github-actions Bot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Oct 30, 2026
@azure-pipelines

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

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

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

Copilot AI 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.

🔵 Needs a closer look

The global 128-compare JIT cap reduces optimization coverage, and the tradeoff requires maintainer evaluation of broader SuperPMI, tpdiff, and throughput results not validated in the local review.

No open issues 🎉

What changed in this PR

This PR bounds the JIT’s redundant branch comparison search to address excessive compilation time in methods with long chains of unrelated conditional branches.

Changes:

  • Caps inspected dominating comparisons at 128 per relational comparison, while preserving the existing related-match limit.
  • Adds the DEBUG-only DOTNET_JitRboDominatorLimit override and a diagnostic message when the cap is reached.
File Description
src/​coreclr/​jit/​redundantbranchopts.cpp Counts dominating comparisons and stops the search at the configured limit.
src/​coreclr/​jit/​jitconfigvalues.h Defines JitRboDominatorLimit with a default of 128.

Review effort: Balanced

@lewing
lewing merged commit 20c7898 into dotnet:main Nov 2, 2026
132 checks passed
@dotnet-milestone-bot dotnet-milestone-bot Bot added this to the 12.0-preview1 milestone Nov 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants