2026-08-29
The Intel uop cache (DSB) is organized around a rigid rule that catches almost everyone by surprise: each entry corresponds to a single aligned 32-byte region of the original x86 instruction stream. Skylake-family cores can hold up to 6 uops per entry, up to 3 entries per 32-byte window, and — crucially — a single macro-instruction's uops cannot span two entries. If a x86 instruction crosses a 32-byte boundary, all of its uops get placed in the entry belonging to whichever 32-byte window starts the instruction. But the fetch of that instruction still counts against both windows' delivery bandwidth.
The waste compounds three ways:
Concrete example: Google's TCMalloc team hit this in 2019 when a compiler update reordered a hot allocation path. A frequently-taken branch instruction (6 bytes) landed at offset 28 within a 32-byte window — spilling 2 bytes into the next window. Nothing about the instructions themselves changed, but IPC on the hot path dropped ~8% because the loop no longer fit cleanly in DSB entries. The fix was a single-byte NOP inserted earlier in the function to shift alignment. Same instructions, same semantics, 8% faster.
Rule of thumb: A 32-byte window can hold at most ~18 uops across 3 entries, but only if instructions don't cross the boundary. Every boundary-crossing instruction costs you roughly one entry slot worth of delivery bandwidth — call it 6 uops of throughput opportunity. For a hot loop delivering 4 uops/cycle from the DSB, one boundary-crossing instruction per iteration can cost you an entire cycle.
This is why -falign-loops=32 exists in GCC, why LLVM's -mllvm -align-all-blocks=5 flag matters, and why performance engineers stare at objdump output looking for instructions at offsets 30 and 31. The compiler can't always fix it — sometimes a jump target must land where it lands. But awareness of the 32-byte grid is the difference between an 4.0 IPC loop and a 3.2 IPC one.
