Description
openedon Sep 18, 2023
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
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