[llvm-dev] __attribute__((apple_abi)): targeting Apple/ARM64 ABI from Linux (and others)

Adrien Guinet via llvm-dev llvm-dev at lists.llvm.org
Fri Oct 9 06:02:03 PDT 2020


Hello Tim,

Thanks for the details you provided! Answers & comments below.

On 10/8/20 11:15 AM, Tim Northover wrote:
> On Thu, 8 Oct 2020 at 08:28, Adrien Guinet via llvm-dev
> <llvm-dev at lists.llvm.org> wrote:
>> * this supposes that the original targeted CC is Apple ARM64 AAPCS. In its current form,
>> there is no way to support for instance vector calls (see for instance
>> https://github.com/aguinet/llvm-project/commit/c4905ded3afb3182435df30e527955031cb0d098#diff-f124368bac3e5d7be20450aa83b166daR218)
> 
> I'm afraid I don't understand this point.

ARM64 defines two calling conventions: aapcs and aapcs-vfp
(https://developer.arm.com/documentation/dui0491/i/Compiler-specific-Features/--attribute----pcs--calling-convention-----function-attribute).

Using __attribute__((apple_abi)) in its current form would only allow to
target aapcs from a foreign OS, not aapcs-vfp.

>> * the fact that we can't target Apple's vector calls ABI shows that having one
>> CC_AArch64Apple (as CC_Win64 exists) calling convention might not be the right
>> implementation of this "apple_abi" attribute. Has someone better suggestions?
> 
> Needing two calling conventions seems really odd to me, unless it's
> for genuinely different ABI slices (arm64 vs arm64e or arm64_32 for
> example), and even there I'm not sure.

See above. The idea would be to also have, for instance,
CC_AArch64Apple_VFP (even if I'm really not found of this). It could
also end up as a non-supported case.

>> The fact that va_start/va_end works by using the Linux ABI from a function whose arguments
>> use the Apple ABI seems completely magical to me, so if someone knows why this work I
>> would also be interested!
> 
> It's a series of coincidences conspiring together, I think. Linux's
> varargs ABI doesn't change from the normal one, so functions have to
> store all GPRs and vector registers that might contain arguments (as
> well as where stack args start), and va_list describes where they were
> stored:
> 
> typedef struct {
>   void *stack;
>   void *gr_top;
>   void *vr_top;
>   int gr_offs;
>   int vr_offs;
> } va_list;
> 
> This is what you're getting with your "va_list" declaration. While the
> Darwin one is just a double pointer, but conceptually
> 
> typedef struct {
>   void *stack;
> } va_list;
> 
> because all anonymous args go on the stack there on Darwin.
> 
> That means when you call (Darwin's) va_start in your vprintf function

It's Linux's va_start no?

> it "correctly" initializes the first field of that struct, leaving the
> rest garbage. The gr_offs and vr_offs fields decide whether to use
> gr_top/vr_top or stack to actually get the argument, and in this case
> if gr_offs happens to be >= 0 it'll "correctly" use the stack to
> retrieve everything. I'm guessing that happens to be the case for
> simple programs (quite possibly the stack is still zero-initialized if
> this is a trivial test-case).

Okay got it. So the good way to lower this va_start would be to correctly set the rest of
the structure to zero if va_start is called from a function which has an Apple ABI (while
targetting Linux)? (actually answered below)

> You're also getting very lucky in that a Darwin varargs function
> changes how much of the stack each argument uses, bringing it in line
> with the normal AAPCS (otherwise the entire forwarding enterprise
> would be doomed and you'd have to implement significant chunks of
> vprintf to repack the arguments).

Indeed!

> So, at a high level what you'll *want* to do to correctly forward from
> Darwin to Linux is make sure that always happens: initialize gr_offs
> and vr_offs to 0 to begin with so only the stack is available (I'd
> also set the *_top fields to NULL for good measure).

Okay that seems to answer my question just above.

> Take the time to be grateful you're not trying to go the other way, too!

Yes :)

>> * For variadic functions (which are among the functions that have different ABIs), GCC and
>> Clang have __builtin_ms_va_list. My understanding is that we should have the Apple
>> equivalent, but I'm not sure to completely understand what's at stake here. Said
>> differently, is this builtin used to make sure we use the va_list type of the Apple ABI,
>> should the need arise to forward it to another function that uses the Apple ABI?
> 
> That, together with __builtin_ms_va_arg and __builtin_ms_va_start, are
> for if you have a Linux-side function that wants to make use of a
> va_list or anonymous args coming from Darwin code in a relatively
> agnostic way. I think what you're doing (here at least) is so
> intimately tied to bridging the two ABIs that using it would just be a
> fig-leaf.

Okay, thanks for the confirmation.

Regards


More information about the llvm-dev mailing list