| name | lattner-compiler-infrastructure |
| description | Write compiler and toolchain code in the style of Chris Lattner, creator of LLVM, Clang, Swift, and MLIR. Emphasizes modular compiler design, reusable infrastructure, progressive lowering, and pragmatic language evolution. Use when building compilers, language tools, or performance-critical infrastructure. |
| tags | llvm, clang, swift, mlir, compiler, ir, optimization, toolchain, language-design, code-generation |
Chris Lattner Style Guide
Overview
Chris Lattner created LLVM (the compiler infrastructure that powers most modern compilers), Clang (the C/C++/Objective-C frontend), Swift (Apple's systems language), and MLIR (multi-level intermediate representation). His work fundamentally changed how compilers are built and how languages evolve.
Core Philosophy
"The key insight of LLVM is that compiler infrastructure should be reusable."
"Good IR design is about finding the right level of abstraction."
"Languages should evolve based on real-world usage, not theoretical purity."
Lattner believes in building robust, reusable infrastructure that enables an ecosystem of tools—not one-off solutions.
Design Principles
-
Modular Infrastructure: Build reusable components, not monolithic systems.
-
Progressive Lowering: Transform through well-defined IR levels.
-
Library-First Design: Compilers are libraries, not just executables.
-
Pragmatic Evolution: Languages improve through real usage feedback.
When Writing Compiler Code
Always
- Design IRs with clear semantics and invariants
- Make passes composable and reusable
- Provide excellent diagnostics and error messages
- Build infrastructure others can extend
- Think about the entire compilation pipeline
- Document design decisions and tradeoffs
Never
- Build closed, monolithic compiler architectures
- Sacrifice usability for implementation convenience
- Ignore error recovery and diagnostics
- Let optimization passes have hidden dependencies
- Couple frontend concerns with backend concerns
- Design IRs without considering transformations
Prefer
- SSA form for optimization IRs
- Explicit type systems over implicit
- Library APIs over command-line tools
- Incremental compilation where possible
- Clear phase ordering over ad-hoc passes
- Compositional design over special cases
Code Patterns
LLVM IR Philosophy
; LLVM IR: explicit, typed, SSA form
; Every value has exactly one definition
; Control flow is explicit
define i32 @factorial(i32 %n) {
entry:
%cmp = icmp sle i32 %n, 1
br i1 %cmp, label %base, label %recurse
base:
ret i32 1
recurse:
%n_minus_1 = sub i32 %n, 1
%fact_sub = call i32 @factorial(i32 %n_minus_1)
%result = mul i32 %n, %fact_sub
ret i32 %result
}
; Key properties:
; - SSA: each %variable defined exactly once
; - Typed: every operation has explicit types
; - Explicit control flow: br, ret, etc.
; - No hidden state or side effects in IR
Pass Infrastructure Design
class MyOptimizationPass : public PassInfoMixin<MyOptimizationPass> {
public:
PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM) {
auto &DT = AM.getResult<DominatorTreeAnalysis>(F);
auto &LI = AM.getResult<LoopAnalysis>(F);
bool Changed = false;
for (auto &BB : F) {
Changed |= optimizeBlock(BB, DT, LI);
}
if (!Changed)
return PreservedAnalyses::all();
PreservedAnalyses PA;
PA.preserve<DominatorTreeAnalysis>();
return PA;
}
private:
bool optimizeBlock(BasicBlock &BB, DominatorTree &DT, LoopInfo &LI);
};
extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo
llvmGetPassPluginInfo() {
return {
LLVM_PLUGIN_API_VERSION, "MyPass", "v0.1",
[](PassBuilder &PB) {
PB.registerPipelineParsingCallback(
[](StringRef Name, FunctionPassManager &FPM,
ArrayRef<PassBuilder::PipelineElement>) {
if (Name == ) {
FPM.(());
;
}
;
});
}
};
}
Diagnostic Excellence
class DiagnosticEngine {
public:
void diagnose(SourceLoc Loc, Diagnostic Diag) {
emitDiagnostic(Loc, Diag.getKind(), Diag.getMessage());
emitSourceSnippet(Loc);
for (auto &FixIt : Diag.getFixIts()) {
emitFixIt(FixIt);
}
for (auto &Note : Diag.getNotes()) {
emitNote(Note);
}
}
};
Progressive Lowering (MLIR Style)
%result = linalg.matmul ins(%A, %B : tensor<4x8xf32>, tensor<8x16xf32>)
outs(%C : tensor<4x16xf32>) -> tensor<4x16xf32>
%tiled = scf.for %i = %c0 to %c4 step %c2 {
%slice_a = tensor.extract_slice %A[%i, 0][2, 8][1, 1]
%slice_c = tensor.extract_slice %C[%i, 0][2, 16][1, 1]
%computed = linalg.matmul ins(%slice_a, %B) outs(%slice_c)
scf.yield %computed
}
%vec = vector.contract {indexing_maps = [...], kind = #vector.kind<add>}
%vec_a, %vec_b, %vec_c : vector<2x8xf32>, vector<8x16xf32> into vector<2x16xf32>
Type System Design
protocol Numeric {
static func +(lhs: Self, rhs: Self) -> Self
static func *(lhs: Self, rhs: Self) -> Self
}
protocol Collection {
associatedtype Element
associatedtype Index: Comparable
var startIndex: Index { get }
var endIndex: Index { get }
subscript(position: Index) -> Element { get }
}
func sum<T: Numeric>(_ values: [T]) -> T {
values.reduce(.zero, +)
}
func find<T: >( : , : []) -> ? {
(index, element) array.enumerated() {
element value {
index
}
}
}
<, : > {
success()
failure()
}
Compiler as Library
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Tooling/Tooling.h"
class FunctionFinder : public RecursiveASTVisitor<FunctionFinder> {
public:
bool VisitFunctionDecl(FunctionDecl *FD) {
if (FD->hasBody()) {
llvm::outs() << "Found function: " << FD->getName() << "\n";
analyzeComplexity(FD);
}
return true;
}
private:
void analyzeComplexity(FunctionDecl *FD);
};
int main(int argc, const char **argv) {
auto ExpectedParser = CommonOptionsParser::create(argc, argv, MyCategory);
if (!ExpectedParser) {
llvm::errs() << ExpectedParser.takeError();
return 1;
}
;
Tool.(<MyFrontendAction>().());
}
Memory Ownership in Swift
class Node {
var value: Int
var children: [Node]
init(value: Int) {
self.value = value
self.children = []
}
}
func processBuffer(_ buffer: borrowing [UInt8]) -> Int {
buffer.reduce(0, +)
}
func consumeBuffer(_ buffer: consuming [UInt8]) -> [UInt8] {
var result = buffer
result.append(0)
return result
}
struct LargeData {
private var storage: Storage
mutating func modify() {
if !(storage) {
storage storage.copy()
}
storage.data[]
}
}
IR Design Principles
Intermediate Representation Design
══════════════════════════════════════════════════════════════
Level Abstraction Purpose
────────────────────────────────────────────────────────────
Source Syntax trees Parsing, early semantic
AST/HIR Typed trees Type checking, inference
MIR/SIL Typed CFG Optimization, ownership
LLVM IR Typed SSA Machine-independent opt
Machine IR Target ops Instruction selection
Assembly Text Final output
Key principles:
• Each level has ONE clear purpose
• Lowering is progressive and well-defined
• Analyses valid at one level may not be at another
• Transformations declare their requirements
Mental Model
Lattner approaches compiler design by asking:
- What's the right abstraction level? Different problems need different IRs
- Is this reusable? Build infrastructure, not one-off tools
- What's the user experience? Diagnostics, error recovery, tooling
- How will this evolve? Design for change and extension
- Can others build on this? Library-first, composable design
Signature Lattner Moves
- LLVM's pass manager: Modular, composable optimization passes
- Clang's diagnostics: The gold standard for helpful error messages
- Swift's optionals: Explicit nullability without verbosity
- MLIR's dialect system: Multi-level IR with extensible operations
- Library-first design: Compilers as reusable infrastructure
- Progressive lowering: Clear transformation stages
- SwiftUI's result builders: Compiler magic that feels natural