Description
Goal
Compile a Perry program to a standalone .wasm that runs under a WASI runtime (wasmtime, wasmer, …) with no JavaScript host, independent of the existing browser --target wasm/web.
Why not extend perry-codegen-wasm
The browser backend is a thin emitter: strings, objects, arrays, closures, JSON etc. are ~250 functions imported from wasm_runtime.js (~5k lines, values live on the JS heap). A WASI host has no JS, so reusing it would mean re-implementing that whole runtime inside the module — a second runtime that shares nothing with perry-runtime, where Perry's Node parity actually lives.
Chosen route
The native LLVM pipeline targeting wasm32-wasip2 (Tier-2 Rust target, prebuilt std), linked against perry-runtime built for that target.
- GC: Perry keeps its own collector in linear memory (as Go/.NET do). Wasm code cannot scan its own stack, so roots come from the existing shadow-stack fallback that codegen already uses on targets without a frame walker. Wasm GC (Wasm 3.0) is not usable here — LLVM cannot emit GC types — and would only matter for a separate, future browser backend with a non-NaN-boxed value representation.
- Networking: available on preview 2 (
wasi:sockets,ip-name-lookup,wasi:http).socket2(withall) builds forwasm32-wasip2but notwasm32-wasip1. Needs a WASI event-loop backend (turnloopis compiled out on wasm). - Threads:
wasm32-wasip1-threadsexists but has no preview-2 equivalent yet. Default:parallelMap/parallelFilterrun sequentially,spawnruns on the main thread (correct, not parallel). A threaded build can come later. - Child processes: no WASI proposal provides process spawning;
child_processthrows a clear "not supported on WASI" error. - Exceptions: runtime + generated code use setjmp/longjmp → LLVM's wasm SjLj lowering on the exception-handling proposal, wasi-libc
libsetjmp, and a runtime with EH enabled.
Measured starting point (2026-09-26)
cargo check -p perry-runtime --target wasm32-wasip1 --no-default-features:
- blocked first by unconditional
socket2/hickory-protoand theperry_sjlj.cbuild (no WASI sysroot); - with those gated off: 61 name-resolution-stage errors (a floor — type-check errors surface after). Two families: 64-bit assumptions (
FRAME_RELEASED: usize = 1 << 63,offset_of!(ObjectHeader, meta) == 8, Map/Set/HotTls/shadow-stack/Boxlayout asserts) and platform code without a WASI arm (child_process/turnloop_proc, DNS,eh.rsdladdr/unwind regs,date.rslocal time).
The ILP32 family also means arm64_32-apple-watchos no longer builds — nothing in CI checks a 32-bit target.
Phases
- 1.
perry-runtimebuilds for 32-bit (ILP32) targets; CIcargo checkarm so it stays that way - 2. WASI arms for platform code (fs via preopens, UTC-only local time, sockets/DNS via wasip2, sequential threads,
child_processunsupported, event-loop backend) - 3. Codegen: initialize the WebAssembly LLVM backend, shadow-stack roots, SjLj via wasm EH
- 4.
--target wasi:wasm-ld/component link,.wasmoutput, wasmtime smoke test - 5. CI: gap-suite subset under a WASI runtime
Activity
--target wasilink driver and wasmtime smoke test #11379perry-runtime: build for ILP32 targets; refuse ILP32 codegen (#11376)
perry-runtime: build for ILP32 targets; refuse ILP32 codegen (#11376)
WASI: link `perry compile --target wasi` into a runnable component
WASI: link `perry compile --target wasi` into a runnable component
WASI: link `perry compile --target wasi` into a runnable component
WASI: link `perry compile --target wasi` into a runnable component
WASI: link `perry compile --target wasi` into a runnable component
proggeramlug commentedon Oct 8, 2026
Progress: #12208 (complete standalone WASIp2 acceptance support) is merged. Leaving this tracking issue open for the remaining items.
guest271314 commentedon Oct 11, 2026
@proggeramlug So is this in the latest release on NPM?
proggeramlug commentedon Oct 11, 2026
@guest271314 not yet on npm, I'll tag you once this is the case. this is on latest main only thus far.
TheHypnoo commentedon Oct 26, 2026
WASI issues that belong to this tracker:
--target wasip1, a WASI 0.1 core-module output next to--target wasi#13259