2026-09-08
The CMOVcc family (CMOVE, CMOVNE, CMOVL, CMOVG, ...) is a conditional move: CMOVcc dst, src copies src into dst only if the flags satisfy the condition. Introduced with the Pentium Pro (1995), it lets the compiler turn a tiny branch into a straight-line sequence with a data dependency instead of a control dependency.
Why does that matter? A mispredicted branch on modern x86 costs 15–20 cycles because the entire pipeline (Reorder Buffer, ~200 in-flight µops) is flushed. CMOV never mispredicts — but it forces the dependent value to wait for the flags to resolve, adding roughly 1 cycle of latency on the critical path. The rule of thumb:
Concrete example — the classic sorted-vs-unsorted array benchmark:
for (i = 0; i < N; i++)
if (data[i] >= 128) sum += data[i];
On sorted data, GCC emits a JGE branch, the predictor hits ~100%, and the loop runs at ~1.5 ns/element. On the same data shuffled, the branch mispredicts ~50% and slows to ~10 ns/element — the famous 6× slowdown. Compile with -O2 and modern GCC/Clang often emit CMOV instead: both sorted and shuffled runs land near ~2.5 ns/element. Sorted got slower; shuffled got 4× faster.
The gotcha compilers know but you might not: CMOV always reads the source operand, even when the condition is false. This matters for two reasons:
if (p) x = *p; won't be if-converted.__builtin_ctz-style tricks, CMOVE for constant-time selects) to avoid leaking secrets through branch timing side channels.To force the compiler's hand: x = cond ? a : b; written on a plain scalar with no side effects is the idiom that most reliably lowers to CMOV. Check with -S or godbolt. If you see jne/je in a hot loop where the data is random, hand-massage the source or use __builtin_expect_with_probability(..., 0.5) to signal unpredictability.
