🧊 Compiler Design β€” Dari Source Code ke Machine Code

Compiler adalah jembatan antara manusia dan mesin β€” menerjemahkan bahasa tingkat tinggi (C, Rust, Java, Go) menjadi instruksi mesin (x86, ARM, RISC-V). Catatan ini memetakan 6 fase kompilasi klasik + 1 fase modern dari lexing hingga machine code generation, backend optimasi (LLVM, GCC), dan teknik frontend/backend yang membedakan compiler production-grade. Ini adalah domain yang sama sekali belum tersentuh di vault β€” sebuah dasar Computer Science yang hilang.


Daftar Isi

  1. 1. Premise β€” Compiler
  2. 2. Seven-Phase Compiler Pipeline
  3. 3. Fase 1 β€” Lexical Analysis (Scanner)
  4. 4. Fase 2 β€” Syntax Analysis (Parser)
  5. 5. Fase 3 β€” Semantic Analysis
  6. 6. Fase 4 β€” Intermediate Representation (IR)
  7. 7. Fase 5 β€” Optimization
  8. 8. Fase 6 β€” Code Generation
  9. 9. Fase 7 β€” Modern
  10. 10. Compiler Architecture
  11. 11. Trade-off Matrix per Fase
  12. 12. Cross-Reference ke Vault
  13. References

1. Premise β€” Compiler: Penerjemah Paling Rumit yang Pernah Dibuat

Setiap baris kode yang kamu tulis melewati 7 fase transformasi sebelum menjadi instruksi mesin:

Source Code
    ↓
[Lexer]  β†’ Token stream
    ↓
[Parser]  β†’ AST (Abstract Syntax Tree)
    ↓
[Semantic] β†’ Annotated AST / Symbol Table
    ↓
[IR Gen]  β†’ Intermediate Representation
    ↓
[Optimizer] β†’ Optimized IR
    ↓
[Code Gen] β†’ Assembly / Machine Code

Fakta mencengangkan:

  • GCC ~15 juta baris kode (setara kernel Linux versi 2.6)
  • LLVM ~5 juta baris kode
  • Compiler pertama ditulis Grace Hopper (1952) β€” A-0 compiler
  • Ken Thompson: compiler adalah the ultimate backdoor (Thompson Hack 1984) β€” kompromi compiler bisa mengkompromi setiap program yang dikompilasi

2. Seven-Phase Compiler Pipeline

FaseInputOutputTugas Utama
1. LexerSource code stringToken streamPisahkan string menjadi token: keyword, identifier, operator, literal
2. ParserToken streamASTBangun pohon sintaks berdasarkan grammar (CFG)
3. SemanticASTAnnotated ASTType checking, scope resolution, symbol table
4. IR GenAnnotated ASTIR (e.g., LLVM IR, GIMPLE, Three-Address Code)Turunkan AST ke bentuk intermediate yang lebih rendah level
5. OptIROptimized IRTransformasi: constant folding, dead code elimination, inlining, loop unrolling
6. CodeGenIRAssembly / Machine CodePilih instruksi, alokasi register, jadwalkan instruksi
7. (Modern) JITIR/BytecodeRuntime machine codeKompilasi pada saat runtime, adaptif

3. Fase 1 β€” Lexical Analysis (Scanner)

3.1 Apa yang Dilakukan

Lexer membaca source code sebagai raw string dan mengelompokkan karakter menjadi tokens.

Contoh:

int x = 42 + y;

Menjadi:

KEYWORD[int]  IDENTIFIER[x]  OPERATOR[=]  LITERAL[42]  OPERATOR[+]  IDENTIFIER[y]  SEMICOLON[;]

3.2 Token Types

Token TypeContoh
Keywordif, while, return, int, fn, let
Identifiervariable_name, functionName, x
Literal42, "hello", 3.14, true
Operator+, -, *, /, ==, &&
Delimiter{, }, ;, (, ), [, ]
Comment// ..., /* ... */

3.3 Teknik Implementasi

PendekatanToolsUse Case
Manual (hand-written)NothingPerformance-critical, error messages bagus
Generator-basedlex (Flex), re2c, RagelGrammar stabil, perubahan minimal
Regex-basedAny regex enginePrototyping, scripting language
DFA-basedAutomatonDeterministic β€” O(n) guarantee

State machine internal:

Start β†’ [a-zA-Z_] β†’ Identifier (any alphanumeric + underscore)
      β†’ [0-9] β†’ Integer (0-9)*
      β†’ '"' β†’ StringLiteral (any char except '"')*
      β†’ '/' β†’ Comment (if next char '/' or '*')

3.4 Pitfalls

PitfallContoh
Maximal munch++i β€” lexer ambil ++ bukan + +
Unicode/UTF-8Identifiers bisa UTF-8 (Python 3, Golang)
String interpolation"x = {x}" β€” parser bukan lexer
Regex ambiguityLexer harus deterministic

4. Fase 2 β€” Syntax Analysis (Parser)

4.1 Apa yang Dilakukan

Parser menerima token stream dan membangun Abstract Syntax Tree (AST) berdasarkan grammar bahasa.

Grammar = Context-Free Grammar (CFG):

Expression  β†’ Term ('+' Term)*
Term        β†’ Factor ('*' Factor)*
Factor      β†’ NUMBER | IDENTIFIER | '(' Expression ')'

AST untuk 2 + 3 * 4:

      β”Œβ”€β”€β”€β”€β”€β”€β”€β”
      β”‚   +   β”‚
      β”œβ”€β”€β”€β”¬β”€β”€β”€β”€
      β”‚ 2 β”‚ * β”‚
          β”œβ”€β”€β”€β”€
          β”‚3 4β”‚
          β””β”€β”€β”€β”˜

4.2 Parsing Strategies

StrategyContoh ToolsKelebihanKekurangan
Recursive Descent (top-down)Hand-written (Rust, Go)Error messages bagus, mudah di-debugPerlu left-factoring
LL(k)ANTLR, JavaCCLinear time, lookahead k tokensGrammar restriction
LR(1)Yacc/Bison, LALR(1)Powerfull grammar, shift-reduceError messages jelek
GLL / GLRElkhound, SGLRAmbiguous grammar supportSlow, kompleks
Parser CombinatorNom (Rust), Parsec (Haskell)Ekspresif, composablePerforma lebih lambat

4.3 AST vs CST (Concrete Syntax Tree)

AspekCST (Parse Tree)AST (Abstract Syntax Tree)
Terminalsβœ… Termasuk semua❌ Tidak termasuk (semikolon, kurung)
Non-terminalsβœ… Semua aturanβœ… Hanya yang relevan
SizeBesar (redundan)Kecil (esensial)
UseLanguage specCompiler internal

5. Fase 3 β€” Semantic Analysis

5.1 Apa yang Dilakukan

Memastikan program bermakna secara logis β€” bukan hanya sintaks benar.

Pemeriksaan:

  • Type checking β€” "hello" + 42? ❌ (kebanyakan bahasa)
  • Scope resolution β€” variabel dideklarasikan sebelum dipakai
  • Borrow checking β€” Rust: reference tidak outlive owner
  • Lifetime analysis β€” Rust: dangling pointer di compile-time
  • Name resolution β€” fungsi dipanggil dengan jumlah argumen tepat
  • Constant folding β€” 2 + 3 β†’ 5 (early optimization)

5.2 Type Systems (dari compiler perspective)

Type SystemBahasaCompiler Action
StaticC, Rust, Java, GoSemua tipe diketahui di compile-time
DynamicPython, Ruby, JSType checking di runtime
GradualTypeScript, Python (mypy)Static check + dynamic fallback
Hindley-MilnerHaskell, OCaml, SMLInference: compiler bisa menentukan tipe tanpa anotasi

5.3 Symbol Table

Struktur data paling penting di fase ini:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Global Scope         β”‚
β”‚   β”œβ”€ function: main  β”‚
β”‚   β”‚   └─ params: []  β”‚
β”‚   └─ var: x (int)    β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Function: main       β”‚
β”‚   └─ local: y (int)  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

6. Fase 4 β€” Intermediate Representation (IR)

6.1 Mengapa IR?

Compiler N bahasa Γ— M arsitektur = NΓ—M frontend+backend. Dengan IR:

  Input: C, Rust, Go, Swift, Kotlin
           ↓  ↓     ↓   ↓      ↓
         [Frontend Γ— N]
           ↓  ↓     ↓   ↓      ↓
              [IR β€” satu format]
           ↓  ↓     ↓   ↓      ↓
         [Backend Γ— M] (x86, ARM, RISC-V, WASM)

Total kompleksitas: N + M (bukan NΓ—M).

6.2 Bentuk IR

IR TypeContohLevelSize
Three-Address Code (TAC)t1 = a + b; t2 = t1 * cLow~3Γ— AST
SSA (Static Single Assignment)LLVM IRLow-Med~4Γ— AST
GIMPLEGCC IRMediumβ€”
RTL (Register Transfer Language)GCC RTLVery Lowβ€”
Stack-based bytecodeJVM .class, WASMLowCompact
Control Flow Graph (CFG)Semua optimizerMetaNodes = basic blocks

6.3 SSA Form β€” Inovasi Kunci

Setiap variabel hanya di-assign sekali:

%0 = add i32 %a, %b
%1 = mul i32 %0, %c
%2 = icmp sgt i32 %1, 10

Dengan Ο†-function di join point:

