JIT: (bug) call argument evaluation reverses two volatile reads
- Dominant language
- C#
- Stars
- 18.3k
- Forks
- 5.6k
- PR merge metrics
- PR metrics pending
Description
## Repro
With a monotonically increasing value, the first `Volatile.Read` can never observe a larger value than the second.
```csharp
using System;
using System.Runtime.CompilerServices;
using System.Threading;
class Program {
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
static bool Test(ref int value) =>
Sink(Volatile.Read(ref value), Volatile.Read(ref value) + 1);
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
static bool Sink(int first, int secondPlusOne) => first > secondPlusOne - 1;
static void Main() {
int value = 0, stop = 0;
var publisher = new Thread(() => {
for (int i = 1; i <= 100_000_000 && Volatile.Read(ref stop) == 0; i++)
Volatile.Write(ref value, i);
});
publisher.Start();
int reversed = 0;
for (int i = 0; i < 2_000_000; i++)
if (Test(ref value))
reversed++;
Volatile.Write(ref stop, 1);
publisher.Join();
Console.WriteLine($"Reversed monotonic reads: {reversed}; expected 0");
}
}
```
## Expected
```
Reversed monotonic reads: 0; expected 0
```
## Actual
```
Reversed monotonic reads: 413; expected 0
```
The count is timing-dependent but was non-zero on every run (9, 199, 413).
The generated code for a two-reference variant shows the reversal deterministically
(`Sink(Volatile.Read(ref first), Volatile.Read(ref second) + 1)` under `DOTNET_JitDisasm`):
```asm
mov edx, dword ptr [rdx] ; read for the SECOND argument
inc edx
mov ecx, dword ptr [rcx] ; read for the FIRST argument
call Sink
```
## Platform
Windows x64, .NET 11.0.0-rc.1.26425.128.
Contributor guide
Assessment
This issue has not been assessed yet.