Repository: bytecodealliance/wasmtime
Pull request #8464 (2024-04-24 20:55:18)

cranelift: Don't resolve VReg aliases too early
When lower_branch_blockparam_args is called, the instructions which define the values used as blockparam args haven't been lowered yet, so we haven't set any aliases referring to them yet, so there's no point checking.

Note that block-param argument aliases are currently resolved after operand collection, so this work does happen eventually.

Also, this method doesn't add any instructions to self.ir_insts, so there's no need to call finish_ir_inst.

With this change, VReg alias resolution is purely local to vcode.rs.


Patch:
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index aa5140a06..f1a352437 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -360,6 +360,19 @@ jobs:
     - run: cargo check -p wasmtime-c-api --no-default-features --features wat
     - run: cargo check -p wasmtime-c-api --no-default-features --features wasi
 
+    # Checks for no_std support, ensure that crates can build on a no_std
+    # target
+    - run: rustup target add x86_64-unknown-none
+    - run: cargo check -p wasmtime-jit-icache-coherence
+      env:
+        CARGO_BUILD_TARGET: x86_64-unknown-none
+    - run: cargo check -p wasmtime-component-util
+      env:
+        CARGO_BUILD_TARGET: x86_64-unknown-none
+    - run: cargo check -p wasmtime-asm-macros
+      env:
+        CARGO_BUILD_TARGET: x86_64-unknown-none
+
     # Check that wasmtime-runtime compiles with panic=abort since there's some
     # #[cfg] for specifically panic=abort there.
     - run: cargo check -p wasmtime-runtime
diff --git a/Cargo.lock b/Cargo.lock
index 47abbadae..f2b0fb461 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -19,9 +19,9 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
 
 [[package]]
 name = "ahash"
