ARM Assembly Cheat Sheet
AArch64 register conventions, common instructions, addressing modes, and a full function-call example for systems programming.
AArch64 Registers
The general-purpose and special register set (64-bit names, x0-x30).
- x0-x7- argument/result registers for function calls (AAPCS64)
- x8- indirect result register / Linux syscall number
- x9-x15- caller-saved temporary registers
- x16-x17 (IP0/IP1)- intra-procedure-call temp registers, used by linkers
- x18- platform register, reserved on some OSes (don't use)
- x19-x28- callee-saved registers, must be preserved across calls
- x29 (FP)- frame pointer
- x30 (LR)- link register, holds return address after bl
- sp- stack pointer, must stay 16-byte aligned
- wN- 32-bit view of register xN (w0 is low 32 bits of x0)
Data Processing
Arithmetic, logical, and move instructions.
mov x0, #5 // x0 = 5mov x1, x0 // x1 = x0add x2, x0, x1 // x2 = x0 + x1sub x3, x2, #1 // x3 = x2 - 1mul x4, x2, x3 // x4 = x2 * x3udiv x5, x4, x2 // x5 = x4 / x2 (unsigned)and x6, x0, #0xF // bitwise ANDorr x7, x0, x1 // bitwise OReor x8, x0, x1 // bitwise XORlsl x9, x0, #2 // logical shift left by 2 (x0 * 4)lsr x10, x0, #1 // logical shift right by 1cmp x0, x1 // compare (sets NZCV flags)
Load/Store & Addressing
Moving data between registers and memory.
ldr x0, [x1] // x0 = *(int64_t*)x1str x0, [x1] // *(int64_t*)x1 = x0ldr x0, [x1, #8] // load from x1 + 8 (offset)ldr x0, [x1, #8]! // pre-index: x1 += 8, then loadldr x0, [x1], #8 // post-index: load, then x1 += 8ldp x0, x1, [sp] // load pair (common for prologue/epilogue)stp x0, x1, [sp, #-16]! // store pair, pre-decrement sp by 16ldrb w0, [x1] // load byte, zero-extend into w0ldrsw x0, [x1] // load 32-bit signed, sign-extend to x0adr x0, label // PC-relative address of labeladrp x0, label // page address (paired with add for full addr)
Branches & Function Calls
Conditional branches and the call/return pattern.
cmp x0, x1b.eq equal_label // branch if equalb.lt less_label // branch if less than (signed)b.ne not_equal_label // branch if not equalb loop_start // unconditional branchbl my_function // branch and link: x30 = return addr, jumpret // return: jumps to address in x30 (LR)my_function: stp x29, x30, [sp, #-16]! // save frame pointer + link register mov x29, sp // set up frame pointer // ... body, x0-x7 hold args, x0 holds return value ... ldp x29, x30, [sp], #16 // restore ret
Linux Syscall Convention
How to invoke a Linux syscall directly on AArch64.
- x8- syscall number goes here
- x0-x5- syscall arguments, in order
- svc #0- the instruction that triggers the syscall
- x0 (return)- syscall return value (or negative errno)
- exit example- mov x8,#93; mov x0,#0; svc #0
NEON / SIMD Vector Instructions
Vector registers v0-v31 overlay the FP register file for parallel data processing.
// NEON vector registers overlay the FP regs: v0-v31, viewed as 8b/16b/4h/8h/2s/4s/1d/2dld1 {v0.4s}, [x0] // load four 32-bit floats from [x0] into v0ld1 {v1.4s}, [x1]fadd v2.4s, v0.4s, v1.4s // element-wise add, 4 lanes at oncefmul v3.4s, v0.4s, v1.4s // element-wise multiplyst1 {v2.4s}, [x2] // store the result vector// Multiply-accumulate: v4 += v0 * v1, per lanefmla v4.4s, v0.4s, v1.4s// Horizontal reduce: sum all 4 lanes of v0 into scalar s0faddp v5.4s, v0.4s, v0.4sfaddp s0, v5.2s// Integer SIMD: add 16 bytes at onceadd v6.16b, v0.16b, v1.16b
Atomics & Memory Barriers
Exclusive-access loops, ARMv8.1 LSE atomics, and the barrier instructions that order them.
// Classic load-linked/store-conditional loop (works on all ARMv8-A)retry: ldxr w0, [x1] // exclusive load add w0, w0, #1 // increment stxr w2, w0, [x1] // exclusive store; w2 = 0 on success cbnz w2, retry // retry if another core interfered// LSE atomics (ARMv8.1+) do the same in one instruction, no retry loopldadd w0, w2, [x1] // *x1 += w0 (atomic add), old value returned in w2swp w0, w2, [x1] // atomic swapcas w0, w3, [x1] // compare-and-swap: if [x1]==w0, store w3// Memory barriersdmb ish // data memory barrier, inner-shareable: order loads/storesdsb sy // data sync barrier: wait for all memory ops to completeisb // instruction sync barrier: flush pipeline (e.g. after self-modifying code)
Conditional Select & Bitfield Ops
Branch-free conditionals and instructions for packing/unpacking bitfields.
// Conditional select avoids branches entirelycmp x0, x1csel x2, x3, x4, gt // x2 = (x0 > x1) ? x3 : x4cset x5, eq // x5 = 1 if last compare was equal, else 0csinc x6, x7, xzr, ne // x6 = ne ? x7 : (xzr + 1) -- conditional incrementccmp x0, #0, #4, ne // conditional compare: only evaluated if previous flag was ne// Bitfield instructionsubfx x0, x1, #8, #4 // unsigned bitfield extract: bits [11:8] of x1 into x0sbfx x0, x1, #8, #4 // same but sign-extendedbfi x0, x1, #4, #8 // bitfield insert: 8 bits of x1 into x0 starting at bit 4bfxil x0, x1, #0, #16 // bitfield extract and insert at the low bits
Scalar Floating Point
s (32-bit) and d (64-bit) registers share the same file as NEON vectors.
// Scalar floating point uses s (32-bit) and d (64-bit) registersfmov s0, #1.0 // load an FP immediatescvtf d1, x0 // signed int64 -> doublefcvtzs x2, d1 // double -> signed int64, round toward zerofcvt s3, d1 // narrow double -> singlefadd d0, d1, d2 // d0 = d1 + d2fdiv s0, s1, s2 // single precision dividefcmp d0, d1 // compare, sets NZCVfcsel d2, d0, d1, gt // conditional select on FP compare resultldr d0, [x0] // load a double from memorystr s0, [x1, #4] // store a float at offset 4
System Registers & Privileged State
Registers accessed via mrs/msr, relevant when writing kernel or low-level runtime code.
- mrs/msr- move a value from/to a system register, e.g. mrs x0, TPIDR_EL0
- TPIDR_EL0- thread-local storage pointer, read by userspace TLS accessors
- NZCV- condition flags register, readable/writable via mrs/msr to save/restore flags
- SP_EL0 / SP_EL1- per-exception-level stack pointers
- CurrentEL- reports the current exception level (EL0-EL3)
- DAIF- interrupt/exception mask bits (Debug, Abort, IRQ, FIQ)
- ESR_ELx- exception syndrome register, decoded by exception handlers
- CNTVCT_EL0- virtual counter register, used for cycle-accurate timing
Remember AAPCS64 requires the stack pointer to be 16-byte aligned at every public function boundary — misaligned sp on a call to libc or the kernel is a classic, hard-to-debug crash source.