Search code examples
c#structmarshalling.net-framework-version

Explicit struct layout with Guid


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");
        }
    }
}

Solution

  • Turns out this was a bug in RyuJIT and resolved with the following pull request.

    https://github.com/dotnet/coreclr/pull/17971