JIT: (bug) Loop hoisting moves a throwing division above a bounds check, so the wrong exception is raised
- Dominant language
- C#
- Stars
- 18.3k
- Forks
- 5.6k
- PR merge metrics
- PR metrics pending
Description
## Repro
```csharp
using System;
using System.Runtime.CompilerServices;
internal static class Program
{
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
private static int Test(int[] arr, int idx, int a, int b, int n)
{
int r = 0;
for (int i = 0; i < n; i++)
{
r += arr[idx];
r += a / b;
}
return r;
}
private static void Main()
{
try
{
Console.WriteLine(Test(new int[1], 5, 1, 0, 5));
}
catch (Exception e)
{
Console.WriteLine(e.GetType().Name);
}
}
}
```
`arr` has length 1 and `idx` is 5, so the very first statement of the first iteration must fault
before `a / b` is ever evaluated.
## Expected
```
IndexOutOfRangeException
```
## Actual
```
DivideByZeroException
```
Also reproduces on the **Release** runner with no environment variables set at all.
`DOTNET_JitDoLoopHoisting=0` and `DOTNET_JITMinOpts=1` both print the correct
`IndexOutOfRangeException`.
The invariant `arr[idx]` bounds check is not hoistable, but because it is loop-invariant it does
not close loop hoisting's exception-ordering barrier, so the invariant `a / b` is hoisted into the
preheader and executes ahead of it.
## Platform
Windows x64, .NET 11 (dotnet/runtime main @ a0b86b1e2a, Checked and Release builds). No special
env vars or CPU features required.
Contributor guide
Assessment
This issue has not been assessed yet.