How LLVM Works: The IR That Took Over Modern Computing
A two-person grant at the University of Illinois became the compiler substrate under Apple, Android, the PlayStation, Google and Meta datacenters, and every serious AI stack shipping today.
Intro
This is the story of how LLVM’s intermediate representation ate modern computing, told at the level of the instructions themselves, with the source numbers checked.
LLVM won because of two decisions made in one semester in the year 2000: a single, strongly typed, target-independent intermediate representation in SSA form that can be shipped and executed on its own, and a modular library architecture that let anyone reuse a compiler pass without dragging the whole compiler along.
Everything else, the trillion-dollar reach across mobile, cloud, consoles and AI, is a consequence of those two decisions.
The current release, LLVM 22.1, shipped in February 2026 and still carries the exact architecture Chris Lattner sketched over a winter break twenty-five years ago.
The hole that GCC left
Why the dominant open compiler of 2000 could not become infrastructure
Compilers are the one technology every other technology has to pass through. They translate every line a human writes into something a processor can run, which means a compiler that is easy to build on is a lever under the entire software economy.
By the year 2000 the open lever was the GNU Compiler Collection, and it was showing its age.
GCC, launched in the 1980s, had become foundational: it built every Linux system, every Apple computer of the era, and a large share of the open source world.
But as Vikram Adve and Chris Lattner recount in their June 2026 retrospective in Communications of the ACM, GCC lagged on the techniques that were becoming central to modern compilation.
It was written in C rather than a modern object-oriented language, it lacked cross-module interprocedural optimization, and it did not gain Static Single Assignment form until GCC 4.0 in 2005.
Static Single Assignment, or SSA, is the representation on which most serious dataflow optimization is built, and GCC arrived late to it.
The deeper problem was structural. GCC was monolithic. You could not lift a piece of it out, an optimizer, a code generator, an analysis, and reuse that piece without pulling most of the compiler with it.
That single fact foreclosed most of the interesting futures: load-time and just-in-time compilation of mobile code, embedded scripting languages, sandboxed browser extensions, graphics shader compilation.
The managed-language runtimes of the day, the Jikes RVM for Java, Mono and later Roslyn for the CLR, were genuinely excellent inside their domain and advanced garbage collection and JIT compilation considerably.
But each imposed a language object model and a set of runtime semantics that ruled it out for the vast majority of other languages, and for anything that needed to ship mobile code without a managed runtime underneath it.
So the opening was specific and it was large: no open system combined ahead-of-time optimizing compilation of the kind static languages need with the self-contained, shippable, just-in-time-capable code representation that managed languages have, and offered both to every language rather than one family. Filling that hole is the whole of what follows.
The federal money that made it possible
Adve had come to Illinois with a research program aimed at flexible dynamic compilation for arbitrary languages.
His CAREER proposal to the National Science Foundation‘s Next Generation Software program, submitted in 2000, funded the work; the acknowledgments in the CACM piece name grant NSF EIA-0093426, and Adve’s own record lists the CAREER award at roughly $499,211.
That grant, Adve and Lattner write, “would prove pivotal to the group’s early work on LLVM,” and it supported both authors through most of the project’s early life.
The point they press, and it is worth pressing, is that this was blue-sky research with an unknowable outcome, funded on the condition that the artifacts be released as open source. The trillion-dollar downstream was not forecast. It was seeded.
A naming footnote
LLVM originally stood for Low Level Virtual Machine. The name outgrew the acronym years ago; the project’s own position, stated on llvm.org, is that “LLVM” is now simply the name of the umbrella project and has “little to do with traditional virtual machines.” We use it as a proper noun throughout.
The IR is the entire argument
Everything LLVM can do is downstream of a single design object
If you understand one thing about LLVM, understand the intermediate representation, because the reach of the project is a property of the IR and not of any particular optimizer or backend.
LLVM IR is a strongly typed, RISC-like virtual instruction set. It abstracts away the machine, there are no physical registers and no addressing modes, but it stays low enough to represent operating systems, kernels and application code without imposing any language’s object model on top.
Three things about it do most of the work.
It is in SSA form. Every value is assigned exactly once, and each use points back to a single, unambiguous definition. Where control flow merges, an explicit phi instruction selects the incoming value based on which predecessor block you arrived from.
This is the representation that Ron Cytron and colleagues formalized in 1991, and it is what makes dataflow optimization tractable: because there is one definition per value, the flow from definitions to uses is explicit, and reordering transformations are freed from spurious dependencies.
Consider, for example, a trivial loop.
int sum_to(int n){
int s = 0;
for (int i = 0; i < n; i++)
s += i;
return s;
}Compiled at -O0, Clang first emits the naive version: local variables become stack slots via alloca, and every read and write is an explicit load or store.
Note the pointer type is simply ptr, a point we return to below.
define i32 @sum_to(i32 %n) {
entry:
%i = alloca i32
%s = alloca i32
store i32 0, ptr %s
store i32 0, ptr %i
br label %cond
; ... loads and stores on every iteration ...
}Then the mem2reg pass (memory-to-register promotion, one of the first transformations any pipeline runs) proves those stack slots never escape and rewrites them into pure SSA values.
The stack traffic disappears, and phi nodes appear at the loop header and at the exit merge:
define i32 @sum_to(i32 %n) {
entry:
%pos = icmp sgt i32 %n, 0
br i1 %pos, label %loop, label %exit
loop: ; preds = %entry, %loop
%i = phi i32 [ 0, %entry ], [ %i.next, %loop ]
%s = phi i32 [ 0, %entry ], [ %s.next, %loop ]
%s.next = add nsw i32 %s, %i
%i.next = add nsw i32 %i, 1
%done = icmp eq i32 %i.next, %n
br i1 %done, label %exit, label %loop
exit: ; preds = %entry, %loop
%r = phi i32 [ 0, %entry ], [ %s.next, %loop ]
ret i32 %r
}The nsw flags mark the additions as having no signed wraparound, a promise the front end makes on the compiler’s behalf so later passes can optimize more aggressively.
This is the texture of LLVM IR: low-level operations, explicit control flow, and a scattering of flags and metadata that carry higher-level facts down to where they can be exploited.
It carries a real type system. Integers of arbitrary bit width (i1, i8, i32, i64), floating point types, vectors as first-class values, arrays and structures.
Vectors matter more than they look: because a vector is a first-class value, the same IR expresses Intel SSE, AVX and AVX-512, Arm Neon and SVE, and the tensor operations that machine-learning front ends emit, and the auto-vectorizer can create them from scalar loops using dependence analysis.
Aggregate access goes through getelementptr, the address-arithmetic instruction that computes a field or element address without loading it:
; struct Point { i32 x; i32 y; }; compute &p->y
%y.ptr = getelementptr inbounds { i32, i32 }, ptr %p, i32 0, i32 1
%y = load i32, ptr %y.ptrIt is extensible through intrinsics. Intrinsic functions are built-ins with defined semantics that behave like ordinary calls, so a pass that does not care about them can ignore them safely, while selected passes and the backend can recognize llvm.memcpy, llvm.masked.load, the matrix and vector-predication intrinsics, atomics and the rest, and lower them specially.
This is the mechanism that let the IR absorb decades of new hardware features without a language change every time.
The complexity was not free, and the authors are candid about it. LLVM IR launched in 2003 with fewer than thirty-five polymorphic operations. It now carries more than one hundred and seventy-five, including in excess of one hundred and ten vector operations defined as intrinsics, plus hundreds of further semantic intrinsics for garbage collection, memory ordering, synchronization and stack manipulation.
Most of that can be ignored when writing a front end or an optimization pass, but it makes writing a new backend, or a formal semantics for the IR, genuinely hard.

