Calling Convention
This section is normative. A caller and callee conform when they independently apply the classification and assignment procedure below and therefore agree on every argument location, return location, and stack boundary. Register lists are summaries; the assignment procedure remains authoritative.
Stack and Calls
The stack grows downward. An architectural CALL pushes an 8-byte return address at [SP], and callers align SP before the call so callee entry observes 16-byte alignment. Between ABI call boundaries, a function may adjust SP while keeping it aligned to at least 8 bytes. Immediately before CALL, the stack pointer satisfies SP modulo 16 equals 8 before return address push. After CALL pushes the return address, it satisfies SP modulo 16 equals 0 at callee entry.
Each object observes its declared type alignment. In particular, relaxing the internal stack-pointer invariant preserves the 16-byte alignment of __int128 objects or other objects whose declared alignment is 16 bytes. Both the red-zone size and fixed outgoing-argument-area size are zero. A callee that dynamically realigns the stack restores the incoming stack pointer before returning.
The first stack argument begins at [SP+16] at callee entry. The eight bytes at [SP+8] are alignment padding. Argument values begin at [SP+16]. Each subsequent stack argument occupies the next 16-byte slot.
Near-call stack frame at callee entry
Frame Pointer
A function that maintains a frame pointer uses callee-saved R15. Other functions retain R15 as an ordinary callee-saved register.
C Unwind Frame
The Bedrock ELF ABI owns the language-independent DWARF register-number assignment. C unwind descriptions use that assignment with the following calling-convention frame rule. At call entry, the canonical frame address is entry SP+8; saved PC is at CFA minus 8 and CS has a same-value rule.
Argument Classification
Classification occurs after the C language has determined the effective type of the argument. A named argument to a prototyped function is placed in one of seven ABI classes:
GENERAL
Integer types up to 64 bits wide and ordinary object or function pointers. One general-register location is required.
GENERAL-PAIR
Signed and unsigned __int128. Two consecutive general registers beginning at an even-numbered register are required.
FLOAT
float, double, and long double. One floating-point register is required.
FLOAT-PAIR
float _Complex, double _Complex, and long double _Complex. Two consecutive floating-point registers are required with unit register alignment.
VECTOR
A direct scalable vector value. One vector register is required.
PREDICATE
A direct scalable predicate value. One predicate register is required.
INDIRECT
Every structure or union argument, regardless of size or member composition. The caller creates a 16-byte-aligned by-value copy and passes the address of that copy as one GENERAL value.
A compound scalable value is INDIRECT. Exhaustion of a scalable value’s direct register class also converts it to INDIRECT: the caller creates an object with the layout above and assigns its pointer through the GENERAL procedure. Each scalable value travels wholly in its register or through an indirect copy.
Signed GENERAL values narrower than 64 bits are sign-extended when assigned to a register. Unsigned values and _Bool are zero-extended. Plain char follows its signed ABI type. A stack slot contains the effective type’s object representation at its low address; bytes after that representation are unspecified.
Assignment Procedure
The caller shall apply the following procedure. The callee may rely on the result solely from assigned registers and value bytes.
-
If the result uses an sret buffer, assign its address to
R0and initialize the general-register cursor to 1. Otherwise initialize it to 0. Initialize the floating-point cursor to 0, the vector cursor to 0, and the predicate cursor to 0. -
Visit arguments in source order. GENERAL and INDIRECT arguments consume the register named by the current general cursor and then advance that cursor. FLOAT arguments analogously consume
F0throughF7using the independent floating-point cursor. VECTOR arguments consumeV0throughV7using the independent vector cursor, and PREDICATE arguments consumeP0throughP3using the independent predicate cursor. -
For a FLOAT-PAIR argument, use the current floating-point register for the real component and the next register for the imaginary component, then advance the cursor by two with unit register alignment. If fewer than two registers remain, place the complete complex value in one stack slot and mark the floating-point class exhausted.
-
For a GENERAL-PAIR argument, round the general cursor upward to an even register number. If that register and its successor both exist, place the low 64 bits in the even register, the high 64 bits in the odd register, and advance the cursor past the pair. A skipped odd register remains unused.
-
If the remaining registers of a class have insufficient capacity for an argument, place the complete argument in one stack slot and mark that register class exhausted. Later arguments of that class also use stack slots, while register holes remain unassigned. Each argument occupies one complete register location or one complete stack slot. For VECTOR or PREDICATE, instead convert the complete argument to INDIRECT as specified above and assign that pointer through GENERAL; exhausting a scalable register class leaves GENERAL independently available.
-
Unnamed variadic arguments and every argument of an unprototyped call are forced to stack slots and leave register cursors unchanged. Apply the default argument promotions before assigning those slots. Every scalable argument in either case is first converted to INDIRECT. Its GENERAL pointer is then subject to this forced-stack rule, so the variadic slot contains the pointer exclusively.
-
Assign stack slots to stack-bound arguments in source order beginning at
[SP+16]. Consequently, a caller that constructs the outgoing area by stores or pushes does so from the rightmost stack-bound argument toward the leftmost. The reserved register-home-slot count is zero.
The general, floating-point, vector, and predicate resources exhaust independently.
C Return Values
Scalar results use their natural register class. A __int128 result places its low word in R0 and high word in R1. A structure or union of at most 16 bytes is returned as raw object bytes: increasing object addresses map first to R0 and then to R1. Bytes beyond the object size and padding bytes with C-unspecified values are unspecified.
A complex result places its real component in F0 and its imaginary component in F1.
A single direct vector result uses V0; a single direct predicate result uses P0. A compound scalable result or any result containing multiple scalable values uses the ordinary indirect-result procedure, even if its fixed fields would otherwise fit in R0:R1.
For a larger aggregate, the caller allocates a 16-byte-aligned result buffer and passes its address in R0 before ordinary argument assignment. The callee stores the result in that buffer and returns the same address in R0.
C Return Register Quick Reference
| Result kinds | Register rule |
|---|---|
bool, i8, u8, i16, u16, i32, u32, i64, u64, pointer, function_pointer |
R0 |
i128, u128 |
R1:R0 |
f32, f64, long_double |
F0 |
complex_f32, complex_f64, complex_long_double |
real component in F0; imaginary component in F1 |
vector |
V0 |
predicate |
P0 |
aggregate |
up to 16 bytes in R1:R0; larger values use sret |
compound_scalable |
sret pointer in R0; result pointer in R0 |
Aggregate Passing
For every by-value aggregate argument, the caller allocates distinct storage, aligns its start to 16 bytes, initializes it with the argument value, and keeps it alive until the call returns. The GENERAL procedure assigns the address exclusively. If GENERAL registers are exhausted, the stack argument slot contains that address exclusively.
The copy gives the callee an addressable object with the declared aggregate type. Modifications to the private copy remain isolated from the caller’s source object. Baseline aggregates containing bit-fields and mixed integer/floating aggregates use the same INDIRECT rule; their member composition preserves the INDIRECT classification. An extension aggregate with packed or unaligned members is outside this ABI contract.
Variadic Calls
Fixed named arguments use the ordinary assignment procedure. Every unnamed argument is first subject to the C default argument promotions and is then placed in a 16-byte stack slot. In particular, float becomes double, and _Bool, char, short, and their signed or unsigned variants promote to int when int can represent all values of the source type. long double remains the Bedrock binary64 long double type. An aggregate slot contains the address of the caller-owned by-value copy described above.
The baseline va_list representation is one 64-bit pointer to the next unnamed argument slot. va_start initializes it to the first unnamed slot after any stack-assigned named arguments. va_arg reads the promoted representation from the low bytes of the current slot, or follows the copy address for an aggregate, and then advances the pointer by 16 bytes. va_copy copies the pointer value and va_end has a zero-instruction machine effect.
An unprototyped call applies the same promotions and places every argument on the stack. As required by C, the callee type must be compatible with the promoted types.