Repository: bytecodealliance/wasmtime
Pull request #8056 (2024-03-07 01:59:50)

cranelift: Add a winch calling convention
Add a `Winch` calling convention to cranelift that has no callee saves, and clobbers everything. This will enable us to use the `wasmtime-cranelift` generated trampolines in the `wasmtime-winch` crate.

I've enabled fuzzing for this calling convention, as well as added clif tests for the `x64` and `aarch64` backends. I've explicitly disabled support in `riscv64` and `s390x`, as it's not currently possible to target either of those architectures with winch.

<!--
Please make sure you include the following information:

- If this work has been discussed elsewhere, please include a link to that
  conversation. If it was discussed in an issue, just mention "issue #...".

- Explain why this change is needed. If the details are in an issue already,
  this can be brief.

Our development process is documented in the Wasmtime book:
https://docs.wasmtime.dev/contributing-development-process.html

Please ensure all communication follows the code of conduct:
https://github.com/bytecodealliance/wasmtime/blob/main/CODE_OF_CONDUCT.md
-->



Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 5b7d43e2b..ff529f58a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1735,6 +1735,15 @@ dependencies = [
  "libc",
 ]
 
+[[package]]
+name = "mach2"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709"
+dependencies = [
+ "libc",
+]
+
 [[package]]
 name = "matchers"
 version = "0.1.0"
@@ -3643,7 +3652,7 @@ dependencies = [
  "indexmap 2.0.0",
  "libc",
  "log",
- "mach",
+ "mach2",
  "memfd",
  "memoffset",
  "once_cell",
diff --git a/cranelift/codegen/src/egraph.rs b/cranelift/codegen/src/egraph.rs
index 2b5db33b3..8fa09e522 100644
--- a/cranelift/codegen/src/egraph.rs
+++ b/cranelift/codegen/src/egraph.rs
@@ -15,6 +15,7 @@ use crate::opts::IsleContext;
 use crate::scoped_hash_map::{Entry as ScopedEntry, ScopedHashMap};
 use crate::trace;
 use crate::unionfind::UnionFind;
+use core::cmp::Ordering;
 use cranelift_control::ControlPlane;
 use cranelift_entity::packed_option::ReservedValue;
 use cranelift_entity::SecondaryMap;
@@ -83,9 +84,11 @@ where
     pub(crate) value_to_opt_value: &'opt mut SecondaryMap<Value, Value>,
     pub(crate) gvn_map: &'opt mut CtxHashMap<(Type, InstructionData), Value>,
     pub(crate) effectful_gvn_map: &'opt mut ScopedHashMap<(Type, InstructionData), Value>,
+    available_block: &'opt mut SecondaryMap<Value, Block>,
     pub(crate) eclasses: &'opt mut UnionFind<Value>,
     pub(crate) remat_values: &'opt mut FxHashSet<Value>,
     pub(crate) stats: &'opt mut Stats,
+    domtree: &'opt DominatorTreePreorder,
     pub(crate) alias_analysis: &'opt mut AliasAnalysis<'analysis>,
     pub(crate) alias_analysis_state: &'opt mut LastStores,
     ctrl_plane: &'opt mut ControlPlane,
@@ -166,14 +169,19 @@ where
             if let NewOrExistingInst::Existing(inst) = inst {
                 debug_assert_eq!(self.func.dfg.inst_results(inst).len(), 1);
                 let result = self.func.dfg.first_result(inst);
+                debug_assert!(
+                    self.domtree.dominates(
+                        self.available_block[orig_result],
+                        self.get_available_block(inst)
+                    ),
+                    "GVN shouldn't replace {result} (available in {}) with non-dominating {orig_result} (available in {})",
+                    self.get_available_block(inst),
+                    self.available_block[orig_result],
+                );
                 self.value_to_opt_value[result] = orig_result;
-                self.eclasses.union(result, orig_result);
                 self.func.dfg.merge_facts(result, orig_result);
-                self.stats.union += 1;
-                result
-            } else {
-                orig_result
             }
+            orig_result
         } else {
             // Now actually insert the InstructionData and attach
             // result value (exactly one).
@@ -195,6 +203,7 @@ where
                 }
             };
 
+            self.available_block[result] = self.get_available_block(inst);
             let opt_value = self.optimize_pure_enode(inst);
 
             for &argument in self.func.dfg.inst_args(inst) {
@@ -212,6 +221,45 @@ where
         }
     }
 
+    /// Find the block where a pure instruction first becomes available,
+    /// defined as the block that is closest to the root where all of
+    /// its arguments are available. In the unusual case where a pure
+    /// instruction has no arguments (e.g. get_return_address), we can
+    /// place it anywhere, so it is available in the entry block.
+    ///
+    /// This function does not compute available blocks recursively.
+    /// All of the instruction's arguments must have had their available
+    /// blocks assigned already.
+    fn get_available_block(&self, inst: Inst) -> Block {
+        // Side-effecting instructions have different rules for where
+        // they become available, so this function does not apply.
+        debug_assert!(is_pure_for_egraph(self.func, inst));
+
+        // Note that the def-point of all arguments to an instruction
+        // in SSA lie on a line of direct ancestors in the domtree, and
+        // so do their available-blocks. This means that for any pair of
+        // arguments, their available blocks are either the same or one
+        // strictly dominates the other. We just need to find any argument
+        // whose available block is deepest in the domtree.
+        self.func.dfg.insts[inst]
+            .arguments(&self.func.dfg.value_lists)
+            .iter()
+            .map(|&v| {
+                let block = self.available_block[v];
+                debug_assert!(!block.is_reserved_value());
+                block
+            })
+            .max_by(|&x, &y| {
+                if self.domtree.dominates(x, y) {
+                    Ordering::Less
+                } else {
+                    debug_assert!(self.domtree.dominates(y, x));
+                    Ordering::Greater
+                }
+            })
+            .unwrap_or(self.func.layout.entry_block().unwrap())
+    }
+
     /// Optimizes an enode by applying any matching mid-end rewrite
     /// rules (or store-to-load forwarding, which is a special case),
     /// unioning together all possible optimized (or rewritten) forms
@@ -251,6 +299,43 @@ where
             &mut optimized_values,
         );
 
+        optimized_values.push(orig_value);
+
+        // Remove any values from optimized_values that do not have
+        // the highest possible available block in the domtree, in
+        // O(n) time. This loop scans in reverse, establishing the
+        // loop invariant that all values at indices >= idx have the
+        // same available block, which is the best available block
+        // seen so far. Note that orig_value must also be removed if
+        // it isn't in the best block, so we push it above, which means
+        // optimized_values is never empty: there's always at least one
+        // value in best_block.
+        let mut best_block = self.available_block[*optimized_values.last().unwrap()];
+        for idx in (0..optimized_values.len() - 1).rev() {
+            // At the beginning of each iteration, there is a non-empty
+            // collection of values after idx, which are all available
+            // at best_block.
+            let this_block = self.available_block[optimized_values[idx]];
+            if this_block != best_block {
+                if self.domtree.dominates(this_block, best_block) {
+                    // If the available block for this value dominates
+                    // the best block we've seen so far, discard all
+                    // the values we already checked and leave only this
+                    // value in the tail of the vector.
+                    optimized_values.truncate(idx + 1);
+                    best_block = this_block;
+                } else {
+                    // Otherwise the tail of the vector contains values
+                    // which are all better than this value, so we can
+                    // swap any of them in place of this value to delete
+                    // this one in O(1) time.
+                    debug_assert!(self.domtree.dominates(best_block, this_block));
+                    optimized_values.swap_remove(idx);
+                    debug_assert!(optimized_values.len() > idx);
+                }
+            }
+        }
+
         // It's not supposed to matter what order `simplify` returns values in.
         self.ctrl_plane.shuffle(&mut optimized_values);
 
@@ -268,7 +353,7 @@ where
         // Create a union of all new values with the original (or
         // maybe just one new value marked as "subsuming" the
         // original, if present.)
