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

Craig Topper via llvm-dev llvm-dev at lists.llvm.org
Tue Sep 8 13:57:31 PDT 2020


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
>> >> >>
>> >>
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20200908/1fd91cd7/attachment-0001.html>


More information about the llvm-dev mailing list