2026-08-30
The micro-op cache stores decoded instructions in a compressed format so the CPU can skip the expensive x86 decoders. But that compression comes with a catch: each uop cache entry has a fixed number of bits reserved for immediate operands, and instructions with large immediates can consume extra slots or get kicked out entirely.
On Intel Skylake-through-Alder Lake, each uop cache line holds up to 6 uops in 32 bytes of instruction footprint. Each uop has a small immediate/displacement field — typically 32 bits per uop slot. If an instruction needs a 64-bit immediate (like MOV RAX, 0x123456789ABCDEF0), it consumes two uop slots instead of one, even though it decodes to a single uop. Worse, if two instructions in the same fetch window both carry 32-bit displacements, they can exhaust the line's immediate storage before hitting the 6-uop limit.
The concrete rule of thumb: a uop cache line can hold at most 2 large (32-bit) immediates or displacements. A third instruction with a 32-bit immediate ends the line early, wasting cache capacity and forcing an extra fetch cycle.
Real-world example: imagine a hot loop that touches several globals through RIP-relative addressing:
MOV RAX, [rip+0x00401000] — 32-bit displacementADD RAX, [rip+0x00402000] — 32-bit displacementMOV [rip+0x00403000], RAX — 32-bit displacementThree instructions, three large displacements — but only two fit per uop cache line. The third splits into a new line, meaning the loop now consumes 2 uop cache lines instead of 1. If the loop body was already close to the 3-lines-per-32-byte-window limit, this pushes it out of the uop cache entirely, dropping frontend throughput from ~6 uops/cycle to ~4 uops/cycle through the legacy decoders.
Why this exists: storing full 64-bit immediates for every uop slot would roughly double the uop cache's SRAM footprint. Designers picked the sweet spot — most instructions have small or no immediates, so the common case stays compact. The corner cases (64-bit MOVs, multiple RIP-relative addresses in one window) pay the tax.
Practical mitigation: pool your globals into a single struct addressed through a base register. One MOV RBX, [rip+offset] to load the base, then small 8-bit displacements to reach individual fields — those don't count against the large-immediate budget.
