COMPILER DESIGN / 7. FULL PIPELINE
Full Compiler Pipeline — Source to Execution
Putting it all together — compile and run a mini language end to end
EXPLANATION
Let's tie everything together by building a complete mini-compiler that takes a simple language all the way from source text to execution. This demonstrates every phase working as an integrated system. The mini language supports: - Integer arithmetic: +, -, *, / - Variable assignment: x = expr - If/else statements - While loops - Print statements - Return values The complete pipeline: ① Source text → Lexer → Token stream ② Token stream → Parser → AST ③ AST → Semantic Analyzer → Typed AST + Symbol Table ④ Typed AST → IR Generator → Three-Address Code ⑤ TAC → Optimizer → Optimized TAC ⑥ Optimized TAC → Code Generator → Target code ⑦ Target code → Execution Real-world compiler architectures: GCC (GNU Compiler Collection): - Front-ends: C, C++, Fortran, Ada, Go - IR: GIMPLE (high-level) → RTL (Register Transfer Language) - Back-ends: x86, ARM, RISC-V, MIPS, PowerPC - Optimization: 3 levels (-O1, -O2, -O3) LLVM/Clang: - Front-ends: Clang (C/C++), Rust, Swift, Kotlin/Native, Julia - IR: LLVM IR (SSA form, typed, explicit memory model) - Back-ends: x86, ARM, RISC-V, WebAssembly, GPU - Middle-end: 100+ optimization passes CPython: - Front-end: Python tokenizer + parser → AST - No separate IR: AST → bytecode directly - Back-end: CPython VM interprets bytecode - No machine code generation (unless PyPy, Cython, Numba) PyPy: - JIT compiles hot Python bytecode to native x86 - Uses tracing JIT: traces execution paths, compiles traces - Can be 10-100× faster than CPython for CPU-bound code The key insight connecting all your modules: TOC → defines what's computable and what languages can be parsed (DFA for lexer, CFG for parser, TM for the full computation) Compiler Design → implements those theoretical machines as practical tools COA → defines the target (ISA, registers, instruction set the code generator targets) OS → runs the compiled code (loads executable, manages process, handles syscalls) FastAPI/Python → you write source code that all of this processes invisibly
DIAGRAM
COMPLETE COMPILATION:
"while (x > 0) { x = x - 1; }"
LEXER:
[WHILE][(][ID:x][>][NUM:0][)][{][ID:x][=][ID:x][-][NUM:1][;][}]
PARSER → AST:
While(
condition: BinOp(Var(x), >, Num(0)),
body: [Assign(x, BinOp(Var(x), -, Num(1)))]
)
IR GENERATION:
L1: t1 = x > 0
if t1 goto L2 else L3
L2: t2 = x - 1
x = t2
goto L1
L3: (end)
AFTER OPTIMIZATION:
L1: if x > 0 goto L2 else L3 (fused compare+branch)
L2: x = x - 1 (t2 eliminated)
goto L1
L3:
x86-64 ASSEMBLY:
.L1:
cmp [x], 0 ; compare x with 0
jle .L3 ; jump if x <= 0
.L2:
dec [x] ; x-- (strength reduced from x-1)
jmp .L1
.L3:CODE