Compiler Engineering · 2026 Edition

LLVM
Engineer
Roadmap

A ground-up curriculum for systems programmers with C and x86-64 Assembly foundations. From IR basics to production compiler passes — no shortcuts, no handwaving.

5
Phases
10+
Months
3
Final Tracks
~40
Topics
MONTH 1 3 5 7 9 12+
Phase 1: Architect View
Phase 2: IR Fluency
Phase 3: Kaleidoscope
Phase 4: Passes
Phase 5: Specialization
Overall Progress
0%
Prerequisite check: You have C fluency and x86-64 Assembly basics — that puts you ahead of 80% of beginners. LLVM IR will feel familiar because it IS assembly, just hardware-independent. Every checkbox below is trackable. Progress is saved in your browser.
01
Month 1 · Foundation
The Architect's View
Goal: Understand LLVM's Three-Phase design. Know what IR is and why it exists. Run your first clang/opt/llc pipeline.
Core Concepts
Why Three-Phase Compilers Win
Frontend → Optimizer → Backend. N languages × M targets = N+M components, not N×M. This is the entire reason LLVM exists. Read the AOSA chapter by Chris Lattner — it's 20 pages that reframe how you think about tools.
AOSA ArticleArchitecture
What is LLVM IR? (The "Virtual Assembly")
IR is a RISC-like assembly language with infinite virtual registers, strong typing, and no hardware assumptions. It's what lets the optimizer work independently of both the source language and the CPU. Your x86-64 knowledge transfers — IR is just a cleaner ISA.
IRSSA
Clang vs GCC Architecture Differences
GCC is a monolith — frontend and backend are tightly coupled. Clang is a library-based frontend that emits LLVM IR. Understand why this matters for tooling, IDE integration, and static analysis.
Compilers
Tool Mastery
clang — The Frontend
Your entry point. Key flags to know: -emit-llvm (output IR), -S (text form), -O0/-O2/-O3 (optimization levels), -c (object file). Practice: compile a C file to IR, to assembly, and to object code using only clang flags.
# C → LLVM IR (text)
clang -S -emit-llvm -O0 factorial.c -o factorial.ll

# C → x86-64 Assembly
clang -S -O0 factorial.c -o factorial.s

# Compare: see what changes at O2
clang -S -emit-llvm -O2 factorial.c -o factorial_opt.ll
clang
opt — The Optimizer
Takes .ll files, runs passes, outputs optimized .ll. Learn: -passes="mem2reg,dce", --print-changed, -stats. This is the tool you'll use most in Phase 4 when writing your own passes.
# Run mem2reg pass (promotes memory to SSA registers)
opt -passes="mem2reg" factorial.ll -S -o optimized.ll

# See what changed
opt -passes="mem2reg,dce,instcombine" -S --print-changed factorial.ll
opt
llc — The Backend
Converts IR to machine code. Key flags: -march (target arch), -filetype=asm vs obj. Try targeting a different arch than your host to feel what "hardware independence" actually means.
# IR → x86-64 Assembly
llc -march=x86-64 factorial.ll -o factorial_from_ir.s

# IR → ARM Assembly (cross-compile)
llc -march=aarch64 factorial.ll -o factorial_arm.s
llc
Godbolt (Compiler Explorer) Workflow
Set up a permanent workflow: left pane = C source, right pane 1 = clang x86-64, right pane 2 = clang LLVM IR. Highlight a C expression and see both outputs highlight simultaneously. This dual-map exercise will rewire how you think about code.
Godboltgodbolt.org
lli — The JIT Interpreter
Runs .ll files directly. Not for production, but essential for learning IR: write it, run it, see the result. No compilation step required.
# Write IR, run immediately
lli factorial.ll
lliJIT
Phase 1 Project
The Double-Map Exercise
Write a factorial function in C. Generate both x86-64 ASM and LLVM IR. Write a 1-page comparison: What's similar? What's different? (Hint: IR has no memory, no stack frame in SSA form. x86-64 has rbp, rsp. Find where IR uses alloca and why.)
Deliverable
02
Months 2–3 · Core Language
Fluency in LLVM IR
Goal: Read and write LLVM IR by hand. SSA form must become second nature. You should be able to look at IR and know exactly what it does without running it.
SSA Form — The Core Idea
Static Single Assignment (SSA)
Every variable is assigned exactly once. If you need to reassign, you create a new variable. This sounds crazy at first, but it makes data-flow analysis trivially simple. Compilers can prove "this value never changes" without complex analysis.
; Before SSA (pseudocode)
x = 1
x = x + 2 ; x reassigned

