System V ABI x64

This document describes concisely the subset of the x64 ABI as it is implemented in QBE. The subset can handle correctly arbitrary standard C-like structs containing float and integer types. Structs that have unaligned members are also supported through opaque types, see the IL description document for more information about them.

ABI Subset Implemented

Data classes of interest as defined by the ABI:


  1. The size of each argument gets rounded up to eightbytes. (It keeps the stack always 8 bytes aligned.)
  2. _Bool, char, short, int, long, long long and pointers are in the INTEGER class. In the context of QBE, it means that 'l' and 'w' are in the INTEGER class.
  3. float and double are in the SSE class. In the context of QBE, it means that 's' and 'd' are in the SSE class.
  4. If the size of an object is larger than two eightbytes or if contains unaligned fields, it has class MEMORY. In the context of QBE, those are big aggregate types and opaque types.
  5. Otherwise, recursively classify fields and determine the class of the two eightbytes using the classes of their components. If any is INTEGER the result is INTEGER, otherwise the result is SSE.



Alignment on the Stack

The ABI is unclear on the alignment requirement of the stack. What must be ensured is that, right before executing a 'call' instruction, the stack pointer %rsp is aligned on 16 bytes. On entry of the called function, the stack pointer is 8 modulo 16. Since most functions will have a prelude pushing %rbp, the frame pointer, upon entry of the body code of the function is also aligned on 16 bytes (== 0 mod 16).

Here is a diagram of the stack layout after a call from g() to f().

               |             |
               | g() locals  |
          ^    |             | \
          |    | stack arg 2 |  '
          |    |xxxxxxxxxxxxx|  | f()'s MEMORY
 growing  |    +-------------+  |  arguments
addresses |    | stack arg 1 |  ,
          |    |xxxxxxxxxxxxx| /
          |    +-------------+ -> 0 mod 16
          |    |   ret addr  |
               |  saved %rbp |
               +-------------+ -> f()'s %rbp
               | f() locals  |    0 mod 16
               |   ...       |
                               -> %rsp