Question bank — Calling conventions and ABI
Two visual anchors before the questions — study these, then the Q&A refers back to them.

The stack frame above shows, from high address downward: incoming stack arguments (7th+), the return address that call pushed, saved callee-saved registers, local variables, and the red zone below rsp. Every stack-related question below points at a slot in this picture.

The register table above is the "who saves what" split — caller-saved (volatile) in orange, callee-saved (non-volatile) in violet — including the floating-point xmm registers, which the questions ask about.

The quadrant above compares SysV AMD64 (Linux/macOS) with Microsoft x64 (Windows) on the same CPU — same hardware, deliberately different rules.
True or false — justify
Every argument is always passed in a register.
The CPU hardware knows what a "function argument" is.
Two .o files that share a calling convention are guaranteed to link and run correctly together.
If a function never uses rbx, it still must push and pop it.
rbx leaves it alone — no save needed.Caller-saved registers are safer to use than callee-saved ones.
The call instruction is a primitive the hardware provides specifically for functions.
call is just push return_address; jmp target — a convenience combining two primitives. The function abstraction is still convention, not hardware.The return address lives in a register.
call pushes it onto the stack (the labelled slot in figure s01). Some ISAs like ARM64 (AAPCS) instead put it in a link register x30 — see x86-64 vs ARM64 AAPCS.Floating-point arguments share the same registers as integer arguments.
In SysV AMD64 the xmm floating-point registers are all callee-saved.
The ABI is a source-code concept like the API.
Spot the error
"To use rbx inside my function, I push rbp at entry and pop it before ret."
rbx you must save rbx itself — the ABI's promise is per register: the caller trusts each named callee-saved register individually. Saving rbp leaves the caller's rbx corrupted."7 arguments? I'll put the 7th in r10 since it's the next register after r9."
r10 is defined as caller-saved scratch, not an argument slot, so the callee would never read it. The 7th arg goes on the stack."The callee reads the 7th stack argument from [rsp]."
call pushed the return address first, so at entry rsp points at that, not the argument. The ABI lays the 7th argument just above the return address, giving ==[rsp+8]== (see the two adjacent slots in figure s01)."I subtracted 8 from rsp for one local, so alignment is fine."
rsp ≡ 0 (mod 16); call pushes 8 bytes so at entry rsp ≡ 8 (mod 16); subtracting 8 gives 0 (mod 16), but the next call you make pushes another 8 and re-misaligns it. You must size the frame so rsp ≡ 0 (mod 16) right before each call — see the WHY question below."My function returns two values, so I return one in rax and one in rbx."
rdx:rax precisely because both are caller-saved scratch — free for the callee to overwrite. rbx is callee-saved, i.e. reserved to hold the caller's state across the call, so returning through it would clobber a value the caller trusts."The caller sets up the arguments after executing call."
call, because call transfers control to the callee immediately — afterwards the callee is already reading rdi/rsi/….Why questions
Why split registers into caller-saved and callee-saved instead of one rule?
Why does SysV AMD64 pass exactly the first 6 args in registers?
Why must rsp be 16-byte aligned before a call?
movaps fault unless their memory operand is 16-byte aligned. (2) The compiler places aligned locals relative to rsp. (3) Since call pushes an 8-byte return address, the ABI requires rsp ≡ 0 (mod 16) before call so that inside the callee rsp ≡ 8 (mod 16), and the callee can then subtract a padded frame back to a multiple of 16. Guaranteeing this once at the boundary makes every aligned local free.Why are stack arguments pushed right-to-left?
Why does recursion need the stack rather than registers?
Why can't a system call use the same register order as an ordinary function call?
rax, args in rdi…, and r10 replaces rcx because the syscall instruction clobbers rcx). See System Calls and Syscall Interface.Why does C++ need name mangling on top of the C ABI?
Why does Windows use a different x86-64 convention than Linux at all?
.so won't link against a Windows .dll.Edge cases
A function with zero arguments — does the convention still apply?
rax if it has a result.A function that never returns (e.g. calls exit) — must it still preserve callee-saved registers?
A leaf function (calls nothing) with few locals — does it need a stack frame?
rsp (the shaded region at the bottom of figure s01) that a leaf function may use as scratch without moving rsp at all. It works only for leaves because any call would let the callee overwrite that region. This saves the sub rsp / add rsp pair entirely.Passing a struct larger than 16 bytes by value — where does it go?
MEMORY, so the caller copies it into the stack argument area (top of figure s01), not into registers, because it exceeds the two-register class limit. If a return value is that large, the caller instead passes a hidden pointer in rdi (called sret, "structure return") pointing to space it allocated, and the callee writes the result there — because there is no register wide enough to carry it back.A recursive call at depth 10000 — what convention property makes it work at all?
rsp, so activations never collide — until the stack runs out (stack overflow), the boundary case where the invariant meets a hard memory limit.Recall One-line self-test
Cover everything: can you state who saves which register and why the split exists? ::: Caller saves volatile registers it still needs; callee saves any non-volatile register it uses — the split minimizes total memory traffic by making each side save only what's actually at risk.