A WebAssembly (aka Wasm) formalisation in Coq(Rocq), based on the official specification.
(C) M. Bodin, P. Gardner, J. Pichon, C. Watt, X. Rao 2019-2025 - see LICENSE.txt
The quotes from the WebAssembly standard (starting with std-doc) are (C) their respective authors.
The files located in src/Parray are adapted from the Rocq kernel and therefore licensed under GNU LPGL 2.1 - see src/Parray/LICENSE.LGPL.
The current project formalises Wasm version 2.0 with the following additions:
- [+] Subtyping systems (from the future funcref/GC extension proposals);
- [+] Tail-call.
SIMD execution is implemented via the corresponding evaluation functions in the reference implementations. The Rocq mechanisation uses opauqe opcodes (from the binary format) for parsing the SIMD instructions and handling their execution.
A large part of the old Wasm 1.0 formalisation has been published at FM'21, with many additions to the repository since then.
- Core definitions of WasmCert-Coq and WasmRef-Coq.
- Soundness results for WasmRef-Coq (interpreter) with respect to WasmCert-Coq.
- Type safety results for Wasm typing system.
- Soundness and completeness results for the type checker with respect to the typing system.
- Implementing Wasm numerics (via CompCert numerics).
- Soundness results for module instantiation.
- Proof carrying interpreter deriving progress.
- Interpreter with optimised context representations.
- Updates for Wasm 2.0 + subtyping systems + tail-call.
- Validate WasmRef-Coq (conformance tests).
This repository contains a mechanised Wasm program logic using the Iris framework: iris branch.
This is migrated from an older build for the artefact submitted along with the Iris-Wasm publication at PLDI'23.
A more updated version working with opam can be found here.
This repository contains some experimental work on a parser for the binary format which is currently unverified.
As the parser forms a part of the extracted interpreter, any error in the parser would result in the interpreter reporting syntax error for some valid Wasm binaries. Bug reports are appreciated!
The project can be installed using opam.
Compiling the dependencies and codebase requires having at least 8 GB of RAM on your computer.
opam repo add coq-released https://coq.inria.fr/opam/released
opam install .The project comes with a small set of tests for the extracted interpreter. To run these tests:
dune testThe project also includes the official test suite as a submodule under wast_testsuite. To run the interpreter against the test suite, first pull the test suite submodule:
git submodule update --init --recursiveThen, run:
make run_wastThe interpreter is expected to pass all the other core tests (last tested on 17th Aug 2025):
Total passed: 54004/54004 (100.00%)Running the test suite takes around 2-3 minutes.
It is also possible to run a selected subset of the test by filtering the file names. For example,
make run_wast FILTER="simd"runs only the SIMD tests, while
make run_wast FILTER="load"runs only the test files whose names include load (i.e. various memory load instructions both for the Wasm 1.0 values and the v128 values).
Note that tail-call is not part of the standard yet and is therefore not tested.
A file wasm_coq_interpreter will have been generated under _build/install.
It takes as argument a list of Wasm files, followed by a function name to run (with the -r flag).
For instance, to interpret the function main defined in tests/add.wasm, run:
dune exec -- wasm_coq_interpreter tests/add.wasm -r mainThe project has experimental support on passing arguments to function calls in the CLI via the -a flag. For example:
dune exec -- wasm_coq_interpreter tests/add2.wasm -r main -a "i32.const 6" -a "i32.const 36"would produce
i32.const 42Modules in text format can be run with the --text flag. For example:
dune exec -- wasm_coq_interpreter tests/add2.wat -r main -a "i32.const 6" -a "i32.const 36" --textwould produce
i32.const 42The interpreter can also display intermediate states of the operational semantics:
dune exec -- wasm_coq_interpreter tests/add.wasm -r main --viwould produce:
parsing OK
instantiation OK
Post-instantiation stage for table and memory initialisers...
step 1:
(empty)
step 2:
Value:
(empty)
success after 2 steps
Instantiation success
interpreting OK
step 0:
Executing configuration:
frame 0
with values (empty)
invoke 0
end frame
step 1:
frame 0
with values (empty)
frame 1
with values (empty)
label 1
label_cont
i32.const 40
i32.const 2
i32.add
end label
end frame
end frame
step 2:
frame 0
with values (empty)
frame 1
with values (empty)
label 1
label_cont
i32.const 42
end label
end frame
end frame
step 3:
frame 0
with values (empty)
frame 1
with values (empty)
i32.const 42
end frame
end frame
step 4:
frame 0
with values (empty)
i32.const 42
end frame
step 5:
Value:
i32.const 42
success after 5 steps