The model
compiletakes a resolvedhir_lang::Hirand the interner its names came from, and returns aCompiled: the LSB module, its entry function, and an origin table mapping every instruction to its HIR node.CompileraddsOptions(array semantics, key coercion, what the module body compiles to, hooks, budgets) and aLocatefor real line numbers in the line tables (LineIndexfor one source file).CompileErrorsays why a unit does not compile, naming the HIR node and its span;Unsupportednames the forms planned for later releases.
What it guarantees, and how each guarantee is checked:
| Guarantee | How it is held |
|---|---|
| Compiled programs mean what the HIR means. | tests/differential.rs: random well-formed programs (functions, closures, loops, match, try/finally, defer, maps, every op under random policies, promote included) run on bvm-lang and on a reference HIR interpreter written in the tests; results, printed output, error kinds, and error locations (the raising HIR node) must agree, under both array profiles. 1,000,000 programs were run before this release; every cargo test runs 3,000 more. Mutation-checked: removing the array copy, swapping two finally dispatch targets, or compiling floor_mod as rem makes it fail. |
| Every module it emits loads in bvm-lang. | tests/properties.rs: every generated program's module passes bvm-lang's load-time checks (registers, call windows, constants, branch targets, handlers in range) and round-trips through the LSB encoding unchanged. |
| Every instruction maps back to its HIR origin. | The origin table holds one node per instruction of every function, each a node of the unit; line-table rows are strictly increasing; a runtime error's location maps to the raising node and, through a LineIndex, to its source line (tests/properties.rs). |
| Compilation is deterministic. | The same unit compiles to the same bytes, from a clone and on another thread (tests/properties.rs). |
| No recursion over the input. | Code generation is an explicit task stack: 100,000 nested operations and 20,000 nested ifs inside 20,000 blocks compile on a 512 KiB stack in a debug build (tests/limits.rs). Output is bounded by an instruction budget. |
| PHP array semantics. | Arrays are values (copied on assignment, passing, returning, capture, and store, with an O(1) copy-on-write dup), nested writes separate shared arrays and create missing ones, and keys follow PHP's coercion, checked against a reference interpreter that makes real deep copies, and by tests/keys.rs against PHP's canonical-integer rule. |
| A Mox program runs end to end. | tests/e2e.rs: a hand-lowered Mox program is resolved by resolve-lang, compiled, and run on bvm-lang, printing through a host function. |
Installation
[]
= "0.2"
Without the standard library:
[]
= { = "0.2", = false }
Quick start
Compile a unit and run it on bvm-lang:
use ;
use ;
use Interner;
// fn add(a, b) { a + b }
let mut names = new;
let mut b = new;
let = b.local_param;
let = b.local_param;
let = ;
let sum = b.op;
let body = b.block;
let add = b.func;
let root = b.module;
let hir = b.finish?;
let out = compile?;
let func = out.function.unwrap;
let program = load?;
let mut vm = new;
assert_eq!;
# Ok::
Scripts, host functions, and source lines
A module body runs in the module's start function and the entry function
returns its value. Host symbols (Res::Extern) become LSB imports:
use ;
use ;
use ;
use Interner;
// echo("hello"); 42
let mut names = new;
let mut b = new;
let echo = names.intern;
let seg = new;
let segs = b.list;
let path = b.path;
let callee = b.expr;
let hello = b.str_lit;
let call = b.call;
let stmt = b.expr_stmt;
let answer = b.int;
let body = b.block;
let module = Module ;
let root = b.item;
let hir = b.finish?;
let lines = new;
let out = new.with_locator.compile?;
let mut host = new;
host.register;
let program = load?;
assert_eq!;
# Ok::
The full reference, including the calling convention, the dynamic semantics
of every form, and what each option changes, is in
docs/API.md.
Performance
Release build on the development machine (Windows 11), criterion medians from
benches/bench.rs. Compile figures are library-only (HIR in memory to an LSB
module, no encoding); run figures are bvm-lang 2.0.0-alpha.1 executing the
compiled module (VM instance creation included).
| Benchmark | Result |
|---|---|
| Compile, synthetic unit, 152k HIR nodes | 23 ms (6.6 M nodes/s) |
| Compile, synthetic unit, 532k HIR nodes | 103 ms (5.2 M nodes/s) |
| Compile, synthetic unit, 1.52M HIR nodes | 354 ms (4.3 M nodes/s) |
fib(25), direct calls |
8.6 ms |
| Counting loop, 1M iterations | 10.7 ms |
| PHP array: 100k appends, then 100k indexed reads | 13.0 ms |
The same plus 100k nested writes g[i % 64][] = i |
371 ms |
"Nodes" counts items, expressions, statements, patterns, paths, and
parameters. Compile time is linear: tests/scaling.rs measures 150 to 170 ns
per node from 146k to 2.3M nodes (the criterion spread above is allocator
noise around that). The last row is the cost of PHP nested-write separation:
LSB cannot ask whether an inner array is shared, so every nested write copies
the inner array's store while the old copy is uncollected (about 28 times the
plain-append case here). An LSB instruction that separates only when shared
is requested from the spec owners.
Contributing
See dev/DIRECTIVES.md for engineering standards and the definition of done. Before a PR: cargo fmt --all, cargo clippy --all-targets --all-features -- -D warnings, and cargo test --all-features must be clean.