Introduction
This page explains how an A64 instruction is laid out in memory. It covers the top-level groups, the fields that most instructions share, the ways an immediate value is packed into a few bits, how PC-relative targets work and how conditions are coded. The last section explains how to read the disassembler’s text.
Bits are numbered from 31, the most significant, to 0. A range such as 28–25 runs from its high bit to its low bit.
One 32-bit word
Every A64 instruction is exactly one 32-bit word. Instruction fetches are always little-endian, even where data is big-endian, so the least significant byte comes first. Code must be aligned to four bytes.
The bytes 20 40 00 91 in a file are the word 0x91004020,
which is add x0, x1, #0x10.
The top-level groups
Bits 28–25 select the group an instruction belongs to. Bit 31 only matters for the
value 0000. An x means the bit can be either value.
| Bit 31 | Bits 28–25 | Group |
|---|---|---|
0 | 0000 | Reserved. udf is here, so the word 0 is udf #0x0. |
1 | 0000 | SME |
x | 0010 | SVE |
x | 00x1 | Unallocated |
x | 100x | Data processing, immediate |
x | 101x | Branches, exception generation and system instructions |
x | x1x0 | Loads and stores |
x | x101 | Data processing, register |
x | x111 | Scalar floating point and Advanced SIMD |
Each group divides again on other bits. The groups do not match the pages of this reference exactly: Advanced SIMD loads and stores, for example, belong to the loads and stores group here.
Common fields
Many instructions share these fields:
| Field | Bits | Meaning |
|---|---|---|
sf | 31 | In integer instructions, the operand size: 0 for 32-bit w registers, 1 for 64-bit x registers. |
Rd | 4–0 | The destination register. |
Rn | 9–5 | The first source register, or the base register of a load or store. |
Rm | 20–16 | The second source register. |
Rt | 4–0 | The register a load or store transfers, or the register a test-and-branch tests. |
A register number is five bits, so it can be 0 to 31. Number 31 means sp or
the zero register, depending on the instruction and the field. See
Registers.
Immediates
A 32-bit instruction has no room for a 32-bit or 64-bit constant. Each kind of instruction packs its immediate in its own way.
Add and subtract: 12 bits with an optional shift
| Bits | Field | Meaning |
|---|---|---|
| 31 | sf | Operand size. |
| 30 | op | 0 for add, 1 for subtract. |
| 29 | S | 1 to set the flags. |
| 28–23 | 100010 | |
| 22 | sh | 1 to shift the immediate left by 12 bits. |
| 21–10 | imm12 | The unsigned value. |
| 9–5 | Rn | Source. |
| 4–0 | Rd | Destination. |
The value is 0 to 4095, or that value times 4096. The disassembler writes the shifted
form as add x0, x1, #0x1, lsl #12.
Logical: the bitmask immediate
and, orr, eor and ands take a
bitmask immediate, which encodes a repeating pattern in 13 bits.
| Bits | Field | Meaning |
|---|---|---|
| 31 | sf | Operand size. |
| 30–29 | opc | 00 and, 01 orr, 10 eor, 11 ands. |
| 28–23 | 100100 | |
| 22 | N | With imms, gives the element size. |
| 21–16 | immr | The rotation to the right. |
| 15–10 | imms | The element size and the length of the run of ones. |
| 9–5 | Rn | Source. |
| 4–0 | Rd | Destination. |
The pattern is an element of 2, 4, 8, 16, 32 or 64 bits, repeated to fill the
register. N and the top of imms give the element size:
N | imms | Element size |
|---|---|---|
1 | xxxxxx | 64 bits |
0 | 0xxxxx | 32 bits |
0 | 10xxxx | 16 bits |
0 | 110xxx | 8 bits |
0 | 1110xx | 4 bits |
0 | 11110x | 2 bits |
The x bits hold the number of ones in the element, minus one. They cannot
all be 1, because an element cannot be all ones. For an element of 2k bits, the
low k bits of immr rotate it to the right. The 32-bit forms need
N to be 0.
This gives 5,334 different 64-bit values. Neither 0 nor all ones is among them.
Two examples. and x0, x1, #0xff has N = 1,
imms = 000111 and immr = 0: a 64-bit element
with eight ones and no rotation. and x0, x1, #0x5555555555555555 has
N = 0, imms = 111100 and immr = 0:
a 2-bit element with a single one, repeated.
Move wide: 16 bits and a position
| Bits | Field | Meaning |
|---|---|---|
| 31 | sf | Operand size. |
| 30–29 | opc | 00 movn, 10 movz, 11 movk. |
| 28–23 | 100101 | |
| 22–21 | hw | The position, in units of 16 bits. The shift is hw × 16. |
| 20–5 | imm16 | The value. |
| 4–0 | Rd | Destination. |
movz sets the register to the shifted value. movn sets it to
the bitwise inverse of the shifted value. movk replaces only those 16 bits
and keeps the rest. The 32-bit forms allow hw of 0 or 1 only. Code builds a
wider constant with one movz or movn followed by
movk instructions.
Floating point: 8 bits
The scalar fmov immediate has this layout:
| Bits | Field | Meaning |
|---|---|---|
| 31–24 | 00011110 | |
| 23–22 | ftype | 00 single, 01 double, 11 half precision. |
| 21 | 1 | |
| 20–13 | imm8 | The value, as bits abcdefgh. |
| 12–10 | 100 | |
| 9–5 | 00000 | |
| 4–0 | Rd | Destination. |
The value is ±(16 + efgh) ÷ 16 × 2r. Bit a is the
sign, and bcd gives the exponent r:
bcd | 000 | 001 | 010 | 011 | 100 | 101 | 110 | 111 |
|---|---|---|---|---|---|---|---|---|
| r | 1 | 2 | 3 | 4 | −3 | −2 | −1 | 0 |
So the magnitudes run from 0.125 to 31, and 0 cannot be encoded this way. 1.0 is
0x70. The vector and SVE forms of fmov use the same 8-bit
value in their own layouts.
PC-relative targets
A branch or a PC-relative load holds a signed offset from the address of the instruction itself. Because instructions are four bytes apart, most offsets are stored in units of four bytes.
| Instruction | Offset field | Units | Reach |
|---|---|---|---|
b, bl | imm26 in bits 25–0 | 4 bytes | ±128 MB |
b.eq and other conditional branches | imm19 in bits 23–5 | 4 bytes | ±1 MB |
cbz, cbnz | imm19 in bits 23–5 | 4 bytes | ±1 MB |
tbz, tbnz | imm14 in bits 18–5 | 4 bytes | ±32 KB |
ldr (literal) | imm19 in bits 23–5 | 4 bytes | ±1 MB |
adr | 21 bits: immhi in bits 23–5, immlo in bits 30–29 | 1 byte | ±1 MB |
adrp | 21 bits, as for adr | 4 KB | ±4 GB |
adrp works in pages. It clears the low 12 bits of the instruction’s own
address, then adds the offset. Code usually follows it with an add or a load
that supplies the low 12 bits of the address.
In tbz and tbnz, the number of the bit to test is split: bit
31 holds its top bit and bits 23–19 hold the rest.
The condition field
A condition is a 4-bit field that tests the flags. It is in bits 3–0 of a conditional
branch, and in bits 15–12 of csel, ccmp and their relatives.
Inverting bit 0 gives the opposite condition, except for al and
nv.
| Code | Name | Meaning after a comparison | Test |
|---|---|---|---|
0000 | eq | Equal | Z = 1 |
0001 | ne | Not equal | Z = 0 |
0010 | hs (also cs) | Unsigned higher or same | C = 1 |
0011 | lo (also cc) | Unsigned lower | C = 0 |
0100 | mi | Negative | N = 1 |
0101 | pl | Positive or zero | N = 0 |
0110 | vs | Overflow | V = 1 |
0111 | vc | No overflow | V = 0 |
1000 | hi | Unsigned higher | C = 1 and Z = 0 |
1001 | ls | Unsigned lower or same | C = 0 or Z = 1 |
1010 | ge | Signed greater or equal | N = V |
1011 | lt | Signed less than | N ≠ V |
1100 | gt | Signed greater than | Z = 0 and N = V |
1101 | le | Signed less or equal | Z = 1 or N ≠ V |
1110 | al | Always | Always true |
1111 | nv | Always (despite the name) | Always true |
Reading the disassembler’s output
The explorer’s disassembler follows the text that LLVM’s
llvm-objdump prints.
Registers and elements
- General registers.
x0is 64 bits andw0is 32 bits.x29andx30keep those names: LLVM does not printfporlr. Register 31 shows assp,wsp,xzrorwzr. - Vectors.
v0.16bis sixteen bytes andv0.4sis four 32-bit elements.v1.s[0]is one element. - Scalars.
b0,h0,s0,d0andq0are 8, 16, 32, 64 and 128 bits ofv0. - SVE.
z0.sis a scalable vector of 32-bit elements, andp0/mandp0/zare a predicate that merges or zeroes. Loads and stores put their vectors in braces, as inld1w { z0.s }, p0/z, [x0]. - SME.
za0h.s[w12, 0]is one horizontal slice of a tile of ZA. - System registers are written in capitals, such as
TPIDR_EL0andNZCV. One with no name is written by its number, such asS3_3_C15_C15_7.
The Registers page describes these in full.
Operands
- Most immediates are in hexadecimal, as in
add x0, x1, #0x10andldr x0, [sp, #0x8]. Shift amounts and bitfield positions are in decimal, as inlsl x0, x1, #3andubfx x0, x1, #4, #8. - Floating-point immediates have eight decimal places, as in
fmov d0, #1.00000000. - Shifted and extended registers are written after the register, as
in
add x0, x1, x2, lsl #3andadd x0, x1, w2, sxtw #2. - Memory operands are in square brackets:
ldr x0, [sp, #0x8]. A trailing!, as instr x0, [sp, #-0x10]!, adds the offset to the base register before the access. An immediate after the brackets, as inldp x29, x30, [sp], #0x10, adds it after the access. - Branch targets and the addresses of
adr,adrpand literal loads show as the final address, as inb 0x1010. tbzandtbnzname awregister when the bit tested is below 32 and anxregister otherwise. The encoding only has the register number and the bit.- An encoding that is not an instruction shows in the explorer as
.instand the word, as in.inst 0x02000000.
Aliases
Many common operations have no encoding of their own. They are another instruction with particular operands, and LLVM prints the shorter name wherever it applies:
| Printed | Is really |
|---|---|
mov x0, x1 | orr x0, xzr, x1 |
mov sp, x0 | add sp, x0, #0x0 |
mov x0, #0x10000 | movz x0, #0x1, lsl #16; a movn, or an orr from xzr where neither can make the value, also shows as mov |
cmp x0, x1 | subs xzr, x0, x1 |
cmn x0, x1 | adds xzr, x0, x1 |
tst x0, x1 | ands xzr, x0, x1 |
neg x0, x1 | sub x0, xzr, x1 |
mvn x0, x1 | orn x0, xzr, x1 |
mul x0, x1, x2 | madd x0, x1, x2, xzr |
lsl x0, x1, #3 | ubfm x0, x1, #61, #60 |
lsl x0, x1, x2 | lslv x0, x1, x2 |
ror x0, x1, #4 | extr x0, x1, x1, #4 |
ubfx x0, x1, #4, #8 | ubfm x0, x1, #4, #11 |
sxtw x0, w1 | sbfm x0, x1, #0, #31 |
cset x0, eq | csinc x0, xzr, xzr, ne |
csetm x0, eq | csinv x0, xzr, xzr, ne |
cinc x0, x1, eq | csinc x0, x1, x1, ne |
cneg x0, x1, eq | csneg x0, x1, x1, ne |
nop | hint #0x0 |
ret | ret x30 |
ic ivau, x0 | sys #3, c7, c5, #1, x0 |
mov v0.16b, v1.16b | orr v0.16b, v1.16b, v1.16b |
mov w0, v1.s[0] | umov w0, v1.s[0] |
The aliases cset, csetm, cinc and
cneg show the condition inverted from the one in the encoding. A
movz of zero with a shift stays as movz.
Links
Reference
- Arm A64 Instruction Set Architecture – Arm’s description of each A64 instruction
- Arm Architecture Reference Manual for A-profile – the full architecture specification