Repository: bytecodealliance/wasmtime
Pull request #6393 (2023-05-16 23:00:58)

Basic guest-profiler documentation for the book
This isn't nearly as detailed as the other profiling chapters but I'm struggling to figure out what's important to say. I'd welcome feedback, although of course we can merge this and fill in details later.

This documentation reflects the changes merged in #6362, but that merged after the 9.0 branch was created. Perhaps we should backport that PR to 9.0 so the older CLI flags never appear in a release?


Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 7c5c352f7..946b4a90c 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -677,7 +677,7 @@ dependencies = [
  "target-lexicon",
  "thiserror",
  "toml",
- "wasmparser 0.104.0",
+ "wasmparser 0.105.0",
  "wat",
 ]
 
@@ -853,7 +853,7 @@ dependencies = [
  "serde",
  "smallvec",
  "target-lexicon",
- "wasmparser 0.104.0",
+ "wasmparser 0.105.0",
  "wasmtime-types",
  "wat",
 ]
@@ -1320,7 +1320,7 @@ checksum = "c0408e2626025178a6a7f7ffc05a25bc47103229f19c113755de7bf63816290c"
 dependencies = [
  "cfg-if",
  "libc",
- "redox_syscall",
+ "redox_syscall 0.2.13",
  "winapi",
 ]
 
