Introduction
APX (Intel Advanced Performance Extensions) extends the general-purpose part of x86-64. It doubles the general registers to 32, adds a two-byte prefix, REX2, to reach them, and gives most integer instructions an EVEX form with a separate destination register and a choice of whether to set the flags. It also adds conditional compares, two-register pushes and pops, a 64-bit absolute jump and a conditional move that does not fault.
APX works in 64-bit mode only. A program checks CPUID.(EAX=07H, ECX=1):EDX[bit 21] (APX_F), and the operating system must enable XCR0 bit 19. The new registers are XSAVE state component 19, which takes the place in the XSAVE area that MPX’s state had, so the two cannot be enabled together.
Intel published the APX specification in July 2023 and has announced APX for the processors code-named Nova Lake (Core) and Diamond Rapids (Xeon). Neither was on sale when this page was written, in November 2026: Intel has given 2027 for Diamond Rapids.
Most APX instructions are ordinary instructions in a new encoding. Their rows are on the pages of the instructions themselves, and the promoted instructions section lists them by family. This page has full rows only for the EVEX forms of KMOV, which belong to no other page.
When the explorer disassembles a program, each instruction it shows links to its row on these pages.
The registers r16 to r31
The sixteen new registers are r16 to r31. As with r8 to r15, the low 32, 16 and 8 bits have their own names: r16d, r16w and r16b, and so on; there are no high-byte registers for them. They are general registers in every way: instructions read and write them, and they can be the base or index of an address. Writing a 32-bit register clears the upper half, as for the first sixteen.
Only REX2 and EVEX can name them. A legacy REX prefix has one extra bit per register field, which reaches r8 to r15; REX2 and EVEX add a second, R4, X4 and B4 (and V4 for EVEX’s vvvv). VEX has no room, so the VEX instructions that APX wanted to reach the new registers (BMI, AMX’s loads, KMOV and others) were given EVEX forms. In the AVX-512 and AVX10 instructions, B4 and X4 let the base and index of a memory operand be r16 to r31.
REX2
REX2 is the byte D5 followed by one payload byte. Outside 64-bit mode D5 is AAD; in 64-bit mode it was invalid before APX. REX2 comes straight before the opcode, after any legacy prefixes, and a REX prefix may not come before it. It replaces both REX and the 0F escape:
| Bit | Name | Meaning |
|---|---|---|
| 7 | M0 | The opcode map: 0 is the one-byte map, 1 is the 0F map |
| 6 | R4 | Bit 4 of the ModRM.reg register |
| 5 | X4 | Bit 4 of the SIB index register |
| 4 | B4 | Bit 4 of the ModRM.rm, SIB base or opcode register |
| 3 | W | As REX.W: a 64-bit operand size |
| 2 | R3 | As REX.R: bit 3 of the ModRM.reg register |
| 1 | X3 | As REX.X |
| 0 | B3 | As REX.B |
The bits are not inverted. REX2 reaches only maps 0 and 1: the 0F 38 and 0F 3A
instructions get the new registers through EVEX instead. In map 0, rows 4, 7, A and E
(40–4F, the Jcc rel8 at 70–7F, A0–AF and E0–EF) cannot take REX2, except A1 with W = 0,
which is JMPABS; in map 1, rows 3 and 8 (WRMSR, RDTSC, SYSENTER and the rest, the 0F 38
and 0F 3A escapes, and Jcc rel32) cannot. So d5 00 a1 followed by eight
bytes is jmpabs to that address.
With REX2.W = 1, PUSH and POP of a register (50–5F) become PUSHP and POPP, the same operations with the PPX hint (see PUSH2, POP2 and PPX).
The extended EVEX of map 4
APX uses EVEX map 4, which AVX-512 left empty, for its promoted forms of legacy
instructions, and changes the meaning of some of EVEX’s payload bits there. A map 4
instruction starts 62, then three payload bytes:
| Byte | Bits | Meaning in map 4 |
|---|---|---|
| P0 | 7, 6, 5 | R3, X3, B3, inverted (as in AVX-512) |
| P0 | 4 | R4, inverted (AVX-512’s R′) |
| P0 | 3 | B4, not inverted (0 in AVX-512) |
| P0 | 2–0 | The map: 100 |
| P1 | 7 | W |
| P1 | 6–3 | vvvv, inverted: the new data destination (with V4); for CCMP and CTEST the default flags, not inverted |
| P1 | 2 | X4, inverted (AVX-512’s fixed 1) |
| P1 | 1–0 | pp: 66, F3 or F2, as the legacy prefixes |
| P2 | 7 | Must be 0 (z) |
| P2 | 6–5 | Must be 00 (L′L) |
| P2 | 4 | ND: a new data destination (AVX-512’s b) |
| P2 | 3 | V4, inverted (AVX-512’s V′) |
| P2 | 2 | NF: no flags |
| P2 | 1–0 | Must be 00 |
For CCMP and CTEST, P2 bits 3–0 hold the condition instead. EVEX.pp = 01 is an operand-size prefix, as 66 is, unless the opcode needs it; F3 and F2 are part of the opcode.
New data destination (NDD)
With ND = 1, the instruction writes its result to the register in vvvv (a fifth bit
in V4) and leaves its operands alone: add r16, rax, rbx puts rax + rbx in
r16. The first source may be in memory. The destination is always a register, so LOCK
cannot apply. With ND = 0 the instruction is its ordinary two-operand self. We write
the new destination first, as LLVM does.
No flags: {nf}
With NF = 1, an instruction that would set the arithmetic flags leaves them as they
were, so a compiler can put it between a compare and the branch that uses it. The
disassembly shows it as the pseudo-prefix {nf}: {nf} add rax,
rbx. It applies to the NDD forms too. ADC and SBB, which read the carry, and
RCL and RCR have no NF form.
A promoted form with neither ND nor NF is written as the legacy instruction, so LLVM
shows it with the pseudo-prefix {evex}, and so do we.
Zero upper: ZU
In two places ND means something else. SETZUcc (F2, opcodes 40 to 4F with ND = 1) writes its 0 or 1 to the whole 64-bit register, not just the low byte. IMULZU (69 and 6B with ND = 1) is the three-operand IMUL whose 16-bit form clears the rest of the destination.
CCMP and CTEST
CCMPscc and CTESTscc are a compare and a test that happen only
if a condition holds. If it holds, they set the flags as CMP and TEST do. If it does
not, OF, SF, ZF and CF take the default flags value (dfv) that the
instruction carries in vvvv: bit 6 of P1 is OF, then SF, ZF and CF. So a compiler can
test a == 1 && b == 2 with a CMP, a CCMP and one branch.
The condition is in P2 bits 3–0, with the codes of Jcc, except that 1010 and 1011,
which are P and NP there, are T (always) and F (never). The disassembly gives the
condition in the mnemonic and the default flags after it:
ccmpl {dfv=of,sf,zf,cf} cl, al is 62 f4 7c 0c 38 c1. They
are at CMP’s and TEST’s places: 38 to 3B and group 1 /7 for CCMP, 84, 85 and group 3 /0
for CTEST. ND must be 0.
PUSH2, POP2 and PPX
PUSH2 and POP2 push or pop two 64-bit registers,
one in vvvv and one in ModRM.rm, in one instruction: push2 rax, rcx is
62 f4 7c 18 ff f1 (FF /6 with ND = 1) and pop2 is 8F /0 with
ND = 1. The stack pointer must be a multiple of 16, or they raise #GP.
PPX (push-pop acceleration) is a hint that a push and a later pop are a matched pair, as a function’s saves and restores are, so that the processor can speed up passing the value from one to the other. PUSHP and POPP are PUSH and POP with REX2.W = 1; PUSH2P and POP2P are PUSH2 and POP2 with EVEX.W = 1.
JMPABS and CFCMOV
JMPABS jumps to a 64-bit address held in the instruction: REX2 with M0 = 0 and W = 0, then A1, then the eight bytes of the address. Before it, a jump to an arbitrary 64-bit address needed a register or a memory operand.
CFCMOVcc (conditionally faulting CMOV) is a CMOV that touches memory only when the condition holds. An ordinary CMOVcc with a memory source always reads it, and can fault even when it moves nothing. In map 4 at 40 to 4F:
- ND = 0, NF = 0:
cfcmovcc r, r/m, which loads only if the condition holds and otherwise sets the register to zero; - ND = 0, NF = 1:
cfcmovcc r/m, r, which stores only if the condition holds; - ND = 1, NF = 0:
cmovcc r, r, r/m, the NDD form of CMOVcc; - ND = 1, NF = 1:
cfcmovcc r, r, r/m, its conditionally faulting form.
The promoted instructions
These have EVEX forms, which reach r16 to r31; the NDD and NF columns say which take a new data destination and {nf}. Besides these, the legacy instructions of maps 0 and 1 take REX2, apart from the rows given under REX2.
| Family | Instructions | NDD | NF | Rows |
|---|---|---|---|---|
| Integer arithmetic | ADD, SUB, ADC, SBB, NEG, INC, DEC, IMUL (two operands) | Yes | Yes, but not ADC and SBB | Arithmetic |
| Multiply and divide | MUL, IMUL (one and three operands), DIV, IDIV, IMULZU | No (ND gives IMULZU) | Yes | Arithmetic |
| Conditional compare | CCMPscc | No | No | Arithmetic |
| Logic, shifts and rotates | AND, OR, XOR, NOT, ROL, ROR, RCL, RCR, SHL, SHR, SAR, SHLD, SHRD | Yes | Yes, but not NOT (which sets no flags), RCL and RCR | Logic, shifts and bits |
| Conditional test | CTESTscc | No | No | Logic, shifts and bits |
| Data transfer | CMOVcc (NDD only), CFCMOVcc, SETcc, SETZUcc, MOVBE, PUSH2, POP2 | CMOVcc | No | Data transfer |
| Bit manipulation | POPCNT, LZCNT, TZCNT, ADCX, ADOX; from VEX: ANDN, BLSI, BLSMSK, BLSR, BZHI, BEXTR, PDEP, PEXT, MULX, RORX, SARX, SHLX, SHRX | ADCX and ADOX | POPCNT, LZCNT, TZCNT, ANDN, BLSI, BLSMSK, BLSR, BZHI and BEXTR | Bit manipulation |
| CRC32 | CRC32 | No | No | SSE4.1, SSE4.2 and SSE4a |
| Synchronisation | CMPccXADD (from VEX), AADD, AAND, AOR, AXOR, MOVDIRI, MOVDIR64B, ENQCMD, ENQCMDS | No | No | Transactions and synchronisation |
| Read-shared load | MOVRS | No | No | Cache and memory ordering |
| Shadow stack | WRSSD, WRSSQ, WRUSSD, WRUSSQ | No | No | Security |
| System and virtualisation | INVPCID, URDMSR, UWRMSR (register forms), INVEPT, INVVPID | No | No | System, Virtualisation |
| AMX | LDTILECFG, STTILECFG, TILELOADD, TILELOADDT1, TILESTORED, TILELOADDRS, TILELOADDRST1, and AMX-TRANSPOSE’s pair loads (from VEX) | No | No | AMX |
| Mask moves | KMOVB, KMOVW, KMOVD, KMOVQ (from VEX) | No | No | Below |
The promoted VEX instructions are in EVEX maps 2 and 3, at their VEX opcodes; LLVM
shows them without {evex}, and so do we.
Instructions
Mask register moves
The EVEX forms of the KMOV instructions, which can move between a mask register and r16 to r31, or address memory with them.
| Instruction | Forms | Encoding | What it does | Set |
|---|---|---|---|---|
kmovb | k1, k2/m8; m8, k1; k1, r32; r32, k1 | EVEX.L0.66.0F.W0 90 /r; EVEX.L0.66.0F.W0 91 /r; EVEX.L0.66.0F.W0 92 /r; EVEX.L0.66.0F.W0 93 /r | Moves 8 bits between mask registers, memory and general registers (a load into a mask register clears its upper bits; a move to a general register zero-extends). | APX (Intel Nova Lake and Diamond Rapids, announced) |
kmovw | k1, k2/m16; m16, k1; k1, r32; r32, k1 | EVEX.L0.NP.0F.W0 90 /r; EVEX.L0.NP.0F.W0 91 /r; EVEX.L0.NP.0F.W0 92 /r; EVEX.L0.NP.0F.W0 93 /r | Moves 16 bits. | APX (Intel Nova Lake and Diamond Rapids, announced) |
kmovd | k1, k2/m32; m32, k1; k1, r32; r32, k1 | EVEX.L0.66.0F.W1 90 /r; EVEX.L0.66.0F.W1 91 /r; EVEX.L0.F2.0F.W0 92 /r; EVEX.L0.F2.0F.W0 93 /r | Moves 32 bits. | APX (Intel Nova Lake and Diamond Rapids, announced) |
kmovq | k1, k2/m64; m64, k1; k1, r64; r64, k1 | EVEX.L0.NP.0F.W1 90 /r; EVEX.L0.NP.0F.W1 91 /r; EVEX.L0.F2.0F.W1 92 /r; EVEX.L0.F2.0F.W1 93 /r | Moves 64 bits. | APX (Intel Nova Lake and Diamond Rapids, announced) |
Notes
- 64-bit mode only. Outside it, D5 is AAD and EVEX map 4 is invalid.
- LOCK. A promoted instruction with a memory destination that takes LOCK in its legacy form takes it before the EVEX prefix too. With ND = 1 it is invalid, and the explorer says so.
- X4 in AVX-512 instructions. AVX-512 fixed bit 2 of P1 at 1; APX uses it as X4. A register form has no index register, so an APX processor ignores the bit there, and so do we, except with EVEX.b set, where AVX10.2’s withdrawn 256-bit rounding used it (see the AVX10 page).
- Mask registers. As in the VEX forms, only k0 to k7 exist: a register bit that would name k8 or above makes the encoding invalid.
Links
Reference
- Intel Advanced Performance Extensions (Intel APX) Architecture Specification – the extension’s own specification
- Intel 64 and IA-32 Architectures Software Developer’s Manuals – Intel’s manual, with the instructions that APX promotes
- AMD64 Architecture Programmer’s Manual, Volume 3 – AMD’s reference to the general-purpose and system instructions