Registers and encodings
VFP has 32 single-precision registers s0 to s31, which overlap the double-precision registers d0 to d15 (dn is s2n and s2n+1); VFPv3 and later (and NEON) add d16 to d31, and NEON also names pairs of them q0 to q15. The coprocessor number says the precision: 10 for single (.f32), 11 for double (.f64). The A32 and T32 encodings are the same (the T32 first halfword is the high half of the A32 word, with condition 1110); the explorer decodes them with the same code.
A register number is a 4-bit field plus one more bit (D, N or M): for a single register that bit is the least significant bit of the number, for a double register the most significant.
The mnemonic is v<op><cond>.<type>: the condition comes before the type suffix (vaddeq.f32 s0, s1, s2). The results of a VFP instruction are in the floating-point status register fpscr (flags N, Z, C, V in bits 31:28, rounding mode, exception enables); vmrs APSR_nzcv, fpscr copies the flags to the integer status.
Arithmetic
cond 1110 followed by opc1 (bits 23:20, with D in bit 22), Vn, Vd, 101z, N, op (bit 6), M, 0, Vm.
| Bits 23, 21, 20 (p q r) | Bit 6 = 0 | Bit 6 = 1 | Level |
|---|---|---|---|
| 0 0 0 | vmla Vd += Vn × Vm | vmls Vd −= Vn × Vm | VFPv2 |
| 0 0 1 | vnmls | vnmla | VFPv2 |
| 0 1 0 | vmul | vnmul | VFPv2 |
| 0 1 1 | vadd | vsub | VFPv2 |
| 1 0 0 | vdiv | (undefined) | VFPv2 |
| 1 0 1 | vfnms | vfnma | VFPv4 |
| 1 1 0 | vfma | vfms | VFPv4 |
| 1 1 1 | One-operand and special forms, chosen by Vn (bits 19:16) and bits 7:6: vmov (immediate: bits 7:4 = 0000, the 8-bit immediate is Vn:Vm), vmov (register, 0 / 01), vabs (0 / 11), vneg (1 / 01), vsqrt (1 / 11), vcmp and vcmpe (4 and 5, with #0), vrintr (6 / 01), vrintz (6 / 11), vrintx (7 / 01), vcvt (see below) | VFPv2 and later | |
The VFP immediate of vmov is written as LLVM does, #1.000000e+00: sign a, exponent bits NOT(b) and cd, fraction efgh; the value is ±(16 + efgh) / 16 × 2(exponent − 3). The ARMv8 floating-point additions are in the unconditional space (condition 1111): vseleq, vselvs, vselge, vselgt (bits 21:20 pick the condition), vmaxnm and vminnm (bit 6), vrinta, vrintn, vrintp, vrintm (Vn = 10rm). They need FP-ARMv8.
Conversion
| Vn (opc2), bits 7:6 | Instruction |
|---|---|
| 0010, 0011 with bit 6 = 1 | vcvtb, vcvtt (bit 7): between half and single precision (.f32.f16, .f16.f32); ARMv8 adds half to and from double |
| 0111, 11 | vcvt.f64.f32 (cp 10) and vcvt.f32.f64 (cp 11) |
| 1000, x1 | vcvt.f32.s32, .u32 (bit 7: signed), also .f64: an integer in a single register to floating point |
| 1100, 1101, x1 | vcvt.u32.f32, vcvt.s32.f32 (bit 7 = 1: round toward zero) and vcvtr (bit 7 = 0: use the rounding mode in FPSCR) |
| 1010, 1011, 1110, 1111 | Fixed-point: vcvt.f32.s32 s0, s0, #fbits and back; bit 7 chooses 16 or 32 bits, the number of fraction bits is the size minus (imm4:i) |
| cond 1111, 1111 1110 1D11 1 rm … | vcvta, vcvtn, vcvtp, vcvtm (ARMv8): float to integer with the rounding named by the suffix |
Register transfers
| Pattern | Instruction |
|---|---|
| cond 1110 000 op Vn Rt 1010 N 001 0000 | vmov Sn, Rt (op 0), vmov Rt, Sn (op 1) |
| cond 1100 010 op Rt2 Rt 1011 00 M 1 Vm | vmov Dm, Rt, Rt2 and vmov Rt, Rt2, Dm; with 1010: vmov Sm, Sm+1, Rt, Rt2 (two consecutive single registers) |
| cond 1110 0opc1 0 Vn Rt 1011 N opc2 1 0000 | vmov.8/16/32 Dn[x], Rt: a core register into one lane |
| cond 1110 U opc1 1 Vn Rt 1011 N opc2 1 0000 | vmov.s8/u8/s16/u16/32 Rt, Dn[x] |
| cond 1110 1 B Q 0 Vd Rt 1011 D 0 E 1 0000 | vdup.8/16/32 Dd or Qd, Rt (Advanced SIMD) |
| cond 1110 1110 reg Rt 1010 0001 0000; 1110 1111 … | vmsr fpscr, Rt, vmrs Rt, fpscr; vmrs APSR_nzcv, fpscr when Rt is 15. Registers: 0 fpsid, 1 fpscr, 5 mvfr2, 6 mvfr1, 7 mvfr0, 8 fpexc, 9 fpinst, 10 fpinst2 |
Load and store
| Pattern | Instruction |
|---|---|
| cond 1101 UD01 Rn Vd 101z imm8 | vldr Sd or Dd, [Rn, #±imm8 × 4]; vstr is UD00. Rn = pc: the address is aligned PC + 8 (or + 4 in Thumb) ± offset |
| cond 110 P U D W L Rn Vd 101z imm8 (PU = 01 or 10) | vldmia, vstmia (PU = 01), vldmdb, vstmdb (PU = 10, W = 1): a list of imm8 single registers or imm8 / 2 double ones. The decoder writes vpush for vstmdb sp! and vpop for vldmia sp!. With doubles, an odd imm8 is the legacy fldmiax, fstmiax, fldmdbx, fstmdbx. |
Advanced SIMD (NEON)
Advanced SIMD is decoded in A32 (condition 1111, bits 27:24 = 0010 or 0011 for data processing, 0100 for element and structure loads and stores) and in T32 (first halfword 111U 1111, and 1111 1001); the Thumb forms are mapped to the A32 word and decoded once. Bit 24 is the U bit, bit 6 is Q (a 128-bit operation: the registers are q0 to q15, each a pair of D registers, and an odd D number is undefined), bits 21:20 are the element size for most of the groups.
| Group | How it is told | Instructions |
|---|---|---|
| Three registers, same length | bit 23 = 0; bits 11:8 and bit 4 | vhadd, vqadd, vrhadd, vand, vbic, vorr, vorn, veor, vbsl, vbit, vbif (and vmov for vorr with equal sources, A32 only), vhsub, vqsub, vcgt, vcge, vshl, vqshl, vrshl, vqrshl (register shifts: the operands are Dd, Dm, Dn), vmax, vmin, vabd, vaba, vadd, vsub, vtst, vceq, vmla, vmls, vmul, vpmax, vpmin, vqdmulh, vqrdmulh, vpadd, vfma, vfms, the f32 forms vabd, vacge, vacgt, vrecps, vrsqrts, vmaxnm, vminnm; cryptography sha1c, sha1p, sha1m, sha1su0, sha256h, sha256h2, sha256su1 |
| One register and a modified immediate | bit 23 = 1, bit 4 = 1, bits 21:19 = 000 | vmov, vmvn, vorr, vbic with an 8-bit immediate expanded by cmode (bits 11:8) into 8, 16, 32 or 64 bits or an f32; the expanded value is written |
| Two registers and a shift | bit 23 = 1, bit 4 = 1, bits 21:19 ≠ 000 | vshr, vsra, vrshr, vrsra, vsri, vshl, vsli, vqshlu, vqshl, vshrn, vrshrn, vqshrn, vqrshrn, vqshrun, vqrshrun, vshll, vmovl, vcvt to and from fixed point; the size and the amount come from L:imm6 |
| Three registers, different lengths | bit 23 = 1, bit 4 = 0, bits 21:20 ≠ 11, bit 6 = 0 | vaddl, vaddw, vsubl, vsubw, vaddhn, vraddhn, vsubhn, vrsubhn, vabal, vabdl, vmlal, vmlsl, vmull (also .p8 and .p64), vqdmlal, vqdmlsl, vqdmull |
| Two registers and a scalar | as above with bit 6 = 1 | vmla, vmls, vmul, vmlal, vmlsl, vmull, vqdmlal, vqdmlsl, vqdmull, vqdmulh, vqrdmulh by a lane of a D register (d2[1]) |
| Two registers, miscellaneous | bits 21:20 = 11, bit 4 = 0, bits 19:18 size, 17:16 opc1, 10:7 opc2 | vrev64, vrev32, vrev16, vpaddl, vpadal, vcls, vclz, vcnt, vmvn, vqabs, vqneg, vcgt, vcge, vceq, vcle, vclt against zero, vabs, vneg, vswp, vtrn, vuzp, vzip, vmovn, vqmovn, vqmovun, vshll (maximum shift), vcvt f16 and f32, vrecpe, vrsqrte, vcvt between f32 and integers, vrintn, vrintx, vrinta, vrintz, vrintm, vrintp, vcvta, vcvtn, vcvtp, vcvtm; cryptography aese, aesd, aesmc, aesimc, sha1h, sha1su1 |
| Extract, table, duplicate | bits 21:20 = 11 with U = 0 (vext), or bits 11:8 = 10xx (vtbl, vtbx), or 1100 (vdup from a lane) | vext.8, written by LLVM as vext.16, vext.32 or vext.64 when the byte offset allows; vtbl.8 Dd, {Dn, …}, Dm with one to four registers; vdup.8/16/32 Dd, Dm[x] |
| Element and structure load and store | cond 1111, bits 27:24 = 0100 | vld1 to vld4 and vst1 to vst4: all lanes ({d0[]}), one lane ({d0[1]}) and whole registers; the address has an optional alignment ([r0:64]), then ! or a register for the post-index. Rm = 13 is writeback, Rm = 15 none. |
Not decoded: the ARMv8.2 half-precision Advanced SIMD and VFP forms (vadd.f16, vldr.16, vmov.f16, vcvt.s16.f16), the dot-product, complex-number (vcmla, vcadd), vfmal and vfmsl, int8 matrix and bf16 instructions, and the ARMv8.1-M MVE (Helium) vector extension, which reuses this space in Thumb. The comparison tests count the first group as “in the Advanced SIMD space we do not decode” and keep them apart from agreement.