Data types beyond 64 bit MUST BE returned by the callee via the
hidden first argument allocated by the caller, NOT in XMM0!
Demo/proof: from this source
--- llvm-bug.c ---
#ifndef __clang__
typedef struct {
unsigned __int64 low;
unsigned __int64 high;
} __uint128_t;
#else
__attribute__((ms_abi))
#endif
__uint128_t __udivmodti4(__uint128_t dividend, __uint128_t divisor, __uint128_t *remainder) {
if (remainder != 0)
*remainder = divisor;
return dividend;
}
--- EOF ---
clang -c -O1 generates the following INCOMPATIBLE and WRONG code:
__udivmodti4 proc public
movaps xmm0, xmmword ptr [rcx]
test r8, r8
jz 0f
movaps xmm1, xmmword ptr [rdx]
movaps xmmword ptr [r8], xmm1
0: ret
__udivmodti4 endp
clang's misunderstanding of the MS-ABI can be clearly seen here:
- RCX holds the address of the return value, NOT the address
of the dividend;
- RDX holds the address of the dividend, NOT the address of
the divisor;
- R8 holds the address of the divisor, NOT the address of the
remainder;
- R9 holds the address of the remainder;
- aggregate data types are NOT returned in XMM0, but via the
hidden first argument addressed by RCX;
- the address of the hidden first argument is returned in RAX!
JFTR: an 128-bit integer data type is not supported by MS.
clang is also rather confused here: why is the return
value mapped to an XMM register, but not the arguments?
Microsoft's CL.EXE -c -Ox generates the following (of course)
CONFORMANT code:
__udivmodti4 proc public
; Line 10
test r9, r9
je SHORT $LN1@udivmodti4
; Line 11
mov rax, QWORD PTR [r8]
mov QWORD PTR [r9], rax
mov rax, QWORD PTR [r8+8]
mov QWORD PTR [r9+8], rax
$LN1@udivmodti4:
; Line 12
mov rax, QWORD PTR [rdx]
mov QWORD PTR [rcx], rax
mov rax, QWORD PTR [rdx+8]
mov QWORD PTR [rcx+8], rax
mov rax, rcx
; Line 13
ret 0
__udivmodti4 endp
NOT AMUSED
Stefan
_______________________________________________
LLVM Developers mailing list
llvm...@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev
"Craig Topper" <craig....@gmail.com> wrote:
> __uint128_t isn't getting the usual x86 handling.
Correct.
That's why I posted this bug, and referred the misleading/wrong statement
<https://blog.llvm.org/2018/03/clang-is-now-used-to-build-chrome-for.html>
| Clang is the first-ever open-source C++ compiler that's ABI-compatible
| with Microsoft Visual C++ (MSVC) - meaning you can build some parts of
| your program (for example, system libraries) with the MSVC compiler
| ("cl.exe"), other parts with Clang, and when linked together (either by
| MSVC's linker, "link.exe", or LLD, the LLVM project's linker - see below)
| the parts will form a working program.
> I think the usual handling for i128 return would be rdx:rax.
For the SysV-ABI, but not the MS-ABI, which uses ONLY rax, not the register
pair rdx:rax
> Instead I believe it's being coerced to v2i64 by this code in
> clang/lib/CodeGen/TargetInfo.cpp
>
> // Mingw64 GCC returns i128 in XMM0. Coerce to v2i64 to handle that.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> // Clang matches them for compatibility.
> return ABIArgInfo::getDirect(llvm::FixedVectorType::get(
> llvm::Type::getInt64Ty(getVMContext()), 2));
I underlined the culprit.
Now please remove that wrong statement cited above from the blog.
JFTR: for MSVC, __uint128_t (or however you name it) is a USER-DEFINED type,
so the caller has to pass a pointer to a hidden first argument in RCX,
which the callee needs to return in RAX
<https://msdn.microsoft.com/en-us/library/zthk2dkh.aspx>
| To return a user-defined type by value in RAX, it must have a length of 1, 2, 4,
| 8, 16, 32, or 64 bits. [.] Otherwise, the caller must allocate memory for the
| return value and pass a pointer to it as the first argument. The remaining
| arguments are then shifted one argument to the right. The same pointer must be
| returned by the callee in RAX.
So Reid Kleckner is wrong with his statement "no rule".
regards
Stefan
"Craig Topper" <craig....@gmail.com>
> __uint128_t is a "builtin type" like int or short or char or float. It is
> not a "user-defined type".
ARGH!
For MSVC it is a user-defined type. Follow the MS-ABI.
It's all about compatibility, which LLVM states, but fails to deliver.
> The user did not define the type, its part of the compiler.
It's irrelevant who defined it.
> Since MSVC does not have such a builtin type we are free to
> do whatever we want with it because the type can't exist in any code
> compiled with MSVC so we don't need to interoperate.
OUCH: every type you define, but MSVC doesn't know, is a user-defined
type for MSVC. Just follow the MS-ABI then.
OR REMOVE THE WRONG STATEMENT FROM THE BLOG!