Skip to content

[release/10.0] JIT: fix widened IV init placed in unreachable block#130324

Open
EgorBo wants to merge 1 commit into
dotnet:release/10.0from
EgorBo:backport/pr-130190-to-release/10.0
Open

[release/10.0] JIT: fix widened IV init placed in unreachable block#130324
EgorBo wants to merge 1 commit into
dotnet:release/10.0from
EgorBo:backport/pr-130190-to-release/10.0

Conversation

@EgorBo

@EgorBo EgorBo commented Jul 7, 2026

Copy link
Copy Markdown
Member

Backport of #130190 to release/10.0

/cc @EgorBo

Customer Impact

  • Customer reported
  • Found internally

Bad codegen. IV widening could emit the widened IV initialization into a block that redundant branch opts' jump threading had made unreachable (stale SSA). A later flow-graph pass removes that dead block, so the widened IV is never initialized and the loop computes a wrong result (#130189).

Regression

  • Yes
  • No

Latent wrong-code bug introduced in .NET 9

Testing

Added regression test Runtime_130189 (fails without the fix, passes with it).

Risk

Low

Note

This PR description was generated with the help of GitHub Copilot.

Fixes dotnet#130189.

`optWidenPrimaryIV` initializes the widened IV in the block that holds
the narrow IV's reaching def when that block is not the preheader.
Redundant branch opts' dominator-based jump threading can bypass that
def block on the path that reaches the loop while its SSA def still
reaches the loop (stale SSA). The widened IV init was then emitted into
that (now dead) block, which a later flow-graph pass removes, so the
widened IV was never initialized and the loop produced a wrong result.

Fix: only use the reaching-def block for the init when the reaching def
is **not** a PHI. RBO's jump threading only leaves stale SSA at join
points (PHIs), so a PHI reaching def is exactly the case that can no
longer reach the loop; a non-PHI def always dominates its uses and
therefore reaches the loop. When the reaching def is a PHI we fall back
to the preheader, which always reaches the loop.

A DFS-reachability check (`m_dfsTree->Contains`) is not sufficient here:
a PHI's block can remain reachable via another path while the path that
reaches the loop was threaded away, so it would still be selected even
though it no longer reaches the loop. The PHI-def condition does not
have that hole.

Notes:
- The root cause is stale SSA left by RBO dominator-based jump
threading. Making RBO bail in that case is correct but pessimizes many
cases where the stale SSA is harmless, so this instead hardens the only
consumer that trips on it (IV widening).
- SPMI (x64) shows this only moves a handful of widened-IV inits into
the preheader: +85 bytes over ~676K contexts
(libraries.pmi/crossgen2/aspnet2/realworld), mostly neutral placement
churn, some improvements.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Jakob Botsch Nielsen <Jakob.botsch.nielsen@gmail.com>
(cherry picked from commit 5f44651)
Copilot AI review requested due to automatic review settings July 7, 2026 22:29
@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 Jul 7, 2026
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

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

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

Backport of a JIT wrong-code fix for IV widening where the widened IV initialization could be placed into a block that redundant branch opts (RBO) later makes unreachable due to stale SSA at PHIs, causing the widened IV to never be initialized. Adds a focused regression test for #130189.

Changes:

  • Harden optWidenPrimaryIV to only choose the narrow IV’s reaching-def block for widened-IV initialization when that reaching def is not a PHI; otherwise fall back to the loop preheader.
  • Add a new JIT regression test project Runtime_130189 that reproduces the miscompile and asserts the correct result.
  • Add the corresponding minimal csproj for building the regression test with optimizations enabled.

Reviewed changes

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

File Description
src/coreclr/jit/inductionvariableopts.cpp Avoid emitting widened-IV init into a potentially-dead PHI-def block by falling back to the always-reaching preheader when the reaching def is a PHI.
src/tests/JIT/Regression/JitBlue/Runtime_130189/Runtime_130189.cs New regression test asserting correct behavior for the miscompile scenario in #130189.
src/tests/JIT/Regression/JitBlue/Runtime_130189/Runtime_130189.csproj Adds the test project configuration (optimized build) for the new regression.

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