[llvm-dev] [RFC] Introducing an explicit calling convention

David Chisnall via llvm-dev
Wed, 16 Jan 2019 09:33:08 -0000

On 15/01/2019 21:56, Manuel Jacob via llvm-dev wrote:
>
> Sometimes people complain that ABI handling in the frontend is hard and
> that they have to duplicate (in their language’s frontend) what Clang
> does.  However, I don’t think that pushing more of this complexity into
> LLVM would be a good idea.  What I suggest is that there should be a
> helper library for ABI lowering (ideally that library should be used by
> Clang eventually).  What do you think?

I would love to see this and I was hoping that some of the Swift work
would lead to a refactored clang library that allowed you to generate IR
corresponding to C/C++ functions.

I think we have four use cases for front ends:

1. JIT'd language, minimal FFI.  This doesn't care what the calling
convention is, doesn't care that it's stable, does care that it's
efficient.  Except at well-defined external calls, is very happy for
LLVM to pick a different calling convention for every function.

2. Language that has only an LLVM implementation, but does support
static compilation and linking across different compiler versions.
Doesn't care what the calling convention is, but it has to be stable.
LLVM is free to pick a different calling convention for every type
signature, but the convention must be stable and doesn't have to match
the platform ABI.

3. Language that is adding a new LLVM implementation that is expected to
interoperate with an existing implementation.  There are a lot of these
out there and they all end up suffering.  Typically they either fork
LLVM and add a custom calling convention, or they try to upstream a
calling convention.  When they want to support a new architecture, then
need to coordinate their releases with LLVM to get their calling
conventions supported by the new back end.  They make live even harder
for the next person to want to use their calling convention by adding
more undocumented informal contracts between how the front end generates
LLVM IR and how the back end maps this to register / stack layout.

4. Language that wants to interoperate with C/C++.  These sometimes
embed a big chunk of clang.  This probably should be the preferred
solution, because it will invariably miss corner cases (packed structs,
for example) if it does anything simpler.  Unfortunately, clang isn't
really designed for this kind of embedding and the APIs that consumers
need change quite frequently.  Alternatively, they implement their own
partial ABI support.  For the core C ABI (ignoring GNU extensions and
C++), it is *much* easier to do this in a JIT that generates assembly
directly than one that targets LLVM IR.  ABI specs describe a fairly
simple (even amd64, though that's stretching it somewhat) set of rules
for mapping C constructs to registers / stack offsets.  These are well
defined and well documented.  The corresponding set of mappings from C
constructs to IR constructs are undocumented outside of the code and the
only way that you can reliably find them is to run clang and see what it
emits (I have done this - it's not fun).  If you need only basic C
interop, doing this for each platform that you support ought to be
simpler than embedding clang, but it isn't.

Ideally, we'd have both a clean set of APIs for other front ends to
embed clang (including a lot of what libclang exposes: parsing C
headers, walking types, and then generating interoperable code), built
on top of a clean way for the front end to either specify the exact ABI
requirements or leave them entirely up to later optimisations or the
back end.

David