@@ -1499,9 +1499,9 @@ dependencies = [
 
 [[package]]
 name = "h2"
-version = "0.3.16"
+version = "0.3.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5be7b54589b581f624f566bf5d8eb2bab1db736c51528720b6bd36b96b55924d"
+checksum = "d357c7ae988e7d2182f7d7871d0b963962420b0678b0997ce7de72001aeab782"
 dependencies = [
  "bytes",
  "fnv",
@@ -1943,6 +1943,15 @@ dependencies = [
  "libc",
 ]
 
+[[package]]
+name = "matchers"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
+dependencies = [
+ "regex-automata",
+]
+
 [[package]]
 name = "maybe-owned"
 version = "0.3.4"
@@ -2170,16 +2179,6 @@ dependencies = [
  "pretty_env_logger",
 ]
 
-[[package]]
-name = "os_pipe"
-version = "0.9.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fb233f06c2307e1f5ce2ecad9f8121cffbbee2c95428f44ea85222e460d0d213"
-dependencies = [
- "libc",
- "winapi",
-]
-
 [[package]]
 name = "os_str_bytes"
 version = "6.0.0"
@@ -2217,7 +2216,7 @@ dependencies = [
  "cfg-if",
  "instant",
  "libc",
- "redox_syscall",
+ "redox_syscall 0.2.13",
  "smallvec",
  "winapi",
 ]
@@ -2600,6 +2599,15 @@ dependencies = [
  "bitflags 1.3.2",
 ]
 
+[[package]]
+name = "redox_syscall"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
+dependencies = [
+ "bitflags 1.3.2",
+]
+
 [[package]]
 name = "redox_users"
 version = "0.4.3"
@@ -2607,7 +2615,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
 dependencies = [
  "getrandom 0.2.6",
- "redox_syscall",
+ "redox_syscall 0.2.13",
  "thiserror",
 ]
 
@@ -2641,6 +2649,9 @@ name = "regex-automata"
 version = "0.1.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
+dependencies = [
+ "regex-syntax",
+]
 
 [[package]]
 name = "regex-syntax"
@@ -2660,15 +2671,6 @@ dependencies = [
  "winapi",
 ]
 
-[[package]]
-name = "remove_dir_all"
-version = "0.5.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
-dependencies = [
- "winapi",
-]
-
 [[package]]
 name = "ring"
 version = "0.16.20"
@@ -3066,16 +3068,15 @@ checksum = "d7fa7e55043acb85fca6b3c01485a2eeb6b69c5d21002e273c79e465f43b7ac1"
 
 [[package]]
 name = "tempfile"
-version = "3.3.0"
+version = "3.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
+checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998"
 dependencies = [
  "cfg-if",
  "fastrand",
- "libc",
- "redox_syscall",
- "remove_dir_all",
- "winapi",
+ "redox_syscall 0.3.5",
+ "rustix",
+ "windows-sys 0.45.0",
 ]
 
 [[package]]
@@ -3097,6 +3098,17 @@ dependencies = [
  "winapi",
 ]
 
+[[package]]
+name = "test-log"
+version = "0.2.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38f0c854faeb68a048f0f2dc410c5ddae3bf83854ef0e4977d58306a5edef50e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.92",
+]
+
 [[package]]
 name = "test-programs"
 version = "0.0.0"
@@ -3105,21 +3117,23 @@ dependencies = [
  "cap-std",
  "cargo_metadata",
  "cfg-if",
+ "heck",
  "http",
  "http-body",
  "http-body-util",
  "hyper",
- "os_pipe",
- "target-lexicon",
+ "lazy_static",
  "tempfile",
+ "test-log",
  "tokio",
+ "tracing",
  "tracing-subscriber",
  "wasi-cap-std-sync",
  "wasi-common",
  "wasmtime",
  "wasmtime-wasi",
  "wasmtime-wasi-http",
- "wat",
+ "wit-component 0.9.0",
 ]
 
 [[package]]
@@ -3284,8 +3298,12 @@ version = "0.3.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4bc28f93baff38037f64e6f43d34cfa1605f27a49c34e8a04c5e78b0babf2596"
 dependencies = [
+ "lazy_static",
+ "matchers",
+ "regex",
  "sharded-slab",
  "thread_local",
+ "tracing",
  "tracing-core",
 ]
 
@@ -3536,6 +3554,14 @@ dependencies = [
  "zeroize",
 ]
 
+[[package]]
+name = "wasi-http-tests"
+version = "0.0.0"
+dependencies = [
+ "anyhow",
+ "wit-bindgen",
+]
+
 [[package]]
 name = "wasi-preview1-component-adapter"
 version = "10.0.0"
@@ -3547,6 +3573,15 @@ dependencies = [
  "wit-bindgen",
 ]
 
+[[package]]
+name = "wasi-tests"
+version = "0.0.0"
+dependencies = [
+ "libc",
+ "once_cell",
+ "wasi 0.11.0+wasi-snapshot-preview1",
+]
+
 [[package]]
 name = "wasi-tokio"
 version = "10.0.0"
@@ -3636,6 +3671,15 @@ dependencies = [
  "leb128",
 ]
 
+[[package]]
+name = "wasm-encoder"
+version = "0.27.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e77053dc709db790691d3732cfc458adc5acc881dec524965c608effdcd9c581"
+dependencies = [
+ "leb128",
+]
+
 [[package]]
 name = "wasm-metadata"
 version = "0.5.0"
@@ -3649,32 +3693,45 @@ dependencies = [
  "wasmparser 0.104.0",
 ]
 
+[[package]]
+name = "wasm-metadata"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "27e6532071b112df81d756fabafab8cc882b84ae3d6ce9b90f3d90c80e4abe05"
+dependencies = [
+ "anyhow",
+ "indexmap",
+ "serde",
+ "wasm-encoder 0.27.0",
+ "wasmparser 0.105.0",
+]
+
 [[package]]
 name = "wasm-mutate"
-version = "0.2.24"
+version = "0.2.25"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77e6309ff390c0389b5ced7ad7812a0bc091976c606e661e71184f077bfd0ddc"
+checksum = "7b8c74fd20d97b9ba8b1a6e3ccc6ec715e6c6d4605668926e582e88580180d96"
 dependencies = [
  "egg",
  "log",
  "rand 0.8.5",
  "thiserror",
- "wasm-encoder 0.26.0",
- "wasmparser 0.104.0",
+ "wasm-encoder 0.27.0",
+ "wasmparser 0.105.0",
 ]
 
 [[package]]
 name = "wasm-smith"
-version = "0.12.7"
+version = "0.12.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "649447f144ee5db453e2b5ce7d3f60a9d7dd0bb5b2fde500125d3aa79a9387c8"
+checksum = "13c11c745f6f4ff2f953d8a94ef056b04b468fc021285cfaf614e77791c05499"
 dependencies = [
  "arbitrary",
  "flagset",
  "indexmap",
  "leb128",
- "wasm-encoder 0.26.0",
- "wasmparser 0.104.0",
+ "wasm-encoder 0.27.0",
+ "wasmparser 0.105.0",
 ]
 
 [[package]]
@@ -3734,6 +3791,16 @@ dependencies = [
  "url",
 ]
 
+[[package]]
+name = "wasmparser"
+version = "0.105.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "83be9e0b3f9570dc1979a33ae7b89d032c73211564232b99976553e5c155ec32"
+dependencies = [
+ "indexmap",
+ "url",
+]
+
 [[package]]
 name = "wasmparser-nostd"
 version = "0.91.0"
@@ -3745,12 +3812,12 @@ dependencies = [
 
 [[package]]
 name = "wasmprinter"
-version = "0.2.56"
+version = "0.2.57"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "731da2505d5437cd5d6feb09457835f76186be13be7677fe00781ae99d5bbe8a"
+checksum = "50b0e5ed7a74a065637f0d7798ce5f29cadb064980d24b0c82af5200122fa0d8"
 dependencies = [
  "anyhow",
- "wasmparser 0.104.0",
+ "wasmparser 0.105.0",
 ]
 
 [[package]]
@@ -3777,7 +3844,7 @@ dependencies = [
  "target-lexicon",
  "tempfile",
  "wasi-cap-std-sync",
- "wasmparser 0.104.0",
+ "wasmparser 0.105.0",
  "wasmtime-cache",
  "wasmtime-component-macro",
  "wasmtime-component-util",
@@ -3892,8 +3959,8 @@ dependencies = [
  "test-programs",
  "tokio",
  "walkdir",
- "wasm-encoder 0.26.0",
- "wasmparser 0.104.0",
+ "wasm-encoder 0.27.0",
+ "wasmparser 0.105.0",
  "wasmtime",
  "wasmtime-cache",
  "wasmtime-cli-flags",
@@ -3908,7 +3975,7 @@ dependencies = [
  "wasmtime-wasi-nn",
  "wasmtime-wasi-threads",
  "wasmtime-wast",
- "wast 57.0.0",
+ "wast 58.0.0",
  "wat",
  "windows-sys 0.48.0",
 ]
@@ -3961,7 +4028,7 @@ dependencies = [
  "object",
  "target-lexicon",
  "thiserror",
- "wasmparser 0.104.0",
+ "wasmparser 0.105.0",
  "wasmtime-cranelift-shared",
  "wasmtime-environ",
 ]
@@ -3996,8 +4063,8 @@ dependencies = [
  "serde",
  "target-lexicon",
  "thiserror",
- "wasm-encoder 0.26.0",
- "wasmparser 0.104.0",
+ "wasm-encoder 0.27.0",
+ "wasmparser 0.105.0",
  "wasmprinter",
  "wasmtime-component-util",
  "wasmtime-types",
@@ -4012,7 +4079,7 @@ dependencies = [
  "component-fuzz-util",
  "env_logger 0.10.0",
  "libfuzzer-sys",
- "wasmparser 0.104.0",
+ "wasmparser 0.105.0",
  "wasmprinter",
  "wasmtime-environ",
  "wat",
@@ -4066,7 +4133,7 @@ dependencies = [
  "rand 0.8.5",
  "smallvec",
  "target-lexicon",
- "wasmparser 0.104.0",
+ "wasmparser 0.105.0",
  "wasmtime",
  "wasmtime-fuzzing",
 ]
@@ -4086,12 +4153,12 @@ dependencies = [
  "target-lexicon",
  "tempfile",
  "v8",
- "wasm-encoder 0.26.0",
+ "wasm-encoder 0.27.0",
  "wasm-mutate",
  "wasm-smith",
  "wasm-spec-interpreter",
  "wasmi",
- "wasmparser 0.104.0",
+ "wasmparser 0.105.0",
  "wasmprinter",
  "wasmtime",
  "wasmtime-wast",
@@ -4173,7 +4240,7 @@ dependencies = [
  "cranelift-entity",
  "serde",
  "thiserror",
- "wasmparser 0.104.0",
+ "wasmparser 0.105.0",
 ]
 
 [[package]]
@@ -4247,7 +4314,7 @@ dependencies = [
  "anyhow",
  "log",
  "wasmtime",
- "wast 57.0.0",
+ "wast 58.0.0",
 ]
 
 [[package]]
@@ -4259,7 +4326,7 @@ dependencies = [
  "gimli",
  "object",
  "target-lexicon",
- "wasmparser 0.104.0",
+ "wasmparser 0.105.0",
  "wasmtime-cranelift-shared",
  "wasmtime-environ",
  "winch-codegen",
@@ -4286,23 +4353,23 @@ dependencies = [
 
 [[package]]
 name = "wast"
-version = "57.0.0"
+version = "58.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6eb0f5ed17ac4421193c7477da05892c2edafd67f9639e3c11a82086416662dc"
+checksum = "372eecae2d10a5091c2005b32377d7ecd6feecdf2c05838056d02d8b4f07c429"
 dependencies = [
  "leb128",
  "memchr",
  "unicode-width",
- "wasm-encoder 0.26.0",
+ "wasm-encoder 0.27.0",
 ]
 
 [[package]]
 name = "wat"
-version = "1.0.63"
+version = "1.0.64"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ab9ab0d87337c3be2bb6fc5cd331c4ba9fd6bcb4ee85048a0dd59ed9ecf92e53"
+checksum = "6d47446190e112ab1579ab40b3ad7e319d859d74e5134683f04e9f0747bf4173"
 dependencies = [
- "wast 57.0.0",
+ "wast 58.0.0",
 ]
 
 [[package]]
@@ -4430,7 +4497,7 @@ dependencies = [
  "regalloc2",
  "smallvec",
  "target-lexicon",
- "wasmparser 0.104.0",
+ "wasmparser 0.105.0",
  "wasmtime-environ",
 ]
 
@@ -4438,7 +4505,7 @@ dependencies = [
 name = "winch-environ"
 version = "0.8.0"
 dependencies = [
- "wasmparser 0.104.0",
+ "wasmparser 0.105.0",
  "wasmtime-environ",
  "winch-codegen",
 ]
@@ -4484,7 +4551,7 @@ dependencies = [
  "similar",
  "target-lexicon",
  "toml",
- "wasmparser 0.104.0",
+ "wasmparser 0.105.0",
  "wasmtime-environ",
  "wat",
  "winch-codegen",
@@ -4653,7 +4720,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5bc1b5a6e87f16491f2297f75312dc0fb354f8c88c8bece53ea0d3167fc98867"
 dependencies = [
  "anyhow",
- "wit-component",
+ "wit-component 0.8.2",
  "wit-parser",
 ]
 
@@ -4664,10 +4731,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7946a66f1132d3322c29de9d28097bd263f67e1e0909054f91253aa103cdf8be"
 dependencies = [
  "heck",
- "wasm-metadata",
+ "wasm-metadata 0.5.0",
  "wit-bindgen-core",
  "wit-bindgen-rust-lib",
- "wit-component",
+ "wit-component 0.8.2",
 ]
 
 [[package]]
@@ -4691,7 +4758,7 @@ dependencies = [
  "syn 2.0.16",
  "wit-bindgen-core",
  "wit-bindgen-rust",
- "wit-component",
+ "wit-component 0.8.2",
 ]
 
 [[package]]
@@ -4706,11 +4773,28 @@ dependencies = [
  "log",
  "url",
  "wasm-encoder 0.26.0",
- "wasm-metadata",
+ "wasm-metadata 0.5.0",
  "wasmparser 0.104.0",
  "wit-parser",
 ]
 
+[[package]]
+name = "wit-component"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b3cc435c15009f80d5e114d0dd3792959171f1fc28be2418e0f570d83b925a33"
+dependencies = [
+ "anyhow",
+ "bitflags 1.3.2",
+ "indexmap",
+ "log",
+ "url",
+ "wasm-encoder 0.27.0",
+ "wasm-metadata 0.6.0",
+ "wasmparser 0.105.0",
+ "wit-parser",
+]
+
 [[package]]
 name = "wit-parser"
 version = "0.7.1"
diff --git a/Cargo.toml b/Cargo.toml
index d2333dff3..bf442d483 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -54,8 +54,8 @@ rustix = { workspace = true, features = ["mm", "param"] }
 wasmtime = { workspace = true, features = ['component-model', 'async', 'default', 'winch'] }
 env_logger = { workspace = true }
 log = { workspace = true }
-filecheck = "0.5.0"
-tempfile = "3.1.0"
+filecheck = { workspace = true }
+tempfile = { workspace = true }
 test-programs = { path = "crates/test-programs" }
 wasmtime-runtime = { workspace = true }
 tokio = { version = "1.8.0", features = ["rt", "time", "macros", "rt-multi-thread"] }
@@ -97,6 +97,8 @@ members = [
   "crates/cli-flags",
   "crates/environ/fuzz",
   "crates/jit-icache-coherence",
+  "crates/test-programs/wasi-tests",
+  "crates/test-programs/wasi-http-tests",
   "crates/wasi-preview1-component-adapter",
   "crates/wasi-preview1-component-adapter/verify",
   "crates/winch",
@@ -179,30 +181,44 @@ winch-test-macros = { path = "winch/test-macros" }
 wasi-preview1-component-adapter = { path = "crates/wasi-preview1-component-adapter" }
 byte-array-literals = { path = "crates/wasi-preview1-component-adapter/byte-array-literals" }
 
+# Bytecode Alliance maintained dependencies:
+# ---------------------------
+regalloc2 = "0.8.1"
+
+# cap-std family:
 target-lexicon = { version = "0.12.3", default-features = false, features = ["std"] }
-anyhow = "1.0.22"
-wasmparser = "0.104.0"
-wat = "1.0.63"
-wast = "57.0.0"
-wasmprinter = "0.2.56"
-wasm-encoder = "0.26.0"
-wasm-smith = "0.12.7"
-wasm-mutate = "0.2.24"
+cap-std = "1.0.0"
+cap-rand = "1.0.0"
+io-lifetimes = { version = "1.0.0", default-features = false }
+rustix = "0.37.13"
+
+# wit-bindgen:
+wit-bindgen = "0.6.0"
+
+# wasm-tools family:
+wasmparser = "0.105.0"
+wat = "1.0.64"
+wast = "58.0.0"
+wasmprinter = "0.2.57"
+wasm-encoder = "0.27.0"
+wasm-smith = "0.12.8"
+wasm-mutate = "0.2.25"
 wit-parser = "0.7.1"
+wit-component = "0.9.0"
+
+# Non-Bytecode Alliance maintained dependencies:
+# --------------------------
+object = { version = "0.30.3", default-features = false, features = ['read_core', 'elf', 'std'] }
+gimli = { version = "0.27.0", default-features = false, features = ['read', 'std'] }
+anyhow = "1.0.22"
 windows-sys = "0.48.0"
 env_logger = "0.10"
-rustix = "0.37.13"
 log = { version = "0.4.8", default-features = false }
-object = { version = "0.30.3", default-features = false, features = ['read_core', 'elf', 'std'] }
-gimli = { version = "0.27.0", default-features = false, features = ['read', 'std'] }
 clap = { version = "3.2.0", features = ["color", "suggestions", "derive"] }
 hashbrown = "0.13.2"
-cap-std = "1.0.0"
-cap-rand = "1.0.0"
 capstone = "0.9.0"
 once_cell = "1.12.0"
 smallvec = { version = "1.6.1", features = ["union"] }
-io-lifetimes = { version = "1.0.0", default-features = false }
 tracing = "0.1.26"
 bitflags = "1.2"
 thiserror = "1.0.15"
@@ -214,8 +230,10 @@ serde = "1.0.94"
 serde_json = "1.0.80"
 glob = "0.3.0"
 libfuzzer-sys = "0.4.0"
-regalloc2 = "0.8.1"
 walkdir = "2.3.3"
+cfg-if = "1.0"
+tempfile = "3.1.0"
+filecheck = "0.5.0"
 
 [features]
 default = [
diff --git a/build.rs b/build.rs
index ab728b739..5825af1be 100644
--- a/build.rs
+++ b/build.rs
@@ -193,12 +193,73 @@ fn ignore(testsuite: &str, testname: &str, strategy: &str) -> bool {
             testname == "simd_f32x4_pmin_pmax" || testname == "simd_f64x2_pmin_pmax"
         }
 
-        // Currently the simd wasm proposal is not implemented in the riscv64
-        // backend so skip all tests which could use simd.
         "riscv64" => {
-            testsuite.contains("simd")
-                || testname.contains("simd")
-                || testname.contains("memory_multi")
+            if testname.contains("memory_multi") || testsuite.contains("relaxed_simd") {
+                return true;
+            }
+
+            // The memory64 testsuite has a single SIMD test that we don't pass yet.
+            if testname == "simd" && testsuite == "memory64" {
+                return true;
+            }
+
+            let known_failure = [
+                "almost_extmul",
+                "canonicalize_nan",
+                "cvt_from_uint",
+                "issue4807",
+                "issue_3327_bnot_lowering",
+                "load_splat_out_of_bounds",
+                "replace_lane_preserve",
+                "simd_align",
+                "simd_bit_shift",
+                "simd_bitwise",
+                "simd_boolean",
+                "simd_conversions",
+                "simd_f32x4",
+                "simd_f32x4_arith",
+                "simd_f32x4_cmp",
+                "simd_f32x4_pmin_pmax",
+                "simd_f32x4_rounding",
+                "simd_f64x2",
+                "simd_f64x2_arith",
+                "simd_f64x2_cmp",
+                "simd_f64x2_pmin_pmax",
+                "simd_f64x2_rounding",
+                "simd_i16x8_arith2",
+                "simd_i16x8_cmp",
+                "simd_i16x8_extadd_pairwise_i8x16",
+                "simd_i16x8_extmul_i8x16",
+                "simd_i16x8_q15mulr_sat_s",
+                "simd_i16x8_sat_arith",
+                "simd_i32x4_arith2",
+                "simd_i32x4_cmp",
+                "simd_i32x4_dot_i16x8",
+                "simd_i32x4_extadd_pairwise_i16x8",
+                "simd_i32x4_extmul_i16x8",
+                "simd_i32x4_trunc_sat_f32x4",
+                "simd_i32x4_trunc_sat_f64x2",
+                "simd_i64x2_arith2",
+                "simd_i64x2_cmp",
+                "simd_i64x2_extmul_i32x4",
+                "simd_i8x16_arith2",
+                "simd_i8x16_cmp",
+                "simd_i8x16_sat_arith",
+                "simd_int_to_int_extend",
+                "simd_lane",
+                "simd_load",
+                "simd_load16_lane",
+                "simd_load32_lane",
+                "simd_load64_lane",
+                "simd_load8_lane",
+                "simd_load_extend",
+                "simd_load_zero",
+                "simd_splat",
+                "v128_select",
+            ]
+            .contains(&testname);
+
+            known_failure
         }
 
         _ => false,
diff --git a/cranelift/Cargo.toml b/cranelift/Cargo.toml
index c94f0f40f..03bcba849 100644
--- a/cranelift/Cargo.toml
+++ b/cranelift/Cargo.toml
@@ -19,7 +19,7 @@ path = "tests/filetests.rs"
 harness = false
 
 [dependencies]
-cfg-if = "1.0"
+cfg-if = { workspace = true }
 cranelift-codegen = { workspace = true, features = ["disas"] }
 cranelift-entity = { workspace = true }
 cranelift-interpreter = { workspace = true }
@@ -32,7 +32,7 @@ cranelift-module = { workspace = true }
 cranelift-object = { workspace = true }
 cranelift-jit = { workspace = true }
 cranelift = { workspace = true }
-filecheck = "0.5.0"
+filecheck = { workspace = true }
 log = { workspace = true }
 termcolor = "1.1.2"
 capstone = { workspace = true, optional = true }
diff --git a/cranelift/codegen/src/isa/riscv64/inst.isle b/cranelift/codegen/src/isa/riscv64/inst.isle
index 2a6ef131e..9a4f99b8e 100644
--- a/cranelift/codegen/src/isa/riscv64/inst.isle
+++ b/cranelift/codegen/src/isa/riscv64/inst.isle
@@ -337,6 +337,18 @@
       (imm Imm5)
       (vstate VState))
 
+    (VecAluRR
+      (op VecAluOpRR)
+      (vd WritableReg)
+      (vs Reg)
+      (vstate VState))
+
+    (VecAluRImm5
+      (op VecAluOpRImm5)
+      (vd WritableReg)
+      (imm Imm5)
+      (vstate VState))
+
     (VecSetState
       (rd WritableReg)
       (vstate VState))
@@ -1353,6 +1365,14 @@
 (extractor (replicated_imm5 n)
   (def_inst (splat (iconst (u64_from_imm64 (imm5_from_u64 n))))))
 
+;; UImm5 Helpers
+
+;; Extract a `UImm5` from an `u8`.
+(decl pure partial uimm5_from_u8 (UImm5) u8)
+(extern extractor uimm5_from_u8 uimm5_from_u8)
+
+(decl uimm5_bitcast_to_imm5 (UImm5) Imm5)
+(extern constructor uimm5_bitcast_to_imm5 uimm5_bitcast_to_imm5)
 
 ;; Float Helpers
 
diff --git a/cranelift/codegen/src/isa/riscv64/inst/emit.rs b/cranelift/codegen/src/isa/riscv64/inst/emit.rs
index df7567918..d4c03c1ea 100644
--- a/cranelift/codegen/src/isa/riscv64/inst/emit.rs
+++ b/cranelift/codegen/src/isa/riscv64/inst/emit.rs
@@ -467,7 +467,9 @@ impl Inst {
             // VecSetState does not expect any vstate, rather it updates it.
             Inst::VecSetState { .. } => None,
 
+            Inst::VecAluRR { vstate, .. } |
             Inst::VecAluRRR { vstate, .. } |
+            Inst::VecAluRImm5 { vstate, .. } |
             Inst::VecAluRRImm5 { vstate, .. } |
             // TODO: Unit-stride loads and stores only need the AVL to be correct, not
             // the full vtype. A future optimization could be to decouple these two when
@@ -2818,6 +2820,17 @@ impl MachInstEmit for Inst {
 
                 sink.put4(encode_valu_imm(op, vd, imm, vs2, VecOpMasking::Disabled));
             }
+            &Inst::VecAluRR { op, vd, vs, .. } => {
+                let vs = allocs.next(vs);
+                let vd = allocs.next_writable(vd);
+
+                sink.put4(encode_valu_rr(op, vd, vs, VecOpMasking::Disabled));
+            }
+            &Inst::VecAluRImm5 { op, vd, imm, .. } => {
+                let vd = allocs.next_writable(vd);
+
+                sink.put4(encode_valu_r_imm(op, vd, imm, VecOpMasking::Disabled));
+            }
             &Inst::VecSetState { rd, ref vstate } => {
                 let rd = allocs.next_writable(rd);
 
diff --git a/cranelift/codegen/src/isa/riscv64/inst/encode.rs b/cranelift/codegen/src/isa/riscv64/inst/encode.rs
index 2a4798675..e52d05aa4 100644
--- a/cranelift/codegen/src/isa/riscv64/inst/encode.rs
+++ b/cranelift/codegen/src/isa/riscv64/inst/encode.rs
@@ -9,7 +9,8 @@
 use super::{Imm12, Imm5, UImm5, VType};
 use crate::isa::riscv64::inst::reg_to_gpr_num;
 use crate::isa::riscv64::lower::isle::generated_code::{
-    VecAluOpRRImm5, VecAluOpRRR, VecElementWidth, VecOpCategory, VecOpMasking,
+    VecAluOpRImm5, VecAluOpRR, VecAluOpRRImm5, VecAluOpRRR, VecElementWidth, VecOpCategory,
+    VecOpMasking,
 };
 use crate::machinst::isle::WritableReg;
 use crate::Reg;
@@ -145,6 +146,48 @@ pub fn encode_valu_imm(
     )
 }
 
+pub fn encode_valu_rr(op: VecAluOpRR, vd: WritableReg, vs: Reg, masking: VecOpMasking) -> u32 {
+    let funct7 = (op.funct6() << 1) | masking.encode();
+
+    let (vs1, vs2) = if op.vs_is_vs2_encoded() {
+        (op.aux_encoding(), reg_to_gpr_num(vs))
+    } else {
+        (reg_to_gpr_num(vs), op.aux_encoding())
+    };
+
+    encode_r_type_bits(
+        op.opcode(),
+        reg_to_gpr_num(vd.to_reg()),
+        op.funct3(),
+        vs1,
+        vs2,
+        funct7,
+    )
+}
+
+pub fn encode_valu_r_imm(
+    op: VecAluOpRImm5,
+    vd: WritableReg,
+    imm: Imm5,
+    masking: VecOpMasking,
+) -> u32 {
+    let funct7 = (op.funct6() << 1) | masking.encode();
+
+    // This is true for this opcode, not sure if there are any other ones.
+    debug_assert_eq!(op, VecAluOpRImm5::VmvVI);
+    let vs1 = imm.bits() as u32;
+    let vs2 = op.aux_encoding();
+
+    encode_r_type_bits(
+        op.opcode(),
+        reg_to_gpr_num(vd.to_reg()),
+        op.funct3(),
+        vs1,
+        vs2,
+        funct7,
+    )
+}
+
 /// Encodes a Vector CFG Imm instruction.
 ///
 /// See: https://github.com/riscv/riscv-v-spec/blob/master/vcfg-format.adoc
diff --git a/cranelift/codegen/src/isa/riscv64/inst/imms.rs b/cranelift/codegen/src/isa/riscv64/inst/imms.rs
index 7ef4f5245..2f9b544b1 100644
--- a/cranelift/codegen/src/isa/riscv64/inst/imms.rs
+++ b/cranelift/codegen/src/isa/riscv64/inst/imms.rs
@@ -143,6 +143,12 @@ impl Imm5 {
         }
     }
 
+    pub fn from_bits(value: u8) -> Imm5 {
+        assert_eq!(value & 0x1f, value);
+        let signed = ((value << 3) as i8) >> 3;
+        Imm5 { value: signed }
+    }
+
     /// Bits for encoding.
     pub fn bits(&self) -> u8 {
         self.value as u8 & 0x1f
diff --git a/cranelift/codegen/src/isa/riscv64/inst/mod.rs b/cranelift/codegen/src/isa/riscv64/inst/mod.rs
index d654e7776..4a13ca4c6 100644
--- a/cranelift/codegen/src/isa/riscv64/inst/mod.rs
+++ b/cranelift/codegen/src/isa/riscv64/inst/mod.rs
@@ -642,6 +642,18 @@ fn riscv64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut Operan
             collector.reg_use(vs2);
             collector.reg_def(vd);
         }
+        &Inst::VecAluRR { op, vd, vs, .. } => {
+            debug_assert_eq!(vd.to_reg().class(), op.dst_regclass());
+            debug_assert_eq!(vs.class(), op.src_regclass());
+
+            collector.reg_use(vs);
+            collector.reg_def(vd);
+        }
+        &Inst::VecAluRImm5 { vd, .. } => {
+            debug_assert_eq!(vd.to_reg().class(), RegClass::Vector);
+
+            collector.reg_def(vd);
+        }
         &Inst::VecSetState { rd, .. } => {
             collector.reg_def(rd);
         }
@@ -1585,7 +1597,36 @@ impl Inst {
                 let vs2_s = format_reg(vs2, allocs);
                 let vd_s = format_reg(vd.to_reg(), allocs);
 
-                format!("{} {},{},{} {}", op, vd_s, vs2_s, imm, vstate)
+                // Some opcodes interpret the immediate as unsigned, lets show the
+                // correct number here.
+                let imm_s = if op.imm_is_unsigned() {
+                    format!("{}", imm.bits())
+                } else {
+                    format!("{}", imm)
+                };
+
+                format!("{} {},{},{} {}", op, vd_s, vs2_s, imm_s, vstate)
+            }
+            &Inst::VecAluRR {
+                op,
+                vd,
+                vs,
+                ref vstate,
+            } => {
+                let vs_s = format_reg(vs, allocs);
+                let vd_s = format_reg(vd.to_reg(), allocs);
+
+                format!("{} {},{} {}", op, vd_s, vs_s, vstate)
+            }
+            &Inst::VecAluRImm5 {
+                op,
+                vd,
+                imm,
+                ref vstate,
+            } => {
+                let vd_s = format_reg(vd.to_reg(), allocs);
+
+                format!("{} {},{} {}", op, vd_s, imm, vstate)
             }
             &Inst::VecSetState { rd, ref vstate } => {
                 let rd_s = format_reg(rd.to_reg(), allocs);
diff --git a/cranelift/codegen/src/isa/riscv64/inst/vector.rs b/cranelift/codegen/src/isa/riscv64/inst/vector.rs
index 603da8690..a45cf39b3 100644
--- a/cranelift/codegen/src/isa/riscv64/inst/vector.rs
+++ b/cranelift/codegen/src/isa/riscv64/inst/vector.rs
@@ -1,8 +1,8 @@
 use crate::isa::riscv64::inst::AllocationConsumer;
 use crate::isa::riscv64::inst::EmitState;
 use crate::isa::riscv64::lower::isle::generated_code::{
-    VecAMode, VecAluOpRRImm5, VecAluOpRRR, VecAvl, VecElementWidth, VecLmul, VecMaskMode,
-    VecOpCategory, VecOpMasking, VecTailMode,
+    VecAMode, VecAluOpRImm5, VecAluOpRR, VecAluOpRRImm5, VecAluOpRRR, VecAvl, VecElementWidth,
+    VecLmul, VecMaskMode, VecOpCategory, VecOpMasking, VecTailMode,
 };
 use crate::machinst::RegClass;
 use crate::Reg;
@@ -260,6 +260,7 @@ impl VecAluOpRRR {
             VecAluOpRRR::VandVV => 0b001001,
             VecAluOpRRR::VorVV => 0b001010,
             VecAluOpRRR::VxorVV => 0b001011,
+            VecAluOpRRR::VslidedownVX => 0b001111,
         }
     }
 
@@ -273,9 +274,10 @@ impl VecAluOpRRR {
             VecAluOpRRR::VmulVV | VecAluOpRRR::VmulhVV | VecAluOpRRR::VmulhuVV => {
                 VecOpCategory::OPMVV
             }
-            VecAluOpRRR::VaddVX | VecAluOpRRR::VsubVX | VecAluOpRRR::VrsubVX => {
-                VecOpCategory::OPIVX
-            }
+            VecAluOpRRR::VaddVX
+            | VecAluOpRRR::VsubVX
+            | VecAluOpRRR::VrsubVX
+            | VecAluOpRRR::VslidedownVX => VecOpCategory::OPIVX,
         }
     }
 
@@ -305,13 +307,30 @@ impl VecAluOpRRImm5 {
         0x57
     }
     pub fn funct3(&self) -> u32 {
-        VecOpCategory::OPIVI.encode()
+        self.category().encode()
     }
+
     pub fn funct6(&self) -> u32 {
         // See: https://github.com/riscv/riscv-v-spec/blob/master/inst-table.adoc
         match self {
             VecAluOpRRImm5::VaddVI => 0b000000,
             VecAluOpRRImm5::VrsubVI => 0b000011,
+            VecAluOpRRImm5::VslidedownVI => 0b001111,
+        }
+    }
+
+    pub fn category(&self) -> VecOpCategory {
+        match self {
+            VecAluOpRRImm5::VaddVI | VecAluOpRRImm5::VrsubVI | VecAluOpRRImm5::VslidedownVI => {
+                VecOpCategory::OPIVI
+            }
+        }
+    }
+
+    pub fn imm_is_unsigned(&self) -> bool {
+        match self {
+            VecAluOpRRImm5::VslidedownVI => true,
+            VecAluOpRRImm5::VaddVI | VecAluOpRRImm5::VrsubVI => false,
         }
     }
 }
@@ -325,6 +344,139 @@ impl fmt::Display for VecAluOpRRImm5 {
     }
 }
 
+impl VecAluOpRR {
+    pub fn opcode(&self) -> u32 {
+        // Vector Opcode
+        0x57
+    }
+
+    pub fn funct3(&self) -> u32 {
+        self.category().encode()
+    }
+
+    pub fn funct6(&self) -> u32 {
+        // See: https://github.com/riscv/riscv-v-spec/blob/master/inst-table.adoc
+        match self {
+            VecAluOpRR::VmvSX | VecAluOpRR::VmvXS | VecAluOpRR::VfmvSF | VecAluOpRR::VfmvFS => {
+                0b010000
+            }
+            VecAluOpRR::VmvVV | VecAluOpRR::VmvVX | VecAluOpRR::VfmvVF => 0b010111,
+        }
+    }
+
+    pub fn category(&self) -> VecOpCategory {
+        match self {
+            VecAluOpRR::VmvSX => VecOpCategory::OPMVX,
+            VecAluOpRR::VmvXS => VecOpCategory::OPMVV,
+            VecAluOpRR::VfmvSF | VecAluOpRR::VfmvVF => VecOpCategory::OPFVF,
+            VecAluOpRR::VfmvFS => VecOpCategory::OPFVV,
+            VecAluOpRR::VmvVV => VecOpCategory::OPIVV,
+            VecAluOpRR::VmvVX => VecOpCategory::OPIVX,
+        }
+    }
+
+    /// Returns the auxiliary encoding field for the instruction, if any.
+    pub fn aux_encoding(&self) -> u32 {
+        match self {
+            // VRXUNARY0
+            VecAluOpRR::VmvSX => 0b00000,
+            // VWXUNARY0
+            VecAluOpRR::VmvXS => 0b00000,
+            // VRFUNARY0
+            VecAluOpRR::VfmvSF => 0b00000,
+            // VWFUNARY0
+            VecAluOpRR::VfmvFS => 0b00000,
+            // These don't have a explicit encoding table, but Section 11.16 Vector Integer Move Instruction states:
+            // > The first operand specifier (vs2) must contain v0, and any other vector register number in vs2 is reserved.
+            VecAluOpRR::VmvVV | VecAluOpRR::VmvVX | VecAluOpRR::VfmvVF => 0,
+        }
+    }
+
+    /// Most of these opcodes have the source register encoded in the VS2 field and
+    /// the `aux_encoding` field in VS1. However some special snowflakes have it the
+    /// other way around. As far as I can tell only vmv.v.* are backwards.
+    pub fn vs_is_vs2_encoded(&self) -> bool {
+        match self {
+            VecAluOpRR::VmvSX | VecAluOpRR::VmvXS | VecAluOpRR::VfmvSF | VecAluOpRR::VfmvFS => true,
+            VecAluOpRR::VmvVV | VecAluOpRR::VmvVX | VecAluOpRR::VfmvVF => false,
+        }
+    }
+
+    pub fn dst_regclass(&self) -> RegClass {
+        match self {
+            VecAluOpRR::VfmvSF
+            | VecAluOpRR::VmvSX
+            | VecAluOpRR::VmvVV
+            | VecAluOpRR::VmvVX
+            | VecAluOpRR::VfmvVF => RegClass::Vector,
+            VecAluOpRR::VmvXS => RegClass::Int,
+            VecAluOpRR::VfmvFS => RegClass::Float,
+        }
+    }
+
+    pub fn src_regclass(&self) -> RegClass {
+        match self {
+            VecAluOpRR::VmvXS | VecAluOpRR::VfmvFS | VecAluOpRR::VmvVV => RegClass::Vector,
+            VecAluOpRR::VfmvSF | VecAluOpRR::VfmvVF => RegClass::Float,
+            VecAluOpRR::VmvSX | VecAluOpRR::VmvVX => RegClass::Int,
+        }
+    }
+}
+
+impl fmt::Display for VecAluOpRR {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        f.write_str(match self {
+            VecAluOpRR::VmvSX => "vmv.s.x",
+            VecAluOpRR::VmvXS => "vmv.x.s",
+            VecAluOpRR::VfmvSF => "vfmv.s.f",
+            VecAluOpRR::VfmvFS => "vfmv.f.s",
+            VecAluOpRR::VmvVV => "vmv.v.v",
+            VecAluOpRR::VmvVX => "vmv.v.x",
+            VecAluOpRR::VfmvVF => "vfmv.v.f",
+        })
+    }
+}
+
+impl VecAluOpRImm5 {
+    pub fn opcode(&self) -> u32 {
+        // Vector Opcode
+        0x57
+    }
+    pub fn funct3(&self) -> u32 {
+        self.category().encode()
+    }
+
+    pub fn funct6(&self) -> u32 {
+        // See: https://github.com/riscv/riscv-v-spec/blob/master/inst-table.adoc
+        match self {
+            VecAluOpRImm5::VmvVI => 0b010111,
+        }
+    }
+
+    pub fn category(&self) -> VecOpCategory {
+        match self {
+            VecAluOpRImm5::VmvVI => VecOpCategory::OPIVI,
+        }
+    }
+
+    /// Returns the auxiliary encoding field for the instruction, if any.
+    pub fn aux_encoding(&self) -> u32 {
+        match self {
+            // These don't have a explicit encoding table, but Section 11.16 Vector Integer Move Instruction states:
+            // > The first operand specifier (vs2) must contain v0, and any other vector register number in vs2 is reserved.
+            VecAluOpRImm5::VmvVI => 0,
+        }
+    }
+}
+
+impl fmt::Display for VecAluOpRImm5 {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        f.write_str(match self {
+            VecAluOpRImm5::VmvVI => "vmv.v.i",
+        })
+    }
+}
+
 impl VecAMode {
     pub fn get_base_register(&self) -> Option<Reg> {
         match self {
diff --git a/cranelift/codegen/src/isa/riscv64/inst_vector.isle b/cranelift/codegen/src/isa/riscv64/inst_vector.isle
index ee02f7b7c..bfbe10e95 100644
--- a/cranelift/codegen/src/isa/riscv64/inst_vector.isle
+++ b/cranelift/codegen/src/isa/riscv64/inst_vector.isle
@@ -97,14 +97,39 @@
   (VaddVX)
   (VsubVX)
   (VrsubVX)
+  (VslidedownVX)
 ))
 
 ;; Register-Imm ALU Ops
 (type VecAluOpRRImm5 (enum
+  ;; Regular VI Opcodes
   (VaddVI)
   (VrsubVI)
+  (VslidedownVI)
 ))
 
+;; Imm only ALU Ops
+(type VecAluOpRImm5 (enum
+  (VmvVI)
+))
+
+;; These are all of the special cases that have weird encodings. They are all
+;; single source, single destination instructions, and usually use one of
+;; the two source registers as auxiliary encoding space.
+(type VecAluOpRR (enum
+  (VmvSX)
+  (VmvXS)
+  (VfmvSF)
+  (VfmvFS)
+  ;; vmv.v* is special in that vs2 must be v0 (and is ignored) otherwise the instruction is illegal.
+  (VmvVV)
+  (VmvVX)
+  (VfmvVF)
+))
+
+;; Returns the canonical destination type for a VecAluOpRRImm5.
+(decl pure vec_alu_rr_dst_type (VecAluOpRR) Type)
+(extern constructor vec_alu_rr_dst_type vec_alu_rr_dst_type)
 
 
 ;; Vector Addressing Mode
@@ -139,9 +164,15 @@
 (rule (element_width_from_type ty)
   (if-let $I32 (lane_type ty))
   (VecElementWidth.E32))
+(rule (element_width_from_type ty)
+  (if-let $F32 (lane_type ty))
+  (VecElementWidth.E32))
 (rule (element_width_from_type ty)
   (if-let $I64 (lane_type ty))
   (VecElementWidth.E64))
+(rule (element_width_from_type ty)
+  (if-let $F64 (lane_type ty))
+  (VecElementWidth.E64))
 
 (decl pure min_vec_reg_size () u64)
 (extern constructor min_vec_reg_size min_vec_reg_size)
@@ -172,6 +203,27 @@
             (_ Unit (emit (MInst.VecAluRRImm5 op vd vs2 imm vstate))))
         vd))
 
+;; Helper for emitting `MInst.VecAluRRImm5` instructions where the immediate
+;; is zero extended instead of sign extended.
+(decl vec_alu_rr_uimm5 (VecAluOpRRImm5 Reg UImm5 VState) Reg)
+(rule (vec_alu_rr_uimm5 op vs2 imm vstate)
+      (vec_alu_rr_imm5 op vs2 (uimm5_bitcast_to_imm5 imm) vstate))
+
+;; Helper for emitting `MInst.VecAluRRImm5` instructions that use the Imm5 as
+;; auxiliary encoding space.
+(decl vec_alu_rr (VecAluOpRR Reg VState) Reg)
+(rule (vec_alu_rr op vs vstate)
+      (let ((vd WritableReg (temp_writable_reg (vec_alu_rr_dst_type op)))
+            (_ Unit (emit (MInst.VecAluRR op vd vs vstate))))
+        vd))
+
+;; Helper for emitting `MInst.VecAluRImm5` instructions.
+(decl vec_alu_r_imm5 (VecAluOpRImm5 Imm5 VState) Reg)
+(rule (vec_alu_r_imm5 op imm vstate)
+      (let ((vd WritableReg (temp_writable_reg $I8X16))
+            (_ Unit (emit (MInst.VecAluRImm5 op vd imm vstate))))
+        vd))
+
 ;; Helper for emitting `MInst.VecLoad` instructions.
 (decl vec_load (VecElementWidth VecAMode MemFlags VState) Reg)
 (rule (vec_load eew from flags vstate)
@@ -254,3 +306,70 @@
 (decl rv_vxor_vv (Reg Reg VState) Reg)
 (rule (rv_vxor_vv vs2 vs1 vstate)
   (vec_alu_rrr (VecAluOpRRR.VxorVV) vs2 vs1 vstate))
+
+;; Helper for emitting the `vslidedown.vx` instruction.
+;; `vslidedown` moves all elements in the vector down by n elements.
+;; The top most elements are up to the tail policy.
+(decl rv_vslidedown_vx (Reg Reg VState) Reg)
+(rule (rv_vslidedown_vx vs2 vs1 vstate)
+  (vec_alu_rrr (VecAluOpRRR.VslidedownVX) vs2 vs1 vstate))
+
+;; Helper for emitting the `vslidedown.vi` instruction.
+;; Unlike other `vi` instructions the immediate is zero extended.
+(decl rv_vslidedown_vi (Reg UImm5 VState) Reg)
+(rule (rv_vslidedown_vi vs2 imm vstate)
+  (vec_alu_rr_uimm5 (VecAluOpRRImm5.VslidedownVI) vs2 imm vstate))
+
+;; Helper for emitting the `vmv.x.s` instruction.
+;; This instruction copies the first element of the source vector to the destination X register.
+(decl rv_vmv_xs (Reg VState) Reg)
+(rule (rv_vmv_xs vs vstate)
+  (vec_alu_rr (VecAluOpRR.VmvXS) vs vstate))
+
+;; Helper for emitting the `vfmv.f.s` instruction.
+;; This instruction copies the first element of the source vector to the destination F register.
+(decl rv_vfmv_fs (Reg VState) Reg)
+(rule (rv_vfmv_fs vs vstate)
+  (vec_alu_rr (VecAluOpRR.VfmvFS) vs vstate))
+
+;; Helper for emitting the `vmv.v.x` instruction.
+;; This instruction splats the X regsiter into all elements of the destination vector.
+(decl rv_vmv_vx (Reg VState) Reg)
+(rule (rv_vmv_vx vs vstate)
+  (vec_alu_rr (VecAluOpRR.VmvVX) vs vstate))
+
+;; Helper for emitting the `vfmv.v.f` instruction.
+;; This instruction splats the F regsiter into all elements of the destination vector.
+(decl rv_vfmv_vf (Reg VState) Reg)
+(rule (rv_vfmv_vf vs vstate)
+  (vec_alu_rr (VecAluOpRR.VfmvVF) vs vstate))
+
+;; Helper for emitting the `vmv.v.i` instruction.
+;; This instruction splat's the immediate value into all elements of the destination vector.
+(decl rv_vmv_vi (Imm5 VState) Reg)
+(rule (rv_vmv_vi imm vstate)
+  (vec_alu_r_imm5 (VecAluOpRImm5.VmvVI) imm vstate))
+
+;;;; Multi-Instruction Helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(decl gen_extractlane (Type Reg u8) Reg)
+
+;; When extracting lane 0 for floats, we can use `vfmv.f.s` directly.
+(rule 3 (gen_extractlane (ty_vec_fits_in_register ty) src 0)
+  (if (ty_vector_float ty))
+  (rv_vfmv_fs src ty))
+
+;; When extracting lane 0 for integers, we can use `vmv.x.s` directly.
+(rule 2 (gen_extractlane (ty_vec_fits_in_register ty) src 0)
+  (if (ty_vector_not_float ty))
+  (rv_vmv_xs src ty))
+
+;; In the general case, we must first use a `vslidedown` to place the correct lane
+;; in index 0, and then use the appropriate `vmv` instruction.
+;; If the index fits into a 5-bit immediate, we can emit a `vslidedown.vi`.
+(rule 1 (gen_extractlane (ty_vec_fits_in_register ty) src (uimm5_from_u8 idx))
+  (gen_extractlane ty (rv_vslidedown_vi src idx ty) 0))
+
+;; Otherwise lower it into an X register.
+(rule 0 (gen_extractlane (ty_vec_fits_in_register ty) src idx)
+  (gen_extractlane ty (rv_vslidedown_vx src (imm $I64 idx) ty) 0))
diff --git a/cranelift/codegen/src/isa/riscv64/lower.isle b/cranelift/codegen/src/isa/riscv64/lower.isle
index cf30e6e89..7dc69850e 100644
--- a/cranelift/codegen/src/isa/riscv64/lower.isle
+++ b/cranelift/codegen/src/isa/riscv64/lower.isle
@@ -1030,3 +1030,23 @@
 
 (rule (lower (call_indirect sig_ref val inputs))
   (gen_call_indirect sig_ref val inputs))
+
+;;;; Rules for `extractlane` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(rule (lower (extractlane x @ (value_type ty) (u8_from_uimm8 idx)))
+  (gen_extractlane ty x idx))
+
+;;;; Rules for `splat` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(rule 0 (lower (has_type ty (splat n @ (value_type (ty_scalar_float _)))))
+  (rv_vfmv_vf n ty))
+
+(rule 1 (lower (has_type ty (splat n @ (value_type (ty_int_ref_scalar_64 _)))))
+  (rv_vmv_vx n ty))
+
+(rule 2 (lower (has_type ty (splat (iconst (u64_from_imm64 (imm5_from_u64 imm))))))
+  (rv_vmv_vi imm ty))
+
+;; TODO: We can splat out more patterns by using for example a vmv.v.i i8x16 for
+;; a i64x2 const with a compatible bit pattern. The AArch64 Backend does something
+;; similar in its splat rules.
diff --git a/cranelift/codegen/src/isa/riscv64/lower/isle.rs b/cranelift/codegen/src/isa/riscv64/lower/isle.rs
index 1094dfce2..8dc6c6675 100644
--- a/cranelift/codegen/src/isa/riscv64/lower/isle.rs
+++ b/cranelift/codegen/src/isa/riscv64/lower/isle.rs
@@ -6,6 +6,7 @@ pub mod generated_code;
 use generated_code::{Context, ExtendOp, MInst};
 
 // Types that the generated ISLE code uses via `use super::*`.
+use self::generated_code::VecAluOpRR;
 use super::{writable_zero_reg, zero_reg};
 use crate::isa::riscv64::abi::Riscv64ABICaller;
 use crate::isa::riscv64::Riscv64Backend;
@@ -205,6 +206,14 @@ impl generated_code::Context for RV64IsleContext<'_, '_, MInst, Riscv64Backend>
         Imm5::maybe_from_i8(i8::try_from(arg0 as i64).ok()?)
     }
     #[inline]
+    fn uimm5_bitcast_to_imm5(&mut self, arg0: UImm5) -> Imm5 {
+        Imm5::from_bits(arg0.bits() as u8)
+    }
+    #[inline]
+    fn uimm5_from_u8(&mut self, arg0: u8) -> Option<UImm5> {
+        UImm5::maybe_from_u8(arg0)
+    }
+    #[inline]
     fn writable_zero_reg(&mut self) -> WritableReg {
         writable_zero_reg()
     }
@@ -455,6 +464,10 @@ impl generated_code::Context for RV64IsleContext<'_, '_, MInst, Riscv64Backend>
             None
         }
     }
+
+    fn vec_alu_rr_dst_type(&mut self, op: &VecAluOpRR) -> Type {
+        MInst::canonical_type_for_rc(op.dst_regclass())
+    }
 }
 
 /// The main entry point for lowering with ISLE.
diff --git a/cranelift/codegen/src/isa/x64/inst.isle b/cranelift/codegen/src/isa/x64/inst.isle
index b549f9b56..65ca49e47 100644
--- a/cranelift/codegen/src/isa/x64/inst.isle
+++ b/cranelift/codegen/src/isa/x64/inst.isle
@@ -1961,14 +1961,6 @@
             (_ Unit (emit (MInst.XmmUninitializedValue dst))))
         dst))
 
