JIT: (bug) Ordinary field load is hoisted out of a loop across a Volatile.Read
- Dominant language
- C#
- Stars
- 18.3k
- Forks
- 5.6k
- PR merge metrics
- PR metrics pending
Description
## Repro
```csharp
using System;
using System.Runtime.CompilerServices;
using System.Threading;
internal sealed class Box
{
public int Value;
public int Ready;
}
internal static class Program
{
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
private static int Reader(Box box)
{
int value = 0;
int seen = 0;
while (seen < 2)
{
value = box.Value;
if (Volatile.Read(ref box.Ready) != 0)
{
seen++;
}
}
return value;
}
private static void Main()
{
var box = new Box();
var reader = new Thread(() => Console.WriteLine($"reader saw {Reader(box)}"));
reader.Start();
Thread.Sleep(50);
box.Value = 42;
Volatile.Write(ref box.Ready, 1);
reader.Join();
}
}
```
Run with `DOTNET_TieredCompilation=0`.
## Expected
```
reader saw 42
```
The loop exits only after two iterations saw `Ready != 0`, so the returned `box.Value` load runs
after an acquire in an earlier iteration. `DOTNET_JitDoLoopHoisting=0` and `DOTNET_JITMinOpts=1`
both print this, 5 of 5 runs each.
## Actual
```
reader saw 0
```
Reproduced on 5 of 5 runs. The plain `box.Value` load is hoisted out of the loop even though it is
followed by a `Volatile.Read` acquire in the same loop body, so it is executed once before the
writer publishes and never re-read.
## Platform
Windows x64, .NET 11 (dotnet/runtime main @ a0b86b1e2a, Checked build). Requires
`DOTNET_TieredCompilation=0`; no special CPU features.
Contributor guide
Assessment
This issue has not been assessed yet.