Case study 17 / 26
Bellows
An optimizing compiler you can read end to end — from source text to x86-64 machine code, with an IR interpreter that must agree with the native binary on every program.
- Status
- Research
- Period
- 2026
- Domain
- tools · experiments
- Language
- Rust
- Last push
- 06 SEP 2026
- License
- MIT
- Source of claims
- README.md, docs/LANGUAGE.md, ARCHITECTURE.md
A complete pipeline for Ash, a small typed language: lexer, parser, semantic analysis with rustc-style diagnostics, three-address IR and interpreter, a fixpoint optimizer, and x86-64 code generation for Linux and Windows with runtime traps and DWARF line info. Zero dependencies, about 2,600 lines of Rust.
01/The problem
Real compilers are too large to read, and tutorial compilers stop at the first thing that prints 42 — no type checker with real diagnostics, no optimizer, no proof that optimization preserved semantics, no runtime traps.
02/The system
A complete pipeline for Ash, a small typed language: lexer, parser, semantic analysis with rustc-style diagnostics, three-address IR and interpreter, a fixpoint optimizer, and x86-64 code generation for Linux and Windows with runtime traps and DWARF line info. Zero dependencies, about 2,600 lines of Rust.
03/Implementation
- 01Lexer and recursive-descent parser with precedence climbing; every malformed input is a spanned error.
- 02Semantic analysis: scopes, types, mutability, arity, missing-return analysis, unreachable code — 20 diagnostic codes rendered rustc-style with a caret and suggested fix.
- 03Three-address IR over basic blocks with a printer and an interpreter.
- 04Optimizer to a fixpoint: constant folding and propagation, store→load forwarding, dead code, branch folding, jump threading, block merging.
- 05x86-64 assembly for System V and MS x64 with checked arithmetic that traps, and DWARF line info with -g.
- 06--emit tokens|ast|ir|asm and a stats command that reports what the optimizer did.
04/Engineering
Two backends that must agree
The interpreter and native code share one arithmetic definition; golden tests run every example interpreted and native at -O0 and -O1, and all four outputs must match.
Traps are part of the language
Overflow and division by zero are runtime errors in both backends, and the optimizer never folds a trapping operation away.
The optimizer is observable
Tests check IR shape, behavioural equivalence and that fewer instructions execute.
05/Interface
No product screenshots are published for this project. The visual above is a code-driven representation of how it behaves, built from the repository source — not a screenshot.
06/Tech stack
- Rust
- Zero dependencies
- x86-64 (System V + MS x64)
- DWARF
- Fuzz testing
07/Result
Verified outcomes
- 36 negative tests pin the exact line:col of every diagnostic; a deterministic fuzzer feeds 30,000 token soups and 5,000 random byte strings through the pipeline.
- Golden suite compiles and runs every example natively on Linux and Windows in CI.
Known limitations
- The code generator spills every value to the stack, so native code is only marginally faster than the interpreter; register allocation is the first roadmap item.
08/Links
Next case study
Tarn →