Skip to content

JIT: (bug) static struct field read hoisted above its class initializer (CopyFieldByField drops GTF_IND_INITCLASS) #134897

Description

@EgorBo

A promoted struct local copied from a beforefieldinit static struct field inside a loop gets its field loads hoisted into the preheader, ahead of the class-init helper, so the loads see zero-initialized static storage.

Minimal Repro

using System;
using System.Runtime.CompilerServices;

public struct S
{
    public int A;
    public int B;
    public S(int a, int b) { A = a; B = b; }
}

public class Holder
{
    public static S Value = new S(10, 20);   // beforefieldinit
}

public class Program
{
    public static void Main()
    {
        Console.WriteLine(Test(new[] { false, true, true }));
    }

    [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
    public static int Test(bool[] cond)
    {
        int sum = 0;
        for (int i = 0; i < cond.Length; i++)
        {
            if (cond[i])
                sum++;
            S s = Holder.Value;
            sum += s.A + s.B;
        }
        return sum;
    }
}

Expected

92

Actual

2

Regression?

Not a regression: wrong result on .NET 8, 9, 10, 11 RC2 and main.

Notes

MorphCopyBlockHelper::CopyFieldByField (morphblock.cpp) creates per-field INDs via getCopyIndirFlags, which drops the source's GTF_IND_INITCLASS (and volatile/unaligned flags). Loop hoisting then doesn't treat them as cctor-dependent.
DOTNET_JitNoHoist=1 gives 92. Not reproducible with an explicit static constructor.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    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