Backends
Stone has two compilation backends. During development you typically use the WASM backend for instant feedback, then switch to LLVM when you're ready to build for production.
WASM (default for stone run)
An ahead-of-time bytecode compiler. Stone source is compiled directly to WebAssembly binary format — there is no intermediate C or assembly step. The compiled WASM module is instantiated and executed in-process immediately. Because the compiler emits binary bytecode directly (not text that needs assembling), compilation is near-instant even for large programs.
The WASM backend supports SIMD vectorisation and multi-threaded execution when the runtime supports it. Performance-critical builtins (sorting, string operations, PRNG) are pre-compiled from C via Emscripten and injected as native bytecode at link time, so they run at full speed without interpreter overhead.
Best for: development, scripting, quick feedback loops.
LLVM (default for stone build)
Compiles to LLVM IR, then to native machine code via clang — the same toolchain used by C, C++, and Rust compilers. Produces standalone binaries with full LLVM optimisations including auto-vectorisation, register allocation, and target-specific instruction selection. Compilation takes longer but the resulting binaries run at maximum speed.
A C runtime library (stone_runtime.c) is linked into every native binary, providing the standard library implementation (I/O, memory management, string operations).
Best for: production builds, maximum performance, distributable native binaries.
Typical Workflow
Use WASM during development for a fast edit-run cycle with near-zero compile times. When you're ready to ship or benchmark, switch to LLVM for maximum performance.
# Development — fast compile, iterate quickly
stone run main.stn
# Production — optimised native binary
stone build main.stn --optimize 3
./main
# You can also test with LLVM during development
stone run main.stn --llvm
Both backends produce identical results for the same program. The only differences are compile time and execution speed.
Compilation Pipeline
Both backends share the same frontend and optimisation passes. They diverge only at code generation:
Source (.stn)
→ Parser (AST)
→ Type Checker (Hindley-Milner resolution)
→ HIR Lowering (high-level intermediate representation)
→ Optimisation Passes
├─→ WASM Codegen → WebAssembly binary → in-process execution
└─→ LLVM Codegen → LLVM IR → clang → native binary
Optimisation passes
Stone's optimiser runs a sequence of HIR-level passes before code generation. These apply to both backends — the WASM and LLVM codegen stages receive the same optimised HIR.
Level 1 (basic):
- Constant propagation and folding
- Algebraic simplification
- Dead code elimination
Level 2 (standard — default):
- Everything in level 1, plus:
- Evolve copy elimination — removes redundant state variable copies in loops
- Strength reduction — replaces expensive operations (division, modulo) with cheaper equivalents
- Dead store elimination
- Loop-invariant code motion — hoists loop-independent computations
- Common subexpression elimination (CSE)
- Array get fusion — fuses chained array accesses into multi-dimensional operations
- Bounds check elimination — proves array accesses are in bounds and removes runtime checks
- Vectorisation analysis — annotates parallel map loops for SIMD codegen
- BLAS pattern recognition — detects matrix multiply and linear algebra patterns
- Record scalar replacement (SROA) — eliminates temporary record allocations
- Integer promotion — detects integer-only operations for native
i64emission - If-conversion — converts simple branches to branchless
selectinstructions - Loop reduction — replaces simple accumulation loops with closed-form expressions (e.g. a sum loop becomes
n * (n+1) / 2) - Block merging — coalesces linear block chains for further optimisation
Level 3 (aggressive):
- Everything in level 2, plus:
- Function inlining
- Loop unrolling
- Loop fusion — merges adjacent loops over the same range
- Array elimination — removes intermediate arrays that are immediately consumed
You can disable individual passes with --disable-pass <name> and inspect the optimised HIR with --emit-hir.
WASM codegen
Emits WebAssembly binary bytecode directly — no text format, no assembler. Pre-compiled native functions (compiled from C via Emscripten) are injected as bytecode at link time with patched call targets. The resulting WASM module is executed by the CLI's WASM runtime.
LLVM codegen
Emits LLVM IR text, then invokes clang to produce a native binary. Optimisation levels 0–3 map directly to clang's -O0 through -O3, so LLVM's own optimisation passes run on top of Stone's HIR-level passes.
Build Targets
stone build supports multiple output targets via the -t flag:
| Target | Output | Description |
|---|---|---|
llvm (default) | Native binary | Standalone executable via clang |
wasm | .wasm file | WebAssembly module |
c | .c file | Generated C source |
hir | Text | Human-readable HIR (for debugging) |
stone build main.stn # native binary (default)
stone build main.stn -t wasm -o main.wasm # WASM module
stone build main.stn -t c -o main.c # C source
stone build main.stn -t hir # print HIR