Three forms of one thing, and the opaque-pointer turn
The IR exists in three isomorphic forms: a human-readable textual assembly (.ll), a dense binary serialization called bitcode (.bc), and an in-memory form the compiler manipulates directly.
They are interconvertible without loss, which is exactly what lets the IR be shipped and compiled later rather than only used in-process.
clang -S -emit-llvm foo.c -o foo.ll # textual IR
clang -c -emit-llvm foo.c -o foo.bc # bitcode
llvm-as foo.ll -o foo.bc # text -> bitcode
llvm-dis foo.bc -o foo.ll # bitcode -> textThe IR is not frozen. Two recent changes show the project still reshaping its own foundation. The larger one is opaque pointers. For most of LLVM’s life a pointer carried its pointee type, so you wrote i32*.
That information was redundant with the types on the loads and stores that actually used the pointer, and it forced a swarm of no-op bitcast instructions.
Per the official documentation, opaque pointers, a single untyped ptr, became the default in LLVM 15 (2022), and typed pointers were removed entirely in LLVM 17 (2023). Every IR sample in this article uses ptr for that reason.
The second, newer change landed in LLVM 22: a dedicated ptrtoaddr instruction that extracts a pointer’s integer address while separating it from provenance tracking, which the older ptrtoint conflated.
It is a small instruction with a real purpose, sharper semantics for the memory model, and it is the kind of change that keeps the IR honest as verification tools grow more demanding.
Passes, and the separation of concerns
The modular library architecture is the part that actually won
The most cited technical novelty of LLVM is the IR. The most consequential design decision, the authors argue and we agree, is not technical at all in the usual sense: it is the modular, library-based architecture, built on a strict adherence to separation of concerns.
A compiler built on LLVM is assembled from libraries with clean interfaces rather than carved out of a monolith. That is why pieces of LLVM turn up in circuit-design tools, symbolic-execution engines, browser sandboxes and quantum-computing compilers, uses the authors freely admit they never imagined.
Concretely, transformation is organized as a pipeline of passes. Analysis passes compute facts (dominator trees, alias analysis, loop information, scalar evolution) and cache them; transformation passes consume those facts and rewrite the IR, declaring which analyses they preserve so the manager knows what to recompute.
Since LLVM 13 (2021) the default engine is the new pass manager, a rewrite that made analysis caching explicit and pass composition cheaper.
A canonical -O2 pipeline threads dozens of passes together. A representative slice:
SROAandmem2regto lift memory into SSA values;EarlyCSEandGVNto eliminate redundant computation;InstCombineto canonicalize and simplify instruction patterns;SimplifyCFGto clean up control flow; the inliner to pull small callees into their callers;LICMto hoist loop-invariant code;SCCPfor sparse conditional constant propagation;IndVarsandLoopUnrollfor loops;
then the two vectorizers, the loop vectorizer and the SLP (superword-level parallelism) vectorizer, near the end. You can watch any of it run.
# run one pass and print the result
opt -passes=“mem2reg” -S sum.ll -o -
# see the full default -O2 pipeline the pass builder constructs
opt -passes=“default<O2>” -print-pipeline-passes -S sum.ll -o /dev/nullWriting a pass is deliberately cheap, which is the whole point. The 2003 release announcement claimed new users could “write their first LLVM pass in hours,” and the modern out-of-tree plugin form holds to that. Here is a complete, loadable function pass in the new pass manager:
#include “llvm/IR/PassManager.h”
#include “llvm/Passes/PassBuilder.h”
#include “llvm/Passes/PassPlugin.h”
using namespace llvm;
struct CountAllocas : PassInfoMixin<CountAllocas> {
PreservedAnalyses run(Function &F, FunctionAnalysisManager &) {
unsigned n = 0;
for (BasicBlock &BB : F)
for (Instruction &I : BB)
if (isa<AllocaInst>(I)) ++n;
errs() << F.getName() << “: “ << n << “ allocas\n”;
return PreservedAnalyses::all(); // we changed nothing
}
};
extern “C” LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo
llvmGetPassPluginInfo() {
return {LLVM_PLUGIN_API_VERSION, “CountAllocas”, LLVM_VERSION_STRING,
[](PassBuilder &PB) {
PB.registerPipelineParsingCallback(
[](StringRef Name, FunctionPassManager &FPM,
ArrayRef<PassBuilder::PipelineElement>) {
if (Name == “count-allocas”) {
FPM.addPass(CountAllocas()); return true;
}
return false;
});
}};
}clang++ -shared -fPIC CountAllocas.cpp -o CountAllocas.so \
$(llvm-config --cxxflags)
opt -load-pass-plugin=./CountAllocas.so \
-passes=“count-allocas” -disable-output sum.bcThe IR was the innovation everyone cites. The library architecture was the innovation that let ten thousand projects reuse a compiler without inheriting one.
Five capabilities no one else has together
A twenty-year-old assertion from the CGO 2004 paper that still holds
In their 2004 paper at the International Symposium on Code Generation and Optimization, Lattner and Adve made a specific claim: LLVM provides a combination of five capabilities that no other system offers at once.
The paper won the conference’s Most Influential Paper award ten years later, and the retrospective restates the claim as still true more than twenty years on. The five:
CapabilityWhat it meansPersistent program informationThe IR can be preserved across an application’s whole lifetime, so optimization can happen at compile time, link time, load time, run time and even idle time between runs on the user’s machine.
Offline code generationRegardless of persistence, code can be lowered to efficient native machine code offline, using techniques too expensive to run at runtime.User-specific profilingProfile data can be gathered from deployed software as the end user actually runs it, then fed back into profile-guided optimization.
Transparent runtime modelThe IR imposes no object model, exception semantics or runtime, so it serves any language or mix of languages.Uniform whole-program compilationBecause it is language-independent, an entire application, including its language runtime and system libraries, can be optimized together after linking.
The argument is not that each property is unique; it is that the conjunction is. Managed virtual machines like the JVM or the .NET CLI deliver user-specific profiling and partially deliver persistence and whole-program compilation, but they do not deliver a transparent runtime model, which is what confines them to a language family.
And when they do offer offline code generation, the authors note, they do it at the expense of persistence and profiling. GPU instruction sets such as CUDA’s PTX and OpenCL’s SPIR-V, and the JITs behind JavaScript, Python and WebAssembly, cover some combination of persistence and profiling and none of the rest.
GCC, superb at offline code generation, was never designed to ship a language-independent representation for lifelong compilation at all.