-version = "0.8.7"
+version = "0.8.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01"
+checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
 dependencies = [
  "cfg-if",
  "once_cell",
@@ -161,15 +161,6 @@ version = "0.21.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a"
 
-[[package]]
-name = "bincode"
-version = "1.3.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
-dependencies = [
- "serde",
-]
-
 [[package]]
 name = "bit-set"
 version = "0.5.2"
@@ -473,6 +464,12 @@ version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b"
 
+[[package]]
+name = "cobs"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15"
+
 [[package]]
 name = "codespan-reporting"
 version = "0.11.1"
@@ -599,7 +596,6 @@ name = "cranelift-codegen"
 version = "0.108.0"
 dependencies = [
  "anyhow",
- "bincode",
  "bumpalo",
  "capstone",
  "cranelift-bforest",
@@ -610,8 +606,9 @@ dependencies = [
  "cranelift-isle",
  "criterion",
  "gimli",
- "hashbrown 0.14.0",
+ "hashbrown 0.14.3",
  "log",
+ "postcard",
  "regalloc2",
  "serde",
  "serde_derive",
@@ -682,7 +679,7 @@ name = "cranelift-frontend"
 version = "0.108.0"
 dependencies = [
  "cranelift-codegen",
- "hashbrown 0.14.0",
+ "hashbrown 0.14.3",
  "log",
  "similar",
  "smallvec",
@@ -752,7 +749,7 @@ dependencies = [
  "anyhow",
  "cranelift-codegen",
  "cranelift-control",
- "hashbrown 0.14.0",
+ "hashbrown 0.14.3",
  "serde",
  "serde_derive",
 ]
@@ -842,14 +839,14 @@ dependencies = [
  "cranelift-codegen",
  "cranelift-entity",
  "cranelift-frontend",
- "hashbrown 0.14.0",
+ "hashbrown 0.14.3",
  "itertools 0.12.1",
  "log",
  "serde",
  "serde_derive",
  "smallvec",
  "target-lexicon",
- "wasmparser 0.205.0",
+ "wasmparser 0.206.0",
  "wasmtime-types",
  "wat",
 ]
@@ -1045,6 +1042,12 @@ version = "1.6.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
 
+[[package]]
+name = "embedded-io"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced"
+
 [[package]]
 name = "embedding"
 version = "21.0.0"
@@ -1390,9 +1393,9 @@ dependencies = [
 
 [[package]]
 name = "hashbrown"
-version = "0.14.0"
+version = "0.14.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
+checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
 dependencies = [
  "ahash",
 ]
@@ -1549,7 +1552,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d"
 dependencies = [
  "equivalent",
- "hashbrown 0.14.0",
+ "hashbrown 0.14.3",
  "serde",
 ]
 
@@ -1923,7 +1926,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d8dd6c0cdf9429bce006e1362bfce61fa1bfd8c898a643ed8d2b471934701d3d"
 dependencies = [
  "crc32fast",
- "hashbrown 0.14.0",
+ "hashbrown 0.14.3",
  "indexmap 2.0.0",
  "memchr",
 ]
@@ -2090,6 +2093,17 @@ dependencies = [
  "plotters-backend",
 ]
 
+[[package]]
+name = "postcard"
+version = "1.0.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a55c51ee6c0db07e68448e336cf8ea4131a620edefebf9893e759b2d793420f8"
+dependencies = [
+ "cobs",
+ "embedded-io",
+ "serde",
+]
+
 [[package]]
 name = "ppv-lite86"
 version = "0.2.16"
@@ -2756,7 +2770,7 @@ dependencies = [
  "cargo_metadata",
  "heck",
  "wasmtime",
- "wit-component 0.205.0",
+ "wit-component 0.206.0",
 ]
 
 [[package]]
@@ -3092,7 +3106,7 @@ name = "verify-component-adapter"
 version = "21.0.0"
 dependencies = [
  "anyhow",
- "wasmparser 0.205.0",
+ "wasmparser 0.206.0",
  "wat",
 ]
 
@@ -3184,7 +3198,7 @@ dependencies = [
  "byte-array-literals",
  "object 0.33.0",
  "wasi",
- "wasm-encoder 0.205.0",
+ "wasm-encoder 0.206.0",
  "wit-bindgen",
 ]
 
@@ -3253,9 +3267,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-encoder"
-version = "0.205.0"
+version = "0.206.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "90e95b3563d164f33c1cfb0a7efbd5940c37710019be10cd09f800fdec8b0e5c"
+checksum = "d759312e1137f199096d80a70be685899cd7d3d09c572836bb2e9b69b4dc3b1e"
 dependencies = [
  "leb128",
 ]
@@ -3278,9 +3292,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-metadata"
-version = "0.205.0"
+version = "0.206.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e9855ad6dd4d099fa8505f01fb0b84c7e3efd5f555207329e3ad938d5f394fe7"
+checksum = "84435ac34e29de6154fdb180ebbe2bdde96336f7ad0990003c43a21bd441303f"
 dependencies = [
  "anyhow",
  "indexmap 2.0.0",
@@ -3288,36 +3302,36 @@ dependencies = [
  "serde_derive",
  "serde_json",
  "spdx",
- "wasm-encoder 0.205.0",
- "wasmparser 0.205.0",
+ "wasm-encoder 0.206.0",
+ "wasmparser 0.206.0",
 ]
 
 [[package]]
 name = "wasm-mutate"
-version = "0.205.0"
+version = "0.206.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d92842509e0cc163a0f8f4a54e38ec180e18594b9ed3999d0a05108e70d25cb3"
+checksum = "afd8f7a4dc2b33e16b5fe96e31922ab3d5351abd93e404fcdc5bdd67d91915f0"
 dependencies = [
  "egg",
  "log",
  "rand",
  "thiserror",
- "wasm-encoder 0.205.0",
- "wasmparser 0.205.0",
+ "wasm-encoder 0.206.0",
+ "wasmparser 0.206.0",
 ]
 
 [[package]]
 name = "wasm-smith"
-version = "0.205.0"
+version = "0.206.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1218f393276f7a13f6c54467434633144b46c33ecbc3cbafdc938033f0315d1a"
+checksum = "797d1293e8c8ce440c22dc283b7db0f68035929928f1736f12f4d9bf8fd2a24d"
 dependencies = [
  "anyhow",
  "arbitrary",
  "flagset",
  "indexmap 2.0.0",
  "leb128",
- "wasm-encoder 0.205.0",
+ "wasm-encoder 0.206.0",
 ]
 
 [[package]]
@@ -3373,11 +3387,13 @@ dependencies = [
 
 [[package]]
 name = "wasmparser"
-version = "0.205.0"
+version = "0.206.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d457bb52804242e09d55a306e53ddbc65d1d29ed83db6a4eea3ed412ee0cfdf"
+checksum = "39192edb55d55b41963db40fd49b0b542156f04447b5b512744a91d38567bdbc"
 dependencies = [
+ "ahash",
  "bitflags 2.4.1",
+ "hashbrown 0.14.3",
  "indexmap 2.0.0",
  "semver",
 ]
@@ -3393,12 +3409,12 @@ dependencies = [
 
 [[package]]
 name = "wasmprinter"
-version = "0.205.0"
+version = "0.206.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "edcd1e00bc348d4b1c0abe913d86f4a89377cbd5c1c704d4eca5cf3492a27da8"
+checksum = "3dec456f9cc479792c9920055cd499ae1d13f85e98f9443dfe4ea5751b21f3b0"
 dependencies = [
  "anyhow",
- "wasmparser 0.205.0",
+ "wasmparser 0.206.0",
 ]
 
 [[package]]
@@ -3408,7 +3424,6 @@ dependencies = [
  "addr2line",
  "anyhow",
  "async-trait",
- "bincode",
  "bumpalo",
  "cfg-if",
  "encoding_rs",
@@ -3421,6 +3436,7 @@ dependencies = [
  "object 0.33.0",
  "once_cell",
  "paste",
+ "postcard",
  "rayon",
  "rustix",
  "semver",
@@ -3430,8 +3446,8 @@ dependencies = [
  "target-lexicon",
  "tempfile",
  "wasi-common",
- "wasm-encoder 0.205.0",
- "wasmparser 0.205.0",
+ "wasm-encoder 0.206.0",
+ "wasmparser 0.206.0",
  "wasmtime-cache",
  "wasmtime-component-macro",
  "wasmtime-component-util",
@@ -3509,11 +3525,11 @@ version = "21.0.0"
 dependencies = [
  "anyhow",
  "base64",
- "bincode",
  "directories-next",
  "filetime",
  "log",
  "once_cell",
+ "postcard",
  "pretty_env_logger",
  "rustix",
  "serde",
@@ -3571,7 +3587,7 @@ dependencies = [
  "tracing",
  "walkdir",
  "wasi-common",
- "wasmparser 0.205.0",
+ "wasmparser 0.206.0",
  "wasmtime",
  "wasmtime-cache",
  "wasmtime-cli-flags",
@@ -3585,10 +3601,10 @@ dependencies = [
  "wasmtime-wasi-nn",
  "wasmtime-wasi-threads",
  "wasmtime-wast",
- "wast 205.0.0",
+ "wast 206.0.0",
  "wat",
  "windows-sys 0.52.0",
- "wit-component 0.205.0",
+ "wit-component 0.206.0",
 ]
 
 [[package]]
@@ -3619,7 +3635,7 @@ dependencies = [
  "wasmtime",
  "wasmtime-component-util",
  "wasmtime-wit-bindgen",
- "wit-parser 0.205.0",
+ "wit-parser 0.206.0",
 ]
 
 [[package]]
@@ -3643,7 +3659,7 @@ dependencies = [
  "object 0.33.0",
  "target-lexicon",
  "thiserror",
- "wasmparser 0.205.0",
+ "wasmparser 0.206.0",
  "wasmtime-environ",
  "wasmtime-versioned-export-macros",
 ]
@@ -3653,7 +3669,6 @@ name = "wasmtime-environ"
 version = "21.0.0"
 dependencies = [
  "anyhow",
- "bincode",
  "clap",
  "cpp_demangle",
  "cranelift-entity",
@@ -3662,13 +3677,14 @@ dependencies = [
  "indexmap 2.0.0",
  "log",
  "object 0.33.0",
+ "postcard",
  "rustc-demangle",
  "serde",
  "serde_derive",
  "target-lexicon",
  "thiserror",
- "wasm-encoder 0.205.0",
- "wasmparser 0.205.0",
+ "wasm-encoder 0.206.0",
+ "wasmparser 0.206.0",
  "wasmprinter",
  "wasmtime-component-util",
  "wasmtime-types",
@@ -3683,7 +3699,7 @@ dependencies = [
  "component-fuzz-util",
  "env_logger",
  "libfuzzer-sys",
- "wasmparser 0.205.0",
+ "wasmparser 0.206.0",
  "wasmprinter",
  "wasmtime-environ",
  "wat",
@@ -3740,7 +3756,7 @@ dependencies = [
  "rand",
  "smallvec",
  "target-lexicon",
- "wasmparser 0.205.0",
+ "wasmparser 0.206.0",
  "wasmtime",
  "wasmtime-fuzzing",
 ]
@@ -3761,12 +3777,12 @@ dependencies = [
  "target-lexicon",
  "tempfile",
  "v8",
- "wasm-encoder 0.205.0",
+ "wasm-encoder 0.206.0",
  "wasm-mutate",
  "wasm-smith",
  "wasm-spec-interpreter",
  "wasmi",
- "wasmparser 0.205.0",
+ "wasmparser 0.206.0",
  "wasmprinter",
  "wasmtime",
  "wasmtime-wast",
@@ -3787,6 +3803,7 @@ dependencies = [
 name = "wasmtime-jit-icache-coherence"
 version = "21.0.0"
 dependencies = [
+ "anyhow",
  "cfg-if",
  "libc",
  "windows-sys 0.52.0",
@@ -3814,7 +3831,7 @@ dependencies = [
  "rustix",
  "smallvec",
  "sptr",
- "wasm-encoder 0.205.0",
+ "wasm-encoder 0.206.0",
  "wasmtime-asm-macros",
  "wasmtime-environ",
  "wasmtime-fiber",
@@ -3838,7 +3855,7 @@ dependencies = [
  "serde_derive",
  "smallvec",
  "thiserror",
- "wasmparser 0.205.0",
+ "wasmparser 0.206.0",
 ]
 
 [[package]]
@@ -3945,7 +3962,7 @@ dependencies = [
  "anyhow",
  "log",
  "wasmtime",
- "wast 205.0.0",
+ "wast 206.0.0",
 ]
 
 [[package]]
@@ -3957,7 +3974,7 @@ dependencies = [
  "gimli",
  "object 0.33.0",
  "target-lexicon",
- "wasmparser 0.205.0",
+ "wasmparser 0.206.0",
  "wasmtime-cranelift",
  "wasmtime-environ",
  "winch-codegen",
@@ -3970,7 +3987,7 @@ dependencies = [
  "anyhow",
  "heck",
  "indexmap 2.0.0",
- "wit-parser 0.205.0",
+ "wit-parser 0.206.0",
 ]
 
 [[package]]
@@ -3988,24 +4005,24 @@ dependencies = [
 
 [[package]]
 name = "wast"
-version = "205.0.0"
+version = "206.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "441a6a195b3b5245e26d450bbcc91366c6b652382a22f63cbe3c73240e13b2bb"
+checksum = "68586953ee4960b1f5d84ebf26df3b628b17e6173bc088e0acfbce431469795a"
 dependencies = [
  "bumpalo",
  "leb128",
  "memchr",
  "unicode-width",
- "wasm-encoder 0.205.0",
+ "wasm-encoder 0.206.0",
 ]
 
 [[package]]
 name = "wat"
-version = "1.205.0"
+version = "1.206.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "19832624d606e7c6bf3cd4caa73578ecec5eac30c768269256d19c79900beb18"
+checksum = "da4c6f2606276c6e991aebf441b2fc92c517807393f039992a3e0ad873efe4ad"
 dependencies = [
- "wast 205.0.0",
+ "wast 206.0.0",
 ]
 
 [[package]]
@@ -4135,7 +4152,7 @@ dependencies = [
  "regalloc2",
  "smallvec",
  "target-lexicon",
- "wasmparser 0.205.0",
+ "wasmparser 0.206.0",
  "wasmtime-cranelift",
  "wasmtime-environ",
 ]
@@ -4386,9 +4403,9 @@ dependencies = [
 
 [[package]]
 name = "wit-component"
-version = "0.205.0"
+version = "0.206.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "74841e17b2c2bfbd8ca8c45190ef85dff1c5e8659f0e2d164802ffc87dfb18c3"
+checksum = "81e1ae4cace3706e29a8cf1f728b792255ecdc02653b9f4130ab6cc64f435a67"
 dependencies = [
  "anyhow",
  "bitflags 2.4.1",
@@ -4397,10 +4414,10 @@ dependencies = [
  "serde",
  "serde_derive",
  "serde_json",
- "wasm-encoder 0.205.0",
- "wasm-metadata 0.205.0",
- "wasmparser 0.205.0",
- "wit-parser 0.205.0",
+ "wasm-encoder 0.206.0",
+ "wasm-metadata 0.206.0",
+ "wasmparser 0.206.0",
+ "wit-parser 0.206.0",
 ]
 
 [[package]]
@@ -4423,9 +4440,9 @@ dependencies = [
 
 [[package]]
 name = "wit-parser"
-version = "0.205.0"
+version = "0.206.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a3db34c7688c161ed7bd1b2f8055dca9fb2c15201db58754e9c48a0805f32e5f"
+checksum = "d226de4e95e052cb664d2bdad2f31d9cad5117038a3439568f078d1afd8842fe"
 dependencies = [
  "anyhow",
  "id-arena",
@@ -4436,7 +4453,7 @@ dependencies = [
  "serde_derive",
  "serde_json",
  "unicode-xid",
- "wasmparser 0.205.0",
+ "wasmparser 0.206.0",
 ]
 
 [[package]]
diff --git a/Cargo.toml b/Cargo.toml
index f137a2e26..269e071f6 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -32,14 +32,14 @@ wasmtime-cranelift = { workspace = true, optional = true }
 wasmtime-environ = { workspace = true }
 wasmtime-explorer = { workspace = true, optional = true }
 wasmtime-wast = { workspace = true, optional = true }
-wasi-common = { workspace = true, default-features = true, features = ["exit" ], optional = true }
+wasi-common = { workspace = true, default-features = true, features = ["exit"], optional = true }
 wasmtime-wasi = { workspace = true, default-features = true, optional = true }
 wasmtime-wasi-nn = { workspace = true, optional = true }
 wasmtime-wasi-threads = { workspace = true, optional = true }
 wasmtime-wasi-http = { workspace = true, optional = true }
 wasmtime-runtime = { workspace = true, optional = true }
 clap = { workspace = true }
-anyhow = { workspace = true }
+anyhow = { workspace = true, features = ['std'] }
 target-lexicon = { workspace = true }
 once_cell = { workspace = true }
 listenfd = { version = "1.0.0", optional = true }
@@ -104,7 +104,7 @@ object = { workspace = true }
 windows-sys = { workspace = true, features = ["Win32_System_Memory"] }
 
 [build-dependencies]
-anyhow = { workspace = true }
+anyhow = { workspace = true, features = ['std'] }
 
 [profile.release.build-override]
 opt-level = 0
@@ -234,21 +234,21 @@ rustix = "0.38.31"
 wit-bindgen = { version = "0.22.0", default-features = false }
 
 # wasm-tools family:
-wasmparser = "0.205.0"
-wat = "1.205.0"
-wast = "205.0.0"
-wasmprinter = "0.205.0"
-wasm-encoder = "0.205.0"
-wasm-smith = "0.205.0"
-wasm-mutate = "0.205.0"
-wit-parser = "0.205.0"
-wit-component = "0.205.0"
+wasmparser = "0.206.0"
+wat = "1.206.0"
+wast = "206.0.0"
+wasmprinter = "0.206.0"
+wasm-encoder = "0.206.0"
+wasm-smith = "0.206.0"
+wasm-mutate = "0.206.0"
+wit-parser = "0.206.0"
+wit-component = "0.206.0"
 
 # Non-Bytecode Alliance maintained dependencies:
 # --------------------------
 object = { version = "0.33", default-features = false, features = ['read_core', 'elf', 'std'] }
 gimli = { version = "0.28.0", default-features = false, features = ['read', 'std'] }
-anyhow = "1.0.22"
+anyhow = { version = "1.0.22", default-features = false }
 windows-sys = "0.52.0"
 env_logger = "0.10"
 log = { version = "0.4.8", default-features = false }
@@ -290,7 +290,7 @@ test-log = { version = "0.2", default-features = false, features = ["trace"] }
 tracing-subscriber = { version = "0.3.1", default-features = false, features = ['fmt', 'env-filter', 'ansi', 'tracing-log'] }
 url = "2.3.1"
 humantime = "2.0.0"
-bincode = "1.2.1"
+postcard = { version = "1.0.8", default-features = false, features = ['alloc'] }
 
 # =============================================================================
 #
diff --git a/cranelift/codegen/Cargo.toml b/cranelift/codegen/Cargo.toml
index 2dfde787e..8c30b4011 100644
--- a/cranelift/codegen/Cargo.toml
+++ b/cranelift/codegen/Cargo.toml
@@ -16,7 +16,7 @@ edition.workspace = true
 workspace = true
 
 [dependencies]
-anyhow = { workspace = true, optional = true }
+anyhow = { workspace = true, optional = true, features = ['std'] }
 bumpalo = "3"
 capstone = { workspace = true, optional = true }
 cranelift-codegen-shared = { path = "./shared", version = "0.108.0" }
@@ -28,7 +28,7 @@ target-lexicon = { workspace = true }
 log = { workspace = true }
 serde = { workspace = true, optional = true }
 serde_derive = { workspace = true, optional = true }
-bincode = { workspace = true, optional = true }
+postcard = { workspace = true, optional = true }
 gimli = { workspace = true, features = ["write"], optional = true }
 smallvec = { workspace = true }
 regalloc2 = { workspace = true, features = ["checker", "trace-log"] }
@@ -100,7 +100,7 @@ enable-serde = [
 # Enable the incremental compilation cache for hot-reload use cases.
 incremental-cache = [
     "enable-serde",
-    "bincode",
+    "postcard",
     "sha2"
 ]
 
diff --git a/cranelift/codegen/src/incremental_cache.rs b/cranelift/codegen/src/incremental_cache.rs
index 6290e55f8..bf5f6215e 100644
--- a/cranelift/codegen/src/incremental_cache.rs
+++ b/cranelift/codegen/src/incremental_cache.rs
@@ -208,12 +208,12 @@ pub fn compute_cache_key(isa: &dyn TargetIsa, func: &Function) -> CacheKeyHash {
 /// of the function call. The value is left untouched.
 pub fn serialize_compiled(
     result: CompiledCodeStencil,
-) -> (CompiledCodeStencil, Result<Vec<u8>, bincode::Error>) {
+) -> (CompiledCodeStencil, Result<Vec<u8>, postcard::Error>) {
     let cached = CachedFunc {
         version_marker: VersionMarker,
         stencil: result,
     };
-    let result = bincode::serialize(&cached);
+    let result = postcard::to_allocvec(&cached);
     (cached.stencil, result)
 }
 
@@ -223,7 +223,7 @@ pub enum RecompileError {
     /// The version embedded in the cache entry isn't the same as cranelift's current version.
     VersionMismatch,
     /// An error occurred while deserializing the cache entry.
-    Deserialize(bincode::Error),
+    Deserialize(postcard::Error),
 }
 
 impl fmt::Display for RecompileError {
@@ -231,7 +231,7 @@ impl fmt::Display for RecompileError {
         match self {
             RecompileError::VersionMismatch => write!(f, "cranelift version mismatch",),
             RecompileError::Deserialize(err) => {
-                write!(f, "bincode failed during deserialization: {err}")
+                write!(f, "postcard failed during deserialization: {err}")
             }
         }
     }
@@ -243,7 +243,7 @@ impl fmt::Display for RecompileError {
 /// Precondition: the bytes must have retrieved from a cache store entry which hash value
 /// is strictly the same as the `Function`'s computed hash retrieved from `compute_cache_key`.
 pub fn try_finish_recompile(func: &Function, bytes: &[u8]) -> Result<CompiledCode, RecompileError> {
-    match bincode::deserialize::<CachedFunc>(bytes) {
+    match postcard::from_bytes::<CachedFunc>(bytes) {
         Ok(result) => {
             if result.version_marker != func.stencil.version_marker {
                 Err(RecompileError::VersionMismatch)
diff --git a/cranelift/codegen/src/isa/x64/inst/args.rs b/cranelift/codegen/src/isa/x64/inst/args.rs
index a434703bb..aa7f19ce5 100644
--- a/cranelift/codegen/src/isa/x64/inst/args.rs
+++ b/cranelift/codegen/src/isa/x64/inst/args.rs
@@ -55,6 +55,12 @@ macro_rules! newtype_of_reg {
             }
         }
 
+        impl From<&$newtype_reg> for Reg {
+            fn from(r: &$newtype_reg) -> Self {
+                r.0
+            }
+        }
+
         impl $newtype_reg {
             /// Create this newtype from the given register, or return `None` if the register
             /// is not a valid instance of this newtype.
@@ -365,13 +371,13 @@ impl Amode {
                     collector.reg_use(*base);
                 }
             }
-            Amode::ImmRegRegShift { base, index, .. } => {
+            &Amode::ImmRegRegShift { base, index, .. } => {
                 debug_assert_ne!(base.to_reg(), regs::rbp());
                 debug_assert_ne!(base.to_reg(), regs::rsp());
-                collector.reg_use(base.to_reg());
+                collector.reg_use(base);
                 debug_assert_ne!(index.to_reg(), regs::rbp());
                 debug_assert_ne!(index.to_reg(), regs::rsp());
-                collector.reg_use(index.to_reg());
+                collector.reg_use(index);
             }
             Amode::RipRelative { .. } => {
                 // RIP isn't involved in regalloc.
@@ -388,9 +394,9 @@ impl Amode {
             Amode::ImmReg { base, .. } => {
                 collector.reg_late_use(*base);
             }
-            Amode::ImmRegRegShift { base, index, .. } => {
-                collector.reg_late_use(base.to_reg());
-                collector.reg_late_use(index.to_reg());
+            &Amode::ImmRegRegShift { base, index, .. } => {
+                collector.reg_late_use(base);
+                collector.reg_late_use(index);
             }
             Amode::RipRelative { .. } => {
                 // RIP isn't involved in regalloc.
diff --git a/cranelift/codegen/src/isa/x64/inst/mod.rs b/cranelift/codegen/src/isa/x64/inst/mod.rs
index 5785a68c3..277bafd8e 100644
--- a/cranelift/codegen/src/isa/x64/inst/mod.rs
+++ b/cranelift/codegen/src/isa/x64/inst/mod.rs
@@ -1912,29 +1912,29 @@ fn x64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut OperandCol
         Inst::AluRmiR {
             src1, src2, dst, ..
         } => {
-            collector.reg_use(src1.to_reg());
-            collector.reg_reuse_def(dst.to_writable_reg(), 0);
+            collector.reg_use(src1);
+            collector.reg_reuse_def(*dst, 0);
             src2.get_operands(collector);
         }
-        Inst::AluConstOp { dst, .. } => collector.reg_def(dst.to_writable_reg()),
+        &Inst::AluConstOp { dst, .. } => collector.reg_def(dst),
         Inst::AluRM { src1_dst, src2, .. } => {
-            collector.reg_use(src2.to_reg());
+            collector.reg_use(src2);
             src1_dst.get_operands(collector);
         }
         Inst::AluRmRVex {
             src1, src2, dst, ..
         } => {
-            collector.reg_def(dst.to_writable_reg());
-            collector.reg_use(src1.to_reg());
+            collector.reg_def(*dst);
+            collector.reg_use(src1);
             src2.get_operands(collector);
         }
-        Inst::Not { src, dst, .. } => {
-            collector.reg_use(src.to_reg());
-            collector.reg_reuse_def(dst.to_writable_reg(), 0);
+        &Inst::Not { src, dst, .. } => {
+            collector.reg_use(src);
+            collector.reg_reuse_def(dst, 0);
         }
-        Inst::Neg { src, dst, .. } => {
-            collector.reg_use(src.to_reg());
-            collector.reg_reuse_def(dst.to_writable_reg(), 0);
+        &Inst::Neg { src, dst, .. } => {
+            collector.reg_use(src);
+            collector.reg_reuse_def(dst, 0);
         }
         Inst::Div {
             dividend_lo,
@@ -1952,22 +1952,22 @@ fn x64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut OperandCol
         } => {
             match inst {
                 Inst::Div { divisor, .. } => divisor.get_operands(collector),
-                Inst::CheckedSRemSeq { divisor, .. } => collector.reg_use(divisor.to_reg()),
+                Inst::CheckedSRemSeq { divisor, .. } => collector.reg_use(divisor),
                 _ => {}
             }
-            collector.reg_fixed_use(dividend_lo.to_reg(), regs::rax());
-            collector.reg_fixed_use(dividend_hi.to_reg(), regs::rdx());
-            collector.reg_fixed_def(dst_quotient.to_writable_reg(), regs::rax());
-            collector.reg_fixed_def(dst_remainder.to_writable_reg(), regs::rdx());
+            collector.reg_fixed_use(dividend_lo, regs::rax());
+            collector.reg_fixed_use(dividend_hi, regs::rdx());
+            collector.reg_fixed_def(*dst_quotient, regs::rax());
+            collector.reg_fixed_def(*dst_remainder, regs::rdx());
         }
         Inst::Div8 { dividend, dst, .. } | Inst::CheckedSRemSeq8 { dividend, dst, .. } => {
             match inst {
                 Inst::Div8 { divisor, .. } => divisor.get_operands(collector),
-                Inst::CheckedSRemSeq8 { divisor, .. } => collector.reg_use(divisor.to_reg()),
+                Inst::CheckedSRemSeq8 { divisor, .. } => collector.reg_use(divisor),
                 _ => {}
             }
-            collector.reg_fixed_use(dividend.to_reg(), regs::rax());
-            collector.reg_fixed_def(dst.to_writable_reg(), regs::rax());
+            collector.reg_fixed_use(dividend, regs::rax());
+            collector.reg_fixed_def(*dst, regs::rax());
         }
         Inst::Mul {
             src1,
@@ -1976,53 +1976,53 @@ fn x64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut OperandCol
             dst_hi,
             ..
         } => {
-            collector.reg_fixed_use(src1.to_reg(), regs::rax());
-            collector.reg_fixed_def(dst_lo.to_writable_reg(), regs::rax());
-            collector.reg_fixed_def(dst_hi.to_writable_reg(), regs::rdx());
+            collector.reg_fixed_use(src1, regs::rax());
+            collector.reg_fixed_def(*dst_lo, regs::rax());
+            collector.reg_fixed_def(*dst_hi, regs::rdx());
             src2.get_operands(collector);
         }
         Inst::Mul8 {
             src1, src2, dst, ..
         } => {
-            collector.reg_fixed_use(src1.to_reg(), regs::rax());
-            collector.reg_fixed_def(dst.to_writable_reg(), regs::rax());
+            collector.reg_fixed_use(src1, regs::rax());
+            collector.reg_fixed_def(*dst, regs::rax());
             src2.get_operands(collector);
         }
         Inst::IMul {
             src1, src2, dst, ..
         } => {
-            collector.reg_use(src1.to_reg());
-            collector.reg_reuse_def(dst.to_writable_reg(), 0);
+            collector.reg_use(src1);
+            collector.reg_reuse_def(*dst, 0);
             src2.get_operands(collector);
         }
         Inst::IMulImm { src1, dst, .. } => {
-            collector.reg_def(dst.to_writable_reg());
+            collector.reg_def(*dst);
             src1.get_operands(collector);
         }
-        Inst::SignExtendData { size, src, dst } => {
+        &Inst::SignExtendData { size, src, dst } => {
             match size {
                 OperandSize::Size8 => {
                     // Note `rax` on both src and dest: 8->16 extend
                     // does AL -> AX.
-                    collector.reg_fixed_use(src.to_reg(), regs::rax());
-                    collector.reg_fixed_def(dst.to_writable_reg(), regs::rax());
+                    collector.reg_fixed_use(src, regs::rax());
+                    collector.reg_fixed_def(dst, regs::rax());
                 }
                 _ => {
                     // All other widths do RAX -> RDX (AX -> DX:AX,
                     // EAX -> EDX:EAX).
-                    collector.reg_fixed_use(src.to_reg(), regs::rax());
-                    collector.reg_fixed_def(dst.to_writable_reg(), regs::rdx());
+                    collector.reg_fixed_use(src, regs::rax());
+                    collector.reg_fixed_def(dst, regs::rdx());
                 }
             }
         }
         Inst::UnaryRmR { src, dst, .. }
         | Inst::UnaryRmRVex { src, dst, .. }
         | Inst::UnaryRmRImmVex { src, dst, .. } => {
-            collector.reg_def(dst.to_writable_reg());
+            collector.reg_def(*dst);
             src.get_operands(collector);
         }
         Inst::XmmUnaryRmR { src, dst, .. } | Inst::XmmUnaryRmRImm { src, dst, .. } => {
-            collector.reg_def(dst.to_writable_reg());
+            collector.reg_def(*dst);
             src.get_operands(collector);
         }
         Inst::XmmUnaryRmREvex { src, dst, .. }
@@ -2030,21 +2030,21 @@ fn x64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut OperandCol
         | Inst::XmmUnaryRmRUnaligned { src, dst, .. }
         | Inst::XmmUnaryRmRVex { src, dst, .. }
         | Inst::XmmUnaryRmRImmVex { src, dst, .. } => {
-            collector.reg_def(dst.to_writable_reg());
+            collector.reg_def(*dst);
             src.get_operands(collector);
         }
         Inst::XmmRmR {
             src1, src2, dst, ..
         } => {
-            collector.reg_use(src1.to_reg());
-            collector.reg_reuse_def(dst.to_writable_reg(), 0);
+            collector.reg_use(src1);
+            collector.reg_reuse_def(*dst, 0);
             src2.get_operands(collector);
         }
         Inst::XmmRmRUnaligned {
             src1, src2, dst, ..
         } => {
-            collector.reg_use(src1.to_reg());
-            collector.reg_reuse_def(dst.to_writable_reg(), 0);
+            collector.reg_use(src1);
+            collector.reg_reuse_def(*dst, 0);
             src2.get_operands(collector);
         }
         Inst::XmmRmRBlend {
@@ -2054,35 +2054,34 @@ fn x64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut OperandCol
             dst,
             op,
         } => {
-            assert!(
-                *op == SseOpcode::Blendvpd
-                    || *op == SseOpcode::Blendvps
-                    || *op == SseOpcode::Pblendvb
-            );
-            collector.reg_use(src1.to_reg());
-            collector.reg_fixed_use(mask.to_reg(), regs::xmm0());
-            collector.reg_reuse_def(dst.to_writable_reg(), 0);
+            assert!(matches!(
+                op,
+                SseOpcode::Blendvpd | SseOpcode::Blendvps | SseOpcode::Pblendvb
+            ));
+            collector.reg_use(src1);
+            collector.reg_fixed_use(mask, regs::xmm0());
+            collector.reg_reuse_def(*dst, 0);
             src2.get_operands(collector);
         }
         Inst::XmmRmiRVex {
             src1, src2, dst, ..
         } => {
-            collector.reg_def(dst.to_writable_reg());
-            collector.reg_use(src1.to_reg());
+            collector.reg_def(*dst);
+            collector.reg_use(src1);
             src2.get_operands(collector);
         }
         Inst::XmmRmRImmVex {
             src1, src2, dst, ..
         } => {
-            collector.reg_def(dst.to_writable_reg());
-            collector.reg_use(src1.to_reg());
+            collector.reg_def(*dst);
+            collector.reg_use(src1);
             src2.get_operands(collector);
         }
         Inst::XmmVexPinsr {
             src1, src2, dst, ..
         } => {
-            collector.reg_def(dst.to_writable_reg());
-            collector.reg_use(src1.to_reg());
+            collector.reg_def(*dst);
+            collector.reg_use(src1);
             src2.get_operands(collector);
         }
         Inst::XmmRmRVex3 {
@@ -2092,9 +2091,9 @@ fn x64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut OperandCol
             dst,
             ..
         } => {
-            collector.reg_use(src1.to_reg());
-            collector.reg_reuse_def(dst.to_writable_reg(), 0);
-            collector.reg_use(src2.to_reg());
+            collector.reg_use(src1);
+            collector.reg_reuse_def(*dst, 0);
+            collector.reg_use(src2);
             src3.get_operands(collector);
         }
         Inst::XmmRmRBlendVex {
@@ -2104,10 +2103,10 @@ fn x64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut OperandCol
             dst,
             ..
         } => {
-            collector.reg_def(dst.to_writable_reg());
-            collector.reg_use(src1.to_reg());
+            collector.reg_def(*dst);
+            collector.reg_use(src1);
             src2.get_operands(collector);
-            collector.reg_use(mask.to_reg());
+            collector.reg_use(mask);
         }
         Inst::XmmRmREvex {
             op,
@@ -2117,9 +2116,9 @@ fn x64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut OperandCol
             ..
         } => {
             assert_ne!(*op, Avx512Opcode::Vpermi2b);
-            collector.reg_use(src1.to_reg());
+            collector.reg_use(src1);
             src2.get_operands(collector);
-            collector.reg_def(dst.to_writable_reg());
+            collector.reg_def(*dst);
         }
         Inst::XmmRmREvex3 {
             op,
@@ -2130,10 +2129,10 @@ fn x64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut OperandCol
             ..
         } => {
             assert_eq!(*op, Avx512Opcode::Vpermi2b);
-            collector.reg_use(src1.to_reg());
-            collector.reg_use(src2.to_reg());
+            collector.reg_use(src1);
+            collector.reg_use(src2);
             src3.get_operands(collector);
-            collector.reg_reuse_def(dst.to_writable_reg(), 0); // Reuse `src1`.
+            collector.reg_reuse_def(*dst, 0); // Reuse `src1`.
         }
         Inst::XmmRmRImm {
             src1, src2, dst, ..
@@ -2142,101 +2141,101 @@ fn x64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut OperandCol
             collector.reg_reuse_def(*dst, 0);
             src2.get_operands(collector);
         }
-        Inst::XmmUninitializedValue { dst } => collector.reg_def(dst.to_writable_reg()),
-        Inst::XmmMinMaxSeq { lhs, rhs, dst, .. } => {
-            collector.reg_use(rhs.to_reg());
-            collector.reg_use(lhs.to_reg());
-            collector.reg_reuse_def(dst.to_writable_reg(), 0); // Reuse RHS.
+        &Inst::XmmUninitializedValue { dst } => collector.reg_def(dst),
+        &Inst::XmmMinMaxSeq { lhs, rhs, dst, .. } => {
+            collector.reg_use(rhs);
+            collector.reg_use(lhs);
+            collector.reg_reuse_def(dst, 0); // Reuse RHS.
         }
         Inst::XmmRmiReg {
             src1, src2, dst, ..
         } => {
-            collector.reg_use(src1.to_reg());
-            collector.reg_reuse_def(dst.to_writable_reg(), 0); // Reuse RHS.
+            collector.reg_use(src1);
+            collector.reg_reuse_def(*dst, 0); // Reuse RHS.
             src2.get_operands(collector);
         }
         Inst::XmmMovRM { src, dst, .. }
         | Inst::XmmMovRMVex { src, dst, .. }
         | Inst::XmmMovRMImm { src, dst, .. }
         | Inst::XmmMovRMImmVex { src, dst, .. } => {
-            collector.reg_use(src.to_reg());
+            collector.reg_use(src);
             dst.get_operands(collector);
         }
         Inst::XmmCmpRmR { src1, src2, .. } => {
-            collector.reg_use(src1.to_reg());
+            collector.reg_use(src1);
             src2.get_operands(collector);
         }
         Inst::XmmCmpRmRVex { src1, src2, .. } => {
-            collector.reg_use(src1.to_reg());
+            collector.reg_use(src1);
             src2.get_operands(collector);
         }
-        Inst::Imm { dst, .. } => {
-            collector.reg_def(dst.to_writable_reg());
+        &Inst::Imm { dst, .. } => {
+            collector.reg_def(dst);
         }
-        Inst::MovRR { src, dst, .. } => {
-            collector.reg_use(src.to_reg());
-            collector.reg_def(dst.to_writable_reg());
+        &Inst::MovRR { src, dst, .. } => {
+            collector.reg_use(src);
+            collector.reg_def(dst);
         }
-        Inst::MovFromPReg { dst, src } => {
+        &Inst::MovFromPReg { dst, src } => {
             debug_assert!(dst.to_reg().to_reg().is_virtual());
-            collector.reg_fixed_nonallocatable(*src);
-            collector.reg_def(dst.to_writable_reg());
+            collector.reg_fixed_nonallocatable(src);
+            collector.reg_def(dst);
         }
         Inst::MovToPReg { dst, src } => {
             debug_assert!(src.to_reg().is_virtual());
-            collector.reg_use(src.to_reg());
+            collector.reg_use(src);
             collector.reg_fixed_nonallocatable(*dst);
         }
-        Inst::XmmToGpr { src, dst, .. }
-        | Inst::XmmToGprVex { src, dst, .. }
-        | Inst::XmmToGprImm { src, dst, .. }
-        | Inst::XmmToGprImmVex { src, dst, .. } => {
-            collector.reg_use(src.to_reg());
-            collector.reg_def(dst.to_writable_reg());
+        &Inst::XmmToGpr { src, dst, .. }
+        | &Inst::XmmToGprVex { src, dst, .. }
+        | &Inst::XmmToGprImm { src, dst, .. }
+        | &Inst::XmmToGprImmVex { src, dst, .. } => {
+            collector.reg_use(src);
+            collector.reg_def(dst);
         }
         Inst::GprToXmm { src, dst, .. } | Inst::GprToXmmVex { src, dst, .. } => {
-            collector.reg_def(dst.to_writable_reg());
+            collector.reg_def(*dst);
             src.get_operands(collector);
         }
         Inst::CvtIntToFloat {
             src1, src2, dst, ..
         } => {
-            collector.reg_use(src1.to_reg());
-            collector.reg_reuse_def(dst.to_writable_reg(), 0);
+            collector.reg_use(src1);
+            collector.reg_reuse_def(*dst, 0);
             src2.get_operands(collector);
         }
         Inst::CvtIntToFloatVex {
             src1, src2, dst, ..
         } => {
-            collector.reg_def(dst.to_writable_reg());
-            collector.reg_use(src1.to_reg());
+            collector.reg_def(*dst);
+            collector.reg_use(src1);
             src2.get_operands(collector);
         }
-        Inst::CvtUint64ToFloatSeq {
+        &Inst::CvtUint64ToFloatSeq {
             src,
             dst,
             tmp_gpr1,
             tmp_gpr2,
             ..
         } => {
-            collector.reg_use(src.to_reg());
-            collector.reg_early_def(dst.to_writable_reg());
-            collector.reg_early_def(tmp_gpr1.to_writable_reg());
-            collector.reg_early_def(tmp_gpr2.to_writable_reg());
+            collector.reg_use(src);
+            collector.reg_early_def(dst);
+            collector.reg_early_def(tmp_gpr1);
+            collector.reg_early_def(tmp_gpr2);
         }
-        Inst::CvtFloatToSintSeq {
+        &Inst::CvtFloatToSintSeq {
             src,
             dst,
             tmp_xmm,
             tmp_gpr,
             ..
         } => {
-            collector.reg_use(src.to_reg());
-            collector.reg_early_def(dst.to_writable_reg());
-            collector.reg_early_def(tmp_gpr.to_writable_reg());
-            collector.reg_early_def(tmp_xmm.to_writable_reg());
+            collector.reg_use(src);
+            collector.reg_early_def(dst);
+            collector.reg_early_def(tmp_gpr);
+            collector.reg_early_def(tmp_xmm);
         }
-        Inst::CvtFloatToUintSeq {
+        &Inst::CvtFloatToUintSeq {
             src,
             dst,
             tmp_gpr,
@@ -2244,11 +2243,11 @@ fn x64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut OperandCol
             tmp_xmm2,
             ..
         } => {
-            collector.reg_use(src.to_reg());
-            collector.reg_early_def(dst.to_writable_reg());
-            collector.reg_early_def(tmp_gpr.to_writable_reg());
-            collector.reg_early_def(tmp_xmm.to_writable_reg());
-            collector.reg_early_def(tmp_xmm2.to_writable_reg());
+            collector.reg_use(src);
+            collector.reg_early_def(dst);
+            collector.reg_early_def(tmp_gpr);
+            collector.reg_early_def(tmp_xmm);
+            collector.reg_early_def(tmp_xmm2);
         }
 
         Inst::MovImmM { dst, .. } => {
@@ -2256,44 +2255,44 @@ fn x64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut OperandCol
         }
 
         Inst::MovzxRmR { src, dst, .. } => {
-            collector.reg_def(dst.to_writable_reg());
+            collector.reg_def(*dst);
             src.get_operands(collector);
         }
         Inst::Mov64MR { src, dst, .. } => {
-            collector.reg_def(dst.to_writable_reg());
+            collector.reg_def(*dst);
             src.get_operands(collector);
         }
         Inst::LoadEffectiveAddress { addr: src, dst, .. } => {
-            collector.reg_def(dst.to_writable_reg());
+            collector.reg_def(*dst);
             src.get_operands(collector);
         }
         Inst::MovsxRmR { src, dst, .. } => {
-            collector.reg_def(dst.to_writable_reg());
+            collector.reg_def(*dst);
             src.get_operands(collector);
         }
         Inst::MovRM { src, dst, .. } => {
-            collector.reg_use(src.to_reg());
+            collector.reg_use(src);
             dst.get_operands(collector);
         }
         Inst::ShiftR {
             num_bits, src, dst, ..
         } => {
-            collector.reg_use(src.to_reg());
-            collector.reg_reuse_def(dst.to_writable_reg(), 0);
+            collector.reg_use(src);
+            collector.reg_reuse_def(*dst, 0);
             if let &Imm8Reg::Reg { reg } = num_bits.as_imm8_reg() {
                 collector.reg_fixed_use(reg, regs::rcx());
             }
         }
         Inst::CmpRmiR { src1, src2, .. } => {
-            collector.reg_use(src1.to_reg());
+            collector.reg_use(src1);
             src2.get_operands(collector);
         }
         Inst::Setcc { dst, .. } => {
-            collector.reg_def(dst.to_writable_reg());
+            collector.reg_def(*dst);
         }
         Inst::Bswap { src, dst, .. } => {
-            collector.reg_use(src.to_reg());
-            collector.reg_reuse_def(dst.to_writable_reg(), 0);
+            collector.reg_use(src);
+            collector.reg_reuse_def(*dst, 0);
         }
         Inst::Cmove {
             consequent,
@@ -2301,8 +2300,8 @@ fn x64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut OperandCol
             dst,
             ..
         } => {
-            collector.reg_use(alternative.to_reg());
-            collector.reg_reuse_def(dst.to_writable_reg(), 0);
+            collector.reg_use(alternative);
+            collector.reg_reuse_def(*dst, 0);
             consequent.get_operands(collector);
         }
         Inst::XmmCmove {
@@ -2311,15 +2310,15 @@ fn x64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut OperandCol
             dst,
             ..
         } => {
-            collector.reg_use(alternative.to_reg());
-            collector.reg_reuse_def(dst.to_writable_reg(), 0);
-            collector.reg_use(consequent.to_reg());
+            collector.reg_use(alternative);
+            collector.reg_reuse_def(*dst, 0);
+            collector.reg_use(consequent);
         }
         Inst::Push64 { src } => {
             src.get_operands(collector);
         }
         Inst::Pop64 { dst } => {
-            collector.reg_def(dst.to_writable_reg());
+            collector.reg_def(*dst);
         }
         Inst::StackProbeLoop { tmp, .. } => {
             collector.reg_early_def(*tmp);
@@ -2363,7 +2362,7 @@ fn x64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut OperandCol
 
         Inst::ReturnCallKnown { callee, info } => {
             let ReturnCallInfo { uses, tmp, .. } = &**info;
-            collector.reg_fixed_def(tmp.to_writable_reg(), regs::r11());
+            collector.reg_fixed_def(*tmp, regs::r11());
             // Same as in the `Inst::CallKnown` branch.
             debug_assert_ne!(*callee, ExternalName::LibCall(LibCall::Probestack));
             for u in uses {
@@ -2381,24 +2380,21 @@ fn x64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut OperandCol
             // safe to use.
             collector.reg_fixed_use(*callee, regs::r10());
 
-            collector.reg_fixed_def(tmp.to_writable_reg(), regs::r11());
+            collector.reg_fixed_def(*tmp, regs::r11());
             for u in uses {
                 collector.reg_fixed_use(u.vreg, u.preg);
             }
         }
 
-        Inst::JmpTableSeq {
-            ref idx,
-            ref tmp1,
-            ref tmp2,
-            ..
+        &Inst::JmpTableSeq {
+            idx, tmp1, tmp2, ..
         } => {
-            collector.reg_use(*idx);
-            collector.reg_early_def(*tmp1);
+            collector.reg_use(idx);
+            collector.reg_early_def(tmp1);
             // In the sequence emitted for this pseudoinstruction in emit.rs,
             // tmp2 is only written after idx is read, so it doesn't need to be
             // an early def.
-            collector.reg_def(*tmp2);
+            collector.reg_def(tmp2);
         }
 
         Inst::JmpUnknown { target } => {
@@ -2466,8 +2462,8 @@ fn x64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut OperandCol
             // No registers are used.
         }
 
-        Inst::ElfTlsGetAddr { dst, .. } | Inst::MachOTlsGetAddr { dst, .. } => {
-            collector.reg_fixed_def(dst.to_writable_reg(), regs::rax());
+        &Inst::ElfTlsGetAddr { dst, .. } | &Inst::MachOTlsGetAddr { dst, .. } => {
+            collector.reg_fixed_def(dst, regs::rax());
             // All caller-saves are clobbered.
             //
             // We use the SysV calling convention here because the
@@ -2479,15 +2475,15 @@ fn x64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut OperandCol
             collector.reg_clobbers(clobbers);
         }
 
-        Inst::CoffTlsGetAddr { dst, tmp, .. } => {
+        &Inst::CoffTlsGetAddr { dst, tmp, .. } => {
             // We also use the gs register. But that register is not allocatable by the
             // register allocator, so we don't need to mark it as used here.
 
             // We use %rax to set the address
-            collector.reg_fixed_def(dst.to_writable_reg(), regs::rax());
+            collector.reg_fixed_def(dst, regs::rax());
 
             // We use %rcx as a temporary variable to load the _tls_index
-            collector.reg_fixed_def(tmp.to_writable_reg(), regs::rcx());
+            collector.reg_fixed_def(tmp, regs::rcx());
         }
 
         Inst::Unwind { .. } => {}
diff --git a/cranelift/codegen/src/machinst/abi.rs b/cranelift/codegen/src/machinst/abi.rs
index 3e0f3a52a..a5de3824d 100644
--- a/cranelift/codegen/src/machinst/abi.rs
+++ b/cranelift/codegen/src/machinst/abi.rs
@@ -1094,16 +1094,10 @@ pub struct Callee<M: ABIMachineSpec> {
     /// Register-argument defs, to be provided to the `args`
     /// pseudo-inst, and pregs to constrain them to.
     reg_args: Vec<ArgPair>,
-    /// Clobbered registers, from regalloc.
-    clobbered: Vec<Writable<RealReg>>,
-    /// Total number of spillslots, including for 'dynamic' types, from regalloc.
-    spillslots: Option<usize>,
     /// Finalized frame layout for this function.
     frame_layout: Option<FrameLayout>,
     /// The register holding the return-area pointer, if needed.
-    ret_area_ptr: Option<Writable<Reg>>,
-    /// Temp registers required for argument setup, if needed.
-    arg_temp_reg: Vec<Option<Writable<Reg>>>,
+    ret_area_ptr: Option<Reg>,
     /// Calling convention this function expects.
     call_conv: isa::CallConv,
     /// The settings controlling this function's compilation.
@@ -1243,11 +1237,8 @@ impl<M: ABIMachineSpec> Callee<M> {
             outgoing_args_size: 0,
             tail_args_size,
             reg_args: vec![],
-            clobbered: vec![],
-            spillslots: None,
             frame_layout: None,
             ret_area_ptr: None,
-            arg_temp_reg: vec![],
             call_conv,
             flags,
             isa_flags: isa_flags.clone(),
@@ -1412,45 +1403,19 @@ impl<M: ABIMachineSpec> Callee<M> {
         &self.ir_sig
     }
 
-    /// Does the ABI-body code need temp registers (and if so, of what type)?
-    /// They will be provided to `init()` as the `temps` arg if so.
-    pub fn temps_needed(&self, sigs: &SigSet) -> Vec<Type> {
-        let mut temp_tys = vec![];
-        for arg in sigs.args(self.sig) {
-            match arg {
-                &ABIArg::ImplicitPtrArg { pointer, .. } => match &pointer {
-                    &ABIArgSlot::Reg { .. } => {}
-                    &ABIArgSlot::Stack { ty, .. } => {
-                        temp_tys.push(ty);
-                    }
-                },
-                _ => {}
-            }
-        }
-        if sigs[self.sig].stack_ret_arg.is_some() {
-            temp_tys.push(M::word_type());
-        }
-        temp_tys
-    }
-
     /// Initialize. This is called after the Callee is constructed because it
-    /// may be provided with a vector of temp vregs, which can only be allocated
-    /// once the lowering context exists.
-    pub fn init(&mut self, sigs: &SigSet, temps: Vec<Writable<Reg>>) {
-        let mut temps_iter = temps.into_iter();
-        for arg in sigs.args(self.sig) {
-            let temp = match arg {
-                &ABIArg::ImplicitPtrArg { pointer, .. } => match &pointer {
-                    &ABIArgSlot::Reg { .. } => None,
-                    &ABIArgSlot::Stack { .. } => Some(temps_iter.next().unwrap()),
-                },
-                _ => None,
-            };
-            self.arg_temp_reg.push(temp);
-        }
+    /// may allocate a temp vreg, which can only be allocated once the lowering
+    /// context exists.
+    pub fn init_retval_area(
+        &mut self,
+        sigs: &SigSet,
+        vregs: &mut VRegAllocator<M::I>,
+    ) -> CodegenResult<()> {
         if sigs[self.sig].stack_ret_arg.is_some() {
-            self.ret_area_ptr = Some(temps_iter.next().unwrap());
+            let ret_area_ptr = vregs.alloc(M::word_type())?;
+            self.ret_area_ptr = Some(ret_area_ptr.only_reg().unwrap());
         }
+        Ok(())
     }
 
     /// Accumulate outgoing arguments.
@@ -1580,9 +1545,9 @@ impl<M: ABIMachineSpec> Callee<M> {
                         tmp
                     }
                     &ABIArgSlot::Stack { offset, ty, .. } => {
-                        // In this case we need a temp register to hold the address.
-                        // This was allocated in the `init` routine.
-                        let addr_reg = self.arg_temp_reg[idx].unwrap();
+                        let addr_reg = writable_value_regs(vregs.alloc_with_deferred_error(ty))
+                            .only_reg()
+                            .unwrap();
                         insts.push(M::gen_load_stack(
                             StackAMode::IncomingArg(offset, sigs[self.sig].sized_stack_arg_space),
                             addr_reg,
@@ -1682,7 +1647,7 @@ impl<M: ABIMachineSpec> Callee<M> {
                                 _ => {}
                             };
                             ret.push(M::gen_store_base_offset(
-                                self.ret_area_ptr.unwrap().to_reg(),
+                                self.ret_area_ptr.unwrap(),
                                 off,
                                 from_reg,
                                 ty,
@@ -1712,17 +1677,14 @@ impl<M: ABIMachineSpec> Callee<M> {
         vregs: &mut VRegAllocator<M::I>,
     ) -> Option<M::I> {
         if let Some(i) = sigs[self.sig].stack_ret_arg {
-            let insts = self.gen_copy_arg_to_regs(
-                sigs,
-                i.into(),
-                ValueRegs::one(self.ret_area_ptr.unwrap()),
-                vregs,
-            );
+            let ret_area_ptr = Writable::from_reg(self.ret_area_ptr.unwrap());
+            let insts =
+                self.gen_copy_arg_to_regs(sigs, i.into(), ValueRegs::one(ret_area_ptr), vregs);
             insts.into_iter().next().map(|inst| {
                 trace!(
                     "gen_retval_area_setup: inst {:?}; ptr reg is {:?}",
                     inst,
-                    self.ret_area_ptr.unwrap().to_reg()
+                    ret_area_ptr.to_reg()
                 );
                 inst
             })
@@ -1777,16 +1739,6 @@ impl<M: ABIMachineSpec> Callee<M> {
 /// These methods of `Callee` may only be called after
 /// regalloc.
 impl<M: ABIMachineSpec> Callee<M> {
-    /// Update with the number of spillslots, post-regalloc.
-    pub fn set_num_spillslots(&mut self, slots: usize) {
-        self.spillslots = Some(slots);
-    }
-
-    /// Update with the clobbered registers, post-regalloc.
-    pub fn set_clobbered(&mut self, clobbered: Vec<Writable<RealReg>>) {
-        self.clobbered = clobbered;
-    }
-
     /// Generate a stack map, given a list of spillslots and the emission state
     /// at a given program point (prior to emission of the safepointing
     /// instruction).
@@ -1823,16 +1775,21 @@ impl<M: ABIMachineSpec> Callee<M> {
     /// Compute the final frame layout, post-regalloc.
     ///
     /// This must be called before gen_prologue or gen_epilogue.
-    pub fn compute_frame_layout(&mut self, sigs: &SigSet) {
+    pub fn compute_frame_layout(
+        &mut self,
+        sigs: &SigSet,
+        spillslots: usize,
+        clobbered: Vec<Writable<RealReg>>,
+    ) {
         let bytes = M::word_bytes();
-        let total_stacksize = self.stackslots_size + bytes * self.spillslots.unwrap() as u32;
+        let total_stacksize = self.stackslots_size + bytes * spillslots as u32;
         let mask = M::stack_align(self.call_conv) - 1;
         let total_stacksize = (total_stacksize + mask) & !mask; // 16-align the stack.
         self.frame_layout = Some(M::compute_frame_layout(
             self.call_conv,
             &self.flags,
             self.signature(),
-            &self.clobbered,
+            &clobbered,
             self.is_leaf,
             self.stack_args_size(sigs),
             self.tail_args_size,
@@ -2401,7 +2358,7 @@ impl<M: ABIMachineSpec> CallSite<M> {
                 "if the tail callee has a return pointer, then the tail caller \
                  must as well",
             );
-            self.gen_arg(ctx, i.into(), ValueRegs::one(ret_area_ptr.to_reg()));
+            self.gen_arg(ctx, i.into(), ValueRegs::one(ret_area_ptr));
         }
     }
 
diff --git a/cranelift/codegen/src/machinst/lower.rs b/cranelift/codegen/src/machinst/lower.rs
index a6b3bc501..d7ef8b26c 100644
--- a/cranelift/codegen/src/machinst/lower.rs
+++ b/cranelift/codegen/src/machinst/lower.rs
@@ -989,14 +989,10 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
             for &arg in branch_args {
                 let arg = self.f.dfg.resolve_aliases(arg);
                 let regs = self.put_value_in_regs(arg);
-                for &vreg in regs.regs() {
-                    let vreg = self.vcode.vcode.resolve_vreg_alias(vreg.into());
-                    branch_arg_vregs.push(vreg.into());
-                }
+                branch_arg_vregs.extend_from_slice(regs.regs());
             }
             self.vcode.add_succ(succ, &branch_arg_vregs[..]);
         }
-        self.finish_ir_inst(Default::default());
     }
 
     fn collect_branches_and_targets(
@@ -1019,15 +1015,7 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
     ) -> CodegenResult<VCode<I>> {
         trace!("about to lower function: {:?}", self.f);
 
-        // Initialize the ABI object, giving it temps if requested.
-        let temps = self
-            .vcode
-            .abi()
-            .temps_needed(self.sigs())
-            .into_iter()
-            .map(|temp_ty| self.alloc_tmp(temp_ty).only_reg().unwrap())
-            .collect::<Vec<_>>();
-        self.vcode.init_abi(temps);
+        self.vcode.init_retval_area(&mut self.vregs)?;
 
         // Get the pinned reg here (we only parameterize this function on `B`,
         // not the whole `Lower` impl).
diff --git a/cranelift/codegen/src/machinst/reg.rs b/cranelift/codegen/src/machinst/reg.rs
index 1407f80a4..4421a38f4 100644
--- a/cranelift/codegen/src/machinst/reg.rs
+++ b/cranelift/codegen/src/machinst/reg.rs
@@ -352,46 +352,47 @@ impl<'a, F: Fn(VReg) -> VReg> OperandCollector<'a, F> {
 
     /// Add a register use, at the start of the instruction (`Before`
     /// position).
-    pub fn reg_use(&mut self, reg: Reg) {
-        self.reg_maybe_fixed(reg, OperandKind::Use, OperandPos::Early);
+    pub fn reg_use(&mut self, reg: impl Into<Reg>) {
+        self.reg_maybe_fixed(reg.into(), OperandKind::Use, OperandPos::Early);
     }
 
     /// Add a register use, at the end of the instruction (`After` position).
-    pub fn reg_late_use(&mut self, reg: Reg) {
-        self.reg_maybe_fixed(reg, OperandKind::Use, OperandPos::Late);
+    pub fn reg_late_use(&mut self, reg: impl Into<Reg>) {
+        self.reg_maybe_fixed(reg.into(), OperandKind::Use, OperandPos::Late);
     }
 
     /// Add a register def, at the end of the instruction (`After`
     /// position). Use only when this def will be written after all
     /// uses are read.
-    pub fn reg_def(&mut self, reg: Writable<Reg>) {
-        self.reg_maybe_fixed(reg.to_reg(), OperandKind::Def, OperandPos::Late);
+    pub fn reg_def(&mut self, reg: Writable<impl Into<Reg>>) {
+        self.reg_maybe_fixed(reg.to_reg().into(), OperandKind::Def, OperandPos::Late);
     }
 
     /// Add a register "early def", which logically occurs at the
     /// beginning of the instruction, alongside all uses. Use this
     /// when the def may be written before all uses are read; the
     /// regalloc will ensure that it does not overwrite any uses.
-    pub fn reg_early_def(&mut self, reg: Writable<Reg>) {
-        self.reg_maybe_fixed(reg.to_reg(), OperandKind::Def, OperandPos::Early);
+    pub fn reg_early_def(&mut self, reg: Writable<impl Into<Reg>>) {
+        self.reg_maybe_fixed(reg.to_reg().into(), OperandKind::Def, OperandPos::Early);
     }
 
     /// Add a register "fixed use", which ties a vreg to a particular
     /// RealReg at the end of the instruction.
-    pub fn reg_fixed_late_use(&mut self, reg: Reg, rreg: Reg) {
-        self.reg_fixed(reg, rreg, OperandKind::Use, OperandPos::Late);
+    pub fn reg_fixed_late_use(&mut self, reg: impl Into<Reg>, rreg: Reg) {
+        self.reg_fixed(reg.into(), rreg, OperandKind::Use, OperandPos::Late);
     }
 
     /// Add a register "fixed use", which ties a vreg to a particular
     /// RealReg at this point.
-    pub fn reg_fixed_use(&mut self, reg: Reg, rreg: Reg) {
-        self.reg_fixed(reg, rreg, OperandKind::Use, OperandPos::Early);
+    pub fn reg_fixed_use(&mut self, reg: impl Into<Reg>, rreg: Reg) {
+        self.reg_fixed(reg.into(), rreg, OperandKind::Use, OperandPos::Early);
     }
 
     /// Add a register "fixed def", which ties a vreg to a particular
     /// RealReg at this point.
-    pub fn reg_fixed_def(&mut self, reg: Writable<Reg>, rreg: Reg) {
-        self.reg_fixed(reg.to_reg(), rreg, OperandKind::Def, OperandPos::Late);
+    pub fn reg_fixed_def(&mut self, reg: Writable<impl Into<Reg>>, rreg: Reg) {
+        let reg = reg.to_reg().into();
+        self.reg_fixed(reg, rreg, OperandKind::Def, OperandPos::Late);
     }
 
     /// Add an operand tying a virtual register to a physical register.
@@ -419,20 +420,21 @@ impl<'a, F: Fn(VReg) -> VReg> OperandCollector<'a, F> {
     /// Add a register def that reuses an earlier use-operand's
     /// allocation. The index of that earlier operand (relative to the
     /// current instruction's start of operands) must be known.
-    pub fn reg_reuse_def(&mut self, reg: Writable<Reg>, idx: usize) {
-        if let Some(rreg) = reg.to_reg().to_real_reg() {
+    pub fn reg_reuse_def(&mut self, reg: Writable<impl Into<Reg>>, idx: usize) {
+        let reg = reg.to_reg().into();
+        if let Some(rreg) = reg.to_real_reg() {
             // In some cases we see real register arguments to a reg_reuse_def
             // constraint. We assume the creator knows what they're doing
             // here, though we do also require that the real register be a
             // fixed-nonallocatable register.
             self.reg_fixed_nonallocatable(rreg.into());
         } else {
-            debug_assert!(reg.to_reg().is_virtual());
+            debug_assert!(reg.is_virtual());
             // The operand we're reusing must not be fixed-nonallocatable, as
             // that would imply that the register has been allocated to a
             // virtual register.
             let constraint = OperandConstraint::Reuse(idx);
-            self.add_operand(reg.to_reg(), constraint, OperandKind::Def, OperandPos::Late);
+            self.add_operand(reg, constraint, OperandKind::Def, OperandPos::Late);
         }
     }
 
diff --git a/cranelift/codegen/src/machinst/vcode.rs b/cranelift/codegen/src/machinst/vcode.rs
index d246a31dd..381556da9 100644
--- a/cranelift/codegen/src/machinst/vcode.rs
+++ b/cranelift/codegen/src/machinst/vcode.rs
@@ -291,8 +291,8 @@ impl<I: VCodeInst> VCodeBuilder<I> {
         }
     }
 
-    pub fn init_abi(&mut self, temps: Vec<Writable<Reg>>) {
-        self.vcode.abi.init(&self.vcode.sigs, temps);
+    pub fn init_retval_area(&mut self, vregs: &mut VRegAllocator<I>) -> CodegenResult<()> {
+        self.vcode.abi.init_retval_area(&self.vcode.sigs, vregs)
     }
 
     /// Access the ABI object.
@@ -658,18 +658,12 @@ impl<I: VCodeInst> VCode<I> {
     }
 
     fn compute_clobbers(&self, regalloc: &regalloc2::Output) -> Vec<Writable<RealReg>> {
-        // Compute clobbered registers.
-        let mut clobbered = vec![];
-        let mut clobbered_set = FxHashSet::default();
+        let mut clobbered = PRegSet::default();
 
         // All moves are included in clobbers.
-        for edit in &regalloc.edits {
-            let Edit::Move { to, .. } = edit.1;
+        for (_, Edit::Move { to, .. }) in &regalloc.edits {
             if let Some(preg) = to.as_reg() {
-                let reg = RealReg::from(preg);
-                if clobbered_set.insert(reg) {
-                    clobbered.push(Writable::from_reg(reg));
-                }
+                clobbered.add(preg);
             }
         }
 
@@ -689,33 +683,23 @@ impl<I: VCodeInst> VCode<I> {
             let operands = &self.operands[start..end];
             let allocs = &regalloc.allocs[start..end];
             for (operand, alloc) in operands.iter().zip(allocs.iter()) {
-                // We're interested only in writes (Mods or Defs).
-                if operand.kind() == OperandKind::Use {
-                    continue;
-                }
-                if let Some(preg) = alloc.as_reg() {
-                    let reg = RealReg::from(preg);
-                    if clobbered_set.insert(reg) {
-                        clobbered.push(Writable::from_reg(reg));
+                if operand.kind() == OperandKind::Def {
+                    if let Some(preg) = alloc.as_reg() {
+                        clobbered.add(preg);
                     }
                 }
             }
 
             // Also add explicitly-clobbered registers.
-            for preg in self
-                .clobbers
-                .get(&InsnIndex::new(i))
-                .cloned()
-                .unwrap_or_default()
-            {
-                let reg = RealReg::from(preg);
-                if clobbered_set.insert(reg) {
-                    clobbered.push(Writable::from_reg(reg));
-                }
+            if let Some(&inst_clobbered) = self.clobbers.get(&InsnIndex::new(i)) {
+                clobbered.union_from(inst_clobbered);
             }
         }
 
         clobbered
+            .into_iter()
+            .map(|preg| Writable::from_reg(RealReg::from(preg)))
+            .collect()
     }
 
     /// Emit the instructions to a `MachBuffer`, containing fixed-up
@@ -772,9 +756,8 @@ impl<I: VCodeInst> VCode<I> {
         // setting clobbers is fairly minimal so this should be
         // relatively cheap.
         let clobbers = self.compute_clobbers(regalloc);
-        self.abi.set_num_spillslots(regalloc.num_spillslots);
-        self.abi.set_clobbered(clobbers);
-        self.abi.compute_frame_layout(&self.sigs);
+        self.abi
+            .compute_frame_layout(&self.sigs, regalloc.num_spillslots, clobbers);
 
         // Emit blocks.
         let mut cur_srcloc = None;
@@ -1231,7 +1214,7 @@ impl<I: VCodeInst> VCode<I> {
         self.block_order.lowered_order()[block.index()].orig_block()
     }
 
-    pub fn resolve_vreg_alias(&self, from: regalloc2::VReg) -> regalloc2::VReg {
+    fn resolve_vreg_alias(&self, from: regalloc2::VReg) -> regalloc2::VReg {
         Self::resolve_vreg_alias_impl(&self.vreg_aliases, from)
     }
 
@@ -1260,26 +1243,8 @@ impl<I: VCodeInst> VCode<I> {
     }
 
     #[inline]
-    fn assert_no_vreg_aliases<'a>(&self, list: &'a [VReg]) -> &'a [VReg] {
-        for vreg in list {
-            self.assert_not_vreg_alias(*vreg);
-        }
-        list
-    }
-
-    #[inline]
-    fn assert_not_vreg_alias(&self, vreg: VReg) -> VReg {
-        debug_assert!(self.resolve_vreg_alias(vreg) == vreg);
-        vreg
-    }
-
-    #[inline]
-    fn assert_operand_not_vreg_alias(&self, op: Operand) -> Operand {
-        // It should be true by construction that `Operand`s do not contain any
-        // aliased vregs since they're all collected and mapped when the VCode
-        // is itself constructed.
-        self.assert_not_vreg_alias(op.vreg());
-        op
+    fn debug_assert_no_vreg_aliases(&self, mut list: impl Iterator<Item = VReg>) {
+        debug_assert!(list.all(|vreg| !self.vreg_aliases.contains_key(&vreg)));
     }
 
     /// Get the type of a VReg.
@@ -1356,7 +1321,8 @@ impl<I: VCodeInst> RegallocFunction for VCode<I> {
         let ret = &self.block_params[start as usize..end as usize];
         // Currently block params are never aliased to another vreg, but
         // double-check just to be sure.
-        self.assert_no_vreg_aliases(ret)
+        self.debug_assert_no_vreg_aliases(ret.iter().copied());
+        ret
     }
 
     fn branch_blockparams(&self, block: BlockIndex, _insn: InsnIndex, succ_idx: usize) -> &[VReg] {
@@ -1366,7 +1332,8 @@ impl<I: VCodeInst> RegallocFunction for VCode<I> {
         let (branch_block_args_start, branch_block_args_end) = succ_ranges[succ_idx];
         let ret = &self.branch_block_args
             [branch_block_args_start as usize..branch_block_args_end as usize];
-        self.assert_no_vreg_aliases(ret)
+        self.debug_assert_no_vreg_aliases(ret.iter().copied());
+        ret
     }
 
     fn is_ret(&self, insn: InsnIndex) -> bool {
@@ -1392,9 +1359,10 @@ impl<I: VCodeInst> RegallocFunction for VCode<I> {
     fn inst_operands(&self, insn: InsnIndex) -> &[Operand] {
         let (start, end) = self.operand_ranges[insn.index()];
         let ret = &self.operands[start as usize..end as usize];
-        for op in ret {
-            self.assert_operand_not_vreg_alias(*op);
-        }
+        // It should be true by construction that `Operand`s do not contain any
+        // aliased vregs since they're all collected and mapped when the VCode
+        // is itself constructed.
+        self.debug_assert_no_vreg_aliases(ret.iter().map(|op| op.vreg()));
         ret
     }
 
@@ -1407,17 +1375,18 @@ impl<I: VCodeInst> RegallocFunction for VCode<I> {
     }
 
     fn reftype_vregs(&self) -> &[VReg] {
-        self.assert_no_vreg_aliases(&self.reftyped_vregs[..])
+        let ret = &self.reftyped_vregs;
+        self.debug_assert_no_vreg_aliases(ret.iter().copied());
+        ret
     }
 
     fn debug_value_labels(&self) -> &[(VReg, InsnIndex, InsnIndex, u32)] {
         // VRegs here are inserted into `debug_value_labels` after code is
-        // generated and aliases are fully defined, so no double-check that
+        // generated and aliases are fully defined, so double-check that
         // aliases are not lingering.
-        for (vreg, ..) in self.debug_value_labels.iter() {
-            self.assert_not_vreg_alias(*vreg);
-        }
-        &self.debug_value_labels[..]
+        let ret = &self.debug_value_labels;
+        self.debug_assert_no_vreg_aliases(ret.iter().map(|&(vreg, ..)| vreg));
+        ret
     }
 
     fn spillslot_size(&self, regclass: RegClass) -> usize {
diff --git a/cranelift/module/Cargo.toml b/cranelift/module/Cargo.toml
index 347ce4133..a33d82b13 100644
--- a/cranelift/module/Cargo.toml
+++ b/cranelift/module/Cargo.toml
@@ -17,7 +17,7 @@ workspace = true
 cranelift-codegen = { workspace = true }
 cranelift-control = { workspace = true }
 hashbrown = { workspace = true, optional = true }
-anyhow = { workspace = true }
+anyhow = { workspace = true, features = ['std'] }
 serde = { workspace = true, optional = true }
 serde_derive = { workspace = true, optional = true }
 
diff --git a/cranelift/object/src/backend.rs b/cranelift/object/src/backend.rs
index 581f6a87a..af51c5602 100644
--- a/cranelift/object/src/backend.rs
+++ b/cranelift/object/src/backend.rs
@@ -39,10 +39,10 @@ impl ObjectBuilder {
     /// Create a new `ObjectBuilder` using the given Cranelift target, that
     /// can be passed to [`ObjectModule::new`].
     ///
-    /// The `libcall_names` function provides a way to translate `cranelift_codegen`'s [ir::LibCall]
+    /// The `libcall_names` function provides a way to translate `cranelift_codegen`'s [`ir::LibCall`]
     /// enum to symbols. LibCalls are inserted in the IR as part of the legalization for certain
     /// floating point instructions, and for stack probes. If you don't know what to use for this
-    /// argument, use [cranelift_module::default_libcall_names]().
+    /// argument, use [`cranelift_module::default_libcall_names`].
     pub fn new<V: Into<Vec<u8>>>(
         isa: OwnedTargetIsa,
         name: V,
diff --git a/cranelift/reader/Cargo.toml b/cranelift/reader/Cargo.toml
index 889d2c569..a1df50523 100644
--- a/cranelift/reader/Cargo.toml
+++ b/cranelift/reader/Cargo.toml
@@ -13,7 +13,7 @@ edition.workspace = true
 workspace = true
 
 [dependencies]
-anyhow.workspace = true
+anyhow = { workspace = true, features = ['std'] }
 cranelift-codegen = { workspace = true }
 smallvec = { workspace = true }
 target-lexicon = { workspace = true }
diff --git a/cranelift/wasm/src/sections_translator.rs b/cranelift/wasm/src/sections_translator.rs
index ba1b4ad0a..c9c89633b 100644
--- a/cranelift/wasm/src/sections_translator.rs
+++ b/cranelift/wasm/src/sections_translator.rs
@@ -381,6 +381,7 @@ pub fn parse_name_section<'data>(
             | wasmparser::Name::Element(_)
             | wasmparser::Name::Data(_)
             | wasmparser::Name::Tag(_)
+            | wasmparser::Name::Field(_)
             | wasmparser::Name::Unknown { .. } => {}
         }
     }
diff --git a/crates/asm-macros/src/lib.rs b/crates/asm-macros/src/lib.rs
index efd970d6b..0fd62e8b3 100644
--- a/crates/asm-macros/src/lib.rs
+++ b/crates/asm-macros/src/lib.rs
@@ -6,6 +6,8 @@
 //! function) and additionally handles visibility across platforms. All symbols
 //! should be visible to Rust but not visible externally outside of a `*.so`.
 
+#![no_std]
+
 cfg_if::cfg_if! {
     if #[cfg(target_os = "macos")] {
         #[macro_export]
diff --git a/crates/c-api/include/wasmtime/global.h b/crates/c-api/include/wasmtime/global.h
index 5d69c19f5..8b20af915 100644
--- a/crates/c-api/include/wasmtime/global.h
+++ b/crates/c-api/include/wasmtime/global.h
@@ -54,7 +54,7 @@ wasmtime_global_type(const wasmtime_context_t *store,
  * \param out where to store the value in this global.
  *
  * This function returns ownership of the contents of `out`, so
- * #wasmtime_val_delete may need to be called on the value.
+ * #wasmtime_val_unroot may need to be called on the value.
  */
 WASM_API_EXTERN void wasmtime_global_get(wasmtime_context_t *store,
                                          const wasmtime_global_t *global,
diff --git a/crates/c-api/include/wasmtime/val.h b/crates/c-api/include/wasmtime/val.h
index dc2ab3710..bdac5b9c7 100644
--- a/crates/c-api/include/wasmtime/val.h
+++ b/crates/c-api/include/wasmtime/val.h
@@ -20,38 +20,84 @@ extern "C" {
  * \brief Convenience alias for #wasmtime_anyref
  *
  * \struct wasmtime_anyref
- * \brief A host-defined un-forgeable reference to pass into WebAssembly.
+ * \brief A WebAssembly value in the `any` hierarchy of GC types.
  *
- * This structure represents an `anyref` that can be passed to WebAssembly.
- * It cannot be forged by WebAssembly itself and is guaranteed to have been
- * created by the host.
+ * This structure represents an `anyref` that WebAssembly can create and
+ * pass back to the host. The host can also create values to pass to a guest.
+ *
+ * Note that this structure does not itself contain the data that it refers to.
+ * Instead to contains metadata to point back within a #wasmtime_context_t, so
+ * referencing the internal data requires using a `wasmtime_context_t`.
+ *
+ * Anyref values are required to be explicitly unrooted via
+ * #wasmtime_anyref_unroot to enable them to be garbage-collected.
+ *
+ * Null anyref values are represented by this structure and can be tested and
+ * created with the `wasmtime_anyref_is_null` and `wasmtime_anyref_set_null`
+ * functions.
  */
-typedef struct wasmtime_anyref wasmtime_anyref_t;
+typedef struct wasmtime_anyref {
+  /// Internal metadata tracking within the store, embedders should not
+  /// configure or modify these fields.
+  uint64_t store_id;
+  /// Internal to Wasmtime.
+  uint32_t __private1;
+  /// Internal to Wasmtime.
+  uint32_t __private2;
+} wasmtime_anyref_t;
+
+/// \brief Helper function to initialize the `ref` provided to a null anyref
+/// value.
+static inline void wasmtime_anyref_set_null(wasmtime_anyref_t *ref) {
+  ref->store_id = 0;
+}
+
+/// \brief Helper function to return whether the provided `ref` points to a null
+/// `anyref` value.
+///
+/// Note that `ref` itself should not be null as null is represented internally
+/// within a #wasmtime_anyref_t value.
+static inline bool wasmtime_anyref_is_null(const wasmtime_anyref_t *ref) {
+  return ref->store_id == 0;
+}
 
 /**
- * \brief Creates a shallow copy of the `anyref` argument, returning a
- * separately owned pointer (depending on the configured collector this might
- * increase a reference count or create a new GC root).
+ * \brief Creates a new reference pointing to the same data that `anyref`
+ * points to (depending on the configured collector this might increase a
+ * reference count or create a new GC root).
+ *
+ * The returned reference is stored in `out`.
  */
-WASM_API_EXTERN wasmtime_anyref_t *
-wasmtime_anyref_clone(wasmtime_context_t *context, wasmtime_anyref_t *ref);
+WASM_API_EXTERN void wasmtime_anyref_clone(wasmtime_context_t *context,
+                                           const wasmtime_anyref_t *anyref,
+                                           wasmtime_anyref_t *out);
 
 /**
- * \brief Drops an owned pointer to `ref`, potentially deleting it if it's the
- * last reference, or allowing it to be collected during the next GC.
+ * \brief Unroots the `ref` provided within the `context`.
+ *
+ * This API is required to enable the `ref` value provided to be
+ * garbage-collected. This API itself does not necessarily garbage-collect the
+ * value, but it's possible to collect it in the future after this.
+ *
+ * This may modify `ref` and the contents of `ref` are left in an undefined
+ * state after this API is called and it should no longer be used.
+ *
+ * Note that null or i32 anyref values do not need to be unrooted but are still
+ * valid to pass to this function.
  */
-WASM_API_EXTERN void wasmtime_anyref_delete(wasmtime_context_t *context,
+WASM_API_EXTERN void wasmtime_anyref_unroot(wasmtime_context_t *context,
                                             wasmtime_anyref_t *ref);
 
 /**
  * \brief Converts a raw `anyref` value coming from #wasmtime_val_raw_t into
  * a #wasmtime_anyref_t.
  *
- * Note that the returned #wasmtime_anyref_t is an owned value that must be
- * deleted via #wasmtime_anyref_delete by the caller if it is non-null.
+ * The provided `out` pointer is filled in with a reference converted from
+ * `raw`.
  */
-WASM_API_EXTERN wasmtime_anyref_t *
-wasmtime_anyref_from_raw(wasmtime_context_t *context, uint32_t raw);
+WASM_API_EXTERN void wasmtime_anyref_from_raw(wasmtime_context_t *context,
+                                              uint32_t raw,
+                                              wasmtime_anyref_t *out);
 
 /**
  * \brief Converts a #wasmtime_anyref_t to a raw value suitable for storing
@@ -74,8 +120,9 @@ WASM_API_EXTERN uint32_t wasmtime_anyref_to_raw(wasmtime_context_t *context,
  *
  * If `i31val` does not fit in 31 bits, it is wrapped.
  */
-WASM_API_EXTERN wasmtime_anyref_t *
-wasmtime_anyref_from_i31(wasmtime_context_t *context, uint32_t i31val);
+WASM_API_EXTERN void wasmtime_anyref_from_i31(wasmtime_context_t *context,
+                                              uint32_t i31val,
+                                              wasmtime_anyref_t *out);
 
 /**
  * \brief Get the `anyref`'s underlying `i31ref` value, zero extended, if any.
@@ -87,7 +134,7 @@ wasmtime_anyref_from_i31(wasmtime_context_t *context, uint32_t i31val);
  * returned and `dst` is left unmodified.
  */
 WASM_API_EXTERN bool wasmtime_anyref_i31_get_u(wasmtime_context_t *context,
-                                               wasmtime_anyref_t *anyref,
+                                               const wasmtime_anyref_t *anyref,
                                                uint32_t *dst);
 
 /**
@@ -100,7 +147,7 @@ WASM_API_EXTERN bool wasmtime_anyref_i31_get_u(wasmtime_context_t *context,
  * returned and `dst` is left unmodified.
  */
 WASM_API_EXTERN bool wasmtime_anyref_i31_get_s(wasmtime_context_t *context,
-                                               wasmtime_anyref_t *anyref,
+                                               const wasmtime_anyref_t *anyref,
                                                int32_t *dst);
 
 /**
@@ -113,28 +160,65 @@ WASM_API_EXTERN bool wasmtime_anyref_i31_get_s(wasmtime_context_t *context,
  * This structure represents an `externref` that can be passed to WebAssembly.
  * It cannot be forged by WebAssembly itself and is guaranteed to have been
  * created by the host.
+ *
+ * This structure is similar to #wasmtime_anyref_t but represents the
+ * `externref` type in WebAssembly. This can be created on the host from
+ * arbitrary host pointers/destructors. Note that this value is itself a
+ * reference into a #wasmtime_context_t and must be explicitly unrooted to
+ * enable garbage collection.
+ *
+ * Note that null is represented with this structure and created with
+ * `wasmtime_externref_set_null`. Null can be tested for with the
+ * `wasmtime_externref_is_null` function.
  */
-typedef struct wasmtime_externref wasmtime_externref_t;
+typedef struct wasmtime_externref {
+  /// Internal metadata tracking within the store, embedders should not
+  /// configure or modify these fields.
+  uint64_t store_id;
+  /// Internal to Wasmtime.
+  uint32_t __private1;
+  /// Internal to Wasmtime.
+  uint32_t __private2;
+} wasmtime_externref_t;
+
+/// \brief Helper function to initialize the `ref` provided to a null externref
+/// value.
+static inline void wasmtime_externref_set_null(wasmtime_externref_t *ref) {
+  ref->store_id = 0;
+}
+
+/// \brief Helper function to return whether the provided `ref` points to a null
+/// `externref` value.
+///
+/// Note that `ref` itself should not be null as null is represented internally
+/// within a #wasmtime_externref_t value.
+static inline bool wasmtime_externref_is_null(const wasmtime_externref_t *ref) {
+  return ref->store_id == 0;
+}
 
 /**
  * \brief Create a new `externref` value.
  *
- * Creates a new `externref` value wrapping the provided data, returning the
- * pointer to the externref.
+ * Creates a new `externref` value wrapping the provided data, returning whether
+ * it was created or not.
  *
  * \param context the store context to allocate this externref within
  * \param data the host-specific data to wrap
  * \param finalizer an optional finalizer for `data`
+ * \param out where to store the created value.
  *
  * When the reference is reclaimed, the wrapped data is cleaned up with the
  * provided `finalizer`.
  *
- * The returned value must be deleted with #wasmtime_externref_delete and may
- * not be used after the context is destroyed.
+ * If `true` is returned then `out` has been filled in and must be unrooted
+ * in the future with #wasmtime_externref_unroot. If `false` is returned then
+ * the host wasn't able to create more GC values at this time. Performing a GC
+ * may free up enough space to try again.
  */
-WASM_API_EXTERN wasmtime_externref_t *
-wasmtime_externref_new(wasmtime_context_t *context, void *data,
-                       void (*finalizer)(void *));
+WASM_API_EXTERN bool wasmtime_externref_new(wasmtime_context_t *context,
+                                            void *data,
+                                            void (*finalizer)(void *),
+                                            wasmtime_externref_t *out);
 
 /**
  * \brief Get an `externref`'s wrapped data
@@ -143,33 +227,45 @@ wasmtime_externref_new(wasmtime_context_t *context, void *data,
  * that `data` is not `NULL`.
  */
 WASM_API_EXTERN void *wasmtime_externref_data(wasmtime_context_t *context,
-                                              wasmtime_externref_t *data);
+                                              const wasmtime_externref_t *data);
 
 /**
- * \brief Creates a shallow copy of the `externref` argument, returning a
- * separately owned pointer (depending on the configured collector this might
- * increase a reference count or create a new GC root).
+ * \brief Creates a new reference pointing to the same data that `ref` points
+ * to (depending on the configured collector this might increase a reference
+ * count or create a new GC root).
+ *
+ * The `out` parameter stores the cloned reference. This reference must
+ * eventually be unrooted with #wasmtime_externref_unroot in the future to
+ * enable GC'ing it.
  */
-WASM_API_EXTERN wasmtime_externref_t *
-wasmtime_externref_clone(wasmtime_context_t *context,
-                         wasmtime_externref_t *ref);
+WASM_API_EXTERN void wasmtime_externref_clone(wasmtime_context_t *context,
+                                              const wasmtime_externref_t *ref,
+                                              wasmtime_externref_t *out);
 
 /**
- * \brief Drops an owned pointer to `ref`, potentially deleting it if it's the
- * last reference, or allowing it to be collected during the next GC.
+ * \brief Unroots the pointer `ref` from the `context` provided.
+ *
+ * This function will enable future garbage collection of the value pointed to
+ * by `ref` once there are no more references. The `ref` value may be mutated in
+ * place by this function and its contents are undefined after this function
+ * returns. It should not be used until after re-initializing it.
+ *
+ * Note that null externref values do not need to be unrooted but are still
+ * valid to pass to this function.
  */
-WASM_API_EXTERN void wasmtime_externref_delete(wasmtime_context_t *context,
+WASM_API_EXTERN void wasmtime_externref_unroot(wasmtime_context_t *context,
                                                wasmtime_externref_t *ref);
 
 /**
  * \brief Converts a raw `externref` value coming from #wasmtime_val_raw_t into
  * a #wasmtime_externref_t.
  *
- * Note that the returned #wasmtime_externref_t is an owned value that must be
- * deleted via #wasmtime_externref_delete by the caller if it is non-null.
+ * The `out` reference is filled in with the non-raw version of this externref.
+ * It must eventually be unrooted with #wasmtime_externref_unroot.
  */
-WASM_API_EXTERN wasmtime_externref_t *
-wasmtime_externref_from_raw(wasmtime_context_t *context, uint32_t raw);
+WASM_API_EXTERN void wasmtime_externref_from_raw(wasmtime_context_t *context,
+                                                 uint32_t raw,
+                                                 wasmtime_externref_t *out);
 
 /**
  * \brief Converts a #wasmtime_externref_t to a raw value suitable for storing
@@ -230,15 +326,9 @@ typedef union wasmtime_valunion {
   /// Field used if #wasmtime_val_t::kind is #WASMTIME_F64
   float64_t f64;
   /// Field used if #wasmtime_val_t::kind is #WASMTIME_ANYREF
-  ///
-  /// If this value represents a `ref.null any` value then this pointer will
-  /// be `NULL`.
-  wasmtime_anyref_t *anyref;
+  wasmtime_anyref_t anyref;
   /// Field used if #wasmtime_val_t::kind is #WASMTIME_EXTERNREF
-  ///
-  /// If this value represents a `ref.null extern` value then this pointer will
-  /// be `NULL`.
-  wasmtime_externref_t *externref;
+  wasmtime_externref_t externref;
   /// Field used if #wasmtime_val_t::kind is #WASMTIME_FUNCREF
   ///
   /// Use `wasmtime_funcref_is_null` to test whether this is a null function
@@ -345,8 +435,8 @@ static inline void __wasmtime_val_assertions() {
  * Note that this structure may contain an owned value, namely rooted GC
  * references, depending on the context in which this is used. APIs which
  * consume a #wasmtime_val_t do not take ownership, but APIs that return
- * #wasmtime_val_t require that #wasmtime_val_delete is called to deallocate the
- * value.
+ * #wasmtime_val_t require that #wasmtime_val_unroot is called to clean up
+ * any possible GC roots in the value.
  */
 typedef struct wasmtime_val {
   /// Discriminant of which field of #of is valid.
@@ -356,20 +446,29 @@ typedef struct wasmtime_val {
 } wasmtime_val_t;
 
 /**
- * \brief Deletes an owned #wasmtime_val_t.
+ * \brief Unroot the value contained by `val`.
+ *
+ * This function will unroot any GC references that `val` points to, for
+ * example if it has the `WASMTIME_EXTERNREF` or `WASMTIME_ANYREF` kinds. This
+ * function leaves `val` in an undefined state and it should not be used again
+ * without re-initializing.
  *
- * Note that this only deletes the contents, not the memory that `val` points to
- * itself (which is owned by the caller).
+ * This method does not need to be called for integers, floats, v128, or
+ * funcref values.
  */
-WASM_API_EXTERN void wasmtime_val_delete(wasmtime_context_t *context,
+WASM_API_EXTERN void wasmtime_val_unroot(wasmtime_context_t *context,
                                          wasmtime_val_t *val);
 
 /**
- * \brief Copies `src` into `dst`.
+ * \brief Clones the value pointed to by `src` into the `dst` provided.
+ *
+ * This function will clone any rooted GC values in `src` and have them
+ * newly rooted inside of `dst`. When using this API the `dst` should be
+ * later unrooted with #wasmtime_val_unroot if it contains GC values.
  */
-WASM_API_EXTERN void wasmtime_val_copy(wasmtime_context_t *context,
-                                       wasmtime_val_t *dst,
-                                       const wasmtime_val_t *src);
+WASM_API_EXTERN void wasmtime_val_clone(wasmtime_context_t *context,
+                                        const wasmtime_val_t *src,
+                                        wasmtime_val_t *dst);
 
 #ifdef __cplusplus
 } // extern "C"
diff --git a/crates/c-api/src/ref.rs b/crates/c-api/src/ref.rs
index 1af4a194b..44f14c3f9 100644
--- a/crates/c-api/src/ref.rs
+++ b/crates/c-api/src/ref.rs
@@ -1,5 +1,5 @@
 use crate::{abort, WasmtimeStoreContextMut};
-use std::{mem::MaybeUninit, os::raw::c_void, ptr};
+use std::{mem::MaybeUninit, num::NonZeroU64, os::raw::c_void, ptr};
 use wasmtime::{AnyRef, ExternRef, ManuallyRooted, Ref, RootScope, Val, I31};
 
 /// `*mut wasm_ref_t` is a reference type (`externref` or `funcref`), as seen by
@@ -182,25 +182,70 @@ pub extern "C" fn wasm_foreign_new(_store: &crate::wasm_store_t) -> Box<wasm_for
     abort("wasm_foreign_new")
 }
 
-pub type wasmtime_anyref_t = ManuallyRooted<AnyRef>;
-pub type wasmtime_externref_t = ManuallyRooted<ExternRef>;
+/// C-API representation of `anyref`.
+///
+/// This represented differently in the C API from the header to handle how
+/// this is dispatched internally. Null anyref values are represented with a
+/// `store_id` of zero, and otherwise the `rooted` field is valid.
+///
+/// Note that this relies on the Wasmtime definition of `ManuallyRooted` to have
+/// a 64-bit store_id first.
+macro_rules! ref_wrapper {
+    ($wasmtime:ident => $c:ident) => {
+        pub struct $c {
+            store_id: u64,
+            a: u32,
+            b: u32,
+        }
+
+        impl $c {
+            pub unsafe fn as_wasmtime(&self) -> Option<ManuallyRooted<$wasmtime>> {
+                let store_id = NonZeroU64::new(self.store_id)?;
+                Some(ManuallyRooted::from_raw_parts_for_c_api(
+                    store_id, self.a, self.b,
+                ))
+            }
+        }
+
+        impl From<Option<ManuallyRooted<$wasmtime>>> for $c {
+            fn from(rooted: Option<ManuallyRooted<$wasmtime>>) -> $c {
+                let mut ret = $c {
+                    store_id: 0,
+                    a: 0,
+                    b: 0,
+                };
+                if let Some(rooted) = rooted {
+                    let (store_id, a, b) = rooted.into_parts_for_c_api();
+                    ret.store_id = store_id.get();
+                    ret.a = a;
+                    ret.b = b;
+                }
+                ret
+            }
+        }
+    };
+}
+
+ref_wrapper!(AnyRef => wasmtime_anyref_t);
+ref_wrapper!(ExternRef => wasmtime_externref_t);
 
 #[no_mangle]
-pub extern "C" fn wasmtime_anyref_clone(
+pub unsafe extern "C" fn wasmtime_anyref_clone(
     cx: WasmtimeStoreContextMut<'_>,
     anyref: Option<&wasmtime_anyref_t>,
-) -> Option<Box<wasmtime_anyref_t>> {
-    let anyref = anyref?;
-    Some(Box::new((*anyref).clone(cx)))
+    out: &mut MaybeUninit<wasmtime_anyref_t>,
+) {
+    let anyref = anyref.and_then(|a| a.as_wasmtime()).map(|a| a.clone(cx));
+    crate::initialize(out, anyref.into());
 }
 
 #[no_mangle]
-pub extern "C" fn wasmtime_anyref_delete(
+pub unsafe extern "C" fn wasmtime_anyref_unroot(
     cx: WasmtimeStoreContextMut<'_>,
-    val: Option<Box<wasmtime_anyref_t>>,
+    val: Option<&mut MaybeUninit<wasmtime_anyref_t>>,
 ) {
-    if let Some(e) = val {
-        e.unroot(cx);
+    if let Some(val) = val.and_then(|v| v.assume_init_read().as_wasmtime()) {
+        val.unroot(cx);
     }
 }
 
@@ -209,39 +254,42 @@ pub unsafe extern "C" fn wasmtime_anyref_to_raw(
     cx: WasmtimeStoreContextMut<'_>,
     val: Option<&wasmtime_anyref_t>,
 ) -> u32 {
-    val.and_then(|e| e.to_raw(cx).ok()).unwrap_or_default()
+    val.and_then(|v| v.as_wasmtime())
+        .and_then(|e| e.to_raw(cx).ok())
+        .unwrap_or_default()
 }
 
 #[no_mangle]
 pub unsafe extern "C" fn wasmtime_anyref_from_raw(
     cx: WasmtimeStoreContextMut<'_>,
     raw: u32,
-) -> Option<Box<wasmtime_anyref_t>> {
+    val: &mut MaybeUninit<wasmtime_anyref_t>,
+) {
     let mut scope = RootScope::new(cx);
-    let e = AnyRef::from_raw(&mut scope, raw)?;
-    Some(Box::new(
-        e.to_manually_rooted(&mut scope).expect("in scope"),
-    ))
+    let anyref = AnyRef::from_raw(&mut scope, raw)
+        .map(|a| a.to_manually_rooted(&mut scope).expect("in scope"));
+    crate::initialize(val, anyref.into());
 }
 
 #[no_mangle]
 pub extern "C" fn wasmtime_anyref_from_i31(
     cx: WasmtimeStoreContextMut<'_>,
     val: u32,
-) -> Box<wasmtime_anyref_t> {
+    out: &mut MaybeUninit<wasmtime_anyref_t>,
+) {
     let mut scope = RootScope::new(cx);
     let anyref = AnyRef::from_i31(&mut scope, I31::wrapping_u32(val));
     let anyref = anyref.to_manually_rooted(&mut scope).expect("in scope");
-    Box::new(anyref)
+    crate::initialize(out, Some(anyref).into())
 }
 
 #[no_mangle]
-pub extern "C" fn wasmtime_anyref_i31_get_u(
+pub unsafe extern "C" fn wasmtime_anyref_i31_get_u(
     cx: WasmtimeStoreContextMut<'_>,
     anyref: Option<&wasmtime_anyref_t>,
     dst: &mut MaybeUninit<u32>,
 ) -> bool {
-    match anyref {
+    match anyref.and_then(|a| a.as_wasmtime()) {
         Some(anyref) if anyref.is_i31(&cx).expect("ManuallyRooted always in scope") => {
             let val = anyref
                 .unwrap_i31(&cx)
@@ -255,12 +303,12 @@ pub extern "C" fn wasmtime_anyref_i31_get_u(
 }
 
 #[no_mangle]
-pub extern "C" fn wasmtime_anyref_i31_get_s(
+pub unsafe extern "C" fn wasmtime_anyref_i31_get_s(
     cx: WasmtimeStoreContextMut<'_>,
     anyref: Option<&wasmtime_anyref_t>,
     dst: &mut MaybeUninit<i32>,
 ) -> bool {
-    match anyref {
+    match anyref.and_then(|a| a.as_wasmtime()) {
         Some(anyref) if anyref.is_i31(&cx).expect("ManuallyRooted always in scope") => {
             let val = anyref
                 .unwrap_i31(&cx)
@@ -278,19 +326,25 @@ pub extern "C" fn wasmtime_externref_new(
     cx: WasmtimeStoreContextMut<'_>,
     data: *mut c_void,
     finalizer: Option<extern "C" fn(*mut c_void)>,
-) -> Option<Box<wasmtime_externref_t>> {
+    out: &mut MaybeUninit<wasmtime_externref_t>,
+) -> bool {
     let mut scope = RootScope::new(cx);
-    let e = ExternRef::new(&mut scope, crate::ForeignData { data, finalizer }).ok()?;
+    let e = match ExternRef::new(&mut scope, crate::ForeignData { data, finalizer }) {
+        Ok(e) => e,
+        Err(_) => return false,
+    };
     let e = e.to_manually_rooted(&mut scope).expect("in scope");
-    Some(Box::new(e))
+    crate::initialize(out, Some(e).into());
+    true
 }
 
 #[no_mangle]
-pub extern "C" fn wasmtime_externref_data(
+pub unsafe extern "C" fn wasmtime_externref_data(
     cx: WasmtimeStoreContextMut<'_>,
     externref: Option<&wasmtime_externref_t>,
 ) -> *mut c_void {
     externref
+        .and_then(|e| e.as_wasmtime())
         .and_then(|e| {
             let data = e.data(cx).ok()?;
             Some(data.downcast_ref::<crate::ForeignData>().unwrap().data)
@@ -299,21 +353,22 @@ pub extern "C" fn wasmtime_externref_data(
 }
 
 #[no_mangle]
-pub extern "C" fn wasmtime_externref_clone(
+pub unsafe extern "C" fn wasmtime_externref_clone(
     cx: WasmtimeStoreContextMut<'_>,
     externref: Option<&wasmtime_externref_t>,
-) -> Option<Box<wasmtime_externref_t>> {
-    let externref = externref?;
-    Some(Box::new((*externref).clone(cx)))
+    out: &mut MaybeUninit<wasmtime_externref_t>,
+) {
+    let externref = externref.and_then(|e| e.as_wasmtime()).map(|e| e.clone(cx));
+    crate::initialize(out, externref.into());
 }
 
 #[no_mangle]
-pub extern "C" fn wasmtime_externref_delete(
+pub unsafe extern "C" fn wasmtime_externref_unroot(
     cx: WasmtimeStoreContextMut<'_>,
-    val: Option<Box<wasmtime_externref_t>>,
+    val: Option<&mut MaybeUninit<wasmtime_externref_t>>,
 ) {
-    if let Some(e) = val {
-        e.unroot(cx);
+    if let Some(val) = val.and_then(|v| v.assume_init_read().as_wasmtime()) {
+        val.unroot(cx);
     }
 }
 
@@ -322,17 +377,19 @@ pub unsafe extern "C" fn wasmtime_externref_to_raw(
     cx: WasmtimeStoreContextMut<'_>,
     val: Option<&wasmtime_externref_t>,
 ) -> u32 {
-    val.and_then(|e| e.to_raw(cx).ok()).unwrap_or_default()
+    val.and_then(|e| e.as_wasmtime())
+        .and_then(|e| e.to_raw(cx).ok())
+        .unwrap_or_default()
 }
 
 #[no_mangle]
 pub unsafe extern "C" fn wasmtime_externref_from_raw(
     cx: WasmtimeStoreContextMut<'_>,
     raw: u32,
-) -> Option<Box<wasmtime_externref_t>> {
+    val: &mut MaybeUninit<wasmtime_externref_t>,
+) {
     let mut scope = RootScope::new(cx);
-    let e = ExternRef::from_raw(&mut scope, raw)?;
-    Some(Box::new(
-        e.to_manually_rooted(&mut scope).expect("in scope"),
-    ))
+    let rooted = ExternRef::from_raw(&mut scope, raw)
+        .map(|e| e.to_manually_rooted(&mut scope).expect("in scope"));
+    crate::initialize(val, rooted.into());
 }
diff --git a/crates/c-api/src/table.rs b/crates/c-api/src/table.rs
index a3b8f1e36..404121c94 100644
--- a/crates/c-api/src/table.rs
+++ b/crates/c-api/src/table.rs
@@ -34,7 +34,7 @@ impl wasm_table_t {
 
 fn option_wasm_ref_t_to_ref(r: Option<&wasm_ref_t>, table_ty: &TableType) -> Ref {
     match (r.map(|r| r.r.clone()), table_ty.element().heap_type()) {
-        (None, HeapType::NoFunc | HeapType::Func | HeapType::Concrete(_)) => Ref::Func(None),
+        (None, HeapType::NoFunc | HeapType::Func | HeapType::ConcreteFunc(_)) => Ref::Func(None),
         (None, HeapType::Extern) => Ref::Extern(None),
         (None, HeapType::Any | HeapType::I31 | HeapType::None) => Ref::Any(None),
         (Some(r), _) => r,
diff --git a/crates/c-api/src/val.rs b/crates/c-api/src/val.rs
index 9917457ec..070462d63 100644
--- a/crates/c-api/src/val.rs
+++ b/crates/c-api/src/val.rs
@@ -144,8 +144,8 @@ pub union wasmtime_val_union {
     pub i64: i64,
     pub f32: u32,
     pub f64: u64,
-    pub anyref: *mut wasmtime_anyref_t,
-    pub externref: *mut wasmtime_externref_t,
+    pub anyref: ManuallyDrop<wasmtime_anyref_t>,
+    pub externref: ManuallyDrop<wasmtime_externref_t>,
     pub funcref: wasmtime_func_t,
     pub v128: [u8; 16],
 }
@@ -234,17 +234,15 @@ impl wasmtime_val_t {
             Val::AnyRef(a) => wasmtime_val_t {
                 kind: crate::WASMTIME_ANYREF,
                 of: wasmtime_val_union {
-                    anyref: a
-                        .and_then(|a| Some(Box::into_raw(Box::new(a.to_manually_rooted(cx).ok()?))))
-                        .unwrap_or(ptr::null_mut()),
+                    anyref: ManuallyDrop::new(a.and_then(|a| a.to_manually_rooted(cx).ok()).into()),
                 },
             },
             Val::ExternRef(e) => wasmtime_val_t {
                 kind: crate::WASMTIME_EXTERNREF,
                 of: wasmtime_val_union {
-                    externref: e
-                        .and_then(|e| Some(Box::into_raw(Box::new(e.to_manually_rooted(cx).ok()?))))
-                        .unwrap_or(ptr::null_mut()),
+                    externref: ManuallyDrop::new(
+                        e.and_then(|e| e.to_manually_rooted(cx).ok()).into(),
+                    ),
                 },
             },
             Val::FuncRef(func) => wasmtime_val_t {
@@ -284,9 +282,11 @@ impl wasmtime_val_t {
             crate::WASMTIME_F32 => Val::F32(self.of.f32),
             crate::WASMTIME_F64 => Val::F64(self.of.f64),
             crate::WASMTIME_V128 => Val::V128(u128::from_le_bytes(self.of.v128).into()),
-            crate::WASMTIME_ANYREF => Val::AnyRef(self.of.anyref.as_ref().map(|a| a.to_rooted(cx))),
+            crate::WASMTIME_ANYREF => {
+                Val::AnyRef(self.of.anyref.as_wasmtime().map(|a| a.to_rooted(cx)))
+            }
             crate::WASMTIME_EXTERNREF => {
-                Val::ExternRef(self.of.externref.as_ref().map(|e| e.to_rooted(cx)))
+                Val::ExternRef(self.of.externref.as_wasmtime().map(|e| e.to_rooted(cx)))
             }
             crate::WASMTIME_FUNCREF => Val::FuncRef(self.of.funcref.as_wasmtime()),
             other => panic!("unknown wasmtime_valkind_t: {}", other),
@@ -294,45 +294,32 @@ impl wasmtime_val_t {
     }
 }
 
-impl Drop for wasmtime_val_t {
-    fn drop(&mut self) {
-        unsafe {
-            match self.kind {
-                crate::WASMTIME_ANYREF if !self.of.anyref.is_null() => {
-                    let _ = Box::from_raw(self.of.anyref);
-                }
-                crate::WASMTIME_EXTERNREF if !self.of.externref.is_null() => {
-                    let _ = Box::from_raw(self.of.externref);
-                }
-                _ => {}
-            }
-        }
-    }
-}
-
 #[no_mangle]
-pub unsafe extern "C" fn wasmtime_val_delete(
+pub unsafe extern "C" fn wasmtime_val_unroot(
     cx: WasmtimeStoreContextMut<'_>,
-    val: &mut ManuallyDrop<wasmtime_val_t>,
+    val: &mut MaybeUninit<wasmtime_val_t>,
 ) {
+    let val = val.assume_init_read();
     match val.kind {
-        crate::WASMTIME_ANYREF if !val.of.anyref.is_null() => {
-            let _ = ptr::read(val.of.anyref).unroot(cx);
+        crate::WASMTIME_ANYREF => {
+            if let Some(val) = ManuallyDrop::into_inner(val.of.anyref).as_wasmtime() {
+                val.unroot(cx);
+            }
         }
-        crate::WASMTIME_EXTERNREF if !val.of.externref.is_null() => {
-            let _ = ptr::read(val.of.externref).unroot(cx);
+        crate::WASMTIME_EXTERNREF => {
+            if let Some(val) = ManuallyDrop::into_inner(val.of.externref).as_wasmtime() {
+                val.unroot(cx);
+            }
         }
         _ => {}
     }
-
-    ManuallyDrop::drop(val)
 }
 
 #[no_mangle]
-pub unsafe extern "C" fn wasmtime_val_copy(
+pub unsafe extern "C" fn wasmtime_val_clone(
     cx: WasmtimeStoreContextMut<'_>,
-    dst: &mut MaybeUninit<wasmtime_val_t>,
     src: &wasmtime_val_t,
+    dst: &mut MaybeUninit<wasmtime_val_t>,
 ) {
     let mut scope = RootScope::new(cx);
     let val = src.to_val(&mut scope);
diff --git a/crates/cache/Cargo.toml b/crates/cache/Cargo.toml
index 475f3828e..bd6b57194 100644
--- a/crates/cache/Cargo.toml
+++ b/crates/cache/Cargo.toml
@@ -12,9 +12,9 @@ edition.workspace = true
 workspace = true
 
 [dependencies]
-anyhow = { workspace = true }
+anyhow = { workspace = true, features = ['std'] }
 base64 = "0.21.0"
-bincode = { workspace = true }
+postcard = { workspace = true }
 directories-next = "2.0"
 log = { workspace = true }
 serde = { workspace = true }
diff --git a/crates/cache/src/lib.rs b/crates/cache/src/lib.rs
index 279831259..aabaded44 100644
--- a/crates/cache/src/lib.rs
+++ b/crates/cache/src/lib.rs
@@ -54,8 +54,8 @@ impl<'config> ModuleCacheEntry<'config> {
         self.get_data_raw(
             &state,
             compute,
-            |_state, data| bincode::serialize(data).ok(),
-            |_state, data| bincode::deserialize(&data).ok(),
+            |_state, data| postcard::to_allocvec(data).ok(),
+            |_state, data| postcard::from_bytes(&data).ok(),
         )
     }
 
diff --git a/crates/component-util/src/lib.rs b/crates/component-util/src/lib.rs
index db1e4b976..642b8e349 100644
--- a/crates/component-util/src/lib.rs
+++ b/crates/component-util/src/lib.rs
@@ -1,3 +1,5 @@
+#![no_std]
+
 /// Represents the possible sizes in bytes of the discriminant of a variant type in the component model
 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
 pub enum DiscriminantSize {
diff --git a/crates/cranelift/src/compiler.rs b/crates/cranelift/src/compiler.rs
index 97f7b2410..a6c26c7f5 100644
--- a/crates/cranelift/src/compiler.rs
+++ b/crates/cranelift/src/compiler.rs
@@ -133,7 +133,7 @@ impl wasmtime_environ::Compiler for Compiler {
         let module = &translation.module;
         let func_index = module.func_index(func_index);
         let sig = translation.module.functions[func_index].signature;
-        let wasm_func_ty = &types[sig];
+        let wasm_func_ty = types[sig].unwrap_func();
 
         let mut compiler = self.function_compiler();
 
@@ -241,7 +241,7 @@ impl wasmtime_environ::Compiler for Compiler {
     ) -> Result<Box<dyn Any + Send>, CompileError> {
         let func_index = translation.module.func_index(def_func_index);
         let sig = translation.module.functions[func_index].signature;
-        let wasm_func_ty = &types[sig];
+        let wasm_func_ty = types[sig].unwrap_func();
 
         let isa = &*self.isa;
         let pointer_type = isa.pointer_type();
@@ -309,7 +309,7 @@ impl wasmtime_environ::Compiler for Compiler {
     ) -> Result<Box<dyn Any + Send>, CompileError> {
         let func_index = translation.module.func_index(def_func_index);
         let sig = translation.module.functions[func_index].signature;
-        let wasm_func_ty = &types[sig];
+        let wasm_func_ty = types[sig].unwrap_func();
 
         let isa = &*self.isa;
         let pointer_type = isa.pointer_type();
diff --git a/crates/cranelift/src/compiler/component.rs b/crates/cranelift/src/compiler/component.rs
index dc1736d14..250f7c304 100644
--- a/crates/cranelift/src/compiler/component.rs
+++ b/crates/cranelift/src/compiler/component.rs
@@ -43,7 +43,7 @@ impl<'a> TrampolineCompiler<'a> {
     ) -> TrampolineCompiler<'a> {
         let isa = &*compiler.isa;
         let signature = component.trampolines[index];
-        let ty = &types[signature];
+        let ty = types[signature].unwrap_func();
         let func = ir::Function::with_name_signature(
             ir::UserFuncName::user(0, 0),
             match abi {
@@ -127,7 +127,7 @@ impl<'a> TrampolineCompiler<'a> {
         let pointer_type = self.isa.pointer_type();
         let args = self.builder.func.dfg.block_params(self.block0).to_vec();
         let vmctx = args[0];
-        let wasm_func_ty = &self.types[self.signature];
+        let wasm_func_ty = self.types[self.signature].unwrap_func();
 
         // More handling is necessary here if this changes
         assert!(matches!(
@@ -291,7 +291,10 @@ impl<'a> TrampolineCompiler<'a> {
         host_args.push(args[2]);
 
         // Currently this only support resources represented by `i32`
-        assert_eq!(self.types[self.signature].params()[0], WasmValType::I32);
+        assert_eq!(
+            self.types[self.signature].unwrap_func().params()[0],
+            WasmValType::I32
+        );
         let (host_sig, offset) = host::resource_new32(self.isa, &mut self.builder.func);
 
         let host_fn = self.load_libcall(vmctx, offset);
@@ -322,7 +325,10 @@ impl<'a> TrampolineCompiler<'a> {
         host_args.push(args[2]);
 
         // Currently this only support resources represented by `i32`
-        assert_eq!(self.types[self.signature].returns()[0], WasmValType::I32);
+        assert_eq!(
+            self.types[self.signature].unwrap_func().returns()[0],
+            WasmValType::I32
+        );
         let (host_sig, offset) = host::resource_rep32(self.isa, &mut self.builder.func);
 
         let host_fn = self.load_libcall(vmctx, offset);
@@ -493,7 +499,7 @@ impl<'a> TrampolineCompiler<'a> {
 
             let sig = crate::wasm_call_signature(
                 self.isa,
-                &self.types[self.signature],
+                &self.types[self.signature].unwrap_func(),
                 &self.compiler.tunables,
             );
             let sig_ref = self.builder.import_signature(sig);
@@ -586,7 +592,7 @@ impl<'a> TrampolineCompiler<'a> {
             // and those are used to load the actual wasm parameters.
             Abi::Array => {
                 let results = self.compiler.load_values_from_array(
-                    self.types[self.signature].params(),
+                    self.types[self.signature].unwrap_func().params(),
                     &mut self.builder,
                     block0_params[2],
                     block0_params[3],
@@ -613,7 +619,7 @@ impl<'a> TrampolineCompiler<'a> {
                 let (ptr, len) = (block0_params[2], block0_params[3]);
                 self.compiler.store_values_to_array(
                     &mut self.builder,
-                    self.types[self.signature].returns(),
+                    self.types[self.signature].unwrap_func().returns(),
                     results,
                     ptr,
                     len,
diff --git a/crates/cranelift/src/func_environ.rs b/crates/cranelift/src/func_environ.rs
index a94d694f8..090af7f10 100644
--- a/crates/cranelift/src/func_environ.rs
+++ b/crates/cranelift/src/func_environ.rs
@@ -1180,7 +1180,7 @@ impl<'a, 'func, 'module_env> Call<'a, 'func, 'module_env> {
             // Functions that have a statically known type are either going to
             // always succeed or always fail. Figure out by inspecting the types
             // further.
-            WasmHeapType::Concrete(EngineOrModuleTypeIndex::Module(table_ty)) => {
+            WasmHeapType::ConcreteFunc(EngineOrModuleTypeIndex::Module(table_ty)) => {
                 // If `ty_index` matches `table_ty`, then this call is
                 // statically known to have the right type, so no checks are
                 // necessary.
@@ -1223,8 +1223,8 @@ impl<'a, 'func, 'module_env> Call<'a, 'func, 'module_env> {
             // Engine-indexed types don't show up until runtime and it's a wasm
             // validation error to perform a call through a non-function table,
             // so these cases are dynamically not reachable.
-            WasmHeapType::Concrete(EngineOrModuleTypeIndex::Engine(_))
-            | WasmHeapType::Concrete(EngineOrModuleTypeIndex::RecGroup(_))
+            WasmHeapType::ConcreteFunc(EngineOrModuleTypeIndex::Engine(_))
+            | WasmHeapType::ConcreteFunc(EngineOrModuleTypeIndex::RecGroup(_))
             | WasmHeapType::Extern
             | WasmHeapType::Any
             | WasmHeapType::I31
@@ -1419,7 +1419,7 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
         init_value: ir::Value,
     ) -> WasmResult<ir::Value> {
         let grow = match self.module.table_plans[table_index].table.wasm_ty.heap_type {
-            WasmHeapType::Func | WasmHeapType::Concrete(_) | WasmHeapType::NoFunc => {
+            WasmHeapType::Func | WasmHeapType::ConcreteFunc(_) | WasmHeapType::NoFunc => {
                 self.builtin_functions.table_grow_func_ref(&mut pos.func)
             }
 
@@ -1479,7 +1479,7 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
             }
 
             // Function types.
-            WasmHeapType::Func | WasmHeapType::Concrete(_) | WasmHeapType::NoFunc => {
+            WasmHeapType::Func | WasmHeapType::ConcreteFunc(_) | WasmHeapType::NoFunc => {
                 match plan.style {
                     TableStyle::CallerChecksSignature => {
                         Ok(self.get_or_init_func_ref_table_elem(builder, table_index, index))
@@ -1532,7 +1532,7 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
             }
 
             // Function types.
-            WasmHeapType::Func | WasmHeapType::Concrete(_) | WasmHeapType::NoFunc => {
+            WasmHeapType::Func | WasmHeapType::ConcreteFunc(_) | WasmHeapType::NoFunc => {
                 match plan.style {
                     TableStyle::CallerChecksSignature => {
                         let (elem_addr, flags) = table_data.prepare_table_addr(
@@ -1566,7 +1566,7 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
         len: ir::Value,
     ) -> WasmResult<()> {
         let libcall = match self.module.table_plans[table_index].table.wasm_ty.heap_type {
-            WasmHeapType::Func | WasmHeapType::Concrete(_) | WasmHeapType::NoFunc => {
+            WasmHeapType::Func | WasmHeapType::ConcreteFunc(_) | WasmHeapType::NoFunc => {
                 self.builtin_functions.table_fill_func_ref(&mut pos.func)
             }
             ty @ WasmHeapType::Extern
@@ -1625,7 +1625,7 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
         ht: WasmHeapType,
     ) -> WasmResult<ir::Value> {
         Ok(match ht {
-            WasmHeapType::Func | WasmHeapType::Concrete(_) | WasmHeapType::NoFunc => {
+            WasmHeapType::Func | WasmHeapType::ConcreteFunc(_) | WasmHeapType::NoFunc => {
                 pos.ins().iconst(self.pointer_type(), 0)
             }
             WasmHeapType::Extern | WasmHeapType::Any | WasmHeapType::I31 | WasmHeapType::None => {
@@ -1983,7 +1983,8 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
         index: TypeIndex,
     ) -> WasmResult<ir::SigRef> {
         let index = self.module.types[index];
-        let sig = crate::wasm_call_signature(self.isa, &self.types[index], &self.tunables);
+        let sig =
+            crate::wasm_call_signature(self.isa, self.types[index].unwrap_func(), &self.tunables);
         Ok(func.import_signature(sig))
     }
 
@@ -1993,7 +1994,8 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
         index: FuncIndex,
     ) -> WasmResult<ir::FuncRef> {
         let sig = self.module.functions[index].signature;
-        let sig = crate::wasm_call_signature(self.isa, &self.types[sig], &self.tunables);
+        let sig =
+            crate::wasm_call_signature(self.isa, self.types[sig].unwrap_func(), &self.tunables);
         let signature = func.import_signature(sig);
         let name =
             ir::ExternalName::User(func.declare_imported_user_function(ir::UserExternalName {
diff --git a/crates/cranelift/src/gc/enabled.rs b/crates/cranelift/src/gc/enabled.rs
index 43458814d..602a30e75 100644
--- a/crates/cranelift/src/gc/enabled.rs
+++ b/crates/cranelift/src/gc/enabled.rs
@@ -173,7 +173,7 @@ impl FuncEnvironment<'_> {
 
         let might_be_i31 = match ty.heap_type {
             WasmHeapType::Any => true,
-            WasmHeapType::Extern | WasmHeapType::None | WasmHeapType::Concrete(_) => false,
+            WasmHeapType::Extern | WasmHeapType::None | WasmHeapType::ConcreteFunc(_) => false,
             WasmHeapType::Func | WasmHeapType::NoFunc | WasmHeapType::I31 => {
                 unreachable!("we don't manage instances of these types with the GC")
             }
diff --git a/crates/cranelift/src/lib.rs b/crates/cranelift/src/lib.rs
index 41bcad224..fdf976891 100644
--- a/crates/cranelift/src/lib.rs
+++ b/crates/cranelift/src/lib.rs
@@ -267,7 +267,7 @@ fn wasm_call_signature(
 /// Returns the reference type to use for the provided wasm type.
 fn reference_type(wasm_ht: WasmHeapType, pointer_type: ir::Type) -> ir::Type {
     match wasm_ht {
-        WasmHeapType::Func | WasmHeapType::Concrete(_) | WasmHeapType::NoFunc => pointer_type,
+        WasmHeapType::Func | WasmHeapType::ConcreteFunc(_) | WasmHeapType::NoFunc => pointer_type,
         WasmHeapType::Extern | WasmHeapType::Any | WasmHeapType::I31 | WasmHeapType::None => {
             match pointer_type {
                 ir::types::I32 => ir::types::R32,
diff --git a/crates/environ/Cargo.toml b/crates/environ/Cargo.toml
index bcf072581..7933a0399 100644
--- a/crates/environ/Cargo.toml
+++ b/crates/environ/Cargo.toml
@@ -14,8 +14,8 @@ edition.workspace = true
 workspace = true
 
 [dependencies]
-anyhow = { workspace = true }
-bincode = { workspace = true }
+anyhow = { workspace = true, features = ['std'] }
+postcard = { workspace = true }
 cpp_demangle = { version = "0.4.3", optional = true }
 cranelift-entity = { workspace = true }
 wasmtime-types = { workspace = true }
diff --git a/crates/environ/src/compile/module_artifacts.rs b/crates/environ/src/compile/module_artifacts.rs
index 68f19e377..79b6f4c77 100644
--- a/crates/environ/src/compile/module_artifacts.rs
+++ b/crates/environ/src/compile/module_artifacts.rs
@@ -1,5 +1,5 @@
 //! Definitions of runtime structures and metadata which are serialized into ELF
-//! with `bincode` as part of a module's compilation process.
+//! with `postcard` as part of a module's compilation process.
 
 use crate::{
     obj, CompiledFunctionInfo, CompiledModuleInfo, DebugInfoData, DefinedFuncIndex, FunctionLoc,
@@ -261,7 +261,7 @@ impl<'a> ObjectBuilder<'a> {
             obj::ELF_WASMTIME_INFO.as_bytes().to_vec(),
             SectionKind::ReadOnlyData,
         );
-        let data = bincode::serialize(info).unwrap();
+        let data = postcard::to_allocvec(info).unwrap();
         self.obj.set_section_data(section, data, 1);
     }
 
diff --git a/crates/environ/src/component/types.rs b/crates/environ/src/component/types.rs
index 93d002baf..45f352ac7 100644
--- a/crates/environ/src/component/types.rs
+++ b/crates/environ/src/component/types.rs
@@ -480,21 +480,24 @@ impl ComponentTypesBuilder {
         (self.component_types, ty)
     }
 
-    /// Smaller helper method to find a `SignatureIndex` which corresponds to
-    /// the `resource.drop` intrinsic in components, namely a core wasm function
-    /// type which takes one `i32` argument and has no results.
+    /// Smaller helper method to find a `ModuleInternedTypeIndex` which
+    /// corresponds to the `resource.drop` intrinsic in components, namely a
+    /// core wasm function type which takes one `i32` argument and has no
+    /// results.
     ///
     /// This is a bit of a hack right now as ideally this find operation
-    /// wouldn't be needed and instead the `SignatureIndex` itself would be
-    /// threaded through appropriately, but that's left for a future
+    /// wouldn't be needed and instead the `ModuleInternedTypeIndex` itself
+    /// would be threaded through appropriately, but that's left for a future
     /// refactoring. Try not to lean too hard on this method though.
     pub fn find_resource_drop_signature(&self) -> Option<ModuleInternedTypeIndex> {
         self.module_types
-            .wasm_signatures()
-            .find(|(_, sig)| {
-                sig.params().len() == 1
-                    && sig.returns().len() == 0
-                    && sig.params()[0] == WasmValType::I32
+            .wasm_types()
+            .find(|(_, ty)| match &ty.composite_type {
+                wasmtime_types::WasmCompositeType::Func(sig) => {
+                    sig.params().len() == 1
+                        && sig.returns().len() == 0
+                        && sig.params()[0] == WasmValType::I32
+                }
             })
             .map(|(i, _)| i)
     }
@@ -828,7 +831,7 @@ impl ComponentTypesBuilder {
         self.add_tuple_type(TypeTuple { types, abi })
     }
 
-    fn flags_type(&mut self, flags: &IndexSet<KebabString>) -> TypeFlagsIndex {
+    fn flags_type(&mut self, flags: &wasmparser::map::IndexSet<KebabString>) -> TypeFlagsIndex {
         let flags = TypeFlags {
             names: flags.iter().map(|s| s.to_string()).collect(),
             abi: CanonicalAbiInfo::flags(flags.len()),
@@ -836,7 +839,7 @@ impl ComponentTypesBuilder {
         self.add_flags_type(flags)
     }
 
-    fn enum_type(&mut self, variants: &IndexSet<KebabString>) -> TypeEnumIndex {
+    fn enum_type(&mut self, variants: &wasmparser::map::IndexSet<KebabString>) -> TypeEnumIndex {
         let names = variants
             .iter()
             .map(|s| s.to_string())
diff --git a/crates/environ/src/module_environ.rs b/crates/environ/src/module_environ.rs
index 2c70a9fed..0257d3d2d 100644
--- a/crates/environ/src/module_environ.rs
+++ b/crates/environ/src/module_environ.rs
@@ -550,7 +550,7 @@ impl<'a, 'data> ModuleEnvironment<'a, 'data> {
 
                 if self.tunables.generate_native_debuginfo {
                     let sig_index = self.result.module.functions[func_index].signature;
-                    let sig = &self.types[sig_index];
+                    let sig = self.types[sig_index].unwrap_func();
                     let mut locals = Vec::new();
                     for pair in body.get_locals_reader()? {
                         let (cnt, ty) = pair?;
@@ -876,6 +876,7 @@ and for re-adding support for interface types you can see this issue:
                 | wasmparser::Name::Element(_)
                 | wasmparser::Name::Data(_)
                 | wasmparser::Name::Tag(_)
+                | wasmparser::Name::Field(_)
                 | wasmparser::Name::Unknown { .. } => {}
             }
         }
@@ -1185,7 +1186,7 @@ impl ModuleTranslation<'_> {
                 .wasm_ty
                 .heap_type
             {
-                WasmHeapType::Func | WasmHeapType::Concrete(_) | WasmHeapType::NoFunc => {}
+                WasmHeapType::Func | WasmHeapType::ConcreteFunc(_) | WasmHeapType::NoFunc => {}
                 // If this is not a funcref table, then we can't support a
                 // pre-computed table of function indices. Technically this
                 // initializer won't trap so we could continue processing
diff --git a/crates/environ/src/module_types.rs b/crates/environ/src/module_types.rs
index 693175e8f..6005b7030 100644
--- a/crates/environ/src/module_types.rs
+++ b/crates/environ/src/module_types.rs
@@ -1,14 +1,14 @@
-use crate::{Module, PrimaryMap, TypeConvert, TypeIndex, WasmFuncType, WasmHeapType};
+use crate::{Module, PrimaryMap, TypeConvert, TypeIndex, WasmHeapType};
 use cranelift_entity::EntityRef;
 use serde_derive::{Deserialize, Serialize};
 use std::{
     collections::HashMap,
     ops::{Index, Range},
 };
-use wasmparser::{CompositeType, UnpackedIndex, Validator, ValidatorId};
+use wasmparser::{UnpackedIndex, Validator, ValidatorId};
 use wasmtime_types::{
-    wasm_unsupported, EngineOrModuleTypeIndex, ModuleInternedRecGroupIndex,
-    ModuleInternedTypeIndex, WasmResult,
+    EngineOrModuleTypeIndex, ModuleInternedRecGroupIndex, ModuleInternedTypeIndex, WasmResult,
+    WasmSubType,
 };
 
 /// All types used in a core wasm module.
@@ -21,7 +21,7 @@ use wasmtime_types::{
 #[derive(Default, Serialize, Deserialize)]
 pub struct ModuleTypes {
     rec_groups: PrimaryMap<ModuleInternedRecGroupIndex, Range<ModuleInternedTypeIndex>>,
-    wasm_types: PrimaryMap<ModuleInternedTypeIndex, WasmFuncType>,
+    wasm_types: PrimaryMap<ModuleInternedTypeIndex, WasmSubType>,
 }
 
 impl ModuleTypes {
@@ -29,12 +29,12 @@ impl ModuleTypes {
     /// this module.
     pub fn wasm_types(
         &self,
-    ) -> impl ExactSizeIterator<Item = (ModuleInternedTypeIndex, &WasmFuncType)> {
+    ) -> impl ExactSizeIterator<Item = (ModuleInternedTypeIndex, &WasmSubType)> {
         self.wasm_types.iter()
     }
 
     /// Get the type at the specified index.
-    pub fn get(&self, ty: ModuleInternedTypeIndex) -> &WasmFuncType {
+    pub fn get(&self, ty: ModuleInternedTypeIndex) -> &WasmSubType {
         &self.wasm_types[ty]
     }
 
@@ -58,9 +58,9 @@ impl ModuleTypes {
 }
 
 impl Index<ModuleInternedTypeIndex> for ModuleTypes {
-    type Output = WasmFuncType;
+    type Output = WasmSubType;
 
-    fn index(&self, sig: ModuleInternedTypeIndex) -> &WasmFuncType {
+    fn index(&self, sig: ModuleInternedTypeIndex) -> &WasmSubType {
         &self.wasm_types[sig]
     }
 }
@@ -163,17 +163,8 @@ impl ModuleTypesBuilder {
 
         for id in validator_types.rec_group_elements(rec_group_id) {
             let ty = &validator_types[id];
-            if ty.supertype_idx.is_some() {
-                return Err(wasm_unsupported!("wasm gc: explicit subtyping"));
-            }
-            match &ty.composite_type {
-                CompositeType::Func(ty) => {
-                    let wasm_ty = WasmparserTypeConverter::new(self, module).convert_func_type(ty);
-                    self.wasm_func_type_in_rec_group(id, wasm_ty);
-                }
-                CompositeType::Array(_) => return Err(wasm_unsupported!("wasm gc: array types")),
-                CompositeType::Struct(_) => return Err(wasm_unsupported!("wasm gc: struct types")),
-            }
+            let wasm_ty = WasmparserTypeConverter::new(self, module).convert_sub_type(ty)?;
+            self.wasm_sub_type_in_rec_group(id, wasm_ty);
         }
 
         Ok(self.end_rec_group(rec_group_id))
@@ -275,18 +266,18 @@ impl ModuleTypesBuilder {
         Ok(interned_type)
     }
 
-    /// Define a new Wasm function type while we are defining a rec group.
-    fn wasm_func_type_in_rec_group(
+    /// Define a new Wasm type while we are defining a rec group.
+    fn wasm_sub_type_in_rec_group(
         &mut self,
         id: wasmparser::types::CoreTypeId,
-        func_ty: WasmFuncType,
+        ty: WasmSubType,
     ) -> ModuleInternedTypeIndex {
         assert!(
             self.defining_rec_group.is_some(),
             "must be defining a rec group to define new types"
         );
 
-        let module_interned_index = self.types.wasm_types.push(func_ty);
+        let module_interned_index = self.types.wasm_types.push(ty);
         debug_assert_eq!(
             self.wasmparser_to_wasmtime.get(&id),
             Some(&module_interned_index),
@@ -309,11 +300,9 @@ impl ModuleTypesBuilder {
         self.types.rec_group_elements(rec_group)
     }
 
-    /// Returns an iterator over all the wasm function signatures found within
-    /// this module.
-    pub fn wasm_signatures(
-        &self,
-    ) -> impl Iterator<Item = (ModuleInternedTypeIndex, &WasmFuncType)> {
+    /// Returns an iterator over all the unique wasm types defined thus far
+    /// within this builder.
+    pub fn wasm_types(&self) -> impl Iterator<Item = (ModuleInternedTypeIndex, &WasmSubType)> {
         self.types.wasm_types()
     }
 }
@@ -348,12 +337,12 @@ impl TypeConvert for WasmparserTypeConverter<'_> {
         match index {
             UnpackedIndex::Id(id) => {
                 let signature = self.types.wasmparser_to_wasmtime[&id];
-                WasmHeapType::Concrete(EngineOrModuleTypeIndex::Module(signature))
+                WasmHeapType::ConcreteFunc(EngineOrModuleTypeIndex::Module(signature))
             }
             UnpackedIndex::Module(module_index) => {
                 let module_index = TypeIndex::from_u32(module_index);
                 let interned_index = self.module.types[module_index];
-                WasmHeapType::Concrete(EngineOrModuleTypeIndex::Module(interned_index))
+                WasmHeapType::ConcreteFunc(EngineOrModuleTypeIndex::Module(interned_index))
             }
             UnpackedIndex::RecGroup(_) => unreachable!(),
         }
diff --git a/crates/fuzzing/src/oracles/dummy.rs b/crates/fuzzing/src/oracles/dummy.rs
index da85f367c..0a504c0ad 100644
--- a/crates/fuzzing/src/oracles/dummy.rs
+++ b/crates/fuzzing/src/oracles/dummy.rs
@@ -48,7 +48,7 @@ pub fn dummy_value(val_ty: ValType) -> Result<Val> {
         ValType::Ref(r) => match r.heap_type() {
             _ if !r.is_nullable() => bail!("cannot construct a dummy value of type `{r}`"),
             HeapType::Extern => Val::null_extern_ref(),
-            HeapType::NoFunc | HeapType::Func | HeapType::Concrete(_) => Val::null_func_ref(),
+            HeapType::NoFunc | HeapType::Func | HeapType::ConcreteFunc(_) => Val::null_func_ref(),
             HeapType::Any | HeapType::I31 | HeapType::None => Val::null_any_ref(),
         },
     })
diff --git a/crates/jit-icache-coherence/Cargo.toml b/crates/jit-icache-coherence/Cargo.toml
index 8fcbcc280..2f7461f1c 100644
--- a/crates/jit-icache-coherence/Cargo.toml
+++ b/crates/jit-icache-coherence/Cargo.toml
@@ -13,6 +13,7 @@ workspace = true
 
 [dependencies]
 cfg-if = { workspace = true }
+anyhow = { workspace = true }
 
 [target.'cfg(target_os = "windows")'.dependencies.windows-sys]
 workspace = true
diff --git a/crates/jit-icache-coherence/src/lib.rs b/crates/jit-icache-coherence/src/lib.rs
index 8964372b7..fc9a20d3a 100644
--- a/crates/jit-icache-coherence/src/lib.rs
+++ b/crates/jit-icache-coherence/src/lib.rs
@@ -34,7 +34,7 @@
 //! #   len: usize,
 //! # }
 //! #
-//! # fn main() -> io::Result<()> {
+//! # fn main() -> anyhow::Result<()> {
 //! #
 //! # let run_code = || {};
 //! # let code = vec![0u8; 64];
@@ -67,8 +67,9 @@
 //!
 //! [ARM Community - Caches and Self-Modifying Code]: https://community.arm.com/arm-community-blogs/b/architectures-and-processors-blog/posts/caches-and-self-modifying-code
 
-use std::ffi::c_void;
-use std::io::Result;
+#![no_std]
+
+use core::ffi::c_void;
 
 cfg_if::cfg_if! {
     if #[cfg(target_os = "windows")] {
@@ -91,7 +92,7 @@ cfg_if::cfg_if! {
 /// after all calls to [clear_cache].
 ///
 /// If the architecture does not require a pipeline flush, this function does nothing.
-pub fn pipeline_flush_mt() -> Result<()> {
+pub fn pipeline_flush_mt() -> imp::Result<()> {
     imp::pipeline_flush_mt()
 }
 
@@ -103,6 +104,6 @@ pub fn pipeline_flush_mt() -> Result<()> {
 ///
 /// It is necessary to call [pipeline_flush_mt] after this function if you are running in a multi-threaded
 /// environment.
-pub unsafe fn clear_cache(ptr: *const c_void, len: usize) -> Result<()> {
+pub unsafe fn clear_cache(ptr: *const c_void, len: usize) -> imp::Result<()> {
     imp::clear_cache(ptr, len)
 }
diff --git a/crates/jit-icache-coherence/src/libc.rs b/crates/jit-icache-coherence/src/libc.rs
index 364658bd1..0d7462042 100644
--- a/crates/jit-icache-coherence/src/libc.rs
+++ b/crates/jit-icache-coherence/src/libc.rs
@@ -1,11 +1,20 @@
-use std::ffi::c_void;
-use std::io::Result;
+use core::ffi::c_void;
+
+#[cfg(any(target_os = "linux", target_os = "android"))]
+extern crate std;
+#[cfg(any(target_os = "linux", target_os = "android"))]
+pub use std::io::Result;
+
+#[cfg(not(any(target_os = "linux", target_os = "android")))]
+pub use anyhow::Result;
 
 #[cfg(all(
     target_arch = "aarch64",
     any(target_os = "linux", target_os = "android")
 ))]
 mod details {
+    extern crate std;
+
     use super::*;
     use libc::{syscall, EINVAL, EPERM};
     use std::io::Error;
@@ -87,20 +96,25 @@ mod details {
     any(target_os = "linux", target_os = "android")
 )))]
 mod details {
-    pub(crate) fn pipeline_flush_mt() -> std::io::Result<()> {
+    // NB: this uses `anyhow::Result` instead of `std::io::Result` to compile on
+    // `no_std`.
+    pub(crate) fn pipeline_flush_mt() -> super::Result<()> {
         Ok(())
     }
 }
+
 #[cfg(all(target_arch = "riscv64", target_os = "linux"))]
 fn riscv_flush_icache(start: u64, end: u64) -> Result<()> {
     cfg_if::cfg_if! {
         if #[cfg(feature = "one-core")] {
-            use std::arch::asm;
+            use core::arch::asm;
             unsafe {
                 asm!("fence.i");
             };
             Ok(())
         } else {
+            extern crate std;
+
             match unsafe {
                 libc::syscall(
                     {
diff --git a/crates/jit-icache-coherence/src/miri.rs b/crates/jit-icache-coherence/src/miri.rs
index 1ff641046..a29e244ab 100644
--- a/crates/jit-icache-coherence/src/miri.rs
+++ b/crates/jit-icache-coherence/src/miri.rs
@@ -1,5 +1,5 @@
-use std::ffi::c_void;
-use std::io::Result;
+pub use anyhow::Result;
+use core::ffi::c_void;
 
 pub(crate) fn pipeline_flush_mt() -> Result<()> {
     Ok(())
diff --git a/crates/jit-icache-coherence/src/win.rs b/crates/jit-icache-coherence/src/win.rs
index 488e15f46..83e0ff667 100644
--- a/crates/jit-icache-coherence/src/win.rs
+++ b/crates/jit-icache-coherence/src/win.rs
@@ -1,9 +1,13 @@
+extern crate std;
+
 use std::ffi::c_void;
-use std::io::{Error, Result};
+use std::io::Error;
 use windows_sys::Win32::System::Diagnostics::Debug::FlushInstructionCache;
 use windows_sys::Win32::System::Threading::FlushProcessWriteBuffers;
 use windows_sys::Win32::System::Threading::GetCurrentProcess;
 
+pub use std::io::Result;
+
 /// See docs on [crate::pipeline_flush_mt] for a description of what this function is trying to do.
 #[inline]
 pub(crate) fn pipeline_flush_mt() -> Result<()> {
diff --git a/crates/runtime/src/instance.rs b/crates/runtime/src/instance.rs
index 13f75924e..8dc65abfc 100644
--- a/crates/runtime/src/instance.rs
+++ b/crates/runtime/src/instance.rs
@@ -897,8 +897,8 @@ impl Instance {
                                 VMGcRef::from_raw_u32(raw.get_anyref())
                             }),
                         )?,
-                    WasmHeapType::Func | WasmHeapType::Concrete(_) | WasmHeapType::NoFunc => table
-                        .init_func(
+                    WasmHeapType::Func | WasmHeapType::ConcreteFunc(_) | WasmHeapType::NoFunc => {
+                        table.init_func(
                             dst,
                             exprs.iter().map(|expr| unsafe {
                                 const_evaluator
@@ -907,7 +907,8 @@ impl Instance {
                                     .get_funcref()
                                     .cast()
                             }),
-                        )?,
+                        )?
+                    }
                 }
             }
         }
diff --git a/crates/runtime/src/instance/allocator.rs b/crates/runtime/src/instance/allocator.rs
index d1d2a2eb6..f33032ecf 100644
--- a/crates/runtime/src/instance/allocator.rs
+++ b/crates/runtime/src/instance/allocator.rs
@@ -594,7 +594,7 @@ fn initialize_tables(instance: &mut Instance, module: &Module) -> Result<()> {
                     }
 
                     wasmtime_environ::WasmHeapType::Func
-                    | wasmtime_environ::WasmHeapType::Concrete(_)
+                    | wasmtime_environ::WasmHeapType::ConcreteFunc(_)
                     | wasmtime_environ::WasmHeapType::NoFunc => {
                         let funcref = raw.get_funcref().cast::<VMFuncRef>();
                         let items = (0..table.size()).map(|_| funcref);
diff --git a/crates/runtime/src/table.rs b/crates/runtime/src/table.rs
index f138d1a37..484dfa022 100644
--- a/crates/runtime/src/table.rs
+++ b/crates/runtime/src/table.rs
@@ -248,7 +248,7 @@ impl From<DynamicGcRefTable> for Table {
 
 fn wasm_to_table_type(ty: WasmRefType) -> TableElementType {
     match ty.heap_type {
-        WasmHeapType::Func | WasmHeapType::Concrete(_) | WasmHeapType::NoFunc => {
+        WasmHeapType::Func | WasmHeapType::ConcreteFunc(_) | WasmHeapType::NoFunc => {
             TableElementType::Func
         }
         WasmHeapType::Extern | WasmHeapType::Any | WasmHeapType::I31 | WasmHeapType::None => {
diff --git a/crates/runtime/src/vmcontext.rs b/crates/runtime/src/vmcontext.rs
index f4ba5a02a..840512fec 100644
--- a/crates/runtime/src/vmcontext.rs
+++ b/crates/runtime/src/vmcontext.rs
@@ -456,7 +456,7 @@ impl VMGlobalDefinition {
                 WasmHeapType::Any | WasmHeapType::I31 | WasmHeapType::None => {
                     global.init_gc_ref(VMGcRef::from_raw_u32(raw.get_anyref()))
                 }
-                WasmHeapType::Func | WasmHeapType::Concrete(_) | WasmHeapType::NoFunc => {
+                WasmHeapType::Func | WasmHeapType::ConcreteFunc(_) | WasmHeapType::NoFunc => {
                     *global.as_func_ref_mut() = raw.get_funcref().cast()
                 }
             },
@@ -485,7 +485,7 @@ impl VMGlobalDefinition {
                     self.as_gc_ref()
                         .map_or(0, |r| gc_store.clone_gc_ref(r).as_raw_u32()),
                 ),
-                WasmHeapType::Func | WasmHeapType::Concrete(_) | WasmHeapType::NoFunc => {
+                WasmHeapType::Func | WasmHeapType::ConcreteFunc(_) | WasmHeapType::NoFunc => {
                     ValRaw::funcref(self.as_func_ref().cast())
                 }
             },
diff --git a/crates/types/src/lib.rs b/crates/types/src/lib.rs
index 46a127a77..4a2c04ca3 100644
--- a/crates/types/src/lib.rs
+++ b/crates/types/src/lib.rs
@@ -376,7 +376,7 @@ impl EngineOrModuleTypeIndex {
 pub enum WasmHeapType {
     Extern,
     Func,
-    Concrete(EngineOrModuleTypeIndex),
+    ConcreteFunc(EngineOrModuleTypeIndex),
     NoFunc,
     Any,
     I31,
@@ -388,7 +388,7 @@ impl fmt::Display for WasmHeapType {
         match self {
             Self::Extern => write!(f, "extern"),
             Self::Func => write!(f, "func"),
-            Self::Concrete(i) => write!(f, "{i}"),
+            Self::ConcreteFunc(i) => write!(f, "func {i}"),
             Self::NoFunc => write!(f, "nofunc"),
             Self::Any => write!(f, "any"),
             Self::I31 => write!(f, "i31"),
@@ -403,7 +403,7 @@ impl TypeTrace for WasmHeapType {
         F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
     {
         match *self {
-            Self::Concrete(i) => func(i),
+            Self::ConcreteFunc(i) => func(i),
             _ => Ok(()),
         }
     }
@@ -413,7 +413,7 @@ impl TypeTrace for WasmHeapType {
         F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
     {
         match self {
-            Self::Concrete(i) => func(i),
+            Self::ConcreteFunc(i) => func(i),
             _ => Ok(()),
         }
     }
@@ -428,7 +428,7 @@ impl WasmHeapType {
             Self::Extern | Self::Any | Self::I31 | Self::None => true,
 
             // All `t <: (ref null func)` are not.
-            Self::Func | Self::Concrete(_) | Self::NoFunc => false,
+            Self::Func | Self::ConcreteFunc(_) | Self::NoFunc => false,
         }
     }
 
@@ -523,6 +523,95 @@ impl WasmFuncType {
     }
 }
 
+/// A function, array, or struct type.
+#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
+pub enum WasmCompositeType {
+    Func(WasmFuncType),
+    //
+    // TODO: Array and struct types.
+}
+
+impl WasmCompositeType {
+    #[inline]
+    pub fn is_func(&self) -> bool {
+        matches!(self, Self::Func(_))
+    }
+
+    #[inline]
+    pub fn as_func(&self) -> Option<&WasmFuncType> {
+        match self {
+            WasmCompositeType::Func(f) => Some(f),
+        }
+    }
+
+    #[inline]
+    pub fn unwrap_func(&self) -> &WasmFuncType {
+        self.as_func().unwrap()
+    }
+}
+
+impl TypeTrace for WasmCompositeType {
+    fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
+    where
+        F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
+    {
+        match self {
+            WasmCompositeType::Func(f) => f.trace(func),
+        }
+    }
+
+    fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
+    where
+        F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
+    {
+        match self {
+            WasmCompositeType::Func(f) => f.trace_mut(func),
+        }
+    }
+}
+
+/// A concrete, user-defined (or host-defined) Wasm type.
+#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
+pub struct WasmSubType {
+    // TODO: is_final, supertype
+    //
+    /// The array, function, or struct that is defined.
+    pub composite_type: WasmCompositeType,
+}
+
+impl WasmSubType {
+    #[inline]
+    pub fn is_func(&self) -> bool {
+        self.composite_type.is_func()
+    }
+
+    #[inline]
+    pub fn as_func(&self) -> Option<&WasmFuncType> {
+        self.composite_type.as_func()
+    }
+
+    #[inline]
+    pub fn unwrap_func(&self) -> &WasmFuncType {
+        self.composite_type.unwrap_func()
+    }
+}
+
+impl TypeTrace for WasmSubType {
+    fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
+    where
+        F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
+    {
+        self.composite_type.trace(func)
+    }
+
+    fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
+    where
+        F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
+    {
+        self.composite_type.trace_mut(func)
+    }
+}
+
 /// A recursive type group.
 ///
 /// Types within a recgroup can have forward references to each other, which
@@ -536,7 +625,7 @@ impl WasmFuncType {
 #[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
 pub struct WasmRecGroup {
     /// The types inside of this recgroup.
-    pub types: Box<[WasmFuncType]>,
+    pub types: Box<[WasmSubType]>,
 }
 
 impl TypeTrace for WasmRecGroup {
@@ -1062,6 +1151,27 @@ pub trait TypeConvert {
         }
     }
 
+    fn convert_sub_type(&self, ty: &wasmparser::SubType) -> WasmResult<WasmSubType> {
+        if ty.supertype_idx.is_some() {
+            return Err(wasm_unsupported!("wasm gc: explicit subtyping"));
+        }
+        let composite_type = self.convert_composite_type(&ty.composite_type)?;
+        Ok(WasmSubType { composite_type })
+    }
+
+    fn convert_composite_type(
+        &self,
+        ty: &wasmparser::CompositeType,
+    ) -> WasmResult<WasmCompositeType> {
+        match ty {
+            wasmparser::CompositeType::Func(f) => {
+                Ok(WasmCompositeType::Func(self.convert_func_type(f)))
+            }
+            wasmparser::CompositeType::Array(_) => Err(wasm_unsupported!("wasm gc: array types")),
+            wasmparser::CompositeType::Struct(_) => Err(wasm_unsupported!("wasm gc: struct types")),
+        }
+    }
+
     /// Converts a wasmparser function type to a wasmtime type
     fn convert_func_type(&self, ty: &wasmparser::FuncType) -> WasmFuncType {
         let params = ty
diff --git a/crates/wasi-preview1-component-adapter/verify/Cargo.toml b/crates/wasi-preview1-component-adapter/verify/Cargo.toml
index f739dd707..27234761a 100644
--- a/crates/wasi-preview1-component-adapter/verify/Cargo.toml
+++ b/crates/wasi-preview1-component-adapter/verify/Cargo.toml
@@ -11,4 +11,4 @@ workspace = true
 [dependencies]
 wasmparser = { workspace = true }
 wat = { workspace = true }
-anyhow = { workspace = true }
+anyhow = { workspace = true, features = ['std'] }
diff --git a/crates/wasi/src/preview1.rs b/crates/wasi/src/preview1.rs
index 41390112f..57fd0ebf5 100644
--- a/crates/wasi/src/preview1.rs
+++ b/crates/wasi/src/preview1.rs
@@ -2321,6 +2321,35 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiP1Ctx {
             // Indefinite sleeping is not supported in preview1.
             return Err(types::Errno::Inval.into());
         }
+
+        // This is a special case where `poll_oneoff` is just sleeping
+        // on a single relative timer event. This special case was added
+        // after experimental observations showed that std::thread::sleep
+        // results in more consistent sleep times. This design ensures that
+        // wasmtime can handle real-time requirements more accurately.
+        if nsubscriptions == 1 {
+            let sub = subs.read()?;
+            if let types::SubscriptionU::Clock(clocksub) = sub.u {
+                if !clocksub
+                    .flags
+                    .contains(types::Subclockflags::SUBSCRIPTION_CLOCK_ABSTIME)
+                    && self.ctx().allow_blocking_current_thread
+                {
+                    std::thread::sleep(std::time::Duration::from_nanos(clocksub.timeout));
+                    events.write(types::Event {
+                        userdata: sub.userdata,
+                        error: types::Errno::Success,
+                        type_: types::Eventtype::Clock,
+                        fd_readwrite: types::EventFdReadwrite {
+                            flags: types::Eventrwflags::empty(),
+                            nbytes: 1,
+                        },
+                    })?;
+                    return Ok(1);
+                }
+            }
+        }
+
         let subs = subs.as_array(nsubscriptions);
         let events = events.as_array(nsubscriptions);
 
diff --git a/crates/wasmtime/Cargo.toml b/crates/wasmtime/Cargo.toml
index 07e72b0fa..fccd5ba11 100644
--- a/crates/wasmtime/Cargo.toml
+++ b/crates/wasmtime/Cargo.toml
@@ -44,7 +44,7 @@ wat = { workspace = true, optional = true }
 serde = { workspace = true }
 serde_derive = { workspace = true }
 serde_json = { workspace = true }
-bincode = { workspace = true }
+postcard = { workspace = true }
 indexmap = { workspace = true }
 paste = "1.0.3"
 once_cell = { workspace = true }
diff --git a/crates/wasmtime/src/compile.rs b/crates/wasmtime/src/compile.rs
index 5ea9e33bc..0727f6f8a 100644
--- a/crates/wasmtime/src/compile.rs
+++ b/crates/wasmtime/src/compile.rs
@@ -401,7 +401,8 @@ impl<'a> CompileInputs<'a> {
         if component.component.num_resources > 0 {
             if let Some(sig) = types.find_resource_drop_signature() {
                 ret.push_input(move |compiler| {
-                    let trampoline = compiler.compile_wasm_to_native_trampoline(&types[sig])?;
+                    let trampoline =
+                        compiler.compile_wasm_to_native_trampoline(types[sig].unwrap_func())?;
                     Ok(CompileOutput {
                         key: CompileKey::resource_drop_wasm_to_native_trampoline(),
                         symbol: "resource_drop_trampoline".to_string(),
@@ -493,7 +494,7 @@ impl<'a> CompileInputs<'a> {
 
         for signature in sigs {
             self.push_input(move |compiler| {
-                let wasm_func_ty = &types[signature];
+                let wasm_func_ty = types[signature].unwrap_func();
                 let trampoline = compiler.compile_wasm_to_native_trampoline(wasm_func_ty)?;
                 Ok(CompileOutput {
                     key: CompileKey::wasm_to_native_trampoline(signature),
diff --git a/crates/wasmtime/src/engine/serialization.rs b/crates/wasmtime/src/engine/serialization.rs
index d6254cdff..4aca40bc1 100644
--- a/crates/wasmtime/src/engine/serialization.rs
+++ b/crates/wasmtime/src/engine/serialization.rs
@@ -15,7 +15,7 @@
 //! 1. A version byte, currently `VERSION`.
 //! 2. A byte indicating how long the next field is.
 //! 3. A version string of the length of the previous byte value.
-//! 4. A `bincode`-encoded `Metadata` structure.
+//! 4. A `postcard`-encoded `Metadata` structure.
 //!
 //! This is hoped to help distinguish easily Wasmtime-based ELF files from
 //! other random ELF files, as well as provide better error messages for
@@ -110,7 +110,7 @@ pub fn check_compatible(engine: &Engine, mmap: &[u8], expected: ObjectKind) -> R
         }
         ModuleVersionStrategy::None => { /* ignore the version info, accept all */ }
     }
-    bincode::deserialize::<Metadata<'_>>(data)?.check_compatible(engine)
+    postcard::from_bytes::<Metadata<'_>>(data)?.check_compatible(engine)
 }
 
 #[cfg(any(feature = "cranelift", feature = "winch"))]
@@ -134,7 +134,7 @@ pub fn append_compiler_info(engine: &Engine, obj: &mut Object<'_>, metadata: &Me
     );
     data.push(version.len() as u8);
     data.extend_from_slice(version.as_bytes());
-    bincode::serialize_into(&mut data, metadata).unwrap();
+    data.extend(postcard::to_allocvec(metadata).unwrap());
     obj.set_section_data(section, data, 1);
 }
 
diff --git a/crates/wasmtime/src/runtime/component/component.rs b/crates/wasmtime/src/runtime/component/component.rs
index 4e263ba3f..09908b6b4 100644
--- a/crates/wasmtime/src/runtime/component/component.rs
+++ b/crates/wasmtime/src/runtime/component/component.rs
@@ -375,7 +375,7 @@ impl Component {
             static_modules,
         } = match artifacts {
             Some(artifacts) => artifacts,
-            None => bincode::deserialize(code_memory.wasmtime_info())?,
+            None => postcard::from_bytes(code_memory.wasmtime_info())?,
         };
 
         // Validate that the component can be used with the current instance
diff --git a/crates/wasmtime/src/runtime/component/types.rs b/crates/wasmtime/src/runtime/component/types.rs
index b40780b5d..9536327ad 100644
--- a/crates/wasmtime/src/runtime/component/types.rs
+++ b/crates/wasmtime/src/runtime/component/types.rs
@@ -873,7 +873,7 @@ impl ComponentItem {
             TypeDef::Module(idx) => Self::Module(Module::from(*idx, ty)),
             TypeDef::CoreFunc(idx) => Self::CoreFunc(FuncType::from_wasm_func_type(
                 engine,
-                ty.types[*idx].clone(),
+                ty.types[*idx].unwrap_func().clone(),
             )),
             TypeDef::Resource(idx) => {
                 let resource_index = ty.types[*idx].ty;
diff --git a/crates/wasmtime/src/runtime/coredump.rs b/crates/wasmtime/src/runtime/coredump.rs
index 04b376185..ee98659fd 100644
--- a/crates/wasmtime/src/runtime/coredump.rs
+++ b/crates/wasmtime/src/runtime/coredump.rs
@@ -179,7 +179,7 @@ impl WasmCoreDump {
                     ValType::Ref(r) => match r.heap_type() {
                         HeapType::Extern => wasm_encoder::ValType::EXTERNREF,
 
-                        HeapType::Func | HeapType::Concrete(_) | HeapType::NoFunc => {
+                        HeapType::Func | HeapType::ConcreteFunc(_) | HeapType::NoFunc => {
                             wasm_encoder::ValType::FUNCREF
                         }
 
diff --git a/crates/wasmtime/src/runtime/externals/global.rs b/crates/wasmtime/src/runtime/externals/global.rs
index 5f78ca9ad..2af59fff7 100644
--- a/crates/wasmtime/src/runtime/externals/global.rs
+++ b/crates/wasmtime/src/runtime/externals/global.rs
@@ -117,7 +117,7 @@ impl Global {
                 ValType::V128 => Val::V128((*definition.as_u128()).into()),
                 ValType::Ref(ref_ty) => {
                     let reference: Ref = match ref_ty.heap_type() {
-                        HeapType::Func | HeapType::Concrete(_) => {
+                        HeapType::Func | HeapType::ConcreteFunc(_) => {
                             Func::_from_raw(&mut store, definition.as_func_ref().cast()).into()
                         }
 
diff --git a/crates/wasmtime/src/runtime/gc/enabled/rooting.rs b/crates/wasmtime/src/runtime/gc/enabled/rooting.rs
index 293765e0a..2a120a6e5 100644
--- a/crates/wasmtime/src/runtime/gc/enabled/rooting.rs
+++ b/crates/wasmtime/src/runtime/gc/enabled/rooting.rs
@@ -107,6 +107,7 @@ use crate::{
     AsContext, AsContextMut, GcRef, Result, RootedGcRef,
 };
 use anyhow::anyhow;
+use std::num::NonZeroU64;
 use std::{
     fmt::Debug,
     hash::Hash,
@@ -186,12 +187,19 @@ pub(crate) use sealed::*;
 // Just `pub` to avoid `warn(private_interfaces)` in public APIs, which we can't
 // `allow(...)` on our MSRV yet.
 #[doc(hidden)]
+#[repr(C)] // NB: if this layout changes be sure to change the C API as well
 pub struct GcRootIndex {
     store_id: StoreId,
     generation: u32,
     index: PackedIndex,
 }
 
+const _: () = {
+    // NB: these match the C API which should also be updated if this changes
+    assert!(std::mem::size_of::<GcRootIndex>() == 16);
+    assert!(std::mem::align_of::<GcRootIndex>() == 8);
+};
+
 impl GcRootIndex {
     #[inline]
     pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool {
@@ -271,6 +279,7 @@ impl GcRootIndex {
 /// where the high bit is the discriminant and the lower 31 bits are the
 /// payload.
 #[derive(Clone, Copy, PartialEq, Eq, Hash)]
+#[repr(transparent)]
 struct PackedIndex(u32);
 
 impl Debug for PackedIndex {
@@ -1192,6 +1201,7 @@ where
 /// unroot. Sometimes leaking is intentional and desirable, particularly when
 /// dealing with short-lived [`Store`][crate::Store]s where unrooting would just
 /// be busy work since the whole store is about to be dropped.
+#[repr(transparent)] // NB: the C API relies on this
 pub struct ManuallyRooted<T>
 where
     T: GcRef,
@@ -1200,6 +1210,16 @@ where
     _phantom: std::marker::PhantomData<T>,
 }
 
+const _: () = {
+    use crate::{AnyRef, ExternRef};
+
+    // NB: these match the C API which should also be updated if this changes
+    assert!(std::mem::size_of::<ManuallyRooted<AnyRef>>() == 16);
+    assert!(std::mem::align_of::<ManuallyRooted<AnyRef>>() == 8);
+    assert!(std::mem::size_of::<ManuallyRooted<ExternRef>>() == 16);
+    assert!(std::mem::align_of::<ManuallyRooted<ExternRef>>() == 8);
+};
+
 impl<T: GcRef> Debug for ManuallyRooted<T> {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         let name = format!("ManuallyRooted<{}>", std::any::type_name::<T>());
@@ -1513,6 +1533,27 @@ where
             .expect("ManuallyRooted's get_gc_ref is infallible");
         gc_ref.hash(state);
     }
+
+    #[doc(hidden)]
+    pub fn into_parts_for_c_api(self) -> (NonZeroU64, u32, u32) {
+        (
+            self.inner.store_id.as_raw(),
+            self.inner.generation,
+            self.inner.index.0,
+        )
+    }
+
+    #[doc(hidden)]
+    pub unsafe fn from_raw_parts_for_c_api(a: NonZeroU64, b: u32, c: u32) -> ManuallyRooted<T> {
+        ManuallyRooted {
+            inner: GcRootIndex {
+                store_id: StoreId::from_raw(a),
+                generation: b,
+                index: PackedIndex(c),
+            },
+            _phantom: std::marker::PhantomData,
+        }
+    }
 }
 
 impl<T: GcRef> RootedGcRefImpl<T> for ManuallyRooted<T> {
diff --git a/crates/wasmtime/src/runtime/linker.rs b/crates/wasmtime/src/runtime/linker.rs
index 096a2a43e..3a93f9c3c 100644
--- a/crates/wasmtime/src/runtime/linker.rs
+++ b/crates/wasmtime/src/runtime/linker.rs
@@ -345,7 +345,7 @@ impl<T> Linker<T> {
                                         debug_assert!(r.is_nullable());
                                         match r.heap_type() {
                                             HeapType::Func
-                                            | HeapType::Concrete(_)
+                                            | HeapType::ConcreteFunc(_)
                                             | HeapType::NoFunc => Val::null_func_ref(),
                                             HeapType::Extern => Val::null_extern_ref(),
                                             HeapType::Any | HeapType::I31 | HeapType::None => {
diff --git a/crates/wasmtime/src/runtime/module.rs b/crates/wasmtime/src/runtime/module.rs
index 04ecc47f1..42906a361 100644
--- a/crates/wasmtime/src/runtime/module.rs
+++ b/crates/wasmtime/src/runtime/module.rs
@@ -445,7 +445,7 @@ impl Module {
         // already.
         let (info, types) = match info_and_types {
             Some((info, types)) => (info, types),
-            None => bincode::deserialize(code_memory.wasmtime_info())?,
+            None => postcard::from_bytes(code_memory.wasmtime_info())?,
         };
 
         // Register function type signatures into the engine for the lifetime
diff --git a/crates/wasmtime/src/runtime/store/data.rs b/crates/wasmtime/src/runtime/store/data.rs
index 3c390d915..dad8654d0 100644
--- a/crates/wasmtime/src/runtime/store/data.rs
+++ b/crates/wasmtime/src/runtime/store/data.rs
@@ -202,6 +202,7 @@ where
 /// it came from. Comparisons with this value are how panics are generated for
 /// mismatching the item that a store belongs to.
 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+#[repr(transparent)] // NB: relied on in the C API
 pub struct StoreId(NonZeroU64);
 
 impl StoreId {
@@ -235,6 +236,16 @@ impl StoreId {
         }
         store_id_mismatch();
     }
+
+    /// Raw accessor for the C API.
+    pub fn as_raw(&self) -> NonZeroU64 {
+        self.0
+    }
+
+    /// Raw constructor for the C API.
+    pub fn from_raw(id: NonZeroU64) -> StoreId {
+        StoreId(id)
+    }
 }
 
 #[repr(C)] // used by reference in the C API, also in `wasmtime_func_t`.
diff --git a/crates/wasmtime/src/runtime/type_registry.rs b/crates/wasmtime/src/runtime/type_registry.rs
index 4262a40eb..729570961 100644
--- a/crates/wasmtime/src/runtime/type_registry.rs
+++ b/crates/wasmtime/src/runtime/type_registry.rs
@@ -20,7 +20,7 @@ use std::{
 };
 use wasmtime_environ::{
     iter_entity_range, EngineOrModuleTypeIndex, ModuleInternedTypeIndex, ModuleTypes, PrimaryMap,
-    TypeTrace, VMSharedTypeIndex, WasmFuncType, WasmRecGroup,
+    TypeTrace, VMSharedTypeIndex, WasmRecGroup, WasmSubType,
 };
 use wasmtime_slab::{Id as SlabId, Slab};
 
@@ -169,11 +169,11 @@ fn slab_id_to_shared_type_index(id: SlabId) -> VMSharedTypeIndex {
 /// Automatically unregisters the type on drop. (Unless other `RegisteredTypes`
 /// are keeping the type registered).
 ///
-/// Dereferences to its underlying `WasmFuncType`.
+/// Dereferences to its underlying `WasmSubType`.
 pub struct RegisteredType {
     engine: Engine,
     entry: RecGroupEntry,
-    ty: Arc<WasmFuncType>,
+    ty: Arc<WasmSubType>,
     index: VMSharedTypeIndex,
 }
 
@@ -218,7 +218,7 @@ impl Drop for RegisteredType {
 }
 
 impl std::ops::Deref for RegisteredType {
-    type Target = WasmFuncType;
+    type Target = WasmSubType;
 
     fn deref(&self) -> &Self::Target {
         &self.ty
@@ -254,7 +254,7 @@ impl Hash for RegisteredType {
 impl RegisteredType {
     /// Constructs a new `RegisteredType`, registering the given type with the
     /// engine's `TypeRegistry`.
-    pub fn new(engine: &Engine, ty: WasmFuncType) -> RegisteredType {
+    pub fn new(engine: &Engine, ty: WasmSubType) -> RegisteredType {
         let (entry, index, ty) = {
             log::trace!("RegisteredType::new({ty:?})");
 
@@ -317,7 +317,7 @@ impl RegisteredType {
         engine: Engine,
         entry: RecGroupEntry,
         index: VMSharedTypeIndex,
-        ty: Arc<WasmFuncType>,
+        ty: Arc<WasmSubType>,
     ) -> Self {
         debug_assert!(entry.0.registrations.load(Acquire) != 0);
         RegisteredType {
@@ -432,7 +432,7 @@ struct TypeRegistryInner {
     // Wasm type.
     //
     // These types are always canonicalized for runtime usage.
-    types: Slab<Arc<WasmFuncType>>,
+    types: Slab<Arc<WasmSubType>>,
 
     // A map that lets you walk backwards from a `VMSharedTypeIndex` to its
     // `RecGroupEntry`.
@@ -511,7 +511,7 @@ impl TypeRegistryInner {
         &mut self,
         map: &PrimaryMap<ModuleInternedTypeIndex, VMSharedTypeIndex>,
         range: Range<ModuleInternedTypeIndex>,
-        types: impl ExactSizeIterator<Item = WasmFuncType>,
+        types: impl ExactSizeIterator<Item = WasmSubType>,
     ) -> RecGroupEntry {
         debug_assert_eq!(iter_entity_range(range.clone()).len(), types.len());
 
@@ -594,7 +594,7 @@ impl TypeRegistryInner {
     }
 
     /// Is the given type canonicalized for runtime usage this registry?
-    fn assert_canonicalized_for_runtime_usage_in_this_registry(&self, ty: &WasmFuncType) {
+    fn assert_canonicalized_for_runtime_usage_in_this_registry(&self, ty: &WasmSubType) {
         ty.trace::<_, ()>(&mut |index| match index {
             EngineOrModuleTypeIndex::RecGroup(_) | EngineOrModuleTypeIndex::Module(_) => {
                 panic!("not canonicalized for runtime usage: {ty:?}")
@@ -619,7 +619,7 @@ impl TypeRegistryInner {
     fn insert_one_type_from_rec_group(
         &mut self,
         module_index: ModuleInternedTypeIndex,
-        ty: WasmFuncType,
+        ty: WasmSubType,
     ) -> VMSharedTypeIndex {
         // Despite being canonicalized for runtime usage, this type may still
         // have forward references to other types in the rec group we haven't
@@ -648,7 +648,7 @@ impl TypeRegistryInner {
     ///
     /// The returned entry will have already had its reference count incremented
     /// on behalf of callers.
-    fn register_singleton_rec_group(&mut self, ty: WasmFuncType) -> RecGroupEntry {
+    fn register_singleton_rec_group(&mut self, ty: WasmSubType) -> RecGroupEntry {
         self.assert_canonicalized_for_runtime_usage_in_this_registry(&ty);
 
         // This type doesn't have any module-level type references, since it is
@@ -800,7 +800,7 @@ impl TypeRegistry {
     /// still using the resulting value! Use the `RegisteredType::root`
     /// constructor if you need to ensure that property and you don't have some
     /// other mechanism already keeping the type registered.
-    pub fn borrow(&self, index: VMSharedTypeIndex) -> Option<Arc<WasmFuncType>> {
+    pub fn borrow(&self, index: VMSharedTypeIndex) -> Option<Arc<WasmSubType>> {
         let id = shared_type_index_to_slab_id(index);
         let inner = self.0.read().unwrap();
         inner.types.get(id).cloned()
diff --git a/crates/wasmtime/src/runtime/types.rs b/crates/wasmtime/src/runtime/types.rs
index 1cca5e5a9..1e306acbb 100644
--- a/crates/wasmtime/src/runtime/types.rs
+++ b/crates/wasmtime/src/runtime/types.rs
@@ -2,7 +2,8 @@ use anyhow::{bail, Result};
 use std::fmt::{self, Display};
 use wasmtime_environ::{
     EngineOrModuleTypeIndex, EntityType, Global, Memory, ModuleTypes, Table, TypeTrace,
-    VMSharedTypeIndex, WasmFuncType, WasmHeapType, WasmRefType, WasmValType,
+    VMSharedTypeIndex, WasmCompositeType, WasmFuncType, WasmHeapType, WasmRefType, WasmSubType,
+    WasmValType,
 };
 
 use crate::{type_registry::RegisteredType, Engine};
@@ -452,7 +453,7 @@ pub enum HeapType {
     /// specific, concrete type.
     ///
     /// This is a subtype of `func` and a supertype of `nofunc`.
-    Concrete(FuncType),
+    ConcreteFunc(FuncType),
 
     /// The `nofunc` heap type represents the null function reference.
     ///
@@ -486,7 +487,7 @@ impl Display for HeapType {
             HeapType::Any => write!(f, "any"),
             HeapType::I31 => write!(f, "i31"),
             HeapType::None => write!(f, "none"),
-            HeapType::Concrete(ty) => write!(f, "(concrete {:?})", ty.type_index()),
+            HeapType::ConcreteFunc(ty) => write!(f, "(concrete {:?})", ty.type_index()),
         }
     }
 }
@@ -494,7 +495,7 @@ impl Display for HeapType {
 impl From<FuncType> for HeapType {
     #[inline]
     fn from(f: FuncType) -> Self {
-        HeapType::Concrete(f)
+        HeapType::ConcreteFunc(f)
     }
 }
 
@@ -540,7 +541,7 @@ impl HeapType {
     ///
     /// Types that are not concrete, user-defined types are abstract types.
     pub fn is_concrete(&self) -> bool {
-        matches!(self, HeapType::Concrete(_))
+        matches!(self, HeapType::ConcreteFunc(_))
     }
 
     /// Get the underlying concrete, user-defined type, if any.
@@ -548,7 +549,7 @@ impl HeapType {
     /// Returns `None` for abstract types.
     pub fn as_concrete(&self) -> Option<&FuncType> {
         match self {
-            HeapType::Concrete(f) => Some(f),
+            HeapType::ConcreteFunc(f) => Some(f),
             _ => None,
         }
     }
@@ -570,7 +571,7 @@ impl HeapType {
         let _ = engine;
 
         match self {
-            HeapType::Func | HeapType::Concrete(_) | HeapType::NoFunc => HeapType::Func,
+            HeapType::Func | HeapType::ConcreteFunc(_) | HeapType::NoFunc => HeapType::Func,
             HeapType::Extern => HeapType::Extern,
             HeapType::Any | HeapType::I31 | HeapType::None => HeapType::Any,
         }
@@ -589,12 +590,14 @@ impl HeapType {
             (HeapType::Extern, HeapType::Extern) => true,
             (HeapType::Extern, _) => false,
 
-            (HeapType::NoFunc, HeapType::NoFunc | HeapType::Concrete(_) | HeapType::Func) => true,
+            (HeapType::NoFunc, HeapType::NoFunc | HeapType::ConcreteFunc(_) | HeapType::Func) => {
+                true
+            }
             (HeapType::NoFunc, _) => false,
 
-            (HeapType::Concrete(_), HeapType::Func) => true,
-            (HeapType::Concrete(a), HeapType::Concrete(b)) => a.matches(b),
-            (HeapType::Concrete(_), _) => false,
+            (HeapType::ConcreteFunc(_), HeapType::Func) => true,
+            (HeapType::ConcreteFunc(a), HeapType::ConcreteFunc(b)) => a.matches(b),
+            (HeapType::ConcreteFunc(_), _) => false,
 
             (HeapType::Func, HeapType::Func) => true,
             (HeapType::Func, _) => false,
@@ -642,7 +645,7 @@ impl HeapType {
             | HeapType::Any
             | HeapType::I31
             | HeapType::None => true,
-            HeapType::Concrete(ty) => ty.comes_from_same_engine(engine),
+            HeapType::ConcreteFunc(ty) => ty.comes_from_same_engine(engine),
         }
     }
 
@@ -654,8 +657,8 @@ impl HeapType {
             HeapType::Any => WasmHeapType::Any,
             HeapType::I31 => WasmHeapType::I31,
             HeapType::None => WasmHeapType::None,
-            HeapType::Concrete(f) => {
-                WasmHeapType::Concrete(EngineOrModuleTypeIndex::Engine(f.type_index()))
+            HeapType::ConcreteFunc(f) => {
+                WasmHeapType::ConcreteFunc(EngineOrModuleTypeIndex::Engine(f.type_index()))
             }
         }
     }
@@ -668,11 +671,11 @@ impl HeapType {
             WasmHeapType::Any => HeapType::Any,
             WasmHeapType::I31 => HeapType::I31,
             WasmHeapType::None => HeapType::None,
-            WasmHeapType::Concrete(EngineOrModuleTypeIndex::Engine(idx)) => {
-                HeapType::Concrete(FuncType::from_shared_type_index(engine, *idx))
+            WasmHeapType::ConcreteFunc(EngineOrModuleTypeIndex::Engine(idx)) => {
+                HeapType::ConcreteFunc(FuncType::from_shared_type_index(engine, *idx))
             }
-            WasmHeapType::Concrete(EngineOrModuleTypeIndex::Module(_))
-            | WasmHeapType::Concrete(EngineOrModuleTypeIndex::RecGroup(_)) => {
+            WasmHeapType::ConcreteFunc(EngineOrModuleTypeIndex::Module(_))
+            | WasmHeapType::ConcreteFunc(EngineOrModuleTypeIndex::RecGroup(_)) => {
                 panic!("HeapType::from_wasm_type on non-canonical heap type")
             }
         }
@@ -688,7 +691,7 @@ impl HeapType {
             // TODO: Once we support concrete struct and array types, we will
             // need to inspect the payload to determine whether this is a
             // GC-managed type or not.
-            Self::Concrete(_) => false,
+            Self::ConcreteFunc(_) => false,
 
             // These are compatible with GC references, but don't actually point
             // to GC objecs. It would generally be safe to return `true` here,
@@ -768,7 +771,7 @@ impl ExternType {
                     FuncType::from_shared_type_index(engine, *e).into()
                 }
                 EngineOrModuleTypeIndex::Module(m) => {
-                    FuncType::from_wasm_func_type(engine, types[*m].clone()).into()
+                    FuncType::from_wasm_func_type(engine, types[*m].unwrap_func().clone()).into()
                 }
                 EngineOrModuleTypeIndex::RecGroup(_) => unreachable!(),
             },
@@ -889,6 +892,7 @@ impl FuncType {
     pub fn param(&self, i: usize) -> Option<ValType> {
         let engine = self.engine();
         self.registered_type
+            .unwrap_func()
             .params()
             .get(i)
             .map(|ty| ValType::from_wasm_type(engine, ty))
@@ -899,6 +903,7 @@ impl FuncType {
     pub fn params(&self) -> impl ExactSizeIterator<Item = ValType> + '_ {
         let engine = self.engine();
         self.registered_type
+            .unwrap_func()
             .params()
             .iter()
             .map(|ty| ValType::from_wasm_type(engine, ty))
@@ -910,6 +915,7 @@ impl FuncType {
     pub fn result(&self, i: usize) -> Option<ValType> {
         let engine = self.engine();
         self.registered_type
+            .unwrap_func()
             .returns()
             .get(i)
             .map(|ty| ValType::from_wasm_type(engine, ty))
@@ -920,6 +926,7 @@ impl FuncType {
     pub fn results(&self) -> impl ExactSizeIterator<Item = ValType> + '_ {
         let engine = self.engine();
         self.registered_type
+            .unwrap_func()
             .returns()
             .iter()
             .map(|ty| ValType::from_wasm_type(engine, ty))
@@ -980,7 +987,7 @@ impl FuncType {
     }
 
     pub(crate) fn as_wasm_func_type(&self) -> &WasmFuncType {
-        &self.registered_type
+        self.registered_type.unwrap_func()
     }
 
     pub(crate) fn into_registered_type(self) -> RegisteredType {
@@ -998,7 +1005,16 @@ impl FuncType {
     /// holding a strong reference to all of its types, including any `(ref null
     /// <index>)` types used in the function's parameters and results.
     pub(crate) fn from_wasm_func_type(engine: &Engine, ty: WasmFuncType) -> FuncType {
-        let ty = RegisteredType::new(engine, ty);
+        let ty = RegisteredType::new(
+            engine,
+            WasmSubType {
+                // TODO:
+                //
+                // is_final: true,
+                // supertype: None,
+                composite_type: WasmCompositeType::Func(ty),
+            },
+        );
         Self {
             registered_type: ty,
         }
diff --git a/crates/wasmtime/src/runtime/types/matching.rs b/crates/wasmtime/src/runtime/types/matching.rs
index 45504aceb..9511d9b03 100644
--- a/crates/wasmtime/src/runtime/types/matching.rs
+++ b/crates/wasmtime/src/runtime/types/matching.rs
@@ -1,8 +1,8 @@
 use crate::{linker::DefinitionType, Engine, FuncType};
 use anyhow::{anyhow, bail, Result};
 use wasmtime_environ::{
-    EntityType, Global, Memory, ModuleTypes, Table, TypeTrace, VMSharedTypeIndex, WasmFuncType,
-    WasmHeapType, WasmRefType, WasmValType,
+    EntityType, Global, Memory, ModuleTypes, Table, TypeTrace, VMSharedTypeIndex,
+    WasmCompositeType, WasmHeapType, WasmRefType, WasmSubType, WasmValType,
 };
 
 pub struct MatchCx<'a> {
@@ -36,7 +36,7 @@ impl MatchCx<'_> {
             None => panic!("{actual:?} is not registered"),
         };
 
-        Err(func_ty_mismatch(msg, &expected, &actual))
+        Err(concrete_type_mismatch(msg, &expected, &actual))
     }
 
     /// Validates that the `expected` type matches the type of `actual`
@@ -96,7 +96,7 @@ pub fn entity_ty(
                 if expected == actual {
                     Ok(())
                 } else {
-                    Err(func_ty_mismatch(
+                    Err(concrete_type_mismatch(
                         "function types incompatible",
                         expected,
                         actual,
@@ -109,22 +109,29 @@ pub fn entity_ty(
     }
 }
 
-fn func_ty_mismatch(msg: &str, expected: &WasmFuncType, actual: &WasmFuncType) -> anyhow::Error {
-    let render = |ty: &WasmFuncType| {
-        let params = ty
-            .params()
-            .iter()
-            .map(|s| s.to_string())
-            .collect::<Vec<_>>()
-            .join(", ");
-        let returns = ty
-            .returns()
-            .iter()
-            .map(|s| s.to_string())
-            .collect::<Vec<_>>()
-            .join(", ");
-        format!("`({}) -> ({})`", params, returns)
+fn concrete_type_mismatch(
+    msg: &str,
+    expected: &WasmSubType,
+    actual: &WasmSubType,
+) -> anyhow::Error {
+    let render = |ty: &WasmSubType| match &ty.composite_type {
+        WasmCompositeType::Func(ty) => {
+            let params = ty
+                .params()
+                .iter()
+                .map(|s| s.to_string())
+                .collect::<Vec<_>>()
+                .join(", ");
+            let returns = ty
+                .returns()
+                .iter()
+                .map(|s| s.to_string())
+                .collect::<Vec<_>>()
+                .join(", ");
+            format!("`({}) -> ({})`", params, returns)
+        }
     };
+
     anyhow!(
         "{msg}: expected func of type {}, found func of type {}",
         render(expected),
@@ -197,15 +204,15 @@ fn match_heap(expected: WasmHeapType, actual: WasmHeapType, desc: &str) -> Resul
     let result = match (actual, expected) {
         // TODO: Wasm GC introduces subtyping between function types, so it will
         // no longer suffice to check whether canonicalized type IDs are equal.
-        (H::Concrete(actual), H::Concrete(expected)) => actual == expected,
+        (H::ConcreteFunc(actual), H::ConcreteFunc(expected)) => actual == expected,
 
         (H::NoFunc, H::NoFunc) => true,
         (_, H::NoFunc) => false,
 
-        (H::NoFunc, H::Concrete(_)) => true,
-        (_, H::Concrete(_)) => false,
+        (H::NoFunc, H::ConcreteFunc(_)) => true,
+        (_, H::ConcreteFunc(_)) => false,
 
-        (H::NoFunc | H::Concrete(_) | H::Func, H::Func) => true,
+        (H::NoFunc | H::ConcreteFunc(_) | H::Func, H::Func) => true,
         (_, H::Func) => false,
 
         (H::Extern, H::Extern) => true,
diff --git a/crates/wasmtime/src/runtime/values.rs b/crates/wasmtime/src/runtime/values.rs
index 96f21a581..0b03ade19 100644
--- a/crates/wasmtime/src/runtime/values.rs
+++ b/crates/wasmtime/src/runtime/values.rs
@@ -80,7 +80,7 @@ impl Val {
     /// Returns the null reference for the given heap type.
     pub fn null_ref(heap_type: HeapType) -> Val {
         match heap_type {
-            HeapType::Func | HeapType::NoFunc | HeapType::Concrete(_) => Val::FuncRef(None),
+            HeapType::Func | HeapType::NoFunc | HeapType::ConcreteFunc(_) => Val::FuncRef(None),
             HeapType::Extern => Val::ExternRef(None),
             HeapType::Any | HeapType::I31 | HeapType::None => Val::AnyRef(None),
         }
@@ -129,9 +129,10 @@ impl Val {
             Val::V128(_) => ValType::V128,
             Val::ExternRef(_) => ValType::EXTERNREF,
             Val::FuncRef(None) => ValType::NULLFUNCREF,
-            Val::FuncRef(Some(f)) => {
-                ValType::Ref(RefType::new(false, HeapType::Concrete(f.load_ty(store))))
-            }
+            Val::FuncRef(Some(f)) => ValType::Ref(RefType::new(
+                false,
+                HeapType::ConcreteFunc(f.load_ty(store)),
+            )),
             Val::AnyRef(None) => ValType::NULLREF,
             Val::AnyRef(Some(_)) => {
                 assert!(VMGcRef::ONLY_EXTERN_REF_AND_I31);
@@ -242,7 +243,7 @@ impl Val {
             ValType::V128 => Val::V128(raw.get_v128().into()),
             ValType::Ref(ref_ty) => {
                 let ref_ = match ref_ty.heap_type() {
-                    HeapType::Func | HeapType::Concrete(_) => {
+                    HeapType::Func | HeapType::ConcreteFunc(_) => {
                         Func::from_raw(store, raw.get_funcref()).into()
                     }
                     HeapType::NoFunc => Ref::Func(None),
@@ -757,7 +758,7 @@ impl Ref {
                 // NB: We choose the most-specific heap type we can here and let
                 // subtyping do its thing if callers are matching against a
                 // `HeapType::Func`.
-                Ref::Func(Some(f)) => HeapType::Concrete(f.load_ty(store)),
+                Ref::Func(Some(f)) => HeapType::ConcreteFunc(f.load_ty(store)),
                 Ref::Func(None) => HeapType::NoFunc,
 
                 Ref::Any(Some(_)) => {
@@ -791,8 +792,8 @@ impl Ref {
             (Ref::Extern(_), _) => false,
 
             (Ref::Func(_), HeapType::Func) => true,
-            (Ref::Func(None), HeapType::NoFunc | HeapType::Concrete(_)) => true,
-            (Ref::Func(Some(f)), HeapType::Concrete(func_ty)) => f._matches_ty(store, func_ty),
+            (Ref::Func(None), HeapType::NoFunc | HeapType::ConcreteFunc(_)) => true,
+            (Ref::Func(Some(f)), HeapType::ConcreteFunc(func_ty)) => f._matches_ty(store, func_ty),
             (Ref::Func(_), _) => false,
 
             (Ref::Any(_), HeapType::Any) => true,
@@ -923,7 +924,7 @@ mod tests {
         // Should panic.
         let _ = Val::FuncRef(Some(f)).matches_ty(
             &s1,
-            &ValType::Ref(RefType::new(true, HeapType::Concrete(t2))),
+            &ValType::Ref(RefType::new(true, HeapType::ConcreteFunc(t2))),
         );
     }
 
@@ -940,6 +941,6 @@ mod tests {
         let f = Func::new(&mut s1, t1.clone(), |_caller, _args, _results| Ok(()));
 
         // Should panic.
-        let _ = Ref::Func(Some(f)).matches_ty(&s1, &RefType::new(true, HeapType::Concrete(t2)));
+        let _ = Ref::Func(Some(f)).matches_ty(&s1, &RefType::new(true, HeapType::ConcreteFunc(t2)));
     }
 }
diff --git a/crates/winch/src/compiler.rs b/crates/winch/src/compiler.rs
index ecb93dacd..217f3daff 100644
--- a/crates/winch/src/compiler.rs
+++ b/crates/winch/src/compiler.rs
@@ -94,7 +94,7 @@ impl wasmtime_environ::Compiler for Compiler {
     ) -> Result<(WasmFunctionInfo, Box<dyn Any + Send>), CompileError> {
         let index = translation.module.func_index(index);
         let sig = translation.module.functions[index].signature;
-        let ty = &types[sig];
+        let ty = types[sig].unwrap_func();
         let FunctionBodyData { body, validator } = data;
         let mut context = self.get_context(translation);
         let mut validator = validator.into_validator(mem::take(&mut context.allocations));
diff --git a/examples/anyref.c b/examples/anyref.c
index a54899b27..cf0a67f3b 100644
--- a/examples/anyref.c
+++ b/examples/anyref.c
@@ -94,12 +94,13 @@ int main() {
 
   // Create a new `anyref` value from an i31 (`i31ref` is a subtype of
   // `anyref`).
-  wasmtime_anyref_t *anyref = wasmtime_anyref_from_i31(context, 1234);
-  assert(anyref != NULL);
+  wasmtime_anyref_t anyref;
+  wasmtime_anyref_from_i31(context, 1234, &anyref);
+  assert(!wasmtime_anyref_is_null(&anyref));
 
   // The `anyref`'s inner i31 value should be 1234.
   uint32_t i31val = 0;
-  bool is_i31 = wasmtime_anyref_i31_get_u(context, anyref, &i31val);
+  bool is_i31 = wasmtime_anyref_i31_get_u(context, &anyref, &i31val);
   assert(is_i31);
   assert(i31val == 1234);
 
@@ -128,10 +129,10 @@ int main() {
   assert(elem.kind == WASMTIME_ANYREF);
   is_i31 = false;
   i31val = 0;
-  is_i31 = wasmtime_anyref_i31_get_u(context, elem.of.anyref, &i31val);
+  is_i31 = wasmtime_anyref_i31_get_u(context, &elem.of.anyref, &i31val);
   assert(is_i31);
   assert(i31val == 1234);
-  wasmtime_val_delete(context, &elem);
+  wasmtime_val_unroot(context, &elem);
 
   printf("Touching `anyref` global...\n");
 
@@ -152,10 +153,10 @@ int main() {
   assert(global_val.kind == WASMTIME_ANYREF);
   is_i31 = false;
   i31val = 0;
-  is_i31 = wasmtime_anyref_i31_get_u(context, global_val.of.anyref, &i31val);
+  is_i31 = wasmtime_anyref_i31_get_u(context, &global_val.of.anyref, &i31val);
   assert(is_i31);
   assert(i31val == 1234);
-  wasmtime_val_delete(context, &global_val);
+  wasmtime_val_unroot(context, &global_val);
 
   printf("Passing `anyref` into func...\n");
 
@@ -190,11 +191,11 @@ int main() {
   assert(results[0].kind == WASMTIME_ANYREF);
   is_i31 = false;
   i31val = 0;
-  is_i31 = wasmtime_anyref_i31_get_u(context, results[0].of.anyref, &i31val);
+  is_i31 = wasmtime_anyref_i31_get_u(context, &results[0].of.anyref, &i31val);
   assert(is_i31);
   assert(i31val == 42);
-  wasmtime_val_delete(context, &results[0]);
-  wasmtime_val_delete(context, &anyref_val);
+  wasmtime_val_unroot(context, &results[0]);
+  wasmtime_val_unroot(context, &anyref_val);
 
   // We can GC any now-unused references to our anyref that the store is
   // holding.
diff --git a/examples/externref.c b/examples/externref.c
index 79fa82841..fe8098a6b 100644
--- a/examples/externref.c
+++ b/examples/externref.c
@@ -94,11 +94,12 @@ int main() {
   //
   // Note that the NULL here is a finalizer callback, but we don't need one for
   // this example.
-  wasmtime_externref_t *externref =
-      wasmtime_externref_new(context, "Hello, World!", NULL);
+  wasmtime_externref_t externref;
+  ok = wasmtime_externref_new(context, "Hello, World!", NULL, &externref);
+  assert(ok);
 
   // The `externref`'s wrapped data should be the string "Hello, World!".
-  void *data = wasmtime_externref_data(context, externref);
+  void *data = wasmtime_externref_data(context, &externref);
   assert(strcmp((char *)data, "Hello, World!") == 0);
 
   wasmtime_extern_t item;
@@ -124,9 +125,9 @@ int main() {
     ok = wasmtime_table_get(context, &item.of.table, 3, &elem);
     assert(ok);
     assert(elem.kind == WASMTIME_EXTERNREF);
-    assert(strcmp((char *)wasmtime_externref_data(context, elem.of.externref),
+    assert(strcmp((char *)wasmtime_externref_data(context, &elem.of.externref),
                   "Hello, World!") == 0);
-    wasmtime_val_delete(context, &elem);
+    wasmtime_val_unroot(context, &elem);
   }
 
   printf("Touching `externref` global...\n");
@@ -148,9 +149,9 @@ int main() {
     wasmtime_global_get(context, &item.of.global, &global_val);
     assert(global_val.kind == WASMTIME_EXTERNREF);
     assert(strcmp((char *)wasmtime_externref_data(context,
-                                                  global_val.of.externref),
+                                                  &global_val.of.externref),
                   "Hello, World!") == 0);
-    wasmtime_val_delete(context, &global_val);
+    wasmtime_val_unroot(context, &global_val);
   }
 
   printf("Calling `externref` func...\n");
@@ -173,11 +174,11 @@ int main() {
     // our `externref`.
     assert(results[0].kind == WASMTIME_EXTERNREF);
     assert(strcmp((char *)wasmtime_externref_data(context,
-                                                  results[0].of.externref),
+                                                  &results[0].of.externref),
                   "Hello, World!") == 0);
-    wasmtime_val_delete(context, &results[0]);
+    wasmtime_val_unroot(context, &results[0]);
   }
-  wasmtime_val_delete(context, &externref_val);
+  wasmtime_val_unroot(context, &externref_val);
 
   // We can GC any now-unused references to our externref that the store is
   // holding.
diff --git a/examples/min-platform/Cargo.toml b/examples/min-platform/Cargo.toml
index 0bb0249a1..76bbb2e47 100644
--- a/examples/min-platform/Cargo.toml
+++ b/examples/min-platform/Cargo.toml
@@ -9,6 +9,6 @@ rust-version.workspace = true
 workspace = true
 
 [dependencies]
-anyhow = { workspace = true }
+anyhow = { workspace = true, features = ['std'] }
 libloading = "0.8"
 object = { workspace = true }
diff --git a/supply-chain/audits.toml b/supply-chain/audits.toml
index 0c5195ca7..21ffd2869 100644
--- a/supply-chain/audits.toml
+++ b/supply-chain/audits.toml
@@ -1062,6 +1062,12 @@ who = "Pat Hickey <phickey@fastly.com>"
 criteria = "safe-to-deploy"
 version = "0.2.0"
 
+[[audits.cobs]]
+who = "Alex Crichton <alex@alexcrichton.com>"
+criteria = "safe-to-deploy"
+version = "0.2.3"
+notes = "No `unsafe` code in the crate and no usage of `std`"
+
 [[audits.codespan-reporting]]
 who = "Jamey Sharp <jsharp@fastly.com>"
 criteria = "safe-to-deploy"
@@ -1197,6 +1203,12 @@ This diff brings in a number of minor updates of which none are related to
 `unsafe` code or anything system-related like filesystems.
 """
 
+[[audits.embedded-io]]
+who = "Alex Crichton <alex@alexcrichton.com>"
+criteria = "safe-to-deploy"
+version = "0.4.0"
+notes = "No `unsafe` code and only uses `std` in ways one would expect the crate to do so."
+
 [[audits.errno]]
 who = "Dan Gohman <dev@sunfishcode.online>"
 criteria = "safe-to-deploy"
@@ -2007,6 +2019,15 @@ No `unsafe` additions or anything outside of the purview of the crate in this
 change.
 """
 
+[[audits.postcard]]
+who = "Alex Crichton <alex@alexcrichton.com>"
+criteria = "safe-to-deploy"
+version = "1.0.8"
+notes = """
+I've audited the unsafe code to do what it looks like it's doing. Otherwise the
+crate is a standard serializer/deserializer crate.
+"""
+
 [[audits.pretty_env_logger]]
 who = "Alex Crichton <alex@alexcrichton.com>"
 criteria = "safe-to-deploy"
diff --git a/supply-chain/config.toml b/supply-chain/config.toml
index eb2b7081d..79a239e79 100644
--- a/supply-chain/config.toml
+++ b/supply-chain/config.toml
@@ -189,7 +189,7 @@ version = "0.17.0"
 criteria = "safe-to-deploy"
 
 [[exemptions.ahash]]
-version = "0.7.6"
+version = "0.8.11"
 criteria = "safe-to-deploy"
 
 [[exemptions.bincode]]
diff --git a/supply-chain/imports.lock b/supply-chain/imports.lock
index 61311c79f..9a86ce4b0 100644
--- a/supply-chain/imports.lock
+++ b/supply-chain/imports.lock
@@ -826,6 +826,13 @@ user-id = 2915
 user-login = "Amanieu"
 user-name = "Amanieu d'Antras"
 
+[[publisher.hashbrown]]
+version = "0.14.3"
+when = "2023-11-26"
+user-id = 2915
+user-login = "Amanieu"
+user-name = "Amanieu d'Antras"
+
 [[publisher.indexmap]]
 version = "1.9.1"
 when = "2022-06-21"
@@ -1196,6 +1203,12 @@ when = "2024-04-18"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasm-encoder]]
+version = "0.206.0"
+when = "2024-04-25"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasm-metadata]]
 version = "0.201.0"
 when = "2024-02-27"
@@ -1226,6 +1239,12 @@ when = "2024-04-18"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasm-metadata]]
+version = "0.206.0"
+when = "2024-04-25"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasm-mutate]]
 version = "0.201.0"
 when = "2024-02-27"
@@ -1256,6 +1275,12 @@ when = "2024-04-18"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasm-mutate]]
+version = "0.206.0"
+when = "2024-04-25"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasm-smith]]
 version = "0.201.0"
 when = "2024-02-27"
@@ -1286,6 +1311,12 @@ when = "2024-04-18"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasm-smith]]
+version = "0.206.0"
+when = "2024-04-25"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmparser]]
 version = "0.201.0"
 when = "2024-02-27"
@@ -1316,6 +1347,12 @@ when = "2024-04-18"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmparser]]
+version = "0.206.0"
+when = "2024-04-25"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmprinter]]
 version = "0.201.0"
 when = "2024-02-27"
@@ -1346,6 +1383,12 @@ when = "2024-04-18"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmprinter]]
+version = "0.206.0"
+when = "2024-04-25"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmtime]]
 version = "18.0.2"
 when = "2024-02-28"
@@ -1664,6 +1707,12 @@ when = "2024-04-18"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wast]]
+version = "206.0.0"
+when = "2024-04-25"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wat]]
 version = "1.201.0"
 when = "2024-02-27"
@@ -1694,6 +1743,12 @@ when = "2024-04-18"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wat]]
+version = "1.206.0"
+when = "2024-04-25"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wiggle]]
 version = "18.0.2"
 when = "2024-02-28"
@@ -1994,6 +2049,12 @@ when = "2024-04-18"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wit-component]]
+version = "0.206.0"
+when = "2024-04-25"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wit-parser]]
 version = "0.201.0"
 when = "2024-02-27"
@@ -2024,6 +2085,12 @@ when = "2024-04-18"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wit-parser]]
+version = "0.206.0"
+when = "2024-04-25"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.witx]]
 version = "0.9.1"
 when = "2021-06-22"
diff --git a/winch/codegen/src/codegen/env.rs b/winch/codegen/src/codegen/env.rs
index 6818c30f8..a571f2b38 100644
--- a/winch/codegen/src/codegen/env.rs
+++ b/winch/codegen/src/codegen/env.rs
@@ -198,7 +198,7 @@ impl<'a, 'translation, 'data, P: PtrSize> FuncEnv<'a, 'translation, 'data, P> {
             }
             FuncType(idx) => {
                 let sig_index = self.translation.module.types[TypeIndex::from_u32(idx)];
-                let sig = &self.types[sig_index];
+                let sig = self.types[sig_index].unwrap_func();
                 BlockSig::new(control::BlockType::func(sig.clone()))
             }
         }
@@ -340,7 +340,7 @@ impl<'a, 'translation, 'data, P: PtrSize> FuncEnv<'a, 'translation, 'data, P> {
             Callee::FuncRef(idx) => {
                 let val = || {
                     let sig_index = self.translation.module.types[*idx];
-                    let ty = &self.types[sig_index];
+                    let ty = self.types[sig_index].unwrap_func();
                     let sig = wasm_sig::<A>(ty);
                     sig
                 };


Test patch:
diff --git a/cranelift/filetests/filetests/isa/s390x/call.clif b/cranelift/filetests/filetests/isa/s390x/call.clif
index 1d72253b1..41bf58ea7 100644
--- a/cranelift/filetests/filetests/isa/s390x/call.clif
+++ b/cranelift/filetests/filetests/isa/s390x/call.clif
@@ -332,14 +332,14 @@ block0(v0: i128, v1: i128, v2: i128, v3: i128, v4: i128, v5: i128, v6: i128, v7:
 ;   vl %v24, 0(%r5)
 ;   lg %r4, 184(%r15)
 ;   vl %v27, 0(%r4)
-;   vaq %v16, %v1, %v3
-;   vaq %v17, %v5, %v7
-;   vaq %v18, %v18, %v21
-;   vaq %v19, %v24, %v27
-;   vaq %v16, %v16, %v17
-;   vaq %v17, %v18, %v19
-;   vaq %v16, %v16, %v17
-;   vst %v16, 0(%r2)
+;   vaq %v4, %v1, %v3
+;   vaq %v5, %v5, %v7
+;   vaq %v6, %v18, %v21
+;   vaq %v7, %v24, %v27
+;   vaq %v4, %v4, %v5
+;   vaq %v5, %v6, %v7
+;   vaq %v4, %v4, %v5
+;   vst %v4, 0(%r2)
 ;   br %r14
 ;
 ; Disassembled:
@@ -356,14 +356,14 @@ block0(v0: i128, v1: i128, v2: i128, v3: i128, v4: i128, v5: i128, v6: i128, v7:
 ;   vl %v24, 0(%r5)
 ;   lg %r4, 0xb8(%r15)
 ;   vl %v27, 0(%r4)
-;   vaq %v16, %v1, %v3
-;   vaq %v17, %v5, %v7
-;   vaq %v18, %v18, %v21
-;   vaq %v19, %v24, %v27
-;   vaq %v16, %v16, %v17
-;   vaq %v17, %v18, %v19
-;   vaq %v16, %v16, %v17
-;   vst %v16, 0(%r2)
+;   vaq %v4, %v1, %v3
+;   vaq %v5, %v5, %v7
+;   vaq %v6, %v18, %v21
+;   vaq %v7, %v24, %v27
+;   vaq %v4, %v4, %v5
+;   vaq %v5, %v6, %v7
+;   vaq %v4, %v4, %v5
+;   vst %v4, 0(%r2)
 ;   br %r14
 
 function %call_sret() -> i64 {
diff --git a/crates/test-programs/Cargo.toml b/crates/test-programs/Cargo.toml
index 86854364f..eae086c8a 100644
--- a/crates/test-programs/Cargo.toml
+++ b/crates/test-programs/Cargo.toml
@@ -10,7 +10,7 @@ license = "Apache-2.0 WITH LLVM-exception"
 workspace = true
 
 [dependencies]
-anyhow = { workspace = true }
+anyhow = { workspace = true, features = ['std'] }
 wasi = "0.11.0"
 wasi-nn = "0.6.0"
 wit-bindgen = { workspace = true, features = ['default'] }
diff --git a/tests/all/externals.rs b/tests/all/externals.rs
index be89ef934..d6e5878a8 100644
--- a/tests/all/externals.rs
+++ b/tests/all/externals.rs
@@ -433,7 +433,7 @@ fn dummy_funcs_and_subtypes(
         None,
         Some(ValType::Ref(RefType::new(
             true,
-            HeapType::Concrete(FuncType::new(&engine, None, None)),
+            HeapType::ConcreteFunc(FuncType::new(&engine, None, None)),
         ))),
     );
     let b = Func::new(
diff --git a/tests/all/func.rs b/tests/all/func.rs
index 30cebd6d4..ff69e172a 100644
--- a/tests/all/func.rs
+++ b/tests/all/func.rs
@@ -1773,7 +1773,10 @@ fn call_wasm_passing_subtype_func_param() -> anyhow::Result<()> {
     let h_ty = FuncType::new(
         &engine,
         None,
-        Some(ValType::Ref(RefType::new(true, HeapType::Concrete(g_ty)))),
+        Some(ValType::Ref(RefType::new(
+            true,
+            HeapType::ConcreteFunc(g_ty),
+        ))),
     );
     let h = Func::new(&mut store, h_ty, move |_caller, _params, results| {
         results[0] = Val::FuncRef(Some(g.clone()));
diff --git a/tests/all/linker.rs b/tests/all/linker.rs
index 16722af2b..222f7f38a 100644
--- a/tests/all/linker.rs
+++ b/tests/all/linker.rs
@@ -461,13 +461,13 @@ fn linker_instantiate_with_concrete_func_refs() -> Result<()> {
     )?;
 
     let a = FuncType::new(&engine, None, Some(ValType::I32));
-    let ref_null_a = ValType::from(RefType::new(true, HeapType::Concrete(a.clone())));
+    let ref_null_a = ValType::from(RefType::new(true, HeapType::ConcreteFunc(a.clone())));
 
     let b = FuncType::new(&engine, None, Some(ref_null_a));
-    let ref_null_b = ValType::from(RefType::new(true, HeapType::Concrete(b.clone())));
+    let ref_null_b = ValType::from(RefType::new(true, HeapType::ConcreteFunc(b.clone())));
 
     let c = FuncType::new(&engine, None, Some(ref_null_b));
-    let ref_null_c = ValType::from(RefType::new(true, HeapType::Concrete(c.clone())));
+    let ref_null_c = ValType::from(RefType::new(true, HeapType::ConcreteFunc(c.clone())));
 
     let mut store = Store::new(&engine, ());
     let a_func = Func::new(&mut store, a, |_caller, _args, results| {
@@ -540,7 +540,7 @@ fn linker_defines_func_subtype() -> Result<()> {
         |_caller, _args, _results| Ok(()),
     )?;
     let nop_ty = FuncType::new(&engine, None, None);
-    let ref_null_nop = ValType::from(RefType::new(true, HeapType::Concrete(nop_ty)));
+    let ref_null_nop = ValType::from(RefType::new(true, HeapType::ConcreteFunc(nop_ty)));
     linker.func_new(
         "env",
         "h",
@@ -676,7 +676,7 @@ fn linker_defines_global_subtype_mut_err() -> Result<()> {
     // Not mutable.
     let mut linker = Linker::new(&engine);
     let nop = FuncType::new(&engine, None, None);
-    let ref_null_nop = ValType::from(RefType::new(true, HeapType::Concrete(nop)));
+    let ref_null_nop = ValType::from(RefType::new(true, HeapType::ConcreteFunc(nop)));
     let g = Global::new(
         &mut store,
         GlobalType::new(ref_null_nop, Mutability::Const),