https://gcc.gnu.org/bugzilla/show_bug.cgi?id=78799

            Bug ID: 78799
           Summary: ms_abi handles return of __int128 by value incorrectly
           Product: gcc
           Version: 6.2.1
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: target
          Assignee: unassigned at gcc dot gnu.org
          Reporter: simonas+gcc.gnu.org at kazlauskas dot me
  Target Milestone: ---

I found out a case where gcc does not honour the Windows’ x64 calling
convention even if explicitly asked to use this ABI. Consider a function as
simple as this:

```c
__attribute__((ms_abi, noinline)) __int128 passthrough_a_c(__int128 a) {
   return a;
}
```

This assembly gets generated:

```asm
passthrough_a_c:
   movdqa (%rcx), %xmm0
   ret
```

The argument argument `a` is passed as expected: via reference. That is correct
behaviour as per strict reading of [this document][ms1]. Namely this excerpt:

> Integer arguments are passed in registers RCX, RDX, R8, and R9. Floating 
> point arguments are passed in XMM0L, XMM1L, XMM2L, and XMM3L. 16-byte 
> arguments are passed by reference.

Return value, however, gets put into the %xmm0 register and __int128 being an…
integer… this is alarming. Turns out, wrong too. A strict reading of [this
other documentation page][msdn2] has this to say:

> To be returned by value in RAX, user-defined types must have a length of 1, 
> 2, 4, 8, 16, 32, or 64 bits; [snip] Otherwise, the caller assumes the 
> responsibility of allocating memory and passing a pointer for the return 
> value as the first argument [NB: that would become the new RCX]. Subsequent 
> arguments are then shifted one argument to the right. The same pointer must 
> be returned by the callee in RAX.

So, instead of being compiled as it is currently, the function should be
compiled like this instead (intel syntax):

```asm
passthrough_a_c:
   mov         rax,rcx
   vmovups     xmm0,xmmword ptr [rdx]
   vmovdqa     xmmword ptr [rcx],xmm0
   ret
```

or, alternatively, this:

```asm
passthrough_a_c:
   movq %rcx, %rax
   movq (%rdx), %r9
   movq 8(%rdx), %r10
   movq %r9, (%rcx)
   movq %r10, 8(%rcx)
   ret
```

which is what gcc generates for a slight variation of the original case:

```c
struct i128 { uint64_t a; uint64_t b; };
__attribute__((ms_abi, noinline)) struct i128 passthrough_a_c(struct i128 a) {
   return a;
}
```

As far as my testing indicates, this is a bug from as far back as gcc 4.6.

[msdn1]: https://msdn.microsoft.com/en-us/library/ms235286.aspx
[msdn2]: https://msdn.microsoft.com/en-us/library/7572ztz4.aspx

Reply via email to