dotnet / dotnet/runtime

JIT (bug): jump threading deletes a PHI and rewrites its uses with an SSA def that does not reach all remaining predecessors

Open
#133,981 1 comment 0 reactions 0 assignees View on GitHub
area-CodeGen-coreclr
Dominant language
C#
Stars
18.3k
Forks
5.6k
PR merge metrics
PR metrics pending

Description

`optJumpThreadCore` can delete a PHI in a join block and rewrite all of its uses with the SSA number from one ambiguous predecessor, even when another remaining predecessor reaches the block with a different reaching definition. Wrong code results.

### Minimal Repro

```csharp
using System;
using System.Runtime.CompilerServices;

class P
{
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
static int Test(bool q, bool r, int u, int v)
{
int z = 0, w = 0, m = 0, n = 0;
if (r) { m = v; n = 1; goto T; }
if (q) { z = u; w = 1; }
if (z != w) return -1;
T:
if (m == n) return 200;
return 100 + n;
}

static void Main() => Console.WriteLine(Test(false, false, 0, 0));
}
```

### Expected

```
200
```

`r == false`, so `m`/`n` keep their initial `0`; `q == false`, so `z == w == 0`; control falls into `T:` and `m == n` holds.

### Actual

```
101
```

`101` is `100 + n` with `n == 1`, the value `n` only has on the other (`r == true`) path. `V07` (`n`) is `zero-ref` in the final codegen and folded to the constant `1`.

### Notes

Correct (`200`) under `DOTNET_JITMinOpts=1` and on released .NET 10.0.12; deterministic on `main` under both default tiering and `DOTNET_TieredCompilation=0`.

Root cause: an earlier jump-thread in the same phase redirects an edge into the join block via `fgReplaceJumpTarget`, which updates the pred list but adds **no** `GT_PHI_ARG` for the new edge. `optGetThreadedSsaNumForBlock` / `optGetThreadedSsaNumForSuccessor` iterate only `phi->Uses()`, so that predecessor is invisible — even though the classification loop already printed "Could not map phi inputs from pred BB03" and added it to `m_ambiguousPreds`. They conclude all ambiguous preds agree, remove the PHI and rewrite the uses. Neither helper cross-checks the covered pred count against `jti.m_numAmbiguousPreds`. Introduced by #126907 (`6323aecb949b`).

Contributor guide

Open the contributing guide

Research direction

Start by reproducing the minimal C# program under default tiering and DOTNET_TieredCompilation=0, then inspect optJumpThreadCore, optGetThreadedSsaNumForBlock, and optGetThreadedSsaNumForSuccessor around the reported ambiguous predecessor handling. Done means the repro prints 200 on main without incorrectly rewriting the PHI uses or folding n to 1.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp
Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.