Introduction
XOP (eXtended Operations) is a set of AMD vector instructions. It came with the Bulldozer processors in 2011, together with FMA4, and grew out of AMD’s SSE5 plans of 2007. Most of it works on integers: multiply-accumulate, horizontal adds, compares with a predicate, a bitwise select, a byte permute, and shifts and rotates whose count can differ in each element. It also has a floating-point fraction instruction and a two-source permute of floats.
A program checks CPUID leaf 80000001H, ECX bit 11 (XOP), and, as for VEX instructions, that the operating system saves the AVX state (CPUID leaf 01H, ECX bit 27, OSXSAVE, and bits 1 and 2 of XCR0). AMD dropped XOP from Zen (2017) on: those processors do not report it in CPUID, and programs must not use it there. No Intel processor has it.
The instructions use the xmm registers: 16 in 64-bit mode, 8 elsewhere. Only vpcmov, vfrczps, vfrczpd and the two vpermil2 instructions have 256-bit forms on the ymm registers. A 128-bit form clears bits 255:128 of its destination, as VEX instructions do.
The XOP prefix
XOP instructions start with the byte 8F, then two bytes laid out as the three-byte VEX prefix (C4) lays them out:
- byte 1: R, X and B, inverted, then five bits of map number: 8, 9 or 10 (written 08, 09 and 0A below);
- byte 2: W, then vvvv (a register, inverted), then L (0 for 128 bits, 1 for 256), then pp, which must be 00.
Then come the opcode, the ModRM byte and the rest, as in any instruction. 8F is also pop r/m, which needs ModRM.reg = 0. Bits 5:3 of the byte after 8F hold B and the top two bits of the map number, and a map number of 8 or more always sets one of those, so the two cannot be confused.
The maps hold:
- map 08: instructions with an immediate byte. In the four-operand instructions the fourth register is in imm8[7:4] (written
/is4); - map 09: instructions with no immediate, and also the TBM and LWP instructions on the general registers (see Bit manipulation and System instructions);
- map 0A: instructions with a 32-bit immediate, which are TBM’s
bextrand LWP’slwpinsandlwpval.
The encodings below are written as XOP.L.map.W opcode, in the style of Intel’s VEX notation. AMD’s manual writes the same thing as the prefix bits: XOP.128.08.W0 A2 /r /is4 is AMD’s 8F RXB.08 0.src.0.00 A2 /r is4.
VEX.W (here XOP.W) does two jobs. In the four-operand instructions and the shifts and rotates by a register, it swaps the last two sources, so that either can be in memory. Everywhere else it must be 0.
Instructions
Integer multiply and accumulate
Each of these works out a = b × c + d on signed integers, where a, b, c and d are the four operands in order and d is the accumulator. The ss forms saturate the result to the signed range of the destination element; the others keep its low bits.
| Instruction | Forms | Encoding | What it does | Set |
|---|---|---|---|---|
vpmacsww | xmm1, xmm2, xmm3/m128, xmm4 | XOP.128.08.W0 95 /r /is4 | Multiplies each signed word of b and c and adds the word of d; eight words | XOP (AMD Bulldozer, 2011) |
vpmacssww | xmm1, xmm2, xmm3/m128, xmm4 | XOP.128.08.W0 85 /r /is4 | As vpmacsww, with signed saturation | XOP (AMD Bulldozer, 2011) |
vpmacswd | xmm1, xmm2, xmm3/m128, xmm4 | XOP.128.08.W0 96 /r /is4 | Multiplies the odd-numbered signed words (1, 3, 5, 7) of b and c to 32 bits and adds the doublewords of d; four doublewords | XOP (AMD Bulldozer, 2011) |
vpmacsswd | xmm1, xmm2, xmm3/m128, xmm4 | XOP.128.08.W0 86 /r /is4 | As vpmacswd, with signed saturation | XOP (AMD Bulldozer, 2011) |
vpmacsdd | xmm1, xmm2, xmm3/m128, xmm4 | XOP.128.08.W0 9E /r /is4 | Multiplies each signed doubleword of b and c and adds the doubleword of d; four doublewords | XOP (AMD Bulldozer, 2011) |
vpmacssdd | xmm1, xmm2, xmm3/m128, xmm4 | XOP.128.08.W0 8E /r /is4 | As vpmacsdd, with signed saturation | XOP (AMD Bulldozer, 2011) |
vpmacsdql | xmm1, xmm2, xmm3/m128, xmm4 | XOP.128.08.W0 97 /r /is4 | Multiplies doublewords 0 and 2 of b and c to signed 64-bit products and adds the quadwords of d; two quadwords | XOP (AMD Bulldozer, 2011) |
vpmacssdql | xmm1, xmm2, xmm3/m128, xmm4 | XOP.128.08.W0 87 /r /is4 | As vpmacsdql, with signed saturation | XOP (AMD Bulldozer, 2011) |
vpmacsdqh | xmm1, xmm2, xmm3/m128, xmm4 | XOP.128.08.W0 9F /r /is4 | Multiplies doublewords 1 and 3 of b and c to signed 64-bit products and adds the quadwords of d; two quadwords | XOP (AMD Bulldozer, 2011) |
vpmacssdqh | xmm1, xmm2, xmm3/m128, xmm4 | XOP.128.08.W0 8F /r /is4 | As vpmacsdqh, with signed saturation | XOP (AMD Bulldozer, 2011) |
vpmadcswd | xmm1, xmm2, xmm3/m128, xmm4 | XOP.128.08.W0 B6 /r /is4 | Multiplies each signed word of b and c, adds each adjacent pair of products and the doubleword of d; four doublewords | XOP (AMD Bulldozer, 2011) |
vpmadcsswd | xmm1, xmm2, xmm3/m128, xmm4 | XOP.128.08.W0 A6 /r /is4 | As vpmadcswd, with signed saturation | XOP (AMD Bulldozer, 2011) |
Horizontal add and subtract
These take one source and add (or subtract) neighbouring elements of it into wider elements, so that nothing overflows. The u forms treat the elements as unsigned and zero-extend; the others are signed and sign-extend.
| Instruction | Forms | Encoding | What it does | Set |
|---|---|---|---|---|
vphaddbw | xmm1, xmm2/m128 | XOP.128.09.W0 C1 /r | Adds each pair of adjacent signed bytes into a word; eight words | XOP (AMD Bulldozer, 2011) |
vphaddbd | xmm1, xmm2/m128 | XOP.128.09.W0 C2 /r | Adds each group of four adjacent signed bytes into a doubleword; four doublewords | XOP (AMD Bulldozer, 2011) |
vphaddbq | xmm1, xmm2/m128 | XOP.128.09.W0 C3 /r | Adds each group of eight adjacent signed bytes into a quadword; two quadwords | XOP (AMD Bulldozer, 2011) |
vphaddwd | xmm1, xmm2/m128 | XOP.128.09.W0 C6 /r | Adds each pair of adjacent signed words into a doubleword | XOP (AMD Bulldozer, 2011) |
vphaddwq | xmm1, xmm2/m128 | XOP.128.09.W0 C7 /r | Adds each group of four adjacent signed words into a quadword | XOP (AMD Bulldozer, 2011) |
vphadddq | xmm1, xmm2/m128 | XOP.128.09.W0 CB /r | Adds each pair of adjacent signed doublewords into a quadword | XOP (AMD Bulldozer, 2011) |
vphaddubw | xmm1, xmm2/m128 | XOP.128.09.W0 D1 /r | Adds each pair of adjacent unsigned bytes into a word | XOP (AMD Bulldozer, 2011) |
vphaddubd | xmm1, xmm2/m128 | XOP.128.09.W0 D2 /r | Adds each group of four adjacent unsigned bytes into a doubleword | XOP (AMD Bulldozer, 2011) |
vphaddubq | xmm1, xmm2/m128 | XOP.128.09.W0 D3 /r | Adds each group of eight adjacent unsigned bytes into a quadword | XOP (AMD Bulldozer, 2011) |
vphadduwd | xmm1, xmm2/m128 | XOP.128.09.W0 D6 /r | Adds each pair of adjacent unsigned words into a doubleword | XOP (AMD Bulldozer, 2011) |
vphadduwq | xmm1, xmm2/m128 | XOP.128.09.W0 D7 /r | Adds each group of four adjacent unsigned words into a quadword | XOP (AMD Bulldozer, 2011) |
vphaddudq | xmm1, xmm2/m128 | XOP.128.09.W0 DB /r | Adds each pair of adjacent unsigned doublewords into a quadword | XOP (AMD Bulldozer, 2011) |
vphsubbw | xmm1, xmm2/m128 | XOP.128.09.W0 E1 /r | For each pair of adjacent signed bytes, subtracts the odd-numbered (upper) one from the even-numbered one into a word | XOP (AMD Bulldozer, 2011) |
vphsubwd | xmm1, xmm2/m128 | XOP.128.09.W0 E2 /r | The same for signed words, into doublewords | XOP (AMD Bulldozer, 2011) |
vphsubdq | xmm1, xmm2/m128 | XOP.128.09.W0 E3 /r | The same for signed doublewords, into quadwords | XOP (AMD Bulldozer, 2011) |
Compare
vpcom compares each element of b (the second operand) with the matching element of c (the third) and sets the element of a to all ones where the predicate holds and to zero where it does not. The predicate is in imm8[2:0]: 0 less than, 1 less or equal, 2 greater than, 3 greater or equal, 4 equal, 5 not equal, 6 false (always zero), 7 true (always ones). The other bits of imm8 are not used. The result suits vpcmov.
As LLVM does, we write predicates 0 to 7 into the mnemonic and drop the immediate (vpcomltb xmm0, xmm1, xmm2). AMD’s manual names the same eight. Any other immediate byte is shown as it is, after the plain mnemonic (vpcomb xmm0, xmm1, xmm2, 0x88).
| Instruction | Forms | Encoding | What it does | Set |
|---|---|---|---|---|
vpcomb | xmm1, xmm2, xmm3/m128, imm8 | XOP.128.08.W0 CC /r ib | Compares signed bytes. Predicate names: vpcomltb, vpcomleb, vpcomgtb, vpcomgeb, vpcomeqb, vpcomneqb, vpcomfalseb, vpcomtrueb | XOP (AMD Bulldozer, 2011) |
vpcomw | xmm1, xmm2, xmm3/m128, imm8 | XOP.128.08.W0 CD /r ib | Compares signed words. Predicate names: vpcomltw, vpcomlew, vpcomgtw, vpcomgew, vpcomeqw, vpcomneqw, vpcomfalsew, vpcomtruew | XOP (AMD Bulldozer, 2011) |
vpcomd | xmm1, xmm2, xmm3/m128, imm8 | XOP.128.08.W0 CE /r ib | Compares signed doublewords. Predicate names: vpcomltd, vpcomled, vpcomgtd, vpcomged, vpcomeqd, vpcomneqd, vpcomfalsed, vpcomtrued | XOP (AMD Bulldozer, 2011) |
vpcomq | xmm1, xmm2, xmm3/m128, imm8 | XOP.128.08.W0 CF /r ib | Compares signed quadwords. Predicate names: vpcomltq, vpcomleq, vpcomgtq, vpcomgeq, vpcomeqq, vpcomneqq, vpcomfalseq, vpcomtrueq | XOP (AMD Bulldozer, 2011) |
vpcomub | xmm1, xmm2, xmm3/m128, imm8 | XOP.128.08.W0 EC /r ib | Compares unsigned bytes. Predicate names: vpcomltub, vpcomleub, vpcomgtub, vpcomgeub, vpcomequb, vpcomnequb, vpcomfalseub, vpcomtrueub | XOP (AMD Bulldozer, 2011) |
vpcomuw | xmm1, xmm2, xmm3/m128, imm8 | XOP.128.08.W0 ED /r ib | Compares unsigned words. Predicate names: vpcomltuw, vpcomleuw, vpcomgtuw, vpcomgeuw, vpcomequw, vpcomnequw, vpcomfalseuw, vpcomtrueuw | XOP (AMD Bulldozer, 2011) |
vpcomud | xmm1, xmm2, xmm3/m128, imm8 | XOP.128.08.W0 EE /r ib | Compares unsigned doublewords. Predicate names: vpcomltud, vpcomleud, vpcomgtud, vpcomgeud, vpcomequd, vpcomnequd, vpcomfalseud, vpcomtrueud | XOP (AMD Bulldozer, 2011) |
vpcomuq | xmm1, xmm2, xmm3/m128, imm8 | XOP.128.08.W0 EF /r ib | Compares unsigned quadwords. Predicate names: vpcomltuq, vpcomleuq, vpcomgtuq, vpcomgeuq, vpcomequq, vpcomnequq, vpcomfalseuq, vpcomtrueuq | XOP (AMD Bulldozer, 2011) |
Select and permute
In these four-operand instructions the fourth operand is the selector. With W0 it is the register in imm8[7:4] and the third operand may be in memory; with W1 the two swap places in the encoding, so the selector may be in memory.
| Instruction | Forms | Encoding | What it does | Set |
|---|---|---|---|---|
vpcmov | xmm1, xmm2, xmm3/m128, xmm4xmm1, xmm2, xmm3, xmm4/m128ymm1, ymm2, ymm3/m256, ymm4ymm1, ymm2, ymm3, ymm4/m256 | XOP.128.08.W0 A2 /r /is4XOP.128.08.W1 A2 /r /is4XOP.256.08.W0 A2 /r /is4XOP.256.08.W1 A2 /r /is4 | Bitwise select: each bit of the result comes from the second operand where the selector’s bit is 1 and from the third where it is 0 | XOP (AMD Bulldozer, 2011) |
vpperm | xmm1, xmm2, xmm3/m128, xmm4xmm1, xmm2, xmm3, xmm4/m128 | XOP.128.08.W0 A3 /r /is4XOP.128.08.W1 A3 /r /is4 | Builds each result byte from one selector byte: bits 4:0 pick one of the 32 bytes of the second and third operands, and bits 7:5 say what to do with it: 0 copy it, 1 invert it, 2 reverse its bits, 3 reverse and invert, 4 give 00, 5 give FF, 6 copy its top bit to all eight, 7 the same inverted | XOP (AMD Bulldozer, 2011) |
vpermil2ps | xmm1, xmm2, xmm3/m128, xmm4, imm4xmm1, xmm2, xmm3, xmm4/m128, imm4ymm1, ymm2, ymm3/m256, ymm4, imm4ymm1, ymm2, ymm3, ymm4/m256, imm4 | VEX.128.66.0F3A.W0 48 /r /is4VEX.128.66.0F3A.W1 48 /r /is4VEX.256.66.0F3A.W0 48 /r /is4VEX.256.66.0F3A.W1 48 /r /is4 | Picks each single-precision element of the result from the eight in the same 128-bit lane of the second and third operands, by the matching element of the selector; imm8[1:0] (the last operand) says when the selector zeroes the element instead | XOP (AMD Bulldozer, 2011) |
vpermil2pd | xmm1, xmm2, xmm3/m128, xmm4, imm4xmm1, xmm2, xmm3, xmm4/m128, imm4ymm1, ymm2, ymm3/m256, ymm4, imm4ymm1, ymm2, ymm3, ymm4/m256, imm4 | VEX.128.66.0F3A.W0 49 /r /is4VEX.128.66.0F3A.W1 49 /r /is4VEX.256.66.0F3A.W0 49 /r /is4VEX.256.66.0F3A.W1 49 /r /is4 | The same for double-precision elements: each comes from the four in the same 128-bit lane of the two sources | XOP (AMD Bulldozer, 2011) |
Shift and rotate
Each element of the second operand is shifted or rotated by its own count, the signed low byte of the matching element of the third operand: a positive count goes left and a negative one right. With W0 the data may be in memory and the counts are the register in VEX.vvvv; with W1 the counts may be in memory. vprot also has a form with one count for every element, a signed imm8, in map 08.
| Instruction | Forms | Encoding | What it does | Set |
|---|---|---|---|---|
vprotb | xmm1, xmm2/m128, xmm3xmm1, xmm2, xmm3/m128xmm1, xmm2/m128, imm8 | XOP.128.09.W0 90 /rXOP.128.09.W1 90 /rXOP.128.08.W0 C0 /r ib | Rotates each byte | XOP (AMD Bulldozer, 2011) |
vprotw | xmm1, xmm2/m128, xmm3xmm1, xmm2, xmm3/m128xmm1, xmm2/m128, imm8 | XOP.128.09.W0 91 /rXOP.128.09.W1 91 /rXOP.128.08.W0 C1 /r ib | Rotates each word | XOP (AMD Bulldozer, 2011) |
vprotd | xmm1, xmm2/m128, xmm3xmm1, xmm2, xmm3/m128xmm1, xmm2/m128, imm8 | XOP.128.09.W0 92 /rXOP.128.09.W1 92 /rXOP.128.08.W0 C2 /r ib | Rotates each doubleword | XOP (AMD Bulldozer, 2011) |
vprotq | xmm1, xmm2/m128, xmm3xmm1, xmm2, xmm3/m128xmm1, xmm2/m128, imm8 | XOP.128.09.W0 93 /rXOP.128.09.W1 93 /rXOP.128.08.W0 C3 /r ib | Rotates each quadword | XOP (AMD Bulldozer, 2011) |
vpshlb | xmm1, xmm2/m128, xmm3xmm1, xmm2, xmm3/m128 | XOP.128.09.W0 94 /rXOP.128.09.W1 94 /r | Shifts each byte logically: zeros come in at either end | XOP (AMD Bulldozer, 2011) |
vpshlw | xmm1, xmm2/m128, xmm3xmm1, xmm2, xmm3/m128 | XOP.128.09.W0 95 /rXOP.128.09.W1 95 /r | Shifts each word logically | XOP (AMD Bulldozer, 2011) |
vpshld | xmm1, xmm2/m128, xmm3xmm1, xmm2, xmm3/m128 | XOP.128.09.W0 96 /rXOP.128.09.W1 96 /r | Shifts each doubleword logically | XOP (AMD Bulldozer, 2011) |
vpshlq | xmm1, xmm2/m128, xmm3xmm1, xmm2, xmm3/m128 | XOP.128.09.W0 97 /rXOP.128.09.W1 97 /r | Shifts each quadword logically | XOP (AMD Bulldozer, 2011) |
vpshab | xmm1, xmm2/m128, xmm3xmm1, xmm2, xmm3/m128 | XOP.128.09.W0 98 /rXOP.128.09.W1 98 /r | Shifts each signed byte arithmetically: a right shift copies the sign bit in | XOP (AMD Bulldozer, 2011) |
vpshaw | xmm1, xmm2/m128, xmm3xmm1, xmm2, xmm3/m128 | XOP.128.09.W0 99 /rXOP.128.09.W1 99 /r | Shifts each signed word arithmetically | XOP (AMD Bulldozer, 2011) |
vpshad | xmm1, xmm2/m128, xmm3xmm1, xmm2, xmm3/m128 | XOP.128.09.W0 9A /rXOP.128.09.W1 9A /r | Shifts each signed doubleword arithmetically | XOP (AMD Bulldozer, 2011) |
vpshaq | xmm1, xmm2/m128, xmm3xmm1, xmm2, xmm3/m128 | XOP.128.09.W0 9B /rXOP.128.09.W1 9B /r | Shifts each signed quadword arithmetically | XOP (AMD Bulldozer, 2011) |
Floating-point fraction
| Instruction | Forms | Encoding | What it does | Set |
|---|---|---|---|---|
vfrczps | xmm1, xmm2/m128ymm1, ymm2/m256 | XOP.128.09.W0 80 /rXOP.256.09.W0 80 /r | The fractional part of each single-precision value: the part a conversion to integer by truncation would drop | XOP (AMD Bulldozer, 2011) |
vfrczpd | xmm1, xmm2/m128ymm1, ymm2/m256 | XOP.128.09.W0 81 /rXOP.256.09.W0 81 /r | The fractional part of each double-precision value | XOP (AMD Bulldozer, 2011) |
vfrczss | xmm1, xmm2/m32 | XOP.128.09.W0 82 /r | The fractional part of the low single-precision value; the rest of the destination is cleared | XOP (AMD Bulldozer, 2011) |
vfrczsd | xmm1, xmm2/m64 | XOP.128.09.W0 83 /r | The fractional part of the low double-precision value; the rest of the destination is cleared | XOP (AMD Bulldozer, 2011) |
Notes
- VPERMIL2PS and VPERMIL2PD. These two are not XOP-encoded: they use a VEX prefix, in map 0F 3A with 66, beside FMA4. AMD documents them with XOP, and the XOP CPUID bit reports them. The last operand is the low four bits of the
/is4byte; the top four name the fourth register. We show those four bits as the fifth operand, as LLVM does. - Lengths and W. Every XOP instruction here takes XOP.pp = 00. Apart from
vpcmovand thevfrczpacked forms, XOP.L must be 0. XOP.W must be 0 except where the tables show a W1 form. - No prefixes. A 66, F2, F3, LOCK or REX prefix before 8F makes the instruction invalid (#UD), as before VEX. We show it as invalid, as a processor runs it; LLVM does not.
- Every mode. We decode XOP in 16-bit and 32-bit code as well as in 64-bit code. Since a map number of 8 or more cannot be a
pop, there is no doubt about the byte 8F in any mode. - The fourth register outside 64-bit mode. Only xmm0–xmm7 exist there, so bit 7 of the
/is4byte has no register to name. We ignore it, as GNU binutils does; LLVM does not. - Exceptions. The integer instructions raise no floating-point exceptions and do not change EFLAGS.
vfrczraises the SIMD floating-point exceptions through MXCSR. A memory operand need not be aligned. - Assembler names. GNU and LLVM use AMD’s mnemonics. Compilers reach the instructions through intrinsics in
xopintrin.h, such as_mm_perm_epi8forvppermand_mm_cmov_si128forvpcmov.
Links
Reference
- AMD64 Architecture Programmer’s Manual, Volume 4 – AMD’s manual of the media instructions, which describes each XOP instruction
- AMD64 Architecture Programmer’s Manual, Volume 3 – AMD’s manual of the general-purpose instructions, whose appendix A has the XOP opcode maps
- Intel 64 and IA-32 Architectures Software Developer’s Manuals – Intel’s manual; it has no XOP, but volume 2 describes the VEX prefix whose layout XOP copies