-;; Helper for creating an SSE register holding an `i64x2` from two `i64` values.
-(decl make_i64x2_from_lanes (GprMem GprMem) Xmm)
-(rule (make_i64x2_from_lanes lo hi)
-      (let ((dst Xmm (xmm_uninit_value))
-            (dst Xmm (x64_pinsrq dst lo 0))
-            (dst Xmm (x64_pinsrq dst hi 1)))
-        dst))
-
 ;; Move a `RegMemImm.Reg` operand to an XMM register, if necessary.
 (decl mov_rmi_to_xmm (RegMemImm) XmmMemImm)
 (rule (mov_rmi_to_xmm rmi @ (RegMemImm.Mem _)) (xmm_mem_imm_new rmi))
diff --git a/cranelift/codegen/src/isa/x64/lower.isle b/cranelift/codegen/src/isa/x64/lower.isle
index 56d3dc721..90baf2dc7 100644
--- a/cranelift/codegen/src/isa/x64/lower.isle
+++ b/cranelift/codegen/src/isa/x64/lower.isle
@@ -741,22 +741,66 @@
       (x64_psrad src (mov_rmi_to_xmm (mask_xmm_shift ty amt))))
 
 ;; The `sshr.i64x2` CLIF instruction has no single x86 instruction in the older
-;; feature sets. Newer ones like AVX512VL + AVX512F include `vpsraq`, a 128-bit
-;; instruction that would fit here, but this backend does not currently have
-;; support for EVEX encodings. To remedy this, we extract each 64-bit lane to a
-;; GPR, shift each using a scalar instruction, and insert the shifted values
-;; back in the `dst` XMM register.
+;; feature sets. To remedy this, a small dance is done with an unsigned right
+;; shift plus some extra ops.
 ;;
-;; (TODO: when EVEX support is available, add an alternate lowering here).
+;; TODO: add a lowering for `vpsraq` with AVX512{VL,F}
+(rule 1 (lower (has_type $I64X2 (sshr src (iconst (u64_from_imm64 (u64_as_u32 amt))))))
+        (lower_i64x2_sshr_imm src (u32_and amt 63)))
 (rule (lower (has_type $I64X2 (sshr src amt)))
-      (let ((src_ Xmm (put_in_xmm src))
-            (lo Gpr (x64_pextrq src_ 0))
-            (hi Gpr (x64_pextrq src_ 1))
-            (amt_ Imm8Gpr (put_masked_in_imm8_gpr amt $I64))
-            (shifted_lo Gpr (x64_sar $I64 lo amt_))
-            (shifted_hi Gpr (x64_sar $I64 hi amt_)))
-        (make_i64x2_from_lanes shifted_lo
-                               shifted_hi)))
+      (lower_i64x2_sshr_gpr src (x64_and $I64 amt (RegMemImm.Imm 63))))
+
+(decl lower_i64x2_sshr_imm (Xmm u32) Xmm)
+
+;; If the shift amount is less than 32 then do an sshr with 32-bit lanes to
+;; produce the upper halves of each result, followed by a ushr of 64-bit lanes
+;; to produce the lower halves of each result. Interleave results at the end.
+(rule 2 (lower_i64x2_sshr_imm vec imm)
+        (if-let $true (u64_lt imm 32))
+        (let (
+            (high32 Xmm (x64_psrad vec (xmi_imm imm)))
+            (high32 Xmm (x64_pshufd high32 0b11_10_11_01))
+            (low32  Xmm (x64_psrlq vec (xmi_imm imm)))
+            (low32  Xmm (x64_pshufd low32 0b11_10_10_00))
+          )
+          (x64_punpckldq low32 high32)))
+
+;; If the shift amount is 32 then the `psrlq` from the above rule can be avoided
+(rule 1 (lower_i64x2_sshr_imm vec 32)
+        (let (
+            (low32  Xmm (x64_pshufd vec 0b11_10_11_01))
+            (high32 Xmm (x64_psrad vec (xmi_imm 31)))
+            (high32 Xmm (x64_pshufd high32 0b11_10_11_01))
+          )
+          (x64_punpckldq low32 high32)))
+
+;; Shifts >= 32 use one `psrad` to generate the upper bits and second `psrad` to
+;; generate the lower bits. Everything is then woven back together with
+;; shuffles.
+(rule (lower_i64x2_sshr_imm vec imm)
+      (if-let $true (u64_lt 32 imm))
+      (let (
+          (high32 Xmm (x64_psrad vec (xmi_imm 31)))
+          (high32 Xmm (x64_pshufd high32 0b11_10_11_01))
+          (low32  Xmm (x64_psrad vec (xmi_imm (u32_sub imm 32))))
+          (low32  Xmm (x64_pshufd low32 0b11_10_10_01))
+        )
+        (x64_punpckldq low32 high32)))
+
+;; A variable shift amount is slightly more complicated than the immediate
+;; shift amounts from above. The `Gpr` argument is guaranteed to be <= 63 by
+;; earlier masking. A `ushr` operation is used with some xor/sub math to
+;; generate the sign bits.
+(decl lower_i64x2_sshr_gpr (Xmm Gpr) Xmm)
+(rule (lower_i64x2_sshr_gpr vec val)
+      (let (
+          (val                Xmm (x64_movq_to_xmm val))
+          (mask               Xmm (flip_high_bit_mask $I64X2))
+          (sign_bit_loc       Xmm (x64_psrlq mask val))
+          (ushr               Xmm (x64_psrlq vec val))
+          (ushr_sign_bit_flip Xmm (x64_pxor sign_bit_loc ushr))
+        )
+        (x64_psubq ushr_sign_bit_flip sign_bit_loc)))
 
 ;;;; Rules for `rotl` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
diff --git a/cranelift/codegen/src/isle_prelude.rs b/cranelift/codegen/src/isle_prelude.rs
index 4d460a61b..426790956 100644
--- a/cranelift/codegen/src/isle_prelude.rs
+++ b/cranelift/codegen/src/isle_prelude.rs
@@ -612,6 +612,16 @@ macro_rules! isle_common_prelude_methods {
             a.wrapping_add(b)
         }
 
+        #[inline]
+        fn u32_sub(&mut self, a: u32, b: u32) -> u32 {
+            a.wrapping_sub(b)
+        }
+
+        #[inline]
+        fn u32_and(&mut self, a: u32, b: u32) -> u32 {
+            a & b
+        }
+
         #[inline]
         fn s32_add_fallible(&mut self, a: u32, b: u32) -> Option<u32> {
             let a = a as i32;
diff --git a/cranelift/codegen/src/prelude.isle b/cranelift/codegen/src/prelude.isle
index 93381ef6d..54047aadc 100644
--- a/cranelift/codegen/src/prelude.isle
+++ b/cranelift/codegen/src/prelude.isle
@@ -121,6 +121,12 @@
 (decl pure u32_add (u32 u32) u32)
 (extern constructor u32_add u32_add)
 
+(decl pure u32_sub (u32 u32) u32)
+(extern constructor u32_sub u32_sub)
+
+(decl pure u32_and (u32 u32) u32)
+(extern constructor u32_and u32_and)
+
 ;; Pure/fallible constructor that tries to add two `u32`s, interpreted
 ;; as signed values, and fails to match on overflow.
 (decl pure partial s32_add_fallible (u32 u32) u32)
diff --git a/cranelift/wasm/src/code_translator.rs b/cranelift/wasm/src/code_translator.rs
index 3f506374b..030fe43c3 100644
--- a/cranelift/wasm/src/code_translator.rs
+++ b/cranelift/wasm/src/code_translator.rs
@@ -2347,6 +2347,9 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
                 op
             ));
         }
+        Operator::I31New | Operator::I31GetS | Operator::I31GetU => {
+            unimplemented!("GC operators not yet implemented")
+        }
     };
     Ok(())
 }
diff --git a/crates/asm-macros/Cargo.toml b/crates/asm-macros/Cargo.toml
index b8b667573..8b9d96cc9 100644
--- a/crates/asm-macros/Cargo.toml
+++ b/crates/asm-macros/Cargo.toml
@@ -10,4 +10,4 @@ version.workspace = true
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [dependencies]
-cfg-if = "1"
+cfg-if = { workspace = true }
diff --git a/crates/fiber/Cargo.toml b/crates/fiber/Cargo.toml
index 1ba281275..e2acc6c0f 100644
--- a/crates/fiber/Cargo.toml
+++ b/crates/fiber/Cargo.toml
@@ -14,7 +14,7 @@ edition.workspace = true
 links = "wasmtime-fiber-shims"
 
 [dependencies]
-cfg-if = "1.0"
+cfg-if = { workspace = true }
 
 [target.'cfg(unix)'.dependencies]
 rustix = { workspace = true, features = ["mm", "param"] }
diff --git a/crates/jit-icache-coherence/Cargo.toml b/crates/jit-icache-coherence/Cargo.toml
index 334755617..8500c7924 100644
--- a/crates/jit-icache-coherence/Cargo.toml
+++ b/crates/jit-icache-coherence/Cargo.toml
@@ -9,7 +9,7 @@ repository = "https://github.com/bytecodealliance/wasmtime"
 edition.workspace = true
 
 [dependencies]
-cfg-if = "1.0"
+cfg-if = { workspace = true }
 
 [target.'cfg(target_os = "windows")'.dependencies.windows-sys]
 workspace = true
