C Mastery / How C Becomes Assembly: Case Studies
Part 7 — CPU Architecture and Assembly

How C Becomes Assembly: Case Studies

This chapter walks through concrete examples of how C constructs — locals, pointers, arrays, structs, loops, branches, and calls — map to assembly.

Why This Matters

Reading the assembly your compiler emits is the surest way to understand what your C code actually does. It connects the abstract machine to the real CPU and is the foundation of optimization and debugging.

Prerequisites

Core Concept

The compiler lowers C to assembly following the target ABI. Simple local variables may live in registers; pointers are addresses; arrays are contiguous memory accessed by scaled addressing; structs are offsets; loops and branches become jumps; calls become call/ret.

Case Studies

Locals and arithmetic

int f(int x) { int y = x * 3 + 1; return y; }

x86-64 (-O2):

f:
    lea eax, [rdi + rdi*2 + 1]
    ret

The expression is folded into a single scaled-address computation.

Arrays

int g(int *a, int i) { return a[i]; }
g:
    movsx rax, esi
    mov eax, DWORD PTR [rdi + rax*4]
    ret

Indexing compiles to a scaled load (*4 for int).

Structs

struct P { int x; int y; };
int get_y(struct P *p) { return p->y; }
get_y:
    mov eax, DWORD PTR [rdi + 4]
    ret

p->y is a load at offset 4 (assuming no padding before y).

Loops and branches

int sum(int n) {
    int s = 0;
    for (int i = 0; i < n; i++) s += i;
    return s;
}

The compiler emits a loop with a conditional branch, and at -O3 may vectorize or unroll it.

Function calls

int add(int a, int b);
int h(int x) { return add(x, 1); }
h:
    mov esi, 1
    jmp add          ; tail call

How It Works

The compiler's back end does instruction selection, register allocation, and instruction scheduling, producing assembly from the IR. The ABI dictates argument/return placement.

Variations

Optimization changes the mapping

At -O0, locals are stored on the stack; at -O2, they live in registers or disappear entirely. The same source can produce very different assembly.

Common Mistakes

Undefined Behavior

match the source's apparent intent (c.opt.2).

Portability

Under the Hood

-S shows assembly; objdump -d disassembles a binary; -fverbose-asm adds source comments.

Practical Usage

Exercises

1. Compile each case study at -O0 and -O2 and compare. 2. Identify the prologue/epilogue, scaled addressing, and tail call. 3. Add a switch and observe jump table vs. branch chain.

Deep Challenge

Write a function that the compiler vectorizes at -O3, and explain, in the generated assembly, the vector load, the packed operation, and the scalar remainder loop.

References

Verification

verified.`

Progress

Concept checkboxes

Mastery levels

ConceptCurrent level (0–8)Target level
c.cpu.c-to-asm06