Skip to content

Registers

arm64

AArch64 Registers

ARM64 provides 31 general-purpose registers (X0–X30) with 32-bit W-aliases, plus SP, LR, PC, and PSTATE — each with a defined ABI role.

AArch64 (ARM64) defines 31 general-purpose registers plus several special- purpose registers. All GPRs are 64 bits wide; their 32-bit aliases begin with W.

General-purpose registers

64-bit32-bitABI role
X0W0Argument 1 / Return value
X1W1Argument 2 / Return value (2nd word)
X2W2Argument 3
X3W3Argument 4
X4W4Argument 5
X5W5Argument 6
X6W6Argument 7
X7W7Argument 8
X8W8Indirect result location (struct return pointer)
X9X15W9W15Temporary / caller-saved
X16W16Intra-procedure-call scratch (IP0)
X17W17Intra-procedure-call scratch (IP1)
X18W18Platform register (reserved on some OSes)
X19X28W19W28Callee-saved
X29W29Frame pointer (FP)
X30W30Link register (LR) — return address

Special-purpose registers

NameWidthDescription
SP64-bitStack pointer (not a GPR — cannot be used as general operand)
LR (X30)64-bitLink register — holds return address set by BL/BLR
PC64-bitProgram counter — not directly readable as a GPR (use ADR)
XZR / WZRZero register — reads as 0, writes discarded
PSTATEProcess State (condition flags N/Z/C/V, SP select, etc.)

W-register write behaviour

Writing a W register zero-extends into the full X register — identical to the x86-64 32-bit write rule. W0 = 0xFFX0 = 0x00000000000000FF.

Reverse-engineering notes

  • X30 (LR) is the return address on ARM64 — there is no hardware "return address on the stack" mechanism. Functions that call other functions must save LR to the stack in their prologue (STP X29, X30, [SP, #-16]!).
  • X16/X17 (IP0/IP1) are used by the linker for PLT/veneers — treating them as scratch in hand-written assembly is safe, but they may be clobbered between a BL and its target.
  • X8 as the indirect result register: when a function returns a large struct, the caller passes a pointer in X8 and the callee writes the struct there. Decompilers sometimes miss this, making the return type look void.
  • XZR appearing as a source is always zero; as a destination it discards the result — a common way to call an instruction purely for its flags effect (e.g. ADDS XZR, X0, X1 sets flags without storing the sum).