Skip to content

JIT: GDV reorders null checks of 'this' with arguments #75607

@jakobbotsch

Description

@jakobbotsch

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:

  1. Evaluate this
  2. Evaluate arguments
  3. Null check this
  4. 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

Metadata

Metadata

Assignees

Labels

area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions