Quiz Entry - updated: 2026.07.10
What optimizations does GCC apply when generating assembly for an if-else with function calls?
It minimizes branches and redundant register moves, pre-positions shared arguments before the branch, and uses cheap arithmetic substitutes.
Consider this function, where both branches call a function and the result is doubled:
int cf_if(long a, long b) {
int res;
if (a >= b) res = foo(a, b);
else res = bar(b, a);
return res * 2;
}
GCC's tricks, visible in the output below:
- Invert the condition — test for
<so the common "then" path falls through. - Hoist a shared move —
bis moved into%rdibefore the branch, so the else-path needs fewermovs. - 16-byte stack alignment —
subq $8, %rspbeforecall, as the ABI requires. - Cheaper multiply —
addl %eax, %eax(a self-add) instead ofimul $2.
cf_if:
movq %rdi, %r8 # stash a in r8
subq $8, %rsp # align stack for the calls
movq %rsi, %rdi # pre-move b into rdi (shared setup)
cmpq %rsi, %r8
jl .L2 # inverted: jump to else when a < b
movq %r8, %rdi # then: rdi = a, then foo(a, b)
call foo@PLT
jmp .L_exit
.L2:
movq %r8, %rsi # else: rsi = a (rdi already = b), bar(b, a)
call bar@PLT
.L_exit:
addl %eax, %eax # res * 2 via add (cheaper than imul)
addq $8, %rsp
ret
Takeaway: Don't expect a one-to-one C↔asm mapping at -O. The compiler reshapes control flow and reuses registers aggressively while preserving behavior.
Go deeper:
Compiler Explorer (godbolt) — watch these optimizations appear as you change the -O level.
LEA — x86 instruction reference — LEA, the flag-free arithmetic trick GCC leans on.