This calling conventions cheat sheet provides a quick reference for parameter passing, register usage, and stack rules across the three major 64-bit calling conventions: System V AMD64 ABI (Linux/macOS x86-64), Windows x64, and AAPCS64 (ARM64/AArch64). Bookmark this page for instant answers during debugging and reverse engineering sessions.
For the complete deep dive with working assembly examples and Godbolt links, see our x86-64 & ARM64 Calling Conventions Demystified post. The official specifications are the System V AMD64 ABI, Microsoft x64 calling convention, and AAPCS64.
Integer/Pointer Argument Passing
The first several integer and pointer arguments are passed in registers. Additional arguments spill to the stack in right-to-left order. Note that Windows x64 uses only 4 register slots, while System V and AAPCS64 use 6 and 8 respectively.
| Argument | System V x86-64 | Windows x64 | ARM64 (AAPCS64) |
|---|---|---|---|
| 1st | RDI | RCX | X0 |
| 2nd | RSI | RDX | X1 |
| 3rd | RDX | R8 | X2 |
| 4th | RCX | R9 | X3 |
| 5th | R8 | Stack | X4 |
| 6th | R9 | Stack | X5 |
| 7th | Stack | Stack | X6 |
| 8th | Stack | Stack | X7 |
| 9th+ | Stack | Stack | Stack |
Floating-Point Argument Passing
Floating-point and SIMD arguments use a separate set of registers. On Windows x64, float arguments occupy the same positional slot as integers — if the 1st argument is a float, it goes in XMM0 (not RCX).
| Argument | System V x86-64 | Windows x64 | ARM64 (AAPCS64) |
|---|---|---|---|
| 1st float | XMM0 | XMM0 | D0 / S0 |
| 2nd float | XMM1 | XMM1 | D1 / S1 |
| 3rd float | XMM2 | XMM2 | D2 / S2 |
| 4th float | XMM3 | XMM3 | D3 / S3 |
| 5th–8th | XMM4–XMM7 | Stack | D4–D7 / S4–S7 |
| 9th+ | Stack | Stack | Stack |
Return Values
Return values follow similar patterns across conventions, but large struct returns differ significantly — ARM64 uses a dedicated indirect result register (X8) rather than stealing an argument register.
| Return Type | System V x86-64 | Windows x64 | ARM64 (AAPCS64) |
|---|---|---|---|
| Integer / Pointer | RAX | RAX | X0 |
| 2nd integer (struct) | RDX | — | X1 |
| Float / Double | XMM0 | XMM0 | D0 / S0 |
| Large struct | Hidden ptr in RDI | Hidden ptr in RCX | X8 (indirect result) |
Callee-Saved (Non-Volatile) Registers
These registers must be preserved across function calls. If a function uses them, it must save and restore their values (typically in the prologue/epilogue). This is critical knowledge for this calling conventions cheat sheet — getting callee-saved registers wrong causes subtle, hard-to-debug corruption.
| Category | System V x86-64 | Windows x64 | ARM64 (AAPCS64) |
|---|---|---|---|
| General-purpose | RBX, RBP, R12–R15 | RBX, RBP, RDI, RSI, R12–R15 | X19–X29 (FP) |
| Link register | — (return addr on stack) | — (return addr on stack) | X30 (LR)* |
| SIMD / Float | — (none) | XMM6–XMM15 | D8–D15 (lower 64 bits of V8–V15)** |
*X30 (LR) note: The link register is not formally in the same callee-saved class as X19–X28 in AAPCS64. It is preserved by non-leaf functions because bl clobbers it, so they must save/restore it in the prologue/epilogue. Leaf functions (which never call other functions) do not need to preserve X30.
**ARM64 SIMD note: For V8–V15, only the lower 64 bits (D8–D15) are callee-saved. The upper 64 bits of these registers are not preserved across calls. Registers V0–V7 and V16–V31 are entirely caller-saved (all 128 bits). If you use the full 128-bit Q8–Q15 registers, the upper halves will be lost across function calls.
Caller-Saved (Volatile) Registers
These registers may be destroyed by any function call. Save them before calling if you need their values afterward.
| Category | System V x86-64 | Windows x64 | ARM64 (AAPCS64) |
|---|---|---|---|
| General-purpose | RAX, RCX, RDX, RSI, RDI, R8–R11 | RAX, RCX, RDX, R8–R11 | X0–X18 (X16/X17 = IP0/IP1; X18 = platform register) |
| SIMD / Float | XMM0–XMM15 | XMM0–XMM5 | V0–V7, V16–V31 (all 128 bits); upper 64 bits of V8–V15 |
Stack & Alignment Rules
Stack alignment violations cause crashes on all three platforms. The shadow space requirement on Windows x64 is the single biggest source of bugs when porting Unix assembly to Windows.
| Rule | System V x86-64 | Windows x64 | ARM64 (AAPCS64) |
|---|---|---|---|
Alignment before call | 16-byte | 16-byte | 16-byte (SP must always be aligned) |
| Shadow space | None | 32 bytes (always required) | None |
| Red zone | 128 bytes below RSP | None | None |
| Stack direction | Grows down | Grows down | Grows down |
Side-by-Side Example: int add(int a, int b, int c)
The same function compiled under all three conventions shows the calling conventions cheat sheet rules in action. Notice how argument registers differ but the logic is identical.
System V x86-64 (Linux / macOS)
; int add(int a, int b, int c)
; a = edi, b = esi, c = edx
add:
mov eax, edi ; eax = a
add eax, esi ; eax += b
add eax, edx ; eax += c
ret ; return in eax
Windows x64
; int add(int a, int b, int c)
; a = ecx, b = edx, c = r8d
add:
mov eax, ecx ; eax = a
add eax, edx ; eax += b
add eax, r8d ; eax += c
ret ; return in eax
ARM64 (AAPCS64)
// int add(int a, int b, int c)
// a = w0, b = w1, c = w2
add:
add w0, w0, w1 // w0 = a + b
add w0, w0, w2 // w0 += c
ret // return in w0
Related Resources
Deep dive: x86-64 & ARM64 Calling Conventions Demystified — full tutorial with Godbolt examples, struct passing rules, and variadic function handling.
Also see: Stack Frames & Function Prologues | Systems Programming Glossary
Official specs: System V AMD64 ABI | Microsoft x64 ABI | ARM AAPCS64