From IR to silicon
Instruction selection, register allocation, and the TableGen machine
Optimized IR is still target-independent. Turning it into machine code is the backend’s job, and LLVM has, notably, two backends that do it.
The mature path is SelectionDAG. Each basic block is turned into a directed acyclic graph of operations; the DAG is legalized so every node is something the target can actually do; instruction selection matches subgraphs against target patterns to pick real machine instructions; the result is scheduled into a linear order; and then register allocation and emission follow.
The newer path is GlobalISel, designed to eventually replace SelectionDAG. It works on the whole function rather than one block at a time and skips the DAG entirely: an IRTranslator lowers IR to generic machine instructions, a Legalizer makes them legal, RegBankSelect assigns register banks, and an InstructionSelect pass chooses final opcodes.
As of the current release GlobalISel is the default at -O0 on AArch64 and is used for AMD GPUs, and, as an ongoing LLVM forum discussion from February 2026 shows, work to enable it more broadly on AArch64 is still live. Two instruction selectors, maintained in parallel for years, is itself a statement about how much the project values getting this layer right.
Register allocation is where virtual registers, of which the IR has infinitely many, meet the finite physical register file. The default at -O1 and above is the greedy allocator, which orders live ranges by priority and spills to the stack when it must; -O0 uses a fast allocator that trades quality for speed.
Both are among the passes now being handed to machine learning, a thread we pick up in section 10.
Almost none of this is hand-written per target. Targets are described declaratively in TableGen, a domain-specific language whose .td files specify registers, instructions, calling conventions, scheduling models and selection patterns; a generator turns them into the C++ tables the backend uses.
A single instruction definition ties an assembly syntax to a SelectionDAG pattern:
def ADDrr : Instruction {
let Namespace = “MyTarget”;
let OutOperandList = (outs GPR:$dst);
let InOperandList = (ins GPR:$src1, GPR:$src2);
let AsmString = “add $dst, $src1, $src2”;
// match (add a, b) in the DAG and emit this instruction
let Pattern = [(set i32:$dst, (add i32:$src1, i32:$src2))];
}This is why standing up a new architecture in LLVM, RISC-V being the most visible recent example, is a matter of writing target descriptions rather than a compiler.
The LLVM 22 notes alone record new scheduling models for NVIDIA’s Olympus CPU, additional Arm C1 cores, Intel Wildcat Lake and Nova Lake, and fresh RISC-V vector extensions, all arriving as target-description work on top of the shared infrastructure.
The link-time frontier, and why ThinLTO exists
Whole-program optimization that actually scales to a datacenter build
Persistent IR unlocks link-time optimization: instead of discarding each source file’s IR after compiling it, you keep the bitcode, hand all of it to the linker, and optimize across module boundaries, inlining a function defined in one file into a caller in another, propagating constants program-wide.
Classic full LTO merges every module into one giant IR module and optimizes it as a unit. The result is excellent and the cost is brutal: the whole program has to fit in one process, and the work is largely serial, which is a non-starter when the program is a datacenter binary with tens of thousands of translation units.
ThinLTO, first presented at EuroLLVM 2015 by Teresa Johnson, Mehdi Amini and David Li and merged into LLVM over the following year, is the answer that made link-time optimization practical at industrial scale. Each module is compiled to bitcode alongside a compact summary: a thin index of its call graph and symbols.
A cheap global step reads only the summaries, decides cross-module actions such as which functions to import for inlining, and then the heavy per-module optimization runs in parallel, importing only the specific functions each module needs. It recovers most of full LTO’s benefit at a fraction of the memory, and it parallelizes. The numbers are quite stark.
Full LTO can inflate compile time two to three fold and demands that the whole program fit in a single process; ThinLTO’s per-module summaries add under one percent to bitcode size, roughly 0.8 percent for Clang built without debug information, and keep build time and peak memory close to a non-LTO build.
On some benchmarks ThinLTO even edges out full LTO, because its scalable design lets each module run a more aggressive backend pipeline than a single monolithic module could afford.
LLVM 22 is now upstreaming Distributed ThinLTO (DTLTO), which pushes those parallel backend jobs across a build cluster, with caching for incremental rebuilds. Turning it on is a flag.
clang -flto=thin -O2 a.c b.c c.c -o app # scalable cross-module LTO
clang -flto -O2 a.c b.c c.c -o app # full (monolithic) LTO
This is the mechanism underneath one of the headline claims: that all of Google’s and Meta’s datacenter C and C++ is built with LLVM. It is not merely that Clang compiles the files; it is that link-time optimization across an entire service is tractable because ThinLTO made it so.
What LLVM actually runs
The reach, with the adoption dates checked against primary sources
The scale claims in the retrospective are large, and they hold up. LLVM long ago replaced GCC as the foundation of Apple’s software across every device line. Google uses it for Android and the Android NDK. Applications on Qualcomm’s Snapdragon processors are built in substantial part with LLVM.
The two dominant CPU vendors for PCs and servers, Arm and Intel, retired their proprietary compilers, ARMCC and ICC, in favor of LLVM-based toolchains. Google’s and Meta’s datacenter C and C++ is compiled with it. Sony’s PlayStation 4 and 5 and the Nintendo Switch use Clang as their primary application toolchain.
And the AI stack leans on it heavily, through NVIDIA’s CUDA compiler, through the Triton compiler used by PyTorch and OpenAI, and most broadly through MLIR.
The adoption did not happen at once, and the sequence matters, because it shows a single seed (Apple, via Lattner’s hiring) catalyzing an industry.

The bitcode chapter is the sharpest illustration of the “lifelong compilation” idea reaching production. For years, developers shipped iOS, watchOS and tvOS apps to Apple’s App Store not as machine code but as LLVM bitcode, and Apple compiled and specialized each app for the target device on its side.
That let one submission serve 32-bit and 64-bit Arm iPhones, low-power Apple Watch variants and Apple TV, shrank downloads, and let Apple roll out new compiler optimizations without developers rebuilding.
Apple deprecated bitcode in Xcode 14 in 2022, once its hardware had converged on Arm64 and the flexibility was no longer worth the friction. The feature retired, but it stands as the largest deployment of shippable IR the industry has seen.
Sizing the surface area
The authors put a figure on all this: software compiled with LLVM, they write, represents hundreds of billions of dollars in annual revenue across mobile, cloud, desktop, server, gaming, supercomputing and AI.
We think that undercounts it, and it is worth being precise about what can actually be measured, because “revenue compiled by LLVM” is not a single clean quantity.
The honest move is to measure the top-line revenue of the platforms whose flagship products are demonstrably built with LLVM, treat it as a floor rather than an attribution, and be explicit that this is revenue riding on LLVM-compiled software, not value the compiler captures.
Do that and the picture is not subtle. In their most recent full fiscal years three companies alone, Apple, Alphabet and NVIDIA, booked about 1.04 trillion dollars in combined revenue, and the flagship products behind every one of those dollars are built with LLVM: Apple’s entire device line through Clang and Swift, Android and Google’s hyperscale C and C++ through Clang and ThinLTO, and the AI-accelerator stack through the CUDA, Triton and MLIR compilers that lower into LLVM.

