Skip to content

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

Closed

Description

@EgorBo

Repro

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 @ a0b86b1, Checked build). Requires
DOTNET_TieredCompilation=0; no special CPU features.

Activity

  1. added this to the 12.0.0 milestone on Sep 10, 2026
  2. added
    area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI
    on Sep 10, 2026
  3. dotnet-policy-service commented on Sep 10, 2026

    @dotnet-policy-service
    Contributor

    Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch
    See info in area-owners.md if you want to be subscribed.

  4. MihuBot commented on Sep 10, 2026

    @MihuBot

    I'm a bot. Here is a possible related and/or duplicate issue (I may be wrong):

  5. self-assigned this
    on Sep 11, 2026
  6. added a commit that references this issue on Sep 15, 2026
    e173d24
  7. added a commit that references this issue on Sep 18, 2026
    721baf8
  8. EgorBo commented on Sep 18, 2026

    @EgorBo
    MemberAuthor

    Affected SDK versions:

    • 8.0.425
    • 9.0.318
    • 10.0.401
    • 11.0.100-rc.2.26467.112
  9. hamarb123 commented on Sep 18, 2026

    @hamarb123
    Contributor

    @AndyAyersMS would this be considered for backport to at least 10 and 11? I think I probably have some code that could run into this. And to me, this seems like a bug that is unlikely to be correctly root-caused by an AI, since it relies on it:

    • Causing a crash (or somehow otherwise noticing it) rather than just some silent corruption
    • AI not finding some other unrelated plausible cause, as the issue is likely hard to reproduce (unless you already know what or where the bug is)
    • AI being certain of its root cause being a bug in in the runtime's implementation of volatile, rather than it thinking "maybe i just got the memory model wrong and volatile is more like volatile in c++, etc."
    • Likely in the real world, the issue is spread across multiple methods & relies on inlining, making it harder again
  10. snakefoot commented on Oct 8, 2026

    @snakefoot
    Contributor

    @JulieLeeMSFT + @AndyAyersMS Possible to get backport to NET10 since also affected?

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

Metadata

Metadata

Assignees

Labels

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

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions