dotnet / dotnet/runtime

JIT: (bug) Ordinary field load is hoisted out of a loop across a Volatile.Read

Open
#133,579 2 comments 0 reactions 1 assignee Claimed by @AndyAyersMS View on GitHub
area-CodeGen-coreclr
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.