Seventy years of turning human notation into machine instructions — the people, the breakthroughs and the ideas (analysis/synthesis, SSA, a common IR) that still shape every compiler you use today.
A roughly chronological tour. We trace how compilation grew from a clever bookkeeping trick into a rigorous engineering discipline and, finally, into shared infrastructure — and we name the people who made each leap.
Each milestone below is unpacked later in the deck. Note the rhythm: a burst of theory in the late 1950s and 60s, a wave of tooling in the 70s–80s, and a shift to reusable infrastructure from the 2000s on.
amber = compilers & languages · green = theory/standards · purple = tools/infra · blue = optimisation/runtime
The first stored-program computers (EDSAC 1949, Manchester Baby 1948, EDVAC) were programmed in raw machine code: numeric opcodes and absolute addresses, entered in binary, octal or via teleprinter codes. Every jump target had to be computed by hand, and inserting an instruction renumbered everything after it.
Cambridge’s EDSAC (1949) booted via David Wheeler’s Initial Orders — a tiny bootstrap loader (35 instructions) that read symbolic, relocatable order codes from paper tape and placed them in memory, converting decimal addresses as it went. Arguably the first practical assembler / loader.
With no hardware return-address mechanism, Wheeler devised the idiom for calling and returning from a subroutine: the caller plants its return address into the subroutine’s final jump. This closed subroutine made the first reusable code libraries possible — the ancestor of every function call.
Wheeler, Wilkes & Gill’s 1951 book The Preparation of Programs for an Electronic Digital Computer was the first textbook on programming — and on subroutine libraries.
On the UNIVAC I, Grace Hopper built A-0 (1952) and coined the word “compiler”. Crucially, its original meaning differs from ours: A-0 read a sequence of subroutine call numbers with arguments and compiled — in the librarian’s sense of “gathering together” — the pre-written routines from tape, linking and relocating them into one runnable program.
Hopper’s FLOW-MATIC (1955–59) used English-like statements for business data processing. It became the primary template for COBOL (1959), defined by the CODASYL committee — making Hopper a guiding force behind the most widely deployed language of the century.
In Britain, the equivalent term was autocode. These were the first systems to compile genuine algebraic expressions into machine code — pre-dating, and partly inspiring, the more famous American efforts.
# Flavour of an early autocode line (Brooker-style):
n1 = n2 + n3 x n4 # arithmetic in near-mathematical form
j1, 3 >= n1 # conditional jump to label 3 if n1 <= 0
# The autocode expands each line into a sequence of machine orders,
# allocating working store and handling the accumulator for you.
The lesson the autocodes taught: programmers would adopt a higher notation only if the translator’s output was efficient enough to be worth it. That bar is exactly what FORTRAN set out to clear.
John Backus’s IBM team shipped FORTRAN for the IBM 704 in 1957. It was not the first compiler, but it was the first widely successful one — and its central challenge was economic: it had to generate code nearly as fast as expert hand-written assembly, or no one would trust it.
C FORTRAN II — sum 1..N (fixed-form, col 7+)
SUM = 0.0
DO 10 I = 1, N
SUM = SUM + A(I) * B(I)
10 CONTINUE
WRITE (6, 20) SUM
20 FORMAT (1H , F12.4)
Machine time was vastly more expensive than programmer time in 1957. A compiler that produced slow code was useless. Backus reportedly spent the bulk of the effort not on parsing but on the optimiser — especially efficient use of the 704’s three index registers.
FORTRAN’s parser was largely ad hoc. The late 1950s gave compiler writers a formal theory of grammar — and a notation to write languages down precisely.
Noam Chomsky classified grammars into four nested types. Two of them became the bedrock of compiling:
To define ALGOL, John Backus (1959) and Peter Naur (1960) introduced a metasyntax for context-free grammars. BNF made a language’s syntax a precise, machine-readable artefact rather than English prose.
<expr> ::= <expr> "+" <term>
| <term>
<term> ::= <term> "*" <factor>
| <factor>
<factor> ::= "(" <expr> ")"
| <number>
This grammar — expressions over + and * — reappears in every parsing chapter to come, encoding precedence and associativity structurally.
ALGOL 60, defined by an international committee and written up in the famous Revised Report (Naur, ed., 1963), was “a language so far ahead of its time that it was an improvement on its successors” (Hoare). It introduced ideas now universal.
begin…end)ALGOL is the common ancestor of the entire imperative / block-structured family: CPL → BCPL → B → C, plus Pascal, Simula (and thus object orientation), Ada and beyond. “ALGOL-like” is still shorthand for the mainstream syntax tradition.
begin
integer procedure fact(n);
value n; integer n;
fact := if n < 2 then 1
else n * fact(n - 1);
integer k;
for k := 1 step 1 until 5 do
outinteger(1, fact(k))
end
For the first time, a language’s reference was a rigorous document — syntax in BNF, semantics in careful prose. It set the standard for how languages are specified, and made independent, conforming compilers possible.
With context-free grammars formalised, the question became: how do you build a parser mechanically and efficiently for any reasonable grammar? A decade of beautiful algorithms answered it.
| Technique | Who / When | Idea |
|---|---|---|
| Recursive descent | folklore, late 50s/60s | One function per non-terminal; hand-written, top-down (LL) |
| LR(k) | Knuth, 1965 | Bottom-up, shift-reduce; the largest class of deterministic CFGs |
| LALR(1) | DeRemer, 1969 | Merged LR states — small tables, practical; powers yacc |
| Earley | Earley, 1970 | Handles any CFG, even ambiguous; O(n³), O(n²)/O(n) special cases |
| SLR / GLR | DeRemer ’71 / Tomita ’86 | Simpler tables / generalised parallel parsing |
“On the Translation of Languages from Left to Right” showed a single DFA over items can deterministically parse a huge class of grammars in linear time — the theoretical heart of bottom-up parsing.
At Bell Labs in the early 1970s, theory met practice. Dennis Ritchie created C (~1972) to write Unix itself; alongside it grew the generator-based toolchain that made building compilers routine.
Generators turn declarative specs into C source — the front-end stops being hand-written.
No book shaped how compilers are taught more than the “Dragon Book” series — named for its cover, a knight battling a dragon labelled “complexity of compiler design”. It codified the standard phase pipeline a generation learned by heart.
| Edition | Authors | Nickname |
|---|---|---|
| 1977 | Aho & Ullman, Principles of Compiler Design | green dragon |
| 1986 | Aho, Sethi, Ullman | red dragon |
| 2006 | Aho, Lam, Sethi, Ullman (2nd ed.) | purple dragon |
With M languages and N machines, writing every front-end against every back-end means M×N compilers. The recurring dream: insert a common intermediate language so you write only M + N halves. It took 50 years to fully realise.
The UNiversal Computer-Oriented Language — proposed by Melvin Conway and a SHARE committee as a single intermediate target all compilers would emit and all machines would implement. Hugely influential as an idea; never realised in its time (the language design problem was too hard for the era).
Niklaus Wirth’s Pascal-P compiler (Zurich, mid-1970s) emitted P-code for a hypothetical stack machine. To port Pascal you wrote a small P-code interpreter — so Pascal spread to micros via the UCSD p-System. The same idea reappears as the JVM bytecode, CLR and WebAssembly.
A compiler for language L written in L. You bootstrap by first compiling a subset with another tool, then compiling the full compiler with itself. C, Pascal, Go, Rust and Swift are all self-hosting today.
The single most important structural idea in compiler engineering, stated visually. A shared intermediate representation collapses a quadratic problem into a linear one — the insight UNCOL named and LLVM finally delivered.
Richard Stallman released the GNU C Compiler in 1987 as a cornerstone of the GNU project — a production-quality, retargetable, free compiler. More than any other single program, GCC made open-source operating systems (and later Linux) possible.
.md files) drive instruction selection — one back-end per targetA single high-quality compiler everyone could read, modify and target turned the compiler from a vendor product into shared infrastructure — a theme LLVM would take even further.
Static Single Assignment form — Cytron, Ferrante, Rosen, Wegman & Zadeck (1991) — gave every variable exactly one definition. This makes use-def chains explicit and turns many optimisations into near-trivial graph walks. Almost every serious optimiser since is built on it.
; before SSA — x reassigned
x = 1
if cond:
x = 2
y = x + 3
; after SSA — each def is unique
x1 = 1
if cond:
x2 = 2
x3 = phi(x1, x2) ; pick by predecessor
y1 = x3 + 3
At a control-flow merge, φ selects the value of the variable depending on which edge was taken. φ-nodes are placed at the dominance frontier — the elegant insight that made SSA construction efficient.
φ sits at the merge block (the dominance frontier of the two defs).
A parallel story: instead of compiling ahead of time, compile at runtime, using information only available then. Dynamic languages drove the breakthroughs that today power JavaScript and Java alike.
Self’s hidden classes are exactly V8’s “maps”; its inline caches are why modern JavaScript is fast. Speculate that types are stable, compile aggressively, and deoptimise to a safe interpreter if a guard fails. Every fast dynamic-language runtime today is a Self descendant.
Chris Lattner began LLVM as a research project at the University of Illinois (UIUC, ~2003), advised by Vikram Adve. Its radical move was treating the compiler as a set of reusable libraries around a well-specified, typed, SSA-based IR — the long-promised common IR, finally done right.
; LLVM IR — typed, SSA, target-independent
define i32 @square(i32 %x) {
entry:
%r = mul nsw i32 %x, %x
ret i32 %r
}
A from-scratch C/C++/Objective-C front-end (Lattner et al., ~2007) with fast compilation and famously good diagnostics. Apple adopted LLVM/Clang as its system toolchain, displacing GCC.
Lattner & Adve received the 2012 ACM Software System Award for LLVM.
How the threads connect. The ALGOL line gives us C and the systems languages; the dynamic line runs through Smalltalk and Self; modern languages converge on shared back-ends (LLVM).
From the 2010s on, compilers stopped being monolithic programs and became platforms — reusable engines you build languages, accelerators and tools on top of.
Where LLVM IR is one level, MLIR (Lattner et al., Google, 2019) lets you define many coexisting IR dialects — from tensor ops down to LLVM IR — and reuse passes across them. It powers ML compilers (TensorFlow, IREE) and increasingly general toolchains, extending the “reusable infrastructure” idea another level up.
A compressed who’s-who of the field. Several won the Turing Award (the discipline’s highest honour) for the work below.
| Person | Era | Contribution | Honour |
|---|---|---|---|
| David Wheeler | 1949 | EDSAC Initial Orders; the closed subroutine (“Wheeler jump”) | FRS |
| Grace Hopper | 1952 | A-0 system; coined “compiler”; FLOW-MATIC → COBOL | Nat. Medal of Tech. |
| Alick Glennie | 1952 | Autocode — arguably the first compiled language | — |
| Tony Brooker | 1954 | Mark 1 Autocode; the Compiler Compiler (metacompiler) | — |
| John Backus | 1957 | FORTRAN; co-creator of BNF | Turing 1977 |
| Noam Chomsky | 1956 | The grammar hierarchy underlying lexing & parsing | — |
| Peter Naur | 1960 | BNF; editor of the ALGOL 60 report | Turing 2005 |
| Donald Knuth | 1965 | LR parsing; attribute grammars; TAOCP | Turing 1974 |
| Frank DeRemer | 1969 | LALR & SLR parsing — made LR practical (yacc) | — |
| Dennis Ritchie | 1972 | C; co-creator of Unix | Turing 1983 |
| Steve Johnson | 1975 | yacc; the Portable C Compiler (PCC) | — |
| Aho, (Lam,) Sethi, Ullman | 1977–2006 | The Dragon Book; parsing & algorithms (Aho & Ullman, Turing 2020) | Turing 2020 |
| Niklaus Wirth | 1970s | Pascal, P-code, Modula, Oberon | Turing 1984 |
| Cytron, Ferrante, Rosen, Wegman, Zadeck | 1991 | Efficient SSA construction via dominance frontiers | — |
| Ungar & Hölzle | 1987–94 | Self: maps, inline caches, dynamic deoptimisation | — |
| Chris Lattner | 2003 | LLVM, Clang, Swift, MLIR | ACM SW Award 2012 |
One structural idea has survived intact since FORTRAN: a compiler is a front-end that analyses the source into a meaning-bearing IR, and a back-end that synthesises target code from it. Every later breakthrough — BNF, the Dragon pipeline, GCC’s RTL, LLVM IR, MLIR — is a refinement of where to draw that line and what the IR should be.
History rhymes. A handful of debates and “laws” resurface in every compiler generation.
Todd Proebsting’s wry counter to Moore’s Law: compiler optimisation advances double program performance roughly every 18 years — not 18 months. The provocative claim: optimisation research delivers far less than hardware. It reframed where compiler value really lies (safety, productivity, new languages, portability) rather than raw speed.
A recurring myth/jargon-file joke: performance excuses that begin “a sufficiently smart compiler could…” The reality — many high-level abstractions are not automatically optimised away, which is precisely why low-level languages and explicit control persist.
ML-driven heuristics, polyhedral & tensor compilers, verified compilers (CompCert), and ever-higher reusable IRs (MLIR). The 70-year arc bends towards shared, composable, trustworthy infrastructure.
Almost every “new” compiler idea has a 1950s–90s ancestor. Knowing the lineage tells you why today’s tools are shaped as they are — and where the next generalisation will come from.
The Compiler Pipeline — we zoom into the phases this history produced: source → tokens → AST → IR → optimised IR → machine code, and how a real compiler is structured.