Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JIT: Field access of struct is not hoisted when bounds check is not eliminated #113107

Open
BoyBaykiller opened this issue Mar 4, 2025 · 3 comments
Assignees
Labels
area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI
Milestone

Comments

@BoyBaykiller
Copy link

https://godbolt.org/z/nr6zPYveW

First the case where bounds check was elided and codegen in the loop is perfect (loads are hoisted):

struct BuildData
{
    public float[] RightCostsAccum;
    public float Value;
}

void HoistedMov(BuildData buildData)
{
    for (int i = 0; i < buildData.RightCostsAccum.Length; i++)
    {
        buildData.RightCostsAccum[i] = buildData.Value;
    }
}
G_M27756_IG04:
       vmovss   dword ptr [rax], xmm0
       add      rax, 4
       dec      ecx
       jne      SHORT G_M27756_IG04

Now when bounds check is not elided like here:

void NotHoistedMov(BuildData buildData)
{
    for (int i = 0; i < 100; i++)
    {
        buildData.RightCostsAccum[i] = buildData.Value;
    }
}

we repeatedly load buildData.RightCostsAccum and buildData.Value inside the loop:

G_M15865_IG03:
       mov      rcx, gword ptr [rdx]               ; load buildData.RightCostsAccum
       cmp      eax, dword ptr [rcx+0x08]          ; load buildData.RightCostsAccum.Length
       jae      SHORT G_M15865_IG05
       vmovss   xmm0, dword ptr [rdx+0x08]         ; load buildData.Value
       vmovss   dword ptr [rcx+4*rax+0x10], xmm0
       inc      eax
       cmp      eax, 100
       jl       SHORT G_M15865_IG03

As a workarround you can manually load the fields outside the loop.
When the fields of the struct are passed in individual registers this is not an issue.

@dotnet-issue-labeler dotnet-issue-labeler bot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Mar 4, 2025
@dotnet-policy-service dotnet-policy-service bot added the untriaged New issue has not been triaged by the area owner label Mar 4, 2025
Copy link
Contributor

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

@EgorBo
Copy link
Member

EgorBo commented Mar 4, 2025

So minimal repro:

struct MyStruct
{
    public int[] Array;
    public long dummyFld;
}

void Test(MyStruct myStruct)
{
    for (int i = 0; i < 100; i++)
        myStruct.Array[i] = 1;
}

Loop Clonning does not kick in here if dummyFld is defined. Although, SysV ABI is fine with and without it.

cc @dotnet/jit-contrib

@EgorBo EgorBo added this to the Future milestone Mar 4, 2025
@EgorBo EgorBo removed the untriaged New issue has not been triaged by the area owner label Mar 4, 2025
@jakobbotsch
Copy link
Member

Note that with physical promotion + PGO (or DOTNET_JitSynthesizeCounts=1) this gets promoted and optimized just fine, so this seems like some suboptimal behavior in how implicit byrefs are handled by old promotion.

@jakobbotsch jakobbotsch self-assigned this Mar 4, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI
Projects
None yet
Development

No branches or pull requests

3 participants