We do not sum the rows of that table: the smartphone market overlaps the Apple and Alphabet figures, and the point is the surface area, not a grand total.
But the three company revenues in Figure 4 are independently additive and clear a trillion dollars between them, which is why we are comfortable calling LLVM’s reach a trillion-dollar one. The precise figure is unauditable and we mark it accordingly in the dossier; the order of magnitude is not in doubt.
A large majority of end users across computing are, at some layer, running code that LLVM compiled.

The academic and language dividend
Two quieter forms of impact compound over time. LLVM underlies a generation of programming languages that an easy-to-target, modular back end made feasible: Swift, Rust, Julia, Halide and Mojo among them.
Rust’s memory-safety guarantees and Julia’s JIT-driven scientific performance both rest on LLVM code generation.
And in the academy, LLVM democratized compiler research and teaching: a graduate student can write a novel pass in a day and test it on large real programs immediately, and the authors report meeting undergraduates who arrived at college having already built compiler projects on LLVM in high school, “for fun.”
MLIR, and the compiler layer under modern AI
When one IR was not enough, the answer was a framework for making IRs
This is the part that matters most for anyone working on inference infrastructure, so we will spend a moment on it. LLVM IR is deliberately low-level, which is a strength for representing machines and a weakness for representing a matrix multiply.
By the time a tensor operation has been shredded into scalar loads, adds and branches, the structure a compiler most wants to exploit, the fact that this is a dense linear-algebra kernel, is gone. That loss is fine for C. It is expensive for machine learning, where the highest-value optimizations (tiling, fusion, layout, targeting a systolic array) live at exactly the level LLVM IR throws away.
MLIR, the Multi-Level Intermediate Representation, introduced in 2019 and presented at CGO 2021, is the response, and it is characteristically an infrastructure answer rather than a point solution. Instead of one IR, MLIR provides the machinery to define many coexisting IRs, called dialects, that all share the same structural substrate of operations, regions and blocks.
A compiler keeps a computation at a high level of abstraction as long as that is useful, then progressively lowers it, dialect by dialect, until it reaches the llvm dialect and finally ordinary LLVM IR, where the mature backends take over. A tensor program can begin like this:
// a matmul on tensors, before any lowering
func.func @matmul(%A: tensor<128x256xf32>,
%B: tensor<256x512xf32>,
%C: tensor<128x512xf32>) -> tensor<128x512xf32> {
%0 = linalg.matmul
ins(%A, %B : tensor<128x256xf32>, tensor<256x512xf32>)
outs(%C : tensor<128x512xf32>) -> tensor<128x512xf32>
return %0 : tensor<128x512xf32>
}From there the same operation descends through structured and loop dialects (linalg, affine, scf, tensor), gets its buffers assigned (memref, bufferization, vector), is retargeted to a device dialect (gpu, nvvm, rocdl, spirv), and finally lands in the llvm dialect and LLVM IR.
Each step is a rewrite in the same framework, and each step is where a tiling or fusion decision can be made while the structure is still visible.

For an inference audience the payoff of this layering has a name: fusion. Because progressive lowering keeps tensor-level structure visible until late, the compiler can fuse a chain of operations, a matmul followed by a bias add followed by an activation, into a single kernel, so the intermediate tensors are never written out to high-bandwidth memory and read back.
That matters because decode, the token-by-token phase of LLM serving, is bound by memory bandwidth rather than arithmetic: the accelerator spends its time moving weights and activations, not multiplying them.
Collapsing those HBM round trips is therefore one of the largest levers on tokens per second per GPU, and it is a compiler transformation, made at exactly the abstraction level that raw LLVM IR throws away.
This is the concrete reason every serious inference stack now ships a compiler rather than a fixed library of kernels, and why the layer we are describing sits directly upstream of inference cost.
This is not a research curiosity. MLIR is the backbone of TensorFlow’s XLA compiler and the broader OpenXLA ecosystem, of parts of PyTorch through Torch-MLIR, of IREE, and of OpenAI’s Triton, the kernel language a great deal of contemporary GPU work is written in. Even components of NVIDIA’s own CUDA toolkit use it.
And the underlying LLVM backend still does the final, unglamorous work of register allocation and scheduling for all of them. When people say the AI stack runs on LLVM, this is the mechanism they are pointing at: not that models are written in C, but that the compilers turning tensor graphs into GPU code are, almost universally, MLIR lowering into LLVM.
The hardware-design world is following the same path through CIRCT, which extends MLIR into digital circuit design.
The one thing LLVM is bad at
Compile speed, and the backends being built to route around it
A retrospective written by the project’s founders is not the place to look for LLVM’s weaknesses, so we will supply the missing side.
LLVM is engineered for the quality of the code it emits, and it pays for that in the time it takes to emit it. Even with optimizations disabled the pipeline is heavy, and for a developer sitting in an edit, compile, run loop the wait is a real tax.
As bjorn3, the author of Rust’s Cranelift backend, puts it, LLVM is optimized for output quality at the cost of compilation speed even when optimizations are turned off. This is the most common and most legitimate complaint about the project, and two major language communities are now building their own code generators specifically to escape it.
Rust took the incremental path. Its alternative rustc_codegen_cranelift backend uses Cranelift, a code generator built for speed rather than peak output and aimed squarely at debug builds.
The Rust team’s 2025 measurements on large real projects, among them Zed, Tauri and hickory-dns, show roughly a twenty percent cut in code-generation time and about a five percent speedup in total clean-build time, and getting the backend production-ready for local development is an official 2025 project goal.
The bargain is stated plainly: Cranelift produces code almost as fast as LLVM with optimizations off, in exchange for compiling far more quickly.
Zig went further and wrote its own backends outright. In mid-2025 it made its self-hosted x86-64 backend the default for debug builds on Linux and macOS, bypassing LLVM entirely on that path, and reported wall-clock compile improvements ranging from five to fifty percent across real projects.
The rationale is a direct indictment: because compilation time was dominated by LLVM, and because LLVM’s heavy use of shared state kept Zig’s LLVM path effectively single-threaded while its own backend parallelized code generation across cores, the only way to get materially faster was to leave.
Notably, Zig still emits LLVM bitcode when it wants LLVM’s optimizer for release builds. That is the pattern worth seeing.