; After SSA
%x1 = add i32 0, 1
%x2 = add i32 %x1, 2 ; new name
SSAData Flow
Phi Nodes — Handling Branches
The one hard part of SSA. When control flow merges (after an if/else), which version of a variable do you use? The phi node selects based on which basic block you came from. Critical to understand before Phase 4.
; if (cond) { x = 1; } else { x = 2; }
%result = phi i32 [ 1, %if.true ], [ 2, %if.false ]
Phi NodesCFG
mem2reg Pass — Why alloca Exists
Clang at -O0 doesn't generate SSA directly — it generates alloca/store/load. The mem2reg pass promotes these memory operations to SSA registers. This is why unoptimized IR looks messy: it's designed to be simple to generate, then cleaned up.
mem2regopt
IR Syntax & Types
Type System
IR is strongly typed. Master: i1, i8, i32, i64 (integers), float, double, ptr (opaque pointer — modern LLVM), [N x T] (arrays), {T1, T2} (structs), T(T1, T2)* (function pointers).
TypesLangRef
Core Instruction Set
Must know: alloca (stack), load/store, add/sub/mul/sdiv, icmp/fcmp, br (conditional and unconditional), call/ret, getelementptr (GEP — pointer arithmetic), bitcast/trunc/zext/sext.
; Simple function: int add(int a, int b)
define i32 @add(i32 %a, i32 %b) {
  %result = add i32 %a, %b
  ret i32 %result
}
Instructions
getelementptr (GEP) — The Tricky One
GEP is pointer arithmetic, not a memory access. It calculates an address — it does NOT dereference. This trips up everyone. The first index dereferences the pointer type, subsequent indices navigate struct/array fields. Study at least 5 examples before moving on.
; Access arr[3] where arr is [10 x i32]*
%ptr = getelementptr [10 x i32], ptr %arr, i64 0, i64 3
GEPPointers
Basic Blocks & Control Flow Graph (CFG)
A function is a collection of basic blocks. Each block ends with a terminator (br or ret). Edges between blocks form the CFG. Every optimization in LLVM operates on this graph structure. Draw CFGs by hand for small functions.
CFGBasic Blocks
LLVM LangRef — Your Dictionary
Don't read it cover to cover. Learn to navigate it. Every time you see an instruction you don't know, look it up. Bookmark the sections: Type System, Instruction Reference, Intrinsics. You'll consult this daily.
llvm.org/docs/LangRef.html
Phase 2 Project
Hand-Write Binary Search in .ll
Write a complete binary search function in LLVM IR by hand (no clang). Include: loop with phi nodes, array access via GEP, comparison + branch, return value. Run with lli. This is the IR equivalent of writing assembly from scratch — it will hurt once and teach forever.
Deliverablelli
03
Months 4–5 · First Compiler
The Kaleidoscope Rite
Goal: Build a real compiler end-to-end. Text in → JIT execution out. Use the official LLVM Kaleidoscope tutorial as your guide, but understand every line — don't copy-paste.
Compiler Frontend
Lexing (Tokenization)
Convert raw text into a stream of tokens (keywords, identifiers, numbers, operators). Write a hand-rolled lexer — no lex/flex. Kaleidoscope's lexer is ~100 lines of C++. Key: every token needs a type and a value.
// "def foo(x) x + 1" →
[ TOKEN_DEF, IDENT("foo"), LPAREN,
  IDENT("x"), RPAREN, IDENT("x"),
  PLUS, NUMBER(1) ]