%phi = phi i32 [%val1, %bb_true], [%val2, %bb_false]

SSA menyederhanakan optimasi secara drastis β€” setiap use memiliki single reaching definition.

6.4 IR Comparison: LLVM vs GCC

AspekLLVM IRGCC GIMPLE
FormatTextual (.ll), Binary (.bc)Internal
SSAβœ… All-passβœ… Some passes
Type systemFirst-class (i32, float, ptr)Less expressive
MetadataDebug, profile, TBAALimited
ModularityPer-function at modulePer-function only

7. Fase 5 β€” Optimization

7.1 Klasifikasi Optimasi

KategoriContohEfek
Local (basic block)Constant folding, copy propagation5-10%
Global (function-wide)Dead code elimination, loop invariant10-30%
Inter-procedural (cross-function)Inlining, IPO, devirtualization10-50%
Profile-guided (PGO)Hot/cold splitting, branch prediction5-20%
Link-time (LTO)Cross-module inlining, alias analysis5-15%

7.2 Optimasi Penting

OptimasiMekanismeTypical Speedup
Constant folding3 + 4 β†’ 7 saat compileMinimal
Constant propagationconst int x = 5; y = x + 2 β†’ y = 7Minimal
Dead code eliminationHapus kode yang tidak pernah dijalankan1-5%
Loop unrollingDuplikasi body loop untuk mengurangi overhead iterasi2-10%
InliningGanti function call dengan body function5-20%
VectorizationSIMD: proses 4 int sekaligus2-4Γ—
Strength reductionx * 2 β†’ x << 1Marginal
Common subexpressionHitung a + b sekali, reuse1-5%

7.3 Trade-off Optimasi

Level GCC/ClangWaktu KompilasiKinerja RuntimeDebuggability
O0TercepatTerlambatβœ… Full
O1CepatMediumβœ… Mostly
O2MediumTinggi🟑 Partial
O3LambatTertinggi❌
OsMediumMedium (size)🟑
OzLambatRendah (size kecil)❌

8. Fase 6 β€” Code Generation

8.1 Tugas

Convert IR ke target-specific assembly/machine code:

  1. Instruction selection β€” pilih instruksi yang cocok untuk IR
  2. Register allocation β€” mapping infinite virtual registers β†’ finite physical registers
  3. Instruction scheduling β€” urutkan instruksi untuk pipeline efficiency

8.2 Register Allocation β€” Masalah NP-Hard

Teknik:

TeknikQualitySpeed
Graph coloring (Chaitin)HighSlow (NP-hard approximation)
Linear scanMediumFast (O(n))
PBQPVery highSlow
Greedy (LLVM default)HighFast

Efek: Buruknya register allocation bisa menyebabkan spill β€” variabel dibuang ke memory dan di-load kembali. Spill cost = 100-200 cycles per load/store.

8.3 Peephole Optimization

Optimasi kecil pada jendela 3-5 instruksi berurutan:

; Before peephole
mov eax, 0
; After peephole
xor eax, eax    ; 1 byte vs 5 byte

9. Fase 7 β€” Modern: JIT & Runtime Compilation

9.1 AOT vs JIT

AspekAOT (Ahead-of-Time)JIT (Just-in-Time)
Waktu kompilasiSebelum runSelama run
StartupInstantLambat (kompilasi awal)
OptimalisasiGlobalAdaptif (hot paths)
ContohGCC, Rustc, GoV8, JVM, LuaJIT
PortabilitySatu binary per archSatu bytecode semua arch

9.2 JIT Pipeline

Source β†’ Bytecode β†’ Interpreter (warmup)
                              ↓
                    Hot function detection (tier 1)
                              ↓
                    Simple JIT (tier 2)
                              ↓
                    Optimizing JIT (tier 3)
                              ↓
                    Deoptimization jika asumsi salah

Contoh tier:

  • V8: Ignition (interpreter) β†’ Sparkplug (baseline) β†’ Turbofan (optimizing)
  • JVM: Interpreter β†’ C1 (client) β†’ C2 (server, Graal)

9.3 Deoptimization

Ketika optimizer membuat asumsi (tipe class, monomorphic call) yang ternyata salah di runtime:

  • Stack harus β€œkembali” ke interpreter state
  • Semua optimized code yang bergantung pada asumsi itu dibuang

10. Compiler Architecture: GCC vs LLVM

10.1 Perbandingan Arsitektur

AspekGCCLLVM
FrontendAda per bahasa (C, C++, Fortran, Ada, D, Go)Clang (C/C++), rustc (Rust), Swift, flang (Fortran)
IRGIMPLE (high) β†’ RTL (low)LLVM IR (SSA-based)
OptimizerPer-pass dengan GIMPLE/RTLPer-pass pipeline (opt)
BackendTarget-specific .md filesTarget-specific (TableGen)
LisensiGPLv3Apache 2.0 (with LLVM exceptions)
PluginGPL-only (GCC plugin)BSD-friendly
LibraryMonolithic (cc1)Library-based (libLLVM)

