Repository: bytecodealliance/wasmtime
Pull request #8484 (2024-04-26 00:02:19)

Gate type-builder types from `wasmtime-environ` on `compile`
This commit gates the `*Builder` types related to building sets of types in the `wasmtime-environ` crate on the `compile` feature. This helps bring in less code when the feature is disabled and helps exclude some dependencies for the upcoming `no_std` migration as well.

This commit doesn't change anything, it's just moving code around.

<!--
Please make sure you include the following information:

- If this work has been discussed elsewhere, please include a link to that
  conversation. If it was discussed in an issue, just mention "issue #...".

- Explain why this change is needed. If the details are in an issue already,
  this can be brief.

Our development process is documented in the Wasmtime book:
https://docs.wasmtime.dev/contributing-development-process.html

Please ensure all communication follows the code of conduct:
https://github.com/bytecodealliance/wasmtime/blob/main/CODE_OF_CONDUCT.md
-->



Patch:
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index f1a352437..f2b5a0c93 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -372,6 +372,9 @@ jobs:
     - run: cargo check -p wasmtime-asm-macros
       env:
         CARGO_BUILD_TARGET: x86_64-unknown-none
+    - run: cargo check -p wasmtime-slab
+      env:
+        CARGO_BUILD_TARGET: x86_64-unknown-none
 
     # Check that wasmtime-runtime compiles with panic=abort since there's some
     # #[cfg] for specifically panic=abort there.
@@ -508,7 +511,7 @@ jobs:
     strategy:
       fail-fast: true
       matrix:
-        os: [ubuntu-latest, macos-latest, macos-14, windows-latest]
+        os: [ubuntu-latest, macos-latest, windows-latest]
 
     steps:
     - uses: actions/checkout@v4
