Start here
Build a compiler around Esper's IR, one step at a time.
Esper is a C99 compiler backend library. Your frontend decides what a program means. Esper stores that program, transforms it, and hands it to a backend. You can begin with a text file before writing any C integration code.
esper 1
fn main() i32 {
entry:
answer i32 = add 20, 22
return answer
}
Save this as hello.esp. Build the project, then run esper hello.esp --run.
The program returns 42; a shell reports that as exit status 42, not printed text.
Use esper hello.esp --emit=text to inspect the IR Esper reads back.
The path through the library
- Create a translation unit using the builder, or parse
esper 1text. - Verify that values, types and control flow agree.
- Run selected optimization passes. These modify the unit in place.
- Select a backend and emit code, or execute through the interpreter.
- Free the unit when finished.
Read the text guide first, then embedding. The language guides show how frontend features become lower-level operations. You do not need to implement every feature before compiling a useful first language: integers, functions, conditions and loops are enough.
What works today
The checkout builds a static C library, an optional C++ wrapper, a CLI, and test runners. Versioned text supports named values, numeric temporary names, mutable locals, aggregates, embedded bytes and a subset of annotations. Internally, passes and the public builder still use SSA with block parameters. The text reader promotes locals into that representation; the printer inserts edge copies so ordinary text does not need PHI nodes or block arguments.
C99, LLVM text, WebAssembly, native tinyasm and shader emitters exist. Their
coverage differs; inspect --list-backends and the backend tests before choosing
one. Native object/link output depends on sibling tinyasm and tinylinker builds.
LLVM text output does not include an LLVM optimizer or machine-code compiler.
Where we are going
The main product is a small IR library and format. The intended distribution is one amalgamated C core header, a convenient C++ API, and separate amalgamated backend headers. Binary unit archives and independent-result merging now work; see saving and linking units for the supported boundaries. A public mutable-local builder, portable layout policies and richer source-type descriptions remain work to do. See the implementation roadmap for exact boundaries.
The native backend’s performance goal is roughly 90% of LLVM’s generated program speed with much lower compilation cost. That is a target, not a measured claim. Benchmark reports begin tracking compiler cost now.