JIT (bug): dominator-based jump threading in redundantbranchopts drops a mandatory DivideByZeroException
- Dominant language
- C#
- Stars
- 18.3k
- Forks
- 5.6k
- PR merge metrics
- PR metrics pending
Description
`optRedundantBranch` -> `optJumpThreadDom` can thread flow around a `BBJ_COND` block whose `JTRUE` tree has a `GTF_EXCEPT` side effect that the dominating compare does not cover, orphaning the exception-raising IR. A mandatory `DivideByZeroException` is silently dropped.
### Minimal Repro
```csharp
using System;
using System.Runtime.CompilerServices;
class Program
{
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
static int Test(int a, int b, int c)
{
int r;
if (a == b) { r = 1; } else { r = 2; }
if (a == b + (a / c - a / c)) { r += 10; } else { r += 20; }
return r;
}
static int Main()
{
try
{
Console.WriteLine("No exception, result=" + Test(5, 5, 0));
return 1;
}
catch (DivideByZeroException)
{
Console.WriteLine("DivideByZeroException");
return 100;
}
}
}
```
### Expected
```
DivideByZeroException
exit=100
```
This is what MinOpts produces.
### Actual
```
No exception, result=11
exit=1
```
The division is gone from the codegen entirely and `V02 arg2` (`c`) is `zero-ref`.
### Notes
Deterministic; reproduces with default tiering as well as `DOTNET_TieredCompilation=0`. Also reproduces on released .NET 10.0.12, so it is not a `main`-only regression.
Root cause: the two relops are matched on their **normal** VNs only (both sides go through `VNUnpackExc`), so the dominated relop's exception set can be a strict superset of the dominating one's. `optJumpThreadCheck` nonetheless grants a blanket exemption to a `GTF_EXCEPT`-only `JTRUE` tree, commenting that the exceptions "are covered by the exception effects in the dominating compare... because the VNs match". The non-threading branch-folding path in the same function gets this right by testing `vnStore->VNExcIsSubset(domCmpExcVN, treeExcVN)` first; the jump-threading path should do the same (or bail out).
Contributor guide
Research direction
Start by reproducing the minimal C# program and confirming the expected DivideByZeroException output. Then inspect optRedundantBranch, optJumpThreadDom, and optJumpThreadCheck, comparing the jump-threading path with the non-threading branch-folding path described in the issue. Done means the repro exits with 100 and the mandatory exception is preserved.
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
- 55/100