WebAssembly
--target wasm32-none and --target wasm32-wasi build a program into a WebAssembly module. As on
bare metal, nothing in the build is C: voltc compiles the
program and std through LLVM and links the module with wasm-ld, which comes with LLVM’s lld.
voltc build hello.volt --target wasm32-wasi -o hello.wasm # for a WASI hostvoltc build hello.volt --target wasm32-none -o hello.wasm # for a web pageThe two targets
Section titled “The two targets”--target |
For | Imports |
|---|---|---|
wasm32-wasi |
a WASI host: node, wasmtime, wasmer | wasi_snapshot_preview1’s fd_write and proc_exit |
wasm32-none |
a web page, or any host that gives the module a console | env.volt_console_write(ptr, len) and env.volt_exit(code) |
Both export _start, which runs the program’s main and then exits with its result, and the
module’s memory. usize is 32 bits, as a pointer is. std’s runtime is the bare-metal one: printing
goes to the console, a panic prints its message there and exits with 101, and the heap starts where
wasm-ld puts __heap_base and grows the memory a 64 KiB page at a time when it needs more. A
program that defines volt_console_write or volt_exit itself (as an export fn) is given that
instead of the host’s.
In both, @cfg("os") is none (std has no OS layer there yet), @cfg("arch") is wasm32 and
@cfg("target") is the --target name, so a library can tell them apart.
Running a module
Section titled “Running a module”Under node, WASI’s module runs through node:wasi:
import { readFile } from "node:fs/promises";import { WASI } from "node:wasi";
const wasi = new WASI({ version: "preview1", args: [], env: {}, returnOnExit: true });const { instance } = await WebAssembly.instantiate(await readFile("hello.wasm"), { wasi_snapshot_preview1: wasi.wasiImport,});process.exitCode = wasi.start(instance);A page gives a wasm32-none module its console and exit:
let memory;const text = new TextDecoder();const env = { volt_console_write: (p, n) => console.log(text.decode(new Uint8Array(memory.buffer, p, n))), volt_exit: (code) => { throw new Error(`exited with ${code}`); },};const { instance } = await WebAssembly.instantiateStreaming(fetch("hello.wasm"), { env });memory = instance.exports.memory;instance.exports._start();examples/wasm/run.mjs runs either kind under node. The test suite builds examples/wasm/hello.volt,
128-bit integer arithmetic and every f128 operation (std::softfloat’s quad routines: the module
has no compiler-rt) for both targets, in debug and release builds, and runs them there.
Not yet
Section titled “Not yet”- WASI’s files, arguments, environment and clock: std’s
fs,processandtimeare bare metal’s there, sostd::process::args()is empty and opening a file fails. - Threads, and calling JavaScript functions other than the console and exit from Volt.