Skip to content

Pipeline

The stages, gates, and artifacts that transform Atoll source into WebAssembly.

Updated View as Markdown

Compilation narrows the program through three semantic representations and two WebAssembly artifacts:

source files
    ↓  lex, parse, recover
tokens + AST
    ↓  hoist, resolve, check, infer, lower
typed HIR
    ↓  discover and materialize generic instances
monomorphic HIR
    ↓  build CFG, make suspension explicit, optimize, insert ownership
MIR
    ↓  canonicalize, lower to WasmIR, allocate, encode
per-unit WebAssembly
    ↓  merge allocator + units, remap indices, add metadata
linked program WebAssembly

The arrows are contracts. A stage consumes only the previous stage’s public facts, establishes the invariants needed by the next stage, and returns diagnostics when it cannot do so.

Driver entry

atoll build accepts a file, a bare directory, or a path within a manifest project:

Input Compilation scope
File outside an atoll.toml project That file, using the single-file compatibility path
Bare directory Every discovered .at file as an ad-hoc project
Path at or below atoll.toml The complete manifest project

Project builds use the Salsa-backed driver in atoll-db. A one-shot CLI build creates a fresh database, so it gains one canonical query composition but not cross-command cache reuse. Long-lived consumers such as the language server retain the database and update tracked file inputs.

atoll check stops after parsing and semantic analysis. atoll build continues through specialization and WebAssembly emission. atoll run selects an emitted unit and hands it to the runtime. These commands should agree on front-end diagnostics because they share the same parsing and semantic pipeline.

Stage contracts

Syntax

The syntax crate turns bytes into typed tokens and an arena-backed AST. It interns source strings, attaches byte ranges, and attempts error recovery so a single malformed construct does not erase the remainder of the file. The AST contains syntax, not resolved identities or inferred types.

Analysis

Semantic analysis combines files into one project:

  1. install the compiler prelude and project configuration;
  2. hoist declaration surfaces;
  3. resolve module imports and names;
  4. lower bodies while checking types, effects, visibility, and decorators;
  5. register layouts and data-query metadata;
  6. return typed HIR and diagnostics.

This is the last stage allowed to decide language meaning. Later stages may validate their own representation invariants but should not invent a missing type, effect, layout, or overload decision.

Specialization

Generic source functions remain useful semantic definitions, but the backend requires concrete bodies. Monomorphization walks reachable calls, derives a canonical substitution for each generic callee, reuses an existing specialization on a cache hit, and otherwise clones and substitutes the body. Calls are rewritten to concrete symbols. Instantiated nominal types receive concrete layouts at the same boundary.

MIR

MIR makes control flow and suspension explicit. It builds blocks and terminators, computes frame state, runs validation and optimization, selects calling conventions, and inserts explicit ownership operations such as Clone and Drop. Its pipeline contains both per-function passes and unit-wide passes, so “compile one function” does not imply that every cross-function optimization can be skipped.

WebAssembly

Code generation discovers entry units and each unit’s deterministic transitive closure. Canonical MIR lowers mechanically into WasmIR, a smaller SSA form whose values already have natural WebAssembly types. WasmIR passes perform backend-local transforms, allocation, and control-flow structuring. The encoder produces a validator-clean WebAssembly module per unit.

The ordinary build writer currently writes those per-unit modules beneath target/atoll/units/. The linker is a separate compiler API used by deployment and runtime assembly; it can merge the allocator module and one or more units into a program module.

Hard gates

Each transition has a stop condition:

Gate Failure behavior
Parse or semantic errors No specialization or code generation
Specialization errors No partial unit artifacts
MIR invariant or ABI errors Diagnostic attributed to the IR layer
Codegen errors Unit bytes are not written as a successful build
Invalid encoded module Backend failure; validation must not be bypassed
Link invariant mismatch Link error naming the input module or unsupported section

Warnings and hints can survive a successful build. Error-severity diagnostics cannot. The CLI aggregates diagnostics by layer so users can see the source problem while compiler tests can assert which stage owned it.

Optimization modes

The command-line optimization levels select a policy rather than different languages:

Mode Intent
-O0 / --debug Lowering core, debuggable output, minimal optional optimization
-O1 Balanced optimization
-O2 / --release Full release pipeline; current default

All modes must preserve observable language behavior and emit valid modules. Pass-disable and trace controls are debugging tools; a fixed source, compiler revision, and effective option set must still produce deterministic output.

Artifacts and identity

An Atoll unit is a compiler work item rooted at an entry function. The current discovery convention recognizes main and entry_*, with extra roots available to test infrastructure. A unit owns a stable name, root symbol, source range, and ordered static closure.

A per-unit module is not yet a deployed instance. The linker can combine units into one or more link groups and embeds the atoll.units metadata consumed by the runtime. The runtime then creates unit instances with their own scheduler and arena state. See Units for the distinction.

Observability

The compiler can expose AST, HIR, MIR, WasmIR, and WebAssembly-oriented dumps. Typed trace events connect transformations across stages, while source-origin tables connect lower operations back to expressions. Provenance defines how those identities survive relocation, optimization, instruction expansion, and encoding. Debug output must remain optional: enabling it may add debug sections or trace side channels, but it must not change program semantics.

Continue with Syntax for the first stage or Diagnostics for the cross-stage failure model.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close