diff --git a/Cargo.lock b/Cargo.lock
index f2b0fb461..639755bbc 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -876,7 +876,6 @@ dependencies = [
  "num-traits",
  "once_cell",
  "oorandom",
- "plotters",
  "rayon",
  "regex",
  "serde",
@@ -2065,34 +2064,6 @@ version = "0.3.29"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2900ede94e305130c13ddd391e0ab7cbaeb783945ae07a279c268cb05109c6cb"
 
-[[package]]
-name = "plotters"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32a3fd9ec30b9749ce28cd91f255d569591cdf937fe280c312143e3c4bad6f2a"
-dependencies = [
- "num-traits",
- "plotters-backend",
- "plotters-svg",
- "wasm-bindgen",
- "web-sys",
-]
-
-[[package]]
-name = "plotters-backend"
-version = "0.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d88417318da0eaf0fdcdb51a0ee6c3bed624333bff8f946733049380be67ac1c"
-
-[[package]]
-name = "plotters-svg"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "521fa9638fa597e1dc53e9412a4f9cefb01187ee1f7413076f9e6749e2885ba9"
-dependencies = [
- "plotters-backend",
-]
-
 [[package]]
 name = "postcard"
 version = "1.0.8"
@@ -4025,16 +3996,6 @@ dependencies = [
  "wast 206.0.0",
 ]
 
-[[package]]
-name = "web-sys"
-version = "0.3.57"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7b17e741662c70c8bd24ac5c5b18de314a2c26c32bf8346ee1e6f53de919c283"
-dependencies = [
- "js-sys",
- "wasm-bindgen",
-]
-
 [[package]]
 name = "webpki-roots"
 version = "0.26.1"
diff --git a/Cargo.toml b/Cargo.toml
index 269e071f6..118c26787 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -73,7 +73,7 @@ tempfile = { workspace = true }
 wasmtime-runtime = { workspace = true }
 tokio = { workspace = true, features = ["rt", "time", "macros", "rt-multi-thread"] }
 wast = { workspace = true }
-criterion = "0.5.0"
+criterion = { workspace = true }
 num_cpus = "1.13.0"
 memchr = "2.4"
 async-trait = { workspace = true }
@@ -291,6 +291,7 @@ tracing-subscriber = { version = "0.3.1", default-features = false, features = [
 url = "2.3.1"
 humantime = "2.0.0"
 postcard = { version = "1.0.8", default-features = false, features = ['alloc'] }
+criterion = { version = "0.5.0", default-features = false, features = ["html_reports", "rayon"] }
 
 # =============================================================================
 #
diff --git a/ci/build-build-matrix.js b/ci/build-build-matrix.js
index b51be1801..9b67ef5a9 100644
--- a/ci/build-build-matrix.js
+++ b/ci/build-build-matrix.js
@@ -36,7 +36,7 @@ const array = [
   },
   {
     "build": "aarch64-macos",
-    "os": "macos-14",
+    "os": "macos-latest",
     "target": "aarch64-apple-darwin",
   },
   {
diff --git a/cranelift/codegen/Cargo.toml b/cranelift/codegen/Cargo.toml
index 8c30b4011..f6d67951d 100644
--- a/cranelift/codegen/Cargo.toml
+++ b/cranelift/codegen/Cargo.toml
@@ -40,7 +40,7 @@ sha2 = { version = "0.10.2", optional = true }
 # accomodated in `tests`.
 
 [dev-dependencies]
-criterion = { version = "0.5.0", features = ["html_reports"] }
+criterion = { workspace = true }
 similar = "2.1.0"
 
 [build-dependencies]
diff --git a/cranelift/codegen/src/machinst/vcode.rs b/cranelift/codegen/src/machinst/vcode.rs
index 381556da9..53cc6e7ea 100644
--- a/cranelift/codegen/src/machinst/vcode.rs
+++ b/cranelift/codegen/src/machinst/vcode.rs
@@ -509,6 +509,7 @@ impl<I: VCodeInst> VCodeBuilder<I> {
     }
 
     fn collect_operands(&mut self) {
+        let allocatable = PRegSet::from(self.vcode.machine_env());
         for (i, insn) in self.vcode.insts.iter().enumerate() {
             // Push operands from the instruction onto the operand list.
             //
@@ -522,7 +523,6 @@ impl<I: VCodeInst> VCodeBuilder<I> {
             // its register fields (which is slow, branchy code) once.
 
             let vreg_aliases = &self.vcode.vreg_aliases;
-            let allocatable = PRegSet::from(self.vcode.machine_env());
             let mut op_collector =
                 OperandCollector::new(&mut self.vcode.operands, allocatable, |vreg| {
                     VCode::<I>::resolve_vreg_alias_impl(vreg_aliases, vreg)
diff --git a/crates/c-api/CMakeLists.txt b/crates/c-api/CMakeLists.txt
index 2075dd2bd..432df8185 100644
--- a/crates/c-api/CMakeLists.txt
+++ b/crates/c-api/CMakeLists.txt
@@ -1,4 +1,5 @@
 cmake_minimum_required(VERSION 3.10)
+project(wasmtime C)
 
 set(WASMTIME_USER_CARGO_BUILD_OPTIONS "" CACHE STRING "Additional cargo flags (such as --features) to apply to the build command")
 option(BUILD_SHARED_LIBS "Build using shared libraries" OFF)
@@ -83,6 +84,7 @@ ExternalProject_Add(
 	CONFIGURE_COMMAND ""
 	INSTALL_COMMAND "${WASMTIME_INSTALL_COMMAND}"
         BUILD_COMMAND ${WASMTIME_PREBUILD_COMMAND} ${WASMTIME_CARGO_BINARY} build ${WASMTIME_BUILD_TYPE_FLAG} ${WASMTIME_USER_CARGO_BUILD_OPTIONS} ${WASMTIME_BUILD_TARGET}
+	USES_TERMINAL_BUILD TRUE
 	BINARY_DIR ${CMAKE_CURRENT_SOURCE_DIR}/artifact
         BUILD_ALWAYS ${WASMTIME_ALWAYS_BUILD}
 	BUILD_BYPRODUCTS ${WASMTIME_BUILD_PRODUCT})
diff --git a/crates/environ/src/compile/mod.rs b/crates/environ/src/compile/mod.rs
index a88c0f32b..c8b1343ce 100644
--- a/crates/environ/src/compile/mod.rs
+++ b/crates/environ/src/compile/mod.rs
@@ -3,9 +3,8 @@
 
 use crate::{obj, Tunables};
 use crate::{
-    BuiltinFunctionIndex, DefinedFuncIndex, FlagValue, FuncIndex, FunctionBodyData, FunctionLoc,
-    ModuleTranslation, ModuleTypesBuilder, ObjectKind, PrimaryMap, WasmError, WasmFuncType,
-    WasmFunctionInfo,
+    BuiltinFunctionIndex, DefinedFuncIndex, FlagValue, FuncIndex, FunctionLoc, ObjectKind,
+    PrimaryMap, WasmError, WasmFuncType, WasmFunctionInfo,
 };
 use anyhow::Result;
 use object::write::{Object, SymbolId};
@@ -19,10 +18,14 @@ use thiserror::Error;
 
 mod address_map;
 mod module_artifacts;
+mod module_environ;
+mod module_types;
 mod trap_encoding;
 
 pub use self::address_map::*;
 pub use self::module_artifacts::*;
+pub use self::module_environ::*;
+pub use self::module_types::*;
 pub use self::trap_encoding::*;
 
 /// An error while compiling WebAssembly to machine code.
diff --git a/crates/environ/src/module_environ.rs b/crates/environ/src/compile/module_environ.rs
similarity index 100%
rename from crates/environ/src/module_environ.rs
rename to crates/environ/src/compile/module_environ.rs
diff --git a/crates/environ/src/compile/module_types.rs b/crates/environ/src/compile/module_types.rs
new file mode 100644
index 000000000..c41e88da3
--- /dev/null
+++ b/crates/environ/src/compile/module_types.rs
@@ -0,0 +1,292 @@
+use crate::{Module, ModuleTypes, TypeConvert, TypeIndex, WasmHeapType};
+use cranelift_entity::EntityRef;
+use std::{collections::HashMap, ops::Index};
+use wasmparser::{UnpackedIndex, Validator, ValidatorId};
+use wasmtime_types::{
+    EngineOrModuleTypeIndex, ModuleInternedRecGroupIndex, ModuleInternedTypeIndex, WasmResult,
+    WasmSubType,
+};
+
+/// A type marking the start of a recursion group's definition.
+///
+/// This is initialized by `ModuleTypesBuilder::start_rec_group` and then
+/// finished in `ModuleTypes::end_rec_group` after all of the types in the rec
+/// group have been defined.
+struct RecGroupStart {
+    rec_group_index: ModuleInternedRecGroupIndex,
+    start: ModuleInternedTypeIndex,
+    end: ModuleInternedTypeIndex,
+}
+
+/// A builder for [`ModuleTypes`].
+pub struct ModuleTypesBuilder {
+    /// The ID of the validator that this builder is configured for. Using a
+    /// different validator, or multiple validators, with this builder would
+    /// result in silliness because our `wasmparser::types::*Id`s are only
+    /// unique within the context of a particular validator. Getting this wrong
+    /// could result in generating calls to functions of the wrong type, for
+    /// example. So therefore we always assert that a builder instances is only
+    /// ever paired with a particular validator context.
+    validator_id: ValidatorId,
+
+    /// The canonicalized and deduplicated set of types we are building.
+    types: ModuleTypes,
+
+    /// A map from already-interned `wasmparser` types to their corresponding
+    /// Wasmtime type.
+    wasmparser_to_wasmtime: HashMap<wasmparser::types::CoreTypeId, ModuleInternedTypeIndex>,
+
+    /// The set of recursion groups we have already seen and interned.
+    already_seen: HashMap<wasmparser::types::RecGroupId, ModuleInternedRecGroupIndex>,
+
+    /// If we are in the middle of defining a recursion group, this is the
+    /// metadata about the recursion group we started defining.
+    defining_rec_group: Option<RecGroupStart>,
+}
+
+impl ModuleTypesBuilder {
+    /// Construct a new `ModuleTypesBuilder` using the given validator.
+    pub fn new(validator: &Validator) -> Self {
+        Self {
+            validator_id: validator.id(),
+            types: ModuleTypes::default(),
+            wasmparser_to_wasmtime: HashMap::default(),
+            already_seen: HashMap::default(),
+            defining_rec_group: None,
+        }
+    }
+
+    /// Reserves space for `amt` more type signatures.
+    pub fn reserve_wasm_signatures(&mut self, amt: usize) {
+        self.types.reserve(amt);
+        self.wasmparser_to_wasmtime.reserve(amt);
+        self.already_seen.reserve(amt);
+    }
+
+    /// Get the id of the validator that this builder is configured for.
+    pub fn validator_id(&self) -> ValidatorId {
+        self.validator_id
+    }
+
+    /// Intern a recursion group and all of its types into this builder.
+    ///
+    /// If the recursion group has already been interned, then it is reused.
+    ///
+    /// Panics if given types from a different validator than the one that this
+    /// builder is associated with.
+    pub fn intern_rec_group(
+        &mut self,
+        module: &Module,
+        validator_types: wasmparser::types::TypesRef<'_>,
+        rec_group_id: wasmparser::types::RecGroupId,
+    ) -> WasmResult<ModuleInternedRecGroupIndex> {
+        assert_eq!(validator_types.id(), self.validator_id);
+
+        if let Some(interned) = self.already_seen.get(&rec_group_id) {
+            return Ok(*interned);
+        }
+
+        self.define_new_rec_group(module, validator_types, rec_group_id)
+    }
+
+    /// Define a new recursion group that we haven't already interned.
+    fn define_new_rec_group(
+        &mut self,
+        module: &Module,
+        validator_types: wasmparser::types::TypesRef<'_>,
+        rec_group_id: wasmparser::types::RecGroupId,
+    ) -> WasmResult<ModuleInternedRecGroupIndex> {
+        assert_eq!(validator_types.id(), self.validator_id);
+
+        self.start_rec_group(
+            validator_types,
+            validator_types.rec_group_elements(rec_group_id),
+        );
+
+        for id in validator_types.rec_group_elements(rec_group_id) {
+            let ty = &validator_types[id];
+            let wasm_ty = WasmparserTypeConverter::new(self, module).convert_sub_type(ty)?;
+            self.wasm_sub_type_in_rec_group(id, wasm_ty);
+        }
+
+        Ok(self.end_rec_group(rec_group_id))
+    }
+
+    /// Start defining a recursion group.
+    fn start_rec_group(
+        &mut self,
+        validator_types: wasmparser::types::TypesRef<'_>,
+        elems: impl ExactSizeIterator<Item = wasmparser::types::CoreTypeId>,
+    ) {
+        log::trace!("Starting rec group of length {}", elems.len());
+
+        assert!(self.defining_rec_group.is_none());
+        assert_eq!(validator_types.id(), self.validator_id);
+
+        // Eagerly define the reverse map's entries for this rec group's types
+        // so that we can use them when converting `wasmparser` types to our
+        // types.
+        let len = elems.len();
+        for (i, wasmparser_id) in elems.enumerate() {
+            let interned = ModuleInternedTypeIndex::new(self.types.len_types() + i);
+            log::trace!(
+                "Reserving {interned:?} for {wasmparser_id:?} = {:?}",
+                validator_types[wasmparser_id]
+            );
+
+            let old_entry = self.wasmparser_to_wasmtime.insert(wasmparser_id, interned);
+            debug_assert_eq!(
+                old_entry, None,
+                "should not have already inserted {wasmparser_id:?}"
+            );
+        }
+
+        self.defining_rec_group = Some(RecGroupStart {
+            rec_group_index: self.types.next_rec_group(),
+            start: self.types.next_ty(),
+            end: ModuleInternedTypeIndex::new(self.types.len_types() + len),
+        });
+    }
+
+    /// Finish defining a recursion group.
+    fn end_rec_group(
+        &mut self,
+        rec_group_id: wasmparser::types::RecGroupId,
+    ) -> ModuleInternedRecGroupIndex {
+        let RecGroupStart {
+            rec_group_index,
+            start,
+            end,
+        } = self
+            .defining_rec_group
+            .take()
+            .expect("should be defining a rec group");
+
+        log::trace!("Ending rec group {start:?}..{end:?}");
+
+        debug_assert!(start.index() < self.types.len_types());
+        debug_assert_eq!(
+            end,
+            self.types.next_ty(),
+            "should have defined the number of types declared in `start_rec_group`"
+        );
+
+        let idx = self.types.push_rec_group(start..end);
+        debug_assert_eq!(idx, rec_group_index);
+
+        self.already_seen.insert(rec_group_id, rec_group_index);
+        rec_group_index
+    }
+
+    /// Intern a type into this builder and get its Wasmtime index.
+    ///
+    /// This will intern not only the single given type, but the type's entire
+    /// rec group. This helper method is provided as a convenience so that
+    /// callers don't have to get the type's rec group, intern the rec group,
+    /// and then look up the Wasmtime index for the original type themselves.
+    pub fn intern_type(
+        &mut self,
+        module: &Module,
+        validator_types: wasmparser::types::TypesRef<'_>,
+        id: wasmparser::types::CoreTypeId,
+    ) -> WasmResult<ModuleInternedTypeIndex> {
+        assert!(self.defining_rec_group.is_none());
+        assert_eq!(validator_types.id(), self.validator_id);
+
+        let rec_group_id = validator_types.rec_group_id_of(id);
+        debug_assert!(validator_types
+            .rec_group_elements(rec_group_id)
+            .any(|e| e == id));
+
+        let interned_rec_group = self.intern_rec_group(module, validator_types, rec_group_id)?;
+
+        let interned_type = self.wasmparser_to_wasmtime[&id];
+        debug_assert!(self
+            .rec_group_elements(interned_rec_group)
+            .any(|e| e == interned_type));
+
+        Ok(interned_type)
+    }
+
+    /// Define a new Wasm type while we are defining a rec group.
+    fn wasm_sub_type_in_rec_group(
+        &mut self,
+        id: wasmparser::types::CoreTypeId,
+        ty: WasmSubType,
+    ) -> ModuleInternedTypeIndex {
+        assert!(
+            self.defining_rec_group.is_some(),
+            "must be defining a rec group to define new types"
+        );
+
+        let module_interned_index = self.types.push(ty);
+        debug_assert_eq!(
+            self.wasmparser_to_wasmtime.get(&id),
+            Some(&module_interned_index),
+            "should have reserved the right module-interned index for this wasmparser type already"
+        );
+
+        module_interned_index
+    }
+
+    /// Returns the result [`ModuleTypes`] of this builder.
+    pub fn finish(self) -> ModuleTypes {
+        self.types
+    }
+
+    /// Get the elements within an already-defined rec group.
+    pub fn rec_group_elements(
+        &self,
+        rec_group: ModuleInternedRecGroupIndex,
+    ) -> impl ExactSizeIterator<Item = ModuleInternedTypeIndex> {
+        self.types.rec_group_elements(rec_group)
+    }
+
+    /// Returns an iterator over all the unique wasm types defined thus far
+    /// within this builder.
+    pub fn wasm_types(&self) -> impl Iterator<Item = (ModuleInternedTypeIndex, &WasmSubType)> {
+        self.types.wasm_types()
+    }
+}
+
+// Forward the indexing impl to the internal `ModuleTypes`
+impl<T> Index<T> for ModuleTypesBuilder
+where
+    ModuleTypes: Index<T>,
+{
+    type Output = <ModuleTypes as Index<T>>::Output;
+
+    fn index(&self, sig: T) -> &Self::Output {
+        &self.types[sig]
+    }
+}
+
+/// A convert from `wasmparser` types to Wasmtime types.
+pub struct WasmparserTypeConverter<'a> {
+    types: &'a ModuleTypesBuilder,
+    module: &'a Module,
+}
+
+impl<'a> WasmparserTypeConverter<'a> {
+    /// Construct a new type converter from `wasmparser` types to Wasmtime types.
+    pub fn new(types: &'a ModuleTypesBuilder, module: &'a Module) -> Self {
+        Self { types, module }
+    }
+}
+
+impl TypeConvert for WasmparserTypeConverter<'_> {
+    fn lookup_heap_type(&self, index: UnpackedIndex) -> WasmHeapType {
+        match index {
+            UnpackedIndex::Id(id) => {
+                let signature = self.types.wasmparser_to_wasmtime[&id];
+                WasmHeapType::ConcreteFunc(EngineOrModuleTypeIndex::Module(signature))
+            }
+            UnpackedIndex::Module(module_index) => {
+                let module_index = TypeIndex::from_u32(module_index);
+                let interned_index = self.module.types[module_index];
+                WasmHeapType::ConcreteFunc(EngineOrModuleTypeIndex::Module(interned_index))
+            }
+            UnpackedIndex::RecGroup(_) => unreachable!(),
+        }
+    }
+}
diff --git a/crates/environ/src/component.rs b/crates/environ/src/component.rs
index 67dce3681..96846182b 100644
--- a/crates/environ/src/component.rs
+++ b/crates/environ/src/component.rs
@@ -53,9 +53,13 @@ pub mod dfg;
 #[cfg(feature = "compile")]
 mod translate;
 #[cfg(feature = "compile")]
+mod types_builder;
+#[cfg(feature = "compile")]
 pub use self::compiler::*;
 #[cfg(feature = "compile")]
 pub use self::translate::*;
+#[cfg(feature = "compile")]
+pub use self::types_builder::*;
 
 /// Helper macro to iterate over the transcoders that the host will provide
 /// adapter modules through libcalls.
diff --git a/crates/environ/src/component/types.rs b/crates/environ/src/component/types.rs
index 45f352ac7..5b4b1b284 100644
--- a/crates/environ/src/component/types.rs
+++ b/crates/environ/src/component/types.rs
@@ -1,34 +1,15 @@
-use crate::component::{Export, MAX_FLAT_PARAMS, MAX_FLAT_RESULTS};
-use crate::{
-    CompiledModuleInfo, EntityType, Module, ModuleTypes, ModuleTypesBuilder, PrimaryMap,
-    TypeConvert, WasmHeapType, WasmValType,
-};
-use anyhow::{bail, Result};
-use cranelift_entity::EntityRef;
+use crate::component::{MAX_FLAT_PARAMS, MAX_FLAT_RESULTS};
+use crate::{EntityType, ModuleTypes, PrimaryMap};
 use indexmap::{IndexMap, IndexSet};
 use serde_derive::{Deserialize, Serialize};
-use std::collections::HashMap;
 use std::hash::{Hash, Hasher};
 use std::ops::Index;
-use wasmparser::names::KebabString;
-use wasmparser::{types, Validator};
+use wasmparser::types;
 use wasmtime_component_util::{DiscriminantSize, FlagsSize};
 use wasmtime_types::ModuleInternedTypeIndex;
 
 pub use wasmtime_types::StaticModuleIndex;
 
-mod resources;
-pub use resources::ResourcesBuilder;
-
-/// Maximum nesting depth of a type allowed in Wasmtime.
-///
-/// This constant isn't chosen via any scientific means and its main purpose is
-/// to enable most of Wasmtime to handle types via recursion without worrying
-/// about stack overflow.
-///
-/// Some more information about this can be found in #4814
-const MAX_TYPE_DEPTH: u32 = 100;
-
 macro_rules! indices {
     ($(
         $(#[$a:meta])*
@@ -241,21 +222,21 @@ pub enum ComponentItem {
 /// will have a pointer to this value as well.
 #[derive(Default, Serialize, Deserialize)]
 pub struct ComponentTypes {
-    modules: PrimaryMap<TypeModuleIndex, TypeModule>,
-    components: PrimaryMap<TypeComponentIndex, TypeComponent>,
-    component_instances: PrimaryMap<TypeComponentInstanceIndex, TypeComponentInstance>,
-    functions: PrimaryMap<TypeFuncIndex, TypeFunc>,
-    lists: PrimaryMap<TypeListIndex, TypeList>,
-    records: PrimaryMap<TypeRecordIndex, TypeRecord>,
-    variants: PrimaryMap<TypeVariantIndex, TypeVariant>,
-    tuples: PrimaryMap<TypeTupleIndex, TypeTuple>,
-    enums: PrimaryMap<TypeEnumIndex, TypeEnum>,
-    flags: PrimaryMap<TypeFlagsIndex, TypeFlags>,
-    options: PrimaryMap<TypeOptionIndex, TypeOption>,
-    results: PrimaryMap<TypeResultIndex, TypeResult>,
-    resource_tables: PrimaryMap<TypeResourceTableIndex, TypeResourceTable>,
-
-    module_types: Option<ModuleTypes>,
+    pub(super) modules: PrimaryMap<TypeModuleIndex, TypeModule>,
+    pub(super) components: PrimaryMap<TypeComponentIndex, TypeComponent>,
+    pub(super) component_instances: PrimaryMap<TypeComponentInstanceIndex, TypeComponentInstance>,
+    pub(super) functions: PrimaryMap<TypeFuncIndex, TypeFunc>,
+    pub(super) lists: PrimaryMap<TypeListIndex, TypeList>,
+    pub(super) records: PrimaryMap<TypeRecordIndex, TypeRecord>,
+    pub(super) variants: PrimaryMap<TypeVariantIndex, TypeVariant>,
+    pub(super) tuples: PrimaryMap<TypeTupleIndex, TypeTuple>,
+    pub(super) enums: PrimaryMap<TypeEnumIndex, TypeEnum>,
+    pub(super) flags: PrimaryMap<TypeFlagsIndex, TypeFlags>,
+    pub(super) options: PrimaryMap<TypeOptionIndex, TypeOption>,
+    pub(super) results: PrimaryMap<TypeResultIndex, TypeResult>,
+    pub(super) resource_tables: PrimaryMap<TypeResourceTableIndex, TypeResourceTable>,
+
+    pub(super) module_types: Option<ModuleTypes>,
 }
 
 impl ComponentTypes {
@@ -295,6 +276,11 @@ impl ComponentTypes {
             InterfaceType::Result(i) => &self[*i].abi,
         }
     }
+
+    /// Adds a new `table` to the list of resource tables for this component.
+    pub fn push_resource_table(&mut self, table: TypeResourceTable) -> TypeResourceTableIndex {
+        self.resource_tables.push(table)
+    }
 }
 
 macro_rules! impl_index {
@@ -307,11 +293,12 @@ macro_rules! impl_index {
             }
         }
 
-        impl std::ops::Index<$ty> for ComponentTypesBuilder {
+        #[cfg(feature = "compile")]
+        impl std::ops::Index<$ty> for super::ComponentTypesBuilder {
             type Output = $output;
             #[inline]
             fn index(&self, idx: $ty) -> &$output {
-                &self.component_types[idx]
+                &self.component_types()[idx]
             }
         }
     )*)
@@ -345,693 +332,6 @@ where
     }
 }
 
-impl<T> Index<T> for ComponentTypesBuilder
-where
-    ModuleTypes: Index<T>,
-{
-    type Output = <ModuleTypes as Index<T>>::Output;
-    fn index(&self, idx: T) -> &Self::Output {
-        self.module_types.index(idx)
-    }
-}
-
-/// Structured used to build a [`ComponentTypes`] during translation.
-///
-/// This contains tables to intern any component types found as well as
-/// managing building up core wasm [`ModuleTypes`] as well.
-pub struct ComponentTypesBuilder {
-    functions: HashMap<TypeFunc, TypeFuncIndex>,
-    lists: HashMap<TypeList, TypeListIndex>,
-    records: HashMap<TypeRecord, TypeRecordIndex>,
-    variants: HashMap<TypeVariant, TypeVariantIndex>,
-    tuples: HashMap<TypeTuple, TypeTupleIndex>,
-    enums: HashMap<TypeEnum, TypeEnumIndex>,
-    flags: HashMap<TypeFlags, TypeFlagsIndex>,
-    options: HashMap<TypeOption, TypeOptionIndex>,
-    results: HashMap<TypeResult, TypeResultIndex>,
-
-    component_types: ComponentTypes,
-    module_types: ModuleTypesBuilder,
-
-    // Cache of what the "flat" representation of all types are which is only
-    // used at compile-time and not used at runtime, hence the location here
-    // as opposed to `ComponentTypes`.
-    type_info: TypeInformationCache,
-
-    resources: ResourcesBuilder,
-}
-
-macro_rules! intern_and_fill_flat_types {
-    ($me:ident, $name:ident, $val:ident) => {{
-        if let Some(idx) = $me.$name.get(&$val) {
-            return *idx;
-        }
-        let idx = $me.component_types.$name.push($val.clone());
-        let mut info = TypeInformation::new();
-        info.$name($me, &$val);
-        let idx2 = $me.type_info.$name.push(info);
-        assert_eq!(idx, idx2);
-        $me.$name.insert($val, idx);
-        return idx;
-    }};
-}
-
-impl ComponentTypesBuilder {
-    /// Construct a new `ComponentTypesBuilder` for use with the given validator.
-    pub fn new(validator: &Validator) -> Self {
-        Self {
-            module_types: ModuleTypesBuilder::new(validator),
-
-            functions: HashMap::default(),
-            lists: HashMap::default(),
-            records: HashMap::default(),
-            variants: HashMap::default(),
-            tuples: HashMap::default(),
-            enums: HashMap::default(),
-            flags: HashMap::default(),
-            options: HashMap::default(),
-            results: HashMap::default(),
-            component_types: ComponentTypes::default(),
-            type_info: TypeInformationCache::default(),
-            resources: ResourcesBuilder::default(),
-        }
-    }
-
-    fn export_type_def(
-        &mut self,
-        static_modules: &PrimaryMap<StaticModuleIndex, CompiledModuleInfo>,
-        ty: &Export,
-    ) -> TypeDef {
-        match ty {
-            Export::LiftedFunction { ty, .. } => TypeDef::ComponentFunc(*ty),
-            Export::ModuleStatic(idx) => {
-                let mut module_ty = TypeModule::default();
-                let module = &static_modules[*idx].module;
-                for (namespace, name, ty) in module.imports() {
-                    module_ty
-                        .imports
-                        .insert((namespace.to_string(), name.to_string()), ty);
-                }
-                for (name, ty) in module.exports.iter() {
-                    module_ty
-                        .exports
-                        .insert(name.to_string(), module.type_of(*ty));
-                }
-                TypeDef::Module(self.component_types.modules.push(module_ty))
-            }
-            Export::ModuleImport { ty, .. } => TypeDef::Module(*ty),
-            Export::Instance { ty: Some(ty), .. } => TypeDef::ComponentInstance(*ty),
-            Export::Instance { exports, .. } => {
-                let mut instance_ty = TypeComponentInstance::default();
-                for (name, ty) in exports {
-                    instance_ty
-                        .exports
-                        .insert(name.to_string(), self.export_type_def(static_modules, ty));
-                }
-                TypeDef::ComponentInstance(
-                    self.component_types.component_instances.push(instance_ty),
-                )
-            }
-            Export::Type(ty) => *ty,
-        }
-    }
-
-    /// Finishes this list of component types and returns the finished
-    /// structure and the [`TypeComponentIndex`] corresponding to top-level component
-    /// with `imports` and `exports` specified.
-    pub fn finish<'a>(
-        mut self,
-        static_modules: &PrimaryMap<StaticModuleIndex, CompiledModuleInfo>,
-        imports: impl IntoIterator<Item = (String, TypeDef)>,
-        exports: impl IntoIterator<Item = (String, &'a Export)>,
-    ) -> (ComponentTypes, TypeComponentIndex) {
-        let mut component_ty = TypeComponent::default();
-        for (name, ty) in imports {
-            component_ty.imports.insert(name, ty);
-        }
-        for (name, ty) in exports {
-            component_ty
-                .exports
-                .insert(name, self.export_type_def(static_modules, ty));
-        }
-        let ty = self.component_types.components.push(component_ty);
-
-        self.component_types.module_types = Some(self.module_types.finish());
-        (self.component_types, ty)
-    }
-
-    /// Smaller helper method to find a `ModuleInternedTypeIndex` which
-    /// corresponds to the `resource.drop` intrinsic in components, namely a
-    /// core wasm function type which takes one `i32` argument and has no
-    /// results.
-    ///
-    /// This is a bit of a hack right now as ideally this find operation
-    /// wouldn't be needed and instead the `ModuleInternedTypeIndex` itself
-    /// would be threaded through appropriately, but that's left for a future
-    /// refactoring. Try not to lean too hard on this method though.
-    pub fn find_resource_drop_signature(&self) -> Option<ModuleInternedTypeIndex> {
-        self.module_types
-            .wasm_types()
-            .find(|(_, ty)| match &ty.composite_type {
-                wasmtime_types::WasmCompositeType::Func(sig) => {
-                    sig.params().len() == 1
-                        && sig.returns().len() == 0
-                        && sig.params()[0] == WasmValType::I32
-                }
-            })
-            .map(|(i, _)| i)
-    }
-
-    /// Returns the underlying builder used to build up core wasm module types.
-    ///
-    /// Note that this is shared across all modules found within a component to
-    /// improve the wins from deduplicating function signatures.
-    pub fn module_types_builder(&self) -> &ModuleTypesBuilder {
-        &self.module_types
-    }
-
-    /// Same as `module_types_builder`, but `mut`.
-    pub fn module_types_builder_mut(&mut self) -> &mut ModuleTypesBuilder {
-        &mut self.module_types
-    }
-
-    /// Returns the number of resource tables allocated so far, or the maximum
-    /// `TypeResourceTableIndex`.
-    pub fn num_resource_tables(&self) -> usize {
-        self.component_types.resource_tables.len()
-    }
-
-    /// Returns a mutable reference to the underlying `ResourcesBuilder`.
-    pub fn resources_mut(&mut self) -> &mut ResourcesBuilder {
-        &mut self.resources
-    }
-
-    /// Work around the borrow checker to borrow two sub-fields simultaneously
-    /// externally.
-    pub fn resources_mut_and_types(&mut self) -> (&mut ResourcesBuilder, &ComponentTypes) {
-        (&mut self.resources, &self.component_types)
-    }
-
-    /// Converts a wasmparser `ComponentFuncType` into Wasmtime's type
-    /// representation.
-    pub fn convert_component_func_type(
-        &mut self,
-        types: types::TypesRef<'_>,
-        id: types::ComponentFuncTypeId,
-    ) -> Result<TypeFuncIndex> {
-        assert_eq!(types.id(), self.module_types.validator_id());
-        let ty = &types[id];
-        let params = ty
-            .params
-            .iter()
-            .map(|(_name, ty)| self.valtype(types, ty))
-            .collect::<Result<_>>()?;
-        let results = ty
-            .results
-            .iter()
-            .map(|(_name, ty)| self.valtype(types, ty))
-            .collect::<Result<_>>()?;
-        let ty = TypeFunc {
-            params: self.new_tuple_type(params),
-            results: self.new_tuple_type(results),
-        };
-        Ok(self.add_func_type(ty))
-    }
-
-    /// Converts a wasmparser `ComponentEntityType` into Wasmtime's type
-    /// representation.
-    pub fn convert_component_entity_type(
-        &mut self,
-        types: types::TypesRef<'_>,
-        ty: types::ComponentEntityType,
-    ) -> Result<TypeDef> {
-        assert_eq!(types.id(), self.module_types.validator_id());
-        Ok(match ty {
-            types::ComponentEntityType::Module(id) => {
-                TypeDef::Module(self.convert_module(types, id)?)
-            }
-            types::ComponentEntityType::Component(id) => {
-                TypeDef::Component(self.convert_component(types, id)?)
-            }
-            types::ComponentEntityType::Instance(id) => {
-                TypeDef::ComponentInstance(self.convert_instance(types, id)?)
-            }
-            types::ComponentEntityType::Func(id) => {
-                TypeDef::ComponentFunc(self.convert_component_func_type(types, id)?)
-            }
-            types::ComponentEntityType::Type { created, .. } => match created {
-                types::ComponentAnyTypeId::Defined(id) => {
-                    TypeDef::Interface(self.defined_type(types, id)?)
-                }
-                types::ComponentAnyTypeId::Resource(id) => {
-                    TypeDef::Resource(self.resource_id(id.resource()))
-                }
-                _ => bail!("unsupported type export"),
-            },
-            types::ComponentEntityType::Value(_) => bail!("values not supported"),
-        })
-    }
-
-    /// Converts a wasmparser `Type` into Wasmtime's type representation.
-    pub fn convert_type(
-        &mut self,
-        types: types::TypesRef<'_>,
-        id: types::ComponentAnyTypeId,
-    ) -> Result<TypeDef> {
-        assert_eq!(types.id(), self.module_types.validator_id());
-        Ok(match id {
-            types::ComponentAnyTypeId::Defined(id) => {
-                TypeDef::Interface(self.defined_type(types, id)?)
-            }
-            types::ComponentAnyTypeId::Component(id) => {
-                TypeDef::Component(self.convert_component(types, id)?)
-            }
-            types::ComponentAnyTypeId::Instance(id) => {
-                TypeDef::ComponentInstance(self.convert_instance(types, id)?)
-            }
-            types::ComponentAnyTypeId::Func(id) => {
-                TypeDef::ComponentFunc(self.convert_component_func_type(types, id)?)
-            }
-            types::ComponentAnyTypeId::Resource(id) => {
-                TypeDef::Resource(self.resource_id(id.resource()))
-            }
-        })
-    }
-
-    fn convert_component(
-        &mut self,
-        types: types::TypesRef<'_>,
-        id: types::ComponentTypeId,
-    ) -> Result<TypeComponentIndex> {
-        assert_eq!(types.id(), self.module_types.validator_id());
-        let ty = &types[id];
-        let mut result = TypeComponent::default();
-        for (name, ty) in ty.imports.iter() {
-            result.imports.insert(
-                name.clone(),
-                self.convert_component_entity_type(types, *ty)?,
-            );
-        }
-        for (name, ty) in ty.exports.iter() {
-            result.exports.insert(
-                name.clone(),
-                self.convert_component_entity_type(types, *ty)?,
-            );
-        }
-        Ok(self.component_types.components.push(result))
-    }
-
-    pub(crate) fn convert_instance(
-        &mut self,
-        types: types::TypesRef<'_>,
-        id: types::ComponentInstanceTypeId,
-    ) -> Result<TypeComponentInstanceIndex> {
-        assert_eq!(types.id(), self.module_types.validator_id());
-        let ty = &types[id];
-        let mut result = TypeComponentInstance::default();
-        for (name, ty) in ty.exports.iter() {
-            result.exports.insert(
-                name.clone(),
-                self.convert_component_entity_type(types, *ty)?,
-            );
-        }
-        Ok(self.component_types.component_instances.push(result))
-    }
-
-    fn convert_module(
-        &mut self,
-        types: types::TypesRef<'_>,
-        id: types::ComponentCoreModuleTypeId,
-    ) -> Result<TypeModuleIndex> {
-        assert_eq!(types.id(), self.module_types.validator_id());
-        let ty = &types[id];
-        let mut result = TypeModule::default();
-        for ((module, field), ty) in ty.imports.iter() {
-            result.imports.insert(
-                (module.clone(), field.clone()),
-                self.entity_type(types, ty)?,
-            );
-        }
-        for (name, ty) in ty.exports.iter() {
-            result
-                .exports
-                .insert(name.clone(), self.entity_type(types, ty)?);
-        }
-        Ok(self.component_types.modules.push(result))
-    }
-
-    fn entity_type(
-        &mut self,
-        types: types::TypesRef<'_>,
-        ty: &types::EntityType,
-    ) -> Result<EntityType> {
-        assert_eq!(types.id(), self.module_types.validator_id());
-        Ok(match ty {
-            types::EntityType::Func(id) => EntityType::Function({
-                let module = Module::default();
-                self.module_types_builder_mut()
-                    .intern_type(&module, types, *id)?
-                    .into()
-            }),
-            types::EntityType::Table(ty) => EntityType::Table(self.convert_table_type(ty)),
-            types::EntityType::Memory(ty) => EntityType::Memory(ty.clone().into()),
-            types::EntityType::Global(ty) => EntityType::Global(self.convert_global_type(ty)),
-            types::EntityType::Tag(_) => bail!("exceptions proposal not implemented"),
-        })
-    }
-
-    fn defined_type(
-        &mut self,
-        types: types::TypesRef<'_>,
-        id: types::ComponentDefinedTypeId,
-    ) -> Result<InterfaceType> {
-        assert_eq!(types.id(), self.module_types.validator_id());
-        let ret = match &types[id] {
-            types::ComponentDefinedType::Primitive(ty) => ty.into(),
-            types::ComponentDefinedType::Record(e) => {
-                InterfaceType::Record(self.record_type(types, e)?)
-            }
-            types::ComponentDefinedType::Variant(e) => {
-                InterfaceType::Variant(self.variant_type(types, e)?)
-            }
-            types::ComponentDefinedType::List(e) => InterfaceType::List(self.list_type(types, e)?),
-            types::ComponentDefinedType::Tuple(e) => {
-                InterfaceType::Tuple(self.tuple_type(types, e)?)
-            }
-            types::ComponentDefinedType::Flags(e) => InterfaceType::Flags(self.flags_type(e)),
-            types::ComponentDefinedType::Enum(e) => InterfaceType::Enum(self.enum_type(e)),
-            types::ComponentDefinedType::Option(e) => {
-                InterfaceType::Option(self.option_type(types, e)?)
-            }
-            types::ComponentDefinedType::Result { ok, err } => {
-                InterfaceType::Result(self.result_type(types, ok, err)?)
-            }
-            types::ComponentDefinedType::Own(r) => {
-                InterfaceType::Own(self.resource_id(r.resource()))
-            }
-            types::ComponentDefinedType::Borrow(r) => {
-                InterfaceType::Borrow(self.resource_id(r.resource()))
-            }
-        };
-        let info = self.type_information(&ret);
-        if info.depth > MAX_TYPE_DEPTH {
-            bail!("type nesting is too deep");
-        }
-        Ok(ret)
-    }
-
-    fn valtype(
-        &mut self,
-        types: types::TypesRef<'_>,
-        ty: &types::ComponentValType,
-    ) -> Result<InterfaceType> {
-        assert_eq!(types.id(), self.module_types.validator_id());
-        match ty {
-            types::ComponentValType::Primitive(p) => Ok(p.into()),
-            types::ComponentValType::Type(id) => self.defined_type(types, *id),
-        }
-    }
-
-    fn record_type(
-        &mut self,
-        types: types::TypesRef<'_>,
-        ty: &types::RecordType,
-    ) -> Result<TypeRecordIndex> {
-        assert_eq!(types.id(), self.module_types.validator_id());
-        let fields = ty
-            .fields
-            .iter()
-            .map(|(name, ty)| {
-                Ok(RecordField {
-                    name: name.to_string(),
-                    ty: self.valtype(types, ty)?,
-                })
-            })
-            .collect::<Result<Box<[_]>>>()?;
-        let abi = CanonicalAbiInfo::record(
-            fields
-                .iter()
-                .map(|field| self.component_types.canonical_abi(&field.ty)),
-        );
-        Ok(self.add_record_type(TypeRecord { fields, abi }))
-    }
-
-    fn variant_type(
-        &mut self,
-        types: types::TypesRef<'_>,
-        ty: &types::VariantType,
-    ) -> Result<TypeVariantIndex> {
-        assert_eq!(types.id(), self.module_types.validator_id());
-        let cases = ty
-            .cases
-            .iter()
-            .map(|(name, case)| {
-                // FIXME: need to implement `refines`, not sure what that
-                // is at this time.
-                if case.refines.is_some() {
-                    bail!("refines is not supported at this time");
-                }
-                Ok((
-                    name.to_string(),
-                    match &case.ty.as_ref() {
-                        Some(ty) => Some(self.valtype(types, ty)?),
-                        None => None,
-                    },
-                ))
-            })
-            .collect::<Result<IndexMap<_, _>>>()?;
-        let (info, abi) = VariantInfo::new(
-            cases
-                .iter()
-                .map(|(_, c)| c.as_ref().map(|ty| self.component_types.canonical_abi(ty))),
-        );
-        Ok(self.add_variant_type(TypeVariant { cases, abi, info }))
-    }
-
-    fn tuple_type(
-        &mut self,
-        types: types::TypesRef<'_>,
-        ty: &types::TupleType,
-    ) -> Result<TypeTupleIndex> {
-        assert_eq!(types.id(), self.module_types.validator_id());
-        let types = ty
-            .types
-            .iter()
-            .map(|ty| self.valtype(types, ty))
-            .collect::<Result<Box<[_]>>>()?;
-        Ok(self.new_tuple_type(types))
-    }
-
-    fn new_tuple_type(&mut self, types: Box<[InterfaceType]>) -> TypeTupleIndex {
-        let abi = CanonicalAbiInfo::record(
-            types
-                .iter()
-                .map(|ty| self.component_types.canonical_abi(ty)),
-        );
-        self.add_tuple_type(TypeTuple { types, abi })
-    }
-
-    fn flags_type(&mut self, flags: &wasmparser::map::IndexSet<KebabString>) -> TypeFlagsIndex {
-        let flags = TypeFlags {
-            names: flags.iter().map(|s| s.to_string()).collect(),
-            abi: CanonicalAbiInfo::flags(flags.len()),
-        };
-        self.add_flags_type(flags)
-    }
-
-    fn enum_type(&mut self, variants: &wasmparser::map::IndexSet<KebabString>) -> TypeEnumIndex {
-        let names = variants
-            .iter()
-            .map(|s| s.to_string())
-            .collect::<IndexSet<_>>();
-        let (info, abi) = VariantInfo::new(names.iter().map(|_| None));
-        self.add_enum_type(TypeEnum { names, abi, info })
-    }
-
-    fn option_type(
-        &mut self,
-        types: types::TypesRef<'_>,
-        ty: &types::ComponentValType,
-    ) -> Result<TypeOptionIndex> {
-        assert_eq!(types.id(), self.module_types.validator_id());
-        let ty = self.valtype(types, ty)?;
-        let (info, abi) = VariantInfo::new([None, Some(self.component_types.canonical_abi(&ty))]);
-        Ok(self.add_option_type(TypeOption { ty, abi, info }))
-    }
-
-    fn result_type(
-        &mut self,
-        types: types::TypesRef<'_>,
-        ok: &Option<types::ComponentValType>,
-        err: &Option<types::ComponentValType>,
-    ) -> Result<TypeResultIndex> {
-        assert_eq!(types.id(), self.module_types.validator_id());
-        let ok = match ok {
-            Some(ty) => Some(self.valtype(types, ty)?),
-            None => None,
-        };
-        let err = match err {
-            Some(ty) => Some(self.valtype(types, ty)?),
-            None => None,
-        };
-        let (info, abi) = VariantInfo::new([
-            ok.as_ref().map(|t| self.component_types.canonical_abi(t)),
-            err.as_ref().map(|t| self.component_types.canonical_abi(t)),
-        ]);
-        Ok(self.add_result_type(TypeResult { ok, err, abi, info }))
-    }
-
-    fn list_type(
-        &mut self,
-        types: types::TypesRef<'_>,
-        ty: &types::ComponentValType,
-    ) -> Result<TypeListIndex> {
-        assert_eq!(types.id(), self.module_types.validator_id());
-        let element = self.valtype(types, ty)?;
-        Ok(self.add_list_type(TypeList { element }))
-    }
-
-    /// Converts a wasmparser `id`, which must point to a resource, to its
-    /// corresponding `TypeResourceTableIndex`.
-    pub fn resource_id(&mut self, id: types::ResourceId) -> TypeResourceTableIndex {
-        self.resources.convert(id, &mut self.component_types)
-    }
-
-    /// Interns a new function type within this type information.
-    pub fn add_func_type(&mut self, ty: TypeFunc) -> TypeFuncIndex {
-        intern(&mut self.functions, &mut self.component_types.functions, ty)
-    }
-
-    /// Interns a new record type within this type information.
-    pub fn add_record_type(&mut self, ty: TypeRecord) -> TypeRecordIndex {
-        intern_and_fill_flat_types!(self, records, ty)
-    }
-
-    /// Interns a new flags type within this type information.
-    pub fn add_flags_type(&mut self, ty: TypeFlags) -> TypeFlagsIndex {
-        intern_and_fill_flat_types!(self, flags, ty)
-    }
-
-    /// Interns a new tuple type within this type information.
-    pub fn add_tuple_type(&mut self, ty: TypeTuple) -> TypeTupleIndex {
-        intern_and_fill_flat_types!(self, tuples, ty)
-    }
-
-    /// Interns a new variant type within this type information.
-    pub fn add_variant_type(&mut self, ty: TypeVariant) -> TypeVariantIndex {
-        intern_and_fill_flat_types!(self, variants, ty)
-    }
-
-    /// Interns a new enum type within this type information.
-    pub fn add_enum_type(&mut self, ty: TypeEnum) -> TypeEnumIndex {
-        intern_and_fill_flat_types!(self, enums, ty)
-    }
-
-    /// Interns a new option type within this type information.
-    pub fn add_option_type(&mut self, ty: TypeOption) -> TypeOptionIndex {
-        intern_and_fill_flat_types!(self, options, ty)
-    }
-
-    /// Interns a new result type within this type information.
-    pub fn add_result_type(&mut self, ty: TypeResult) -> TypeResultIndex {
-        intern_and_fill_flat_types!(self, results, ty)
-    }
-
-    /// Interns a new type within this type information.
-    pub fn add_list_type(&mut self, ty: TypeList) -> TypeListIndex {
-        intern_and_fill_flat_types!(self, lists, ty)
-    }
-
-    /// Returns the canonical ABI information about the specified type.
-    pub fn canonical_abi(&self, ty: &InterfaceType) -> &CanonicalAbiInfo {
-        self.component_types.canonical_abi(ty)
-    }
-
-    /// Returns the "flat types" for the given interface type used in the
-    /// canonical ABI.
-    ///
-    /// Returns `None` if the type is too large to be represented via flat types
-    /// in the canonical abi.
-    pub fn flat_types(&self, ty: &InterfaceType) -> Option<FlatTypes<'_>> {
-        self.type_information(ty).flat.as_flat_types()
-    }
-
-    /// Returns whether the type specified contains any borrowed resources
-    /// within it.
-    pub fn ty_contains_borrow_resource(&self, ty: &InterfaceType) -> bool {
-        self.type_information(ty).has_borrow
-    }
-
-    fn type_information(&self, ty: &InterfaceType) -> &TypeInformation {
-        match ty {
-            InterfaceType::U8
-            | InterfaceType::S8
-            | InterfaceType::Bool
-            | InterfaceType::U16
-            | InterfaceType::S16
-            | InterfaceType::U32
-            | InterfaceType::S32
-            | InterfaceType::Char
-            | InterfaceType::Own(_) => {
-                static INFO: TypeInformation = TypeInformation::primitive(FlatType::I32);
-                &INFO
-            }
-            InterfaceType::Borrow(_) => {
-                static INFO: TypeInformation = {
-                    let mut info = TypeInformation::primitive(FlatType::I32);
-                    info.has_borrow = true;
-                    info
-                };
-                &INFO
-            }
-            InterfaceType::U64 | InterfaceType::S64 => {
-                static INFO: TypeInformation = TypeInformation::primitive(FlatType::I64);
-                &INFO
-            }
-            InterfaceType::Float32 => {
-                static INFO: TypeInformation = TypeInformation::primitive(FlatType::F32);
-                &INFO
-            }
-            InterfaceType::Float64 => {
-                static INFO: TypeInformation = TypeInformation::primitive(FlatType::F64);
-                &INFO
-            }
-            InterfaceType::String => {
-                static INFO: TypeInformation = TypeInformation::string();
-                &INFO
-            }
-
-            InterfaceType::List(i) => &self.type_info.lists[*i],
-            InterfaceType::Record(i) => &self.type_info.records[*i],
-            InterfaceType::Variant(i) => &self.type_info.variants[*i],
-            InterfaceType::Tuple(i) => &self.type_info.tuples[*i],
-            InterfaceType::Flags(i) => &self.type_info.flags[*i],
-            InterfaceType::Enum(i) => &self.type_info.enums[*i],
-            InterfaceType::Option(i) => &self.type_info.options[*i],
-            InterfaceType::Result(i) => &self.type_info.results[*i],
-        }
-    }
-}
-
-impl TypeConvert for ComponentTypesBuilder {
-    fn lookup_heap_type(&self, _index: wasmparser::UnpackedIndex) -> WasmHeapType {
-        panic!("heap types are not supported yet")
-    }
-}
-
-fn intern<T, U>(map: &mut HashMap<T, U>, list: &mut PrimaryMap<U, T>, item: T) -> U
-where
-    T: Hash + Clone + Eq,
-    U: Copy + EntityRef,
-{
-    if let Some(idx) = map.get(&item) {
-        return *idx;
-    }
-    let idx = list.push(item.clone());
-    map.insert(item, idx);
-    return idx;
-}
-
 /// Types of imports and exports in the component model.
 ///
 /// These types are what's available for import and export in components. Note
@@ -1661,7 +961,8 @@ pub struct TypeList {
     pub element: InterfaceType,
 }
 
-const MAX_FLAT_TYPES: usize = if MAX_FLAT_PARAMS > MAX_FLAT_RESULTS {
+/// Maximum number of flat types, for either params or results.
+pub const MAX_FLAT_TYPES: usize = if MAX_FLAT_PARAMS > MAX_FLAT_RESULTS {
     MAX_FLAT_PARAMS
 } else {
     MAX_FLAT_RESULTS
@@ -1726,281 +1027,3 @@ pub enum FlatType {
     F32,
     F64,
 }
-
-struct FlatTypesStorage {
-    // This could be represented as `Vec<FlatType>` but on 64-bit architectures
-    // that's 24 bytes. Otherwise `FlatType` is 1 byte large and
-    // `MAX_FLAT_TYPES` is 16, so it should ideally be more space-efficient to
-    // use a flat array instead of a heap-based vector.
-    memory32: [FlatType; MAX_FLAT_TYPES],
-    memory64: [FlatType; MAX_FLAT_TYPES],
-
-    // Tracks the number of flat types pushed into this storage. If this is
-    // `MAX_FLAT_TYPES + 1` then this storage represents an un-reprsentable
-    // type in flat types.
-    len: u8,
-}
-
-impl FlatTypesStorage {
-    const fn new() -> FlatTypesStorage {
-        FlatTypesStorage {
-            memory32: [FlatType::I32; MAX_FLAT_TYPES],
-            memory64: [FlatType::I32; MAX_FLAT_TYPES],
-            len: 0,
-        }
-    }
-
-    fn as_flat_types(&self) -> Option<FlatTypes<'_>> {
-        let len = usize::from(self.len);
-        if len > MAX_FLAT_TYPES {
-            assert_eq!(len, MAX_FLAT_TYPES + 1);
-            None
-        } else {
-            Some(FlatTypes {
-                memory32: &self.memory32[..len],
-                memory64: &self.memory64[..len],
-            })
-        }
-    }
-
-    /// Pushes a new flat type into this list using `t32` for 32-bit memories
-    /// and `t64` for 64-bit memories.
-    ///
-    /// Returns whether the type was actually pushed or whether this list of
-    /// flat types just exceeded the maximum meaning that it is now
-    /// unrepresentable with a flat list of types.
-    fn push(&mut self, t32: FlatType, t64: FlatType) -> bool {
-        let len = usize::from(self.len);
-        if len < MAX_FLAT_TYPES {
-            self.memory32[len] = t32;
-            self.memory64[len] = t64;
-            self.len += 1;
-            true
-        } else {
-            // If this was the first one to go over then flag the length as
-            // being incompatible with a flat representation.
-            if len == MAX_FLAT_TYPES {
-                self.len += 1;
-            }
-            false
-        }
-    }
-}
-
-impl FlatType {
-    fn join(&mut self, other: FlatType) {
-        if *self == other {
-            return;
-        }
-        *self = match (*self, other) {
-            (FlatType::I32, FlatType::F32) | (FlatType::F32, FlatType::I32) => FlatType::I32,
-            _ => FlatType::I64,
-        };
-    }
-}
-
-#[derive(Default)]
-struct TypeInformationCache {
-    records: PrimaryMap<TypeRecordIndex, TypeInformation>,
-    variants: PrimaryMap<TypeVariantIndex, TypeInformation>,
-    tuples: PrimaryMap<TypeTupleIndex, TypeInformation>,
-    enums: PrimaryMap<TypeEnumIndex, TypeInformation>,
-    flags: PrimaryMap<TypeFlagsIndex, TypeInformation>,
-    options: PrimaryMap<TypeOptionIndex, TypeInformation>,
-    results: PrimaryMap<TypeResultIndex, TypeInformation>,
-    lists: PrimaryMap<TypeListIndex, TypeInformation>,
-}
-
-struct TypeInformation {
-    depth: u32,
-    flat: FlatTypesStorage,
-    has_borrow: bool,
-}
-
-impl TypeInformation {
-    const fn new() -> TypeInformation {
-        TypeInformation {
-            depth: 0,
-            flat: FlatTypesStorage::new(),
-            has_borrow: false,
-        }
-    }
-
-    const fn primitive(flat: FlatType) -> TypeInformation {
-        let mut info = TypeInformation::new();
-        info.depth = 1;
-        info.flat.memory32[0] = flat;
-        info.flat.memory64[0] = flat;
-        info.flat.len = 1;
-        info
-    }
-
-    const fn string() -> TypeInformation {
-        let mut info = TypeInformation::new();
-        info.depth = 1;
-        info.flat.memory32[0] = FlatType::I32;
-        info.flat.memory32[1] = FlatType::I32;
-        info.flat.memory64[0] = FlatType::I64;
-        info.flat.memory64[1] = FlatType::I64;
-        info.flat.len = 2;
-        info
-    }
-
-    /// Builds up all flat types internally using the specified representation
-    /// for all of the component fields of the record.
-    fn build_record<'a>(&mut self, types: impl Iterator<Item = &'a TypeInformation>) {
-        self.depth = 1;
-        for info in types {
-            self.depth = self.depth.max(1 + info.depth);
-            self.has_borrow = self.has_borrow || info.has_borrow;
-            match info.flat.as_flat_types() {
-                Some(types) => {
-                    for (t32, t64) in types.memory32.iter().zip(types.memory64) {
-                        if !self.flat.push(*t32, *t64) {
-                            break;
-                        }
-                    }
-                }
-                None => {
-                    self.flat.len = u8::try_from(MAX_FLAT_TYPES + 1).unwrap();
-                }
-            }
-        }
-    }
-
-    /// Builds up the flat types used to represent a `variant` which notably
-    /// handles "join"ing types together so each case is representable as a
-    /// single flat list of types.
-    ///
-    /// The iterator item is:
-    ///
-    /// * `None` - no payload for this case
-    /// * `Some(None)` - this case has a payload but can't be represented with
-    ///   flat types
-    /// * `Some(Some(types))` - this case has a payload and is represented with
-    ///   the types specified in the flat representation.
-    fn build_variant<'a, I>(&mut self, cases: I)
-    where
-        I: IntoIterator<Item = Option<&'a TypeInformation>>,
-    {
-        let cases = cases.into_iter();
-        self.flat.push(FlatType::I32, FlatType::I32);
-        self.depth = 1;
-
-        for info in cases {
-            let info = match info {
-                Some(info) => info,
-                // If this case doesn't have a payload then it doesn't change
-                // the depth/flat representation
-                None => continue,
-            };
-            self.depth = self.depth.max(1 + info.depth);
-            self.has_borrow = self.has_borrow || info.has_borrow;
-
-            // If this variant is already unrepresentable in a flat
-            // representation then this can be skipped.
-            if usize::from(self.flat.len) > MAX_FLAT_TYPES {
-                continue;
-            }
-
-            let types = match info.flat.as_flat_types() {
-                Some(types) => types,
-                // If this case isn't representable with a flat list of types
-                // then this variant also isn't representable.
-                None => {
-                    self.flat.len = u8::try_from(MAX_FLAT_TYPES + 1).unwrap();
-                    continue;
-                }
-            };
-            // If the case used all of the flat types then the discriminant
-            // added for this variant means that this variant is no longer
-            // representable.
-            if types.memory32.len() >= MAX_FLAT_TYPES {
-                self.flat.len = u8::try_from(MAX_FLAT_TYPES + 1).unwrap();
-                continue;
-            }
-            let dst = self
-                .flat
-                .memory32
-                .iter_mut()
-                .zip(&mut self.flat.memory64)
-                .skip(1);
-            for (i, ((t32, t64), (dst32, dst64))) in types
-                .memory32
-                .iter()
-                .zip(types.memory64)
-                .zip(dst)
-                .enumerate()
-            {
-                if i + 1 < usize::from(self.flat.len) {
-                    // If this index hs already been set by some previous case
-                    // then the types are joined together.
-                    dst32.join(*t32);
-                    dst64.join(*t64);
-                } else {
-                    // Otherwise if this is the first time that the
-                    // representation has gotten this large then the destination
-                    // is simply whatever the type is. The length is also
-                    // increased here to indicate this.
-                    self.flat.len += 1;
-                    *dst32 = *t32;
-                    *dst64 = *t64;
-                }
-            }
-        }
-    }
-
-    fn records(&mut self, types: &ComponentTypesBuilder, ty: &TypeRecord) {
-        self.build_record(ty.fields.iter().map(|f| types.type_information(&f.ty)));
-    }
-
-    fn tuples(&mut self, types: &ComponentTypesBuilder, ty: &TypeTuple) {
-        self.build_record(ty.types.iter().map(|t| types.type_information(t)));
-    }
-
-    fn enums(&mut self, _types: &ComponentTypesBuilder, _ty: &TypeEnum) {
-        self.depth = 1;
-        self.flat.push(FlatType::I32, FlatType::I32);
-    }
-
-    fn flags(&mut self, _types: &ComponentTypesBuilder, ty: &TypeFlags) {
-        self.depth = 1;
-        match FlagsSize::from_count(ty.names.len()) {
-            FlagsSize::Size0 => {}
-            FlagsSize::Size1 | FlagsSize::Size2 => {
-                self.flat.push(FlatType::I32, FlatType::I32);
-            }
-            FlagsSize::Size4Plus(n) => {
-                for _ in 0..n {
-                    self.flat.push(FlatType::I32, FlatType::I32);
-                }
-            }
-        }
-    }
-
-    fn variants(&mut self, types: &ComponentTypesBuilder, ty: &TypeVariant) {
-        self.build_variant(
-            ty.cases
-                .iter()
-                .map(|(_, c)| c.as_ref().map(|ty| types.type_information(ty))),
-        )
-    }
-
-    fn results(&mut self, types: &ComponentTypesBuilder, ty: &TypeResult) {
-        self.build_variant([
-            ty.ok.as_ref().map(|ty| types.type_information(ty)),
-            ty.err.as_ref().map(|ty| types.type_information(ty)),
-        ])
-    }
-
-    fn options(&mut self, types: &ComponentTypesBuilder, ty: &TypeOption) {
-        self.build_variant([None, Some(types.type_information(&ty.ty))]);
-    }
-
-    fn lists(&mut self, types: &ComponentTypesBuilder, ty: &TypeList) {
-        *self = TypeInformation::string();
-        let info = types.type_information(&ty.element);
-        self.depth += info.depth;
-        self.has_borrow = info.has_borrow;
-    }
-}
diff --git a/crates/environ/src/component/types_builder.rs b/crates/environ/src/component/types_builder.rs
new file mode 100644
index 000000000..e13974495
--- /dev/null
+++ b/crates/environ/src/component/types_builder.rs
@@ -0,0 +1,997 @@
+use crate::component::*;
+use crate::{
+    CompiledModuleInfo, EntityType, Module, ModuleTypes, ModuleTypesBuilder, PrimaryMap,
+    TypeConvert, WasmHeapType, WasmValType,
+};
+use anyhow::{bail, Result};
+use cranelift_entity::EntityRef;
+use indexmap::{IndexMap, IndexSet};
+use std::collections::HashMap;
+use std::hash::Hash;
+use std::ops::Index;
+use wasmparser::names::KebabString;
+use wasmparser::{types, Validator};
+use wasmtime_component_util::FlagsSize;
+use wasmtime_types::ModuleInternedTypeIndex;
+
+mod resources;
+pub use resources::ResourcesBuilder;
+
+/// Maximum nesting depth of a type allowed in Wasmtime.
+///
+/// This constant isn't chosen via any scientific means and its main purpose is
+/// to enable most of Wasmtime to handle types via recursion without worrying
+/// about stack overflow.
+///
+/// Some more information about this can be found in #4814
+const MAX_TYPE_DEPTH: u32 = 100;
+
+/// Structured used to build a [`ComponentTypes`] during translation.
+///
+/// This contains tables to intern any component types found as well as
+/// managing building up core wasm [`ModuleTypes`] as well.
+pub struct ComponentTypesBuilder {
+    functions: HashMap<TypeFunc, TypeFuncIndex>,
+    lists: HashMap<TypeList, TypeListIndex>,
+    records: HashMap<TypeRecord, TypeRecordIndex>,
+    variants: HashMap<TypeVariant, TypeVariantIndex>,
+    tuples: HashMap<TypeTuple, TypeTupleIndex>,
+    enums: HashMap<TypeEnum, TypeEnumIndex>,
+    flags: HashMap<TypeFlags, TypeFlagsIndex>,
+    options: HashMap<TypeOption, TypeOptionIndex>,
+    results: HashMap<TypeResult, TypeResultIndex>,
+
+    component_types: ComponentTypes,
+    module_types: ModuleTypesBuilder,
+
+    // Cache of what the "flat" representation of all types are which is only
+    // used at compile-time and not used at runtime, hence the location here
+    // as opposed to `ComponentTypes`.
+    type_info: TypeInformationCache,
+
+    resources: ResourcesBuilder,
+}
+
+impl<T> Index<T> for ComponentTypesBuilder
+where
+    ModuleTypes: Index<T>,
+{
+    type Output = <ModuleTypes as Index<T>>::Output;
+    fn index(&self, idx: T) -> &Self::Output {
+        self.module_types.index(idx)
+    }
+}
+
+macro_rules! intern_and_fill_flat_types {
+    ($me:ident, $name:ident, $val:ident) => {{
+        if let Some(idx) = $me.$name.get(&$val) {
+            return *idx;
+        }
+        let idx = $me.component_types.$name.push($val.clone());
+        let mut info = TypeInformation::new();
+        info.$name($me, &$val);
+        let idx2 = $me.type_info.$name.push(info);
+        assert_eq!(idx, idx2);
+        $me.$name.insert($val, idx);
+        return idx;
+    }};
+}
+
+impl ComponentTypesBuilder {
+    /// Construct a new `ComponentTypesBuilder` for use with the given validator.
+    pub fn new(validator: &Validator) -> Self {
+        Self {
+            module_types: ModuleTypesBuilder::new(validator),
+
+            functions: HashMap::default(),
+            lists: HashMap::default(),
+            records: HashMap::default(),
+            variants: HashMap::default(),
+            tuples: HashMap::default(),
+            enums: HashMap::default(),
+            flags: HashMap::default(),
+            options: HashMap::default(),
+            results: HashMap::default(),
+            component_types: ComponentTypes::default(),
+            type_info: TypeInformationCache::default(),
+            resources: ResourcesBuilder::default(),
+        }
+    }
+
+    fn export_type_def(
+        &mut self,
+        static_modules: &PrimaryMap<StaticModuleIndex, CompiledModuleInfo>,
+        ty: &Export,
+    ) -> TypeDef {
+        match ty {
+            Export::LiftedFunction { ty, .. } => TypeDef::ComponentFunc(*ty),
+            Export::ModuleStatic(idx) => {
+                let mut module_ty = TypeModule::default();
+                let module = &static_modules[*idx].module;
+                for (namespace, name, ty) in module.imports() {
+                    module_ty
+                        .imports
+                        .insert((namespace.to_string(), name.to_string()), ty);
+                }
+                for (name, ty) in module.exports.iter() {
+                    module_ty
+                        .exports
+                        .insert(name.to_string(), module.type_of(*ty));
+                }
+                TypeDef::Module(self.component_types.modules.push(module_ty))
+            }
+            Export::ModuleImport { ty, .. } => TypeDef::Module(*ty),
+            Export::Instance { ty: Some(ty), .. } => TypeDef::ComponentInstance(*ty),
+            Export::Instance { exports, .. } => {
+                let mut instance_ty = TypeComponentInstance::default();
+                for (name, ty) in exports {
+                    instance_ty
+                        .exports
+                        .insert(name.to_string(), self.export_type_def(static_modules, ty));
+                }
+                TypeDef::ComponentInstance(
+                    self.component_types.component_instances.push(instance_ty),
+                )
+            }
+            Export::Type(ty) => *ty,
+        }
+    }
+
+    /// Finishes this list of component types and returns the finished
+    /// structure and the [`TypeComponentIndex`] corresponding to top-level component
+    /// with `imports` and `exports` specified.
+    pub fn finish<'a>(
+        mut self,
+        static_modules: &PrimaryMap<StaticModuleIndex, CompiledModuleInfo>,
+        imports: impl IntoIterator<Item = (String, TypeDef)>,
+        exports: impl IntoIterator<Item = (String, &'a Export)>,
+    ) -> (ComponentTypes, TypeComponentIndex) {
+        let mut component_ty = TypeComponent::default();
+        for (name, ty) in imports {
+            component_ty.imports.insert(name, ty);
+        }
+        for (name, ty) in exports {
+            component_ty
+                .exports
+                .insert(name, self.export_type_def(static_modules, ty));
+        }
+        let ty = self.component_types.components.push(component_ty);
+
+        self.component_types.module_types = Some(self.module_types.finish());
+        (self.component_types, ty)
+    }
+
+    /// Smaller helper method to find a `ModuleInternedTypeIndex` which
+    /// corresponds to the `resource.drop` intrinsic in components, namely a
+    /// core wasm function type which takes one `i32` argument and has no
+    /// results.
+    ///
+    /// This is a bit of a hack right now as ideally this find operation
+    /// wouldn't be needed and instead the `ModuleInternedTypeIndex` itself
+    /// would be threaded through appropriately, but that's left for a future
+    /// refactoring. Try not to lean too hard on this method though.
+    pub fn find_resource_drop_signature(&self) -> Option<ModuleInternedTypeIndex> {
+        self.module_types
+            .wasm_types()
+            .find(|(_, ty)| match &ty.composite_type {
+                wasmtime_types::WasmCompositeType::Func(sig) => {
+                    sig.params().len() == 1
+                        && sig.returns().len() == 0
+                        && sig.params()[0] == WasmValType::I32
+                }
+            })
+            .map(|(i, _)| i)
+    }
+
+    /// Returns the underlying builder used to build up core wasm module types.
+    ///
+    /// Note that this is shared across all modules found within a component to
+    /// improve the wins from deduplicating function signatures.
+    pub fn module_types_builder(&self) -> &ModuleTypesBuilder {
+        &self.module_types
+    }
+
+    /// Same as `module_types_builder`, but `mut`.
+    pub fn module_types_builder_mut(&mut self) -> &mut ModuleTypesBuilder {
+        &mut self.module_types
+    }
+
+    /// Returns the internal reference to the in-progress `&ComponentTypes`.
+    pub(super) fn component_types(&self) -> &ComponentTypes {
+        &self.component_types
+    }
+
+    /// Returns the number of resource tables allocated so far, or the maximum
+    /// `TypeResourceTableIndex`.
+    pub fn num_resource_tables(&self) -> usize {
+        self.component_types.resource_tables.len()
+    }
+
+    /// Returns a mutable reference to the underlying `ResourcesBuilder`.
+    pub fn resources_mut(&mut self) -> &mut ResourcesBuilder {
+        &mut self.resources
+    }
+
+    /// Work around the borrow checker to borrow two sub-fields simultaneously
+    /// externally.
+    pub fn resources_mut_and_types(&mut self) -> (&mut ResourcesBuilder, &ComponentTypes) {
+        (&mut self.resources, &self.component_types)
+    }
+
+    /// Converts a wasmparser `ComponentFuncType` into Wasmtime's type
+    /// representation.
+    pub fn convert_component_func_type(
+        &mut self,
+        types: types::TypesRef<'_>,
+        id: types::ComponentFuncTypeId,
+    ) -> Result<TypeFuncIndex> {
+        assert_eq!(types.id(), self.module_types.validator_id());
+        let ty = &types[id];
+        let params = ty
+            .params
+            .iter()
+            .map(|(_name, ty)| self.valtype(types, ty))
+            .collect::<Result<_>>()?;
+        let results = ty
+            .results
+            .iter()
+            .map(|(_name, ty)| self.valtype(types, ty))
+            .collect::<Result<_>>()?;
+        let ty = TypeFunc {
+            params: self.new_tuple_type(params),
+            results: self.new_tuple_type(results),
+        };
+        Ok(self.add_func_type(ty))
+    }
+
+    /// Converts a wasmparser `ComponentEntityType` into Wasmtime's type
+    /// representation.
+    pub fn convert_component_entity_type(
+        &mut self,
+        types: types::TypesRef<'_>,
+        ty: types::ComponentEntityType,
+    ) -> Result<TypeDef> {
+        assert_eq!(types.id(), self.module_types.validator_id());
+        Ok(match ty {
+            types::ComponentEntityType::Module(id) => {
+                TypeDef::Module(self.convert_module(types, id)?)
+            }
+            types::ComponentEntityType::Component(id) => {
+                TypeDef::Component(self.convert_component(types, id)?)
+            }
+            types::ComponentEntityType::Instance(id) => {
+                TypeDef::ComponentInstance(self.convert_instance(types, id)?)
+            }
+            types::ComponentEntityType::Func(id) => {
+                TypeDef::ComponentFunc(self.convert_component_func_type(types, id)?)
+            }
+            types::ComponentEntityType::Type { created, .. } => match created {
+                types::ComponentAnyTypeId::Defined(id) => {
+                    TypeDef::Interface(self.defined_type(types, id)?)
+                }
+                types::ComponentAnyTypeId::Resource(id) => {
+                    TypeDef::Resource(self.resource_id(id.resource()))
+                }
+                _ => bail!("unsupported type export"),
+            },
+            types::ComponentEntityType::Value(_) => bail!("values not supported"),
+        })
+    }
+
+    /// Converts a wasmparser `Type` into Wasmtime's type representation.
+    pub fn convert_type(
+        &mut self,
+        types: types::TypesRef<'_>,
+        id: types::ComponentAnyTypeId,
+    ) -> Result<TypeDef> {
+        assert_eq!(types.id(), self.module_types.validator_id());
+        Ok(match id {
+            types::ComponentAnyTypeId::Defined(id) => {
+                TypeDef::Interface(self.defined_type(types, id)?)
+            }
+            types::ComponentAnyTypeId::Component(id) => {
+                TypeDef::Component(self.convert_component(types, id)?)
+            }
+            types::ComponentAnyTypeId::Instance(id) => {
+                TypeDef::ComponentInstance(self.convert_instance(types, id)?)
+            }
+            types::ComponentAnyTypeId::Func(id) => {
+                TypeDef::ComponentFunc(self.convert_component_func_type(types, id)?)
+            }
+            types::ComponentAnyTypeId::Resource(id) => {
+                TypeDef::Resource(self.resource_id(id.resource()))
+            }
+        })
+    }
+
+    fn convert_component(
+        &mut self,
+        types: types::TypesRef<'_>,
+        id: types::ComponentTypeId,
+    ) -> Result<TypeComponentIndex> {
+        assert_eq!(types.id(), self.module_types.validator_id());
+        let ty = &types[id];
+        let mut result = TypeComponent::default();
+        for (name, ty) in ty.imports.iter() {
+            result.imports.insert(
+                name.clone(),
+                self.convert_component_entity_type(types, *ty)?,
+            );
+        }
+        for (name, ty) in ty.exports.iter() {
+            result.exports.insert(
+                name.clone(),
+                self.convert_component_entity_type(types, *ty)?,
+            );
+        }
+        Ok(self.component_types.components.push(result))
+    }
+
+    pub(crate) fn convert_instance(
+        &mut self,
+        types: types::TypesRef<'_>,
+        id: types::ComponentInstanceTypeId,
+    ) -> Result<TypeComponentInstanceIndex> {
+        assert_eq!(types.id(), self.module_types.validator_id());
+        let ty = &types[id];
+        let mut result = TypeComponentInstance::default();
+        for (name, ty) in ty.exports.iter() {
+            result.exports.insert(
+                name.clone(),
+                self.convert_component_entity_type(types, *ty)?,
+            );
+        }
+        Ok(self.component_types.component_instances.push(result))
+    }
+
+    fn convert_module(
+        &mut self,
+        types: types::TypesRef<'_>,
+        id: types::ComponentCoreModuleTypeId,
+    ) -> Result<TypeModuleIndex> {
+        assert_eq!(types.id(), self.module_types.validator_id());
+        let ty = &types[id];
+        let mut result = TypeModule::default();
+        for ((module, field), ty) in ty.imports.iter() {
+            result.imports.insert(
+                (module.clone(), field.clone()),
+                self.entity_type(types, ty)?,
+            );
+        }
+        for (name, ty) in ty.exports.iter() {
+            result
+                .exports
+                .insert(name.clone(), self.entity_type(types, ty)?);
+        }
+        Ok(self.component_types.modules.push(result))
+    }
+
+    fn entity_type(
+        &mut self,
+        types: types::TypesRef<'_>,
+        ty: &types::EntityType,
+    ) -> Result<EntityType> {
+        assert_eq!(types.id(), self.module_types.validator_id());
+        Ok(match ty {
+            types::EntityType::Func(id) => EntityType::Function({
+                let module = Module::default();
+                self.module_types_builder_mut()
+                    .intern_type(&module, types, *id)?
+                    .into()
+            }),
+            types::EntityType::Table(ty) => EntityType::Table(self.convert_table_type(ty)),
+            types::EntityType::Memory(ty) => EntityType::Memory(ty.clone().into()),
+            types::EntityType::Global(ty) => EntityType::Global(self.convert_global_type(ty)),
+            types::EntityType::Tag(_) => bail!("exceptions proposal not implemented"),
+        })
+    }
+
+    fn defined_type(
+        &mut self,
+        types: types::TypesRef<'_>,
+        id: types::ComponentDefinedTypeId,
+    ) -> Result<InterfaceType> {
+        assert_eq!(types.id(), self.module_types.validator_id());
+        let ret = match &types[id] {
+            types::ComponentDefinedType::Primitive(ty) => ty.into(),
+            types::ComponentDefinedType::Record(e) => {
+                InterfaceType::Record(self.record_type(types, e)?)
+            }
+            types::ComponentDefinedType::Variant(e) => {
+                InterfaceType::Variant(self.variant_type(types, e)?)
+            }
+            types::ComponentDefinedType::List(e) => InterfaceType::List(self.list_type(types, e)?),
+            types::ComponentDefinedType::Tuple(e) => {
+                InterfaceType::Tuple(self.tuple_type(types, e)?)
+            }
+            types::ComponentDefinedType::Flags(e) => InterfaceType::Flags(self.flags_type(e)),
+            types::ComponentDefinedType::Enum(e) => InterfaceType::Enum(self.enum_type(e)),
+            types::ComponentDefinedType::Option(e) => {
+                InterfaceType::Option(self.option_type(types, e)?)
+            }
+            types::ComponentDefinedType::Result { ok, err } => {
+                InterfaceType::Result(self.result_type(types, ok, err)?)
+            }
+            types::ComponentDefinedType::Own(r) => {
+                InterfaceType::Own(self.resource_id(r.resource()))
+            }
+            types::ComponentDefinedType::Borrow(r) => {
+                InterfaceType::Borrow(self.resource_id(r.resource()))
+            }
+        };
+        let info = self.type_information(&ret);
+        if info.depth > MAX_TYPE_DEPTH {
+            bail!("type nesting is too deep");
+        }
+        Ok(ret)
+    }
+
+    fn valtype(
+        &mut self,
+        types: types::TypesRef<'_>,
+        ty: &types::ComponentValType,
+    ) -> Result<InterfaceType> {
+        assert_eq!(types.id(), self.module_types.validator_id());
+        match ty {
+            types::ComponentValType::Primitive(p) => Ok(p.into()),
+            types::ComponentValType::Type(id) => self.defined_type(types, *id),
+        }
+    }
+
+    fn record_type(
+        &mut self,
+        types: types::TypesRef<'_>,
+        ty: &types::RecordType,
+    ) -> Result<TypeRecordIndex> {
+        assert_eq!(types.id(), self.module_types.validator_id());
+        let fields = ty
+            .fields
+            .iter()
+            .map(|(name, ty)| {
+                Ok(RecordField {
+                    name: name.to_string(),
+                    ty: self.valtype(types, ty)?,
+                })
+            })
+            .collect::<Result<Box<[_]>>>()?;
+        let abi = CanonicalAbiInfo::record(
+            fields
+                .iter()
+                .map(|field| self.component_types.canonical_abi(&field.ty)),
+        );
+        Ok(self.add_record_type(TypeRecord { fields, abi }))
+    }
+
+    fn variant_type(
+        &mut self,
+        types: types::TypesRef<'_>,
+        ty: &types::VariantType,
+    ) -> Result<TypeVariantIndex> {
+        assert_eq!(types.id(), self.module_types.validator_id());
+        let cases = ty
+            .cases
+            .iter()
+            .map(|(name, case)| {
+                // FIXME: need to implement `refines`, not sure what that
+                // is at this time.
+                if case.refines.is_some() {
+                    bail!("refines is not supported at this time");
+                }
+                Ok((
+                    name.to_string(),
+                    match &case.ty.as_ref() {
+                        Some(ty) => Some(self.valtype(types, ty)?),
+                        None => None,
+                    },
+                ))
+            })
+            .collect::<Result<IndexMap<_, _>>>()?;
+        let (info, abi) = VariantInfo::new(
+            cases
+                .iter()
+                .map(|(_, c)| c.as_ref().map(|ty| self.component_types.canonical_abi(ty))),
+        );
+        Ok(self.add_variant_type(TypeVariant { cases, abi, info }))
+    }
+
+    fn tuple_type(
+        &mut self,
+        types: types::TypesRef<'_>,
+        ty: &types::TupleType,
+    ) -> Result<TypeTupleIndex> {
+        assert_eq!(types.id(), self.module_types.validator_id());
+        let types = ty
+            .types
+            .iter()
+            .map(|ty| self.valtype(types, ty))
+            .collect::<Result<Box<[_]>>>()?;
+        Ok(self.new_tuple_type(types))
+    }
+
+    fn new_tuple_type(&mut self, types: Box<[InterfaceType]>) -> TypeTupleIndex {
+        let abi = CanonicalAbiInfo::record(
+            types
+                .iter()
+                .map(|ty| self.component_types.canonical_abi(ty)),
+        );
+        self.add_tuple_type(TypeTuple { types, abi })
+    }
+
+    fn flags_type(&mut self, flags: &wasmparser::map::IndexSet<KebabString>) -> TypeFlagsIndex {
+        let flags = TypeFlags {
+            names: flags.iter().map(|s| s.to_string()).collect(),
+            abi: CanonicalAbiInfo::flags(flags.len()),
+        };
+        self.add_flags_type(flags)
+    }
+
+    fn enum_type(&mut self, variants: &wasmparser::map::IndexSet<KebabString>) -> TypeEnumIndex {
+        let names = variants
+            .iter()
+            .map(|s| s.to_string())
+            .collect::<IndexSet<_>>();
+        let (info, abi) = VariantInfo::new(names.iter().map(|_| None));
+        self.add_enum_type(TypeEnum { names, abi, info })
+    }
+
+    fn option_type(
+        &mut self,
+        types: types::TypesRef<'_>,
+        ty: &types::ComponentValType,
+    ) -> Result<TypeOptionIndex> {
+        assert_eq!(types.id(), self.module_types.validator_id());
+        let ty = self.valtype(types, ty)?;
+        let (info, abi) = VariantInfo::new([None, Some(self.component_types.canonical_abi(&ty))]);
+        Ok(self.add_option_type(TypeOption { ty, abi, info }))
+    }
+
+    fn result_type(
+        &mut self,
+        types: types::TypesRef<'_>,
+        ok: &Option<types::ComponentValType>,
+        err: &Option<types::ComponentValType>,
+    ) -> Result<TypeResultIndex> {
+        assert_eq!(types.id(), self.module_types.validator_id());
+        let ok = match ok {
+            Some(ty) => Some(self.valtype(types, ty)?),
+            None => None,
+        };
+        let err = match err {
+            Some(ty) => Some(self.valtype(types, ty)?),
+            None => None,
+        };
+        let (info, abi) = VariantInfo::new([
+            ok.as_ref().map(|t| self.component_types.canonical_abi(t)),
+            err.as_ref().map(|t| self.component_types.canonical_abi(t)),
+        ]);
+        Ok(self.add_result_type(TypeResult { ok, err, abi, info }))
+    }
+
+    fn list_type(
+        &mut self,
+        types: types::TypesRef<'_>,
+        ty: &types::ComponentValType,
+    ) -> Result<TypeListIndex> {
+        assert_eq!(types.id(), self.module_types.validator_id());
+        let element = self.valtype(types, ty)?;
+        Ok(self.add_list_type(TypeList { element }))
+    }
+
+    /// Converts a wasmparser `id`, which must point to a resource, to its
+    /// corresponding `TypeResourceTableIndex`.
+    pub fn resource_id(&mut self, id: types::ResourceId) -> TypeResourceTableIndex {
+        self.resources.convert(id, &mut self.component_types)
+    }
+
+    /// Interns a new function type within this type information.
+    pub fn add_func_type(&mut self, ty: TypeFunc) -> TypeFuncIndex {
+        intern(&mut self.functions, &mut self.component_types.functions, ty)
+    }
+
+    /// Interns a new record type within this type information.
+    pub fn add_record_type(&mut self, ty: TypeRecord) -> TypeRecordIndex {
+        intern_and_fill_flat_types!(self, records, ty)
+    }
+
+    /// Interns a new flags type within this type information.
+    pub fn add_flags_type(&mut self, ty: TypeFlags) -> TypeFlagsIndex {
+        intern_and_fill_flat_types!(self, flags, ty)
+    }
+
+    /// Interns a new tuple type within this type information.
+    pub fn add_tuple_type(&mut self, ty: TypeTuple) -> TypeTupleIndex {
+        intern_and_fill_flat_types!(self, tuples, ty)
+    }
+
+    /// Interns a new variant type within this type information.
+    pub fn add_variant_type(&mut self, ty: TypeVariant) -> TypeVariantIndex {
+        intern_and_fill_flat_types!(self, variants, ty)
+    }
+
+    /// Interns a new enum type within this type information.
+    pub fn add_enum_type(&mut self, ty: TypeEnum) -> TypeEnumIndex {
+        intern_and_fill_flat_types!(self, enums, ty)
+    }
+
+    /// Interns a new option type within this type information.
+    pub fn add_option_type(&mut self, ty: TypeOption) -> TypeOptionIndex {
+        intern_and_fill_flat_types!(self, options, ty)
+    }
+
+    /// Interns a new result type within this type information.
+    pub fn add_result_type(&mut self, ty: TypeResult) -> TypeResultIndex {
+        intern_and_fill_flat_types!(self, results, ty)
+    }
+
+    /// Interns a new type within this type information.
+    pub fn add_list_type(&mut self, ty: TypeList) -> TypeListIndex {
+        intern_and_fill_flat_types!(self, lists, ty)
+    }
+
+    /// Returns the canonical ABI information about the specified type.
+    pub fn canonical_abi(&self, ty: &InterfaceType) -> &CanonicalAbiInfo {
+        self.component_types.canonical_abi(ty)
+    }
+
+    /// Returns the "flat types" for the given interface type used in the
+    /// canonical ABI.
+    ///
+    /// Returns `None` if the type is too large to be represented via flat types
+    /// in the canonical abi.
+    pub fn flat_types(&self, ty: &InterfaceType) -> Option<FlatTypes<'_>> {
+        self.type_information(ty).flat.as_flat_types()
+    }
+
+    /// Returns whether the type specified contains any borrowed resources
+    /// within it.
+    pub fn ty_contains_borrow_resource(&self, ty: &InterfaceType) -> bool {
+        self.type_information(ty).has_borrow
+    }
+
+    fn type_information(&self, ty: &InterfaceType) -> &TypeInformation {
+        match ty {
+            InterfaceType::U8
+            | InterfaceType::S8
+            | InterfaceType::Bool
+            | InterfaceType::U16
+            | InterfaceType::S16
+            | InterfaceType::U32
+            | InterfaceType::S32
+            | InterfaceType::Char
+            | InterfaceType::Own(_) => {
+                static INFO: TypeInformation = TypeInformation::primitive(FlatType::I32);
+                &INFO
+            }
+            InterfaceType::Borrow(_) => {
+                static INFO: TypeInformation = {
+                    let mut info = TypeInformation::primitive(FlatType::I32);
+                    info.has_borrow = true;
+                    info
+                };
+                &INFO
+            }
+            InterfaceType::U64 | InterfaceType::S64 => {
+                static INFO: TypeInformation = TypeInformation::primitive(FlatType::I64);
+                &INFO
+            }
+            InterfaceType::Float32 => {
+                static INFO: TypeInformation = TypeInformation::primitive(FlatType::F32);
+                &INFO
+            }
+            InterfaceType::Float64 => {
+                static INFO: TypeInformation = TypeInformation::primitive(FlatType::F64);
+                &INFO
+            }
+            InterfaceType::String => {
+                static INFO: TypeInformation = TypeInformation::string();
+                &INFO
+            }
+
+            InterfaceType::List(i) => &self.type_info.lists[*i],
+            InterfaceType::Record(i) => &self.type_info.records[*i],
+            InterfaceType::Variant(i) => &self.type_info.variants[*i],
+            InterfaceType::Tuple(i) => &self.type_info.tuples[*i],
+            InterfaceType::Flags(i) => &self.type_info.flags[*i],
+            InterfaceType::Enum(i) => &self.type_info.enums[*i],
+            InterfaceType::Option(i) => &self.type_info.options[*i],
+            InterfaceType::Result(i) => &self.type_info.results[*i],
+        }
+    }
+}
+
+impl TypeConvert for ComponentTypesBuilder {
+    fn lookup_heap_type(&self, _index: wasmparser::UnpackedIndex) -> WasmHeapType {
+        panic!("heap types are not supported yet")
+    }
+}
+
+fn intern<T, U>(map: &mut HashMap<T, U>, list: &mut PrimaryMap<U, T>, item: T) -> U
+where
+    T: Hash + Clone + Eq,
+    U: Copy + EntityRef,
+{
+    if let Some(idx) = map.get(&item) {
+        return *idx;
+    }
+    let idx = list.push(item.clone());
+    map.insert(item, idx);
+    return idx;
+}
+
+struct FlatTypesStorage {
+    // This could be represented as `Vec<FlatType>` but on 64-bit architectures
+    // that's 24 bytes. Otherwise `FlatType` is 1 byte large and
+    // `MAX_FLAT_TYPES` is 16, so it should ideally be more space-efficient to
+    // use a flat array instead of a heap-based vector.
+    memory32: [FlatType; MAX_FLAT_TYPES],
+    memory64: [FlatType; MAX_FLAT_TYPES],
+
+    // Tracks the number of flat types pushed into this storage. If this is
+    // `MAX_FLAT_TYPES + 1` then this storage represents an un-reprsentable
+    // type in flat types.
+    len: u8,
+}
+
+impl FlatTypesStorage {
+    const fn new() -> FlatTypesStorage {
+        FlatTypesStorage {
+            memory32: [FlatType::I32; MAX_FLAT_TYPES],
+            memory64: [FlatType::I32; MAX_FLAT_TYPES],
+            len: 0,
+        }
+    }
+
+    fn as_flat_types(&self) -> Option<FlatTypes<'_>> {
+        let len = usize::from(self.len);
+        if len > MAX_FLAT_TYPES {
+            assert_eq!(len, MAX_FLAT_TYPES + 1);
+            None
+        } else {
+            Some(FlatTypes {
+                memory32: &self.memory32[..len],
+                memory64: &self.memory64[..len],
+            })
+        }
+    }
+
+    /// Pushes a new flat type into this list using `t32` for 32-bit memories
+    /// and `t64` for 64-bit memories.
+    ///
+    /// Returns whether the type was actually pushed or whether this list of
+    /// flat types just exceeded the maximum meaning that it is now
+    /// unrepresentable with a flat list of types.
+    fn push(&mut self, t32: FlatType, t64: FlatType) -> bool {
+        let len = usize::from(self.len);
+        if len < MAX_FLAT_TYPES {
+            self.memory32[len] = t32;
+            self.memory64[len] = t64;
+            self.len += 1;
+            true
+        } else {
+            // If this was the first one to go over then flag the length as
+            // being incompatible with a flat representation.
+            if len == MAX_FLAT_TYPES {
+                self.len += 1;
+            }
+            false
+        }
+    }
+}
+
+impl FlatType {
+    fn join(&mut self, other: FlatType) {
+        if *self == other {
+            return;
+        }
+        *self = match (*self, other) {
+            (FlatType::I32, FlatType::F32) | (FlatType::F32, FlatType::I32) => FlatType::I32,
+            _ => FlatType::I64,
+        };
+    }
+}
+
+#[derive(Default)]
+struct TypeInformationCache {
+    records: PrimaryMap<TypeRecordIndex, TypeInformation>,
+    variants: PrimaryMap<TypeVariantIndex, TypeInformation>,
+    tuples: PrimaryMap<TypeTupleIndex, TypeInformation>,
+    enums: PrimaryMap<TypeEnumIndex, TypeInformation>,
+    flags: PrimaryMap<TypeFlagsIndex, TypeInformation>,
+    options: PrimaryMap<TypeOptionIndex, TypeInformation>,
+    results: PrimaryMap<TypeResultIndex, TypeInformation>,
+    lists: PrimaryMap<TypeListIndex, TypeInformation>,
+}
+
+struct TypeInformation {
+    depth: u32,
+    flat: FlatTypesStorage,
+    has_borrow: bool,
+}
+
+impl TypeInformation {
+    const fn new() -> TypeInformation {
+        TypeInformation {
+            depth: 0,
+            flat: FlatTypesStorage::new(),
+            has_borrow: false,
+        }
+    }
+
+    const fn primitive(flat: FlatType) -> TypeInformation {
+        let mut info = TypeInformation::new();
+        info.depth = 1;
+        info.flat.memory32[0] = flat;
+        info.flat.memory64[0] = flat;
+        info.flat.len = 1;
+        info
+    }
+
+    const fn string() -> TypeInformation {
+        let mut info = TypeInformation::new();
+        info.depth = 1;
+        info.flat.memory32[0] = FlatType::I32;
+        info.flat.memory32[1] = FlatType::I32;
+        info.flat.memory64[0] = FlatType::I64;
+        info.flat.memory64[1] = FlatType::I64;
+        info.flat.len = 2;
+        info
+    }
+
+    /// Builds up all flat types internally using the specified representation
+    /// for all of the component fields of the record.
+    fn build_record<'a>(&mut self, types: impl Iterator<Item = &'a TypeInformation>) {
+        self.depth = 1;
+        for info in types {
+            self.depth = self.depth.max(1 + info.depth);
+            self.has_borrow = self.has_borrow || info.has_borrow;
+            match info.flat.as_flat_types() {
+                Some(types) => {
+                    for (t32, t64) in types.memory32.iter().zip(types.memory64) {
+                        if !self.flat.push(*t32, *t64) {
+                            break;
+                        }
+                    }
+                }
+                None => {
+                    self.flat.len = u8::try_from(MAX_FLAT_TYPES + 1).unwrap();
+                }
+            }
+        }
+    }
+
+    /// Builds up the flat types used to represent a `variant` which notably
+    /// handles "join"ing types together so each case is representable as a
+    /// single flat list of types.
+    ///
+    /// The iterator item is:
+    ///
+    /// * `None` - no payload for this case
+    /// * `Some(None)` - this case has a payload but can't be represented with
+    ///   flat types
+    /// * `Some(Some(types))` - this case has a payload and is represented with
+    ///   the types specified in the flat representation.
+    fn build_variant<'a, I>(&mut self, cases: I)
+    where
+        I: IntoIterator<Item = Option<&'a TypeInformation>>,
+    {
+        let cases = cases.into_iter();
+        self.flat.push(FlatType::I32, FlatType::I32);
+        self.depth = 1;
+
+        for info in cases {
+            let info = match info {
+                Some(info) => info,
+                // If this case doesn't have a payload then it doesn't change
+                // the depth/flat representation
+                None => continue,
+            };
+            self.depth = self.depth.max(1 + info.depth);
+            self.has_borrow = self.has_borrow || info.has_borrow;
+
+            // If this variant is already unrepresentable in a flat
+            // representation then this can be skipped.
+            if usize::from(self.flat.len) > MAX_FLAT_TYPES {
+                continue;
+            }
+
+            let types = match info.flat.as_flat_types() {
+                Some(types) => types,
+                // If this case isn't representable with a flat list of types
+                // then this variant also isn't representable.
+                None => {
+                    self.flat.len = u8::try_from(MAX_FLAT_TYPES + 1).unwrap();
+                    continue;
+                }
+            };
+            // If the case used all of the flat types then the discriminant
+            // added for this variant means that this variant is no longer
+            // representable.
+            if types.memory32.len() >= MAX_FLAT_TYPES {
+                self.flat.len = u8::try_from(MAX_FLAT_TYPES + 1).unwrap();
+                continue;
+            }
+            let dst = self
+                .flat
+                .memory32
+                .iter_mut()
+                .zip(&mut self.flat.memory64)
+                .skip(1);
+            for (i, ((t32, t64), (dst32, dst64))) in types
+                .memory32
+                .iter()
+                .zip(types.memory64)
+                .zip(dst)
+                .enumerate()
+            {
+                if i + 1 < usize::from(self.flat.len) {
+                    // If this index hs already been set by some previous case
+                    // then the types are joined together.
+                    dst32.join(*t32);
+                    dst64.join(*t64);
+                } else {
+                    // Otherwise if this is the first time that the
+                    // representation has gotten this large then the destination
+                    // is simply whatever the type is. The length is also
+                    // increased here to indicate this.
+                    self.flat.len += 1;
+                    *dst32 = *t32;
+                    *dst64 = *t64;
+                }
+            }
+        }
+    }
+
+    fn records(&mut self, types: &ComponentTypesBuilder, ty: &TypeRecord) {
+        self.build_record(ty.fields.iter().map(|f| types.type_information(&f.ty)));
+    }
+
+    fn tuples(&mut self, types: &ComponentTypesBuilder, ty: &TypeTuple) {
+        self.build_record(ty.types.iter().map(|t| types.type_information(t)));
+    }
+
+    fn enums(&mut self, _types: &ComponentTypesBuilder, _ty: &TypeEnum) {
+        self.depth = 1;
+        self.flat.push(FlatType::I32, FlatType::I32);
+    }
+
+    fn flags(&mut self, _types: &ComponentTypesBuilder, ty: &TypeFlags) {
+        self.depth = 1;
+        match FlagsSize::from_count(ty.names.len()) {
+            FlagsSize::Size0 => {}
+            FlagsSize::Size1 | FlagsSize::Size2 => {
+                self.flat.push(FlatType::I32, FlatType::I32);
+            }
+            FlagsSize::Size4Plus(n) => {
+                for _ in 0..n {
+                    self.flat.push(FlatType::I32, FlatType::I32);
+                }
+            }
+        }
+    }
+
+    fn variants(&mut self, types: &ComponentTypesBuilder, ty: &TypeVariant) {
+        self.build_variant(
+            ty.cases
+                .iter()
+                .map(|(_, c)| c.as_ref().map(|ty| types.type_information(ty))),
+        )
+    }
+
+    fn results(&mut self, types: &ComponentTypesBuilder, ty: &TypeResult) {
+        self.build_variant([
+            ty.ok.as_ref().map(|ty| types.type_information(ty)),
+            ty.err.as_ref().map(|ty| types.type_information(ty)),
+        ])
+    }
+
+    fn options(&mut self, types: &ComponentTypesBuilder, ty: &TypeOption) {
+        self.build_variant([None, Some(types.type_information(&ty.ty))]);
+    }
+
+    fn lists(&mut self, types: &ComponentTypesBuilder, ty: &TypeList) {
+        *self = TypeInformation::string();
+        let info = types.type_information(&ty.element);
+        self.depth += info.depth;
+        self.has_borrow = info.has_borrow;
+    }
+}
diff --git a/crates/environ/src/component/types/resources.rs b/crates/environ/src/component/types_builder/resources.rs
similarity index 98%
rename from crates/environ/src/component/types/resources.rs
rename to crates/environ/src/component/types_builder/resources.rs
index 21d42b20e..93df0ca2d 100644
--- a/crates/environ/src/component/types/resources.rs
+++ b/crates/environ/src/component/types_builder/resources.rs
@@ -152,9 +152,7 @@ impl ResourcesBuilder {
             .or_insert_with(|| {
                 let ty = self.resource_id_to_resource_index[&id];
                 let instance = self.current_instance.expect("current instance not set");
-                types
-                    .resource_tables
-                    .push(TypeResourceTable { ty, instance })
+                types.push_resource_table(TypeResourceTable { ty, instance })
             })
     }
 
diff --git a/crates/environ/src/lib.rs b/crates/environ/src/lib.rs
index 11b03adab..aa9334e20 100644
--- a/crates/environ/src/lib.rs
+++ b/crates/environ/src/lib.rs
@@ -39,11 +39,7 @@ pub use object;
 #[cfg(feature = "compile")]
 mod compile;
 #[cfg(feature = "compile")]
-mod module_environ;
-#[cfg(feature = "compile")]
 pub use crate::compile::*;
-#[cfg(feature = "compile")]
-pub use crate::module_environ::*;
 
 #[cfg(feature = "component-model")]
 pub mod component;
diff --git a/crates/environ/src/module_types.rs b/crates/environ/src/module_types.rs
index 6005b7030..cf9eb6a28 100644
--- a/crates/environ/src/module_types.rs
+++ b/crates/environ/src/module_types.rs
@@ -1,15 +1,7 @@
-use crate::{Module, PrimaryMap, TypeConvert, TypeIndex, WasmHeapType};
-use cranelift_entity::EntityRef;
+use crate::PrimaryMap;
 use serde_derive::{Deserialize, Serialize};
-use std::{
-    collections::HashMap,
-    ops::{Index, Range},
-};
-use wasmparser::{UnpackedIndex, Validator, ValidatorId};
-use wasmtime_types::{
-    EngineOrModuleTypeIndex, ModuleInternedRecGroupIndex, ModuleInternedTypeIndex, WasmResult,
-    WasmSubType,
-};
+use std::ops::{Index, Range};
+use wasmtime_types::{ModuleInternedRecGroupIndex, ModuleInternedTypeIndex, WasmSubType};
 
 /// All types used in a core wasm module.
 ///
@@ -55,296 +47,45 @@ impl ModuleTypes {
         let range = &self.rec_groups[rec_group];
         (range.start.as_u32()..range.end.as_u32()).map(|i| ModuleInternedTypeIndex::from_u32(i))
     }
-}
-
-impl Index<ModuleInternedTypeIndex> for ModuleTypes {
-    type Output = WasmSubType;
-
-    fn index(&self, sig: ModuleInternedTypeIndex) -> &WasmSubType {
-        &self.wasm_types[sig]
-    }
-}
-
-/// A type marking the start of a recursion group's definition.
-///
-/// This is initialized by `ModuleTypesBuilder::start_rec_group` and then
-/// finished in `ModuleTypes::end_rec_group` after all of the types in the rec
-/// group have been defined.
-struct RecGroupStart {
-    rec_group_index: ModuleInternedRecGroupIndex,
-    start: ModuleInternedTypeIndex,
-    end: ModuleInternedTypeIndex,
-}
-
-/// A builder for [`ModuleTypes`].
-pub struct ModuleTypesBuilder {
-    /// The ID of the validator that this builder is configured for. Using a
-    /// different validator, or multiple validators, with this builder would
-    /// result in silliness because our `wasmparser::types::*Id`s are only
-    /// unique within the context of a particular validator. Getting this wrong
-    /// could result in generating calls to functions of the wrong type, for
-    /// example. So therefore we always assert that a builder instances is only
-    /// ever paired with a particular validator context.
-    validator_id: ValidatorId,
-
-    /// The canonicalized and deduplicated set of types we are building.
-    types: ModuleTypes,
-
-    /// A map from already-interned `wasmparser` types to their corresponding
-    /// Wasmtime type.
-    wasmparser_to_wasmtime: HashMap<wasmparser::types::CoreTypeId, ModuleInternedTypeIndex>,
-
-    /// The set of recursion groups we have already seen and interned.
-    already_seen: HashMap<wasmparser::types::RecGroupId, ModuleInternedRecGroupIndex>,
-
-    /// If we are in the middle of defining a recursion group, this is the
-    /// metadata about the recursion group we started defining.
-    defining_rec_group: Option<RecGroupStart>,
-}
 
-impl ModuleTypesBuilder {
-    /// Construct a new `ModuleTypesBuilder` using the given validator.
-    pub fn new(validator: &Validator) -> Self {
-        Self {
-            validator_id: validator.id(),
-            types: ModuleTypes::default(),
-            wasmparser_to_wasmtime: HashMap::default(),
-            already_seen: HashMap::default(),
-            defining_rec_group: None,
-        }
+    /// Returns the number of types interned.
+    pub fn len_types(&self) -> usize {
+        self.wasm_types.len()
     }
 
-    /// Reserves space for `amt` more type signatures.
-    pub fn reserve_wasm_signatures(&mut self, amt: usize) {
-        self.types.wasm_types.reserve(amt);
-        self.wasmparser_to_wasmtime.reserve(amt);
-        self.already_seen.reserve(amt);
+    /// Adds a new type to this interned list of types.
+    pub fn push(&mut self, ty: WasmSubType) -> ModuleInternedTypeIndex {
+        self.wasm_types.push(ty)
     }
 
-    /// Get the id of the validator that this builder is configured for.
-    pub fn validator_id(&self) -> ValidatorId {
-        self.validator_id
-    }
-
-    /// Intern a recursion group and all of its types into this builder.
-    ///
-    /// If the recursion group has already been interned, then it is reused.
-    ///
-    /// Panics if given types from a different validator than the one that this
-    /// builder is associated with.
-    pub fn intern_rec_group(
-        &mut self,
-        module: &Module,
-        validator_types: wasmparser::types::TypesRef<'_>,
-        rec_group_id: wasmparser::types::RecGroupId,
-    ) -> WasmResult<ModuleInternedRecGroupIndex> {
-        assert_eq!(validator_types.id(), self.validator_id);
-
-        if let Some(interned) = self.already_seen.get(&rec_group_id) {
-            return Ok(*interned);
-        }
-
-        self.define_new_rec_group(module, validator_types, rec_group_id)
-    }
-
-    /// Define a new recursion group that we haven't already interned.
-    fn define_new_rec_group(
-        &mut self,
-        module: &Module,
-        validator_types: wasmparser::types::TypesRef<'_>,
-        rec_group_id: wasmparser::types::RecGroupId,
-    ) -> WasmResult<ModuleInternedRecGroupIndex> {
-        assert_eq!(validator_types.id(), self.validator_id);
-
-        self.start_rec_group(
-            validator_types,
-            validator_types.rec_group_elements(rec_group_id),
-        );
-
-        for id in validator_types.rec_group_elements(rec_group_id) {
-            let ty = &validator_types[id];
-            let wasm_ty = WasmparserTypeConverter::new(self, module).convert_sub_type(ty)?;
-            self.wasm_sub_type_in_rec_group(id, wasm_ty);
-        }
-
-        Ok(self.end_rec_group(rec_group_id))
-    }
-
-    /// Start defining a recursion group.
-    fn start_rec_group(
+    /// Adds a new rec group to this interned list of types.
+    pub fn push_rec_group(
         &mut self,
-        validator_types: wasmparser::types::TypesRef<'_>,
-        elems: impl ExactSizeIterator<Item = wasmparser::types::CoreTypeId>,
-    ) {
-        log::trace!("Starting rec group of length {}", elems.len());
-
-        assert!(self.defining_rec_group.is_none());
-        assert_eq!(validator_types.id(), self.validator_id);
-
-        // Eagerly define the reverse map's entries for this rec group's types
-        // so that we can use them when converting `wasmparser` types to our
-        // types.
-        let len = elems.len();
-        for (i, wasmparser_id) in elems.enumerate() {
-            let interned = ModuleInternedTypeIndex::new(self.types.wasm_types.len() + i);
-            log::trace!(
-                "Reserving {interned:?} for {wasmparser_id:?} = {:?}",
-                validator_types[wasmparser_id]
-            );
-
-            let old_entry = self.wasmparser_to_wasmtime.insert(wasmparser_id, interned);
-            debug_assert_eq!(
-                old_entry, None,
-                "should not have already inserted {wasmparser_id:?}"
-            );
-        }
-
-        self.defining_rec_group = Some(RecGroupStart {
-            rec_group_index: self.types.rec_groups.next_key(),
-            start: self.types.wasm_types.next_key(),
-            end: ModuleInternedTypeIndex::new(self.types.wasm_types.len() + len),
-        });
-    }
-
-    /// Finish defining a recursion group.
-    fn end_rec_group(
-        &mut self,
-        rec_group_id: wasmparser::types::RecGroupId,
+        range: Range<ModuleInternedTypeIndex>,
     ) -> ModuleInternedRecGroupIndex {
-        let RecGroupStart {
-            rec_group_index,
-            start,
-            end,
-        } = self
-            .defining_rec_group
-            .take()
-            .expect("should be defining a rec group");
-
-        log::trace!("Ending rec group {start:?}..{end:?}");
-
-        debug_assert!(start.index() < self.types.wasm_types.len());
-        debug_assert_eq!(
-            end,
-            self.types.wasm_types.next_key(),
-            "should have defined the number of types declared in `start_rec_group`"
-        );
-
-        let idx = self.types.rec_groups.push(start..end);
-        debug_assert_eq!(idx, rec_group_index);
-
-        self.already_seen.insert(rec_group_id, rec_group_index);
-        rec_group_index
+        self.rec_groups.push(range)
     }
 
-    /// Intern a type into this builder and get its Wasmtime index.
-    ///
-    /// This will intern not only the single given type, but the type's entire
-    /// rec group. This helper method is provided as a convenience so that
-    /// callers don't have to get the type's rec group, intern the rec group,
-    /// and then look up the Wasmtime index for the original type themselves.
-    pub fn intern_type(
-        &mut self,
-        module: &Module,
-        validator_types: wasmparser::types::TypesRef<'_>,
-        id: wasmparser::types::CoreTypeId,
-    ) -> WasmResult<ModuleInternedTypeIndex> {
-        assert!(self.defining_rec_group.is_none());
-        assert_eq!(validator_types.id(), self.validator_id);
-
-        let rec_group_id = validator_types.rec_group_id_of(id);
-        debug_assert!(validator_types
-            .rec_group_elements(rec_group_id)
-            .any(|e| e == id));
-
-        let interned_rec_group = self.intern_rec_group(module, validator_types, rec_group_id)?;
-
-        let interned_type = self.wasmparser_to_wasmtime[&id];
-        debug_assert!(self
-            .rec_group_elements(interned_rec_group)
-            .any(|e| e == interned_type));
-
-        Ok(interned_type)
+    /// Reserves space for `amt` more types.
+    pub fn reserve(&mut self, amt: usize) {
+        self.wasm_types.reserve(amt)
     }
 
-    /// Define a new Wasm type while we are defining a rec group.
-    fn wasm_sub_type_in_rec_group(
-        &mut self,
-        id: wasmparser::types::CoreTypeId,
-        ty: WasmSubType,
-    ) -> ModuleInternedTypeIndex {
-        assert!(
-            self.defining_rec_group.is_some(),
-            "must be defining a rec group to define new types"
-        );
-
-        let module_interned_index = self.types.wasm_types.push(ty);
-        debug_assert_eq!(
-            self.wasmparser_to_wasmtime.get(&id),
-            Some(&module_interned_index),
-            "should have reserved the right module-interned index for this wasmparser type already"
-        );
-
-        module_interned_index
+    /// Returns the next return value of `push_rec_group`.
+    pub fn next_rec_group(&self) -> ModuleInternedRecGroupIndex {
+        self.rec_groups.next_key()
     }
 
-    /// Returns the result [`ModuleTypes`] of this builder.
-    pub fn finish(self) -> ModuleTypes {
-        self.types
-    }
-
-    /// Get the elements within an already-defined rec group.
-    pub fn rec_group_elements(
-        &self,
-        rec_group: ModuleInternedRecGroupIndex,
-    ) -> impl ExactSizeIterator<Item = ModuleInternedTypeIndex> {
-        self.types.rec_group_elements(rec_group)
-    }
-
-    /// Returns an iterator over all the unique wasm types defined thus far
-    /// within this builder.
-    pub fn wasm_types(&self) -> impl Iterator<Item = (ModuleInternedTypeIndex, &WasmSubType)> {
-        self.types.wasm_types()
-    }
-}
-
-// Forward the indexing impl to the internal `ModuleTypes`
-impl<T> Index<T> for ModuleTypesBuilder
-where
-    ModuleTypes: Index<T>,
-{
-    type Output = <ModuleTypes as Index<T>>::Output;
-
-    fn index(&self, sig: T) -> &Self::Output {
-        &self.types[sig]
+    /// Returns the next return value of `push`.
+    pub fn next_ty(&self) -> ModuleInternedTypeIndex {
+        self.wasm_types.next_key()
     }
 }
 
-/// A convert from `wasmparser` types to Wasmtime types.
-pub struct WasmparserTypeConverter<'a> {
-    types: &'a ModuleTypesBuilder,
-    module: &'a Module,
-}
-
-impl<'a> WasmparserTypeConverter<'a> {
-    /// Construct a new type converter from `wasmparser` types to Wasmtime types.
-    pub fn new(types: &'a ModuleTypesBuilder, module: &'a Module) -> Self {
-        Self { types, module }
-    }
-}
+impl Index<ModuleInternedTypeIndex> for ModuleTypes {
+    type Output = WasmSubType;
 
-impl TypeConvert for WasmparserTypeConverter<'_> {
-    fn lookup_heap_type(&self, index: UnpackedIndex) -> WasmHeapType {
-        match index {
-            UnpackedIndex::Id(id) => {
-                let signature = self.types.wasmparser_to_wasmtime[&id];
-                WasmHeapType::ConcreteFunc(EngineOrModuleTypeIndex::Module(signature))
-            }
-            UnpackedIndex::Module(module_index) => {
-                let module_index = TypeIndex::from_u32(module_index);
-                let interned_index = self.module.types[module_index];
-                WasmHeapType::ConcreteFunc(EngineOrModuleTypeIndex::Module(interned_index))
-            }
-            UnpackedIndex::RecGroup(_) => unreachable!(),
-        }
+    fn index(&self, sig: ModuleInternedTypeIndex) -> &WasmSubType {
+        &self.wasm_types[sig]
     }
 }
diff --git a/crates/slab/src/lib.rs b/crates/slab/src/lib.rs
index ca0173496..aa3d213a9 100644
--- a/crates/slab/src/lib.rs
+++ b/crates/slab/src/lib.rs
@@ -120,18 +120,23 @@
 //! }
 //! ```
 
+#![no_std]
 #![forbid(unsafe_code)]
 #![deny(missing_docs, missing_debug_implementations)]
 
-use std::num::NonZeroU32;
+extern crate alloc;
+
+use alloc::vec::Vec;
+use core::fmt;
+use core::num::NonZeroU32;
 
 /// An identifier for an allocated value inside a `slab`.
 #[derive(Clone, Copy, PartialEq, Eq, Hash)]
 #[repr(transparent)]
 pub struct Id(EntryIndex);
 
-impl std::fmt::Debug for Id {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+impl fmt::Debug for Id {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         f.debug_tuple("Id").field(&self.0.index()).finish()
     }
 }
@@ -167,11 +172,11 @@ pub struct Slab<T> {
     len: u32,
 }
 
-impl<T> std::fmt::Debug for Slab<T>
+impl<T> fmt::Debug for Slab<T>
 where
-    T: std::fmt::Debug,
+    T: fmt::Debug,
 {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         f.debug_map().entries(self.iter()).finish()
     }
 }
@@ -218,7 +223,7 @@ impl<T> Default for Slab<T> {
     }
 }
 
-impl<T> std::ops::Index<Id> for Slab<T> {
+impl<T> core::ops::Index<Id> for Slab<T> {
     type Output = T;
 
     #[inline]
@@ -228,7 +233,7 @@ impl<T> std::ops::Index<Id> for Slab<T> {
     }
 }
 
-impl<T> std::ops::IndexMut<Id> for Slab<T> {
+impl<T> core::ops::IndexMut<Id> for Slab<T> {
     #[inline]
     fn index_mut(&mut self, id: Id) -> &mut Self::Output {
         self.get_mut(id)
@@ -282,7 +287,7 @@ impl<T> Slab<T> {
         // we add some amount of minimum additional capacity, since doubling
         // zero capacity isn't useful.
         const MIN_CAPACITY: usize = 16;
-        let additional = std::cmp::max(self.entries.capacity(), MIN_CAPACITY);
+        let additional = core::cmp::max(self.entries.capacity(), MIN_CAPACITY);
         self.reserve(additional);
     }
 
@@ -419,7 +424,7 @@ impl<T> Slab<T> {
     /// deallocate an arbitrary value.
     #[inline]
     pub fn dealloc(&mut self, id: Id) -> T {
-        let entry = std::mem::replace(
+        let entry = core::mem::replace(
             self.entries
                 .get_mut(id.0.index())
                 .expect("id from a different slab"),
@@ -428,7 +433,7 @@ impl<T> Slab<T> {
         match entry {
             Entry::Free { .. } => panic!("attempt to deallocate an entry that is already vacant"),
             Entry::Occupied(value) => {
-                let next_free = std::mem::replace(&mut self.free, Some(id.0));
+                let next_free = core::mem::replace(&mut self.free, Some(id.0));
                 self.entries[id.0.index()] = Entry::Free { next_free };
                 self.len -= 1;
                 value
diff --git a/crates/wit-bindgen/src/lib.rs b/crates/wit-bindgen/src/lib.rs
index d5e0dcf15..2dee30e3e 100644
--- a/crates/wit-bindgen/src/lib.rs
+++ b/crates/wit-bindgen/src/lib.rs
@@ -1391,9 +1391,20 @@ impl<'a> InterfaceGenerator<'a> {
     {
         let info = self.info(id);
 
+        // We use a BTree set to make sure we don't have any duplicates and we have a stable order
+        let additional_derives: BTreeSet<String> = self
+            .gen
+            .opts
+            .additional_derive_attributes
+            .iter()
+            .cloned()
+            .collect();
+
         for (name, mode) in self.modes_of(id) {
             let name = to_rust_upper_camel_case(&name);
 
+            let mut derives = additional_derives.clone();
+
             self.rustdoc(docs);
             let lt = self.lifetime_for(&info, mode);
             self.push_str("#[derive(wasmtime::component::ComponentType)]\n");
@@ -1403,10 +1414,17 @@ impl<'a> InterfaceGenerator<'a> {
             self.push_str("#[derive(wasmtime::component::Lower)]\n");
             self.push_str(&format!("#[component({})]\n", derive_component));
             if info.is_copy() {
-                self.push_str("#[derive(Copy, Clone)]\n");
+                derives.extend(["Copy", "Clone"].into_iter().map(|s| s.to_string()));
             } else if info.is_clone() {
-                self.push_str("#[derive(Clone)]\n");
+                derives.insert("Clone".to_string());
+            }
+
+            if !derives.is_empty() {
+                self.push_str("#[derive(");
+                self.push_str(&derives.into_iter().collect::<Vec<_>>().join(", "));
+                self.push_str(")]\n")
             }
+
             self.push_str(&format!("pub enum {name}"));
             self.print_generics(lt);
             self.push_str("{\n");
diff --git a/winch/codegen/src/isa/aarch64/asm.rs b/winch/codegen/src/isa/aarch64/asm.rs
index bd0e519d9..fb2aedc12 100644
--- a/winch/codegen/src/isa/aarch64/asm.rs
+++ b/winch/codegen/src/isa/aarch64/asm.rs
@@ -1,8 +1,9 @@
 //! Assembler library implementation for Aarch64.
 
 use super::{address::Address, regs};
+use crate::masm::RoundingMode;
 use crate::{masm::OperandSize, reg::Reg};
-use cranelift_codegen::isa::aarch64::inst::{FPUOp2, ScalarSize};
+use cranelift_codegen::isa::aarch64::inst::{FPUOp1, FPUOp2, FpuRoundMode, ScalarSize};
 use cranelift_codegen::{
     ir::{MemFlags, SourceLoc},
     isa::aarch64::inst::{
@@ -264,6 +265,37 @@ impl Assembler {
         self.emit_fpu_rrr(FPUOp2::Min, rm, rn, rd, size);
     }
 
+    /// Float neg with two registers.
+    pub fn fneg_rr(&mut self, rn: Reg, rd: Reg, size: OperandSize) {
+        self.emit_fpu_rr(FPUOp1::Neg, rn, rd, size);
+    }
+
+    /// Float abs with two registers.
+    pub fn fabs_rr(&mut self, rn: Reg, rd: Reg, size: OperandSize) {
+        self.emit_fpu_rr(FPUOp1::Abs, rn, rd, size);
+    }
+
+    /// Float sqrt with two registers.
+    pub fn fsqrt_rr(&mut self, rn: Reg, rd: Reg, size: OperandSize) {
+        self.emit_fpu_rr(FPUOp1::Sqrt, rn, rd, size);
+    }
+
+    /// Float round (ceil, trunc, floor) with two registers.
+    pub fn fround_rr(&mut self, rn: Reg, rd: Reg, mode: RoundingMode, size: OperandSize) {
+        let fpu_mode = match (mode, size) {
+            (RoundingMode::Nearest, OperandSize::S32) => FpuRoundMode::Nearest32,
+            (RoundingMode::Up, OperandSize::S32) => FpuRoundMode::Plus32,
+            (RoundingMode::Down, OperandSize::S32) => FpuRoundMode::Minus32,
+            (RoundingMode::Zero, OperandSize::S32) => FpuRoundMode::Zero32,
+            (RoundingMode::Nearest, OperandSize::S64) => FpuRoundMode::Nearest64,
+            (RoundingMode::Up, OperandSize::S64) => FpuRoundMode::Plus64,
+            (RoundingMode::Down, OperandSize::S64) => FpuRoundMode::Minus64,
+            (RoundingMode::Zero, OperandSize::S64) => FpuRoundMode::Zero64,
+            (m, o) => panic!("Invalid rounding mode or operand size {:?}, {:?}", m, o),
+        };
+        self.emit_fpu_round(fpu_mode, rn, rd)
+    }
+
     /// Return instruction.
     pub fn ret(&mut self) {
         self.emit(Inst::Ret {});
@@ -313,6 +345,23 @@ impl Assembler {
         });
     }
 
+    fn emit_fpu_rr(&mut self, op: FPUOp1, rn: Reg, rd: Reg, size: OperandSize) {
+        self.emit(Inst::FpuRR {
+            fpu_op: op,
+            size: size.into(),
+            rd: Writable::from_reg(rd.into()),
+            rn: rn.into(),
+        });
+    }
+
+    fn emit_fpu_round(&mut self, op: FpuRoundMode, rn: Reg, rd: Reg) {
+        self.emit(Inst::FpuRound {
+            op: op,
+            rd: Writable::from_reg(rd.into()),
+            rn: rn.into(),
+        });
+    }
+
     /// Get a label from the underlying machine code buffer.
     pub fn get_label(&mut self) -> MachLabel {
         self.buffer.get_label()
diff --git a/winch/codegen/src/isa/aarch64/masm.rs b/winch/codegen/src/isa/aarch64/masm.rs
index 3b86dbf7f..39cc2083f 100644
--- a/winch/codegen/src/isa/aarch64/masm.rs
+++ b/winch/codegen/src/isa/aarch64/masm.rs
@@ -316,27 +316,29 @@ impl Masm for MacroAssembler {
         todo!()
     }
 
-    fn float_neg(&mut self, _dst: Reg, _size: OperandSize) {
-        todo!()
+    fn float_neg(&mut self, dst: Reg, size: OperandSize) {
+        self.asm.fneg_rr(dst, dst, size);
     }
 
-    fn float_abs(&mut self, _dst: Reg, _size: OperandSize) {
-        todo!()
+    fn float_abs(&mut self, dst: Reg, size: OperandSize) {
+        self.asm.fabs_rr(dst, dst, size);
     }
 
     fn float_round<F: FnMut(&mut FuncEnv<Self::Ptr>, &mut CodeGenContext, &mut Self)>(
         &mut self,
-        _mode: RoundingMode,
+        mode: RoundingMode,
         _env: &mut FuncEnv<Self::Ptr>,
-        _context: &mut CodeGenContext,
-        _size: OperandSize,
+        context: &mut CodeGenContext,
+        size: OperandSize,
         _fallback: F,
     ) {
-        todo!();
+        let src = context.pop_to_reg(self, None);
+        self.asm.fround_rr(src.into(), src.into(), mode, size);
+        context.stack.push(src.into());
     }
 
-    fn float_sqrt(&mut self, _dst: Reg, _src: Reg, _size: OperandSize) {
-        todo!()
+    fn float_sqrt(&mut self, dst: Reg, src: Reg, size: OperandSize) {
+        self.asm.fsqrt_rr(src, dst, size);
     }
 
     fn and(&mut self, _dst: Reg, _lhs: Reg, _rhs: RegImm, _size: OperandSize) {
diff --git a/winch/codegen/src/masm.rs b/winch/codegen/src/masm.rs
index 4f18842a1..38dbf0297 100644
--- a/winch/codegen/src/masm.rs
+++ b/winch/codegen/src/masm.rs
@@ -425,6 +425,7 @@ impl From<Reg> for RegImm {
     }
 }
 
+#[derive(Debug)]
 pub enum RoundingMode {
     Nearest,
     Up,


Test patch:
diff --git a/ci/build-test-matrix.js b/ci/build-test-matrix.js
index 22c8c8820..92bfedfc2 100644
--- a/ci/build-test-matrix.js
+++ b/ci/build-test-matrix.js
@@ -56,7 +56,7 @@ const array = [
     "isa": "x64"
   },
   {
-    "os": "macos-latest",
+    "os": "macos-13",
     "name": "Test macOS x86_64",
     "filter": "macos-x64",
     "extra_features": "--features wasmtime-wasi-nn/onnx"
diff --git a/crates/component-macro/tests/codegen.rs b/crates/component-macro/tests/codegen.rs
index 09fd32f22..f68f5930b 100644
--- a/crates/component-macro/tests/codegen.rs
+++ b/crates/component-macro/tests/codegen.rs
@@ -531,9 +531,16 @@ mod custom_derives {
             package my:inline;
 
             interface blah {
+                variant abc {
+                    a,
+                    b,
+                    c
+                }
+
                 record foo {
                     field1: string,
-                    field2: list<u32>
+                    field2: list<u32>,
+                    field3: abc
                 }
 
                 bar: func(cool: foo);
@@ -548,7 +555,7 @@ mod custom_derives {
         additional_derives: [serde::Serialize, serde::Deserialize, Hash, Clone, PartialEq, Eq],
     });
 
-    use my::inline::blah::{Foo, Host};
+    use my::inline::blah::{Abc, Foo, Host};
 
     struct X;
 
@@ -558,6 +565,7 @@ mod custom_derives {
             let _blah: HashSet<Foo, RandomState> = HashSet::from_iter([Foo {
                 field1: "hello".to_string(),
                 field2: vec![1, 2, 3],
+                field3: Abc::B,
             }]);
 
             // Check that the attributes from an external crate actually work. If they don't work,
diff --git a/tests/disas/winch/aarch64/f32_abs/f32_abs_const.wat b/tests/disas/winch/aarch64/f32_abs/f32_abs_const.wat
new file mode 100644
index 000000000..cdb328425
--- /dev/null
+++ b/tests/disas/winch/aarch64/f32_abs/f32_abs_const.wat
@@ -0,0 +1,26 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result f32)
+        (f32.const -1.32)
+        (f32.abs)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x10
+;;       mov     x28, sp
+;;       stur    x0, [x28, #8]
+;;       stur    x1, [x28]
+;;       mov     w16, #0xf5c3
+;;       movk    w16, #0xbfa8, lsl #16
+;;       fmov    s0, w16
+;;       fabs    s0, s0
+;;       add     sp, sp, #0x10
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/f32_abs/f32_abs_param.wat b/tests/disas/winch/aarch64/f32_abs/f32_abs_param.wat
new file mode 100644
index 000000000..e798ed68f
--- /dev/null
+++ b/tests/disas/winch/aarch64/f32_abs/f32_abs_param.wat
@@ -0,0 +1,25 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (param f32) (result f32)
+        (local.get 0)
+        (f32.abs)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x18
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x10]
+;;       stur    x1, [x28, #8]
+;;       stur    w0, [x28, #4]
+;;       ldur    w0, [x28, #4]
+;;       fabs    s0, s0
+;;       add     sp, sp, #0x18
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/f32_ceil/f32_ceil_const.wat b/tests/disas/winch/aarch64/f32_ceil/f32_ceil_const.wat
new file mode 100644
index 000000000..3ad47e206
--- /dev/null
+++ b/tests/disas/winch/aarch64/f32_ceil/f32_ceil_const.wat
@@ -0,0 +1,26 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result f32)
+        (f32.const -1.32)
+        (f32.ceil)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x10
+;;       mov     x28, sp
+;;       stur    x0, [x28, #8]
+;;       stur    x1, [x28]
+;;       mov     w16, #0xf5c3
+;;       movk    w16, #0xbfa8, lsl #16
+;;       fmov    s0, w16
+;;       frintp  s0, s0
+;;       add     sp, sp, #0x10
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/f32_ceil/f32_ceil_param.wat b/tests/disas/winch/aarch64/f32_ceil/f32_ceil_param.wat
new file mode 100644
index 000000000..b60ee329c
--- /dev/null
+++ b/tests/disas/winch/aarch64/f32_ceil/f32_ceil_param.wat
@@ -0,0 +1,25 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (param f32) (result f32)
+        (local.get 0)
+        (f32.ceil)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x18
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x10]
+;;       stur    x1, [x28, #8]
+;;       stur    w0, [x28, #4]
+;;       ldur    w0, [x28, #4]
+;;       frintp  s0, s0
+;;       add     sp, sp, #0x18
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/f32_floor/f32_floor_const.wat b/tests/disas/winch/aarch64/f32_floor/f32_floor_const.wat
new file mode 100644
index 000000000..18877fe37
--- /dev/null
+++ b/tests/disas/winch/aarch64/f32_floor/f32_floor_const.wat
@@ -0,0 +1,26 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result f32)
+        (f32.const -1.32)
+        (f32.floor)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x10
+;;       mov     x28, sp
+;;       stur    x0, [x28, #8]
+;;       stur    x1, [x28]
+;;       mov     w16, #0xf5c3
+;;       movk    w16, #0xbfa8, lsl #16
+;;       fmov    s0, w16
+;;       frintm  s0, s0
+;;       add     sp, sp, #0x10
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/f32_floor/f32_floor_param.wat b/tests/disas/winch/aarch64/f32_floor/f32_floor_param.wat
new file mode 100644
index 000000000..f850d6b29
--- /dev/null
+++ b/tests/disas/winch/aarch64/f32_floor/f32_floor_param.wat
@@ -0,0 +1,25 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (param f32) (result f32)
+        (local.get 0)
+        (f32.floor)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x18
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x10]
+;;       stur    x1, [x28, #8]
+;;       stur    w0, [x28, #4]
+;;       ldur    w0, [x28, #4]
+;;       frintm  s0, s0
+;;       add     sp, sp, #0x18
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/f32_nearest/f32_nearest_const.wat b/tests/disas/winch/aarch64/f32_nearest/f32_nearest_const.wat
new file mode 100644
index 000000000..fec543194
--- /dev/null
+++ b/tests/disas/winch/aarch64/f32_nearest/f32_nearest_const.wat
@@ -0,0 +1,26 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result f32)
+        (f32.const -1.32)
+        (f32.nearest)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x10
+;;       mov     x28, sp
+;;       stur    x0, [x28, #8]
+;;       stur    x1, [x28]
+;;       mov     w16, #0xf5c3
+;;       movk    w16, #0xbfa8, lsl #16
+;;       fmov    s0, w16
+;;       frintn  s0, s0
+;;       add     sp, sp, #0x10
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/f32_nearest/f32_nearest_param.wat b/tests/disas/winch/aarch64/f32_nearest/f32_nearest_param.wat
new file mode 100644
index 000000000..fe716327d
--- /dev/null
+++ b/tests/disas/winch/aarch64/f32_nearest/f32_nearest_param.wat
@@ -0,0 +1,25 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (param f32) (result f32)
+        (local.get 0)
+        (f32.nearest)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x18
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x10]
+;;       stur    x1, [x28, #8]
+;;       stur    w0, [x28, #4]
+;;       ldur    w0, [x28, #4]
+;;       frintn  s0, s0
+;;       add     sp, sp, #0x18
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/f32_neg/f32_neg_const.wat b/tests/disas/winch/aarch64/f32_neg/f32_neg_const.wat
new file mode 100644
index 000000000..7998653ea
--- /dev/null
+++ b/tests/disas/winch/aarch64/f32_neg/f32_neg_const.wat
@@ -0,0 +1,26 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result f32)
+        (f32.const -1.32)
+        (f32.neg)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x10
+;;       mov     x28, sp
+;;       stur    x0, [x28, #8]
+;;       stur    x1, [x28]
+;;       mov     w16, #0xf5c3
+;;       movk    w16, #0xbfa8, lsl #16
+;;       fmov    s0, w16
+;;       fneg    s0, s0
+;;       add     sp, sp, #0x10
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/f32_neg/f32_neg_param.wat b/tests/disas/winch/aarch64/f32_neg/f32_neg_param.wat
new file mode 100644
index 000000000..24d6548bb
--- /dev/null
+++ b/tests/disas/winch/aarch64/f32_neg/f32_neg_param.wat
@@ -0,0 +1,25 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (param f32) (result f32)
+        (local.get 0)
+        (f32.neg)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x18
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x10]
+;;       stur    x1, [x28, #8]
+;;       stur    w0, [x28, #4]
+;;       ldur    w0, [x28, #4]
+;;       fneg    s0, s0
+;;       add     sp, sp, #0x18
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/f32_sqrt/f32_sqrt_const.wat b/tests/disas/winch/aarch64/f32_sqrt/f32_sqrt_const.wat
new file mode 100644
index 000000000..4c10b9ed6
--- /dev/null
+++ b/tests/disas/winch/aarch64/f32_sqrt/f32_sqrt_const.wat
@@ -0,0 +1,26 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result f32)
+        (f32.const 1.32)
+        (f32.sqrt)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x10
+;;       mov     x28, sp
+;;       stur    x0, [x28, #8]
+;;       stur    x1, [x28]
+;;       mov     w16, #0xf5c3
+;;       movk    w16, #0x3fa8, lsl #16
+;;       fmov    s0, w16
+;;       fsqrt   s0, s0
+;;       add     sp, sp, #0x10
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/f32_sqrt/f32_sqrt_param.wat b/tests/disas/winch/aarch64/f32_sqrt/f32_sqrt_param.wat
new file mode 100644
index 000000000..9c1e4c4f8
--- /dev/null
+++ b/tests/disas/winch/aarch64/f32_sqrt/f32_sqrt_param.wat
@@ -0,0 +1,25 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (param f32) (result f32)
+        (local.get 0)
+        (f32.sqrt)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x18
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x10]
+;;       stur    x1, [x28, #8]
+;;       stur    w0, [x28, #4]
+;;       ldur    w0, [x28, #4]
+;;       fsqrt   s0, s0
+;;       add     sp, sp, #0x18
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/f32_trunc/f32_trunc_const.wat b/tests/disas/winch/aarch64/f32_trunc/f32_trunc_const.wat
new file mode 100644
index 000000000..263b3e671
--- /dev/null
+++ b/tests/disas/winch/aarch64/f32_trunc/f32_trunc_const.wat
@@ -0,0 +1,26 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result f32)
+        (f32.const -1.32)
+        (f32.trunc)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x10
+;;       mov     x28, sp
+;;       stur    x0, [x28, #8]
+;;       stur    x1, [x28]
+;;       mov     w16, #0xf5c3
+;;       movk    w16, #0xbfa8, lsl #16
+;;       fmov    s0, w16
+;;       frintz  s0, s0
+;;       add     sp, sp, #0x10
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/f32_trunc/f32_trunc_param.wat b/tests/disas/winch/aarch64/f32_trunc/f32_trunc_param.wat
new file mode 100644
index 000000000..3df3e3b03
--- /dev/null
+++ b/tests/disas/winch/aarch64/f32_trunc/f32_trunc_param.wat
@@ -0,0 +1,25 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (param f32) (result f32)
+        (local.get 0)
+        (f32.trunc)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x18
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x10]
+;;       stur    x1, [x28, #8]
+;;       stur    w0, [x28, #4]
+;;       ldur    w0, [x28, #4]
+;;       frintz  s0, s0
+;;       add     sp, sp, #0x18
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/f64_abs/f64_abs_const.wat b/tests/disas/winch/aarch64/f64_abs/f64_abs_const.wat
new file mode 100644
index 000000000..066de1954
--- /dev/null
+++ b/tests/disas/winch/aarch64/f64_abs/f64_abs_const.wat
@@ -0,0 +1,28 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result f64)
+        (f64.const -1.32)
+        (f64.abs)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x10
+;;       mov     x28, sp
+;;       stur    x0, [x28, #8]
+;;       stur    x1, [x28]
+;;       mov     x16, #0x851f
+;;       movk    x16, #0x51eb, lsl #16
+;;       movk    x16, #0x1eb8, lsl #32
+;;       movk    x16, #0xbff5, lsl #48
+;;       fmov    d0, x16
+;;       fabs    d0, d0
+;;       add     sp, sp, #0x10
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/f64_abs/f64_abs_param.wat b/tests/disas/winch/aarch64/f64_abs/f64_abs_param.wat
new file mode 100644
index 000000000..bbc7d51ea
--- /dev/null
+++ b/tests/disas/winch/aarch64/f64_abs/f64_abs_param.wat
@@ -0,0 +1,25 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (param f64) (result f64)
+        (local.get 0)
+        (f64.abs)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x18
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x10]
+;;       stur    x1, [x28, #8]
+;;       stur    x0, [x28]
+;;       ldur    x0, [x28]
+;;       fabs    d0, d0
+;;       add     sp, sp, #0x18
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/f64_ceil/f64_ceil_const.wat b/tests/disas/winch/aarch64/f64_ceil/f64_ceil_const.wat
new file mode 100644
index 000000000..9da2b062e
--- /dev/null
+++ b/tests/disas/winch/aarch64/f64_ceil/f64_ceil_const.wat
@@ -0,0 +1,28 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result f64)
+        (f64.const -1.32)
+        (f64.ceil)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x10
+;;       mov     x28, sp
+;;       stur    x0, [x28, #8]
+;;       stur    x1, [x28]
+;;       mov     x16, #0x851f
+;;       movk    x16, #0x51eb, lsl #16
+;;       movk    x16, #0x1eb8, lsl #32
+;;       movk    x16, #0xbff5, lsl #48
+;;       fmov    d0, x16
+;;       frintp  d0, d0
+;;       add     sp, sp, #0x10
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/f64_ceil/f64_ceil_param.wat b/tests/disas/winch/aarch64/f64_ceil/f64_ceil_param.wat
new file mode 100644
index 000000000..8a4b391ce
--- /dev/null
+++ b/tests/disas/winch/aarch64/f64_ceil/f64_ceil_param.wat
@@ -0,0 +1,25 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (param f64) (result f64)
+        (local.get 0)
+        (f64.ceil)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x18
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x10]
+;;       stur    x1, [x28, #8]
+;;       stur    x0, [x28]
+;;       ldur    x0, [x28]
+;;       frintp  d0, d0
+;;       add     sp, sp, #0x18
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/f64_floor/f64_floor_const.wat b/tests/disas/winch/aarch64/f64_floor/f64_floor_const.wat
new file mode 100644
index 000000000..b99ed3138
--- /dev/null
+++ b/tests/disas/winch/aarch64/f64_floor/f64_floor_const.wat
@@ -0,0 +1,28 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result f64)
+        (f64.const -1.32)
+        (f64.floor)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x10
+;;       mov     x28, sp
+;;       stur    x0, [x28, #8]
+;;       stur    x1, [x28]
+;;       mov     x16, #0x851f
+;;       movk    x16, #0x51eb, lsl #16
+;;       movk    x16, #0x1eb8, lsl #32
+;;       movk    x16, #0xbff5, lsl #48
+;;       fmov    d0, x16
+;;       frintm  d0, d0
+;;       add     sp, sp, #0x10
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/f64_floor/f64_floor_param.wat b/tests/disas/winch/aarch64/f64_floor/f64_floor_param.wat
new file mode 100644
index 000000000..819933f78
--- /dev/null
+++ b/tests/disas/winch/aarch64/f64_floor/f64_floor_param.wat
@@ -0,0 +1,25 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (param f64) (result f64)
+        (local.get 0)
+        (f64.floor)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x18
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x10]
+;;       stur    x1, [x28, #8]
+;;       stur    x0, [x28]
+;;       ldur    x0, [x28]
+;;       frintm  d0, d0
+;;       add     sp, sp, #0x18
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/f64_nearest/f64_nearest_const.wat b/tests/disas/winch/aarch64/f64_nearest/f64_nearest_const.wat
new file mode 100644
index 000000000..c30cb810b
--- /dev/null
+++ b/tests/disas/winch/aarch64/f64_nearest/f64_nearest_const.wat
@@ -0,0 +1,28 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result f64)
+        (f64.const -1.32)
+        (f64.nearest)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x10
+;;       mov     x28, sp
+;;       stur    x0, [x28, #8]
+;;       stur    x1, [x28]
+;;       mov     x16, #0x851f
+;;       movk    x16, #0x51eb, lsl #16
+;;       movk    x16, #0x1eb8, lsl #32
+;;       movk    x16, #0xbff5, lsl #48
+;;       fmov    d0, x16
+;;       frintn  d0, d0
+;;       add     sp, sp, #0x10
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/f64_nearest/f64_nearest_param.wat b/tests/disas/winch/aarch64/f64_nearest/f64_nearest_param.wat
new file mode 100644
index 000000000..d83cd32ee
--- /dev/null
+++ b/tests/disas/winch/aarch64/f64_nearest/f64_nearest_param.wat
@@ -0,0 +1,25 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (param f64) (result f64)
+        (local.get 0)
+        (f64.nearest)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x18
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x10]
+;;       stur    x1, [x28, #8]
+;;       stur    x0, [x28]
+;;       ldur    x0, [x28]
+;;       frintn  d0, d0
+;;       add     sp, sp, #0x18
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/f64_neg/f64_neg_const.wat b/tests/disas/winch/aarch64/f64_neg/f64_neg_const.wat
new file mode 100644
index 000000000..11307202d
--- /dev/null
+++ b/tests/disas/winch/aarch64/f64_neg/f64_neg_const.wat
@@ -0,0 +1,28 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result f64)
+        (f64.const -1.32)
+        (f64.neg)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x10
+;;       mov     x28, sp
+;;       stur    x0, [x28, #8]
+;;       stur    x1, [x28]
+;;       mov     x16, #0x851f
+;;       movk    x16, #0x51eb, lsl #16
+;;       movk    x16, #0x1eb8, lsl #32
+;;       movk    x16, #0xbff5, lsl #48
+;;       fmov    d0, x16
+;;       fneg    d0, d0
+;;       add     sp, sp, #0x10
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/f64_neg/f64_neg_param.wat b/tests/disas/winch/aarch64/f64_neg/f64_neg_param.wat
new file mode 100644
index 000000000..07bcc1a0c
--- /dev/null
+++ b/tests/disas/winch/aarch64/f64_neg/f64_neg_param.wat
@@ -0,0 +1,25 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (param f64) (result f64)
+        (local.get 0)
+        (f64.neg)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x18
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x10]
+;;       stur    x1, [x28, #8]
+;;       stur    x0, [x28]
+;;       ldur    x0, [x28]
+;;       fneg    d0, d0
+;;       add     sp, sp, #0x18
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/f64_sqrt/f64_sqrt_const.wat b/tests/disas/winch/aarch64/f64_sqrt/f64_sqrt_const.wat
new file mode 100644
index 000000000..dc69fbad2
--- /dev/null
+++ b/tests/disas/winch/aarch64/f64_sqrt/f64_sqrt_const.wat
@@ -0,0 +1,28 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result f64)
+        (f64.const 1.32)
+        (f64.sqrt)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x10
+;;       mov     x28, sp
+;;       stur    x0, [x28, #8]
+;;       stur    x1, [x28]
+;;       mov     x16, #0x851f
+;;       movk    x16, #0x51eb, lsl #16
+;;       movk    x16, #0x1eb8, lsl #32
+;;       movk    x16, #0x3ff5, lsl #48
+;;       fmov    d0, x16
+;;       fsqrt   d0, d0
+;;       add     sp, sp, #0x10
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/f64_sqrt/f64_sqrt_param.wat b/tests/disas/winch/aarch64/f64_sqrt/f64_sqrt_param.wat
new file mode 100644
index 000000000..a1e9002e9
--- /dev/null
+++ b/tests/disas/winch/aarch64/f64_sqrt/f64_sqrt_param.wat
@@ -0,0 +1,25 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (param f64) (result f64)
+        (local.get 0)
+        (f64.sqrt)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x18
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x10]
+;;       stur    x1, [x28, #8]
+;;       stur    x0, [x28]
+;;       ldur    x0, [x28]
+;;       fsqrt   d0, d0
+;;       add     sp, sp, #0x18
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/f64_trunc/f64_trunc_const.wat b/tests/disas/winch/aarch64/f64_trunc/f64_trunc_const.wat
new file mode 100644
index 000000000..134eea48d
--- /dev/null
+++ b/tests/disas/winch/aarch64/f64_trunc/f64_trunc_const.wat
@@ -0,0 +1,28 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result f64)
+        (f64.const -1.32)
+        (f64.trunc)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x10
+;;       mov     x28, sp
+;;       stur    x0, [x28, #8]
+;;       stur    x1, [x28]
+;;       mov     x16, #0x851f
+;;       movk    x16, #0x51eb, lsl #16
+;;       movk    x16, #0x1eb8, lsl #32
+;;       movk    x16, #0xbff5, lsl #48
+;;       fmov    d0, x16
+;;       frintz  d0, d0
+;;       add     sp, sp, #0x10
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/f64_trunc/f64_trunc_param.wat b/tests/disas/winch/aarch64/f64_trunc/f64_trunc_param.wat
new file mode 100644
index 000000000..6089eefc1
--- /dev/null
+++ b/tests/disas/winch/aarch64/f64_trunc/f64_trunc_param.wat
@@ -0,0 +1,25 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (param f64) (result f64)
+        (local.get 0)
+        (f64.trunc)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x18
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x10]
+;;       stur    x1, [x28, #8]
+;;       stur    x0, [x28]
+;;       ldur    x0, [x28]
+;;       frintz  d0, d0
+;;       add     sp, sp, #0x18
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret