Skip to content

Calling Convention Examples

The examples apply the normative assignment procedure above.

Independent Register Classes and Pair Alignment

struct pair { uint64_t lo, hi; };
uint64_t mixed(uint64_t count, double scale,
unsigned __int128 wide, struct pair pair, float bias);

The general cursor begins at 0 and the floating-point cursor begins at 0. count consumes R0; scale independently consumes F0. The GENERAL-PAIR argument rounds the general cursor from 1 to 2, so wide uses R3:R2 and R1 remains unused. The address of the caller-owned pair copy uses R4, and bias uses F1. The stack argument area has size zero.

Independent Scalable Register Classes

Assume vec and pred denote direct scalable vector and predicate extension types. For vec blend(vec left, pred mask, uint64_t count, vec right, pred select, double scale), left and right use V0 and V1; mask and select independently use P0 and P1; count uses R0; and scale uses F0. The direct vector result uses V0. Each class advances its own cursor exclusively.

sret Changes Ordinary Argument Assignment

struct triple { uint64_t x, y, z; };
struct triple build(uint64_t tag, signed __int128 wide,
double factor);

Because the result is 24 bytes, its buffer address occupies R0 and the general cursor begins at 1. Thus tag uses R1, wide uses R3:R2, and factor uses F0. The callee stores the result through the entry value of R0 and returns the same address in R0.

Pair Exhaustion of the General Class

void pressure(uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5, uint64_t a6,
unsigned __int128 wide, uint64_t tail);

Arguments a0 through a6 use R0 through R6. The next even pair would begin past R7, so wide is placed completely at [SP+16] and the GENERAL class becomes exhausted. tail therefore uses [SP+32], while R7 remains unassigned.

Variadic Promotions and Slot Traversal

int trace(const char *format, ...);
trace("example", (signed char)-1, (float)1.5, pair);

The named format argument uses R0. The signed character is promoted to int and stored at [SP+16]; the float is promoted to double and stored at [SP+32]. The caller creates the aggregate copy and stores its address at [SP+48]. A va_list initialized by va_start visits those three slots in that order by adding 16 after each access.

Assembly Conventions

The assembly examples illustrate the normative rules. They present the directives, comments, and labels that determine the shown control flow. The scalar leaf example presents its one-instruction counted loop with REP; the counter supplies N directly and zero annuls the body. If an implementation sequence conflicts with a normative rule, the rule controls.

The declarations below state the ABI interfaces used by the examples.

Scalar Leaf Function

int sum(int *p, int n);

On entry, p is in R0 and the sign-extended value of n is in R1. The function uses only volatile registers, preserves the entry SP, and returns the integer sum in R0.

2.45in

sum:
mov.qr0, r2
clr.qr0
maxs.qr0, r1
repr1, add.l [r2++], r0
ret

Scalar leaf function

Non-Leaf Preservation and Call Alignment

int mf_pipeline(int *tmp, int *dst, int *src, int n,
int ca, int cb, int cc);

The seven arguments occupy R0 through R6 in source order. The function keeps n, dst, tmp, and the first intermediate result live in nonvolatile R8 through R11. It therefore saves and restores both canonical register pairs.

mf_pipeline:
pushp4
pushp5
sub.q8, sp
mov.qr3, r8
mov.qr1, r9
mov.qr0, r10
mov.qr2, r1
mov.qr8, r2
mov.qr4, r3
mov.qr5, r4
mov.qr6, r5
callmf_fir_three
mov.qr0, r11
mov.qr9, r0
mov.qr10, r1
mov.qr8, r2
callmf_scale_store
add.lr11, r0
mov.lr0, r2
mov.qr9, r0
mov.qr8, r1
add.q8, sp
popp5
popp4
jmpmf_bias_sum

Non-leaf function with a tail transfer

Each PUSHP saves 16 bytes, so the pair saves leave the entry alignment unchanged. The additional 8-byte subtraction establishes SP mod 16 = 8 before each CALL; the pushed return address therefore gives the callee a 16-byte-aligned entry stack. The epilogue removes the padding and restores R10:R11 before R8:R9. The final JMP is a legal tail transfer because the frame and nonvolatile state have already been restored, the outgoing arguments have the ordinary ABI layout, and the target’s R0 result is also the result required by mf_pipeline.