Repro
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
Actual
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 @ a0b86b1, Checked and Release builds). No special
env vars or CPU features required.
Repro
arrhas length 1 andidxis 5, so the very first statement of the first iteration must faultbefore
a / bis ever evaluated.Expected
Actual
Also reproduces on the Release runner with no environment variables set at all.
DOTNET_JitDoLoopHoisting=0andDOTNET_JITMinOpts=1both print the correctIndexOutOfRangeException.The invariant
arr[idx]bounds check is not hoistable, but because it is loop-invariant it doesnot close loop hoisting's exception-ordering barrier, so the invariant
a / bis hoisted into thepreheader and executes ahead of it.
Platform
Windows x64, .NET 11 (dotnet/runtime main @ a0b86b1, Checked and Release builds). No special
env vars or CPU features required.