What the calling convention promises
Calling a function and trusting your own registers afterwards requires an agreement about who owns what. The 64-bit procedure call standard defines that agreement, and it is short enough to state completely.
The rules
Arguments go in X0 through X7, in order. The eighth argument is the first one on the stack. A return value of sixteen bytes or fewer comes back in X0 and X1; a larger aggregate goes to memory, and X8 holds its address.
Everything from X0 to X18 is caller-saved. A callee may clobber any of it without saving anything, which means a caller who still needs a value across a call must put it somewhere safe itself.
Everything from X19 to X28 is callee-saved. A function that changes one has to save and restore it. X29 and X30 are also callee-saved, but for reasons of their own: they have fixed jobs.
X29 is the frame pointer, and X30 is the link
register. X30 is where bl and blr leave a return address, so a
function that makes any call of its own must spill it. A leaf function that
never calls anything never has to.
The groups in one place
| Registers | Class | Role |
|---|---|---|
| X0–X7 | caller-saved | Arguments, and small return values |
| X8 | caller-saved | Address of a large return value |
| X9–X15 | caller-saved | Scratch |
| X16–X18 | caller-saved | Position-independent code scratch |
| X19–X28 | callee-saved | Callee must preserve |
| X29 | callee-saved | Frame pointer |
| X30 | callee-saved | Link register |
Why X16 to X18 exist separately
X16, X17 and X18 are the intra-procedure-call scratch registers, and they are caller-saved for a specific reason: position-independent code needs somewhere to hold addresses while it resolves them. A dynamically linked call loads the target into X16 or X17, branches to a small stub, and the stub uses the other one to reach the linker's data. Neither survives the call, which is exactly what caller-saved means.
X18 is the odd one out. It is reserved by the platform on many systems: Darwin keeps a thread context pointer there, and Windows uses it for a thread environment block pointer. Portable code must not rely on it.
The cost of caller-saved
The convention pushes a real cost onto callers, and it is worth being clear about why. Because X0 through X7 are caller-saved, a caller that has computed something in X3 and needs it after a call must move it to X19 or spill it to the stack. Neither is free.
The reward is that a callee which returns a small value and touches nothing else has no bookkeeping at all: no prologue, no epilogue, no saved registers. Given that most functions are small and most calls are to small functions, that is the better trade, and it is why the register file looks the way it does rather than being uniformly callee-saved.