-        let mut union_value = orig_value;
+        let mut union_value = optimized_values.pop().unwrap();
         for optimized_value in optimized_values.drain(..) {
             trace!(
                 "Returned from ISLE for {}, got {:?}",
@@ -291,6 +376,7 @@ where
 
             let old_union_value = union_value;
             union_value = self.func.dfg.union(old_union_value, optimized_value);
+            self.available_block[union_value] = best_block;
             self.stats.union += 1;
             trace!(" -> union: now {}", union_value);
             self.eclasses.add(union_value);
@@ -314,6 +400,10 @@ where
     fn optimize_skeleton_inst(&mut self, inst: Inst) -> bool {
         self.stats.skeleton_inst += 1;
 
+        for &result in self.func.dfg.inst_results(inst) {
+            self.available_block[result] = self.func.layout.inst_block(inst).unwrap();
+        }
+
         // First, can we try to deduplicate? We need to keep some copy
         // of the instruction around because it's side-effecting, but
         // we may be able to reuse an earlier instance of it.
@@ -340,7 +430,6 @@ where
                     let orig_result = *o.get();
                     // Hit in GVN map -- reuse value.
                     self.value_to_opt_value[result] = orig_result;
-                    self.eclasses.union(orig_result, result);
                     trace!(" -> merges result {} to {}", result, orig_result);
                     true
                 }
@@ -486,6 +575,20 @@ impl<'a> EgraphPass<'a> {
         let mut effectful_gvn_map: ScopedHashMap<(Type, InstructionData), Value> =
             ScopedHashMap::new();
 
+        // We assign an "available block" to every value. Values tied to
+        // the side-effecting skeleton are available in the block where
+        // they're defined. Results from pure instructions could legally
+        // float up the domtree so they are available as soon as all
+        // their arguments are available. Values which identify union
+        // nodes are available in the same block as all values in the
+        // eclass, enforced by optimize_pure_enode.
+        let mut available_block: SecondaryMap<Value, Block> =
+            SecondaryMap::with_default(Block::reserved_value());
+        // This is an initial guess at the size we'll need, but we add
+        // more values as we build simplified alternative expressions so
+        // this is likely to realloc again later.
+        available_block.resize(cursor.func.dfg.num_values());
+
         // In domtree preorder, visit blocks. (TODO: factor out an
         // iterator from this and elaborator.)
         let root = cursor.layout().entry_block().unwrap();
@@ -516,6 +619,7 @@ impl<'a> EgraphPass<'a> {
                         trace!("creating initial singleton eclass for blockparam {}", param);
                         self.eclasses.add(param);
                         value_to_opt_value[param] = param;
+                        available_block[param] = block;
                     }
                     while let Some(inst) = cursor.next_inst() {
                         trace!("Processing inst {}", inst);
@@ -549,11 +653,13 @@ impl<'a> EgraphPass<'a> {
                             value_to_opt_value: &mut value_to_opt_value,
                             gvn_map: &mut gvn_map,
                             effectful_gvn_map: &mut effectful_gvn_map,
+                            available_block: &mut available_block,
                             eclasses: &mut self.eclasses,
                             rewrite_depth: 0,
                             subsume_values: FxHashSet::default(),
                             remat_values: &mut self.remat_values,
                             stats: &mut self.stats,
+                            domtree: &self.domtree,
                             alias_analysis: self.alias_analysis,
                             alias_analysis_state: &mut alias_analysis_state,
                             ctrl_plane: self.ctrl_plane,
diff --git a/cranelift/codegen/src/isa/aarch64/abi.rs b/cranelift/codegen/src/isa/aarch64/abi.rs
index 518c9ccdd..dcd7e947b 100644
--- a/cranelift/codegen/src/isa/aarch64/abi.rs
+++ b/cranelift/codegen/src/isa/aarch64/abi.rs
@@ -1106,10 +1106,10 @@ impl ABIMachineSpec for AArch64MachineDeps {
     }
 
     fn get_regs_clobbered_by_call(call_conv_of_callee: isa::CallConv) -> PRegSet {
-        if call_conv_of_callee == isa::CallConv::Tail {
-            TAIL_CLOBBERS
-        } else {
-            DEFAULT_AAPCS_CLOBBERS
+        match call_conv_of_callee {
+            isa::CallConv::Tail => ALL_CLOBBERS,
+            isa::CallConv::Winch => ALL_CLOBBERS,
+            _ => DEFAULT_AAPCS_CLOBBERS,
         }
     }
 
@@ -1299,7 +1299,7 @@ fn compute_arg_locs_tail<'a, I>(
 where
     I: IntoIterator<Item = &'a ir::AbiParam>,
 {
-    let mut xregs = TAIL_CLOBBERS
+    let mut xregs = ALL_CLOBBERS
         .into_iter()
         .filter(|r| r.class() == RegClass::Int)
         // We reserve `x0` for the return area pointer. For simplicity, we
@@ -1313,7 +1313,7 @@ where
         // indirect calls. So skip `x1` also, reserving it for that role.
         .skip(2);
 
-    let mut vregs = TAIL_CLOBBERS
+    let mut vregs = ALL_CLOBBERS
         .into_iter()
         .filter(|r| r.class() == RegClass::Float);
 
@@ -1429,7 +1429,7 @@ fn is_reg_saved_in_prologue(
     sig: &Signature,
     r: RealReg,
 ) -> bool {
-    if call_conv == isa::CallConv::Tail {
+    if call_conv == isa::CallConv::Tail || call_conv == isa::CallConv::Winch {
         return false;
     }
 
@@ -1542,8 +1542,8 @@ const fn default_aapcs_clobbers() -> PRegSet {
 
 const DEFAULT_AAPCS_CLOBBERS: PRegSet = default_aapcs_clobbers();
 
-// NB: The `tail` calling convention clobbers all allocatable registers.
-const TAIL_CLOBBERS: PRegSet = PRegSet::empty()
+// For calling conventions that clobber all registers.
+const ALL_CLOBBERS: PRegSet = PRegSet::empty()
     .with(xreg_preg(0))
     .with(xreg_preg(1))
     .with(xreg_preg(2))
diff --git a/cranelift/codegen/src/isa/call_conv.rs b/cranelift/codegen/src/isa/call_conv.rs
index fea9ff0a4..231962429 100644
--- a/cranelift/codegen/src/isa/call_conv.rs
+++ b/cranelift/codegen/src/isa/call_conv.rs
@@ -33,6 +33,12 @@ pub enum CallConv {
     /// FIXME: remove this when Wasmtime uses the "tail" calling convention for
     /// all wasm functions.
     WasmtimeSystemV,
+    /// The winch calling convention, not ABI-stable.
+    ///
+    /// The main difference to WasmtimeSystemV is that the winch calling
+    /// convention defines no callee-save registers, and restricts the number
+    /// of return registers to one integer, and one floating point.
+    Winch,
 }
 
 impl CallConv {
@@ -97,6 +103,7 @@ impl fmt::Display for CallConv {
             Self::AppleAarch64 => "apple_aarch64",
             Self::Probestack => "probestack",
             Self::WasmtimeSystemV => "wasmtime_system_v",
+            Self::Winch => "winch",
         })
     }
 }
@@ -113,6 +120,7 @@ impl str::FromStr for CallConv {
             "apple_aarch64" => Ok(Self::AppleAarch64),
             "probestack" => Ok(Self::Probestack),
             "wasmtime_system_v" => Ok(Self::WasmtimeSystemV),
+            "winch" => Ok(Self::Winch),
             _ => Err(()),
         }
     }
diff --git a/cranelift/codegen/src/isa/riscv64/abi.rs b/cranelift/codegen/src/isa/riscv64/abi.rs
index ab80aa06a..0f0d32e8f 100644
--- a/cranelift/codegen/src/isa/riscv64/abi.rs
+++ b/cranelift/codegen/src/isa/riscv64/abi.rs
@@ -98,6 +98,12 @@ impl ABIMachineSpec for Riscv64MachineDeps {
     where
         I: IntoIterator<Item = &'a ir::AbiParam>,
     {
+        assert_ne!(
+            call_conv,
+            isa::CallConv::Winch,
+            "riscv64 does not support the 'winch' calling convention yet"
+        );
+
         // All registers that can be used as parameters or rets.
         // both start and end are included.
         let (x_start, x_end, f_start, f_end) = match (call_conv, args_or_rets) {
diff --git a/cranelift/codegen/src/isa/s390x/abi.rs b/cranelift/codegen/src/isa/s390x/abi.rs
index 28ca3fcfa..af34bb5a4 100644
--- a/cranelift/codegen/src/isa/s390x/abi.rs
+++ b/cranelift/codegen/src/isa/s390x/abi.rs
@@ -236,6 +236,11 @@ impl ABIMachineSpec for S390xMachineDeps {
             isa::CallConv::Tail,
             "s390x does not support the 'tail' calling convention yet"
         );
+        assert_ne!(
+            call_conv,
+            isa::CallConv::Winch,
+            "s390x does not support the 'winch' calling convention yet"
+        );
 
         let mut next_gpr = 0;
         let mut next_fpr = 0;
diff --git a/cranelift/codegen/src/isa/x64/abi.rs b/cranelift/codegen/src/isa/x64/abi.rs
index 4de60f6ec..eeda5b6cd 100644
--- a/cranelift/codegen/src/isa/x64/abi.rs
+++ b/cranelift/codegen/src/isa/x64/abi.rs
@@ -803,12 +803,11 @@ impl ABIMachineSpec for X64ABIMachineSpec {
     }
 
     fn get_regs_clobbered_by_call(call_conv_of_callee: isa::CallConv) -> PRegSet {
-        if call_conv_of_callee == isa::CallConv::Tail {
-            TAIL_CLOBBERS
-        } else if call_conv_of_callee.extends_windows_fastcall() {
-            WINDOWS_CLOBBERS
-        } else {
-            SYSV_CLOBBERS
+        match call_conv_of_callee {
+            isa::CallConv::Tail => ALL_CLOBBERS,
+            isa::CallConv::Winch => ALL_CLOBBERS,
+            _ if call_conv_of_callee.extends_windows_fastcall() => WINDOWS_CLOBBERS,
+            _ => SYSV_CLOBBERS,
         }
     }
 
@@ -833,6 +832,9 @@ impl ABIMachineSpec for X64ABIMachineSpec {
             // The `tail` calling convention doesn't have any callee-save
             // registers.
             CallConv::Tail => vec![],
+            // The `winch` calling convention doesn't have any callee-save
+            // registers.
+            CallConv::Winch => vec![],
             CallConv::Fast | CallConv::Cold | CallConv::SystemV => regs
                 .iter()
                 .cloned()
@@ -1068,6 +1070,10 @@ fn get_intreg_for_retval(
             1 => Some(regs::rdx()), // The Rust ABI for i128s needs this.
             _ => None,
         },
+        CallConv::Winch => match intreg_idx {
+            0 => Some(regs::rax()),
+            _ => None,
+        },
         CallConv::Probestack => todo!(),
         CallConv::WasmtimeSystemV | CallConv::AppleAarch64 => unreachable!(),
     }
@@ -1091,7 +1097,7 @@ fn get_fltreg_for_retval(call_conv: &CallConv, fltreg_idx: usize) -> Option<Reg>
             1 => Some(regs::xmm1()),
             _ => None,
         },
-        CallConv::WindowsFastcall => match fltreg_idx {
+        CallConv::WindowsFastcall | CallConv::Winch => match fltreg_idx {
             0 => Some(regs::xmm0()),
             _ => None,
         },
@@ -1152,7 +1158,7 @@ fn compute_clobber_size(clobbers: &[Writable<RealReg>]) -> u32 {
 
 const WINDOWS_CLOBBERS: PRegSet = windows_clobbers();
 const SYSV_CLOBBERS: PRegSet = sysv_clobbers();
-const TAIL_CLOBBERS: PRegSet = tail_clobbers();
+const ALL_CLOBBERS: PRegSet = all_clobbers();
 
 const fn windows_clobbers() -> PRegSet {
     PRegSet::empty()
@@ -1200,7 +1206,8 @@ const fn sysv_clobbers() -> PRegSet {
         .with(regs::fpr_preg(15))
 }
 
-const fn tail_clobbers() -> PRegSet {
+/// For calling conventions that clobber all registers.
+const fn all_clobbers() -> PRegSet {
     PRegSet::empty()
         .with(regs::gpr_preg(regs::ENC_RAX))
         .with(regs::gpr_preg(regs::ENC_RCX))
diff --git a/cranelift/codegen/src/machinst/abi.rs b/cranelift/codegen/src/machinst/abi.rs
index fce5634fb..3a91d01b4 100644
--- a/cranelift/codegen/src/machinst/abi.rs
+++ b/cranelift/codegen/src/machinst/abi.rs
@@ -1112,7 +1112,8 @@ impl<M: ABIMachineSpec> Callee<M> {
                 || call_conv == isa::CallConv::Cold
                 || call_conv.extends_windows_fastcall()
                 || call_conv == isa::CallConv::WasmtimeSystemV
-                || call_conv == isa::CallConv::AppleAarch64,
+                || call_conv == isa::CallConv::AppleAarch64
+                || call_conv == isa::CallConv::Winch,
             "Unsupported calling convention: {:?}",
             call_conv
         );
diff --git a/cranelift/fuzzgen/src/cranelift_arbitrary.rs b/cranelift/fuzzgen/src/cranelift_arbitrary.rs
index e52c887ea..1082c6071 100644
--- a/cranelift/fuzzgen/src/cranelift_arbitrary.rs
+++ b/cranelift/fuzzgen/src/cranelift_arbitrary.rs
@@ -64,6 +64,14 @@ impl<'a> CraneliftArbitrary for &mut Unstructured<'a> {
             allowed_callconvs.push(CallConv::Tail);
         }
 
+        // The winch calling convention is supposed to work on x64 and aarch64
+        if matches!(
+            architecture,
+            Architecture::X86_64 | Architecture::Aarch64(_)
+        ) {
+            allowed_callconvs.push(CallConv::Winch);
+        }
+
         Ok(*self.choose(&allowed_callconvs[..])?)
     }
 
diff --git a/crates/c-api/include/wasmtime.h b/crates/c-api/include/wasmtime.h
index cb092ade7..a9051d7c9 100644
--- a/crates/c-api/include/wasmtime.h
+++ b/crates/c-api/include/wasmtime.h
@@ -193,6 +193,7 @@
 #include <wasmtime/memory.h>
 #include <wasmtime/module.h>
 #include <wasmtime/profiling.h>
+#include <wasmtime/sharedmemory.h>
 #include <wasmtime/store.h>
 #include <wasmtime/table.h>
 #include <wasmtime/trap.h>
diff --git a/crates/c-api/include/wasmtime/extern.h b/crates/c-api/include/wasmtime/extern.h
index 427c5ba7f..7cb058b7d 100644
--- a/crates/c-api/include/wasmtime/extern.h
+++ b/crates/c-api/include/wasmtime/extern.h
@@ -85,6 +85,11 @@ typedef uint8_t wasmtime_extern_kind_t;
 /// \brief Value of #wasmtime_extern_kind_t meaning that #wasmtime_extern_t is a
 /// memory
 #define WASMTIME_EXTERN_MEMORY 3
+/// \brief Value of #wasmtime_extern_kind_t meaning that #wasmtime_extern_t is a
+/// shared memory
+#define WASMTIME_EXTERN_SHAREDMEMORY 4
+
+struct wasmtime_sharedmemory;
 
 /**
  * \typedef wasmtime_extern_union_t
@@ -105,6 +110,8 @@ typedef union wasmtime_extern_union {
   wasmtime_table_t table;
   /// Field used if #wasmtime_extern_t::kind is #WASMTIME_EXTERN_MEMORY
   wasmtime_memory_t memory;
+  /// Field used if #wasmtime_extern_t::kind is #WASMTIME_EXTERN_SHAREDMEMORY
+  struct wasmtime_sharedmemory *sharedmemory;
 } wasmtime_extern_union_t;
 
 /**
diff --git a/crates/c-api/include/wasmtime/linker.h b/crates/c-api/include/wasmtime/linker.h
index c70423495..da07db30b 100644
--- a/crates/c-api/include/wasmtime/linker.h
+++ b/crates/c-api/include/wasmtime/linker.h
@@ -42,6 +42,15 @@ typedef struct wasmtime_linker wasmtime_linker_t;
  */
 WASM_API_EXTERN wasmtime_linker_t *wasmtime_linker_new(wasm_engine_t *engine);
 
+/**
+ * \brief Clones existing linker.
+ *
+ * This function does not take ownership of the linker argument, and the caller
+ * is expected to delete the returned linker.
+ */
+WASM_API_EXTERN wasmtime_linker_t *
+wasmtime_linker_clone(wasmtime_linker_t *linker);
+
 /**
  * \brief Deletes a linker
  */
diff --git a/crates/c-api/include/wasmtime/memory.h b/crates/c-api/include/wasmtime/memory.h
index a22b93c97..53e82fcc6 100644
--- a/crates/c-api/include/wasmtime/memory.h
+++ b/crates/c-api/include/wasmtime/memory.h
@@ -54,6 +54,11 @@ WASM_API_EXTERN bool wasmtime_memorytype_maximum(const wasm_memorytype_t *ty,
  */
 WASM_API_EXTERN bool wasmtime_memorytype_is64(const wasm_memorytype_t *ty);
 
+/**
+ * \brief Returns whether this type of memory represents a shared memory.
+ */
+WASM_API_EXTERN bool wasmtime_memorytype_isshared(const wasm_memorytype_t *ty);
+
 /**
  * \brief Creates a new WebAssembly linear memory
  *
diff --git a/crates/c-api/include/wasmtime/sharedmemory.h b/crates/c-api/include/wasmtime/sharedmemory.h
new file mode 100644
index 000000000..0c68d7403
--- /dev/null
+++ b/crates/c-api/include/wasmtime/sharedmemory.h
@@ -0,0 +1,115 @@
+/**
+ * \file wasmtime/sharedmemory.h
+ *
+ * Wasmtime API for interacting with wasm shared memories.
+ */
+
+#ifndef WASMTIME_SHAREDMEMORY_H
+#define WASMTIME_SHAREDMEMORY_H
+
+#include <wasm.h>
+#include <wasmtime/error.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief Interface for shared memories.
+ *
+ * For more information see the Rust documentation at:
+ * https://docs.wasmtime.dev/api/wasmtime/struct.SharedMemory.html
+ */
+typedef struct wasmtime_sharedmemory wasmtime_sharedmemory_t;
+
+/**
+ * \brief Creates a new WebAssembly shared linear memory
+ *
+ * \param engine engine that created shared memory is associated with
+ * \param ty the type of the memory to create
+ * \param ret where to store the returned memory
+ *
+ * If an error happens when creating the memory it's returned and owned by the
+ * caller. If an error happens then `ret` is not filled in.
+ */
+WASM_API_EXTERN wasmtime_error_t *
+wasmtime_sharedmemory_new(const wasm_engine_t *engine,
+                          const wasm_memorytype_t *ty,
+                          wasmtime_sharedmemory_t **ret);
+
+/**
+ * \brief Deletes shared linear memory
+ *
+ * \param memory memory to be deleted
+ */
+WASM_API_EXTERN void
+wasmtime_sharedmemory_delete(wasmtime_sharedmemory_t *memory);
+
+/**
+ * \brief Clones shared linear memory
+ *
+ * \param memory memory to be cloned
+ *
+ * This function makes shallow clone, ie. copy of reference counted
+ * memory handle.
+ */
+WASM_API_EXTERN wasmtime_sharedmemory_t *
+wasmtime_sharedmemory_clone(const wasmtime_sharedmemory_t *memory);
+
+/**
+ * \brief Moves shared linear memory into wasmtime_extern_t object
+ *
+ * \param memory memory to be moved
+ * \param out where to store resulting `wasmtime_extern_t`
+ *
+ * This function moves ownership of `memory` into resulting `wasmtime_extern_t`.
+ */
+WASM_API_EXTERN void
+wasmtime_sharedmemory_into_extern(wasmtime_sharedmemory_t *memory,
+                                  wasmtime_extern_t *out);
+
+/**
+ * \brief Returns the type of the shared memory specified
+ */
+WASM_API_EXTERN wasm_memorytype_t *
+wasmtime_sharedmemory_type(const wasmtime_sharedmemory_t *memory);
+
+/**
+ * \brief Returns the base pointer in memory where
+          the shared linear memory starts.
+ */
+WASM_API_EXTERN uint8_t *
+wasmtime_sharedmemory_data(const wasmtime_sharedmemory_t *memory);
+
+/**
+ * \brief Returns the byte length of this shared linear memory.
+ */
+WASM_API_EXTERN size_t
+wasmtime_sharedmemory_data_size(const wasmtime_sharedmemory_t *memory);
+
+/**
+ * \brief Returns the length, in WebAssembly pages, of this shared linear memory
+ */
+WASM_API_EXTERN uint64_t
+wasmtime_sharedmemory_size(const wasmtime_sharedmemory_t *memory);
+
+/**
+ * \brief Attempts to grow the specified shared memory by `delta` pages.
+ *
+ * \param memory the memory to grow
+ * \param delta the number of pages to grow by
+ * \param prev_size where to store the previous size of memory
+ *
+ * If memory cannot be grown then `prev_size` is left unchanged and an error is
+ * returned. Otherwise `prev_size` is set to the previous size of the memory, in
+ * WebAssembly pages, and `NULL` is returned.
+ */
+WASM_API_EXTERN wasmtime_error_t *
+wasmtime_sharedmemory_grow(const wasmtime_sharedmemory_t *memory,
+                           uint64_t delta, uint64_t *prev_size);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // WASMTIME_SHAREDMEMORY_H
diff --git a/crates/c-api/src/extern.rs b/crates/c-api/src/extern.rs
index b97ba1f19..3fbf1d0cd 100644
--- a/crates/c-api/src/extern.rs
+++ b/crates/c-api/src/extern.rs
@@ -3,7 +3,7 @@ use crate::{
     CStoreContext, StoreRef,
 };
 use std::mem::ManuallyDrop;
-use wasmtime::{Extern, Func, Global, Memory, Table};
+use wasmtime::{Extern, Func, Global, Memory, SharedMemory, Table};
 
 #[derive(Clone)]
 pub struct wasm_extern_t {
@@ -82,6 +82,7 @@ pub const WASMTIME_EXTERN_FUNC: wasmtime_extern_kind_t = 0;
 pub const WASMTIME_EXTERN_GLOBAL: wasmtime_extern_kind_t = 1;
 pub const WASMTIME_EXTERN_TABLE: wasmtime_extern_kind_t = 2;
 pub const WASMTIME_EXTERN_MEMORY: wasmtime_extern_kind_t = 3;
+pub const WASMTIME_EXTERN_SHAREDMEMORY: wasmtime_extern_kind_t = 4;
 
 #[repr(C)]
 pub union wasmtime_extern_union {
@@ -89,6 +90,17 @@ pub union wasmtime_extern_union {
     pub table: Table,
     pub global: Global,
     pub memory: Memory,
+    pub sharedmemory: ManuallyDrop<Box<SharedMemory>>,
+}
+
+impl Drop for wasmtime_extern_t {
+    fn drop(&mut self) {
+        if self.kind == WASMTIME_EXTERN_SHAREDMEMORY {
+            unsafe {
+                ManuallyDrop::drop(&mut self.of.sharedmemory);
+            }
+        }
+    }
 }
 
 impl wasmtime_extern_t {
@@ -98,7 +110,8 @@ impl wasmtime_extern_t {
             WASMTIME_EXTERN_GLOBAL => Extern::Global(self.of.global),
             WASMTIME_EXTERN_TABLE => Extern::Table(self.of.table),
             WASMTIME_EXTERN_MEMORY => Extern::Memory(self.of.memory),
-            other => panic!("unknown wasm_extern_kind_t: {}", other),
+            WASMTIME_EXTERN_SHAREDMEMORY => Extern::SharedMemory((**self.of.sharedmemory).clone()),
+            other => panic!("unknown wasmtime_extern_kind_t: {}", other),
         }
     }
 }
@@ -122,7 +135,12 @@ impl From<Extern> for wasmtime_extern_t {
                 kind: WASMTIME_EXTERN_MEMORY,
                 of: wasmtime_extern_union { memory },
             },
-            Extern::SharedMemory(_memory) => todo!(),
+            Extern::SharedMemory(sharedmemory) => wasmtime_extern_t {
+                kind: WASMTIME_EXTERN_SHAREDMEMORY,
+                of: wasmtime_extern_union {
+                    sharedmemory: ManuallyDrop::new(Box::new(sharedmemory)),
+                },
+            },
         }
     }
 }
diff --git a/crates/c-api/src/lib.rs b/crates/c-api/src/lib.rs
index a8f2bca41..fbf3ab287 100644
--- a/crates/c-api/src/lib.rs
+++ b/crates/c-api/src/lib.rs
@@ -29,6 +29,7 @@ mod module;
 #[cfg(feature = "profiling")]
 mod profiling;
 mod r#ref;
+mod sharedmemory;
 mod store;
 mod table;
 mod trap;
diff --git a/crates/c-api/src/linker.rs b/crates/c-api/src/linker.rs
index 4ef4aaecf..0c2fd8a61 100644
--- a/crates/c-api/src/linker.rs
+++ b/crates/c-api/src/linker.rs
@@ -21,6 +21,13 @@ pub extern "C" fn wasmtime_linker_new(engine: &wasm_engine_t) -> Box<wasmtime_li
     })
 }
 
+#[no_mangle]
+pub extern "C" fn wasmtime_linker_clone(linker: &wasmtime_linker_t) -> Box<wasmtime_linker_t> {
+    Box::new(wasmtime_linker_t {
+        linker: linker.linker.clone(),
+    })
+}
+
 #[no_mangle]
 pub extern "C" fn wasmtime_linker_allow_shadowing(
     linker: &mut wasmtime_linker_t,
diff --git a/crates/c-api/src/sharedmemory.rs b/crates/c-api/src/sharedmemory.rs
new file mode 100644
index 000000000..1521373c4
--- /dev/null
+++ b/crates/c-api/src/sharedmemory.rs
@@ -0,0 +1,68 @@
+use crate::{handle_result, wasm_engine_t, wasm_memorytype_t, wasmtime_error_t, wasmtime_extern_t};
+use std::cell::UnsafeCell;
+use std::mem::MaybeUninit;
+use wasmtime::{Extern, SharedMemory};
+
+type wasmtime_sharedmemory_t = SharedMemory;
+
+wasmtime_c_api_macros::declare_own!(wasmtime_sharedmemory_t);
+
+#[no_mangle]
+pub extern "C" fn wasmtime_sharedmemory_new(
+    engine: &wasm_engine_t,
+    ty: &wasm_memorytype_t,
+    ret: &mut *mut wasmtime_sharedmemory_t,
+) -> Option<Box<wasmtime_error_t>> {
+    handle_result(
+        SharedMemory::new(&engine.engine, ty.ty().ty.clone()),
+        |mem| *ret = Box::<wasmtime_sharedmemory_t>::into_raw(Box::new(mem)),
+    )
+}
+
+#[no_mangle]
+pub extern "C" fn wasmtime_sharedmemory_clone(
+    mem: &wasmtime_sharedmemory_t,
+) -> Box<wasmtime_sharedmemory_t> {
+    Box::new(mem.clone())
+}
+
+#[no_mangle]
+pub extern "C" fn wasmtime_sharedmemory_into_extern(
+    mem: Box<wasmtime_sharedmemory_t>,
+    ext: &mut MaybeUninit<wasmtime_extern_t>,
+) {
+    crate::initialize(ext, Extern::from(*mem).into());
+}
+
+#[no_mangle]
+pub extern "C" fn wasmtime_sharedmemory_type(
+    mem: &wasmtime_sharedmemory_t,
+) -> Box<wasm_memorytype_t> {
+    Box::new(wasm_memorytype_t::new(mem.ty()))
+}
+
+#[no_mangle]
+pub extern "C" fn wasmtime_sharedmemory_data(
+    mem: &wasmtime_sharedmemory_t,
+) -> *const UnsafeCell<u8> {
+    mem.data().as_ptr()
+}
+
+#[no_mangle]
+pub extern "C" fn wasmtime_sharedmemory_data_size(mem: &wasmtime_sharedmemory_t) -> usize {
+    mem.data().len()
+}
+
+#[no_mangle]
+pub extern "C" fn wasmtime_sharedmemory_size(mem: &wasmtime_sharedmemory_t) -> u64 {
+    mem.size()
+}
+
+#[no_mangle]
+pub extern "C" fn wasmtime_sharedmemory_grow(
+    mem: &wasmtime_sharedmemory_t,
+    delta: u64,
+    prev_size: &mut u64,
+) -> Option<Box<wasmtime_error_t>> {
+    handle_result(mem.grow(delta), |prev| *prev_size = prev)
+}
diff --git a/crates/c-api/src/types/memory.rs b/crates/c-api/src/types/memory.rs
index 034bea862..bcdb933d7 100644
--- a/crates/c-api/src/types/memory.rs
+++ b/crates/c-api/src/types/memory.rs
@@ -108,6 +108,11 @@ pub extern "C" fn wasmtime_memorytype_is64(mt: &wasm_memorytype_t) -> bool {
     mt.ty().ty.is_64()
 }
 
+#[no_mangle]
+pub extern "C" fn wasmtime_memorytype_isshared(mt: &wasm_memorytype_t) -> bool {
+    mt.ty().ty.is_shared()
+}
+
 #[no_mangle]
 pub extern "C" fn wasm_memorytype_as_externtype(ty: &wasm_memorytype_t) -> &wasm_externtype_t {
     &ty.ext
diff --git a/crates/c-api/src/wasi.rs b/crates/c-api/src/wasi.rs
index 7061eacc0..091bf5816 100644
--- a/crates/c-api/src/wasi.rs
+++ b/crates/c-api/src/wasi.rs
@@ -292,6 +292,14 @@ pub unsafe extern "C" fn wasi_config_preopen_socket(
     fd_num: u32,
     host_port: *const c_char,
 ) -> bool {
+    const VAR: &str = "WASMTIME_WASI_CONFIG_PREOPEN_SOCKET_ALLOW";
+    if std::env::var(VAR).is_err() {
+        panic!(
+            "wasmtime c-api: wasi_config_preopen_socket will be deprecated in the \
+            wasmtime 20.0.0 release. set {VAR} to enable temporarily"
+        );
+    }
+
     let address = match cstr_to_str(host_port) {
         Some(s) => s,
         None => return false,
diff --git a/crates/component-macro/src/component.rs b/crates/component-macro/src/component.rs
index 0f49ef07a..00d5ba83e 100644
--- a/crates/component-macro/src/component.rs
+++ b/crates/component-macro/src/component.rs
@@ -447,7 +447,7 @@ impl Expander for LiftExpander {
             if let Some(ty) = ty {
                 let payload_ty = match style {
                     VariantStyle::Variant => {
-                        quote!(ty.cases[#index].ty.unwrap_or_else(#internal::bad_type_info))
+                        quote!(ty.cases[#index].unwrap_or_else(#internal::bad_type_info))
                     }
                     VariantStyle::Enum => unreachable!(),
                 };
@@ -626,7 +626,7 @@ impl Expander for LowerExpander {
             if ty.is_some() {
                 let ty = match style {
                     VariantStyle::Variant => {
-                        quote!(ty.cases[#index].ty.unwrap_or_else(#internal::bad_type_info))
+                        quote!(ty.cases[#index].unwrap_or_else(#internal::bad_type_info))
                     }
                     VariantStyle::Enum => unreachable!(),
                 };
diff --git a/crates/environ/src/component/types.rs b/crates/environ/src/component/types.rs
index f873d86e3..489b5707e 100644
--- a/crates/environ/src/component/types.rs
+++ b/crates/environ/src/component/types.rs
@@ -8,7 +8,7 @@ use cranelift_entity::EntityRef;
 use indexmap::{IndexMap, IndexSet};
 use serde_derive::{Deserialize, Serialize};
 use std::collections::HashMap;
-use std::hash::Hash;
+use std::hash::{Hash, Hasher};
 use std::ops::Index;
 use wasmparser::names::KebabString;
 use wasmparser::types;
@@ -757,19 +757,20 @@ impl ComponentTypesBuilder {
                 if case.refines.is_some() {
                     bail!("refines is not supported at this time");
                 }
-                Ok(VariantCase {
-                    name: name.to_string(),
-                    ty: match &case.ty.as_ref() {
+                Ok((
+                    name.to_string(),
+                    match &case.ty.as_ref() {
                         Some(ty) => Some(self.valtype(types, ty)?),
                         None => None,
                     },
-                })
+                ))
             })
-            .collect::<Result<Box<[_]>>>()?;
-        let (info, abi) = VariantInfo::new(cases.iter().map(|c| {
-            c.ty.as_ref()
-                .map(|ty| self.component_types.canonical_abi(ty))
-        }));
+            .collect::<Result<IndexMap<_, _>>>()?;
+        let (info, abi) = VariantInfo::new(
+            cases
+                .iter()
+                .map(|(_, c)| c.as_ref().map(|ty| self.component_types.canonical_abi(ty))),
+        );
         Ok(self.add_variant_type(TypeVariant { cases, abi, info }))
     }
 
@@ -804,7 +805,10 @@ impl ComponentTypesBuilder {
     }
 
     fn enum_type(&mut self, variants: &IndexSet<KebabString>) -> TypeEnumIndex {
-        let names = variants.iter().map(|s| s.to_string()).collect::<Box<[_]>>();
+        let names = variants
+            .iter()
+            .map(|s| s.to_string())
+            .collect::<IndexSet<_>>();
         let (info, abi) = VariantInfo::new(names.iter().map(|_| None));
         self.add_enum_type(TypeEnum { names, abi, info })
     }
@@ -1485,24 +1489,23 @@ pub struct RecordField {
 /// Variants are close to Rust `enum` declarations where a value is one of many
 /// cases and each case has a unique name and an optional payload associated
 /// with it.
-#[derive(Serialize, Deserialize, Clone, Hash, Eq, PartialEq, Debug)]
+#[derive(Serialize, Deserialize, Clone, Eq, PartialEq, Debug)]
 pub struct TypeVariant {
     /// The list of cases that this variant can take.
-    pub cases: Box<[VariantCase]>,
+    pub cases: IndexMap<String, Option<InterfaceType>>,
     /// Byte information about this type in the canonical ABI.
     pub abi: CanonicalAbiInfo,
     /// Byte information about this variant type.
     pub info: VariantInfo,
 }
 
-/// One case of a `variant` type which contains the name of the variant as well
-/// as the payload.
-#[derive(Serialize, Deserialize, Clone, Hash, Eq, PartialEq, Debug)]
-pub struct VariantCase {
-    /// Name of the variant, unique amongst all cases in a variant.
-    pub name: String,
-    /// Optional type associated with this payload.
-    pub ty: Option<InterfaceType>,
+impl Hash for TypeVariant {
+    fn hash<H: Hasher>(&self, h: &mut H) {
+        let TypeVariant { cases, abi, info } = self;
+        cases.as_slice().hash(h);
+        abi.hash(h);
+        info.hash(h);
+    }
 }
 
 /// Shape of a "tuple" type in interface types.
@@ -1521,29 +1524,46 @@ pub struct TypeTuple {
 ///
 /// This can be thought of as a record-of-bools, although the representation is
 /// more efficient as bitflags.
-#[derive(Serialize, Deserialize, Clone, Hash, Eq, PartialEq, Debug)]
+#[derive(Serialize, Deserialize, Clone, Eq, PartialEq, Debug)]
 pub struct TypeFlags {
     /// The names of all flags, all of which are unique.
-    pub names: Box<[String]>,
+    pub names: IndexSet<String>,
     /// Byte information about this type in the canonical ABI.
     pub abi: CanonicalAbiInfo,
 }
 
+impl Hash for TypeFlags {
+    fn hash<H: Hasher>(&self, h: &mut H) {
+        let TypeFlags { names, abi } = self;
+        names.as_slice().hash(h);
+        abi.hash(h);
+    }
+}
+
 /// Shape of an "enum" type in interface types, not to be confused with a Rust
 /// `enum` type.
 ///
 /// In interface types enums are simply a bag of names, and can be seen as a
 /// variant where all payloads are `Unit`.
-#[derive(Serialize, Deserialize, Clone, Hash, Eq, PartialEq, Debug)]
+#[derive(Serialize, Deserialize, Clone, Eq, PartialEq, Debug)]
 pub struct TypeEnum {
     /// The names of this enum, all of which are unique.
-    pub names: Box<[String]>,
+    pub names: IndexSet<String>,
     /// Byte information about this type in the canonical ABI.
     pub abi: CanonicalAbiInfo,
     /// Byte information about this variant type.
     pub info: VariantInfo,
 }
 
+impl Hash for TypeEnum {
+    fn hash<H: Hasher>(&self, h: &mut H) {
+        let TypeEnum { names, abi, info } = self;
+        names.as_slice().hash(h);
+        abi.hash(h);
+        info.hash(h);
+    }
+}
+
 /// Shape of an "option" interface type.
 #[derive(Serialize, Deserialize, Clone, Hash, Eq, PartialEq, Debug)]
 pub struct TypeOption {
@@ -1909,7 +1929,7 @@ impl TypeInformation {
         self.build_variant(
             ty.cases
                 .iter()
-                .map(|c| c.ty.as_ref().map(|ty| types.type_information(ty))),
+                .map(|(_, c)| c.as_ref().map(|ty| types.type_information(ty))),
         )
     }
 
diff --git a/crates/environ/src/fact/trampoline.rs b/crates/environ/src/fact/trampoline.rs
index 5936de013..f85a60c6d 100644
--- a/crates/environ/src/fact/trampoline.rs
+++ b/crates/environ/src/fact/trampoline.rs
@@ -2179,25 +2179,29 @@ impl Compiler<'_, '_> {
             _ => panic!("expected a variant"),
         };
 
-        let src_info = variant_info(self.types, src_ty.cases.iter().map(|c| c.ty.as_ref()));
-        let dst_info = variant_info(self.types, dst_ty.cases.iter().map(|c| c.ty.as_ref()));
+        let src_info = variant_info(self.types, src_ty.cases.iter().map(|(_, c)| c.as_ref()));
+        let dst_info = variant_info(self.types, dst_ty.cases.iter().map(|(_, c)| c.as_ref()));
 
-        let iter = src_ty.cases.iter().enumerate().map(|(src_i, src_case)| {
-            let dst_i = dst_ty
-                .cases
-                .iter()
-                .position(|c| c.name == src_case.name)
-                .unwrap();
-            let dst_case = &dst_ty.cases[dst_i];
-            let src_i = u32::try_from(src_i).unwrap();
-            let dst_i = u32::try_from(dst_i).unwrap();
-            VariantCase {
-                src_i,
-                src_ty: src_case.ty.as_ref(),
-                dst_i,
-                dst_ty: dst_case.ty.as_ref(),
-            }
-        });
+        let iter = src_ty
+            .cases
+            .iter()
+            .enumerate()
+            .map(|(src_i, (src_case, src_case_ty))| {
+                let dst_i = dst_ty
+                    .cases
+                    .iter()
+                    .position(|(c, _)| c == src_case)
+                    .unwrap();
+                let dst_case_ty = &dst_ty.cases[dst_i];
+                let src_i = u32::try_from(src_i).unwrap();
+                let dst_i = u32::try_from(dst_i).unwrap();
+                VariantCase {
+                    src_i,
+                    src_ty: src_case_ty.as_ref(),
+                    dst_i,
+                    dst_ty: dst_case_ty.as_ref(),
+                }
+            });
         self.convert_variant(src, &src_info, dst, &dst_info, iter);
     }
 
diff --git a/crates/environ/src/tunables.rs b/crates/environ/src/tunables.rs
index 4ff360fcb..7328345ee 100644
--- a/crates/environ/src/tunables.rs
+++ b/crates/environ/src/tunables.rs
@@ -1,4 +1,6 @@
+use anyhow::{anyhow, bail, Result};
 use serde_derive::{Deserialize, Serialize};
+use target_lexicon::{PointerWidth, Triple};
 
 /// Tunable parameters for WebAssembly compilation.
 #[derive(Clone, Hash, Serialize, Deserialize, Debug)]
@@ -70,6 +72,18 @@ impl Tunables {
         }
     }
 
+    /// Returns the default set of tunables for the given target triple.
+    pub fn default_for_target(target: &Triple) -> Result<Self> {
+        match target
+            .pointer_width()
+            .map_err(|_| anyhow!("failed to retrieve target pointer width"))?
+        {
+            PointerWidth::U32 => Ok(Tunables::default_u32()),
+            PointerWidth::U64 => Ok(Tunables::default_u64()),
+            _ => bail!("unsupported target pointer width"),
+        }
+    }
+
     /// Returns the default set of tunables for running under MIRI.
     pub fn default_miri() -> Tunables {
         Tunables {
diff --git a/crates/fuzzing/src/generators/component_types.rs b/crates/fuzzing/src/generators/component_types.rs
index 80b2838b1..37e34e83e 100644
--- a/crates/fuzzing/src/generators/component_types.rs
+++ b/crates/fuzzing/src/generators/component_types.rs
@@ -46,77 +46,67 @@ pub fn arbitrary_val(ty: &component::Type, input: &mut Unstructured) -> arbitrar
                 Ok(ControlFlow::Continue(()))
             })?;
 
-            list.new_val(values.into()).unwrap()
+            Val::List(values.into())
         }
-        Type::Record(record) => record
-            .new_val(
-                record
-                    .fields()
-                    .map(|field| Ok((field.name, arbitrary_val(&field.ty, input)?)))
-                    .collect::<arbitrary::Result<Vec<_>>>()?,
-            )
-            .unwrap(),
-        Type::Tuple(tuple) => tuple
-            .new_val(
-                tuple
-                    .types()
-                    .map(|ty| arbitrary_val(&ty, input))
-                    .collect::<arbitrary::Result<_>>()?,
-            )
-            .unwrap(),
+        Type::Record(record) => Val::Record(
+            record
+                .fields()
+                .map(|field| Ok((field.name.to_string(), arbitrary_val(&field.ty, input)?)))
+                .collect::<arbitrary::Result<_>>()?,
+        ),
+        Type::Tuple(tuple) => Val::Tuple(
+            tuple
+                .types()
+                .map(|ty| arbitrary_val(&ty, input))
+                .collect::<arbitrary::Result<_>>()?,
+        ),
         Type::Variant(variant) => {
             let cases = variant.cases().collect::<Vec<_>>();
             let case = input.choose(&cases)?;
             let payload = match &case.ty {
-                Some(ty) => Some(arbitrary_val(ty, input)?),
+                Some(ty) => Some(Box::new(arbitrary_val(ty, input)?)),
                 None => None,
             };
-            variant.new_val(case.name, payload).unwrap()
+            Val::Variant(case.name.to_string(), payload)
         }
         Type::Enum(en) => {
             let names = en.names().collect::<Vec<_>>();
             let name = input.choose(&names)?;
-            en.new_val(name).unwrap()
+            Val::Enum(name.to_string())
         }
         Type::Option(option) => {
             let discriminant = input.int_in_range(0..=1)?;
-            option
-                .new_val(match discriminant {
-                    0 => None,
-                    1 => Some(arbitrary_val(&option.ty(), input)?),
-                    _ => unreachable!(),
-                })
-                .unwrap()
+            Val::Option(match discriminant {
+                0 => None,
+                1 => Some(Box::new(arbitrary_val(&option.ty(), input)?)),
+                _ => unreachable!(),
+            })
         }
         Type::Result(result) => {
             let discriminant = input.int_in_range(0..=1)?;
-            result
-                .new_val(match discriminant {
-                    0 => Ok(match result.ok() {
-                        Some(ty) => Some(arbitrary_val(&ty, input)?),
-                        None => None,
-                    }),
-                    1 => Err(match result.err() {
-                        Some(ty) => Some(arbitrary_val(&ty, input)?),
-                        None => None,
-                    }),
-                    _ => unreachable!(),
-                })
-                .unwrap()
+            Val::Result(match discriminant {
+                0 => Ok(match result.ok() {
+                    Some(ty) => Some(Box::new(arbitrary_val(&ty, input)?)),
+                    None => None,
+                }),
+                1 => Err(match result.err() {
+                    Some(ty) => Some(Box::new(arbitrary_val(&ty, input)?)),
+                    None => None,
+                }),
+                _ => unreachable!(),
+            })
         }
-        Type::Flags(flags) => flags
-            .new_val(
-                &flags
-                    .names()
-                    .filter_map(|name| {
-                        input
-                            .arbitrary()
-                            .map(|p| if p { Some(name) } else { None })
-                            .transpose()
-                    })
-                    .collect::<arbitrary::Result<Box<[_]>>>()?,
-            )
-            .unwrap(),
+        Type::Flags(flags) => Val::Flags(
+            flags
+                .names()
+                .filter_map(|name| {
+                    input
+                        .arbitrary()
+                        .map(|p| if p { Some(name.to_string()) } else { None })
+                        .transpose()
+                })
+                .collect::<arbitrary::Result<_>>()?,
+        ),
 
         // Resources aren't fuzzed at this time.
         Type::Own(_) | Type::Borrow(_) => unreachable!(),
diff --git a/crates/runtime/Cargo.toml b/crates/runtime/Cargo.toml
index 364668577..17196cfa0 100644
--- a/crates/runtime/Cargo.toml
+++ b/crates/runtime/Cargo.toml
@@ -35,7 +35,7 @@ wasm-encoder = { workspace = true }
 memfd = "0.6.2"
 
 [target.'cfg(target_os = "macos")'.dependencies]
-mach = "0.3.2"
+mach2 = "0.4.1"
 
 [target.'cfg(unix)'.dependencies]
 rustix = { workspace = true, features = ["mm"] }
diff --git a/crates/runtime/src/sys/unix/machports.rs b/crates/runtime/src/sys/unix/machports.rs
index 7ab1276fc..3b6f8dfa5 100644
--- a/crates/runtime/src/sys/unix/machports.rs
+++ b/crates/runtime/src/sys/unix/machports.rs
@@ -35,134 +35,21 @@
 
 use crate::sys::traphandlers::wasmtime_longjmp;
 use crate::traphandlers::tls;
-use mach::exception_types::*;
-use mach::kern_return::*;
-use mach::mach_init::*;
-use mach::mach_port::*;
-use mach::message::*;
-use mach::port::*;
-use mach::thread_act::*;
-use mach::traps::*;
+use mach2::exc::*;
+use mach2::exception_types::*;
+use mach2::kern_return::*;
+use mach2::mach_init::*;
+use mach2::mach_port::*;
+use mach2::message::*;
+use mach2::ndr::*;
+use mach2::port::*;
+use mach2::thread_act::*;
+use mach2::thread_status::*;
+use mach2::traps::*;
 use std::mem;
 use std::ptr::addr_of_mut;
 use std::thread;
 
-/// Other `mach` declarations awaiting <https://github.com/fitzgen/mach/pull/64>
-/// to be merged.
-mod mach_addons {
-    #![allow(non_camel_case_types)]
-    #![allow(non_upper_case_globals)]
-    #![allow(dead_code)]
-
-    use mach::{
-        exception_types::*, kern_return::*, mach_types::*, message::*, port::*, thread_status::*,
-    };
-    use std::mem;
-
-    #[repr(C)]
-    #[derive(Copy, Clone, Debug)]
-    #[allow(dead_code)]
-    pub struct NDR_record_t {
-        mig_vers: libc::c_uchar,
-        if_vers: libc::c_uchar,
-        reserved1: libc::c_uchar,
-        mig_encoding: libc::c_uchar,
-        int_rep: libc::c_uchar,
-        char_rep: libc::c_uchar,
-        float_rep: libc::c_uchar,
-        reserved32: libc::c_uchar,
-    }
-
-    extern "C" {
-        pub static NDR_record: NDR_record_t;
-    }
-
-    // Note that this is copied from Gecko at
-    //
-    // https://searchfox.org/mozilla-central/rev/ed93119be4818da1509bbcb7b28e245853eeedd5/js/src/wasm/WasmSignalHandlers.cpp#583-601
-    //
-    // which distinctly diverges from the actual version of this in the header
-    // files provided by macOS, notably in the `code` field which uses `i64`
-    // instead of `i32`.
-    //
-    // Also note the `packed(4)` here which forcibly decreases alignment to 4 to
-    // additionally match what mach expects (apparently, I wish I had a better
-    // reference for this).
-    #[repr(C, packed(4))]
-    #[allow(dead_code)]
-    #[derive(Copy, Clone, Debug)]
-    pub struct __Request__exception_raise_t {
-        pub Head: mach_msg_header_t,
-        /* start of the kernel processed data */
-        pub msgh_body: mach_msg_body_t,
-        pub thread: mach_msg_port_descriptor_t,
-        pub task: mach_msg_port_descriptor_t,
-        /* end of the kernel processed data */
-        pub NDR: NDR_record_t,
-        pub exception: exception_type_t,
-        pub codeCnt: mach_msg_type_number_t,
-
-        // Note that this is a divergence from the C headers which use
-        // `integer_t` here for this field which is a `c_int`. That isn't
-        // actually reflecting reality apparently though because if `c_int` is
-        // used here then the structure is too small to receive a message.
-        pub code: [i64; 2],
-    }
-
-    #[repr(C)]
-    #[allow(dead_code)]
-    #[derive(Copy, Clone, Debug)]
-    pub struct __Reply__exception_raise_t {
-        pub Head: mach_msg_header_t,
-        pub NDR: NDR_record_t,
-        pub RetCode: kern_return_t,
-    }
-
-    #[repr(C)]
-    #[derive(Copy, Clone, Debug, Default, Hash, PartialOrd, PartialEq, Eq, Ord)]
-    pub struct arm_thread_state64_t {
-        pub __x: [u64; 29],
-        pub __fp: u64, // frame pointer x29
-        pub __lr: u64, // link register x30
-        pub __sp: u64, // stack pointer x31
-        pub __pc: u64,
-        pub __cpsr: u32,
-        pub __pad: u32,
-    }
-
-    impl arm_thread_state64_t {
-        pub fn count() -> mach_msg_type_number_t {
-            (mem::size_of::<Self>() / mem::size_of::<u32>()) as mach_msg_type_number_t
-        }
-    }
-
-    pub static ARM_THREAD_STATE64: thread_state_flavor_t = 6;
-
-    #[cfg(target_arch = "x86_64")]
-    pub static THREAD_STATE_NONE: thread_state_flavor_t = 13;
-    #[cfg(target_arch = "aarch64")]
-    pub static THREAD_STATE_NONE: thread_state_flavor_t = 5;
-
-    extern "C" {
-        pub fn thread_set_state(
-            target_act: thread_port_t,
-            flavor: thread_state_flavor_t,
-            new_state: thread_state_t,
-            new_stateCnt: mach_msg_type_number_t,
-        ) -> kern_return_t;
-
-        pub fn thread_set_exception_ports(
-            thread: thread_port_t,
-            exception_mask: exception_mask_t,
-            new_port: mach_port_t,
-            behavior: libc::c_uint,
-            new_flavor: thread_state_flavor_t,
-        ) -> kern_return_t;
-    }
-}
-
-use mach_addons::*;
-
 /// Process-global port that we use to route thread-level exceptions to.
 static mut WASMTIME_PORT: mach_port_name_t = MACH_PORT_NULL;
 
@@ -192,6 +79,38 @@ pub unsafe fn platform_init() {
     thread::spawn(|| handler_thread());
 }
 
+// Note that this is copied from Gecko at
+//
+// https://searchfox.org/mozilla-central/rev/ed93119be4818da1509bbcb7b28e245853eeedd5/js/src/wasm/WasmSignalHandlers.cpp#583-601
+//
+// which distinctly diverges from the actual version of this in the header
+// files provided by macOS, notably in the `code` field which uses `i64`
+// instead of `i32`.
+//
+// Also note the `packed(4)` here which forcibly decreases alignment to 4 to
+// additionally match what mach expects (apparently, I wish I had a better
+// reference for this).
+#[repr(C, packed(4))]
+#[allow(dead_code)]
+#[derive(Copy, Clone, Debug)]
+struct __Request__exception_raise_t {
+    Head: mach_msg_header_t,
+    /* start of the kernel processed data */
+    msgh_body: mach_msg_body_t,
+    thread: mach_msg_port_descriptor_t,
+    task: mach_msg_port_descriptor_t,
+    /* end of the kernel processed data */
+    NDR: NDR_record_t,
+    exception: exception_type_t,
+    codeCnt: mach_msg_type_number_t,
+
+    // Note that this is a divergence from the C headers which use
+    // `integer_t` here for this field which is a `c_int`. That isn't
+    // actually reflecting reality apparently though because if `c_int` is
+    // used here then the structure is too small to receive a message.
+    code: [i64; 2],
+}
+
 // This is largely just copied from SpiderMonkey.
 #[repr(C)]
 #[allow(dead_code)]
@@ -301,8 +220,8 @@ unsafe fn handle_exception(request: &mut ExceptionRequest) -> bool {
     // * `thread_state_count` - the size to pass to `mach_msg`.
     cfg_if::cfg_if! {
         if #[cfg(target_arch = "x86_64")] {
-            use mach::structs::x86_thread_state64_t;
-            use mach::thread_status::x86_THREAD_STATE64;
+            use mach2::structs::x86_thread_state64_t;
+            use mach2::thread_status::x86_THREAD_STATE64;
 
             type ThreadState = x86_thread_state64_t;
 
@@ -345,7 +264,7 @@ unsafe fn handle_exception(request: &mut ExceptionRequest) -> bool {
             };
             let mut thread_state = ThreadState::new();
         } else if #[cfg(target_arch = "aarch64")] {
-            type ThreadState = arm_thread_state64_t;
+            type ThreadState = mach2::structs::arm_thread_state64_t;
 
             let thread_state_flavor = ARM_THREAD_STATE64;
 
@@ -479,8 +398,8 @@ pub fn lazy_per_thread_init() {
             this_thread,
             EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC,
             WASMTIME_PORT,
-            EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES,
-            mach_addons::THREAD_STATE_NONE,
+            (EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES) as exception_behavior_t,
+            THREAD_STATE_NONE,
         );
         mach_port_deallocate(mach_task_self(), this_thread);
         assert_eq!(kret, KERN_SUCCESS, "failed to set thread exception port");
diff --git a/crates/wasi/Cargo.toml b/crates/wasi/Cargo.toml
index 5ee1cc235..f35908fb5 100644
--- a/crates/wasi/Cargo.toml
+++ b/crates/wasi/Cargo.toml
@@ -37,7 +37,7 @@ url = { workspace = true }
 once_cell = { workspace = true }
 
 [dev-dependencies]
-tokio = { workspace = true, features = ["time", "sync", "io-std", "io-util", "rt", "rt-multi-thread", "net", "macros"] }
+tokio = { workspace = true, features = ["time", "sync", "io-std", "io-util", "rt", "rt-multi-thread", "net", "macros", "fs"] }
 test-log = { workspace = true }
 tracing-subscriber = { workspace = true }
 test-programs-artifacts = { workspace = true }
diff --git a/crates/wasi/src/ctx.rs b/crates/wasi/src/ctx.rs
index 090135a0f..4e9f3578e 100644
--- a/crates/wasi/src/ctx.rs
+++ b/crates/wasi/src/ctx.rs
@@ -1,3 +1,5 @@
+#[cfg(feature = "preview1")]
+use crate::WasiP1Ctx;
 use crate::{
     clocks::{
         host::{monotonic_clock, wall_clock},
@@ -127,6 +129,10 @@ impl WasiCtxBuilder {
         self
     }
 
+    pub fn inherit_env(&mut self) -> &mut Self {
+        self.envs(&std::env::vars().collect::<Vec<(String, String)>>())
+    }
+
     pub fn args(&mut self, args: &[impl AsRef<str>]) -> &mut Self {
         self.args.extend(args.iter().map(|a| a.as_ref().to_owned()));
         self
@@ -137,6 +143,10 @@ impl WasiCtxBuilder {
         self
     }
 
+    pub fn inherit_args(&mut self) -> &mut Self {
+        self.args(&std::env::args().collect::<Vec<String>>())
+    }
+
     pub fn preopened_dir(
         &mut self,
         dir: cap_std::fs::Dir,
@@ -273,6 +283,12 @@ impl WasiCtxBuilder {
             allowed_network_uses,
         }
     }
+
+    #[cfg(feature = "preview1")]
+    pub fn build_p1(&mut self) -> WasiP1Ctx {
+        let wasi = self.build();
+        WasiP1Ctx::new(wasi)
+    }
 }
 
 pub trait WasiView: Send {
diff --git a/crates/wasi/src/lib.rs b/crates/wasi/src/lib.rs
index 225245b47..6ef80d83e 100644
--- a/crates/wasi/src/lib.rs
+++ b/crates/wasi/src/lib.rs
@@ -15,6 +15,8 @@ mod filesystem;
 mod host;
 mod ip_name_lookup;
 mod network;
+#[cfg(feature = "preview1")]
+mod p1ctx;
 pub mod pipe;
 mod poll;
 #[cfg(feature = "preview1")]
@@ -34,10 +36,13 @@ pub use self::ctx::{WasiCtx, WasiCtxBuilder, WasiView};
 pub use self::error::{I32Exit, TrappableError};
 pub use self::filesystem::{DirPerms, FilePerms, FsError, FsResult};
 pub use self::network::{Network, SocketError, SocketResult};
+#[cfg(feature = "preview1")]
+pub use self::p1ctx::WasiP1Ctx;
 pub use self::poll::{subscribe, ClosureFuture, MakeFuture, Pollable, PollableFuture, Subscribe};
 pub use self::random::{thread_rng, Deterministic};
 pub use self::stdio::{
-    stderr, stdin, stdout, IsATTY, Stderr, Stdin, StdinStream, Stdout, StdoutStream,
+    stderr, stdin, stdout, AsyncStdinStream, AsyncStdoutStream, IsATTY, Stderr, Stdin, StdinStream,
+    Stdout, StdoutStream,
 };
 pub use self::stream::{
     HostInputStream, HostOutputStream, InputStream, OutputStream, StreamError, StreamResult,
diff --git a/crates/wasi/src/p1ctx.rs b/crates/wasi/src/p1ctx.rs
new file mode 100644
index 000000000..990609de3
--- /dev/null
+++ b/crates/wasi/src/p1ctx.rs
@@ -0,0 +1,37 @@
+use crate::preview1::{WasiPreview1Adapter, WasiPreview1View};
+use crate::{WasiCtx, WasiView};
+use wasmtime::component::ResourceTable;
+
+pub struct WasiP1Ctx {
+    pub table: ResourceTable,
+    pub wasi: WasiCtx,
+    pub adapter: WasiPreview1Adapter,
+}
+
+impl WasiP1Ctx {
+    pub fn new(wasi: WasiCtx) -> Self {
+        Self {
+            table: ResourceTable::new(),
+            wasi,
+            adapter: WasiPreview1Adapter::new(),
+        }
+    }
+}
+
+impl WasiView for WasiP1Ctx {
+    fn table(&mut self) -> &mut ResourceTable {
+        &mut self.table
+    }
+    fn ctx(&mut self) -> &mut WasiCtx {
+        &mut self.wasi
+    }
+}
+
+impl WasiPreview1View for WasiP1Ctx {
+    fn adapter(&self) -> &WasiPreview1Adapter {
+        &self.adapter
+    }
+    fn adapter_mut(&mut self) -> &mut WasiPreview1Adapter {
+        &mut self.adapter
+    }
+}
diff --git a/crates/wasi/src/pipe.rs b/crates/wasi/src/pipe.rs
index 819455c59..3ecc9cd3b 100644
--- a/crates/wasi/src/pipe.rs
+++ b/crates/wasi/src/pipe.rs
@@ -22,9 +22,9 @@ pub struct MemoryInputPipe {
 }
 
 impl MemoryInputPipe {
-    pub fn new(bytes: Bytes) -> Self {
+    pub fn new(bytes: impl Into<Bytes>) -> Self {
         Self {
-            buffer: Arc::new(Mutex::new(bytes)),
+            buffer: Arc::new(Mutex::new(bytes.into())),
         }
     }
 
diff --git a/crates/wasi/src/preview0.rs b/crates/wasi/src/preview0.rs
index b7c8c40a7..38c0de8dd 100644
--- a/crates/wasi/src/preview0.rs
+++ b/crates/wasi/src/preview0.rs
@@ -4,16 +4,18 @@ use crate::preview1::wasi_snapshot_preview1::WasiSnapshotPreview1 as Snapshot1;
 use crate::preview1::WasiPreview1View;
 use wiggle::{GuestError, GuestPtr};
 
-pub fn add_to_linker_async<T: WasiPreview1View>(
+pub fn add_to_linker_async<T: Send, W: WasiPreview1View>(
     linker: &mut wasmtime::Linker<T>,
+    f: impl Fn(&mut T) -> &mut W + Copy + Send + Sync + 'static,
 ) -> anyhow::Result<()> {
-    wasi_unstable::add_to_linker(linker, |t| t)
+    wasi_unstable::add_to_linker(linker, f)
 }
 
-pub fn add_to_linker_sync<T: WasiPreview1View>(
+pub fn add_to_linker_sync<T: Send, W: WasiPreview1View>(
     linker: &mut wasmtime::Linker<T>,
+    f: impl Fn(&mut T) -> &mut W + Copy + Send + Sync + 'static,
 ) -> anyhow::Result<()> {
-    sync::add_wasi_unstable_to_linker(linker, |t| t)
+    sync::add_wasi_unstable_to_linker(linker, f)
 }
 
 wiggle::from_witx!({
diff --git a/crates/wasi/src/preview1.rs b/crates/wasi/src/preview1.rs
index 16a5e58ee..0147a8103 100644
--- a/crates/wasi/src/preview1.rs
+++ b/crates/wasi/src/preview1.rs
@@ -490,18 +490,19 @@ trait WasiPreview1ViewExt:
 
 impl<T: WasiPreview1View + preopens::Host> WasiPreview1ViewExt for T {}
 
-pub fn add_to_linker_async<T: WasiPreview1View>(
+pub fn add_to_linker_async<T: Send, W: WasiPreview1View>(
     linker: &mut wasmtime::Linker<T>,
+    f: impl Fn(&mut T) -> &mut W + Copy + Send + Sync + 'static,
 ) -> anyhow::Result<()> {
-    wasi_snapshot_preview1::add_to_linker(linker, |t| t)
+    crate::preview1::wasi_snapshot_preview1::add_to_linker(linker, f)
 }
 
-pub fn add_to_linker_sync<T: WasiPreview1View>(
+pub fn add_to_linker_sync<T: Send, W: WasiPreview1View>(
     linker: &mut wasmtime::Linker<T>,
+    f: impl Fn(&mut T) -> &mut W + Copy + Send + Sync + 'static,
 ) -> anyhow::Result<()> {
-    sync::add_wasi_snapshot_preview1_to_linker(linker, |t| t)
+    crate::preview1::sync::add_wasi_snapshot_preview1_to_linker(linker, f)
 }
-
 // Generate the wasi_snapshot_preview1::WasiSnapshotPreview1 trait,
 // and the module types.
 // None of the generated modules, traits, or types should be used externally
@@ -520,7 +521,7 @@ wiggle::from_witx!({
     errors: { errno => trappable Error },
 });
 
-mod sync {
+pub(crate) mod sync {
     use anyhow::Result;
     use std::future::Future;
 
diff --git a/crates/wasi/src/stdio.rs b/crates/wasi/src/stdio.rs
index 5c6b191eb..04bab3bdc 100644
--- a/crates/wasi/src/stdio.rs
+++ b/crates/wasi/src/stdio.rs
@@ -6,7 +6,11 @@ use crate::bindings::io::streams;
 use crate::pipe;
 use crate::{HostInputStream, HostOutputStream, StreamError, StreamResult, Subscribe, WasiView};
 use bytes::Bytes;
+use std::future::Future;
 use std::io::IsTerminal;
+use std::pin::Pin;
+use std::sync::{Arc, Mutex};
+use std::task::{Context, Poll};
 use wasmtime::component::Resource;
 
 /// A trait used to represent the standard input to a guest program.
@@ -54,6 +58,52 @@ impl StdinStream for pipe::ClosedInputStream {
     }
 }
 
+/// An impl of [`StdinStream`] built on top of [`crate::pipe::AsyncReadStream`].
+pub struct AsyncStdinStream(Arc<Mutex<crate::pipe::AsyncReadStream>>);
+
+impl AsyncStdinStream {
+    pub fn new(s: crate::pipe::AsyncReadStream) -> Self {
+        Self(Arc::new(Mutex::new(s)))
+    }
+}
+
+impl StdinStream for AsyncStdinStream {
+    fn stream(&self) -> Box<dyn HostInputStream> {
+        Box::new(Self(self.0.clone()))
+    }
+    fn isatty(&self) -> bool {
+        false
+    }
+}
+
+impl HostInputStream for AsyncStdinStream {
+    fn read(&mut self, size: usize) -> Result<bytes::Bytes, crate::StreamError> {
+        self.0.lock().unwrap().read(size)
+    }
+    fn skip(&mut self, size: usize) -> Result<usize, crate::StreamError> {
+        self.0.lock().unwrap().skip(size)
+    }
+}
+
+impl Subscribe for AsyncStdinStream {
+    fn ready<'a, 'b>(&'a mut self) -> Pin<Box<dyn Future<Output = ()> + Send + 'b>>
+    where
+        Self: 'b,
+        'a: 'b,
+    {
+        struct F(AsyncStdinStream);
+        impl Future for F {
+            type Output = ();
+            fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
+                let mut inner = self.0 .0.lock().unwrap();
+                let mut fut = inner.ready();
+                fut.as_mut().poll(cx)
+            }
+        }
+        Box::pin(F(Self(self.0.clone())))
+    }
+}
+
 mod worker_thread_stdin;
 pub use self::worker_thread_stdin::{stdin, Stdin};
 
@@ -181,6 +231,72 @@ impl Subscribe for OutputStream {
     async fn ready(&mut self) {}
 }
 
+/// A wrapper of [`crate::pipe::AsyncWriteStream`] that implements
+/// [`StdoutStream`]. Note that the [`HostOutputStream`] impl for this is not
+/// correct when used for interleaved async IO.
+pub struct AsyncStdoutStream(Arc<Mutex<crate::pipe::AsyncWriteStream>>);
+
+impl AsyncStdoutStream {
+    pub fn new(s: crate::pipe::AsyncWriteStream) -> Self {
+        Self(Arc::new(Mutex::new(s)))
+    }
+}
+
+impl StdoutStream for AsyncStdoutStream {
+    fn stream(&self) -> Box<dyn HostOutputStream> {
+        Box::new(Self(self.0.clone()))
+    }
+    fn isatty(&self) -> bool {
+        false
+    }
+}
+
+// This implementation is known to be bogus. All check-writes and writes are
+// directed at the same underlying stream. The check-write/write protocol does
+// require the size returned by a check-write to be accepted by write, even if
+// other side-effects happen between those calls, and this implementation
+// permits another view (created by StdoutStream::stream()) of the same
+// underlying stream to accept a write which will invalidate a prior
+// check-write of another view.
+// Ultimately, the Std{in,out}Stream::stream() methods exist because many
+// different places in a linked component (which may itself contain many
+// modules) may need to access stdio without any coordination to keep those
+// accesses all using pointing to the same resource. So, we allow many
+// resources to be created. We have the reasonable expectation that programs
+// won't attempt to interleave async IO from these disparate uses of stdio.
+// If that expectation doesn't turn out to be true, and you find yourself at
+// this comment to correct it: sorry about that.
+impl HostOutputStream for AsyncStdoutStream {
+    fn check_write(&mut self) -> Result<usize, StreamError> {
+        self.0.lock().unwrap().check_write()
+    }
+    fn write(&mut self, bytes: Bytes) -> Result<(), StreamError> {
+        self.0.lock().unwrap().write(bytes)
+    }
+    fn flush(&mut self) -> Result<(), StreamError> {
+        self.0.lock().unwrap().flush()
+    }
+}
+
+impl Subscribe for AsyncStdoutStream {
+    fn ready<'a, 'b>(&'a mut self) -> Pin<Box<dyn Future<Output = ()> + Send + 'b>>
+    where
+        Self: 'b,
+        'a: 'b,
+    {
+        struct F(AsyncStdoutStream);
+        impl Future for F {
+            type Output = ();
+            fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
+                let mut inner = self.0 .0.lock().unwrap();
+                let mut fut = inner.ready();
+                fut.as_mut().poll(cx)
+            }
+        }
+        Box::pin(F(Self(self.0.clone())))
+    }
+}
+
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
 pub enum IsATTY {
     Yes,
@@ -255,3 +371,73 @@ impl<T: WasiView> terminal_stderr::Host for T {
         }
     }
 }
+
+#[cfg(test)]
+mod test {
+    #[test]
+    fn memory_stdin_stream() {
+        // A StdinStream has the property that there are multiple
+        // HostInputStreams created, using the stream() method which are each
+        // views on the same shared state underneath. Consuming input on one
+        // stream results in consuming that input on all streams.
+        //
+        // The simplest way to measure this is to check if the MemoryInputPipe
+        // impl of StdinStream follows this property.
+
+        let pipe = super::pipe::MemoryInputPipe::new(
+            "the quick brown fox jumped over the three lazy dogs",
+        );
+
+        use super::StdinStream;
+
+        let mut view1 = pipe.stream();
+        let mut view2 = pipe.stream();
+
+        let read1 = view1.read(10).expect("read first 10 bytes");
+        assert_eq!(read1, "the quick ".as_bytes(), "first 10 bytes");
+        let read2 = view2.read(10).expect("read second 10 bytes");
+        assert_eq!(read2, "brown fox ".as_bytes(), "second 10 bytes");
+        let read3 = view1.read(10).expect("read third 10 bytes");
+        assert_eq!(read3, "jumped ove".as_bytes(), "third 10 bytes");
+        let read4 = view2.read(10).expect("read fourth 10 bytes");
+        assert_eq!(read4, "r the thre".as_bytes(), "fourth 10 bytes");
+    }
+    #[tokio::test]
+    async fn async_stdin_stream() {
+        // A StdinStream has the property that there are multiple
+        // HostInputStreams created, using the stream() method which are each
+        // views on the same shared state underneath. Consuming input on one
+        // stream results in consuming that input on all streams.
+        //
+        // AsyncStdinStream is a slightly more complex impl of StdinStream
+        // than the MemoryInputPipe above. We can create an AsyncReadStream
+        // from a file on the disk, and an AsyncStdinStream from that common
+        // stream, then check that the same property holds as above.
+
+        let dir = tempfile::tempdir().unwrap();
+        let mut path = std::path::PathBuf::from(dir.path());
+        path.push("file");
+        std::fs::write(&path, "the quick brown fox jumped over the three lazy dogs").unwrap();
+
+        let file = tokio::fs::File::open(&path)
+            .await
+            .expect("open created file");
+        let stdin_stream = super::AsyncStdinStream::new(crate::pipe::AsyncReadStream::new(file));
+
+        use super::StdinStream;
+
+        let mut view1 = stdin_stream.stream();
+        let mut view2 = stdin_stream.stream();
+
+        view1.ready().await;
+
+        let read1 = view1.read(10).expect("read first 10 bytes");
+        assert_eq!(read1, "the quick ".as_bytes(), "first 10 bytes");
+        let read2 = view2.read(10).expect("read second 10 bytes");
+        assert_eq!(read2, "brown fox ".as_bytes(), "second 10 bytes");
+        let read3 = view1.read(10).expect("read third 10 bytes");
+        assert_eq!(read3, "jumped ove".as_bytes(), "third 10 bytes");
+        let read4 = view2.read(10).expect("read fourth 10 bytes");
+        assert_eq!(read4, "r the thre".as_bytes(), "fourth 10 bytes");
+    }
+}
diff --git a/crates/wasmtime/src/config.rs b/crates/wasmtime/src/config.rs
index b9a22efdf..35e9fe4ba 100644
--- a/crates/wasmtime/src/config.rs
+++ b/crates/wasmtime/src/config.rs
@@ -6,7 +6,7 @@ use std::fmt;
 use std::path::Path;
 use std::str::FromStr;
 use std::sync::Arc;
-use target_lexicon::{Architecture, PointerWidth};
+use target_lexicon::Architecture;
 use wasmparser::WasmFeatures;
 #[cfg(feature = "cache")]
 use wasmtime_cache::CacheConfig;
@@ -1684,11 +1684,7 @@ impl Config {
         let mut tunables = Tunables::default_host();
         #[cfg(any(feature = "cranelift", feature = "winch"))]
         let mut tunables = match &self.compiler_config.target.as_ref() {
-            Some(target) => match target.pointer_width() {
-                Ok(PointerWidth::U32) => Tunables::default_u32(),
-                Ok(PointerWidth::U64) => Tunables::default_u64(),
-                _ => bail!("unknown pointer width"),
-            },
+            Some(target) => Tunables::default_for_target(target)?,
             None => Tunables::default_host(),
         };
 
diff --git a/crates/wasmtime/src/runtime/component/func.rs b/crates/wasmtime/src/runtime/component/func.rs
index 2458a132c..2791da879 100644
--- a/crates/wasmtime/src/runtime/component/func.rs
+++ b/crates/wasmtime/src/runtime/component/func.rs
@@ -395,11 +395,6 @@ impl Func {
             );
         }
 
-        for (param, ty) in params.iter().zip(param_tys.iter()) {
-            ty.is_supertype_of(param)
-                .context("type mismatch with parameters")?;
-        }
-
         self.call_raw(
             store,
             params,
diff --git a/crates/wasmtime/src/runtime/component/func/host.rs b/crates/wasmtime/src/runtime/component/func/host.rs
index c00f38c83..22946854f 100644
--- a/crates/wasmtime/src/runtime/component/func/host.rs
+++ b/crates/wasmtime/src/runtime/component/func/host.rs
@@ -1,7 +1,7 @@
 use crate::component::func::{LiftContext, LowerContext, Options};
 use crate::component::matching::InstanceType;
 use crate::component::storage::slice_to_storage_mut;
-use crate::component::{ComponentNamedList, ComponentType, Lift, Lower, Type, Val};
+use crate::component::{ComponentNamedList, ComponentType, Lift, Lower, Val};
 use crate::{AsContextMut, StoreContextMut, ValRaw};
 use anyhow::{anyhow, bail, Context, Result};
 use std::any::Any;
@@ -383,10 +383,6 @@ where
     flags.set_may_leave(false);
 
     let mut cx = LowerContext::new(store, &options, types, instance);
-    let instance = cx.instance_type();
-    for (val, ty) in result_vals.iter().zip(result_tys.types.iter()) {
-        Type::from(ty, &instance).is_supertype_of(val)?;
-    }
     if let Some(cnt) = result_tys.abi.flat_count(MAX_FLAT_RESULTS) {
         let mut dst = storage[..cnt].iter_mut();
         for (val, ty) in result_vals.iter().zip(result_tys.types.iter()) {
diff --git a/crates/wasmtime/src/runtime/component/func/options.rs b/crates/wasmtime/src/runtime/component/func/options.rs
index f08a29223..0217c31ff 100644
--- a/crates/wasmtime/src/runtime/component/func/options.rs
+++ b/crates/wasmtime/src/runtime/component/func/options.rs
@@ -338,8 +338,14 @@ impl<'a, T> LowerContext<'a, T> {
     ///
     /// Note that this is a special case for `Resource<T>`. Most of the time a
     /// host value shouldn't be lowered with a lowering context.
-    pub fn host_resource_lower_own(&mut self, rep: u32) -> Result<HostResourceIndex> {
-        self.resource_tables().host_resource_lower_own(rep)
+    pub fn host_resource_lower_own(
+        &mut self,
+        rep: u32,
+        dtor: Option<NonNull<VMFuncRef>>,
+        flags: Option<InstanceFlags>,
+    ) -> Result<HostResourceIndex> {
+        self.resource_tables()
+            .host_resource_lower_own(rep, dtor, flags)
     }
 
     /// Returns the underlying resource type for the `ty` table specified.
@@ -491,8 +497,14 @@ impl<'a> LiftContext<'a> {
 
     /// Lowers a resource into the host-owned table, returning the index it was
     /// inserted at.
-    pub fn host_resource_lower_own(&mut self, rep: u32) -> Result<HostResourceIndex> {
-        self.resource_tables().host_resource_lower_own(rep)
+    pub fn host_resource_lower_own(
+        &mut self,
+        rep: u32,
+        dtor: Option<NonNull<VMFuncRef>>,
+        flags: Option<InstanceFlags>,
+    ) -> Result<HostResourceIndex> {
+        self.resource_tables()
+            .host_resource_lower_own(rep, dtor, flags)
     }
 
     /// Lowers a resource into the host-owned table, returning the index it was
diff --git a/crates/wasmtime/src/runtime/component/func/typed.rs b/crates/wasmtime/src/runtime/component/func/typed.rs
index 60bbcaeb4..3240a884a 100644
--- a/crates/wasmtime/src/runtime/component/func/typed.rs
+++ b/crates/wasmtime/src/runtime/component/func/typed.rs
@@ -1787,12 +1787,12 @@ pub fn typecheck_variant(
                 );
             }
 
-            for (case, &(name, check)) in cases.iter().zip(expected) {
-                if case.name != name {
-                    bail!("expected variant case named {name}, found {}", case.name);
+            for ((case_name, case_ty), &(name, check)) in cases.iter().zip(expected) {
+                if *case_name != name {
+                    bail!("expected variant case named {name}, found {case_name}");
                 }
 
-                match (check, &case.ty) {
+                match (check, case_ty) {
                     (Some(check), Some(ty)) => check(ty, types)
                         .with_context(|| format!("type mismatch for case {name}"))?,
                     (None, None) => {}
diff --git a/crates/wasmtime/src/runtime/component/instance.rs b/crates/wasmtime/src/runtime/component/instance.rs
index d15740995..0e2633e0e 100644
--- a/crates/wasmtime/src/runtime/component/instance.rs
+++ b/crates/wasmtime/src/runtime/component/instance.rs
@@ -1,8 +1,6 @@
 use crate::component::func::HostFunc;
 use crate::component::matching::InstanceType;
-use crate::component::{
-    Component, ComponentNamedList, Func, Lift, Lower, ResourceImportIndex, ResourceType, TypedFunc,
-};
+use crate::component::{Component, ComponentNamedList, Func, Lift, Lower, ResourceType, TypedFunc};
 use crate::instance::OwnedImports;
 use crate::linker::DefinitionType;
 use crate::store::{StoreOpaque, Stored};
@@ -525,7 +523,6 @@ impl<'a> Instantiator<'a> {
 pub struct InstancePre<T> {
     component: Component,
     imports: Arc<PrimaryMap<RuntimeImportIndex, RuntimeImport>>,
-    resource_imports: Arc<PrimaryMap<ResourceImportIndex, Option<RuntimeImportIndex>>>,
     _marker: marker::PhantomData<fn() -> T>,
 }
 
@@ -535,7 +532,6 @@ impl<T> Clone for InstancePre<T> {
         Self {
             component: self.component.clone(),
             imports: self.imports.clone(),
-            resource_imports: self.resource_imports.clone(),
             _marker: self._marker,
         }
     }
@@ -551,28 +547,14 @@ impl<T> InstancePre<T> {
     pub(crate) unsafe fn new_unchecked(
         component: Component,
         imports: PrimaryMap<RuntimeImportIndex, RuntimeImport>,
-        resource_imports: PrimaryMap<ResourceImportIndex, Option<RuntimeImportIndex>>,
     ) -> InstancePre<T> {
         InstancePre {
             component,
             imports: Arc::new(imports),
-            resource_imports: Arc::new(resource_imports),
             _marker: marker::PhantomData,
         }
     }
 
-    pub(crate) fn resource_import_index(
-        &self,
-        path: ResourceImportIndex,
-    ) -> Option<RuntimeImportIndex> {
-        *self.resource_imports.get(path)?
-    }
-
-    pub(crate) fn resource_import(&self, path: ResourceImportIndex) -> Option<&RuntimeImport> {
-        let idx = self.resource_import_index(path)?;
-        self.imports.get(idx)
-    }
-
     /// Returns the underlying component that will be instantiated.
     pub fn component(&self) -> &Component {
         &self.component
diff --git a/crates/wasmtime/src/runtime/component/linker.rs b/crates/wasmtime/src/runtime/component/linker.rs
index e41cca1c2..4b232cb11 100644
--- a/crates/wasmtime/src/runtime/component/linker.rs
+++ b/crates/wasmtime/src/runtime/component/linker.rs
@@ -16,7 +16,7 @@ use std::ops::Deref;
 use std::pin::Pin;
 use std::sync::Arc;
 use wasmtime_environ::component::TypeDef;
-use wasmtime_environ::{EntityRef, PrimaryMap};
+use wasmtime_environ::PrimaryMap;
 
 /// A type used to instantiate [`Component`]s.
 ///
@@ -64,7 +64,6 @@ pub struct Linker<T> {
     strings: Strings,
     map: NameMap,
     path: Vec<usize>,
-    resource_imports: usize,
     allow_shadowing: bool,
     _marker: marker::PhantomData<fn() -> T>,
 }
@@ -76,7 +75,6 @@ impl<T> Clone for Linker<T> {
             strings: self.strings.clone(),
             map: self.map.clone(),
             path: self.path.clone(),
-            resource_imports: self.resource_imports.clone(),
             allow_shadowing: self.allow_shadowing,
             _marker: self._marker,
         }
@@ -100,50 +98,10 @@ pub struct LinkerInstance<'a, T> {
     path_len: usize,
     strings: &'a mut Strings,
     map: &'a mut NameMap,
-    resource_imports: &'a mut usize,
     allow_shadowing: bool,
     _marker: marker::PhantomData<fn() -> T>,
 }
 
-/// Index correlating a resource definition to the import path.
-/// This is assigned by [`Linker::resource`] and may be used to associate it to
-/// [`RuntimeImportIndex`](wasmtime_environ::component::RuntimeImportIndex)
-/// at a later stage
-///
-/// [`Linker::resource`]: crate::component::LinkerInstance::resource
-#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
-pub struct ResourceImportIndex(usize);
-
-impl EntityRef for ResourceImportIndex {
-    fn new(idx: usize) -> Self {
-        Self(idx)
-    }
-
-    fn index(self) -> usize {
-        self.0
-    }
-}
-
-impl Deref for ResourceImportIndex {
-    type Target = usize;
-
-    fn deref(&self) -> &Self::Target {
-        &self.0
-    }
-}
-
-impl From<usize> for ResourceImportIndex {
-    fn from(idx: usize) -> Self {
-        Self(idx)
-    }
-}
-
-impl From<ResourceImportIndex> for usize {
-    fn from(idx: ResourceImportIndex) -> Self {
-        idx.0
-    }
-}
-
 #[derive(Clone, Default)]
 pub(crate) struct NameMap {
     /// A map of interned strings to the name that they define.
@@ -184,11 +142,7 @@ pub(crate) enum Definition {
     Instance(NameMap),
     Func(Arc<HostFunc>),
     Module(Module),
-    Resource(
-        ResourceImportIndex,
-        ResourceType,
-        Arc<crate::func::HostFunc>,
-    ),
+    Resource(ResourceType, Arc<crate::func::HostFunc>),
 }
 
 impl<T> Linker<T> {
@@ -201,7 +155,6 @@ impl<T> Linker<T> {
             map: NameMap::default(),
             allow_shadowing: false,
             path: Vec::new(),
-            resource_imports: 0,
             _marker: marker::PhantomData,
         }
     }
@@ -229,7 +182,6 @@ impl<T> Linker<T> {
             path_len: 0,
             strings: &mut self.strings,
             map: &mut self.map,
-            resource_imports: &mut self.resource_imports,
             allow_shadowing: self.allow_shadowing,
             _marker: self._marker,
         }
@@ -304,7 +256,6 @@ impl<T> Linker<T> {
         // component-compile-time.
         let env_component = component.env_component();
         let mut imports = PrimaryMap::with_capacity(env_component.imports.len());
-        let mut resource_imports = PrimaryMap::from(vec![None; self.resource_imports]);
         for (idx, (import, names)) in env_component.imports.iter() {
             let (root, _) = &env_component.import_types[*import];
 
@@ -321,14 +272,11 @@ impl<T> Linker<T> {
             let import = match cur {
                 Definition::Module(m) => RuntimeImport::Module(m.clone()),
                 Definition::Func(f) => RuntimeImport::Func(f.clone()),
-                Definition::Resource(res_idx, t, dtor) => {
-                    resource_imports[*res_idx] = Some(idx);
-                    RuntimeImport::Resource {
-                        ty: t.clone(),
-                        _dtor: dtor.clone(),
-                        dtor_funcref: component.resource_drop_func_ref(dtor),
-                    }
-                }
+                Definition::Resource(t, dtor) => RuntimeImport::Resource {
+                    ty: t.clone(),
+                    _dtor: dtor.clone(),
+                    dtor_funcref: component.resource_drop_func_ref(dtor),
+                },
 
                 // This is guaranteed by the compilation process that "leaf"
                 // runtime imports are never instances.
@@ -337,7 +285,7 @@ impl<T> Linker<T> {
             let i = imports.push(import);
             assert_eq!(i, idx);
         }
-        Ok(unsafe { InstancePre::new_unchecked(component.clone(), imports, resource_imports) })
+        Ok(unsafe { InstancePre::new_unchecked(component.clone(), imports) })
     }
 
     /// Instantiates the [`Component`] provided into the `store` specified.
@@ -402,7 +350,6 @@ impl<T> LinkerInstance<'_, T> {
             path_len: self.path_len,
             strings: self.strings,
             map: self.map,
-            resource_imports: self.resource_imports,
             allow_shadowing: self.allow_shadowing,
             _marker: self._marker,
         }
@@ -581,18 +528,13 @@ impl<T> LinkerInstance<'_, T> {
         name: &str,
         ty: ResourceType,
         dtor: impl Fn(StoreContextMut<'_, T>, u32) -> Result<()> + Send + Sync + 'static,
-    ) -> Result<ResourceImportIndex> {
+    ) -> Result<()> {
         let dtor = Arc::new(crate::func::HostFunc::wrap(
             &self.engine,
             move |mut cx: crate::Caller<'_, T>, param: u32| dtor(cx.as_context_mut(), param),
         ));
-        let idx = ResourceImportIndex::new(*self.resource_imports);
-        *self.resource_imports = self
-            .resource_imports
-            .checked_add(1)
-            .context("resource import count would overflow")?;
-        self.insert(name, Definition::Resource(idx, ty, dtor))?;
-        Ok(idx)
+        self.insert(name, Definition::Resource(ty, dtor))?;
+        Ok(())
     }
 
     /// Defines a nested instance within this instance.
diff --git a/crates/wasmtime/src/runtime/component/matching.rs b/crates/wasmtime/src/runtime/component/matching.rs
index ba93a5fbf..7c47d98c9 100644
--- a/crates/wasmtime/src/runtime/component/matching.rs
+++ b/crates/wasmtime/src/runtime/component/matching.rs
@@ -53,7 +53,7 @@ impl TypeChecker<'_> {
             TypeDef::Resource(i) => {
                 let i = self.types[i].ty;
                 let actual = match actual {
-                    Some(Definition::Resource(_idx, actual, _dtor)) => actual,
+                    Some(Definition::Resource(actual, _dtor)) => actual,
 
                     // If a resource is imported yet nothing was supplied then
                     // that's only successful if the resource has itself
diff --git a/crates/wasmtime/src/runtime/component/mod.rs b/crates/wasmtime/src/runtime/component/mod.rs
index f3e40cb8e..7247b93a1 100644
--- a/crates/wasmtime/src/runtime/component/mod.rs
+++ b/crates/wasmtime/src/runtime/component/mod.rs
@@ -57,11 +57,11 @@ pub use self::func::{
     ComponentNamedList, ComponentType, Func, Lift, Lower, TypedFunc, WasmList, WasmStr,
 };
 pub use self::instance::{ExportInstance, Exports, Instance, InstancePre};
-pub use self::linker::{Linker, LinkerInstance, ResourceImportIndex};
+pub use self::linker::{Linker, LinkerInstance};
 pub use self::resource_table::{ResourceTable, ResourceTableError};
 pub use self::resources::{Resource, ResourceAny};
 pub use self::types::{ResourceType, Type};
-pub use self::values::{Enum, Flags, List, OptionVal, Record, ResultVal, Tuple, Val, Variant};
+pub use self::values::Val;
 
 pub(crate) use self::resources::HostResourceData;
 
diff --git a/crates/wasmtime/src/runtime/component/resources.rs b/crates/wasmtime/src/runtime/component/resources.rs
index 6cc1e8b89..061412eda 100644
--- a/crates/wasmtime/src/runtime/component/resources.rs
+++ b/crates/wasmtime/src/runtime/component/resources.rs
@@ -1,15 +1,14 @@
 use crate::component::func::{bad_type_info, desc, LiftContext, LowerContext};
-use crate::component::instance::RuntimeImport;
-use crate::component::linker::ResourceImportIndex;
 use crate::component::matching::InstanceType;
-use crate::component::{ComponentType, InstancePre, Lift, Lower};
+use crate::component::{ComponentType, Lift, Lower};
 use crate::store::{StoreId, StoreOpaque};
 use crate::{AsContextMut, StoreContextMut, Trap};
-use anyhow::{bail, ensure, Context, Result};
+use anyhow::{bail, ensure, Result};
 use std::any::TypeId;
 use std::fmt;
 use std::marker;
 use std::mem::MaybeUninit;
+use std::ptr::NonNull;
 use std::sync::atomic::{AtomicU64, Ordering::Relaxed};
 use wasmtime_environ::component::{
     CanonicalAbiInfo, ComponentTypes, DefinedResourceIndex, InterfaceType, ResourceIndex,
@@ -336,7 +335,14 @@ pub struct HostResourceTables<'a> {
 #[derive(Default)]
 pub struct HostResourceData {
     cur_generation: u32,
-    generation_of_table_slot: Vec<u32>,
+    table_slot_metadata: Vec<TableSlot>,
+}
+
+#[derive(Copy, Clone)]
+struct TableSlot {
+    generation: u32,
+    flags: Option<InstanceFlags>,
+    dtor: Option<SendSyncPtr<VMFuncRef>>,
 }
 
 /// Host representation of an index into a table slot.
@@ -388,42 +394,43 @@ impl<'a> HostResourceTables<'a> {
     /// Lifts an `own` resource that resides in the host's tables at the `idx`
     /// specified into its `rep`.
     ///
-    /// This method additionally takes an `expected` type which the resource is
-    /// expected to have. All host resources are stored into a single table so
-    /// this is used to perform a runtime check to ensure that the resource
-    /// still has the same type as when it was originally inserted.
-    ///
     /// # Errors
     ///
-    /// Returns an error if `idx` doesn't point to a valid owned resource, if
-    /// `idx` can't be lifted as an `own` (e.g. it has active borrows), or if
-    /// the resource at `idx` does not have the type `expected`.
+    /// Returns an error if `idx` doesn't point to a valid owned resource, or
+    /// if `idx` can't be lifted as an `own` (e.g. it has active borrows).
     pub fn host_resource_lift_own(&mut self, idx: HostResourceIndex) -> Result<u32> {
-        let idx = self.validate_host_index(idx, true)?;
+        let (idx, _) = self.validate_host_index(idx, true)?;
         self.tables.resource_lift_own(None, idx)
     }
 
     /// See [`HostResourceTables::host_resource_lift_own`].
     pub fn host_resource_lift_borrow(&mut self, idx: HostResourceIndex) -> Result<u32> {
-        let idx = self.validate_host_index(idx, false)?;
+        let (idx, _) = self.validate_host_index(idx, false)?;
         self.tables.resource_lift_borrow(None, idx)
     }
 
     /// Lowers an `own` resource to be owned by the host.
     ///
     /// This returns a new index into the host's set of resource tables which
-    /// will point to the `rep` specified as well as recording that it has the
-    /// `ty` specified. The returned index is suitable for conversion into
-    /// either [`Resource`] or [`ResourceAny`].
-    pub fn host_resource_lower_own(&mut self, rep: u32) -> Result<HostResourceIndex> {
+    /// will point to the `rep` specified. The returned index is suitable for
+    /// conversion into either [`Resource`] or [`ResourceAny`].
+    ///
+    /// The `dtor` and instance `flags` are specified as well to know what
+    /// destructor to run when this resource is destroyed.
+    pub fn host_resource_lower_own(
+        &mut self,
+        rep: u32,
+        dtor: Option<NonNull<VMFuncRef>>,
+        flags: Option<InstanceFlags>,
+    ) -> Result<HostResourceIndex> {
         let idx = self.tables.resource_lower_own(None, rep)?;
-        Ok(self.new_host_index(idx))
+        Ok(self.new_host_index(idx, dtor, flags))
     }
 
     /// See [`HostResourceTables::host_resource_lower_own`].
     pub fn host_resource_lower_borrow(&mut self, rep: u32) -> Result<HostResourceIndex> {
         let idx = self.tables.resource_lower_borrow(None, rep)?;
-        Ok(self.new_host_index(idx))
+        Ok(self.new_host_index(idx, None, None))
     }
 
     /// Validates that `idx` is still valid for the host tables, notably
@@ -433,19 +440,20 @@ impl<'a> HostResourceTables<'a> {
     /// The `is_removal` option indicates whether or not this table access will
     /// end up removing the element from the host table. In such a situation the
     /// current generation number is incremented.
-    fn validate_host_index(&mut self, idx: HostResourceIndex, is_removal: bool) -> Result<u32> {
-        let actual = usize::try_from(idx.index()).ok().and_then(|i| {
-            self.host_resource_data
-                .generation_of_table_slot
-                .get(i)
-                .copied()
-        });
+    fn validate_host_index(
+        &mut self,
+        idx: HostResourceIndex,
+        is_removal: bool,
+    ) -> Result<(u32, Option<TableSlot>)> {
+        let actual = usize::try_from(idx.index())
+            .ok()
+            .and_then(|i| self.host_resource_data.table_slot_metadata.get(i).copied());
 
         // If `idx` is out-of-bounds then skip returning an error. In such a
         // situation the operation that this is guarding will return a more
         // precise error, such as a lift operation.
         if let Some(actual) = actual {
-            if actual != idx.gen() {
+            if actual.generation != idx.gen() {
                 bail!("host-owned resource is being used with the wrong type");
             }
         }
@@ -457,7 +465,7 @@ impl<'a> HostResourceTables<'a> {
             self.host_resource_data.cur_generation += 1;
         }
 
-        Ok(idx.index())
+        Ok((idx.index(), actual))
     }
 
     /// Creates a new `HostResourceIndex` which will point to the raw table
@@ -465,24 +473,37 @@ impl<'a> HostResourceTables<'a> {
     ///
     /// This will register metadata necessary to track the current generation
     /// in the returned `HostResourceIndex` as well.
-    fn new_host_index(&mut self, idx: u32) -> HostResourceIndex {
-        let list = &mut self.host_resource_data.generation_of_table_slot;
-        let gen = self.host_resource_data.cur_generation;
+    fn new_host_index(
+        &mut self,
+        idx: u32,
+        dtor: Option<NonNull<VMFuncRef>>,
+        flags: Option<InstanceFlags>,
+    ) -> HostResourceIndex {
+        let list = &mut self.host_resource_data.table_slot_metadata;
+        let info = TableSlot {
+            generation: self.host_resource_data.cur_generation,
+            flags,
+            dtor: dtor.map(SendSyncPtr::new),
+        };
         match list.get_mut(idx as usize) {
-            Some(slot) => *slot = gen,
+            Some(slot) => *slot = info,
             None => {
                 // Resource handles start at 1, not zero, so push two elements
                 // for the first resource handle.
                 if list.is_empty() {
                     assert_eq!(idx, 1);
-                    list.push(0);
+                    list.push(TableSlot {
+                        generation: 0,
+                        flags: None,
+                        dtor: None,
+                    });
                 }
                 assert_eq!(idx as usize, list.len());
-                list.push(gen);
+                list.push(info);
             }
         }
 
-        HostResourceIndex::new(idx, gen)
+        HostResourceIndex::new(idx, info.generation)
     }
 
     /// Drops a host-owned resource from host tables.
@@ -497,9 +518,12 @@ impl<'a> HostResourceTables<'a> {
     /// Returns an error if `idx` doesn't point to a valid resource, points to
     /// an `own` with active borrows, or if it doesn't have the type `expected`
     /// in the host tables.
-    pub fn host_resource_drop(&mut self, idx: HostResourceIndex) -> Result<Option<u32>> {
-        let idx = self.validate_host_index(idx, true)?;
-        self.tables.resource_drop(None, idx)
+    fn host_resource_drop(&mut self, idx: HostResourceIndex) -> Result<Option<(u32, TableSlot)>> {
+        let (idx, slot) = self.validate_host_index(idx, true)?;
+        match self.tables.resource_drop(None, idx)? {
+            Some(rep) => Ok(Some((rep, slot.unwrap()))),
+            None => Ok(None),
+        }
     }
 
     /// Lowers an `own` resource into the guest, converting the `rep` specified
@@ -663,8 +687,11 @@ where
                     // state.
                     //
                     // Afterwards this is the same as the `idx` case below.
+                    //
+                    // Note that flags/dtor are passed as `None` here since
+                    // `Resource<T>` doesn't offer destruction support.
                     ResourceState::NotInTable => {
-                        let idx = cx.host_resource_lower_own(self.rep)?;
+                        let idx = cx.host_resource_lower_own(self.rep, None, None)?;
                         let prev = self.state.swap(ResourceState::Index(idx));
                         assert_eq!(prev, ResourceState::NotInTable);
                         cx.host_resource_lift_borrow(idx)?
@@ -737,14 +764,10 @@ where
         mut store: impl AsContextMut,
     ) -> Result<Self> {
         let store = store.as_context_mut();
-        let store_id = store.0.id();
         let mut tables = HostResourceTables::new_host(store.0);
-        let ResourceAny { idx, ty, own_state } = resource;
+        let ResourceAny { idx, ty, owned } = resource;
         ensure!(ty == ResourceType::host::<T>(), "resource type mismatch");
-        let (state, rep) = if let Some(OwnState { store, dtor, flags }) = own_state {
-            assert_eq!(store_id, store, "wrong store used to convert resource");
-            assert!(dtor.is_some(), "destructor must be set");
-            assert!(flags.is_none(), "flags must not be set");
+        let (state, rep) = if owned {
             let rep = tables.host_resource_lift_own(idx)?;
             (AtomicResourceState::NOT_IN_TABLE, rep)
         } else {
@@ -759,13 +782,8 @@ where
     }
 
     /// See [`ResourceAny::try_from_resource`]
-    pub fn try_into_resource_any<U>(
-        self,
-        store: impl AsContextMut,
-        instance: &InstancePre<U>,
-        idx: ResourceImportIndex,
-    ) -> Result<ResourceAny> {
-        ResourceAny::try_from_resource(self, store, instance, idx)
+    pub fn try_into_resource_any(self, store: impl AsContextMut) -> Result<ResourceAny> {
+        ResourceAny::try_from_resource(self, store)
     }
 }
 
@@ -865,14 +883,7 @@ impl<T> fmt::Debug for Resource<T> {
 pub struct ResourceAny {
     idx: HostResourceIndex,
     ty: ResourceType,
-    own_state: Option<OwnState>,
-}
-
-#[derive(Copy, Clone)]
-struct OwnState {
-    store: StoreId,
-    flags: Option<InstanceFlags>,
-    dtor: Option<SendSyncPtr<VMFuncRef>>,
+    owned: bool,
 }
 
 impl ResourceAny {
@@ -880,63 +891,38 @@ impl ResourceAny {
     ///
     /// * `resource` is the resource to convert.
     /// * `store` is the store to place the returned resource into.
-    /// * `instance_pre` is the instance from where `idx` below was derived.
-    /// * `idx` is the [`ResourceImportIndex`] returned by [`Linker::resource`].
     ///
-    /// [`Linker::resource`]: crate::component::LinkerInstance::resource
+    /// The returned `ResourceAny` will not have a destructor attached to it
+    /// meaning that if `resource_drop` is called then it will not invoked a
+    /// host-defined destructor. This is similar to how `Resource<T>` does not
+    /// have a destructor associated with it.
     ///
     /// # Errors
     ///
-    /// This method will return an error if `idx` isn't valid for
-    /// `instance_pre` or if `resource` is not of the correct type for the
-    /// `idx` import.
-    pub fn try_from_resource<T: 'static, U>(
+    /// This method will return an error if `resource` has already been "taken"
+    /// and has ownership transferred elsewhere which can happen in situations
+    /// such as when it's already lowered into a component.
+    pub fn try_from_resource<T: 'static>(
         resource: Resource<T>,
         mut store: impl AsContextMut,
-        instance_pre: &InstancePre<U>,
-        idx: ResourceImportIndex,
     ) -> Result<Self> {
         let Resource { rep, state, .. } = resource;
         let store = store.as_context_mut();
-        let import = instance_pre
-            .resource_import(idx)
-            .context("import not found")?;
-        let RuntimeImport::Resource {
-            ty, dtor_funcref, ..
-        } = import
-        else {
-            bail!("import is not a resource")
-        };
-        ensure!(*ty == ResourceType::host::<T>(), "resource type mismatch");
 
         let mut tables = HostResourceTables::new_host(store.0);
-        let (idx, own_state) = match state.get() {
-            ResourceState::Borrow => (tables.host_resource_lower_borrow(rep)?, None),
+        let (idx, owned) = match state.get() {
+            ResourceState::Borrow => (tables.host_resource_lower_borrow(rep)?, false),
             ResourceState::NotInTable => {
-                let idx = tables.host_resource_lower_own(rep)?;
-                (
-                    idx,
-                    Some(OwnState {
-                        dtor: Some(dtor_funcref.into()),
-                        flags: None,
-                        store: store.0.id(),
-                    }),
-                )
+                let idx = tables.host_resource_lower_own(rep, None, None)?;
+                (idx, true)
             }
             ResourceState::Taken => bail!("host resource already consumed"),
-            ResourceState::Index(idx) => (
-                idx,
-                Some(OwnState {
-                    dtor: Some(dtor_funcref.into()),
-                    flags: None,
-                    store: store.0.id(),
-                }),
-            ),
+            ResourceState::Index(idx) => (idx, true),
         };
         Ok(Self {
             idx,
-            ty: *ty,
-            own_state,
+            ty: ResourceType::host::<T>(),
+            owned,
         })
     }
 
@@ -960,7 +946,7 @@ impl ResourceAny {
     /// Returns whether this is an owned resource, and if not it's a borrowed
     /// resource.
     pub fn owned(&self) -> bool {
-        self.own_state.is_some()
+        self.owned
     }
 
     /// Destroy this resource and release any state associated with it.
@@ -1002,33 +988,25 @@ impl ResourceAny {
         //
         // This could fail if the index is invalid or if this is removing an
         // `Own` entry which is currently being borrowed.
-        let rep = HostResourceTables::new_host(store.0).host_resource_drop(self.idx)?;
+        let pair = HostResourceTables::new_host(store.0).host_resource_drop(self.idx)?;
 
-        let (rep, state) = match (rep, &self.own_state) {
-            (Some(rep), Some(state)) => (rep, state),
+        let (rep, slot) = match (pair, self.owned) {
+            (Some(pair), true) => pair,
 
             // A `borrow` was removed from the table and no further
             // destruction, e.g. the destructor, is required so we're done.
-            (None, None) => return Ok(()),
+            (None, false) => return Ok(()),
 
             _ => unreachable!(),
         };
 
-        // Double-check that accessing the raw pointers on `state` are safe due
-        // to the presence of `store` above.
-        assert_eq!(
-            store.0.id(),
-            state.store,
-            "wrong store used to destroy resource"
-        );
-
         // Implement the reentrance check required by the canonical ABI. Note
         // that this happens whether or not a destructor is present.
         //
         // Note that this should be safe because the raw pointer access in
         // `flags` is valid due to `store` being the owner of the flags and
         // flags are never destroyed within the store.
-        if let Some(flags) = state.flags {
+        if let Some(flags) = slot.flags {
             unsafe {
                 if !flags.may_enter() {
                     bail!(Trap::CannotEnterComponent);
@@ -1036,7 +1014,7 @@ impl ResourceAny {
             }
         }
 
-        let dtor = match state.dtor {
+        let dtor = match slot.dtor {
             Some(dtor) => dtor.as_non_null(),
             None => return Ok(()),
         };
@@ -1075,15 +1053,11 @@ impl ResourceAny {
             InterfaceType::Own(t) => {
                 let ty = cx.resource_type(t);
                 let (rep, dtor, flags) = cx.guest_resource_lift_own(t, index)?;
-                let idx = cx.host_resource_lower_own(rep)?;
+                let idx = cx.host_resource_lower_own(rep, dtor, flags)?;
                 Ok(ResourceAny {
                     idx,
                     ty,
-                    own_state: Some(OwnState {
-                        dtor: dtor.map(SendSyncPtr::new),
-                        flags,
-                        store: cx.store_id(),
-                    }),
+                    owned: true,
                 })
             }
             InterfaceType::Borrow(t) => {
@@ -1093,7 +1067,7 @@ impl ResourceAny {
                 Ok(ResourceAny {
                     idx,
                     ty,
-                    own_state: None,
+                    owned: false,
                 })
             }
             _ => bad_type_info(),
@@ -1147,23 +1121,3 @@ unsafe impl Lift for ResourceAny {
         ResourceAny::lift_from_index(cx, ty, index)
     }
 }
-
-impl fmt::Debug for OwnState {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_struct("OwnState")
-            .field("store", &self.store)
-            .finish()
-    }
-}
-
-// This is a loose definition for `Val` primarily so it doesn't need to be
-// strictly 100% correct, and equality of resources is a bit iffy anyway, so
-// ignore equality here and only factor in the indices and other metadata in
-// `ResourceAny`.
-impl PartialEq for OwnState {
-    fn eq(&self, _other: &OwnState) -> bool {
-        true
-    }
-}
-
-impl Eq for OwnState {}
diff --git a/crates/wasmtime/src/runtime/component/types.rs b/crates/wasmtime/src/runtime/component/types.rs
index 06b29d47e..b40780b5d 100644
--- a/crates/wasmtime/src/runtime/component/types.rs
+++ b/crates/wasmtime/src/runtime/component/types.rs
@@ -1,18 +1,15 @@
 //! This module defines the `Type` type, representing the dynamic form of a component interface type.
 
 use crate::component::matching::InstanceType;
-use crate::component::values::{self, Val};
 use crate::{Engine, ExternType, FuncType};
-use anyhow::{anyhow, Result};
 use std::fmt;
-use std::mem;
 use std::ops::Deref;
 use std::sync::Arc;
 use wasmtime_environ::component::{
-    CanonicalAbiInfo, ComponentTypes, InterfaceType, ResourceIndex, TypeComponentIndex,
-    TypeComponentInstanceIndex, TypeDef, TypeEnumIndex, TypeFlagsIndex, TypeFuncIndex,
-    TypeListIndex, TypeModuleIndex, TypeOptionIndex, TypeRecordIndex, TypeResourceTableIndex,
-    TypeResultIndex, TypeTupleIndex, TypeVariantIndex,
+    ComponentTypes, InterfaceType, ResourceIndex, TypeComponentIndex, TypeComponentInstanceIndex,
+    TypeDef, TypeEnumIndex, TypeFlagsIndex, TypeFuncIndex, TypeListIndex, TypeModuleIndex,
+    TypeOptionIndex, TypeRecordIndex, TypeResourceTableIndex, TypeResultIndex, TypeTupleIndex,
+    TypeVariantIndex,
 };
 use wasmtime_environ::PrimaryMap;
 
@@ -183,18 +180,21 @@ impl TypeChecker<'_> {
         if a.cases.len() != b.cases.len() {
             return false;
         }
-        a.cases.iter().zip(b.cases.iter()).all(|(a_case, b_case)| {
-            if a_case.name != b_case.name {
-                return false;
-            }
-            match (a_case.ty, b_case.ty) {
-                (Some(a_case_ty), Some(b_case_ty)) => {
-                    self.interface_types_equal(a_case_ty, b_case_ty)
+        a.cases
+            .iter()
+            .zip(b.cases.iter())
+            .all(|((a_name, a_ty), (b_name, b_ty))| {
+                if a_name != b_name {
+                    return false;
                 }
-                (None, None) => true,
-                _ => false,
-            }
-        })
+                match (a_ty, b_ty) {
+                    (Some(a_case_ty), Some(b_case_ty)) => {
+                        self.interface_types_equal(*a_case_ty, *b_case_ty)
+                    }
+                    (None, None) => true,
+                    _ => false,
+                }
+            })
     }
 
     fn results_equal(&self, r1: TypeResultIndex, r2: TypeResultIndex) -> bool {
@@ -259,11 +259,6 @@ impl PartialEq for List {
 impl Eq for List {}
 
 impl List {
-    /// Instantiate this type with the specified `values`.
-    pub fn new_val(&self, values: Box<[Val]>) -> Result<Val> {
-        Ok(Val::List(values::List::new(self, values)?))
-    }
-
     pub(crate) fn from(index: TypeListIndex, ty: &InstanceType<'_>) -> Self {
         List(Handle::new(index, ty))
     }
@@ -288,11 +283,6 @@ pub struct Field<'a> {
 pub struct Record(Handle<TypeRecordIndex>);
 
 impl Record {
-    /// Instantiate this type with the specified `values`.
-    pub fn new_val<'a>(&self, values: impl IntoIterator<Item = (&'a str, Val)>) -> Result<Val> {
-        Ok(Val::Record(values::Record::new(self, values)?))
-    }
-
     pub(crate) fn from(index: TypeRecordIndex, ty: &InstanceType<'_>) -> Self {
         Record(Handle::new(index, ty))
     }
@@ -319,11 +309,6 @@ impl Eq for Record {}
 pub struct Tuple(Handle<TypeTupleIndex>);
 
 impl Tuple {
-    /// Instantiate this type ith the specified `values`.
-    pub fn new_val(&self, values: Box<[Val]>) -> Result<Val> {
-        Ok(Val::Tuple(values::Tuple::new(self, values)?))
-    }
-
     pub(crate) fn from(index: TypeTupleIndex, ty: &InstanceType<'_>) -> Self {
         Tuple(Handle::new(index, ty))
     }
@@ -358,24 +343,19 @@ pub struct Case<'a> {
 pub struct Variant(Handle<TypeVariantIndex>);
 
 impl Variant {
-    /// Instantiate this type with the specified case `name` and `value`.
-    pub fn new_val(&self, name: &str, value: Option<Val>) -> Result<Val> {
-        Ok(Val::Variant(values::Variant::new(self, name, value)?))
-    }
-
     pub(crate) fn from(index: TypeVariantIndex, ty: &InstanceType<'_>) -> Self {
         Variant(Handle::new(index, ty))
     }
 
     /// Retrieve the cases of this `variant` in declaration order.
     pub fn cases(&self) -> impl ExactSizeIterator<Item = Case> {
-        self.0.types[self.0.index].cases.iter().map(|case| Case {
-            name: &case.name,
-            ty: case
-                .ty
-                .as_ref()
-                .map(|ty| Type::from(ty, &self.0.instance())),
-        })
+        self.0.types[self.0.index]
+            .cases
+            .iter()
+            .map(|(name, ty)| Case {
+                name: name,
+                ty: ty.as_ref().map(|ty| Type::from(ty, &self.0.instance())),
+            })
     }
 }
 
@@ -392,11 +372,6 @@ impl Eq for Variant {}
 pub struct Enum(Handle<TypeEnumIndex>);
 
 impl Enum {
-    /// Instantiate this type with the specified case `name`.
-    pub fn new_val(&self, name: &str) -> Result<Val> {
-        Ok(Val::Enum(values::Enum::new(self, name)?))
-    }
-
     pub(crate) fn from(index: TypeEnumIndex, ty: &InstanceType<'_>) -> Self {
         Enum(Handle::new(index, ty))
     }
@@ -423,11 +398,6 @@ impl Eq for Enum {}
 pub struct OptionType(Handle<TypeOptionIndex>);
 
 impl OptionType {
-    /// Instantiate this type with the specified `value`.
-    pub fn new_val(&self, value: Option<Val>) -> Result<Val> {
-        Ok(Val::Option(values::OptionVal::new(self, value)?))
-    }
-
     pub(crate) fn from(index: TypeOptionIndex, ty: &InstanceType<'_>) -> Self {
         OptionType(Handle::new(index, ty))
     }
@@ -451,11 +421,6 @@ impl Eq for OptionType {}
 pub struct ResultType(Handle<TypeResultIndex>);
 
 impl ResultType {
-    /// Instantiate this type with the specified `value`.
-    pub fn new_val(&self, value: Result<Option<Val>, Option<Val>>) -> Result<Val> {
-        Ok(Val::Result(values::ResultVal::new(self, value)?))
-    }
-
     pub(crate) fn from(index: TypeResultIndex, ty: &InstanceType<'_>) -> Self {
         ResultType(Handle::new(index, ty))
     }
@@ -490,11 +455,6 @@ impl Eq for ResultType {}
 pub struct Flags(Handle<TypeFlagsIndex>);
 
 impl Flags {
-    /// Instantiate this type with the specified flag `names`.
-    pub fn new_val(&self, names: &[&str]) -> Result<Val> {
-        Ok(Val::Flags(values::Flags::new(self, names)?))
-    }
-
     pub(crate) fn from(index: TypeFlagsIndex, ty: &InstanceType<'_>) -> Self {
         Flags(Handle::new(index, ty))
     }
@@ -506,10 +466,6 @@ impl Flags {
             .iter()
             .map(|name| name.deref())
     }
-
-    pub(crate) fn canonical_abi(&self) -> &CanonicalAbiInfo {
-        &self.0.types[self.0.index].abi
-    }
 }
 
 impl PartialEq for Flags {
@@ -678,31 +634,6 @@ impl Type {
         }
     }
 
-    /// Checks whether type of `value` is a subtype of `self`.
-    ///
-    /// # Errors
-    ///
-    /// Returns an error in case of a type mismatch
-    pub(crate) fn is_supertype_of(&self, value: &Val) -> Result<()> {
-        let other = &value.ty();
-        if self == other
-            || matches!((self, other), (Self::Borrow(s), Self::Own(other)) if s == other)
-        {
-            Ok(())
-        } else if mem::discriminant(self) != mem::discriminant(other) {
-            Err(anyhow!(
-                "type mismatch: expected {}, got {}",
-                self.desc(),
-                other.desc()
-            ))
-        } else {
-            Err(anyhow!(
-                "type mismatch for {}, possibly due to mixing distinct composite types",
-                self.desc()
-            ))
-        }
-    }
-
     /// Convert the specified `InterfaceType` to a `Type`.
     pub(crate) fn from(ty: &InterfaceType, instance: &InstanceType<'_>) -> Self {
         match ty {
diff --git a/crates/wasmtime/src/runtime/component/values.rs b/crates/wasmtime/src/runtime/component/values.rs
index 52d08d120..f6bfd1893 100644
--- a/crates/wasmtime/src/runtime/component/values.rs
+++ b/crates/wasmtime/src/runtime/component/values.rs
@@ -1,640 +1,14 @@
-use crate::component::func::{bad_type_info, Lift, LiftContext, Lower, LowerContext};
-use crate::component::types::{self, Type};
+use crate::component::func::{desc, Lift, LiftContext, Lower, LowerContext};
 use crate::component::ResourceAny;
 use crate::ValRaw;
-use anyhow::{anyhow, bail, Context, Error, Result};
-use std::collections::HashMap;
-use std::fmt;
-use std::iter;
+use anyhow::{anyhow, bail, Result};
 use std::mem::MaybeUninit;
-use std::ops::Deref;
 use wasmtime_component_util::{DiscriminantSize, FlagsSize};
 use wasmtime_environ::component::{
-    CanonicalAbiInfo, ComponentTypes, InterfaceType, TypeListIndex, VariantInfo,
+    CanonicalAbiInfo, InterfaceType, TypeEnum, TypeFlags, TypeListIndex, TypeOption, TypeResult,
+    TypeVariant, VariantInfo,
 };
 
-/// Dynamic representation of `list<T>` in the component model.
-///
-/// This structure is part of the dynamically-typed [`Val`] which represents
-/// all possible values in the component model. This is often used in
-/// conjunction with [`Func::call`](crate::component::Func::call).
-///
-/// Note that `Vec<T>` is also supported for component model lists when
-/// using [`TypedFunc`](crate::component::TypedFunc).
-#[derive(PartialEq, Eq, Clone)]
-pub struct List {
-    ty: types::List,
-    values: Box<[Val]>,
-}
-
-impl List {
-    /// Creates a new list with the specified type with the specified `values`.
-    ///
-    /// # Errors
-    ///
-    /// Returns an error if any value in `values` does not have the element
-    /// type of `ty` specified.
-    pub fn new(ty: &types::List, values: Box<[Val]>) -> Result<Self> {
-        let element_type = ty.ty();
-        for (index, value) in values.iter().enumerate() {
-            element_type
-                .is_supertype_of(value)
-                .with_context(|| format!("type mismatch for element {index} of list"))?;
-        }
-
-        Ok(Self {
-            ty: ty.clone(),
-            values,
-        })
-    }
-
-    /// Returns the corresponding type of this list
-    pub fn ty(&self) -> &types::List {
-        &self.ty
-    }
-}
-
-impl Deref for List {
-    type Target = [Val];
-
-    fn deref(&self) -> &[Val] {
-        &self.values
-    }
-}
-
-impl fmt::Debug for List {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let mut f = f.debug_list();
-        for val in self.iter() {
-            f.entry(val);
-        }
-        f.finish()
-    }
-}
-
-/// Dynamic representation of `record` in the component model.
-///
-/// This structure is part of the dynamically-typed [`Val`] which represents
-/// all possible values in the component model. This is often used in
-/// conjunction with [`Func::call`](crate::component::Func::call).
-///
-/// Note that records can also be mapped to Rust `struct`s through the
-/// [`#[derive(ComponentType)]`](macro@crate::component::ComponentType) macro
-/// and using [`TypedFunc`](crate::component::TypedFunc).
-#[derive(PartialEq, Eq, Clone)]
-pub struct Record {
-    ty: types::Record,
-    values: Box<[Val]>,
-}
-
-impl Record {
-    /// Creates a new record with the specified `ty` and the `values` for fields.
-    ///
-    /// Note that the `values` specified for fields must list fields in the
-    /// same order that the `ty` lists them in.
-    ///
-    /// # Errors
-    ///
-    /// Returns an error if any of the `values` specified are in the wrong order
-    /// field-wise or have the wrong type relative to what's specified in `ty`.
-    pub fn new<'a>(
-        ty: &types::Record,
-        values: impl IntoIterator<Item = (&'a str, Val)>,
-    ) -> Result<Self> {
-        let mut fields = ty.fields();
-        let expected_len = fields.len();
-        let mut iter = values.into_iter();
-        let mut values = Vec::with_capacity(expected_len);
-        loop {
-            match (fields.next(), iter.next()) {
-                (Some(field), Some((name, value))) => {
-                    if name == field.name {
-                        field
-                            .ty
-                            .is_supertype_of(&value)
-                            .with_context(|| format!("type mismatch for field {name} of record"))?;
-
-                        values.push(value);
-                    } else {
-                        bail!("field name mismatch: expected {}; got {name}", field.name)
-                    }
-                }
-                (None, Some((_, value))) => values.push(value),
-                _ => break,
-            }
-        }
-
-        if values.len() != expected_len {
-            bail!("expected {} value(s); got {}", expected_len, values.len());
-        }
-
-        Ok(Self {
-            ty: ty.clone(),
-            values: values.into(),
-        })
-    }
-
-    /// Returns the corresponding type of this record.
-    pub fn ty(&self) -> &types::Record {
-        &self.ty
-    }
-
-    /// Returns the list of fields that this struct contains with their name
-    /// and value.
-    pub fn fields(&self) -> impl ExactSizeIterator<Item = (&str, &Val)> {
-        assert_eq!(self.values.len(), self.ty.fields().len());
-        self.ty
-            .fields()
-            .zip(self.values.iter())
-            .map(|(ty, val)| (ty.name, val))
-    }
-}
-
-impl fmt::Debug for Record {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let mut f = f.debug_struct("Record");
-        for (name, val) in self.fields() {
-            f.field(name, val);
-        }
-        f.finish()
-    }
-}
-
-/// Dynamic representation of `tuple<...>` in the component model.
-///
-/// This structure is part of the dynamically-typed [`Val`] which represents
-/// all possible values in the component model. This is often used in
-/// conjunction with [`Func::call`](crate::component::Func::call).
-///
-/// Note that tuples are also mapped to Rust tuples when using
-/// [`TypedFunc`](crate::component::TypedFunc).
-#[derive(PartialEq, Eq, Clone)]
-pub struct Tuple {
-    ty: types::Tuple,
-    values: Box<[Val]>,
-}
-
-impl Tuple {
-    /// Creates a new tuple with the `ty` specified and `values` for each
-    /// entry.
-    ///
-    /// # Errors
-    ///
-    /// Returns an error if `values` does not match the `ty` specified, such as
-    /// if it has the wrong length or if any field has the wrong type.
-    pub fn new(ty: &types::Tuple, values: Box<[Val]>) -> Result<Self> {
-        if values.len() != ty.types().len() {
-            bail!(
-                "expected {} value(s); got {}",
-                ty.types().len(),
-                values.len()
-            );
-        }
-
-        for (index, (value, ty)) in values.iter().zip(ty.types()).enumerate() {
-            ty.is_supertype_of(value)
-                .with_context(|| format!("type mismatch for field {index} of tuple"))?;
-        }
-
-        Ok(Self {
-            ty: ty.clone(),
-            values,
-        })
-    }
-
-    /// Returns the type of this tuple.
-    pub fn ty(&self) -> &types::Tuple {
-        &self.ty
-    }
-
-    /// Returns the list of values that this tuple contains.
-    pub fn values(&self) -> &[Val] {
-        &self.values
-    }
-}
-
-impl fmt::Debug for Tuple {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let mut tuple = f.debug_tuple("");
-        for val in self.values() {
-            tuple.field(val);
-        }
-        tuple.finish()
-    }
-}
-
-/// Dynamic representation of `variant` in the component model.
-///
-/// This structure is part of the dynamically-typed [`Val`] which represents
-/// all possible values in the component model. This is often used in
-/// conjunction with [`Func::call`](crate::component::Func::call).
-///
-/// Note that variants can also be mapped to Rust `enum`s through the
-/// [`#[derive(ComponentType)]`](macro@crate::component::ComponentType) macro
-/// and using [`TypedFunc`](crate::component::TypedFunc).
-#[derive(PartialEq, Eq, Clone)]
-pub struct Variant {
-    ty: types::Variant,
-    discriminant: u32,
-    value: Option<Box<Val>>,
-}
-
-impl Variant {
-    /// Creates a new variant with the specified `ty` and the case `name` and
-    /// `value` provided.
-    ///
-    /// This will create a instance of a variant with the case `name` which has
-    /// the optional payload of `value`.
-    ///
-    /// # Errors
-    ///
-    /// Returns an error if `name` doesn't match any case of `ty` or if
-    /// `value` is of the wrong type. Note that `ty` must match the payload
-    /// type exactly, in that if the case for `name` doesn't have a type then
-    /// `value` must be `None`.
-    pub fn new(ty: &types::Variant, name: &str, value: Option<Val>) -> Result<Self> {
-        let (discriminant, case_type) = ty
-            .cases()
-            .enumerate()
-            .find_map(|(index, case)| {
-                if case.name == name {
-                    Some((index, case.ty))
-                } else {
-                    None
-                }
-            })
-            .ok_or_else(|| anyhow!("unknown variant case: {name}"))?;
-
-        typecheck_payload(name, case_type.as_ref(), value.as_ref())?;
-
-        Ok(Self {
-            ty: ty.clone(),
-            discriminant: u32::try_from(discriminant)?,
-            value: value.map(Box::new),
-        })
-    }
-
-    /// Returns the type of this variant.
-    pub fn ty(&self) -> &types::Variant {
-        &self.ty
-    }
-
-    /// Returns name of the discriminant of this value within the variant type.
-    pub fn discriminant(&self) -> &str {
-        self.ty
-            .cases()
-            .nth(self.discriminant as usize)
-            .unwrap()
-            .name
-    }
-
-    /// Returns the payload value for this variant.
-    pub fn payload(&self) -> Option<&Val> {
-        self.value.as_deref()
-    }
-
-    fn as_generic<'a>(
-        &'a self,
-        types: &'a ComponentTypes,
-        ty: InterfaceType,
-    ) -> GenericVariant<'a> {
-        let ty = match ty {
-            InterfaceType::Variant(i) => &types[i],
-            _ => bad_type_info(),
-        };
-        GenericVariant {
-            discriminant: self.discriminant,
-            abi: &ty.abi,
-            info: &ty.info,
-            payload: self
-                .value
-                .as_deref()
-                .zip(ty.cases[self.discriminant as usize].ty),
-        }
-    }
-}
-
-fn typecheck_payload(name: &str, case_type: Option<&Type>, value: Option<&Val>) -> Result<()> {
-    match (case_type, value) {
-        (Some(expected), Some(actual)) => expected
-            .is_supertype_of(&actual)
-            .with_context(|| format!("type mismatch for case {name} of variant")),
-        (None, None) => Ok(()),
-        (Some(_), None) => bail!("expected a payload for case `{name}`"),
-        (None, Some(_)) => bail!("did not expect payload for case `{name}`"),
-    }
-}
-
-impl fmt::Debug for Variant {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_tuple(self.discriminant())
-            .field(&self.payload())
-            .finish()
-    }
-}
-
-/// Dynamic representation of `enum` in the component model.
-///
-/// This structure is part of the dynamically-typed [`Val`] which represents
-/// all possible values in the component model. This is often used in
-/// conjunction with [`Func::call`](crate::component::Func::call).
-///
-/// Note that enums can also be mapped to Rust `enum`s through the
-/// [`#[derive(ComponentType)]`](macro@crate::component::ComponentType) macro
-/// and using [`TypedFunc`](crate::component::TypedFunc).
-#[derive(PartialEq, Eq, Clone)]
-pub struct Enum {
-    ty: types::Enum,
-    discriminant: u32,
-}
-
-impl Enum {
-    /// Creates a new enum value with the `ty` specified selecting the
-    /// specified variant called `name`.
-    ///
-    /// # Errors
-    ///
-    /// Returns an error if `name` isn't listed within `ty`.
-    pub fn new(ty: &types::Enum, name: &str) -> Result<Self> {
-        let discriminant = u32::try_from(
-            ty.names()
-                .position(|n| n == name)
-                .ok_or_else(|| anyhow!("unknown enum case: {name}"))?,
-        )?;
-
-        Ok(Self {
-            ty: ty.clone(),
-            discriminant,
-        })
-    }
-
-    /// Returns the type of this value.
-    pub fn ty(&self) -> &types::Enum {
-        &self.ty
-    }
-
-    /// Returns name of this enum value.
-    pub fn discriminant(&self) -> &str {
-        self.ty.names().nth(self.discriminant as usize).unwrap()
-    }
-
-    fn as_generic<'a>(
-        &'a self,
-        types: &'a ComponentTypes,
-        ty: InterfaceType,
-    ) -> GenericVariant<'a> {
-        let ty = match ty {
-            InterfaceType::Enum(i) => &types[i],
-            _ => bad_type_info(),
-        };
-        GenericVariant {
-            discriminant: self.discriminant,
-            abi: &ty.abi,
-            info: &ty.info,
-            payload: None,
-        }
-    }
-}
-
-impl fmt::Debug for Enum {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        fmt::Display::fmt(&self.discriminant(), f)
-    }
-}
-
-/// Dynamic representation of `option<T>` in the component model.
-///
-/// This structure is part of the dynamically-typed [`Val`] which represents
-/// all possible values in the component model. This is often used in
-/// conjunction with [`Func::call`](crate::component::Func::call).
-///
-/// Note that options are also mapped to Rust `Option<T>` when using
-/// [`TypedFunc`](crate::component::TypedFunc).
-#[derive(PartialEq, Eq, Clone)]
-pub struct OptionVal {
-    ty: types::OptionType,
-    discriminant: u32,
-    value: Option<Box<Val>>,
-}
-
-impl OptionVal {
-    /// Creates a new value with the `ty` specified holding the payload `value`.
-    ///
-    /// # Errors
-    ///
-    /// Returns an error if `value` is `Some` and the payload has the wrong
-    /// type for `ty`.
-    pub fn new(ty: &types::OptionType, value: Option<Val>) -> Result<Self> {
-        let value = value
-            .map(|value| {
-                ty.ty()
-                    .is_supertype_of(&value)
-                    .context("type mismatch for option")?;
-
-                Ok::<_, Error>(value)
-            })
-            .transpose()?;
-
-        Ok(Self {
-            ty: ty.clone(),
-            discriminant: if value.is_none() { 0 } else { 1 },
-            value: value.map(Box::new),
-        })
-    }
-
-    /// Returns the type of this value.
-    pub fn ty(&self) -> &types::OptionType {
-        &self.ty
-    }
-
-    /// Returns the optional value contained within.
-    pub fn value(&self) -> Option<&Val> {
-        self.value.as_deref()
-    }
-
-    fn as_generic<'a>(
-        &'a self,
-        types: &'a ComponentTypes,
-        ty: InterfaceType,
-    ) -> GenericVariant<'a> {
-        let ty = match ty {
-            InterfaceType::Option(i) => &types[i],
-            _ => bad_type_info(),
-        };
-        GenericVariant {
-            discriminant: self.discriminant,
-            abi: &ty.abi,
-            info: &ty.info,
-            payload: self.value.as_deref().zip(Some(ty.ty)),
-        }
-    }
-}
-
-impl fmt::Debug for OptionVal {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        self.value().fmt(f)
-    }
-}
-
-/// Dynamic representation of `result<T, E>` in the component model.
-///
-/// This structure is part of the dynamically-typed [`Val`] which represents
-/// all possible values in the component model. This is often used in
-/// conjunction with [`Func::call`](crate::component::Func::call).
-///
-/// Note that tuples are also mapped to Rust's `Result<T, E>` when using
-/// [`TypedFunc`](crate::component::TypedFunc).
-#[derive(PartialEq, Eq, Clone)]
-pub struct ResultVal {
-    ty: types::ResultType,
-    discriminant: u32,
-    value: Option<Box<Val>>,
-}
-
-impl ResultVal {
-    /// Creates a new `Result` from the specified `ty` and `value`.
-    ///
-    /// Note that the `value` uses `Option<Val>` for payloads to map to the
-    /// optional payload value of the component model within each variant.
-    ///
-    /// # Errors
-    ///
-    /// Returns an error if the `value` payload has the wrong type for `ty`.
-    pub fn new(ty: &types::ResultType, value: Result<Option<Val>, Option<Val>>) -> Result<Self> {
-        Ok(Self {
-            ty: ty.clone(),
-            discriminant: if value.is_ok() { 0 } else { 1 },
-            value: match value {
-                Ok(value) => {
-                    typecheck_payload("ok", ty.ok().as_ref(), value.as_ref())?;
-                    value.map(Box::new)
-                }
-                Err(value) => {
-                    typecheck_payload("err", ty.err().as_ref(), value.as_ref())?;
-                    value.map(Box::new)
-                }
-            },
-        })
-    }
-
-    /// Returns the type of this value.
-    pub fn ty(&self) -> &types::ResultType {
-        &self.ty
-    }
-
-    /// Returns the result value contained within.
-    pub fn value(&self) -> Result<Option<&Val>, Option<&Val>> {
-        if self.discriminant == 0 {
-            Ok(self.value.as_deref())
-        } else {
-            Err(self.value.as_deref())
-        }
-    }
-
-    fn as_generic<'a>(
-        &'a self,
-        types: &'a ComponentTypes,
-        ty: InterfaceType,
-    ) -> GenericVariant<'a> {
-        let ty = match ty {
-            InterfaceType::Result(i) => &types[i],
-            _ => bad_type_info(),
-        };
-        GenericVariant {
-            discriminant: self.discriminant,
-            abi: &ty.abi,
-            info: &ty.info,
-            payload: self.value.as_deref().zip(if self.discriminant == 0 {
-                ty.ok
-            } else {
-                ty.err
-            }),
-        }
-    }
-}
-
-impl fmt::Debug for ResultVal {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        self.value().fmt(f)
-    }
-}
-
-/// Dynamic representation of `flags` in the component model.
-///
-/// This structure is part of the dynamically-typed [`Val`] which represents
-/// all possible values in the component model. This is often used in
-/// conjunction with [`Func::call`](crate::component::Func::call).
-///
-/// Note that flags can also be mapped to Rust structures through the
-/// [`flags!`](crate::component::flags) macro and using
-/// [`TypedFunc`](crate::component::TypedFunc).
-#[derive(PartialEq, Eq, Clone)]
-pub struct Flags {
-    ty: types::Flags,
-    count: u32,
-    value: Box<[u32]>,
-}
-
-impl Flags {
-    /// Creates a new set of flags with the `ty` specified and the `names` as
-    /// members of the flags.
-    ///
-    /// # Errors
-    ///
-    /// An error is returned if any of `names` aren't present within the `ty`
-    /// specified.
-    pub fn new(ty: &types::Flags, names: &[&str]) -> Result<Self> {
-        let map = ty
-            .names()
-            .enumerate()
-            .map(|(index, name)| (name, index))
-            .collect::<HashMap<_, _>>();
-
-        let count = usize::from(ty.canonical_abi().flat_count.unwrap());
-        let mut values = vec![0_u32; count];
-
-        for name in names {
-            let index = map
-                .get(name)
-                .ok_or_else(|| anyhow!("unknown flag: {name}"))?;
-            values[index / 32] |= 1 << (index % 32);
-        }
-
-        Ok(Self {
-            ty: ty.clone(),
-            count: u32::try_from(map.len())?,
-            value: values.into(),
-        })
-    }
-
-    /// Returns the type of this value.
-    pub fn ty(&self) -> &types::Flags {
-        &self.ty
-    }
-
-    /// Returns an iterator over the set of names that this flags set contains.
-    pub fn flags(&self) -> impl Iterator<Item = &str> {
-        (0..self.count).filter_map(|i| {
-            let (idx, bit) = ((i / 32) as usize, i % 32);
-            if self.value[idx] & (1 << bit) != 0 {
-                Some(self.ty.names().nth(i as usize).unwrap())
-            } else {
-                None
-            }
-        })
-    }
-}
-
-impl fmt::Debug for Flags {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let mut set = f.debug_set();
-        for flag in self.flags() {
-            set.entry(&flag);
-        }
-        set.finish()
-    }
-}
-
 /// Represents possible runtime values which a component function can either
 /// consume or produce
 ///
@@ -647,7 +21,7 @@ impl fmt::Debug for Flags {
 /// This type is used in conjunction with [`Func::call`] for example if the
 /// signature of a component is not statically known ahead of time.
 ///
-/// # Notes on Equality
+/// # Equality and `Val`
 ///
 /// This type implements both the Rust `PartialEq` and `Eq` traits. This type
 /// additionally contains values which are not necessarily easily equated,
@@ -673,6 +47,18 @@ impl fmt::Debug for Flags {
 /// recommended to "build your own" as this equality intended for fuzzing
 /// Wasmtime may not be suitable for you.
 ///
+/// # Component model types and `Val`
+///
+/// The `Val` type here does not contain enough information to say what the
+/// component model type of a `Val` is. This is instead more of an AST of sorts.
+/// For example the `Val::Enum` only carries information about a single
+/// discriminant, not the entire enumeration or what it's a discriminant of.
+///
+/// This means that when a `Val` is passed to Wasmtime, for example as a
+/// function parameter when calling a function or as a return value from an
+/// host-defined imported function, then it must pass a type-check. Instances of
+/// `Val` are type-checked against what's required by the component itself.
+///
 /// [`Func::call`]: crate::component::Func::call
 #[derive(Debug, Clone)]
 #[allow(missing_docs)]
@@ -689,53 +75,19 @@ pub enum Val {
     Float32(f32),
     Float64(f64),
     Char(char),
-    String(Box<str>),
-    List(List),
-    Record(Record),
-    Tuple(Tuple),
-    Variant(Variant),
-    Enum(Enum),
-    Option(OptionVal),
-    Result(ResultVal),
-    Flags(Flags),
+    String(String),
+    List(Vec<Val>),
+    Record(Vec<(String, Val)>),
+    Tuple(Vec<Val>),
+    Variant(String, Option<Box<Val>>),
+    Enum(String),
+    Option(Option<Box<Val>>),
+    Result(Result<Option<Box<Val>>, Option<Box<Val>>>),
+    Flags(Vec<String>),
     Resource(ResourceAny),
 }
 
 impl Val {
-    /// Retrieve the [`Type`] of this value.
-    pub fn ty(&self) -> Type {
-        match self {
-            Val::Bool(_) => Type::Bool,
-            Val::S8(_) => Type::S8,
-            Val::U8(_) => Type::U8,
-            Val::S16(_) => Type::S16,
-            Val::U16(_) => Type::U16,
-            Val::S32(_) => Type::S32,
-            Val::U32(_) => Type::U32,
-            Val::S64(_) => Type::S64,
-            Val::U64(_) => Type::U64,
-            Val::Float32(_) => Type::Float32,
-            Val::Float64(_) => Type::Float64,
-            Val::Char(_) => Type::Char,
-            Val::String(_) => Type::String,
-            Val::List(List { ty, .. }) => Type::List(ty.clone()),
-            Val::Record(Record { ty, .. }) => Type::Record(ty.clone()),
-            Val::Tuple(Tuple { ty, .. }) => Type::Tuple(ty.clone()),
-            Val::Variant(Variant { ty, .. }) => Type::Variant(ty.clone()),
-            Val::Enum(Enum { ty, .. }) => Type::Enum(ty.clone()),
-            Val::Option(OptionVal { ty, .. }) => Type::Option(ty.clone()),
-            Val::Result(ResultVal { ty, .. }) => Type::Result(ty.clone()),
-            Val::Flags(Flags { ty, .. }) => Type::Flags(ty.clone()),
-            Val::Resource(r) => {
-                if r.owned() {
-                    Type::Own(r.ty())
-                } else {
-                    Type::Borrow(r.ty())
-                }
-            }
-        }
-    }
-
     /// Deserialize a value of this type from core Wasm stack values.
     pub(crate) fn lift(
         cx: &mut LiftContext<'_>,
@@ -758,71 +110,62 @@ impl Val {
             InterfaceType::Own(_) | InterfaceType::Borrow(_) => {
                 Val::Resource(ResourceAny::lift(cx, ty, next(src))?)
             }
-            InterfaceType::String => {
-                Val::String(Box::<str>::lift(cx, ty, &[*next(src), *next(src)])?)
-            }
+            InterfaceType::String => Val::String(<_>::lift(cx, ty, &[*next(src), *next(src)])?),
             InterfaceType::List(i) => {
                 // FIXME: needs memory64 treatment
                 let ptr = u32::lift(cx, InterfaceType::U32, next(src))? as usize;
                 let len = u32::lift(cx, InterfaceType::U32, next(src))? as usize;
                 load_list(cx, i, ptr, len)?
             }
-            InterfaceType::Record(i) => Val::Record(Record {
-                ty: types::Record::from(i, &cx.instance_type()),
-                values: cx.types[i]
+            InterfaceType::Record(i) => Val::Record(
+                cx.types[i]
                     .fields
                     .iter()
-                    .map(|field| Self::lift(cx, field.ty, src))
+                    .map(|field| {
+                        let val = Self::lift(cx, field.ty, src)?;
+                        Ok((field.name.to_string(), val))
+                    })
                     .collect::<Result<_>>()?,
-            }),
-            InterfaceType::Tuple(i) => Val::Tuple(Tuple {
-                ty: types::Tuple::from(i, &cx.instance_type()),
-                values: cx.types[i]
+            ),
+            InterfaceType::Tuple(i) => Val::Tuple(
+                cx.types[i]
                     .types
                     .iter()
                     .map(|ty| Self::lift(cx, *ty, src))
                     .collect::<Result<_>>()?,
-            }),
+            ),
             InterfaceType::Variant(i) => {
+                let vty = &cx.types[i];
                 let (discriminant, value) = lift_variant(
                     cx,
                     cx.types.canonical_abi(&ty).flat_count(usize::MAX).unwrap(),
-                    cx.types[i].cases.iter().map(|case| case.ty),
+                    vty.cases.values().copied(),
                     src,
                 )?;
 
-                Val::Variant(Variant {
-                    ty: types::Variant::from(i, &cx.instance_type()),
-                    discriminant,
-                    value,
-                })
+                let (k, _) = vty.cases.get_index(discriminant as usize).unwrap();
+                Val::Variant(k.clone(), value)
             }
             InterfaceType::Enum(i) => {
+                let ety = &cx.types[i];
                 let (discriminant, _) = lift_variant(
                     cx,
                     cx.types.canonical_abi(&ty).flat_count(usize::MAX).unwrap(),
-                    cx.types[i].names.iter().map(|_| None),
+                    ety.names.iter().map(|_| None),
                     src,
                 )?;
 
-                Val::Enum(Enum {
-                    ty: types::Enum::from(i, &cx.instance_type()),
-                    discriminant,
-                })
+                Val::Enum(ety.names[discriminant as usize].clone())
             }
             InterfaceType::Option(i) => {
-                let (discriminant, value) = lift_variant(
+                let (_discriminant, value) = lift_variant(
                     cx,
                     cx.types.canonical_abi(&ty).flat_count(usize::MAX).unwrap(),
                     [None, Some(cx.types[i].ty)].into_iter(),
                     src,
                 )?;
 
-                Val::Option(OptionVal {
-                    ty: types::OptionType::from(i, &cx.instance_type()),
-                    discriminant,
-                    value,
-                })
+                Val::Option(value)
             }
             InterfaceType::Result(i) => {
                 let result_ty = &cx.types[i];
@@ -833,24 +176,26 @@ impl Val {
                     src,
                 )?;
 
-                Val::Result(ResultVal {
-                    ty: types::ResultType::from(i, &cx.instance_type()),
-                    discriminant,
-                    value,
+                Val::Result(if discriminant == 0 {
+                    Ok(value)
+                } else {
+                    Err(value)
                 })
             }
             InterfaceType::Flags(i) => {
-                let count = u32::try_from(cx.types[i].names.len()).unwrap();
                 let u32_count = cx.types.canonical_abi(&ty).flat_count(usize::MAX).unwrap();
-                let value = iter::repeat_with(|| u32::lift(cx, InterfaceType::U32, next(src)))
-                    .take(u32_count)
-                    .collect::<Result<_>>()?;
+                let ty = &cx.types[i];
+                let mut flags = Vec::new();
+                for i in 0..u32_count {
+                    push_flags(
+                        ty,
+                        &mut flags,
+                        (i as u32) * 32,
+                        u32::lift(cx, InterfaceType::U32, next(src))?,
+                    );
+                }
 
-                Val::Flags(Flags {
-                    ty: types::Flags::from(i, &cx.instance_type()),
-                    count,
-                    value,
-                })
+                Val::Flags(flags.into())
             }
         })
     }
@@ -870,7 +215,7 @@ impl Val {
             InterfaceType::Float32 => Val::Float32(f32::load(cx, ty, bytes)?),
             InterfaceType::Float64 => Val::Float64(f64::load(cx, ty, bytes)?),
             InterfaceType::Char => Val::Char(char::load(cx, ty, bytes)?),
-            InterfaceType::String => Val::String(<Box<str>>::load(cx, ty, bytes)?),
+            InterfaceType::String => Val::String(<_>::load(cx, ty, bytes)?),
             InterfaceType::Own(_) | InterfaceType::Borrow(_) => {
                 Val::Resource(ResourceAny::load(cx, ty, bytes)?)
             }
@@ -881,79 +226,98 @@ impl Val {
                 load_list(cx, i, ptr, len)?
             }
 
-            InterfaceType::Record(i) => Val::Record(Record {
-                ty: types::Record::from(i, &cx.instance_type()),
-                values: load_record(cx, cx.types[i].fields.iter().map(|field| field.ty), bytes)?,
-            }),
-            InterfaceType::Tuple(i) => Val::Tuple(Tuple {
-                ty: types::Tuple::from(i, &cx.instance_type()),
-                values: load_record(cx, cx.types[i].types.iter().copied(), bytes)?,
-            }),
+            InterfaceType::Record(i) => {
+                let mut offset = 0;
+                let fields = cx.types[i].fields.iter();
+                Val::Record(
+                    fields
+                        .map(|field| -> Result<(String, Val)> {
+                            let abi = cx.types.canonical_abi(&field.ty);
+                            let offset = abi.next_field32(&mut offset);
+                            let offset = usize::try_from(offset).unwrap();
+                            let size = usize::try_from(abi.size32).unwrap();
+                            Ok((
+                                field.name.to_string(),
+                                Val::load(cx, field.ty, &bytes[offset..][..size])?,
+                            ))
+                        })
+                        .collect::<Result<_>>()?,
+                )
+            }
+            InterfaceType::Tuple(i) => {
+                let types = cx.types[i].types.iter().copied();
+                let mut offset = 0;
+                Val::Tuple(
+                    types
+                        .map(|ty| {
+                            let abi = cx.types.canonical_abi(&ty);
+                            let offset = abi.next_field32(&mut offset);
+                            let offset = usize::try_from(offset).unwrap();
+                            let size = usize::try_from(abi.size32).unwrap();
+                            Val::load(cx, ty, &bytes[offset..][..size])
+                        })
+                        .collect::<Result<_>>()?,
+                )
+            }
             InterfaceType::Variant(i) => {
                 let ty = &cx.types[i];
                 let (discriminant, value) =
-                    load_variant(cx, &ty.info, ty.cases.iter().map(|case| case.ty), bytes)?;
+                    load_variant(cx, &ty.info, ty.cases.values().copied(), bytes)?;
 
-                Val::Variant(Variant {
-                    ty: types::Variant::from(i, &cx.instance_type()),
-                    discriminant,
-                    value,
-                })
+                let (k, _) = ty.cases.get_index(discriminant as usize).unwrap();
+                Val::Variant(k.clone(), value)
             }
             InterfaceType::Enum(i) => {
                 let ty = &cx.types[i];
                 let (discriminant, _) =
                     load_variant(cx, &ty.info, ty.names.iter().map(|_| None), bytes)?;
 
-                Val::Enum(Enum {
-                    ty: types::Enum::from(i, &cx.instance_type()),
-                    discriminant,
-                })
+                Val::Enum(ty.names[discriminant as usize].clone())
             }
             InterfaceType::Option(i) => {
                 let ty = &cx.types[i];
-                let (discriminant, value) =
+                let (_discriminant, value) =
                     load_variant(cx, &ty.info, [None, Some(ty.ty)].into_iter(), bytes)?;
 
-                Val::Option(OptionVal {
-                    ty: types::OptionType::from(i, &cx.instance_type()),
-                    discriminant,
-                    value,
-                })
+                Val::Option(value)
             }
             InterfaceType::Result(i) => {
                 let ty = &cx.types[i];
                 let (discriminant, value) =
                     load_variant(cx, &ty.info, [ty.ok, ty.err].into_iter(), bytes)?;
 
-                Val::Result(ResultVal {
-                    ty: types::ResultType::from(i, &cx.instance_type()),
-                    discriminant,
-                    value,
+                Val::Result(if discriminant == 0 {
+                    Ok(value)
+                } else {
+                    Err(value)
                 })
             }
-            InterfaceType::Flags(i) => Val::Flags(Flags {
-                ty: types::Flags::from(i, &cx.instance_type()),
-                count: u32::try_from(cx.types[i].names.len())?,
-                value: match FlagsSize::from_count(cx.types[i].names.len()) {
-                    FlagsSize::Size0 => Box::new([]),
+            InterfaceType::Flags(i) => {
+                let ty = &cx.types[i];
+                let mut flags = Vec::new();
+                match FlagsSize::from_count(ty.names.len()) {
+                    FlagsSize::Size0 => {}
                     FlagsSize::Size1 => {
-                        iter::once(u8::load(cx, InterfaceType::U8, bytes)?.into()).collect()
+                        let bits = u8::load(cx, InterfaceType::U8, bytes)?;
+                        push_flags(ty, &mut flags, 0, u32::from(bits));
                     }
                     FlagsSize::Size2 => {
-                        iter::once(u16::load(cx, InterfaceType::U16, bytes)?.into()).collect()
+                        let bits = u16::load(cx, InterfaceType::U16, bytes)?;
+                        push_flags(ty, &mut flags, 0, u32::from(bits));
                     }
-                    FlagsSize::Size4Plus(n) => (0..n)
-                        .map(|index| {
-                            u32::load(
+                    FlagsSize::Size4Plus(n) => {
+                        for i in 0..n {
+                            let bits = u32::load(
                                 cx,
                                 InterfaceType::U32,
-                                &bytes[usize::from(index) * 4..][..4],
-                            )
-                        })
-                        .collect::<Result<_>>()?,
-                },
-            }),
+                                &bytes[usize::from(i) * 4..][..4],
+                            )?;
+                            push_flags(ty, &mut flags, u32::from(i) * 32, bits);
+                        }
+                    }
+                }
+                Val::Flags(flags.into())
+            }
         })
     }
 
@@ -964,68 +328,107 @@ impl Val {
         ty: InterfaceType,
         dst: &mut std::slice::IterMut<'_, MaybeUninit<ValRaw>>,
     ) -> Result<()> {
-        match self {
-            Val::Bool(value) => value.lower(cx, ty, next_mut(dst))?,
-            Val::S8(value) => value.lower(cx, ty, next_mut(dst))?,
-            Val::U8(value) => value.lower(cx, ty, next_mut(dst))?,
-            Val::S16(value) => value.lower(cx, ty, next_mut(dst))?,
-            Val::U16(value) => value.lower(cx, ty, next_mut(dst))?,
-            Val::S32(value) => value.lower(cx, ty, next_mut(dst))?,
-            Val::U32(value) => value.lower(cx, ty, next_mut(dst))?,
-            Val::S64(value) => value.lower(cx, ty, next_mut(dst))?,
-            Val::U64(value) => value.lower(cx, ty, next_mut(dst))?,
-            Val::Float32(value) => value.lower(cx, ty, next_mut(dst))?,
-            Val::Float64(value) => value.lower(cx, ty, next_mut(dst))?,
-            Val::Char(value) => value.lower(cx, ty, next_mut(dst))?,
-            Val::Resource(value) => value.lower(cx, ty, next_mut(dst))?,
-            Val::String(value) => {
+        match (ty, self) {
+            (InterfaceType::Bool, Val::Bool(value)) => value.lower(cx, ty, next_mut(dst)),
+            (InterfaceType::Bool, _) => unexpected(ty, self),
+            (InterfaceType::S8, Val::S8(value)) => value.lower(cx, ty, next_mut(dst)),
+            (InterfaceType::S8, _) => unexpected(ty, self),
+            (InterfaceType::U8, Val::U8(value)) => value.lower(cx, ty, next_mut(dst)),
+            (InterfaceType::U8, _) => unexpected(ty, self),
+            (InterfaceType::S16, Val::S16(value)) => value.lower(cx, ty, next_mut(dst)),
+            (InterfaceType::S16, _) => unexpected(ty, self),
+            (InterfaceType::U16, Val::U16(value)) => value.lower(cx, ty, next_mut(dst)),
+            (InterfaceType::U16, _) => unexpected(ty, self),
+            (InterfaceType::S32, Val::S32(value)) => value.lower(cx, ty, next_mut(dst)),
+            (InterfaceType::S32, _) => unexpected(ty, self),
+            (InterfaceType::U32, Val::U32(value)) => value.lower(cx, ty, next_mut(dst)),
+            (InterfaceType::U32, _) => unexpected(ty, self),
+            (InterfaceType::S64, Val::S64(value)) => value.lower(cx, ty, next_mut(dst)),
+            (InterfaceType::S64, _) => unexpected(ty, self),
+            (InterfaceType::U64, Val::U64(value)) => value.lower(cx, ty, next_mut(dst)),
+            (InterfaceType::U64, _) => unexpected(ty, self),
+            (InterfaceType::Float32, Val::Float32(value)) => value.lower(cx, ty, next_mut(dst)),
+            (InterfaceType::Float32, _) => unexpected(ty, self),
+            (InterfaceType::Float64, Val::Float64(value)) => value.lower(cx, ty, next_mut(dst)),
+            (InterfaceType::Float64, _) => unexpected(ty, self),
+            (InterfaceType::Char, Val::Char(value)) => value.lower(cx, ty, next_mut(dst)),
+            (InterfaceType::Char, _) => unexpected(ty, self),
+            // NB: `lower` on `ResourceAny` does its own type-checking, so skip
+            // looking at it here.
+            (InterfaceType::Borrow(_) | InterfaceType::Own(_), Val::Resource(value)) => {
+                value.lower(cx, ty, next_mut(dst))
+            }
+            (InterfaceType::Borrow(_) | InterfaceType::Own(_), _) => unexpected(ty, self),
+            (InterfaceType::String, Val::String(value)) => {
                 let my_dst = &mut MaybeUninit::<[ValRaw; 2]>::uninit();
                 value.lower(cx, ty, my_dst)?;
                 let my_dst = unsafe { my_dst.assume_init() };
                 next_mut(dst).write(my_dst[0]);
                 next_mut(dst).write(my_dst[1]);
+                Ok(())
             }
-            Val::List(List { values, .. }) => {
-                let ty = match ty {
-                    InterfaceType::List(i) => &cx.types[i],
-                    _ => bad_type_info(),
-                };
+            (InterfaceType::String, _) => unexpected(ty, self),
+            (InterfaceType::List(ty), Val::List(values)) => {
+                let ty = &cx.types[ty];
                 let (ptr, len) = lower_list(cx, ty.element, values)?;
                 next_mut(dst).write(ValRaw::i64(ptr as i64));
                 next_mut(dst).write(ValRaw::i64(len as i64));
+                Ok(())
             }
-            Val::Record(Record { values, .. }) => {
-                let ty = match ty {
-                    InterfaceType::Record(i) => &cx.types[i],
-                    _ => bad_type_info(),
-                };
-                for (value, field) in values.iter().zip(ty.fields.iter()) {
+            (InterfaceType::List(_), _) => unexpected(ty, self),
+            (InterfaceType::Record(ty), Val::Record(values)) => {
+                let ty = &cx.types[ty];
+                if ty.fields.len() != values.len() {
+                    bail!("expected {} fields, got {}", ty.fields.len(), values.len());
+                }
+                for ((name, value), field) in values.iter().zip(ty.fields.iter()) {
+                    if *name != field.name {
+                        bail!("expected field `{}`, got `{name}`", field.name);
+                    }
                     value.lower(cx, field.ty, dst)?;
                 }
+                Ok(())
             }
-            Val::Tuple(Tuple { values, .. }) => {
-                let ty = match ty {
-                    InterfaceType::Tuple(i) => &cx.types[i],
-                    _ => bad_type_info(),
-                };
+            (InterfaceType::Record(_), _) => unexpected(ty, self),
+            (InterfaceType::Tuple(ty), Val::Tuple(values)) => {
+                let ty = &cx.types[ty];
+                if ty.types.len() != values.len() {
+                    bail!("expected {} types, got {}", ty.types.len(), values.len());
+                }
                 for (value, ty) in values.iter().zip(ty.types.iter()) {
                     value.lower(cx, *ty, dst)?;
                 }
-            }
-            Val::Variant(v) => v.as_generic(cx.types, ty).lower(cx, dst)?,
-            Val::Option(v) => v.as_generic(cx.types, ty).lower(cx, dst)?,
-            Val::Result(v) => v.as_generic(cx.types, ty).lower(cx, dst)?,
-            Val::Enum(Enum { discriminant, .. }) => {
-                next_mut(dst).write(ValRaw::u32(*discriminant));
-            }
-            Val::Flags(Flags { value, .. }) => {
-                for value in value.deref() {
-                    next_mut(dst).write(ValRaw::u32(*value));
+                Ok(())
+            }
+            (InterfaceType::Tuple(_), _) => unexpected(ty, self),
+            (InterfaceType::Variant(ty), Val::Variant(n, v)) => {
+                GenericVariant::variant(&cx.types[ty], n, v)?.lower(cx, dst)
+            }
+            (InterfaceType::Variant(_), _) => unexpected(ty, self),
+            (InterfaceType::Option(ty), Val::Option(v)) => {
+                GenericVariant::option(&cx.types[ty], v).lower(cx, dst)
+            }
+            (InterfaceType::Option(_), _) => unexpected(ty, self),
+            (InterfaceType::Result(ty), Val::Result(v)) => {
+                GenericVariant::result(&cx.types[ty], v)?.lower(cx, dst)
+            }
+            (InterfaceType::Result(_), _) => unexpected(ty, self),
+            (InterfaceType::Enum(ty), Val::Enum(discriminant)) => {
+                let discriminant = get_enum_discriminant(&cx.types[ty], discriminant)?;
+                next_mut(dst).write(ValRaw::u32(discriminant));
+                Ok(())
+            }
+            (InterfaceType::Enum(_), _) => unexpected(ty, self),
+            (InterfaceType::Flags(ty), Val::Flags(value)) => {
+                let ty = &cx.types[ty];
+                let storage = flags_to_storage(ty, value)?;
+                for value in storage {
+                    next_mut(dst).write(ValRaw::u32(value));
                 }
+                Ok(())
             }
+            (InterfaceType::Flags(_), _) => unexpected(ty, self),
         }
-
-        Ok(())
     }
 
     /// Serialize this value to the heap at the specified memory location.
@@ -1037,38 +440,58 @@ impl Val {
     ) -> Result<()> {
         debug_assert!(offset % usize::try_from(cx.types.canonical_abi(&ty).align32)? == 0);
 
-        match self {
-            Val::Bool(value) => value.store(cx, ty, offset)?,
-            Val::U8(value) => value.store(cx, ty, offset)?,
-            Val::S8(value) => value.store(cx, ty, offset)?,
-            Val::U16(value) => value.store(cx, ty, offset)?,
-            Val::S16(value) => value.store(cx, ty, offset)?,
-            Val::U32(value) => value.store(cx, ty, offset)?,
-            Val::S32(value) => value.store(cx, ty, offset)?,
-            Val::U64(value) => value.store(cx, ty, offset)?,
-            Val::S64(value) => value.store(cx, ty, offset)?,
-            Val::Float32(value) => value.store(cx, ty, offset)?,
-            Val::Float64(value) => value.store(cx, ty, offset)?,
-            Val::Char(value) => value.store(cx, ty, offset)?,
-            Val::String(value) => value.store(cx, ty, offset)?,
-            Val::Resource(value) => value.store(cx, ty, offset)?,
-            Val::List(List { values, .. }) => {
-                let ty = match ty {
-                    InterfaceType::List(i) => &cx.types[i],
-                    _ => bad_type_info(),
-                };
+        match (ty, self) {
+            (InterfaceType::Bool, Val::Bool(value)) => value.store(cx, ty, offset),
+            (InterfaceType::Bool, _) => unexpected(ty, self),
+            (InterfaceType::U8, Val::U8(value)) => value.store(cx, ty, offset),
+            (InterfaceType::U8, _) => unexpected(ty, self),
+            (InterfaceType::S8, Val::S8(value)) => value.store(cx, ty, offset),
+            (InterfaceType::S8, _) => unexpected(ty, self),
+            (InterfaceType::U16, Val::U16(value)) => value.store(cx, ty, offset),
+            (InterfaceType::U16, _) => unexpected(ty, self),
+            (InterfaceType::S16, Val::S16(value)) => value.store(cx, ty, offset),
+            (InterfaceType::S16, _) => unexpected(ty, self),
+            (InterfaceType::U32, Val::U32(value)) => value.store(cx, ty, offset),
+            (InterfaceType::U32, _) => unexpected(ty, self),
+            (InterfaceType::S32, Val::S32(value)) => value.store(cx, ty, offset),
+            (InterfaceType::S32, _) => unexpected(ty, self),
+            (InterfaceType::U64, Val::U64(value)) => value.store(cx, ty, offset),
+            (InterfaceType::U64, _) => unexpected(ty, self),
+            (InterfaceType::S64, Val::S64(value)) => value.store(cx, ty, offset),
+            (InterfaceType::S64, _) => unexpected(ty, self),
+            (InterfaceType::Float32, Val::Float32(value)) => value.store(cx, ty, offset),
+            (InterfaceType::Float32, _) => unexpected(ty, self),
+            (InterfaceType::Float64, Val::Float64(value)) => value.store(cx, ty, offset),
+            (InterfaceType::Float64, _) => unexpected(ty, self),
+            (InterfaceType::Char, Val::Char(value)) => value.store(cx, ty, offset),
+            (InterfaceType::Char, _) => unexpected(ty, self),
+            (InterfaceType::String, Val::String(value)) => value.store(cx, ty, offset),
+            (InterfaceType::String, _) => unexpected(ty, self),
+
+            // NB: resources do type-checking when they lower.
+            (InterfaceType::Borrow(_) | InterfaceType::Own(_), Val::Resource(value)) => {
+                value.store(cx, ty, offset)
+            }
+            (InterfaceType::Borrow(_) | InterfaceType::Own(_), _) => unexpected(ty, self),
+            (InterfaceType::List(ty), Val::List(values)) => {
+                let ty = &cx.types[ty];
                 let (ptr, len) = lower_list(cx, ty.element, values)?;
                 // FIXME: needs memory64 handling
                 *cx.get(offset + 0) = (ptr as i32).to_le_bytes();
                 *cx.get(offset + 4) = (len as i32).to_le_bytes();
+                Ok(())
             }
-            Val::Record(Record { values, .. }) => {
-                let ty = match ty {
-                    InterfaceType::Record(i) => &cx.types[i],
-                    _ => bad_type_info(),
-                };
+            (InterfaceType::List(_), _) => unexpected(ty, self),
+            (InterfaceType::Record(ty), Val::Record(values)) => {
+                let ty = &cx.types[ty];
+                if ty.fields.len() != values.len() {
+                    bail!("expected {} fields, got {}", ty.fields.len(), values.len());
+                }
                 let mut offset = offset;
-                for (value, field) in values.iter().zip(ty.fields.iter()) {
+                for ((name, value), field) in values.iter().zip(ty.fields.iter()) {
+                    if *name != field.name {
+                        bail!("expected field `{}`, got `{name}`", field.name);
+                    }
                     value.store(
                         cx,
                         field.ty,
@@ -1077,12 +500,14 @@ impl Val {
                             .next_field32_size(&mut offset),
                     )?;
                 }
+                Ok(())
             }
-            Val::Tuple(Tuple { values, .. }) => {
-                let ty = match ty {
-                    InterfaceType::Tuple(i) => &cx.types[i],
-                    _ => bad_type_info(),
-                };
+            (InterfaceType::Record(_), _) => unexpected(ty, self),
+            (InterfaceType::Tuple(ty), Val::Tuple(values)) => {
+                let ty = &cx.types[ty];
+                if ty.types.len() != values.len() {
+                    bail!("expected {} types, got {}", ty.types.len(), values.len());
+                }
                 let mut offset = offset;
                 for (value, ty) in values.iter().zip(ty.types.iter()) {
                     value.store(
@@ -1091,38 +516,81 @@ impl Val {
                         cx.types.canonical_abi(ty).next_field32_size(&mut offset),
                     )?;
                 }
+                Ok(())
             }
+            (InterfaceType::Tuple(_), _) => unexpected(ty, self),
 
-            Val::Variant(v) => v.as_generic(cx.types, ty).store(cx, offset)?,
-            Val::Enum(v) => v.as_generic(cx.types, ty).store(cx, offset)?,
-            Val::Option(v) => v.as_generic(cx.types, ty).store(cx, offset)?,
-            Val::Result(v) => v.as_generic(cx.types, ty).store(cx, offset)?,
+            (InterfaceType::Variant(ty), Val::Variant(n, v)) => {
+                GenericVariant::variant(&cx.types[ty], n, v)?.store(cx, offset)
+            }
+            (InterfaceType::Variant(_), _) => unexpected(ty, self),
+            (InterfaceType::Enum(ty), Val::Enum(v)) => {
+                GenericVariant::enum_(&cx.types[ty], v)?.store(cx, offset)
+            }
+            (InterfaceType::Enum(_), _) => unexpected(ty, self),
+            (InterfaceType::Option(ty), Val::Option(v)) => {
+                GenericVariant::option(&cx.types[ty], v).store(cx, offset)
+            }
+            (InterfaceType::Option(_), _) => unexpected(ty, self),
+            (InterfaceType::Result(ty), Val::Result(v)) => {
+                GenericVariant::result(&cx.types[ty], v)?.store(cx, offset)
+            }
+            (InterfaceType::Result(_), _) => unexpected(ty, self),
 
-            Val::Flags(Flags { count, value, .. }) => {
-                match FlagsSize::from_count(*count as usize) {
+            (InterfaceType::Flags(ty), Val::Flags(flags)) => {
+                let ty = &cx.types[ty];
+                let storage = flags_to_storage(ty, flags)?;
+                match FlagsSize::from_count(ty.names.len()) {
                     FlagsSize::Size0 => {}
                     FlagsSize::Size1 => {
-                        u8::try_from(value[0])
+                        u8::try_from(storage[0])
                             .unwrap()
                             .store(cx, InterfaceType::U8, offset)?
                     }
                     FlagsSize::Size2 => {
-                        u16::try_from(value[0])
+                        u16::try_from(storage[0])
                             .unwrap()
                             .store(cx, InterfaceType::U16, offset)?
                     }
                     FlagsSize::Size4Plus(_) => {
                         let mut offset = offset;
-                        for value in value.deref() {
+                        for value in storage {
                             value.store(cx, InterfaceType::U32, offset)?;
                             offset += 4;
                         }
                     }
                 }
+                Ok(())
             }
+            (InterfaceType::Flags(_), _) => unexpected(ty, self),
         }
+    }
 
-        Ok(())
+    fn desc(&self) -> &'static str {
+        match self {
+            Val::Bool(_) => "bool",
+            Val::U8(_) => "u8",
+            Val::S8(_) => "s8",
+            Val::U16(_) => "u16",
+            Val::S16(_) => "s16",
+            Val::U32(_) => "u32",
+            Val::S32(_) => "s32",
+            Val::U64(_) => "u64",
+            Val::S64(_) => "s64",
+            Val::Float32(_) => "f32",
+            Val::Float64(_) => "f64",
+            Val::Char(_) => "char",
+            Val::List(_) => "list",
+            Val::String(_) => "string",
+            Val::Record(_) => "record",
+            Val::Enum(_) => "enum",
+            Val::Variant(..) => "variant",
+            Val::Tuple(_) => "tuple",
+            Val::Option(_) => "option",
+            Val::Result(_) => "result",
+            Val::Resource(_) => "resource",
+            Val::Flags(_) => "flags",
+        }
     }
 }
 
@@ -1175,8 +643,8 @@ impl PartialEq for Val {
             (Self::Record(_), _) => false,
             (Self::Tuple(l), Self::Tuple(r)) => l == r,
             (Self::Tuple(_), _) => false,
-            (Self::Variant(l), Self::Variant(r)) => l == r,
-            (Self::Variant(_), _) => false,
+            (Self::Variant(ln, lv), Self::Variant(rn, rv)) => ln == rn && lv == rv,
+            (Self::Variant(..), _) => false,
             (Self::Enum(l), Self::Enum(r)) => l == r,
             (Self::Enum(_), _) => false,
             (Self::Option(l), Self::Option(r)) => l == r,
@@ -1201,6 +669,92 @@ struct GenericVariant<'a> {
 }
 
 impl GenericVariant<'_> {
+    fn result<'a>(
+        ty: &'a TypeResult,
+        r: &'a Result<Option<Box<Val>>, Option<Box<Val>>>,
+    ) -> Result<GenericVariant<'a>> {
+        let (discriminant, payload) = match r {
+            Ok(val) => {
+                let payload = match (val, ty.ok) {
+                    (Some(val), Some(ty)) => Some((&**val, ty)),
+                    (None, None) => None,
+                    (Some(_), None) => {
+                        bail!("payload provided to `ok` but not expected");
+                    }
+                    (None, Some(_)) => {
+                        bail!("payload expected to `ok` but not provided");
+                    }
+                };
+                (0, payload)
+            }
+            Err(val) => {
+                let payload = match (val, ty.err) {
+                    (Some(val), Some(ty)) => Some((&**val, ty)),
+                    (None, None) => None,
+                    (Some(_), None) => {
+                        bail!("payload provided to `err` but not expected");
+                    }
+                    (None, Some(_)) => {
+                        bail!("payload expected to `err` but not provided");
+                    }
+                };
+                (1, payload)
+            }
+        };
+        Ok(GenericVariant {
+            discriminant,
+            payload,
+            abi: &ty.abi,
+            info: &ty.info,
+        })
+    }
+
+    fn option<'a>(ty: &'a TypeOption, r: &'a Option<Box<Val>>) -> GenericVariant<'a> {
+        let (discriminant, payload) = match r {
+            None => (0, None),
+            Some(val) => (1, Some((&**val, ty.ty))),
+        };
+        GenericVariant {
+            discriminant,
+            payload,
+            abi: &ty.abi,
+            info: &ty.info,
+        }
+    }
+
+    fn enum_<'a>(ty: &'a TypeEnum, discriminant: &str) -> Result<GenericVariant<'a>> {
+        let discriminant = get_enum_discriminant(ty, discriminant)?;
+
+        Ok(GenericVariant {
+            discriminant,
+            payload: None,
+            abi: &ty.abi,
+            info: &ty.info,
+        })
+    }
+
+    fn variant<'a>(
+        ty: &'a TypeVariant,
+        discriminant_name: &str,
+        payload: &'a Option<Box<Val>>,
+    ) -> Result<GenericVariant<'a>> {
+        let (discriminant, payload_ty) = get_variant_discriminant(ty, discriminant_name)?;
+
+        let payload = match (payload, payload_ty) {
+            (Some(val), Some(ty)) => Some((&**val, *ty)),
+            (None, None) => None,
+            (Some(_), None) => bail!("did not expect a payload for case `{discriminant_name}`"),
+            (None, Some(_)) => bail!("expected a payload for case `{discriminant_name}`"),
+        };
+
+        Ok(GenericVariant {
+            discriminant,
+            payload,
+            abi: &ty.abi,
+            info: &ty.info,
+        })
+    }
+
     fn lower<T>(
         &self,
         cx: &mut LowerContext<'_, T>,
@@ -1266,9 +820,8 @@ fn load_list(cx: &mut LiftContext<'_>, ty: TypeListIndex, ptr: usize, len: usize
         bail!("list pointer is not aligned")
     }
 
-    Ok(Val::List(List {
-        ty: types::List::from(ty, &cx.instance_type()),
-        values: (0..len)
+    Ok(Val::List(
+        (0..len)
             .map(|index| {
                 Val::load(
                     cx,
@@ -1277,24 +830,7 @@ fn load_list(cx: &mut LiftContext<'_>, ty: TypeListIndex, ptr: usize, len: usize
                 )
             })
             .collect::<Result<_>>()?,
-    }))
-}
-
-fn load_record(
-    cx: &mut LiftContext<'_>,
-    types: impl Iterator<Item = InterfaceType>,
-    bytes: &[u8],
-) -> Result<Box<[Val]>> {
-    let mut offset = 0;
-    types
-        .map(|ty| {
-            let abi = cx.types.canonical_abi(&ty);
-            let offset = abi.next_field32(&mut offset);
-            let offset = usize::try_from(offset).unwrap();
-            let size = usize::try_from(abi.size32).unwrap();
-            Val::load(cx, ty, &bytes[offset..][..size])
-        })
-        .collect()
+    ))
 }
 
 fn load_variant(
@@ -1377,6 +913,51 @@ fn lower_list<T>(
     Ok((ptr, items.len()))
 }
 
+fn push_flags(ty: &TypeFlags, flags: &mut Vec<String>, mut offset: u32, mut bits: u32) {
+    while bits > 0 {
+        if bits & 1 != 0 {
+            flags.push(ty.names[offset as usize].clone());
+        }
+        bits >>= 1;
+        offset += 1;
+    }
+}
+
+fn flags_to_storage(ty: &TypeFlags, flags: &[String]) -> Result<Vec<u32>> {
+    let mut storage = match FlagsSize::from_count(ty.names.len()) {
+        FlagsSize::Size0 => Vec::new(),
+        FlagsSize::Size1 | FlagsSize::Size2 => vec![0],
+        FlagsSize::Size4Plus(n) => vec![0; n.into()],
+    };
+
+    for flag in flags {
+        let bit = ty
+            .names
+            .get_index_of(flag)
+            .ok_or_else(|| anyhow::anyhow!("unknown flag: `{flag}`"))?;
+        storage[bit / 32] |= 1 << (bit % 32);
+    }
+    Ok(storage)
+}
+
+fn get_enum_discriminant(ty: &TypeEnum, n: &str) -> Result<u32> {
+    ty.names
+        .get_index_of(n)
+        .ok_or_else(|| anyhow::anyhow!("enum variant name `{n}` is not valid"))
+        .map(|i| i as u32)
+}
+
+fn get_variant_discriminant<'a>(
+    ty: &'a TypeVariant,
+    name: &str,
+) -> Result<(u32, &'a Option<InterfaceType>)> {
+    let (i, _, ty) = ty
+        .cases
+        .get_full(name)
+        .ok_or_else(|| anyhow::anyhow!("unknown variant case: `{name}`"))?;
+    Ok((i as u32, ty))
+}
+
 fn next<'a>(src: &mut std::slice::Iter<'a, ValRaw>) -> &'a ValRaw {
     src.next().unwrap()
 }
@@ -1386,3 +967,12 @@ fn next_mut<'a>(
 ) -> &'a mut MaybeUninit<ValRaw> {
     dst.next().unwrap()
 }
+
+#[cold]
+fn unexpected<T>(ty: InterfaceType, val: &Val) -> Result<T> {
+    bail!(
+        "type mismatch: expected {}, found {}",
+        desc(&ty),
+        val.desc()
+    )
+}
diff --git a/crates/wast/src/component.rs b/crates/wast/src/component.rs
index 2ce11c865..34c63260d 100644
--- a/crates/wast/src/component.rs
+++ b/crates/wast/src/component.rs
@@ -1,13 +1,13 @@
 use crate::core;
-use anyhow::{anyhow, bail, Context, Result};
-use std::collections::{BTreeSet, HashMap};
+use anyhow::{bail, Context, Result};
+use std::collections::BTreeSet;
 use std::fmt::Debug;
 use wast::component::WastVal;
 use wast::core::NanPattern;
 
 pub use wasmtime::component::*;
 
-pub fn val(v: &WastVal<'_>, ty: &Type) -> Result<Val> {
+pub fn val(v: &WastVal<'_>) -> Result<Val> {
     Ok(match v {
         WastVal::Bool(b) => Val::Bool(*b),
         WastVal::U8(b) => Val::U8(*b),
@@ -22,104 +22,44 @@ pub fn val(v: &WastVal<'_>, ty: &Type) -> Result<Val> {
         WastVal::Float64(b) => Val::Float64(f64::from_bits(b.bits)),
         WastVal::Char(b) => Val::Char(*b),
         WastVal::String(s) => Val::String(s.to_string().into()),
-        WastVal::List(vals) => match ty {
-            Type::List(t) => {
-                let element = t.ty();
-                let vals = vals
-                    .iter()
-                    .map(|v| val(v, &element))
-                    .collect::<Result<Vec<_>>>()?;
-                t.new_val(vals.into())?
-            }
-            _ => bail!("expected a list value"),
-        },
-        WastVal::Record(fields) => match ty {
-            Type::Record(t) => {
-                let mut fields_by_name = HashMap::new();
-                for (name, val) in fields {
-                    let prev = fields_by_name.insert(*name, val);
-                    if prev.is_some() {
-                        bail!("field `{name}` specified twice");
-                    }
-                }
-                let mut values = Vec::new();
-                for field in t.fields() {
-                    let name = field.name;
-                    let v = fields_by_name
-                        .remove(name)
-                        .ok_or_else(|| anyhow!("field `{name}` not specified"))?;
-                    values.push((name, val(v, &field.ty)?));
-                }
-                if let Some((field, _)) = fields_by_name.iter().next() {
-                    bail!("extra field `{field}` specified");
-                }
-                t.new_val(values)?
-            }
-            _ => bail!("expected a record value"),
-        },
-        WastVal::Tuple(vals) => match ty {
-            Type::Tuple(t) => {
-                if vals.len() != t.types().len() {
-                    bail!("expected {} values got {}", t.types().len(), vals.len());
-                }
-                t.new_val(
-                    vals.iter()
-                        .zip(t.types())
-                        .map(|(v, ty)| val(v, &ty))
-                        .collect::<Result<Vec<_>>>()?
-                        .into(),
-                )?
-            }
-            _ => bail!("expected a tuple value"),
-        },
-        WastVal::Enum(name) => match ty {
-            Type::Enum(t) => t.new_val(name)?,
-            _ => bail!("expected an enum value"),
-        },
-        WastVal::Variant(name, payload) => match ty {
-            Type::Variant(t) => {
-                let case = match t.cases().find(|c| c.name == *name) {
-                    Some(case) => case,
-                    None => bail!("no case named `{}", name),
-                };
-                let payload = payload_val(case.name, payload.as_deref(), case.ty.as_ref())?;
-                t.new_val(name, payload)?
-            }
-            _ => bail!("expected a variant value"),
-        },
-        WastVal::Option(v) => match ty {
-            Type::Option(t) => {
-                let v = match v {
-                    Some(v) => Some(val(v, &t.ty())?),
-                    None => None,
-                };
-                t.new_val(v)?
-            }
-            _ => bail!("expected an option value"),
-        },
-        WastVal::Result(v) => match ty {
-            Type::Result(t) => {
-                let v = match v {
-                    Ok(v) => Ok(payload_val("ok", v.as_deref(), t.ok().as_ref())?),
-                    Err(v) => Err(payload_val("err", v.as_deref(), t.err().as_ref())?),
-                };
-                t.new_val(v)?
+        WastVal::List(vals) => {
+            let vals = vals.iter().map(|v| val(v)).collect::<Result<Vec<_>>>()?;
+            Val::List(vals.into())
+        }
+        WastVal::Record(vals) => {
+            let mut fields = Vec::new();
+            for (name, v) in vals {
+                fields.push((name.to_string(), val(v)?));
             }
-            _ => bail!("expected an expected value"),
-        },
-        WastVal::Flags(v) => match ty {
-            Type::Flags(t) => t.new_val(v)?,
-            _ => bail!("expected a flags value"),
-        },
+            Val::Record(fields.into())
+        }
+        WastVal::Tuple(vals) => Val::Tuple(
+            vals.iter()
+                .map(|v| val(v))
+                .collect::<Result<Vec<_>>>()?
+                .into(),
+        ),
+        WastVal::Enum(name) => Val::Enum(name.to_string()),
+        WastVal::Variant(name, payload) => {
+            let payload = payload_val(payload.as_deref())?;
+            Val::Variant(name.to_string(), payload)
+        }
+        WastVal::Option(v) => Val::Option(match v {
+            Some(v) => Some(Box::new(val(v)?)),
+            None => None,
+        }),
+        WastVal::Result(v) => Val::Result(match v {
+            Ok(v) => Ok(payload_val(v.as_deref())?),
+            Err(v) => Err(payload_val(v.as_deref())?),
+        }),
+        WastVal::Flags(v) => Val::Flags(v.iter().map(|s| s.to_string()).collect()),
     })
 }
 
-fn payload_val(name: &str, v: Option<&WastVal<'_>>, ty: Option<&Type>) -> Result<Option<Val>> {
-    match (v, ty) {
-        (Some(v), Some(ty)) => Ok(Some(val(v, ty)?)),
-        (None, None) => Ok(None),
-        (Some(_), None) => bail!("expected payload for case `{name}`"),
-        (None, Some(_)) => bail!("unexpected payload for case `{name}`"),
+fn payload_val(v: Option<&WastVal<'_>>) -> Result<Option<Box<Val>>> {
+    match v {
+        Some(v) => Ok(Some(Box::new(val(v)?))),
+        None => Ok(None),
     }
 }
 
@@ -192,22 +132,15 @@ pub fn match_val(expected: &WastVal<'_>, actual: &Val) -> Result<()> {
         },
         WastVal::Record(e) => match actual {
             Val::Record(a) => {
-                let mut fields_by_name = HashMap::new();
-                for (name, val) in e {
-                    let prev = fields_by_name.insert(*name, val);
-                    if prev.is_some() {
-                        bail!("field `{name}` specified twice");
-                    }
-                }
-                for (name, actual) in a.fields() {
-                    let expected = fields_by_name
-                        .remove(name)
-                        .ok_or_else(|| anyhow!("field `{name}` not specified"))?;
-                    match_val(expected, actual)
-                        .with_context(|| format!("failed to match field `{name}`"))?;
+                if e.len() != e.len() {
+                    bail!("mismatched number of record fields");
                 }
-                if let Some((field, _)) = fields_by_name.iter().next() {
-                    bail!("extra field `{field}` specified");
+                for ((e_name, e_val), (a_name, a_val)) in e.iter().zip(a.iter()) {
+                    if e_name != a_name {
+                        bail!("expected field `{e_name}` got `{a_name}`");
+                    }
+                    match_val(e_val, a_val)
+                        .with_context(|| format!("failed to match field `{e_name}`"))?;
                 }
                 Ok(())
             }
@@ -215,14 +148,10 @@ pub fn match_val(expected: &WastVal<'_>, actual: &Val) -> Result<()> {
         },
         WastVal::Tuple(e) => match actual {
             Val::Tuple(a) => {
-                if e.len() != a.values().len() {
-                    bail!(
-                        "expected {}-tuple, found {}-tuple",
-                        e.len(),
-                        a.values().len()
-                    );
+                if e.len() != a.len() {
+                    bail!("expected {}-tuple, found {}-tuple", e.len(), a.len());
                 }
-                for (i, (expected, actual)) in e.iter().zip(a.values()).enumerate() {
+                for (i, (expected, actual)) in e.iter().zip(a.iter()).enumerate() {
                     match_val(expected, actual)
                         .with_context(|| format!("failed to match tuple element {i}"))?;
                 }
@@ -231,18 +160,18 @@ pub fn match_val(expected: &WastVal<'_>, actual: &Val) -> Result<()> {
             _ => mismatch(expected, actual),
         },
         WastVal::Variant(name, e) => match actual {
-            Val::Variant(a) => {
-                if a.discriminant() != *name {
-                    bail!("expected discriminant `{name}` got `{}`", a.discriminant());
+            Val::Variant(discr, payload) => {
+                if *discr != *name {
+                    bail!("expected discriminant `{name}` got `{discr}`");
                 }
-                match_payload_val(name, e.as_deref(), a.payload())
+                match_payload_val(name, e.as_deref(), payload.as_deref())
             }
             _ => mismatch(expected, actual),
         },
         WastVal::Enum(name) => match actual {
             Val::Enum(a) => {
-                if a.discriminant() != *name {
-                    bail!("expected discriminant `{name}` got `{}`", a.discriminant());
+                if *a != *name {
+                    bail!("expected discriminant `{name}` got `{a}`");
                 } else {
                     Ok(())
                 }
@@ -250,7 +179,7 @@ pub fn match_val(expected: &WastVal<'_>, actual: &Val) -> Result<()> {
             _ => mismatch(expected, actual),
         },
         WastVal::Option(e) => match actual {
-            Val::Option(a) => match (e, a.value()) {
+            Val::Option(a) => match (e, a) {
                 (None, None) => Ok(()),
                 (Some(expected), Some(actual)) => match_val(expected, actual),
                 (None, Some(_)) => bail!("expected `none`, found `some`"),
@@ -259,18 +188,18 @@ pub fn match_val(expected: &WastVal<'_>, actual: &Val) -> Result<()> {
             _ => mismatch(expected, actual),
         },
         WastVal::Result(e) => match actual {
-            Val::Result(a) => match (e, a.value()) {
+            Val::Result(a) => match (e, a) {
                 (Ok(_), Err(_)) => bail!("expected `ok`, found `err`"),
                 (Err(_), Ok(_)) => bail!("expected `err`, found `ok`"),
-                (Err(e), Err(a)) => match_payload_val("err", e.as_deref(), a),
-                (Ok(e), Ok(a)) => match_payload_val("ok", e.as_deref(), a),
+                (Err(e), Err(a)) => match_payload_val("err", e.as_deref(), a.as_deref()),
+                (Ok(e), Ok(a)) => match_payload_val("ok", e.as_deref(), a.as_deref()),
             },
             _ => mismatch(expected, actual),
         },
         WastVal::Flags(e) => match actual {
             Val::Flags(a) => {
                 let expected = e.iter().copied().collect::<BTreeSet<_>>();
-                let actual = a.flags().collect::<BTreeSet<_>>();
+                let actual = a.iter().map(|s| s.as_str()).collect::<BTreeSet<_>>();
                 match_debug(&actual, &expected)
             }
             _ => mismatch(expected, actual),
diff --git a/crates/wast/src/wast.rs b/crates/wast/src/wast.rs
index 6763bd3f8..fcb2b8d47 100644
--- a/crates/wast/src/wast.rs
+++ b/crates/wast/src/wast.rs
@@ -186,16 +186,11 @@ where
             }
             #[cfg(feature = "component-model")]
             Export::Component(func) => {
-                let params = func.params(&self.store);
-                if exec.args.len() != params.len() {
-                    bail!("mismatched number of parameters")
-                }
                 let values = exec
                     .args
                     .iter()
-                    .zip(params.iter())
-                    .map(|(v, t)| match v {
-                        WastArg::Component(v) => component::val(v, t),
+                    .map(|v| match v {
+                        WastArg::Component(v) => component::val(v),
                         WastArg::Core(_) => bail!("expected core function, found component"),
                     })
                     .collect::<Result<Vec<_>>>()?;
diff --git a/examples/wasi-async/main.rs b/examples/wasi-async/main.rs
index 87661cd24..9fea4ce8a 100644
--- a/examples/wasi-async/main.rs
+++ b/examples/wasi-async/main.rs
@@ -46,7 +46,7 @@ async fn main() -> Result<()> {
     // Add the WASI preview1 API to the linker (will be implemented in terms of
     // the preview2 API)
     let mut linker: Linker<WasiHostCtx> = Linker::new(&engine);
-    wasmtime_wasi::preview1::add_to_linker_async(&mut linker)?;
+    wasmtime_wasi::preview1::add_to_linker_async(&mut linker, |t| t)?;
 
     // Add capabilities (e.g. filesystem access) to the WASI preview2 context here.
     let wasi_ctx = wasmtime_wasi::WasiCtxBuilder::new().inherit_stdio().build();
diff --git a/src/commands/run.rs b/src/commands/run.rs
index c0c04c787..8f25ba91e 100644
--- a/src/commands/run.rs
+++ b/src/commands/run.rs
@@ -628,9 +628,9 @@ impl RunCommand {
                         // default-disabled in the future.
                         (Some(true), _) | (None, Some(false) | None) => {
                             if self.run.common.wasi.preview0 != Some(false) {
-                                wasmtime_wasi::preview0::add_to_linker_sync(linker)?;
+                                wasmtime_wasi::preview0::add_to_linker_sync(linker, |t| t)?;
                             }
-                            wasmtime_wasi::preview1::add_to_linker_sync(linker)?;
+                            wasmtime_wasi::preview1::add_to_linker_sync(linker, |t| t)?;
                             self.set_preview2_ctx(store)?;
                         }
                     }
diff --git a/src/commands/settings.rs b/src/commands/settings.rs
index 1a4a967f0..7055f9203 100644
--- a/src/commands/settings.rs
+++ b/src/commands/settings.rs
@@ -5,7 +5,7 @@ use clap::Parser;
 use serde::{ser::SerializeMap, Serialize};
 use std::collections::BTreeMap;
 use std::str::FromStr;
-use wasmtime_environ::{CompilerBuilder, FlagValue, Setting, SettingKind};
+use wasmtime_environ::{CompilerBuilder, FlagValue, Setting, SettingKind, Tunables};
 
 /// Displays available Cranelift settings for a target.
 #[derive(Parser, PartialEq)]
@@ -108,10 +108,16 @@ impl SettingsCommand {
     pub fn execute(self) -> Result<()> {
         // Gather settings from the cranelift compiler builder
         let mut builder = wasmtime_cranelift::builder(None)?;
-        if let Some(target) = &self.target {
+        let tunables = if let Some(target) = &self.target {
             let target = target_lexicon::Triple::from_str(target).map_err(|e| anyhow!(e))?;
+            let tunables = Tunables::default_for_target(&target)?;
             builder.target(target)?;
-        }
+            tunables
+        } else {
+            Tunables::default_host()
+        };
+
+        builder.set_tunables(tunables)?;
         let mut settings = Settings::from_builder(&builder);
 
         // Add inferred settings if no target specified
diff --git a/supply-chain/audits.toml b/supply-chain/audits.toml
index d3551da5f..5f6db2f7b 100644
--- a/supply-chain/audits.toml
+++ b/supply-chain/audits.toml
@@ -1695,6 +1695,12 @@ criteria = "safe-to-deploy"
 version = "0.3.3"
 notes = "I am the author of this crate."
 
+[[audits.mach2]]
+who = "Nick Fitzgerald <fitzgen@gmail.com>"
+criteria = "safe-to-deploy"
+delta = "0.4.1 -> 0.4.2"
+notes = "It does unsafe FFI bindings, as expected. I didn't check the FFI bindings against the C headers."
+
 [[audits.matchers]]
 who = "Pat Hickey <phickey@fastly.com>"
 criteria = "safe-to-deploy"
diff --git a/supply-chain/imports.lock b/supply-chain/imports.lock
index 14d7058cb..3a2bea9a3 100644
--- a/supply-chain/imports.lock
+++ b/supply-chain/imports.lock
@@ -2453,6 +2453,12 @@ criteria = "safe-to-deploy"
 version = "0.4.17"
 aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml"
 
+[[audits.mozilla.audits.mach2]]
+who = "Gabriele Svelto <gsvelto@mozilla.com>"
+criteria = "safe-to-deploy"
+version = "0.4.1"
+aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml"
+
 [[audits.mozilla.audits.memoffset]]
 who = "Gabriele Svelto <gsvelto@mozilla.com>"
 criteria = "safe-to-deploy"
diff --git a/winch/codegen/src/codegen/mod.rs b/winch/codegen/src/codegen/mod.rs
index 475c7f74d..8358d71d8 100644
--- a/winch/codegen/src/codegen/mod.rs
+++ b/winch/codegen/src/codegen/mod.rs
@@ -494,7 +494,6 @@ where
         let index = Index::from_typed_reg(self.context.pop_to_reg(self.masm, None));
         let offset = bounds::ensure_index_and_offset(self.masm, index, memarg.offset, ptr_size);
         let offset_with_access_size = add_offset_and_access_size(offset, access_size);
-        let scratch = <M::ABI as ABI>::scratch_reg();
 
         let addr = match heap.style {
             // == Dynamic Heaps ==
@@ -509,17 +508,25 @@ where
                     bounds::load_dynamic_heap_bounds(&mut self.context, self.masm, &heap, ptr_size);
 
                 let index_reg = index.as_typed_reg().reg;
-
-                // Move the value of the index to the scratch register
-                // to perform the overflow check to avoid clobbering the
-                // initial index value.
-                self.masm.mov(index_reg.into(), scratch, heap.ty.into());
+                // Allocate a temporary register to hold
+                //      index + offset + access_size
+                //  which will serve as the check condition.
+                let index_offset_and_access_size = self.context.any_gpr(self.masm);
+
+                // Move the value of the index to the
+                // index_offset_and_access_size register to perform the overflow
+                // check to avoid clobbering the initial index value.
+                self.masm.mov(
+                    index_reg.into(),
+                    index_offset_and_access_size,
+                    heap.ty.into(),
+                );
                 // Perform
                 // index = index + offset + access_size, trapping if the
                 // addition overflows.
                 self.masm.checked_uadd(
-                    scratch,
-                    scratch,
+                    index_offset_and_access_size,
+                    index_offset_and_access_size,
                     RegImm::i64(offset_with_access_size as i64),
                     ptr_size,
                     TrapCode::HeapOutOfBounds,
@@ -534,14 +541,18 @@ where
                     bounds,
                     index,
                     offset,
-                    |masm, bounds, index| {
-                        let index_reg = index.as_typed_reg().reg;
+                    |masm, bounds, _| {
                         let bounds_reg = bounds.as_typed_reg().reg;
-                        masm.cmp(bounds_reg.into(), index_reg.into(), heap.ty.into());
+                        masm.cmp(
+                            bounds_reg.into(),
+                            index_offset_and_access_size.into(),
+                            heap.ty.into(),
+                        );
                         IntCmpKind::GtU
                     },
                 );
                 self.context.free_reg(bounds.as_typed_reg().reg);
+                self.context.free_reg(index_offset_and_access_size);
                 Some(addr)
             }
 


Test patch:
diff --git a/cranelift/filetests/filetests/egraph/issue-7891.clif b/cranelift/filetests/filetests/egraph/issue-7891.clif
new file mode 100644
index 000000000..c30e1edc7
--- /dev/null
+++ b/cranelift/filetests/filetests/egraph/issue-7891.clif
@@ -0,0 +1,103 @@
+test optimize
+set opt_level=speed
+target x86_64
+
+; An eclass that is transitively referenced by a side-effecting instruction in a
+; block must not contain any alternatives that use values which are not available
+; in that block. If we violate this rule, then we only get correct results if
+; elaboration happens to not choose the bad alternatives, which means we're
+; relying on the cost function, and there's no reason to believe that's reliable
+; for this purpose.
+; 
+; Unfortunately, we can't currently write test assertions about which alternatives
+; are generated by simplification in the egraph pass. We can only observe which
+; choice was selected by the cost function. So instead these tests try to hit
+; assertions in the egraph pass if the above rule is broken.
+; 
+; These tests are carefully constructed to hit specific code paths in the egraph
+; pass without getting key parts transformed away by other optimizations first:
+; 
+; - There are no loops, to avoid having LICM move the code we care about.
+; 
+; - There are two blocks which contain pure instructions that are identical after
+;   simplification.
+; 
+; - Neither of these blocks dominates the other, so GVN will only combine them if
+;   we allow it to consider instructions in different scopes.
+; 
+; - References to these blocks are constructed to create a specific domtree so the
+;   egraph pass will traverse them in a specific order.
+; 
+; - The block which the egraph pass examines first uses a value that is only
+;   available in that block: a block-param or the result of an effectful
+;   instruction.
+; 
+; By doing all that, we create an opportunity where the first block could produce
+; an eclass which references the value that's only available in that block, and
+; GVN could reuse that eclass for the equivalent instruction in the second block.
+; If the egraph pass does both of those things then it violates the rule above.
+;
+; For more discussion, see:
+; https://github.com/bytecodealliance/wasmtime/pull/7891
+
+function %bad_param_reuse(i32) -> i8 {
+block0(v0: i32): 
+    v1 = iconst.i32 0
+    brif v0, block1, block2(v0)
+
+block1:
+; To prevent the remove-constant-phi pass from removing the block-param, there are
+; multiple references to block2 using different values for the block-param. This
+; reference is dynamically unreachable but Cranelift doesn't know that.
+    brif v0, block3, block2(v1)
+
+block2(v2: i32):
+    v3 = isub v2, v2
+    ; v3 simplifies to `iconst 0`, which is equivalent to v1
+    v4 = icmp eq v0, v3
+    return v4
+
+block3:
+    ; If this icmp replaces v1 with v3, elaboration would clone the isub into this
+    ; block, introducing a reference to v2, which is not in scope here.
+    v5 = icmp eq v0, v1
+    return v5
+}
+
+; check: block2(v2: i32):
+; check:     v8 = iconst.i32 0
+; check:     v4 = icmp.i32 eq v0, v8  ; v8 = 0
+; check:     return v4
+; check: block3:
+; check:     v6 = iconst.i32 0
+; check:     v7 = icmp.i32 eq v0, v6  ; v6 = 0
+; check:     return v7
+
+function %bad_effect_reuse(i64) -> i8 {
+block0(v0: i64):
+    brif v0, block1, block2
+
+block1:
+    v1 = load.i64 heap v0
+    v2 = isub v1, v1
+    ; v2 simplifies to `iconst 0`, which is equivalent to v4
+    v3 = icmp eq v0, v2
+    return v3
+
+block2:
+    v4 = iconst.i64 0
+    ; If this icmp replaces v4 with v2, elaboration would clone the isub into this
+    ; block, introducing a reference to v1, which is not in scope here.
+    v5 = icmp eq v0, v4
+    return v5
+}
+
+; check: block1:
+; check:     v1 = load.i64 heap v0
+; check:     v7 = iconst.i64 0
+; check:     v8 = icmp.i64 eq v0, v7  ; v7 = 0
+; check:     return v8
+; check: block2:
+; check:     v6 = iconst.i64 0
+; check:     v3 = icmp.i64 eq v0, v6  ; v6 = 0
+; check:     return v3
diff --git a/cranelift/filetests/filetests/isa/aarch64/winch.clif b/cranelift/filetests/filetests/isa/aarch64/winch.clif
new file mode 100644
index 000000000..a872dab3e
--- /dev/null
+++ b/cranelift/filetests/filetests/isa/aarch64/winch.clif
@@ -0,0 +1,240 @@
+test compile precise-output
+target aarch64
+
+function %f1() winch {
+block0:
+    return
+}
+
+; VCode:
+; block0:
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   ret
+
+function %f2(i64, i64, i64, i64, i64, i64) -> i64 winch {
+  sig0 = () winch
+  fn0 = %g sig0
+
+block0(v0:i64, v1:i64, v2:i64, v3:i64, v4:i64, v5:i64):
+  call fn0()
+  return v0
+}
+
+; VCode:
+;   stp fp, lr, [sp, #-16]!
+;   mov fp, sp
+;   sub sp, sp, #16
+; block0:
+;   str x0, [sp]
+;   load_ext_name x7, TestCase(%g)+0
+;   blr x7
+;   ldr x0, [sp]
+;   add sp, sp, #16
+;   ldp fp, lr, [sp], #16
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   stp x29, x30, [sp, #-0x10]!
+;   mov x29, sp
+;   sub sp, sp, #0x10
+; block1: ; offset 0xc
+;   stur x0, [sp]
+;   ldr x7, #0x18
+;   b #0x20
+;   .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 %g 0
+;   .byte 0x00, 0x00, 0x00, 0x00
+;   blr x7
+;   ldur x0, [sp]
+;   add sp, sp, #0x10
+;   ldp x29, x30, [sp], #0x10
+;   ret
+
+function %f3(i64, i64, i64, i64, i64, i64) -> i64 {
+  sig0 = () winch
+  fn0 = %g sig0
+
+block0(v0:i64, v1:i64, v2:i64, v3:i64, v4:i64, v5:i64):
+  call fn0()
+  return v0
+}
+
+; VCode:
+;   stp fp, lr, [sp, #-16]!
+;   mov fp, sp
+;   stp x27, x28, [sp, #-16]!
+;   stp x25, x26, [sp, #-16]!
+;   stp x23, x24, [sp, #-16]!
+;   stp x21, x22, [sp, #-16]!
+;   stp x19, x20, [sp, #-16]!
+;   stp d14, d15, [sp, #-16]!
+;   stp d12, d13, [sp, #-16]!
+;   stp d10, d11, [sp, #-16]!
+;   stp d8, d9, [sp, #-16]!
+;   sub sp, sp, #16
+; block0:
+;   str x0, [sp]
+;   load_ext_name x7, TestCase(%g)+0
+;   blr x7
+;   ldr x0, [sp]
+;   add sp, sp, #16
+;   ldp d8, d9, [sp], #16
+;   ldp d10, d11, [sp], #16
+;   ldp d12, d13, [sp], #16
+;   ldp d14, d15, [sp], #16
+;   ldp x19, x20, [sp], #16
+;   ldp x21, x22, [sp], #16
+;   ldp x23, x24, [sp], #16
+;   ldp x25, x26, [sp], #16
+;   ldp x27, x28, [sp], #16
+;   ldp fp, lr, [sp], #16
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   stp x29, x30, [sp, #-0x10]!
+;   mov x29, sp
+;   stp x27, x28, [sp, #-0x10]!
+;   stp x25, x26, [sp, #-0x10]!
+;   stp x23, x24, [sp, #-0x10]!
+;   stp x21, x22, [sp, #-0x10]!
+;   stp x19, x20, [sp, #-0x10]!
+;   stp d14, d15, [sp, #-0x10]!
+;   stp d12, d13, [sp, #-0x10]!
+;   stp d10, d11, [sp, #-0x10]!
+;   stp d8, d9, [sp, #-0x10]!
+;   sub sp, sp, #0x10
+; block1: ; offset 0x30
+;   stur x0, [sp]
+;   ldr x7, #0x3c
+;   b #0x44
+;   .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 %g 0
+;   .byte 0x00, 0x00, 0x00, 0x00
+;   blr x7
+;   ldur x0, [sp]
+;   add sp, sp, #0x10
+;   ldp d8, d9, [sp], #0x10
+;   ldp d10, d11, [sp], #0x10
+;   ldp d12, d13, [sp], #0x10
+;   ldp d14, d15, [sp], #0x10
+;   ldp x19, x20, [sp], #0x10
+;   ldp x21, x22, [sp], #0x10
+;   ldp x23, x24, [sp], #0x10
+;   ldp x25, x26, [sp], #0x10
+;   ldp x27, x28, [sp], #0x10
+;   ldp x29, x30, [sp], #0x10
+;   ret
+
+function %f4(i64, i64, i64, i64, i64, i64) -> i64 winch {
+  sig0 = (i64, i64, i64, i64, i64, i64) -> i64 winch
+  fn0 = %g sig0
+
+block0(v0:i64, v1:i64, v2:i64, v3:i64, v4:i64, v5:i64):
+  v6 = call fn0(v5, v1, v2, v3, v4, v0)
+  return v6
+}
+
+; VCode:
+;   stp fp, lr, [sp, #-16]!
+;   mov fp, sp
+; block0:
+;   mov x6, x0
+;   mov x0, x5
+;   mov x5, x6
+;   load_ext_name x8, TestCase(%g)+0
+;   blr x8
+;   ldp fp, lr, [sp], #16
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   stp x29, x30, [sp, #-0x10]!
+;   mov x29, sp
+; block1: ; offset 0x8
+;   mov x6, x0
+;   mov x0, x5
+;   mov x5, x6
+;   ldr x8, #0x1c
+;   b #0x24
+;   .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 %g 0
+;   .byte 0x00, 0x00, 0x00, 0x00
+;   blr x8
+;   ldp x29, x30, [sp], #0x10
+;   ret
+
+function %f5(i64, i64, i64, i64, i64, i64) -> i64 {
+  sig0 = (i64, i64, i64, i64, i64, i64) -> i64 winch
+  fn0 = %g sig0
+
+block0(v0:i64, v1:i64, v2:i64, v3:i64, v4:i64, v5:i64):
+  v6 = call fn0(v5, v1, v2, v3, v4, v0)
+  return v6
+}
+
+; VCode:
+;   stp fp, lr, [sp, #-16]!
+;   mov fp, sp
+;   stp x27, x28, [sp, #-16]!
+;   stp x25, x26, [sp, #-16]!
+;   stp x23, x24, [sp, #-16]!
+;   stp x21, x22, [sp, #-16]!
+;   stp x19, x20, [sp, #-16]!
+;   stp d14, d15, [sp, #-16]!
+;   stp d12, d13, [sp, #-16]!
+;   stp d10, d11, [sp, #-16]!
+;   stp d8, d9, [sp, #-16]!
+; block0:
+;   mov x6, x0
+;   mov x0, x5
+;   mov x5, x6
+;   load_ext_name x8, TestCase(%g)+0
+;   blr x8
+;   ldp d8, d9, [sp], #16
+;   ldp d10, d11, [sp], #16
+;   ldp d12, d13, [sp], #16
+;   ldp d14, d15, [sp], #16
+;   ldp x19, x20, [sp], #16
+;   ldp x21, x22, [sp], #16
+;   ldp x23, x24, [sp], #16
+;   ldp x25, x26, [sp], #16
+;   ldp x27, x28, [sp], #16
+;   ldp fp, lr, [sp], #16
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   stp x29, x30, [sp, #-0x10]!
+;   mov x29, sp
+;   stp x27, x28, [sp, #-0x10]!
+;   stp x25, x26, [sp, #-0x10]!
+;   stp x23, x24, [sp, #-0x10]!
+;   stp x21, x22, [sp, #-0x10]!
+;   stp x19, x20, [sp, #-0x10]!
+;   stp d14, d15, [sp, #-0x10]!
+;   stp d12, d13, [sp, #-0x10]!
+;   stp d10, d11, [sp, #-0x10]!
+;   stp d8, d9, [sp, #-0x10]!
+; block1: ; offset 0x2c
+;   mov x6, x0
+;   mov x0, x5
+;   mov x5, x6
+;   ldr x8, #0x40
+;   b #0x48
+;   .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 %g 0
+;   .byte 0x00, 0x00, 0x00, 0x00
+;   blr x8
+;   ldp d8, d9, [sp], #0x10
+;   ldp d10, d11, [sp], #0x10
+;   ldp d12, d13, [sp], #0x10
+;   ldp d14, d15, [sp], #0x10
+;   ldp x19, x20, [sp], #0x10
+;   ldp x21, x22, [sp], #0x10
+;   ldp x23, x24, [sp], #0x10
+;   ldp x25, x26, [sp], #0x10
+;   ldp x27, x28, [sp], #0x10
+;   ldp x29, x30, [sp], #0x10
+;   ret
+
diff --git a/cranelift/filetests/filetests/isa/x64/winch.clif b/cranelift/filetests/filetests/isa/x64/winch.clif
new file mode 100644
index 000000000..876d6c90f
--- /dev/null
+++ b/cranelift/filetests/filetests/isa/x64/winch.clif
@@ -0,0 +1,217 @@
+test compile precise-output
+target x86_64
+
+function %f1() winch {
+block0:
+    return
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+function %f2(i64, i64, i64, i64, i64, i64) -> i64 winch {
+  sig0 = () winch
+  fn0 = %g sig0
+
+block0(v0:i64, v1:i64, v2:i64, v3:i64, v4:i64, v5:i64):
+  call fn0()
+  return v0
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+;   subq    %rsp, $16, %rsp
+; block0:
+;   movq    %rdi, rsp(0 + virtual offset)
+;   load_ext_name %g+0, %r10
+;   call    *%r10
+;   movq    rsp(0 + virtual offset), %rax
+;   addq    %rsp, $16, %rsp
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+;   subq $0x10, %rsp
+; block1: ; offset 0x8
+;   movq %rdi, (%rsp)
+;   movabsq $0, %r10 ; reloc_external Abs8 %g 0
+;   callq *%r10
+;   movq (%rsp), %rax
+;   addq $0x10, %rsp
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+function %f3(i64, i64, i64, i64, i64, i64) -> i64 {
+  sig0 = () winch
+  fn0 = %g sig0
+
+block0(v0:i64, v1:i64, v2:i64, v3:i64, v4:i64, v5:i64):
+  call fn0()
+  return v0
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+;   subq    %rsp, $64, %rsp
+;   movq    %rbx, 16(%rsp)
+;   movq    %r12, 24(%rsp)
+;   movq    %r13, 32(%rsp)
+;   movq    %r14, 40(%rsp)
+;   movq    %r15, 48(%rsp)
+; block0:
+;   movq    %rdi, rsp(0 + virtual offset)
+;   load_ext_name %g+0, %r10
+;   call    *%r10
+;   movq    rsp(0 + virtual offset), %rax
+;   movq    16(%rsp), %rbx
+;   movq    24(%rsp), %r12
+;   movq    32(%rsp), %r13
+;   movq    40(%rsp), %r14
+;   movq    48(%rsp), %r15
+;   addq    %rsp, $64, %rsp
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+;   subq $0x40, %rsp
+;   movq %rbx, 0x10(%rsp)
+;   movq %r12, 0x18(%rsp)
+;   movq %r13, 0x20(%rsp)
+;   movq %r14, 0x28(%rsp)
+;   movq %r15, 0x30(%rsp)
+; block1: ; offset 0x21
+;   movq %rdi, (%rsp)
+;   movabsq $0, %r10 ; reloc_external Abs8 %g 0
+;   callq *%r10
+;   movq (%rsp), %rax
+;   movq 0x10(%rsp), %rbx
+;   movq 0x18(%rsp), %r12
+;   movq 0x20(%rsp), %r13
+;   movq 0x28(%rsp), %r14
+;   movq 0x30(%rsp), %r15
+;   addq $0x40, %rsp
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+function %f4(i64, i64, i64, i64, i64, i64) -> i64 winch {
+  sig0 = (i64, i64, i64, i64, i64, i64) -> i64 winch
+  fn0 = %g sig0
+
+block0(v0:i64, v1:i64, v2:i64, v3:i64, v4:i64, v5:i64):
+  v6 = call fn0(v5, v1, v2, v3, v4, v0)
+  return v6
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   movq    %rdi, %rax
+;   movq    %r9, %rdi
+;   movq    %rax, %r9
+;   load_ext_name %g+0, %r11
+;   call    *%r11
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   movq %rdi, %rax
+;   movq %r9, %rdi
+;   movq %rax, %r9
+;   movabsq $0, %r11 ; reloc_external Abs8 %g 0
+;   callq *%r11
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+function %f5(i64, i64, i64, i64, i64, i64) -> i64 {
+  sig0 = (i64, i64, i64, i64, i64, i64) -> i64 winch
+  fn0 = %g sig0
+
+block0(v0:i64, v1:i64, v2:i64, v3:i64, v4:i64, v5:i64):
+  v6 = call fn0(v5, v1, v2, v3, v4, v0)
+  return v6
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+;   subq    %rsp, $48, %rsp
+;   movq    %rbx, 0(%rsp)
+;   movq    %r12, 8(%rsp)
+;   movq    %r13, 16(%rsp)
+;   movq    %r14, 24(%rsp)
+;   movq    %r15, 32(%rsp)
+; block0:
+;   movq    %rdi, %rax
+;   movq    %r9, %rdi
+;   movq    %rax, %r9
+;   load_ext_name %g+0, %r11
+;   call    *%r11
+;   movq    0(%rsp), %rbx
+;   movq    8(%rsp), %r12
+;   movq    16(%rsp), %r13
+;   movq    24(%rsp), %r14
+;   movq    32(%rsp), %r15
+;   addq    %rsp, $48, %rsp
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+;   subq $0x30, %rsp
+;   movq %rbx, (%rsp)
+;   movq %r12, 8(%rsp)
+;   movq %r13, 0x10(%rsp)
+;   movq %r14, 0x18(%rsp)
+;   movq %r15, 0x20(%rsp)
+; block1: ; offset 0x20
+;   movq %rdi, %rax
+;   movq %r9, %rdi
+;   movq %rax, %r9
+;   movabsq $0, %r11 ; reloc_external Abs8 %g 0
+;   callq *%r11
+;   movq (%rsp), %rbx
+;   movq 8(%rsp), %r12
+;   movq 0x10(%rsp), %r13
+;   movq 0x18(%rsp), %r14
+;   movq 0x20(%rsp), %r15
+;   addq $0x30, %rsp
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
diff --git a/crates/wasi/tests/all/preview1.rs b/crates/wasi/tests/all/preview1.rs
index 7dd8f2db5..f0d958fdf 100644
--- a/crates/wasi/tests/all/preview1.rs
+++ b/crates/wasi/tests/all/preview1.rs
@@ -8,10 +8,10 @@ async fn run(path: &str, inherit_stdio: bool) -> Result<()> {
     let path = Path::new(path);
     let name = path.file_stem().unwrap().to_str().unwrap();
     let mut config = Config::new();
-    config.async_support(true).wasm_component_model(true);
+    config.async_support(true);
     let engine = Engine::new(&config)?;
     let mut linker = Linker::new(&engine);
-    add_to_linker_async(&mut linker)?;
+    add_to_linker_async(&mut linker, |t| t)?;
 
     let module = Module::from_file(&engine, path)?;
     let (mut store, _td) = store(&engine, name, inherit_stdio)?;
diff --git a/tests/all/cli_tests.rs b/tests/all/cli_tests.rs
index 175d3de1a..8fab74ac6 100644
--- a/tests/all/cli_tests.rs
+++ b/tests/all/cli_tests.rs
@@ -1588,3 +1588,10 @@ mod test_programs {
         Ok(())
     }
 }
+
+#[test]
+fn settings_command() -> Result<()> {
+    let output = run_wasmtime(&["settings"])?;
+    assert!(output.contains("Cranelift settings for target"));
+    Ok(())
+}
diff --git a/tests/all/component_model/dynamic.rs b/tests/all/component_model/dynamic.rs
index f83f550d8..e01742a79 100644
--- a/tests/all/component_model/dynamic.rs
+++ b/tests/all/component_model/dynamic.rs
@@ -4,7 +4,7 @@ use super::{make_echo_component, make_echo_component_with_params, Param, Type};
 use anyhow::Result;
 use component_test_util::FuncExt;
 use wasmtime::component::types::{self, Case, ComponentItem, Field};
-use wasmtime::component::{self, Component, Linker, ResourceType, Val};
+use wasmtime::component::{Component, Linker, ResourceType, Val};
 use wasmtime::{Module, Store};
 use wasmtime_component_util::REALLOC_AND_FREE;
 
@@ -102,7 +102,7 @@ fn strings() -> Result<()> {
     let component = Component::new(&engine, make_echo_component("string", 8))?;
     let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
     let func = instance.get_func(&mut store, "echo").unwrap();
-    let input = Val::String(Box::from("hello, component!"));
+    let input = Val::String("hello, component!".into());
     let mut output = [Val::Bool(false)];
     func.call_and_post_return(&mut store, &[input.clone()], &mut output)?;
     assert_eq!(input, output[0]);
@@ -118,12 +118,11 @@ fn lists() -> Result<()> {
     let component = Component::new(&engine, make_echo_component("(list u32)", 8))?;
     let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
     let func = instance.get_func(&mut store, "echo").unwrap();
-    let ty = &func.params(&store)[0];
-    let input = ty.unwrap_list().new_val(Box::new([
+    let input = Val::List(vec![
         Val::U32(32343),
         Val::U32(79023439),
         Val::U32(2084037802),
-    ]))?;
+    ]);
     let mut output = [Val::Bool(false)];
     func.call_and_post_return(&mut store, &[input.clone()], &mut output)?;
 
@@ -131,15 +130,14 @@ fn lists() -> Result<()> {
 
     // Sad path: type mismatch
 
-    let err = ty
-        .unwrap_list()
-        .new_val(Box::new([
-            Val::U32(32343),
-            Val::U32(79023439),
-            Val::Float32(3.14159265),
-        ]))
+    let err = Val::List(vec![
+        Val::U32(32343),
+        Val::U32(79023439),
+        Val::Float32(3.14159265),
+    ]);
+    let err = func
+        .call_and_post_return(&mut store, &[err], &mut output)
         .unwrap_err();
-
     assert!(err.to_string().contains("type mismatch"), "{err}");
 
     Ok(())
@@ -175,18 +173,17 @@ fn records() -> Result<()> {
     )?;
     let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
     let func = instance.get_func(&mut store, "echo").unwrap();
-    let ty = &func.params(&store)[0];
-    let inner_type = &ty.unwrap_record().fields().nth(2).unwrap().ty;
-    let input = ty.unwrap_record().new_val([
-        ("A", Val::U32(32343)),
-        ("B", Val::Float64(3.14159265)),
+    let input = Val::Record(vec![
+        ("A".into(), Val::U32(32343)),
+        ("B".into(), Val::Float64(3.14159265)),
         (
-            "C",
-            inner_type
-                .unwrap_record()
-                .new_val([("D", Val::Bool(false)), ("E", Val::U32(2084037802))])?,
+            "C".into(),
+            Val::Record(vec![
+                ("D".into(), Val::Bool(false)),
+                ("E".into(), Val::U32(2084037802)),
+            ]),
         ),
-    ])?;
+    ]);
     let mut output = [Val::Bool(false)];
     func.call_and_post_return(&mut store, &[input.clone()], &mut output)?;
 
@@ -194,53 +191,61 @@ fn records() -> Result<()> {
 
     // Sad path: type mismatch
 
-    let err = ty
-        .unwrap_record()
-        .new_val([
-            ("A", Val::S32(32343)),
-            ("B", Val::Float64(3.14159265)),
-            (
-                "C",
-                inner_type
-                    .unwrap_record()
-                    .new_val([("D", Val::Bool(false)), ("E", Val::U32(2084037802))])?,
-            ),
-        ])
+    let err = Val::Record(vec![
+        ("A".into(), Val::S32(32343)),
+        ("B".into(), Val::Float64(3.14159265)),
+        (
+            "C".into(),
+            Val::Record(vec![
+                ("D".into(), Val::Bool(false)),
+                ("E".into(), Val::U32(2084037802)),
+            ]),
+        ),
+    ]);
+    let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
+    let func = instance.get_func(&mut store, "echo").unwrap();
+    let err = func
+        .call_and_post_return(&mut store, &[err], &mut output)
         .unwrap_err();
-
     assert!(err.to_string().contains("type mismatch"), "{err}");
 
     // Sad path: too many fields
 
-    let err = ty
-        .unwrap_record()
-        .new_val([
-            ("A", Val::U32(32343)),
-            ("B", Val::Float64(3.14159265)),
-            (
-                "C",
-                inner_type
-                    .unwrap_record()
-                    .new_val([("D", Val::Bool(false)), ("E", Val::U32(2084037802))])?,
-            ),
-            ("F", Val::Bool(true)),
-        ])
+    let err = Val::Record(vec![
+        ("A".into(), Val::U32(32343)),
+        ("B".into(), Val::Float64(3.14159265)),
+        (
+            "C".into(),
+            Val::Record(vec![
+                ("D".into(), Val::Bool(false)),
+                ("E".into(), Val::U32(2084037802)),
+            ]),
+        ),
+        ("F".into(), Val::Bool(true)),
+    ]);
+    let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
+    let func = instance.get_func(&mut store, "echo").unwrap();
+    let err = func
+        .call_and_post_return(&mut store, &[err], &mut output)
         .unwrap_err();
-
     assert!(
-        err.to_string().contains("expected 3 value(s); got 4"),
+        err.to_string().contains("expected 3 fields, got 4"),
         "{err}"
     );
 
     // Sad path: too few fields
 
-    let err = ty
-        .unwrap_record()
-        .new_val([("A", Val::U32(32343)), ("B", Val::Float64(3.14159265))])
+    let err = Val::Record(vec![
+        ("A".into(), Val::U32(32343)),
+        ("B".into(), Val::Float64(3.14159265)),
+    ]);
+    let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
+    let func = instance.get_func(&mut store, "echo").unwrap();
+    let err = func
+        .call_and_post_return(&mut store, &[err], &mut output)
         .unwrap_err();
-
     assert!(
-        err.to_string().contains("expected 3 value(s); got 2"),
+        err.to_string().contains("expected 3 fields, got 2"),
         "{err}"
     );
 
@@ -275,10 +280,7 @@ fn variants() -> Result<()> {
     )?;
     let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
     let func = instance.get_func(&mut store, "echo").unwrap();
-    let ty = &func.params(&store)[0];
-    let input = ty
-        .unwrap_variant()
-        .new_val("B", Some(Val::Float64(3.14159265)))?;
+    let input = Val::Variant("B".into(), Some(Box::new(Val::Float64(3.14159265))));
     let mut output = [Val::Bool(false)];
     func.call_and_post_return(&mut store, &[input.clone()], &mut output)?;
 
@@ -299,28 +301,33 @@ fn variants() -> Result<()> {
     )?;
     let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
     let func = instance.get_func(&mut store, "echo").unwrap();
-    let ty = &func.params(&store)[0];
-    let c_type = &ty.unwrap_variant().cases().nth(2).unwrap().ty.unwrap();
-    let input = ty.unwrap_variant().new_val(
-        "C",
-        Some(
-            c_type
-                .unwrap_record()
-                .new_val([("D", Val::Bool(true)), ("E", Val::U32(314159265))])?,
-        ),
-    )?;
+    let input = Val::Variant(
+        "C".into(),
+        Some(Box::new(Val::Record(vec![
+            ("D".into(), Val::Bool(true)),
+            ("E".into(), Val::U32(314159265)),
+        ]))),
+    );
     func.call_and_post_return(&mut store, &[input.clone()], &mut output)?;
 
     assert_eq!(input, output[0]);
 
     // Sad path: type mismatch
 
-    let err = ty
-        .unwrap_variant()
-        .new_val("B", Some(Val::U64(314159265)))
+    let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
+    let func = instance.get_func(&mut store, "echo").unwrap();
+    let err = Val::Variant("B".into(), Some(Box::new(Val::U64(314159265))));
+    let err = func
+        .call_and_post_return(&mut store, &[err], &mut output)
         .unwrap_err();
     assert!(err.to_string().contains("type mismatch"), "{err}");
-    let err = ty.unwrap_variant().new_val("B", None).unwrap_err();
+
+    let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
+    let func = instance.get_func(&mut store, "echo").unwrap();
+    let err = Val::Variant("B".into(), None);
+    let err = func
+        .call_and_post_return(&mut store, &[err], &mut output)
+        .unwrap_err();
     assert!(
         err.to_string().contains("expected a payload for case `B`"),
         "{err}"
@@ -328,12 +335,20 @@ fn variants() -> Result<()> {
 
     // Sad path: unknown case
 
-    let err = ty
-        .unwrap_variant()
-        .new_val("D", Some(Val::U64(314159265)))
+    let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
+    let func = instance.get_func(&mut store, "echo").unwrap();
+    let err = Val::Variant("D".into(), Some(Box::new(Val::U64(314159265))));
+    let err = func
+        .call_and_post_return(&mut store, &[err], &mut output)
         .unwrap_err();
     assert!(err.to_string().contains("unknown variant case"), "{err}");
-    let err = ty.unwrap_variant().new_val("D", None).unwrap_err();
+
+    let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
+    let func = instance.get_func(&mut store, "echo").unwrap();
+    let err = Val::Variant("D".into(), None);
+    let err = func
+        .call_and_post_return(&mut store, &[err], &mut output)
+        .unwrap_err();
     assert!(err.to_string().contains("unknown variant case"), "{err}");
 
     // Make sure we lift variants which have cases of different sizes with the correct alignment
@@ -365,17 +380,13 @@ fn variants() -> Result<()> {
     )?;
     let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
     let func = instance.get_func(&mut store, "echo").unwrap();
-    let ty = &func.params(&store)[0];
-    let a_type = &ty.unwrap_record().fields().nth(0).unwrap().ty;
-    let input = ty.unwrap_record().new_val([
+    let input = Val::Record(vec![
         (
-            "A",
-            a_type
-                .unwrap_variant()
-                .new_val("A", Some(Val::U32(314159265)))?,
+            "A".into(),
+            Val::Variant("A".into(), Some(Box::new(Val::U32(314159265)))),
         ),
-        ("B", Val::U32(628318530)),
-    ])?;
+        ("B".into(), Val::U32(628318530)),
+    ]);
     func.call_and_post_return(&mut store, &[input.clone()], &mut output)?;
 
     assert_eq!(input, output[0]);
@@ -397,8 +408,7 @@ fn flags() -> Result<()> {
     )?;
     let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
     let func = instance.get_func(&mut store, "echo").unwrap();
-    let ty = &func.params(&store)[0];
-    let input = ty.unwrap_flags().new_val(&["B", "D"])?;
+    let input = Val::Flags(vec!["B".into(), "D".into()]);
     let mut output = [Val::Bool(false)];
     func.call_and_post_return(&mut store, &[input.clone()], &mut output)?;
 
@@ -406,8 +416,10 @@ fn flags() -> Result<()> {
 
     // Sad path: unknown flags
 
-    let err = ty.unwrap_flags().new_val(&["B", "D", "F"]).unwrap_err();
-
+    let err = Val::Flags(vec!["B".into(), "D".into(), "F".into()]);
+    let err = func
+        .call_and_post_return(&mut store, &[err], &mut output)
+        .unwrap_err();
     assert!(err.to_string().contains("unknown flag"), "{err}");
 
     Ok(())
@@ -490,62 +502,42 @@ fn everything() -> Result<()> {
     )?;
     let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
     let func = instance.get_func(&mut store, "echo").unwrap();
-    let ty = &func.params(&store)[0];
-    let types = ty
-        .unwrap_record()
-        .fields()
-        .map(|field| field.ty)
-        .collect::<Box<[component::Type]>>();
-    let (b_type, c_type, f_type, j_type, y_type, aa_type, bb_type) = (
-        &types[1], &types[2], &types[3], &types[4], &types[13], &types[14], &types[15],
-    );
-    let f_element_type = &f_type.unwrap_list().ty();
-    let input = ty.unwrap_record().new_val([
-        ("A", Val::U32(32343)),
-        ("B", b_type.unwrap_enum().new_val("b")?),
-        (
-            "C",
-            c_type
-                .unwrap_record()
-                .new_val([("D", Val::Bool(false)), ("E", Val::U32(2084037802))])?,
-        ),
+    let input = Val::Record(vec![
+        ("A".into(), Val::U32(32343)),
+        ("B".into(), Val::Enum("b".to_string())),
         (
-            "F",
-            f_type.unwrap_list().new_val(Box::new([f_element_type
-                .unwrap_flags()
-                .new_val(&["G", "I"])?]))?,
+            "C".into(),
+            Val::Record(vec![
+                ("D".to_string(), Val::Bool(false)),
+                ("E".to_string(), Val::U32(2084037802)),
+            ]),
         ),
         (
-            "J",
-            j_type
-                .unwrap_variant()
-                .new_val("L", Some(Val::Float64(3.14159265)))?,
+            "F".into(),
+            Val::List(vec![Val::Flags(vec!["G".to_string(), "I".to_string()])]),
         ),
-        ("P", Val::S8(42)),
-        ("Q", Val::S16(4242)),
-        ("R", Val::S32(42424242)),
-        ("S", Val::S64(424242424242424242)),
-        ("T", Val::Float32(3.14159265)),
-        ("U", Val::Float64(3.14159265)),
-        ("V", Val::String(Box::from("wow, nice types"))),
-        ("W", Val::Char('🦀')),
         (
-            "Y",
-            y_type
-                .unwrap_tuple()
-                .new_val(Box::new([Val::U32(42), Val::U32(24)]))?,
+            "J".into(),
+            Val::Variant("L".to_string(), Some(Box::new(Val::Float64(3.14159265)))),
         ),
+        ("P".into(), Val::S8(42)),
+        ("Q".into(), Val::S16(4242)),
+        ("R".into(), Val::S32(42424242)),
+        ("S".into(), Val::S64(424242424242424242)),
+        ("T".into(), Val::Float32(3.14159265)),
+        ("U".into(), Val::Float64(3.14159265)),
+        ("V".into(), Val::String("wow, nice types".to_string())),
+        ("W".into(), Val::Char('🦀')),
+        ("Y".into(), Val::Tuple(vec![Val::U32(42), Val::U32(24)])),
         (
-            "AA",
-            aa_type.unwrap_option().new_val(Some(Val::U32(314159265)))?,
+            "AA".into(),
+            Val::Option(Some(Box::new(Val::U32(314159265)))),
         ),
         (
-            "BB",
-            bb_type
-                .unwrap_result()
-                .new_val(Ok(Some(Val::String(Box::from("no problem")))))?,
+            "BB".into(),
+            Val::Result(Ok(Some(Box::new(Val::String("no problem".to_string()))))),
         ),
-    ])?;
+    ]);
     let mut output = [Val::Bool(false)];
     func.call_and_post_return(&mut store, &[input.clone()], &mut output)?;
 
diff --git a/tests/all/component_model/import.rs b/tests/all/component_model/import.rs
index 9f8ae09f0..3b453457f 100644
--- a/tests/all/component_model/import.rs
+++ b/tests/all/component_model/import.rs
@@ -706,7 +706,7 @@ fn stack_and_heap_args_and_rets() -> Result<()> {
         .root()
         .func_new(&component, "f2", |_, args, results| {
             if let Val::Tuple(tuple) = &args[0] {
-                if let Val::String(s) = &tuple.values()[0] {
+                if let Val::String(s) = &tuple[0] {
                     assert_eq!(s.deref(), "abc");
                     results[0] = Val::U32(3);
                     Ok(())
@@ -732,7 +732,7 @@ fn stack_and_heap_args_and_rets() -> Result<()> {
         .root()
         .func_new(&component, "f4", |_, args, results| {
             if let Val::Tuple(tuple) = &args[0] {
-                if let Val::String(s) = &tuple.values()[0] {
+                if let Val::String(s) = &tuple[0] {
                     assert_eq!(s.deref(), "abc");
                     results[0] = Val::String("xyz".into());
                     Ok(())
diff --git a/tests/all/component_model/resources.rs b/tests/all/component_model/resources.rs
index bd5fa9d20..343cd4c21 100644
--- a/tests/all/component_model/resources.rs
+++ b/tests/all/component_model/resources.rs
@@ -584,7 +584,7 @@ fn dynamic_val() -> Result<()> {
 
     let mut store = Store::new(&engine, ());
     let mut linker = Linker::new(&engine);
-    let idx = linker
+    linker
         .root()
         .resource("t1", ResourceType::host::<MyType>(), |_, _| Ok(()))?;
     let i_pre = linker.instantiate_pre(&c)?;
@@ -614,14 +614,14 @@ fn dynamic_val() -> Result<()> {
             assert_eq!(resource.rep(), 100);
             assert!(resource.owned());
 
-            let resource = resource.try_into_resource_any(&mut store, &i_pre, idx)?;
+            let resource = resource.try_into_resource_any(&mut store)?;
             assert_eq!(resource.ty(), ResourceType::host::<MyType>());
             assert!(resource.owned());
         }
         _ => unreachable!(),
     }
 
-    let t1_any = Resource::<MyType>::new_own(100).try_into_resource_any(&mut store, &i_pre, idx)?;
+    let t1_any = Resource::<MyType>::new_own(100).try_into_resource_any(&mut store)?;
     let mut results = [Val::Bool(false)];
     a.call(&mut store, &[Val::Resource(t1_any)], &mut results)?;
     a.post_return(&mut store)?;
@@ -634,7 +634,7 @@ fn dynamic_val() -> Result<()> {
             assert_eq!(resource.rep(), 100);
             assert!(resource.owned());
 
-            let resource = resource.try_into_resource_any(&mut store, &i_pre, idx)?;
+            let resource = resource.try_into_resource_any(&mut store)?;
             assert_eq!(resource.ty(), ResourceType::host::<MyType>());
             assert!(resource.owned());
         }
@@ -642,7 +642,7 @@ fn dynamic_val() -> Result<()> {
     }
 
     let t1 = Resource::<MyType>::new_own(100)
-        .try_into_resource_any(&mut store, &i_pre, idx)?
+        .try_into_resource_any(&mut store)?
         .try_into_resource(&mut store)?;
     let (t1,) = a_typed_result.call(&mut store, (t1,))?;
     a_typed_result.post_return(&mut store)?;
@@ -650,9 +650,9 @@ fn dynamic_val() -> Result<()> {
     assert!(t1.owned());
 
     let t1_any = t1
-        .try_into_resource_any(&mut store, &i_pre, idx)?
+        .try_into_resource_any(&mut store)?
         .try_into_resource::<MyType>(&mut store)?
-        .try_into_resource_any(&mut store, &i_pre, idx)?;
+        .try_into_resource_any(&mut store)?;
     let mut results = [Val::Bool(false)];
     a.call(&mut store, &[Val::Resource(t1_any)], &mut results)?;
     a.post_return(&mut store)?;
@@ -665,7 +665,7 @@ fn dynamic_val() -> Result<()> {
             assert_eq!(resource.rep(), 100);
             assert!(resource.owned());
 
-            let resource = resource.try_into_resource_any(&mut store, &i_pre, idx)?;
+            let resource = resource.try_into_resource_any(&mut store)?;
             assert_eq!(resource.ty(), ResourceType::host::<MyType>());
             assert!(resource.owned());
         }
@@ -915,7 +915,7 @@ fn can_use_own_for_borrow() -> Result<()> {
 
     let mut store = Store::new(&engine, ());
     let mut linker = Linker::new(&engine);
-    let ty_idx = linker
+    linker
         .root()
         .resource("t", ResourceType::host::<MyType>(), |_, _| Ok(()))?;
     let i_pre = linker.instantiate_pre(&c)?;
@@ -932,8 +932,7 @@ fn can_use_own_for_borrow() -> Result<()> {
     f_typed.call(&mut store, (&resource,))?;
     f_typed.post_return(&mut store)?;
 
-    let resource =
-        Resource::<MyType>::new_own(300).try_into_resource_any(&mut store, &i_pre, ty_idx)?;
+    let resource = Resource::<MyType>::new_own(300).try_into_resource_any(&mut store)?;
     f.call(&mut store, &[Val::Resource(resource)], &mut [])?;
     f.post_return(&mut store)?;
     resource.resource_drop(&mut store)?;
diff --git a/tests/all/host_funcs.rs b/tests/all/host_funcs.rs
index bf8f4b6de..9814b6140 100644
--- a/tests/all/host_funcs.rs
+++ b/tests/all/host_funcs.rs
@@ -1,7 +1,5 @@
 use anyhow::bail;
 use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
-use wasi_common::sync::WasiCtxBuilder;
-use wasi_common::I32Exit;
 use wasmtime::*;
 
 #[test]
@@ -717,7 +715,7 @@ fn store_with_context() -> Result<()> {
 fn wasi_imports() -> Result<()> {
     let engine = Engine::default();
     let mut linker = Linker::new(&engine);
-    wasi_common::sync::add_to_linker(&mut linker, |s| s)?;
+    wasmtime_wasi::preview1::add_to_linker_sync(&mut linker, |t| t)?;
 
     let wasm = wat::parse_str(
         r#"
@@ -730,14 +728,14 @@ fn wasi_imports() -> Result<()> {
     )?;
 
     let module = Module::new(&engine, wasm)?;
-    let mut store = Store::new(&engine, WasiCtxBuilder::new().build());
+    let mut store = Store::new(&engine, wasmtime_wasi::WasiCtxBuilder::new().build_p1());
     let instance = linker.instantiate(&mut store, &module)?;
 
     let start = instance.get_typed_func::<(), ()>(&mut store, "_start")?;
     let exit = start
         .call(&mut store, ())
         .unwrap_err()
-        .downcast::<I32Exit>()?;
+        .downcast::<wasmtime_wasi::I32Exit>()?;
     assert_eq!(exit.0, 123);
 
     Ok(())
diff --git a/tests/all/traps.rs b/tests/all/traps.rs
index affb2ac7c..6679704e0 100644
--- a/tests/all/traps.rs
+++ b/tests/all/traps.rs
@@ -751,13 +751,8 @@ fn parse_dwarf_info() -> Result<()> {
     let engine = Engine::new(&config)?;
     let module = Module::new(&engine, &wasm)?;
     let mut linker = Linker::new(&engine);
-    wasi_common::sync::add_to_linker(&mut linker, |s| s)?;
-    let mut store = Store::new(
-        &engine,
-        wasi_common::sync::WasiCtxBuilder::new()
-            .inherit_stdio()
-            .build(),
-    );
+    wasmtime_wasi::preview1::add_to_linker_sync(&mut linker, |t| t)?;
+    let mut store = Store::new(&engine, wasmtime_wasi::WasiCtxBuilder::new().build_p1());
     linker.module(&mut store, "", &module)?;
     let run = linker.get_default(&mut store, "")?;
     let trap = run.call(&mut store, &[], &mut []).unwrap_err();
diff --git a/tests/all/winch.rs b/tests/all/winch.rs
index addfc2783..664f845d1 100644
--- a/tests/all/winch.rs
+++ b/tests/all/winch.rs
@@ -340,3 +340,57 @@ fn static_oob() -> Result<()> {
 
     Ok(())
 }
+
+#[test]
+#[cfg_attr(miri, ignore)]
+#[cfg_attr(windows, ignore)]
+fn dynamic_heap_with_zero_max_size() -> Result<()> {
+    let mut c = Config::new();
+    c.static_memory_maximum_size(0);
+    c.strategy(Strategy::Winch);
+    let engine = Engine::new(&c)?;
+    let wat = r#"
+        (module
+          (type (;0;) (func (result i64)))
+          (type (;1;) (func (param f32)))
+          (func (;0;) (type 0) (result i64)
+            (local i32 i32 i32 i32 i32 i32 f32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32)
+            global.get 0
+            i32.eqz
+            if ;; label = @1
+              unreachable
+            end
+            global.get 0
+            i32.const 1
+            i32.sub
+            global.set 0
+            memory.size
+            f64.load offset=3598476644
+            loop (result i32) ;; label = @1
+              global.get 0
+              i32.eqz
+              if ;; label = @2
+                unreachable
+              end
+              global.get 0
+              i32.const 1
+              i32.sub
+              global.set 0
+              unreachable
+            end
+            unreachable
+          )
+          (memory (;0;) 0 8)
+          (global (;0;) (mut i32) i32.const 1000)
+          (export "" (func 0))
+        )
+    "#;
+    let mut store = Store::new(&engine, ());
+    let module = Module::new(&engine, wat)?;
+    let instance = Instance::new(&mut store, &module, &[])?;
+    let f = instance.get_typed_func::<(), i64>(&mut store, "")?;
+    let result = f.call(&mut store, ()).unwrap_err();
+    assert!(result.downcast_ref::<WasmBacktrace>().is_some());
+
+    Ok(())
+}