Skip to content
The Compiler Pipeline
step 1/5

Reading — step 1 of 5

Read

~1 min readLexing & Parsing

The Compiler Pipeline

A C compiler turns C source into an executable. Stages:

.c source
   ↓ Preprocessor (cpp): expand #include, #define, #if
.i preprocessed
   ↓ Lexer + Parser: tokens → AST
AST
   ↓ Semantic analysis: type-check, build symbol table
typed AST
   ↓ IR generation: AST → SSA / 3-address code
IR
   ↓ Optimization: constant folding, dead code, loop unrolling
optimized IR
   ↓ Code generation: IR → assembly
.s assembly
   ↓ Assembler (as): assembly → object code
.o object file
   ↓ Linker (ld): combine object files + libraries
executable

Each stage is a transformation. The middle stages (IR, optimization) are language-independent; the outer stages handle C-specific or target-specific concerns.

Real compilers:

  • GCC (GNU Compiler Collection): C, C++, Go, Fortran, etc. Most-used.
  • Clang/LLVM: C/C++/Objective-C frontends; LLVM is the reusable backend.
  • TinyCC (TCC): tiny, fast compile times. ~30k lines.
  • chibicc: educational C compiler in 5k lines (Rui Ueyama).
  • MSVC: Microsoft.

Why this many stages?

  • Modularity: each stage has clear input/output.
  • Reusability: LLVM IR powers many languages (Rust, Swift, Julia).
  • Optimization opportunities: optimizing IR is easier than AST.

We'll build a small C compiler — a subset (no preprocessor, no full struct, no float types) that compiles to x86-64 assembly. The architecture matches real compilers; only the scope is reduced.

Reference: chibicc (Rui Ueyama), Writing a C Compiler (Sandler), Crafting Interpreters (Nystrom — though that's for tree-walking interpreters).

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…