Keep LLVM for the optimized release build, where its code quality is still unmatched. Route around it for the fast debug build, where its latency hurts.
The shape of both the Rust and the Zig response to LLVM’s one real weakness
That shared pattern is not a threat to the empire so much as a map of its single soft border, and it is worth being honest that the border exists. There is a second, quieter tension living inside LLVM’s own success.
MLIR’s openness invites a proliferation of dialects, and a representation that can be anything risks fragmenting into many incompatible somethings; the whole force of the original LLVM argument was that the IR won because it was one thing.
“The IR won because it was one representation” and “the future is many coexisting representations” are in genuine tension, and how MLIR holds its dialects together as their number grows is one of the more interesting open questions in the field. So far the shared substrate has held.
The bet is that it keeps holding.
Where LLVM goes next
Verified compilation, auto-generated backends, machine-learned heuristics, hardened code
The retrospective closes on active research, and several threads are worth stating precisely because they point at the next decade of the project.
Proving the optimizer correct
A compiler bug silently miscompiles correct source into wrong machine code, and optimizers are where these bugs breed.
Alive2 (Nuno Lopes and colleagues, PLDI 2021) attacks this with bounded translation validation: it takes an IR function before and after a transformation and uses an SMT solver to check whether the rewrite is sound for all inputs, encoding LLVM’s tricky semantics, including undefined behavior and poison, into the query.
Alive2 runs continuously against LLVM and has found real miscompilations in core passes such as InstCombine. The ptrtoaddr instruction we met in section 02 is part of the same trend: the project is steadily sharpening its own semantics so that this kind of verification can go further.
Generating the compiler from the chip
Two projects challenge the assumption that backends and peephole optimizations must be written by hand. Hydride (ASPLOS 2024) and MISAAL (published in the ACM’s Proceedings on Programming Languages, 2025) use program synthesis to generate compiler components, target-independent IR operations, front-end translators and backend code generators, directly from a vendor’s formal specification of an instruction set.
The reported result is striking: auto-generated compilers that match or beat a heavily hand-engineered production compiler for Halide while requiring roughly an order of magnitude less manual effort.
As instruction sets multiply, matrix and vector extensions arriving on nearly every new chip, generating the compiler from the specification rather than writing it by hand is a plausible future for the backend.
Machine learning inside the compiler, and about the compiler
There are two distinct AI-and-compiler stories, and they are easy to confuse. The first is machine learning inside LLVM: Google’s MLGO framework replaces hand-tuned heuristics for inlining-for-size and register-allocation eviction with policies trained by reinforcement learning, and these have shipped in production builds.
The second is LLVM as training data for models about code. Meta’s LLM Compiler (2024), built on Code Llama and published at Compiler Construction 2025, was trained on a corpus of 546 billion tokens of LLVM IR and assembly, then instruction-tuned to predict the effect of optimization passes; the flag-tuning and disassembly variants add a further 164 billion tokens for 710 billion in total.
Meta reports the model reaching 77 percent of the optimization benefit of an autotuning search, and a 45 percent round-trip on disassembling machine code back to IR.
The reason such a model can exist at all is the reason LLVM matters everywhere else: the sheer volume of LLVM-compiled open source makes a compiler-scale training corpus possible.
One number, corrected
The CACM retrospective states the LLM Compiler was fine-tuned on “537 billion tokens.” The figure in Meta’s paper and in the peer-reviewed Compiler Construction 2025 version is 546 billion tokens of LLVM IR and assembly. We use 546 billion, which is what the primary source reports.
Hardened code and heterogeneous parallelism
Two more directions round out the picture.
On security, LLVM and Clang are the delivery vehicle for a generation of hardening: forward-edge control-flow integrity, shadow call stacks, and the Arm hardware defenses of pointer authentication (PAC), branch target identification (BTI) and memory tagging (MTE), alongside the sanitizer family (AddressSanitizer, ThreadSanitizer, MemorySanitizer, UndefinedBehaviorSanitizer) that made whole classes of C and C++ bugs findable.
LLVM 22 even added the AArch64 build-attribute machinery that lets linkers reason about PAC and BTI compatibility across object files.
On parallelism, the HPVM project (IEEE Micro 2022) layers a hierarchical dataflow graph over LLVM IR to target heterogeneous systems, CPUs, GPUs and accelerators, from a single representation, and national laboratories are extending LLVM for exascale machines through efforts such as Los Alamos’s Kitsune and the NNSA-backed Flang Fortran front end.
What the story is actually about
Cadence, and the transferable lesson under the technology
Strip away the specifics and LLVM is a case study in how foundational infrastructure gets built, and the ingredients are unglamorous. Federal money funded early-stage research whose payoff no one could forecast, on the condition that the results be released openly.
An academic effort took known techniques, SSA, separation of concerns, retargetable code generation, interprocedural analysis, and applied all of them together more thoroughly than any production compiler had.
A commercially neutral open license, eventually Apache 2.0 with LLVM exceptions, let the result be reused, again and again, in products that competed with each other. Strong technical and community leadership, first inside Apple and then across the industry, drove adoption.
And a family of successful derivatives, Clang, Swift and MLIR chief among them, widened the reach far beyond the original core.
The result compounds on a schedule. Twenty-three years after the first release, LLVM ships a full toolchain, Clang, LLD, LLDB, libc++, compiler-rt, MLIR, Flang, Polly, BOLT, on a strict six-month train.

