dotnet / dotnet/runtime

JIT (bug): dominator-based jump threading in redundantbranchopts drops a mandatory DivideByZeroException

Open
#133,982 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

`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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.