The following example throws NRE without printing any output when tiered PGO is enabled. It should print "Should be printed" before throwing NRE.
using System;
using System.Runtime.CompilerServices;
using System.Threading;
public class Program
{
public static void Main()
{
for (int i = 0; i < 100; i++)
{
long sum = Foo(i => i * 42, true);
if (i > 30 && i < 40)
Thread.Sleep(100);
}
Foo(null, false);
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static long Foo(Func<long, long> f, bool silent)
{
long result = 0;
for (long i = 0; i < 100000; i++)
result += f(Test(i, silent));
return result;
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static long Test(long i, bool silent)
{
if (i == 0 && !silent)
Console.WriteLine("Should be printed");
return i;
}
}
The evaluation order is meant to be:
- Evaluate this
- Evaluate arguments
- Null check this
- Do call
With GDV, since the guard involves dereferencing 'this', step 3 happens too early. This happens for both vtable, interface and delegate GDV.
Tricky to solve unfortunately. We need to evaluate the guard after the arguments (hard to represent without lots of additional spilling) or add a null check to the guard (expensive). cc @AndyAyersMS @EgorBo
The following example throws NRE without printing any output when tiered PGO is enabled. It should print "Should be printed" before throwing NRE.
The evaluation order is meant to be:
With GDV, since the guard involves dereferencing 'this', step 3 happens too early. This happens for both vtable, interface and delegate GDV.
Tricky to solve unfortunately. We need to evaluate the guard after the arguments (hard to represent without lots of additional spilling) or add a null check to the guard (expensive). cc @AndyAyersMS @EgorBo