diff --git a/crates/jit/Cargo.toml b/crates/jit/Cargo.toml
index 90a11f87e..a90737b47 100644
--- a/crates/jit/Cargo.toml
+++ b/crates/jit/Cargo.toml
@@ -16,7 +16,7 @@ wasmtime-jit-debug = { workspace = true, features = ["perf_jitdump"], optional =
 wasmtime-runtime = { workspace = true }
 target-lexicon = { workspace = true }
 anyhow = { workspace = true }
-cfg-if = "1.0"
+cfg-if = { workspace = true }
 gimli = { workspace = true }
 object = { workspace = true }
 serde = { version = "1.0.94", features = ["derive"] }
diff --git a/crates/runtime/Cargo.toml b/crates/runtime/Cargo.toml
index 0ff570f4c..279bc0b72 100644
--- a/crates/runtime/Cargo.toml
+++ b/crates/runtime/Cargo.toml
@@ -19,7 +19,7 @@ libc = { version = "0.2.112", default-features = false }
 log = { workspace = true }
 memoffset = "0.8.0"
 indexmap = "1.0.2"
-cfg-if = "1.0"
+cfg-if = { workspace = true }
 rand = { version = "0.8.3", features = ['small_rng'] }
 anyhow = { workspace = true }
 memfd = "0.6.2"
diff --git a/crates/wasmtime/Cargo.toml b/crates/wasmtime/Cargo.toml
index 0ebab2787..26d89b034 100644
--- a/crates/wasmtime/Cargo.toml
+++ b/crates/wasmtime/Cargo.toml
@@ -31,7 +31,7 @@ target-lexicon = { workspace = true }
 wasmparser = { workspace = true }
 anyhow = { workspace = true }
 libc = "0.2"
-cfg-if = "1.0"
+cfg-if = { workspace = true }
 log = { workspace = true }
 wat = { workspace = true, optional = true }
 serde = { version = "1.0.94", features = ["derive"] }
diff --git a/crates/wasmtime/src/engine/serialization.rs b/crates/wasmtime/src/engine/serialization.rs
index eb73e969e..f3093bafc 100644
--- a/crates/wasmtime/src/engine/serialization.rs
+++ b/crates/wasmtime/src/engine/serialization.rs
@@ -188,6 +188,7 @@ impl Metadata {
             extended_const,
             memory_control,
             function_references,
+            gc,
 
             // Always on; we don't currently have knobs for these.
             mutable_global: _,
@@ -199,6 +200,7 @@ impl Metadata {
         assert!(!memory_control);
         assert!(!tail_call);
         assert!(!function_references);
+        assert!(!gc);
 
         Metadata {
             target: engine.compiler().triple().to_string(),
diff --git a/deny.toml b/deny.toml
index 5feba83d8..0c1303cfd 100644
--- a/deny.toml
+++ b/deny.toml
@@ -37,6 +37,13 @@ wildcards = "allow"
 deny = []
 
 skip-tree = [
+    # wit-bindgen and wasmtime both depend on the wasm-tools crates, and
+    # wit-bindgen's latest release may sometimes lag behind when we bump
+    # wasmtime's dependencies on wasm-tools. wit-bindgen is only used for
+    # building component-based test programs, so we are going to ignore
+    # the multiple-versions errors it will introduce.
+    { name = "wit-bindgen", depth = 20 },
+
     # Criterion 0.3 is pretty old at this point and has had an upcoming 0.4 for
     # a long time. This is a dev-dependency so we don't really mind its
     # dependency tree, so skip it entirely.
diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md
index f5e6bad15..52abd5788 100644
--- a/docs/SUMMARY.md
+++ b/docs/SUMMARY.md
@@ -10,6 +10,7 @@
   - [Profiling WebAssembly](./examples-profiling.md)
     - [Profiling with Perf](./examples-profiling-perf.md)
     - [Profiling with VTune](./examples-profiling-vtune.md)
+    - [Cross-platform Profiling](./examples-profiling-guest.md)
   - [Embedding in Rust](./examples-rust-embed.md)
     - [Hello, world!](./examples-rust-hello-world.md)
     - [Calculating the GCD](./examples-rust-gcd.md)
diff --git a/docs/examples-profiling-guest.md b/docs/examples-profiling-guest.md
new file mode 100644
index 000000000..692a07f4a
--- /dev/null
+++ b/docs/examples-profiling-guest.md
@@ -0,0 +1,14 @@
+# Using Wasmtime's cross-platform profiler
+
+The guest profiling strategy enables in-process sampling and will write the
+captured profile to a file which can be viewed at
+<https://profiler.firefox.com/>.
+
+To use this profiler with the Wasmtime CLI, pass the
+`--profile=guest[,path[,interval]]` flag.
+
+- `path` is where to write the profile, `wasmtime-guest-profile.json` by default
+- `interval` is the duration between samples, 10ms by default
+
+When used with `--wasm-timeout`, the timeout will be rounded up to the nearest
+multiple of the profiling interval.
diff --git a/docs/examples-profiling.md b/docs/examples-profiling.md
index d040dc27b..348979c03 100644
--- a/docs/examples-profiling.md
+++ b/docs/examples-profiling.md
@@ -6,8 +6,28 @@ well your Wasm module is performing! From time to time you might want to dive a
 bit deeper into the performance of your Wasm, and this is where profiling comes
 into the picture.
 
-Profiling support in Wasmtime is still under development, but if you're using
-either [perf](./examples-profiling-perf.md) or
-[VTune](./examples-profiling-vtune.md) the examples in these sections are
-targeted at helping you get some information about the performance of your Wasm
-modules.
+For best results, ideally you'd use hardware performance counters for your
+timing measurements. However, that requires special support from your CPU and
+operating system. Because Wasmtime is a JIT, that also requires hooks from
+Wasmtime to your platform's native profiling tools.
+
+As a result, Wasmtime support for native profiling is limited to certain
+platforms. See the following sections of this book if you're using these
+platforms:
+
+- On Linux, we support [perf](./examples-profiling-perf.md).
+
+- For Intel's x86 CPUs on Linux or Windows, we support
+  [VTune](./examples-profiling-vtune.md).
+
+- For everything else, see the cross-platform profiler below.
+
+The native profilers can measure time spent in WebAssembly guest code as well as
+time spent in the Wasmtime host and potentially even time spent in the kernel.
+This provides a comprehensive view of performance.
+
+If the native profiling tools don't work for you, Wasmtime also provides a
+[cross-platform profiler](./examples-profiling-guest.md). This profiler can only
+measure time spent in WebAssembly guest code, and its timing measurements are
+not as precise as the native profilers. However, it works on every platform that
+Wasmtime supports.
diff --git a/src/commands/run.rs b/src/commands/run.rs
index 5b8be6620..95c5fc43e 100644
--- a/src/commands/run.rs
+++ b/src/commands/run.rs
@@ -834,7 +834,14 @@ fn generate_coredump(err: &anyhow::Error, source_name: &str, coredump_path: &str
     let coredump = wasm_encoder::CoreDumpSection::new(source_name);
     let mut stacksection = wasm_encoder::CoreDumpStackSection::new("main");
     for f in bt.frames() {
+        // We don't have the information at this point to map frames to
+        // individual instances of a module, so we won't be able to create the
+        // "frame ∈ instance ∈ module" hierarchy described in the core dump spec
+        // until we move core dump generation into the runtime. So for now
+        // instanceidx will be 0 for all frames
+        let instanceidx = 0;
         stacksection.frame(
+            instanceidx,
             f.func_index(),
             u32::try_from(f.func_offset().unwrap_or(0)).unwrap(),
             // We don't currently have access to locals/stack values
diff --git a/supply-chain/audits.toml b/supply-chain/audits.toml
index 657d02dc5..08c49595e 100644
--- a/supply-chain/audits.toml
+++ b/supply-chain/audits.toml
@@ -241,6 +241,13 @@ I am employed by a member of the Bytecode Alliance and plan to continue doing
 so and will actively maintain this crate over time.
 """
 
+[[wildcard-audits.wit-component]]
+who = "Nick Fitzgerald <fitzgen@gmail.com>"
+criteria = "safe-to-deploy"
+user-id = 696
+start = "2019-03-16"
+end = "2024-03-10"
+
 [[wildcard-audits.wit-parser]]
 who = "Alex Crichton <alex@alexcrichton.com>"
 criteria = "safe-to-deploy"
@@ -983,6 +990,11 @@ criteria = "safe-to-deploy"
 version = "0.3.3"
 notes = "I am the author of this crate."
 
+[[audits.matchers]]
+who = "Pat Hickey <phickey@fastly.com>"
+criteria = "safe-to-deploy"
+version = "0.1.0"
+
 [[audits.memfd]]
 who = "Alex Crichton <alex@alexcrichton.com>"
 criteria = "safe-to-deploy"
@@ -1341,6 +1353,16 @@ criteria = "safe-to-deploy"
 delta = "0.25.6 -> 0.25.7"
 notes = "This is a minor bug-fix update."
 
+[[audits.tempfile]]
+who = "Pat Hickey <phickey@fastly.com>"
+criteria = "safe-to-deploy"
+delta = "3.3.0 -> 3.5.0"
+
+[[audits.test-log]]
+who = "Pat Hickey <phickey@fastly.com>"
+criteria = "safe-to-deploy"
+version = "0.2.11"
+
 [[audits.tinyvec]]
 who = "Alex Crichton <alex@alexcrichton.com>"
 criteria = "safe-to-deploy"
diff --git a/supply-chain/config.toml b/supply-chain/config.toml
index ac785564b..bf17a372a 100644
--- a/supply-chain/config.toml
+++ b/supply-chain/config.toml
@@ -320,7 +320,7 @@ version = "0.10.0"
 criteria = "safe-to-deploy"
 
 [[exemptions.h2]]
-version = "0.3.16"
+version = "0.3.19"
 criteria = "safe-to-deploy"
 notes = "we are exempting tokio, hyper, and their tightly coupled dependencies by the same authors, expecting that the authors at aws will publish attestions we can import at some point soon"
 
@@ -497,10 +497,6 @@ criteria = "safe-to-deploy"
 version = "0.4.1"
 criteria = "safe-to-deploy"
 
-[[exemptions.os_pipe]]
-version = "0.9.2"
-criteria = "safe-to-run"
-
 [[exemptions.os_str_bytes]]
 version = "6.0.0"
 criteria = "safe-to-deploy"
@@ -641,6 +637,10 @@ criteria = "safe-to-deploy"
 version = "0.2.13"
 criteria = "safe-to-deploy"
 
+[[exemptions.redox_syscall]]
+version = "0.3.5"
+criteria = "safe-to-deploy"
+
 [[exemptions.redox_users]]
 version = "0.4.3"
 criteria = "safe-to-deploy"
@@ -661,10 +661,6 @@ criteria = "safe-to-deploy"
 version = "2.2.0"
 criteria = "safe-to-deploy"
 
-[[exemptions.remove_dir_all]]
-version = "0.5.3"
-criteria = "safe-to-deploy"
-
 [[exemptions.ring]]
 version = "0.16.20"
 criteria = "safe-to-deploy"
diff --git a/supply-chain/imports.lock b/supply-chain/imports.lock
index 89bcb5356..b23cb2a39 100644
--- a/supply-chain/imports.lock
+++ b/supply-chain/imports.lock
@@ -57,6 +57,20 @@ user-id = 1
 user-login = "alexcrichton"
 user-name = "Alex Crichton"
 
+[[publisher.wasm-encoder]]
+version = "0.27.0"
+when = "2023-05-15"
+user-id = 1
+user-login = "alexcrichton"
+user-name = "Alex Crichton"
+
+[[publisher.wasm-metadata]]
+version = "0.3.1"
+when = "2023-03-06"
+user-id = 1
+user-login = "alexcrichton"
+user-name = "Alex Crichton"
+
 [[publisher.wasm-metadata]]
 version = "0.5.0"
 when = "2023-04-27"
@@ -64,9 +78,9 @@ user-id = 1
 user-login = "alexcrichton"
 user-name = "Alex Crichton"
 
-[[publisher.wasm-mutate]]
-version = "0.2.23"
-when = "2023-04-13"
+[[publisher.wasm-metadata]]
+version = "0.6.0"
+when = "2023-05-15"
 user-id = 1
 user-login = "alexcrichton"
 user-name = "Alex Crichton"
@@ -78,9 +92,9 @@ user-id = 1
 user-login = "alexcrichton"
 user-name = "Alex Crichton"
 
-[[publisher.wasm-smith]]
-version = "0.12.6"
-when = "2023-04-13"
+[[publisher.wasm-mutate]]
+version = "0.2.25"
+when = "2023-05-15"
 user-id = 1
 user-login = "alexcrichton"
 user-name = "Alex Crichton"
@@ -92,9 +106,9 @@ user-id = 1
 user-login = "alexcrichton"
 user-name = "Alex Crichton"
 
-[[publisher.wasmparser]]
-version = "0.103.0"
-when = "2023-04-13"
+[[publisher.wasm-smith]]
+version = "0.12.8"
+when = "2023-05-15"
 user-id = 1
 user-login = "alexcrichton"
 user-name = "Alex Crichton"
@@ -106,9 +120,9 @@ user-id = 1
 user-login = "alexcrichton"
 user-name = "Alex Crichton"
 
-[[publisher.wasmprinter]]
-version = "0.2.55"
-when = "2023-04-13"
+[[publisher.wasmparser]]
+version = "0.105.0"
+when = "2023-05-15"
 user-id = 1
 user-login = "alexcrichton"
 user-name = "Alex Crichton"
@@ -120,9 +134,9 @@ user-id = 1
 user-login = "alexcrichton"
 user-name = "Alex Crichton"
 
-[[publisher.wast]]
-version = "56.0.0"
-when = "2023-04-13"
+[[publisher.wasmprinter]]
+version = "0.2.57"
+when = "2023-05-15"
 user-id = 1
 user-login = "alexcrichton"
 user-name = "Alex Crichton"
@@ -134,9 +148,9 @@ user-id = 1
 user-login = "alexcrichton"
 user-name = "Alex Crichton"
 
-[[publisher.wat]]
-version = "1.0.62"
-when = "2023-04-13"
+[[publisher.wast]]
+version = "58.0.0"
+when = "2023-05-15"
 user-id = 1
 user-login = "alexcrichton"
 user-name = "Alex Crichton"
@@ -148,6 +162,13 @@ user-id = 1
 user-login = "alexcrichton"
 user-name = "Alex Crichton"
 
+[[publisher.wat]]
+version = "1.0.64"
+when = "2023-05-15"
+user-id = 1
+user-login = "alexcrichton"
+user-name = "Alex Crichton"
+
 [[publisher.wit-bindgen]]
 version = "0.6.0"
 when = "2023-04-27"
@@ -183,6 +204,13 @@ user-id = 1
 user-login = "alexcrichton"
 user-name = "Alex Crichton"
 
+[[publisher.wit-component]]
+version = "0.7.4"
+when = "2023-03-10"
+user-id = 696
+user-login = "fitzgen"
+user-name = "Nick Fitzgerald"
+
 [[publisher.wit-component]]
 version = "0.8.2"
 when = "2023-04-27"
@@ -190,6 +218,13 @@ user-id = 1
 user-login = "alexcrichton"
 user-name = "Alex Crichton"
 
+[[publisher.wit-component]]
+version = "0.9.0"
+when = "2023-05-16"
+user-id = 1
+user-login = "alexcrichton"
+user-name = "Alex Crichton"
+
 [[publisher.wit-parser]]
 version = "0.7.1"
 when = "2023-04-27"


Test patch:
diff --git a/ci/run-tests.sh b/ci/run-tests.sh
index 67e53eb87..f748b0d85 100755
--- a/ci/run-tests.sh
+++ b/ci/run-tests.sh
@@ -2,9 +2,10 @@
 
 cargo test \
     --features "test-programs/test_programs" \
-    --features "test-programs/test_programs_http" \
     --features wasi-threads \
     --workspace \
     --exclude 'wasmtime-wasi-*' \
     --exclude wasi-crypto \
+    --exclude wasi-tests \
+    --exclude wasi-http-tests \
     $@
diff --git a/cranelift/filetests/Cargo.toml b/cranelift/filetests/Cargo.toml
index 789dfa5c6..4ff5037bd 100644
--- a/cranelift/filetests/Cargo.toml
+++ b/cranelift/filetests/Cargo.toml
@@ -19,7 +19,7 @@ cranelift-jit = { workspace = true, features = ["selinux-fix"] }
 cranelift-module = { workspace = true }
 cranelift-control = { workspace = true }
 file-per-thread-logger = "0.1.2"
-filecheck = "0.5.0"
+filecheck = { workspace = true }
 gimli = { workspace = true }
 log = { workspace = true }
 num_cpus = "1.8.0"
diff --git a/cranelift/filetests/filetests/isa/riscv64/simd-extractlane.clif b/cranelift/filetests/filetests/isa/riscv64/simd-extractlane.clif
new file mode 100644
index 000000000..1cd78cd55
--- /dev/null
+++ b/cranelift/filetests/filetests/isa/riscv64/simd-extractlane.clif
@@ -0,0 +1,446 @@
+test compile precise-output
+set unwind_info=false
+target riscv64 has_v
+
+function %extractlane_i8x16_idx_0(i8x16) -> i8 {
+block0(v0: i8x16):
+    v1 = extractlane v0, 0
+    return v1
+}
+
+; VCode:
+;   add sp,-16
+;   sd ra,8(sp)
+;   sd fp,0(sp)
+;   mv fp,sp
+; block0:
+;   vle8.v v0,16(fp) #avl=16, #vtype=(e8, m1, ta, ma)
+;   vmv.x.s a0,v0 #avl=16, #vtype=(e8, m1, ta, ma)
+;   ld ra,8(sp)
+;   ld fp,0(sp)
+;   add sp,+16
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   addi sp, sp, -0x10
+;   sd ra, 8(sp)
+;   sd s0, 0(sp)
+;   ori s0, sp, 0
+; block1: ; offset 0x10
+;   .byte 0x57, 0x70, 0x08, 0xcc
+;   addi t6, s0, 0x10
+;   .byte 0x07, 0x80, 0x0f, 0x02
+;   .byte 0x57, 0x25, 0x00, 0x42
+;   ld ra, 8(sp)
+;   ld s0, 0(sp)
+;   addi sp, sp, 0x10
+;   ret
+
+function %extractlane_i16x8_idx_0(i16x8) -> i16 {
+block0(v0: i16x8):
+    v1 = extractlane v0, 0
+    return v1
+}
+
+; VCode:
+;   add sp,-16
+;   sd ra,8(sp)
+;   sd fp,0(sp)
+;   mv fp,sp
+; block0:
+;   vle8.v v0,16(fp) #avl=16, #vtype=(e8, m1, ta, ma)
+;   vmv.x.s a0,v0 #avl=8, #vtype=(e16, m1, ta, ma)
+;   ld ra,8(sp)
+;   ld fp,0(sp)
+;   add sp,+16
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   addi sp, sp, -0x10
+;   sd ra, 8(sp)
+;   sd s0, 0(sp)
+;   ori s0, sp, 0
+; block1: ; offset 0x10
+;   .byte 0x57, 0x70, 0x08, 0xcc
+;   addi t6, s0, 0x10
+;   .byte 0x07, 0x80, 0x0f, 0x02
+;   .byte 0x57, 0x70, 0x84, 0xcc
+;   .byte 0x57, 0x25, 0x00, 0x42
+;   ld ra, 8(sp)
+;   ld s0, 0(sp)
+;   addi sp, sp, 0x10
+;   ret
+
+function %extractlane_i32x4_idx_0(i32x4) -> i32 {
+block0(v0: i32x4):
+    v1 = extractlane v0, 0
+    return v1
+}
+
+; VCode:
+;   add sp,-16
+;   sd ra,8(sp)
+;   sd fp,0(sp)
+;   mv fp,sp
+; block0:
+;   vle8.v v0,16(fp) #avl=16, #vtype=(e8, m1, ta, ma)
+;   vmv.x.s a0,v0 #avl=4, #vtype=(e32, m1, ta, ma)
+;   ld ra,8(sp)
+;   ld fp,0(sp)
+;   add sp,+16
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   addi sp, sp, -0x10
+;   sd ra, 8(sp)
+;   sd s0, 0(sp)
+;   ori s0, sp, 0
+; block1: ; offset 0x10
+;   .byte 0x57, 0x70, 0x08, 0xcc
+;   addi t6, s0, 0x10
+;   .byte 0x07, 0x80, 0x0f, 0x02
+;   .byte 0x57, 0x70, 0x02, 0xcd
+;   .byte 0x57, 0x25, 0x00, 0x42
+;   ld ra, 8(sp)
+;   ld s0, 0(sp)
+;   addi sp, sp, 0x10
+;   ret
+
+function %extractlane_i64x2_idx_0(i64x2) -> i64 {
+block0(v0: i64x2):
+    v1 = extractlane v0, 0
+    return v1
+}
+
+; VCode:
+;   add sp,-16
+;   sd ra,8(sp)
+;   sd fp,0(sp)
+;   mv fp,sp
+; block0:
+;   vle8.v v0,16(fp) #avl=16, #vtype=(e8, m1, ta, ma)
+;   vmv.x.s a0,v0 #avl=2, #vtype=(e64, m1, ta, ma)
+;   ld ra,8(sp)
+;   ld fp,0(sp)
+;   add sp,+16
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   addi sp, sp, -0x10
+;   sd ra, 8(sp)
+;   sd s0, 0(sp)
+;   ori s0, sp, 0
+; block1: ; offset 0x10
+;   .byte 0x57, 0x70, 0x08, 0xcc
+;   addi t6, s0, 0x10
+;   .byte 0x07, 0x80, 0x0f, 0x02
+;   .byte 0x57, 0x70, 0x81, 0xcd
+;   .byte 0x57, 0x25, 0x00, 0x42
+;   ld ra, 8(sp)
+;   ld s0, 0(sp)
+;   addi sp, sp, 0x10
+;   ret
+
+function %extractlane_f32x4_idx_0(f32x4) -> f32 {
+block0(v0: f32x4):
+    v1 = extractlane v0, 0
+    return v1
+}
+
+; VCode:
+;   add sp,-16
+;   sd ra,8(sp)
+;   sd fp,0(sp)
+;   mv fp,sp
+; block0:
+;   vle8.v v0,16(fp) #avl=16, #vtype=(e8, m1, ta, ma)
+;   vfmv.f.s fa0,v0 #avl=4, #vtype=(e32, m1, ta, ma)
+;   ld ra,8(sp)
+;   ld fp,0(sp)
+;   add sp,+16
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   addi sp, sp, -0x10
+;   sd ra, 8(sp)
+;   sd s0, 0(sp)
+;   ori s0, sp, 0
+; block1: ; offset 0x10
+;   .byte 0x57, 0x70, 0x08, 0xcc
+;   addi t6, s0, 0x10
+;   .byte 0x07, 0x80, 0x0f, 0x02
+;   .byte 0x57, 0x70, 0x02, 0xcd
+;   .byte 0x57, 0x15, 0x00, 0x42
+;   ld ra, 8(sp)
+;   ld s0, 0(sp)
+;   addi sp, sp, 0x10
+;   ret
+
+function %extractlane_f64x2_idx_0(f64x2) -> f64 {
+block0(v0: f64x2):
+    v1 = extractlane v0, 0
+    return v1
+}
+
+; VCode:
+;   add sp,-16
+;   sd ra,8(sp)
+;   sd fp,0(sp)
+;   mv fp,sp
+; block0:
+;   vle8.v v0,16(fp) #avl=16, #vtype=(e8, m1, ta, ma)
+;   vfmv.f.s fa0,v0 #avl=2, #vtype=(e64, m1, ta, ma)
+;   ld ra,8(sp)
+;   ld fp,0(sp)
+;   add sp,+16
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   addi sp, sp, -0x10
+;   sd ra, 8(sp)
+;   sd s0, 0(sp)
+;   ori s0, sp, 0
+; block1: ; offset 0x10
+;   .byte 0x57, 0x70, 0x08, 0xcc
+;   addi t6, s0, 0x10
+;   .byte 0x07, 0x80, 0x0f, 0x02
+;   .byte 0x57, 0x70, 0x81, 0xcd
+;   .byte 0x57, 0x15, 0x00, 0x42
+;   ld ra, 8(sp)
+;   ld s0, 0(sp)
+;   addi sp, sp, 0x10
+;   ret
+
+function %extractlane_i8x16_idx_1(i8x16) -> i8 {
+block0(v0: i8x16):
+    v1 = extractlane v0, 1
+    return v1
+}
+
+; VCode:
+;   add sp,-16
+;   sd ra,8(sp)
+;   sd fp,0(sp)
+;   mv fp,sp
+; block0:
+;   vle8.v v0,16(fp) #avl=16, #vtype=(e8, m1, ta, ma)
+;   vslidedown.vi v2,v0,1 #avl=16, #vtype=(e8, m1, ta, ma)
+;   vmv.x.s a0,v2 #avl=16, #vtype=(e8, m1, ta, ma)
+;   ld ra,8(sp)
+;   ld fp,0(sp)
+;   add sp,+16
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   addi sp, sp, -0x10
+;   sd ra, 8(sp)
+;   sd s0, 0(sp)
+;   ori s0, sp, 0
+; block1: ; offset 0x10
+;   .byte 0x57, 0x70, 0x08, 0xcc
+;   addi t6, s0, 0x10
+;   .byte 0x07, 0x80, 0x0f, 0x02
+;   .byte 0x57, 0xb1, 0x00, 0x3e
+;   .byte 0x57, 0x25, 0x20, 0x42
+;   ld ra, 8(sp)
+;   ld s0, 0(sp)
+;   addi sp, sp, 0x10
+;   ret
+
+function %extractlane_i16x8_idx_1(i16x8) -> i16 {
+block0(v0: i16x8):
+    v1 = extractlane v0, 1
+    return v1
+}
+
+; VCode:
+;   add sp,-16
+;   sd ra,8(sp)
+;   sd fp,0(sp)
+;   mv fp,sp
+; block0:
+;   vle8.v v0,16(fp) #avl=16, #vtype=(e8, m1, ta, ma)
+;   vslidedown.vi v2,v0,1 #avl=8, #vtype=(e16, m1, ta, ma)
+;   vmv.x.s a0,v2 #avl=8, #vtype=(e16, m1, ta, ma)
+;   ld ra,8(sp)
+;   ld fp,0(sp)
+;   add sp,+16
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   addi sp, sp, -0x10
+;   sd ra, 8(sp)
+;   sd s0, 0(sp)
+;   ori s0, sp, 0
+; block1: ; offset 0x10
+;   .byte 0x57, 0x70, 0x08, 0xcc
+;   addi t6, s0, 0x10
+;   .byte 0x07, 0x80, 0x0f, 0x02
+;   .byte 0x57, 0x70, 0x84, 0xcc
+;   .byte 0x57, 0xb1, 0x00, 0x3e
+;   .byte 0x57, 0x25, 0x20, 0x42
+;   ld ra, 8(sp)
+;   ld s0, 0(sp)
+;   addi sp, sp, 0x10
+;   ret
+
+function %extractlane_i32x4_idx_1(i32x4) -> i32 {
+block0(v0: i32x4):
+    v1 = extractlane v0, 1
+    return v1
+}
+
+; VCode:
+;   add sp,-16
+;   sd ra,8(sp)
+;   sd fp,0(sp)
+;   mv fp,sp
+; block0:
+;   vle8.v v0,16(fp) #avl=16, #vtype=(e8, m1, ta, ma)
+;   vslidedown.vi v2,v0,1 #avl=4, #vtype=(e32, m1, ta, ma)
+;   vmv.x.s a0,v2 #avl=4, #vtype=(e32, m1, ta, ma)
+;   ld ra,8(sp)
+;   ld fp,0(sp)
+;   add sp,+16
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   addi sp, sp, -0x10
+;   sd ra, 8(sp)
+;   sd s0, 0(sp)
+;   ori s0, sp, 0
+; block1: ; offset 0x10
+;   .byte 0x57, 0x70, 0x08, 0xcc
+;   addi t6, s0, 0x10
+;   .byte 0x07, 0x80, 0x0f, 0x02
+;   .byte 0x57, 0x70, 0x02, 0xcd
+;   .byte 0x57, 0xb1, 0x00, 0x3e
+;   .byte 0x57, 0x25, 0x20, 0x42
+;   ld ra, 8(sp)
+;   ld s0, 0(sp)
+;   addi sp, sp, 0x10
+;   ret
+
+function %extractlane_i64x2_idx_1(i64x2) -> i64 {
+block0(v0: i64x2):
+    v1 = extractlane v0, 1
+    return v1
+}
+
+; VCode:
+;   add sp,-16
+;   sd ra,8(sp)
+;   sd fp,0(sp)
+;   mv fp,sp
+; block0:
+;   vle8.v v0,16(fp) #avl=16, #vtype=(e8, m1, ta, ma)
+;   vslidedown.vi v2,v0,1 #avl=2, #vtype=(e64, m1, ta, ma)
+;   vmv.x.s a0,v2 #avl=2, #vtype=(e64, m1, ta, ma)
+;   ld ra,8(sp)
+;   ld fp,0(sp)
+;   add sp,+16
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   addi sp, sp, -0x10
+;   sd ra, 8(sp)
+;   sd s0, 0(sp)
+;   ori s0, sp, 0
+; block1: ; offset 0x10
+;   .byte 0x57, 0x70, 0x08, 0xcc
+;   addi t6, s0, 0x10
+;   .byte 0x07, 0x80, 0x0f, 0x02
+;   .byte 0x57, 0x70, 0x81, 0xcd
+;   .byte 0x57, 0xb1, 0x00, 0x3e
+;   .byte 0x57, 0x25, 0x20, 0x42
+;   ld ra, 8(sp)
+;   ld s0, 0(sp)
+;   addi sp, sp, 0x10
+;   ret
+
+function %extractlane_f32x4_idx_1(f32x4) -> f32 {
+block0(v0: f32x4):
+    v1 = extractlane v0, 1
+    return v1
+}
+
+; VCode:
+;   add sp,-16
+;   sd ra,8(sp)
+;   sd fp,0(sp)
+;   mv fp,sp
+; block0:
+;   vle8.v v0,16(fp) #avl=16, #vtype=(e8, m1, ta, ma)
+;   vslidedown.vi v2,v0,1 #avl=4, #vtype=(e32, m1, ta, ma)
+;   vfmv.f.s fa0,v2 #avl=4, #vtype=(e32, m1, ta, ma)
+;   ld ra,8(sp)
+;   ld fp,0(sp)
+;   add sp,+16
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   addi sp, sp, -0x10
+;   sd ra, 8(sp)
+;   sd s0, 0(sp)
+;   ori s0, sp, 0
+; block1: ; offset 0x10
+;   .byte 0x57, 0x70, 0x08, 0xcc
+;   addi t6, s0, 0x10
+;   .byte 0x07, 0x80, 0x0f, 0x02
+;   .byte 0x57, 0x70, 0x02, 0xcd
+;   .byte 0x57, 0xb1, 0x00, 0x3e
+;   .byte 0x57, 0x15, 0x20, 0x42
+;   ld ra, 8(sp)
+;   ld s0, 0(sp)
+;   addi sp, sp, 0x10
+;   ret
+
+function %extractlane_f64x2_idx_1(f64x2) -> f64 {
+block0(v0: f64x2):
+    v1 = extractlane v0, 1
+    return v1
+}
+
+; VCode:
+;   add sp,-16
+;   sd ra,8(sp)
+;   sd fp,0(sp)
+;   mv fp,sp
+; block0:
+;   vle8.v v0,16(fp) #avl=16, #vtype=(e8, m1, ta, ma)
+;   vslidedown.vi v2,v0,1 #avl=2, #vtype=(e64, m1, ta, ma)
+;   vfmv.f.s fa0,v2 #avl=2, #vtype=(e64, m1, ta, ma)
+;   ld ra,8(sp)
+;   ld fp,0(sp)
+;   add sp,+16
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   addi sp, sp, -0x10
+;   sd ra, 8(sp)
+;   sd s0, 0(sp)
+;   ori s0, sp, 0
+; block1: ; offset 0x10
+;   .byte 0x57, 0x70, 0x08, 0xcc
+;   addi t6, s0, 0x10
+;   .byte 0x07, 0x80, 0x0f, 0x02
+;   .byte 0x57, 0x70, 0x81, 0xcd
+;   .byte 0x57, 0xb1, 0x00, 0x3e
+;   .byte 0x57, 0x15, 0x20, 0x42
+;   ld ra, 8(sp)
+;   ld s0, 0(sp)
+;   addi sp, sp, 0x10
+;   ret
+
diff --git a/cranelift/filetests/filetests/isa/riscv64/simd-splat.clif b/cranelift/filetests/filetests/isa/riscv64/simd-splat.clif
new file mode 100644
index 000000000..af0099034
--- /dev/null
+++ b/cranelift/filetests/filetests/isa/riscv64/simd-splat.clif
@@ -0,0 +1,206 @@
+test compile precise-output
+set unwind_info=false
+target riscv64 has_v
+
+function %splat_i8x16(i8) -> i8x16 {
+block0(v0: i8):
+    v1 = splat.i8x16 v0
+    return v1
+}
+
+; VCode:
+; block0:
+;   vmv.v.x v3,a0 #avl=16, #vtype=(e8, m1, ta, ma)
+;   vse8.v v3,0(a1) #avl=16, #vtype=(e8, m1, ta, ma)
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   .byte 0x57, 0x70, 0x08, 0xcc
+;   .byte 0xd7, 0x41, 0x05, 0x5e
+;   .byte 0xa7, 0x81, 0x05, 0x02
+;   ret
+
+function %splat_i16x8(i16) -> i16x8 {
+block0(v0: i16):
+    v1 = splat.i16x8 v0
+    return v1
+}
+
+; VCode:
+; block0:
+;   vmv.v.x v3,a0 #avl=8, #vtype=(e16, m1, ta, ma)
+;   vse8.v v3,0(a1) #avl=16, #vtype=(e8, m1, ta, ma)
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   .byte 0x57, 0x70, 0x84, 0xcc
+;   .byte 0xd7, 0x41, 0x05, 0x5e
+;   .byte 0x57, 0x70, 0x08, 0xcc
+;   .byte 0xa7, 0x81, 0x05, 0x02
+;   ret
+
+function %splat_i32x4(i32) -> i32x4 {
+block0(v0: i32):
+    v1 = splat.i32x4 v0
+    return v1
+}
+
+; VCode:
+; block0:
+;   vmv.v.x v3,a0 #avl=4, #vtype=(e32, m1, ta, ma)
+;   vse8.v v3,0(a1) #avl=16, #vtype=(e8, m1, ta, ma)
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   .byte 0x57, 0x70, 0x02, 0xcd
+;   .byte 0xd7, 0x41, 0x05, 0x5e
+;   .byte 0x57, 0x70, 0x08, 0xcc
+;   .byte 0xa7, 0x81, 0x05, 0x02
+;   ret
+
+function %splat_i64x2(i64) -> i64x2 {
+block0(v0: i64):
+    v1 = splat.i64x2 v0
+    return v1
+}
+
+; VCode:
+; block0:
+;   vmv.v.x v3,a0 #avl=2, #vtype=(e64, m1, ta, ma)
+;   vse8.v v3,0(a1) #avl=16, #vtype=(e8, m1, ta, ma)
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   .byte 0x57, 0x70, 0x81, 0xcd
+;   .byte 0xd7, 0x41, 0x05, 0x5e
+;   .byte 0x57, 0x70, 0x08, 0xcc
+;   .byte 0xa7, 0x81, 0x05, 0x02
+;   ret
+
+function %splat_const_i8x16() -> i8x16 {
+block0:
+    v0 = iconst.i8 2
+    v1 = splat.i8x16 v0
+    return v1
+}
+
+; VCode:
+; block0:
+;   vmv.v.i v2,2 #avl=16, #vtype=(e8, m1, ta, ma)
+;   vse8.v v2,0(a0) #avl=16, #vtype=(e8, m1, ta, ma)
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   .byte 0x57, 0x70, 0x08, 0xcc
+;   .byte 0x57, 0x31, 0x01, 0x5e
+;   .byte 0x27, 0x01, 0x05, 0x02
+;   ret
+
+function %splat_const_i16x8() -> i16x8 {
+block0:
+    v0 = iconst.i16 2
+    v1 = splat.i16x8 v0
+    return v1
+}
+
+; VCode:
+; block0:
+;   vmv.v.i v2,2 #avl=8, #vtype=(e16, m1, ta, ma)
+;   vse8.v v2,0(a0) #avl=16, #vtype=(e8, m1, ta, ma)
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   .byte 0x57, 0x70, 0x84, 0xcc
+;   .byte 0x57, 0x31, 0x01, 0x5e
+;   .byte 0x57, 0x70, 0x08, 0xcc
+;   .byte 0x27, 0x01, 0x05, 0x02
+;   ret
+
+function %splat_const_i32x4() -> i32x4 {
+block0:
+    v0 = iconst.i32 2
+    v1 = splat.i32x4 v0
+    return v1
+}
+
+; VCode:
+; block0:
+;   vmv.v.i v2,2 #avl=4, #vtype=(e32, m1, ta, ma)
+;   vse8.v v2,0(a0) #avl=16, #vtype=(e8, m1, ta, ma)
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   .byte 0x57, 0x70, 0x02, 0xcd
+;   .byte 0x57, 0x31, 0x01, 0x5e
+;   .byte 0x57, 0x70, 0x08, 0xcc
+;   .byte 0x27, 0x01, 0x05, 0x02
+;   ret
+
+function %splat_const_i64x2() -> i64x2 {
+block0:
+    v0 = iconst.i64 2
+    v1 = splat.i64x2 v0
+    return v1
+}
+
+; VCode:
+; block0:
+;   vmv.v.i v2,2 #avl=2, #vtype=(e64, m1, ta, ma)
+;   vse8.v v2,0(a0) #avl=16, #vtype=(e8, m1, ta, ma)
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   .byte 0x57, 0x70, 0x81, 0xcd
+;   .byte 0x57, 0x31, 0x01, 0x5e
+;   .byte 0x57, 0x70, 0x08, 0xcc
+;   .byte 0x27, 0x01, 0x05, 0x02
+;   ret
+
+function %splat_f32x4(f32) -> f32x4 {
+block0(v0: f32):
+    v1 = splat.f32x4 v0
+    return v1
+}
+
+; VCode:
+; block0:
+;   vfmv.v.f v3,fa0 #avl=4, #vtype=(e32, m1, ta, ma)
+;   vse8.v v3,0(a0) #avl=16, #vtype=(e8, m1, ta, ma)
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   .byte 0x57, 0x70, 0x02, 0xcd
+;   .byte 0xd7, 0x51, 0x05, 0x5e
+;   .byte 0x57, 0x70, 0x08, 0xcc
+;   .byte 0xa7, 0x01, 0x05, 0x02
+;   ret
+
+function %splat_f64x2(f64) -> f64x2 {
+block0(v0: f64):
+    v1 = splat.f64x2 v0
+    return v1
+}
+
+; VCode:
+; block0:
+;   vfmv.v.f v3,fa0 #avl=2, #vtype=(e64, m1, ta, ma)
+;   vse8.v v3,0(a0) #avl=16, #vtype=(e8, m1, ta, ma)
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   .byte 0x57, 0x70, 0x81, 0xcd
+;   .byte 0xd7, 0x51, 0x05, 0x5e
+;   .byte 0x57, 0x70, 0x08, 0xcc
+;   .byte 0xa7, 0x01, 0x05, 0x02
+;   ret
+
diff --git a/cranelift/filetests/filetests/isa/x64/simd-bitwise-compile.clif b/cranelift/filetests/filetests/isa/x64/simd-bitwise-compile.clif
index 1d4a2ad8b..8f059e7c6 100644
--- a/cranelift/filetests/filetests/isa/x64/simd-bitwise-compile.clif
+++ b/cranelift/filetests/filetests/isa/x64/simd-bitwise-compile.clif
@@ -737,6 +737,118 @@ block0(v0: i32x4):
 ;   popq %rbp
 ;   retq
 
+function %sshr_i64x2_imm1(i64x2) -> i64x2 {
+block0(v0: i64x2):
+    v1 = iconst.i32 1
+    v2 = sshr v0, v1
+    return v2
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   movdqa  %xmm0, %xmm2
+;   psrad   %xmm2, $1, %xmm2
+;   pshufd  $237, %xmm2, %xmm4
+;   movdqa  %xmm0, %xmm6
+;   psrlq   %xmm6, $1, %xmm6
+;   pshufd  $232, %xmm6, %xmm0
+;   punpckldq %xmm0, %xmm4, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   movdqa %xmm0, %xmm2
+;   psrad $1, %xmm2
+;   pshufd $0xed, %xmm2, %xmm4
+;   movdqa %xmm0, %xmm6
+;   psrlq $1, %xmm6
+;   pshufd $0xe8, %xmm6, %xmm0
+;   punpckldq %xmm4, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+function %sshr_i64x2_imm32(i64x2) -> i64x2 {
+block0(v0: i64x2):
+    v1 = iconst.i32 32
+    v2 = sshr v0, v1
+    return v2
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   pshufd  $237, %xmm0, %xmm5
+;   movdqa  %xmm0, %xmm4
+;   psrad   %xmm4, $31, %xmm4
+;   pshufd  $237, %xmm4, %xmm6
+;   movdqa  %xmm5, %xmm0
+;   punpckldq %xmm0, %xmm6, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   pshufd $0xed, %xmm0, %xmm5
+;   movdqa %xmm0, %xmm4
+;   psrad $0x1f, %xmm4
+;   pshufd $0xed, %xmm4, %xmm6
+;   movdqa %xmm5, %xmm0
+;   punpckldq %xmm6, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+function %sshr_i64x2_imm54(i64x2) -> i64x2 {
+block0(v0: i64x2):
+    v1 = iconst.i32 54
+    v2 = sshr v0, v1
+    return v2
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   movdqa  %xmm0, %xmm2
+;   psrad   %xmm2, $31, %xmm2
+;   pshufd  $237, %xmm2, %xmm4
+;   movdqa  %xmm0, %xmm6
+;   psrad   %xmm6, $22, %xmm6
+;   pshufd  $233, %xmm6, %xmm0
+;   punpckldq %xmm0, %xmm4, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   movdqa %xmm0, %xmm2
+;   psrad $0x1f, %xmm2
+;   pshufd $0xed, %xmm2, %xmm4
+;   movdqa %xmm0, %xmm6
+;   psrad $0x16, %xmm6
+;   pshufd $0xe9, %xmm6, %xmm0
+;   punpckldq %xmm4, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
 function %sshr_i64x2_imm(i64x2) -> i64x2 {
 block0(v0: i64x2):
     v1 = iconst.i32 100
@@ -748,13 +860,13 @@ block0(v0: i64x2):
 ;   pushq   %rbp
 ;   movq    %rsp, %rbp
 ; block0:
-;   pextrq  $0, %xmm0, %rdx
-;   pextrq  $1, %xmm0, %r9
-;   sarq    $36, %rdx, %rdx
-;   sarq    $36, %r9, %r9
-;   uninit  %xmm0
-;   pinsrd.w $0, %xmm0, %rdx, %xmm0
-;   pinsrd.w $1, %xmm0, %r9, %xmm0
+;   movdqa  %xmm0, %xmm2
+;   psrad   %xmm2, $31, %xmm2
+;   pshufd  $237, %xmm2, %xmm4
+;   movdqa  %xmm0, %xmm6
+;   psrad   %xmm6, $4, %xmm6
+;   pshufd  $233, %xmm6, %xmm0
+;   punpckldq %xmm0, %xmm4, %xmm0
 ;   movq    %rbp, %rsp
 ;   popq    %rbp
 ;   ret
@@ -764,12 +876,13 @@ block0(v0: i64x2):
 ;   pushq %rbp
 ;   movq %rsp, %rbp
 ; block1: ; offset 0x4
-;   pextrq $0, %xmm0, %rdx
-;   pextrq $1, %xmm0, %r9
-;   sarq $0x24, %rdx
-;   sarq $0x24, %r9
-;   pinsrq $0, %rdx, %xmm0
-;   pinsrq $1, %r9, %xmm0
+;   movdqa %xmm0, %xmm2
+;   psrad $0x1f, %xmm2
+;   pshufd $0xed, %xmm2, %xmm4
+;   movdqa %xmm0, %xmm6
+;   psrad $4, %xmm6
+;   pshufd $0xe9, %xmm6, %xmm0
+;   punpckldq %xmm4, %xmm0
 ;   movq %rbp, %rsp
 ;   popq %rbp
 ;   retq
@@ -784,14 +897,16 @@ block0(v0: i64x2, v1: i32):
 ;   pushq   %rbp
 ;   movq    %rsp, %rbp
 ; block0:
-;   pextrq  $0, %xmm0, %r8
-;   pextrq  $1, %xmm0, %r10
 ;   movq    %rdi, %rcx
-;   sarq    %cl, %r8, %r8
-;   sarq    %cl, %r10, %r10
-;   uninit  %xmm0
-;   pinsrd.w $0, %xmm0, %r8, %xmm0
-;   pinsrd.w $1, %xmm0, %r10, %xmm0
+;   andq    %rcx, $63, %rcx
+;   movq    %rcx, %xmm5
+;   movdqu  const(0), %xmm8
+;   psrlq   %xmm8, %xmm5, %xmm8
+;   movdqa  %xmm0, %xmm11
+;   psrlq   %xmm11, %xmm5, %xmm11
+;   movdqa  %xmm8, %xmm0
+;   pxor    %xmm0, %xmm11, %xmm0
+;   psubq   %xmm0, %xmm8, %xmm0
 ;   movq    %rbp, %rsp
 ;   popq    %rbp
 ;   ret
@@ -801,14 +916,24 @@ block0(v0: i64x2, v1: i32):
 ;   pushq %rbp
 ;   movq %rsp, %rbp
 ; block1: ; offset 0x4
-;   pextrq $0, %xmm0, %r8
-;   pextrq $1, %xmm0, %r10
 ;   movq %rdi, %rcx
-;   sarq %cl, %r8
-;   sarq %cl, %r10
-;   pinsrq $0, %r8, %xmm0
-;   pinsrq $1, %r10, %xmm0
+;   andq $0x3f, %rcx
+;   movq %rcx, %xmm5
+;   movdqu 0x27(%rip), %xmm8
+;   psrlq %xmm5, %xmm8
+;   movdqa %xmm0, %xmm11
+;   psrlq %xmm5, %xmm11
+;   movdqa %xmm8, %xmm0
+;   pxor %xmm11, %xmm0
+;   psubq %xmm8, %xmm0
 ;   movq %rbp, %rsp
 ;   popq %rbp
 ;   retq
+;   addb %al, (%rax)
+;   addb %al, (%rax)
+;   addb %al, (%rax)
+;   addb %al, (%rax)
+;   addb %al, (%rax)
+;   addb %al, (%rax)
+;   addb %al, (%rax)
 
diff --git a/cranelift/filetests/filetests/runtests/simd-extractlane.clif b/cranelift/filetests/filetests/runtests/simd-extractlane.clif
index 016f504ed..032079c0d 100644
--- a/cranelift/filetests/filetests/runtests/simd-extractlane.clif
+++ b/cranelift/filetests/filetests/runtests/simd-extractlane.clif
@@ -8,6 +8,7 @@ target x86_64
 target x86_64 sse41
 target x86_64 sse42
 target x86_64 sse42 has_avx
+target riscv64 has_v
 
 function %extractlane_4(i8x16) -> i8 {
 block0(v0: i8x16):
diff --git a/cranelift/filetests/filetests/runtests/simd-fadd-splat.clif b/cranelift/filetests/filetests/runtests/simd-fadd-splat.clif
new file mode 100644
index 000000000..4e74ba91b
--- /dev/null
+++ b/cranelift/filetests/filetests/runtests/simd-fadd-splat.clif
@@ -0,0 +1,27 @@
+test run
+target aarch64
+target s390x
+target x86_64 ssse3 has_sse41=false
+set enable_simd
+target x86_64
+target x86_64 sse41
+target x86_64 sse42
+target x86_64 sse42 has_avx
+
+function %splat_f32x4_2(f32x4) -> f32x4 {
+block0(v0: f32x4):
+  v1 = f32const 0x1.5
+  v2 = splat.f32x4 v1
+  v3 = fadd v0, v2
+  return v3
+}
+; run: %splat_f32x4_2([0x0.0 NaN 0x1.0 0x2.0]) == [0x1.5 NaN 0x2.5 0x3.5]
+
+function %splat_f64x2_2(f64x2) -> f64x2 {
+block0(v0: f64x2):
+  v1 = f64const 0x7.5
+  v2 = splat.f64x2 v1
+  v3 = fadd v0, v2
+  return v3
+}
+; run: %splat_f64x2_2([0x0.0 0x1.0]) == [0x7.5 0x8.5]
\ No newline at end of file
diff --git a/cranelift/filetests/filetests/runtests/simd-splat.clif b/cranelift/filetests/filetests/runtests/simd-splat.clif
index de2b49fd4..c07ef8833 100644
--- a/cranelift/filetests/filetests/runtests/simd-splat.clif
+++ b/cranelift/filetests/filetests/runtests/simd-splat.clif
@@ -6,6 +6,7 @@ set enable_simd
 target x86_64 has_sse3 has_ssse3 has_sse41
 target x86_64 has_sse3 has_ssse3 has_sse41 has_avx
 target x86_64 has_sse3 has_ssse3 has_sse41 has_avx has_avx2
+target riscv64 has_v
 
 function %splat_i8x16(i8) -> i8x16 {
 block0(v0: i8):
@@ -127,24 +128,6 @@ block0(v0: i64x2):
 }
 ; run: %splat_i64x2_2([-1 0]) == [-2 -1]
 
-function %splat_f32x4_2(f32x4) -> f32x4 {
-block0(v0: f32x4):
-  v1 = f32const 0x1.5
-  v2 = splat.f32x4 v1
-  v3 = fadd v0, v2
-  return v3
-}
-; run: %splat_f32x4_2([0x0.0 NaN 0x1.0 0x2.0]) == [0x1.5 NaN 0x2.5 0x3.5]
-
-function %splat_f64x2_2(f64x2) -> f64x2 {
-block0(v0: f64x2):
-  v1 = f64const 0x7.5
-  v2 = splat.f64x2 v1
-  v3 = fadd v0, v2
-  return v3
-}
-; run: %splat_f64x2_2([0x0.0 0x1.0]) == [0x7.5 0x8.5]
-
 function %load_splat_i8x16(i8) -> i8x16 {
     ss0 = explicit_slot 8
 
diff --git a/cranelift/filetests/filetests/runtests/simd-sshr.clif b/cranelift/filetests/filetests/runtests/simd-sshr.clif
index 8de902a5b..92c0d3a2b 100644
--- a/cranelift/filetests/filetests/runtests/simd-sshr.clif
+++ b/cranelift/filetests/filetests/runtests/simd-sshr.clif
@@ -1,8 +1,11 @@
 test run
+target x86_64 ssse3 has_sse41=false
 set enable_simd
 target aarch64
 target s390x
 target x86_64
+target x86_64 sse41
+target x86_64 sse42
 target x86_64 skylake
 
 
@@ -93,3 +96,25 @@ block0(v0: i64x2):
 ; run: %i64x2_sshr_const([0x1 0xf]) == [0 0]
 ; run: %i64x2_sshr_const([0x100000000 0]) == [1 0]
 ; run: %i64x2_sshr_const([-1 -1]) == [-1 -1]
+
+function %i64x2_sshr_const2(i64x2) -> i64x2 {
+block0(v0: i64x2):
+    v1 = iconst.i32 8
+    v2 = sshr v0, v1
+    return v2
+}
+; run: %i64x2_sshr_const2([0x1 0xf]) == [0 0]
+; run: %i64x2_sshr_const2([0x100000000 0]) == [0x1000000 0]
+; run: %i64x2_sshr_const2([-1 -1]) == [-1 -1]
+
+function %i64x2_sshr_const3(i64x2) -> i64x2 {
+block0(v0: i64x2):
+    v1 = iconst.i32 40
+    v2 = sshr v0, v1
+    return v2
+}
+; run: %i64x2_sshr_const3([0x1 0xf]) == [0 0]
+; run: %i64x2_sshr_const3([0x10000000000 0]) == [1 0]
+; run: %i64x2_sshr_const3([-1 -1]) == [-1 -1]
+; run: %i64x2_sshr_const3([0x8000000080000000 0x8000000080000000]) == [0xffffffffff800000 0xffffffffff800000]
+
diff --git a/crates/test-programs/Cargo.toml b/crates/test-programs/Cargo.toml
index a45cb7587..d26664790 100644
--- a/crates/test-programs/Cargo.toml
+++ b/crates/test-programs/Cargo.toml
@@ -8,22 +8,26 @@ publish = false
 license = "Apache-2.0 WITH LLVM-exception"
 
 [build-dependencies]
-cfg-if = "1.0"
+cfg-if = { workspace = true }
 cargo_metadata = "0.15.3"
+wit-component = { workspace = true }
+heck = { workspace = true }
 
 [dev-dependencies]
+anyhow = { workspace = true }
+tempfile = { workspace = true }
+test-log = { version = "0.2", default-features = false, features = ["trace"] }
+tracing = { workspace = true }
+tracing-subscriber = { version = "0.3.1", default-features = false, features = ['fmt', 'env-filter'] }
+lazy_static = "1"
+wasmtime = { workspace = true, features = ['cranelift', 'component-model'] }
+
 wasi-common = { workspace = true }
 wasi-cap-std-sync = { workspace = true }
-wasmtime = { workspace = true, features = ['cranelift'] }
 wasmtime-wasi = { workspace = true, features = ["tokio"] }
-target-lexicon = { workspace = true }
-tracing-subscriber = { version = "0.3.1", default-features = false, features = ['fmt'] }
-tempfile = "3.1.0"
-os_pipe = "0.9"
-anyhow = { workspace = true }
-wat = { workspace = true }
 cap-std = { workspace = true }
-tokio = { version = "1.8.0", features = ["net", "rt-multi-thread"] }
+tokio = { version = "1.8.0", features = ["net", "rt-multi-thread", "macros"] }
+
 wasmtime-wasi-http = { workspace = true }
 hyper = { version = "1.0.0-rc.3", features = ["full"] }
 http = { version = "0.2.9" }
diff --git a/crates/test-programs/build.rs b/crates/test-programs/build.rs
index 60c010aa0..e96e754a2 100644
--- a/crates/test-programs/build.rs
+++ b/crates/test-programs/build.rs
@@ -1,365 +1,239 @@
-#![allow(dead_code, unused_imports)]
-//! Build program to generate a program which runs all the testsuites.
-//!
-//! By generating a separate `#[test]` test for each file, we allow cargo test
-//! to automatically run the files in parallel.
-use std::fs::{read_dir, File};
-use std::io::{self, Write};
-use std::path::{Path, PathBuf};
-use std::process::{Command, Stdio};
+#![cfg_attr(not(feature = "test_programs"), allow(dead_code))]
+
+use heck::ToSnakeCase;
+use std::env;
+use std::fs;
+use std::path::PathBuf;
+use std::process::Command;
+use wit_component::ComponentEncoder;
 
 fn main() {
     #[cfg(feature = "test_programs")]
-    wasi_tests::build_and_generate_tests();
-    #[cfg(feature = "test_programs_http")]
-    wasi_http_tests::build_and_generate_tests();
+    build_and_generate_tests();
 }
 
-fn build_tests(testsuite: &str, out_dir: &Path) -> io::Result<Vec<String>> {
-    let mut cmd = Command::new("cargo");
-    cmd.env("CARGO_PROFILE_RELEASE_DEBUG", "1");
-    cmd.env_remove("CARGO_ENCODED_RUSTFLAGS");
-    cmd.args(&[
-        "build",
-        "--release",
-        "--target=wasm32-wasi",
-        "--target-dir",
-        out_dir.to_str().unwrap(),
-    ])
-    .stdout(Stdio::inherit())
-    .stderr(Stdio::inherit())
-    .current_dir(testsuite);
-    let output = cmd.output()?;
+fn build_and_generate_tests() {
+    let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
+
+    // Not needed yet, but will be shortly:
+    //let reactor_adapter = build_adapter("reactor", &[]);
+
+    let command_adapter = build_adapter(
+        &out_dir,
+        "command",
+        &["--no-default-features", "--features=command"],
+    );
+
+    println!("cargo:rerun-if-changed=./wasi-tests");
+    println!("cargo:rerun-if-changed=./wasi-http-tests");
+
+    // Build the test programs:
+    let mut cmd = Command::new("rustup");
+    cmd.arg("run")
+        .arg("stable")
+        .arg("cargo")
+        .arg("build")
+        .arg("--target=wasm32-wasi")
+        .arg("--package=wasi-tests")
+        .arg("--package=wasi-http-tests")
+        .env("CARGO_TARGET_DIR", &out_dir)
+        .env("CARGO_PROFILE_DEV_DEBUG", "1")
+        .env_remove("CARGO_ENCODED_RUSTFLAGS");
+    let status = cmd.status().unwrap();
+    assert!(status.success());
+
+    let meta = cargo_metadata::MetadataCommand::new().exec().unwrap();
+
+    modules_rs(&meta, "wasi-tests", "bin", &out_dir);
+    components_rs(&meta, "wasi-tests", "bin", &command_adapter, &out_dir);
+
+    modules_rs(&meta, "wasi-http-tests", "bin", &out_dir);
+    // FIXME Unsure why this is broken at the moment:
+    //components_rs(&meta, "wasi-http-tests", "bin", &command_adapter, &out_dir);
+}
 
-    let status = output.status;
-    if !status.success() {
-        panic!(
-            "Building tests failed: exit code: {}",
-            status.code().unwrap()
+// Creates an `${out_dir}/${package}_modules.rs` file that exposes a `get_module(&str) -> Module`,
+// and a contains a `use self::{module} as _;` for each module that ensures that the user defines
+// a symbol (ideally a #[test]) corresponding to each module.
+fn modules_rs(meta: &cargo_metadata::Metadata, package: &str, kind: &str, out_dir: &PathBuf) {
+    let modules = targets_in_package(meta, package, kind)
+        .into_iter()
+        .map(|stem| {
+            (
+                stem.clone(),
+                out_dir
+                    .join("wasm32-wasi")
+                    .join("debug")
+                    .join(format!("{stem}.wasm"))
+                    .as_os_str()
+                    .to_str()
+                    .unwrap()
+                    .to_string(),
+            )
+        })
+        .collect::<Vec<_>>();
+
+    let mut decls = String::new();
+    let mut cases = String::new();
+    let mut uses = String::new();
+    for (stem, file) in modules {
+        let global = format!("{}_MODULE", stem.to_uppercase());
+        // Load the module from disk only once, in case it is used many times:
+        decls += &format!(
+            "
+            lazy_static::lazy_static!{{
+                static ref {global}: wasmtime::Module = {{
+                    wasmtime::Module::from_file(&ENGINE, {file:?}).unwrap()
+                }};
+            }}
+        "
         );
+        // Match the stem str literal to the module. Cloning is just a ref count incr.
+        cases += &format!("{stem:?} => {global}.clone(),\n");
+        // Statically ensure that the user defines a function (ideally a #[test]) for each stem.
+        uses += &format!("#[allow(unused_imports)] use self::{stem} as _;\n");
     }
 
-    let meta = cargo_metadata::MetadataCommand::new()
-        .manifest_path(PathBuf::from(testsuite).join("Cargo.toml"))
-        .exec()
-        .expect("cargo metadata");
-
-    Ok(meta
-        .packages
-        .iter()
-        .find(|p| p.name == testsuite)
-        .unwrap()
-        .targets
-        .iter()
-        .filter(|t| t.kind == ["bin"])
-        .map(|t| t.name.clone())
-        .collect::<Vec<String>>())
+    std::fs::write(
+        out_dir.join(&format!("{}_modules.rs", package.to_snake_case())),
+        format!(
+            "
+        {decls}
+        pub fn get_module(s: &str) -> wasmtime::Module {{
+            match s {{
+                {cases}
+                _ => panic!(\"no such module: {{}}\", s),
+            }}
+        }}
+        {uses}
+        "
+        ),
+    )
+    .unwrap();
 }
 
-#[allow(dead_code)]
-fn test_directory(
-    out: &mut File,
-    test_binaries: &[String],
-    testsuite: &str,
-    runtime: &str,
-    out_dir: &Path,
-    mut write_testsuite_tests: impl FnMut(&mut File, &Path, &str) -> io::Result<()>,
-) -> io::Result<()> {
-    writeln!(
-        out,
-        "mod {} {{",
-        Path::new(testsuite)
-            .file_stem()
-            .expect("testsuite filename should have a stem")
-            .to_str()
-            .expect("testsuite filename should be representable as a string")
-            .replace("-", "_")
-    )?;
-    writeln!(
-        out,
-        "    use super::{{runtime::{} as runtime, utils, setup_log}};",
-        runtime
-    )?;
-    for test_binary in test_binaries {
-        let binary_path = out_dir
-            .join("wasm32-wasi")
-            .join("release")
-            .join(format!("{}.wasm", test_binary.replace("-", "_")));
-        write_testsuite_tests(out, &binary_path, testsuite)?;
+// Build the WASI Preview 1 adapter, and get the binary:
+fn build_adapter(out_dir: &PathBuf, name: &str, features: &[&str]) -> Vec<u8> {
+    println!("cargo:rerun-if-changed=../wasi-preview1-component-adapter");
+    let mut cmd = Command::new("cargo");
+    cmd.arg("build")
+        .arg("--release")
+        .arg("--package=wasi-preview1-component-adapter")
+        .arg("--target=wasm32-unknown-unknown")
+        .env("CARGO_TARGET_DIR", out_dir)
+        .env_remove("CARGO_ENCODED_RUSTFLAGS");
+    for f in features {
+        cmd.arg(f);
     }
-    writeln!(out, "}}")?;
-    Ok(())
-}
+    let status = cmd.status().unwrap();
+    assert!(status.success());
 
-#[cfg(feature = "test_programs")]
-mod wasi_tests {
-    use super::*;
-    use std::env;
+    let adapter = out_dir.join(format!("wasi_preview1_component_adapter.{name}.wasm"));
+    std::fs::copy(
+        out_dir
+            .join("wasm32-unknown-unknown")
+            .join("release")
+            .join("wasi_preview1_component_adapter.wasm"),
+        &adapter,
+    )
+    .unwrap();
+    println!("wasi {name} adapter: {:?}", &adapter);
+    fs::read(&adapter).unwrap()
+}
 
-    pub(super) fn build_and_generate_tests() {
-        // Validate if any of test sources are present and if they changed
-        // This should always work since there is no submodule to init anymore
-        let bin_tests = read_dir("wasi-tests/src/bin").unwrap();
-        for test in bin_tests {
-            if let Ok(test_file) = test {
-                let test_file_path = test_file
-                    .path()
-                    .into_os_string()
-                    .into_string()
-                    .expect("test file path");
-                println!("cargo:rerun-if-changed={}", test_file_path);
-            }
-        }
-        println!("cargo:rerun-if-changed=wasi-tests/Cargo.toml");
-        println!("cargo:rerun-if-changed=wasi-tests/src/lib.rs");
-        // Build tests to OUT_DIR (target/*/build/wasi-common-*/out/wasm32-wasi/release/*.wasm)
-        let out_dir = PathBuf::from(
-            env::var("OUT_DIR").expect("The OUT_DIR environment variable must be set"),
+// Builds components out of modules, and creates an `${out_dir}/${package}_component.rs` file that
+// exposes a `get_component(&str) -> Component`
+// and a contains a `use self::{component} as _;` for each module that ensures that the user defines
+// a symbol (ideally a #[test]) corresponding to each component.
+fn components_rs(
+    meta: &cargo_metadata::Metadata,
+    package: &str,
+    kind: &str,
+    adapter: &[u8],
+    out_dir: &PathBuf,
+) {
+    let mut decls = String::new();
+    let mut cases = String::new();
+    let mut uses = String::new();
+    for target_name in targets_in_package(&meta, package, kind) {
+        let stem = target_name.to_snake_case();
+        let file = compile_component(&stem, out_dir, adapter);
+
+        let global = format!("{}_COMPONENT", stem.to_uppercase());
+        decls += &format!(
+            "
+            lazy_static::lazy_static!{{
+                static ref {global}: wasmtime::component::Component = {{
+                    wasmtime::component::Component::from_file(&ENGINE, {file:?}).unwrap()
+                }};
+            }}
+        "
         );
-        let mut out =
-            File::create(out_dir.join("wasi_tests.rs")).expect("error generating test source file");
-        let test_binaries = build_tests("wasi-tests", &out_dir).expect("building tests");
-        test_directory(
-            &mut out,
-            &test_binaries,
-            "wasi-cap-std-sync",
-            "cap_std_sync",
-            &out_dir,
-            write_testsuite_tests,
-        )
-        .expect("generating wasi-cap-std-sync tests");
-        test_directory(
-            &mut out,
-            &test_binaries,
-            "wasi-tokio",
-            "tokio",
-            &out_dir,
-            write_testsuite_tests,
-        )
-        .expect("generating wasi-tokio tests");
-    }
-
-    fn write_testsuite_tests(out: &mut File, path: &Path, testsuite: &str) -> io::Result<()> {
-        let stemstr = path
-            .file_stem()
-            .expect("file_stem")
-            .to_str()
-            .expect("to_str");
-
-        writeln!(out, "    #[test]")?;
-        let test_fn_name = stemstr.replace("-", "_");
-        if ignore(testsuite, &test_fn_name) {
-            writeln!(out, "    #[ignore]")?;
-        }
-        writeln!(out, "    fn r#{}() -> anyhow::Result<()> {{", test_fn_name,)?;
-        writeln!(out, "        setup_log();")?;
-        writeln!(
-            out,
-            "        let path = std::path::Path::new(r#\"{}\"#);",
-            path.display()
-        )?;
-        writeln!(out, "        let data = wat::parse_file(path)?;")?;
-        writeln!(
-            out,
-            "        let bin_name = utils::extract_exec_name_from_path(path)?;"
-        )?;
-        let workspace = if no_preopens(testsuite, stemstr) {
-            "None"
-        } else {
-            writeln!(
-                out,
-                "        let workspace = utils::prepare_workspace(&bin_name)?;"
-            )?;
-            "Some(workspace.path())"
-        };
-        writeln!(
-            out,
-            "        runtime::{}(&data, &bin_name, {})",
-            if inherit_stdio(testsuite, stemstr) {
-                "instantiate_inherit_stdio"
-            } else {
-                "instantiate"
-            },
-            workspace,
-        )?;
-        writeln!(out, "    }}")?;
-        writeln!(out)?;
-        Ok(())
-    }
-
-    fn ignore(testsuite: &str, name: &str) -> bool {
-        match testsuite {
-            "wasi-cap-std-sync" => cap_std_sync_ignore(name),
-            "wasi-virtfs" => virtfs_ignore(name),
-            "wasi-tokio" => tokio_ignore(name),
-            _ => panic!("unknown test suite: {}", testsuite),
-        }
-    }
-
-    #[cfg(not(windows))]
-    /// Ignore tests that aren't supported yet.
-    fn cap_std_sync_ignore(name: &str) -> bool {
-        [
-            // Trailing slash related bugs:
-            "path_rename_file_trailing_slashes",
-            "remove_directory_trailing_slashes",
-        ]
-        .contains(&name)
+        cases += &format!("{stem:?} => {global}.clone(),\n");
+        uses += &format!("use self::{stem} as _;\n");
     }
 
-    #[cfg(windows)]
-    /// Ignore tests that aren't supported yet.
-    fn cap_std_sync_ignore(name: &str) -> bool {
-        [
-            // Trailing slash related bugs
-            "interesting_paths",
-            "path_rename_file_trailing_slashes",
-            "remove_directory_trailing_slashes",
-        ]
-        .contains(&name)
-    }
-
-    /// Tokio should support the same things as cap_std_sync
-    fn tokio_ignore(name: &str) -> bool {
-        cap_std_sync_ignore(name)
-    }
-    /// Virtfs barely works at all and is not suitable for any purpose
-    fn virtfs_ignore(name: &str) -> bool {
-        [
-            "dangling_fd",
-            "dangling_symlink",
-            "directory_seek",
-            "fd_advise",
-            "fd_filestat_set",
-            "fd_flags_set",
-            "fd_readdir",
-            "file_allocate",
-            "file_pread_pwrite",
-            "file_seek_tell",
-            "file_truncation",
-            "file_unbuffered_write",
-            "interesting_paths",
-            "isatty",
-            "nofollow_errors",
-            "path_filestat",
-            "path_link",
-            "path_open_create_existing",
-            "path_open_dirfd_not_dir",
-            "path_open_read_without_rights",
-            "path_rename",
-            "path_rename_dir_trailing_slashes",
-            "path_rename_file_trailing_slashes",
-            "path_symlink_trailing_slashes",
-            "poll_oneoff",
-            "poll_oneoff_stdio",
-            "readlink",
-            "remove_directory_trailing_slashes",
-            "remove_nonempty_directory",
-            "renumber",
-            "symlink_create",
-            "symlink_filestat",
-            "symlink_loop",
-            "truncation_rights",
-            "unlink_file_trailing_slashes",
-        ]
-        .contains(&name)
-    }
-
-    /// Mark tests which do not require preopens
-    fn no_preopens(testsuite: &str, name: &str) -> bool {
-        if testsuite.starts_with("wasi-") {
-            match name {
-                "big_random_buf" => true,
-                "clock_time_get" => true,
-                "sched_yield" => true,
-                "poll_oneoff_stdio" => true,
-                _ => false,
-            }
-        } else {
-            panic!("unknown test suite {}", testsuite)
-        }
-    }
-
-    /// Mark tests which require inheriting parent process stdio
-    fn inherit_stdio(testsuite: &str, name: &str) -> bool {
-        match testsuite {
-            "wasi-cap-std-sync" | "wasi-tokio" => match name {
-                "poll_oneoff_stdio" => true,
-                _ => false,
-            },
-            "wasi-virtfs" => false,
-            _ => panic!("unknown test suite {}", testsuite),
-        }
-    }
+    std::fs::write(
+        out_dir.join(&format!("{}_components.rs", package.to_snake_case())),
+        format!(
+            "
+        {decls}
+        pub fn get_component(s: &str) -> wasmtime::component::Component {{
+            match s {{
+                {cases}
+                _ => panic!(\"no such component: {{}}\", s),
+            }}
+        }}
+        {uses}
+        "
+        ),
+    )
+    .unwrap();
 }
 
-#[cfg(feature = "test_programs_http")]
-mod wasi_http_tests {
-    use super::*;
-    use std::env;
-
-    pub(super) fn build_and_generate_tests() {
-        // Validate if any of test sources are present and if they changed
-        // This should always work since there is no submodule to init anymore
-        let bin_tests = read_dir("wasi-http-tests/src/bin").unwrap();
-        for test in bin_tests {
-            if let Ok(test_file) = test {
-                let test_file_path = test_file
-                    .path()
-                    .into_os_string()
-                    .into_string()
-                    .expect("test file path");
-                println!("cargo:rerun-if-changed={}", test_file_path);
-            }
-        }
-        println!("cargo:rerun-if-changed=wasi-http-tests/Cargo.toml");
-        println!("cargo:rerun-if-changed=wasi-http-tests/src/lib.rs");
-        // Build tests to OUT_DIR (target/*/build/wasi-common-*/out/wasm32-wasi/release/*.wasm)
-        let out_dir = PathBuf::from(
-            env::var("OUT_DIR").expect("The OUT_DIR environment variable must be set"),
-        );
-        let mut out = File::create(out_dir.join("wasi_http_tests.rs"))
-            .expect("error generating test source file");
-
-        let test_binaries = build_tests("wasi-http-tests", &out_dir).expect("building tests");
-        test_directory(
-            &mut out,
-            &test_binaries,
-            "wasi-http-tests",
-            "wasi_http_tests",
-            &out_dir,
-            write_testsuite_tests,
-        )
-        .expect("generating wasi-cap-std-sync tests");
-    }
-
-    fn write_testsuite_tests(out: &mut File, path: &Path, _testsuite: &str) -> io::Result<()> {
-        let stemstr = path
-            .file_stem()
-            .expect("file_stem")
-            .to_str()
-            .expect("to_str");
+// Compile a component, return the path of the binary:
+fn compile_component(stem: &str, out_dir: &PathBuf, adapter: &[u8]) -> PathBuf {
+    let file = out_dir
+        .join("wasm32-wasi")
+        .join("debug")
+        .join(format!("{stem}.wasm"));
+    let module = fs::read(&file).expect("read wasm module");
+    let component = ComponentEncoder::default()
+        .module(module.as_slice())
+        .unwrap()
+        .validate(true)
+        .adapter("wasi_snapshot_preview1", adapter)
+        .unwrap()
+        .encode()
+        .expect(&format!(
+            "module {:?} can be translated to a component",
+            file
+        ));
+    let component_path = out_dir.join(format!("{}.component.wasm", &stem));
+    fs::write(&component_path, component).expect("write component to disk");
+    component_path
+}
 
-        writeln!(out, "    #[test]")?;
-        let test_fn_name = stemstr.replace("-", "_");
-        writeln!(out, "    fn r#{}() -> anyhow::Result<()> {{", test_fn_name,)?;
-        writeln!(out, "        setup_log();")?;
-        writeln!(
-            out,
-            "        let path = std::path::Path::new(r#\"{}\"#);",
-            path.display()
-        )?;
-        writeln!(out, "        let data = wat::parse_file(path)?;")?;
-        writeln!(
-            out,
-            "        let bin_name = utils::extract_exec_name_from_path(path)?;"
-        )?;
-        writeln!(
-            out,
-            "        runtime::instantiate_inherit_stdio(&data, &bin_name, None)",
-        )?;
-        writeln!(out, "    }}")?;
-        writeln!(out)?;
-        Ok(())
+// Get all targets in a given package with a given kind
+// kind is "bin" for test program crates that expose a `fn main`, and
+// "cdylib" for crates that implement a reactor.
+fn targets_in_package<'a>(
+    meta: &'a cargo_metadata::Metadata,
+    package: &'a str,
+    kind: &'a str,
+) -> Vec<String> {
+    let targets = meta
+        .packages
+        .iter()
+        .find(|p| p.name == package)
+        .unwrap()
+        .targets
+        .iter()
+        .filter(move |t| t.kind == &[kind])
+        .map(|t| t.name.to_snake_case())
+        .collect::<Vec<_>>();
+    if targets.is_empty() {
+        panic!("no targets for package {package:?} of kind {kind:?}")
     }
+    targets
 }
diff --git a/crates/test-programs/tests/http_tests/main.rs b/crates/test-programs/tests/http_tests/main.rs
deleted file mode 100644
index 28a8c956d..000000000
--- a/crates/test-programs/tests/http_tests/main.rs
+++ /dev/null
@@ -1,12 +0,0 @@
-#![cfg(feature = "test_programs_http")]
-use std::sync::Once;
-mod runtime;
-mod utils;
-
-static LOG_INIT: Once = Once::new();
-
-fn setup_log() {
-    LOG_INIT.call_once(tracing_subscriber::fmt::init)
-}
-
-include!(concat!(env!("OUT_DIR"), "/wasi_http_tests.rs"));
diff --git a/crates/test-programs/tests/http_tests/runtime/mod.rs b/crates/test-programs/tests/http_tests/runtime/mod.rs
deleted file mode 100644
index 5af38c5cb..000000000
--- a/crates/test-programs/tests/http_tests/runtime/mod.rs
+++ /dev/null
@@ -1,1 +0,0 @@
-pub mod wasi_http_tests;
diff --git a/crates/test-programs/tests/http_tests/utils.rs b/crates/test-programs/tests/http_tests/utils.rs
deleted file mode 100644
index d455b9659..000000000
--- a/crates/test-programs/tests/http_tests/utils.rs
+++ /dev/null
@@ -1,13 +0,0 @@
-use std::path::Path;
-
-pub fn extract_exec_name_from_path(path: &Path) -> anyhow::Result<String> {
-    path.file_stem()
-        .and_then(|s| s.to_str())
-        .map(String::from)
-        .ok_or_else(|| {
-            anyhow::anyhow!(
-                "couldn't extract the file stem from path {}",
-                path.display()
-            )
-        })
-}
diff --git a/crates/test-programs/tests/wasi-cap-std-sync.rs b/crates/test-programs/tests/wasi-cap-std-sync.rs
new file mode 100644
index 000000000..c81e26539
--- /dev/null
+++ b/crates/test-programs/tests/wasi-cap-std-sync.rs
@@ -0,0 +1,299 @@
+#![cfg(feature = "test_programs")]
+use anyhow::Result;
+use tempfile::TempDir;
+use wasi_common::pipe::WritePipe;
+use wasmtime::{Config, Engine, Linker, Store};
+
+lazy_static::lazy_static! {
+    static ref ENGINE: Engine = {
+        let mut config = Config::new();
+        config.wasm_backtrace_details(wasmtime::WasmBacktraceDetails::Enable);
+        config.wasm_component_model(false);
+        config.async_support(false);
+
+        let engine = Engine::new(&config).unwrap();
+        engine
+    };
+}
+// uses ENGINE, creates a fn get_module(&str) -> Module
+include!(concat!(env!("OUT_DIR"), "/wasi_tests_modules.rs"));
+
+pub fn prepare_workspace(exe_name: &str) -> Result<TempDir> {
+    let prefix = format!("wasi_cap_std_sync_{}_", exe_name);
+    let tempdir = tempfile::Builder::new().prefix(&prefix).tempdir()?;
+    Ok(tempdir)
+}
+
+pub fn test_suite_environment() -> &'static [(&'static str, &'static str)] {
+    #[cfg(windows)]
+    {
+        &[
+            ("ERRNO_MODE_WINDOWS", "1"),
+            // Windows does not support dangling links or symlinks in the filesystem.
+            ("NO_DANGLING_FILESYSTEM", "1"),
+            // Windows does not support renaming a directory to an empty directory -
+            // empty directory must be deleted.
+            ("NO_RENAME_DIR_TO_EMPTY_DIR", "1"),
+            // cap-std-sync does not support the sync family of fdflags
+            ("NO_FDFLAGS_SYNC_SUPPORT", "1"),
+        ]
+    }
+    #[cfg(all(unix, not(target_os = "macos")))]
+    {
+        &[
+            ("ERRNO_MODE_UNIX", "1"),
+            // cap-std-sync does not support the sync family of fdflags
+            ("NO_FDFLAGS_SYNC_SUPPORT", "1"),
+        ]
+    }
+    #[cfg(target_os = "macos")]
+    {
+        &[
+            ("ERRNO_MODE_MACOS", "1"),
+            // cap-std-sync does not support the sync family of fdflags
+            ("NO_FDFLAGS_SYNC_SUPPORT", "1"),
+        ]
+    }
+}
+
+use wasmtime_wasi::sync::{add_to_linker, WasiCtxBuilder};
+fn run(name: &str, inherit_stdio: bool) -> Result<()> {
+    let workspace = prepare_workspace(name)?;
+    let stdout = WritePipe::new_in_memory();
+    let stderr = WritePipe::new_in_memory();
+    let r = {
+        let mut linker = Linker::new(&ENGINE);
+        add_to_linker(&mut linker, |cx| cx)?;
+
+        // Create our wasi context.
+        // Additionally register any preopened directories if we have them.
+        let mut builder = WasiCtxBuilder::new();
+
+        if inherit_stdio {
+            builder = builder.inherit_stdio();
+        } else {
+            builder = builder
+                .stdout(Box::new(stdout.clone()))
+                .stderr(Box::new(stderr.clone()));
+        }
+        builder = builder.arg(name)?.arg(".")?;
+        println!("preopen: {:?}", workspace);
+        let preopen_dir =
+            cap_std::fs::Dir::open_ambient_dir(workspace.path(), cap_std::ambient_authority())?;
+        builder = builder.preopened_dir(preopen_dir, ".")?;
+        for (var, val) in test_suite_environment() {
+            builder = builder.env(var, val)?;
+        }
+
+        let mut store = Store::new(&ENGINE, builder.build());
+        let instance = linker.instantiate(&mut store, &get_module(name))?;
+        let start = instance.get_typed_func::<(), ()>(&mut store, "_start")?;
+        start.call(&mut store, ())?;
+        Ok(())
+    };
+
+    r.map_err(move |trap: anyhow::Error| {
+        let stdout = stdout
+            .try_into_inner()
+            .expect("sole ref to stdout")
+            .into_inner();
+        if !stdout.is_empty() {
+            println!("guest stdout:\n{}\n===", String::from_utf8_lossy(&stdout));
+        }
+        let stderr = stderr
+            .try_into_inner()
+            .expect("sole ref to stderr")
+            .into_inner();
+        if !stderr.is_empty() {
+            println!("guest stderr:\n{}\n===", String::from_utf8_lossy(&stderr));
+        }
+        trap.context(format!(
+            "error while testing wasi-tests {} with cap-std-sync",
+            name
+        ))
+    })?;
+    Ok(())
+}
+
+// Below here is mechanical: there should be one test for every binary in
+// wasi-tests. The only differences should be should_panic annotations for
+// tests which fail.
+#[test_log::test]
+fn big_random_buf() {
+    run("big_random_buf", true).unwrap()
+}
+#[test_log::test]
+fn clock_time_get() {
+    run("clock_time_get", true).unwrap()
+}
+#[test_log::test]
+fn close_preopen() {
+    run("close_preopen", true).unwrap()
+}
+#[test_log::test]
+fn dangling_fd() {
+    run("dangling_fd", true).unwrap()
+}
+#[test_log::test]
+fn dangling_symlink() {
+    run("dangling_symlink", true).unwrap()
+}
+#[test_log::test]
+fn directory_seek() {
+    run("directory_seek", true).unwrap()
+}
+#[test_log::test]
+fn dir_fd_op_failures() {
+    run("dir_fd_op_failures", true).unwrap()
+}
+#[test_log::test]
+fn fd_advise() {
+    run("fd_advise", true).unwrap()
+}
+#[test_log::test]
+fn fd_filestat_get() {
+    run("fd_filestat_get", true).unwrap()
+}
+#[test_log::test]
+fn fd_filestat_set() {
+    run("fd_filestat_set", true).unwrap()
+}
+#[test_log::test]
+fn fd_flags_set() {
+    run("fd_flags_set", true).unwrap()
+}
+#[test_log::test]
+fn fd_readdir() {
+    run("fd_readdir", true).unwrap()
+}
+#[test_log::test]
+fn file_allocate() {
+    run("file_allocate", true).unwrap()
+}
+#[test_log::test]
+fn file_pread_pwrite() {
+    run("file_pread_pwrite", true).unwrap()
+}
+#[test_log::test]
+fn file_seek_tell() {
+    run("file_seek_tell", true).unwrap()
+}
+#[test_log::test]
+fn file_truncation() {
+    run("file_truncation", true).unwrap()
+}
+#[test_log::test]
+fn file_unbuffered_write() {
+    run("file_unbuffered_write", true).unwrap()
+}
+#[test_log::test]
+#[cfg_attr(windows, should_panic)]
+fn interesting_paths() {
+    run("interesting_paths", true).unwrap()
+}
+#[test_log::test]
+fn isatty() {
+    run("isatty", true).unwrap()
+}
+#[test_log::test]
+fn nofollow_errors() {
+    run("nofollow_errors", true).unwrap()
+}
+#[test_log::test]
+fn overwrite_preopen() {
+    run("overwrite_preopen", true).unwrap()
+}
+#[test_log::test]
+fn path_exists() {
+    run("path_exists", true).unwrap()
+}
+#[test_log::test]
+fn path_filestat() {
+    run("path_filestat", true).unwrap()
+}
+#[test_log::test]
+fn path_link() {
+    run("path_link", true).unwrap()
+}
+#[test_log::test]
+fn path_open_create_existing() {
+    run("path_open_create_existing", true).unwrap()
+}
+#[test_log::test]
+fn path_open_dirfd_not_dir() {
+    run("path_open_dirfd_not_dir", true).unwrap()
+}
+#[test_log::test]
+fn path_open_missing() {
+    run("path_open_missing", true).unwrap()
+}
+#[test_log::test]
+fn path_open_nonblock() {
+    run("path_open_nonblock", true).unwrap()
+}
+#[test_log::test]
+fn path_rename_dir_trailing_slashes() {
+    run("path_rename_dir_trailing_slashes", true).unwrap()
+}
+#[test_log::test]
+#[should_panic]
+fn path_rename_file_trailing_slashes() {
+    run("path_rename_file_trailing_slashes", false).unwrap()
+}
+#[test_log::test]
+fn path_rename() {
+    run("path_rename", true).unwrap()
+}
+#[test_log::test]
+fn path_symlink_trailing_slashes() {
+    run("path_symlink_trailing_slashes", true).unwrap()
+}
+#[test_log::test]
+fn poll_oneoff_files() {
+    run("poll_oneoff_files", false).unwrap()
+}
+#[test_log::test]
+fn poll_oneoff_stdio() {
+    run("poll_oneoff_stdio", true).unwrap()
+}
+#[test_log::test]
+fn readlink() {
+    run("readlink", true).unwrap()
+}
+#[test_log::test]
+#[should_panic]
+fn remove_directory_trailing_slashes() {
+    run("remove_directory_trailing_slashes", false).unwrap()
+}
+#[test_log::test]
+fn remove_nonempty_directory() {
+    run("remove_nonempty_directory", true).unwrap()
+}
+#[test_log::test]
+fn renumber() {
+    run("renumber", true).unwrap()
+}
+#[test_log::test]
+fn sched_yield() {
+    run("sched_yield", true).unwrap()
+}
+#[test_log::test]
+fn stdio() {
+    run("stdio", true).unwrap()
+}
+#[test_log::test]
+fn symlink_create() {
+    run("symlink_create", true).unwrap()
+}
+#[test_log::test]
+fn symlink_filestat() {
+    run("symlink_filestat", true).unwrap()
+}
+#[test_log::test]
+fn symlink_loop() {
+    run("symlink_loop", true).unwrap()
+}
+#[test_log::test]
+fn unlink_file_trailing_slashes() {
+    run("unlink_file_trailing_slashes", true).unwrap()
+}
diff --git a/crates/test-programs/tests/http_tests/runtime/wasi_http_tests.rs b/crates/test-programs/tests/wasi-http.rs
similarity index 73%
rename from crates/test-programs/tests/http_tests/runtime/wasi_http_tests.rs
rename to crates/test-programs/tests/wasi-http.rs
index ad5cd21c3..78c99dc93 100644
--- a/crates/test-programs/tests/http_tests/runtime/wasi_http_tests.rs
+++ b/crates/test-programs/tests/wasi-http.rs
@@ -1,6 +1,5 @@
-use anyhow::Context;
-use std::path::Path;
-use wasmtime::{Config, Engine, Linker, Module, Store};
+#![cfg(feature = "test_programs")]
+use wasmtime::{Config, Engine, Linker, Store};
 use wasmtime_wasi::{sync::WasiCtxBuilder, WasiCtx};
 use wasmtime_wasi_http::WasiHttp;
 
@@ -11,6 +10,17 @@ use hyper::{body::Bytes, service::service_fn, Request, Response};
 use std::{error::Error, net::SocketAddr};
 use tokio::net::TcpListener;
 
+lazy_static::lazy_static! {
+    static ref ENGINE: Engine = {
+        let mut config = Config::new();
+        config.wasm_backtrace_details(wasmtime::WasmBacktraceDetails::Enable);
+        let engine = Engine::new(&config).unwrap();
+        engine
+    };
+}
+// uses ENGINE, creates a fn get_module(&str) -> Module
+include!(concat!(env!("OUT_DIR"), "/wasi_http_tests_modules.rs"));
+
 async fn test(
     req: Request<hyper::body::Incoming>,
 ) -> http::Result<Response<BoxBody<Bytes, hyper::Error>>> {
@@ -49,19 +59,13 @@ fn run_server() -> Result<(), Box<dyn Error + Send + Sync>> {
     Ok(())
 }
 
-pub fn instantiate_inherit_stdio(
-    data: &[u8],
-    bin_name: &str,
-    _workspace: Option<&Path>,
-) -> anyhow::Result<()> {
+pub fn run(name: &str) -> anyhow::Result<()> {
     let _thread = std::thread::spawn(|| {
         run_server().unwrap();
     });
 
-    let config = Config::new();
-    let engine = Engine::new(&config)?;
-    let module = Module::new(&engine, &data).context("failed to create wasm module")?;
-    let mut linker = Linker::new(&engine);
+    let module = get_module(name);
+    let mut linker = Linker::new(&ENGINE);
 
     struct Ctx {
         wasi: WasiCtx,
@@ -72,10 +76,10 @@ pub fn instantiate_inherit_stdio(
     wasmtime_wasi_http::add_to_linker(&mut linker, |cx: &mut Ctx| &mut cx.http)?;
 
     // Create our wasi context.
-    let builder = WasiCtxBuilder::new().inherit_stdio().arg(bin_name)?;
+    let builder = WasiCtxBuilder::new().inherit_stdio().arg(name)?;
 
     let mut store = Store::new(
-        &engine,
+        &ENGINE,
         Ctx {
             wasi: builder.build(),
             http: WasiHttp::new(),
@@ -86,3 +90,8 @@ pub fn instantiate_inherit_stdio(
     let start = instance.get_typed_func::<(), ()>(&mut store, "_start")?;
     start.call(&mut store, ())
 }
+
+#[test_log::test]
+fn outbound_request() {
+    run("outbound_request").unwrap()
+}
diff --git a/crates/test-programs/tests/wasi-tokio.rs b/crates/test-programs/tests/wasi-tokio.rs
new file mode 100644
index 000000000..1746dc594
--- /dev/null
+++ b/crates/test-programs/tests/wasi-tokio.rs
@@ -0,0 +1,305 @@
+#![cfg(feature = "test_programs")]
+use anyhow::Result;
+use tempfile::TempDir;
+use wasi_common::pipe::WritePipe;
+use wasmtime::{Config, Engine, Linker, Store};
+use wasmtime_wasi::tokio::{add_to_linker, WasiCtxBuilder};
+
+lazy_static::lazy_static! {
+    static ref ENGINE: Engine = {
+        let mut config = Config::new();
+        config.wasm_backtrace_details(wasmtime::WasmBacktraceDetails::Enable);
+        config.wasm_component_model(false);
+        config.async_support(true);
+
+        let engine = Engine::new(&config).unwrap();
+        engine
+    };
+}
+// uses ENGINE, creates a fn get_module(&str) -> Module
+include!(concat!(env!("OUT_DIR"), "/wasi_tests_modules.rs"));
+
+pub fn prepare_workspace(exe_name: &str) -> Result<TempDir> {
+    let prefix = format!("wasi_cap_std_sync_{}_", exe_name);
+    let tempdir = tempfile::Builder::new().prefix(&prefix).tempdir()?;
+    Ok(tempdir)
+}
+
+pub fn test_suite_environment() -> &'static [(&'static str, &'static str)] {
+    #[cfg(windows)]
+    {
+        &[
+            ("ERRNO_MODE_WINDOWS", "1"),
+            // Windows does not support dangling links or symlinks in the filesystem.
+            ("NO_DANGLING_FILESYSTEM", "1"),
+            // Windows does not support renaming a directory to an empty directory -
+            // empty directory must be deleted.
+            ("NO_RENAME_DIR_TO_EMPTY_DIR", "1"),
+            // cap-std-sync does not support the sync family of fdflags
+            ("NO_FDFLAGS_SYNC_SUPPORT", "1"),
+        ]
+    }
+    #[cfg(all(unix, not(target_os = "macos")))]
+    {
+        &[
+            ("ERRNO_MODE_UNIX", "1"),
+            // cap-std-sync does not support the sync family of fdflags
+            ("NO_FDFLAGS_SYNC_SUPPORT", "1"),
+        ]
+    }
+    #[cfg(target_os = "macos")]
+    {
+        &[
+            ("ERRNO_MODE_MACOS", "1"),
+            // cap-std-sync does not support the sync family of fdflags
+            ("NO_FDFLAGS_SYNC_SUPPORT", "1"),
+        ]
+    }
+}
+
+async fn run(name: &str, inherit_stdio: bool) -> Result<()> {
+    let workspace = prepare_workspace(name)?;
+    let stdout = WritePipe::new_in_memory();
+    let stderr = WritePipe::new_in_memory();
+    let r = {
+        let mut linker = Linker::new(&ENGINE);
+        add_to_linker(&mut linker, |cx| cx)?;
+
+        // Create our wasi context.
+        // Additionally register any preopened directories if we have them.
+        let mut builder = WasiCtxBuilder::new();
+
+        if inherit_stdio {
+            builder = builder.inherit_stdio();
+        } else {
+            builder = builder
+                .stdout(Box::new(stdout.clone()))
+                .stderr(Box::new(stderr.clone()));
+        }
+        builder = builder.arg(name)?.arg(".")?;
+        println!("preopen: {:?}", workspace);
+        let preopen_dir =
+            cap_std::fs::Dir::open_ambient_dir(workspace.path(), cap_std::ambient_authority())?;
+        builder = builder.preopened_dir(preopen_dir, ".")?;
+        for (var, val) in test_suite_environment() {
+            builder = builder.env(var, val)?;
+        }
+
+        let mut store = Store::new(&ENGINE, builder.build());
+        let instance = linker
+            .instantiate_async(&mut store, &get_module(name))
+            .await?;
+        let start = instance.get_typed_func::<(), ()>(&mut store, "_start")?;
+        start.call_async(&mut store, ()).await?;
+        Ok(())
+    };
+
+    r.map_err(move |trap: anyhow::Error| {
+        let stdout = stdout
+            .try_into_inner()
+            .expect("sole ref to stdout")
+            .into_inner();
+        if !stdout.is_empty() {
+            println!("guest stdout:\n{}\n===", String::from_utf8_lossy(&stdout));
+        }
+        let stderr = stderr
+            .try_into_inner()
+            .expect("sole ref to stderr")
+            .into_inner();
+        if !stderr.is_empty() {
+            println!("guest stderr:\n{}\n===", String::from_utf8_lossy(&stderr));
+        }
+        trap.context(format!(
+            "error while testing wasi-tests {} with cap-std-sync",
+            name
+        ))
+    })?;
+    Ok(())
+}
+
+// Below here is mechanical: there should be one test for every binary in
+// wasi-tests. The only differences should be should_panic annotations for
+// tests which fail.
+#[test_log::test(tokio::test(flavor = "multi_thread"))]
+async fn big_random_buf() {
+    run("big_random_buf", true).await.unwrap()
+}
+#[test_log::test(tokio::test(flavor = "multi_thread"))]
+async fn clock_time_get() {
+    run("clock_time_get", true).await.unwrap()
+}
+#[test_log::test(tokio::test(flavor = "multi_thread"))]
+async fn close_preopen() {
+    run("close_preopen", true).await.unwrap()
+}
+#[test_log::test(tokio::test(flavor = "multi_thread"))]
+async fn dangling_fd() {
+    run("dangling_fd", true).await.unwrap()
+}
+#[test_log::test(tokio::test(flavor = "multi_thread"))]
+async fn dangling_symlink() {
+    run("dangling_symlink", true).await.unwrap()
+}
+#[test_log::test(tokio::test(flavor = "multi_thread"))]
+async fn directory_seek() {
+    run("directory_seek", true).await.unwrap()
+}
+#[test_log::test(tokio::test(flavor = "multi_thread"))]
+async fn dir_fd_op_failures() {
+    run("dir_fd_op_failures", true).await.unwrap()
+}
+#[test_log::test(tokio::test(flavor = "multi_thread"))]
+async fn fd_advise() {
+    run("fd_advise", true).await.unwrap()
+}
+#[test_log::test(tokio::test(flavor = "multi_thread"))]
+async fn fd_filestat_get() {
+    run("fd_filestat_get", true).await.unwrap()
+}
+#[test_log::test(tokio::test(flavor = "multi_thread"))]
+async fn fd_filestat_set() {
+    run("fd_filestat_set", true).await.unwrap()
+}
+#[test_log::test(tokio::test(flavor = "multi_thread"))]
+async fn fd_flags_set() {
+    run("fd_flags_set", true).await.unwrap()
+}
+#[test_log::test(tokio::test(flavor = "multi_thread"))]
+async fn fd_readdir() {
+    run("fd_readdir", true).await.unwrap()
+}
+#[test_log::test(tokio::test(flavor = "multi_thread"))]
+async fn file_allocate() {
+    run("file_allocate", true).await.unwrap()
+}
+#[test_log::test(tokio::test(flavor = "multi_thread"))]
+async fn file_pread_pwrite() {
+    run("file_pread_pwrite", true).await.unwrap()
+}
+#[test_log::test(tokio::test(flavor = "multi_thread"))]
+async fn file_seek_tell() {
+    run("file_seek_tell", true).await.unwrap()
+}
+#[test_log::test(tokio::test(flavor = "multi_thread"))]
+async fn file_truncation() {
+    run("file_truncation", true).await.unwrap()
+}
+#[test_log::test(tokio::test(flavor = "multi_thread"))]
+async fn file_unbuffered_write() {
+    run("file_unbuffered_write", true).await.unwrap()
+}
+#[test_log::test(tokio::test(flavor = "multi_thread"))]
+#[cfg_attr(windows, should_panic)]
+async fn interesting_paths() {
+    run("interesting_paths", true).await.unwrap()
+}
+#[test_log::test(tokio::test(flavor = "multi_thread"))]
+async fn isatty() {
+    run("isatty", true).await.unwrap()
+}
+#[test_log::test(tokio::test(flavor = "multi_thread"))]
+async fn nofollow_errors() {
+    run("nofollow_errors", true).await.unwrap()
+}
+#[test_log::test(tokio::test(flavor = "multi_thread"))]
+async fn overwrite_preopen() {
+    run("overwrite_preopen", true).await.unwrap()
+}
+#[test_log::test(tokio::test(flavor = "multi_thread"))]
+async fn path_exists() {
+    run("path_exists", true).await.unwrap()
+}
+#[test_log::test(tokio::test(flavor = "multi_thread"))]
+async fn path_filestat() {
+    run("path_filestat", true).await.unwrap()
+}
+#[test_log::test(tokio::test(flavor = "multi_thread"))]
+async fn path_link() {
+    run("path_link", true).await.unwrap()
+}
+#[test_log::test(tokio::test(flavor = "multi_thread"))]
+async fn path_open_create_existing() {
+    run("path_open_create_existing", true).await.unwrap()
+}
+#[test_log::test(tokio::test(flavor = "multi_thread"))]
+async fn path_open_dirfd_not_dir() {
+    run("path_open_dirfd_not_dir", true).await.unwrap()
+}
+#[test_log::test(tokio::test(flavor = "multi_thread"))]
+async fn path_open_missing() {
+    run("path_open_missing", true).await.unwrap()
+}
+#[test_log::test(tokio::test(flavor = "multi_thread"))]
+async fn path_open_nonblock() {
+    run("path_open_nonblock", true).await.unwrap()
+}
+#[test_log::test(tokio::test(flavor = "multi_thread"))]
+async fn path_rename_dir_trailing_slashes() {
+    run("path_rename_dir_trailing_slashes", true).await.unwrap()
+}
+#[test_log::test(tokio::test(flavor = "multi_thread"))]
+#[should_panic]
+async fn path_rename_file_trailing_slashes() {
+    run("path_rename_file_trailing_slashes", false)
+        .await
+        .unwrap()
+}
+#[test_log::test(tokio::test(flavor = "multi_thread"))]
+async fn path_rename() {
+    run("path_rename", true).await.unwrap()
+}
+#[test_log::test(tokio::test(flavor = "multi_thread"))]
+async fn path_symlink_trailing_slashes() {
+    run("path_symlink_trailing_slashes", true).await.unwrap()
+}
+#[test_log::test(tokio::test(flavor = "multi_thread"))]
+async fn poll_oneoff_files() {
+    run("poll_oneoff_files", false).await.unwrap()
+}
+#[test_log::test(tokio::test(flavor = "multi_thread"))]
+async fn poll_oneoff_stdio() {
+    run("poll_oneoff_stdio", true).await.unwrap()
+}
+#[test_log::test(tokio::test(flavor = "multi_thread"))]
+async fn readlink() {
+    run("readlink", true).await.unwrap()
+}
+#[test_log::test(tokio::test(flavor = "multi_thread"))]
+#[should_panic]
+async fn remove_directory_trailing_slashes() {
+    run("remove_directory_trailing_slashes", false)
+        .await
+        .unwrap()
+}
+#[test_log::test(tokio::test(flavor = "multi_thread"))]
+async fn remove_nonempty_directory() {
+    run("remove_nonempty_directory", true).await.unwrap()
+}
+#[test_log::test(tokio::test(flavor = "multi_thread"))]
+async fn renumber() {
+    run("renumber", true).await.unwrap()
+}
+#[test_log::test(tokio::test(flavor = "multi_thread"))]
+async fn sched_yield() {
+    run("sched_yield", true).await.unwrap()
+}
+#[test_log::test(tokio::test(flavor = "multi_thread"))]
+async fn stdio() {
+    run("stdio", true).await.unwrap()
+}
+#[test_log::test(tokio::test(flavor = "multi_thread"))]
+async fn symlink_create() {
+    run("symlink_create", true).await.unwrap()
+}
+#[test_log::test(tokio::test(flavor = "multi_thread"))]
+async fn symlink_filestat() {
+    run("symlink_filestat", true).await.unwrap()
+}
+#[test_log::test(tokio::test(flavor = "multi_thread"))]
+async fn symlink_loop() {
+    run("symlink_loop", true).await.unwrap()
+}
+#[test_log::test(tokio::test(flavor = "multi_thread"))]
+async fn unlink_file_trailing_slashes() {
+    run("unlink_file_trailing_slashes", true).await.unwrap()
+}
diff --git a/crates/test-programs/tests/wasm_tests/main.rs b/crates/test-programs/tests/wasm_tests/main.rs
deleted file mode 100644
index 44fb127db..000000000
--- a/crates/test-programs/tests/wasm_tests/main.rs
+++ /dev/null
@@ -1,13 +0,0 @@
-#![cfg(feature = "test_programs")]
-mod runtime;
-mod utils;
-
-use std::sync::Once;
-
-static LOG_INIT: Once = Once::new();
-
-fn setup_log() {
-    LOG_INIT.call_once(tracing_subscriber::fmt::init)
-}
-
-include!(concat!(env!("OUT_DIR"), "/wasi_tests.rs"));
diff --git a/crates/test-programs/tests/wasm_tests/runtime/cap_std_sync.rs b/crates/test-programs/tests/wasm_tests/runtime/cap_std_sync.rs
deleted file mode 100644
index 939b0bdb9..000000000
--- a/crates/test-programs/tests/wasm_tests/runtime/cap_std_sync.rs
+++ /dev/null
@@ -1,86 +0,0 @@
-use anyhow::Context;
-use std::path::Path;
-use wasi_common::pipe::WritePipe;
-use wasmtime::{Engine, Linker, Module, Store};
-use wasmtime_wasi::sync::{add_to_linker, WasiCtxBuilder};
-
-pub fn instantiate(data: &[u8], bin_name: &str, workspace: Option<&Path>) -> anyhow::Result<()> {
-    run(data, bin_name, workspace, false)
-}
-pub fn instantiate_inherit_stdio(
-    data: &[u8],
-    bin_name: &str,
-    workspace: Option<&Path>,
-) -> anyhow::Result<()> {
-    run(data, bin_name, workspace, true)
-}
-
-fn run(
-    data: &[u8],
-    bin_name: &str,
-    workspace: Option<&Path>,
-    inherit_stdio: bool,
-) -> anyhow::Result<()> {
-    let stdout = WritePipe::new_in_memory();
-    let stderr = WritePipe::new_in_memory();
-
-    let r = {
-        let engine = Engine::default();
-        let module = Module::new(&engine, &data).context("failed to create wasm module")?;
-        let mut linker = Linker::new(&engine);
-        add_to_linker(&mut linker, |cx| cx)?;
-
-        // Create our wasi context.
-        // Additionally register any preopened directories if we have them.
-        let mut builder = WasiCtxBuilder::new();
-
-        if inherit_stdio {
-            builder = builder.inherit_stdio();
-        } else {
-            builder = builder
-                .stdout(Box::new(stdout.clone()))
-                .stderr(Box::new(stderr.clone()));
-        }
-
-        builder = builder.arg(bin_name)?.arg(".")?;
-
-        if let Some(workspace) = workspace {
-            println!("preopen: {:?}", workspace);
-            let preopen_dir =
-                cap_std::fs::Dir::open_ambient_dir(workspace, cap_std::ambient_authority())?;
-            builder = builder.preopened_dir(preopen_dir, ".")?;
-        }
-        for (var, val) in super::test_suite_environment() {
-            builder = builder.env(var, val)?;
-        }
-
-        // cap-std-sync does not yet support the sync family of fdflags
-        builder = builder.env("NO_FDFLAGS_SYNC_SUPPORT", "1")?;
-
-        let mut store = Store::new(&engine, builder.build());
-        let instance = linker.instantiate(&mut store, &module)?;
-        let start = instance.get_typed_func::<(), ()>(&mut store, "_start")?;
-        start.call(&mut store, ()).map_err(anyhow::Error::from)
-    };
-
-    match r {
-        Ok(()) => Ok(()),
-        Err(trap) => {
-            let stdout = stdout
-                .try_into_inner()
-                .expect("sole ref to stdout")
-                .into_inner();
-            if !stdout.is_empty() {
-                println!("guest stdout:\n{}\n===", String::from_utf8_lossy(&stdout));
-            }
-            let stderr = stderr
-                .try_into_inner()
-                .expect("sole ref to stderr")
-                .into_inner();
-            if !stderr.is_empty() {
-                println!("guest stderr:\n{}\n===", String::from_utf8_lossy(&stderr));
-            }
-            Err(trap.context(format!("error while testing Wasm module '{}'", bin_name,)))
-        }
-    }
-}
diff --git a/crates/test-programs/tests/wasm_tests/runtime/mod.rs b/crates/test-programs/tests/wasm_tests/runtime/mod.rs
deleted file mode 100644
index 2ea46982d..000000000
--- a/crates/test-programs/tests/wasm_tests/runtime/mod.rs
+++ /dev/null
@@ -1,28 +0,0 @@
-pub mod cap_std_sync;
-pub mod tokio;
-
-// Configure the test suite environment.
-// Test programs use these environment variables to determine what behavior
-// is expected: different errnos are expected on windows, mac, and other unixes,
-// and other filesystem operations are supported or not.
-pub fn test_suite_environment() -> &'static [(&'static str, &'static str)] {
-    #[cfg(windows)]
-    {
-        &[
-            ("ERRNO_MODE_WINDOWS", "1"),
-            // Windows does not support dangling links or symlinks in the filesystem.
-            ("NO_DANGLING_FILESYSTEM", "1"),
-            // Windows does not support renaming a directory to an empty directory -
-            // empty directory must be deleted.
-            ("NO_RENAME_DIR_TO_EMPTY_DIR", "1"),
-        ]
-    }
-    #[cfg(all(unix, not(target_os = "macos")))]
-    {
-        &[("ERRNO_MODE_UNIX", "1")]
-    }
-    #[cfg(target_os = "macos")]
-    {
-        &[("ERRNO_MODE_MACOS", "1")]
-    }
-}
diff --git a/crates/test-programs/tests/wasm_tests/runtime/tokio.rs b/crates/test-programs/tests/wasm_tests/runtime/tokio.rs
deleted file mode 100644
index 565dc088d..000000000
--- a/crates/test-programs/tests/wasm_tests/runtime/tokio.rs
+++ /dev/null
@@ -1,97 +0,0 @@
-use anyhow::Context;
-use std::path::Path;
-use wasi_common::pipe::WritePipe;
-use wasmtime::{Config, Engine, Linker, Module, Store};
-use wasmtime_wasi::tokio::{add_to_linker, WasiCtxBuilder};
-
-pub fn instantiate(data: &[u8], bin_name: &str, workspace: Option<&Path>) -> anyhow::Result<()> {
-    run(data, bin_name, workspace, false)
-}
-pub fn instantiate_inherit_stdio(
-    data: &[u8],
-    bin_name: &str,
-    workspace: Option<&Path>,
-) -> anyhow::Result<()> {
-    run(data, bin_name, workspace, true)
-}
-
-fn run(
-    data: &[u8],
-    bin_name: &str,
-    workspace: Option<&Path>,
-    inherit_stdio: bool,
-) -> anyhow::Result<()> {
-    let stdout = WritePipe::new_in_memory();
-    let stdout_ = stdout.clone();
-    let stderr = WritePipe::new_in_memory();
-    let stderr_ = stderr.clone();
-
-    let r = tokio::runtime::Runtime::new()
-        .expect("create runtime")
-        .block_on(async move {
-            let mut config = Config::new();
-            config.async_support(true);
-            let engine = Engine::new(&config)?;
-            let module = Module::new(&engine, &data).context("failed to create wasm module")?;
-            let mut linker = Linker::new(&engine);
-            add_to_linker(&mut linker, |cx| cx)?;
-
-            // Create our wasi context.
-            let mut builder = WasiCtxBuilder::new();
-
-            if inherit_stdio {
-                builder = builder.inherit_stdio();
-            } else {
-                builder = builder
-                    .stdout(Box::new(stdout_.clone()))
-                    .stderr(Box::new(stderr_.clone()));
-            }
-
-            builder = builder.arg(bin_name)?.arg(".")?;
-
-            if let Some(workspace) = workspace {
-                println!("preopen: {:?}", workspace);
-                let preopen_dir =
-                    cap_std::fs::Dir::open_ambient_dir(workspace, cap_std::ambient_authority())?;
-                builder = builder.preopened_dir(preopen_dir, ".")?;
-            }
-
-            for (var, val) in super::test_suite_environment() {
-                builder = builder.env(var, val)?;
-            }
-
-            // tokio does not yet support the sync family of fdflags, because cap-std-sync
-            // does not.
-            builder = builder.env("NO_FDFLAGS_SYNC_SUPPORT", "1")?;
-
-            let mut store = Store::new(&engine, builder.build());
-
-            let instance = linker.instantiate_async(&mut store, &module).await?;
-            let start = instance.get_typed_func::<(), ()>(&mut store, "_start")?;
-            start
-                .call_async(&mut store, ())
-                .await
-                .map_err(anyhow::Error::from)
-        });
-
-    match r {
-        Ok(()) => Ok(()),
-        Err(trap) => {
-            let stdout = stdout
-                .try_into_inner()
-                .expect("sole ref to stdout")
-                .into_inner();
-            if !stdout.is_empty() {
-                println!("guest stdout:\n{}\n===", String::from_utf8_lossy(&stdout));
-            }
-            let stderr = stderr
-                .try_into_inner()
-                .expect("sole ref to stderr")
-                .into_inner();
-            if !stderr.is_empty() {
-                println!("guest stderr:\n{}\n===", String::from_utf8_lossy(&stderr));
-            }
-            Err(trap.context(format!("error while testing Wasm module '{}'", bin_name,)))
-        }
-    }
-}
diff --git a/crates/test-programs/tests/wasm_tests/utils.rs b/crates/test-programs/tests/wasm_tests/utils.rs
deleted file mode 100644
index c8b35f92d..000000000
--- a/crates/test-programs/tests/wasm_tests/utils.rs
+++ /dev/null
@@ -1,20 +0,0 @@
-use std::path::Path;
-use tempfile::{Builder, TempDir};
-
-pub fn prepare_workspace(exe_name: &str) -> anyhow::Result<TempDir> {
-    let prefix = format!("wasi_common_{}", exe_name);
-    let tempdir = Builder::new().prefix(&prefix).tempdir()?;
-    Ok(tempdir)
-}
-
-pub fn extract_exec_name_from_path(path: &Path) -> anyhow::Result<String> {
-    path.file_stem()
-        .and_then(|s| s.to_str())
-        .map(String::from)
-        .ok_or_else(|| {
-            anyhow::anyhow!(
-                "couldn't extract the file stem from path {}",
-                path.display()
-            )
-        })
-}
diff --git a/crates/test-programs/wasi-http-tests/Cargo.lock b/crates/test-programs/wasi-http-tests/Cargo.lock
deleted file mode 100644
index b8d40a06d..000000000
--- a/crates/test-programs/wasi-http-tests/Cargo.lock
+++ /dev/null
@@ -1,380 +0,0 @@
-# This file is automatically @generated by Cargo.
-# It is not intended for manual editing.
-version = 3
-
-[[package]]
-name = "anyhow"
-version = "1.0.70"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4"
-
-[[package]]
-name = "autocfg"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
-
-[[package]]
-name = "bitflags"
-version = "1.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
-
-[[package]]
-name = "cfg-if"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
-
-[[package]]
-name = "form_urlencoded"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8"
-dependencies = [
- "percent-encoding",
-]
-
-[[package]]
-name = "hashbrown"
-version = "0.12.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
-
-[[package]]
-name = "heck"
-version = "0.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
-dependencies = [
- "unicode-segmentation",
-]
-
-[[package]]
-name = "id-arena"
-version = "2.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005"
-
-[[package]]
-name = "idna"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6"
-dependencies = [
- "unicode-bidi",
- "unicode-normalization",
-]
-
-[[package]]
-name = "indexmap"
-version = "1.9.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
-dependencies = [
- "autocfg",
- "hashbrown",
- "serde",
-]
-
-[[package]]
-name = "leb128"
-version = "0.2.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
-
-[[package]]
-name = "log"
-version = "0.4.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
-dependencies = [
- "cfg-if",
-]
-
-[[package]]
-name = "memchr"
-version = "2.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
-
-[[package]]
-name = "percent-encoding"
-version = "2.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
-
-[[package]]
-name = "proc-macro2"
-version = "1.0.53"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ba466839c78239c09faf015484e5cc04860f88242cff4d03eb038f04b4699b73"
-dependencies = [
- "unicode-ident",
-]
-
-[[package]]
-name = "pulldown-cmark"
-version = "0.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ffade02495f22453cd593159ea2f59827aae7f53fa8323f756799b670881dcf8"
-dependencies = [
- "bitflags",
- "memchr",
- "unicase",
-]
-
-[[package]]
-name = "quote"
-version = "1.0.26"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc"
-dependencies = [
- "proc-macro2",
-]
-
-[[package]]
-name = "serde"
-version = "1.0.158"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "771d4d9c4163ee138805e12c710dd365e4f44be8be0503cb1bb9eb989425d9c9"
-dependencies = [
- "serde_derive",
-]
-
-[[package]]
-name = "serde_derive"
-version = "1.0.158"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e801c1712f48475582b7696ac71e0ca34ebb30e09338425384269d9717c62cad"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.9",
-]
-
-[[package]]
-name = "syn"
-version = "1.0.109"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
-dependencies = [
- "proc-macro2",
- "quote",
- "unicode-ident",
-]
-
-[[package]]
-name = "syn"
-version = "2.0.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0da4a3c17e109f700685ec577c0f85efd9b19bcf15c913985f14dc1ac01775aa"
-dependencies = [
- "proc-macro2",
- "quote",
- "unicode-ident",
-]
-
-[[package]]
-name = "tinyvec"
-version = "1.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
-dependencies = [
- "tinyvec_macros",
-]
-
-[[package]]
-name = "tinyvec_macros"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
-
-[[package]]
-name = "unicase"
-version = "2.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
-dependencies = [
- "version_check",
-]
-
-[[package]]
-name = "unicode-bidi"
-version = "0.3.13"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460"
-
-[[package]]
-name = "unicode-ident"
-version = "1.0.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
-
-[[package]]
-name = "unicode-normalization"
-version = "0.1.22"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
-dependencies = [
- "tinyvec",
-]
-
-[[package]]
-name = "unicode-segmentation"
-version = "1.10.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
-
-[[package]]
-name = "unicode-xid"
-version = "0.2.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
-
-[[package]]
-name = "url"
-version = "2.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643"
-dependencies = [
- "form_urlencoded",
- "idna",
- "percent-encoding",
-]
-
-[[package]]
-name = "version_check"
-version = "0.9.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
-
-[[package]]
-name = "wasi-http-tests"
-version = "0.0.0"
-dependencies = [
- "anyhow",
- "wit-bindgen",
-]
-
-[[package]]
-name = "wasm-encoder"
-version = "0.25.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4eff853c4f09eec94d76af527eddad4e9de13b11d6286a1ef7134bc30135a2b7"
-dependencies = [
- "leb128",
-]
-
-[[package]]
-name = "wasm-metadata"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fd6956efd8a1a2c48a707e9a1b2da729834a0f8e4c58117493b0d9d089cee468"
-dependencies = [
- "anyhow",
- "indexmap",
- "serde",
- "wasm-encoder",
- "wasmparser",
-]
-
-[[package]]
-name = "wasmparser"
-version = "0.102.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "48134de3d7598219ab9eaf6b91b15d8e50d31da76b8519fe4ecfcec2cf35104b"
-dependencies = [
- "indexmap",
- "url",
-]
-
-[[package]]
-name = "wit-bindgen"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4e7cf57f8786216c5652e1228b25203af2ff523808b5e9d3671894eee2bf7264"
-dependencies = [
- "bitflags",
- "wit-bindgen-rust-macro",
-]
-
-[[package]]
-name = "wit-bindgen-core"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ef177b73007d86c720931d0e2ea7e30eb8c9776e58361717743fc1e83cfacfe5"
-dependencies = [
- "anyhow",
- "wit-component",
- "wit-parser",
-]
-
-[[package]]
-name = "wit-bindgen-rust"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "efdf5b00935b7b52d0e56cae1960f8ac13019a285f5aa762ff6bd7139a5c28a2"
-dependencies = [
- "heck",
- "wasm-metadata",
- "wit-bindgen-core",
- "wit-bindgen-rust-lib",
- "wit-component",
-]
-
-[[package]]
-name = "wit-bindgen-rust-lib"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ab0a8f4b5fb1820b9d232beb122936425f72ec8fe6acb56e5d8782cfe55083da"
-dependencies = [
- "heck",
- "wit-bindgen-core",
-]
-
-[[package]]
-name = "wit-bindgen-rust-macro"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cadf1adf12ed25629b06272c16b335ef8c5a240d0ca64ab508a955ac3b46172c"
-dependencies = [
- "anyhow",
- "proc-macro2",
- "syn 1.0.109",
- "wit-bindgen-core",
- "wit-bindgen-rust",
- "wit-component",
-]
-
-[[package]]
-name = "wit-component"
-version = "0.7.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed04310239706efc71cc8b995cb0226089c5b5fd260c3bd800a71486bd3cec97"
-dependencies = [
- "anyhow",
- "bitflags",
- "indexmap",
- "log",
- "url",
- "wasm-encoder",
- "wasm-metadata",
- "wasmparser",
- "wit-parser",
-]
-
-[[package]]
-name = "wit-parser"
-version = "0.6.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f887c3da527a51b321076ebe6a7513026a4757b6d4d144259946552d6fc728b3"
-dependencies = [
- "anyhow",
- "id-arena",
- "indexmap",
- "log",
- "pulldown-cmark",
- "unicode-xid",
- "url",
-]
diff --git a/crates/test-programs/wasi-http-tests/Cargo.toml b/crates/test-programs/wasi-http-tests/Cargo.toml
index 0c04ce698..d6f90599f 100644
--- a/crates/test-programs/wasi-http-tests/Cargo.toml
+++ b/crates/test-programs/wasi-http-tests/Cargo.toml
@@ -6,10 +6,5 @@ edition = "2021"
 publish = false
 
 [dependencies]
-anyhow = "1"
-wit-bindgen = "0.4.0"
-
-# This crate is built with the wasm32-wasi target, so it's separate
-# from the main Wasmtime build, so use this directive to exclude it
-# from the parent directory's workspace.
-[workspace]
+anyhow = { workspace = true }
+wit-bindgen = { workspace = true }
diff --git a/crates/test-programs/wasi-http-tests/src/bin/outbound_request.rs b/crates/test-programs/wasi-http-tests/src/bin/outbound_request.rs
index 8bcca28a4..0c087d889 100644
--- a/crates/test-programs/wasi-http-tests/src/bin/outbound_request.rs
+++ b/crates/test-programs/wasi-http-tests/src/bin/outbound_request.rs
@@ -30,8 +30,8 @@ impl Response {
 }
 
 fn request(
-    method: types::MethodParam<'_>,
-    scheme: types::SchemeParam<'_>,
+    method: types::Method,
+    scheme: types::Scheme,
     authority: &str,
     path: &str,
     query: &str,
@@ -43,7 +43,7 @@ fn request(
     ]);
 
     let request =
-        types::new_outgoing_request(method, path, query, Some(scheme), authority, headers);
+        types::new_outgoing_request(&method, path, query, Some(&scheme), authority, headers);
 
     let request_body = types::outgoing_request_write(request)
         .map_err(|_| anyhow!("outgoing request write failed"))?;
@@ -103,8 +103,8 @@ fn request(
 
 fn main() -> Result<()> {
     let r1 = request(
-        types::MethodParam::Get,
-        types::SchemeParam::Http,
+        types::Method::Get,
+        types::Scheme::Http,
         "localhost:3000",
         "/get",
         "?some=arg?goes=here",
@@ -121,8 +121,8 @@ fn main() -> Result<()> {
     assert_eq!(r1.body, b"");
 
     let r2 = request(
-        types::MethodParam::Post,
-        types::SchemeParam::Http,
+        types::Method::Post,
+        types::Scheme::Http,
         "localhost:3000",
         "/post",
         "",
@@ -137,8 +137,8 @@ fn main() -> Result<()> {
     assert_eq!(r2.body, b"{\"foo\": \"bar\"}");
 
     let r3 = request(
-        types::MethodParam::Put,
-        types::SchemeParam::Http,
+        types::Method::Put,
+        types::Scheme::Http,
         "localhost:3000",
         "/put",
         "",
@@ -153,8 +153,8 @@ fn main() -> Result<()> {
     assert_eq!(r3.body, b"");
 
     let r4 = request(
-        types::MethodParam::Other("OTHER"),
-        types::SchemeParam::Http,
+        types::Method::Other("OTHER".to_owned()),
+        types::Scheme::Http,
         "localhost:3000",
         "/",
         "",
@@ -164,12 +164,12 @@ fn main() -> Result<()> {
     let error = r4.unwrap_err();
     assert_eq!(
         error.to_string(),
-        "ErrorResult::UnexpectedError(\"unknown method OTHER\")"
+        "Error::UnexpectedError(\"unknown method OTHER\")"
     );
 
     let r5 = request(
-        types::MethodParam::Get,
-        types::SchemeParam::Other("WS"),
+        types::Method::Get,
+        types::Scheme::Other("WS".to_owned()),
         "localhost:3000",
         "/",
         "",
@@ -179,7 +179,7 @@ fn main() -> Result<()> {
     let error = r5.unwrap_err();
     assert_eq!(
         error.to_string(),
-        "ErrorResult::UnexpectedError(\"unsupported scheme WS\")"
+        "Error::UnexpectedError(\"unsupported scheme WS\")"
     );
 
     Ok(())
diff --git a/crates/test-programs/wasi-tests/Cargo.lock b/crates/test-programs/wasi-tests/Cargo.lock
deleted file mode 100644
index d1d241ba8..000000000
--- a/crates/test-programs/wasi-tests/Cargo.lock
+++ /dev/null
@@ -1,30 +0,0 @@
-# This file is automatically @generated by Cargo.
-# It is not intended for manual editing.
-version = 3
-
-[[package]]
-name = "libc"
-version = "0.2.72"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a9f8082297d534141b30c8d39e9b1773713ab50fdbe4ff30f750d063b3bfd701"
-
-[[package]]
-name = "once_cell"
-version = "1.12.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225"
-
-[[package]]
-name = "wasi"
-version = "0.11.0+wasi-snapshot-preview1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
-
-[[package]]
-name = "wasi-tests"
-version = "0.0.0"
-dependencies = [
- "libc",
- "once_cell",
- "wasi",
-]
diff --git a/crates/test-programs/wasi-tests/Cargo.toml b/crates/test-programs/wasi-tests/Cargo.toml
index 64a5ca3db..1cf5c4949 100644
--- a/crates/test-programs/wasi-tests/Cargo.toml
+++ b/crates/test-programs/wasi-tests/Cargo.toml
@@ -9,8 +9,3 @@ publish = false
 libc = "0.2.65"
 wasi = "0.11.0"
 once_cell = "1.12"
-
-# This crate is built with the wasm32-wasi target, so it's separate
-# from the main Wasmtime build, so use this directive to exclude it
-# from the parent directory's workspace.
-[workspace]
diff --git a/crates/test-programs/wasi-tests/src/bin/overwrite_preopen.rs b/crates/test-programs/wasi-tests/src/bin/overwrite_preopen.rs
index 7b509c503..0721d18ab 100644
--- a/crates/test-programs/wasi-tests/src/bin/overwrite_preopen.rs
+++ b/crates/test-programs/wasi-tests/src/bin/overwrite_preopen.rs
@@ -9,8 +9,7 @@ unsafe fn test_overwrite_preopen(dir_fd: wasi::Fd) {
     let old_dir_filestat = wasi::fd_filestat_get(dir_fd).expect("failed fd_filestat_get");
 
     // Try to renumber over a preopened directory handle.
-    wasi::fd_renumber(dir_fd, pre_fd)
-            .expect("renumbering over a preopened file descriptor");
+    wasi::fd_renumber(dir_fd, pre_fd).expect("renumbering over a preopened file descriptor");
 
     // Ensure that pre_fd is still open.
     let new_dir_filestat = wasi::fd_filestat_get(pre_fd).expect("failed fd_filestat_get");
diff --git a/crates/test-programs/wasi-tests/src/bin/path_open_nonblock.rs b/crates/test-programs/wasi-tests/src/bin/path_open_nonblock.rs
index 35c02ce2d..3ca994181 100644
--- a/crates/test-programs/wasi-tests/src/bin/path_open_nonblock.rs
+++ b/crates/test-programs/wasi-tests/src/bin/path_open_nonblock.rs
@@ -1,8 +1,9 @@
 use std::{env, process};
-use wasi_tests::{open_scratch_directory};
+use wasi_tests::open_scratch_directory;
 
 unsafe fn try_path_open(dir_fd: wasi::Fd) {
-    let fd = wasi::path_open(dir_fd, 0, ".", 0, 0, 0, wasi::FDFLAGS_NONBLOCK).expect("opening the dir");
+    let _fd =
+        wasi::path_open(dir_fd, 0, ".", 0, 0, 0, wasi::FDFLAGS_NONBLOCK).expect("opening the dir");
 }
 
 fn main() {
diff --git a/crates/test-programs/wasi-tests/src/bin/readlink.rs b/crates/test-programs/wasi-tests/src/bin/readlink.rs
index 615647247..eb8b8aa5f 100644
--- a/crates/test-programs/wasi-tests/src/bin/readlink.rs
+++ b/crates/test-programs/wasi-tests/src/bin/readlink.rs
@@ -1,5 +1,5 @@
 use std::{env, process};
-use wasi_tests::{assert_errno, create_file, open_scratch_directory};
+use wasi_tests::{create_file, open_scratch_directory};
 
 unsafe fn test_readlink(dir_fd: wasi::Fd) {
     // Create a file in the scratch directory.
diff --git a/crates/test-programs/wasi-tests/src/lib.rs b/crates/test-programs/wasi-tests/src/lib.rs
index 31174f120..3a701ad86 100644
--- a/crates/test-programs/wasi-tests/src/lib.rs
+++ b/crates/test-programs/wasi-tests/src/lib.rs
@@ -39,10 +39,7 @@ pub fn open_scratch_directory(path: &str) -> Result<wasi::Fd, String> {
 pub unsafe fn create_file(dir_fd: wasi::Fd, filename: &str) {
     let file_fd =
         wasi::path_open(dir_fd, 0, filename, wasi::OFLAGS_CREAT, 0, 0, 0).expect("creating a file");
-    assert!(
-        file_fd > libc::STDERR_FILENO as wasi::Fd,
-        "file descriptor range check",
-    );
+    assert!(file_fd > STDERR_FD, "file descriptor range check",);
     wasi::fd_close(file_fd).expect("closing a file");
 }