4.8 GNAT with the LLVM Back End

This section outlines the usage of the GNAT compiler with the LLVM back end and highlights its key limitations. Certain GNAT versions, referred to as GNAT LLVM, include an alternative LLVM back end alongside the GCC back end, providing access to utilities that operate at the LLVM Intermediate Representation (IR) level. This also enhances safety by facilitating dissimilar redundancy through diverse code generation techniques, allowing for the creation of two distinct binaries from the same source code.

Although both GNAT LLVM and the GCC-based GNAT follow most ABI rules, there are some cases where there you may encounter an incompatibility between the two compilers. One such case for the 64-bit Intel X86 is a difference in parameter passing when a structure that consists of 64 bits is passed. The native LLVM handling (and hence that of GNAT LLVM) and clang disagree in this case. GCC follows clang. The formal ABI agrees with LLVM.

In any case, we don’t recommend you link code compiled with GNAT LLVM to code compiled by the GCC version of GNAT. This is a specific case of the general rule that you should compile all your Ada code with the same version of GNAT. Both gnatmake and gprbuild ensure this is done.

You may, however, run into this incompatibility if you pass such a record between C and Ada. In general, we recommend keeping the data passed between C and Ada as simple as practical.

GNAT LLVM currently provides limited support for debugging data. It provides full line number information for declarations and statements, but not sufficient debugging data to display all Ada data structures. GNAT LLVM outputs complete debugging data only for types with a direct equivalent in C, namely records without discriminants and constrained arrays whose dimensions are known at compile time. You will not be able to use gdb print commands to look at objects not of those types or to display components of those types. You can use low-level gdb commands that display memory to view such data provided you know how they’re laid out. Debugging information may also be limited for bitfields (fields whose size and position aren’t on byte boundaries)

In addition, debugging information may be confusing if you have out parameters to subprograms. If you have a procedure with only one out parameter, GNAT LLVM converts that to a function returning an object of that type. If you have multiple out parameters or have a function that also has an out parameter, GNAT LLVM converts that subprogram into a function that returns a record where each field is either an out parameter or the function return value, if any. The debug information reflects these transformations and not the original Ada source code.

GNAT LLVM doesn’t fully implement the -fcheck-stack switch. When you specify it, the code generated by GNAT LLVM tests for allocating overly-large items on the stack, but not all cases of stack overflow. For example, if you have a very deep recursion where each call only uses a small amount of stack and the total stack depth exceeds the amount of available stack, the program will be terminated by a signal instead of raising an Ada exception.

GNAT LLVM doesn’t support the Scalar_Storage_Order pragma except when it’s used to confirm the chosen storage order. This is because this facility is provided by GCC but not by LLVM.

GNAT LLVM doesn’t support Convention C++, which provides so-called ‘name mangling’ by encoding parameter and return datatypes into a function name.

We provide two options that you can use to build code with GNAT LLVM:

GNAT LLVM understands the same target triplets as the GCC version of GNAT.