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