Repository: bytecodealliance/wasmtime
Pull request #7628 (2023-12-04 17:17:33)

winch: Rework `br_table` jumps

Follow up to: https://github.com/bytecodealliance/wasmtime/pull/7616

The fuzzer has been running for a while now (~2 days) without reporting new issues. 

This commit reworks the `br_table` logic so that it correctly handles all the jumps involved to each of the targets.

Even though it is safe to use the default branch for type information, it is not safe to use it to derive the base stack pointer and base value stack length. Each block might and will probably have, different values as base stack pointer and base value stack length. This change ensures that each target offset is taken into account to balance the value stack prior to each jump.


 

<!--
Please make sure you include the following information:

- If this work has been discussed elsewhere, please include a link to that
  conversation. If it was discussed in an issue, just mention "issue #...".

- Explain why this change is needed. If the details are in an issue already,
  this can be brief.

Our development process is documented in the Wasmtime book:
https://docs.wasmtime.dev/contributing-development-process.html

Please ensure all communication follows the code of conduct:
https://github.com/bytecodealliance/wasmtime/blob/main/CODE_OF_CONDUCT.md
-->


github-actions[bot]: #### Subscribe to Label Action

cc @saulecabrera

<details>
This issue or pull request has been labeled: "winch"

Thus the following users have been cc'd because of the following labels:

* saulecabrera: winch

To subscribe or unsubscribe from this label, edit the <code>.github/subscribe-to-label.json</code> configuration file.

[Learn more.](https://github.com/bytecodealliance/subscribe-to-label-action)
</details>

Patch:
diff --git a/Cargo.lock b/Cargo.lock
index ae5e9bbb3..7de50af65 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -226,7 +226,7 @@ checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535"
 
 [[package]]
 name = "byte-array-literals"
-version = "16.0.0"
+version = "17.0.0"
 
 [[package]]
 name = "byteorder"
@@ -563,7 +563,7 @@ dependencies = [
 
 [[package]]
 name = "cranelift"
-version = "0.103.0"
+version = "0.104.0"
 dependencies = [
  "cranelift-codegen",
  "cranelift-frontend",
@@ -571,14 +571,14 @@ dependencies = [
 
 [[package]]
 name = "cranelift-bforest"
-version = "0.103.0"
+version = "0.104.0"
 dependencies = [
  "cranelift-entity",
 ]
 
 [[package]]
 name = "cranelift-codegen"
-version = "0.103.0"
+version = "0.104.0"
 dependencies = [
  "anyhow",
  "bincode",
@@ -606,25 +606,25 @@ dependencies = [
 
 [[package]]
 name = "cranelift-codegen-meta"
-version = "0.103.0"
+version = "0.104.0"
 dependencies = [
  "cranelift-codegen-shared",
 ]
 
 [[package]]
 name = "cranelift-codegen-shared"
-version = "0.103.0"
+version = "0.104.0"
 
 [[package]]
 name = "cranelift-control"
-version = "0.103.0"
+version = "0.104.0"
 dependencies = [
  "arbitrary",
 ]
 
 [[package]]
 name = "cranelift-entity"
-version = "0.103.0"
+version = "0.104.0"
 dependencies = [
  "serde",
  "serde_derive",
@@ -663,7 +663,7 @@ dependencies = [
 
 [[package]]
 name = "cranelift-frontend"
-version = "0.103.0"
+version = "0.104.0"
 dependencies = [
  "cranelift-codegen",
  "hashbrown 0.14.0",
@@ -687,7 +687,7 @@ dependencies = [
 
 [[package]]
 name = "cranelift-interpreter"
-version = "0.103.0"
+version = "0.104.0"
 dependencies = [
  "cranelift-codegen",
  "cranelift-entity",
@@ -701,7 +701,7 @@ dependencies = [
 
 [[package]]
 name = "cranelift-isle"
-version = "0.103.0"
+version = "0.104.0"
 dependencies = [
  "codespan-reporting",
  "log",
@@ -710,7 +710,7 @@ dependencies = [
 
 [[package]]
 name = "cranelift-jit"
-version = "0.103.0"
+version = "0.104.0"
 dependencies = [
  "anyhow",
  "cranelift",
@@ -731,7 +731,7 @@ dependencies = [
 
 [[package]]
 name = "cranelift-module"
-version = "0.103.0"
+version = "0.104.0"
 dependencies = [
  "anyhow",
  "cranelift-codegen",
@@ -743,7 +743,7 @@ dependencies = [
 
 [[package]]
 name = "cranelift-native"
-version = "0.103.0"
+version = "0.104.0"
 dependencies = [
  "cranelift-codegen",
  "libc",
@@ -752,7 +752,7 @@ dependencies = [
 
 [[package]]
 name = "cranelift-object"
-version = "0.103.0"
+version = "0.104.0"
 dependencies = [
  "anyhow",
  "cranelift-codegen",
@@ -767,7 +767,7 @@ dependencies = [
 
 [[package]]
 name = "cranelift-reader"
-version = "0.103.0"
+version = "0.104.0"
 dependencies = [
  "anyhow",
  "cranelift-codegen",
@@ -777,7 +777,7 @@ dependencies = [
 
 [[package]]
 name = "cranelift-serde"
-version = "0.103.0"
+version = "0.104.0"
 dependencies = [
  "clap",
  "cranelift-codegen",
@@ -824,7 +824,7 @@ dependencies = [
 
 [[package]]
 name = "cranelift-wasm"
-version = "0.103.0"
+version = "0.104.0"
 dependencies = [
  "cranelift-codegen",
  "cranelift-entity",
@@ -2895,7 +2895,7 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
 
 [[package]]
 name = "verify-component-adapter"
-version = "16.0.0"
+version = "17.0.0"
 dependencies = [
  "anyhow",
  "wasmparser",
@@ -2945,7 +2945,7 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
 
 [[package]]
 name = "wasi-cap-std-sync"
-version = "16.0.0"
+version = "17.0.0"
 dependencies = [
  "anyhow",
  "async-trait",
@@ -2967,7 +2967,7 @@ dependencies = [
 
 [[package]]
 name = "wasi-common"
-version = "16.0.0"
+version = "17.0.0"
 dependencies = [
  "anyhow",
  "bitflags 2.4.1",
@@ -2991,7 +2991,7 @@ dependencies = [
 
 [[package]]
 name = "wasi-preview1-component-adapter"
-version = "16.0.0"
+version = "17.0.0"
 dependencies = [
  "byte-array-literals",
  "object",
@@ -3002,7 +3002,7 @@ dependencies = [
 
 [[package]]
 name = "wasi-tokio"
-version = "16.0.0"
+version = "17.0.0"
 dependencies = [
  "anyhow",
  "cap-std",
@@ -3192,7 +3192,7 @@ dependencies = [
 
 [[package]]
 name = "wasmtime"
-version = "16.0.0"
+version = "17.0.0"
 dependencies = [
  "anyhow",
  "async-trait",
@@ -3207,7 +3207,6 @@ dependencies = [
  "object",
  "once_cell",
  "paste",
- "psm",
  "rayon",
  "serde",
  "serde_derive",
@@ -3233,14 +3232,14 @@ dependencies = [
 
 [[package]]
 name = "wasmtime-asm-macros"
-version = "16.0.0"
+version = "17.0.0"
 dependencies = [
  "cfg-if",
 ]
 
 [[package]]
 name = "wasmtime-bench-api"
-version = "16.0.0"
+version = "17.0.0"
 dependencies = [
  "anyhow",
  "cap-std",
@@ -3257,14 +3256,14 @@ dependencies = [
 
 [[package]]
 name = "wasmtime-c-api"
-version = "16.0.0"
+version = "17.0.0"
 dependencies = [
  "wasmtime-c-api-impl",
 ]
 
 [[package]]
 name = "wasmtime-c-api-impl"
-version = "16.0.0"
+version = "17.0.0"
 dependencies = [
  "anyhow",
  "cap-std",
@@ -3291,7 +3290,7 @@ dependencies = [
 
 [[package]]
 name = "wasmtime-cache"
-version = "16.0.0"
+version = "17.0.0"
 dependencies = [
  "anyhow",
  "base64",
@@ -3313,7 +3312,7 @@ dependencies = [
 
 [[package]]
 name = "wasmtime-cli"
-version = "16.0.0"
+version = "17.0.0"
 dependencies = [
  "anyhow",
  "async-trait",
@@ -3368,7 +3367,7 @@ dependencies = [
 
 [[package]]
 name = "wasmtime-cli-flags"
-version = "16.0.0"
+version = "17.0.0"
 dependencies = [
  "anyhow",
  "clap",
@@ -3381,7 +3380,7 @@ dependencies = [
 
 [[package]]
 name = "wasmtime-component-macro"
-version = "16.0.0"
+version = "17.0.0"
 dependencies = [
  "anyhow",
  "component-macro-test-helpers",
@@ -3397,11 +3396,11 @@ dependencies = [
 
 [[package]]
 name = "wasmtime-component-util"
-version = "16.0.0"
+version = "17.0.0"
 
 [[package]]
 name = "wasmtime-cranelift"
-version = "16.0.0"
+version = "17.0.0"
 dependencies = [
  "anyhow",
  "cfg-if",
@@ -3424,7 +3423,7 @@ dependencies = [
 
 [[package]]
 name = "wasmtime-cranelift-shared"
-version = "16.0.0"
+version = "17.0.0"
 dependencies = [
  "anyhow",
  "cranelift-codegen",
@@ -3438,7 +3437,7 @@ dependencies = [
 
 [[package]]
 name = "wasmtime-environ"
-version = "16.0.0"
+version = "17.0.0"
 dependencies = [
  "anyhow",
  "clap",
@@ -3476,7 +3475,7 @@ dependencies = [
 
 [[package]]
 name = "wasmtime-explorer"
-version = "16.0.0"
+version = "17.0.0"
 dependencies = [
  "anyhow",
  "capstone",
@@ -3490,7 +3489,7 @@ dependencies = [
 
 [[package]]
 name = "wasmtime-fiber"
-version = "16.0.0"
+version = "17.0.0"
 dependencies = [
  "anyhow",
  "backtrace",
@@ -3559,7 +3558,7 @@ dependencies = [
 
 [[package]]
 name = "wasmtime-jit"
-version = "16.0.0"
+version = "17.0.0"
 dependencies = [
  "addr2line",
  "anyhow",
@@ -3584,7 +3583,7 @@ dependencies = [
 
 [[package]]
 name = "wasmtime-jit-debug"
-version = "16.0.0"
+version = "17.0.0"
 dependencies = [
  "object",
  "once_cell",
@@ -3594,7 +3593,7 @@ dependencies = [
 
 [[package]]
 name = "wasmtime-jit-icache-coherence"
-version = "16.0.0"
+version = "17.0.0"
 dependencies = [
  "cfg-if",
  "libc",
@@ -3603,7 +3602,7 @@ dependencies = [
 
 [[package]]
 name = "wasmtime-runtime"
-version = "16.0.0"
+version = "17.0.0"
 dependencies = [
  "anyhow",
  "cc",
@@ -3618,6 +3617,7 @@ dependencies = [
  "once_cell",
  "paste",
  "proptest",
+ "psm",
  "rand",
  "rustix",
  "sptr",
@@ -3633,7 +3633,7 @@ dependencies = [
 
 [[package]]
 name = "wasmtime-types"
-version = "16.0.0"
+version = "17.0.0"
 dependencies = [
  "cranelift-entity",
  "serde",
@@ -3644,7 +3644,7 @@ dependencies = [
 
 [[package]]
 name = "wasmtime-versioned-export-macros"
-version = "16.0.0"
+version = "17.0.0"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -3653,7 +3653,7 @@ dependencies = [
 
 [[package]]
 name = "wasmtime-wasi"
-version = "16.0.0"
+version = "17.0.0"
 dependencies = [
  "anyhow",
  "async-trait",
@@ -3691,7 +3691,7 @@ dependencies = [
 
 [[package]]
 name = "wasmtime-wasi-http"
-version = "16.0.0"
+version = "17.0.0"
 dependencies = [
  "anyhow",
  "async-trait",
@@ -3717,7 +3717,7 @@ dependencies = [
 
 [[package]]
 name = "wasmtime-wasi-nn"
-version = "16.0.0"
+version = "17.0.0"
 dependencies = [
  "anyhow",
  "openvino",
@@ -3730,7 +3730,7 @@ dependencies = [
 
 [[package]]
 name = "wasmtime-wasi-threads"
-version = "16.0.0"
+version = "17.0.0"
 dependencies = [
  "anyhow",
  "log",
@@ -3742,7 +3742,7 @@ dependencies = [
 
 [[package]]
 name = "wasmtime-wast"
-version = "16.0.0"
+version = "17.0.0"
 dependencies = [
  "anyhow",
  "log",
@@ -3752,7 +3752,7 @@ dependencies = [
 
 [[package]]
 name = "wasmtime-winch"
-version = "16.0.0"
+version = "17.0.0"
 dependencies = [
  "anyhow",
  "cranelift-codegen",
@@ -3767,7 +3767,7 @@ dependencies = [
 
 [[package]]
 name = "wasmtime-wit-bindgen"
-version = "16.0.0"
+version = "17.0.0"
 dependencies = [
  "anyhow",
  "heck",
@@ -3777,7 +3777,7 @@ dependencies = [
 
 [[package]]
 name = "wasmtime-wmemcheck"
-version = "16.0.0"
+version = "17.0.0"
 
 [[package]]
 name = "wast"
@@ -3838,7 +3838,7 @@ dependencies = [
 
 [[package]]
 name = "wiggle"
-version = "16.0.0"
+version = "17.0.0"
 dependencies = [
  "anyhow",
  "async-trait",
@@ -3855,7 +3855,7 @@ dependencies = [
 
 [[package]]
 name = "wiggle-generate"
-version = "16.0.0"
+version = "17.0.0"
 dependencies = [
  "anyhow",
  "heck",
@@ -3868,7 +3868,7 @@ dependencies = [
 
 [[package]]
 name = "wiggle-macro"
-version = "16.0.0"
+version = "17.0.0"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -3923,7 +3923,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
 
 [[package]]
 name = "winch-codegen"
-version = "0.14.0"
+version = "0.15.0"
 dependencies = [
  "anyhow",
  "cranelift-codegen",
diff --git a/Cargo.toml b/Cargo.toml
index 071809bdf..d89162d30 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -65,7 +65,7 @@ rustix = { workspace = true, features = ["mm", "param"] }
 
 [dev-dependencies]
 # depend again on wasmtime to activate its default features for tests
-wasmtime = { workspace = true, features = ['component-model', 'async', 'default', 'winch'] }
+wasmtime = { workspace = true, features = ['component-model', 'async', 'default', 'winch', 'debug-builtins'] }
 env_logger = { workspace = true }
 log = { workspace = true }
 filecheck = { workspace = true }
@@ -128,7 +128,7 @@ exclude = [
 ]
 
 [workspace.package]
-version = "16.0.0"
+version = "17.0.0"
 authors = ["The Wasmtime Project Developers"]
 edition = "2021"
 # Wasmtime's current policy is that this number can be no larger than the
@@ -150,59 +150,59 @@ all = 'allow'
 
 [workspace.dependencies]
 arbitrary = { version = "1.3.1" }
-wasmtime-wmemcheck = { path = "crates/wmemcheck", version = "=16.0.0" }
-wasmtime = { path = "crates/wasmtime", version = "16.0.0", default-features = false }
-wasmtime-cache = { path = "crates/cache", version = "=16.0.0" }
-wasmtime-cli-flags = { path = "crates/cli-flags", version = "=16.0.0" }
-wasmtime-cranelift = { path = "crates/cranelift", version = "=16.0.0" }
-wasmtime-cranelift-shared = { path = "crates/cranelift-shared", version = "=16.0.0" }
-wasmtime-winch = { path = "crates/winch", version = "=16.0.0" }
-wasmtime-environ = { path = "crates/environ", version = "=16.0.0" }
-wasmtime-explorer = { path = "crates/explorer", version = "=16.0.0" }
-wasmtime-fiber = { path = "crates/fiber", version = "=16.0.0" }
-wasmtime-types = { path = "crates/types", version = "16.0.0" }
-wasmtime-jit = { path = "crates/jit", version = "=16.0.0" }
-wasmtime-jit-debug = { path = "crates/jit-debug", version = "=16.0.0" }
-wasmtime-runtime = { path = "crates/runtime", version = "=16.0.0" }
-wasmtime-wast = { path = "crates/wast", version = "=16.0.0" }
-wasmtime-wasi = { path = "crates/wasi", version = "16.0.0", default-features = false }
-wasmtime-wasi-http = { path = "crates/wasi-http", version = "=16.0.0", default-features = false }
-wasmtime-wasi-nn = { path = "crates/wasi-nn", version = "16.0.0" }
-wasmtime-wasi-threads = { path = "crates/wasi-threads", version = "16.0.0" }
-wasmtime-component-util = { path = "crates/component-util", version = "=16.0.0" }
-wasmtime-component-macro = { path = "crates/component-macro", version = "=16.0.0" }
-wasmtime-asm-macros = { path = "crates/asm-macros", version = "=16.0.0" }
-wasmtime-versioned-export-macros = { path = "crates/versioned-export-macros", version = "=16.0.0" }
+wasmtime-wmemcheck = { path = "crates/wmemcheck", version = "=17.0.0" }
+wasmtime = { path = "crates/wasmtime", version = "17.0.0", default-features = false }
+wasmtime-cache = { path = "crates/cache", version = "=17.0.0" }
+wasmtime-cli-flags = { path = "crates/cli-flags", version = "=17.0.0" }
+wasmtime-cranelift = { path = "crates/cranelift", version = "=17.0.0" }
+wasmtime-cranelift-shared = { path = "crates/cranelift-shared", version = "=17.0.0" }
+wasmtime-winch = { path = "crates/winch", version = "=17.0.0" }
+wasmtime-environ = { path = "crates/environ", version = "=17.0.0" }
+wasmtime-explorer = { path = "crates/explorer", version = "=17.0.0" }
+wasmtime-fiber = { path = "crates/fiber", version = "=17.0.0" }
+wasmtime-types = { path = "crates/types", version = "17.0.0" }
+wasmtime-jit = { path = "crates/jit", version = "=17.0.0" }
+wasmtime-jit-debug = { path = "crates/jit-debug", version = "=17.0.0" }
+wasmtime-runtime = { path = "crates/runtime", version = "=17.0.0" }
+wasmtime-wast = { path = "crates/wast", version = "=17.0.0" }
+wasmtime-wasi = { path = "crates/wasi", version = "17.0.0", default-features = false }
+wasmtime-wasi-http = { path = "crates/wasi-http", version = "=17.0.0", default-features = false }
+wasmtime-wasi-nn = { path = "crates/wasi-nn", version = "17.0.0" }
+wasmtime-wasi-threads = { path = "crates/wasi-threads", version = "17.0.0" }
+wasmtime-component-util = { path = "crates/component-util", version = "=17.0.0" }
+wasmtime-component-macro = { path = "crates/component-macro", version = "=17.0.0" }
+wasmtime-asm-macros = { path = "crates/asm-macros", version = "=17.0.0" }
+wasmtime-versioned-export-macros = { path = "crates/versioned-export-macros", version = "=17.0.0" }
 component-test-util = { path = "crates/misc/component-test-util" }
 component-fuzz-util = { path = "crates/misc/component-fuzz-util" }
-wiggle = { path = "crates/wiggle", version = "=16.0.0", default-features = false }
-wiggle-macro = { path = "crates/wiggle/macro", version = "=16.0.0" }
-wiggle-generate = { path = "crates/wiggle/generate", version = "=16.0.0" }
-wasi-common = { path = "crates/wasi-common", version = "=16.0.0" }
-wasi-tokio = { path = "crates/wasi-common/tokio", version = "=16.0.0" }
-wasi-cap-std-sync = { path = "crates/wasi-common/cap-std-sync", version = "=16.0.0" }
+wiggle = { path = "crates/wiggle", version = "=17.0.0", default-features = false }
+wiggle-macro = { path = "crates/wiggle/macro", version = "=17.0.0" }
+wiggle-generate = { path = "crates/wiggle/generate", version = "=17.0.0" }
+wasi-common = { path = "crates/wasi-common", version = "=17.0.0" }
+wasi-tokio = { path = "crates/wasi-common/tokio", version = "=17.0.0" }
+wasi-cap-std-sync = { path = "crates/wasi-common/cap-std-sync", version = "=17.0.0" }
 wasmtime-fuzzing = { path = "crates/fuzzing" }
-wasmtime-jit-icache-coherence = { path = "crates/jit-icache-coherence", version = "=16.0.0" }
-wasmtime-wit-bindgen = { path = "crates/wit-bindgen", version = "=16.0.0" }
+wasmtime-jit-icache-coherence = { path = "crates/jit-icache-coherence", version = "=17.0.0" }
+wasmtime-wit-bindgen = { path = "crates/wit-bindgen", version = "=17.0.0" }
 test-programs-artifacts = { path = 'crates/test-programs/artifacts' }
 
-cranelift-wasm = { path = "cranelift/wasm", version = "0.103.0" }
-cranelift-codegen = { path = "cranelift/codegen", version = "0.103.0", default-features = false, features = ["std", "unwind"] }
-cranelift-frontend = { path = "cranelift/frontend", version = "0.103.0" }
-cranelift-entity = { path = "cranelift/entity", version = "0.103.0" }
-cranelift-native = { path = "cranelift/native", version = "0.103.0" }
-cranelift-module = { path = "cranelift/module", version = "0.103.0" }
-cranelift-interpreter = { path = "cranelift/interpreter", version = "0.103.0" }
-cranelift-reader = { path = "cranelift/reader", version = "0.103.0" }
+cranelift-wasm = { path = "cranelift/wasm", version = "0.104.0" }
+cranelift-codegen = { path = "cranelift/codegen", version = "0.104.0", default-features = false, features = ["std", "unwind"] }
+cranelift-frontend = { path = "cranelift/frontend", version = "0.104.0" }
+cranelift-entity = { path = "cranelift/entity", version = "0.104.0" }
+cranelift-native = { path = "cranelift/native", version = "0.104.0" }
+cranelift-module = { path = "cranelift/module", version = "0.104.0" }
+cranelift-interpreter = { path = "cranelift/interpreter", version = "0.104.0" }
+cranelift-reader = { path = "cranelift/reader", version = "0.104.0" }
 cranelift-filetests = { path = "cranelift/filetests" }
-cranelift-object = { path = "cranelift/object", version = "0.103.0" }
-cranelift-jit = { path = "cranelift/jit", version = "0.103.0" }
+cranelift-object = { path = "cranelift/object", version = "0.104.0" }
+cranelift-jit = { path = "cranelift/jit", version = "0.104.0" }
 cranelift-fuzzgen = { path = "cranelift/fuzzgen" }
-cranelift-bforest = { path = "cranelift/bforest", version = "0.103.0" }
-cranelift-control = { path = "cranelift/control", version = "0.103.0" }
-cranelift = { path = "cranelift/umbrella", version = "0.103.0" }
+cranelift-bforest = { path = "cranelift/bforest", version = "0.104.0" }
+cranelift-control = { path = "cranelift/control", version = "0.104.0" }
+cranelift = { path = "cranelift/umbrella", version = "0.104.0" }
 
-winch-codegen = { path = "winch/codegen", version = "=0.14.0" }
+winch-codegen = { path = "winch/codegen", version = "=0.15.0" }
 winch-filetests = { path = "winch/filetests" }
 winch-test-macros = { path = "winch/test-macros" }
 
diff --git a/RELEASES.md b/RELEASES.md
index 5c97e7bd4..8c60992f1 100644
--- a/RELEASES.md
+++ b/RELEASES.md
@@ -1,5 +1,15 @@
 --------------------------------------------------------------------------------
 
+## 17.0.0
+
+Unreleased.
+
+### Added
+
+### Changed
+
+--------------------------------------------------------------------------------
+
 ## 16.0.0
 
 Unreleased.
diff --git a/cranelift/Cargo.toml b/cranelift/Cargo.toml
index 06dcf4ef8..31dd54578 100644
--- a/cranelift/Cargo.toml
+++ b/cranelift/Cargo.toml
@@ -23,7 +23,7 @@ harness = false
 
 [dependencies]
 cfg-if = { workspace = true }
-cranelift-codegen = { workspace = true, features = ["disas", "trace-log"] }
+cranelift-codegen = { workspace = true, features = ["disas", "trace-log", "timing"] }
 cranelift-entity = { workspace = true }
 cranelift-interpreter = { workspace = true }
 cranelift-reader = { workspace = true }
diff --git a/cranelift/bforest/Cargo.toml b/cranelift/bforest/Cargo.toml
index 46103432e..09a5e0c2b 100644
--- a/cranelift/bforest/Cargo.toml
+++ b/cranelift/bforest/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 authors = ["The Cranelift Project Developers"]
 name = "cranelift-bforest"
-version = "0.103.0"
+version = "0.104.0"
 description = "A forest of B+-trees"
 license = "Apache-2.0 WITH LLVM-exception"
 documentation = "https://docs.rs/cranelift-bforest"
diff --git a/cranelift/codegen/Cargo.toml b/cranelift/codegen/Cargo.toml
index bde448f5a..f8f128e1a 100644
--- a/cranelift/codegen/Cargo.toml
+++ b/cranelift/codegen/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 authors = ["The Cranelift Project Developers"]
 name = "cranelift-codegen"
-version = "0.103.0"
+version = "0.104.0"
 description = "Low-level code generator library"
 license = "Apache-2.0 WITH LLVM-exception"
 documentation = "https://docs.rs/cranelift-codegen"
@@ -19,7 +19,7 @@ workspace = true
 anyhow = { workspace = true, optional = true }
 bumpalo = "3"
 capstone = { workspace = true, optional = true }
-cranelift-codegen-shared = { path = "./shared", version = "0.103.0" }
+cranelift-codegen-shared = { path = "./shared", version = "0.104.0" }
 cranelift-entity = { workspace = true }
 cranelift-bforest = { workspace = true }
 cranelift-control = { workspace = true }
@@ -44,11 +44,11 @@ criterion = { version = "0.5.0", features = ["html_reports"] }
 similar = "2.1.0"
 
 [build-dependencies]
-cranelift-codegen-meta = { path = "meta", version = "0.103.0" }
-cranelift-isle = { path = "../isle/isle", version = "=0.103.0" }
+cranelift-codegen-meta = { path = "meta", version = "0.104.0" }
+cranelift-isle = { path = "../isle/isle", version = "=0.104.0" }
 
 [features]
-default = ["std", "unwind", "host-arch"]
+default = ["std", "unwind", "host-arch", "timing"]
 
 # The "std" feature enables use of libstd. The "core" feature enables use
 # of some minimal std-like replacement libraries. At least one of these two
@@ -114,6 +114,11 @@ isle-errors = ["cranelift-isle/fancy-errors"]
 # inspection, rather than inside of target/.
 isle-in-source-tree = []
 
+# Enable tracking how long passes take in Cranelift.
+#
+# Enabled by default.
+timing = []
+
 [[bench]]
 name = "x64-evex-encoding"
 harness = false
diff --git a/cranelift/codegen/meta/Cargo.toml b/cranelift/codegen/meta/Cargo.toml
index d3121ee49..fab88a03a 100644
--- a/cranelift/codegen/meta/Cargo.toml
+++ b/cranelift/codegen/meta/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "cranelift-codegen-meta"
 authors = ["The Cranelift Project Developers"]
-version = "0.103.0"
+version = "0.104.0"
 description = "Metaprogram for cranelift-codegen code generator library"
 license = "Apache-2.0 WITH LLVM-exception"
 repository = "https://github.com/bytecodealliance/wasmtime"
@@ -15,4 +15,4 @@ workspace = true
 rustdoc-args = [ "--document-private-items" ]
 
 [dependencies]
-cranelift-codegen-shared = { path = "../shared", version = "0.103.0" }
+cranelift-codegen-shared = { path = "../shared", version = "0.104.0" }
diff --git a/cranelift/codegen/shared/Cargo.toml b/cranelift/codegen/shared/Cargo.toml
index 9187f8382..b5fd7caf7 100644
--- a/cranelift/codegen/shared/Cargo.toml
+++ b/cranelift/codegen/shared/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 authors = ["The Cranelift Project Developers"]
 name = "cranelift-codegen-shared"
-version = "0.103.0"
+version = "0.104.0"
 description = "For code shared between cranelift-codegen-meta and cranelift-codegen"
 license = "Apache-2.0 WITH LLVM-exception"
 repository = "https://github.com/bytecodealliance/wasmtime"
diff --git a/cranelift/codegen/src/timing.rs b/cranelift/codegen/src/timing.rs
index 573b82c0c..47f0eaec1 100644
--- a/cranelift/codegen/src/timing.rs
+++ b/cranelift/codegen/src/timing.rs
@@ -5,9 +5,9 @@
 use core::fmt;
 use std::any::Any;
 use std::boxed::Box;
-use std::cell::{Cell, RefCell};
+use std::cell::RefCell;
 use std::mem;
-use std::time::{Duration, Instant};
+use std::time::Duration;
 
 // Each pass that can be timed is predefined with the `define_passes!` macro. Each pass has a
 // snake_case name and a plain text description used when printing out the timing report.
@@ -130,22 +130,6 @@ fn start_pass(pass: Pass) -> Box<dyn Any> {
     PROFILER.with(|profiler| profiler.borrow().start_pass(pass))
 }
 
-/// A timing token is responsible for timing the currently running pass. Timing starts when it
-/// is created and ends when it is dropped.
-///
-/// Multiple passes can be active at the same time, but they must be started and stopped in a
-/// LIFO fashion.
-struct DefaultTimingToken {
-    /// Start time for this pass.
-    start: Instant,
-
-    // Pass being timed by this token.
-    pass: Pass,
-
-    // The previously active pass which will be restored when this token is dropped.
-    prev: Pass,
-}
-
 /// Accumulated timing information for a single pass.
 #[derive(Default, Copy, Clone)]
 struct PassTime {
@@ -215,42 +199,12 @@ impl fmt::Display for PassTimes {
 
 // Information about passes in a single thread.
 thread_local! {
-    static CURRENT_PASS: Cell<Pass> = const { Cell::new(Pass::None) };
     static PASS_TIME: RefCell<PassTimes> = RefCell::new(Default::default());
 }
 
 /// The default profiler. You can get the results using [`take_current`].
 pub struct DefaultProfiler;
 
-impl Profiler for DefaultProfiler {
-    fn start_pass(&self, pass: Pass) -> Box<dyn Any> {
-        let prev = CURRENT_PASS.with(|p| p.replace(pass));
-        log::debug!("timing: Starting {}, (during {})", pass, prev);
-        Box::new(DefaultTimingToken {
-            start: Instant::now(),
-            pass,
-            prev,
-        })
-    }
-}
-
-/// Dropping a timing token indicated the end of the pass.
-impl Drop for DefaultTimingToken {
-    fn drop(&mut self) {
-        let duration = self.start.elapsed();
-        log::debug!("timing: Ending {}: {}ms", self.pass, duration.as_millis());
-        let old_cur = CURRENT_PASS.with(|p| p.replace(self.prev));
-        debug_assert_eq!(self.pass, old_cur, "Timing tokens dropped out of order");
-        PASS_TIME.with(|rc| {
-            let mut table = rc.borrow_mut();
-            table.pass[self.pass.idx()].total += duration;
-            if let Some(parent) = table.pass.get_mut(self.prev.idx()) {
-                parent.child += duration;
-            }
-        })
-    }
-}
-
 /// Take the current accumulated pass timings and reset the timings for the current thread.
 ///
 /// Only applies when [`DefaultProfiler`] is used.
@@ -258,6 +212,78 @@ pub fn take_current() -> PassTimes {
     PASS_TIME.with(|rc| mem::take(&mut *rc.borrow_mut()))
 }
 
+#[cfg(feature = "timing")]
+mod enabled {
+    use super::{DefaultProfiler, Pass, Profiler, PASS_TIME};
+    use std::any::Any;
+    use std::boxed::Box;
+    use std::cell::Cell;
+    use std::time::Instant;
+
+    // Information about passes in a single thread.
+    thread_local! {
+        static CURRENT_PASS: Cell<Pass> = const { Cell::new(Pass::None) };
+    }
+
+    impl Profiler for DefaultProfiler {
+        fn start_pass(&self, pass: Pass) -> Box<dyn Any> {
+            let prev = CURRENT_PASS.with(|p| p.replace(pass));
+            log::debug!("timing: Starting {}, (during {})", pass, prev);
+            Box::new(DefaultTimingToken {
+                start: Instant::now(),
+                pass,
+                prev,
+            })
+        }
+    }
+
+    /// A timing token is responsible for timing the currently running pass. Timing starts when it
+    /// is created and ends when it is dropped.
+    ///
+    /// Multiple passes can be active at the same time, but they must be started and stopped in a
+    /// LIFO fashion.
+    struct DefaultTimingToken {
+        /// Start time for this pass.
+        start: Instant,
+
+        // Pass being timed by this token.
+        pass: Pass,
+
+        // The previously active pass which will be restored when this token is dropped.
+        prev: Pass,
+    }
+
+    /// Dropping a timing token indicated the end of the pass.
+    impl Drop for DefaultTimingToken {
+        fn drop(&mut self) {
+            let duration = self.start.elapsed();
+            log::debug!("timing: Ending {}: {}ms", self.pass, duration.as_millis());
+            let old_cur = CURRENT_PASS.with(|p| p.replace(self.prev));
+            debug_assert_eq!(self.pass, old_cur, "Timing tokens dropped out of order");
+            PASS_TIME.with(|rc| {
+                let mut table = rc.borrow_mut();
+                table.pass[self.pass.idx()].total += duration;
+                if let Some(parent) = table.pass.get_mut(self.prev.idx()) {
+                    parent.child += duration;
+                }
+            })
+        }
+    }
+}
+
+#[cfg(not(feature = "timing"))]
+mod disabled {
+    use super::{DefaultProfiler, Pass, Profiler};
+    use std::any::Any;
+    use std::boxed::Box;
+
+    impl Profiler for DefaultProfiler {
+        fn start_pass(&self, _pass: Pass) -> Box<dyn Any> {
+            Box::new(())
+        }
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
diff --git a/cranelift/control/Cargo.toml b/cranelift/control/Cargo.toml
index 2d7856581..28e82d3fd 100644
--- a/cranelift/control/Cargo.toml
+++ b/cranelift/control/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 authors = ["The Cranelift Project Developers"]
 name = "cranelift-control"
-version = "0.103.0"
+version = "0.104.0"
 description = "White-box fuzz testing framework"
 license = "Apache-2.0 WITH LLVM-exception"
 repository = "https://github.com/bytecodealliance/wasmtime"
diff --git a/cranelift/entity/Cargo.toml b/cranelift/entity/Cargo.toml
index ce6d90b5a..3e424c4c3 100644
--- a/cranelift/entity/Cargo.toml
+++ b/cranelift/entity/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 authors = ["The Cranelift Project Developers"]
 name = "cranelift-entity"
-version = "0.103.0"
+version = "0.104.0"
 description = "Data structures using entity references as mapping keys"
 license = "Apache-2.0 WITH LLVM-exception"
 documentation = "https://docs.rs/cranelift-entity"
diff --git a/cranelift/frontend/Cargo.toml b/cranelift/frontend/Cargo.toml
index 7c803de42..86a939068 100644
--- a/cranelift/frontend/Cargo.toml
+++ b/cranelift/frontend/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 authors = ["The Cranelift Project Developers"]
 name = "cranelift-frontend"
-version = "0.103.0"
+version = "0.104.0"
 description = "Cranelift IR builder helper"
 license = "Apache-2.0 WITH LLVM-exception"
 documentation = "https://docs.rs/cranelift-frontend"
diff --git a/cranelift/interpreter/Cargo.toml b/cranelift/interpreter/Cargo.toml
index 0661624f4..b4117bb03 100644
--- a/cranelift/interpreter/Cargo.toml
+++ b/cranelift/interpreter/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "cranelift-interpreter"
-version = "0.103.0"
+version = "0.104.0"
 authors = ["The Cranelift Project Developers"]
 description = "Interpret Cranelift IR"
 repository = "https://github.com/bytecodealliance/wasmtime"
diff --git a/cranelift/isle/isle/Cargo.toml b/cranelift/isle/isle/Cargo.toml
index 0176b5725..91c02d814 100644
--- a/cranelift/isle/isle/Cargo.toml
+++ b/cranelift/isle/isle/Cargo.toml
@@ -6,7 +6,7 @@ license = "Apache-2.0 WITH LLVM-exception"
 name = "cranelift-isle"
 readme = "../README.md"
 repository = "https://github.com/bytecodealliance/wasmtime/tree/main/cranelift/isle"
-version = "0.103.0"
+version = "0.104.0"
 
 [lints]
 workspace = true
diff --git a/cranelift/jit/Cargo.toml b/cranelift/jit/Cargo.toml
index d8e5cafca..b8a4c9d48 100644
--- a/cranelift/jit/Cargo.toml
+++ b/cranelift/jit/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "cranelift-jit"
-version = "0.103.0"
+version = "0.104.0"
 authors = ["The Cranelift Project Developers"]
 description = "A JIT library backed by Cranelift"
 repository = "https://github.com/bytecodealliance/wasmtime"
diff --git a/cranelift/module/Cargo.toml b/cranelift/module/Cargo.toml
index b063361ee..962c1a05b 100644
--- a/cranelift/module/Cargo.toml
+++ b/cranelift/module/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "cranelift-module"
-version = "0.103.0"
+version = "0.104.0"
 authors = ["The Cranelift Project Developers"]
 description = "Support for linking functions and data with Cranelift"
 repository = "https://github.com/bytecodealliance/wasmtime"
diff --git a/cranelift/native/Cargo.toml b/cranelift/native/Cargo.toml
index 7cb7d2f56..676faa761 100644
--- a/cranelift/native/Cargo.toml
+++ b/cranelift/native/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "cranelift-native"
-version = "0.103.0"
+version = "0.104.0"
 authors = ["The Cranelift Project Developers"]
 description = "Support for targeting the host with Cranelift"
 documentation = "https://docs.rs/cranelift-native"
diff --git a/cranelift/object/Cargo.toml b/cranelift/object/Cargo.toml
index 6f2027ad0..3a5a3f63e 100644
--- a/cranelift/object/Cargo.toml
+++ b/cranelift/object/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "cranelift-object"
-version = "0.103.0"
+version = "0.104.0"
 authors = ["The Cranelift Project Developers"]
 description = "Emit Cranelift output to native object files with `object`"
 repository = "https://github.com/bytecodealliance/wasmtime"
diff --git a/cranelift/reader/Cargo.toml b/cranelift/reader/Cargo.toml
index a559ed46d..2d43aa649 100644
--- a/cranelift/reader/Cargo.toml
+++ b/cranelift/reader/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 authors = ["The Cranelift Project Developers"]
 name = "cranelift-reader"
-version = "0.103.0"
+version = "0.104.0"
 description = "Cranelift textual IR reader"
 license = "Apache-2.0 WITH LLVM-exception"
 documentation = "https://docs.rs/cranelift-reader"
diff --git a/cranelift/serde/Cargo.toml b/cranelift/serde/Cargo.toml
index 79aa48126..4ff26b83c 100644
--- a/cranelift/serde/Cargo.toml
+++ b/cranelift/serde/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "cranelift-serde"
-version = "0.103.0"
+version = "0.104.0"
 authors = ["The Cranelift Project Developers"]
 description = "Serializer/Deserializer for Cranelift IR"
 repository = "https://github.com/bytecodealliance/wasmtime"
diff --git a/cranelift/umbrella/Cargo.toml b/cranelift/umbrella/Cargo.toml
index eabeafd6f..1d785ac19 100644
--- a/cranelift/umbrella/Cargo.toml
+++ b/cranelift/umbrella/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 authors = ["The Cranelift Project Developers"]
 name = "cranelift"
-version = "0.103.0"
+version = "0.104.0"
 description = "Umbrella for commonly-used cranelift crates"
 license = "Apache-2.0 WITH LLVM-exception"
 documentation = "https://docs.rs/cranelift"
diff --git a/cranelift/wasm/Cargo.toml b/cranelift/wasm/Cargo.toml
index 0ffe3bf91..4401b19f1 100644
--- a/cranelift/wasm/Cargo.toml
+++ b/cranelift/wasm/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "cranelift-wasm"
-version = "0.103.0"
+version = "0.104.0"
 authors = ["The Cranelift Project Developers"]
 description = "Translator from WebAssembly to Cranelift IR"
 documentation = "https://docs.rs/cranelift-wasm"
diff --git a/crates/c-api/include/wasmtime.h b/crates/c-api/include/wasmtime.h
index fb1e36f64..9dac550e4 100644
--- a/crates/c-api/include/wasmtime.h
+++ b/crates/c-api/include/wasmtime.h
@@ -205,11 +205,11 @@
 /**
  * \brief Wasmtime version string.
  */
-#define WASMTIME_VERSION "16.0.0"
+#define WASMTIME_VERSION "17.0.0"
 /**
  * \brief Wasmtime major version number.
  */
-#define WASMTIME_VERSION_MAJOR 16
+#define WASMTIME_VERSION_MAJOR 17
 /**
  * \brief Wasmtime minor version number.
  */
diff --git a/crates/cranelift/Cargo.toml b/crates/cranelift/Cargo.toml
index 733c9a377..354e29e16 100644
--- a/crates/cranelift/Cargo.toml
+++ b/crates/cranelift/Cargo.toml
@@ -18,7 +18,7 @@ anyhow = { workspace = true }
 log = { workspace = true }
 wasmtime-environ = { workspace = true }
 cranelift-wasm = { workspace = true }
-cranelift-codegen = { workspace = true, features = ["default"] }
+cranelift-codegen = { workspace = true, features = ["host-arch"] }
 cranelift-frontend = { workspace = true }
 cranelift-entity = { workspace = true }
 cranelift-native = { workspace = true }
diff --git a/crates/jit/Cargo.toml b/crates/jit/Cargo.toml
index 93ba8bde6..c8412d181 100644
--- a/crates/jit/Cargo.toml
+++ b/crates/jit/Cargo.toml
@@ -15,9 +15,7 @@ workspace = true
 
 [dependencies]
 wasmtime-environ = { workspace = true }
-wasmtime-jit-debug = { workspace = true, features = [
-  "perf_jitdump",
-], optional = true }
+wasmtime-jit-debug = { workspace = true, features = ["perf_jitdump"], optional = true }
 wasmtime-runtime = { workspace = true }
 target-lexicon = { workspace = true }
 anyhow = { workspace = true }
@@ -44,5 +42,6 @@ features = ["Win32_System_Diagnostics_Debug"]
 ittapi = { version = "0.4.0", optional = true }
 
 [features]
-profiling = ['dep:wasmtime-jit-debug', 'dep:ittapi']
+profiling = ['dep:ittapi', 'dep:wasmtime-jit-debug']
 demangle = ['dep:rustc-demangle', 'dep:cpp_demangle']
+debug-builtins = ['wasmtime-runtime/debug-builtins']
diff --git a/crates/jit/src/code_memory.rs b/crates/jit/src/code_memory.rs
index 517b5d1b9..2a08b5af2 100644
--- a/crates/jit/src/code_memory.rs
+++ b/crates/jit/src/code_memory.rs
@@ -1,7 +1,6 @@
 //! Memory management for executable code.
 
 use crate::subslice_range;
-use crate::unwind::UnwindRegistration;
 use anyhow::{anyhow, bail, Context, Result};
 use object::read::{File, Object, ObjectSection};
 use object::ObjectSymbol;
@@ -9,8 +8,7 @@ use std::mem::ManuallyDrop;
 use std::ops::Range;
 use wasmtime_environ::obj;
 use wasmtime_jit_icache_coherence as icache_coherence;
-use wasmtime_runtime::libcalls;
-use wasmtime_runtime::MmapVec;
+use wasmtime_runtime::{libcalls, MmapVec, UnwindRegistration};
 
 /// Management of executable memory within a `MmapVec`
 ///
diff --git a/crates/jit/src/instantiate.rs b/crates/jit/src/instantiate.rs
index a991c9a4c..7f73d7745 100644
--- a/crates/jit/src/instantiate.rs
+++ b/crates/jit/src/instantiate.rs
@@ -4,9 +4,8 @@
 //! steps.
 
 use crate::code_memory::CodeMemory;
-use crate::debug::create_gdbjit_image;
 use crate::profiling::ProfilingAgent;
-use anyhow::{bail, Context, Error, Result};
+use anyhow::{bail, Error, Result};
 use object::write::{Object, SectionId, StandardSegment, WritableBuffer};
 use object::SectionKind;
 use serde_derive::{Deserialize, Serialize};
@@ -19,9 +18,7 @@ use wasmtime_environ::{
     DefinedFuncIndex, FuncIndex, FunctionLoc, MemoryInitialization, Module, ModuleTranslation,
     PrimaryMap, SignatureIndex, StackMapInformation, Tunables, WasmFunctionInfo,
 };
-use wasmtime_runtime::{
-    CompiledModuleId, CompiledModuleIdAllocator, GdbJitImageRegistration, MmapVec,
-};
+use wasmtime_runtime::{CompiledModuleId, CompiledModuleIdAllocator, MmapVec};
 
 /// Secondary in-memory results of function compilation.
 #[derive(Serialize, Deserialize)]
@@ -426,7 +423,8 @@ pub struct CompiledModule {
     wasm_to_native_trampolines: Vec<(SignatureIndex, FunctionLoc)>,
     meta: Metadata,
     code_memory: Arc<CodeMemory>,
-    dbg_jit_registration: Option<GdbJitImageRegistration>,
+    #[cfg(feature = "debug-builtins")]
+    dbg_jit_registration: Option<wasmtime_runtime::GdbJitImageRegistration>,
     /// A unique ID used to register this module with the engine.
     unique_id: CompiledModuleId,
     func_names: Vec<FunctionName>,
@@ -459,6 +457,7 @@ impl CompiledModule {
             module: Arc::new(info.module),
             funcs: info.funcs,
             wasm_to_native_trampolines: info.wasm_to_native_trampolines,
+            #[cfg(feature = "debug-builtins")]
             dbg_jit_registration: None,
             code_memory,
             meta: info.meta,
@@ -471,11 +470,17 @@ impl CompiledModule {
     }
 
     fn register_debug_and_profiling(&mut self, profiler: &dyn ProfilingAgent) -> Result<()> {
+        #[cfg(feature = "debug-builtins")]
         if self.meta.native_debug_info_present {
+            use anyhow::Context;
+
             let text = self.text();
-            let bytes = create_gdbjit_image(self.mmap().to_vec(), (text.as_ptr(), text.len()))
-                .context("failed to create jit image for gdb")?;
-            let reg = GdbJitImageRegistration::register(bytes);
+            let bytes = crate::debug::create_gdbjit_image(
+                self.mmap().to_vec(),
+                (text.as_ptr(), text.len()),
+            )
+            .context("failed to create jit image for gdb")?;
+            let reg = wasmtime_runtime::GdbJitImageRegistration::register(bytes);
             self.dbg_jit_registration = Some(reg);
         }
         profiler.register_module(&self.code_memory, &|addr| {
@@ -669,6 +674,7 @@ impl CompiledModule {
     /// what filename and line number a wasm pc comes from.
     #[cfg(feature = "addr2line")]
     pub fn symbolize_context(&self) -> Result<Option<SymbolizeContext<'_>>> {
+        use anyhow::Context;
         use gimli::EndianSlice;
         if !self.meta.has_wasm_debuginfo {
             return Ok(None);
diff --git a/crates/jit/src/lib.rs b/crates/jit/src/lib.rs
index 438e44aac..7aac110b8 100644
--- a/crates/jit/src/lib.rs
+++ b/crates/jit/src/lib.rs
@@ -3,11 +3,11 @@
 #![deny(missing_docs)]
 
 mod code_memory;
+#[cfg(feature = "debug-builtins")]
 mod debug;
 mod demangling;
 mod instantiate;
 pub mod profiling;
-mod unwind;
 
 pub use crate::code_memory::CodeMemory;
 #[cfg(feature = "addr2line")]
diff --git a/crates/jit/src/unwind.rs b/crates/jit/src/unwind.rs
deleted file mode 100644
index 9ce8660f3..000000000
--- a/crates/jit/src/unwind.rs
+++ /dev/null
@@ -1,14 +0,0 @@
-cfg_if::cfg_if! {
-    if #[cfg(all(windows, any(target_arch = "x86_64", target_arch = "aarch64")))] {
-        mod winx64;
-        pub use self::winx64::*;
-    } else if #[cfg(miri)] {
-        mod miri;
-        pub use self::miri::*;
-    } else if #[cfg(unix)] {
-        mod systemv;
-        pub use self::systemv::*;
-    } else {
-        compile_error!("unsupported target platform for unwind");
-    }
-}
diff --git a/crates/runtime/Cargo.toml b/crates/runtime/Cargo.toml
index fa0074e74..3738335b4 100644
--- a/crates/runtime/Cargo.toml
+++ b/crates/runtime/Cargo.toml
@@ -18,21 +18,22 @@ wasmtime-wmemcheck = { workspace = true }
 wasmtime-asm-macros = { workspace = true }
 wasmtime-environ = { workspace = true }
 wasmtime-fiber = { workspace = true, optional = true }
-wasmtime-jit-debug = { workspace = true, features = ["gdb_jit_int"] }
+wasmtime-jit-debug = { workspace = true, features = ["gdb_jit_int"], optional = true }
 wasmtime-versioned-export-macros = { workspace = true }
 libc = { version = "0.2.112", default-features = false }
 log = { workspace = true }
 memoffset = "0.9.0"
 indexmap = { workspace = true }
 cfg-if = { workspace = true }
-rand = { version = "0.8.3", features = ['small_rng'] }
 anyhow = { workspace = true }
-memfd = "0.6.2"
 paste = "1.0.3"
 encoding_rs = { version = "0.8.31", optional = true }
 sptr = "0.3.2"
 wasm-encoder = { workspace = true }
 
+[target.'cfg(target_os = "linux")'.dependencies]
+memfd = "0.6.2"
+
 [target.'cfg(target_os = "macos")'.dependencies]
 mach = "0.3.2"
 
@@ -51,9 +52,13 @@ features = [
   "Win32_Security",
 ]
 
+[target.'cfg(target_arch = "s390x")'.dependencies]
+psm = "0.1.11"
+
 [dev-dependencies]
 once_cell = { workspace = true }
 proptest = "1.0.0"
+rand = { version = "0.8.3", features = ['small_rng'] }
 
 [build-dependencies]
 cc = "1.0"
@@ -64,4 +69,4 @@ async = ["wasmtime-fiber"]
 pooling-allocator = []
 component-model = ["wasmtime-environ/component-model", "dep:encoding_rs"]
 wmemcheck = []
-debug-builtins = []
+debug-builtins = ['wasmtime-jit-debug']
diff --git a/crates/runtime/build.rs b/crates/runtime/build.rs
index 571f0c4b2..d8ba14f6c 100644
--- a/crates/runtime/build.rs
+++ b/crates/runtime/build.rs
@@ -2,6 +2,15 @@ use std::env;
 use wasmtime_versioned_export_macros::versioned_suffix;
 
 fn main() {
+    println!("cargo:rerun-if-changed=build.rs");
+
+    // If this platform is neither unix nor windows then there's no default need
+    // for a C helper library since `helpers.c` is tailored for just these
+    // platforms currently.
+    if env::var("CARGO_CFG_UNIX").is_err() && env::var("CARGO_CFG_WINDOWS").is_err() {
+        return;
+    }
+
     let mut build = cc::Build::new();
     build.warnings(true);
     let arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
@@ -11,7 +20,7 @@ fn main() {
     build.define("VERSIONED_SUFFIX", Some(versioned_suffix!()));
     if arch == "s390x" {
         println!("cargo:rerun-if-changed=src/trampolines/s390x.S");
-        build.file("src/trampolines/s390x.S");
+        build.file("src/arch/s390x.S");
     }
     println!("cargo:rerun-if-changed=src/helpers.c");
     build.file("src/helpers.c");
diff --git a/crates/runtime/src/traphandlers/backtrace/aarch64.rs b/crates/runtime/src/arch/aarch64.rs
similarity index 53%
rename from crates/runtime/src/traphandlers/backtrace/aarch64.rs
rename to crates/runtime/src/arch/aarch64.rs
index 8c36291e3..e212b412d 100644
--- a/crates/runtime/src/traphandlers/backtrace/aarch64.rs
+++ b/crates/runtime/src/arch/aarch64.rs
@@ -1,3 +1,24 @@
+//! Arm64-specific definitions of architecture-specific functions in Wasmtime.
+
+/// AArch64 uses vector registered which here is used with a vector type.
+/// Note that the specific type shouldn't matter too much but the choice
+/// of using a vector is the significant part.
+pub type V128Abi = std::arch::aarch64::uint8x16_t;
+
+#[inline]
+#[allow(missing_docs)]
+pub fn get_stack_pointer() -> usize {
+    let stack_pointer: usize;
+    unsafe {
+        std::arch::asm!(
+            "mov {}, sp",
+            out(reg) stack_pointer,
+            options(nostack,nomem),
+        );
+    }
+    stack_pointer
+}
+
 // The aarch64 calling conventions save the return PC one i64 above the FP and
 // the previous FP is pointed to by the current FP:
 //
@@ -53,3 +74,47 @@ pub fn assert_fp_is_aligned(_fp: usize) {
     //
     // [0]: https://github.com/ARM-software/abi-aa/blob/2022Q1/aapcs64/aapcs64.rst#the-frame-pointer
 }
+
+#[rustfmt::skip]
+macro_rules! wasm_to_libcall_trampoline {
+    ($libcall:ident ; $libcall_impl:ident) => {
+        wasmtime_asm_macros::asm_func!(
+            wasmtime_versioned_export_macros::versioned_stringify_ident!($libcall),
+            "
+                .cfi_startproc
+                bti c
+
+                // Load the pointer to `VMRuntimeLimits` in `x9`.
+                ldur x9, [x0, #8]
+
+                // Store the last Wasm FP into the `last_wasm_exit_fp` in the limits.
+                stur fp, [x9, #24]
+
+                // Store the last Wasm PC into the `last_wasm_exit_pc` in the limits.
+                stur lr, [x9, #32]
+
+                // Tail call to the actual implementation of this libcall.
+                b {}
+
+                .cfi_endproc
+            ",
+            sym $libcall_impl
+        );
+    };
+}
+pub(crate) use wasm_to_libcall_trampoline;
+
+#[cfg(test)]
+mod wasm_to_libcall_trampoline_offsets_tests {
+    use wasmtime_environ::{Module, PtrSize, VMOffsets};
+
+    #[test]
+    fn test() {
+        let module = Module::new();
+        let offsets = VMOffsets::new(std::mem::size_of::<*mut u8>() as u8, &module);
+
+        assert_eq!(8, offsets.vmctx_runtime_limits());
+        assert_eq!(24, offsets.ptr.vmruntime_limits_last_wasm_exit_fp());
+        assert_eq!(32, offsets.ptr.vmruntime_limits_last_wasm_exit_pc());
+    }
+}
diff --git a/crates/runtime/src/arch/mod.rs b/crates/runtime/src/arch/mod.rs
new file mode 100644
index 000000000..a46f152db
--- /dev/null
+++ b/crates/runtime/src/arch/mod.rs
@@ -0,0 +1,32 @@
+//! Architecture-specific support required by Wasmtime.
+//!
+//! This crate houses any architecture-specific tidbits required when running
+//! Wasmtime. Each architecture has its own file in the `arch` folder which is
+//! referenced here.
+//!
+//! All architectures have the same interface when exposed to the rest of the
+//! crate.
+
+cfg_if::cfg_if! {
+    if #[cfg(target_arch = "x86_64")] {
+        mod x86_64;
+        pub use x86_64::*;
+    } else if #[cfg(target_arch = "aarch64")] {
+        mod aarch64;
+        pub use aarch64::*;
+    } else if #[cfg(target_arch = "s390x")] {
+        mod s390x;
+        pub use s390x::*;
+    } else if #[cfg(target_arch = "riscv64")] {
+        mod riscv64;
+        pub use riscv64::*;
+    } else {
+        compile_error!(
+            "Wasmtime is being compiled for an architecture \
+             that it does not support. If this architecture is \
+             one you would like to see supported you may file an \
+             issue on Wasmtime's issue tracker: \
+             https://github.com/bytecodealliance/wasmtime/issues/new\
+        ");
+    }
+}
diff --git a/crates/runtime/src/trampolines/riscv64.rs b/crates/runtime/src/arch/riscv64.rs
similarity index 53%
rename from crates/runtime/src/trampolines/riscv64.rs
rename to crates/runtime/src/arch/riscv64.rs
index b43afcbf4..7f600b43d 100644
--- a/crates/runtime/src/trampolines/riscv64.rs
+++ b/crates/runtime/src/arch/riscv64.rs
@@ -1,3 +1,45 @@
+//! Riscv64-specific definitions of architecture-specific functions in Wasmtime.
+
+/// RISC-V currently always passes all vector arguments indirectly in the
+/// ABI. Currently Rust has no stable means of representing this meaning
+/// that a 128-bit representation is chosen here but it can't be passed
+/// directly to WebAssembly, for example, and must instead be passed
+/// through an array-call trampoline.
+pub type V128Abi = u128;
+
+#[inline]
+#[allow(missing_docs)]
+pub fn get_stack_pointer() -> usize {
+    let stack_pointer: usize;
+    unsafe {
+        std::arch::asm!(
+            "mv {}, sp",
+            out(reg) stack_pointer,
+            options(nostack,nomem),
+        );
+    }
+    stack_pointer
+}
+
+pub unsafe fn get_next_older_pc_from_fp(fp: usize) -> usize {
+    *(fp as *mut usize).offset(1)
+}
+
+// And the current frame pointer points to the next older frame pointer.
+pub const NEXT_OLDER_FP_FROM_FP_OFFSET: usize = 0;
+
+pub fn reached_entry_sp(fp: usize, entry_sp: usize) -> bool {
+    fp >= entry_sp
+}
+
+pub fn assert_entry_sp_is_aligned(sp: usize) {
+    assert_eq!(sp % 16, 0, "stack should always be aligned to 16");
+}
+
+pub fn assert_fp_is_aligned(fp: usize) {
+    assert_eq!(fp % 16, 0, "stack should always be aligned to 16");
+}
+
 #[rustfmt::skip]
 macro_rules! wasm_to_libcall_trampoline {
     ($libcall:ident ; $libcall_impl:ident) => {
@@ -28,6 +70,7 @@ macro_rules! wasm_to_libcall_trampoline {
         );
     };
 }
+pub(crate) use wasm_to_libcall_trampoline;
 
 #[cfg(test)]
 mod wasm_to_libcall_trampoline_offsets_tests {
diff --git a/crates/runtime/src/trampolines/s390x.S b/crates/runtime/src/arch/s390x.S
similarity index 100%
rename from crates/runtime/src/trampolines/s390x.S
rename to crates/runtime/src/arch/s390x.S
diff --git a/crates/runtime/src/arch/s390x.rs b/crates/runtime/src/arch/s390x.rs
new file mode 100644
index 000000000..3ad6889b1
--- /dev/null
+++ b/crates/runtime/src/arch/s390x.rs
@@ -0,0 +1,61 @@
+//! s390x-specific definitions of architecture-specific functions in Wasmtime.
+
+/// Currently Rust has no stable means of representing vector registers
+/// so like RISC-V at this time this uses a bland 128-bit representation.
+pub type V128Abi = u128;
+
+#[inline]
+#[allow(missing_docs)]
+pub fn get_stack_pointer() -> usize {
+    psm::stack_pointer() as usize
+}
+
+pub unsafe fn get_next_older_pc_from_fp(fp: usize) -> usize {
+    // The next older PC can be found in register %r14 at function entry, which
+    // was saved into slot 14 of the register save area pointed to by "FP" (the
+    // backchain pointer).
+    *(fp as *mut usize).offset(14)
+}
+
+// The next older "FP" (backchain pointer) was saved in the slot pointed to
+// by the current "FP".
+pub const NEXT_OLDER_FP_FROM_FP_OFFSET: usize = 0;
+
+pub fn reached_entry_sp(fp: usize, entry_sp: usize) -> bool {
+    fp > entry_sp
+}
+
+pub fn assert_entry_sp_is_aligned(sp: usize) {
+    assert_eq!(sp % 8, 0, "stack should always be aligned to 8");
+}
+
+pub fn assert_fp_is_aligned(fp: usize) {
+    assert_eq!(fp % 8, 0, "stack should always be aligned to 8");
+}
+
+// The implementation for libcall trampolines is in the s390x.S
+// file.  We provide this dummy definition of wasm_to_libcall_trampoline
+// here to make libcalls.rs compile on s390x.  Note that this means we
+// have to duplicate the list of libcalls used in the assembler file.
+
+macro_rules! wasm_to_libcall_trampoline {
+    ($libcall:ident ; $libcall_impl:ident) => {};
+}
+pub(crate) use wasm_to_libcall_trampoline;
+
+// The wasm_to_host_trampoline implementation is in the s390x.S
+// file, but we still want to have this unit test here.
+#[cfg(test)]
+mod wasm_to_libcall_trampoline_offsets_tests {
+    use wasmtime_environ::{Module, PtrSize, VMOffsets};
+
+    #[test]
+    fn test() {
+        let module = Module::new();
+        let offsets = VMOffsets::new(std::mem::size_of::<*mut u8>() as u8, &module);
+
+        assert_eq!(8, offsets.vmctx_runtime_limits());
+        assert_eq!(24, offsets.ptr.vmruntime_limits_last_wasm_exit_fp());
+        assert_eq!(32, offsets.ptr.vmruntime_limits_last_wasm_exit_pc());
+    }
+}
diff --git a/crates/runtime/src/trampolines/x86_64.rs b/crates/runtime/src/arch/x86_64.rs
similarity index 51%
rename from crates/runtime/src/trampolines/x86_64.rs
rename to crates/runtime/src/arch/x86_64.rs
index d22bcaf1a..a179831f9 100644
--- a/crates/runtime/src/trampolines/x86_64.rs
+++ b/crates/runtime/src/arch/x86_64.rs
@@ -1,21 +1,62 @@
+//! x86_64-specific definitions of architecture-specific functions in Wasmtime.
+
+/// x86 vectors are represented with XMM registers which are represented
+/// with the `__m128i` type. This type is considered a vector type for
+/// ABI purposes which is implemented by Cranelift.
+pub type V128Abi = std::arch::x86_64::__m128i;
+
+#[inline]
+#[allow(missing_docs)]
+pub fn get_stack_pointer() -> usize {
+    let stack_pointer: usize;
+    unsafe {
+        std::arch::asm!(
+            "mov {}, rsp",
+            out(reg) stack_pointer,
+            options(nostack,nomem),
+        );
+    }
+    stack_pointer
+}
+
+pub unsafe fn get_next_older_pc_from_fp(fp: usize) -> usize {
+    // The calling convention always pushes the return pointer (aka the PC of
+    // the next older frame) just before this frame.
+    *(fp as *mut usize).offset(1)
+}
+
+// And the current frame pointer points to the next older frame pointer.
+pub const NEXT_OLDER_FP_FROM_FP_OFFSET: usize = 0;
+
+pub fn reached_entry_sp(fp: usize, entry_sp: usize) -> bool {
+    fp >= entry_sp
+}
+
+pub fn assert_entry_sp_is_aligned(sp: usize) {
+    assert_eq!(sp % 16, 0, "stack should always be aligned to 16");
+}
+
+pub fn assert_fp_is_aligned(fp: usize) {
+    assert_eq!(fp % 16, 0, "stack should always be aligned to 16");
+}
+
 // Helper macros for getting the first and second arguments according to the
 // system calling convention, as well as some callee-saved scratch registers we
 // can safely use in the trampolines.
 cfg_if::cfg_if! {
     if #[cfg(windows)] {
         macro_rules! callee_vmctx { () => ("rcx") }
-        #[allow(unused)] macro_rules! caller_vmctx { () => ("rdx") }
         macro_rules! scratch0 { () => ("r10") }
         macro_rules! scratch1 { () => ("r11") }
     } else if #[cfg(unix)] {
         macro_rules! callee_vmctx { () => ("rdi") }
-        #[allow(unused)] macro_rules! caller_vmctx { () => ("rsi") }
         macro_rules! scratch0 { () => ("r10") }
         macro_rules! scratch1 { () => ("r11") }
     } else {
-        compile_error!("platform not supported");
+        compile_error!("default calling convention for this platform is not known");
     }
 }
+pub(crate) use {callee_vmctx, scratch0, scratch1};
 
 #[rustfmt::skip]
 macro_rules! wasm_to_libcall_trampoline {
@@ -27,15 +68,15 @@ macro_rules! wasm_to_libcall_trampoline {
                    .cfi_startproc simple
                    .cfi_def_cfa_offset 0
 
-                    // Load the pointer to `VMRuntimeLimits` in `", scratch0!(), "`.
-                    mov ", scratch0!(), ", 8[", callee_vmctx!(), "]
+                    // Load the pointer to `VMRuntimeLimits` in `scratch0!()`.
+                    mov ", crate::arch::scratch0!(), ", 8[", crate::arch::callee_vmctx!(), "]
 
                     // Store the last Wasm FP into the `last_wasm_exit_fp` in the limits.
-                    mov 24[", scratch0!(), "], rbp
+                    mov 24[", crate::arch::scratch0!(), "], rbp
 
                     // Store the last Wasm PC into the `last_wasm_exit_pc` in the limits.
-                    mov ", scratch1!(), ", [rsp]
-                    mov 32[", scratch0!(), "], ", scratch1!(), "
+                    mov ", crate::arch::scratch1!(), ", [rsp]
+                    mov 32[", crate::arch::scratch0!(), "], ", crate::arch::scratch1!(), "
 
                     // Tail call to the actual implementation of this libcall.
                     jmp {}
@@ -47,6 +88,7 @@ macro_rules! wasm_to_libcall_trampoline {
         );
     };
 }
+pub(crate) use wasm_to_libcall_trampoline;
 
 #[cfg(test)]
 mod wasm_to_libcall_trampoline_offsets_tests {
diff --git a/crates/runtime/src/cow.rs b/crates/runtime/src/cow.rs
index ccd34b828..75738be7f 100644
--- a/crates/runtime/src/cow.rs
+++ b/crates/runtime/src/cow.rs
@@ -1,12 +1,10 @@
 //! Copy-on-write initialization support: creation of backing images for
 //! modules, and logic to support mapping these backing images into memory.
 
-#![cfg_attr(any(not(unix), miri), allow(unused_imports, unused_variables))]
-
+use crate::sys::vm::{self, MemoryImageSource};
 use crate::{MmapVec, SendSyncPtr};
 use anyhow::Result;
-use libc::c_void;
-use std::fs::File;
+use std::ffi::c_void;
 use std::ptr::NonNull;
 use std::sync::Arc;
 use std::{convert::TryFrom, ops::Range};
@@ -32,12 +30,12 @@ impl ModuleMemoryImages {
 /// One backing image for one memory.
 #[derive(Debug, PartialEq)]
 pub struct MemoryImage {
-    /// The file descriptor source of this image.
+    /// The platform-specific source of this image.
     ///
-    /// This might be an mmaped `*.cwasm` file or on Linux it could also be a
-    /// `Memfd` as an anonymous file in memory. In either case this is used as
-    /// the backing-source for the CoW image.
-    fd: FdSource,
+    /// This might be a mapped `*.cwasm` file or on Unix it could also be a
+    /// `Memfd` as an anonymous file in memory on Linux. In either case this is
+    /// used as the backing-source for the CoW image.
+    source: MemoryImageSource,
 
     /// Length of image, in bytes.
     ///
@@ -47,13 +45,13 @@ pub struct MemoryImage {
     /// Must be a multiple of the system page size.
     len: usize,
 
-    /// Image starts this many bytes into `fd` source.
+    /// Image starts this many bytes into `source`.
     ///
-    /// This is 0 for anonymous-backed memfd files and is the offset of the data
-    /// section in a `*.cwasm` file for `*.cwasm`-backed images.
+    /// This is 0 for anonymous-backed memfd files and is the offset of the
+    /// data section in a `*.cwasm` file for `*.cwasm`-backed images.
     ///
     /// Must be a multiple of the system page size.
-    fd_offset: u64,
+    source_offset: u64,
 
     /// Image starts this many bytes into heap space.
     ///
@@ -61,39 +59,6 @@ pub struct MemoryImage {
     linear_memory_offset: usize,
 }
 
-#[derive(Debug)]
-enum FdSource {
-    #[cfg(all(unix, not(miri)))]
-    Mmap(Arc<File>),
-    #[cfg(all(target_os = "linux", not(miri)))]
-    Memfd(memfd::Memfd),
-}
-
-impl FdSource {
-    #[cfg(all(unix, not(miri)))]
-    fn as_file(&self) -> &File {
-        match self {
-            FdSource::Mmap(ref file) => file,
-            #[cfg(target_os = "linux")]
-            FdSource::Memfd(ref memfd) => memfd.as_file(),
-        }
-    }
-}
-
-impl PartialEq for FdSource {
-    fn eq(&self, other: &FdSource) -> bool {
-        cfg_if::cfg_if! {
-            if #[cfg(all(unix, not(miri)))] {
-                use rustix::fd::AsRawFd;
-                self.as_file().as_raw_fd() == other.as_file().as_raw_fd()
-            } else {
-                let _ = other;
-                match *self {}
-            }
-        }
-    }
-}
-
 impl MemoryImage {
     fn new(
         page_size: u32,
@@ -124,7 +89,6 @@ impl MemoryImage {
         // files, but for now this is still a Linux-specific region of Wasmtime.
         // Some work will be needed to get this file compiling for macOS and
         // Windows.
-        #[cfg(not(any(windows, miri)))]
         if let Some(mmap) = mmap {
             let start = mmap.as_ptr() as usize;
             let end = start + mmap.len();
@@ -137,126 +101,45 @@ impl MemoryImage {
             assert_eq!((mmap.original_offset() as u32) % page_size, 0);
 
             if let Some(file) = mmap.original_file() {
-                return Ok(Some(MemoryImage {
-                    fd: FdSource::Mmap(file.clone()),
-                    fd_offset: u64::try_from(mmap.original_offset() + (data_start - start))
-                        .unwrap(),
-                    linear_memory_offset,
-                    len,
-                }));
+                if let Some(source) = MemoryImageSource::from_file(file) {
+                    return Ok(Some(MemoryImage {
+                        source,
+                        source_offset: u64::try_from(mmap.original_offset() + (data_start - start))
+                            .unwrap(),
+                        linear_memory_offset,
+                        len,
+                    }));
+                }
             }
         }
 
         // If `mmap` doesn't come from a file then platform-specific mechanisms
         // may be used to place the data in a form that's amenable to an mmap.
-
-        cfg_if::cfg_if! {
-            if #[cfg(all(target_os = "linux", not(miri)))] {
-                // On Linux `memfd_create` is used to create an anonymous
-                // in-memory file to represent the heap image. This anonymous
-                // file is then used as the basis for further mmaps.
-
-                use std::io::Write;
-
-                let memfd = match create_memfd()? {
-                    Some(memfd) => memfd,
-                    None => return Ok(None),
-                };
-                memfd.as_file().write_all(data)?;
-
-                // Seal the memfd's data and length.
-                //
-                // This is a defense-in-depth security mitigation. The
-                // memfd will serve as the starting point for the heap of
-                // every instance of this module. If anything were to
-                // write to this, it could affect every execution. The
-                // memfd object itself is owned by the machinery here and
-                // not exposed elsewhere, but it is still an ambient open
-                // file descriptor at the syscall level, so some other
-                // vulnerability that allowed writes to arbitrary fds
-                // could modify it. Or we could have some issue with the
-                // way that we map it into each instance. To be
-                // extra-super-sure that it never changes, and because
-                // this costs very little, we use the kernel's "seal" API
-                // to make the memfd image permanently read-only.
-                memfd.add_seals(&[
-                    memfd::FileSeal::SealGrow,
-                    memfd::FileSeal::SealShrink,
-                    memfd::FileSeal::SealWrite,
-                    memfd::FileSeal::SealSeal,
-                ])?;
-
-                Ok(Some(MemoryImage {
-                    fd: FdSource::Memfd(memfd),
-                    fd_offset: 0,
-                    linear_memory_offset,
-                    len,
-                }))
-            } else {
-                // Other platforms don't have an easily available way of
-                // representing the heap image as an mmap-source right now. We
-                // could theoretically create a file and immediately unlink it
-                // but that means that data may likely be preserved to disk
-                // which isn't what we want here.
-                Ok(None)
-            }
+        if let Some(source) = MemoryImageSource::from_data(data)? {
+            return Ok(Some(MemoryImage {
+                source,
+                source_offset: 0,
+                linear_memory_offset,
+                len,
+            }));
         }
+
+        Ok(None)
     }
 
     unsafe fn map_at(&self, base: *mut u8) -> Result<()> {
-        cfg_if::cfg_if! {
-            if #[cfg(all(unix, not(miri)))] {
-                let ptr = rustix::mm::mmap(
-                    base.add(self.linear_memory_offset).cast(),
-                    self.len,
-                    rustix::mm::ProtFlags::READ | rustix::mm::ProtFlags::WRITE,
-                    rustix::mm::MapFlags::PRIVATE | rustix::mm::MapFlags::FIXED,
-                    self.fd.as_file(),
-                    self.fd_offset,
-                )?;
-                assert_eq!(ptr, base.add(self.linear_memory_offset).cast());
-                Ok(())
-            } else {
-                match self.fd {}
-            }
-        }
+        self.source.map_at(
+            base.add(self.linear_memory_offset),
+            self.len,
+            self.source_offset,
+        )?;
+        Ok(())
     }
 
     unsafe fn remap_as_zeros_at(&self, base: *mut u8) -> Result<()> {
-        cfg_if::cfg_if! {
-            if #[cfg(unix)] {
-                let ptr = rustix::mm::mmap_anonymous(
-                    base.add(self.linear_memory_offset).cast(),
-                    self.len,
-                    rustix::mm::ProtFlags::READ | rustix::mm::ProtFlags::WRITE,
-                    rustix::mm::MapFlags::PRIVATE | rustix::mm::MapFlags::FIXED,
-                )?;
-                assert_eq!(ptr.cast(), base.add(self.linear_memory_offset));
-                Ok(())
-            } else {
-                match self.fd {}
-            }
-        }
-    }
-}
-
-#[cfg(all(target_os = "linux", not(miri)))]
-fn create_memfd() -> Result<Option<memfd::Memfd>> {
-    use std::io::ErrorKind;
-
-    // Create the memfd. It needs a name, but the
-    // documentation for `memfd_create()` says that names can
-    // be duplicated with no issues.
-    match memfd::MemfdOptions::new()
-        .allow_sealing(true)
-        .create("wasm-memory-image")
-    {
-        Ok(memfd) => Ok(Some(memfd)),
-        // If this kernel is old enough to not support memfd then attempt to
-        // gracefully handle that and fall back to skipping the memfd
-        // optimization.
-        Err(memfd::Error::Create(err)) if err.kind() == ErrorKind::Unsupported => Ok(None),
-        Err(e) => Err(e.into()),
+        self.source
+            .remap_as_zeros_at(base.add(self.linear_memory_offset), self.len)?;
+        Ok(())
     }
 }
 
@@ -593,7 +476,7 @@ impl MemoryImageSlot {
 
     #[allow(dead_code)] // ignore warnings as this is only used in some cfgs
     unsafe fn reset_all_memory_contents(&mut self, keep_resident: usize) -> Result<()> {
-        if !cfg!(target_os = "linux") || cfg!(miri) {
+        if !vm::supports_madvise_dontneed() {
             // If we're not on Linux then there's no generic platform way to
             // reset memory back to its original state, so instead reset memory
             // back to entirely zeros with an anonymous backing.
@@ -703,18 +586,8 @@ impl MemoryImageSlot {
         if len == 0 {
             return Ok(());
         }
-        cfg_if::cfg_if! {
-            if #[cfg(target_os = "linux")] {
-                rustix::mm::madvise(
-                    self.base.as_ptr().add(base).cast(),
-                    len,
-                    rustix::mm::Advice::LinuxDontNeed,
-                )?;
-                Ok(())
-            } else {
-                unreachable!();
-            }
-        }
+        vm::madvise_dontneed(self.base.as_ptr().add(base), len)?;
+        Ok(())
     }
 
     fn set_protection(&self, range: Range<usize>, readwrite: bool) -> Result<()> {
@@ -726,30 +599,10 @@ impl MemoryImageSlot {
 
         unsafe {
             let start = self.base.as_ptr().add(range.start);
-            cfg_if::cfg_if! {
-                if #[cfg(miri)] {
-                    if readwrite {
-                        std::ptr::write_bytes(start, 0u8, range.len());
-                    }
-                } else if #[cfg(unix)] {
-                    let flags = if readwrite {
-                        rustix::mm::MprotectFlags::READ | rustix::mm::MprotectFlags::WRITE
-                    } else {
-                        rustix::mm::MprotectFlags::empty()
-                    };
-                    rustix::mm::mprotect(start.cast(), range.len(), flags)?;
-                } else {
-                    use windows_sys::Win32::System::Memory::*;
-
-                    let failure = if readwrite {
-                        VirtualAlloc(start.cast(), range.len(), MEM_COMMIT, PAGE_READWRITE).is_null()
-                    } else {
-                        VirtualFree(start.cast(), range.len(), MEM_DECOMMIT) == 0
-                    };
-                    if failure {
-                        return Err(std::io::Error::last_os_error().into());
-                    }
-                }
+            if readwrite {
+                vm::expose_existing_mapping(start, range.len())?;
+            } else {
+                vm::hide_existing_mapping(start, range.len())?;
             }
         }
 
@@ -775,24 +628,7 @@ impl MemoryImageSlot {
         }
 
         unsafe {
-            cfg_if::cfg_if! {
-                if #[cfg(miri)] {
-                    std::ptr::write_bytes(self.base.as_ptr(), 0, self.static_size);
-                } else if #[cfg(unix)] {
-                    let ptr = rustix::mm::mmap_anonymous(
-                        self.base.as_ptr().cast(),
-                        self.static_size,
-                        rustix::mm::ProtFlags::empty(),
-                        rustix::mm::MapFlags::PRIVATE | rustix::mm::MapFlags::FIXED,
-                    )?;
-                    assert_eq!(ptr, self.base.as_ptr().cast());
-                } else {
-                    use windows_sys::Win32::System::Memory::*;
-                    if VirtualFree(self.base.as_ptr().cast(), self.static_size, MEM_DECOMMIT) == 0 {
-                        return Err(std::io::Error::last_os_error().into());
-                    }
-                }
-            }
+            vm::erase_existing_mapping(self.base.as_ptr(), self.static_size)?;
         }
 
         self.image = None;
@@ -845,31 +681,23 @@ impl Drop for MemoryImageSlot {
 mod test {
     use std::sync::Arc;
 
-    use super::{FdSource, MemoryImage, MemoryImageSlot, MemoryPlan, MemoryStyle};
+    use super::{MemoryImage, MemoryImageSlot, MemoryImageSource, MemoryPlan, MemoryStyle};
     use crate::mmap::Mmap;
     use anyhow::Result;
-    use std::io::Write;
     use wasmtime_environ::Memory;
 
-    fn create_memfd() -> Result<memfd::Memfd> {
-        Ok(super::create_memfd()?.expect("kernel doesn't support memfd"))
-    }
-
     fn create_memfd_with_data(offset: usize, data: &[u8]) -> Result<MemoryImage> {
         // Offset must be page-aligned.
         let page_size = crate::page_size();
         assert_eq!(offset & (page_size - 1), 0);
-        let memfd = create_memfd()?;
-        memfd.as_file().write_all(data)?;
 
         // The image length is rounded up to the nearest page size
         let image_len = (data.len() + page_size - 1) & !(page_size - 1);
-        memfd.as_file().set_len(image_len as u64)?;
 
         Ok(MemoryImage {
-            fd: FdSource::Memfd(memfd),
+            source: MemoryImageSource::from_data(data)?.unwrap(),
             len: image_len,
-            fd_offset: 0,
+            source_offset: 0,
             linear_memory_offset: offset,
         })
     }
diff --git a/crates/runtime/src/instance/allocator/pooling.rs b/crates/runtime/src/instance/allocator/pooling.rs
index e0584726f..9fa5d83b3 100644
--- a/crates/runtime/src/instance/allocator/pooling.rs
+++ b/crates/runtime/src/instance/allocator/pooling.rs
@@ -65,16 +65,6 @@ mod table_pool;
 #[cfg(all(feature = "async", unix, not(miri)))]
 mod stack_pool;
 
-cfg_if::cfg_if! {
-    if #[cfg(windows)] {
-        mod windows;
-        use windows as imp;
-    } else {
-        mod unix;
-        use unix as imp;
-    }
-}
-
 use super::{
     InstanceAllocationRequest, InstanceAllocatorImpl, MemoryAllocationIndex, TableAllocationIndex,
 };
diff --git a/crates/runtime/src/instance/allocator/pooling/memory_pool.rs b/crates/runtime/src/instance/allocator/pooling/memory_pool.rs
index 3bf096927..67aefe106 100644
--- a/crates/runtime/src/instance/allocator/pooling/memory_pool.rs
+++ b/crates/runtime/src/instance/allocator/pooling/memory_pool.rs
@@ -68,7 +68,7 @@ use crate::{
     MpkEnabled, PoolingInstanceAllocatorConfig,
 };
 use anyhow::{anyhow, bail, Context, Result};
-use libc::c_void;
+use std::ffi::c_void;
 use std::sync::atomic::{AtomicUsize, Ordering};
 use std::sync::Mutex;
 use wasmtime_environ::{
diff --git a/crates/runtime/src/instance/allocator/pooling/stack_pool.rs b/crates/runtime/src/instance/allocator/pooling/stack_pool.rs
index edb169f34..eb1581d5f 100644
--- a/crates/runtime/src/instance/allocator/pooling/stack_pool.rs
+++ b/crates/runtime/src/instance/allocator/pooling/stack_pool.rs
@@ -1,8 +1,8 @@
 use super::{
-    imp::{commit_stack_pages, reset_stack_pages_to_zero},
     index_allocator::{SimpleIndexAllocator, SlotId},
     round_up_to_pow2,
 };
+use crate::sys::vm::{commit_stack_pages, reset_stack_pages_to_zero};
 use crate::{Mmap, PoolingInstanceAllocatorConfig};
 use anyhow::{anyhow, bail, Context, Result};
 
@@ -166,10 +166,10 @@ impl StackPool {
                 0,
                 size_to_memset,
             );
-        }
 
-        // Use the system to reset remaining stack pages to zero.
-        reset_stack_pages_to_zero(bottom as _, size - size_to_memset).unwrap();
+            // Use the system to reset remaining stack pages to zero.
+            reset_stack_pages_to_zero(bottom as _, size - size_to_memset).unwrap();
+        }
     }
 }
 
diff --git a/crates/runtime/src/instance/allocator/pooling/table_pool.rs b/crates/runtime/src/instance/allocator/pooling/table_pool.rs
index 558cb548e..45ce3c3b9 100644
--- a/crates/runtime/src/instance/allocator/pooling/table_pool.rs
+++ b/crates/runtime/src/instance/allocator/pooling/table_pool.rs
@@ -1,8 +1,8 @@
 use super::{
-    imp::{commit_table_pages, decommit_table_pages},
     index_allocator::{SimpleIndexAllocator, SlotId},
     round_up_to_pow2, TableAllocationIndex,
 };
+use crate::sys::vm::{commit_table_pages, decommit_table_pages};
 use crate::{InstanceAllocationRequest, Mmap, PoolingInstanceAllocatorConfig, SendSyncPtr, Table};
 use anyhow::{anyhow, bail, Context, Result};
 use std::mem;
@@ -129,10 +129,12 @@ impl TablePool {
         match (|| {
             let base = self.get(allocation_index);
 
-            commit_table_pages(
-                base as *mut u8,
-                self.table_elements * mem::size_of::<*mut u8>(),
-            )?;
+            unsafe {
+                commit_table_pages(
+                    base as *mut u8,
+                    self.table_elements * mem::size_of::<*mut u8>(),
+                )?;
+            }
 
             let ptr = NonNull::new(std::ptr::slice_from_raw_parts_mut(
                 base.cast(),
diff --git a/crates/runtime/src/instance/allocator/pooling/unix.rs b/crates/runtime/src/instance/allocator/pooling/unix.rs
deleted file mode 100644
index 00259f4b0..000000000
--- a/crates/runtime/src/instance/allocator/pooling/unix.rs
+++ /dev/null
@@ -1,56 +0,0 @@
-use anyhow::Result;
-
-fn decommit(addr: *mut u8, len: usize) -> Result<()> {
-    if len == 0 {
-        return Ok(());
-    }
-
-    unsafe {
-        cfg_if::cfg_if! {
-            if #[cfg(miri)] {
-                std::ptr::write_bytes(addr, 0, len);
-            } else if #[cfg(target_os = "linux")] {
-                use rustix::mm::{madvise, Advice};
-
-                // On Linux, this is enough to cause the kernel to initialize
-                // the pages to 0 on next access
-                madvise(addr as _, len, Advice::LinuxDontNeed)?;
-            } else {
-                use rustix::mm::{mmap_anonymous, ProtFlags, MapFlags};
-
-                // By creating a new mapping at the same location, this will
-                // discard the mapping for the pages in the given range.
-                // The new mapping will be to the CoW zero page, so this
-                // effectively zeroes the pages.
-                mmap_anonymous(
-                    addr as _,
-                    len,
-                    ProtFlags::READ | ProtFlags::WRITE,
-                    MapFlags::PRIVATE | MapFlags::FIXED,
-                )?;
-            }
-        }
-    }
-
-    Ok(())
-}
-
-pub fn commit_table_pages(_addr: *mut u8, _len: usize) -> Result<()> {
-    // A no-op as table pages remain READ|WRITE
-    Ok(())
-}
-
-pub fn decommit_table_pages(addr: *mut u8, len: usize) -> Result<()> {
-    decommit(addr, len)
-}
-
-#[cfg(all(feature = "async", not(miri)))]
-pub fn commit_stack_pages(_addr: *mut u8, _len: usize) -> Result<()> {
-    // A no-op as stack pages remain READ|WRITE
-    Ok(())
-}
-
-#[cfg(all(feature = "async", not(miri)))]
-pub fn reset_stack_pages_to_zero(addr: *mut u8, len: usize) -> Result<()> {
-    decommit(addr, len)
-}
diff --git a/crates/runtime/src/instance/allocator/pooling/windows.rs b/crates/runtime/src/instance/allocator/pooling/windows.rs
deleted file mode 100644
index 5e9d0c51e..000000000
--- a/crates/runtime/src/instance/allocator/pooling/windows.rs
+++ /dev/null
@@ -1,38 +0,0 @@
-use anyhow::{bail, Result};
-use windows_sys::Win32::System::Memory::*;
-
-pub fn commit(addr: *mut u8, len: usize) -> Result<()> {
-    if len == 0 {
-        return Ok(());
-    }
-
-    // Memory needs to be committed, so don't use the `region` crate
-    if unsafe { VirtualAlloc(addr as _, len, MEM_COMMIT, PAGE_READWRITE).is_null() } {
-        bail!("failed to commit memory as read/write");
-    }
-
-    Ok(())
-}
-
-pub fn decommit(addr: *mut u8, len: usize) -> Result<()> {
-    if len == 0 {
-        return Ok(());
-    }
-
-    if unsafe { VirtualFree(addr as _, len, MEM_DECOMMIT) } == 0 {
-        bail!(
-            "failed to decommit memory pages: {}",
-            std::io::Error::last_os_error()
-        );
-    }
-
-    Ok(())
-}
-
-pub fn commit_table_pages(addr: *mut u8, len: usize) -> Result<()> {
-    commit(addr, len)
-}
-
-pub fn decommit_table_pages(addr: *mut u8, len: usize) -> Result<()> {
-    decommit(addr, len)
-}
diff --git a/crates/runtime/src/lib.rs b/crates/runtime/src/lib.rs
index d3cf7565a..de6574a68 100644
--- a/crates/runtime/src/lib.rs
+++ b/crates/runtime/src/lib.rs
@@ -10,9 +10,7 @@ use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
 use std::sync::Arc;
 use wasmtime_environ::{DefinedFuncIndex, DefinedMemoryIndex, HostPtr, VMOffsets};
 
-#[macro_use]
-mod trampolines;
-
+mod arch;
 #[cfg(feature = "component-model")]
 pub mod component;
 mod export;
@@ -25,6 +23,7 @@ mod mmap_vec;
 mod parking_spot;
 mod send_sync_ptr;
 mod store_box;
+mod sys;
 mod table;
 mod traphandlers;
 mod vmcontext;
@@ -34,8 +33,10 @@ pub mod debug_builtins;
 pub mod libcalls;
 pub mod mpk;
 
+#[cfg(feature = "debug-builtins")]
 pub use wasmtime_jit_debug::gdb_jit_int::GdbJitImageRegistration;
 
+pub use crate::arch::{get_stack_pointer, V128Abi};
 pub use crate::export::*;
 pub use crate::externref::*;
 pub use crate::imports::Imports;
@@ -54,6 +55,7 @@ pub use crate::mmap::Mmap;
 pub use crate::mmap_vec::MmapVec;
 pub use crate::mpk::MpkEnabled;
 pub use crate::store_box::*;
+pub use crate::sys::unwind::UnwindRegistration;
 pub use crate::table::{Table, TableElement};
 pub use crate::traphandlers::*;
 pub use crate::vmcontext::{
@@ -219,30 +221,13 @@ pub fn page_size() -> usize {
 
     return match PAGE_SIZE.load(Ordering::Relaxed) {
         0 => {
-            let size = get_page_size();
+            let size = sys::vm::get_page_size();
             assert!(size != 0);
             PAGE_SIZE.store(size, Ordering::Relaxed);
             size
         }
         n => n,
     };
-
-    #[cfg(windows)]
-    fn get_page_size() -> usize {
-        use std::mem::MaybeUninit;
-        use windows_sys::Win32::System::SystemInformation::*;
-
-        unsafe {
-            let mut info = MaybeUninit::uninit();
-            GetSystemInfo(info.as_mut_ptr());
-            info.assume_init_ref().dwPageSize as usize
-        }
-    }
-
-    #[cfg(unix)]
-    fn get_page_size() -> usize {
-        unsafe { libc::sysconf(libc::_SC_PAGESIZE).try_into().unwrap() }
-    }
 }
 
 /// Result of [`Memory::atomic_wait32`] and [`Memory::atomic_wait64`]
diff --git a/crates/runtime/src/libcalls.rs b/crates/runtime/src/libcalls.rs
index b43c11b33..539a1b3f5 100644
--- a/crates/runtime/src/libcalls.rs
+++ b/crates/runtime/src/libcalls.rs
@@ -80,6 +80,7 @@ use wasmtime_wmemcheck::AccessError::{
 /// now to ensure that the fp/sp on exit are recorded for backtraces to work
 /// properly.
 pub mod trampolines {
+    use crate::arch::wasm_to_libcall_trampoline;
     use crate::{Instance, TrapReason, VMContext};
 
     macro_rules! libcall {
diff --git a/crates/runtime/src/mmap.rs b/crates/runtime/src/mmap.rs
index 341cd4dee..4275cd68f 100644
--- a/crates/runtime/src/mmap.rs
+++ b/crates/runtime/src/mmap.rs
@@ -1,30 +1,18 @@
 //! Low-level abstraction for allocating and managing zero-filled pages
 //! of memory.
 
+use crate::sys::mmap;
 use anyhow::{Context, Result};
 use std::fs::File;
 use std::ops::Range;
 use std::path::Path;
 use std::sync::Arc;
 
-cfg_if::cfg_if! {
-    if #[cfg(windows)] {
-        mod windows;
-        use windows as sys;
-    } else if #[cfg(miri)] {
-        mod miri;
-        use miri as sys;
-    } else {
-        mod unix;
-        use unix as sys;
-    }
-}
-
 /// A simple struct consisting of a page-aligned pointer to page-aligned
 /// and initially-zeroed memory and a length.
 #[derive(Debug)]
 pub struct Mmap {
-    sys: sys::Mmap,
+    sys: mmap::Mmap,
     file: Option<Arc<File>>,
 }
 
@@ -47,7 +35,7 @@ impl Mmap {
     /// The memory mapping and the length of the file within the mapping are
     /// returned.
     pub fn from_file(path: &Path) -> Result<Self> {
-        let (sys, file) = sys::Mmap::from_file(path)?;
+        let (sys, file) = mmap::Mmap::from_file(path)?;
         Ok(Mmap {
             sys,
             file: Some(Arc::new(file)),
@@ -70,18 +58,18 @@ impl Mmap {
 
         if mapping_size == 0 {
             Ok(Mmap {
-                sys: sys::Mmap::new_empty(),
+                sys: mmap::Mmap::new_empty(),
                 file: None,
             })
         } else if accessible_size == mapping_size {
             Ok(Mmap {
-                sys: sys::Mmap::new(mapping_size)
+                sys: mmap::Mmap::new(mapping_size)
                     .context(format!("mmap failed to allocate {mapping_size:#x} bytes"))?,
                 file: None,
             })
         } else {
             let mut result = Mmap {
-                sys: sys::Mmap::reserve(mapping_size)
+                sys: mmap::Mmap::reserve(mapping_size)
                     .context(format!("mmap failed to reserve {mapping_size:#x} bytes"))?,
                 file: None,
             };
diff --git a/crates/runtime/src/mmap/miri.rs b/crates/runtime/src/sys/miri/mmap.rs
similarity index 100%
rename from crates/runtime/src/mmap/miri.rs
rename to crates/runtime/src/sys/miri/mmap.rs
diff --git a/crates/runtime/src/sys/miri/mod.rs b/crates/runtime/src/sys/miri/mod.rs
new file mode 100644
index 000000000..76cf11e68
--- /dev/null
+++ b/crates/runtime/src/sys/miri/mod.rs
@@ -0,0 +1,10 @@
+//! "Dummy" implementations of some system primitives for MIRI emulation.
+//!
+//! Note that at this time this is just enough to run some tests in MIRI but
+//! notably WebAssembly tests are not executed at this time (MIRI can't execute
+//! Cranelift-generated code).
+
+pub mod mmap;
+pub mod traphandlers;
+pub mod unwind;
+pub mod vm;
diff --git a/crates/runtime/src/sys/miri/traphandlers.rs b/crates/runtime/src/sys/miri/traphandlers.rs
new file mode 100644
index 000000000..ab600913e
--- /dev/null
+++ b/crates/runtime/src/sys/miri/traphandlers.rs
@@ -0,0 +1,42 @@
+// For MIRI, set up just enough of a setjmp/longjmp with catching panics
+// to get a few tests working that use this.
+//
+// Note that no actual JIT code runs in MIRI so this is purely here for
+// host-to-host calls.
+
+use crate::VMContext;
+
+struct WasmtimeLongjmp;
+
+pub fn wasmtime_setjmp(
+    _jmp_buf: *mut *const u8,
+    callback: extern "C" fn(*mut u8, *mut VMContext),
+    payload: *mut u8,
+    callee: *mut VMContext,
+) -> i32 {
+    use std::panic::{self, AssertUnwindSafe};
+    let result = panic::catch_unwind(AssertUnwindSafe(|| {
+        callback(payload, callee);
+    }));
+    match result {
+        Ok(()) => 1,
+        Err(e) => {
+            if e.is::<WasmtimeLongjmp>() {
+                0
+            } else {
+                panic::resume_unwind(e)
+            }
+        }
+    }
+}
+
+pub fn wasmtime_longjmp(_jmp_buf: *const u8) -> ! {
+    std::panic::panic_any(WasmtimeLongjmp)
+}
+
+#[allow(missing_docs)]
+pub type SignalHandler<'a> = dyn Fn() + Send + Sync + 'a;
+
+pub fn platform_init(_macos_use_mach_ports: bool) {}
+
+pub fn lazy_per_thread_init() {}
diff --git a/crates/jit/src/unwind/miri.rs b/crates/runtime/src/sys/miri/unwind.rs
similarity index 93%
rename from crates/jit/src/unwind/miri.rs
rename to crates/runtime/src/sys/miri/unwind.rs
index 4b4527e66..d6181940c 100644
--- a/crates/jit/src/unwind/miri.rs
+++ b/crates/runtime/src/sys/miri/unwind.rs
@@ -1,3 +1,5 @@
+#![allow(missing_docs)]
+
 use anyhow::Result;
 
 pub struct UnwindRegistration {}
diff --git a/crates/runtime/src/sys/miri/vm.rs b/crates/runtime/src/sys/miri/vm.rs
new file mode 100644
index 000000000..a4bf0de57
--- /dev/null
+++ b/crates/runtime/src/sys/miri/vm.rs
@@ -0,0 +1,63 @@
+use std::fs::File;
+use std::io;
+use std::sync::Arc;
+
+pub unsafe fn expose_existing_mapping(ptr: *mut u8, len: usize) -> io::Result<()> {
+    std::ptr::write_bytes(ptr, 0u8, len);
+    Ok(())
+}
+
+pub unsafe fn hide_existing_mapping(ptr: *mut u8, len: usize) -> io::Result<()> {
+    std::ptr::write_bytes(ptr, 0, len);
+    Ok(())
+}
+
+pub unsafe fn erase_existing_mapping(ptr: *mut u8, len: usize) -> io::Result<()> {
+    std::ptr::write_bytes(ptr, 0, len);
+    Ok(())
+}
+
+#[cfg(feature = "pooling-allocator")]
+pub unsafe fn commit_table_pages(ptr: *mut u8, len: usize) -> io::Result<()> {
+    std::ptr::write_bytes(ptr, 0, len);
+    Ok(())
+}
+
+#[cfg(feature = "pooling-allocator")]
+pub unsafe fn decommit_table_pages(ptr: *mut u8, len: usize) -> io::Result<()> {
+    std::ptr::write_bytes(ptr, 0, len);
+    Ok(())
+}
+
+pub fn get_page_size() -> usize {
+    4096
+}
+
+pub fn supports_madvise_dontneed() -> bool {
+    false
+}
+
+pub unsafe fn madvise_dontneed(_ptr: *mut u8, _len: usize) -> io::Result<()> {
+    unreachable!()
+}
+
+#[derive(PartialEq, Debug)]
+pub enum MemoryImageSource {}
+
+impl MemoryImageSource {
+    pub fn from_file(_file: &Arc<File>) -> Option<MemoryImageSource> {
+        None
+    }
+
+    pub fn from_data(_data: &[u8]) -> io::Result<Option<MemoryImageSource>> {
+        Ok(None)
+    }
+
+    pub unsafe fn map_at(&self, _base: *mut u8, _len: usize, _offset: u64) -> io::Result<()> {
+        match *self {}
+    }
+
+    pub unsafe fn remap_as_zeros_at(&self, _base: *mut u8, _len: usize) -> io::Result<()> {
+        match *self {}
+    }
+}
diff --git a/crates/runtime/src/sys/mod.rs b/crates/runtime/src/sys/mod.rs
new file mode 100644
index 000000000..e88435682
--- /dev/null
+++ b/crates/runtime/src/sys/mod.rs
@@ -0,0 +1,30 @@
+//! OS-related abstractions required by Wasmtime.
+//!
+//! This module is intended to house all logic that's specific to either Unix
+//! or Windows, for example. The goal of this module is to be the "single
+//! module" to edit if Wasmtime is ported to a new platform. Ideally all that's
+//! needed is an extra block below and a new platform should be good to go after
+//! filling out the implementation.
+
+#![allow(clippy::cast_sign_loss)] // platforms too fiddly to worry about this
+
+cfg_if::cfg_if! {
+    if #[cfg(miri)] {
+        mod miri;
+        pub use miri::*;
+    } else if #[cfg(windows)] {
+        mod windows;
+        pub use windows::*;
+    } else if #[cfg(unix)] {
+        mod unix;
+        pub use unix::*;
+    } else {
+        compile_error!(
+            "Wasmtime is being compiled for a platform \
+             that it does not support. If this platform is \
+             one you would like to see supported you may file an \
+             issue on Wasmtime's issue tracker: \
+             https://github.com/bytecodealliance/wasmtime/issues/new\
+        ");
+    }
+}
diff --git a/crates/runtime/src/traphandlers/macos.rs b/crates/runtime/src/sys/unix/machports.rs
similarity index 98%
rename from crates/runtime/src/traphandlers/macos.rs
rename to crates/runtime/src/sys/unix/machports.rs
index 34d707498..677579676 100644
--- a/crates/runtime/src/traphandlers/macos.rs
+++ b/crates/runtime/src/sys/unix/machports.rs
@@ -33,7 +33,8 @@
 
 #![allow(non_snake_case, clippy::cast_sign_loss)]
 
-use crate::traphandlers::{tls, wasmtime_longjmp};
+use crate::sys::traphandlers::wasmtime_longjmp;
+use crate::traphandlers::tls;
 use mach::exception_types::*;
 use mach::kern_return::*;
 use mach::mach_init::*;
@@ -167,9 +168,6 @@ static mut WASMTIME_PORT: mach_port_name_t = MACH_PORT_NULL;
 static mut CHILD_OF_FORKED_PROCESS: bool = false;
 
 pub unsafe fn platform_init() {
-    if cfg!(miri) {
-        return;
-    }
     // Mach ports do not currently work across forks, so configure Wasmtime to
     // panic in `lazy_per_thread_init` if the child attempts to invoke
     // WebAssembly.
@@ -404,7 +402,7 @@ unsafe fn handle_exception(request: &mut ExceptionRequest) -> bool {
     // pointer value and if `MAP` changes happen after we read our entry that's
     // ok since they won't invalidate our entry.
     let (pc, fp) = get_pc_and_fp(&thread_state);
-    if !super::IS_WASM_PC(pc as usize) {
+    if !crate::traphandlers::IS_WASM_PC(pc as usize) {
         return false;
     }
 
@@ -443,7 +441,7 @@ unsafe extern "C" fn unwind(
             None
         };
         state.set_jit_trap(wasm_pc, wasm_fp, faulting_addr);
-        state.jmp_buf.get()
+        state.take_jmp_buf()
     });
     debug_assert!(!jmp_buf.is_null());
     wasmtime_longjmp(jmp_buf);
@@ -468,9 +466,6 @@ unsafe extern "C" fn unwind(
 /// exception handlers to get registered.
 #[cold]
 pub fn lazy_per_thread_init() {
-    if cfg!(miri) {
-        return;
-    }
     unsafe {
         assert!(
             !CHILD_OF_FORKED_PROCESS,
diff --git a/crates/runtime/src/sys/unix/macos_traphandlers.rs b/crates/runtime/src/sys/unix/macos_traphandlers.rs
new file mode 100644
index 000000000..fd0d60753
--- /dev/null
+++ b/crates/runtime/src/sys/unix/macos_traphandlers.rs
@@ -0,0 +1,28 @@
+/// Whether or not macOS is using mach ports.
+#[cfg(target_os = "macos")]
+static mut USE_MACH_PORTS: bool = false;
+
+pub use super::signals::{wasmtime_longjmp, wasmtime_setjmp, SignalHandler};
+
+pub unsafe fn platform_init(macos_use_mach_ports: bool) {
+    USE_MACH_PORTS = macos_use_mach_ports;
+    if macos_use_mach_ports {
+        super::machports::platform_init();
+    } else {
+        super::signals::platform_init(false);
+    }
+}
+
+pub fn using_mach_ports() -> bool {
+    unsafe { USE_MACH_PORTS }
+}
+
+pub fn lazy_per_thread_init() {
+    unsafe {
+        if USE_MACH_PORTS {
+            super::machports::lazy_per_thread_init();
+        } else {
+            super::signals::lazy_per_thread_init();
+        }
+    }
+}
diff --git a/crates/runtime/src/mmap/unix.rs b/crates/runtime/src/sys/unix/mmap.rs
similarity index 100%
rename from crates/runtime/src/mmap/unix.rs
rename to crates/runtime/src/sys/unix/mmap.rs
diff --git a/crates/runtime/src/sys/unix/mod.rs b/crates/runtime/src/sys/unix/mod.rs
new file mode 100644
index 000000000..f124598ac
--- /dev/null
+++ b/crates/runtime/src/sys/unix/mod.rs
@@ -0,0 +1,21 @@
+//! Implementation of Wasmtime's system primitives for Unix-like operating
+//! systems.
+//!
+//! This module handles Linux and macOS for example.
+
+pub mod mmap;
+pub mod unwind;
+pub mod vm;
+
+pub mod signals;
+
+cfg_if::cfg_if! {
+    if #[cfg(target_os = "macos")] {
+        pub mod machports;
+
+        pub mod macos_traphandlers;
+        pub use macos_traphandlers as traphandlers;
+    } else {
+        pub use signals as traphandlers;
+    }
+}
diff --git a/crates/runtime/src/traphandlers/unix.rs b/crates/runtime/src/sys/unix/signals.rs
similarity index 95%
rename from crates/runtime/src/traphandlers/unix.rs
rename to crates/runtime/src/sys/unix/signals.rs
index c4a993f15..59b7982d2 100644
--- a/crates/runtime/src/traphandlers/unix.rs
+++ b/crates/runtime/src/sys/unix/signals.rs
@@ -1,11 +1,27 @@
-#![allow(clippy::cast_sign_loss)] // platforms too fiddly to worry about this
+//! Trap handling on Unix based on POSIX signals.
 
-use crate::traphandlers::{tls, wasmtime_longjmp};
+use crate::traphandlers::tls;
+use crate::VMContext;
 use std::cell::RefCell;
 use std::io;
 use std::mem::{self, MaybeUninit};
 use std::ptr::{self, null_mut};
 
+#[link(name = "wasmtime-helpers")]
+extern "C" {
+    #[wasmtime_versioned_export_macros::versioned_link]
+    #[allow(improper_ctypes)]
+    pub fn wasmtime_setjmp(
+        jmp_buf: *mut *const u8,
+        callback: extern "C" fn(*mut u8, *mut VMContext),
+        payload: *mut u8,
+        callee: *mut VMContext,
+    ) -> i32;
+
+    #[wasmtime_versioned_export_macros::versioned_link]
+    pub fn wasmtime_longjmp(jmp_buf: *const u8) -> !;
+}
+
 /// Function which may handle custom signals while processing traps.
 pub type SignalHandler<'a> =
     dyn Fn(libc::c_int, *const libc::siginfo_t, *const libc::c_void) -> bool + Send + Sync + 'a;
@@ -15,10 +31,11 @@ static mut PREV_SIGBUS: MaybeUninit<libc::sigaction> = MaybeUninit::uninit();
 static mut PREV_SIGILL: MaybeUninit<libc::sigaction> = MaybeUninit::uninit();
 static mut PREV_SIGFPE: MaybeUninit<libc::sigaction> = MaybeUninit::uninit();
 
-pub unsafe fn platform_init() {
-    if cfg!(miri) {
-        return;
-    }
+pub unsafe fn platform_init(macos_use_mach_ports: bool) {
+    // Either mach ports shouldn't be in use or we shouldn't be on macOS,
+    // otherwise the `machports.rs` module should be used instead.
+    assert!(!macos_use_mach_ports || !cfg!(target_os = "macos"));
+
     let register = |slot: &mut MaybeUninit<libc::sigaction>, signal: i32| {
         let mut handler: libc::sigaction = mem::zeroed();
         // The flags here are relatively careful, and they are...
@@ -317,10 +334,6 @@ pub fn lazy_per_thread_init() {
     });
 
     unsafe fn allocate_sigaltstack() -> Option<Stack> {
-        if cfg!(miri) {
-            return None;
-        }
-
         // Check to see if the existing sigaltstack, if it exists, is big
         // enough. If so we don't need to allocate our own.
         let mut old_stack = mem::zeroed();
diff --git a/crates/jit/src/unwind/systemv.rs b/crates/runtime/src/sys/unix/unwind.rs
similarity index 97%
rename from crates/jit/src/unwind/systemv.rs
rename to crates/runtime/src/sys/unix/unwind.rs
index 2233e4dcb..87d0a28d4 100644
--- a/crates/jit/src/unwind/systemv.rs
+++ b/crates/runtime/src/sys/unix/unwind.rs
@@ -14,6 +14,7 @@ extern "C" {
 }
 
 impl UnwindRegistration {
+    #[allow(missing_docs)]
     pub const SECTION_NAME: &'static str = ".eh_frame";
 
     /// Registers precompiled unwinding information with the system.
@@ -28,7 +29,7 @@ impl UnwindRegistration {
         unwind_len: usize,
     ) -> Result<UnwindRegistration> {
         debug_assert_eq!(
-            unwind_info as usize % wasmtime_runtime::page_size(),
+            unwind_info as usize % crate::page_size(),
             0,
             "The unwind info must always be aligned to a page"
         );
diff --git a/crates/runtime/src/sys/unix/vm.rs b/crates/runtime/src/sys/unix/vm.rs
new file mode 100644
index 000000000..d0e76a145
--- /dev/null
+++ b/crates/runtime/src/sys/unix/vm.rs
@@ -0,0 +1,208 @@
+use rustix::fd::AsRawFd;
+use rustix::mm::{mmap, mmap_anonymous, mprotect, MapFlags, MprotectFlags, ProtFlags};
+use std::fs::File;
+use std::io;
+use std::sync::Arc;
+
+pub unsafe fn expose_existing_mapping(ptr: *mut u8, len: usize) -> io::Result<()> {
+    mprotect(ptr.cast(), len, MprotectFlags::READ | MprotectFlags::WRITE)?;
+    Ok(())
+}
+
+pub unsafe fn hide_existing_mapping(ptr: *mut u8, len: usize) -> io::Result<()> {
+    mprotect(ptr.cast(), len, MprotectFlags::empty())?;
+    Ok(())
+}
+
+pub unsafe fn erase_existing_mapping(ptr: *mut u8, len: usize) -> io::Result<()> {
+    let ret = mmap_anonymous(
+        ptr.cast(),
+        len,
+        ProtFlags::empty(),
+        MapFlags::PRIVATE | MapFlags::FIXED,
+    )?;
+    assert_eq!(ptr, ret.cast());
+    Ok(())
+}
+
+#[cfg(feature = "pooling-allocator")]
+unsafe fn decommit(addr: *mut u8, len: usize) -> io::Result<()> {
+    if len == 0 {
+        return Ok(());
+    }
+
+    unsafe {
+        cfg_if::cfg_if! {
+            if #[cfg(target_os = "linux")] {
+                use rustix::mm::{madvise, Advice};
+
+                // On Linux, this is enough to cause the kernel to initialize
+                // the pages to 0 on next access
+                madvise(addr as _, len, Advice::LinuxDontNeed)?;
+            } else {
+                // By creating a new mapping at the same location, this will
+                // discard the mapping for the pages in the given range.
+                // The new mapping will be to the CoW zero page, so this
+                // effectively zeroes the pages.
+                mmap_anonymous(
+                    addr as _,
+                    len,
+                    ProtFlags::READ | ProtFlags::WRITE,
+                    MapFlags::PRIVATE | MapFlags::FIXED,
+                )?;
+            }
+        }
+    }
+
+    Ok(())
+}
+
+#[cfg(feature = "pooling-allocator")]
+pub unsafe fn commit_table_pages(_addr: *mut u8, _len: usize) -> io::Result<()> {
+    // Table pages are always READ | WRITE so there's nothing that needs to be
+    // done here.
+    Ok(())
+}
+
+#[cfg(feature = "pooling-allocator")]
+pub unsafe fn decommit_table_pages(addr: *mut u8, len: usize) -> io::Result<()> {
+    decommit(addr, len)
+}
+
+#[cfg(all(feature = "pooling-allocator", feature = "async"))]
+pub unsafe fn commit_stack_pages(_addr: *mut u8, _len: usize) -> io::Result<()> {
+    // Like table pages stack pages are always READ | WRITE so nothing extra
+    // needs to be done to ensure they can be committed.
+    Ok(())
+}
+
+#[cfg(all(feature = "pooling-allocator", feature = "async"))]
+pub unsafe fn reset_stack_pages_to_zero(addr: *mut u8, len: usize) -> io::Result<()> {
+    decommit(addr, len)
+}
+
+pub fn get_page_size() -> usize {
+    unsafe { libc::sysconf(libc::_SC_PAGESIZE).try_into().unwrap() }
+}
+
+pub fn supports_madvise_dontneed() -> bool {
+    cfg!(target_os = "linux")
+}
+
+pub unsafe fn madvise_dontneed(ptr: *mut u8, len: usize) -> io::Result<()> {
+    cfg_if::cfg_if! {
+        if #[cfg(target_os = "linux")] {
+            rustix::mm::madvise(ptr.cast(), len, rustix::mm::Advice::LinuxDontNeed)?;
+            Ok(())
+        } else {
+            let _ = (ptr, len);
+            unreachable!();
+        }
+    }
+}
+
+#[derive(Debug)]
+pub enum MemoryImageSource {
+    Mmap(Arc<File>),
+    #[cfg(target_os = "linux")]
+    Memfd(memfd::Memfd),
+}
+
+impl MemoryImageSource {
+    pub fn from_file(file: &Arc<File>) -> Option<MemoryImageSource> {
+        Some(MemoryImageSource::Mmap(file.clone()))
+    }
+
+    #[cfg(not(target_os = "linux"))]
+    pub fn from_data(_data: &[u8]) -> io::Result<Option<MemoryImageSource>> {
+        Ok(None)
+    }
+
+    #[cfg(target_os = "linux")]
+    pub fn from_data(data: &[u8]) -> anyhow::Result<Option<MemoryImageSource>> {
+        // On Linux `memfd_create` is used to create an anonymous
+        // in-memory file to represent the heap image. This anonymous
+        // file is then used as the basis for further mmaps.
+
+        use std::io::{ErrorKind, Write};
+
+        // Create the memfd. It needs a name, but the documentation for
+        // `memfd_create()` says that names can be duplicated with no issues.
+        let memfd = match memfd::MemfdOptions::new()
+            .allow_sealing(true)
+            .create("wasm-memory-image")
+        {
+            Ok(memfd) => memfd,
+            // If this kernel is old enough to not support memfd then attempt to
+            // gracefully handle that and fall back to skipping the memfd
+            // optimization.
+            Err(memfd::Error::Create(err)) if err.kind() == ErrorKind::Unsupported => {
+                return Ok(None)
+            }
+            Err(e) => return Err(e.into()),
+        };
+        memfd.as_file().write_all(data)?;
+
+        // Seal the memfd's data and length.
+        //
+        // This is a defense-in-depth security mitigation. The
+        // memfd will serve as the starting point for the heap of
+        // every instance of this module. If anything were to
+        // write to this, it could affect every execution. The
+        // memfd object itself is owned by the machinery here and
+        // not exposed elsewhere, but it is still an ambient open
+        // file descriptor at the syscall level, so some other
+        // vulnerability that allowed writes to arbitrary fds
+        // could modify it. Or we could have some issue with the
+        // way that we map it into each instance. To be
+        // extra-super-sure that it never changes, and because
+        // this costs very little, we use the kernel's "seal" API
+        // to make the memfd image permanently read-only.
+        memfd.add_seals(&[
+            memfd::FileSeal::SealGrow,
+            memfd::FileSeal::SealShrink,
+            memfd::FileSeal::SealWrite,
+            memfd::FileSeal::SealSeal,
+        ])?;
+
+        Ok(Some(MemoryImageSource::Memfd(memfd)))
+    }
+
+    fn as_file(&self) -> &File {
+        match self {
+            MemoryImageSource::Mmap(file) => file,
+            #[cfg(target_os = "linux")]
+            MemoryImageSource::Memfd(memfd) => memfd.as_file(),
+        }
+    }
+
+    pub unsafe fn map_at(&self, base: *mut u8, len: usize, offset: u64) -> io::Result<()> {
+        let ptr = mmap(
+            base.cast(),
+            len,
+            ProtFlags::READ | ProtFlags::WRITE,
+            MapFlags::PRIVATE | MapFlags::FIXED,
+            self.as_file(),
+            offset,
+        )?;
+        assert_eq!(base, ptr.cast());
+        Ok(())
+    }
+
+    pub unsafe fn remap_as_zeros_at(&self, base: *mut u8, len: usize) -> io::Result<()> {
+        let ptr = mmap_anonymous(
+            base.cast(),
+            len,
+            ProtFlags::READ | ProtFlags::WRITE,
+            MapFlags::PRIVATE | MapFlags::FIXED,
+        )?;
+        assert_eq!(base, ptr.cast());
+        Ok(())
+    }
+}
+
+impl PartialEq for MemoryImageSource {
+    fn eq(&self, other: &MemoryImageSource) -> bool {
+        self.as_file().as_raw_fd() == other.as_file().as_raw_fd()
+    }
+}
diff --git a/crates/runtime/src/mmap/windows.rs b/crates/runtime/src/sys/windows/mmap.rs
similarity index 100%
rename from crates/runtime/src/mmap/windows.rs
rename to crates/runtime/src/sys/windows/mmap.rs
diff --git a/crates/runtime/src/sys/windows/mod.rs b/crates/runtime/src/sys/windows/mod.rs
new file mode 100644
index 000000000..d055ef692
--- /dev/null
+++ b/crates/runtime/src/sys/windows/mod.rs
@@ -0,0 +1,6 @@
+//! Implementation of Wasmtime's system primitives for Windows.
+
+pub mod mmap;
+pub mod traphandlers;
+pub mod unwind;
+pub mod vm;
diff --git a/crates/runtime/src/traphandlers/windows.rs b/crates/runtime/src/sys/windows/traphandlers.rs
similarity index 86%
rename from crates/runtime/src/traphandlers/windows.rs
rename to crates/runtime/src/sys/windows/traphandlers.rs
index 5ad7295f5..f383477ff 100644
--- a/crates/runtime/src/traphandlers/windows.rs
+++ b/crates/runtime/src/sys/windows/traphandlers.rs
@@ -1,13 +1,29 @@
-use crate::traphandlers::{tls, wasmtime_longjmp};
+use crate::traphandlers::tls;
+use crate::VMContext;
 use std::io;
 use windows_sys::Win32::Foundation::*;
 use windows_sys::Win32::System::Diagnostics::Debug::*;
 use windows_sys::Win32::System::Kernel::*;
 
+#[link(name = "wasmtime-helpers")]
+extern "C" {
+    #[wasmtime_versioned_export_macros::versioned_link]
+    #[allow(improper_ctypes)]
+    pub fn wasmtime_setjmp(
+        jmp_buf: *mut *const u8,
+        callback: extern "C" fn(*mut u8, *mut VMContext),
+        payload: *mut u8,
+        callee: *mut VMContext,
+    ) -> i32;
+
+    #[wasmtime_versioned_export_macros::versioned_link]
+    pub fn wasmtime_longjmp(jmp_buf: *const u8) -> !;
+}
+
 /// Function which may handle custom signals while processing traps.
 pub type SignalHandler<'a> = dyn Fn(*mut EXCEPTION_POINTERS) -> bool + Send + Sync + 'a;
 
-pub unsafe fn platform_init() {
+pub unsafe fn platform_init(_macos_use_mach_ports: bool) {
     // our trap handler needs to go first, so that we can recover from
     // wasm faults and continue execution, so pass `1` as a true value
     // here.
diff --git a/crates/jit/src/unwind/winx64.rs b/crates/runtime/src/sys/windows/unwind.rs
similarity index 95%
rename from crates/jit/src/unwind/winx64.rs
rename to crates/runtime/src/sys/windows/unwind.rs
index 6468b87c3..30f136180 100644
--- a/crates/jit/src/unwind/winx64.rs
+++ b/crates/runtime/src/sys/windows/unwind.rs
@@ -10,8 +10,10 @@ pub struct UnwindRegistration {
 }
 
 impl UnwindRegistration {
+    #[allow(missing_docs)]
     pub const SECTION_NAME: &'static str = ".pdata";
 
+    #[allow(missing_docs)]
     pub unsafe fn new(
         base_address: *const u8,
         unwind_info: *const u8,
diff --git a/crates/runtime/src/sys/windows/vm.rs b/crates/runtime/src/sys/windows/vm.rs
new file mode 100644
index 000000000..52501f5cf
--- /dev/null
+++ b/crates/runtime/src/sys/windows/vm.rs
@@ -0,0 +1,79 @@
+use std::fs::File;
+use std::io;
+use std::mem::MaybeUninit;
+use std::sync::Arc;
+use windows_sys::Win32::System::Memory::*;
+use windows_sys::Win32::System::SystemInformation::*;
+
+pub unsafe fn expose_existing_mapping(ptr: *mut u8, len: usize) -> io::Result<()> {
+    if len == 0 {
+        return Ok(());
+    }
+    if VirtualAlloc(ptr.cast(), len, MEM_COMMIT, PAGE_READWRITE).is_null() {
+        Err(std::io::Error::last_os_error())
+    } else {
+        Ok(())
+    }
+}
+
+pub unsafe fn hide_existing_mapping(ptr: *mut u8, len: usize) -> io::Result<()> {
+    erase_existing_mapping(ptr, len)
+}
+
+pub unsafe fn erase_existing_mapping(ptr: *mut u8, len: usize) -> io::Result<()> {
+    if len == 0 {
+        return Ok(());
+    }
+    if VirtualFree(ptr.cast(), len, MEM_DECOMMIT) == 0 {
+        Err(std::io::Error::last_os_error())
+    } else {
+        Ok(())
+    }
+}
+
+#[cfg(feature = "pooling-allocator")]
+pub unsafe fn commit_table_pages(addr: *mut u8, len: usize) -> io::Result<()> {
+    expose_existing_mapping(addr, len)
+}
+
+#[cfg(feature = "pooling-allocator")]
+pub unsafe fn decommit_table_pages(addr: *mut u8, len: usize) -> io::Result<()> {
+    erase_existing_mapping(addr, len)
+}
+
+pub fn get_page_size() -> usize {
+    unsafe {
+        let mut info = MaybeUninit::uninit();
+        GetSystemInfo(info.as_mut_ptr());
+        info.assume_init_ref().dwPageSize as usize
+    }
+}
+
+pub fn supports_madvise_dontneed() -> bool {
+    false
+}
+
+pub unsafe fn madvise_dontneed(_ptr: *mut u8, _len: usize) -> io::Result<()> {
+    unreachable!()
+}
+
+#[derive(PartialEq, Debug)]
+pub enum MemoryImageSource {}
+
+impl MemoryImageSource {
+    pub fn from_file(_file: &Arc<File>) -> Option<MemoryImageSource> {
+        None
+    }
+
+    pub fn from_data(_data: &[u8]) -> io::Result<Option<MemoryImageSource>> {
+        Ok(None)
+    }
+
+    pub unsafe fn map_at(&self, _base: *mut u8, _len: usize, _offset: u64) -> io::Result<()> {
+        match *self {}
+    }
+
+    pub unsafe fn remap_as_zeros_at(&self, _base: *mut u8, _len: usize) -> io::Result<()> {
+        match *self {}
+    }
+}
diff --git a/crates/runtime/src/trampolines.rs b/crates/runtime/src/trampolines.rs
deleted file mode 100644
index 16741075c..000000000
--- a/crates/runtime/src/trampolines.rs
+++ /dev/null
@@ -1,19 +0,0 @@
-//! Wasm-to-libcall trampolines.
-
-cfg_if::cfg_if! {
-    if #[cfg(target_arch = "x86_64")] {
-        #[macro_use]
-        mod x86_64;
-    } else if #[cfg(target_arch = "aarch64")] {
-        #[macro_use]
-        mod aarch64;
-    } else if #[cfg(target_arch = "s390x")] {
-        #[macro_use]
-        mod s390x;
-    }else if #[cfg(target_arch = "riscv64")] {
-        #[macro_use]
-        mod riscv64;
-    } else {
-        compile_error!("unsupported architecture");
-    }
-}
diff --git a/crates/runtime/src/trampolines/aarch64.rs b/crates/runtime/src/trampolines/aarch64.rs
deleted file mode 100644
index 29852bad1..000000000
--- a/crates/runtime/src/trampolines/aarch64.rs
+++ /dev/null
@@ -1,42 +0,0 @@
-#[rustfmt::skip]
-macro_rules! wasm_to_libcall_trampoline {
-    ($libcall:ident ; $libcall_impl:ident) => {
-        wasmtime_asm_macros::asm_func!(
-            wasmtime_versioned_export_macros::versioned_stringify_ident!($libcall),
-            "
-                .cfi_startproc
-                bti c
-
-                // Load the pointer to `VMRuntimeLimits` in `x9`.
-                ldur x9, [x0, #8]
-
-                // Store the last Wasm FP into the `last_wasm_exit_fp` in the limits.
-                stur fp, [x9, #24]
-
-                // Store the last Wasm PC into the `last_wasm_exit_pc` in the limits.
-                stur lr, [x9, #32]
-
-                // Tail call to the actual implementation of this libcall.
-                b {}
-
-                .cfi_endproc
-            ",
-            sym $libcall_impl
-        );
-    };
-}
-
-#[cfg(test)]
-mod wasm_to_libcall_trampoline_offsets_tests {
-    use wasmtime_environ::{Module, PtrSize, VMOffsets};
-
-    #[test]
-    fn test() {
-        let module = Module::new();
-        let offsets = VMOffsets::new(std::mem::size_of::<*mut u8>() as u8, &module);
-
-        assert_eq!(8, offsets.vmctx_runtime_limits());
-        assert_eq!(24, offsets.ptr.vmruntime_limits_last_wasm_exit_fp());
-        assert_eq!(32, offsets.ptr.vmruntime_limits_last_wasm_exit_pc());
-    }
-}
diff --git a/crates/runtime/src/trampolines/s390x.rs b/crates/runtime/src/trampolines/s390x.rs
deleted file mode 100644
index f8ca65ca4..000000000
--- a/crates/runtime/src/trampolines/s390x.rs
+++ /dev/null
@@ -1,25 +0,0 @@
-// The implementation for libcall trampolines is in the s390x.S
-// file.  We provide this dummy definition of wasm_to_libcall_trampoline
-// here to make libcalls.rs compile on s390x.  Note that this means we
-// have to duplicate the list of libcalls used in the assembler file.
-
-macro_rules! wasm_to_libcall_trampoline {
-    ($libcall:ident ; $libcall_impl:ident) => {};
-}
-
-// The wasm_to_host_trampoline implementation is in the s390x.S
-// file, but we still want to have this unit test here.
-#[cfg(test)]
-mod wasm_to_libcall_trampoline_offsets_tests {
-    use wasmtime_environ::{Module, PtrSize, VMOffsets};
-
-    #[test]
-    fn test() {
-        let module = Module::new();
-        let offsets = VMOffsets::new(std::mem::size_of::<*mut u8>() as u8, &module);
-
-        assert_eq!(8, offsets.vmctx_runtime_limits());
-        assert_eq!(24, offsets.ptr.vmruntime_limits_last_wasm_exit_fp());
-        assert_eq!(32, offsets.ptr.vmruntime_limits_last_wasm_exit_pc());
-    }
-}
diff --git a/crates/runtime/src/traphandlers.rs b/crates/runtime/src/traphandlers.rs
index 21a7a519a..3a0eeed68 100644
--- a/crates/runtime/src/traphandlers.rs
+++ b/crates/runtime/src/traphandlers.rs
@@ -4,6 +4,7 @@
 mod backtrace;
 mod coredump;
 
+use crate::sys::traphandlers;
 use crate::{Instance, VMContext, VMRuntimeLimits};
 use anyhow::Error;
 use std::any::Any;
@@ -16,85 +17,14 @@ pub use self::backtrace::{Backtrace, Frame};
 pub use self::coredump::CoreDumpStack;
 pub use self::tls::{tls_eager_initialize, AsyncWasmCallState, PreviousAsyncWasmCallState};
 
-cfg_if::cfg_if! {
-    if #[cfg(miri)] {
-        // With MIRI set up just enough of a setjmp/longjmp with catching panics
-        // to get a few tests working that use this.
-        //
-        // Note that no actual JIT code runs in MIRI so this is purely here for
-        // host-to-host calls.
-
-        struct WasmtimeLongjmp;
-
-        #[wasmtime_versioned_export_macros::versioned_export]
-        unsafe extern "C" fn wasmtime_setjmp(
-            _jmp_buf: *mut *const u8,
-            callback: extern "C" fn(*mut u8, *mut VMContext),
-            payload: *mut u8,
-            callee: *mut VMContext,
-        ) -> i32 {
-            use std::panic::{self, AssertUnwindSafe};
-            let result = panic::catch_unwind(AssertUnwindSafe(|| {
-                callback(payload, callee);
-            }));
-            match result {
-                Ok(()) => 1,
-                Err(e) => {
-                    if e.is::<WasmtimeLongjmp>() {
-                        0
-                    } else {
-                        panic::resume_unwind(e)
-                    }
-                }
-            }
-        }
-
-        #[wasmtime_versioned_export_macros::versioned_export]
-        unsafe extern "C" fn wasmtime_longjmp(_jmp_buf: *const u8) -> ! {
-            std::panic::panic_any(WasmtimeLongjmp)
-        }
-    } else {
-        #[link(name = "wasmtime-helpers")]
-        extern "C" {
-            #[wasmtime_versioned_export_macros::versioned_link]
-            #[allow(improper_ctypes)]
-            fn wasmtime_setjmp(
-                jmp_buf: *mut *const u8,
-                callback: extern "C" fn(*mut u8, *mut VMContext),
-                payload: *mut u8,
-                callee: *mut VMContext,
-            ) -> i32;
-            #[wasmtime_versioned_export_macros::versioned_link]
-            fn wasmtime_longjmp(jmp_buf: *const u8) -> !;
-        }
-    }
-}
-
-cfg_if::cfg_if! {
-    if #[cfg(unix)] {
-        mod unix;
-        use unix as sys;
-    } else if #[cfg(target_os = "windows")] {
-        mod windows;
-        use windows as sys;
-    }
-}
-
-#[cfg(target_os = "macos")]
-mod macos;
-
-pub use sys::SignalHandler;
+pub use traphandlers::SignalHandler;
 
 /// Globally-set callback to determine whether a program counter is actually a
 /// wasm trap.
 ///
 /// This is initialized during `init_traps` below. The definition lives within
 /// `wasmtime` currently.
-static mut IS_WASM_PC: fn(usize) -> bool = |_| false;
-
-/// Whether or not macOS is using mach ports.
-#[cfg(target_os = "macos")]
-static mut MACOS_USE_MACH_PORTS: bool = false;
+pub(crate) static mut IS_WASM_PC: fn(usize) -> bool = |_| false;
 
 /// This function is required to be called before any WebAssembly is entered.
 /// This will configure global state such as signal handlers to prepare the
@@ -112,38 +42,21 @@ static mut MACOS_USE_MACH_PORTS: bool = false;
 pub fn init_traps(is_wasm_pc: fn(usize) -> bool, macos_use_mach_ports: bool) {
     static INIT: Once = Once::new();
 
-    // only used on macos, so squelch warnings about this not being used on
-    // other platform.
-    let _ = macos_use_mach_ports;
-
     INIT.call_once(|| unsafe {
         IS_WASM_PC = is_wasm_pc;
-        #[cfg(target_os = "macos")]
-        if macos_use_mach_ports {
-            MACOS_USE_MACH_PORTS = macos_use_mach_ports;
-            return macos::platform_init();
-        }
-        sys::platform_init();
+        traphandlers::platform_init(macos_use_mach_ports);
     });
 
     #[cfg(target_os = "macos")]
-    unsafe {
-        assert_eq!(
-            MACOS_USE_MACH_PORTS, macos_use_mach_ports,
-            "cannot configure two different methods of signal handling in the same process"
-        );
-    }
+    assert_eq!(
+        traphandlers::using_mach_ports(),
+        macos_use_mach_ports,
+        "cannot configure two different methods of signal handling in the same process"
+    );
 }
 
 fn lazy_per_thread_init() {
-    #[cfg(target_os = "macos")]
-    unsafe {
-        if MACOS_USE_MACH_PORTS {
-            return macos::lazy_per_thread_init();
-        }
-    }
-
-    sys::lazy_per_thread_init();
+    traphandlers::lazy_per_thread_init();
 }
 
 /// Raises a trap immediately.
@@ -303,7 +216,7 @@ where
 
     let result = CallThreadState::new(signal_handler, capture_backtrace, capture_coredump, *limits)
         .with(|cx| {
-            wasmtime_setjmp(
+            traphandlers::wasmtime_setjmp(
                 cx.jmp_buf.as_ptr(),
                 call_closure::<F>,
                 &mut closure as *mut F as *mut u8,
@@ -470,7 +383,7 @@ impl CallThreadState {
             (*self.unwind.get())
                 .as_mut_ptr()
                 .write((reason, backtrace, coredump));
-            wasmtime_longjmp(self.jmp_buf.get());
+            traphandlers::wasmtime_longjmp(self.jmp_buf.get());
         }
     }
 
@@ -489,8 +402,8 @@ impl CallThreadState {
     ///   instance, and the trap handler should quickly return.
     /// * a different pointer - a jmp_buf buffer to longjmp to, meaning that
     ///   the wasm trap was succesfully handled.
-    #[cfg_attr(target_os = "macos", allow(dead_code))] // macOS is more raw and doesn't use this
-    fn take_jmp_buf_if_trap(
+    #[cfg_attr(miri, allow(dead_code))] // miri doesn't handle traps yet
+    pub(crate) fn take_jmp_buf_if_trap(
         &self,
         pc: *const u8,
         call_handler: impl Fn(&SignalHandler) -> bool,
@@ -516,10 +429,15 @@ impl CallThreadState {
 
         // If all that passed then this is indeed a wasm trap, so return the
         // `jmp_buf` passed to `wasmtime_longjmp` to resume.
+        self.take_jmp_buf()
+    }
+
+    pub(crate) fn take_jmp_buf(&self) -> *const u8 {
         self.jmp_buf.replace(ptr::null())
     }
 
-    fn set_jit_trap(&self, pc: *const u8, fp: usize, faulting_addr: Option<usize>) {
+    #[cfg_attr(miri, allow(dead_code))] // miri doesn't handle traps yet
+    pub(crate) fn set_jit_trap(&self, pc: *const u8, fp: usize, faulting_addr: Option<usize>) {
         let backtrace = self.capture_backtrace(self.limits, Some((pc as usize, fp)));
         let coredump = self.capture_coredump(self.limits, Some((pc as usize, fp)));
         unsafe {
@@ -581,7 +499,7 @@ impl<T: Copy> Drop for ResetCell<'_, T> {
 // happen which requires us to read some contextual state to figure out what to
 // do with the trap. This `tls` module is used to persist that information from
 // the caller to the trap site.
-mod tls {
+pub(crate) mod tls {
     use super::CallThreadState;
     use std::mem;
     use std::ops::Range;
diff --git a/crates/runtime/src/traphandlers/backtrace.rs b/crates/runtime/src/traphandlers/backtrace.rs
index 0b61fb425..4cab189dc 100644
--- a/crates/runtime/src/traphandlers/backtrace.rs
+++ b/crates/runtime/src/traphandlers/backtrace.rs
@@ -20,39 +20,13 @@
 //! exit FP and stopping once we reach the entry SP (meaning that the next older
 //! frame is a host frame).
 
+use crate::arch;
 use crate::{
     traphandlers::{tls, CallThreadState},
     VMRuntimeLimits,
 };
-use cfg_if::cfg_if;
 use std::ops::ControlFlow;
 
-// Architecture-specific bits for stack walking. Each of these modules should
-// define and export the following functions:
-//
-// * `unsafe fn get_next_older_pc_from_fp(fp: usize) -> usize`
-// * `unsafe fn get_next_older_fp_from_fp(fp: usize) -> usize`
-// * `fn reached_entry_sp(fp: usize, first_wasm_sp: usize) -> bool`
-// * `fn assert_entry_sp_is_aligned(sp: usize)`
-// * `fn assert_fp_is_aligned(fp: usize)`
-cfg_if! {
-    if #[cfg(target_arch = "x86_64")] {
-        mod x86_64;
-        use x86_64 as arch;
-    } else if #[cfg(target_arch = "aarch64")] {
-        mod aarch64;
-        use aarch64 as arch;
-    } else if #[cfg(target_arch = "s390x")] {
-        mod s390x;
-        use s390x as arch;
-    } else if #[cfg(target_arch = "riscv64")] {
-        mod riscv64;
-        use riscv64 as arch;
-    } else {
-        compile_error!("unsupported architecture");
-    }
-}
-
 /// A WebAssembly stack trace.
 #[derive(Debug)]
 pub struct Backtrace(Vec<Frame>);
diff --git a/crates/runtime/src/traphandlers/backtrace/riscv64.rs b/crates/runtime/src/traphandlers/backtrace/riscv64.rs
deleted file mode 100644
index bc3f5b084..000000000
--- a/crates/runtime/src/traphandlers/backtrace/riscv64.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-pub unsafe fn get_next_older_pc_from_fp(fp: usize) -> usize {
-    *(fp as *mut usize).offset(1)
-}
-
-// And the current frame pointer points to the next older frame pointer.
-pub const NEXT_OLDER_FP_FROM_FP_OFFSET: usize = 0;
-
-pub fn reached_entry_sp(fp: usize, entry_sp: usize) -> bool {
-    fp >= entry_sp
-}
-
-pub fn assert_entry_sp_is_aligned(sp: usize) {
-    assert_eq!(sp % 16, 0, "stack should always be aligned to 16");
-}
-
-pub fn assert_fp_is_aligned(fp: usize) {
-    assert_eq!(fp % 16, 0, "stack should always be aligned to 16");
-}
diff --git a/crates/runtime/src/traphandlers/backtrace/s390x.rs b/crates/runtime/src/traphandlers/backtrace/s390x.rs
deleted file mode 100644
index 7290c4850..000000000
--- a/crates/runtime/src/traphandlers/backtrace/s390x.rs
+++ /dev/null
@@ -1,22 +0,0 @@
-pub unsafe fn get_next_older_pc_from_fp(fp: usize) -> usize {
-    // The next older PC can be found in register %r14 at function entry, which
-    // was saved into slot 14 of the register save area pointed to by "FP" (the
-    // backchain pointer).
-    *(fp as *mut usize).offset(14)
-}
-
-// The next older "FP" (backchain pointer) was saved in the slot pointed to
-// by the current "FP".
-pub const NEXT_OLDER_FP_FROM_FP_OFFSET: usize = 0;
-
-pub fn reached_entry_sp(fp: usize, entry_sp: usize) -> bool {
-    fp > entry_sp
-}
-
-pub fn assert_entry_sp_is_aligned(sp: usize) {
-    assert_eq!(sp % 8, 0, "stack should always be aligned to 8");
-}
-
-pub fn assert_fp_is_aligned(fp: usize) {
-    assert_eq!(fp % 8, 0, "stack should always be aligned to 8");
-}
diff --git a/crates/runtime/src/traphandlers/backtrace/x86_64.rs b/crates/runtime/src/traphandlers/backtrace/x86_64.rs
deleted file mode 100644
index ed1a3eb4b..000000000
--- a/crates/runtime/src/traphandlers/backtrace/x86_64.rs
+++ /dev/null
@@ -1,20 +0,0 @@
-pub unsafe fn get_next_older_pc_from_fp(fp: usize) -> usize {
-    // The calling convention always pushes the return pointer (aka the PC of
-    // the next older frame) just before this frame.
-    *(fp as *mut usize).offset(1)
-}
-
-// And the current frame pointer points to the next older frame pointer.
-pub const NEXT_OLDER_FP_FROM_FP_OFFSET: usize = 0;
-
-pub fn reached_entry_sp(fp: usize, entry_sp: usize) -> bool {
-    fp >= entry_sp
-}
-
-pub fn assert_entry_sp_is_aligned(sp: usize) {
-    assert_eq!(sp % 16, 0, "stack should always be aligned to 16");
-}
-
-pub fn assert_fp_is_aligned(fp: usize) {
-    assert_eq!(fp % 16, 0, "stack should always be aligned to 16");
-}
diff --git a/crates/wasi-http/src/types_impl.rs b/crates/wasi-http/src/types_impl.rs
index 4ecb27481..74ee18c2f 100644
--- a/crates/wasi-http/src/types_impl.rs
+++ b/crates/wasi-http/src/types_impl.rs
@@ -145,20 +145,34 @@ impl<T: WasiHttpView> crate::bindings::http::types::HostFields for T {
         fields: Resource<HostFields>,
         name: String,
     ) -> wasmtime::Result<Vec<Vec<u8>>> {
+        let fields = get_fields(self.table(), &fields).context("[fields_get] getting fields")?;
+
         let header = match hyper::header::HeaderName::from_bytes(name.as_bytes()) {
             Ok(header) => header,
             Err(_) => return Ok(vec![]),
         };
 
-        let res = get_fields(self.table(), &fields)
-            .context("[fields_get] getting fields")?
-            .get_all(header)
+        if !fields.contains_key(&header) {
+            return Ok(vec![]);
+        }
+
+        let res = fields
+            .get_all(&header)
             .into_iter()
             .map(|val| val.as_bytes().to_owned())
             .collect();
         Ok(res)
     }
 
+    fn has(&mut self, fields: Resource<HostFields>, name: String) -> wasmtime::Result<bool> {
+        let fields = get_fields(self.table(), &fields).context("[fields_get] getting fields")?;
+
+        match hyper::header::HeaderName::from_bytes(name.as_bytes()) {
+            Ok(header) => Ok(fields.contains_key(&header)),
+            Err(_) => Ok(false),
+        }
+    }
+
     fn set(
         &mut self,
         fields: Resource<HostFields>,
diff --git a/crates/wasi-http/wit/deps/http/types.wit b/crates/wasi-http/wit/deps/http/types.wit
index c42f841bf..0f698e769 100644
--- a/crates/wasi-http/wit/deps/http/types.wit
+++ b/crates/wasi-http/wit/deps/http/types.wit
@@ -169,9 +169,16 @@ interface types {
       entries: list<tuple<field-key,field-value>>
     ) -> result<fields, header-error>;
 
-    /// Get all of the values corresponding to a key.
+    /// Get all of the values corresponding to a key. If the key is not present
+    /// in this `fields`, an empty list is returned. However, if the key is
+    /// present but empty, this is represented by a list with one or more
+    /// empty field-values present.
     get: func(name: field-key) -> list<field-value>;
 
+    /// Returns `true` when the key is present in this `fields`. If the key is
+    /// syntactically invalid, `false` is returned.
+    has: func(name: field-key) -> bool;
+
     /// Set all of the values for a key. Clears any existing values for that
     /// key, if they have been set.
     ///
@@ -191,7 +198,7 @@ interface types {
     append: func(name: field-key, value: field-value) -> result<_, header-error>;
 
     /// Retrieve the full set of keys and values in the Fields. Like the
-    /// constructor, the list represents each key-value pair. 
+    /// constructor, the list represents each key-value pair.
     ///
     /// The outer list represents each key-value pair in the Fields. Keys
     /// which have multiple values are represented by multiple entries in this
@@ -308,8 +315,9 @@ interface types {
     headers: func() -> headers;
   }
 
-  /// Parameters for making an HTTP Request. Each of these parameters is an
-  /// optional timeout, applicable to the transport layer of the HTTP protocol.
+  /// Parameters for making an HTTP Request. Each of these parameters is
+  /// currently an optional timeout applicable to the transport layer of the
+  /// HTTP protocol.
   ///
   /// These timeouts are separate from any the user may use to bound a
   /// blocking call to `wasi:io/poll.poll`.
diff --git a/crates/wasi/wit/deps/http/types.wit b/crates/wasi/wit/deps/http/types.wit
index c42f841bf..0f698e769 100644
--- a/crates/wasi/wit/deps/http/types.wit
+++ b/crates/wasi/wit/deps/http/types.wit
@@ -169,9 +169,16 @@ interface types {
       entries: list<tuple<field-key,field-value>>
     ) -> result<fields, header-error>;
 
-    /// Get all of the values corresponding to a key.
+    /// Get all of the values corresponding to a key. If the key is not present
+    /// in this `fields`, an empty list is returned. However, if the key is
+    /// present but empty, this is represented by a list with one or more
+    /// empty field-values present.
     get: func(name: field-key) -> list<field-value>;
 
+    /// Returns `true` when the key is present in this `fields`. If the key is
+    /// syntactically invalid, `false` is returned.
+    has: func(name: field-key) -> bool;
+
     /// Set all of the values for a key. Clears any existing values for that
     /// key, if they have been set.
     ///
@@ -191,7 +198,7 @@ interface types {
     append: func(name: field-key, value: field-value) -> result<_, header-error>;
 
     /// Retrieve the full set of keys and values in the Fields. Like the
-    /// constructor, the list represents each key-value pair. 
+    /// constructor, the list represents each key-value pair.
     ///
     /// The outer list represents each key-value pair in the Fields. Keys
     /// which have multiple values are represented by multiple entries in this
@@ -308,8 +315,9 @@ interface types {
     headers: func() -> headers;
   }
 
-  /// Parameters for making an HTTP Request. Each of these parameters is an
-  /// optional timeout, applicable to the transport layer of the HTTP protocol.
+  /// Parameters for making an HTTP Request. Each of these parameters is
+  /// currently an optional timeout applicable to the transport layer of the
+  /// HTTP protocol.
   ///
   /// These timeouts are separate from any the user may use to bound a
   /// blocking call to `wasi:io/poll.poll`.
diff --git a/crates/wasmtime/Cargo.toml b/crates/wasmtime/Cargo.toml
index a91a1c096..f2a2e1c9d 100644
--- a/crates/wasmtime/Cargo.toml
+++ b/crates/wasmtime/Cargo.toml
@@ -58,9 +58,6 @@ features = [
   "Win32_System_Diagnostics_Debug",
 ]
 
-[target.'cfg(target_arch = "s390x")'.dependencies]
-psm = "0.1.11"
-
 [dev-dependencies]
 tempfile = "3.0"
 wasmtime-wasi = { path = "../wasi", default-features = true }
@@ -161,4 +158,4 @@ addr2line = ["wasmtime-jit/addr2line"]
 
 # Export some symbols from the final binary to assist in debugging
 # Cranelift-generated code with native debuggers like GDB and LLDB.
-debug-builtins = ["wasmtime-runtime/debug-builtins"]
+debug-builtins = ["wasmtime-runtime/debug-builtins", "wasmtime-jit/debug-builtins"]
diff --git a/crates/wasmtime/src/func.rs b/crates/wasmtime/src/func.rs
index 3a23d51d4..8bb6cb288 100644
--- a/crates/wasmtime/src/func.rs
+++ b/crates/wasmtime/src/func.rs
@@ -1416,7 +1416,7 @@ fn enter_wasm<T>(store: &mut StoreContextMut<'_, T>) -> Option<usize> {
         return None;
     }
 
-    let stack_pointer = stack_pointer();
+    let stack_pointer = wasmtime_runtime::get_stack_pointer();
 
     // Determine the stack pointer where, after which, any wasm code will
     // immediately trap. This is checked on the entry to all wasm functions.
@@ -1443,43 +1443,6 @@ fn enter_wasm<T>(store: &mut StoreContextMut<'_, T>) -> Option<usize> {
     Some(prev_stack)
 }
 
-#[inline]
-fn stack_pointer() -> usize {
-    let stack_pointer: usize;
-    cfg_if::cfg_if! {
-        if #[cfg(target_arch = "x86_64")] {
-            unsafe {
-                std::arch::asm!(
-                    "mov {}, rsp",
-                    out(reg) stack_pointer,
-                    options(nostack,nomem),
-                );
-            }
-        } else if #[cfg(target_arch = "aarch64")] {
-            unsafe {
-                std::arch::asm!(
-                    "mov {}, sp",
-                    out(reg) stack_pointer,
-                    options(nostack,nomem),
-                );
-            }
-        } else if #[cfg(target_arch = "riscv64")] {
-            unsafe {
-                std::arch::asm!(
-                    "mv {}, sp",
-                    out(reg) stack_pointer,
-                    options(nostack,nomem),
-                );
-            }
-        } else if #[cfg(target_arch = "s390x")] {
-            stack_pointer = psm::stack_pointer() as usize;
-        } else {
-            compile_error!("unsupported platform");
-        }
-    }
-    stack_pointer
-}
-
 fn exit_wasm<T>(store: &mut StoreContextMut<'_, T>, prev_stack: Option<usize>) {
     // If we don't have a previous stack pointer to restore, then there's no
     // cleanup we need to perform here.
diff --git a/crates/wasmtime/src/lib.rs b/crates/wasmtime/src/lib.rs
index da6a6c3a0..152475f5d 100644
--- a/crates/wasmtime/src/lib.rs
+++ b/crates/wasmtime/src/lib.rs
@@ -476,7 +476,9 @@ pub use anyhow::{Error, Result};
 pub mod component;
 
 cfg_if::cfg_if! {
-    if #[cfg(unix)] {
+    if #[cfg(miri)] {
+        // no extensions on miri
+    } else if #[cfg(unix)] {
         pub mod unix;
     } else if #[cfg(windows)] {
         pub mod windows;
diff --git a/crates/wasmtime/src/v128.rs b/crates/wasmtime/src/v128.rs
index 25b48b1e3..595fe8061 100644
--- a/crates/wasmtime/src/v128.rs
+++ b/crates/wasmtime/src/v128.rs
@@ -7,6 +7,7 @@ use crate::store::StoreOpaque;
 use crate::{ValRaw, ValType, WasmTy};
 use std::cmp::Ordering;
 use std::fmt;
+use wasmtime_runtime::V128Abi;
 
 /// Representation of a 128-bit vector type, `v128`, for WebAssembly.
 ///
@@ -25,40 +26,10 @@ use std::fmt;
 /// [`TypedFunc`]: crate::TypedFunc
 #[derive(Copy, Clone)]
 #[repr(transparent)]
-pub struct V128(Abi);
-
-// NB: this is why this type is only suitable with `TypedFunc` on some platforms
-// and no other. See the documentation for each platform for why each ABI is
-// chosen.
-cfg_if::cfg_if! {
-    if #[cfg(target_arch = "x86_64")] {
-        // x86 vectors are represented with XMM registers which are represented
-        // with the `__m128i` type. This type is considered a vector type for
-        // ABI purposes which is implemented by Cranelift.
-        type Abi = std::arch::x86_64::__m128i;
-    } else if #[cfg(target_arch = "aarch64")] {
-        // AArch64 uses vector registered which here is used with a vector type.
-        // Note that the specific type shouldn't matter too much but the choice
-        // of using a vector is the significant part.
-        type Abi = std::arch::aarch64::uint8x16_t;
-    } else if #[cfg(target_arch = "riscv64")] {
-        // RISC-V currently always passes all vector arguments indirectly in the
-        // ABI. Currently Rust has no stable means of representing this meaning
-        // that a 128-bit representation is chosen here but it can't be passed
-        // directly to WebAssembly, for example, and must instead be passed
-        // through an array-call trampoline.
-        type Abi = u128;
-    } else if #[cfg(target_arch = "s390x")] {
-        // Currently Rust has no stable means of representing vector registers
-        // so like RISC-V at this time this uses a bland 128-bit representation.
-        type Abi = u128;
-    } else {
-        compile_error!("unsupported platform");
-    }
-}
+pub struct V128(V128Abi);
 
 union Reinterpret {
-    abi: Abi,
+    abi: V128Abi,
     u128: u128,
 }
 
@@ -112,7 +83,7 @@ impl Ord for V128 {
 // the documentation above in the `cfg_if!` for why this is conditional.
 #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
 unsafe impl WasmTy for V128 {
-    type Abi = Abi;
+    type Abi = V128Abi;
 
     #[inline]
     fn valtype() -> ValType {
diff --git a/supply-chain/imports.lock b/supply-chain/imports.lock
index fe9e858e9..3ac5dbc48 100644
--- a/supply-chain/imports.lock
+++ b/supply-chain/imports.lock
@@ -9,6 +9,10 @@ audited_as = "0.101.2"
 version = "0.103.0"
 audited_as = "0.101.4"
 
+[[unpublished.cranelift]]
+version = "0.104.0"
+audited_as = "0.102.1"
+
 [[unpublished.cranelift-bforest]]
 version = "0.102.0"
 audited_as = "0.101.2"
@@ -17,6 +21,10 @@ audited_as = "0.101.2"
 version = "0.103.0"
 audited_as = "0.101.4"
 
+[[unpublished.cranelift-bforest]]
+version = "0.104.0"
+audited_as = "0.102.1"
+
 [[unpublished.cranelift-codegen]]
 version = "0.102.0"
 audited_as = "0.101.2"
@@ -25,6 +33,10 @@ audited_as = "0.101.2"
 version = "0.103.0"
 audited_as = "0.101.4"
 
+[[unpublished.cranelift-codegen]]
+version = "0.104.0"
+audited_as = "0.102.1"
+
 [[unpublished.cranelift-codegen-meta]]
 version = "0.102.0"
 audited_as = "0.101.2"
@@ -33,6 +45,10 @@ audited_as = "0.101.2"
 version = "0.103.0"
 audited_as = "0.101.4"
 
+[[unpublished.cranelift-codegen-meta]]
+version = "0.104.0"
+audited_as = "0.102.1"
+
 [[unpublished.cranelift-codegen-shared]]
 version = "0.102.0"
 audited_as = "0.101.2"
@@ -41,6 +57,10 @@ audited_as = "0.101.2"
 version = "0.103.0"
 audited_as = "0.101.4"
 
+[[unpublished.cranelift-codegen-shared]]
+version = "0.104.0"
+audited_as = "0.102.1"
+
 [[unpublished.cranelift-control]]
 version = "0.102.0"
 audited_as = "0.101.2"
@@ -49,6 +69,10 @@ audited_as = "0.101.2"
 version = "0.103.0"
 audited_as = "0.101.4"
 
+[[unpublished.cranelift-control]]
+version = "0.104.0"
+audited_as = "0.102.1"
+
 [[unpublished.cranelift-entity]]
 version = "0.102.0"
 audited_as = "0.101.2"
@@ -57,6 +81,10 @@ audited_as = "0.101.2"
 version = "0.103.0"
 audited_as = "0.101.4"
 
+[[unpublished.cranelift-entity]]
+version = "0.104.0"
+audited_as = "0.102.1"
+
 [[unpublished.cranelift-frontend]]
 version = "0.102.0"
 audited_as = "0.101.2"
@@ -65,6 +93,10 @@ audited_as = "0.101.2"
 version = "0.103.0"
 audited_as = "0.101.4"
 
+[[unpublished.cranelift-frontend]]
+version = "0.104.0"
+audited_as = "0.102.1"
+
 [[unpublished.cranelift-interpreter]]
 version = "0.102.0"
 audited_as = "0.101.2"
@@ -73,6 +105,10 @@ audited_as = "0.101.2"
 version = "0.103.0"
 audited_as = "0.101.4"
 
+[[unpublished.cranelift-interpreter]]
+version = "0.104.0"
+audited_as = "0.102.1"
+
 [[unpublished.cranelift-isle]]
 version = "0.102.0"
 audited_as = "0.101.2"
@@ -81,6 +117,10 @@ audited_as = "0.101.2"
 version = "0.103.0"
 audited_as = "0.101.4"
 
+[[unpublished.cranelift-isle]]
+version = "0.104.0"
+audited_as = "0.102.1"
+
 [[unpublished.cranelift-jit]]
 version = "0.102.0"
 audited_as = "0.101.2"
@@ -89,6 +129,10 @@ audited_as = "0.101.2"
 version = "0.103.0"
 audited_as = "0.101.4"
 
+[[unpublished.cranelift-jit]]
+version = "0.104.0"
+audited_as = "0.102.1"
+
 [[unpublished.cranelift-module]]
 version = "0.102.0"
 audited_as = "0.101.2"
@@ -97,6 +141,10 @@ audited_as = "0.101.2"
 version = "0.103.0"
 audited_as = "0.101.4"
 
+[[unpublished.cranelift-module]]
+version = "0.104.0"
+audited_as = "0.102.1"
+
 [[unpublished.cranelift-native]]
 version = "0.102.0"
 audited_as = "0.101.2"
@@ -105,6 +153,10 @@ audited_as = "0.101.2"
 version = "0.103.0"
 audited_as = "0.101.4"
 
+[[unpublished.cranelift-native]]
+version = "0.104.0"
+audited_as = "0.102.1"
+
 [[unpublished.cranelift-object]]
 version = "0.102.0"
 audited_as = "0.101.2"
@@ -113,6 +165,10 @@ audited_as = "0.101.2"
 version = "0.103.0"
 audited_as = "0.101.4"
 
+[[unpublished.cranelift-object]]
+version = "0.104.0"
+audited_as = "0.102.1"
+
 [[unpublished.cranelift-reader]]
 version = "0.102.0"
 audited_as = "0.101.2"
@@ -121,6 +177,10 @@ audited_as = "0.101.2"
 version = "0.103.0"
 audited_as = "0.101.4"
 
+[[unpublished.cranelift-reader]]
+version = "0.104.0"
+audited_as = "0.102.1"
+
 [[unpublished.cranelift-serde]]
 version = "0.102.0"
 audited_as = "0.101.2"
@@ -129,6 +189,10 @@ audited_as = "0.101.2"
 version = "0.103.0"
 audited_as = "0.101.4"
 
+[[unpublished.cranelift-serde]]
+version = "0.104.0"
+audited_as = "0.102.1"
+
 [[unpublished.cranelift-wasm]]
 version = "0.102.0"
 audited_as = "0.101.2"
@@ -137,6 +201,10 @@ audited_as = "0.101.2"
 version = "0.103.0"
 audited_as = "0.101.4"
 
+[[unpublished.cranelift-wasm]]
+version = "0.104.0"
+audited_as = "0.102.1"
+
 [[unpublished.wasi-cap-std-sync]]
 version = "15.0.0"
 audited_as = "14.0.2"
@@ -145,6 +213,10 @@ audited_as = "14.0.2"
 version = "16.0.0"
 audited_as = "14.0.4"
 
+[[unpublished.wasi-cap-std-sync]]
+version = "17.0.0"
+audited_as = "15.0.1"
+
 [[unpublished.wasi-common]]
 version = "15.0.0"
 audited_as = "14.0.2"
@@ -153,6 +225,10 @@ audited_as = "14.0.2"
 version = "16.0.0"
 audited_as = "14.0.4"
 
+[[unpublished.wasi-common]]
+version = "17.0.0"
+audited_as = "15.0.1"
+
 [[unpublished.wasi-tokio]]
 version = "15.0.0"
 audited_as = "14.0.2"
@@ -161,6 +237,10 @@ audited_as = "14.0.2"
 version = "16.0.0"
 audited_as = "14.0.4"
 
+[[unpublished.wasi-tokio]]
+version = "17.0.0"
+audited_as = "15.0.1"
+
 [[unpublished.wasmtime]]
 version = "15.0.0"
 audited_as = "14.0.2"
@@ -169,6 +249,10 @@ audited_as = "14.0.2"
 version = "16.0.0"
 audited_as = "14.0.4"
 
+[[unpublished.wasmtime]]
+version = "17.0.0"
+audited_as = "15.0.1"
+
 [[unpublished.wasmtime-asm-macros]]
 version = "15.0.0"
 audited_as = "14.0.2"
@@ -177,6 +261,10 @@ audited_as = "14.0.2"
 version = "16.0.0"
 audited_as = "14.0.4"
 
+[[unpublished.wasmtime-asm-macros]]
+version = "17.0.0"
+audited_as = "15.0.1"
+
 [[unpublished.wasmtime-cache]]
 version = "15.0.0"
 audited_as = "14.0.2"
@@ -185,6 +273,10 @@ audited_as = "14.0.2"
 version = "16.0.0"
 audited_as = "14.0.4"
 
+[[unpublished.wasmtime-cache]]
+version = "17.0.0"
+audited_as = "15.0.1"
+
 [[unpublished.wasmtime-cli]]
 version = "15.0.0"
 audited_as = "14.0.2"
@@ -193,6 +285,10 @@ audited_as = "14.0.2"
 version = "16.0.0"
 audited_as = "14.0.4"
 
+[[unpublished.wasmtime-cli]]
+version = "17.0.0"
+audited_as = "15.0.1"
+
 [[unpublished.wasmtime-cli-flags]]
 version = "15.0.0"
 audited_as = "14.0.2"
@@ -201,6 +297,10 @@ audited_as = "14.0.2"
 version = "16.0.0"
 audited_as = "14.0.4"
 
+[[unpublished.wasmtime-cli-flags]]
+version = "17.0.0"
+audited_as = "15.0.1"
+
 [[unpublished.wasmtime-component-macro]]
 version = "15.0.0"
 audited_as = "14.0.2"
@@ -209,6 +309,10 @@ audited_as = "14.0.2"
 version = "16.0.0"
 audited_as = "14.0.4"
 
+[[unpublished.wasmtime-component-macro]]
+version = "17.0.0"
+audited_as = "15.0.1"
+
 [[unpublished.wasmtime-component-util]]
 version = "15.0.0"
 audited_as = "14.0.2"
@@ -217,6 +321,10 @@ audited_as = "14.0.2"
 version = "16.0.0"
 audited_as = "14.0.4"
 
+[[unpublished.wasmtime-component-util]]
+version = "17.0.0"
+audited_as = "15.0.1"
+
 [[unpublished.wasmtime-cranelift]]
 version = "15.0.0"
 audited_as = "14.0.2"
@@ -225,6 +333,10 @@ audited_as = "14.0.2"
 version = "16.0.0"
 audited_as = "14.0.4"
 
+[[unpublished.wasmtime-cranelift]]
+version = "17.0.0"
+audited_as = "15.0.1"
+
 [[unpublished.wasmtime-cranelift-shared]]
 version = "15.0.0"
 audited_as = "14.0.2"
@@ -233,6 +345,10 @@ audited_as = "14.0.2"
 version = "16.0.0"
 audited_as = "14.0.4"
 
+[[unpublished.wasmtime-cranelift-shared]]
+version = "17.0.0"
+audited_as = "15.0.1"
+
 [[unpublished.wasmtime-environ]]
 version = "15.0.0"
 audited_as = "14.0.2"
@@ -241,6 +357,10 @@ audited_as = "14.0.2"
 version = "16.0.0"
 audited_as = "14.0.4"
 
+[[unpublished.wasmtime-environ]]
+version = "17.0.0"
+audited_as = "15.0.1"
+
 [[unpublished.wasmtime-explorer]]
 version = "15.0.0"
 audited_as = "14.0.2"
@@ -249,6 +369,10 @@ audited_as = "14.0.2"
 version = "16.0.0"
 audited_as = "14.0.4"
 
+[[unpublished.wasmtime-explorer]]
+version = "17.0.0"
+audited_as = "15.0.1"
+
 [[unpublished.wasmtime-fiber]]
 version = "15.0.0"
 audited_as = "14.0.2"
@@ -257,6 +381,10 @@ audited_as = "14.0.2"
 version = "16.0.0"
 audited_as = "14.0.4"
 
+[[unpublished.wasmtime-fiber]]
+version = "17.0.0"
+audited_as = "15.0.1"
+
 [[unpublished.wasmtime-jit]]
 version = "15.0.0"
 audited_as = "14.0.2"
@@ -265,6 +393,10 @@ audited_as = "14.0.2"
 version = "16.0.0"
 audited_as = "14.0.4"
 
+[[unpublished.wasmtime-jit]]
+version = "17.0.0"
+audited_as = "15.0.1"
+
 [[unpublished.wasmtime-jit-debug]]
 version = "15.0.0"
 audited_as = "14.0.2"
@@ -273,6 +405,10 @@ audited_as = "14.0.2"
 version = "16.0.0"
 audited_as = "14.0.4"
 
+[[unpublished.wasmtime-jit-debug]]
+version = "17.0.0"
+audited_as = "15.0.1"
+
 [[unpublished.wasmtime-jit-icache-coherence]]
 version = "15.0.0"
 audited_as = "14.0.2"
@@ -281,6 +417,10 @@ audited_as = "14.0.2"
 version = "16.0.0"
 audited_as = "14.0.4"
 
+[[unpublished.wasmtime-jit-icache-coherence]]
+version = "17.0.0"
+audited_as = "15.0.1"
+
 [[unpublished.wasmtime-runtime]]
 version = "15.0.0"
 audited_as = "14.0.2"
@@ -289,6 +429,10 @@ audited_as = "14.0.2"
 version = "16.0.0"
 audited_as = "14.0.4"
 
+[[unpublished.wasmtime-runtime]]
+version = "17.0.0"
+audited_as = "15.0.1"
+
 [[unpublished.wasmtime-types]]
 version = "15.0.0"
 audited_as = "14.0.2"
@@ -297,6 +441,10 @@ audited_as = "14.0.2"
 version = "16.0.0"
 audited_as = "14.0.4"
 
+[[unpublished.wasmtime-types]]
+version = "17.0.0"
+audited_as = "15.0.1"
+
 [[unpublished.wasmtime-wasi]]
 version = "15.0.0"
 audited_as = "14.0.2"
@@ -305,6 +453,10 @@ audited_as = "14.0.2"
 version = "16.0.0"
 audited_as = "14.0.4"
 
+[[unpublished.wasmtime-wasi]]
+version = "17.0.0"
+audited_as = "15.0.1"
+
 [[unpublished.wasmtime-wasi-http]]
 version = "15.0.0"
 audited_as = "14.0.2"
@@ -313,6 +465,10 @@ audited_as = "14.0.2"
 version = "16.0.0"
 audited_as = "14.0.4"
 
+[[unpublished.wasmtime-wasi-http]]
+version = "17.0.0"
+audited_as = "15.0.1"
+
 [[unpublished.wasmtime-wasi-nn]]
 version = "15.0.0"
 audited_as = "14.0.2"
@@ -321,6 +477,10 @@ audited_as = "14.0.2"
 version = "16.0.0"
 audited_as = "14.0.4"
 
+[[unpublished.wasmtime-wasi-nn]]
+version = "17.0.0"
+audited_as = "15.0.1"
+
 [[unpublished.wasmtime-wasi-threads]]
 version = "15.0.0"
 audited_as = "14.0.2"
@@ -329,6 +489,10 @@ audited_as = "14.0.2"
 version = "16.0.0"
 audited_as = "14.0.4"
 
+[[unpublished.wasmtime-wasi-threads]]
+version = "17.0.0"
+audited_as = "15.0.1"
+
 [[unpublished.wasmtime-wast]]
 version = "15.0.0"
 audited_as = "14.0.2"
@@ -337,6 +501,10 @@ audited_as = "14.0.2"
 version = "16.0.0"
 audited_as = "14.0.4"
 
+[[unpublished.wasmtime-wast]]
+version = "17.0.0"
+audited_as = "15.0.1"
+
 [[unpublished.wasmtime-winch]]
 version = "15.0.0"
 audited_as = "14.0.2"
@@ -345,6 +513,10 @@ audited_as = "14.0.2"
 version = "16.0.0"
 audited_as = "14.0.4"
 
+[[unpublished.wasmtime-winch]]
+version = "17.0.0"
+audited_as = "15.0.1"
+
 [[unpublished.wasmtime-wit-bindgen]]
 version = "15.0.0"
 audited_as = "14.0.2"
@@ -353,6 +525,10 @@ audited_as = "14.0.2"
 version = "16.0.0"
 audited_as = "14.0.4"
 
+[[unpublished.wasmtime-wit-bindgen]]
+version = "17.0.0"
+audited_as = "15.0.1"
+
 [[unpublished.wasmtime-wmemcheck]]
 version = "15.0.0"
 audited_as = "14.0.2"
@@ -361,6 +537,10 @@ audited_as = "14.0.2"
 version = "16.0.0"
 audited_as = "14.0.4"
 
+[[unpublished.wasmtime-wmemcheck]]
+version = "17.0.0"
+audited_as = "15.0.1"
+
 [[unpublished.wiggle]]
 version = "15.0.0"
 audited_as = "14.0.2"
@@ -369,6 +549,10 @@ audited_as = "14.0.2"
 version = "16.0.0"
 audited_as = "14.0.4"
 
+[[unpublished.wiggle]]
+version = "17.0.0"
+audited_as = "15.0.1"
+
 [[unpublished.wiggle-generate]]
 version = "15.0.0"
 audited_as = "14.0.2"
@@ -377,6 +561,10 @@ audited_as = "14.0.2"
 version = "16.0.0"
 audited_as = "14.0.4"
 
+[[unpublished.wiggle-generate]]
+version = "17.0.0"
+audited_as = "15.0.1"
+
 [[unpublished.wiggle-macro]]
 version = "15.0.0"
 audited_as = "14.0.2"
@@ -385,6 +573,10 @@ audited_as = "14.0.2"
 version = "16.0.0"
 audited_as = "14.0.4"
 
+[[unpublished.wiggle-macro]]
+version = "17.0.0"
+audited_as = "15.0.1"
+
 [[unpublished.wiggle-test]]
 version = "0.0.0"
 audited_as = "0.1.0"
@@ -397,6 +589,10 @@ audited_as = "0.12.2"
 version = "0.14.0"
 audited_as = "0.12.4"
 
+[[unpublished.winch-codegen]]
+version = "0.15.0"
+audited_as = "0.13.1"
+
 [[publisher.aho-corasick]]
 version = "1.0.2"
 when = "2023-06-04"
@@ -577,6 +773,12 @@ when = "2023-11-01"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.cranelift]]
+version = "0.102.1"
+when = "2023-12-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.cranelift-bforest]]
 version = "0.101.2"
 when = "2023-10-26"
@@ -589,6 +791,12 @@ when = "2023-11-01"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.cranelift-bforest]]
+version = "0.102.1"
+when = "2023-12-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.cranelift-codegen]]
 version = "0.101.2"
 when = "2023-10-26"
@@ -601,6 +809,12 @@ when = "2023-11-01"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.cranelift-codegen]]
+version = "0.102.1"
+when = "2023-12-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.cranelift-codegen-meta]]
 version = "0.101.2"
 when = "2023-10-26"
@@ -613,6 +827,12 @@ when = "2023-11-01"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.cranelift-codegen-meta]]
+version = "0.102.1"
+when = "2023-12-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.cranelift-codegen-shared]]
 version = "0.101.2"
 when = "2023-10-26"
@@ -625,6 +845,12 @@ when = "2023-11-01"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.cranelift-codegen-shared]]
+version = "0.102.1"
+when = "2023-12-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.cranelift-control]]
 version = "0.101.2"
 when = "2023-10-26"
@@ -637,6 +863,12 @@ when = "2023-11-01"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.cranelift-control]]
+version = "0.102.1"
+when = "2023-12-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.cranelift-entity]]
 version = "0.101.2"
 when = "2023-10-26"
@@ -649,6 +881,12 @@ when = "2023-11-01"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.cranelift-entity]]
+version = "0.102.1"
+when = "2023-12-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.cranelift-frontend]]
 version = "0.101.2"
 when = "2023-10-26"
@@ -661,6 +899,12 @@ when = "2023-11-01"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.cranelift-frontend]]
+version = "0.102.1"
+when = "2023-12-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.cranelift-interpreter]]
 version = "0.101.2"
 when = "2023-10-26"
@@ -673,6 +917,12 @@ when = "2023-11-01"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.cranelift-interpreter]]
+version = "0.102.1"
+when = "2023-12-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.cranelift-isle]]
 version = "0.101.2"
 when = "2023-10-26"
@@ -685,6 +935,12 @@ when = "2023-11-01"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.cranelift-isle]]
+version = "0.102.1"
+when = "2023-12-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.cranelift-jit]]
 version = "0.101.2"
 when = "2023-10-26"
@@ -697,6 +953,12 @@ when = "2023-11-01"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.cranelift-jit]]
+version = "0.102.1"
+when = "2023-12-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.cranelift-module]]
 version = "0.101.2"
 when = "2023-10-26"
@@ -709,6 +971,12 @@ when = "2023-11-01"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.cranelift-module]]
+version = "0.102.1"
+when = "2023-12-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.cranelift-native]]
 version = "0.101.2"
 when = "2023-10-26"
@@ -721,6 +989,12 @@ when = "2023-11-01"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.cranelift-native]]
+version = "0.102.1"
+when = "2023-12-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.cranelift-object]]
 version = "0.101.2"
 when = "2023-10-26"
@@ -733,6 +1007,12 @@ when = "2023-11-01"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.cranelift-object]]
+version = "0.102.1"
+when = "2023-12-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.cranelift-reader]]
 version = "0.101.2"
 when = "2023-10-26"
@@ -745,6 +1025,12 @@ when = "2023-11-01"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.cranelift-reader]]
+version = "0.102.1"
+when = "2023-12-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.cranelift-serde]]
 version = "0.101.2"
 when = "2023-10-26"
@@ -757,6 +1043,12 @@ when = "2023-11-01"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.cranelift-serde]]
+version = "0.102.1"
+when = "2023-12-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.cranelift-wasm]]
 version = "0.101.2"
 when = "2023-10-26"
@@ -769,6 +1061,12 @@ when = "2023-11-01"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.cranelift-wasm]]
+version = "0.102.1"
+when = "2023-12-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.derive_arbitrary]]
 version = "1.3.0"
 when = "2023-03-13"
@@ -1109,6 +1407,12 @@ when = "2023-11-01"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasi-cap-std-sync]]
+version = "15.0.1"
+when = "2023-12-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasi-common]]
 version = "14.0.2"
 when = "2023-10-26"
@@ -1121,6 +1425,12 @@ when = "2023-11-01"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasi-common]]
+version = "15.0.1"
+when = "2023-12-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasi-tokio]]
 version = "14.0.2"
 when = "2023-10-26"
@@ -1133,6 +1443,12 @@ when = "2023-11-01"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasi-tokio]]
+version = "15.0.1"
+when = "2023-12-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasm-bindgen]]
 version = "0.2.87"
 when = "2023-06-12"
@@ -1383,6 +1699,12 @@ when = "2023-11-01"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmtime]]
+version = "15.0.1"
+when = "2023-12-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmtime-asm-macros]]
 version = "14.0.2"
 when = "2023-10-26"
@@ -1395,6 +1717,12 @@ when = "2023-11-01"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmtime-asm-macros]]
+version = "15.0.1"
+when = "2023-12-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmtime-cache]]
 version = "14.0.2"
 when = "2023-10-26"
@@ -1407,6 +1735,12 @@ when = "2023-11-01"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmtime-cache]]
+version = "15.0.1"
+when = "2023-12-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmtime-cli]]
 version = "14.0.2"
 when = "2023-10-26"
@@ -1419,6 +1753,12 @@ when = "2023-11-01"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmtime-cli]]
+version = "15.0.1"
+when = "2023-12-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmtime-cli-flags]]
 version = "14.0.2"
 when = "2023-10-26"
@@ -1431,6 +1771,12 @@ when = "2023-11-01"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmtime-cli-flags]]
+version = "15.0.1"
+when = "2023-12-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmtime-component-macro]]
 version = "14.0.2"
 when = "2023-10-26"
@@ -1443,6 +1789,12 @@ when = "2023-11-01"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmtime-component-macro]]
+version = "15.0.1"
+when = "2023-12-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmtime-component-util]]
 version = "14.0.2"
 when = "2023-10-26"
@@ -1455,6 +1807,12 @@ when = "2023-11-01"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmtime-component-util]]
+version = "15.0.1"
+when = "2023-12-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmtime-cranelift]]
 version = "14.0.2"
 when = "2023-10-26"
@@ -1467,6 +1825,12 @@ when = "2023-11-01"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmtime-cranelift]]
+version = "15.0.1"
+when = "2023-12-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmtime-cranelift-shared]]
 version = "14.0.2"
 when = "2023-10-26"
@@ -1479,6 +1843,12 @@ when = "2023-11-01"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmtime-cranelift-shared]]
+version = "15.0.1"
+when = "2023-12-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmtime-environ]]
 version = "14.0.2"
 when = "2023-10-26"
@@ -1491,6 +1861,12 @@ when = "2023-11-01"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmtime-environ]]
+version = "15.0.1"
+when = "2023-12-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmtime-explorer]]
 version = "14.0.2"
 when = "2023-10-26"
@@ -1503,6 +1879,12 @@ when = "2023-11-01"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmtime-explorer]]
+version = "15.0.1"
+when = "2023-12-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmtime-fiber]]
 version = "14.0.2"
 when = "2023-10-26"
@@ -1515,6 +1897,12 @@ when = "2023-11-01"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmtime-fiber]]
+version = "15.0.1"
+when = "2023-12-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmtime-jit]]
 version = "14.0.2"
 when = "2023-10-26"
@@ -1527,6 +1915,12 @@ when = "2023-11-01"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmtime-jit]]
+version = "15.0.1"
+when = "2023-12-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmtime-jit-debug]]
 version = "14.0.2"
 when = "2023-10-26"
@@ -1539,6 +1933,12 @@ when = "2023-11-01"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmtime-jit-debug]]
+version = "15.0.1"
+when = "2023-12-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmtime-jit-icache-coherence]]
 version = "14.0.2"
 when = "2023-10-26"
@@ -1551,6 +1951,12 @@ when = "2023-11-01"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmtime-jit-icache-coherence]]
+version = "15.0.1"
+when = "2023-12-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmtime-runtime]]
 version = "14.0.2"
 when = "2023-10-26"
@@ -1563,6 +1969,12 @@ when = "2023-11-01"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmtime-runtime]]
+version = "15.0.1"
+when = "2023-12-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmtime-types]]
 version = "14.0.2"
 when = "2023-10-26"
@@ -1575,6 +1987,12 @@ when = "2023-11-01"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmtime-types]]
+version = "15.0.1"
+when = "2023-12-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmtime-wasi]]
 version = "14.0.2"
 when = "2023-10-26"
@@ -1587,6 +2005,12 @@ when = "2023-11-01"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmtime-wasi]]
+version = "15.0.1"
+when = "2023-12-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmtime-wasi-http]]
 version = "14.0.2"
 when = "2023-10-26"
@@ -1599,6 +2023,12 @@ when = "2023-11-01"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmtime-wasi-http]]
+version = "15.0.1"
+when = "2023-12-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmtime-wasi-nn]]
 version = "14.0.2"
 when = "2023-10-26"
@@ -1611,6 +2041,12 @@ when = "2023-11-01"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmtime-wasi-nn]]
+version = "15.0.1"
+when = "2023-12-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmtime-wasi-threads]]
 version = "14.0.2"
 when = "2023-10-26"
@@ -1623,6 +2059,12 @@ when = "2023-11-01"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmtime-wasi-threads]]
+version = "15.0.1"
+when = "2023-12-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmtime-wast]]
 version = "14.0.2"
 when = "2023-10-26"
@@ -1635,6 +2077,12 @@ when = "2023-11-01"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmtime-wast]]
+version = "15.0.1"
+when = "2023-12-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmtime-winch]]
 version = "14.0.2"
 when = "2023-10-26"
@@ -1647,6 +2095,12 @@ when = "2023-11-01"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmtime-winch]]
+version = "15.0.1"
+when = "2023-12-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmtime-wit-bindgen]]
 version = "14.0.2"
 when = "2023-10-26"
@@ -1659,6 +2113,12 @@ when = "2023-11-01"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmtime-wit-bindgen]]
+version = "15.0.1"
+when = "2023-12-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmtime-wmemcheck]]
 version = "14.0.2"
 when = "2023-10-26"
@@ -1671,6 +2131,12 @@ when = "2023-11-01"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmtime-wmemcheck]]
+version = "15.0.1"
+when = "2023-12-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wast]]
 version = "66.0.2"
 when = "2023-10-14"
@@ -1753,6 +2219,12 @@ when = "2023-11-01"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wiggle]]
+version = "15.0.1"
+when = "2023-12-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wiggle-generate]]
 version = "14.0.2"
 when = "2023-10-26"
@@ -1765,6 +2237,12 @@ when = "2023-11-01"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wiggle-generate]]
+version = "15.0.1"
+when = "2023-12-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wiggle-macro]]
 version = "14.0.2"
 when = "2023-10-26"
@@ -1777,6 +2255,12 @@ when = "2023-11-01"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wiggle-macro]]
+version = "15.0.1"
+when = "2023-12-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wiggle-test]]
 version = "0.1.0"
 when = "2020-03-12"
@@ -1803,6 +2287,12 @@ when = "2023-11-01"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.winch-codegen]]
+version = "0.13.1"
+when = "2023-12-01"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.windows-sys]]
 version = "0.48.0"
 when = "2023-03-31"
diff --git a/winch/codegen/Cargo.toml b/winch/codegen/Cargo.toml
index e24628e35..05729e96f 100644
--- a/winch/codegen/Cargo.toml
+++ b/winch/codegen/Cargo.toml
@@ -4,7 +4,7 @@ name = "winch-codegen"
 description = "Winch code generation library"
 license = "Apache-2.0 WITH LLVM-exception"
 repository = "https://github.com/bytecodealliance/wasmtime"
-version = "0.14.0"
+version = "0.15.0"
 edition.workspace = true
 
 [lints]
diff --git a/winch/codegen/src/visitor.rs b/winch/codegen/src/visitor.rs
index 8a26cda94..4db1150e2 100644
--- a/winch/codegen/src/visitor.rs
+++ b/winch/codegen/src/visitor.rs
@@ -1246,6 +1246,11 @@ where
         };
 
         self.masm.jmp_table(&labels, index.into(), tmp);
+        // Save the original stack pointer offset; we will reset the stack
+        // pointer to this offset after jumping to each of the targets. Each
+        // jump might adjust the stack according to the base offset of the
+        // target.
+        let current_sp = self.masm.sp_offset();
 
         for (t, l) in targets
             .targets()
@@ -1255,14 +1260,26 @@ where
         {
             let control_index = control_index(t.unwrap(), self.control_frames.len());
             let frame = &mut self.control_frames[control_index];
+            // Reset the stack pointer to its original offset. This is needed
+            // because each jump will potentially adjust the stack pointer
+            // according to the base offset of the target.
+            self.masm.reset_stack_pointer(current_sp);
 
             // NB: We don't perform any result handling as it was
             // already taken care of above before jumping to the
             // jump table.
             self.masm.bind(*l);
+            // Ensure that the stack pointer is correctly positioned before
+            // jumping to the jump table code.
+            let (_, offset) = frame.base_stack_len_and_sp();
+            self.masm.ensure_sp_for_jump(offset);
             self.masm.jmp(*frame.label());
             frame.set_as_target();
         }
+        // Finally reset the stack pointer to the original location.
+        // The reachability analysis, will ensure it's correctly located
+        // once reachability is restored.
+        self.masm.reset_stack_pointer(current_sp);
         self.context.reachable = false;
         self.context.free_reg(index.reg);
         self.context.free_reg(tmp);


Test patch:
diff --git a/crates/test-programs/src/bin/api_proxy.rs b/crates/test-programs/src/bin/api_proxy.rs
index 33114699b..a0b26cfd7 100644
--- a/crates/test-programs/src/bin/api_proxy.rs
+++ b/crates/test-programs/src/bin/api_proxy.rs
@@ -24,7 +24,7 @@ impl bindings::exports::wasi::http::incoming_handler::Guest for T {
         let req_hdrs = request.headers();
 
         assert!(
-            req_hdrs.get(&header).is_empty(),
+            !req_hdrs.has(&header),
             "forbidden `custom-forbidden-header` found in request"
         );
 
@@ -32,7 +32,7 @@ impl bindings::exports::wasi::http::incoming_handler::Guest for T {
         assert!(req_hdrs.append(&header, &b"no".to_vec()).is_err());
 
         assert!(
-            req_hdrs.get(&header).is_empty(),
+            !req_hdrs.has(&header),
             "append of forbidden header succeeded"
         );
 
diff --git a/tests/misc_testsuite/winch/br_table.wast b/tests/misc_testsuite/winch/br_table.wast
index 54784e13d..5cef4ec58 100644
--- a/tests/misc_testsuite/winch/br_table.wast
+++ b/tests/misc_testsuite/winch/br_table.wast
@@ -10,6 +10,25 @@
   (func (export "type-i64")
     (block (drop (i64.ctz (br_table 0 0 (i32.const 0)))))
   )
+  (func (export "type-f32")
+    (block (drop (f32.neg (br_table 0 0 (i32.const 0)))))
+  )
+  (func (export "type-f64")
+    (block (drop (f64.neg (br_table 0 0 (i32.const 0)))))
+  )
+
+  (func (export "type-i32-value") (result i32)
+    (block (result i32) (i32.ctz (br_table 0 0 (i32.const 1) (i32.const 0))))
+  )
+  (func (export "type-i64-value") (result i64)
+    (block (result i64) (i64.ctz (br_table 0 0 (i64.const 2) (i32.const 0))))
+  )
+  (func (export "type-f32-value") (result f32)
+    (block (result f32) (f32.neg (br_table 0 0 (f32.const 3) (i32.const 0))))
+  )
+  (func (export "type-f64-value") (result f64)
+    (block (result f64) (f64.neg (br_table 0 0 (f64.const 4) (i32.const 0))))
+  )
 
   (func (export "empty") (param i32) (result i32)
     (block (br_table 0 (local.get 0)) (return (i32.const 21)))
@@ -1123,6 +1142,12 @@
 
 (assert_return (invoke "type-i32"))
 (assert_return (invoke "type-i64"))
+(assert_return (invoke "type-f32"))
+(assert_return (invoke "type-f64"))
+(assert_return (invoke "type-i32-value") (i32.const 1))
+(assert_return (invoke "type-i64-value") (i64.const 2))
+(assert_return (invoke "type-f32-value") (f32.const 3))
+(assert_return (invoke "type-f64-value") (f64.const 4))
 
 (assert_return (invoke "empty" (i32.const 0)) (i32.const 22))
 (assert_return (invoke "empty" (i32.const 1)) (i32.const 22))
diff --git a/tests/misc_testsuite/winch/misc.wast b/tests/misc_testsuite/winch/misc.wast
new file mode 100644
index 000000000..31f5098d4
--- /dev/null
+++ b/tests/misc_testsuite/winch/misc.wast
@@ -0,0 +1,14 @@
+;; Additional run tests for Winch not covered in the official spec test suite.
+
+(module
+  (func (export "br-table-ensure-sp") (result i32)
+    (block (result i32)
+       (i32.const 0)
+    )
+    (i32.const 0)
+    (i32.const 0)
+    (br_table 0)
+  )
+)
+
+(assert_return (invoke "br-table-ensure-sp") (i32.const 0))
diff --git a/winch/filetests/filetests/x64/br_table/ensure_sp_state.wat b/winch/filetests/filetests/x64/br_table/ensure_sp_state.wat
new file mode 100644
index 000000000..09bd310eb
--- /dev/null
+++ b/winch/filetests/filetests/x64/br_table/ensure_sp_state.wat
@@ -0,0 +1,34 @@
+;;! target = "x86_64"
+
+(module
+  (func (export "") (result i32)
+    block (result i32)
+       i32.const 0
+    end
+    i32.const 0
+    i32.const 0
+    br_table 0
+  )
+)
+;;    0:	 55                   	push	rbp
+;;    1:	 4889e5               	mov	rbp, rsp
+;;    4:	 4883ec08             	sub	rsp, 8
+;;    8:	 4c893424             	mov	qword ptr [rsp], r14
+;;    c:	 b800000000           	mov	eax, 0
+;;   11:	 4883ec04             	sub	rsp, 4
+;;   15:	 890424               	mov	dword ptr [rsp], eax
+;;   18:	 b900000000           	mov	ecx, 0
+;;   1d:	 b800000000           	mov	eax, 0
+;;   22:	 ba00000000           	mov	edx, 0
+;;   27:	 39ca                 	cmp	edx, ecx
+;;   29:	 0f42ca               	cmovb	ecx, edx
+;;   2c:	 4c8d1d0a000000       	lea	r11, [rip + 0xa]
+;;   33:	 4963148b             	movsxd	rdx, dword ptr [r11 + rcx*4]
+;;   37:	 4901d3               	add	r11, rdx
+;;   3a:	 41ffe3               	jmp	r11
+;;   3d:	 0400                 	add	al, 0
+;;   3f:	 0000                 	add	byte ptr [rax], al
+;;   41:	 4883c404             	add	rsp, 4
+;;   45:	 4883c408             	add	rsp, 8
+;;   49:	 5d                   	pop	rbp
+;;   4a:	 c3                   	ret	
diff --git a/winch/filetests/filetests/x64/br_table/stack_handling.wat b/winch/filetests/filetests/x64/br_table/stack_handling.wat
index e16c598b4..ee945632f 100644
--- a/winch/filetests/filetests/x64/br_table/stack_handling.wat
+++ b/winch/filetests/filetests/x64/br_table/stack_handling.wat
@@ -26,13 +26,16 @@
 ;;   33:	 49630c83             	movsxd	rcx, dword ptr [r11 + rax*4]
 ;;   37:	 4901cb               	add	r11, rcx
 ;;   3a:	 41ffe3               	jmp	r11
-;;   3d:	 0c00                 	or	al, 0
+;;   3d:	 1a00                 	sbb	al, byte ptr [rax]
 ;;   3f:	 0000                 	add	byte ptr [rax], al
-;;   41:	 1000                 	adc	byte ptr [rax], al
+;;   41:	 1100                 	adc	dword ptr [rax], eax
 ;;   43:	 0000                 	add	byte ptr [rax], al
-;;   45:	 0c00                 	or	al, 0
+;;   45:	 1a00                 	sbb	al, byte ptr [rax]
 ;;   47:	 0000                 	add	byte ptr [rax], al
-;;   49:	 4883c404             	add	rsp, 4
-;;   4d:	 4883c410             	add	rsp, 0x10
-;;   51:	 5d                   	pop	rbp
-;;   52:	 c3                   	ret	
+;;   49:	 e909000000           	jmp	0x57
+;;   4e:	 4883c404             	add	rsp, 4
+;;   52:	 e904000000           	jmp	0x5b
+;;   57:	 4883c404             	add	rsp, 4
+;;   5b:	 4883c410             	add	rsp, 0x10
+;;   5f:	 5d                   	pop	rbp
+;;   60:	 c3                   	ret