Repository: bytecodealliance/wasmtime
Pull request #8555 (2024-05-05 18:19:49)

Update documentation and example for no_std
This commit has a few sub-parts aimed at improving and updating the documentation/examples around Wasmtime's support for no_std now that the implementation is landed in https://github.com/bytecodealliance/wasmtime/pull/8533. Namely this includes:

* Updating our tier-related documentation with developments in the last year or so
* Updating platform-support documentation with references to tiers and rewriting the no_std section
* Updating the min-platform example to build on no_std
* Adding `wasmtime-platform.h` as a release artifact.


Patch:
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 360a7b295..ec8c7d959 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -477,16 +477,6 @@ jobs:
     - run: cargo fuzz build --dev -s none --fuzz-dir ./cranelift/isle/fuzz
     - run: cargo fuzz build --dev -s none --fuzz-dir ./crates/environ/fuzz --features component-model
 
-    # Verify the "min platform" example still works.
-    #
-    # Afterwards make sure the generated header file is up to date by ensuring
-    # that the regeneration process didn't change anything in-tree.
-    - run: cargo install cbindgen --vers "^0.26" --locked
-    - run: rustup component add rust-src
-    - run: ./build.sh ./embedding/x86_64-unknown-unknown.json
-      working-directory: ./examples/min-platform
-    - run: git diff --exit-code
-
     # common logic to cancel the entire run if this job fails
     - run: gh run cancel ${{ github.run_id }}
       if: failure() && github.event_name != 'pull_request'
@@ -694,6 +684,38 @@ jobs:
       env:
         GH_TOKEN: ${{ github.token }}
 
+  # Verify the "min platform" example still works.
+  test-min-platform-example:
+    name: Test the min-platform example
+    needs: determine
+    if: needs.determine.outputs.run-full
+    runs-on: ubuntu-latest
+    steps:
+    - uses: actions/checkout@v4
+      with:
+        submodules: true
+    - uses: ./.github/actions/install-rust
+    - run: cargo install cbindgen --vers "^0.26" --locked
+    - run: rustup target add x86_64-unknown-none
+    - run: ./build.sh x86_64-unknown-none
+      working-directory: ./examples/min-platform
+
+    # Afterwards make sure the generated header file is up to date by ensuring
+    # that the regeneration process didn't change anything in-tree.
+    - run: git diff --exit-code
+
+    # Add the `wasmtime-platform.h` file as a release artifact
+    - uses: actions/upload-artifact@v4
+      with:
+        name: wasmtime-platform-header
+        path: examples/min-platform/embedding/wasmtime-platform.h
+
+    # common logic to cancel the entire run if this job fails
+    - run: gh run cancel ${{ github.run_id }}
+      if: failure() && github.event_name != 'pull_request'
+      env:
+        GH_TOKEN: ${{ github.token }}
+
 
   build-wasmtime-target-wasm32:
     name: Build wasmtime-target-wasm32
@@ -915,6 +937,7 @@ jobs:
       - miri
       - build-preview1-component-adapter
       - build-wasmtime-target-wasm32
+      - test-min-platform-example
     if: always()
     steps:
     - name: Successful test and build
diff --git a/Cargo.lock b/Cargo.lock
index 56781f00d..a434d46a4 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1347,7 +1347,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0"
 dependencies = [
  "fallible-iterator",
- "indexmap 2.0.0",
+ "indexmap 2.2.6",
  "stable_deref_trait",
 ]
 
@@ -1363,7 +1363,7 @@ dependencies = [
  "futures-sink",
  "futures-util",
  "http",
- "indexmap 2.0.0",
+ "indexmap 2.2.6",
  "slab",
  "tokio",
  "tokio-util",
@@ -1547,9 +1547,9 @@ dependencies = [
 
 [[package]]
 name = "indexmap"
-version = "2.0.0"
+version = "2.2.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d"
+checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
 dependencies = [
  "equivalent",
  "hashbrown 0.14.3",
@@ -1853,6 +1853,7 @@ dependencies = [
  "anyhow",
  "libloading",
  "object 0.33.0",
+ "wasmtime",
 ]
 
 [[package]]
@@ -1927,7 +1928,7 @@ checksum = "d8dd6c0cdf9429bce006e1362bfce61fa1bfd8c898a643ed8d2b471934701d3d"
 dependencies = [
  "crc32fast",
  "hashbrown 0.14.3",
- "indexmap 2.0.0",
+ "indexmap 2.2.6",
  "memchr",
 ]
 
@@ -2890,7 +2891,7 @@ version = "0.22.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0c9ffdf896f8daaabf9b66ba8e77ea1ed5ed0f72821b398aba62352e95062951"
 dependencies = [
- "indexmap 2.0.0",
+ "indexmap 2.2.6",
  "serde",
  "serde_spanned",
  "toml_datetime",
@@ -3253,7 +3254,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0fd83062c17b9f4985d438603cde0a5e8c5c8198201a6937f778b607924c7da2"
 dependencies = [
  "anyhow",
- "indexmap 2.0.0",
+ "indexmap 2.2.6",
  "serde",
  "serde_derive",
  "serde_json",
@@ -3269,7 +3270,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "84435ac34e29de6154fdb180ebbe2bdde96336f7ad0990003c43a21bd441303f"
 dependencies = [
  "anyhow",
- "indexmap 2.0.0",
+ "indexmap 2.2.6",
  "serde",
  "serde_derive",
  "serde_json",
@@ -3301,7 +3302,7 @@ dependencies = [
  "anyhow",
  "arbitrary",
  "flagset",
- "indexmap 2.0.0",
+ "indexmap 2.2.6",
  "leb128",
  "wasm-encoder 0.206.0",
 ]
@@ -3353,7 +3354,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "84e5df6dba6c0d7fafc63a450f1738451ed7a0b52295d83e868218fa286bf708"
 dependencies = [
  "bitflags 2.4.1",
- "indexmap 2.0.0",
+ "indexmap 2.2.6",
  "semver",
 ]
 
@@ -3366,7 +3367,7 @@ dependencies = [
  "ahash",
  "bitflags 2.4.1",
  "hashbrown 0.14.3",
- "indexmap 2.0.0",
+ "indexmap 2.2.6",
  "semver",
 ]
 
@@ -3403,7 +3404,7 @@ dependencies = [
  "fxprof-processed-profile",
  "gimli",
  "hashbrown 0.14.3",
- "indexmap 2.0.0",
+ "indexmap 2.2.6",
  "ittapi",
  "libc",
  "libm",
@@ -3658,7 +3659,7 @@ dependencies = [
  "cranelift-entity",
  "env_logger",
  "gimli",
- "indexmap 2.0.0",
+ "indexmap 2.2.6",
  "log",
  "object 0.33.0",
  "postcard",
@@ -3935,7 +3936,7 @@ version = "22.0.0"
 dependencies = [
  "anyhow",
  "heck",
- "indexmap 2.0.0",
+ "indexmap 2.2.6",
  "wit-parser 0.206.0",
 ]
 
@@ -4301,7 +4302,7 @@ checksum = "d8a39a15d1ae2077688213611209849cad40e9e5cccf6e61951a425850677ff3"
 dependencies = [
  "anyhow",
  "heck",
- "indexmap 2.0.0",
+ "indexmap 2.2.6",
  "wasm-metadata 0.201.0",
  "wit-bindgen-core",
  "wit-component 0.201.0",
@@ -4329,7 +4330,7 @@ checksum = "421c0c848a0660a8c22e2fd217929a0191f14476b68962afd2af89fd22e39825"
 dependencies = [
  "anyhow",
  "bitflags 2.4.1",
- "indexmap 2.0.0",
+ "indexmap 2.2.6",
  "log",
  "serde",
  "serde_derive",
@@ -4348,7 +4349,7 @@ checksum = "81e1ae4cace3706e29a8cf1f728b792255ecdc02653b9f4130ab6cc64f435a67"
 dependencies = [
  "anyhow",
  "bitflags 2.4.1",
- "indexmap 2.0.0",
+ "indexmap 2.2.6",
  "log",
  "serde",
  "serde_derive",
@@ -4367,7 +4368,7 @@ checksum = "196d3ecfc4b759a8573bf86a9b3f8996b304b3732e4c7de81655f875f6efdca6"
 dependencies = [
  "anyhow",
  "id-arena",
- "indexmap 2.0.0",
+ "indexmap 2.2.6",
  "log",
  "semver",
  "serde",
@@ -4385,7 +4386,7 @@ checksum = "d226de4e95e052cb664d2bdad2f31d9cad5117038a3439568f078d1afd8842fe"
 dependencies = [
  "anyhow",
  "id-arena",
- "indexmap 2.0.0",
+ "indexmap 2.2.6",
  "log",
  "semver",
  "serde",
diff --git a/docs/stability-platform-support.md b/docs/stability-platform-support.md
index a725e6a3d..bdd242815 100644
--- a/docs/stability-platform-support.md
+++ b/docs/stability-platform-support.md
@@ -1,139 +1,81 @@
 # Platform Support
 
-The `wasmtime` project is a configurable and lightweight runtime for WebAssembly
-which has a number of ways it can be configured. Not all features are supported
-on all platforms, but it is intended that `wasmtime` can run in some capacity on
-almost all platforms! The matrix of what's being tested, what works, and what's
-supported where is evolving over time, and this document hopes to capture a
-snapshot of what the current state of the world looks like.
-
-All features of `wasmtime` should work on the following platforms:
-
-* Linux x86\_64
-* Linux aarch64
-* macOS x86\_64
+This page is intended to give a high-level overview of Wasmtime's platform
+support along with some aspirations of Wasmtime. For more details see the
+documentation on [tiers of stability](./stability-tiers.md) which has specific
+information about what's supported in Wasmtime on a per-matrix-combination
+basis.
+
+Wasmtime strives to support hardware that anyone wants to run WebAssembly on.
+Maintainers of Wasmtime support a number of "major" platforms themselves but
+porting work may be required to support platforms that maintainers are not
+themselves familiar with. Out-of-the box Wasmtime supports:
+
+* Linux x86\_64, aarch64, s390x, and riscv64
+* macOS x86\_64, aarch64
 * Windows x86\_64
 
-For more detailed information about supported platforms, please check out the
-sections below!
+Other platforms such as Android, iOS, and the BSD family of OSes are not
+built-in yet. PRs for porting are welcome and maintainers are happy to add more
+entries to the CI matrix for these platforms.
 
-## JIT compiler support
+## Compiler Support
 
-The JIT compiler, backed by Cranelift, supports the x86\_64 and aarch64
-architectures at this time. Support for at least ARM and x86 is planned as well.
+Cranelift supports x86\_64, aarch64, s390x, and riscv64. No 32-bit platform is
+currently supported. Building a new backend for Cranelift is a relatively large
+undertaking which maintainers are willing to help with but it's recommended to
+reach out to Cranelift maintainers first to discuss this.
 
-Usage of the JIT compiler will require a host operating system which supports
-creating executable memory pages on-the-fly. In Rust terms this generally means
-that `std` needs to be supported on this platform.
+Winch supports x86\_64. The aarch64 backend is in development. Winch is built on
+Cranelift's support for emitting instructions so Winch's possible backend list
+is currently limited to what Cranelift supports.
+
+Usage of the Cranelift or Winch requires a host operating system which supports
+creating executable memory pages on-the-fly. Support for statically linking in a
+single precompiled module is not supported at this time.
+
+Both Cranelift and Winch can be used either in AOT or JIT mode. In AOT mode one
+process precompiles a module/component and then loads it into another process.
+In JIT mode this is all done within the same process.
+
+Neither Cranelift nor Winch support tiering at this time in the sense of having
+a WebAssembly module start from a Winch compilation and automatically switch to
+a Cranelift compilation. Modules are either entirely compiled with Winch or
+Cranelift.
 
 ## Interpreter support
 
 At this time `wasmtime` does not have a mode in which it simply interprets
-WebAssembly code. It is planned to add support for an interpreter, however, and
+WebAssembly code. It is desired to add support for an interpreter, however, and
 this will have minimal system dependencies. It is planned that the system will
 need to support some form of dynamic memory allocation, but other than that not
 much else will be needed.
 
-## What about `#[no_std]`?
-
-The `wasmtime` project does not currently use `#[no_std]` for its crates, but
-this is not because it won't support it! For information on building Wasmtime
-for a custom target see [the minimal build
-  documentation](./examples-minimal.md). Please [open an
-issue](https://github.com/bytecodealliance/wasmtime/issues/new) on the
-`wasmtime` repository to request support for a specific platform.
-
-This is a common question we are asked, however, so to provide some more context
-on why Wasmtime is the way it is, here's some responses to frequent points
-raised about `#![no_std]`:
-
-* **What if my platform doesn't have `std`?** - For platforms without support
-  for the Rust standard library the JIT compiler of Wasmtime often won't run on
-  the platform as well. The JIT compiler requires `mmap` (or an equivalent), and
-  presence of `mmap` often implies presence of a libc which means Rust's `std`
-  library works.
-
-  Cargo's [`-Z build-std` feature][zbuild-std] feature is also intended to help
-  easily build the standard library for all platforms. With this feature you can
-  recompile the standard library (using Nightly Rust for now) with a [custom
-  target specification][custom-target] if necessary. Additionally the intention
-  at this time is to get `std` building for all platforms, regardless of what
-  the platform actually supports. This change is taking time to implement, but
-  [rust-lang/rust#74033] is an example of this support growing over time.
-
-  We're also interested in running Wasmtime without a JIT compiler in the
-  future, but that is not implemented at this time. Implementing this will
-  require a lot more work than tagging crates `#![no_std]`. The Wasmtime
-  developers are also very interested in supporting as many targets as possible,
-  so if Wasmtime doesn't work on your platform yet we'd love to learn why and
-  what we can do to support that platform, but the conversation here is
-  typically more nuanced than simply making `wasmtime` compile without `std`.
-
-* **Doesn't `#![no_std]` have smaller binary sizes?** - There's a lot of factors
-  that affect binary size in Rust. Compilation options are a huge one but beyond
-  that idioms and libraries linked matter quite a lot as well. Code is not
-  inherently large when using `std` instead of `core`, it's just that often code
-  using `std` has more dependencies (like `std::thread`) which requires code to
-  bind. Code size improvements can be made to code using `std` and `core`
-  equally, and switching to `#![no_std]` is not a silver bullet for compile
-  sizes.
-
-* **The patch to switch to `#![no_std]` is small, why not accept it?** - PRs to
-  switch to `#![no_std]` are often relatively small or don't impact too many
-  parts of the system. There's a lot more to developing a `#![no_std]`
-  WebAssembly runtime than switching a few crates, however. Maintaining a
-  `#![no_std]` library over time has a number of costs associated with it:
-
-  * Rust has no stable way to diagnose `no_std` errors in an otherwise `std`
-    build, which means that to support this feature it must be tested on CI with
-    a `no_std` target. This is costly in terms of CI time, CI maintenance, and
-    developers having to do extra builds to avoid CI errors. Note that this
-    isn't *more* costly than any other platform supported by Wasmtime, but it's
-    a cost nonetheless.
-
-  * Idioms in `#![no_std]` are quite different than normal Rust code. You'll
-    import from different crates (`core` instead of `std`) and data structures
-    have to all be manually imported from `alloc`. These idioms are difficult to
-    learn for newcomers to the project and are not well documented in the
-    ecosystem. This cost of development and maintenance is not unique to
-    Wasmtime but in general affects the `#![no_std]` ecosystem at large,
-    unfortunately.
-
-  * Currently Wasmtime does not have a target use case which requires
-    `#![no_std]` support, so it's hard to justify these costs of development.
-    We're very interested in supporting as many use cases and targets as
-    possible, but the decision to support a target needs to take into account
-    the costs associated so we can plan accordingly. Effectively we need to have
-    a goal in mind instead of taking on the costs of `#![no_std]` blindly.
-
-  * At this time it's not clear whether `#![no_std]` will be needed long-term,
-    so eating short-term costs may not pay off in the long run. Features like
-    Cargo's [`-Z build-std`][zbuild-std] may mean that `#![no_std]` is less and
-    less necessary over time.
-
-* **How can Wasmtime support `#![no_std]` if it uses X?** - Wasmtime as-is today
-  is not suitable for many `#![no_std]` contexts. For example it might use
-  `mmap` for allocating JIT code memory, leverage threads for caching, or use
-  thread locals when calling into JIT code. These features are difficult to
-  support in their full fidelity on all platforms, but the Wasmtime developers
-  are very much aware of this! Wasmtime is intended to be configurable where
-  many of these features are compile-time or runtime options. For example caches
-  can be disabled, JITs can be removed and replaced with interpreters, or users
-  could provide a callback to allocate memory instead of using the OS.
-  This is sort of a long-winded way of saying that Wasmtime on the surface may
-  today look like it won't support `#![no_std]`, but this is almost always
-  simply a matter of time and development priorities rather than a fundamental
-  reason why Wasmtime *couldn't* support `#![no_std]`.
-
-Note that at this time these guidelines apply not only to Wasmtime but also to
-some of its dependencies developed by the Bytecode Alliance such as the
-[wasm-tools repository](https://github.com/bytecodealliance/wasm-tools). These
-projects don't have the same runtime requirements as Wasmtime (e.g. `wasmparser`
-doesn't need `mmap`), but we're following the same guidelines above at this
-time. Patches to add `#![no_std]`, while possibly small, incur many of the same
-costs and also have an unclear longevity as features like [`-Z
-build-std`][zbuild-std] evolve.
-
-[zbuild-std]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#build-std
-[custom-target]: https://doc.rust-lang.org/rustc/targets/custom.html
-[rust-lang/rust#74033]: https://github.com/rust-lang/rust/pull/74033
+## Support for `#![no_std]`
+
+The `wasmtime` crate supports being build on no\_std platforms in Rust, but
+only for a subset of its compile-time Cargo features. Currently supported
+Cargo features are:
+
+* `runtime`
+* `gc`
+* `component-model`
+
+This notably does not include the `default` feature which means that when
+depending on Wasmtime you'll need to specify `default-features = false`. This
+also notably does not include Cranelift or Winch at this time meaning that
+no\_std platforms must be used in AOT mode where the module is precompiled
+elsewhere.
+
+Wasmtime's support for no\_std requires the embedder to implement the equivalent
+of a C header file to indicate how to perform basic OS operations such as
+allocating virtual memory. This API can be found as `wasmtime-platform.h` in
+Wasmtime's release artifacts or at
+`examples/min-platform/embedding/wasmtime-platform.h` in the source tree. Note
+that this API is not guaranteed to be stable at this time, it'll need to be
+updated when Wasmtime is updated.
+
+Wasmtime's runtime will use the symbols defined in this file meaning that if
+they're not defined then a link-time error will be generated. Embedders are
+required to implement these functions in accordance with their documentation to
+enable Wasmtime to run on custom platforms.
diff --git a/docs/stability-tiers.md b/docs/stability-tiers.md
index 042c4f7e1..97b290997 100644
--- a/docs/stability-tiers.md
+++ b/docs/stability-tiers.md
@@ -25,8 +25,7 @@ For explanations of what each tier means see below.
 | Target               | `x86_64-apple-darwin`                      |
 | Target               | `x86_64-pc-windows-msvc`                   |
 | Target               | `x86_64-unknown-linux-gnu`                 |
-| WASI Proposal        | `wasi_snapshot_preview1`                   |
-| WASI Proposal        | `wasi_unstable`                            |
+| Compiler Backend     | Cranelift                                  |
 | WebAssembly Proposal | [`mutable-globals`]                        |
 | WebAssembly Proposal | [`sign-extension-ops`]                     |
 | WebAssembly Proposal | [`nontrapping-float-to-int-conversion`]    |
@@ -34,6 +33,19 @@ For explanations of what each tier means see below.
 | WebAssembly Proposal | [`bulk-memory`]                            |
 | WebAssembly Proposal | [`reference-types`]                        |
 | WebAssembly Proposal | [`simd`]                                   |
+| WebAssembly Proposal | [`component-model`]                        |
+| WebAssembly Proposal | [`relaxed-simd`]                           |
+| WebAssembly Proposal | [`multi-memory`]                           |
+| WebAssembly Proposal | [`threads`]                                |
+| WebAssembly Proposal | [`tail-call`]                              |
+| WASI Proposal        | [`wasi-io`]                                |
+| WASI Proposal        | [`wasi-clocks`]                            |
+| WASI Proposal        | [`wasi-filesystem`]                        |
+| WASI Proposal        | [`wasi-random`]                            |
+| WASI Proposal        | [`wasi-sockets`]                           |
+| WASI Proposal        | [`wasi-http`]                              |
+| WASI Proposal        | `wasi_snapshot_preview1`                   |
+| WASI Proposal        | `wasi_unstable`                            |
 
 [`mutable-globals`]: https://github.com/WebAssembly/mutable-global/blob/master/proposals/mutable-global/Overview.md
 [`sign-extension-ops`]: https://github.com/WebAssembly/spec/blob/master/proposals/sign-extension-ops/Overview.md
@@ -42,58 +54,51 @@ For explanations of what each tier means see below.
 [`bulk-memory`]: https://github.com/WebAssembly/bulk-memory-operations/blob/master/proposals/bulk-memory-operations/Overview.md
 [`reference-types`]: https://github.com/WebAssembly/reference-types/blob/master/proposals/reference-types/Overview.md
 [`simd`]: https://github.com/WebAssembly/simd/blob/master/proposals/simd/SIMD.md
+[`wasi-clocks`]: https://github.com/WebAssembly/wasi-clocks
+[`wasi-filesystem`]: https://github.com/WebAssembly/wasi-filesystem
+[`wasi-io`]: https://github.com/WebAssembly/wasi-io
+[`wasi-random`]: https://github.com/WebAssembly/wasi-random
+[`wasi-sockets`]: https://github.com/WebAssembly/wasi-sockets
+[`wasi-http`]: https://github.com/WebAssembly/wasi-http
+[`tail-call`]: https://github.com/WebAssembly/tail-call/blob/main/proposals/tail-call/Overview.md
 
 #### Tier 2
 
 | Category             | Description                | Missing Tier 1 Requirements |
 |----------------------|----------------------------|-----------------------------|
 | Target               | `aarch64-unknown-linux-gnu`| Continuous fuzzing          |
+| Target               | `aarch64-apple-darwin`     | Continuous fuzzing          |
 | Target               | `s390x-unknown-linux-gnu`  | Continuous fuzzing          |
 | Target               | `x86_64-pc-windows-gnu`    | Clear owner of the target   |
+| Target               | Support for `#![no_std]`   | Support beyond CI checks    |
+| Compiler Backend     | Winch on x86\_64           | Consenus on moving to Tier 1 |
 | WebAssembly Proposal | [`memory64`]               | Unstable wasm proposal      |
-| WebAssembly Proposal | [`multi-memory`]           | Unstable wasm proposal      |
-| WebAssembly Proposal | [`threads`]                | Unstable wasm proposal      |
-| WebAssembly Proposal | [`component-model`]        | Unstable wasm proposal      |
-| WebAssembly Proposal | [`tail-call`]              | Unstable wasm proposal, performance work |
-| WebAssembly Proposal | [`relaxed-simd`]           | Unstable wasm proposal      |
 | WebAssembly Proposal | [`function-references`]    | Unstable wasm proposal      |
-| WASI Proposal        | [`wasi-io`]                | Unstable WASI proposal      |
-| WASI Proposal        | [`wasi-clocks`]            | Unstable WASI proposal      |
-| WASI Proposal        | [`wasi-filesystem`]        | Unstable WASI proposal      |
-| WASI Proposal        | [`wasi-random`]            | Unstable WASI proposal      |
-| WASI Proposal        | [`wasi-poll`]              | Unstable WASI proposal      |
 
 [`memory64`]: https://github.com/WebAssembly/memory64/blob/master/proposals/memory64/Overview.md
 [`multi-memory`]: https://github.com/WebAssembly/multi-memory/blob/master/proposals/multi-memory/Overview.md
 [`threads`]: https://github.com/WebAssembly/threads/blob/master/proposals/threads/Overview.md
 [`component-model`]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/Explainer.md
-[`tail-call`]: https://github.com/WebAssembly/tail-call/blob/main/proposals/tail-call/Overview.md
 [`relaxed-simd`]: https://github.com/WebAssembly/relaxed-simd/blob/main/proposals/relaxed-simd/Overview.md
 [`function-references`]: https://github.com/WebAssembly/function-references/blob/main/proposals/function-references/Overview.md
-[`wasi-clocks`]: https://github.com/WebAssembly/wasi-clocks
-[`wasi-filesystem`]: https://github.com/WebAssembly/wasi-filesystem
-[`wasi-io`]: https://github.com/WebAssembly/wasi-io
-[`wasi-random`]: https://github.com/WebAssembly/wasi-random
-[`wasi-poll`]: https://github.com/WebAssembly/wasi-poll
 
 #### Tier 3
 
 | Category             | Description                       | Missing Tier 2 Requirements |
 |----------------------|-----------------------------------|-----------------------------|
-| Target               | `aarch64-apple-darwin`            | CI testing                  |
 | Target               | `aarch64-pc-windows-msvc`         | CI testing, unwinding, full-time maintainer |
 | Target               | `riscv64gc-unknown-linux-gnu`     | full-time maintainer        |
+| Target               | `wasm32-wasi` [^3]                | Supported but not tested    |
+| Compiler Backend     | Winch on aarch64                  | finished implementation     |
+| WebAssembly Proposal | [`gc`]                            | Complete implementation     |
 | WASI Proposal        | [`wasi-nn`]                       | More expansive CI testing   |
 | WASI Proposal        | [`wasi-threads`]                  | More CI, unstable proposal  |
-| WASI Proposal        | [`wasi-sockets`]                  | Complete implementation     |
-| WASI Proposal        | [`wasi-http`]                     | Complete implementation     |
 | *misc*               | Non-Wasmtime Cranelift usage [^1] | CI testing, full-time maintainer |
 | *misc*               | DWARF debugging [^2]              | CI testing, full-time maintainer, improved quality |
 
-[`wasi-sockets`]: https://github.com/WebAssembly/wasi-sockets
 [`wasi-nn`]: https://github.com/WebAssembly/wasi-nn
 [`wasi-threads`]: https://github.com/WebAssembly/wasi-threads
-[`wasi-http`]: https://github.com/WebAssembly/wasi-http
+[`gc`]: https://github.com/WebAssembly/gc
 
 [^1]: This is intended to encompass features that Cranelift supports as a
 general-purpose code generator such as integer value types other than `i32` and
@@ -108,6 +113,10 @@ support is currently best-effort. Additionally there are known shortcomings
 and bugs. At this time there's no developer time to improve the situation here
 as well.
 
+[^3]: Wasmtime's `cranelift` and `winch` features can be compiled to WebAssembly
+but not the `runtime` feature at this time. This means that
+Wasmtime-compiled-to-wasm can itself compile wasm but cannot execute wasm.
+
 #### Unsupported features and platforms
 
 While this is not an exhaustive list, Wasmtime does not currently have support
@@ -118,7 +127,6 @@ features to figure out how best to implement them and at least move them to Tier
 3 above.
 
 * Target: ARM 32-bit
-* Target: WebAssembly (compiling Wasmtime to WebAssembly itself)
 * Target: [FreeBSD](https://github.com/bytecodealliance/wasmtime/issues/5499)
 * Target: [NetBSD/OpenBSD](https://github.com/bytecodealliance/wasmtime/issues/6962)
 * Target: [i686 (32-bit Intel targets)](https://github.com/bytecodealliance/wasmtime/issues/1980)
@@ -131,7 +139,6 @@ features to figure out how best to implement them and at least move them to Tier
 * [WebAssembly proposal: `exception-handling`](https://github.com/WebAssembly/exception-handling)
 * [WebAssembly proposal: `extended-const`](https://github.com/WebAssembly/extended-const)
 * [WebAssembly proposal: `flexible-vectors`](https://github.com/WebAssembly/flexible-vectors)
-* [WebAssembly proposal: `gc`](https://github.com/WebAssembly/gc)
 * [WebAssembly proposal: `memory-control`](https://github.com/WebAssembly/memory-control)
 * [WebAssembly proposal: `stack-switching`](https://github.com/WebAssembly/stack-switching)
 * [WASI proposal: `proxy-wasm`](https://github.com/proxy-wasm/spec)
diff --git a/examples/min-platform/.gitignore b/examples/min-platform/.gitignore
index 29fa3497b..207ca41be 100644
--- a/examples/min-platform/.gitignore
+++ b/examples/min-platform/.gitignore
@@ -1,1 +1,2 @@
 libwasmtime-platform.so
+libembedding.so
diff --git a/examples/min-platform/Cargo.toml b/examples/min-platform/Cargo.toml
index ca59d183e..3cb9339ec 100644
--- a/examples/min-platform/Cargo.toml
+++ b/examples/min-platform/Cargo.toml
@@ -12,3 +12,4 @@ workspace = true
 anyhow = { workspace = true, features = ['std'] }
 libloading = "0.8"
 object = { workspace = true, features = ['std'] }
+wasmtime = { workspace = true, features = ['cranelift', 'wat'] }
diff --git a/examples/min-platform/build.sh b/examples/min-platform/build.sh
index 89f821714..002093229 100755
--- a/examples/min-platform/build.sh
+++ b/examples/min-platform/build.sh
@@ -4,14 +4,14 @@
 # the embedding itself as well as the example host which will run it.
 #
 # This script takes a single argument which is a path to a Rust target json
-# file. Examples are provided in `embedding/*.json`.
+# file. Example targets are `x86_64-unknown-none` or `aarch64-unknown-none`.
 #
 # This script must be executed with the current-working-directory as
 # `examples/min-platform`.
 
 target=$1
 if [ "$target" = "" ]; then
-  echo "Usage: $0 <target-json-file>"
+  echo "Usage: $0 <target>"
   exit 1
 fi
 
@@ -26,30 +26,28 @@ set -ex
 #
 # which ensures that Rust & C agree on types and such.
 cbindgen ../../crates/wasmtime/src/runtime/vm/sys/custom/capi.rs \
-    --lang C \
-    --cpp-compat > embedding/wasmtime-platform.h
+    --config embedding/cbindgen.toml > embedding/wasmtime-platform.h
 clang -shared -O2 -o libwasmtime-platform.so ./embedding/wasmtime-platform.c \
   -D_GNU_SOURCE
 
-# Next the embedding itself is built. Points of note here:
+# Next the embedding itself is built.
 #
-# * `RUSTC_BOOTSTRAP_SYNTHETIC_TARGET=1` - this "fools" the Rust standard
-#   library to falling back to an "unsupported" implementation of primitives by
-#   default but without marking the standard library as requiring
-#   `feature(restricted_std)`. This is probably something that should be
-#   coordinated with upstream rust-lang/rust and get better support.
-# * `--cfg=wasmtime_custom_platform` - this flag indicates to Wasmtime that the
-#   minimal platform support is being opted into.
-# * `-Zbuild-std=std,panic_abort` - this is a nightly Cargo feature to build the
-#   Rust standard library from source.
-#
-# The final artifacts will be placed in Cargo's standard target directory.
-RUSTC_BOOTSTRAP_SYNTHETIC_TARGET=1 \
-RUSTFLAGS="--cfg=wasmtime_custom_platform" \
-  cargo build -Zbuild-std=std,panic_abort \
-    --manifest-path embedding/Cargo.toml \
-    --target $target \
-    --release
+# Note that this builds the embedding as a static library, here
+# `libembedding.a`. This embedding is then turned into a dynamic library for the
+# host platform using `cc` afterwards. The `*-unknown-none` targets themselves
+# don't support dynamic libraries so this is a bit of a dance to get around the
+# fact that we're pretending this examples in't being compiled for linux.
+cargo build \
+  --manifest-path embedding/Cargo.toml \
+  --target $target \
+  --release
+cc \
+  -Wl,--gc-sections \
+  -Wl,--whole-archive \
+  ../../target/$target/release/libembedding.a \
+  -Wl,--no-whole-archive \
+  -shared \
+  -o libembedding.so
 
 # The final step here is running the host, in the current directory, which will
 # load the embedding and execute it.
diff --git a/examples/min-platform/embedding/Cargo.toml b/examples/min-platform/embedding/Cargo.toml
index 7fbe05954..e218d897f 100644
--- a/examples/min-platform/embedding/Cargo.toml
+++ b/examples/min-platform/embedding/Cargo.toml
@@ -14,12 +14,12 @@ anyhow = { workspace = true }
 # Note that default-features of wasmtime are disabled and only those required
 # are enabled, in this case compilation is done in the guest from the wasm text
 # format so `cranelift` and `wat` are enabled.
-wasmtime = { workspace = true, features = ['cranelift', 'wat', 'runtime'] }
+wasmtime = { workspace = true, features = ['runtime'] }
 
 # Memory allocator used in this example (not required, however)
 dlmalloc = "0.2.4"
 
 [lib]
-crate-type = ['cdylib']
+crate-type = ['staticlib']
 test = false
 doctest = false
diff --git a/examples/min-platform/embedding/aarch64-unknown-unknown.json b/examples/min-platform/embedding/aarch64-unknown-unknown.json
deleted file mode 100644
index e95e609db..000000000
--- a/examples/min-platform/embedding/aarch64-unknown-unknown.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
-  "arch": "aarch64",
-  "data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128",
-  "dynamic-linking": true,
-  "has-thread-local": true,
-  "is-builtin": false,
-  "llvm-target": "aarch64-unknown-linux-gnu",
-  "max-atomic-width": 64,
-  "os": "minwasmtime",
-  "position-independent-executables": true,
-  "panic-strategy": "abort",
-  "frame-pointer": "always",
-  "relro-level": "full",
-  "stack-probes": {
-    "kind": "inline"
-  },
-  "target-pointer-width": "64"
-}
diff --git a/examples/min-platform/embedding/cbindgen.toml b/examples/min-platform/embedding/cbindgen.toml
new file mode 100644
index 000000000..081d2ae23
--- /dev/null
+++ b/examples/min-platform/embedding/cbindgen.toml
@@ -0,0 +1,20 @@
+language = "C"
+include_guard = "_WASMTIME_PLATFORM_H"
+include_version = true
+cpp_compat = true
+
+header = """
+// Platform support for Wasmtime's `no_std` build.
+//
+// This header file is what Wasmtime will rely on when it does not otherwise
+// have support for the native platform. This can happen with `no_std` binaries
+// for example where the traditional Unix-or-Windows implementation is not
+// suitable.
+//
+// Embedders are expected to implement the symbols defined in this header file.
+// These symbols can be defined either in C/C++ or in Rust (using
+// `#[no_mangle]`).
+//
+// Some more information about this header can additionally be found at
+// <https://docs.wasmtime.dev/stability-platform-support.html>.
+"""
diff --git a/examples/min-platform/embedding/src/allocator.rs b/examples/min-platform/embedding/src/allocator.rs
index fc77490ae..821119b0f 100644
--- a/examples/min-platform/embedding/src/allocator.rs
+++ b/examples/min-platform/embedding/src/allocator.rs
@@ -12,10 +12,15 @@
 //! and the `GlobalAlloc for T` trait impl. This should be used when hooking
 //! up to an allocator elsewhere in the system.
 
+use alloc::alloc::{GlobalAlloc, Layout};
+use core::cell::UnsafeCell;
+use core::ops::{Deref, DerefMut};
+use core::ptr;
+use core::sync::atomic::{
+    AtomicBool,
+    Ordering::{Acquire, Release},
+};
 use dlmalloc::Dlmalloc;
-use std::alloc::{GlobalAlloc, Layout};
-use std::ptr;
-use std::sync::Mutex;
 
 #[global_allocator]
 static MALLOC: MyGlobalDmalloc = MyGlobalDmalloc {
@@ -26,36 +31,33 @@ struct MyGlobalDmalloc {
     dlmalloc: Mutex<Dlmalloc<MyAllocator>>,
 }
 
-unsafe impl Send for MyGlobalDmalloc {}
-unsafe impl Sync for MyGlobalDmalloc {}
-
 struct MyAllocator;
 
 unsafe impl GlobalAlloc for MyGlobalDmalloc {
     unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
         self.dlmalloc
-            .lock()
+            .try_lock()
             .unwrap()
             .malloc(layout.size(), layout.align())
     }
 
     unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
         self.dlmalloc
-            .lock()
+            .try_lock()
             .unwrap()
             .calloc(layout.size(), layout.align())
     }
 
     unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
         self.dlmalloc
-            .lock()
+            .try_lock()
             .unwrap()
             .realloc(ptr, layout.size(), layout.align(), new_size)
     }
 
     unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
         self.dlmalloc
-            .lock()
+            .try_lock()
             .unwrap()
             .free(ptr, layout.size(), layout.align())
     }
@@ -84,7 +86,7 @@ unsafe impl dlmalloc::Allocator for MyAllocator {
     }
 
     fn remap(&self, _ptr: *mut u8, _old: usize, _new: usize, _can_move: bool) -> *mut u8 {
-        std::ptr::null_mut()
+        core::ptr::null_mut()
     }
 
     fn free_part(&self, _ptr: *mut u8, _old: usize, _new: usize) -> bool {
@@ -110,3 +112,54 @@ unsafe impl dlmalloc::Allocator for MyAllocator {
         unsafe { wasmtime_page_size() }
     }
 }
+
+// Simple mutex which only supports `try_lock` at this time. This would probably
+// be replaced with a "real" mutex in a "real" embedding.
+struct Mutex<T> {
+    data: UnsafeCell<T>,
+    locked: AtomicBool,
+}
+
+unsafe impl<T: Send> Send for Mutex<T> {}
+unsafe impl<T: Send> Sync for Mutex<T> {}
+
+impl<T> Mutex<T> {
+    const fn new(val: T) -> Mutex<T> {
+        Mutex {
+            data: UnsafeCell::new(val),
+            locked: AtomicBool::new(false),
+        }
+    }
+
+    fn try_lock(&self) -> Option<impl DerefMut<Target = T> + '_> {
+        if self.locked.swap(true, Acquire) {
+            None
+        } else {
+            Some(MutexGuard { lock: self })
+        }
+    }
+}
+
+struct MutexGuard<'a, T> {
+    lock: &'a Mutex<T>,
+}
+
+impl<T> Deref for MutexGuard<'_, T> {
+    type Target = T;
+
+    fn deref(&self) -> &T {
+        unsafe { &*self.lock.data.get() }
+    }
+}
+
+impl<T> DerefMut for MutexGuard<'_, T> {
+    fn deref_mut(&mut self) -> &mut T {
+        unsafe { &mut *self.lock.data.get() }
+    }
+}
+
+impl<T> Drop for MutexGuard<'_, T> {
+    fn drop(&mut self) {
+        self.lock.locked.store(false, Release);
+    }
+}
diff --git a/examples/min-platform/embedding/src/lib.rs b/examples/min-platform/embedding/src/lib.rs
index aabc8484e..49d0ecaf3 100644
--- a/examples/min-platform/embedding/src/lib.rs
+++ b/examples/min-platform/embedding/src/lib.rs
@@ -1,12 +1,35 @@
+#![no_std]
+
+#[macro_use]
+extern crate alloc;
+
 use anyhow::Result;
 use wasmtime::{Engine, Instance, Linker, Module, Store};
 
 mod allocator;
+mod panic;
 
+/// Entrypoint of this embedding.
+///
+/// This takes a number of parameters which are the precompiled module AOT
+/// images that are run for each of the various tests below. The first parameter
+/// is also where to put an error string, if any, if anything fails.
 #[no_mangle]
-pub unsafe extern "C" fn run(buf: *mut u8, size: usize) -> usize {
-    let buf = std::slice::from_raw_parts_mut(buf, size);
-    match run_result() {
+pub unsafe extern "C" fn run(
+    error_buf: *mut u8,
+    error_size: usize,
+    smoke_module: *const u8,
+    smoke_size: usize,
+    simple_add_module: *const u8,
+    simple_add_size: usize,
+    simple_host_fn_module: *const u8,
+    simple_host_fn_size: usize,
+) -> usize {
+    let buf = core::slice::from_raw_parts_mut(error_buf, error_size);
+    let smoke = core::slice::from_raw_parts(smoke_module, smoke_size);
+    let simple_add = core::slice::from_raw_parts(simple_add_module, simple_add_size);
+    let simple_host_fn = core::slice::from_raw_parts(simple_host_fn_module, simple_host_fn_size);
+    match run_result(smoke, simple_add, simple_host_fn) {
         Ok(()) => 0,
         Err(e) => {
             let msg = format!("{e:?}");
@@ -17,50 +40,37 @@ pub unsafe extern "C" fn run(buf: *mut u8, size: usize) -> usize {
     }
 }
 
-fn run_result() -> Result<()> {
-    smoke()?;
-    simple_add()?;
-    simple_host_fn()?;
+fn run_result(
+    smoke_module: &[u8],
+    simple_add_module: &[u8],
+    simple_host_fn_module: &[u8],
+) -> Result<()> {
+    smoke(smoke_module)?;
+    simple_add(simple_add_module)?;
+    simple_host_fn(simple_host_fn_module)?;
     Ok(())
 }
 
-fn smoke() -> Result<()> {
+fn smoke(module: &[u8]) -> Result<()> {
     let engine = Engine::default();
-    let module = Module::new(&engine, "(module)")?;
+    let module = unsafe { Module::deserialize(&engine, module)? };
     Instance::new(&mut Store::new(&engine, ()), &module, &[])?;
     Ok(())
 }
 
-fn simple_add() -> Result<()> {
+fn simple_add(module: &[u8]) -> Result<()> {
     let engine = Engine::default();
-    let module = Module::new(
-        &engine,
-        r#"
-            (module
-                (func (export "add") (param i32 i32) (result i32)
-                    (i32.add (local.get 0) (local.get 1)))
-            )
-        "#,
-    )?;
+    let module = unsafe { Module::deserialize(&engine, module)? };
     let mut store = Store::new(&engine, ());
-    let instance = Instance::new(&mut store, &module, &[])?;
+    let instance = Linker::new(&engine).instantiate(&mut store, &module)?;
     let func = instance.get_typed_func::<(u32, u32), u32>(&mut store, "add")?;
     assert_eq!(func.call(&mut store, (2, 3))?, 5);
     Ok(())
 }
 
-fn simple_host_fn() -> Result<()> {
+fn simple_host_fn(module: &[u8]) -> Result<()> {
     let engine = Engine::default();
-    let module = Module::new(
-        &engine,
-        r#"
-            (module
-                (import "host" "multiply" (func $multiply (param i32 i32) (result i32)))
-                (func (export "add_and_mul") (param i32 i32 i32) (result i32)
-                    (i32.add (call $multiply (local.get 0) (local.get 1)) (local.get 2)))
-            )
-        "#,
-    )?;
+    let module = unsafe { Module::deserialize(&engine, module)? };
     let mut linker = Linker::<()>::new(&engine);
     linker.func_wrap("host", "multiply", |a: u32, b: u32| a.saturating_mul(b))?;
     let mut store = Store::new(&engine, ());
diff --git a/examples/min-platform/embedding/src/panic.rs b/examples/min-platform/embedding/src/panic.rs
new file mode 100644
index 000000000..81e24c9a5
--- /dev/null
+++ b/examples/min-platform/embedding/src/panic.rs
@@ -0,0 +1,12 @@
+#![cfg(not(unix))] // gets `cargo clippy` working
+
+use core::panic::PanicInfo;
+
+#[panic_handler]
+fn handler(_info: &PanicInfo) -> ! {
+    // NB: should ideally print something here but for this example this is left
+    // out. A more complete embedding would likely turn `info` into a
+    // stack-allocated string and then pass that as a message to the outer
+    // system to get printed and trigger a failure.
+    loop {}
+}
diff --git a/examples/min-platform/embedding/wasmtime-platform.h b/examples/min-platform/embedding/wasmtime-platform.h
index df8d60a0c..b063c1dd6 100644
--- a/examples/min-platform/embedding/wasmtime-platform.h
+++ b/examples/min-platform/embedding/wasmtime-platform.h
@@ -1,3 +1,23 @@
+// Platform support for Wasmtime's `no_std` build.
+//
+// This header file is what Wasmtime will rely on when it does not otherwise
+// have support for the native platform. This can happen with `no_std` binaries
+// for example where the traditional Unix-or-Windows implementation is not
+// suitable.
+//
+// Embedders are expected to implement the symbols defined in this header file.
+// These symbols can be defined either in C/C++ or in Rust (using
+// `#[no_mangle]`).
+//
+// Some more information about this header can additionally be found at
+// <https://docs.wasmtime.dev/stability-platform-support.html>.
+
+
+#ifndef _WASMTIME_PLATFORM_H
+#define _WASMTIME_PLATFORM_H
+
+/* Generated with cbindgen:0.26.0 */
+
 #include <stdarg.h>
 #include <stdbool.h>
 #include <stdint.h>
@@ -235,3 +255,5 @@ extern void wasmtime_tls_set(uint8_t *ptr);
 #ifdef __cplusplus
 } // extern "C"
 #endif // __cplusplus
+
+#endif /* _WASMTIME_PLATFORM_H */
diff --git a/examples/min-platform/embedding/x86_64-unknown-unknown.json b/examples/min-platform/embedding/x86_64-unknown-unknown.json
deleted file mode 100644
index 1265585d6..000000000
--- a/examples/min-platform/embedding/x86_64-unknown-unknown.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
-  "arch": "x86_64",
-  "cpu": "x86-64",
-  "data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
-  "features": "+sse4.2,+avx,+fma,+popcnt",
-  "dynamic-linking": true,
-  "has-thread-local": true,
-  "is-builtin": false,
-  "llvm-target": "x86_64-unknown-linux-gnu",
-  "max-atomic-width": 64,
-  "os": "minwasmtime",
-  "position-independent-executables": true,
-  "panic-strategy": "abort",
-  "frame-pointer": "always",
-  "relro-level": "full",
-  "stack-probes": {
-    "kind": "inline"
-  },
-  "target-pointer-width": "64"
-}
diff --git a/examples/min-platform/src/main.rs b/examples/min-platform/src/main.rs
index 64689be1b..a5967a6db 100644
--- a/examples/min-platform/src/main.rs
+++ b/examples/min-platform/src/main.rs
@@ -11,16 +11,15 @@ fn main() -> Result<()> {
     use libloading::os::unix::{Library, Symbol, RTLD_GLOBAL, RTLD_NOW};
     use object::{Object, ObjectSymbol, SymbolKind};
     use std::io::Write;
-    use std::path::Path;
+    use wasmtime::{Config, Engine};
 
     let target = std::env::args().nth(1).unwrap();
-    let target = Path::new(&target).file_stem().unwrap().to_str().unwrap();
     // Path to the artifact which is the build of the embedding.
     //
     // In this example this is a dynamic library intended to be run on Linux.
     // Note that this is just an example of an artifact and custom build
     // processes can produce different kinds of artifacts.
-    let lib = format!("../../target/{target}/release/libembedding.so");
+    let lib = format!("./libembedding.so");
     let binary = std::fs::read(&lib)?;
     let object = object::File::parse(&binary[..])?;
 
@@ -47,6 +46,34 @@ fn main() -> Result<()> {
         }
     }
 
+    // Precompile modules for the embedding. Right now Wasmtime in no_std mode
+    // does not have support for Cranelift meaning that AOT mode must be used.
+    // Modules are compiled here and then given to the embedding via the `run`
+    // function below.
+    //
+    // Note that `Config::target` is used here to enable cross-compilation.
+    let mut config = Config::new();
+    config.target(&target)?;
+    let engine = Engine::new(&config)?;
+    let smoke = engine.precompile_module(b"(module)")?;
+    let simple_add = engine.precompile_module(
+        br#"
+            (module
+                (func (export "add") (param i32 i32) (result i32)
+                    (i32.add (local.get 0) (local.get 1)))
+            )
+        "#,
+    )?;
+    let simple_host_fn = engine.precompile_module(
+        br#"
+            (module
+                (import "host" "multiply" (func $multiply (param i32 i32) (result i32)))
+                (func (export "add_and_mul") (param i32 i32 i32) (result i32)
+                    (i32.add (call $multiply (local.get 0) (local.get 1)) (local.get 2)))
+            )
+        "#,
+    )?;
+
     // Next is an example of running this embedding, which also serves as test
     // that basic functionality actually works.
     //
@@ -70,13 +97,33 @@ fn main() -> Result<()> {
             Library::open(Some("./libwasmtime-platform.so"), RTLD_NOW | RTLD_GLOBAL)?;
 
         let lib = Library::new(&lib)?;
-        let run: Symbol<extern "C" fn(*mut u8, usize) -> usize> = lib.get(b"run")?;
+        let run: Symbol<
+            extern "C" fn(
+                *mut u8,
+                usize,
+                *const u8,
+                usize,
+                *const u8,
+                usize,
+                *const u8,
+                usize,
+            ) -> usize,
+        > = lib.get(b"run")?;
 
-        let mut buf = Vec::with_capacity(1024);
-        let len = run(buf.as_mut_ptr(), buf.capacity());
-        buf.set_len(len);
+        let mut error_buf = Vec::with_capacity(1024);
+        let len = run(
+            error_buf.as_mut_ptr(),
+            error_buf.capacity(),
+            smoke.as_ptr(),
+            smoke.len(),
+            simple_add.as_ptr(),
+            simple_add.len(),
+            simple_host_fn.as_ptr(),
+            simple_host_fn.len(),
+        );
+        error_buf.set_len(len);
 
-        std::io::stderr().write_all(&buf).unwrap();
+        std::io::stderr().write_all(&error_buf).unwrap();
     }
     Ok(())
 }
diff --git a/supply-chain/imports.lock b/supply-chain/imports.lock
index b1d59a2ac..7c2778ae4 100644
--- a/supply-chain/imports.lock
+++ b/supply-chain/imports.lock
@@ -1129,6 +1129,13 @@ user-id = 539
 user-login = "cuviper"
 user-name = "Josh Stone"
 
+[[publisher.indexmap]]
+version = "2.2.6"
+when = "2024-03-23"
+user-id = 539
+user-login = "cuviper"
+user-name = "Josh Stone"
+
 [[publisher.io-extras]]
 version = "0.18.1"
 when = "2023-12-01"
diff --git a/winch/codegen/src/isa/aarch64/asm.rs b/winch/codegen/src/isa/aarch64/asm.rs
index fb2aedc12..aed2780ae 100644
--- a/winch/codegen/src/isa/aarch64/asm.rs
+++ b/winch/codegen/src/isa/aarch64/asm.rs
@@ -3,7 +3,11 @@
 use super::{address::Address, regs};
 use crate::masm::RoundingMode;
 use crate::{masm::OperandSize, reg::Reg};
-use cranelift_codegen::isa::aarch64::inst::{FPUOp1, FPUOp2, FpuRoundMode, ScalarSize};
+use cranelift_codegen::isa::aarch64::inst::FPUOpRI::{UShr32, UShr64};
+use cranelift_codegen::isa::aarch64::inst::{
+    FPULeftShiftImm, FPUOp1, FPUOp2, FPUOpRI, FPUOpRIMod, FPURightShiftImm, FpuRoundMode,
+    ScalarSize,
+};
 use cranelift_codegen::{
     ir::{MemFlags, SourceLoc},
     isa::aarch64::inst::{
@@ -296,6 +300,35 @@ impl Assembler {
         self.emit_fpu_round(fpu_mode, rn, rd)
     }
 
+    /// Float unsigned shift right with two registers and an immediate.
+    pub fn fushr_rri(&mut self, rn: Reg, rd: Reg, amount: u8, size: OperandSize) {
+        let imm = FPURightShiftImm {
+            amount: amount,
+            lane_size_in_bits: size.num_bits(),
+        };
+        let ushr = match size {
+            OperandSize::S32 => UShr32(imm),
+            OperandSize::S64 => UShr64(imm),
+            _ => unreachable!(),
+        };
+        self.emit_fpu_rri(ushr, rn, rd)
+    }
+
+    /// Float unsigned shift left and insert with three registers
+    /// and an immediate.
+    pub fn fsli_rri_mod(&mut self, ri: Reg, rn: Reg, rd: Reg, amount: u8, size: OperandSize) {
+        let imm = FPULeftShiftImm {
+            amount: amount,
+            lane_size_in_bits: size.num_bits(),
+        };
+        let sli = match size {
+            OperandSize::S32 => FPUOpRIMod::Sli32(imm),
+            OperandSize::S64 => FPUOpRIMod::Sli64(imm),
+            _ => unreachable!(),
+        };
+        self.emit_fpu_rri_mod(sli, ri, rn, rd)
+    }
+
     /// Return instruction.
     pub fn ret(&mut self) {
         self.emit(Inst::Ret {});
@@ -345,6 +378,23 @@ impl Assembler {
         });
     }
 
+    fn emit_fpu_rri(&mut self, op: FPUOpRI, rn: Reg, rd: Reg) {
+        self.emit(Inst::FpuRRI {
+            fpu_op: op,
+            rd: Writable::from_reg(rd.into()),
+            rn: rn.into(),
+        });
+    }
+
+    fn emit_fpu_rri_mod(&mut self, op: FPUOpRIMod, ri: Reg, rn: Reg, rd: Reg) {
+        self.emit(Inst::FpuRRIMod {
+            fpu_op: op,
+            rd: Writable::from_reg(rd.into()),
+            ri: ri.into(),
+            rn: rn.into(),
+        });
+    }
+
     fn emit_fpu_rr(&mut self, op: FPUOp1, rn: Reg, rd: Reg, size: OperandSize) {
         self.emit(Inst::FpuRR {
             fpu_op: op,
diff --git a/winch/codegen/src/isa/aarch64/masm.rs b/winch/codegen/src/isa/aarch64/masm.rs
index 39cc2083f..d5d808b94 100644
--- a/winch/codegen/src/isa/aarch64/masm.rs
+++ b/winch/codegen/src/isa/aarch64/masm.rs
@@ -312,8 +312,14 @@ impl Masm for MacroAssembler {
         self.asm.fmax_rrr(rhs, lhs, dst, size);
     }
 
-    fn float_copysign(&mut self, _dst: Reg, _lhs: Reg, _rhs: Reg, _size: OperandSize) {
-        todo!()
+    fn float_copysign(&mut self, dst: Reg, lhs: Reg, rhs: Reg, size: OperandSize) {
+        let max_shift = match size {
+            OperandSize::S32 => 0x1f,
+            OperandSize::S64 => 0x3f,
+            _ => unreachable!(),
+        };
+        self.asm.fushr_rri(rhs, rhs, max_shift, size);
+        self.asm.fsli_rri_mod(lhs, rhs, dst, max_shift, size);
     }
 
     fn float_neg(&mut self, dst: Reg, size: OperandSize) {


Test patch:
diff --git a/tests/disas/winch/aarch64/f32_copysign/const.wat b/tests/disas/winch/aarch64/f32_copysign/const.wat
new file mode 100644
index 000000000..a1c1b01a5
--- /dev/null
+++ b/tests/disas/winch/aarch64/f32_copysign/const.wat
@@ -0,0 +1,32 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result f32)
+        (f32.const -1.1)
+        (f32.const 2.2)
+        (f32.copysign)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x10
+;;       mov     x28, sp
+;;       stur    x0, [x28, #8]
+;;       stur    x1, [x28]
+;;       mov     w16, #0xcccd
+;;       movk    w16, #0x400c, lsl #16
+;;       fmov    s0, w16
+;;       mov     w16, #0xcccd
+;;       movk    w16, #0xbf8c, lsl #16
+;;       fmov    s1, w16
+;;       ushr    v0.2s, v0.2s, #0x1f
+;;       sli     v1.2s, v0.2s, #0x1f
+;;       fmov    s0, s1
+;;       add     sp, sp, #0x10
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/f32_copysign/locals.wat b/tests/disas/winch/aarch64/f32_copysign/locals.wat
new file mode 100644
index 000000000..140e7ece6
--- /dev/null
+++ b/tests/disas/winch/aarch64/f32_copysign/locals.wat
@@ -0,0 +1,47 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result f32)
+        (local $foo f32)  
+        (local $bar f32)
+
+        (f32.const -1.1)
+        (local.set $foo)
+
+        (f32.const 2.2)
+        (local.set $bar)
+
+        (local.get $foo)
+        (local.get $bar)
+        f32.copysign
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x18
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x10]
+;;       stur    x1, [x28, #8]
+;;       mov     x16, #0
+;;       stur    x16, [x28]
+;;       mov     w16, #0xcccd
+;;       movk    w16, #0xbf8c, lsl #16
+;;       fmov    s0, w16
+;;       stur    w0, [x28, #4]
+;;       mov     w16, #0xcccd
+;;       movk    w16, #0x400c, lsl #16
+;;       fmov    s0, w16
+;;       stur    w0, [x28]
+;;       ldur    w0, [x28]
+;;       ldur    w1, [x28, #4]
+;;       ushr    v0.2s, v0.2s, #0x1f
+;;       sli     v1.2s, v0.2s, #0x1f
+;;       fmov    s0, s1
+;;       add     sp, sp, #0x18
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/f32_copysign/params.wat b/tests/disas/winch/aarch64/f32_copysign/params.wat
new file mode 100644
index 000000000..5d99cddc3
--- /dev/null
+++ b/tests/disas/winch/aarch64/f32_copysign/params.wat
@@ -0,0 +1,30 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (param f32) (param f32) (result f32)
+        (local.get 0)
+        (local.get 1)
+        (f32.copysign)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x18
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x10]
+;;       stur    x1, [x28, #8]
+;;       stur    w0, [x28, #4]
+;;       stur    w1, [x28]
+;;       ldur    w0, [x28]
+;;       ldur    w1, [x28, #4]
+;;       ushr    v0.2s, v0.2s, #0x1f
+;;       sli     v1.2s, v0.2s, #0x1f
+;;       fmov    s0, s1
+;;       add     sp, sp, #0x18
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/f64_copysign/const.wat b/tests/disas/winch/aarch64/f64_copysign/const.wat
new file mode 100644
index 000000000..5f6e7ce15
--- /dev/null
+++ b/tests/disas/winch/aarch64/f64_copysign/const.wat
@@ -0,0 +1,36 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result f64)
+        (f64.const -1.1)
+        (f64.const 2.2)
+        (f64.copysign)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x10
+;;       mov     x28, sp
+;;       stur    x0, [x28, #8]
+;;       stur    x1, [x28]
+;;       mov     x16, #0x999a
+;;       movk    x16, #0x9999, lsl #16
+;;       movk    x16, #0x9999, lsl #32
+;;       movk    x16, #0x4001, lsl #48
+;;       fmov    d0, x16
+;;       mov     x16, #0x999a
+;;       movk    x16, #0x9999, lsl #16
+;;       movk    x16, #0x9999, lsl #32
+;;       movk    x16, #0xbff1, lsl #48
+;;       fmov    d1, x16
+;;       ushr    d0, d0, #0x3f
+;;       sli     d1, d0, #0x3f
+;;       fmov    d0, d1
+;;       add     sp, sp, #0x10
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/f64_copysign/locals.wat b/tests/disas/winch/aarch64/f64_copysign/locals.wat
new file mode 100644
index 000000000..dc659515b
--- /dev/null
+++ b/tests/disas/winch/aarch64/f64_copysign/locals.wat
@@ -0,0 +1,52 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result f64)
+        (local $foo f64)  
+        (local $bar f64)
+
+        (f64.const -1.1)
+        (local.set $foo)
+
+        (f64.const 2.2)
+        (local.set $bar)
+
+        (local.get $foo)
+        (local.get $bar)
+        f64.copysign
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x20
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x18]
+;;       stur    x1, [x28, #0x10]
+;;       mov     x16, #0
+;;       stur    x16, [x28, #8]
+;;       stur    x16, [x28]
+;;       mov     x16, #0x999a
+;;       movk    x16, #0x9999, lsl #16
+;;       movk    x16, #0x9999, lsl #32
+;;       movk    x16, #0xbff1, lsl #48
+;;       fmov    d0, x16
+;;       stur    x0, [x28, #8]
+;;       mov     x16, #0x999a
+;;       movk    x16, #0x9999, lsl #16
+;;       movk    x16, #0x9999, lsl #32
+;;       movk    x16, #0x4001, lsl #48
+;;       fmov    d0, x16
+;;       stur    x0, [x28]
+;;       ldur    x0, [x28]
+;;       ldur    x1, [x28, #8]
+;;       ushr    d0, d0, #0x3f
+;;       sli     d1, d0, #0x3f
+;;       fmov    d0, d1
+;;       add     sp, sp, #0x20
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/f64_copysign/params.wat b/tests/disas/winch/aarch64/f64_copysign/params.wat
new file mode 100644
index 000000000..271aa6d33
--- /dev/null
+++ b/tests/disas/winch/aarch64/f64_copysign/params.wat
@@ -0,0 +1,30 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (param f64) (param f64) (result f64)
+        (local.get 0)
+        (local.get 1)
+        (f64.copysign)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x20
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x18]
+;;       stur    x1, [x28, #0x10]
+;;       stur    x0, [x28, #8]
+;;       stur    x1, [x28]
+;;       ldur    x0, [x28]
+;;       ldur    x1, [x28, #8]
+;;       ushr    d0, d0, #0x3f
+;;       sli     d1, d0, #0x3f
+;;       fmov    d0, d1
+;;       add     sp, sp, #0x20
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret