A tiny WebAssembly interpreter written in Zig. This interpreter implements WebAssembly 1.0 core features with selected 2.0 extensions, primarily focusing on SIMD operations. This project is intended for personal understanding of the WebAssembly specification. Do not use in production environment.
- Zig 0.15.2
- Python 3.x (for test runner)
- Git (for fetching test suite)
# Build the main interpreter
zig build
# Build the spec test runner
zig build spec_test
# Or use the Makefile
make build
make build-spec-test# Run a WebAssembly binary file
./zig-out/bin/zig-wasm-interp somefile.wasm -r function_name -a 32
# Run with verbose output
./zig-out/bin/zig-wasm-interp somefile.wasm -v -r function_name -a i32:42 -a f64:3.14# Use the spec test runner for text format files
./zig-out/bin/spec_test test.wast -vThis project uses test files from the spectec repository (WebAssembly 3.0 branch).
Important Note: The test runner executes spec test assertions (assert_return, assert_trap, assert_invalid, etc.) against parsed and loaded modules. The 99.997% assertion pass rate reflects actual execution correctness for implemented features.
# Download and setup WebAssembly test files
make setup-tests
# This will:
# - Clone the spectec repository (wasm-3.0 branch)
# - Copy .wast test files to wasm_tests/
# - Exclude GC-related tests that require advanced runtime support# Run all WebAssembly spec tests
make test
# Run specific test file
python3 ./run_spectec_tests.py wasm_tests/address.wast
# Run with verbose output
python3 ./run_spectec_tests.py --verbose wasm_tests/
# Stop on first failure
python3 ./run_spectec_tests.py --failfast wasm_tests/The codebase is organized into seven main modules:
- wasm-core (
src/core/): WebAssembly types, instructions, and basic structures - wasm-decode (
src/decode/): Binary format parsing and module loading - wasm-text-decode (
src/text_decode/): Text format (.wast/.wat) parsing - wasm-validate (
src/validate/): WebAssembly validation rules - wasm-runtime (
src/runtime/): Execution engine and interpreter
-
spec-types (
src/spec-types/): Pure type definitions for spec test commands and values- No runtime dependencies, only depends on wasm-core
- Defines Command, Action, Value, Result types for test specifications
- Includes FloatType for NaN handling (canonical/arithmetic)
-
spec-test-errors (
src/spec-test-errors/): Error string mapping for spec tests- Maps error strings from test files to typed error enums
- Safe fallback to OtherError for unknown strings (no panics)
-
wasm-spec-test (
src/spec_test/): Test execution framework- Unified test runner for both .wast and .json formats
- Executor module with value conversion and comparison logic
- JSON and WAST readers for different test formats
core
↓
spec-types (pure)
↓
text-decode → spec-test
↓
(decode, validate, runtime)
↓
spec-test-errors
↓
spec-test (executor + runners)
# Development
make help # Show available commands
make build # Build the interpreter
make build-spec-test # Build spec test runner
make clean # Clean all build artifacts
# Testing
make setup-spectec # Clone/update spectec repository
make setup-tests # Setup WebAssembly 2.0 test files
make test # Run WebAssembly spec tests
make clean-tests # Remove test files
# Manual testing
zig build test # Run unit tests
zig fmt src/ # Format source codeThis interpreter implements WebAssembly 1.0 core specification, all WebAssembly 2.0 extensions, and selected WebAssembly 3.0 features. The test suite runs 61,873/61,875 (99.997%) spec assertions successfully across 237 test files. The only 2 remaining failures are in recursive type groups requiring GC proposal support.
- All basic numeric instructions (i32, i64, f32, f64)
- Control flow (block, loop, if, br, br_if, br_table, call, call_indirect)
- Memory operations (load, store with various sizes)
- Local and global variables
- Function calls and returns
-
Vector/SIMD Instructions (Fixed-Width 128-bit)
- All standard SIMD operations (i8x16, i16x8, i32x4, i64x2, f32x4, f64x2)
- Vector load/store operations with lane access
- SIMD arithmetic, comparison, and bitwise operations
- 236 standard SIMD instructions fully implemented
-
Bulk Memory Operations
memory.copy- Fast memory-to-memory copyingmemory.fill- Memory initialization with byte valuesmemory.init- Initialize memory from passive data segmentsdata.drop- Drop passive data segments
-
Reference Types (Basic)
funcrefandexternreftypesref.null,ref.func,ref.is_nullinstructions- Multiple tables with reference types
- Table operations:
table.get,table.set,table.init,table.copy,table.grow,table.size,table.fill
-
Non-Trapping Float-to-Int Conversions
i32.trunc_sat_f32_s/u,i32.trunc_sat_f64_s/ui64.trunc_sat_f32_s/u,i64.trunc_sat_f64_s/u
-
Sign Extension Instructions
i32.extend8_s,i32.extend16_si64.extend8_s,i64.extend16_s,i64.extend32_s
-
Multi-Value Support
- Functions and blocks can return multiple values
- Multi-value block types
-
Relaxed SIMD Instructions
- Non-deterministic SIMD operations for performance
- Relaxed min/max, madd/nmadd operations
- Relaxed lane selection and truncation
- 21 relaxed SIMD instructions implemented
-
Memory64 (64-bit memory addressing)
- Full u64 address handling for all memory operations
- Dynamic address type selection (i32/i64) based on memory type
- 100% pass rate on all memory64 spec tests
-
Multi-Memory (multiple memory instances per module)
- Multiple memory addresses per module instance
memory.copysupports copying between different memories- All memory instructions accept memory index parameter
-
Table64 (64-bit table addressing)
- Dynamic address type (i32/i64) for all table operations
- 100% pass rate on all table64 spec tests
-
Tail Calls (tail call optimization)
return_call- Tail call to a function by indexreturn_call_indirect- Tail call through a table- Discards current frame before invoking callee (zero stack growth)
- 100% pass rate on return_call and return_call_indirect spec tests
-
Extended Constant Expressions
- Arithmetic in constant expressions:
i32.add,i32.sub,i32.mul - 64-bit arithmetic:
i64.add,i64.sub,i64.mul global.getof imported globals in initializer expressions
- Arithmetic in constant expressions:
-
Exception Handling
throw- Throw an exception with tag parameterstry_table- Try block with catch clauses (catch,catch_ref,catch_all,catch_all_ref)- Exception tag imports and exports
- Cross-frame exception propagation
-
Typed Function References
call_ref- Call a function through a typed referencereturn_call_ref- Tail call through a typed referenceref.as_non_null- Assert a reference is non-nullbr_on_null/br_on_non_null- Branch based on reference nullness- 100% pass rate on all typed function reference spec tests
- Text format (.wast/.wat) parsing and execution via spec test runner
- Binary format (.wasm) decoding and execution
- Module validation (types, control flow, stack usage)
- Import/export system with inter-module linking
- Memory bounds checking (traps on out-of-bounds access)
- 237/237 test files from spectec successfully parse and load
- 236/237 test files achieve 100% assertion pass rate
- 61,873/61,875 (99.997%) assertions pass across the test suite
- Only 2 failures remain in
type-rec.wast(GC proposal recursive type groups) - Test files cover WebAssembly 1.0, 2.0, and some 3.0 features
The following WebAssembly 3.0 features are not implemented:
- ❌ Garbage Collection (struct, array, i31ref, recursive type groups, and related GC instructions)
GC features can be parsed from .wast files but their instructions are not executed.
The reference type system internally supports only funcref and externref. Non-nullable references (ref func), typed references (ref null $t, ref $t), and reference subtyping are collapsed to basic types during parsing. Most operations work correctly with collapsed types, but import matching for globals and tables that use finer-grained reference types may fail.
This is an educational project for understanding WebAssembly internals. Feel free to explore and learn from the code structure.