Compiler Architecture
The cryo binary is the compiler, the package manager, the test runner, and
the dependency resolver. It is self-hosted: every line of Cryo you compile
is compiled by a compiler written in Cryo, and the standard library it links
against is written in Cryo too.
The pipeline
A build runs a multi-pass pipeline driven from
compiler/src/compiler/instance.cryo:
Source
-> Lex
-> Parse
-> Module Resolution
-> Declaration Collection
-> Type Resolution
-> Type Lowering
-> Specialisation (monomorphisation)
-> Semantic Analysis
-> Move Check
-> Drop Insertion
-> IR Generation (LLVM 20)
-> Linking (clang)
-> Native binary
Two properties of this ordering are worth calling out, because they are visible in the language:
- Declaration collection runs before any body is type-checked. Every function signature in the compilation unit is gathered in a dedicated pass, so call ordering in source is irrelevant and there is no forward-declaration requirement.
- Specialisation runs before semantic analysis. Generics are monomorphised
into concrete instantiations first, so the analyser only ever sees fully
concrete types. This is why a
wherebound is checked against each concrete instantiation and why layout directives such as![repr(...)]are honoured per instantiation.
Where each stage lives
| Stage | Source |
|---|---|
| Lexing | compiler/src/compiler/lex/ |
| Parsing | compiler/src/compiler/parser/ |
| AST | compiler/src/compiler/AST/ |
| Type system, monomorphisation | compiler/src/compiler/types/ |
| Passes (sema, move, drop, specialisation, type lowering, header import) | compiler/src/compiler/passes/ |
| LLVM IR generation | compiler/src/compiler/codegen/ |
| Diagnostics | compiler/src/compiler/diag/ |
| CLI | compiler/src/CLI/ |
No separate runtime library
Every intrinsic in stdlib/core/intrinsics.cryo, plus format(), is emitted
directly as LLVM IR by compiler/src/compiler/codegen/intrinsics_codegen.cryo.
There is no runtime library to link beside your program — what a hosted build
links is libcryo.a (the standard library), the C runtime, and the panic
handler. A no_runtime build drops even those; see
Project Configuration.
The self-hosting gate
Each release is built by the previous release. make selfhost-check runs a
3-round, 6-stage build and asserts the emitted LLVM IR is byte-identical
between rounds — that is, the compiler compiled by stage N produces exactly
the same output as the compiler compiled by stage N+1. A non-identical stage
means a miscompilation somewhere in the pipeline, and the check gates every push
to main.
The pinned compiler used to bootstrap lives at bin/cryo and is committed to
the repository, so a clean checkout can rebuild the compiler with no external
Cryo toolchain.
Reading further
- Command-Line Interface — every subcommand and build flag.
- Project Configuration — the
cryoconfigformat, build profiles, and the build directory layout. - Grammar — the complete EBNF grammar the parser implements.
- ABI — SysV-amd64 lowering, eightbyte classification,
and the calling convention for both
extern "C"and Cryo-internal calls. - Symbol Mangling — how declarations map onto
emitted symbol names, and how
cryo demanglereverses it. - Test Framework — how
cryo testdiscovers, forks, and reports tests.