Instruction
CMPXCHG8B
Compares EDX:EAX with 8 bytes of memory: if equal, stores ECX:EBX there and sets ZF, else loads them into EDX:EAX; atomic with LOCK.
- Extensions
- Forms
- 2 (2 Legacy)
- Data
- Intel XED v2026.08.23
CMPXCHG8B is the compare-and-swap of 8 bytes for code whose registers hold only 4: the expected value goes in EDX:EAX and the new one in ECX:EBX, high halves in EDX and ECX. With LOCK it is atomic, and it reports the outcome in ZF alone, as CMPXCHG does. It needs no alignment: Zen 5 ran it at an address 4 bytes past a multiple of 8. Like CMPXCHG, it writes its destination whether or not the values matched.
In 32-bit code
There, the 64-bit atomic read-modify-writes all come down to LOCK
CMPXCHG8B. GCC 14 and Clang 23 at -O2 and -m32 compile a 64-bit
compare-exchange to one, and a 64-bit exchange or fetch-and-add to a
loop: load both halves, compute the new value into ECX:EBX (for the
add, with ADD and ADC), LOCK CMPXCHG8B, and JNE back
while another thread got there first. The register pairs are fixed, so
the compilers must free EBX, which the i386 ABI makes callee-saved and
position-independent code uses for its global offset table (see
EBX and position-independent
code): both push it
on entry and pop it before returning.
Alignment decides whether the instruction appears at all. The i386 ABI
promises long long only 4-byte alignment, so on a plain uint64_t *
Clang assumes a misaligned operand and calls libatomic’s
__atomic_compare_exchange instead; with an _Atomic type, aligned to
8, it inlines the loop. GCC inlined both.
In 64-bit mode
Without REX.W, 0F C7 /1 is still CMPXCHG8B, but compilers have no use for it there: a 64-bit CMPXCHG does the same work with one register. Only EAX and EDX are compared, and their upper halves play no part. On success RAX and RDX are left whole; on failure EDX and EAX are loaded, which clears bits 63:32 of both, as Zen 5 recorded. With REX.W the same opcode is CMPXCHG16B.
Odds and ends
- A register operand raises #UD: 0F C7 /1 is a memory-only member of an opcode group.
- The CPUID bit is leaf 1, EDX bit 8, which AMD’s manual also mirrors in leaf 80000001H. Every x86-64 processor has it, and Intel’s manual names the Pentium as its first processor with the instruction.
- The instruction table of AMD’s Zen 5 guide lists CMPXCHG8B as microcoded.
Forms
| Instruction | Encoding | Requires |
|---|---|---|
CMPXCHG8B m64
CMPXCHG8B_MEMq | 0F C7 /1 | Base instruction set |
LOCK CMPXCHG8B m64
CMPXCHG8B_LOCK_MEMq | 0F C7 /1 | Base instruction set |
Latency and throughput
| Model | m64 | CMPXCHG8B m64 |
|---|---|---|
| Sandy Bridge | 6 / 1p015 | 6 / 1p015 |
| Haswell | 17 / 2.75p0156 | 17 / 2.75p0156 |
| Broadwell, Skylake, Skylake-SP, Ice Lake | 16 / 2.75p0156 | 16 / 2.75p0156 |
| Alder Lake, Sapphire Rapids | 25 / 3.5p0 p1 p5 p6 p11 | 25 / 3.5p0 p1 p5 p6 p11 |
| Lunar Lake | 25 / 2.33p012345 | 25 / 2.33p012345 |
| Bonnell | 18 / 9p1 | 18 / 9p1 |
| Silvermont | 10 / 3IEC0 IEC1 MEC | 10 / 3IEC0 IEC1 MEC |
| Piledriver | 3 / 26EX1 | 3 / 26EX1 |
| Zen | 1 / 0.5ALU0 ALU1 ALU2 ALU3 | 1 / 0.5ALU0 ALU1 ALU2 ALU3 |
| Zen 2 | 1 / 0.33ALU0 ALU1 ALU2 ALU3 | 1 / 0.33ALU0 ALU1 ALU2 ALU3 |
| Zen 3 | 3 / 6ALU0 ALU1 ALU2 ALU3 | 3 / 6ALU0 ALU1 ALU2 ALU3 |
| Zen 4 | 3 / 5ALU0 ALU1 ALU2 ALU3 | 3 / 5ALU0 ALU1 ALU2 ALU3 |
| Jaguar | 11 / 1.5ALU0 ALU1 | 19 / 19ALU0 ALU1 |
Operation
What the forms do, in pseudocode that our tests run against results recorded on real processors. How to read.
old = dest.read() # with LOCK: one atomic read-modify-write
if old == edx << 32 | eax:
zf = 1
dest.write(ecx << 32 | ebx)
else:
zf = 0
edx = old >> 32 # both written: bits 63:32 of RDX and RAX
eax = old # clear, even where a half matched
dest.write(old) # memory is written back all the same
# the other flags stay as they wereRun on AMD Zen 5 (EPYC 9645): 16 cases.
Operands
| Operand | Kind | Action |
|---|---|---|
CMPXCHG8B m64 LOCK CMPXCHG8B m64 | ||
m64 | explicit | read, conditional write |
EDX | implicit | read, conditional write |
EAX | implicit | read, conditional write |
ECX | implicit | read |
EBX | implicit | read |
RFLAGS | implicit | write |
Flags
| ZF |
|---|
| All forms |
| M |
Attributes
What else XED records about the forms. How to read.
- Memory forms without LOCK
- accepts a LOCK prefix
- LOCK forms
- locked: atomic read-modify-write; accepts XACQUIRE; accepts XRELEASE
CPUID and processors
Each form belongs to an XED ISA set. A form can be used when the processor reports every CPUID bit of one of its ISA set's alternatives.
| ISA set | CPUID | Processors |
|---|---|---|
PENTIUMREAL | No CPUID bit: part of the base instruction set. | Every x86-64 processor in XED’s model and Zen 3 to Zen 6 |
More General-purpose instructions
Sources
- Intel XED v2026.08.23 (commit
0bcb6237345c): forms, encodings, operands, flags, exception classes, attributes, ISA sets, CPUID bits and chip model. - The CPU data of x86-64.net: Zen 3 to Zen 6, from GCC’s
-marchfeature sets and AMD’s Processor Programming References, which the page of each processor cites. - LLVM 23.1.2's x86 scheduling models (commit
85ac56026243): the latency, reciprocal throughput and ports of each form, as llvm-mca reports them. - Intel 64 and IA-32 Architectures Software Developer's Manual, Volume 2 (325383-093, September 2026): Volume 2A, page 3-196 (CMPXCHG8B/
CMPXCHG16B) - Intel 64 and IA-32 Architectures Software Developer's Manual, Volume 3 (325384-093, September 2026)
- AMD64 Architecture Programmer's Manual, Volume 3: General-Purpose and System Instructions (24594, rev. 3.39, October 2026)
- Software Optimization Guide for the AMD Zen5 Microarchitecture (58455, rev. 1.00, August 2024), Wayback Machine copy of AMD's ZIP
The tables are derived from Intel XED, Copyright Intel Corporation, licensed under the Apache License 2.0; x86-64.net converted and reformatted them. The timings come from LLVM's scheduling models, part of the LLVM Project, licensed under the Apache License 2.0 with LLVM Exceptions. The text is our own.