[cfe-dev] [llvm-dev] RFC: Implementing the Swift calling convention in LLVM and Clang

Bruce Hoult via cfe-dev cfe-dev at lists.llvm.org
Wed Mar 2 03:41:11 PST 2016


I've done the "environment passed in a callee-save register" thing before,
just by using the C compiler's ability to reserve a register and map a
particular C global variable to it.

As you say, there is then no problem when you call code (let's call it
"library code") which doesn't expect it. The library code just
automatically saves and restores that register if it needs it.

However -- and probably you've thought of this -- there is a problem with
callbacks from library code that doesn't know about the environment
argument. The library might save the environment register, put something
else there, and then call back to your code that expects the environment to
be set up. Boom!

This callback might be a function that you passed explicitly as an
argument, a function pointed to by a global hook, or a virtual function of
an object you passed (derived from a base class that the library knows
about).

Any such callbacks need to either 1) not use the environment register, or
2) set up the environment register from somewhere else before using it or
calling other code that uses it, or 3) be a wrapper/thunk that sets up the
environment register before calling the real function.


On Wed, Mar 2, 2016 at 4:14 AM, John McCall via llvm-dev <
llvm-dev at lists.llvm.org> wrote:

> Hi, all.
>
> Swift uses a non-standard calling convention on its supported platforms.
> Implementing this calling convention requires support from LLVM and (to a
> lesser degree) Clang.  If necessary, we’re willing to keep that support in
> “private” branches of LLVM and Clang, but we feel it would be better to
> introduce it in trunk, both to (1) minimize the differences between our
> branches and trunk and (2) allow other language implementations to take
> advantage of that support.
>
> We don’t expect this to be particularly controversial, at least in the
> abstract, since LLVM already includes support for a number of variant,
> language-specific calling conventions.  Some of Swift's variations are more
> invasive than those existing conventions, however, so we want to make sure
> the community is on board before we start landing patches or sending them
> out for review.
>
> Here’s a brief technical summary of the convention:
>
> In general, the calling convention lowers onto an existing C calling
> convention; let’s call this the “intermediary convention”.  The
> intermediary convention is not necessarily the target platform’s standard C
> convention; for example, we intend to use a VFP convention on iOS ARM
> targets.  Aggregate arguments and results are translated to sequences of
> scalar types (possibly just an indirect argument/sret pointer) and, for the
> most part, passed and returned using the intermediary convention’s rules
> for a function with that signature.  For example, if struct A expands to
> the sequence [i32,float,i32], a function type like (A,Int64) -> Bool) would
> be lowered basically like the C function type bool(*)(int32_t, float,
> int32_t, int64_t).
>
> There are four general points of deviation from the intermediary
> convention:
>
>   - We sometimes want to return more values in registers than the
> convention normally does, and we want to be able to use both integer and
> floating-point registers.  For example, we want to return a value of struct
> A, above, purely in registers.  For the most part, I don’t think this is a
> problem to layer on to an existing IR convention: C frontends will
> generally use explicit sret arguments when the convention requires them,
> and so the Swift lowering will produce result types that don’t have legal
> interpretations as direct results under the C convention.  But we can use a
> different IR convention if it’s necessary to disambiguate Swift’s desired
> treatment from the target's normal attempts to retroactively match the C
> convention.
>
>   - We sometimes have both direct results and indirect results.  It would
> be nice to take advantage of the sret convention even in the presence of
> direct results on targets that do use a different (profitable) ABI
> treatment for it.  I don’t know how well-supported this is in LLVM.
>
>   - We want a special “context” treatment for a certain argument.  A
> pointer-sized value is passed in an integer register; the same value should
> be present in that register after the call.  In some cases, the caller may
> pass a context argument to a function that doesn’t expect one, and this
> should not trigger undefined behavior.  Both of these rules suggest that
> the context argument be passed in a register which is normally callee-save.
>
>   - We want a special “error” treatment for a certain argument/result.  A
> pointer-sized value is passed in an integer register; a different value may
> be present in that register after the call.  Much like the context
> treatment, the caller may use the error treatment with a function that
> doesn’t expect it; this should not trigger undefined behavior, and the
> existing value should be left in place.  Like the context treatment, this
> suggests that the error value be passed and returned in a register which is
> normally callee-save.
>
> Here’s a brief summary of the expected code impact for this.
>
> The Clang impact is relatively minor; it is focused on allowing the Swift
> runtime to define functions that use the convention.  It adds a new calling
> convention attribute, a few new parameter attributes constrained to that
> calling convention, and some relatively un-invasive call lowering code in
> IR generation.
>
> The LLVM impact is somewhat larger.
>
> Three things in the convention require a possible change to IR:
>
>   - Using sret together with a direct result may or may not “just work".
> I certainly don’t see a reason why it shouldn’t work in the middle-end.
> Obviously, some targets can’t support it, but we can avoid doing this on
> those targets.
>
>   - Opting in to the two argument treatments requires new parameter
> attributes.  We discussed using separate calling conventions;
> unfortunately, error and context arguments can appear either separately or
> together, so we’d really need several new conventions for all the valid
> combinations.  Furthermore, calling a context-free function with an ignored
> context argument could turn into a call to a function using a mismatched
> calling convention, which LLVM IR generally treats as undefined behavior.
> Also, it wasn’t obvious that just a calling convention would be sufficient
> for the error treatment; see the next bullet.
>
>   - The “error” treatment requires some way to (1) pass and receive the
> value in the caller and (2) receive and change the value in the callee.
> The best way we could think of to represent this was to pretend that the
> argument is actually passed indirectly; the value is “passed” by storing to
> the pointer and “received” by loading from it.  To simplify backend
> lowering, we require the argument to be a special kind of swifterror alloca
> that can only be loaded, stored, and passed as a swifterror argument; in
> the callee, swifterror arguments have similar restrictions.  This ends up
> being fairly invasive in the backend, unfortunately.
>
> The convention also requires a few changes to the targets that support the
> convention, to deal with the context and error treatments and to return
> more values in registers.
>
> Anyway, I would appreciate your thoughts.
>
> John.
> _______________________________________________
> LLVM Developers mailing list
> llvm-dev at lists.llvm.org
> http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20160302/6fc70dbc/attachment.html>


More information about the cfe-dev mailing list