LexerTokens
Parsing — Recursive Descent
Convert tokens into an AST. Recursive descent is the industry standard for hand-written parsers. Each grammar rule becomes a function. Kaleidoscope uses Pratt parsing for expressions (precedence climbing). Learn both.
ParserASTPratt Parsing
AST Node Design
Design your AST classes: ExprAST (base), NumberExprAST, VariableExprAST, BinaryExprAST, CallExprAST, FunctionAST. Each node must have a codegen() method that returns an llvm::Value*. This interface is the bridge between frontend and backend.
AST Design
LLVM C++ API — Code Generation
The Big Three: Context, Module, IRBuilder
LLVMContext owns all IR objects. Module is a compilation unit (a .ll file in C++ form). IRBuilder is your "cursor" — it tracks the current insertion point and has methods for every instruction. You'll use these in every compiler you ever write.
auto TheContext = std::make_unique<llvm::LLVMContext>();
auto TheModule = std::make_unique<llvm::Module>("kaleid", *TheContext);
auto Builder = std::make_unique<llvm::IRBuilder<>>(*TheContext);
LLVM C++ API
IRBuilder Methods for Every Construct
Map each AST node to IRBuilder calls: CreateAdd/Sub/Mul, CreateFCmpOLT (float compare), CreateBr/CreateCondBr, CreateCall, CreateRet, CreateAlloca, CreateStore/Load. Practice until you can translate IR syntax to C++ API calls without looking it up.
IRBuilder
Function & BasicBlock Creation
Function::Create() with FunctionType. BasicBlock::Create() and setting the IRBuilder insertion point with SetInsertPoint(). Handle function arguments with func->arg_begin(). This pattern repeats in every backend codegen.
FunctionsBasicBlocks
JIT Compilation
ORC JIT (LLVM's Modern JIT API)
ORC (On-Request Compilation) is LLVM's composable JIT framework. Key layers: IRCompileLayer, RTDyldObjectLinkingLayer. For Kaleidoscope, you add functions to the JIT incrementally as the user types them. This mirrors how REPLs work.
ORC JITREPL
Symbol Resolution & Extern Functions
JIT needs to find symbols (like sin, printf). Learn how symbol lookup works: JIT → process symbols → stdlib. Implement extern declarations in your language so Kaleidoscope can call C functions.
Symbol Resolution
Phase 3 Project
Complete Kaleidoscope + One Extension
Finish all 7 chapters of the official tutorial. Then add ONE feature that isn't in the tutorial: string literals, a for loop, user-defined operators, or mutable variables. This extension forces you to truly understand the system rather than follow along.
Deliverablellvm.org/docs/tutorial
04
Months 6–9 · The Job-Ready Phase
Writing Passes & Optimizations
Goal: Write code that transforms other code. This is what compiler engineers do at companies. Master the pass infrastructure, data structures, and analysis frameworks.
New Pass Manager (NPM)
New Pass Manager Architecture
LLVM switched from the Legacy Pass Manager to the New Pass Manager (NPM) in LLVM 13+. All new code uses NPM. Key types: FunctionPass, ModulePass, LoopPass. Pass registration with PassPluginLibraryInfo for out-of-tree passes.
NPMPass Manager
Pass Anatomy: run() Method
Every pass implements a run(Function &F, FunctionAnalysisManager &AM) method. It returns PreservedAnalyses — telling the pass manager which analyses are still valid after your transformation. Returning PreservedAnalyses::all() vs none() has performance implications.
struct MyPass : PassInfoMixin<MyPass> {
  PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM) {
    // walk instructions, transform, ...
    return PreservedAnalyses::none();
  }
};
Pass Anatomy
Analysis Passes
Iterating the CFG
Walk functions, basic blocks, and instructions. Pattern: for (BasicBlock &BB : F), for (Instruction &I : BB). Learn to use dyn_cast<BranchInst>(&I) for instruction type checking. This is boilerplate you'll type hundreds of times.
CFG Traversal
Dominance Analysis
A block A dominates B if every path to B goes through A. Crucial for: finding where to hoist code, validating SSA, loop analysis. Access via DominatorTree &DT = AM.getResult<DominatorTreeAnalysis>(F).
DominanceDomTree
Alias Analysis
Can two pointers point to the same memory? If not, loads/stores can be reordered. AliasAnalysis returns MustAlias, MayAlias, or NoAlias. Fundamental for auto-vectorization and memory-level parallelism.
Alias Analysis
Dead Code Analysis Pass (Project)
Write a pass that identifies unreachable basic blocks (blocks with no predecessors in the CFG, excluding the entry block) and instructions with no users that also have no side effects. Print a report. This teaches you CFG traversal + use-def chains.
Build This
Transform Passes
Instruction Combining (instcombine)
The most important optimization pass. Replaces instruction patterns with cheaper equivalents. Examples: x * 2 → x << 1, x + 0 → x, x / 2 → x >> 1 (unsigned). Study the existing instcombine source — it's 50k lines of patterns and teaches you how to think about transformations.
; Before instcombine
%r = mul i32 %x, 2

