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…