Compilers
LLVM: compiler infrastructure
In 2000, every compiler wrote its own backend from scratch: optimizations, register allocator, code generator for every architecture. Today Rust, Swift, Kotlin Native, Julia, and a dozen other languages use the same backend - LLVM. Building a new language now means writing a frontend that emits LLVM IR and getting native code for x86, ARM, RISC-V, and WebAssembly without writing a line of platform-specific code.
- **rustc** compiles all Rust through the LLVM backend. `cargo build --release` invokes LLVM optimizations (equivalent to -O2) and produces native code for the target architecture
- **Apple Silicon toolchain**: swiftc, the Metal shader compiler, and Xcode Instruments all use LLVM. The 2020 move to ARM took less than a year because LLVM already supported AArch64
- **Google BOLT**: a post-link binary optimizer based on LLVM. Applied to production Chrome binaries, it delivers 5-10% speedup without source changes
LLVM IR
LLVM IR (Intermediate Representation) is a typed, RISC-like language in SSA form. It is the connecting tissue. Frontends (Clang, rustc, swiftc) emit IR; backends (x86, ARM, RISC-V, WebAssembly) compile IR into machine code. IR exists in three formats: human-readable .ll text, binary .bc bitcode, and in-memory objects.
LLVM is used by Clang (C/C++/ObjC), rustc (Rust), swiftc (Swift), kotlinc-native (Kotlin Native), the Julia JIT, Emscripten (Wasm), and XLA (TensorFlow/JAX ML compiler). A shared IR lets all these languages get the same optimizations for free. LLVM 18 (2024) supports more than 20 target architectures.
Why does LLVM IR use SSA (Static Single Assignment) form?
LLVM pass infrastructure
An LLVM pass is a transformation or analysis on IR. The New Pass Manager (NPM, LLVM 13+) organizes passes into a pipeline: FunctionPass runs on one function, ModulePass on the whole module, LoopPass on loops. Passes are combined in a PassManager that runs them in the right order and caches analysis results.
The LLVM pass API lets you write custom optimizations as plugins. Clang-Tidy and many static analyzers are implemented as LLVM passes. Intel Embree (ray tracing), the Apple Metal compiler, and the NVIDIA PTX backend are all custom LLVM passes. A simple analysis pass is ~200 lines of C++ plus CMake.
What does the mem2reg pass do in LLVM?
LLVM module system and LTO
An LLVM Module is the unit of compilation: it contains functions, globals, metadata, and a target triple. Link Time Optimization (LTO) merges modules before optimization, allowing functions in different .cpp files to be inlined into each other. ThinLTO is the distributed variant: modules are optimized in parallel with summary-based cross-module analysis.
Google Chrome with ThinLTO (2020): 10-15% smaller binary, 2-5% faster startup. Firefox with LTO on ARM Android saved 10MB of APK size. The Rust cargo release profile uses ThinLTO through LLVM by default. LTO is especially beneficial for one-liner functions across .cpp files that would otherwise stay as function calls.
What is the advantage of ThinLTO over full LTO?
LLVM toolchain
LLVM is not just a compiler. It is a full toolchain: Clang (C/C++), clang-format/clang-tidy (linting), lld (linker, 5x faster than GNU ld), lldb (debugger), AddressSanitizer, MemorySanitizer, UBSan, libfuzzer, clang-query, clangd (Language Server).
LLVM 16+ includes Polly (loop optimization for HPC), Flang (Fortran frontend), and BOLT (post-link binary optimizer). BOLT uses a PGO profile to reorganize code. Google uses BOLT on production binaries and gets 5-15% speedup without source changes. Apple uses LLVM as the foundation for its entire compiler infrastructure (Xcode).
LLVM is just Clang (a C/C++ compiler)
LLVM is compiler infrastructure: an IR, a pass framework, backends for 20+ architectures, a linker (lld), a debugger (lldb), sanitizers, a fuzzer, and a Language Server (clangd)
Clang is only one LLVM frontend. Rust, Swift, Julia, Kotlin Native, Emscripten, XLA, and dozens of other projects use LLVM IR and backends without touching Clang
What does AddressSanitizer (ASan) do and what is its overhead?
Key ideas
- LLVM IR is a typed SSA language that connects frontends (Clang/rustc/swiftc) with backends (x86/ARM/Wasm). 20+ target architectures from a single IR
- Pass infrastructure: ~150 passes in the -O2 pipeline. mem2reg builds SSA, instcombine simplifies algebra, inline inlines functions
- Full toolchain: lld (5x faster than GNU ld), ASan/TSan/UBSan sanitizers, the clangd LSP, libFuzzer, and the BOLT post-link optimizer
Related topics
LLVM is the foundation for many compiler projects:
- LLVM passes — A detailed look at writing your own function/module passes
- MLIR — MLIR extends the LLVM IR idea for ML compilers with dialects and progressive lowering
- WebAssembly — Emscripten and the LLVM wasm32 backend compile C/C++ to .wasm through LLVM IR
Вопросы для размышления
- LLVM IR is a good abstraction for most languages, but Julia and GHC have their own IRs (Julia IR, STG). Why don't they use LLVM IR as their sole representation?
- ThinLTO speeds up LTO through parallelization. Which categories of interprocedural optimization can ThinLTO not perform compared to full LTO?
- BOLT reorganizes binary code based on a profile. Why can a compiler not do the same thing at compile time without a separate post-link step?