Passing structures as pointers, MSVC x64 style

The MS x64 ABI calling convention (http://msdn.microsoft.com/en-us/library/ms235286(VS.80).aspx) says:

    Any argument that doesn’t fit in 8 bytes, or is not 1, 2, 4, or 8 bytes, must be passed by reference.

Clang isn't doing that for us when passing our triple, x86_64-pc-win32-macho.

Here's a simple example program:

    struct Guid {
      unsigned int Data1;
      unsigned short Data2;
      unsigned short Data3;
      unsigned char Data4[8];
    };

    struct Guid g = { 0x8faf43c9, 0x85e9, 0x41f9, { 0xbe, 0x42, 0x99, 0x96, 0x4, 0xe0, 0x85, 0xb3 } };

    void v(int, ...);

    void byValue(void)
    {
      v(1, g);
    }

    void byReference(void)
    {
      v(1, &g);
    }

And the disassembled output:

    _byValue:
    0000000000000000 pushq %rbp
    0000000000000001 movq %rsp,%rbp
    0000000000000004 subq $0x30,%rsp
    0000000000000008 movq 0x00000008(%rip),%rax
    000000000000000f movq %rax,0xf8(%rbp)
    0000000000000013 movq 0x00000000(%rip),%rax
    000000000000001a movq %rax,0xf0(%rbp)
    000000000000001e movq 0xf0(%rbp),%rdx
    0000000000000022 movq 0xf8(%rbp),%r8
    0000000000000026 movl $0x00000001,%ecx
    000000000000002b callq 0x00000030
    0000000000000030 addq $0x30,%rsp
    0000000000000034 popq %rbp
    0000000000000035 ret

    _byReference:
    0000000000000040 pushq %rbp
    0000000000000041 movq %rsp,%rbp
    0000000000000044 subq $0x20,%rsp
    0000000000000048 movl $0x00000001,%ecx
    000000000000004d leaq 0x00000000(%rip),%rdx
    0000000000000054 callq 0x00000059
    0000000000000059 addq $0x20,%rsp
    000000000000005d popq %rbp
    000000000000005e ret

The same program's output from MSVC:

    0000000000000000 <byValue>:
       0: 48 56 rex.W push %rsi
       2: 57 push %rdi
       3: 48 83 ec 38 sub $0x38,%rsp
       7: 48 8d 44 24 20 lea 0x20(%rsp),%rax
       c: 48 8d 0d 00 00 00 00 lea 0x0(%rip),%rcx # 13 <byValue+0x13>
      13: 48 8b f8 mov %rax,%rdi
      16: 48 8b f1 mov %rcx,%rsi
      19: b9 10 00 00 00 mov $0x10,%ecx
      1e: f3 a4 rep movsb %ds:(%rsi),%es:(%rdi)
      20: 48 8d 54 24 20 lea 0x20(%rsp),%rdx
      25: b9 01 00 00 00 mov $0x1,%ecx
      2a: e8 00 00 00 00 callq 2f <byValue+0x2f>
      2f: 48 83 c4 38 add $0x38,%rsp
      33: 5f pop %rdi
      34: 5e pop %rsi

    0000000000000040 <byReference>:
      40: 48 83 ec 28 sub $0x28,%rsp
      44: 48 8d 15 00 00 00 00 lea 0x0(%rip),%rdx # 4b <byReference+0xb>
      4b: b9 01 00 00 00 mov $0x1,%ecx
      50: e8 00 00 00 00 callq 55 <byReference+0x15>
      55: 48 83 c4 28 add $0x28,%rsp
      59: c3 retq

As you can see, MSVC is making a copy of the structure and then passing a pointer to it in the byValue() call, whereas clang is actually stuffing the whole structure into the parameter transfer registers and spilling over onto the stack. Does clang support the MSVC style calling convention? Did I miss something when submitting our earlier patches?

Background information - we have a printf-like function in EFI that has a "%g" format string for printing GUIDs. It looks like there's some sloppy code around that calls that print function with GUIDs, rather than with pointers to GUIDs as the print function expects. On MSVC, because of this calling convention detail, it works fine - clang-built ROMs crash spectacularly.

Thanks for any suggestions or ideas! I'll happily try to come up with a patch if someone can point me in the right general direction in the source tree!

-- Carl

Carl,

See clang/lib/CodeGen/TargetInfo.cpp.

    // FIXME: mingw64-gcc emits 128-bit struct as i128
    if (Size <= 128 &&
        (Size & (Size - 1)) == 0)
      return ABIArgInfo::getDirect(llvm::IntegerType::get(getVMContext(),
                                                          Size));

It was my workaround, sorry.
Please check to tweak the clause (128 to 64) and lemme know.

...Takumi

Hi Takumi,

I think you hit the nail on the head with that one. Changing that constant to 64 seems to have done the trick. Here's the new disassembled output:

    _byValue:
    0000000000000000 pushq %rbp
    0000000000000001 movq %rsp,%rbp
    0000000000000004 subq $0x30,%rsp
    0000000000000008 leaq 0xf0(%rbp),%rdx
    000000000000000c movq 0x00000008(%rip),%rax
    0000000000000013 movq %rax,0xf8(%rbp)
    0000000000000017 movq 0x00000000(%rip),%rax
    000000000000001e movq %rax,0xf0(%rbp)
    0000000000000022 movl $0x00000001,%ecx
    0000000000000027 callq 0x0000002c
    000000000000002c addq $0x30,%rsp
    0000000000000030 popq %rbp
    0000000000000031 ret

%rdx is now getting the address of a copy of the struct! Patch attached; if it looks good to you I'll commit it.

-- Carl

msabi_patch.txt (773 Bytes)