[llvm-dev] BUG: complete misunterstanding of the MS-ABI

Jameson Nash via llvm-dev llvm-dev at lists.llvm.org
Wed Sep 23 09:45:41 PDT 2020


> Mingw64 GCC returns i128 in XMM0

I believe this was changed some many years ago (or possibly never true). I
forget the exact version where this might have changed in mingw-w64, but I
filed this as a bug some years ago based on my observation of GCC's
behavior (https://bugs.llvm.org/show_bug.cgi?id=16168) but then I only
fixed part of it (the relevant parts of llvm but not of clang).

On Tue, Sep 8, 2020 at 4:57 PM Craig Topper via llvm-dev <
llvm-dev at lists.llvm.org> wrote:

> What if the source file was this instead. Did we follow the MSVC ABI now?
>
> #include <intrin.h>
>
> #ifndef __clang__
> typedef __m128i  __uint128_t;
> #else
> __attribute__((ms_abi))
> #endif
> __uint128_t foo(__uint128_t x) {
>   return x;
> }
>
> ~Craig
>
>
> On Tue, Sep 8, 2020 at 1:42 PM Craig Topper <craig.topper at gmail.com>
> wrote:
>
>> If you used the same struct with clang as you did with MSVC instead of
>> using a compiler defined type we would be compatible. Names starting with 2
>> underscores are reserved for compilers and libraries so user code shouldn't
>> be defining a struct with that name anyway. We work fine on code that
>> compiles with MSVC without detecting the compiler and giving different code
>> based on the compiler.
>>
>> How about I just disable __uint128_t as a keyword when compiling for
>> Windows?
>>
>> ~Craig
>>
>>
>> On Tue, Sep 8, 2020 at 1:34 PM Stefan Kanthak <stefan.kanthak at nexgo.de>
>> wrote:
>>
>>> "Craig Topper" <craig.topper at 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!
>>>
>>> Stefan
>>>
>>> > On Tue, Sep 8, 2020 at 1:08 PM Stefan Kanthak <stefan.kanthak at nexgo.de
>>> >
>>> > wrote:
>>> >
>>> >> "Craig Topper" <craig.topper at 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
>>> >>
>>> >> > On Tue, Sep 8, 2020 at 11:57 AM Reid Kleckner via llvm-dev <
>>> >> > llvm-dev at lists.llvm.org> wrote:
>>> >> >
>>> >> >> The code that you have has a large `#ifndef __clang__` block in
>>> it, and
>>> >> >> IMO that explains the ABI difference.
>>> >> >>
>>> >> >> As you note, MSVC does not have native support for 128 bit
>>> integers, so
>>> >> >> there is no reason for Clang to attempt to be ABI compatible.
>>> >> >>
>>> >> >> The __uint128_t arguments are passed indirectly because MSVC has a
>>> rule
>>> >> >> that requires arguments larger than 64 bits to be passed
>>> indirectly by
>>> >> >> address. I believe exceptions to that rule, such as vector
>>> arguments,
>>> >> are
>>> >> >> made on a case-by-case basis. No such rule exists for return
>>> values, so
>>> >> we
>>> >> >> get the usual i128 handling for x86 instead.
>>> >> >>
>>> >> >> ---
>>> >> >>
>>> >> >> I see that you are interested in using compiler-rt, presumably on
>>> >> Windows,
>>> >> >> and maybe from MSVC compiled objects. I think the proper fix for
>>> your
>>> >> use
>>> >> >> case is to change compiler-rt to use a union when passing these
>>> types by
>>> >> >> value.
>>> >> >>
>>> >> >> On Thu, Sep 3, 2020 at 5:35 PM Stefan Kanthak via llvm-dev <
>>> >> >> llvm-dev at lists.llvm.org> wrote:
>>> >> >>
>>> >> >>> Objects compiled for the MS-ABI don't conform to it!
>>> >> >>>
>>> >> >>> 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 at 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 at 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-dev at lists.llvm.org
>>> >> >>> https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev
>>> >> >>>
>>> >> >> _______________________________________________
>>> >> >> LLVM Developers mailing list
>>> >> >> llvm-dev at lists.llvm.org
>>> >> >> https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev
>>> >> >>
>>> >>
>>>
>> _______________________________________________
> LLVM Developers mailing list
> llvm-dev at lists.llvm.org
> https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20200923/9fe38ab5/attachment.html>


More information about the llvm-dev mailing list