Open
Description
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.