; After instcombine
%r = shl i32 %x, 1
instcombine
Dead Code Elimination (DCE/ADCE)
Remove instructions whose results are never used. ADCE (Aggressive DCE) also removes unreachable blocks. Implement a basic DCE: iterate instructions in reverse, if I.use_empty() and instruction has no side effects, erase it.
DCEBuild This
Loop Transformations
LICM (Loop-Invariant Code Motion): if a computation's inputs don't change in a loop, hoist it outside. Loop Unrolling: replicate loop body N times to reduce branch overhead. Loop Vectorization: convert scalar loops to SIMD. These are where most performance gains come from in real-world code.
LICMUnrollingVectorization
Strength Reduction Pass (Project)
Write a pass that finds multiply-by-power-of-2 patterns (mul i32 %x, 4) and replaces them with shifts (shl i32 %x, 2). Extend to handle division. Test with opt --load-pass-plugin=./libMyPass.so -passes="strength-reduce".
Build This
Inlining
Replace a function call with the function body. Eliminates call overhead and enables further optimizations across call boundaries. Learn the inlining cost model: LLVM uses a heuristic based on instruction count. InlineFunction() utility in LLVM API.
Inlining
LLVM Data Structures (ADT Library)
SmallVector, SmallString, ArrayRef
SmallVector<T, N>: vector with N elements on the stack before heap allocation. Avoids allocations for small collections (most compiler data). ArrayRef: non-owning reference to any array-like container. Use these everywhere in compiler code — never raw std::vector for IR-level work.
ADTSmallVector
StringRef, Twine
StringRef: non-owning reference to a string — zero-copy. Twine: lazy string concatenation tree. Never allocate intermediate strings in hot paths. Profilers hate std::string in compilers.
StringRefTwine
DenseMap, DenseSet
Hash maps/sets optimized for pointer keys (common in IR — Value*, BasicBlock*). Much faster than std::unordered_map for small-to-medium sizes due to open addressing and cache locality.
DenseMap
Use-Def Chains
Every Value in LLVM has a list of uses. Iterate with for (Use &U : V.uses()). Replace all uses with V.replaceAllUsesWith(NewV). This is the foundation of every transformation — finding what uses what.
Use-Def
05
Month 10+ · Specialization
Choose Your Track
Goal: Deep mastery in one high-value niche. All three tracks are hiring. Pick based on what excites you — motivation beats market research at this level.
Track A · AI Hardware
MLIR & AI Compilers
  • MLIR dialects (Linalg, Affine, Vector, GPU)
  • Lowering pipelines: PyTorch/XLA → MLIR → LLVM IR
  • Tiling and fusion for tensor ops
  • Writing a custom MLIR dialect
  • Polyhedral optimization model
  • Target: IREE, ONNX-MLIR, Triton (GPU)
  • Companies: Google, Apple (Core ML), Nvidia (Triton)
Track B · Security
Obfuscation & Binary Analysis
  • Control Flow Flattening pass
  • Instruction substitution (replace ops with equiv)
  • Bogus control flow injection
  • String encryption passes
  • LLVM-based sanitizers (ASan, UBSan internals)
  • Study: OLLVM, Hikari, o-llvm
  • Companies: Security firms, game anti-cheat, DRM
Track C · New Hardware
Custom Backend / CPU
  • TableGen — describing instructions declaratively
  • Target machine classes hierarchy
  • Register file definition
  • Instruction selection (DAG-to-DAG)
  • Instruction scheduling
  • ABI & calling conventions
  • Companies: Chip startups, embedded, RISC-V ecosystem
Resources for All Tracks
LLVM Source Code as Textbook
The best resource at this stage is the source itself. llvm/lib/Transforms/ contains all the production passes. llvm/lib/Target/X86/ is the best-commented backend. Read real code, not tutorials.
github.com/llvm/llvm-project
Contribute to LLVM
Start with good-first-issues on Phabricator/GitHub. Fix a miscompile, improve a diagnostic, add a missing peephole to instcombine. LLVM contributors are among the most rigorous code reviewers in open source — the feedback is invaluable.
Open Sourcereviews.llvm.org