10.2 LLVM Toolchain

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ clang (frontend)                                      β”‚
β”‚   β”œβ”€ libclang (C API)                                 β”‚
β”‚   β”œβ”€ AST matchers / tooling (clang-tidy, clang-format) β”‚
β”‚   └─ Static analysis (clang-tidy, clangd)             β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ opt (optimizer)                                       β”‚
β”‚   └─ libLLVM passes                                   β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ llc (codegen)                                         β”‚
β”‚   β”œβ”€ Instruction selection (DAG->DAG)                 β”‚
β”‚   β”œβ”€ Register allocation (greedy)                     β”‚
β”‚   └─ Instruction scheduling                           β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ lld (linker)                                          β”‚
β”‚   β”œβ”€ LTO (Link-Time Optimization)                     β”‚
β”‚   └─ ThinLTO (distributed LTO)                        β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ compiler-rt (runtime)                                 β”‚
β”‚   β”œβ”€ Sanitizers (ASan, TSan, UBSan, MSan)             β”‚
β”‚   └─ Profile-guided optimization runtime              β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

10.3 GCC vs LLVM: Trade-off

KriteriaPilih GCCPilih LLVM
Performance (SPEC)Slightly better (1-3%)Slightly better (1-3%)
Error messagesGoodExcellent (Clang)
Compile speedSlowerFaster
Binary sizeSmaller (with -Os)Slightly larger
Cross-compilationComplexSimple (single sysroot)
Tools ecosystemLimitedRich (clangd, clang-tidy, Asan)
Language supportC/C++/Fortran/Ada/D/GoC/C++/Rust/Swift/Kotlin/Haskell

11. Trade-off Matrix per Fase

FaseComplexityDebug CostOptimasi PotensialBottleneck
LexerLowLowNoneUTF-8 processing
ParserMediumLowNone (must be correct)Recursive descent overflow
SemanticMedium-HighMediumType errors catch earlyTemplate monomorphization
IR GenMediumMediumIR quality affects all later passesMemory (IR bloat)
OptVery HighVery High2-10Γ— performanceCompile time (O3)
CodeGenHighHigh1-3Γ— performanceRegister allocation
JITVery HighVery HighAdaptive 10Γ—+Deoptimization overhead

12. Cross-Reference ke Vault

KonsepCatatan Vault
Programming Language Evolutionhierarchy-programming-language β€” Evolusi bahasa yang dikompilasi
Type Systemshierarchy-software-engineering-paradigm β€” Paradigma dan type system
WebAssembly (WASM)hierarchy-package-managers β€” WASM sebagai target baru compiler
Compiler Security (Thompson Hack)hierarchy-cybersecurity-defense-architecture β€” Supply chain attack
Rust Compilerrust-systems-programming-tooling-keamanan β€” Rustc sebagai LLVM frontend
Optimization & Performancehierarchy-memory-storage β€” Cache hierarchy dan optimasi
eBPF Verificationebpf-kernel-security β€” eBPF verifier sebagai compiler mini

References

  1. Aho, A., Sethi, R., Ullman, J. β€œCompilers: Principles, Techniques, and Tools (The Dragon Book).” 2nd ed., Pearson, 2006.
  2. Muchnick, S. β€œAdvanced Compiler Design and Implementation.” Morgan Kaufmann, 1997.
  3. Appel, A. β€œModern Compiler Implementation in ML/Java/C.” Cambridge, 1998.
  4. Lattner, C. β€œLLVM: An Infrastructure for Multi-Stage Optimization.” Master’s Thesis, 2002.
  5. Cooper, K. & Torczon, L. β€œEngineering a Compiler.” 3rd ed., Morgan Kaufmann, 2022.
  6. Cytron, R. et al. β€œEfficiently Computing Static Single Assignment Form.” TOPLAS, 1991.
  7. Chaitin, G. β€œRegister Allocation via Coloring.” 1982.
  8. Wimmer, C. & MΓΆssenbΓΆck, H. β€œLinear Scan Register Allocation on SSA Form.” 2005.
  9. GCC Team. β€œGNU Compiler Collection Internals.” 2024.
  10. LLVM Project. β€œLLVM Language Reference Manual.” 2024.
  11. Thompson, K. β€œReflections on Trusting Trust.” Turing Award Lecture, 1984.
  12. Aycock, J. β€œA Brief History of Just-in-Time.” ACM Computing Surveys, 2003.
  13. Hopper, G. β€œThe Education of a Computer.” 1952 β€” first compiler.