Skip to content

Commit 98d06b6

Browse files
authored
Change wasmtime wast to be powered from JSON AST (#11113)
* Change `wasmtime wast` to be powered from JSON AST This commit is a refactoring of the `wasmtime-wast` crate to use the `json-from-wast` crate added in bytecodealliance/wasm-tools#2247 instead of the `wast` crate directly. The primary motivation for this PR is to make spec tests more suitable for running inside of Miri. There are two primary factors in how Miri is slow with wast tests today: * Compiling WebAssembly modules. These are all embedded throughout `*.wast` files and basically need to be precompiled to run in Miri. * Parsing the `*.wast` script itself. The scripts are generally quite large in the upstream spec test suite and parsing the script requires parsing all modules as well, so this can take quite some time. The goal of this commit was to leverage `json-from-wast` to perform much of the heavy lifting on the host and then have a "cheap" parse step in Miri which deserializes the JSON and runs precompiled `*.cwasm` files. The main change then required of `wasmtime-wast` was to use the JSON AST as its "IR" instead of `wast` directly. The actual transformation of the `wasmtime-wast` crate isn't too bad, mostly just some refactorings here and there. A new `./ci/miri-wast.sh` script is added which first runs on native with `wasmtime wast --precompile-save` and then runs in Miri with `wasmtime wast --precompile-load`. In this manner I was able to get a number of spec test `*.wast` files running in Miri. Unfortunately, though, Miri is still far too slow to run in CI. One major bottleneck is that the `*.json` files are pretty large and take a nontrivial amount of time to parse. Another issue is that these tests run a fair bit of code which with Miri's ~million-x slowdown. For the first problem I tried using other more-binary serialization formats but the JSON AST is unfortunately not compatible with many of them (e.g. `postcard`, which we use for Wasmtime, is not compatible with the JSON AST's structure in Rust types). For the latter I'm not sure what to do... In the end I figured this might be a reasonable refactoring to go ahead and land anyway. It at least enables running `*.wast` tests in Miri while removing a large part of the runtime slowdown. We can in theory still allow-list some tests to run as they don't all take forever. Some future breakthrough is still going to be required though to run everything through Miri. * Document usage * Adjust lint * Fix CI issues
1 parent 030a005 commit 98d06b6

File tree

16 files changed

+622
-438
lines changed

16 files changed

+622
-438
lines changed

.github/workflows/main.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1026,6 +1026,7 @@ jobs:
10261026
- crate: "wasmtime-environ --all-features"
10271027
- crate: "pulley-interpreter --all-features"
10281028
- script: ./ci/miri-provenance-test.sh
1029+
- script: ./ci/miri-wast.sh ./tests/spec_testsuite/table.wast
10291030
needs: determine
10301031
if: needs.determine.outputs.test-miri && github.repository == 'bytecodealliance/wasmtime'
10311032
name: Miri

Cargo.lock

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,7 @@ wit-parser = "0.236.0"
339339
wit-component = "0.236.0"
340340
wasm-wave = "0.236.0"
341341
wasm-compose = "0.236.0"
342+
json-from-wast = "0.236.0"
342343

343344
# Non-Bytecode Alliance maintained dependencies:
344345
# --------------------------

ci/miri-wast.sh

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#!/bin/bash
2+
3+
# Helper script to execute a `*.wast` test in Miri. This is only lightly used on
4+
# CI and is provided here to assist with development of anything that ends up
5+
# using unsafe for example.
6+
#
7+
# Example usage is:
8+
#
9+
# ./ci/miri-wast.sh ./tests/spec_testsuite/br_if.wast
10+
#
11+
# extra flags to this script are passed to `cargo run wast` which means they
12+
# must be suitable flags for the `wast` subcommand.
13+
14+
set -ex
15+
16+
rm -rf ./miri-wast
17+
mkdir ./miri-wast
18+
cargo run -- wast --target pulley64 --precompile-save ./miri-wast "$@" \
19+
-O memory-reservation=$((1 << 20)) \
20+
-O memory-guard-size=0 \
21+
-O signals-based-traps=n \
22+
-O memory-init-cow=n
23+
24+
MIRIFLAGS="$MIRIFLAGS -Zmiri-disable-isolation -Zmiri-permissive-provenance" \
25+
cargo miri run -- wast -Ccache=n --target pulley64 --precompile-load ./miri-wast "$@" \
26+
-O memory-init-cow=n

crates/cranelift/src/func_environ/gc.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ pub use imp::*;
2626

2727
/// How to initialize a newly-allocated array's elements.
2828
#[derive(Clone, Copy)]
29-
#[cfg_attr(not(any(feature = "gc-null", feature = "gc-drc")), allow(dead_code))]
29+
#[cfg_attr(not(feature = "gc"), expect(dead_code))]
3030
pub enum ArrayInit<'a> {
3131
/// Initialize the array's elements with the given values.
3232
Elems(&'a [ir::Value]),

crates/cranelift/src/func_environ/gc/enabled.rs

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,10 @@ pub fn gc_compiler(func_env: &mut FuncEnvironment<'_>) -> WasmResult<Box<dyn GcC
5858
}
5959
}
6060

61-
#[cfg_attr(not(feature = "gc-drc"), allow(dead_code))]
61+
#[cfg_attr(
62+
not(feature = "gc-drc"),
63+
expect(dead_code, reason = "easier to define")
64+
)]
6265
fn unbarriered_load_gc_ref(
6366
builder: &mut FunctionBuilder,
6467
ty: WasmHeapType,
@@ -73,7 +76,10 @@ fn unbarriered_load_gc_ref(
7376
Ok(gc_ref)
7477
}
7578

76-
#[cfg_attr(not(any(feature = "gc-drc", feature = "gc-null")), allow(dead_code))]
79+
#[cfg_attr(
80+
not(any(feature = "gc-drc", feature = "gc-null")),
81+
expect(dead_code, reason = "easier to define")
82+
)]
7783
fn unbarriered_store_gc_ref(
7884
builder: &mut FunctionBuilder,
7985
ty: WasmHeapType,
@@ -461,7 +467,10 @@ pub fn translate_array_new_fixed(
461467

462468
impl ArrayInit<'_> {
463469
/// Get the length (as an `i32`-typed `ir::Value`) of these array elements.
464-
#[cfg_attr(not(any(feature = "gc-drc", feature = "gc-null")), allow(dead_code))]
470+
#[cfg_attr(
471+
not(any(feature = "gc-drc", feature = "gc-null")),
472+
expect(dead_code, reason = "easier to define")
473+
)]
465474
fn len(self, pos: &mut FuncCursor) -> ir::Value {
466475
match self {
467476
ArrayInit::Fill { len, .. } => len,
@@ -473,7 +482,10 @@ impl ArrayInit<'_> {
473482
}
474483

475484
/// Initialize a newly-allocated array's elements.
476-
#[cfg_attr(not(any(feature = "gc-drc", feature = "gc-null")), allow(dead_code))]
485+
#[cfg_attr(
486+
not(any(feature = "gc-drc", feature = "gc-null")),
487+
expect(dead_code, reason = "easier to define")
488+
)]
477489
fn initialize(
478490
self,
479491
func_env: &mut FuncEnvironment<'_>,
@@ -1140,7 +1152,10 @@ fn uextend_i32_to_pointer_type(
11401152
/// in its initialization.
11411153
///
11421154
/// Traps if the size overflows.
1143-
#[cfg_attr(not(any(feature = "gc-drc", feature = "gc-null")), allow(dead_code))]
1155+
#[cfg_attr(
1156+
not(any(feature = "gc-drc", feature = "gc-null")),
1157+
expect(dead_code, reason = "easier to define")
1158+
)]
11441159
fn emit_array_size(
11451160
func_env: &mut FuncEnvironment<'_>,
11461161
builder: &mut FunctionBuilder<'_>,
@@ -1185,7 +1200,10 @@ fn emit_array_size(
11851200

11861201
/// Common helper for struct-field initialization that can be reused across
11871202
/// collectors.
1188-
#[cfg_attr(not(any(feature = "gc-drc", feature = "gc-null")), allow(dead_code))]
1203+
#[cfg_attr(
1204+
not(any(feature = "gc-drc", feature = "gc-null")),
1205+
expect(dead_code, reason = "easier to define")
1206+
)]
11891207
fn initialize_struct_fields(
11901208
func_env: &mut FuncEnvironment<'_>,
11911209
builder: &mut FunctionBuilder<'_>,
@@ -1386,7 +1404,10 @@ impl FuncEnvironment<'_> {
13861404
/// reference is null or is an `i31ref`; otherwise, it will be zero.
13871405
///
13881406
/// This method is collector-agnostic.
1389-
#[cfg_attr(not(feature = "gc-drc"), allow(dead_code))]
1407+
#[cfg_attr(
1408+
not(feature = "gc-drc"),
1409+
expect(dead_code, reason = "easier to define")
1410+
)]
13901411
fn gc_ref_is_null_or_i31(
13911412
&mut self,
13921413
builder: &mut FunctionBuilder,

crates/fuzzing/src/oracles.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -764,7 +764,7 @@ pub fn wast_test(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<()> {
764764
})
765765
.unwrap();
766766
wast_context
767-
.run_buffer(test.path.to_str().unwrap(), test.contents.as_bytes())
767+
.run_wast(test.path.to_str().unwrap(), test.contents.as_bytes())
768768
.unwrap();
769769
Ok(())
770770
}

crates/wast/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ wasmtime = { workspace = true, features = ['cranelift', 'wat', 'runtime', 'gc',
1919
wast = { workspace = true, features = ['dwarf'] }
2020
log = { workspace = true }
2121
tokio = { workspace = true, features = ['rt'] }
22+
json-from-wast = { workspace = true }
23+
wasmparser = { workspace = true }
24+
serde_json = { workspace = true }
25+
object = { workspace = true, features = ['unaligned'] }
2226

2327
[features]
2428
component-model = ['wasmtime/component-model']

0 commit comments

Comments
 (0)