I'm trying to have an explicit struct layout where I am reinterpreting a byte array as guids. This works fine when running in 32 bit for both .net 4.7 and .net 4.7.2. But when you run it in 64 bit it works in .NET 4.7, but doesn't seem to work in .NET 4.7.2. Is this code invalid or could this be a bug in .NET?
[UPDATE]: Tested on a few more configurations. Works on 4.6.2 and 4.7, but doesn't work on 4.7.1 and 4.7.2.
[UPDATE 2]: Looking at the disassembly. In 64 bit that assign results in a no-op. So I think that's pretty clear that it's a bug in .net:
32bit disassembly:
TestStruct t = new TestStruct();
03010480 lea edi,[ebp-58h]
03010483 xorps xmm0,xmm0
03010486 movq mmword ptr [edi],xmm0
0301048A movq mmword ptr [edi+8],xmm0
0301048F movq mmword ptr [edi+10h],xmm0
03010494 movq mmword ptr [edi+18h],xmm0
t.Guid1 = Guid.NewGuid();
03010499 lea eax,[ebp-58h]
0301049C mov dword ptr [ebp-64h],eax
0301049F lea ecx,[ebp-74h]
030104A2 call 723628F0
030104A7 mov edi,dword ptr [ebp-64h]
030104AA lea esi,[ebp-74h]
030104AD movq xmm0,mmword ptr [esi]
030104B1 movq mmword ptr [edi],xmm0
030104B5 movq xmm0,mmword ptr [esi+8]
030104BA movq mmword ptr [edi+8],xmm0
t.Guid2 = t.Guid1;
030104BF lea edi,[ebp-58h]
030104C2 add edi,10h
030104C5 lea esi,[ebp-58h]
030104C8 movq xmm0,mmword ptr [esi]
030104CC movq mmword ptr [edi],xmm0
030104D0 movq xmm0,mmword ptr [esi+8]
030104D5 movq mmword ptr [edi+8],xmm0
64bit disassembly:
TestStruct t = new TestStruct();
00007FFB054604B2 lea rcx,[rbp+78h]
00007FFB054604B6 vxorpd xmm0,xmm0,xmm0
00007FFB054604BB vmovdqu xmmword ptr [rcx],xmm0
00007FFB054604C0 vmovdqu xmmword ptr [rcx+10h],xmm0
t.Guid1 = Guid.NewGuid();
00007FFB054604C6 lea rcx,[rbp+78h]
00007FFB054604CA mov qword ptr [rbp+68h],rcx
00007FFB054604CE lea rcx,[rbp+58h]
00007FFB054604D2 call 00007FFB639BE8C0
00007FFB054604D7 mov rax,qword ptr [rbp+68h]
00007FFB054604DB vmovdqu xmm0,xmmword ptr [rbp+58h]
00007FFB054604E1 vmovdqu xmmword ptr [rax],xmm0
t.Guid2 = t.Guid1;
00007FFB054604E6 nop
Code to reproduce:
[StructLayout(LayoutKind.Explicit, Size = SIZE)]
internal unsafe struct TestStruct
{
public const int SIZE = 32;
[FieldOffset(0)]
private fixed byte _data[SIZE];
[FieldOffset(0), MarshalAs(UnmanagedType.Struct, SizeConst = 16)]
public Guid Guid1;
[FieldOffset(16), MarshalAs(UnmanagedType.Struct, SizeConst = 16)]
public Guid Guid2;
}
internal class Program
{
private static void Main()
{
TestStruct t = new TestStruct();
t.Guid1 = Guid.NewGuid();
t.Guid2 = t.Guid1;
if (t.Guid1 != t.Guid2)
{
throw new InvalidOperationException("Guids aren't equal");
}
}
}
Turns out this was a bug in RyuJIT and resolved with the following pull request.