burn / docs

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 pipeline from source to bvm, native code and JavaScript

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:

  1. burn starts. burn is one native executable that contains the command line, the compiler and bvm, all written in Burn (the toolchain).
  2. The command line loads the program. It reads app.bn, finds the project around it (burn.toml), and follows every import to the standard library, the project's own modules and installed packages.
  3. Lexer and parser. Each file becomes tokens, then a syntax tree (front end).
  4. 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.
  5. Ownership. A pass over the IR adds the reference counting that frees memory (memory).
  6. Code generation. The IR becomes a bvm module: bytecode, a type table, strings, source locations.
  7. 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.

The six steps of burn app.bn inside one process

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..92

The 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 : 6

and 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 print

The 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 any are 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 burn starts, where the standard library and packages live, and how the compiler builds itself.