Introduction
The 32-bit ARM architecture has two instruction sets that a program can switch between: A32, the original fixed 32-bit ARM instructions, and Thumb, which started as 16-bit instructions (T16) and gained 32-bit ones (T32, called Thumb-2) with ARMv6T2 and ARMv7. The Cortex-M microcontrollers (and the Raspberry Pi Pico’s RP2040) run Thumb only; Cortex-A and Cortex-R processors, and the ARM7TDMI of the Game Boy Advance, run both. Floating point (VFP) and SIMD (Advanced SIMD, called NEON) use coprocessor numbers 10 and 11 in both sets.
These pages describe the instruction sets as the explorer’s own disassembler reads them. The decoder is in
explorer/cpu/arm/ and is our own code; llvm-objdump and GNU objdump only check it. A decoded
instruction in the viewer links to the group of its entry on these pages.
What the disassembly asks for
An ELF file with machine number 40 (EM_ARM) opens with this decoder. The options bar offers:
- Mode: ARM state, Thumb state, or “From the file” (the default). From the file means: the mapping
symbols
$a(ARM code),$t(Thumb code) and$d(data) that the toolchain writes decide each byte; a stripped file without them is read as Thumb when the build attributes or the architecture say the file is for the M profile, or when the entry point has bit 0 set, else as ARM. Bytes under$dare not decoded. See interworking. - Architecture: which encodings are valid, from ARMv4T to ARMv8-A and the M profile (table below). The default reads
Tag_CPU_archandTag_CPU_arch_profilefrom the section.ARM.attributes, else ARMv8-A, which has every AArch32 encoding that LLVM knows. An instruction of a later architecture is shown as undefined. - VFP and Advanced SIMD: decode coprocessors 10 and 11 as floating point (see VFP and NEON).
- Pseudo-instructions:
push,popand the shift mnemonics, or the instruction as encoded (see pseudo-instructions).
An A32 instruction starts on a 4-byte boundary, a Thumb one on a 2-byte boundary; the explorer decodes at every such offset and lays the results out in columns, so code reached through a jump into the middle of a 32-bit Thumb instruction can be read. Inside an IT block the condition of each instruction comes from the IT instruction in front of it: the layout looks back up to eight bytes for it, so the answer does not depend on where the listing starts.
Architecture levels
| Level | Instruction sets | What it adds to the one before | Cores |
|---|---|---|---|
| ARMv4T | A32, T16 | Thumb state, bx, halfword and signed loads and stores | ARM7TDMI, ARM9TDMI |
| ARMv5TE | A32, T16 | clz, blx, bkpt, ldrd/strd, pld, saturating and 16-bit multiply (DSP) | ARM926EJ-S, XScale |
| ARMv6 (v6K) | A32, T16 | Media instructions (parallel add and subtract, pack, extend, saturate, reverse), ldrex/strex, cps, srs/rfe, setend, umaal | ARM1136, ARM1176 |
| ARMv6-M | T16 and a few T32 (bl, msr, mrs, dmb, dsb, isb) | The M profile baseline: no ARM state | Cortex-M0, M0+, RP2040 |
| ARMv7-A, -R | A32, T16, T32 | All 32-bit Thumb encodings, movw/movt, bit-field instructions, cbz, IT blocks, dmb/dsb/isb, pldw, VFPv3 and NEON | Cortex-A8, A9, A7, A15, R4, R5 |
| ARMv7-M | T16, T32 | Thumb-2 for microcontrollers, hardware divide | Cortex-M3 |
| ARMv7E-M | T16, T32 | The DSP instructions, optional single-precision VFP | Cortex-M4, M7 |
| ARMv8-A AArch32 | A32, T16, T32 | crc32, load-acquire and store-release, hlt, sevl, vsel, vrint*, vcvta/n/p/m, AES and SHA; removes swp, the ‘2’ coprocessor forms, and coprocessors 0 to 13 except 10 and 11 (see coprocessor) | Cortex-A53, A57, A72 in 32-bit state |
| ARMv8-M baseline, mainline | T16, T32 | Load-acquire and store-release, cbz on the baseline, TrustZone state | Cortex-M23; Cortex-M33 and M55 (the latter with MVE, not decoded) |
Status of an encoding
Every decoded instruction carries a status, drawn in the viewer with a highlight and a note.
| Status | Meaning |
|---|---|
| documented | A defined encoding, written as the manual defines it. |
| unpredictable | The encoding exists, but a field that must be zero or one is not, or a register such as SP or PC is used where it must not be. The manual says the result is UNPREDICTABLE; the decoder still shows the instruction and says why. LLVM often refuses such words and GNU objdump shows them; the tests count them apart. |
| deprecated | Defined but not to be used: swp, setend. |
| reserved | An unallocated hint: it executes as a NOP and is shown as nop; reserved Thumb hints 6 to 15 are shown as hint #n. |
| undefined | Not an instruction at the chosen architecture level: shown as .word or .short. |
How it is checked
The decoder is compared with llvm-objdump (LLVM 22.1.8) and with GNU arm-none-eabi-objdump (binutils 2.47), both
reading the same bytes: every 16-bit Thumb halfword, a structured sweep of every first halfword of a 32-bit Thumb instruction with random second
halves, the same for the A32 opcode space (every value of bits 27 to 20 and 7 to 4, with random fills and the conditions E, F and random), and several
million random words from a fixed seed. IT blocks are compared separately: 20,000 random blocks with every first condition and every mask. ELF files built
with clang for Cortex-M0+, M3, M4F, M33, Cortex-A7 (ARM and Thumb, hard and soft float), ARM926 and Cortex-A53, and real ARM files (a shared
library, a position-independent program, a static Thumb program), are compared instruction by instruction through the ELF viewer.
The numbers are in 6502/progress-arm32.md in the project notes.
Where LLVM and the manual differ the decoder follows the manual and says so: LLVM has no disassembly for mul, mla and the long multiplies
below ARMv6 (the architecture has them since ARMv2/v3M), prints __brkdiv0 and trap for udf #0xf9 and udf #0xfe, and
refuses most UNPREDICTABLE words that the decoder shows.
Not described
Known
- ARMv8.2 additions to Advanced SIMD and VFP: half-precision arithmetic and moves (
vadd.f16,vldr.16,vmov.f16), dot product (vsdot,vudot), complex numbers (vcmla,vcadd),vfmal/vfmsl, int8 matrix multiply and bf16: shown as undefined words or as a generic coprocessor instruction. (The rest of Advanced SIMD, loads and stores, and the AES, SHA-1, SHA-256 and PMULL cryptography are decoded: see VFP and NEON.) - M-profile Vector Extension (MVE, Helium) and the ARMv8.1-M branch-future instructions (
bf,bfl,wls,le,clrm). - Jazelle (
bxjis decoded, the bytecode is not) and the ThumbEE instruction set. - ARMv8-M Security Extension:
sg,bxnsandblxnsare decoded, and so are the special-register names of the M profile (msp_nsand so on);tt,tta,ttt,ttatandvlldm/vlstm/vscclrmare not decoded as such (ttshows asstrexwith PC as the register). - Per-instruction register read and write sets (the decoder gives text, targets, flow, status; not a list of registers an instruction reads and writes).
Unknown
- Whether LLVM’s rule that coprocessors 0 to 13 do not exist in ARMv8 AArch32 (and that
cdpandldcto coprocessor 15 are refused) is exactly the architecture’s: the manual page was not read in this work, GNU objdump accepts all of them, and the decoder follows LLVM for the ARMv8-A level only. - The exact set of encodings an ARMv7-A core without the divide extension treats as undefined: the ARMv7-A level here has no
sdiv/udivin A32, which is the LLVM default, but implementations differ.
Links
- ARM Architecture Reference Manual ARMv7-A and ARMv7-R (DDI 0406), the source for A32, T16, T32 and VFPv3/v4 (not fetched while this was written);
- ARM Architecture Reference Manual for A-profile architecture (DDI 0487), for AArch32 in ARMv8 (not fetched while this was written);
- ARMv7-M Architecture Reference Manual (DDI 0403) and ARMv6-M (DDI 0419);
- ELF for the Arm Architecture (mapping symbols) and Addenda to the Arm EABI (build attributes).