JIT (bug): jump threading deletes a PHI and rewrites its uses with an SSA def that does not reach all remaining predecessors
- 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
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