Skip to content

Incorrect NRE during operations on structures in method marked with AgressiveOptimization #92218

Closed

Description

Description

Testing our code on .NET 8 we encountered getting NRE where it is not supposed to be. I managed to write a minimal sample for the test (below). The described behavior is repeated on Preview.7, RC.1, RC.2 (haven't tried other versions, but it works fine on .NET 7)

Apparently, the problem is in the generated JIT code, because with optimization enabled, the method generates an explicit reference to address 0.

; Method MutableStructs.Program:TestMethod() (FullOpts)
G_M000_IG01:                ;; offset=0x0000
       sub      rsp, 56
       xor      eax, eax
       mov      qword ptr [rsp+0x30], rax
       mov      qword ptr [rsp+0x28], rax

G_M000_IG02:                ;; offset=0x0010
       mov      qword ptr [rsp+0x30], 420
       mov      qword ptr [rsp+0x28], 42
       mov      ecx, 3
       call     [System.TimeSpan:op_UnaryNegation(System.TimeSpan):System.TimeSpan]
       mov      rcx, qword ptr [rsp+0x30]
       cmp      rcx, qword ptr [rsp+0x28]
       jl       SHORT G_M000_IG04
       align    [7 bytes for IG03]

G_M000_IG03:                ;; offset=0x0040
       add      qword ptr [0x0000], rax ;; <- NRE
       mov      rcx, qword ptr [rsp+0x30]
       cmp      rcx, qword ptr [rsp+0x28]
       jge      SHORT G_M000_IG03

G_M000_IG04:                ;; offset=0x0054
       add      rsp, 56
       ret      
; Total bytes of code: 89

image

Reproduction Steps

using System.Runtime.CompilerServices;

namespace MutableStructs;

public struct MutableStruct
{
    private long _internalValue;

    public long InternalValue
    {
        get => Volatile.Read(ref _internalValue);
        private set => Volatile.Write(ref _internalValue, value);
    }

    public void Add(long value) => AddInternal(value);
    private void AddInternal(long value) => InternalValue += value;
    public MutableStruct(long value) => InternalValue = value;
}

internal static class Program
{
    private static void Main() => TestMethod();

    [MethodImpl(MethodImplOptions.AggressiveOptimization)]
    private static void TestMethod()
    {
        var test = new MutableStruct(420);
        var from = new MutableStruct(42);

        var wrapper = -new TimeSpan(3);

        while (test.InternalValue >= from.InternalValue)
        {
            test.Add(wrapper.Ticks);  // <- NRE
        }
    }
}

Expected behavior

The code is successfully executed

Actual behavior

Unhandled exception. System.NullReferenceException: Object reference not set to an instance of an object. at MutableStructs.Program.TestMethod()

Regression?

At least since .NET 7

Known Workarounds

The behavior described is quite sensitive to a combination of factors. Small changes will normalize the situation: you can remove Volatile, or make InternalValue an auto property, or not wrap Add/AddInternal calls, or use your own structure instead of TimeSpan - all of the above will remove the issue

Configuration

.NET 8 RC.2
Windows 10 Pro 22H2 x64
12th Gen Intel(R) Core(TM) i9-12900K

Other information

No response

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

Labels

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

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions