Repository: bytecodealliance/wasmtime
Pull request #8164 (2024-03-18 08:20:35)

Cranelift: clone ABIArg instead of allocating SmallInstVec
This allows us to make a single pass over an argument's slots instead of needing to first pre-allocate temporaries, because we don't need to hold a borrow of `ctx`. For the same reason, we can also emit the new instructions directly instead of buffering them and copying them at the end.

This approach also moves less data around. A SmallInstVec is a usize plus four `M::Inst`s, which are 32 or 40 bytes each, while an ABIArg is only 40 bytes. Since the SmallVecs inside ABIArg almost never spill to the heap, cloning one uses less memory than allocating temporary space for a few instructions.

I was inspired to write this after working on #8151 with @elliottt, and working on this also inspired #8163.


Patch:
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index eb48f0ad9..e606cf6bb 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -65,7 +65,11 @@ jobs:
     - uses: actions/checkout@v4
       with:
         submodules: true
-    - run: git ls-files '*.h' '*.c' '*.cpp' | grep -v wasmtime-platform.h | xargs clang-format-15 --dry-run --Werror --verbose
+    - run: |
+        git ls-files '*.h' '*.c' '*.cpp' | \
+          grep -v wasmtime-platform.h | \
+          grep -v wasm.h | \
+          xargs clang-format-15 --dry-run --Werror --verbose
 
     # common logic to cancel the entire run if this job fails
     - run: gh run cancel ${{ github.run_id }}
@@ -360,6 +364,11 @@ jobs:
       run: ./ci/vendor-wit.sh
     - run: git diff --exit-code
 
+    # Re-vendor the C API and make sure it's up-to-date.
+    - name: Re-vendor WIT
+      run: ./ci/vendor-c-api-headers.sh
+    - run: git diff --exit-code
+
     # common logic to cancel the entire run if this job fails
     - run: gh run cancel ${{ github.run_id }}
       if: failure() && github.event_name != 'pull_request'
diff --git a/.gitmodules b/.gitmodules
index bef1a49be..dc9dfa50f 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,9 +1,6 @@
 [submodule "tests/spec_testsuite"]
 	path = tests/spec_testsuite
 	url = https://github.com/WebAssembly/testsuite
-[submodule "crates/c-api/wasm-c-api"]
-	path = crates/c-api/wasm-c-api
-	url = https://github.com/WebAssembly/wasm-c-api
 [submodule "crates/wasi-nn/spec"]
 	path = crates/wasi-nn/spec
 	url = https://github.com/WebAssembly/wasi-nn
diff --git a/ci/build-tarballs.sh b/ci/build-tarballs.sh
index d8a249f11..114174bfe 100755
--- a/ci/build-tarballs.sh
+++ b/ci/build-tarballs.sh
@@ -41,7 +41,6 @@ mkdir tmp/$bin_pkgname
 cp LICENSE README.md tmp/$api_pkgname
 cp LICENSE README.md tmp/$bin_pkgname
 cp -r crates/c-api/include tmp/$api_pkgname
-cp crates/c-api/wasm-c-api/include/wasm.h tmp/$api_pkgname/include
 
 # For *-min builds rename artifacts with a `-min` suffix to avoid eventual
 # clashes with the normal builds when the tarballs are unioned together.
diff --git a/ci/vendor-c-api-headers.sh b/ci/vendor-c-api-headers.sh
new file mode 100755
index 000000000..fa0b41792
--- /dev/null
+++ b/ci/vendor-c-api-headers.sh
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+# Script to re-vendor the headers from the `wasm-c-api` proposal.
+
+rev="2ce1367c9d1271c83fb63bef26d896a2f290cd23"
+files="wasm.h wasm.hh"
+
+set -ex
+
+for file in $files; do
+  dst=crates/c-api/include/$file
+  pretty_url=https://github.com/WebAssembly/wasm-c-api/blob/$rev/include/$file
+  url=https://raw.githubusercontent.com/WebAssembly/wasm-c-api/$rev/include/$file
+  cat >$dst <<-EOF
+// Wasmtime-vendored copy of this header file as present in upstream:
+// <$pretty_url>
+//
+// Wasmtime maintainers can update this vendored copy in-tree with the
+// ./ci/vendor-c-api-headers.sh shell script.
+//
+EOF
+  curl $url >> $dst
+done
diff --git a/cranelift/codegen/src/alias_analysis.rs b/cranelift/codegen/src/alias_analysis.rs
index f3b6339f9..f33f6eebf 100644
--- a/cranelift/codegen/src/alias_analysis.rs
+++ b/cranelift/codegen/src/alias_analysis.rs
@@ -68,7 +68,7 @@ use crate::{
     inst_predicates::{
         has_memory_fence_semantics, inst_addr_offset_type, inst_store_data, visit_block_succs,
     },
-    ir::{immediates::Offset32, Block, Function, Inst, Opcode, Type, Value},
+    ir::{immediates::Offset32, AliasRegion, Block, Function, Inst, Opcode, Type, Value},
     trace,
 };
 use cranelift_entity::{packed_option::PackedOption, EntityRef};
@@ -93,14 +93,11 @@ impl LastStores {
             self.other = inst.into();
         } else if opcode.can_store() {
             if let Some(memflags) = func.dfg.insts[inst].memflags() {
-                if memflags.heap() {
-                    self.heap = inst.into();
-                } else if memflags.table() {
-                    self.table = inst.into();
-                } else if memflags.vmctx() {
-                    self.vmctx = inst.into();
-                } else {
-                    self.other = inst.into();
+                match memflags.alias_region() {
+                    None => self.other = inst.into(),
+                    Some(AliasRegion::Heap) => self.heap = inst.into(),
+                    Some(AliasRegion::Table) => self.table = inst.into(),
+                    Some(AliasRegion::Vmctx) => self.vmctx = inst.into(),
                 }
             } else {
                 self.heap = inst.into();
@@ -113,14 +110,11 @@ impl LastStores {
 
     fn get_last_store(&self, func: &Function, inst: Inst) -> PackedOption<Inst> {
         if let Some(memflags) = func.dfg.insts[inst].memflags() {
-            if memflags.heap() {
-                self.heap
-            } else if memflags.table() {
-                self.table
-            } else if memflags.vmctx() {
-                self.vmctx
-            } else {
-                self.other
+            match memflags.alias_region() {
+                None => self.other,
+                Some(AliasRegion::Heap) => self.heap,
+                Some(AliasRegion::Table) => self.table,
+                Some(AliasRegion::Vmctx) => self.vmctx,
             }
         } else if func.dfg.insts[inst].opcode().can_load()
             || func.dfg.insts[inst].opcode().can_store()
diff --git a/cranelift/codegen/src/ir/memflags.rs b/cranelift/codegen/src/ir/memflags.rs
index 8a5626d74..d9066f13a 100644
--- a/cranelift/codegen/src/ir/memflags.rs
+++ b/cranelift/codegen/src/ir/memflags.rs
@@ -2,57 +2,11 @@
 
 use super::TrapCode;
 use core::fmt;
+use core::str::FromStr;
 
 #[cfg(feature = "enable-serde")]
 use serde_derive::{Deserialize, Serialize};
 
-enum FlagBit {
-    /// Guaranteed not to trap. This may enable additional
-    /// optimizations to be performed.
-    Notrap,
-    /// If this memory access traps, use [TrapCode::TableOutOfBounds]
-    /// instead of [TrapCode::HeapOutOfBounds].
-    TableTrap,
-    /// Guaranteed to use "natural alignment" for the given type. This
-    /// may enable better instruction selection.
-    Aligned,
-    /// A load that reads data in memory that does not change for the
-    /// duration of the function's execution. This may enable
-    /// additional optimizations to be performed.
-    Readonly,
-    /// Load multi-byte values from memory in a little-endian format.
-    LittleEndian,
-    /// Load multi-byte values from memory in a big-endian format.
-    BigEndian,
-    /// Accesses only the "heap" part of abstract state. Used for
-    /// alias analysis. Mutually exclusive with "table" and "vmctx".
-    Heap,
-    /// Accesses only the "table" part of abstract state. Used for
-    /// alias analysis. Mutually exclusive with "heap" and "vmctx".
-    Table,
-    /// Accesses only the "vmctx" part of abstract state. Used for
-    /// alias analysis. Mutually exclusive with "heap" and "table".
-    Vmctx,
-    /// Check this load or store for safety when using the
-    /// proof-carrying-code framework. The address must have a
-    /// `PointsTo` fact attached with a sufficiently large valid range
-    /// for the accessed size.
-    Checked,
-}
-
-const NAMES: [&str; 10] = [
-    "notrap",
-    "tabletrap",
-    "aligned",
-    "readonly",
-    "little",
-    "big",
-    "heap",
-    "table",
-    "vmctx",
-    "checked",
-];
-
 /// Endianness of a memory access.
 #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
 pub enum Endianness {
@@ -62,6 +16,16 @@ pub enum Endianness {
     Big,
 }
 
+/// Which disjoint region of aliasing memory is accessed in this memory
+/// operation.
+#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
+#[allow(missing_docs)]
+pub enum AliasRegion {
+    Heap,
+    Table,
+    Vmctx,
+}
+
 /// Flags for memory operations like load/store.
 ///
 /// Each of these flags introduce a limited form of undefined behavior. The flags each enable
@@ -75,9 +39,56 @@ pub enum Endianness {
 #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
 #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
 pub struct MemFlags {
+    // Initialized to all zeros to have all flags have their default value.
+    // This is interpreted through various methods below. Currently the bits of
+    // this are defined as:
+    //
+    // * 0 - aligned flag
+    // * 1 - readonly flag
+    // * 2 - little endian flag
+    // * 3 - big endian flag
+    // * 4 - checked flag
+    // * 5/6 - alias region
+    // * 7/8/9/10 - trap code
+    // * 11/12/13/14/15 - unallocated
+    //
+    // Current properties upheld are:
+    //
+    // * only one of little/big endian is set
+    // * only one alias region can be set - once set it cannot be changed
     bits: u16,
 }
 
+/// Guaranteed to use "natural alignment" for the given type. This
+/// may enable better instruction selection.
+const BIT_ALIGNED: u16 = 1 << 0;
+
+/// A load that reads data in memory that does not change for the
+/// duration of the function's execution. This may enable
+/// additional optimizations to be performed.
+const BIT_READONLY: u16 = 1 << 1;
+
+/// Load multi-byte values from memory in a little-endian format.
+const BIT_LITTLE_ENDIAN: u16 = 1 << 2;
+
+/// Load multi-byte values from memory in a big-endian format.
+const BIT_BIG_ENDIAN: u16 = 1 << 3;
+
+/// Check this load or store for safety when using the
+/// proof-carrying-code framework. The address must have a
+/// `PointsTo` fact attached with a sufficiently large valid range
+/// for the accessed size.
+const BIT_CHECKED: u16 = 1 << 4;
+
+/// Used for alias analysis, indicates which disjoint part of the abstract state
+/// is being accessed.
+const MASK_ALIAS_REGION: u16 = 0b11 << ALIAS_REGION_OFFSET;
+const ALIAS_REGION_OFFSET: u16 = 5;
+
+/// Trap code, if any, for this memory operation.
+const MASK_TRAP_CODE: u16 = 0b1111 << TRAP_CODE_OFFSET;
+const TRAP_CODE_OFFSET: u16 = 7;
+
 impl MemFlags {
     /// Create a new empty set of flags.
     pub const fn new() -> Self {
@@ -91,35 +102,100 @@ impl MemFlags {
     }
 
     /// Read a flag bit.
-    const fn read(self, bit: FlagBit) -> bool {
-        self.bits & (1 << bit as usize) != 0
+    const fn read_bit(self, bit: u16) -> bool {
+        self.bits & bit != 0
     }
 
     /// Return a new `MemFlags` with this flag bit set.
-    const fn with(mut self, bit: FlagBit) -> Self {
-        self.bits |= 1 << bit as usize;
+    const fn with_bit(mut self, bit: u16) -> Self {
+        self.bits |= bit;
+        self
+    }
+
+    /// Reads the alias region that this memory operation works with.
+    pub const fn alias_region(self) -> Option<AliasRegion> {
+        // NB: keep in sync with `with_alias_region`
+        match (self.bits & MASK_ALIAS_REGION) >> ALIAS_REGION_OFFSET {
+            0b00 => None,
+            0b01 => Some(AliasRegion::Heap),
+            0b10 => Some(AliasRegion::Table),
+            0b11 => Some(AliasRegion::Vmctx),
+            _ => unreachable!(),
+        }
+    }
+
+    /// Sets the alias region that this works on to the specified `region`.
+    pub const fn with_alias_region(mut self, region: Option<AliasRegion>) -> Self {
+        // NB: keep in sync with `alias_region`
+        let bits = match region {
+            None => 0b00,
+            Some(AliasRegion::Heap) => 0b01,
+            Some(AliasRegion::Table) => 0b10,
+            Some(AliasRegion::Vmctx) => 0b11,
+        };
+        self.bits &= !MASK_ALIAS_REGION;
+        self.bits |= bits << ALIAS_REGION_OFFSET;
         self
     }
 
+    /// Sets the alias region that this works on to the specified `region`.
+    pub fn set_alias_region(&mut self, region: Option<AliasRegion>) {
+        *self = self.with_alias_region(region);
+    }
+
     /// Set a flag bit by name.
     ///
-    /// Returns true if the flag was found and set, false for an unknown flag name.
-    /// Will also return false when trying to set inconsistent endianness flags.
-    pub fn set_by_name(&mut self, name: &str) -> bool {
-        match NAMES.iter().position(|&s| s == name) {
-            Some(bit) => {
-                let bits = self.bits | 1 << bit;
-                if (bits & (1 << FlagBit::LittleEndian as usize)) != 0
-                    && (bits & (1 << FlagBit::BigEndian as usize)) != 0
-                {
-                    false
-                } else {
-                    self.bits = bits;
-                    true
+    /// Returns true if the flag was found and set, false for an unknown flag
+    /// name.
+    ///
+    /// # Errors
+    ///
+    /// Returns an error message if the `name` is known but couldn't be applied
+    /// due to it being a semantic error.
+    pub fn set_by_name(&mut self, name: &str) -> Result<bool, &'static str> {
+        *self = match name {
+            "notrap" => self.with_trap_code(None),
+            "aligned" => self.with_aligned(),
+            "readonly" => self.with_readonly(),
+            "little" => {
+                if self.read_bit(BIT_BIG_ENDIAN) {
+                    return Err("cannot set both big and little endian bits");
                 }
+                self.with_endianness(Endianness::Little)
             }
-            None => false,
-        }
+            "big" => {
+                if self.read_bit(BIT_LITTLE_ENDIAN) {
+                    return Err("cannot set both big and little endian bits");
+                }
+                self.with_endianness(Endianness::Big)
+            }
+            "heap" => {
+                if self.alias_region().is_some() {
+                    return Err("cannot set more than one alias region");
+                }
+                self.with_alias_region(Some(AliasRegion::Heap))
+            }
+            "table" => {
+                if self.alias_region().is_some() {
+                    return Err("cannot set more than one alias region");
+                }
+                self.with_alias_region(Some(AliasRegion::Table))
+            }
+            "vmctx" => {
+                if self.alias_region().is_some() {
+                    return Err("cannot set more than one alias region");
+                }
+                self.with_alias_region(Some(AliasRegion::Vmctx))
+            }
+            "checked" => self.with_checked(),
+
+            other => match TrapCode::from_str(other) {
+                Ok(TrapCode::User(_)) => return Err("cannot set user trap code on mem flags"),
+                Ok(code) => self.with_trap_code(Some(code)),
+                Err(()) => return Ok(false),
+            },
+        };
+        Ok(true)
     }
 
     /// Return endianness of the memory access.  This will return the endianness
@@ -128,9 +204,9 @@ impl MemFlags {
     /// caller since it is not explicitly encoded in CLIF IR -- this allows a
     /// front end to create IR without having to know the target endianness.
     pub const fn endianness(self, native_endianness: Endianness) -> Endianness {
-        if self.read(FlagBit::LittleEndian) {
+        if self.read_bit(BIT_LITTLE_ENDIAN) {
             Endianness::Little
-        } else if self.read(FlagBit::BigEndian) {
+        } else if self.read_bit(BIT_BIG_ENDIAN) {
             Endianness::Big
         } else {
             native_endianness
@@ -145,53 +221,36 @@ impl MemFlags {
     /// Set endianness of the memory access, returning new flags.
     pub const fn with_endianness(self, endianness: Endianness) -> Self {
         let res = match endianness {
-            Endianness::Little => self.with(FlagBit::LittleEndian),
-            Endianness::Big => self.with(FlagBit::BigEndian),
+            Endianness::Little => self.with_bit(BIT_LITTLE_ENDIAN),
+            Endianness::Big => self.with_bit(BIT_BIG_ENDIAN),
         };
-        assert!(!(res.read(FlagBit::LittleEndian) && res.read(FlagBit::BigEndian)));
+        assert!(!(res.read_bit(BIT_LITTLE_ENDIAN) && res.read_bit(BIT_BIG_ENDIAN)));
         res
     }
 
-    /// Test if the `notrap` flag is set.
+    /// Test if this memory operation cannot trap.
     ///
-    /// Normally, trapping is part of the semantics of a load/store operation. If the platform
-    /// would cause a trap when accessing the effective address, the Cranelift memory operation is
-    /// also required to trap.
+    /// By default `MemFlags` will assume that any load/store can trap and is
+    /// associated with a `TrapCode::HeapOutOfBounds` code. If the trap code is
+    /// configured to `None` though then this method will return `true` and
+    /// indicates that the memory operation will not trap.
     ///
-    /// The `notrap` flag tells Cranelift that the memory is *accessible*, which means that
-    /// accesses will not trap. This makes it possible to delete an unused load or a dead store
-    /// instruction.
+    /// If this returns `true` then the memory is *accessible*, which means
+    /// that accesses will not trap. This makes it possible to delete an unused
+    /// load or a dead store instruction.
     pub const fn notrap(self) -> bool {
-        self.read(FlagBit::Notrap)
+        self.trap_code().is_none()
     }
 
-    /// Set the `notrap` flag.
+    /// Sets the trap code for this `MemFlags` to `None`.
     pub fn set_notrap(&mut self) {
         *self = self.with_notrap();
     }
 
-    /// Set the `notrap` flag, returning new flags.
+    /// Sets the trap code for this `MemFlags` to `None`, returning the new
+    /// flags.
     pub const fn with_notrap(self) -> Self {
-        self.with(FlagBit::Notrap)
-    }
-
-    /// Test if the `tabletrap` bit is set.
-    ///
-    /// By default, if this memory access traps, the associated trap
-    /// code will be `HeapOutOfBounds`. With this flag set, the trap
-    /// code will instead be `TableOutOfBounds`.
-    pub const fn tabletrap(self) -> bool {
-        self.read(FlagBit::TableTrap)
-    }
-
-    /// Set the `tabletrap` bit.
-    pub fn set_tabletrap(&mut self) {
-        *self = self.with_tabletrap();
-    }
-
-    /// Set the `tabletrap` bit, returning new flags.
-    pub const fn with_tabletrap(self) -> Self {
-        self.with(FlagBit::TableTrap)
+        self.with_trap_code(None)
     }
 
     /// Test if the `aligned` flag is set.
@@ -200,7 +259,7 @@ impl MemFlags {
     /// `aligned` flag is set, the instruction is permitted to trap or return a wrong result if the
     /// effective address is misaligned.
     pub const fn aligned(self) -> bool {
-        self.read(FlagBit::Aligned)
+        self.read_bit(BIT_ALIGNED)
     }
 
     /// Set the `aligned` flag.
@@ -210,7 +269,7 @@ impl MemFlags {
 
     /// Set the `aligned` flag, returning new flags.
     pub const fn with_aligned(self) -> Self {
-        self.with(FlagBit::Aligned)
+        self.with_bit(BIT_ALIGNED)
     }
 
     /// Test if the `readonly` flag is set.
@@ -219,7 +278,7 @@ impl MemFlags {
     /// This results in undefined behavior if the dereferenced memory is mutated at any time
     /// between when the function is called and when it is exited.
     pub const fn readonly(self) -> bool {
-        self.read(FlagBit::Readonly)
+        self.read_bit(BIT_READONLY)
     }
 
     /// Set the `readonly` flag.
@@ -229,79 +288,7 @@ impl MemFlags {
 
     /// Set the `readonly` flag, returning new flags.
     pub const fn with_readonly(self) -> Self {
-        self.with(FlagBit::Readonly)
-    }
-
-    /// Test if the `heap` bit is set.
-    ///
-    /// Loads and stores with this flag accesses the "heap" part of
-    /// abstract state. This is disjoint from the "table", "vmctx",
-    /// and "other" parts of abstract state. In concrete terms, this
-    /// means that behavior is undefined if the same memory is also
-    /// accessed by another load/store with one of the other
-    /// alias-analysis bits (`table`, `vmctx`) set, or `heap` not set.
-    pub const fn heap(self) -> bool {
-        self.read(FlagBit::Heap)
-    }
-
-    /// Set the `heap` bit. See the notes about mutual exclusion with
-    /// other bits in `heap()`.
-    pub fn set_heap(&mut self) {
-        *self = self.with_heap();
-    }
-
-    /// Set the `heap` bit, returning new flags.
-    pub const fn with_heap(self) -> Self {
-        assert!(!self.table() && !self.vmctx());
-        self.with(FlagBit::Heap)
-    }
-
-    /// Test if the `table` bit is set.
-    ///
-    /// Loads and stores with this flag accesses the "table" part of
-    /// abstract state. This is disjoint from the "heap", "vmctx",
-    /// and "other" parts of abstract state. In concrete terms, this
-    /// means that behavior is undefined if the same memory is also
-    /// accessed by another load/store with one of the other
-    /// alias-analysis bits (`heap`, `vmctx`) set, or `table` not set.
-    pub const fn table(self) -> bool {
-        self.read(FlagBit::Table)
-    }
-
-    /// Set the `table` bit. See the notes about mutual exclusion with
-    /// other bits in `table()`.
-    pub fn set_table(&mut self) {
-        *self = self.with_table();
-    }
-
-    /// Set the `table` bit, returning new flags.
-    pub const fn with_table(self) -> Self {
-        assert!(!self.heap() && !self.vmctx());
-        self.with(FlagBit::Table)
-    }
-
-    /// Test if the `vmctx` bit is set.
-    ///
-    /// Loads and stores with this flag accesses the "vmctx" part of
-    /// abstract state. This is disjoint from the "heap", "table",
-    /// and "other" parts of abstract state. In concrete terms, this
-    /// means that behavior is undefined if the same memory is also
-    /// accessed by another load/store with one of the other
-    /// alias-analysis bits (`heap`, `table`) set, or `vmctx` not set.
-    pub const fn vmctx(self) -> bool {
-        self.read(FlagBit::Vmctx)
-    }
-
-    /// Set the `vmctx` bit. See the notes about mutual exclusion with
-    /// other bits in `vmctx()`.
-    pub fn set_vmctx(&mut self) {
-        *self = self.with_vmctx();
-    }
-
-    /// Set the `vmctx` bit, returning new flags.
-    pub const fn with_vmctx(self) -> Self {
-        assert!(!self.heap() && !self.table());
-        self.with(FlagBit::Vmctx)
+        self.with_bit(BIT_READONLY)
     }
 
     /// Test if the `checked` bit is set.
@@ -316,7 +303,7 @@ impl MemFlags {
     /// checker's correctness) to access valid memory. This can be
     /// used to ensure memory safety and sandboxing.
     pub const fn checked(self) -> bool {
-        self.read(FlagBit::Checked)
+        self.read_bit(BIT_CHECKED)
     }
 
     /// Set the `checked` bit.
@@ -326,32 +313,139 @@ impl MemFlags {
 
     /// Set the `checked` bit, returning new flags.
     pub const fn with_checked(self) -> Self {
-        self.with(FlagBit::Checked)
+        self.with_bit(BIT_CHECKED)
     }
 
-    /// Get the trap code to report if this memory access traps. If the
-    /// `notrap` flag is set, then return `None`. Otherwise, the choice
-    /// of trap code depends on the `tabletrap` flag. The default trap
-    /// code is `HeapOutOfBounds`, but with `tabletrap` set, the trap
-    /// code is instead `TableOutOfBounds`.
+    /// Get the trap code to report if this memory access traps.
+    ///
+    /// A `None` trap code indicates that this memory access does not trap.
     pub const fn trap_code(self) -> Option<TrapCode> {
-        if self.notrap() {
-            None
-        } else if self.tabletrap() {
-            Some(TrapCode::TableOutOfBounds)
-        } else {
-            Some(TrapCode::HeapOutOfBounds)
+        // NB: keep this encoding in sync with `with_trap_code` below.
+        //
+        // Also note that the default, all zeros, is `HeapOutOfBounds`. It is
+        // intentionally not `None` so memory operations are all considered
+        // effect-ful by default.
+        match (self.bits & MASK_TRAP_CODE) >> TRAP_CODE_OFFSET {
+            0b0000 => Some(TrapCode::HeapOutOfBounds),
+            0b0001 => Some(TrapCode::StackOverflow),
+            0b0010 => Some(TrapCode::HeapMisaligned),
+            0b0011 => Some(TrapCode::TableOutOfBounds),
+            0b0100 => Some(TrapCode::IndirectCallToNull),
+            0b0101 => Some(TrapCode::BadSignature),
+            0b0110 => Some(TrapCode::IntegerOverflow),
+            0b0111 => Some(TrapCode::IntegerDivisionByZero),
+            0b1000 => Some(TrapCode::BadConversionToInteger),
+            0b1001 => Some(TrapCode::UnreachableCodeReached),
+            0b1010 => Some(TrapCode::Interrupt),
+            0b1011 => Some(TrapCode::NullReference),
+            // 0b1100 => {} not allocated
+            // 0b1101 => {} not allocated
+            // 0b1110 => {} not allocated
+            0b1111 => None,
+            _ => unreachable!(),
         }
     }
+
+    /// Configures these flags with the specified trap code `code`.
+    ///
+    /// Note that `TrapCode::User(_)` cannot be set in `MemFlags`. A trap code
+    /// indicates that this memory operation cannot be optimized away and it
+    /// must "stay where it is" in the programs. Traps are considered side
+    /// effects, for example, and have meaning through the trap code that is
+    /// communicated and which instruction trapped.
+    pub const fn with_trap_code(mut self, code: Option<TrapCode>) -> Self {
+        let bits = match code {
+            Some(TrapCode::HeapOutOfBounds) => 0b0000,
+            Some(TrapCode::StackOverflow) => 0b0001,
+            Some(TrapCode::HeapMisaligned) => 0b0010,
+            Some(TrapCode::TableOutOfBounds) => 0b0011,
+            Some(TrapCode::IndirectCallToNull) => 0b0100,
+            Some(TrapCode::BadSignature) => 0b0101,
+            Some(TrapCode::IntegerOverflow) => 0b0110,
+            Some(TrapCode::IntegerDivisionByZero) => 0b0111,
+            Some(TrapCode::BadConversionToInteger) => 0b1000,
+            Some(TrapCode::UnreachableCodeReached) => 0b1001,
+            Some(TrapCode::Interrupt) => 0b1010,
+            Some(TrapCode::NullReference) => 0b1011,
+            None => 0b1111,
+
+            Some(TrapCode::User(_)) => panic!("cannot set user trap code in mem flags"),
+        };
+        self.bits &= !MASK_TRAP_CODE;
+        self.bits |= bits << TRAP_CODE_OFFSET;
+        self
+    }
 }
 
 impl fmt::Display for MemFlags {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        for (i, n) in NAMES.iter().enumerate() {
-            if self.bits & (1 << i) != 0 {
-                write!(f, " {}", n)?;
-            }
+        match self.trap_code() {
+            None => write!(f, " notrap")?,
+            // This is the default trap code, so don't print anything extra
+            // for this.
+            Some(TrapCode::HeapOutOfBounds) => {}
+            Some(t) => write!(f, " {t}")?,
+        }
+        if self.aligned() {
+            write!(f, " aligned")?;
+        }
+        if self.readonly() {
+            write!(f, " readonly")?;
+        }
+        if self.read_bit(BIT_BIG_ENDIAN) {
+            write!(f, " big")?;
+        }
+        if self.read_bit(BIT_LITTLE_ENDIAN) {
+            write!(f, " little")?;
+        }
+        if self.checked() {
+            write!(f, " checked")?;
+        }
+        match self.alias_region() {
+            None => {}
+            Some(AliasRegion::Heap) => write!(f, " heap")?,
+            Some(AliasRegion::Table) => write!(f, " table")?,
+            Some(AliasRegion::Vmctx) => write!(f, " vmctx")?,
         }
         Ok(())
     }
 }
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn roundtrip_traps() {
+        for trap in TrapCode::non_user_traps().iter().copied() {
+            let flags = MemFlags::new().with_trap_code(Some(trap));
+            assert_eq!(flags.trap_code(), Some(trap));
+        }
+        let flags = MemFlags::new().with_trap_code(None);
+        assert_eq!(flags.trap_code(), None);
+    }
+
+    #[test]
+    fn cannot_set_big_and_little() {
+        let mut big = MemFlags::new().with_endianness(Endianness::Big);
+        assert!(big.set_by_name("little").is_err());
+
+        let mut little = MemFlags::new().with_endianness(Endianness::Little);
+        assert!(little.set_by_name("big").is_err());
+    }
+
+    #[test]
+    fn only_one_region() {
+        let mut big = MemFlags::new().with_alias_region(Some(AliasRegion::Heap));
+        assert!(big.set_by_name("table").is_err());
+        assert!(big.set_by_name("vmctx").is_err());
+
+        let mut big = MemFlags::new().with_alias_region(Some(AliasRegion::Table));
+        assert!(big.set_by_name("heap").is_err());
+        assert!(big.set_by_name("vmctx").is_err());
+
+        let mut big = MemFlags::new().with_alias_region(Some(AliasRegion::Vmctx));
+        assert!(big.set_by_name("heap").is_err());
+        assert!(big.set_by_name("table").is_err());
+    }
+}
diff --git a/cranelift/codegen/src/ir/mod.rs b/cranelift/codegen/src/ir/mod.rs
index aff5078a7..445496e5e 100644
--- a/cranelift/codegen/src/ir/mod.rs
+++ b/cranelift/codegen/src/ir/mod.rs
@@ -53,7 +53,7 @@ pub use crate::ir::jumptable::JumpTableData;
 pub use crate::ir::known_symbol::KnownSymbol;
 pub use crate::ir::layout::Layout;
 pub use crate::ir::libcall::{get_probestack_funcref, LibCall};
-pub use crate::ir::memflags::{Endianness, MemFlags};
+pub use crate::ir::memflags::{AliasRegion, Endianness, MemFlags};
 pub use crate::ir::memtype::{MemoryTypeData, MemoryTypeField};
 pub use crate::ir::pcc::{BaseExpr, Expr, Fact, FactContext, PccError, PccResult};
 pub use crate::ir::progpoint::ProgramPoint;
diff --git a/cranelift/codegen/src/machinst/abi.rs b/cranelift/codegen/src/machinst/abi.rs
index 4700bbaa2..8e16fdae8 100644
--- a/cranelift/codegen/src/machinst/abi.rs
+++ b/cranelift/codegen/src/machinst/abi.rs
@@ -2328,42 +2328,12 @@ impl<M: ABIMachineSpec> CallSite<M> {
         idx: usize,
         from_regs: ValueRegs<Reg>,
     ) -> SmallVec<[(VReg, ArgLoc); 2]> {
-        let mut insts = SmallInstVec::new();
         let mut locs = smallvec![];
         let word_rc = M::word_reg_class();
         let word_bits = M::word_bits() as usize;
 
-        // How many temps do we need for extends? Allocate them ahead
-        // of time, since we can't do it while we're iterating over
-        // the sig and immutably borrowing `ctx`.
-        let needed_tmps = match &ctx.sigs().args(self.sig)[idx] {
-            &ABIArg::Slots { ref slots, .. } => slots
-                .iter()
-                .map(|slot| match slot {
-                    &ABIArgSlot::Reg { extension, .. }
-                        if extension != ir::ArgumentExtension::None =>
-                    {
-                        1
-                    }
-                    &ABIArgSlot::Reg { ty, .. } if ty.is_ref() => 1,
-                    &ABIArgSlot::Reg { .. } => 0,
-                    &ABIArgSlot::Stack { extension, .. }
-                        if extension != ir::ArgumentExtension::None =>
-                    {
-                        1
-                    }
-                    &ABIArgSlot::Stack { .. } => 0,
-                })
-                .sum(),
-            ABIArg::ImplicitPtrArg { .. } => 1,
-            _ => 0,
-        };
-        let mut temps: SmallVec<[Writable<Reg>; 16]> = (0..needed_tmps)
-            .map(|_| ctx.alloc_tmp(M::word_type()).only_reg().unwrap())
-            .collect();
-
-        match &ctx.sigs().args(self.sig)[idx] {
-            &ABIArg::Slots { ref slots, .. } => {
+        match ctx.sigs().args(self.sig)[idx].clone() {
+            ABIArg::Slots { ref slots, .. } => {
                 assert_eq!(from_regs.len(), slots.len());
                 for (slot, from_reg) in slots.iter().zip(from_regs.regs().iter()) {
                     match slot {
@@ -2379,8 +2349,8 @@ impl<M: ABIMachineSpec> CallSite<M> {
                                     _ => unreachable!(),
                                 };
                                 let extend_result =
-                                    temps.pop().expect("Must have allocated enough temps");
-                                insts.push(M::gen_extend(
+                                    ctx.alloc_tmp(M::word_type()).only_reg().unwrap();
+                                ctx.emit(M::gen_extend(
                                     extend_result,
                                     *from_reg,
                                     signed,
@@ -2393,9 +2363,8 @@ impl<M: ABIMachineSpec> CallSite<M> {
                                 // passed as a copy; the original vreg
                                 // is constrained to the stack and
                                 // this copy is in a reg.
-                                let ref_copy =
-                                    temps.pop().expect("Must have allocated enough temps");
-                                insts.push(M::gen_move(ref_copy, *from_reg, M::word_type()));
+                                let ref_copy = ctx.alloc_tmp(M::word_type()).only_reg().unwrap();
+                                ctx.emit(M::gen_move(ref_copy, *from_reg, M::word_type()));
 
                                 locs.push((ref_copy.to_reg().into(), ArgLoc::Reg(reg.into())));
                             } else {
@@ -2418,8 +2387,8 @@ impl<M: ABIMachineSpec> CallSite<M> {
                                         _ => unreachable!(),
                                     };
                                     let extend_result =
-                                        temps.pop().expect("Must have allocated enough temps");
-                                    insts.push(M::gen_extend(
+                                        ctx.alloc_tmp(M::word_type()).only_reg().unwrap();
+                                    ctx.emit(M::gen_extend(
                                         extend_result,
                                         *from_reg,
                                         signed,
@@ -2439,10 +2408,10 @@ impl<M: ABIMachineSpec> CallSite<M> {
                     }
                 }
             }
-            &ABIArg::StructArg { pointer, .. } => {
+            ABIArg::StructArg { pointer, .. } => {
                 assert!(pointer.is_none()); // Only supported via ISLE.
             }
-            &ABIArg::ImplicitPtrArg {
+            ABIArg::ImplicitPtrArg {
                 offset,
                 pointer,
                 ty,
@@ -2451,10 +2420,10 @@ impl<M: ABIMachineSpec> CallSite<M> {
                 assert_eq!(from_regs.len(), 1);
                 let vreg = from_regs.regs()[0];
                 let amode = StackAMode::SPOffset(offset, ty);
-                let tmp = temps[0];
-                insts.push(M::gen_get_stack_addr(amode, tmp, ty));
+                let tmp = ctx.alloc_tmp(M::word_type()).only_reg().unwrap();
+                ctx.emit(M::gen_get_stack_addr(amode, tmp, ty));
                 let tmp = tmp.to_reg();
-                insts.push(M::gen_store_base_offset(tmp, 0, vreg, ty));
+                ctx.emit(M::gen_store_base_offset(tmp, 0, vreg, ty));
                 let loc = match pointer {
                     ABIArgSlot::Reg { reg, .. } => ArgLoc::Reg(reg.into()),
                     ABIArgSlot::Stack { offset, .. } => {
@@ -2466,10 +2435,6 @@ impl<M: ABIMachineSpec> CallSite<M> {
             }
         }
 
-        for inst in insts {
-            ctx.emit(inst);
-        }
-
         locs
     }
 
diff --git a/cranelift/fuzzgen/src/function_generator.rs b/cranelift/fuzzgen/src/function_generator.rs
index 6dc17eac1..4e2506bdb 100644
--- a/cranelift/fuzzgen/src/function_generator.rs
+++ b/cranelift/fuzzgen/src/function_generator.rs
@@ -9,8 +9,8 @@ use cranelift::codegen::ir::instructions::{InstructionFormat, ResolvedConstraint
 use cranelift::codegen::ir::stackslot::StackSize;
 
 use cranelift::codegen::ir::{
-    types::*, AtomicRmwOp, Block, ConstantData, Endianness, ExternalName, FuncRef, Function,
-    LibCall, Opcode, SigRef, Signature, StackSlot, UserExternalName, UserFuncName, Value,
+    types::*, AliasRegion, AtomicRmwOp, Block, ConstantData, Endianness, ExternalName, FuncRef,
+    Function, LibCall, Opcode, SigRef, Signature, StackSlot, UserExternalName, UserFuncName, Value,
 };
 use cranelift::codegen::isa::CallConv;
 use cranelift::frontend::{FunctionBuilder, FunctionBuilderContext, Switch, Variable};
@@ -1176,12 +1176,12 @@ impl AACategory {
     }
 
     pub fn update_memflags(&self, flags: &mut MemFlags) {
-        match self {
-            AACategory::Other => {}
-            AACategory::Heap => flags.set_heap(),
-            AACategory::Table => flags.set_table(),
-            AACategory::VmCtx => flags.set_vmctx(),
-        }
+        flags.set_alias_region(match self {
+            AACategory::Other => None,
+            AACategory::Heap => Some(AliasRegion::Heap),
+            AACategory::Table => Some(AliasRegion::Table),
+            AACategory::VmCtx => Some(AliasRegion::Vmctx),
+        })
     }
 }
 
diff --git a/cranelift/reader/src/parser.rs b/cranelift/reader/src/parser.rs
index 03c29fa4a..0dc9289ef 100644
--- a/cranelift/reader/src/parser.rs
+++ b/cranelift/reader/src/parser.rs
@@ -887,16 +887,18 @@ impl<'a> Parser<'a> {
     }
 
     // Match and a consume a possibly empty sequence of memory operation flags.
-    fn optional_memflags(&mut self) -> MemFlags {
+    fn optional_memflags(&mut self) -> ParseResult<MemFlags> {
         let mut flags = MemFlags::new();
         while let Some(Token::Identifier(text)) = self.token() {
-            if flags.set_by_name(text) {
-                self.consume();
-            } else {
-                break;
+            match flags.set_by_name(text) {
+                Ok(true) => {
+                    self.consume();
+                }
+                Ok(false) => break,
+                Err(msg) => return err!(self.loc, msg),
             }
         }
-        flags
+        Ok(flags)
     }
 
     // Match and consume an identifier.
@@ -1570,7 +1572,7 @@ impl<'a> Parser<'a> {
                     "expected '.' followed by type in load global value decl",
                 )?;
                 let global_type = self.match_type("expected load type")?;
-                let flags = self.optional_memflags();
+                let flags = self.optional_memflags()?;
                 let base = self.match_gv("expected global value: gv«n»")?;
                 let offset = self.optional_offset32()?;
 
@@ -3036,7 +3038,7 @@ impl<'a> Parser<'a> {
                 }
             }
             InstructionFormat::Load => {
-                let flags = self.optional_memflags();
+                let flags = self.optional_memflags()?;
                 let addr = self.match_value("expected SSA value address")?;
                 let offset = self.optional_offset32()?;
                 InstructionData::Load {
@@ -3047,7 +3049,7 @@ impl<'a> Parser<'a> {
                 }
             }
             InstructionFormat::Store => {
-                let flags = self.optional_memflags();
+                let flags = self.optional_memflags()?;
                 let arg = self.match_value("expected SSA value operand")?;
                 self.match_token(Token::Comma, "expected ',' between operands")?;
                 let addr = self.match_value("expected SSA value address")?;
@@ -3070,7 +3072,7 @@ impl<'a> Parser<'a> {
                 InstructionData::CondTrap { opcode, arg, code }
             }
             InstructionFormat::AtomicCas => {
-                let flags = self.optional_memflags();
+                let flags = self.optional_memflags()?;
                 let addr = self.match_value("expected SSA value address")?;
                 self.match_token(Token::Comma, "expected ',' between operands")?;
                 let expected = self.match_value("expected SSA value address")?;
@@ -3083,7 +3085,7 @@ impl<'a> Parser<'a> {
                 }
             }
             InstructionFormat::AtomicRmw => {
-                let flags = self.optional_memflags();
+                let flags = self.optional_memflags()?;
                 let op = self.match_enum("expected AtomicRmwOp")?;
                 let addr = self.match_value("expected SSA value address")?;
                 self.match_token(Token::Comma, "expected ',' between operands")?;
@@ -3096,7 +3098,7 @@ impl<'a> Parser<'a> {
                 }
             }
             InstructionFormat::LoadNoOffset => {
-                let flags = self.optional_memflags();
+                let flags = self.optional_memflags()?;
                 let addr = self.match_value("expected SSA value address")?;
                 InstructionData::LoadNoOffset {
                     opcode,
@@ -3105,7 +3107,7 @@ impl<'a> Parser<'a> {
                 }
             }
             InstructionFormat::StoreNoOffset => {
-                let flags = self.optional_memflags();
+                let flags = self.optional_memflags()?;
                 let arg = self.match_value("expected SSA value operand")?;
                 self.match_token(Token::Comma, "expected ',' between operands")?;
                 let addr = self.match_value("expected SSA value address")?;
diff --git a/cranelift/wasm/src/code_translator.rs b/cranelift/wasm/src/code_translator.rs
index f4a284d5e..bb3d6c21f 100644
--- a/cranelift/wasm/src/code_translator.rs
+++ b/cranelift/wasm/src/code_translator.rs
@@ -177,7 +177,7 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
                     let addr = builder.ins().global_value(environ.pointer_type(), gv);
                     let mut flags = ir::MemFlags::trusted();
                     // Put globals in the "table" abstract heap category as well.
-                    flags.set_table();
+                    flags.set_alias_region(Some(ir::AliasRegion::Table));
                     builder.ins().load(ty, flags, addr, offset)
                 }
                 GlobalVariable::Custom => environ.translate_custom_global_get(
@@ -194,7 +194,7 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
                     let addr = builder.ins().global_value(environ.pointer_type(), gv);
                     let mut flags = ir::MemFlags::trusted();
                     // Put globals in the "table" abstract heap category as well.
-                    flags.set_table();
+                    flags.set_alias_region(Some(ir::AliasRegion::Table));
                     let mut val = state.pop1();
                     // Ensure SIMD values are cast to their default Cranelift type, I8x16.
                     if ty.is_vector() {
@@ -2837,7 +2837,7 @@ where
     // state. This may allow alias analysis to merge redundant loads,
     // etc. when heap accesses occur interleaved with other (table,
     // vmctx, stack) accesses.
-    flags.set_heap();
+    flags.set_alias_region(Some(ir::AliasRegion::Heap));
 
     Ok(Reachability::Reachable((flags, index, addr)))
 }
diff --git a/cranelift/wasm/src/environ/dummy.rs b/cranelift/wasm/src/environ/dummy.rs
index a42a40384..dac12cb1b 100644
--- a/cranelift/wasm/src/environ/dummy.rs
+++ b/cranelift/wasm/src/environ/dummy.rs
@@ -529,7 +529,7 @@ impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environ
         self.ensure_table_exists(builder.func, table_index);
         let table = self.tables[table_index].as_ref().unwrap();
         let table_entry_addr = table.prepare_table_addr(builder, index, pointer_type, true);
-        let flags = ir::MemFlags::trusted().with_table();
+        let flags = ir::MemFlags::trusted().with_alias_region(Some(ir::AliasRegion::Table));
         let value = builder
             .ins()
             .load(self.reference_type(), flags, table_entry_addr, 0);
@@ -547,7 +547,7 @@ impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environ
         self.ensure_table_exists(builder.func, table_index);
         let table = self.tables[table_index].as_ref().unwrap();
         let table_entry_addr = table.prepare_table_addr(builder, index, pointer_type, true);
-        let flags = ir::MemFlags::trusted().with_table();
+        let flags = ir::MemFlags::trusted().with_alias_region(Some(ir::AliasRegion::Table));
         builder.ins().store(flags, value, table_entry_addr, 0);
         Ok(())
     }
diff --git a/crates/c-api/CMakeLists.txt b/crates/c-api/CMakeLists.txt
index ff92a091e..2075dd2bd 100644
--- a/crates/c-api/CMakeLists.txt
+++ b/crates/c-api/CMakeLists.txt
@@ -107,16 +107,14 @@ else()
 	endif()
 endif()
 
-target_include_directories(wasmtime INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include
-	${CMAKE_CURRENT_SOURCE_DIR}/wasm-c-api/include)
+target_include_directories(wasmtime INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include)
 
 include(GNUInstallDirs)
 install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/include/wasmtime.h
 	${CMAKE_CURRENT_SOURCE_DIR}/include/wasi.h
 	${CMAKE_CURRENT_SOURCE_DIR}/include/doc-wasm.h
-	DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
-install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/wasm-c-api/include/wasm.h
-	${CMAKE_CURRENT_SOURCE_DIR}/wasm-c-api/include/wasm.hh
+	${CMAKE_CURRENT_SOURCE_DIR}/include/wasm.h
+	${CMAKE_CURRENT_SOURCE_DIR}/include/wasm.hh
 	DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
 install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/wasmtime
 	DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
diff --git a/crates/c-api/Cargo.toml b/crates/c-api/Cargo.toml
index 2e6942f45..c54b64d89 100644
--- a/crates/c-api/Cargo.toml
+++ b/crates/c-api/Cargo.toml
@@ -8,7 +8,7 @@ repository = "https://github.com/bytecodealliance/wasmtime"
 readme = "README.md"
 edition.workspace = true
 links = "wasmtime-c-api"
-include = ["include", "src", "wasm-c-api/include", "build.rs"]
+include = ["include", "src", "build.rs"]
 
 [lints]
 workspace = true
diff --git a/crates/c-api/build.rs b/crates/c-api/build.rs
index 7f3f03bd9..7c9151fbe 100644
--- a/crates/c-api/build.rs
+++ b/crates/c-api/build.rs
@@ -1,5 +1,4 @@
 fn main() {
     let dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
     println!("cargo:include={dir}/include");
-    println!("cargo:wasm_include={dir}/wasm-c-api/include");
 }
diff --git a/crates/c-api/doxygen.conf b/crates/c-api/doxygen.conf
index 4e43ca407..6c8147e11 100644
--- a/crates/c-api/doxygen.conf
+++ b/crates/c-api/doxygen.conf
@@ -864,7 +864,7 @@ WARN_LOGFILE           =
 # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
 # Note: If this tag is empty the current directory is searched.
 
-INPUT                  = include wasm-c-api/include
+INPUT                  = include
 
 # This tag can be used to specify the character encoding of the source files
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
@@ -2162,8 +2162,7 @@ SEARCH_INCLUDES        = YES
 # preprocessor.
 # This tag requires that the tag SEARCH_INCLUDES is set to YES.
 
-INCLUDE_PATH           = wasm-c-api/include \
-                         include
+INCLUDE_PATH           = include
 
 # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
 # patterns (like *.h and *.hpp) to filter out the header-files in the
diff --git a/crates/c-api/include/wasm.h b/crates/c-api/include/wasm.h
new file mode 100644
index 000000000..c8b3cb7d3
--- /dev/null
+++ b/crates/c-api/include/wasm.h
@@ -0,0 +1,729 @@
+// Wasmtime-vendored copy of this header file as present in upstream:
+// <https://github.com/WebAssembly/wasm-c-api/blob/2ce1367c9d1271c83fb63bef26d896a2f290cd23/include/wasm.h>
+//
+// Wasmtime maintainers can update this vendored copy in-tree with the
+// ./ci/vendor-c-api-headers.sh shell script.
+//
+// WebAssembly C API
+
+#ifndef WASM_H
+#define WASM_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <assert.h>
+
+#ifndef WASM_API_EXTERN
+#if defined(_WIN32) && !defined(__MINGW32__) && !defined(LIBWASM_STATIC)
+#define WASM_API_EXTERN __declspec(dllimport)
+#else
+#define WASM_API_EXTERN
+#endif
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// Auxiliaries
+
+// Machine types
+
+inline void assertions(void) {
+  static_assert(sizeof(float) == sizeof(uint32_t), "incompatible float type");
+  static_assert(sizeof(double) == sizeof(uint64_t), "incompatible double type");
+  static_assert(sizeof(intptr_t) == sizeof(uint32_t) ||
+                sizeof(intptr_t) == sizeof(uint64_t),
+                "incompatible pointer type");
+}
+
+typedef char byte_t;
+typedef float float32_t;
+typedef double float64_t;
+
+
+// Ownership
+
+#define own
+
+// The qualifier `own` is used to indicate ownership of data in this API.
+// It is intended to be interpreted similar to a `const` qualifier:
+//
+// - `own wasm_xxx_t*` owns the pointed-to data
+// - `own wasm_xxx_t` distributes to all fields of a struct or union `xxx`
+// - `own wasm_xxx_vec_t` owns the vector as well as its elements(!)
+// - an `own` function parameter passes ownership from caller to callee
+// - an `own` function result passes ownership from callee to caller
+// - an exception are `own` pointer parameters named `out`, which are copy-back
+//   output parameters passing back ownership from callee to caller
+//
+// Own data is created by `wasm_xxx_new` functions and some others.
+// It must be released with the corresponding `wasm_xxx_delete` function.
+//
+// Deleting a reference does not necessarily delete the underlying object,
+// it merely indicates that this owner no longer uses it.
+//
+// For vectors, `const wasm_xxx_vec_t` is used informally to indicate that
+// neither the vector nor its elements should be modified.
+// TODO: introduce proper `wasm_xxx_const_vec_t`?
+
+
+#define WASM_DECLARE_OWN(name) \
+  typedef struct wasm_##name##_t wasm_##name##_t; \
+  \
+  WASM_API_EXTERN void wasm_##name##_delete(own wasm_##name##_t*);
+
+
+// Vectors
+
+#define WASM_DECLARE_VEC(name, ptr_or_none) \
+  typedef struct wasm_##name##_vec_t { \
+    size_t size; \
+    wasm_##name##_t ptr_or_none* data; \
+  } wasm_##name##_vec_t; \
+  \
+  WASM_API_EXTERN void wasm_##name##_vec_new_empty(own wasm_##name##_vec_t* out); \
+  WASM_API_EXTERN void wasm_##name##_vec_new_uninitialized( \
+    own wasm_##name##_vec_t* out, size_t); \
+  WASM_API_EXTERN void wasm_##name##_vec_new( \
+    own wasm_##name##_vec_t* out, \
+    size_t, own wasm_##name##_t ptr_or_none const[]); \
+  WASM_API_EXTERN void wasm_##name##_vec_copy( \
+    own wasm_##name##_vec_t* out, const wasm_##name##_vec_t*); \
+  WASM_API_EXTERN void wasm_##name##_vec_delete(own wasm_##name##_vec_t*);
+
+
+// Byte vectors
+
+typedef byte_t wasm_byte_t;
+WASM_DECLARE_VEC(byte, )
+
+typedef wasm_byte_vec_t wasm_name_t;
+
+#define wasm_name wasm_byte_vec
+#define wasm_name_new wasm_byte_vec_new
+#define wasm_name_new_empty wasm_byte_vec_new_empty
+#define wasm_name_new_new_uninitialized wasm_byte_vec_new_uninitialized
+#define wasm_name_copy wasm_byte_vec_copy
+#define wasm_name_delete wasm_byte_vec_delete
+
+static inline void wasm_name_new_from_string(
+  own wasm_name_t* out, const char* s
+) {
+  wasm_name_new(out, strlen(s), s);
+}
+
+static inline void wasm_name_new_from_string_nt(
+  own wasm_name_t* out, const char* s
+) {
+  wasm_name_new(out, strlen(s) + 1, s);
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Runtime Environment
+
+// Configuration
+
+WASM_DECLARE_OWN(config)
+
+WASM_API_EXTERN own wasm_config_t* wasm_config_new(void);
+
+// Embedders may provide custom functions for manipulating configs.
+
+
+// Engine
+
+WASM_DECLARE_OWN(engine)
+
+WASM_API_EXTERN own wasm_engine_t* wasm_engine_new(void);
+WASM_API_EXTERN own wasm_engine_t* wasm_engine_new_with_config(own wasm_config_t*);
+
+
+// Store
+
+WASM_DECLARE_OWN(store)
+
+WASM_API_EXTERN own wasm_store_t* wasm_store_new(wasm_engine_t*);
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Type Representations
+
+// Type attributes
+
+typedef uint8_t wasm_mutability_t;
+enum wasm_mutability_enum {
+  WASM_CONST,
+  WASM_VAR,
+};
+
+typedef struct wasm_limits_t {
+  uint32_t min;
+  uint32_t max;
+} wasm_limits_t;
+
+static const uint32_t wasm_limits_max_default = 0xffffffff;
+
+
+// Generic
+
+#define WASM_DECLARE_TYPE(name) \
+  WASM_DECLARE_OWN(name) \
+  WASM_DECLARE_VEC(name, *) \
+  \
+  WASM_API_EXTERN own wasm_##name##_t* wasm_##name##_copy(const wasm_##name##_t*);
+
+
+// Value Types
+
+WASM_DECLARE_TYPE(valtype)
+
+typedef uint8_t wasm_valkind_t;
+enum wasm_valkind_enum {
+  WASM_I32,
+  WASM_I64,
+  WASM_F32,
+  WASM_F64,
+  WASM_EXTERNREF = 128,
+  WASM_FUNCREF,
+};
+
+WASM_API_EXTERN own wasm_valtype_t* wasm_valtype_new(wasm_valkind_t);
+
+WASM_API_EXTERN wasm_valkind_t wasm_valtype_kind(const wasm_valtype_t*);
+
+static inline bool wasm_valkind_is_num(wasm_valkind_t k) {
+  return k < WASM_EXTERNREF;
+}
+static inline bool wasm_valkind_is_ref(wasm_valkind_t k) {
+  return k >= WASM_EXTERNREF;
+}
+
+static inline bool wasm_valtype_is_num(const wasm_valtype_t* t) {
+  return wasm_valkind_is_num(wasm_valtype_kind(t));
+}
+static inline bool wasm_valtype_is_ref(const wasm_valtype_t* t) {
+  return wasm_valkind_is_ref(wasm_valtype_kind(t));
+}
+
+
+// Function Types
+
+WASM_DECLARE_TYPE(functype)
+
+WASM_API_EXTERN own wasm_functype_t* wasm_functype_new(
+  own wasm_valtype_vec_t* params, own wasm_valtype_vec_t* results);
+
+WASM_API_EXTERN const wasm_valtype_vec_t* wasm_functype_params(const wasm_functype_t*);
+WASM_API_EXTERN const wasm_valtype_vec_t* wasm_functype_results(const wasm_functype_t*);
+
+
+// Global Types
+
+WASM_DECLARE_TYPE(globaltype)
+
+WASM_API_EXTERN own wasm_globaltype_t* wasm_globaltype_new(
+  own wasm_valtype_t*, wasm_mutability_t);
+
+WASM_API_EXTERN const wasm_valtype_t* wasm_globaltype_content(const wasm_globaltype_t*);
+WASM_API_EXTERN wasm_mutability_t wasm_globaltype_mutability(const wasm_globaltype_t*);
+
+
+// Table Types
+
+WASM_DECLARE_TYPE(tabletype)
+
+WASM_API_EXTERN own wasm_tabletype_t* wasm_tabletype_new(
+  own wasm_valtype_t*, const wasm_limits_t*);
+
+WASM_API_EXTERN const wasm_valtype_t* wasm_tabletype_element(const wasm_tabletype_t*);
+WASM_API_EXTERN const wasm_limits_t* wasm_tabletype_limits(const wasm_tabletype_t*);
+
+
+// Memory Types
+
+WASM_DECLARE_TYPE(memorytype)
+
+WASM_API_EXTERN own wasm_memorytype_t* wasm_memorytype_new(const wasm_limits_t*);
+
+WASM_API_EXTERN const wasm_limits_t* wasm_memorytype_limits(const wasm_memorytype_t*);
+
+
+// Extern Types
+
+WASM_DECLARE_TYPE(externtype)
+
+typedef uint8_t wasm_externkind_t;
+enum wasm_externkind_enum {
+  WASM_EXTERN_FUNC,
+  WASM_EXTERN_GLOBAL,
+  WASM_EXTERN_TABLE,
+  WASM_EXTERN_MEMORY,
+};
+
+WASM_API_EXTERN wasm_externkind_t wasm_externtype_kind(const wasm_externtype_t*);
+
+WASM_API_EXTERN wasm_externtype_t* wasm_functype_as_externtype(wasm_functype_t*);
+WASM_API_EXTERN wasm_externtype_t* wasm_globaltype_as_externtype(wasm_globaltype_t*);
+WASM_API_EXTERN wasm_externtype_t* wasm_tabletype_as_externtype(wasm_tabletype_t*);
+WASM_API_EXTERN wasm_externtype_t* wasm_memorytype_as_externtype(wasm_memorytype_t*);
+
+WASM_API_EXTERN wasm_functype_t* wasm_externtype_as_functype(wasm_externtype_t*);
+WASM_API_EXTERN wasm_globaltype_t* wasm_externtype_as_globaltype(wasm_externtype_t*);
+WASM_API_EXTERN wasm_tabletype_t* wasm_externtype_as_tabletype(wasm_externtype_t*);
+WASM_API_EXTERN wasm_memorytype_t* wasm_externtype_as_memorytype(wasm_externtype_t*);
+
+WASM_API_EXTERN const wasm_externtype_t* wasm_functype_as_externtype_const(const wasm_functype_t*);
+WASM_API_EXTERN const wasm_externtype_t* wasm_globaltype_as_externtype_const(const wasm_globaltype_t*);
+WASM_API_EXTERN const wasm_externtype_t* wasm_tabletype_as_externtype_const(const wasm_tabletype_t*);
+WASM_API_EXTERN const wasm_externtype_t* wasm_memorytype_as_externtype_const(const wasm_memorytype_t*);
+
+WASM_API_EXTERN const wasm_functype_t* wasm_externtype_as_functype_const(const wasm_externtype_t*);
+WASM_API_EXTERN const wasm_globaltype_t* wasm_externtype_as_globaltype_const(const wasm_externtype_t*);
+WASM_API_EXTERN const wasm_tabletype_t* wasm_externtype_as_tabletype_const(const wasm_externtype_t*);
+WASM_API_EXTERN const wasm_memorytype_t* wasm_externtype_as_memorytype_const(const wasm_externtype_t*);
+
+
+// Import Types
+
+WASM_DECLARE_TYPE(importtype)
+
+WASM_API_EXTERN own wasm_importtype_t* wasm_importtype_new(
+  own wasm_name_t* module, own wasm_name_t* name, own wasm_externtype_t*);
+
+WASM_API_EXTERN const wasm_name_t* wasm_importtype_module(const wasm_importtype_t*);
+WASM_API_EXTERN const wasm_name_t* wasm_importtype_name(const wasm_importtype_t*);
+WASM_API_EXTERN const wasm_externtype_t* wasm_importtype_type(const wasm_importtype_t*);
+
+
+// Export Types
+
+WASM_DECLARE_TYPE(exporttype)
+
+WASM_API_EXTERN own wasm_exporttype_t* wasm_exporttype_new(
+  own wasm_name_t*, own wasm_externtype_t*);
+
+WASM_API_EXTERN const wasm_name_t* wasm_exporttype_name(const wasm_exporttype_t*);
+WASM_API_EXTERN const wasm_externtype_t* wasm_exporttype_type(const wasm_exporttype_t*);
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Runtime Objects
+
+// Values
+
+struct wasm_ref_t;
+
+typedef struct wasm_val_t {
+  wasm_valkind_t kind;
+  union {
+    int32_t i32;
+    int64_t i64;
+    float32_t f32;
+    float64_t f64;
+    struct wasm_ref_t* ref;
+  } of;
+} wasm_val_t;
+
+WASM_API_EXTERN void wasm_val_delete(own wasm_val_t* v);
+WASM_API_EXTERN void wasm_val_copy(own wasm_val_t* out, const wasm_val_t*);
+
+WASM_DECLARE_VEC(val, )
+
+
+// References
+
+#define WASM_DECLARE_REF_BASE(name) \
+  WASM_DECLARE_OWN(name) \
+  \
+  WASM_API_EXTERN own wasm_##name##_t* wasm_##name##_copy(const wasm_##name##_t*); \
+  WASM_API_EXTERN bool wasm_##name##_same(const wasm_##name##_t*, const wasm_##name##_t*); \
+  \
+  WASM_API_EXTERN void* wasm_##name##_get_host_info(const wasm_##name##_t*); \
+  WASM_API_EXTERN void wasm_##name##_set_host_info(wasm_##name##_t*, void*); \
+  WASM_API_EXTERN void wasm_##name##_set_host_info_with_finalizer( \
+    wasm_##name##_t*, void*, void (*)(void*));
+
+#define WASM_DECLARE_REF(name) \
+  WASM_DECLARE_REF_BASE(name) \
+  \
+  WASM_API_EXTERN wasm_ref_t* wasm_##name##_as_ref(wasm_##name##_t*); \
+  WASM_API_EXTERN wasm_##name##_t* wasm_ref_as_##name(wasm_ref_t*); \
+  WASM_API_EXTERN const wasm_ref_t* wasm_##name##_as_ref_const(const wasm_##name##_t*); \
+  WASM_API_EXTERN const wasm_##name##_t* wasm_ref_as_##name##_const(const wasm_ref_t*);
+
+#define WASM_DECLARE_SHARABLE_REF(name) \
+  WASM_DECLARE_REF(name) \
+  WASM_DECLARE_OWN(shared_##name) \
+  \
+  WASM_API_EXTERN own wasm_shared_##name##_t* wasm_##name##_share(const wasm_##name##_t*); \
+  WASM_API_EXTERN own wasm_##name##_t* wasm_##name##_obtain(wasm_store_t*, const wasm_shared_##name##_t*);
+
+
+WASM_DECLARE_REF_BASE(ref)
+
+
+// Frames
+
+WASM_DECLARE_OWN(frame)
+WASM_DECLARE_VEC(frame, *)
+WASM_API_EXTERN own wasm_frame_t* wasm_frame_copy(const wasm_frame_t*);
+
+WASM_API_EXTERN struct wasm_instance_t* wasm_frame_instance(const wasm_frame_t*);
+WASM_API_EXTERN uint32_t wasm_frame_func_index(const wasm_frame_t*);
+WASM_API_EXTERN size_t wasm_frame_func_offset(const wasm_frame_t*);
+WASM_API_EXTERN size_t wasm_frame_module_offset(const wasm_frame_t*);
+
+
+// Traps
+
+typedef wasm_name_t wasm_message_t;  // null terminated
+
+WASM_DECLARE_REF(trap)
+
+WASM_API_EXTERN own wasm_trap_t* wasm_trap_new(wasm_store_t* store, const wasm_message_t*);
+
+WASM_API_EXTERN void wasm_trap_message(const wasm_trap_t*, own wasm_message_t* out);
+WASM_API_EXTERN own wasm_frame_t* wasm_trap_origin(const wasm_trap_t*);
+WASM_API_EXTERN void wasm_trap_trace(const wasm_trap_t*, own wasm_frame_vec_t* out);
+
+
+// Foreign Objects
+
+WASM_DECLARE_REF(foreign)
+
+WASM_API_EXTERN own wasm_foreign_t* wasm_foreign_new(wasm_store_t*);
+
+
+// Modules
+
+WASM_DECLARE_SHARABLE_REF(module)
+
+WASM_API_EXTERN own wasm_module_t* wasm_module_new(
+  wasm_store_t*, const wasm_byte_vec_t* binary);
+
+WASM_API_EXTERN bool wasm_module_validate(wasm_store_t*, const wasm_byte_vec_t* binary);
+
+WASM_API_EXTERN void wasm_module_imports(const wasm_module_t*, own wasm_importtype_vec_t* out);
+WASM_API_EXTERN void wasm_module_exports(const wasm_module_t*, own wasm_exporttype_vec_t* out);
+
+WASM_API_EXTERN void wasm_module_serialize(const wasm_module_t*, own wasm_byte_vec_t* out);
+WASM_API_EXTERN own wasm_module_t* wasm_module_deserialize(wasm_store_t*, const wasm_byte_vec_t*);
+
+
+// Function Instances
+
+WASM_DECLARE_REF(func)
+
+typedef own wasm_trap_t* (*wasm_func_callback_t)(
+  const wasm_val_vec_t* args, own wasm_val_vec_t* results);
+typedef own wasm_trap_t* (*wasm_func_callback_with_env_t)(
+  void* env, const wasm_val_vec_t* args, wasm_val_vec_t* results);
+
+WASM_API_EXTERN own wasm_func_t* wasm_func_new(
+  wasm_store_t*, const wasm_functype_t*, wasm_func_callback_t);
+WASM_API_EXTERN own wasm_func_t* wasm_func_new_with_env(
+  wasm_store_t*, const wasm_functype_t* type, wasm_func_callback_with_env_t,
+  void* env, void (*finalizer)(void*));
+
+WASM_API_EXTERN own wasm_functype_t* wasm_func_type(const wasm_func_t*);
+WASM_API_EXTERN size_t wasm_func_param_arity(const wasm_func_t*);
+WASM_API_EXTERN size_t wasm_func_result_arity(const wasm_func_t*);
+
+WASM_API_EXTERN own wasm_trap_t* wasm_func_call(
+  const wasm_func_t*, const wasm_val_vec_t* args, wasm_val_vec_t* results);
+
+
+// Global Instances
+
+WASM_DECLARE_REF(global)
+
+WASM_API_EXTERN own wasm_global_t* wasm_global_new(
+  wasm_store_t*, const wasm_globaltype_t*, const wasm_val_t*);
+
+WASM_API_EXTERN own wasm_globaltype_t* wasm_global_type(const wasm_global_t*);
+
+WASM_API_EXTERN void wasm_global_get(const wasm_global_t*, own wasm_val_t* out);
+WASM_API_EXTERN void wasm_global_set(wasm_global_t*, const wasm_val_t*);
+
+
+// Table Instances
+
+WASM_DECLARE_REF(table)
+
+typedef uint32_t wasm_table_size_t;
+
+WASM_API_EXTERN own wasm_table_t* wasm_table_new(
+  wasm_store_t*, const wasm_tabletype_t*, wasm_ref_t* init);
+
+WASM_API_EXTERN own wasm_tabletype_t* wasm_table_type(const wasm_table_t*);
+
+WASM_API_EXTERN own wasm_ref_t* wasm_table_get(const wasm_table_t*, wasm_table_size_t index);
+WASM_API_EXTERN bool wasm_table_set(wasm_table_t*, wasm_table_size_t index, wasm_ref_t*);
+
+WASM_API_EXTERN wasm_table_size_t wasm_table_size(const wasm_table_t*);
+WASM_API_EXTERN bool wasm_table_grow(wasm_table_t*, wasm_table_size_t delta, wasm_ref_t* init);
+
+
+// Memory Instances
+
+WASM_DECLARE_REF(memory)
+
+typedef uint32_t wasm_memory_pages_t;
+
+static const size_t MEMORY_PAGE_SIZE = 0x10000;
+
+WASM_API_EXTERN own wasm_memory_t* wasm_memory_new(wasm_store_t*, const wasm_memorytype_t*);
+
+WASM_API_EXTERN own wasm_memorytype_t* wasm_memory_type(const wasm_memory_t*);
+
+WASM_API_EXTERN byte_t* wasm_memory_data(wasm_memory_t*);
+WASM_API_EXTERN size_t wasm_memory_data_size(const wasm_memory_t*);
+
+WASM_API_EXTERN wasm_memory_pages_t wasm_memory_size(const wasm_memory_t*);
+WASM_API_EXTERN bool wasm_memory_grow(wasm_memory_t*, wasm_memory_pages_t delta);
+
+
+// Externals
+
+WASM_DECLARE_REF(extern)
+WASM_DECLARE_VEC(extern, *)
+
+WASM_API_EXTERN wasm_externkind_t wasm_extern_kind(const wasm_extern_t*);
+WASM_API_EXTERN own wasm_externtype_t* wasm_extern_type(const wasm_extern_t*);
+
+WASM_API_EXTERN wasm_extern_t* wasm_func_as_extern(wasm_func_t*);
+WASM_API_EXTERN wasm_extern_t* wasm_global_as_extern(wasm_global_t*);
+WASM_API_EXTERN wasm_extern_t* wasm_table_as_extern(wasm_table_t*);
+WASM_API_EXTERN wasm_extern_t* wasm_memory_as_extern(wasm_memory_t*);
+
+WASM_API_EXTERN wasm_func_t* wasm_extern_as_func(wasm_extern_t*);
+WASM_API_EXTERN wasm_global_t* wasm_extern_as_global(wasm_extern_t*);
+WASM_API_EXTERN wasm_table_t* wasm_extern_as_table(wasm_extern_t*);
+WASM_API_EXTERN wasm_memory_t* wasm_extern_as_memory(wasm_extern_t*);
+
+WASM_API_EXTERN const wasm_extern_t* wasm_func_as_extern_const(const wasm_func_t*);
+WASM_API_EXTERN const wasm_extern_t* wasm_global_as_extern_const(const wasm_global_t*);
+WASM_API_EXTERN const wasm_extern_t* wasm_table_as_extern_const(const wasm_table_t*);
+WASM_API_EXTERN const wasm_extern_t* wasm_memory_as_extern_const(const wasm_memory_t*);
+
+WASM_API_EXTERN const wasm_func_t* wasm_extern_as_func_const(const wasm_extern_t*);
+WASM_API_EXTERN const wasm_global_t* wasm_extern_as_global_const(const wasm_extern_t*);
+WASM_API_EXTERN const wasm_table_t* wasm_extern_as_table_const(const wasm_extern_t*);
+WASM_API_EXTERN const wasm_memory_t* wasm_extern_as_memory_const(const wasm_extern_t*);
+
+
+// Module Instances
+
+WASM_DECLARE_REF(instance)
+
+WASM_API_EXTERN own wasm_instance_t* wasm_instance_new(
+  wasm_store_t*, const wasm_module_t*, const wasm_extern_vec_t* imports,
+  own wasm_trap_t**
+);
+
+WASM_API_EXTERN void wasm_instance_exports(const wasm_instance_t*, own wasm_extern_vec_t* out);
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Convenience
+
+// Vectors
+
+#define WASM_EMPTY_VEC {0, NULL}
+#define WASM_ARRAY_VEC(array) {sizeof(array)/sizeof(*(array)), array}
+
+
+// Value Type construction short-hands
+
+static inline own wasm_valtype_t* wasm_valtype_new_i32(void) {
+  return wasm_valtype_new(WASM_I32);
+}
+static inline own wasm_valtype_t* wasm_valtype_new_i64(void) {
+  return wasm_valtype_new(WASM_I64);
+}
+static inline own wasm_valtype_t* wasm_valtype_new_f32(void) {
+  return wasm_valtype_new(WASM_F32);
+}
+static inline own wasm_valtype_t* wasm_valtype_new_f64(void) {
+  return wasm_valtype_new(WASM_F64);
+}
+
+static inline own wasm_valtype_t* wasm_valtype_new_externref(void) {
+  return wasm_valtype_new(WASM_EXTERNREF);
+}
+static inline own wasm_valtype_t* wasm_valtype_new_funcref(void) {
+  return wasm_valtype_new(WASM_FUNCREF);
+}
+
+
+// Function Types construction short-hands
+
+static inline own wasm_functype_t* wasm_functype_new_0_0(void) {
+  wasm_valtype_vec_t params, results;
+  wasm_valtype_vec_new_empty(&params);
+  wasm_valtype_vec_new_empty(&results);
+  return wasm_functype_new(&params, &results);
+}
+
+static inline own wasm_functype_t* wasm_functype_new_1_0(
+  own wasm_valtype_t* p
+) {
+  wasm_valtype_t* ps[1] = {p};
+  wasm_valtype_vec_t params, results;
+  wasm_valtype_vec_new(&params, 1, ps);
+  wasm_valtype_vec_new_empty(&results);
+  return wasm_functype_new(&params, &results);
+}
+
+static inline own wasm_functype_t* wasm_functype_new_2_0(
+  own wasm_valtype_t* p1, own wasm_valtype_t* p2
+) {
+  wasm_valtype_t* ps[2] = {p1, p2};
+  wasm_valtype_vec_t params, results;
+  wasm_valtype_vec_new(&params, 2, ps);
+  wasm_valtype_vec_new_empty(&results);
+  return wasm_functype_new(&params, &results);
+}
+
+static inline own wasm_functype_t* wasm_functype_new_3_0(
+  own wasm_valtype_t* p1, own wasm_valtype_t* p2, own wasm_valtype_t* p3
+) {
+  wasm_valtype_t* ps[3] = {p1, p2, p3};
+  wasm_valtype_vec_t params, results;
+  wasm_valtype_vec_new(&params, 3, ps);
+  wasm_valtype_vec_new_empty(&results);
+  return wasm_functype_new(&params, &results);
+}
+
+static inline own wasm_functype_t* wasm_functype_new_0_1(
+  own wasm_valtype_t* r
+) {
+  wasm_valtype_t* rs[1] = {r};
+  wasm_valtype_vec_t params, results;
+  wasm_valtype_vec_new_empty(&params);
+  wasm_valtype_vec_new(&results, 1, rs);
+  return wasm_functype_new(&params, &results);
+}
+
+static inline own wasm_functype_t* wasm_functype_new_1_1(
+  own wasm_valtype_t* p, own wasm_valtype_t* r
+) {
+  wasm_valtype_t* ps[1] = {p};
+  wasm_valtype_t* rs[1] = {r};
+  wasm_valtype_vec_t params, results;
+  wasm_valtype_vec_new(&params, 1, ps);
+  wasm_valtype_vec_new(&results, 1, rs);
+  return wasm_functype_new(&params, &results);
+}
+
+static inline own wasm_functype_t* wasm_functype_new_2_1(
+  own wasm_valtype_t* p1, own wasm_valtype_t* p2, own wasm_valtype_t* r
+) {
+  wasm_valtype_t* ps[2] = {p1, p2};
+  wasm_valtype_t* rs[1] = {r};
+  wasm_valtype_vec_t params, results;
+  wasm_valtype_vec_new(&params, 2, ps);
+  wasm_valtype_vec_new(&results, 1, rs);
+  return wasm_functype_new(&params, &results);
+}
+
+static inline own wasm_functype_t* wasm_functype_new_3_1(
+  own wasm_valtype_t* p1, own wasm_valtype_t* p2, own wasm_valtype_t* p3,
+  own wasm_valtype_t* r
+) {
+  wasm_valtype_t* ps[3] = {p1, p2, p3};
+  wasm_valtype_t* rs[1] = {r};
+  wasm_valtype_vec_t params, results;
+  wasm_valtype_vec_new(&params, 3, ps);
+  wasm_valtype_vec_new(&results, 1, rs);
+  return wasm_functype_new(&params, &results);
+}
+
+static inline own wasm_functype_t* wasm_functype_new_0_2(
+  own wasm_valtype_t* r1, own wasm_valtype_t* r2
+) {
+  wasm_valtype_t* rs[2] = {r1, r2};
+  wasm_valtype_vec_t params, results;
+  wasm_valtype_vec_new_empty(&params);
+  wasm_valtype_vec_new(&results, 2, rs);
+  return wasm_functype_new(&params, &results);
+}
+
+static inline own wasm_functype_t* wasm_functype_new_1_2(
+  own wasm_valtype_t* p, own wasm_valtype_t* r1, own wasm_valtype_t* r2
+) {
+  wasm_valtype_t* ps[1] = {p};
+  wasm_valtype_t* rs[2] = {r1, r2};
+  wasm_valtype_vec_t params, results;
+  wasm_valtype_vec_new(&params, 1, ps);
+  wasm_valtype_vec_new(&results, 2, rs);
+  return wasm_functype_new(&params, &results);
+}
+
+static inline own wasm_functype_t* wasm_functype_new_2_2(
+  own wasm_valtype_t* p1, own wasm_valtype_t* p2,
+  own wasm_valtype_t* r1, own wasm_valtype_t* r2
+) {
+  wasm_valtype_t* ps[2] = {p1, p2};
+  wasm_valtype_t* rs[2] = {r1, r2};
+  wasm_valtype_vec_t params, results;
+  wasm_valtype_vec_new(&params, 2, ps);
+  wasm_valtype_vec_new(&results, 2, rs);
+  return wasm_functype_new(&params, &results);
+}
+
+static inline own wasm_functype_t* wasm_functype_new_3_2(
+  own wasm_valtype_t* p1, own wasm_valtype_t* p2, own wasm_valtype_t* p3,
+  own wasm_valtype_t* r1, own wasm_valtype_t* r2
+) {
+  wasm_valtype_t* ps[3] = {p1, p2, p3};
+  wasm_valtype_t* rs[2] = {r1, r2};
+  wasm_valtype_vec_t params, results;
+  wasm_valtype_vec_new(&params, 3, ps);
+  wasm_valtype_vec_new(&results, 2, rs);
+  return wasm_functype_new(&params, &results);
+}
+
+
+// Value construction short-hands
+
+static inline void wasm_val_init_ptr(own wasm_val_t* out, void* p) {
+#if UINTPTR_MAX == UINT32_MAX
+  out->kind = WASM_I32;
+  out->of.i32 = (intptr_t)p;
+#elif UINTPTR_MAX == UINT64_MAX
+  out->kind = WASM_I64;
+  out->of.i64 = (intptr_t)p;
+#endif
+}
+
+static inline void* wasm_val_ptr(const wasm_val_t* val) {
+#if UINTPTR_MAX == UINT32_MAX
+  return (void*)(intptr_t)val->of.i32;
+#elif UINTPTR_MAX == UINT64_MAX
+  return (void*)(intptr_t)val->of.i64;
+#endif
+}
+
+#define WASM_I32_VAL(i) {.kind = WASM_I32, .of = {.i32 = i}}
+#define WASM_I64_VAL(i) {.kind = WASM_I64, .of = {.i64 = i}}
+#define WASM_F32_VAL(z) {.kind = WASM_F32, .of = {.f32 = z}}
+#define WASM_F64_VAL(z) {.kind = WASM_F64, .of = {.f64 = z}}
+#define WASM_REF_VAL(r) {.kind = WASM_EXTERNREF, .of = {.ref = r}}
+#define WASM_INIT_VAL {.kind = WASM_EXTERNREF, .of = {.ref = NULL}}
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+#undef own
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // #ifdef WASM_H
diff --git a/crates/c-api/include/wasm.hh b/crates/c-api/include/wasm.hh
new file mode 100644
index 000000000..e03408241
--- /dev/null
+++ b/crates/c-api/include/wasm.hh
@@ -0,0 +1,849 @@
+// Wasmtime-vendored copy of this header file as present in upstream:
+// <https://github.com/WebAssembly/wasm-c-api/blob/2ce1367c9d1271c83fb63bef26d896a2f290cd23/include/wasm.hh>
+//
+// Wasmtime maintainers can update this vendored copy in-tree with the
+// ./ci/vendor-c-api-headers.sh shell script.
+//
+// WebAssembly C++ API
+
+#ifndef WASM_HH
+#define WASM_HH
+
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+#include <cstring>
+#include <memory>
+#include <limits>
+#include <string>
+
+#ifndef WASM_API_EXTERN
+#if defined(_WIN32) && !defined(__MINGW32__) && !defined(LIBWASM_STATIC)
+#define WASM_API_EXTERN __declspec(dllimport)
+#else
+#define WASM_API_EXTERN
+#endif
+#endif
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Auxiliaries
+
+// Machine types
+
+static_assert(sizeof(float) == sizeof(int32_t), "incompatible float type");
+static_assert(sizeof(double) == sizeof(int64_t), "incompatible double type");
+static_assert(sizeof(intptr_t) == sizeof(int32_t) ||
+              sizeof(intptr_t) == sizeof(int64_t), "incompatible pointer type");
+
+using byte_t = char;
+using float32_t = float;
+using float64_t = double;
+
+
+namespace wasm {
+
+// Vectors
+
+template<class T>
+class vec {
+  static const size_t invalid_size = SIZE_MAX;
+
+  size_t size_;
+  std::unique_ptr<T[]> data_;
+
+#ifdef WASM_API_DEBUG
+  WASM_API_EXTERN void make_data();
+  WASM_API_EXTERN void free_data();
+#else
+  void make_data() {}
+  void free_data() {}
+#endif
+
+  vec(size_t size) : vec(size, size ? new(std::nothrow) T[size] : nullptr) {
+    make_data();
+  }
+
+  vec(size_t size, T* data) : size_(size), data_(data) {
+    assert(!!size_ == !!data_ || size_ == invalid_size);
+  }
+
+public:
+  using elem_type = T;
+
+  vec(vec<T>&& that) : vec(that.size_, that.data_.release()) {
+    that.size_ = invalid_size;
+  }
+
+  ~vec() {
+    free_data();
+  }
+
+  operator bool() const {
+    return bool(size_ != invalid_size);
+  }
+
+  auto size() const -> size_t {
+    return size_;
+  }
+
+  auto get() const -> const T* {
+    return data_.get();
+  }
+
+  auto get() -> T* {
+    return data_.get();
+  }
+
+  auto release() -> T* {
+    size_ = invalid_size;
+    return data_.release();
+  }
+
+  void reset() {
+    free_data();
+    size_ = invalid_size;
+    data_.reset();
+  }
+
+  void reset(vec& that) {
+    free_data();
+    size_ = that.size_;
+    data_.reset(that.data_.release());
+    that.size_ = invalid_size;
+  }
+
+  auto operator=(vec&& that) -> vec& {
+    reset(that);
+    return *this;
+  }
+
+  auto operator[](size_t i) -> T& {
+    assert(i < size_);
+    return data_[i];
+  }
+
+  auto operator[](size_t i) const -> const T& {
+    assert(i < size_);
+    return data_[i];
+  }
+
+  auto copy() const -> vec {
+    auto v = vec(size_);
+    if (v) for (size_t i = 0; i < size_; ++i) v.data_[i] = data_[i];
+    return v;
+  }
+
+  // TODO: This can't be used for e.g. vec<Val>
+  auto deep_copy() const -> vec {
+    auto v = vec(size_);
+    if (v) for (size_t i = 0; i < size_; ++i) v.data_[i] = data_[i]->copy();
+    return v;
+  }
+
+  static auto make_uninitialized(size_t size = 0) -> vec {
+    return vec(size);
+  }
+
+  static auto make(size_t size, T init[]) -> vec {
+    auto v = vec(size);
+    if (v) for (size_t i = 0; i < size; ++i) v.data_[i] = std::move(init[i]);
+    return v;
+  }
+
+  static auto make(std::string s) -> vec<char> {
+    auto v = vec(s.length());
+    if (v) std::strncpy(v.get(), s.data(), s.length());
+    return v;
+  }
+
+  static auto make_nt(std::string s) -> vec<char> {
+    auto v = vec(s.length() + 1);
+    if (v) std::strcpy(v.get(), s.data());
+    return v;
+  }
+
+  // TODO(mvsc): MVSC requires this special case:
+  static auto make() -> vec {
+    return vec(0);
+  }
+
+  template<class... Ts>
+  static auto make(Ts&&... args) -> vec {
+    T data[] = { std::forward<Ts>(args)... };
+    return make(sizeof...(Ts), data);
+  }
+
+  static auto adopt(size_t size, T data[]) -> vec {
+    return vec(size, data);
+  }
+
+  static auto invalid() -> vec {
+    return vec(invalid_size, nullptr);
+  }
+};
+
+
+// Ownership
+
+class destroyer {
+public:
+  template <typename T>
+  void operator()(T* ptr) {
+    ptr->destroy();
+  }
+};
+
+template<class T> using own = std::unique_ptr<T, destroyer>;
+template<class T> using ownvec = vec<own<T>>;
+
+template<class T> own<T> make_own(T* ptr) {
+  return own<T>(ptr);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Runtime Environment
+
+// Configuration
+
+class WASM_API_EXTERN Config {
+  friend class destroyer;
+  void destroy();
+
+protected:
+  Config() = default;
+  ~Config() = default;
+
+public:
+  static auto make() -> own<Config>;
+
+  // Implementations may provide custom methods for manipulating Configs.
+};
+
+
+// Engine
+
+class WASM_API_EXTERN Engine {
+  friend class destroyer;
+  void destroy();
+
+protected:
+  Engine() = default;
+  ~Engine() = default;
+
+public:
+  static auto make(own<Config>&& = Config::make()) -> own<Engine>;
+};
+
+
+// Store
+
+class WASM_API_EXTERN Store {
+  friend class destroyer;
+  void destroy();
+
+protected:
+  Store() = default;
+  ~Store() = default;
+
+public:
+  static auto make(Engine*) -> own<Store>;
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Type Representations
+
+// Type attributes
+
+enum class Mutability : uint8_t { CONST, VAR };
+
+struct Limits {
+  uint32_t min;
+  uint32_t max;
+
+  Limits(uint32_t min, uint32_t max = std::numeric_limits<uint32_t>::max()) :
+    min(min), max(max) {}
+};
+
+
+// Value Types
+
+enum class ValKind : uint8_t {
+  I32, I64, F32, F64,
+  EXTERNREF = 128, FUNCREF,
+};
+
+inline bool is_num(ValKind k) { return k < ValKind::EXTERNREF; }
+inline bool is_ref(ValKind k) { return k >= ValKind::EXTERNREF; }
+
+
+class WASM_API_EXTERN ValType {
+  friend class destroyer;
+  void destroy();
+
+protected:
+  ValType() = default;
+  ~ValType() = default;
+
+public:
+  static auto make(ValKind) -> own<ValType>;
+  auto copy() const -> own<ValType>;
+
+  auto kind() const -> ValKind;
+  auto is_num() const -> bool { return wasm::is_num(kind()); }
+  auto is_ref() const -> bool { return wasm::is_ref(kind()); }
+};
+
+
+// External Types
+
+enum class ExternKind : uint8_t {
+  FUNC, GLOBAL, TABLE, MEMORY
+};
+
+class FuncType;
+class GlobalType;
+class TableType;
+class MemoryType;
+
+class WASM_API_EXTERN ExternType {
+  friend class destroyer;
+  void destroy();
+
+protected:
+  ExternType() = default;
+  ~ExternType() = default;
+
+public:
+  auto copy() const-> own<ExternType>;
+
+  auto kind() const -> ExternKind;
+
+  auto func() -> FuncType*;
+  auto global() -> GlobalType*;
+  auto table() -> TableType*;
+  auto memory() -> MemoryType*;
+
+  auto func() const -> const FuncType*;
+  auto global() const -> const GlobalType*;
+  auto table() const -> const TableType*;
+  auto memory() const -> const MemoryType*;
+};
+
+
+// Function Types
+
+class WASM_API_EXTERN FuncType : public ExternType {
+  friend class destroyer;
+  void destroy();
+
+protected:
+  FuncType() = default;
+  ~FuncType() = default;
+
+public:
+  static auto make(
+    ownvec<ValType>&& params = ownvec<ValType>::make(),
+    ownvec<ValType>&& results = ownvec<ValType>::make()
+  ) -> own<FuncType>;
+
+  auto copy() const -> own<FuncType>;
+
+  auto params() const -> const ownvec<ValType>&;
+  auto results() const -> const ownvec<ValType>&;
+};
+
+
+// Global Types
+
+class WASM_API_EXTERN GlobalType : public ExternType {
+  friend class destroyer;
+  void destroy();
+
+protected:
+  GlobalType() = default;
+  ~GlobalType() = default;
+
+public:
+  static auto make(own<ValType>&&, Mutability) -> own<GlobalType>;
+  auto copy() const -> own<GlobalType>;
+
+  auto content() const -> const ValType*;
+  auto mutability() const -> Mutability;
+};
+
+
+// Table Types
+
+class WASM_API_EXTERN TableType : public ExternType {
+  friend class destroyer;
+  void destroy();
+
+protected:
+  TableType() = default;
+  ~TableType() = default;
+
+public:
+  static auto make(own<ValType>&&, Limits) -> own<TableType>;
+  auto copy() const -> own<TableType>;
+
+  auto element() const -> const ValType*;
+  auto limits() const -> const Limits&;
+};
+
+
+// Memory Types
+
+class WASM_API_EXTERN MemoryType : public ExternType {
+  friend class destroyer;
+  void destroy();
+
+protected:
+  MemoryType() = default;
+  ~MemoryType() = default;
+
+public:
+  static auto make(Limits) -> own<MemoryType>;
+  auto copy() const -> own<MemoryType>;
+
+  auto limits() const -> const Limits&;
+};
+
+
+// Import Types
+
+using Name = vec<byte_t>;
+
+class WASM_API_EXTERN ImportType {
+  friend class destroyer;
+  void destroy();
+
+protected:
+  ImportType() = default;
+  ~ImportType() = default;
+
+public:
+  static auto make(Name&& module, Name&& name, own<ExternType>&&) ->
+    own<ImportType>;
+  auto copy() const -> own<ImportType>;
+
+  auto module() const -> const Name&;
+  auto name() const -> const Name&;
+  auto type() const -> const ExternType*;
+};
+
+
+// Export Types
+
+class WASM_API_EXTERN ExportType {
+  friend class destroyer;
+  void destroy();
+
+protected:
+  ExportType() = default;
+  ~ExportType() = default;
+
+public:
+  static auto make(Name&&, own<ExternType>&&) -> own<ExportType>;
+  auto copy() const -> own<ExportType>;
+
+  auto name() const -> const Name&;
+  auto type() const -> const ExternType*;
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Runtime Objects
+
+// References
+
+class WASM_API_EXTERN Ref {
+  friend class destroyer;
+  void destroy();
+
+protected:
+  Ref() = default;
+  ~Ref() = default;
+
+public:
+  auto copy() const -> own<Ref>;
+  auto same(const Ref*) const -> bool;
+
+  auto get_host_info() const -> void*;
+  void set_host_info(void* info, void (*finalizer)(void*) = nullptr);
+};
+
+
+// Values
+
+class Val {
+  ValKind kind_;
+  union impl {
+    int32_t i32;
+    int64_t i64;
+    float32_t f32;
+    float64_t f64;
+    Ref* ref;
+  } impl_;
+
+  Val(ValKind kind, impl impl) : kind_(kind), impl_(impl) {}
+
+public:
+  Val() : kind_(ValKind::EXTERNREF) { impl_.ref = nullptr; }
+  explicit Val(int32_t i) : kind_(ValKind::I32) { impl_.i32 = i; }
+  explicit Val(int64_t i) : kind_(ValKind::I64) { impl_.i64 = i; }
+  explicit Val(float32_t z) : kind_(ValKind::F32) { impl_.f32 = z; }
+  explicit Val(float64_t z) : kind_(ValKind::F64) { impl_.f64 = z; }
+  explicit Val(own<Ref>&& r) : kind_(ValKind::EXTERNREF) { impl_.ref = r.release(); }
+
+  Val(Val&& that) : kind_(that.kind_), impl_(that.impl_) {
+    if (is_ref()) that.impl_.ref = nullptr;
+  }
+
+  ~Val() {
+    reset();
+  }
+
+  auto is_num() const -> bool { return wasm::is_num(kind_); }
+  auto is_ref() const -> bool { return wasm::is_ref(kind_); }
+
+  static auto i32(int32_t x) -> Val { return Val(x); }
+  static auto i64(int64_t x) -> Val { return Val(x); }
+  static auto f32(float32_t x) -> Val { return Val(x); }
+  static auto f64(float64_t x) -> Val { return Val(x); }
+  static auto ref(own<Ref>&& x) -> Val { return Val(std::move(x)); }
+  template<class T> inline static auto make(T x) -> Val;
+  template<class T> inline static auto make(own<T>&& x) -> Val;
+
+  void reset() {
+    if (is_ref() && impl_.ref) {
+      destroyer()(impl_.ref);
+      impl_.ref = nullptr;
+    }
+  }
+
+  void reset(Val& that) {
+    reset();
+    kind_ = that.kind_;
+    impl_ = that.impl_;
+    if (is_ref()) that.impl_.ref = nullptr;
+  }
+
+  auto operator=(Val&& that) -> Val& {
+    reset(that);
+    return *this;
+  }
+
+  auto kind() const -> ValKind { return kind_; }
+  auto i32() const -> int32_t { assert(kind_ == ValKind::I32); return impl_.i32; }
+  auto i64() const -> int64_t { assert(kind_ == ValKind::I64); return impl_.i64; }
+  auto f32() const -> float32_t { assert(kind_ == ValKind::F32); return impl_.f32; }
+  auto f64() const -> float64_t { assert(kind_ == ValKind::F64); return impl_.f64; }
+  auto ref() const -> Ref* { assert(is_ref()); return impl_.ref; }
+  template<class T> inline auto get() const -> T;
+
+  auto release_ref() -> own<Ref> {
+    assert(is_ref());
+    auto ref = impl_.ref;
+    impl_.ref = nullptr;
+    return own<Ref>(ref);
+  }
+
+  auto copy() const -> Val {
+    if (is_ref() && impl_.ref != nullptr) {
+      // TODO(mvsc): MVSC cannot handle this:
+      // impl impl = {.ref = impl_.ref->copy().release()};
+      impl impl;
+      impl.ref = impl_.ref->copy().release();
+      return Val(kind_, impl);
+    } else {
+      return Val(kind_, impl_);
+    }
+  }
+};
+
+
+template<> inline auto Val::make<int32_t>(int32_t x) -> Val { return Val(x); }
+template<> inline auto Val::make<int64_t>(int64_t x) -> Val { return Val(x); }
+template<> inline auto Val::make<float32_t>(float32_t x) -> Val { return Val(x); }
+template<> inline auto Val::make<float64_t>(float64_t x) -> Val { return Val(x); }
+template<> inline auto Val::make<Ref>(own<Ref>&& x) -> Val {
+  return Val(std::move(x));
+}
+
+template<> inline auto Val::make<uint32_t>(uint32_t x) -> Val {
+  return Val(static_cast<int32_t>(x));
+}
+template<> inline auto Val::make<uint64_t>(uint64_t x) -> Val {
+  return Val(static_cast<int64_t>(x));
+}
+
+template<> inline auto Val::get<int32_t>() const -> int32_t { return i32(); }
+template<> inline auto Val::get<int64_t>() const -> int64_t { return i64(); }
+template<> inline auto Val::get<float32_t>() const -> float32_t { return f32(); }
+template<> inline auto Val::get<float64_t>() const -> float64_t { return f64(); }
+template<> inline auto Val::get<Ref*>() const -> Ref* { return ref(); }
+
+template<> inline auto Val::get<uint32_t>() const -> uint32_t {
+  return static_cast<uint32_t>(i32());
+}
+template<> inline auto Val::get<uint64_t>() const -> uint64_t {
+  return static_cast<uint64_t>(i64());
+}
+
+
+// Traps
+
+using Message = vec<byte_t>;  // null terminated
+
+class Instance;
+
+class WASM_API_EXTERN Frame {
+  friend class destroyer;
+  void destroy();
+
+protected:
+  Frame() = default;
+  ~Frame() = default;
+
+public:
+  auto copy() const -> own<Frame>;
+
+  auto instance() const -> Instance*;
+  auto func_index() const -> uint32_t;
+  auto func_offset() const -> size_t;
+  auto module_offset() const -> size_t;
+};
+
+class WASM_API_EXTERN Trap : public Ref {
+  friend class destroyer;
+  void destroy();
+
+protected:
+  Trap() = default;
+  ~Trap() = default;
+
+public:
+  static auto make(Store*, const Message& msg) -> own<Trap>;
+  auto copy() const -> own<Trap>;
+
+  auto message() const -> Message;
+  auto origin() const -> own<Frame>;  // may be null
+  auto trace() const -> ownvec<Frame>;  // may be empty, origin first
+};
+
+
+// Modules
+
+template<class T> class WASM_API_EXTERN Shared;
+
+class WASM_API_EXTERN Module : public Ref {
+  friend class destroyer;
+  void destroy();
+
+protected:
+  Module() = default;
+  ~Module() = default;
+
+public:
+  static auto validate(Store*, const vec<byte_t>& binary) -> bool;
+  static auto make(Store*, const vec<byte_t>& binary) -> own<Module>;
+  auto copy() const -> own<Module>;
+
+  auto imports() const -> ownvec<ImportType>;
+  auto exports() const -> ownvec<ExportType>;
+
+  auto share() const -> own<Shared<Module>>;
+  static auto obtain(Store*, const Shared<Module>*) -> own<Module>;
+
+  auto serialize() const -> vec<byte_t>;
+  static auto deserialize(Store*, const vec<byte_t>&) -> own<Module>;
+};
+
+
+// Shared objects
+
+template<>
+class WASM_API_EXTERN Shared<Module> {
+  friend class destroyer;
+  void destroy();
+
+protected:
+  Shared() = default;
+  ~Shared() = default;
+};
+
+
+// Foreign Objects
+
+class WASM_API_EXTERN Foreign : public Ref {
+  friend class destroyer;
+  void destroy();
+
+protected:
+  Foreign() = default;
+  ~Foreign() = default;
+
+public:
+  static auto make(Store*) -> own<Foreign>;
+  auto copy() const -> own<Foreign>;
+};
+
+
+// Externals
+
+class Func;
+class Global;
+class Table;
+class Memory;
+
+class WASM_API_EXTERN Extern : public Ref {
+  friend class destroyer;
+  void destroy();
+
+protected:
+  Extern() = default;
+  ~Extern() = default;
+
+public:
+  auto copy() const -> own<Extern>;
+
+  auto kind() const -> ExternKind;
+  auto type() const -> own<ExternType>;
+
+  auto func() -> Func*;
+  auto global() -> Global*;
+  auto table() -> Table*;
+  auto memory() -> Memory*;
+
+  auto func() const -> const Func*;
+  auto global() const -> const Global*;
+  auto table() const -> const Table*;
+  auto memory() const -> const Memory*;
+};
+
+
+// Function Instances
+
+class WASM_API_EXTERN Func : public Extern {
+  friend class destroyer;
+  void destroy();
+
+protected:
+  Func() = default;
+  ~Func() = default;
+
+public:
+  using callback = auto (*)(const vec<Val>&, vec<Val>&) -> own<Trap>;
+  using callback_with_env = auto (*)(void*, const vec<Val>&, vec<Val>&) -> own<Trap>;
+
+  static auto make(Store*, const FuncType*, callback) -> own<Func>;
+  static auto make(Store*, const FuncType*, callback_with_env,
+    void*, void (*finalizer)(void*) = nullptr) -> own<Func>;
+  auto copy() const -> own<Func>;
+
+  auto type() const -> own<FuncType>;
+  auto param_arity() const -> size_t;
+  auto result_arity() const -> size_t;
+
+  auto call(const vec<Val>&, vec<Val>&) const -> own<Trap>;
+};
+
+
+// Global Instances
+
+class WASM_API_EXTERN Global : public Extern {
+  friend class destroyer;
+  void destroy();
+
+protected:
+  Global() = default;
+  ~Global() = default;
+
+ public:
+  static auto make(Store*, const GlobalType*, const Val&) -> own<Global>;
+  auto copy() const -> own<Global>;
+
+  auto type() const -> own<GlobalType>;
+  auto get() const -> Val;
+  void set(const Val&);
+};
+
+
+// Table Instances
+
+class WASM_API_EXTERN Table : public Extern {
+  friend class destroyer;
+  void destroy();
+
+protected:
+  Table() = default;
+  ~Table() = default;
+
+public:
+  using size_t = uint32_t;
+
+  static auto make(
+    Store*, const TableType*, const Ref* init = nullptr) -> own<Table>;
+  auto copy() const -> own<Table>;
+
+  auto type() const -> own<TableType>;
+  auto get(size_t index) const -> own<Ref>;
+  auto set(size_t index, const Ref*) -> bool;
+  auto size() const -> size_t;
+  auto grow(size_t delta, const Ref* init = nullptr) -> bool;
+};
+
+
+// Memory Instances
+
+class WASM_API_EXTERN Memory : public Extern {
+  friend class destroyer;
+  void destroy();
+
+protected:
+  Memory() = default;
+  ~Memory() = default;
+
+public:
+  static auto make(Store*, const MemoryType*) -> own<Memory>;
+  auto copy() const -> own<Memory>;
+
+  using pages_t = uint32_t;
+
+  static const size_t page_size = 0x10000;
+
+  auto type() const -> own<MemoryType>;
+  auto data() const -> byte_t*;
+  auto data_size() const -> size_t;
+  auto size() const -> pages_t;
+  auto grow(pages_t delta) -> bool;
+};
+
+
+// Module Instances
+
+class WASM_API_EXTERN Instance : public Ref {
+  friend class destroyer;
+  void destroy();
+
+protected:
+  Instance() = default;
+  ~Instance() = default;
+
+public:
+  static auto make(
+    Store*, const Module*, const vec<Extern*>&, own<Trap>* = nullptr
+  ) -> own<Instance>;
+  auto copy() const -> own<Instance>;
+
+  auto exports() const -> ownvec<Extern>;
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+}  // namespace wasm
+
+#endif  // #ifdef WASM_HH
diff --git a/crates/c-api/wasm-c-api b/crates/c-api/wasm-c-api
deleted file mode 160000
index c9d312846..000000000
--- a/crates/c-api/wasm-c-api
+++ /dev/null
@@ -1,1 +0,0 @@
-Subproject commit c9d31284651b975f05ac27cee0bab1377560b87e
diff --git a/crates/cli-flags/src/lib.rs b/crates/cli-flags/src/lib.rs
index 821636a53..5875e0b8d 100644
--- a/crates/cli-flags/src/lib.rs
+++ b/crates/cli-flags/src/lib.rs
@@ -293,6 +293,10 @@ wasmtime_option_group! {
         pub udp: Option<bool>,
         /// Allows imports from the `wasi_unstable` core wasm module.
         pub preview0: Option<bool>,
+        /// Inherit all environment variables from the parent process.
+        ///
+        /// This option can be further overwritten with `--env` flags.
+        pub inherit_env: Option<bool>,
     }
 
     enum Wasi {
diff --git a/crates/cranelift/src/func_environ.rs b/crates/cranelift/src/func_environ.rs
index b093e666f..694d8a173 100644
--- a/crates/cranelift/src/func_environ.rs
+++ b/crates/cranelift/src/func_environ.rs
@@ -961,7 +961,7 @@ impl<'module_environment> FuncEnvironment<'module_environment> {
             pointer_type,
             self.isa.flags().enable_table_access_spectre_mitigation(),
         );
-        let flags = ir::MemFlags::trusted().with_table();
+        let flags = ir::MemFlags::trusted().with_alias_region(Some(ir::AliasRegion::Table));
         let value = builder.ins().load(pointer_type, flags, table_entry_addr, 0);
         // Mask off the "initialized bit". See documentation on
         // FUNCREF_INIT_BIT in crates/environ/src/ref_bits.rs for more
@@ -1446,7 +1446,7 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
                     pointer_type,
                     self.isa.flags().enable_table_access_spectre_mitigation(),
                 );
-                let flags = ir::MemFlags::trusted().with_table();
+                let flags = ir::MemFlags::trusted().with_alias_region(Some(ir::AliasRegion::Table));
                 let elem = builder.ins().load(reference_type, flags, elem_addr, 0);
 
                 let elem_is_null = builder.ins().is_null(elem);
@@ -1563,7 +1563,8 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
                         let value_with_init_bit = builder
                             .ins()
                             .bor_imm(value, Imm64::from(FUNCREF_INIT_BIT as i64));
-                        let flags = ir::MemFlags::trusted().with_table();
+                        let flags =
+                            ir::MemFlags::trusted().with_alias_region(Some(ir::AliasRegion::Table));
                         builder
                             .ins()
                             .store(flags, value_with_init_bit, table_entry_addr, 0);
@@ -1633,7 +1634,7 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
                     pointer_type,
                     self.isa.flags().enable_table_access_spectre_mitigation(),
                 );
-                let flags = ir::MemFlags::trusted().with_table();
+                let flags = ir::MemFlags::trusted().with_alias_region(Some(ir::AliasRegion::Table));
                 let current_elem = builder.ins().load(pointer_type, flags, table_entry_addr, 0);
                 builder.ins().store(flags, value, table_entry_addr, 0);
 
diff --git a/examples/async.cpp b/examples/async.cpp
index a5feae115..fac006a2a 100644
--- a/examples/async.cpp
+++ b/examples/async.cpp
@@ -7,7 +7,6 @@ You can compile and run this example on Linux with:
    cargo build --release -p wasmtime-c-api
    c++ examples/async.cpp \
        -I crates/c-api/include \
-       -I crates/c-api/wasm-c-api/include \
        target/release/libwasmtime.a \
        -std=c++11 \
        -lpthread -ldl -lm \
diff --git a/examples/fuel.c b/examples/fuel.c
index 945dc5bf5..521d05628 100644
--- a/examples/fuel.c
+++ b/examples/fuel.c
@@ -7,7 +7,6 @@ You can compile and run this example on Linux with:
    cargo build --release -p wasmtime-c-api
    cc examples/fuel.c \
        -I crates/c-api/include \
-       -I crates/c-api/wasm-c-api/include \
        target/release/libwasmtime.a \
        -lpthread -ldl -lm \
        -o fuel
diff --git a/examples/gcd.c b/examples/gcd.c
index 9f7f45c0a..7447f8f4a 100644
--- a/examples/gcd.c
+++ b/examples/gcd.c
@@ -7,7 +7,6 @@ You can compile and run this example on Linux with:
    cargo build --release -p wasmtime-c-api
    cc examples/gcd.c \
        -I crates/c-api/include \
-       -I crates/c-api/wasm-c-api/include \
        target/release/libwasmtime.a \
        -lpthread -ldl -lm \
        -o gcd
diff --git a/examples/hello.c b/examples/hello.c
index dc7343432..45ff62072 100644
--- a/examples/hello.c
+++ b/examples/hello.c
@@ -7,7 +7,6 @@ You can compile and run this example on Linux with:
    cargo build --release -p wasmtime-c-api
    cc examples/hello.c \
        -I crates/c-api/include \
-       -I crates/c-api/wasm-c-api/include \
        target/release/libwasmtime.a \
        -lpthread -ldl -lm \
        -o hello
diff --git a/examples/interrupt.c b/examples/interrupt.c
index 13292f2ca..3866cec1a 100644
--- a/examples/interrupt.c
+++ b/examples/interrupt.c
@@ -7,7 +7,6 @@ You can compile and run this example on Linux with:
    cargo build --release -p wasmtime-c-api
    cc examples/interrupt.c \
        -I crates/c-api/include \
-       -I crates/c-api/wasm-c-api/include \
        target/release/libwasmtime.a \
        -lpthread -ldl -lm \
        -o interrupt
diff --git a/examples/linking.c b/examples/linking.c
index d048451c2..d9741ab97 100644
--- a/examples/linking.c
+++ b/examples/linking.c
@@ -7,7 +7,6 @@ You can compile and run this example on Linux with:
    cargo build --release -p wasmtime-c-api
    cc examples/linking.c \
        -I crates/c-api/include \
-       -I crates/c-api/wasm-c-api/include \
        target/release/libwasmtime.a \
        -lpthread -ldl -lm \
        -o linking
diff --git a/examples/memory.c b/examples/memory.c
index 3f4bf311e..2edca2198 100644
--- a/examples/memory.c
+++ b/examples/memory.c
@@ -7,7 +7,6 @@ You can compile and run this example on Linux with:
    cargo build --release -p wasmtime-c-api
    cc examples/memory.c \
        -I crates/c-api/include \
-       -I crates/c-api/wasm-c-api/include \
        target/release/libwasmtime.a \
        -lpthread -ldl -lm \
        -o memory
diff --git a/examples/multi.c b/examples/multi.c
index 6edcdd18b..af1e61157 100644
--- a/examples/multi.c
+++ b/examples/multi.c
@@ -7,7 +7,6 @@ You can compile and run this example on Linux with:
    cargo build --release -p wasmtime-c-api
    cc examples/multi.c \
        -I crates/c-api/include \
-       -I crates/c-api/wasm-c-api/include \
        target/release/libwasmtime.a \
        -lpthread -ldl -lm \
        -o multi
diff --git a/examples/multimemory.c b/examples/multimemory.c
index 88f1f5752..8fa4973e7 100644
--- a/examples/multimemory.c
+++ b/examples/multimemory.c
@@ -6,7 +6,6 @@ You can compile and run this example on Linux with:
    cargo build --release -p wasmtime-c-api
    cc examples/multimemory.c \
        -I crates/c-api/include \
-       -I crates/c-api/wasm-c-api/include \
        target/release/libwasmtime.a \
        -lpthread -ldl -lm \
        -o multimemory
diff --git a/examples/serialize.c b/examples/serialize.c
index 1d3acc7b3..65fb7263a 100644
--- a/examples/serialize.c
+++ b/examples/serialize.c
@@ -7,7 +7,6 @@ You can compile and run this example on Linux with:
    cargo build --release -p wasmtime-c-api
    cc examples/hello.c \
        -I crates/c-api/include \
-       -I crates/c-api/wasm-c-api/include \
        target/release/libwasmtime.a \
        -lpthread -ldl -lm \
        -o hello
diff --git a/examples/threads.c b/examples/threads.c
index 7e13b9f3a..d89480db6 100644
--- a/examples/threads.c
+++ b/examples/threads.c
@@ -7,7 +7,6 @@ You can compile and run this example on Linux with:
    cargo build --release -p wasmtime-c-api
    cc examples/threads.c \
        -I crates/c-api/include \
-       -I crates/c-api/wasm-c-api/include \
        target/release/libwasmtime.a \
        -lpthread -ldl -lm \
        -o threads
diff --git a/examples/wasi/main.c b/examples/wasi/main.c
index 753d66497..520259d53 100644
--- a/examples/wasi/main.c
+++ b/examples/wasi/main.c
@@ -7,7 +7,6 @@ You can compile and run this example on Linux with:
    cargo build --release -p wasmtime-c-api
    cc examples/wasi/main.c \
        -I crates/c-api/include \
-       -I crates/c-api/wasm-c-api/include \
        target/release/libwasmtime.a \
        -lpthread -ldl -lm \
        -o wasi
diff --git a/src/commands/run.rs b/src/commands/run.rs
index 8f25ba91e..10c97649a 100644
--- a/src/commands/run.rs
+++ b/src/commands/run.rs
@@ -740,6 +740,11 @@ impl RunCommand {
         let mut builder = WasiCtxBuilder::new();
         builder.inherit_stdio().args(&self.compute_argv()?)?;
 
+        if self.run.common.wasi.inherit_env == Some(true) {
+            for (k, v) in std::env::vars() {
+                builder.env(&k, &v)?;
+            }
+        }
         for (key, value) in self.vars.iter() {
             let value = match value {
                 Some(value) => value.clone(),
@@ -779,6 +784,11 @@ impl RunCommand {
         let mut builder = wasmtime_wasi::WasiCtxBuilder::new();
         builder.inherit_stdio().args(&self.compute_argv()?);
 
+        if self.run.common.wasi.inherit_env == Some(true) {
+            for (k, v) in std::env::vars() {
+                builder.env(&k, &v);
+            }
+        }
         for (key, value) in self.vars.iter() {
             let value = match value {
                 Some(value) => value.clone(),


Test patch:
diff --git a/tests/all/cli_tests.rs b/tests/all/cli_tests.rs
index 8fab74ac6..8fa270135 100644
--- a/tests/all/cli_tests.rs
+++ b/tests/all/cli_tests.rs
@@ -521,6 +521,15 @@ fn specify_env() -> Result<()> {
         .output()?;
     assert!(output.status.success());
 
+    // Inherit all env vars
+    let output = get_wasmtime_command()?
+        .args(&["run", "-Sinherit-env", "tests/all/cli_tests/print_env.wat"])
+        .env("FOO", "bar")
+        .output()?;
+    assert!(output.status.success());
+    let stdout = String::from_utf8_lossy(&output.stdout);
+    assert!(stdout.contains("FOO=bar"), "bad output: {stdout}");
+
     Ok(())
 }
 
diff --git a/tests/disas/typed-funcrefs.wat b/tests/disas/typed-funcrefs.wat
index 2259d7f3b..0e69d7373 100644
--- a/tests/disas/typed-funcrefs.wat
+++ b/tests/disas/typed-funcrefs.wat
@@ -14,9 +14,9 @@
   (type $ic-stub (func (param i32 i32 i32 i32) (result i32)))
 
   ;; This syntax declares a table that is exactly 100 elements, whose
-  ;; elements are non-nullable function references, and whose default
-  ;; value (needed because non-nullable) is a pointer to `$ic1`.
-  (table $ic-sites 100 100 (ref $ic-stub) (ref.func $ic1))
+  ;; elements are nullable function references, and whose default
+  ;; value is `null`.
+  (table $ic-sites 100 100 (ref null $ic-stub))
 
   (func $ic1 (param i32 i32 i32 i32) (result i32)
         local.get 0)
@@ -59,10 +59,10 @@
 ;;
 ;;                                 block0(v0: i64, v1: i64, v2: i32, v3: i32, v4: i32, v5: i32):
 ;;                                     v6 -> v2
-;; @002c                               jump block1
+;; @0027                               jump block1
 ;;
 ;;                                 block1:
-;; @002c                               return v2
+;; @0027                               return v2
 ;; }
 ;;
 ;; function u0:1(i64 vmctx, i64, i32, i32, i32, i32) -> i32 fast {
@@ -87,80 +87,80 @@
 ;;                                     v32 -> v4
 ;;                                     v33 -> v5
 ;;                                     v62 = iconst.i8 0
-;; @003b                               brif v62, block6, block7  ; v62 = 0
+;; @0036                               brif v62, block6, block7  ; v62 = 0
 ;;
 ;;                                 block6 cold:
-;; @003b                               trap table_oob
+;; @0036                               trap table_oob
 ;;
 ;;                                 block7:
-;; @003b                               v12 = load.i64 notrap aligned v0+72
+;; @0036                               v12 = load.i64 notrap aligned v0+72
 ;;                                     v79 = iconst.i8 0
 ;;                                     v70 = iconst.i64 8
-;; @003b                               v14 = iadd v12, v70  ; v70 = 8
-;; @003b                               v16 = select_spectre_guard v79, v12, v14  ; v79 = 0
-;; @003b                               v17 = load.i64 notrap aligned table v16
+;; @0036                               v14 = iadd v12, v70  ; v70 = 8
+;; @0036                               v16 = select_spectre_guard v79, v12, v14  ; v79 = 0
+;; @0036                               v17 = load.i64 notrap aligned table v16
 ;;                                     v58 = iconst.i64 -2
-;; @003b                               v18 = band v17, v58  ; v58 = -2
-;; @003b                               brif v17, block3(v18), block2
+;; @0036                               v18 = band v17, v58  ; v58 = -2
+;; @0036                               brif v17, block3(v18), block2
 ;;
 ;;                                 block2 cold:
-;; @004e                               v48 = load.i64 notrap aligned readonly v0+56
-;; @004e                               v49 = load.i64 notrap aligned readonly v48+72
-;; @002f                               v7 = iconst.i32 0
+;; @0049                               v48 = load.i64 notrap aligned readonly v0+56
+;; @0049                               v49 = load.i64 notrap aligned readonly v48+72
+;; @002a                               v7 = iconst.i32 0
 ;;                                     v28 -> v7
-;; @0039                               v8 = iconst.i32 1
-;; @003b                               v24 = call_indirect sig0, v49(v0, v7, v8)  ; v7 = 0, v8 = 1
-;; @003b                               jump block3(v24)
+;; @0034                               v8 = iconst.i32 1
+;; @0036                               v24 = call_indirect sig0, v49(v0, v7, v8)  ; v7 = 0, v8 = 1
+;; @0036                               jump block3(v24)
 ;;
 ;;                                 block3(v19: i64):
-;; @003d                               brif v19, block9, block8
+;; @0038                               brif v19, block9, block8
 ;;
 ;;                                 block8 cold:
-;; @003d                               trap null_reference
+;; @0038                               trap null_reference
 ;;
 ;;                                 block9:
-;; @003d                               v25 = load.i64 notrap aligned readonly v19+16
-;; @003d                               v26 = load.i64 notrap aligned readonly v19+32
-;; @003d                               v27 = call_indirect sig1, v25(v26, v0, v2, v3, v4, v5)
+;; @0038                               v25 = load.i64 notrap aligned readonly v19+16
+;; @0038                               v26 = load.i64 notrap aligned readonly v19+32
+;; @0038                               v27 = call_indirect sig1, v25(v26, v0, v2, v3, v4, v5)
 ;;                                     v80 = iconst.i8 0
-;; @004e                               brif v80, block10, block11  ; v80 = 0
+;; @0049                               brif v80, block10, block11  ; v80 = 0
 ;;
 ;;                                 block10 cold:
-;; @004e                               trap table_oob
+;; @0049                               trap table_oob
 ;;
 ;;                                 block11:
-;; @004e                               v38 = load.i64 notrap aligned v0+72
+;; @0049                               v38 = load.i64 notrap aligned v0+72
 ;;                                     v81 = iconst.i8 0
 ;;                                     v78 = iconst.i64 16
-;; @004e                               v40 = iadd v38, v78  ; v78 = 16
-;; @004e                               v42 = select_spectre_guard v81, v38, v40  ; v81 = 0
-;; @004e                               v43 = load.i64 notrap aligned table v42
+;; @0049                               v40 = iadd v38, v78  ; v78 = 16
+;; @0049                               v42 = select_spectre_guard v81, v38, v40  ; v81 = 0
+;; @0049                               v43 = load.i64 notrap aligned table v42
 ;;                                     v82 = iconst.i64 -2
 ;;                                     v83 = band v43, v82  ; v82 = -2
-;; @004e                               brif v43, block5(v83), block4
+;; @0049                               brif v43, block5(v83), block4
 ;;
 ;;                                 block4 cold:
 ;;                                     v84 = load.i64 notrap aligned readonly v0+56
 ;;                                     v85 = load.i64 notrap aligned readonly v84+72
 ;;                                     v86 = iconst.i32 0
-;; @004c                               v34 = iconst.i32 2
-;; @004e                               v50 = call_indirect sig0, v85(v0, v86, v34)  ; v86 = 0, v34 = 2
-;; @004e                               jump block5(v50)
+;; @0047                               v34 = iconst.i32 2
+;; @0049                               v50 = call_indirect sig0, v85(v0, v86, v34)  ; v86 = 0, v34 = 2
+;; @0049                               jump block5(v50)
 ;;
 ;;                                 block5(v45: i64):
-;; @0050                               brif v45, block13, block12
+;; @004b                               brif v45, block13, block12
 ;;
 ;;                                 block12 cold:
-;; @0050                               trap null_reference
+;; @004b                               trap null_reference
 ;;
 ;;                                 block13:
-;; @0050                               v51 = load.i64 notrap aligned readonly v45+16
-;; @0050                               v52 = load.i64 notrap aligned readonly v45+32
-;; @0050                               v53 = call_indirect sig1, v51(v52, v0, v2, v3, v4, v5)
-;; @0059                               jump block1
+;; @004b                               v51 = load.i64 notrap aligned readonly v45+16
+;; @004b                               v52 = load.i64 notrap aligned readonly v45+32
+;; @004b                               v53 = call_indirect sig1, v51(v52, v0, v2, v3, v4, v5)
+;; @0054                               jump block1
 ;;
 ;;                                 block1:
-;; @0054                               v55 = iadd.i32 v53, v27
+;; @004f                               v55 = iadd.i32 v53, v27
 ;;                                     v6 -> v55
-;; @0059                               return v55
+;; @0054                               return v55
 ;; }
diff --git a/tests/spec_testsuite b/tests/spec_testsuite
index 5a1a59060..16a839d56 160000
--- a/tests/spec_testsuite
+++ b/tests/spec_testsuite
@@ -1,1 +1,1 @@
-Subproject commit 5a1a590603d81f40ef471abba70a90a9ae5f4627
+Subproject commit 16a839d5601c283541a84572b47637f035b51437