Skip to main content

Module wasm

Module wasm 

Source
Expand description

WebAssembly code generation from VM bytecode: the shared byte emitter (wasm::encode + wasm::func, always built) plus the WS6 browser WASM-JIT tier (wasm::region_jit, behind the wasm-jit feature). The direct AOT backend (largo build --emit wasm) consumes the same emitter. WebAssembly code generation from VM bytecode — one shared codegen, two consumers.

WASM has only structured control flow, so the register-machine bytecode (&[Op], with absolute jumps) is lowered through a basic-block dispatch loop (a loop of blocks with a br_table on a “next block” local). That lowering (cfg) and the hand-rolled binary encoder (encode) are the hard, validated core; both of the following build on it:

  • region_jit — the WS6 browser JIT tier: emits one WebAssembly module per hot function and runs it on wasmi (native) or the host’s real WebAssembly (wasm32). Gated behind the wasm-jit feature.
  • the direct AOT backend (module) — assemble_program (and its source-level wrapper crate::compile::compile_to_wasm): emits one self-contained .wasm for a whole program with NO rustc/cargo/wasm-bindgen/linker. Feature-independent (needs only encode/cfg/kind, not wasmi/js_sys); exposed today as a library API.

§The AOT backend

§Value model (kind)

The bytecode’s arithmetic and Show are runtime-polymorphic (one Op::Add adds Ints or Floats), so a standalone module must know each register’s static kind. A dataflow fixpoint infers a kind::Kind per register, each mapping to exactly one wasm value type:

  • Scalars, by value: Int/Bool/Momenti64, Floatf64, Datei32.
  • Heap values, an i32 handle into linear memory: Text, Struct, Map, Set, Enum, Closure, Tuple (heterogeneous), and the sequences SeqInt/SeqFloat/SeqText/ SeqStruct/SeqEnum/SeqSeqInt (plus SeqAny, a not-yet-refined empty sequence).

Int and Bool share i64, so a register reused across them coalesces. A register reused across kinds of different value types in disjoint live ranges (e.g. an Enum loop variable whose slot a later Int reuses) is Unsupported — a sound refusal (never a miscompile), pending register-live-range splitting.

§Heap value model (self-contained, no linker)

When a program uses a heap op the module gets a linear memory + a bump-allocator pointer global __heap_ptr (scalar-only modules stay memory-free). Every heap value is a stable i32 handle so a realloc-on-grow never invalidates the register holding it:

  • A sequence or Set is a 16-byte header [len:i32][cap:i32][data_ptr:i32] over a separate buffer of 8-byte slots — a scalar element by value, a handle element (Text / Struct / Enum / inner sequence) in the slot’s low word. Indexing is 1-based and bounds-checked (trap on OOB); Push/Add reallocate the buffer and rewrite the header in place. A Seq of Struct flows the element’s field layout to an extracted element so (item N of xs)'s field resolves.
  • A Map is the same header over 16-byte [key][value] entries — Int or Text keys, Int/Float/Bool values — looked up by linear scan, order-independent so byte-identical to the VM’s hashmap for get/insert/contains/length.
  • A Struct is a header over 8-byte field slots (field names are compile-time only; a struct_layout analysis maps each field to its slot + kind). A Tuple is the same, by position. An Enum is [tag:i32][arg slots…] (the tag is the constructor’s constant index, so an i32.eq on tags is the VM’s constructor-name compare; payload args follow). A Closure is [func_idx:i32][captured values…], invoked by call_indirect through the module’s function table; captures (locals and snapshotted globals) pass as trailing parameters.

Show of a scalar, a Text, a sequence of scalars/Text, or a Set (all insertion-ordered, so deterministic) is byte-identical to the VM; whole-Map/whole-Struct display (hashmap/field order is non-reproducible against the AOT’s insertion order) is the deferred remainder.

§Host interface

Raw env.* imports (no wasm-bindgen — runs under wasmi/wasmtime/a browser shim): the print_* sinks (the host formats to match the tree-walker’s to_display_string, reading sequences / Sets / Text out of the exported memory), pow_ff/pow_fi (exact powf/powi), today/now (honoring the test fixed clock), and fmt_*_into (interpolating scalar operands into a module buffer). A runtime error — out-of-bounds index, undefined variable, an overflow the oracle could not rule out — lowers to a wasm unreachable trap (the standalone module has no VM to surface the message), a contract the lock proves as tw-error ⟺ wasm-trap.

§The lock (tests/wasm_aot_lock.rs)

WASM == VM == Tree-walker, mirroring the Futamura locks. An EXHAUSTIVE op_support(&Op) match (compiler-enforced: a new VM op fails to build until classified Supported/Deferred) plus a behavioural biconditional (a program compiles IFF every op is Supported, then runs byte-identically) plus a full-language coverage proof (every Supported instruction is exercised end-to-end over the curated corpus). Goal: the Deferred set shrinks to ∅.

Modules§

encode
WebAssembly binary-encoding primitives — the shared byte layer beneath both the browser WASM-JIT tier (super::region_jit) and the direct AOT backend ([super::module]).
func
Per-function WebAssembly lowering: a region of VM bytecode (&[Op], a register machine) → a WebAssembly function (a stack machine with i64 locals).
region_jit
The WS6 browser WASM-JIT tier (region/function emit + host instantiation). Only the running of emitted modules touches wasmi/js_sys, so this — and only this — is gated behind the wasm-jit feature; encode/func (pure byte emission) are always built. WS6 (FINISH_INTERPRETER Phase 13) — the browser WASM-JIT tier.

Enums§

WasmLowerError
Why the AOT WebAssembly backend declined to lower a program. The backend is total on its supported fragment and rejects everything else explicitly (never miscompiles): the corpus ratchet asserts each program is either lowered-and-correct or on the allowed-unsupported list, so a program silently leaving the fragment fails a test rather than emitting wrong code.

Functions§

assemble_program
Lower a whole program to a self-contained WebAssembly module (the bytes of a .wasm file). Returns WasmLowerError::Unsupported for any program outside the scalar fragment — a sound refusal, never a wrong module.