LOGBOOK

HELP

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:

  1. Invert the condition — test for < so the common "then" path falls through.
  2. Hoist a shared moveb is moved into %rdi before the branch, so the else-path needs fewer movs.
  3. 16-byte stack alignmentsubq $8, %rsp before call, as the ABI requires.
  4. Cheaper multiplyaddl %eax, %eax (a self-add) instead of imul $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:

From Quiz: REVE1 / Translation of C to Assembly | Updated: Jul 10, 2026