How Burn works
How Burn works
This section explains what Burn does in the background: how a program is read, checked and compiled, what its values look like in memory, how memory is freed, and how bvm, native executables and the toolchain run it. None of this is needed to write Burn programs, but it explains why things behave as they do and what they cost.
The journey of burn app.bn
Take this program:
fun greet(name: string): string {
return "hi " + name
}
var who = "ada"
print(greet(who))Running burn app.bn goes through these steps:
burnstarts.burnis one native executable that contains the command line, the compiler and bvm, all written in Burn (the toolchain).- The command line loads the program. It reads
app.bn, finds the project around it (burn.toml), and follows everyimportto the standard library, the project's own modules and installed packages. - Lexer and parser. Each file becomes tokens, then a syntax tree (front end).
- Type checker. Names are resolved across modules, types are inferred and checked, smart casts are tracked, and the program is lowered to a small typed intermediate form, the IR.
- Ownership. A pass over the IR adds the reference counting that frees memory (memory).
- Code generation. The IR becomes a bvm module: bytecode, a type table, strings, source locations.
- bvm runs it. The module is linked with the bytecode libraries it imports, verified and run by the bvm inside the same process; hot code is compiled to machine code while it runs (running on bvm).
All of this happens in one process and usually takes a few milliseconds, which is why burn app.bn feels like an
interpreter. burn build stops after step 5 and generates machine code instead
(native executables), or JavaScript with --target js.
Looking at every stage
burn dump prints what each stage produces:
| Command | Shows |
|---|---|
burn dump --tokens app.bn |
the tokens, with their byte offsets and whether a line break comes before them |
burn dump --ast app.bn |
the syntax tree, with the span of every node |
burn dump --modules app.bn |
the modules the program loads, in the order they are initialized |
burn dump --decls app.bn |
every declared type and function with its signature |
burn dump --diagnostics app.bn |
errors and warnings in a stable, machine-readable form |
burn dump --checked app.bn |
the typed IR after checking |
burn dump --owned app.bn |
the IR after the ownership pass, with every retain and release |
burn dump --bvm app.bn |
the bvm module, as bvm assembly |
burn dump --asm app.bn |
the assembly a native build would contain |
burn dump --js app.bn |
the JavaScript --target js would write |
For the program above, the parser sees the call in the last line as:
ExprStmt @77..94
Call @77..94
callee: Ident print @77..82
arg: Call @83..93
callee: Ident greet @83..88
arg: Ident who @89..92The checker turns greet into a function of the IR, where every node has a type id (6 is string) and the string
+ has become a runtime call:
func 1 greet params=1 ret=6 async=false
return
Rt StrConcat : 6
Str 1 : 6
Local 0 : 6and code generation turns the top level code into bytecode that keeps the reference counts right:
gload "app.bn.who"
rt retain
tee 1
call greet
tee 2
rt printThe pieces
| Piece | Written in | Where |
|---|---|---|
| lexer, parser, checker, ownership, code generators | Burn | compiler/src/ |
the command line, REPL, formatter, burn doc, language server |
Burn | compiler/src/cli, lsp, doc; lib/tools |
| the standard library | Burn | lib/std/, embedded into the compiler |
| bvm: bytecode format, assembler, verifier, linker, mixins, archives, interpreter, JIT | Burn | compiler/src/bvm/ |
| the runtime: memory, strings, arrays, maps, printing, files, processes, JSON, HTTP, tasks | Burn | lib/rt/ |
| the JavaScript runtime | JavaScript | lib/js/prelude.js |
| the seeds: the compiler and its runtime as assembly for each platform | assembly | compiler/boot/ |
Everything is written in Burn and compiles itself. The toolchain only links the C library, and a C compiler assembles the seed for the very first build (how the compiler builds itself).
Read on
- From text to a checked program: lexer, parser, modules, type checking, smart casts and generics.
- Values in memory: how numbers, strings, arrays, maps, structs, enums, closures and
anyare stored, and how method calls find their code. - Memory management: ownership, reference counting, the cycle collector and the heap limit.
- Running on bvm: verifying, the interpreter, the JIT, runtime errors and threads.
- Native executables: the generated machine code, calling conventions and linking.
- The toolchain: how
burnstarts, where the standard library and packages live, and how the compiler builds itself.