Adve and Lattner end their retrospective on the claim that a small, federally funded academic project produced “world-changing, foundational infrastructure,” and on the evidence it is not an overstatement.
The instruction set that Lattner sketched over a winter break in 2000, three isomorphic forms of one strongly typed, target-independent, shippable IR, is visibly the same architecture running underneath the compiler in every phone, console, datacenter and AI accelerator we have discussed.
The empire is real. It was always, underneath, an argument about a representation.
Verification dossier
Every load-bearing claim above, checked against a primary or authoritative source. Confidence tiers: A primary source, direct confirmation · B authoritative secondary · C reasonable inference or the authors’ own estimate · D flagged discrepancy or figure we corrected.
A. LLVM 1.0 was first released in October 2003
Chris Lattner’s announcement, “The LLVM 1.0 Release is finally available!”, is dated October 24, 2003; Wikidata records the same inception date. Discrepancy noted: the CACM retrospective says “October 2003” in its introduction but “December 2003” in two later places. October 24, 2003 is correct; the December date appears to be the release-notes page’s last-modified timestamp (Dec 8, 2003), not the release.
A. Current stable release is LLVM 22.1, from February 2026
LLVM/Clang 22.1 was released February 24, 2026 per the project and Phoronix; the latest point release, 22.1.8, is dated June 16, 2026 on GitHub. LLVM 23 is in development as 23.0.0git. Consistent with the project’s post-2017 six-month cadence.
A. The CGO 2004 paper won Most Influential Paper; the team won the 2012 ACM Software System Award
“LLVM: A Compilation Framework for Lifelong Program Analysis and Transformation” (CGO 2004, pp. 75 to 86, DOI 10.1109/CGO.2004.1281665) was named Most Influential Paper of CGO 2004, awarded 2014. Nuance: the 2012 ACM Software System Award went to Adve, Lattner and Evan Cheng; the CACM retrospective names only the two authors. The “more than 8,000 citations” figure is the authors’ own and is conservative against public citation indices.
D. The Meta LLM Compiler was trained on 546 billion tokens, not 537 billion
The CACM piece states 537 billion tokens. Meta’s paper (arXiv 2407.02524) and the peer-reviewed Compiler Construction 2025 version (DOI 10.1145/3708493.3712691) both report 546 billion tokens of LLVM IR and assembly, with a further 164 billion for flag-tuning and disassembly (710 billion total). We use 546 billion.
A. Opaque pointers became default in LLVM 15; typed pointers were removed in LLVM 17
Confirmed by the official Opaque Pointers documentation: default in LLVM 15 (2022), typed-pointer support removed in LLVM 17 (2023). The ptrtoaddr instruction is a genuine LLVM 22 addition, per the 22.1 release coverage.
A. Apple deprecated bitcode in Xcode 14 (2022)
Xcode 14 release notes: the App Store no longer accepts bitcode submissions and Xcode no longer builds bitcode by default. This bounds the “mid-2010s through 2022” window the retrospective gives for App Store bitcode.
B. Industry adoption: Apple, Google, Arm, Intel, Sony, Nintendo, Qualcomm
Apple’s OpenGL/iOS SDK work began around 2005 and the macOS OpenGL JIT shipped in 2006 (Clang and LLVM histories). Intel switched its C/C++ stack to LLVM (ICX / oneAPI DPC++); Arm shipped LLVM-based Arm Compiler 6 and drove the Apache 2.0 relicensing over patent concerns. Sony uses Clang for PS4/PS5. These are well documented across primary announcements and vendor pages.
C. ”Hundreds of billions of dollars in annual revenue” compiled by LLVM
This is the authors’ order-of-magnitude estimate, not an audited figure, and we present it as such. The qualitative claim, that a large majority of end users run LLVM-compiled code at some layer, is well supported by the adoption evidence above.
A. MLIR underpins XLA, PyTorch (Torch-MLIR), IREE and Triton; GlobalISel is default at -O0 on AArch64
MLIR was presented at CGO 2021 (DOI 10.1109/CGO51591.2021.9370308) and is used across the named AI compilers. GlobalISel’s default-at-O0 status on AArch64 and ongoing work to broaden it are documented in current LLVM forum threads (February 2026).
B. ThinLTO: first presented at EuroLLVM 2015; summaries add under one percent to bitcode; full LTO costs two to three times the compile time
ThinLTO was introduced by Teresa Johnson, Mehdi Amini and David Li (LLVM Project blog, 2016; presented EuroLLVM 2015). The summary overhead of about 0.8 percent for Clang without debug info, and the design goal of build time and memory close to a non-LTO build, are from that write-up and the Clang ThinLTO documentation. The two-to-three-fold compile-time cost of full LTO is a widely reported figure (for example, the Gentoo LTO documentation). “Sometimes beats full LTO” reflects the authors’ own SPEC results, where ThinLTO’s more aggressive per-module pipeline occasionally wins.
C. LLVM’s reach can be sized as a trillion-dollar surface area
Apple FY2025 revenue 416.16 billion dollars (Form 8-K, quarter and year ended Sept 27, 2025); Alphabet 2025 revenue 402.84 billion dollars (Form 10-K / Annual Report 2025); NVIDIA fiscal 2026 revenue 215.9 billion dollars, data center 193.7 billion, for the year ended Jan 25, 2026 (Form 8-K). Combined, about 1.04 trillion dollars. This is top-line company revenue for firms whose flagship products are built with LLVM, presented as a floor, not value attributed to or captured by the compiler. Smartphone units of about 1.25 billion for 2025 are from IDC and Omdia. We do not treat the constructed total as audited.
A. Rust (Cranelift) and Zig are building non-LLVM backends to escape LLVM compile times
Rust’s rustc_codegen_cranelift reports roughly a twenty percent reduction in code-generation time and about five percent in clean-build time on large projects, and production-readiness is a 2025 Rust project goal (rust-project-goals, 2025h2). Zig made its self-hosted x86-64 backend the default for Debug builds on Linux and macOS in mid-2025, reporting five to fifty percent wall-clock improvements and citing LLVM as the compile-time bottleneck (Zig devlog, 2025). Both retain an LLVM path for optimized release builds.
Sources
V. Adve and C. Lattner, “The LLVM Compiler Infrastructure,” Communications of the ACM, June 2026. cacm.acm.org
C. Lattner and V. Adve, “LLVM: A Compilation Framework for Lifelong Program Analysis and Transformation,” CGO 2004, pp. 75 to 86. DOI 10.1109/CGO.2004.1281665.
C. Lattner, “The LLVM 1.0 Release is finally available!”, llvm-announce, October 24, 2003.
LLVM Project, “Opaque Pointers.” llvm.org/docs/OpaquePointers.html
Arm, “What is new in LLVM 22,” 2026; M. Larabel, “LLVM/Clang 22.1 Released,” Phoronix, Feb 24, 2026.
LLVM Project, release tags through 22.1.8. github.com/llvm/llvm-project
Apple, “Xcode 14 Release Notes,” 2022. developer.apple.com
N. P. Lopes et al., “Alive2: Bounded Translation Validation for LLVM,” PLDI 2021. DOI 10.1145/3453483.3454030.
C. Lattner et al., “MLIR: Scaling Compiler Infrastructure for Domain Specific Computation,” CGO 2021. DOI 10.1109/CGO51591.2021.9370308.
C. Cummins et al., “Meta Large Language Model Compiler,” arXiv 2407.02524, 2024; CC 2025, DOI 10.1145/3708493.3712691.
A. Kothari et al., “Hydride,” ASPLOS 2024, DOI 10.1145/3620665.3640385; A. R. Noor et al., “MISAAL,” PACMPL 2025.
A. Ejjeh et al., “HPVM: Hardware-agnostic programming for heterogeneous parallel systems,” IEEE Micro 42(5), 2022.
V. Adve, faculty biography and CV, University of Illinois. vikram.cs.illinois.edu
T. Johnson, M. Amini, D. Li, “ThinLTO: Scalable and Incremental LTO,” LLVM Project Blog, 2016 (presented EuroLLVM 2015); Clang “ThinLTO” documentation. blog.llvm.org
Rust Project Goals 2025h2, “Production-ready Cranelift backend.” rust-lang.github.io
Zig, Devlog 2025 (self-hosted x86-64 backend default for Debug builds). ziglang.org/devlog/2025
Apple Inc., Form 8-K, fiscal 2025 fourth-quarter and full-year results (year ended Sept 27, 2025), revenue 416 billion dollars. SEC EDGAR
Alphabet Inc., Annual Report / Form 10-K for 2025, total revenue 402.84 billion dollars. SEC EDGAR
NVIDIA Corp., Form 8-K, fourth quarter and fiscal 2026 (year ended Jan 25, 2026), revenue 215.9 billion dollars, data center 193.7 billion. SEC EDGAR
IDC and Omdia, worldwide smartphone shipments for 2025, about 1.25 billion units.
Inference.Engineering · Independent analysis of LLM serving infrastructure, GPU economics, and the compiler layer beneath them. This piece is built on the June 2026 CACM retrospective by Vikram Adve and Chris Lattner and cross-checked against LLVM 22.1 documentation, release notes and the primary research literature. Vendor-independent; every figure sourced. Corrections and disputes welcome.




