A self‑hosting compiler is a compiler for a programming language that is written in the same language it compiles. This means the compiler’s own source code can be compiled by an earlier version of itself, creating a self‑sustaining toolchain. Achieving self‑hosting is considered a major milestone in language and compiler development because it demonstrates the language’s maturity, enables bootstrapping (building the compiler from scratch using only a minimal set of tools), and allows the compiler to be improved using the language it targets. The concept originated in the early days of high‑level languages (e.g., Lisp, C) and remains a key design goal for modern systems programming languages.
1 History
1.1 Early examples
1.1.1 Lisp and the first self‑hosting interpreter
The first self‑hosting implementation is widely attributed to the Lisp programming language. In 1962, Tim Hart and Mike Levin developed a Lisp interpreter written in Lisp itself, relying on a small “meta‑circular” evaluator. This work showed that a language could define its own semantics and that an interpreter could be bootstrapped from a minimal set of primitive operations. The self‑hosting Lisp interpreter became a foundational demonstration of language design and compiler bootstrapping.
1.1.2 C and the Unix revolution
The C language achieved self‑hosting in the early 1970s as part of the Unix operating system development. Dennis Ritchie and Ken Thompson wrote the first C compiler in assembly language. Once that compiler was functional, they rewrote the compiler in C itself. The new C compiler was then compiled using the assembly‑based compiler, and the resulting binary was used to compile further versions. This self‑hosting cycle enabled rapid iteration and porting of Unix to different hardware architectures.
1.2 Evolution in the 1980s–1990s
During the 1980s and 1990s, self‑hosting became a standard goal for new programming languages. The Pascal language saw self‑hosting implementations (e.g., the UCSD p‑System), and languages such as Modula‑2, Oberon, and Ada all developed self‑hosting compilers. The GNU Compiler Collection (GCC), initially written in C, demonstrated that a self‑hosting compiler could be portable and free software. The growth of workstation and personal computer markets encouraged language designers to build self‑hosting toolchains to reduce dependency on vendor‑supplied compilers.
1.3 Modern self‑hosting projects
Since the 2000s, many new programming languages have prioritized self‑hosting from early stages. Rust (rustc), Go (gc), and Haskell (GHC) all achieved self‑hosting within a few years of their initial release. Modern languages often leverage existing self‑hosting compilers (e.g., GCC or LLVM/Clang) as bootstrap tools. Self‑hosting remains a sign of a language’s stability and is frequently used as a benchmark for community adoption.
2 Techniques for achieving self‑hosting
2.1 Bootstrapping
2.1.1 Cross‑compilation from a host language
One common technique is to first write the compiler in a different, already available language (the host language). This initial version is a cross‑compiler: it runs on the host system but produces code for the target language. Once the cross‑compiler is mature enough, it can compile a version of itself rewritten in the target language.
2.1.2 T‑diagram (Tombstone diagram)
A T‑diagram visually represents the bootstrapping process. It shows a compiler written in a source language S that compiles a language L to machine code M. When moving to self‑hosting, the diagram’s S column becomes L, indicating the compiler is now written in the language it compiles. The diagrams help reason about the layers of translation and bootstrapping steps.
2.1.3 Minimal bootstrap chain
A minimal bootstrap chain uses the smallest possible set of tools to recreate the full compiler. For example, a tiny interpreter or compiler (often in assembly or an existing language) can compile a larger subset of the language, which then compiles the complete self‑hosting compiler. This approach reduces external dependencies.
2.2 Incremental self‑hosting
2.2.1 Source‑to‑source translation
Instead of rewriting the entire compiler at once, developers may translate the compiler’s source code from the host language to the target language manually or via automated tools. The translated version is then compiled by the existing host‑language compiler, producing a native binary that can compile further iterations.
2.2.2 Using a subset or “seed” compiler
A “seed” compiler is a minimal, often hand‑crafted version of the target language’s compiler, usually supporting only a core subset. This seed is written in an existing language and then used to compile a more complete compiler written in the target language. The complete compiler can then compile the seed’s source code, completing the self‑hosting loop.
2.3 Formal verification and self‑hosting
Some projects combine self‑hosting with formal verification. The compiler is written in the same language and its correctness is proved using formal methods. This ensures that the self‑hosting process does not introduce trust issues. Examples include the CompCert verified C compiler (though CompCert is not self‑hosting in the traditional sense, its verification approach influences self‑hosting designs).
3 Bootstrapping process
3.1 Stage 0: Minimal native compiler
Stage 0 consists of a minimal compiler for the target language, written in another language (often C or assembly). This compiler typically supports only a subset of the language—enough to compile a more complete implementation. It is often called the “bootstrap compiler”.
3.2 Stage 1: Compile the full compiler using Stage 0
The source code of the full compiler (written in the target language) is compiled using the Stage 0 compiler. The output is a binary of the full compiler. At this point, the binary can compile source files of the target language, but it was not yet produced by its own source.
3.3 Stage 2: Compile the compiler with itself (self‑check)
The binary produced in Stage 1 is used to compile the full compiler’s source code again. If the resulting binary is byte‑for‑byte identical to the Stage 1 binary, the compiler is said to pass the “self‑check” or “bootstrap comparison”. This confirms that the compiler can reproduce itself without external influences.
3.4 Stage 3: Optimisation and trust
Once self‑hosting is verified, further optimisations or features can be added by editing the compiler’s source and recompiling it with the last validated binary. This stage continues indefinitely as the compiler evolves. The self‑hosting compiler now serves as its own build tool.
4 Challenges and considerations
4.1 Trusting the compiler
4.1.1 The “trusting trust” attack
In 1984, Ken Thompson described a theoretical attack now known as the “trusting trust” attack. A malicious compiler can inject a backdoor into its own compiled output, even if the source code appears clean. The attack exploits the self‑hosting process: a compromised binary can propagate the backdoor to future versions of the compiler without leaving a trace in the source.
4.1.2 Countermeasures (diverse double‑compilation)
To detect such attacks, the “diverse double‑compilation” technique compares the outputs of two independently built compilers (e.g., a self‑hosted binary and a cross‑compiler from a different trusted environment). Any discrepancy may indicate a trust issue. Other countermeasures include formal verification and reproducible builds.
4.2 Performance and optimisation trade‑offs
A self‑hosting compiler must be written in the language it compiles, which may have performance limitations compared to a systems language like C. Early iterations of self‑hosting compilers can be slower because they lack the optimisations available from a mature compiler. However, once self‑hosting is achieved, the compiler can be optimised using itself, gradually closing the gap.
4.3 Circular dependencies
Self‑hosting introduces circular dependencies: the compiler depends on itself to be built. This makes initial development hard and requires careful version control. If a new version of the compiler introduces a bug, it may be impossible to rebuild that version from source without an older working binary. Maintaining a chain of verified bootstrap binaries is essential.
4.4 Portability across platforms
A self‑hosting compiler initially targets the platform on which it is developed. Porting it to a new architecture requires either cross‑compilation or a native compiler on the target platform. This often involves repeating the bootstrapping process for each new target, which can be time‑consuming. Modern self‑hosting compilers like GCC and LLVM/Clang address portability by supporting many backends and maintaining cross‑compilation infrastructure.
5 Examples of self‑hosting compilers
5.1 C and C++ (GCC, Clang, MSVC)
*GCC (GNU Compiler Collection)*: Initially written in C, GCC became self‑hosting in the mid‑1980s. It is now written in C and C++ and supports many languages and architectures. *Clang/LLVM*: Clang is self‑hosting; its C++ front end is compiled with Clang itself. *MSVC (Microsoft Visual C++)*: Microsoft’s compiler has been self‑hosting since early versions, with the compiler written in C++.
5.2 Rust (rustc)
Rust’s compiler, rustc, achieved self‑hosting in 2011. It was originally written in OCaml, then rewritten in Rust. The bootstrapping process uses a prior Rust release (the “stage0” binary) to compile the current source. Rustc now maintains a clean bootstrap chain with each stable release.
5.3 Go (gc)
The Go compiler (gc) has been self‑hosting since Go version 1.5 (2015). Before that, it was written in C. The transition to self‑hosting was done by translating the compiler from C to Go and then bootstrapping from a Go‑based implementation. The Go toolchain is now entirely written in Go.
5.4 Haskell (GHC)
The Glasgow Haskell Compiler (GHC) has been self‑hosting since version 0.29 (1992). It is written in Haskell itself. GHC relies on a legacy binary from a previous release for bootstrapping; each new version must be compiled by an earlier stable GHC.
5.5 Java (OpenJDK javac)
The javac compiler in OpenJDK is written in Java and has been self‑hosting since early versions. OpenJDK uses a “boot cycle”: an older JDK binary (e.g., the previous release) compiles the new javac source. The resulting javac binary is then used to recompile itself as a sanity check.
5.6 Smalltalk and other dynamic languages
Smalltalk implementations have long been self‑hosting, as the language is both the programming environment and the compiler. The Smalltalk‑80 system, for instance, included a compiler written in Smalltalk. Other dynamic languages like Python and Ruby have experimented with self‑hosting compilers (e.g., PyPy for Python, JRuby for Ruby), though full self‑hosting in the traditional sense (compiling the compiler’s source into machine code) is less common than for statically compiled languages.