Repository: bytecodealliance/wasm-tools
Pull request #1725 (2024-08-15 15:46:39)

Add a new `wasm-tools component unbundle` command
This commit adds a new subcommand to `wasm-tools component` named `unbundle` which is tasked with extracting core wasm modules out of a component. This is intended to showcase an example implementation of doing this and the main motivation is to extract core wasm modules out of a component for runtimes such as Wasmtime. This enables sharing machine code for WebAssembly modules between components that use the same core wasm module.

This new subcommand is primarily built on `wasm_encoder::reencode` with a simple size-based heuristic for which modules to extract.


Patch:
diff --git a/README.md b/README.md
index 4be95984..50722d10 100644
--- a/README.md
+++ b/README.md
@@ -148,6 +148,7 @@ that can be use programmatically as well:
 | `wasm-tools component embed` |  |  | Embed a `component-type` custom section in a core wasm binary |
 | `wasm-tools metadata show` |  [wasm-metadata] |  | Show name and producer metadata in a component or module |
 | `wasm-tools metadata add` |  |  | Add name or producer metadata to a component or module |
+| `wasm-tools metadata unbundle` |  |  | Extract core wasm modules from a component |
 | `wasm-tools addr2line` |  |  | Translate wasm offsets to filename/line numbers with DWARF |
 | `wasm-tools completion` |  |  | Generate shell completion scripts for `wasm-tools` |
 | `wasm-tools json-from-wast` |  |  | Convert a `*.wast` file into JSON commands |
diff --git a/crates/wasm-encoder/src/component/names.rs b/crates/wasm-encoder/src/component/names.rs
index 1cbb1062..f09b2152 100644
--- a/crates/wasm-encoder/src/component/names.rs
+++ b/crates/wasm-encoder/src/component/names.rs
@@ -104,6 +104,12 @@ impl ComponentNameSection {
         self.component_decls(INSTANCE_SORT, names)
     }
 
+    /// Appends a raw subsection with the given id and data.
+    pub fn raw(&mut self, id: u8, data: &[u8]) {
+        self.bytes.push(id);
+        data.encode(&mut self.bytes);
+    }
+
     fn component_decls(&mut self, kind: u8, names: &NameMap) {
         self.subsection_header(Subsection::Decls, 1 + names.size());
         self.bytes.push(kind);
diff --git a/crates/wasm-encoder/src/core/names.rs b/crates/wasm-encoder/src/core/names.rs
index 587ae6a7..37f390d2 100644
--- a/crates/wasm-encoder/src/core/names.rs
+++ b/crates/wasm-encoder/src/core/names.rs
@@ -180,6 +180,12 @@ impl NameSection {
         names.encode(&mut self.bytes);
     }
 
+    /// Appends a raw subsection with the given id and data.
+    pub fn raw(&mut self, id: u8, data: &[u8]) {
+        self.bytes.push(id);
+        data.encode(&mut self.bytes);
+    }
+
     fn subsection_header(&mut self, id: Subsection, len: usize) {
         self.bytes.push(id as u8);
         len.encode(&mut self.bytes);
diff --git a/crates/wasm-encoder/src/reencode.rs b/crates/wasm-encoder/src/reencode.rs
index c38376b7..83fc5286 100644
--- a/crates/wasm-encoder/src/reencode.rs
+++ b/crates/wasm-encoder/src/reencode.rs
@@ -45,6 +45,13 @@ pub trait Reencode {
         utils::type_index(self, ty)
     }
 
+    fn type_index_unpacked(
+        &mut self,
+        ty: wasmparser::UnpackedIndex,
+    ) -> Result<u32, Error<Self::Error>> {
+        utils::type_index_unpacked(self, ty)
+    }
+
     fn external_index(&mut self, kind: wasmparser::ExternalKind, index: u32) -> u32 {
         match kind {
             wasmparser::ExternalKind::Func => self.function_index(index),
@@ -463,6 +470,21 @@ pub trait Reencode {
     ) -> Result<(), Error<Self::Error>> {
         utils::parse_core_module(self, module, parser, data)
     }
+
+    fn custom_name_section(
+        &mut self,
+        section: wasmparser::NameSectionReader<'_>,
+    ) -> Result<crate::NameSection, Error<Self::Error>> {
+        utils::custom_name_section(self, section)
+    }
+
+    fn parse_custom_name_subsection(
+        &mut self,
+        names: &mut crate::NameSection,
+        section: wasmparser::Name<'_>,
+    ) -> Result<(), Error<Self::Error>> {
+        utils::parse_custom_name_subsection(self, names, section)
+    }
 }
 
 /// An error when re-encoding from `wasmparser` to `wasm-encoder`.
@@ -860,7 +882,14 @@ pub mod utils {
         module: &mut crate::Module,
         section: wasmparser::CustomSectionReader<'_>,
     ) -> Result<(), Error<T::Error>> {
-        module.section(&reencoder.custom_section(section));
+        match section.as_known() {
+            wasmparser::KnownCustom::Name(name) => {
+                module.section(&reencoder.custom_name_section(name)?);
+            }
+            _ => {
+                module.section(&reencoder.custom_section(section));
+            }
+        }
         Ok(())
     }
 
@@ -915,6 +944,15 @@ pub mod utils {
         ty
     }
 
+    pub fn type_index_unpacked<T: ?Sized + Reencode>(
+        reencoder: &mut T,
+        ty: wasmparser::UnpackedIndex,
+    ) -> Result<u32, Error<T::Error>> {
+        ty.as_module_index()
+            .map(|ty| reencoder.type_index(ty))
+            .ok_or(Error::CanonicalizedHeapTypeReference)
+    }
+
     pub fn tag_type<T: ?Sized + Reencode>(
         reencoder: &mut T,
         tag_ty: wasmparser::TagType,
@@ -986,11 +1024,7 @@ pub mod utils {
             is_final: sub_ty.is_final,
             supertype_idx: sub_ty
                 .supertype_idx
-                .map(|i| {
-                    i.as_module_index()
-                        .map(|ty| reencoder.type_index(ty))
-                        .ok_or(Error::CanonicalizedHeapTypeReference)
-                })
+                .map(|i| reencoder.type_index_unpacked(i.unpack()))
                 .transpose()?,
             composite_type: reencoder.composite_type(sub_ty.composite_type)?,
         })
@@ -1101,11 +1135,9 @@ pub mod utils {
         heap_type: wasmparser::HeapType,
     ) -> Result<crate::HeapType, Error<T::Error>> {
         Ok(match heap_type {
-            wasmparser::HeapType::Concrete(i) => crate::HeapType::Concrete(
-                i.as_module_index()
-                    .map(|ty| reencoder.type_index(ty))
-                    .ok_or(Error::CanonicalizedHeapTypeReference)?,
-            ),
+            wasmparser::HeapType::Concrete(i) => {
+                crate::HeapType::Concrete(reencoder.type_index_unpacked(i)?)
+            }
             wasmparser::HeapType::Abstract { shared, ty } => crate::HeapType::Abstract {
                 shared,
                 ty: reencoder.abstract_heap_type(ty),
@@ -1580,6 +1612,90 @@ pub mod utils {
         module.section(&crate::RawSection { id, data: contents });
         Ok(())
     }
+
+    pub fn custom_name_section<T: ?Sized + Reencode>(
+        reencoder: &mut T,
+        section: wasmparser::NameSectionReader<'_>,
+    ) -> Result<crate::NameSection, Error<T::Error>> {
+        let mut ret = crate::NameSection::new();
+        for subsection in section {
+            reencoder.parse_custom_name_subsection(&mut ret, subsection?)?;
+        }
+        Ok(ret)
+    }
+
+    pub fn parse_custom_name_subsection<T: ?Sized + Reencode>(
+        reencoder: &mut T,
+        names: &mut crate::NameSection,
+        section: wasmparser::Name<'_>,
+    ) -> Result<(), Error<T::Error>> {
+        match section {
+            wasmparser::Name::Module { name, .. } => {
+                names.module(name);
+            }
+            wasmparser::Name::Function(map) => {
+                names.functions(&name_map(map, |i| reencoder.function_index(i))?);
+            }
+            wasmparser::Name::Type(map) => {
+                names.types(&name_map(map, |i| reencoder.type_index(i))?);
+            }
+            wasmparser::Name::Local(map) => {
+                names.locals(&indirect_name_map(map, |i| reencoder.function_index(i))?);
+            }
+            wasmparser::Name::Label(map) => {
+                names.labels(&indirect_name_map(map, |i| reencoder.function_index(i))?);
+            }
+            wasmparser::Name::Table(map) => {
+                names.tables(&name_map(map, |i| reencoder.table_index(i))?);
+            }
+            wasmparser::Name::Memory(map) => {
+                names.memories(&name_map(map, |i| reencoder.memory_index(i))?);
+            }
+            wasmparser::Name::Global(map) => {
+                names.globals(&name_map(map, |i| reencoder.global_index(i))?);
+            }
+            wasmparser::Name::Element(map) => {
+                names.elements(&name_map(map, |i| reencoder.element_index(i))?);
+            }
+            wasmparser::Name::Data(map) => {
+                names.data(&name_map(map, |i| reencoder.data_index(i))?);
+            }
+            wasmparser::Name::Tag(map) => {
+                names.tags(&name_map(map, |i| reencoder.tag_index(i))?);
+            }
+            wasmparser::Name::Field(map) => {
+                names.fields(&indirect_name_map(map, |i| reencoder.type_index(i))?);
+            }
+            wasmparser::Name::Unknown { ty, data, .. } => {
+                names.raw(ty, data);
+            }
+        }
+        Ok(())
+    }
+
+    pub fn name_map(
+        map: wasmparser::NameMap<'_>,
+        mut map_index: impl FnMut(u32) -> u32,
+    ) -> wasmparser::Result<crate::NameMap> {
+        let mut ret = crate::NameMap::new();
+        for naming in map {
+            let naming = naming?;
+            ret.append(map_index(naming.index), naming.name);
+        }
+        Ok(ret)
+    }
+
+    pub fn indirect_name_map(
+        map: wasmparser::IndirectNameMap<'_>,
+        mut map_index: impl FnMut(u32) -> u32,
+    ) -> wasmparser::Result<crate::IndirectNameMap> {
+        let mut ret = crate::IndirectNameMap::new();
+        for naming in map {
+            let naming = naming?;
+            ret.append(map_index(naming.index), &name_map(naming.names, |i| i)?);
+        }
+        Ok(ret)
+    }
 }
 
 impl From<wasmparser::MemArg> for crate::MemArg {
diff --git a/crates/wasm-encoder/src/reencode/component.rs b/crates/wasm-encoder/src/reencode/component.rs
index 9f7919d7..b0d7139a 100644
--- a/crates/wasm-encoder/src/reencode/component.rs
+++ b/crates/wasm-encoder/src/reencode/component.rs
@@ -30,6 +30,30 @@ pub trait ReencodeComponent: Reencode {
         ty
     }
 
+    fn outer_type_index(&mut self, count: u32, ty: u32) -> u32 {
+        let _ = count;
+        self.type_index(ty)
+    }
+
+    fn outer_component_type_index(&mut self, count: u32, ty: u32) -> u32 {
+        let _ = count;
+        self.component_type_index(ty)
+    }
+
+    fn outer_component_index(&mut self, count: u32, component: u32) -> u32 {
+        let _ = count;
+        self.component_index(component)
+    }
+
+    fn outer_module_index(&mut self, count: u32, module: u32) -> u32 {
+        let _ = count;
+        self.module_index(module)
+    }
+
+    fn push_depth(&mut self) {}
+
+    fn pop_depth(&mut self) {}
+
     fn component_external_index(
         &mut self,
         kind: wasmparser::ComponentExternalKind,
@@ -346,12 +370,28 @@ pub trait ReencodeComponent: Reencode {
     ) -> crate::component::CanonicalOption {
         component_utils::canonical_option(self, ty)
     }
+
+    fn custom_component_name_section(
+        &mut self,
+        section: wasmparser::ComponentNameSectionReader<'_>,
+    ) -> Result<crate::ComponentNameSection, Error<Self::Error>> {
+        component_utils::custom_component_name_section(self, section)
+    }
+
+    fn parse_custom_component_name_subsection(
+        &mut self,
+        names: &mut crate::ComponentNameSection,
+        section: wasmparser::ComponentName<'_>,
+    ) -> Result<(), Error<Self::Error>> {
+        component_utils::parse_custom_component_name_subsection(self, names, section)
+    }
 }
 
 impl ReencodeComponent for RoundtripReencoder {}
 
 #[allow(missing_docs)] // FIXME
 pub mod component_utils {
+    use super::super::utils::name_map;
     use super::ReencodeComponent;
     use crate::reencode::Error;
 
@@ -499,9 +539,11 @@ pub mod component_utils {
         parser: wasmparser::Parser,
         submodule: &[u8],
     ) -> Result<(), Error<T::Error>> {
+        reencoder.push_depth();
         let mut module = crate::Module::new();
         crate::reencode::utils::parse_core_module(reencoder, &mut module, parser, submodule)?;
         component.section(&crate::ModuleSection(&module));
+        reencoder.pop_depth();
         Ok(())
     }
 
@@ -512,9 +554,11 @@ pub mod component_utils {
         data: &[u8],
         whole_component: &[u8],
     ) -> Result<(), Error<T::Error>> {
+        reencoder.push_depth();
         let mut subcomponent = crate::Component::new();
         parse_component(reencoder, &mut subcomponent, parser, data, whole_component)?;
         component.section(&crate::NestedComponentSection(&subcomponent));
+        reencoder.pop_depth();
         Ok(())
     }
 
@@ -533,7 +577,14 @@ pub mod component_utils {
         component: &mut crate::Component,
         section: wasmparser::CustomSectionReader<'_>,
     ) -> Result<(), Error<T::Error>> {
-        component.section(&reencoder.custom_section(section));
+        match section.as_known() {
+            wasmparser::KnownCustom::ComponentName(name) => {
+                component.section(&reencoder.custom_component_name_section(name)?);
+            }
+            _ => {
+                component.section(&reencoder.custom_section(section));
+            }
+        }
         Ok(())
     }
 
@@ -581,10 +632,12 @@ pub mod component_utils {
         reencoder: &mut T,
         ty: Box<[wasmparser::InstanceTypeDeclaration<'_>]>,
     ) -> Result<crate::InstanceType, Error<T::Error>> {
+        reencoder.push_depth();
         let mut ret = crate::InstanceType::new();
         for decl in Vec::from(ty) {
             reencoder.parse_component_instance_type_declaration(&mut ret, decl)?;
         }
+        reencoder.pop_depth();
         Ok(ret)
     }
 
@@ -631,10 +684,12 @@ pub mod component_utils {
         reencoder: &mut T,
         ty: Box<[wasmparser::ComponentTypeDeclaration<'_>]>,
     ) -> Result<crate::ComponentType, Error<T::Error>> {
+        reencoder.push_depth();
         let mut ret = crate::ComponentType::new();
         for decl in Vec::from(ty) {
             reencoder.parse_component_type_declaration(&mut ret, decl)?;
         }
+        reencoder.pop_depth();
         Ok(ret)
     }
 
@@ -751,10 +806,12 @@ pub mod component_utils {
         reencoder: &mut T,
         ty: Box<[wasmparser::ModuleTypeDeclaration<'_>]>,
     ) -> Result<crate::ModuleType, Error<T::Error>> {
+        reencoder.push_depth();
         let mut ret = crate::ModuleType::new();
         for decl in Vec::from(ty) {
             reencoder.parse_component_module_type_declaration(&mut ret, decl)?;
         }
+        reencoder.pop_depth();
         Ok(ret)
     }
 
@@ -775,8 +832,7 @@ pub mod component_utils {
                 count,
                 index,
             } => {
-                // TODO: how to inform `reencoder` that `index` is being used
-                // at the particular depth?
+                let index = reencoder.outer_type_index(count, index);
                 module.alias_outer_core_type(count, index);
             }
             wasmparser::ModuleTypeDeclaration::Import(import) => {
@@ -815,9 +871,21 @@ pub mod component_utils {
             }),
             wasmparser::ComponentAlias::Outer { kind, count, index } => Ok(crate::Alias::Outer {
                 kind: kind.into(),
-                // TODO: how to inform `reencoder` of aliases
                 count,
-                index,
+                index: match kind {
+                    wasmparser::ComponentOuterAliasKind::CoreModule => {
+                        reencoder.outer_module_index(count, index)
+                    }
+                    wasmparser::ComponentOuterAliasKind::CoreType => {
+                        reencoder.outer_type_index(count, index)
+                    }
+                    wasmparser::ComponentOuterAliasKind::Type => {
+                        reencoder.outer_component_type_index(count, index)
+                    }
+                    wasmparser::ComponentOuterAliasKind::Component => {
+                        reencoder.outer_component_index(count, index)
+                    }
+                },
             }),
         }
     }
@@ -1071,7 +1139,7 @@ pub mod component_utils {
     ) -> crate::component::ComponentTypeRef {
         match ty {
             wasmparser::ComponentTypeRef::Module(u) => {
-                crate::component::ComponentTypeRef::Module(reencoder.component_type_index(u))
+                crate::component::ComponentTypeRef::Module(reencoder.type_index(u))
             }
             wasmparser::ComponentTypeRef::Func(u) => {
                 crate::component::ComponentTypeRef::Func(reencoder.component_type_index(u))
@@ -1195,6 +1263,69 @@ pub mod component_utils {
             }
         }
     }
+
+    pub fn custom_component_name_section<T: ?Sized + ReencodeComponent>(
+        reencoder: &mut T,
+        section: wasmparser::ComponentNameSectionReader<'_>,
+    ) -> Result<crate::ComponentNameSection, Error<T::Error>> {
+        let mut ret = crate::ComponentNameSection::new();
+        for subsection in section {
+            reencoder.parse_custom_component_name_subsection(&mut ret, subsection?)?;
+        }
+        Ok(ret)
+    }
+
+    pub fn parse_custom_component_name_subsection<T: ?Sized + ReencodeComponent>(
+        reencoder: &mut T,
+        names: &mut crate::ComponentNameSection,
+        section: wasmparser::ComponentName<'_>,
+    ) -> Result<(), Error<T::Error>> {
+        match section {
+            wasmparser::ComponentName::Component { name, .. } => {
+                names.component(name);
+            }
+            wasmparser::ComponentName::CoreFuncs(map) => {
+                names.core_funcs(&name_map(map, |i| reencoder.function_index(i))?);
+            }
+            wasmparser::ComponentName::CoreGlobals(map) => {
+                names.core_globals(&name_map(map, |i| reencoder.global_index(i))?);
+            }
+            wasmparser::ComponentName::CoreMemories(map) => {
+                names.core_memories(&name_map(map, |i| reencoder.memory_index(i))?);
+            }
+            wasmparser::ComponentName::CoreTables(map) => {
+                names.core_tables(&name_map(map, |i| reencoder.table_index(i))?);
+            }
+            wasmparser::ComponentName::CoreModules(map) => {
+                names.core_modules(&name_map(map, |i| reencoder.module_index(i))?);
+            }
+            wasmparser::ComponentName::CoreInstances(map) => {
+                names.core_instances(&name_map(map, |i| reencoder.instance_index(i))?);
+            }
+            wasmparser::ComponentName::CoreTypes(map) => {
+                names.core_types(&name_map(map, |i| reencoder.type_index(i))?);
+            }
+            wasmparser::ComponentName::Types(map) => {
+                names.types(&name_map(map, |i| reencoder.component_type_index(i))?);
+            }
+            wasmparser::ComponentName::Instances(map) => {
+                names.instances(&name_map(map, |i| reencoder.component_instance_index(i))?);
+            }
+            wasmparser::ComponentName::Components(map) => {
+                names.components(&name_map(map, |i| reencoder.component_index(i))?);
+            }
+            wasmparser::ComponentName::Funcs(map) => {
+                names.funcs(&name_map(map, |i| reencoder.component_func_index(i))?);
+            }
+            wasmparser::ComponentName::Values(map) => {
+                names.values(&name_map(map, |i| reencoder.component_value_index(i))?);
+            }
+            wasmparser::ComponentName::Unknown { ty, data, .. } => {
+                names.raw(ty, data);
+            }
+        }
+        Ok(())
+    }
 }
 
 impl From<wasmparser::ComponentValType> for crate::ComponentValType {
diff --git a/crates/wasmparser/src/resources.rs b/crates/wasmparser/src/resources.rs
index 729fc285..5532e231 100644
--- a/crates/wasmparser/src/resources.rs
+++ b/crates/wasmparser/src/resources.rs
@@ -67,6 +67,13 @@ pub trait WasmModuleResources {
     /// Is `a` a subtype of `b`?
     fn is_subtype(&self, a: ValType, b: ValType) -> bool;
 
+    /// Is the given reference type `shared`?
+    ///
+    /// While abstract heap types do carry along a `shared` flag, concrete heap
+    /// types do not. This function resolves those concrete heap types to
+    /// determine `shared`-ness.
+    fn is_shared(&self, ty: RefType) -> bool;
+
     /// Check and canonicalize a value type.
     ///
     /// This will validate that `t` is valid under the `features` provided and
@@ -161,6 +168,9 @@ where
     fn is_subtype(&self, a: ValType, b: ValType) -> bool {
         T::is_subtype(self, a, b)
     }
+    fn is_shared(&self, ty: RefType) -> bool {
+        T::is_shared(self, ty)
+    }
     fn element_count(&self) -> u32 {
         T::element_count(self)
     }
@@ -220,6 +230,10 @@ where
         T::is_subtype(self, a, b)
     }
 
+    fn is_shared(&self, ty: RefType) -> bool {
+        T::is_shared(self, ty)
+    }
+
     fn element_count(&self) -> u32 {
         T::element_count(self)
     }
diff --git a/crates/wasmparser/src/validator/core.rs b/crates/wasmparser/src/validator/core.rs
index 94087a9c..e6615b16 100644
--- a/crates/wasmparser/src/validator/core.rs
+++ b/crates/wasmparser/src/validator/core.rs
@@ -1322,6 +1322,10 @@ impl WasmModuleResources for OperatorValidatorResources<'_> {
         self.types.valtype_is_subtype(a, b)
     }
 
+    fn is_shared(&self, ty: RefType) -> bool {
+        self.types.reftype_is_shared(ty)
+    }
+
     fn element_count(&self) -> u32 {
         self.module.element_types.len() as u32
     }
@@ -1394,6 +1398,10 @@ impl WasmModuleResources for ValidatorResources {
         self.0.snapshot.as_ref().unwrap().valtype_is_subtype(a, b)
     }
 
+    fn is_shared(&self, ty: RefType) -> bool {
+        self.0.snapshot.as_ref().unwrap().reftype_is_shared(ty)
+    }
+
     fn element_count(&self) -> u32 {
         self.0.element_types.len() as u32
     }
diff --git a/crates/wasmparser/src/validator/func.rs b/crates/wasmparser/src/validator/func.rs
index 9d393585..b890545f 100644
--- a/crates/wasmparser/src/validator/func.rs
+++ b/crates/wasmparser/src/validator/func.rs
@@ -237,7 +237,7 @@ impl<T: WasmModuleResources> FuncValidator<T> {
 mod tests {
     use super::*;
     use crate::types::CoreTypeId;
-    use crate::HeapType;
+    use crate::{HeapType, RefType};
 
     struct EmptyResources(crate::SubType);
 
@@ -288,6 +288,9 @@ mod tests {
         fn is_subtype(&self, _t1: ValType, _t2: ValType) -> bool {
             todo!()
         }
+        fn is_shared(&self, _ty: RefType) -> bool {
+            todo!()
+        }
         fn element_count(&self) -> u32 {
             todo!()
         }
diff --git a/crates/wasmparser/src/validator/operators.rs b/crates/wasmparser/src/validator/operators.rs
index 81547f16..df984683 100644
--- a/crates/wasmparser/src/validator/operators.rs
+++ b/crates/wasmparser/src/validator/operators.rs
@@ -199,15 +199,6 @@ impl From<RefType> for MaybeType {
     }
 }
 
-impl MaybeType {
-    fn as_type(&self) -> Option<ValType> {
-        match *self {
-            Self::Type(ty) => Some(ty),
-            Self::Bot | Self::HeapBot => None,
-        }
-    }
-}
-
 impl OperatorValidator {
     fn new(features: &WasmFeatures, allocs: OperatorValidatorAllocations) -> Self {
         let OperatorValidatorAllocations {
@@ -613,6 +604,7 @@ where
         Ok(actual)
     }
 
+    /// Pop a reference type from the operand stack.
     fn pop_ref(&mut self) -> Result<Option<RefType>> {
         match self.pop_operand(None)? {
             MaybeType::Bot | MaybeType::HeapBot => Ok(None),
@@ -625,6 +617,43 @@ where
         }
     }
 
+    /// Pop a reference type from the operand stack, checking if it is a subtype
+    /// of a nullable type of `expected` or the shared version of `expected`.
+    ///
+    /// This function returns the popped reference type and its `shared`-ness,
+    /// saving extra lookups for concrete types.
+    fn pop_maybe_shared_ref(
+        &mut self,
+        expected: AbstractHeapType,
+    ) -> Result<Option<(RefType, bool)>> {
+        let actual = match self.pop_ref()? {
+            Some(rt) => rt,
+            None => return Ok(None),
+        };
+        // Change our expectation based on whether we're dealing with an actual
+        // shared or unshared type.
+        let is_actual_shared = self.resources.is_shared(actual);
+        let expected = RefType::new(
+            true,
+            HeapType::Abstract {
+                shared: is_actual_shared,
+                ty: expected,
+            },
+        )
+        .unwrap();
+
+        // Check (again) that the actual type is a subtype of the expected type.
+        // Note that `_pop_operand` already does this kind of thing but we leave
+        // that for a future refactoring (TODO).
+        if !self.resources.is_subtype(actual.into(), expected.into()) {
+            bail!(
+                self.offset,
+                "type mismatch: expected subtype of {expected}, found {actual}",
+            )
+        }
+        Ok(Some((actual, is_actual_shared)))
+    }
+
     /// Fetches the type for the local at `idx`, returning an error if it's out
     /// of bounds.
     fn local(&self, idx: u32) -> Result<ValType> {
@@ -2723,8 +2752,22 @@ where
         Ok(())
     }
     fn visit_ref_eq(&mut self) -> Self::Output {
-        self.pop_operand(Some(RefType::EQ.nullable().into()))?;
-        self.pop_operand(Some(RefType::EQ.nullable().into()))?;
+        let a = self.pop_maybe_shared_ref(AbstractHeapType::Eq)?;
+        let b = self.pop_maybe_shared_ref(AbstractHeapType::Eq)?;
+        match (a, b) {
+            (Some((_, is_a_shared)), Some((_, is_b_shared))) => {
+                if is_a_shared != is_b_shared {
+                    bail!(
+                        self.offset,
+                        "type mismatch: expected `ref.eq` types to match `shared`-ness"
+                    );
+                }
+            }
+            _ => {
+                // One or both of the types are from unreachable code; assume
+                // the shared-ness matches.
+            }
+        }
         self.push_operand(ValType::I32)
     }
     fn visit_v128_load(&mut self, memarg: MemArg) -> Self::Output {
@@ -4224,7 +4267,7 @@ where
         Ok(())
     }
     fn visit_array_len(&mut self) -> Self::Output {
-        self.pop_operand(Some(RefType::ARRAY.nullable().into()))?;
+        self.pop_maybe_shared_ref(AbstractHeapType::Array)?;
         self.push_operand(ValType::I32)
     }
     fn visit_array_fill(&mut self, array_type_index: u32) -> Self::Output {
@@ -4406,24 +4449,32 @@ where
         Ok(())
     }
     fn visit_any_convert_extern(&mut self) -> Self::Output {
-        let extern_ref = self.pop_operand(Some(RefType::EXTERNREF.into()))?;
-        let is_nullable = extern_ref
-            .as_type()
-            .map_or(false, |ty| ty.as_reference_type().unwrap().is_nullable());
+        let extern_ref = self.pop_maybe_shared_ref(AbstractHeapType::Extern)?;
+        let (is_nullable, shared) = if let Some((extern_ref, shared)) = extern_ref {
+            (extern_ref.is_nullable(), shared)
+        } else {
+            // TODO: propagating unshared may be incorrect here
+            // (https://github.com/WebAssembly/shared-everything-threads/issues/80)
+            (false, false)
+        };
         let heap_type = HeapType::Abstract {
-            shared: false, // TODO: handle shared--see https://github.com/WebAssembly/shared-everything-threads/issues/65.
+            shared,
             ty: AbstractHeapType::Any,
         };
         let any_ref = RefType::new(is_nullable, heap_type).unwrap();
         self.push_operand(any_ref)
     }
     fn visit_extern_convert_any(&mut self) -> Self::Output {
-        let any_ref = self.pop_operand(Some(RefType::ANY.nullable().into()))?;
-        let is_nullable = any_ref
-            .as_type()
-            .map_or(false, |ty| ty.as_reference_type().unwrap().is_nullable());
+        let any_ref = self.pop_maybe_shared_ref(AbstractHeapType::Any)?;
+        let (is_nullable, shared) = if let Some((any_ref, shared)) = any_ref {
+            (any_ref.is_nullable(), shared)
+        } else {
+            // TODO: propagating unshared may be incorrect here
+            // (https://github.com/WebAssembly/shared-everything-threads/issues/80)
+            (false, false)
+        };
         let heap_type = HeapType::Abstract {
-            shared: false, // TODO: handle shared--see https://github.com/WebAssembly/shared-everything-threads/issues/65.
+            shared,
             ty: AbstractHeapType::Extern,
         };
         let extern_ref = RefType::new(is_nullable, heap_type).unwrap();
@@ -4539,11 +4590,11 @@ where
         ))
     }
     fn visit_i31_get_s(&mut self) -> Self::Output {
-        self.pop_operand(Some(ValType::Ref(RefType::I31REF)))?;
+        self.pop_maybe_shared_ref(AbstractHeapType::I31)?;
         self.push_operand(ValType::I32)
     }
     fn visit_i31_get_u(&mut self) -> Self::Output {
-        self.pop_operand(Some(ValType::Ref(RefType::I31REF)))?;
+        self.pop_maybe_shared_ref(AbstractHeapType::I31)?;
         self.push_operand(ValType::I32)
     }
     fn visit_try(&mut self, mut ty: BlockType) -> Self::Output {
diff --git a/crates/wasmparser/src/validator/types.rs b/crates/wasmparser/src/validator/types.rs
index ca1ea793..1843a462 100644
--- a/crates/wasmparser/src/validator/types.rs
+++ b/crates/wasmparser/src/validator/types.rs
@@ -1945,6 +1945,35 @@ impl<'a> TypesRef<'a> {
     {
         self.list.peel_alias(ty)
     }
+
+    /// Returns an iterator over the core wasm imports found.
+    ///
+    /// Returns `None` if this type information is for a component.
+    pub fn core_imports(
+        &self,
+    ) -> Option<impl Iterator<Item = (&'a str, &'a str, EntityType)> + 'a> {
+        match &self.kind {
+            TypesRefKind::Module(module) => Some(
+                module
+                    .imports
+                    .iter()
+                    .flat_map(|((m, n), t)| t.iter().map(move |t| (m.as_str(), n.as_str(), *t))),
+            ),
+            TypesRefKind::Component(_) => None,
+        }
+    }
+
+    /// Returns an iterator over the core wasm exports found.
+    ///
+    /// Returns `None` if this type information is for a component.
+    pub fn core_exports(&self) -> Option<impl Iterator<Item = (&'a str, EntityType)> + 'a> {
+        match &self.kind {
+            TypesRefKind::Module(module) => {
+                Some(module.exports.iter().map(|(n, t)| (n.as_str(), *t)))
+            }
+            TypesRefKind::Component(_) => None,
+        }
+    }
 }
 
 impl<T> Index<T> for TypesRef<'_>
@@ -2282,6 +2311,16 @@ impl Types {
     {
         self.list.peel_alias(ty)
     }
+
+    /// Same as [`TypesRef::core_imports`]
+    pub fn core_imports<'a>(&self) -> Option<impl Iterator<Item = (&str, &str, EntityType)> + '_> {
+        self.as_ref().core_imports()
+    }
+
+    /// Same as [`TypesRef::core_exports`]
+    pub fn core_exports(&self) -> Option<impl Iterator<Item = (&str, EntityType)> + '_> {
+        self.as_ref().core_exports()
+    }
 }
 
 impl<T> Index<T> for Types
diff --git a/crates/wasmprinter/src/lib.rs b/crates/wasmprinter/src/lib.rs
index f8945d5b..8a7b2a56 100644
--- a/crates/wasmprinter/src/lib.rs
+++ b/crates/wasmprinter/src/lib.rs
@@ -2093,7 +2093,7 @@ impl Printer<'_, '_> {
                     self.result.write_str(" ")?;
                     state.core.modules += 1;
                 }
-                self.print_component_type_ref(state, *idx)?;
+                self.print_core_type_ref(state, *idx)?;
                 self.end_group()?;
             }
             ComponentTypeRef::Func(idx) => {
diff --git a/src/bin/wasm-tools/component.rs b/src/bin/wasm-tools/component.rs
index b7dbafdd..20a5650b 100644
--- a/src/bin/wasm-tools/component.rs
+++ b/src/bin/wasm-tools/component.rs
@@ -1,14 +1,15 @@
 //! The WebAssembly component tool command line interface.
 
+use anyhow::{bail, Context, Result};
+use clap::Parser;
 use std::collections::HashMap;
 use std::io::Read;
 use std::path::{Path, PathBuf};
-
-use anyhow::{bail, Context, Result};
-use clap::Parser;
-
+use wasm_encoder::reencode::{Error, Reencode, ReencodeComponent, RoundtripReencoder};
+use wasm_encoder::ModuleType;
 use wasm_tools::Output;
-use wasmparser::WasmFeatures;
+use wasmparser::types::{CoreTypeId, EntityType, Types};
+use wasmparser::{Payload, ValidPayload, WasmFeatures};
 use wat::Detect;
 use wit_component::{
     embed_component_metadata, ComponentEncoder, DecodedWasm, Linker, StringEncoding, WitPrinter,
@@ -24,6 +25,7 @@ pub enum Opts {
     Targets(TargetsOpts),
     Link(LinkOpts),
     SemverCheck(SemverCheckOpts),
+    Unbundle(UnbundleOpts),
 }
 
 impl Opts {
@@ -35,6 +37,7 @@ impl Opts {
             Opts::Targets(targets) => targets.run(),
             Opts::Link(link) => link.run(),
             Opts::SemverCheck(s) => s.run(),
+            Opts::Unbundle(s) => s.run(),
         }
     }
 
@@ -46,6 +49,7 @@ impl Opts {
             Opts::Targets(targets) => targets.general_opts(),
             Opts::Link(link) => link.general_opts(),
             Opts::SemverCheck(s) => s.general_opts(),
+            Opts::Unbundle(s) => s.general_opts(),
         }
     }
 }
@@ -778,3 +782,329 @@ impl SemverCheckOpts {
         Ok(())
     }
 }
+
+/// Unbundled core wasm modules from a component, switching them from being
+/// embedded to being imported.
+///
+/// This subcommand will remove core wasm modules from a component and place
+/// them in the directory specified by `--module-dir`. Modules are extracted
+/// based on their size according to `--threshold`. The output of this command
+/// is a new component which imports the modules that are extracted.
+#[derive(Parser)]
+pub struct UnbundleOpts {
+    #[clap(flatten)]
+    io: wasm_tools::InputOutput,
+
+    /// Where to place unbundled core wasm modules.
+    ///
+    /// Modules will be placed in this directory with the name
+    /// `unbundled-moduleN.wasm` where `N` is a number starting from 0. The
+    /// output component will import `unbundled-moduleN` and each `*.wasm` is
+    /// expected to be compiled and provided.
+    ///
+    /// Note that this option is required to be passed.
+    #[clap(long, value_name = "PATH")]
+    module_dir: PathBuf,
+
+    /// The size threshold for core wasm modules to unbundled.
+    ///
+    /// Modules that are larger than this value will be removed from the
+    /// component and unbundled. Modules smaller than this value will be left in
+    /// place and bundled within the componente.
+    #[clap(long, value_name = "SIZE", default_value_t = 10 << 10)]
+    threshold: usize,
+
+    /// Output the text format of WebAssembly instead of the binary format.
+    #[clap(short = 't', long)]
+    wat: bool,
+}
+
+impl UnbundleOpts {
+    fn general_opts(&self) -> &wasm_tools::GeneralOpts {
+        self.io.general_opts()
+    }
+
+    fn run(self) -> Result<()> {
+        let input = self.io.parse_input_wasm()?;
+        if !wasmparser::Parser::is_component(&input) {
+            return self.io.output_wasm(&input, self.wat);
+        }
+
+        // Generate a list of all modules in the component in the order they
+        // were found in the component itself. Record for each one the bytes of
+        // the module itself or `None` indicating it's not being extracted.
+        let mut modules_to_extract = Vec::new();
+        for payload in wasmparser::Parser::new(0).parse_all(&input) {
+            let range = match payload? {
+                Payload::ModuleSection {
+                    unchecked_range, ..
+                } => unchecked_range,
+                _ => continue,
+            };
+            modules_to_extract.push(if range.len() > self.threshold {
+                Some(&input[range])
+            } else {
+                None
+            });
+        }
+
+        let mut core_types = CoreTypeInterner::default();
+        let mut imports = wasm_encoder::ComponentImportSection::new();
+        let mut validator = wasmparser::Validator::new_with_features(WasmFeatures::all());
+
+        for module in modules_to_extract.iter().filter_map(|m| *m) {
+            // Validate the `module` to get its type information, but don't
+            // validate function bodies since that's not necessary for this.
+            let mut types = None;
+            for payload in wasmparser::Parser::new(0).parse_all(module) {
+                match validator
+                    .payload(&payload?)
+                    .context("failed to validate core wasm module found in component")?
+                {
+                    ValidPayload::Ok | ValidPayload::Parser(_) | ValidPayload::Func(..) => {}
+                    ValidPayload::End(t) => types = Some(t),
+                }
+            }
+            let types = types.unwrap();
+            validator.reset();
+
+            // Create a `ModuleType` with the imports/exports of `types`. Then
+            // create an import corresponding to that module in the import
+            // section.
+            let mut module_ty = ModuleTypeCreator::new(&mut core_types, &types);
+
+            for (module, name, ty) in types.core_imports().unwrap() {
+                let ty = module_ty.convert_entity_type(ty)?;
+                module_ty.module.import(module, name, ty);
+            }
+            for (name, ty) in types.core_exports().unwrap() {
+                let ty = module_ty.convert_entity_type(ty)?;
+                module_ty.module.export(name, ty);
+            }
+
+            let module_type_idx = module_ty.component.section.len();
+            module_ty.component.section.module(&module_ty.module);
+            let name = format!("unbundled-module{}", imports.len());
+            imports.import(
+                &name,
+                wasm_encoder::ComponentTypeRef::Module(module_type_idx),
+            );
+
+            let dst = self.module_dir.join(&name).with_extension("wasm");
+            let parent = dst.parent().unwrap();
+            std::fs::create_dir_all(parent)
+                .with_context(|| format!("failed to create directory {parent:?}"))?;
+            std::fs::write(&dst, module)
+                .with_context(|| format!("failed to write file {dst:?}"))?;
+        }
+
+        let mut component = wasm_encoder::Component::new();
+        component.section(&core_types.section);
+        component.section(&imports);
+
+        // Next rewrite the original component into the destination component
+        // but fix up references at the top level to core types and core
+        // modules. Additionally replace all modules optionally with an alias to
+        // the appropriate import or leave the module in place.
+        ModuleExtractor {
+            extra_core_types: core_types.section.len(),
+            extra_core_modules: imports.len(),
+            depth: 0,
+            modules: modules_to_extract.iter(),
+            next_module: 0,
+        }
+        .parse_component(&mut component, wasmparser::Parser::new(0), &input)?;
+
+        let bytes = component.finish();
+        self.io.output_wasm(&bytes, self.wat)
+    }
+}
+
+/// Top-level conversion of core wasm function types to a component core types
+/// section.
+///
+/// This is a builder for a core type section in a component from a list of
+/// function types. Functions are dedup'd and translated only once.
+#[derive(Default)]
+struct CoreTypeInterner {
+    section: wasm_encoder::CoreTypeSection,
+    intern: HashMap<CoreTypeId, u32>,
+}
+
+impl CoreTypeInterner {
+    fn convert_func(&mut self, id: CoreTypeId, types: &Types) -> Result<u32> {
+        if let Some(ret) = self.intern.get(&id) {
+            return Ok(*ret);
+        }
+        let ty = &types[id];
+        if !ty.is_final || ty.supertype_idx.is_some() || ty.composite_type.shared {
+            bail!("unsupported core type to translate")
+        }
+        let f = match &ty.composite_type.inner {
+            wasmparser::CompositeInnerType::Func(f) => f,
+            _ => bail!("unsupported core type to translate"),
+        };
+
+        let ret = self.section.len();
+        self.section.function(
+            f.params()
+                .iter()
+                .map(|p| RoundtripReencoder.val_type(*p))
+                .collect::<Result<Vec<_>, _>>()?,
+            f.results()
+                .iter()
+                .map(|p| RoundtripReencoder.val_type(*p))
+                .collect::<Result<Vec<_>, _>>()?,
+        );
+        self.intern.insert(id, ret);
+        Ok(ret)
+    }
+}
+
+/// Second-level conversion of a module into a component module type.
+///
+/// This uses a [`CoreTypeInterner`] to create function types and aliases them
+/// into the module type being created to cut down on space used to encode
+/// function types.
+///
+/// This also itself has an `intern` cache to avoid creating multiple aliases
+/// for the same type.
+struct ModuleTypeCreator<'a> {
+    component: &'a mut CoreTypeInterner,
+    types: &'a Types,
+    intern: HashMap<CoreTypeId, u32>,
+    module: ModuleType,
+}
+
+impl<'a> ModuleTypeCreator<'a> {
+    fn new(component: &'a mut CoreTypeInterner, types: &'a Types) -> Self {
+        ModuleTypeCreator {
+            component,
+            types,
+            intern: HashMap::new(),
+            module: ModuleType::new(),
+        }
+    }
+
+    fn convert_entity_type(
+        &mut self,
+        ty: wasmparser::types::EntityType,
+    ) -> Result<wasm_encoder::EntityType> {
+        Ok(match ty {
+            EntityType::Func(id) => wasm_encoder::EntityType::Function(self.convert_func(id)?),
+            EntityType::Table(t) => wasm_encoder::EntityType::Table(wasm_encoder::TableType {
+                shared: t.shared,
+                maximum: t.maximum,
+                minimum: t.initial,
+                table64: t.table64,
+                element_type: RoundtripReencoder.ref_type(t.element_type)?,
+            }),
+            EntityType::Memory(m) => wasm_encoder::EntityType::Memory(wasm_encoder::MemoryType {
+                shared: m.shared,
+                maximum: m.maximum,
+                minimum: m.initial,
+                memory64: m.memory64,
+                page_size_log2: m.page_size_log2,
+            }),
+            EntityType::Global(g) => wasm_encoder::EntityType::Global(wasm_encoder::GlobalType {
+                mutable: g.mutable,
+                shared: g.shared,
+                val_type: RoundtripReencoder.val_type(g.content_type)?,
+            }),
+            EntityType::Tag(id) => wasm_encoder::EntityType::Tag(wasm_encoder::TagType {
+                kind: wasm_encoder::TagKind::Exception,
+                func_type_idx: self.convert_func(id)?,
+            }),
+        })
+    }
+
+    fn convert_func(&mut self, id: CoreTypeId) -> Result<u32> {
+        if let Some(ret) = self.intern.get(&id) {
+            return Ok(*ret);
+        }
+        let outer_index = self.component.convert_func(id, self.types)?;
+        let ret = self.module.type_count();
+        self.module.alias_outer_core_type(1, outer_index);
+        self.intern.insert(id, ret);
+        Ok(ret)
+    }
+}
+
+struct ModuleExtractor<'a> {
+    extra_core_types: u32,
+    extra_core_modules: u32,
+    depth: u32,
+    modules: std::slice::Iter<'a, Option<&'a [u8]>>,
+    next_module: u32,
+}
+
+impl Reencode for ModuleExtractor<'_> {
+    type Error = std::convert::Infallible;
+
+    fn type_index(&mut self, index: u32) -> u32 {
+        if self.depth == 0 {
+            index + self.extra_core_types
+        } else {
+            index
+        }
+    }
+}
+
+impl ReencodeComponent for ModuleExtractor<'_> {
+    fn module_index(&mut self, index: u32) -> u32 {
+        if self.depth == 0 {
+            index + self.extra_core_modules
+        } else {
+            index
+        }
+    }
+
+    fn outer_type_index(&mut self, count: u32, index: u32) -> u32 {
+        if self.depth == count {
+            index + self.extra_core_types
+        } else {
+            index
+        }
+    }
+
+    fn outer_module_index(&mut self, count: u32, index: u32) -> u32 {
+        if self.depth == count {
+            index + self.extra_core_modules
+        } else {
+            index
+        }
+    }
+
+    fn push_depth(&mut self) {
+        self.depth += 1;
+    }
+
+    fn pop_depth(&mut self) {
+        self.depth -= 1;
+    }
+
+    fn parse_component_submodule(
+        &mut self,
+        component: &mut wasm_encoder::Component,
+        _parser: wasmparser::Parser,
+        module: &[u8],
+    ) -> Result<(), Error<Self::Error>> {
+        let extracted = self.modules.next().unwrap().is_some();
+        if extracted {
+            let mut aliases = wasm_encoder::ComponentAliasSection::new();
+            aliases.alias(wasm_encoder::Alias::Outer {
+                kind: wasm_encoder::ComponentOuterAliasKind::CoreModule,
+                count: self.depth,
+                index: self.next_module,
+            });
+            self.next_module += 1;
+            component.section(&aliases);
+        } else {
+            component.section(&wasm_encoder::RawSection {
+                id: wasm_encoder::ComponentSectionId::CoreModule as u8,
+                data: module,
+            });
+        }
+        Ok(())
+    }
+}


Test patch:
diff --git a/tests/cli.rs b/tests/cli.rs
index 598595a6..96427e12 100644
--- a/tests/cli.rs
+++ b/tests/cli.rs
@@ -79,14 +79,13 @@ fn run_test(test: &Path, bless: bool) -> Result<()> {
     let mut stdin = None;
     let tempdir = TempDir::new()?;
     for arg in line.split_whitespace() {
+        let arg = arg.replace("%tmpdir", tempdir.path().to_str().unwrap());
         if arg == "|" {
             let output = execute(&mut cmd, stdin.as_deref(), false)?;
             stdin = Some(output.stdout);
             cmd = wasm_tools_exe();
         } else if arg == "%" {
             cmd.arg(test);
-        } else if arg == "%tmpdir" {
-            cmd.arg(tempdir.path());
         } else {
             cmd.arg(arg);
         }
diff --git a/tests/cli/unbundle-empty.wat b/tests/cli/unbundle-empty.wat
new file mode 100644
index 00000000..8579b1eb
--- /dev/null
+++ b/tests/cli/unbundle-empty.wat
@@ -0,0 +1,3 @@
+;; RUN: component unbundle --module-dir %tmpdir --wat %
+
+(component)
diff --git a/tests/cli/unbundle-empty.wat.stdout b/tests/cli/unbundle-empty.wat.stdout
new file mode 100644
index 00000000..e5627d1d
--- /dev/null
+++ b/tests/cli/unbundle-empty.wat.stdout
@@ -0,0 +1,1 @@
+(component)
diff --git a/tests/cli/unbundle-fix-core-type-indices.wat b/tests/cli/unbundle-fix-core-type-indices.wat
new file mode 100644
index 00000000..f7beea39
--- /dev/null
+++ b/tests/cli/unbundle-fix-core-type-indices.wat
@@ -0,0 +1,15 @@
+;; RUN: component unbundle --module-dir %tmpdir --wat % --threshold 0
+
+(component $C
+  (core type $m (module))
+  (import "x" (core module (type $m)))
+
+  (component
+  )
+
+  (core module
+    (func $f)
+    (export "x" (func $f))
+  )
+)
+
diff --git a/tests/cli/unbundle-fix-core-type-indices.wat.stdout b/tests/cli/unbundle-fix-core-type-indices.wat.stdout
new file mode 100644
index 00000000..193efee2
--- /dev/null
+++ b/tests/cli/unbundle-fix-core-type-indices.wat.stdout
@@ -0,0 +1,16 @@
+(component $C
+  (core type (;0;) (func))
+  (core type (;1;)
+    (module
+      (alias outer $C 0 (type (;0;)))
+      (export "x" (func (type 0)))
+    )
+  )
+  (import "unbundled-module0" (core module (;0;) (type 1)))
+  (core type $m (;2;)
+    (module)
+  )
+  (import "x" (core module (;1;) (type $m)))
+  (component (;0;))
+  (alias outer $C 0 (core module (;2;)))
+)
diff --git a/tests/cli/unbundle-fixup-outer-aliases.wat b/tests/cli/unbundle-fixup-outer-aliases.wat
new file mode 100644
index 00000000..bddb6484
--- /dev/null
+++ b/tests/cli/unbundle-fixup-outer-aliases.wat
@@ -0,0 +1,30 @@
+;; RUN: component unbundle --module-dir %tmpdir --wat % --threshold 0
+
+(component $C
+  (core type $t (func))
+  (core type $m (module
+    (alias outer $C $t (type))
+  ))
+
+  (component $C2
+    (alias outer $C $t (core type))
+    (core type $t (func))
+    (core type $m (module
+      (alias outer $C2 $t (type))
+    ))
+  )
+
+  (core module $m
+    (func $f)
+    (export "x" (func $f))
+  )
+
+  (alias outer $C $m (core module $m2))
+
+  (component
+    (alias outer $C $m (core module))
+    (alias outer $C $m2 (core module))
+  )
+)
+
+
diff --git a/tests/cli/unbundle-fixup-outer-aliases.wat.stdout b/tests/cli/unbundle-fixup-outer-aliases.wat.stdout
new file mode 100644
index 00000000..98bb11c5
--- /dev/null
+++ b/tests/cli/unbundle-fixup-outer-aliases.wat.stdout
@@ -0,0 +1,31 @@
+(component $C
+  (core type (;0;) (func))
+  (core type (;1;)
+    (module
+      (alias outer $C 0 (type (;0;)))
+      (export "x" (func (type 0)))
+    )
+  )
+  (import "unbundled-module0" (core module (;0;) (type 1)))
+  (core type $t (;2;) (func))
+  (core type $m (;3;)
+    (module
+      (alias outer $C $t (type (;0;)))
+    )
+  )
+  (component $C2 (;0;)
+    (alias outer $C $t (core type (;0;)))
+    (core type $t (;1;) (func))
+    (core type $m (;2;)
+      (module
+        (alias outer $C2 $t (type (;0;)))
+      )
+    )
+  )
+  (alias outer $C 0 (core module $m (;1;)))
+  (alias outer $C $m (core module $m2 (;2;)))
+  (component (;1;)
+    (alias outer $C $m (core module (;0;)))
+    (alias outer $C $m2 (core module (;1;)))
+  )
+)
diff --git a/tests/cli/unbundle-nested.wat b/tests/cli/unbundle-nested.wat
new file mode 100644
index 00000000..bac9055f
--- /dev/null
+++ b/tests/cli/unbundle-nested.wat
@@ -0,0 +1,12 @@
+;; RUN: component unbundle --module-dir %tmpdir --wat % --threshold 0
+
+(component $C
+  (core module $outer
+    (import "a" "b" (global i32))
+  )
+  (component $C2
+    (core module $inner
+      (import "b" "c" (global f32))
+    )
+  )
+)
diff --git a/tests/cli/unbundle-nested.wat.stdout b/tests/cli/unbundle-nested.wat.stdout
new file mode 100644
index 00000000..2ea5b934
--- /dev/null
+++ b/tests/cli/unbundle-nested.wat.stdout
@@ -0,0 +1,18 @@
+(component $C
+  (core type (;0;)
+    (module
+      (import "a" "b" (global i32))
+    )
+  )
+  (core type (;1;)
+    (module
+      (import "b" "c" (global f32))
+    )
+  )
+  (import "unbundled-module0" (core module (;0;) (type 0)))
+  (import "unbundled-module1" (core module (;1;) (type 1)))
+  (alias outer $C 0 (core module $outer (;2;)))
+  (component $C2 (;0;)
+    (alias outer $C 1 (core module $inner (;0;)))
+  )
+)
diff --git a/tests/cli/unbundle-print-module0.wat b/tests/cli/unbundle-print-module0.wat
new file mode 100644
index 00000000..fa01071c
--- /dev/null
+++ b/tests/cli/unbundle-print-module0.wat
@@ -0,0 +1,10 @@
+;; RUN: component unbundle --threshold 0 % --module-dir %tmpdir | print %tmpdir/unbundled-module0.wasm
+
+(component
+  (core module $a
+    (import "a" "a" (func))
+  )
+  (core module $b
+    (import "b" "b" (func))
+  )
+)
diff --git a/tests/cli/unbundle-print-module0.wat.stdout b/tests/cli/unbundle-print-module0.wat.stdout
new file mode 100644
index 00000000..eafedb24
--- /dev/null
+++ b/tests/cli/unbundle-print-module0.wat.stdout
@@ -0,0 +1,4 @@
+(module $a
+  (type (;0;) (func))
+  (import "a" "a" (func (;0;) (type 0)))
+)
diff --git a/tests/cli/unbundle-print-module1.wat b/tests/cli/unbundle-print-module1.wat
new file mode 100644
index 00000000..2ec4fb30
--- /dev/null
+++ b/tests/cli/unbundle-print-module1.wat
@@ -0,0 +1,10 @@
+;; RUN: component unbundle --threshold 0 % --module-dir %tmpdir | print %tmpdir/unbundled-module1.wasm
+
+(component
+  (core module $a
+    (import "a" "a" (func))
+  )
+  (core module $b
+    (import "b" "b" (func))
+  )
+)
diff --git a/tests/cli/unbundle-print-module1.wat.stdout b/tests/cli/unbundle-print-module1.wat.stdout
new file mode 100644
index 00000000..15272095
--- /dev/null
+++ b/tests/cli/unbundle-print-module1.wat.stdout
@@ -0,0 +1,4 @@
+(module $b
+  (type (;0;) (func))
+  (import "b" "b" (func (;0;) (type 0)))
+)
diff --git a/tests/cli/unbundle-simple.wat b/tests/cli/unbundle-simple.wat
new file mode 100644
index 00000000..3573858b
--- /dev/null
+++ b/tests/cli/unbundle-simple.wat
@@ -0,0 +1,9 @@
+;; RUN: component unbundle --module-dir %tmpdir --wat % --threshold 10
+
+(component
+  (core module $not-extracted) ;; not extracted, too small
+
+  (core module $"extract me please"
+    (import "a" "b" (func))
+  )
+)
diff --git a/tests/cli/unbundle-simple.wat.stdout b/tests/cli/unbundle-simple.wat.stdout
new file mode 100644
index 00000000..81e14d92
--- /dev/null
+++ b/tests/cli/unbundle-simple.wat.stdout
@@ -0,0 +1,16 @@
+(component
+  (core type (;0;)
+    (module)
+  )
+  (core type (;1;) (func))
+  (core type (;2;)
+    (module
+      (alias outer 1 1 (type (;0;)))
+      (import "a" "b" (func (type 0)))
+    )
+  )
+  (import "unbundled-module0" (core module (;0;) (type 0)))
+  (import "unbundled-module1" (core module (;1;) (type 2)))
+  (alias outer 0 0 (core module $not-extracted (;2;)))
+  (alias outer 0 1 (core module $"extract me please" (;3;)))
+)
diff --git a/tests/local/shared-everything-threads/array.wast b/tests/local/shared-everything-threads/arrays.wast
similarity index 94%
rename from tests/local/shared-everything-threads/array.wast
rename to tests/local/shared-everything-threads/arrays.wast
index 064fcecf..aa041f96 100644
--- a/tests/local/shared-everything-threads/array.wast
+++ b/tests/local/shared-everything-threads/arrays.wast
@@ -114,7 +114,31 @@
     (array.init_data $i8 0 (local.get 0) (i32.const 0) (i32.const 0) (i32.const 0)))
 )
 
-;; Check `array.atomic.rmw.*` instructions.
+;; Bottom types can be used as shared arrays.
+(module
+  (type $i8 (shared (array (mut i8))))
+  (type $i32 (shared (array (mut i32))))
+  (type $funcs (shared (array (mut (ref null (shared func))))))
+
+  (data)
+  ;; See https://github.com/bytecodealliance/wasm-tools/issues/1717.
+  (elem (ref null (shared func)))
+
+  (func (array.get_s $i8 (ref.null (shared none)) (i32.const 0)) (drop))
+  (func (array.get_u $i8 (ref.null (shared none)) (i32.const 0)) (drop))
+  (func (array.get $i32 (ref.null (shared none)) (i32.const 0)) (drop))
+  (func (array.set $i8 (ref.null (shared none)) (i32.const 0) (i32.const 0)))
+  (func (param (ref null $i8))
+    (array.copy $i8 $i8 (ref.null (shared none)) (i32.const 0) (local.get 0) (i32.const 0) (i32.const 0)))
+  (func (param (ref null $i8))
+    (array.copy $i8 $i8 (local.get 0) (i32.const 0) (ref.null (shared none)) (i32.const 0) (i32.const 0)))
+  (func (array.copy $i8 $i8 (ref.null (shared none)) (i32.const 0) (ref.null (shared none)) (i32.const 0) (i32.const 0)))
+  (func (array.fill $i8 (ref.null (shared none)) (i32.const 0) (i32.const 0) (i32.const 0)))
+  (func (array.init_data $i8 0 (ref.null (shared none)) (i32.const 0) (i32.const 0) (i32.const 0)))
+  (func (array.init_elem $funcs 0 (ref.null (shared none)) (i32.const 0) (i32.const 0) (i32.const 0)))
+)
+
+;; Exhaustively check `array.atomic.rmw.*` instructions.
 (module (; get, i32, seq_cst ;)
   (type $a (shared (array (mut i32))))
   (func (export "array-atomic-get-i32-seq_cst") (param $x (ref null $a)) (param $y i32) (result i32)
diff --git a/tests/local/shared-everything-threads/global.wast b/tests/local/shared-everything-threads/globals.wast
similarity index 100%
rename from tests/local/shared-everything-threads/global.wast
rename to tests/local/shared-everything-threads/globals.wast
diff --git a/tests/local/shared-everything-threads/polymorphism.wast b/tests/local/shared-everything-threads/polymorphism.wast
new file mode 100644
index 00000000..5c9d905e
--- /dev/null
+++ b/tests/local/shared-everything-threads/polymorphism.wast
@@ -0,0 +1,23 @@
+;; Some instructions are shared-polymorphic and work with shared or unshared
+;; references.
+(module
+  (func (drop (ref.eq (ref.null (shared none)) (ref.null (shared none)))))
+
+  (func (param (ref null (shared i31))) (drop (i31.get_s (local.get 0))))
+  (func (param (ref null (shared i31))) (drop (i31.get_u (local.get 0))))
+
+  (func (param (ref null (shared array))) (drop (array.len (local.get 0))))
+
+  (func (param (ref null (shared extern))) (result (ref null (shared any)))
+    (any.convert_extern (local.get 0))
+  )
+  (func (param (ref (shared extern))) (result (ref (shared any)))
+    (any.convert_extern (local.get 0))
+  )
+  (func (param (ref null (shared any))) (result (ref null (shared extern)))
+    (extern.convert_any (local.get 0))
+  )
+  (func (param (ref (shared any))) (result (ref (shared extern)))
+    (extern.convert_any (local.get 0))
+  )
+)
diff --git a/tests/local/shared-everything-threads/ref-equivalence.wast b/tests/local/shared-everything-threads/ref-equivalence.wast
new file mode 100644
index 00000000..662138ee
--- /dev/null
+++ b/tests/local/shared-everything-threads/ref-equivalence.wast
@@ -0,0 +1,202 @@
+(module
+  (type $st (sub (shared (struct))))
+  (type $st' (sub (shared (struct (field i32)))))
+  (type $at (shared (array i8)))
+  (type $st-sub1 (sub $st (shared (struct))))
+  (type $st-sub2 (sub $st (shared (struct))))
+  (type $st'-sub1 (sub $st' (shared (struct (field i32)))))
+  (type $st'-sub2 (sub $st' (shared (struct (field i32)))))
+
+  (table 20 (ref null (shared eq)))
+
+  (func (export "init")
+    (table.set (i32.const 0) (ref.null (shared eq)))
+    (table.set (i32.const 1) (ref.null (shared i31)))
+    (table.set (i32.const 2) (ref.i31_shared (i32.const 7)))
+    (table.set (i32.const 3) (ref.i31_shared (i32.const 7)))
+    (table.set (i32.const 4) (ref.i31_shared (i32.const 8)))
+    (table.set (i32.const 5) (struct.new_default $st))
+    (table.set (i32.const 6) (struct.new_default $st))
+    (table.set (i32.const 7) (array.new_default $at (i32.const 0)))
+    (table.set (i32.const 8) (array.new_default $at (i32.const 0)))
+  )
+
+  (func (export "eq") (param $i i32) (param $j i32) (result i32)
+    (ref.eq (table.get (local.get $i)) (table.get (local.get $j)))
+  )
+)
+
+(invoke "init")
+
+(assert_return (invoke "eq" (i32.const 0) (i32.const 0)) (i32.const 1))
+(assert_return (invoke "eq" (i32.const 0) (i32.const 1)) (i32.const 1))
+(assert_return (invoke "eq" (i32.const 0) (i32.const 2)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 0) (i32.const 3)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 0) (i32.const 4)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 0) (i32.const 5)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 0) (i32.const 6)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 0) (i32.const 7)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 0) (i32.const 8)) (i32.const 0))
+
+(assert_return (invoke "eq" (i32.const 1) (i32.const 0)) (i32.const 1))
+(assert_return (invoke "eq" (i32.const 1) (i32.const 1)) (i32.const 1))
+(assert_return (invoke "eq" (i32.const 1) (i32.const 2)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 1) (i32.const 3)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 1) (i32.const 4)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 1) (i32.const 5)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 1) (i32.const 6)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 1) (i32.const 7)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 1) (i32.const 8)) (i32.const 0))
+
+(assert_return (invoke "eq" (i32.const 2) (i32.const 0)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 2) (i32.const 1)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 2) (i32.const 2)) (i32.const 1))
+(assert_return (invoke "eq" (i32.const 2) (i32.const 3)) (i32.const 1))
+(assert_return (invoke "eq" (i32.const 2) (i32.const 4)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 2) (i32.const 5)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 2) (i32.const 6)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 2) (i32.const 7)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 2) (i32.const 8)) (i32.const 0))
+
+(assert_return (invoke "eq" (i32.const 3) (i32.const 0)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 3) (i32.const 1)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 3) (i32.const 2)) (i32.const 1))
+(assert_return (invoke "eq" (i32.const 3) (i32.const 3)) (i32.const 1))
+(assert_return (invoke "eq" (i32.const 3) (i32.const 4)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 3) (i32.const 5)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 3) (i32.const 6)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 3) (i32.const 7)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 3) (i32.const 8)) (i32.const 0))
+
+(assert_return (invoke "eq" (i32.const 4) (i32.const 0)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 4) (i32.const 1)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 4) (i32.const 2)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 4) (i32.const 3)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 4) (i32.const 4)) (i32.const 1))
+(assert_return (invoke "eq" (i32.const 4) (i32.const 5)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 4) (i32.const 6)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 4) (i32.const 7)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 4) (i32.const 8)) (i32.const 0))
+
+(assert_return (invoke "eq" (i32.const 5) (i32.const 0)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 5) (i32.const 1)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 5) (i32.const 2)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 5) (i32.const 3)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 5) (i32.const 4)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 5) (i32.const 5)) (i32.const 1))
+(assert_return (invoke "eq" (i32.const 5) (i32.const 6)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 5) (i32.const 7)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 5) (i32.const 8)) (i32.const 0))
+
+(assert_return (invoke "eq" (i32.const 6) (i32.const 0)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 6) (i32.const 1)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 6) (i32.const 2)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 6) (i32.const 3)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 6) (i32.const 4)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 6) (i32.const 5)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 6) (i32.const 6)) (i32.const 1))
+(assert_return (invoke "eq" (i32.const 6) (i32.const 7)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 6) (i32.const 8)) (i32.const 0))
+
+(assert_return (invoke "eq" (i32.const 7) (i32.const 0)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 7) (i32.const 1)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 7) (i32.const 2)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 7) (i32.const 3)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 7) (i32.const 4)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 7) (i32.const 5)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 7) (i32.const 6)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 7) (i32.const 7)) (i32.const 1))
+(assert_return (invoke "eq" (i32.const 7) (i32.const 8)) (i32.const 0))
+
+(assert_return (invoke "eq" (i32.const 8) (i32.const 0)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 8) (i32.const 1)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 8) (i32.const 2)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 8) (i32.const 3)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 8) (i32.const 4)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 8) (i32.const 5)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 8) (i32.const 6)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 8) (i32.const 7)) (i32.const 0))
+(assert_return (invoke "eq" (i32.const 8) (i32.const 8)) (i32.const 1))
+
+(assert_invalid
+  (module
+    (func (export "eq") (param $r (ref (shared any))) (result i32)
+      (ref.eq (local.get $r) (local.get $r))
+    )
+  )
+  "type mismatch"
+)
+(assert_invalid
+  (module
+    (func (export "eq") (param $r (ref null (shared any))) (result i32)
+      (ref.eq (local.get $r) (local.get $r))
+    )
+  )
+  "type mismatch"
+)
+(assert_invalid
+  (module
+    (func (export "eq") (param $r (ref null (shared func))) (result i32)
+      (ref.eq (local.get $r) (local.get $r))
+    )
+  )
+  "type mismatch"
+)
+(assert_invalid
+  (module
+    (func (export "eq") (param $r (ref null (shared func))) (result i32)
+      (ref.eq (local.get $r) (local.get $r))
+    )
+  )
+  "type mismatch"
+)
+(assert_invalid
+  (module
+    (func (export "eq") (param $r (ref (shared extern))) (result i32)
+      (ref.eq (local.get $r) (local.get $r))
+    )
+  )
+  "type mismatch"
+)
+(assert_invalid
+  (module
+    (func (export "eq") (param $r (ref null (shared extern))) (result i32)
+      (ref.eq (local.get $r) (local.get $r))
+    )
+  )
+  "type mismatch"
+)
+
+;; Mixed shared / unshared eq
+(assert_invalid
+  (module
+    (func (export "eq") (param $r1 (ref eq)) (param $r2 (ref (shared eq))) (result i32)
+      (ref.eq (local.get $r1) (local.get $r2))
+    )
+  )
+  "type mismatch"
+)
+(assert_invalid
+  (module
+    (func (export "eq") (param $r1 (ref (shared eq))) (param $r2 (ref eq)) (result i32)
+      (ref.eq (local.get $r1) (local.get $r2))
+    )
+  )
+  "type mismatch"
+)
+(assert_invalid
+  (module
+    (func (export "eq") (param $r1 eqref) (param $r2 (ref null (shared eq))) (result i32)
+      (ref.eq (local.get $r1) (local.get $r2))
+    )
+  )
+  "type mismatch"
+)
+(assert_invalid
+  (module
+    (func (export "eq") (param $r1 (ref null (shared eq))) (param $r2 eqref) (result i32)
+      (ref.eq (local.get $r1) (local.get $r2))
+    )
+  )
+  "type mismatch"
+)
diff --git a/tests/local/shared-everything-threads/struct.wast b/tests/local/shared-everything-threads/structs.wast
similarity index 97%
rename from tests/local/shared-everything-threads/struct.wast
rename to tests/local/shared-everything-threads/structs.wast
index cd666463..69274c24 100644
--- a/tests/local/shared-everything-threads/struct.wast
+++ b/tests/local/shared-everything-threads/structs.wast
@@ -91,7 +91,15 @@
     (struct.set $i8 0 (local.get 0) (i32.const 0)))
 )
 
-;; Check struct.atomic.rmw.* instructions
+;; Bottom types can be used as shared structs.
+(module
+  (type $i8 (shared (struct (field (mut i8)))))
+  (func (drop (struct.get_s $i8 0 (ref.null (shared none)))))
+  (func (drop (struct.get_u $i8 0 (ref.null (shared none)))))
+  (func (struct.set $i8 0 (ref.null (shared none)) (i32.const 0)))
+)
+
+;; Exhaustively check `struct.atomic.rmw.*` instructions.
 (module
   (type $s (shared (struct
     (field $i8 (mut i8))
diff --git a/tests/local/shared-everything-threads/table.wast b/tests/local/shared-everything-threads/tables.wast
similarity index 100%
rename from tests/local/shared-everything-threads/table.wast
rename to tests/local/shared-everything-threads/tables.wast
diff --git a/tests/local/shared-everything-threads/global-heap-types.wast b/tests/local/shared-everything-threads/types.wast
similarity index 100%
rename from tests/local/shared-everything-threads/global-heap-types.wast
rename to tests/local/shared-everything-threads/types.wast
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast.json b/tests/snapshots/local/shared-everything-threads/arrays.wast.json
similarity index 54%
rename from tests/snapshots/local/shared-everything-threads/array.wast.json
rename to tests/snapshots/local/shared-everything-threads/arrays.wast.json
index 51189866..59172cb6 100644
--- a/tests/snapshots/local/shared-everything-threads/array.wast.json
+++ b/tests/snapshots/local/shared-everything-threads/arrays.wast.json
@@ -1,429 +1,434 @@
 {
-  "source_filename": "tests/local/shared-everything-threads/array.wast",
+  "source_filename": "tests/local/shared-everything-threads/arrays.wast",
   "commands": [
     {
       "type": "module",
       "line": 2,
-      "filename": "array.0.wasm"
+      "filename": "arrays.0.wasm"
     },
     {
       "type": "assert_invalid",
       "line": 16,
-      "filename": "array.1.wasm",
+      "filename": "arrays.1.wasm",
       "text": "type mismatch",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
       "line": 26,
-      "filename": "array.2.wasm",
+      "filename": "arrays.2.wasm",
       "text": "type mismatch",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
       "line": 37,
-      "filename": "array.3.wasm",
+      "filename": "arrays.3.wasm",
       "text": "sub type must match super type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
       "line": 46,
-      "filename": "array.4.wasm",
+      "filename": "arrays.4.wasm",
       "text": "sub type must match super type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
       "line": 55,
-      "filename": "array.5.wasm",
+      "filename": "arrays.5.wasm",
       "text": "must contain shared type",
       "module_type": "binary"
     },
     {
       "type": "module",
       "line": 62,
-      "filename": "array.6.wasm"
+      "filename": "arrays.6.wasm"
     },
     {
       "type": "module",
       "line": 67,
-      "filename": "array.7.wasm"
+      "filename": "arrays.7.wasm"
     },
     {
       "type": "module",
       "line": 72,
-      "filename": "array.8.wasm"
+      "filename": "arrays.8.wasm"
     },
     {
       "type": "module",
       "line": 118,
-      "filename": "array.9.wasm"
-    },
-    {
-      "type": "module",
-      "line": 126,
-      "filename": "array.10.wasm"
-    },
-    {
-      "type": "module",
-      "line": 134,
-      "filename": "array.11.wasm"
+      "filename": "arrays.9.wasm"
     },
     {
       "type": "module",
       "line": 142,
-      "filename": "array.12.wasm"
+      "filename": "arrays.10.wasm"
     },
     {
       "type": "module",
       "line": 150,
-      "filename": "array.13.wasm"
+      "filename": "arrays.11.wasm"
     },
     {
       "type": "module",
       "line": 158,
-      "filename": "array.14.wasm"
+      "filename": "arrays.12.wasm"
     },
     {
       "type": "module",
       "line": 166,
-      "filename": "array.15.wasm"
+      "filename": "arrays.13.wasm"
     },
     {
       "type": "module",
       "line": 174,
-      "filename": "array.16.wasm"
+      "filename": "arrays.14.wasm"
     },
     {
       "type": "module",
       "line": 182,
-      "filename": "array.17.wasm"
+      "filename": "arrays.15.wasm"
     },
     {
       "type": "module",
       "line": 190,
-      "filename": "array.18.wasm"
+      "filename": "arrays.16.wasm"
     },
     {
       "type": "module",
       "line": 198,
-      "filename": "array.19.wasm"
+      "filename": "arrays.17.wasm"
     },
     {
       "type": "module",
       "line": 206,
-      "filename": "array.20.wasm"
+      "filename": "arrays.18.wasm"
     },
     {
       "type": "module",
       "line": 214,
-      "filename": "array.21.wasm"
+      "filename": "arrays.19.wasm"
     },
     {
       "type": "module",
       "line": 222,
-      "filename": "array.22.wasm"
+      "filename": "arrays.20.wasm"
     },
     {
       "type": "module",
       "line": 230,
-      "filename": "array.23.wasm"
+      "filename": "arrays.21.wasm"
+    },
+    {
+      "type": "module",
+      "line": 238,
+      "filename": "arrays.22.wasm"
+    },
+    {
+      "type": "module",
+      "line": 246,
+      "filename": "arrays.23.wasm"
+    },
+    {
+      "type": "module",
+      "line": 254,
+      "filename": "arrays.24.wasm"
     },
     {
       "type": "module",
-      "line": 239,
-      "filename": "array.24.wasm"
+      "line": 263,
+      "filename": "arrays.25.wasm"
     },
     {
       "type": "module",
-      "line": 248,
-      "filename": "array.25.wasm"
+      "line": 272,
+      "filename": "arrays.26.wasm"
     },
     {
       "type": "module",
-      "line": 257,
-      "filename": "array.26.wasm"
+      "line": 281,
+      "filename": "arrays.27.wasm"
     },
     {
       "type": "module",
-      "line": 266,
-      "filename": "array.27.wasm"
+      "line": 290,
+      "filename": "arrays.28.wasm"
     },
     {
       "type": "module",
-      "line": 275,
-      "filename": "array.28.wasm"
+      "line": 299,
+      "filename": "arrays.29.wasm"
     },
     {
       "type": "module",
-      "line": 284,
-      "filename": "array.29.wasm"
+      "line": 308,
+      "filename": "arrays.30.wasm"
     },
     {
       "type": "module",
-      "line": 293,
-      "filename": "array.30.wasm"
+      "line": 317,
+      "filename": "arrays.31.wasm"
     },
     {
       "type": "module",
-      "line": 302,
-      "filename": "array.31.wasm"
+      "line": 326,
+      "filename": "arrays.32.wasm"
     },
     {
       "type": "module",
-      "line": 311,
-      "filename": "array.32.wasm"
+      "line": 335,
+      "filename": "arrays.33.wasm"
     },
     {
       "type": "module",
-      "line": 320,
-      "filename": "array.33.wasm"
+      "line": 344,
+      "filename": "arrays.34.wasm"
     },
     {
       "type": "module",
-      "line": 329,
-      "filename": "array.34.wasm"
+      "line": 353,
+      "filename": "arrays.35.wasm"
     },
     {
       "type": "module",
-      "line": 338,
-      "filename": "array.35.wasm"
+      "line": 362,
+      "filename": "arrays.36.wasm"
     },
     {
       "type": "module",
-      "line": 347,
-      "filename": "array.36.wasm"
+      "line": 371,
+      "filename": "arrays.37.wasm"
     },
     {
       "type": "module",
-      "line": 356,
-      "filename": "array.37.wasm"
+      "line": 380,
+      "filename": "arrays.38.wasm"
     },
     {
       "type": "module",
-      "line": 365,
-      "filename": "array.38.wasm"
+      "line": 389,
+      "filename": "arrays.39.wasm"
     },
     {
       "type": "module",
-      "line": 374,
-      "filename": "array.39.wasm"
+      "line": 398,
+      "filename": "arrays.40.wasm"
     },
     {
       "type": "module",
-      "line": 383,
-      "filename": "array.40.wasm"
+      "line": 407,
+      "filename": "arrays.41.wasm"
     },
     {
       "type": "module",
-      "line": 392,
-      "filename": "array.41.wasm"
+      "line": 416,
+      "filename": "arrays.42.wasm"
     },
     {
       "type": "module",
-      "line": 401,
-      "filename": "array.42.wasm"
+      "line": 425,
+      "filename": "arrays.43.wasm"
     },
     {
       "type": "module",
-      "line": 410,
-      "filename": "array.43.wasm"
+      "line": 434,
+      "filename": "arrays.44.wasm"
     },
     {
       "type": "module",
-      "line": 419,
-      "filename": "array.44.wasm"
+      "line": 443,
+      "filename": "arrays.45.wasm"
     },
     {
       "type": "module",
-      "line": 428,
-      "filename": "array.45.wasm"
+      "line": 452,
+      "filename": "arrays.46.wasm"
     },
     {
       "type": "module",
-      "line": 437,
-      "filename": "array.46.wasm"
+      "line": 461,
+      "filename": "arrays.47.wasm"
     },
     {
       "type": "module",
-      "line": 446,
-      "filename": "array.47.wasm"
+      "line": 470,
+      "filename": "arrays.48.wasm"
     },
     {
       "type": "module",
-      "line": 455,
-      "filename": "array.48.wasm"
+      "line": 479,
+      "filename": "arrays.49.wasm"
     },
     {
       "type": "module",
-      "line": 464,
-      "filename": "array.49.wasm"
+      "line": 488,
+      "filename": "arrays.50.wasm"
     },
     {
       "type": "module",
-      "line": 473,
-      "filename": "array.50.wasm"
+      "line": 497,
+      "filename": "arrays.51.wasm"
     },
     {
       "type": "module",
-      "line": 482,
-      "filename": "array.51.wasm"
+      "line": 506,
+      "filename": "arrays.52.wasm"
     },
     {
       "type": "module",
-      "line": 491,
-      "filename": "array.52.wasm"
+      "line": 515,
+      "filename": "arrays.53.wasm"
     },
     {
       "type": "module",
-      "line": 500,
-      "filename": "array.53.wasm"
+      "line": 524,
+      "filename": "arrays.54.wasm"
     },
     {
       "type": "module",
-      "line": 509,
-      "filename": "array.54.wasm"
+      "line": 533,
+      "filename": "arrays.55.wasm"
     },
     {
       "type": "module",
-      "line": 518,
-      "filename": "array.55.wasm"
+      "line": 542,
+      "filename": "arrays.56.wasm"
     },
     {
       "type": "module",
-      "line": 527,
-      "filename": "array.56.wasm"
+      "line": 551,
+      "filename": "arrays.57.wasm"
     },
     {
       "type": "module",
-      "line": 536,
-      "filename": "array.57.wasm"
+      "line": 560,
+      "filename": "arrays.58.wasm"
     },
     {
       "type": "module",
-      "line": 545,
-      "filename": "array.58.wasm"
+      "line": 569,
+      "filename": "arrays.59.wasm"
     },
     {
       "type": "module",
-      "line": 554,
-      "filename": "array.59.wasm"
+      "line": 578,
+      "filename": "arrays.60.wasm"
     },
     {
       "type": "module",
-      "line": 564,
-      "filename": "array.60.wasm"
+      "line": 588,
+      "filename": "arrays.61.wasm"
     },
     {
       "type": "module",
-      "line": 574,
-      "filename": "array.61.wasm"
+      "line": 598,
+      "filename": "arrays.62.wasm"
     },
     {
       "type": "module",
-      "line": 584,
-      "filename": "array.62.wasm"
+      "line": 608,
+      "filename": "arrays.63.wasm"
     },
     {
       "type": "module",
-      "line": 594,
-      "filename": "array.63.wasm"
+      "line": 618,
+      "filename": "arrays.64.wasm"
     },
     {
       "type": "module",
-      "line": 604,
-      "filename": "array.64.wasm"
+      "line": 628,
+      "filename": "arrays.65.wasm"
     },
     {
       "type": "assert_invalid",
-      "line": 615,
-      "filename": "array.65.wasm",
+      "line": 639,
+      "filename": "arrays.66.wasm",
       "text": "packed storage type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 625,
-      "filename": "array.66.wasm",
+      "line": 649,
+      "filename": "arrays.67.wasm",
       "text": "non-packed storage type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 635,
-      "filename": "array.67.wasm",
+      "line": 659,
+      "filename": "arrays.68.wasm",
       "text": "non-packed storage type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 645,
-      "filename": "array.68.wasm",
+      "line": 669,
+      "filename": "arrays.69.wasm",
       "text": "non-packed storage type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 655,
-      "filename": "array.69.wasm",
+      "line": 679,
+      "filename": "arrays.70.wasm",
       "text": "non-packed storage type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 665,
-      "filename": "array.70.wasm",
+      "line": 689,
+      "filename": "arrays.71.wasm",
       "text": "invalid type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 676,
-      "filename": "array.71.wasm",
+      "line": 700,
+      "filename": "arrays.72.wasm",
       "text": "invalid type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 687,
-      "filename": "array.72.wasm",
+      "line": 711,
+      "filename": "arrays.73.wasm",
       "text": "invalid type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 698,
-      "filename": "array.73.wasm",
+      "line": 722,
+      "filename": "arrays.74.wasm",
       "text": "invalid type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 709,
-      "filename": "array.74.wasm",
+      "line": 733,
+      "filename": "arrays.75.wasm",
       "text": "invalid type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 720,
-      "filename": "array.75.wasm",
+      "line": 744,
+      "filename": "arrays.76.wasm",
       "text": "invalid type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 731,
-      "filename": "array.76.wasm",
+      "line": 755,
+      "filename": "arrays.77.wasm",
       "text": "invalid type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 743,
-      "filename": "array.77.wasm",
+      "line": 767,
+      "filename": "arrays.78.wasm",
       "text": "invalid type",
       "module_type": "binary"
     }
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/0.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/0.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/0.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/0.print
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/9.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/10.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/9.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/10.print
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/10.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/11.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/10.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/11.print
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/11.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/12.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/11.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/12.print
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/12.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/13.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/12.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/13.print
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/13.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/14.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/13.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/14.print
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/14.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/15.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/14.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/15.print
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/15.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/16.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/15.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/16.print
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/16.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/17.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/16.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/17.print
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/17.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/18.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/17.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/18.print
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/18.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/19.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/18.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/19.print
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/19.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/20.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/19.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/20.print
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/20.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/21.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/20.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/21.print
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/21.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/22.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/21.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/22.print
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/22.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/23.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/22.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/23.print
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/23.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/24.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/23.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/24.print
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/24.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/25.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/24.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/25.print
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/25.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/26.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/25.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/26.print
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/26.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/27.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/26.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/27.print
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/27.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/28.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/27.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/28.print
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/28.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/29.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/28.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/29.print
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/29.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/30.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/29.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/30.print
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/30.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/31.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/30.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/31.print
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/31.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/32.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/31.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/32.print
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/32.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/33.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/32.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/33.print
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/33.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/34.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/33.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/34.print
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/34.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/35.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/34.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/35.print
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/35.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/36.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/35.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/36.print
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/36.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/37.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/36.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/37.print
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/37.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/38.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/37.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/38.print
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/38.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/39.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/38.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/39.print
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/39.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/40.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/39.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/40.print
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/40.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/41.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/40.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/41.print
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/41.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/42.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/41.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/42.print
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/42.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/43.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/42.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/43.print
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/43.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/44.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/43.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/44.print
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/44.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/45.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/44.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/45.print
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/45.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/46.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/45.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/46.print
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/46.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/47.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/46.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/47.print
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/47.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/48.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/47.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/48.print
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/48.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/49.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/48.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/49.print
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/49.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/50.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/49.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/50.print
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/50.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/51.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/50.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/51.print
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/51.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/52.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/51.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/52.print
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/52.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/53.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/52.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/53.print
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/53.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/54.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/53.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/54.print
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/54.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/55.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/54.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/55.print
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/55.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/56.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/55.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/56.print
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/56.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/57.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/56.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/57.print
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/57.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/58.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/57.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/58.print
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/58.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/59.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/58.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/59.print
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/6.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/6.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/6.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/6.print
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/59.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/60.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/59.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/60.print
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/60.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/61.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/60.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/61.print
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/61.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/62.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/61.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/62.print
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/62.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/63.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/62.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/63.print
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/63.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/64.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/63.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/64.print
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/64.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/65.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/64.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/65.print
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/7.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/7.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/7.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/7.print
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/8.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/8.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/array.wast/8.print
rename to tests/snapshots/local/shared-everything-threads/arrays.wast/8.print
diff --git a/tests/snapshots/local/shared-everything-threads/arrays.wast/9.print b/tests/snapshots/local/shared-everything-threads/arrays.wast/9.print
new file mode 100644
index 00000000..ece8d7e9
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/arrays.wast/9.print
@@ -0,0 +1,78 @@
+(module
+  (type $i8 (;0;) (shared(array (mut i8))))
+  (type $i32 (;1;) (shared(array (mut i32))))
+  (type $funcs (;2;) (shared(array (mut (ref null (shared func))))))
+  (type (;3;) (func))
+  (type (;4;) (func (param (ref null $i8))))
+  (func (;0;) (type 3)
+    ref.null (shared none)
+    i32.const 0
+    array.get_s $i8
+    drop
+  )
+  (func (;1;) (type 3)
+    ref.null (shared none)
+    i32.const 0
+    array.get_u $i8
+    drop
+  )
+  (func (;2;) (type 3)
+    ref.null (shared none)
+    i32.const 0
+    array.get $i32
+    drop
+  )
+  (func (;3;) (type 3)
+    ref.null (shared none)
+    i32.const 0
+    i32.const 0
+    array.set $i8
+  )
+  (func (;4;) (type 4) (param (ref null $i8))
+    ref.null (shared none)
+    i32.const 0
+    local.get 0
+    i32.const 0
+    i32.const 0
+    array.copy $i8 $i8
+  )
+  (func (;5;) (type 4) (param (ref null $i8))
+    local.get 0
+    i32.const 0
+    ref.null (shared none)
+    i32.const 0
+    i32.const 0
+    array.copy $i8 $i8
+  )
+  (func (;6;) (type 3)
+    ref.null (shared none)
+    i32.const 0
+    ref.null (shared none)
+    i32.const 0
+    i32.const 0
+    array.copy $i8 $i8
+  )
+  (func (;7;) (type 3)
+    ref.null (shared none)
+    i32.const 0
+    i32.const 0
+    i32.const 0
+    array.fill $i8
+  )
+  (func (;8;) (type 3)
+    ref.null (shared none)
+    i32.const 0
+    i32.const 0
+    i32.const 0
+    array.init_data $i8 0
+  )
+  (func (;9;) (type 3)
+    ref.null (shared none)
+    i32.const 0
+    i32.const 0
+    i32.const 0
+    array.init_elem $funcs 0
+  )
+  (elem (;0;) (ref null (shared func)))
+  (data (;0;) "")
+)
diff --git a/tests/snapshots/local/shared-everything-threads/global.wast.json b/tests/snapshots/local/shared-everything-threads/globals.wast.json
similarity index 75%
rename from tests/snapshots/local/shared-everything-threads/global.wast.json
rename to tests/snapshots/local/shared-everything-threads/globals.wast.json
index 3d4ab677..d28c04df 100644
--- a/tests/snapshots/local/shared-everything-threads/global.wast.json
+++ b/tests/snapshots/local/shared-everything-threads/globals.wast.json
@@ -1,172 +1,172 @@
 {
-  "source_filename": "tests/local/shared-everything-threads/global.wast",
+  "source_filename": "tests/local/shared-everything-threads/globals.wast",
   "commands": [
     {
       "type": "module",
       "line": 3,
-      "filename": "global.0.wasm"
+      "filename": "globals.0.wasm"
     },
     {
       "type": "assert_malformed",
       "line": 30,
-      "filename": "global.1.wat",
+      "filename": "globals.1.wat",
       "text": "unexpected token",
       "module_type": "text"
     },
     {
       "type": "assert_invalid",
       "line": 34,
-      "filename": "global.2.wasm",
+      "filename": "globals.2.wasm",
       "text": "shared value type",
       "module_type": "binary"
     },
     {
       "type": "module",
       "line": 38,
-      "filename": "global.3.wasm"
+      "filename": "globals.3.wasm"
     },
     {
       "type": "assert_invalid",
       "line": 51,
-      "filename": "global.4.wasm",
+      "filename": "globals.4.wasm",
       "text": "global is immutable",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
       "line": 58,
-      "filename": "global.5.wasm",
+      "filename": "globals.5.wasm",
       "text": "invalid type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
       "line": 65,
-      "filename": "global.6.wasm",
+      "filename": "globals.6.wasm",
       "text": "invalid type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
       "line": 72,
-      "filename": "global.7.wasm",
+      "filename": "globals.7.wasm",
       "text": "invalid type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
       "line": 79,
-      "filename": "global.8.wasm",
+      "filename": "globals.8.wasm",
       "text": "invalid type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
       "line": 86,
-      "filename": "global.9.wasm",
+      "filename": "globals.9.wasm",
       "text": "invalid type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
       "line": 93,
-      "filename": "global.10.wasm",
+      "filename": "globals.10.wasm",
       "text": "invalid type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
       "line": 100,
-      "filename": "global.11.wasm",
+      "filename": "globals.11.wasm",
       "text": "invalid type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
       "line": 107,
-      "filename": "global.12.wasm",
+      "filename": "globals.12.wasm",
       "text": "invalid type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
       "line": 114,
-      "filename": "global.13.wasm",
+      "filename": "globals.13.wasm",
       "text": "invalid type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
       "line": 121,
-      "filename": "global.14.wasm",
+      "filename": "globals.14.wasm",
       "text": "invalid type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
       "line": 128,
-      "filename": "global.15.wasm",
+      "filename": "globals.15.wasm",
       "text": "invalid type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
       "line": 135,
-      "filename": "global.16.wasm",
+      "filename": "globals.16.wasm",
       "text": "invalid type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
       "line": 142,
-      "filename": "global.17.wasm",
+      "filename": "globals.17.wasm",
       "text": "invalid type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
       "line": 149,
-      "filename": "global.18.wasm",
+      "filename": "globals.18.wasm",
       "text": "invalid type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
       "line": 156,
-      "filename": "global.19.wasm",
+      "filename": "globals.19.wasm",
       "text": "invalid type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
       "line": 163,
-      "filename": "global.20.wasm",
+      "filename": "globals.20.wasm",
       "text": "invalid type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
       "line": 170,
-      "filename": "global.21.wasm",
+      "filename": "globals.21.wasm",
       "text": "invalid type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
       "line": 177,
-      "filename": "global.22.wasm",
+      "filename": "globals.22.wasm",
       "text": "invalid type",
       "module_type": "binary"
     },
     {
       "type": "module",
       "line": 184,
-      "filename": "global.23.wasm"
+      "filename": "globals.23.wasm"
     },
     {
       "type": "module",
       "line": 277,
-      "filename": "global.24.wasm"
+      "filename": "globals.24.wasm"
     }
   ]
 }
\ No newline at end of file
diff --git a/tests/snapshots/local/shared-everything-threads/global.wast/0.print b/tests/snapshots/local/shared-everything-threads/globals.wast/0.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/global.wast/0.print
rename to tests/snapshots/local/shared-everything-threads/globals.wast/0.print
diff --git a/tests/snapshots/local/shared-everything-threads/global.wast/23.print b/tests/snapshots/local/shared-everything-threads/globals.wast/23.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/global.wast/23.print
rename to tests/snapshots/local/shared-everything-threads/globals.wast/23.print
diff --git a/tests/snapshots/local/shared-everything-threads/global.wast/24.print b/tests/snapshots/local/shared-everything-threads/globals.wast/24.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/global.wast/24.print
rename to tests/snapshots/local/shared-everything-threads/globals.wast/24.print
diff --git a/tests/snapshots/local/shared-everything-threads/global.wast/3.print b/tests/snapshots/local/shared-everything-threads/globals.wast/3.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/global.wast/3.print
rename to tests/snapshots/local/shared-everything-threads/globals.wast/3.print
diff --git a/tests/snapshots/local/shared-everything-threads/polymorphism.wast.json b/tests/snapshots/local/shared-everything-threads/polymorphism.wast.json
new file mode 100644
index 00000000..01cb5ea8
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/polymorphism.wast.json
@@ -0,0 +1,10 @@
+{
+  "source_filename": "tests/local/shared-everything-threads/polymorphism.wast",
+  "commands": [
+    {
+      "type": "module",
+      "line": 3,
+      "filename": "polymorphism.0.wasm"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/tests/snapshots/local/shared-everything-threads/polymorphism.wast/0.print b/tests/snapshots/local/shared-everything-threads/polymorphism.wast/0.print
new file mode 100644
index 00000000..b8b3e7a1
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/polymorphism.wast/0.print
@@ -0,0 +1,46 @@
+(module
+  (type (;0;) (func))
+  (type (;1;) (func (param (ref null (shared i31)))))
+  (type (;2;) (func (param (ref null (shared array)))))
+  (type (;3;) (func (param (ref null (shared extern))) (result (ref null (shared any)))))
+  (type (;4;) (func (param (ref (shared extern))) (result (ref (shared any)))))
+  (type (;5;) (func (param (ref null (shared any))) (result (ref null (shared extern)))))
+  (type (;6;) (func (param (ref (shared any))) (result (ref (shared extern)))))
+  (func (;0;) (type 0)
+    ref.null (shared none)
+    ref.null (shared none)
+    ref.eq
+    drop
+  )
+  (func (;1;) (type 1) (param (ref null (shared i31)))
+    local.get 0
+    i31.get_s
+    drop
+  )
+  (func (;2;) (type 1) (param (ref null (shared i31)))
+    local.get 0
+    i31.get_u
+    drop
+  )
+  (func (;3;) (type 2) (param (ref null (shared array)))
+    local.get 0
+    array.len
+    drop
+  )
+  (func (;4;) (type 3) (param (ref null (shared extern))) (result (ref null (shared any)))
+    local.get 0
+    any.convert_extern
+  )
+  (func (;5;) (type 4) (param (ref (shared extern))) (result (ref (shared any)))
+    local.get 0
+    any.convert_extern
+  )
+  (func (;6;) (type 5) (param (ref null (shared any))) (result (ref null (shared extern)))
+    local.get 0
+    extern.convert_any
+  )
+  (func (;7;) (type 6) (param (ref (shared any))) (result (ref (shared extern)))
+    local.get 0
+    extern.convert_any
+  )
+)
diff --git a/tests/snapshots/local/shared-everything-threads/ref-equivalence.wast.json b/tests/snapshots/local/shared-everything-threads/ref-equivalence.wast.json
new file mode 100644
index 00000000..104f3f22
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/ref-equivalence.wast.json
@@ -0,0 +1,2033 @@
+{
+  "source_filename": "tests/local/shared-everything-threads/ref-equivalence.wast",
+  "commands": [
+    {
+      "type": "module",
+      "line": 1,
+      "filename": "ref-equivalence.0.wasm"
+    },
+    {
+      "type": "action",
+      "line": 29,
+      "action": {
+        "type": "invoke",
+        "field": "init",
+        "args": []
+      }
+    },
+    {
+      "type": "assert_return",
+      "line": 31,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "0"
+          },
+          {
+            "type": "i32",
+            "value": "0"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "1"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 32,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "0"
+          },
+          {
+            "type": "i32",
+            "value": "1"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "1"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 33,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "0"
+          },
+          {
+            "type": "i32",
+            "value": "2"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 34,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "0"
+          },
+          {
+            "type": "i32",
+            "value": "3"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 35,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "0"
+          },
+          {
+            "type": "i32",
+            "value": "4"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 36,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "0"
+          },
+          {
+            "type": "i32",
+            "value": "5"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 37,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "0"
+          },
+          {
+            "type": "i32",
+            "value": "6"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 38,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "0"
+          },
+          {
+            "type": "i32",
+            "value": "7"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 39,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "0"
+          },
+          {
+            "type": "i32",
+            "value": "8"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 41,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "1"
+          },
+          {
+            "type": "i32",
+            "value": "0"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "1"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 42,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "1"
+          },
+          {
+            "type": "i32",
+            "value": "1"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "1"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 43,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "1"
+          },
+          {
+            "type": "i32",
+            "value": "2"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 44,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "1"
+          },
+          {
+            "type": "i32",
+            "value": "3"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 45,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "1"
+          },
+          {
+            "type": "i32",
+            "value": "4"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 46,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "1"
+          },
+          {
+            "type": "i32",
+            "value": "5"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 47,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "1"
+          },
+          {
+            "type": "i32",
+            "value": "6"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 48,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "1"
+          },
+          {
+            "type": "i32",
+            "value": "7"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 49,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "1"
+          },
+          {
+            "type": "i32",
+            "value": "8"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 51,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "2"
+          },
+          {
+            "type": "i32",
+            "value": "0"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 52,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "2"
+          },
+          {
+            "type": "i32",
+            "value": "1"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 53,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "2"
+          },
+          {
+            "type": "i32",
+            "value": "2"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "1"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 54,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "2"
+          },
+          {
+            "type": "i32",
+            "value": "3"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "1"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 55,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "2"
+          },
+          {
+            "type": "i32",
+            "value": "4"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 56,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "2"
+          },
+          {
+            "type": "i32",
+            "value": "5"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 57,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "2"
+          },
+          {
+            "type": "i32",
+            "value": "6"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 58,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "2"
+          },
+          {
+            "type": "i32",
+            "value": "7"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 59,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "2"
+          },
+          {
+            "type": "i32",
+            "value": "8"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 61,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "3"
+          },
+          {
+            "type": "i32",
+            "value": "0"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 62,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "3"
+          },
+          {
+            "type": "i32",
+            "value": "1"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 63,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "3"
+          },
+          {
+            "type": "i32",
+            "value": "2"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "1"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 64,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "3"
+          },
+          {
+            "type": "i32",
+            "value": "3"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "1"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 65,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "3"
+          },
+          {
+            "type": "i32",
+            "value": "4"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 66,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "3"
+          },
+          {
+            "type": "i32",
+            "value": "5"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 67,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "3"
+          },
+          {
+            "type": "i32",
+            "value": "6"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 68,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "3"
+          },
+          {
+            "type": "i32",
+            "value": "7"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 69,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "3"
+          },
+          {
+            "type": "i32",
+            "value": "8"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 71,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "4"
+          },
+          {
+            "type": "i32",
+            "value": "0"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 72,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "4"
+          },
+          {
+            "type": "i32",
+            "value": "1"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 73,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "4"
+          },
+          {
+            "type": "i32",
+            "value": "2"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 74,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "4"
+          },
+          {
+            "type": "i32",
+            "value": "3"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 75,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "4"
+          },
+          {
+            "type": "i32",
+            "value": "4"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "1"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 76,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "4"
+          },
+          {
+            "type": "i32",
+            "value": "5"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 77,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "4"
+          },
+          {
+            "type": "i32",
+            "value": "6"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 78,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "4"
+          },
+          {
+            "type": "i32",
+            "value": "7"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 79,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "4"
+          },
+          {
+            "type": "i32",
+            "value": "8"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 81,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "5"
+          },
+          {
+            "type": "i32",
+            "value": "0"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 82,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "5"
+          },
+          {
+            "type": "i32",
+            "value": "1"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 83,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "5"
+          },
+          {
+            "type": "i32",
+            "value": "2"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 84,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "5"
+          },
+          {
+            "type": "i32",
+            "value": "3"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 85,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "5"
+          },
+          {
+            "type": "i32",
+            "value": "4"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 86,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "5"
+          },
+          {
+            "type": "i32",
+            "value": "5"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "1"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 87,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "5"
+          },
+          {
+            "type": "i32",
+            "value": "6"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 88,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "5"
+          },
+          {
+            "type": "i32",
+            "value": "7"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 89,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "5"
+          },
+          {
+            "type": "i32",
+            "value": "8"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 91,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "6"
+          },
+          {
+            "type": "i32",
+            "value": "0"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 92,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "6"
+          },
+          {
+            "type": "i32",
+            "value": "1"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 93,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "6"
+          },
+          {
+            "type": "i32",
+            "value": "2"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 94,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "6"
+          },
+          {
+            "type": "i32",
+            "value": "3"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 95,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "6"
+          },
+          {
+            "type": "i32",
+            "value": "4"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 96,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "6"
+          },
+          {
+            "type": "i32",
+            "value": "5"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 97,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "6"
+          },
+          {
+            "type": "i32",
+            "value": "6"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "1"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 98,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "6"
+          },
+          {
+            "type": "i32",
+            "value": "7"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 99,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "6"
+          },
+          {
+            "type": "i32",
+            "value": "8"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 101,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "7"
+          },
+          {
+            "type": "i32",
+            "value": "0"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 102,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "7"
+          },
+          {
+            "type": "i32",
+            "value": "1"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 103,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "7"
+          },
+          {
+            "type": "i32",
+            "value": "2"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 104,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "7"
+          },
+          {
+            "type": "i32",
+            "value": "3"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 105,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "7"
+          },
+          {
+            "type": "i32",
+            "value": "4"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 106,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "7"
+          },
+          {
+            "type": "i32",
+            "value": "5"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 107,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "7"
+          },
+          {
+            "type": "i32",
+            "value": "6"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 108,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "7"
+          },
+          {
+            "type": "i32",
+            "value": "7"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "1"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 109,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "7"
+          },
+          {
+            "type": "i32",
+            "value": "8"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 111,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "8"
+          },
+          {
+            "type": "i32",
+            "value": "0"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 112,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "8"
+          },
+          {
+            "type": "i32",
+            "value": "1"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 113,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "8"
+          },
+          {
+            "type": "i32",
+            "value": "2"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 114,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "8"
+          },
+          {
+            "type": "i32",
+            "value": "3"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 115,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "8"
+          },
+          {
+            "type": "i32",
+            "value": "4"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 116,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "8"
+          },
+          {
+            "type": "i32",
+            "value": "5"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 117,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "8"
+          },
+          {
+            "type": "i32",
+            "value": "6"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 118,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "8"
+          },
+          {
+            "type": "i32",
+            "value": "7"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "0"
+        }
+      ]
+    },
+    {
+      "type": "assert_return",
+      "line": 119,
+      "action": {
+        "type": "invoke",
+        "field": "eq",
+        "args": [
+          {
+            "type": "i32",
+            "value": "8"
+          },
+          {
+            "type": "i32",
+            "value": "8"
+          }
+        ]
+      },
+      "expected": [
+        {
+          "type": "i32",
+          "value": "1"
+        }
+      ]
+    },
+    {
+      "type": "assert_invalid",
+      "line": 122,
+      "filename": "ref-equivalence.1.wasm",
+      "text": "type mismatch",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 130,
+      "filename": "ref-equivalence.2.wasm",
+      "text": "type mismatch",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 138,
+      "filename": "ref-equivalence.3.wasm",
+      "text": "type mismatch",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 146,
+      "filename": "ref-equivalence.4.wasm",
+      "text": "type mismatch",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 154,
+      "filename": "ref-equivalence.5.wasm",
+      "text": "type mismatch",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 162,
+      "filename": "ref-equivalence.6.wasm",
+      "text": "type mismatch",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 172,
+      "filename": "ref-equivalence.7.wasm",
+      "text": "type mismatch",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 180,
+      "filename": "ref-equivalence.8.wasm",
+      "text": "type mismatch",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 188,
+      "filename": "ref-equivalence.9.wasm",
+      "text": "type mismatch",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 196,
+      "filename": "ref-equivalence.10.wasm",
+      "text": "type mismatch",
+      "module_type": "binary"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/tests/snapshots/local/shared-everything-threads/ref-equivalence.wast/0.print b/tests/snapshots/local/shared-everything-threads/ref-equivalence.wast/0.print
new file mode 100644
index 00000000..336ac98b
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/ref-equivalence.wast/0.print
@@ -0,0 +1,55 @@
+(module
+  (type $st (;0;) (sub (shared(struct))))
+  (type $st' (;1;) (sub (shared(struct (field i32)))))
+  (type $at (;2;) (shared(array i8)))
+  (type $st-sub1 (;3;) (sub $st (shared(struct))))
+  (type $st-sub2 (;4;) (sub $st (shared(struct))))
+  (type $st'-sub1 (;5;) (sub $st' (shared(struct (field i32)))))
+  (type $st'-sub2 (;6;) (sub $st' (shared(struct (field i32)))))
+  (type (;7;) (func))
+  (type (;8;) (func (param i32 i32) (result i32)))
+  (func (;0;) (type 7)
+    i32.const 0
+    ref.null (shared eq)
+    table.set 0
+    i32.const 1
+    ref.null (shared i31)
+    table.set 0
+    i32.const 2
+    i32.const 7
+    ref.i31_shared
+    table.set 0
+    i32.const 3
+    i32.const 7
+    ref.i31_shared
+    table.set 0
+    i32.const 4
+    i32.const 8
+    ref.i31_shared
+    table.set 0
+    i32.const 5
+    struct.new_default $st
+    table.set 0
+    i32.const 6
+    struct.new_default $st
+    table.set 0
+    i32.const 7
+    i32.const 0
+    array.new_default $at
+    table.set 0
+    i32.const 8
+    i32.const 0
+    array.new_default $at
+    table.set 0
+  )
+  (func (;1;) (type 8) (param $i i32) (param $j i32) (result i32)
+    local.get $i
+    table.get 0
+    local.get $j
+    table.get 0
+    ref.eq
+  )
+  (table (;0;) 20 (ref null (shared eq)))
+  (export "init" (func 0))
+  (export "eq" (func 1))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/struct.wast.json b/tests/snapshots/local/shared-everything-threads/structs.wast.json
similarity index 67%
rename from tests/snapshots/local/shared-everything-threads/struct.wast.json
rename to tests/snapshots/local/shared-everything-threads/structs.wast.json
index 106b265c..2cc841de 100644
--- a/tests/snapshots/local/shared-everything-threads/struct.wast.json
+++ b/tests/snapshots/local/shared-everything-threads/structs.wast.json
@@ -1,154 +1,159 @@
 {
-  "source_filename": "tests/local/shared-everything-threads/struct.wast",
+  "source_filename": "tests/local/shared-everything-threads/structs.wast",
   "commands": [
     {
       "type": "module",
       "line": 2,
-      "filename": "struct.0.wasm"
+      "filename": "structs.0.wasm"
     },
     {
       "type": "assert_invalid",
       "line": 16,
-      "filename": "struct.1.wasm",
+      "filename": "structs.1.wasm",
       "text": "type mismatch",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
       "line": 26,
-      "filename": "struct.2.wasm",
+      "filename": "structs.2.wasm",
       "text": "type mismatch",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
       "line": 37,
-      "filename": "struct.3.wasm",
+      "filename": "structs.3.wasm",
       "text": "must match super type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
       "line": 46,
-      "filename": "struct.4.wasm",
+      "filename": "structs.4.wasm",
       "text": "must match super type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
       "line": 55,
-      "filename": "struct.5.wasm",
+      "filename": "structs.5.wasm",
       "text": "must contain shared type",
       "module_type": "binary"
     },
     {
       "type": "module",
       "line": 62,
-      "filename": "struct.6.wasm"
+      "filename": "structs.6.wasm"
     },
     {
       "type": "module",
       "line": 67,
-      "filename": "struct.7.wasm"
+      "filename": "structs.7.wasm"
     },
     {
       "type": "module",
       "line": 72,
-      "filename": "struct.8.wasm"
+      "filename": "structs.8.wasm"
     },
     {
       "type": "module",
       "line": 95,
-      "filename": "struct.9.wasm"
+      "filename": "structs.9.wasm"
+    },
+    {
+      "type": "module",
+      "line": 103,
+      "filename": "structs.10.wasm"
     },
     {
       "type": "assert_invalid",
-      "line": 322,
-      "filename": "struct.10.wasm",
+      "line": 330,
+      "filename": "structs.11.wasm",
       "text": "non-packed storage type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 331,
-      "filename": "struct.11.wasm",
+      "line": 339,
+      "filename": "structs.12.wasm",
       "text": "non-packed storage types",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 340,
-      "filename": "struct.12.wasm",
+      "line": 348,
+      "filename": "structs.13.wasm",
       "text": "non-packed storage types",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 349,
-      "filename": "struct.13.wasm",
+      "line": 357,
+      "filename": "structs.14.wasm",
       "text": "non-packed storage types",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 358,
-      "filename": "struct.14.wasm",
+      "line": 366,
+      "filename": "structs.15.wasm",
       "text": "non-packed storage types",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 367,
-      "filename": "struct.15.wasm",
+      "line": 375,
+      "filename": "structs.16.wasm",
       "text": "invalid type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 377,
-      "filename": "struct.16.wasm",
+      "line": 385,
+      "filename": "structs.17.wasm",
       "text": "invalid type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 387,
-      "filename": "struct.17.wasm",
+      "line": 395,
+      "filename": "structs.18.wasm",
       "text": "invalid type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 397,
-      "filename": "struct.18.wasm",
+      "line": 405,
+      "filename": "structs.19.wasm",
       "text": "invalid type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 407,
-      "filename": "struct.19.wasm",
+      "line": 415,
+      "filename": "structs.20.wasm",
       "text": "invalid type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 417,
-      "filename": "struct.20.wasm",
+      "line": 425,
+      "filename": "structs.21.wasm",
       "text": "invalid type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 427,
-      "filename": "struct.21.wasm",
+      "line": 435,
+      "filename": "structs.22.wasm",
       "text": "invalid type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 438,
-      "filename": "struct.22.wasm",
+      "line": 446,
+      "filename": "structs.23.wasm",
       "text": "invalid type",
       "module_type": "binary"
     }
diff --git a/tests/snapshots/local/shared-everything-threads/struct.wast/0.print b/tests/snapshots/local/shared-everything-threads/structs.wast/0.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/struct.wast/0.print
rename to tests/snapshots/local/shared-everything-threads/structs.wast/0.print
diff --git a/tests/snapshots/local/shared-everything-threads/struct.wast/9.print b/tests/snapshots/local/shared-everything-threads/structs.wast/10.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/struct.wast/9.print
rename to tests/snapshots/local/shared-everything-threads/structs.wast/10.print
diff --git a/tests/snapshots/local/shared-everything-threads/struct.wast/6.print b/tests/snapshots/local/shared-everything-threads/structs.wast/6.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/struct.wast/6.print
rename to tests/snapshots/local/shared-everything-threads/structs.wast/6.print
diff --git a/tests/snapshots/local/shared-everything-threads/struct.wast/7.print b/tests/snapshots/local/shared-everything-threads/structs.wast/7.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/struct.wast/7.print
rename to tests/snapshots/local/shared-everything-threads/structs.wast/7.print
diff --git a/tests/snapshots/local/shared-everything-threads/struct.wast/8.print b/tests/snapshots/local/shared-everything-threads/structs.wast/8.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/struct.wast/8.print
rename to tests/snapshots/local/shared-everything-threads/structs.wast/8.print
diff --git a/tests/snapshots/local/shared-everything-threads/structs.wast/9.print b/tests/snapshots/local/shared-everything-threads/structs.wast/9.print
new file mode 100644
index 00000000..2416b69b
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/structs.wast/9.print
@@ -0,0 +1,19 @@
+(module
+  (type $i8 (;0;) (shared(struct (field (mut i8)))))
+  (type (;1;) (func))
+  (func (;0;) (type 1)
+    ref.null (shared none)
+    struct.get_s $i8 0
+    drop
+  )
+  (func (;1;) (type 1)
+    ref.null (shared none)
+    struct.get_u $i8 0
+    drop
+  )
+  (func (;2;) (type 1)
+    ref.null (shared none)
+    i32.const 0
+    struct.set $i8 0
+  )
+)
diff --git a/tests/snapshots/local/shared-everything-threads/table.wast.json b/tests/snapshots/local/shared-everything-threads/tables.wast.json
similarity index 76%
rename from tests/snapshots/local/shared-everything-threads/table.wast.json
rename to tests/snapshots/local/shared-everything-threads/tables.wast.json
index 13a852f3..14f91dac 100644
--- a/tests/snapshots/local/shared-everything-threads/table.wast.json
+++ b/tests/snapshots/local/shared-everything-threads/tables.wast.json
@@ -1,65 +1,65 @@
 {
-  "source_filename": "tests/local/shared-everything-threads/table.wast",
+  "source_filename": "tests/local/shared-everything-threads/tables.wast",
   "commands": [
     {
       "type": "module",
       "line": 3,
-      "filename": "table.0.wasm"
+      "filename": "tables.0.wasm"
     },
     {
       "type": "module",
       "line": 17,
-      "filename": "table.1.wasm"
+      "filename": "tables.1.wasm"
     },
     {
       "type": "assert_malformed",
       "line": 22,
-      "filename": "table.2.wat",
+      "filename": "tables.2.wat",
       "text": "unexpected token",
       "module_type": "text"
     },
     {
       "type": "assert_malformed",
       "line": 26,
-      "filename": "table.3.wat",
+      "filename": "tables.3.wat",
       "text": "unexpected token",
       "module_type": "text"
     },
     {
       "type": "assert_malformed",
       "line": 32,
-      "filename": "table.4.wat",
+      "filename": "tables.4.wat",
       "text": "unexpected token",
       "module_type": "text"
     },
     {
       "type": "assert_invalid",
       "line": 36,
-      "filename": "table.5.wasm",
+      "filename": "tables.5.wasm",
       "text": "shared tables must have a shared element type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
       "line": 40,
-      "filename": "table.6.wasm",
+      "filename": "tables.6.wasm",
       "text": "shared tables must have a shared element type",
       "module_type": "binary"
     },
     {
       "type": "module",
       "line": 46,
-      "filename": "table.7.wasm"
+      "filename": "tables.7.wasm"
     },
     {
       "type": "module",
       "line": 115,
-      "filename": "table.8.wasm"
+      "filename": "tables.8.wasm"
     },
     {
       "type": "assert_invalid",
       "line": 167,
-      "filename": "table.9.wasm",
+      "filename": "tables.9.wasm",
       "text": "invalid type",
       "module_type": "binary"
     }
diff --git a/tests/snapshots/local/shared-everything-threads/table.wast/0.print b/tests/snapshots/local/shared-everything-threads/tables.wast/0.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/table.wast/0.print
rename to tests/snapshots/local/shared-everything-threads/tables.wast/0.print
diff --git a/tests/snapshots/local/shared-everything-threads/table.wast/1.print b/tests/snapshots/local/shared-everything-threads/tables.wast/1.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/table.wast/1.print
rename to tests/snapshots/local/shared-everything-threads/tables.wast/1.print
diff --git a/tests/snapshots/local/shared-everything-threads/table.wast/7.print b/tests/snapshots/local/shared-everything-threads/tables.wast/7.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/table.wast/7.print
rename to tests/snapshots/local/shared-everything-threads/tables.wast/7.print
diff --git a/tests/snapshots/local/shared-everything-threads/table.wast/8.print b/tests/snapshots/local/shared-everything-threads/tables.wast/8.print
similarity index 100%
rename from tests/snapshots/local/shared-everything-threads/table.wast/8.print
rename to tests/snapshots/local/shared-everything-threads/tables.wast/8.print
diff --git a/tests/snapshots/local/shared-everything-threads/global-heap-types.wast.json b/tests/snapshots/local/shared-everything-threads/types.wast.json
similarity index 72%
rename from tests/snapshots/local/shared-everything-threads/global-heap-types.wast.json
rename to tests/snapshots/local/shared-everything-threads/types.wast.json
index e0a82de6..deb475c7 100644
--- a/tests/snapshots/local/shared-everything-threads/global-heap-types.wast.json
+++ b/tests/snapshots/local/shared-everything-threads/types.wast.json
@@ -1,393 +1,393 @@
 {
-  "source_filename": "tests/local/shared-everything-threads/global-heap-types.wast",
+  "source_filename": "tests/local/shared-everything-threads/types.wast",
   "commands": [
     {
       "type": "module",
       "line": 4,
-      "filename": "global-heap-types.0.wasm"
+      "filename": "types.0.wasm"
     },
     {
       "type": "assert_invalid",
       "line": 19,
-      "filename": "global-heap-types.1.wasm",
+      "filename": "types.1.wasm",
       "text": "shared globals must have a shared value type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
       "line": 22,
-      "filename": "global-heap-types.2.wasm",
+      "filename": "types.2.wasm",
       "text": "shared globals must have a shared value type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
       "line": 25,
-      "filename": "global-heap-types.3.wasm",
+      "filename": "types.3.wasm",
       "text": "type mismatch",
       "module_type": "binary"
     },
     {
       "type": "module",
       "line": 29,
-      "filename": "global-heap-types.4.wasm"
+      "filename": "types.4.wasm"
     },
     {
       "type": "assert_invalid",
       "line": 44,
-      "filename": "global-heap-types.5.wasm",
+      "filename": "types.5.wasm",
       "text": "shared globals must have a shared value type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
       "line": 47,
-      "filename": "global-heap-types.6.wasm",
+      "filename": "types.6.wasm",
       "text": "shared globals must have a shared value type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
       "line": 50,
-      "filename": "global-heap-types.7.wasm",
+      "filename": "types.7.wasm",
       "text": "type mismatch",
       "module_type": "binary"
     },
     {
       "type": "module",
       "line": 54,
-      "filename": "global-heap-types.8.wasm"
+      "filename": "types.8.wasm"
     },
     {
       "type": "assert_invalid",
       "line": 69,
-      "filename": "global-heap-types.9.wasm",
+      "filename": "types.9.wasm",
       "text": "shared globals must have a shared value type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
       "line": 72,
-      "filename": "global-heap-types.10.wasm",
+      "filename": "types.10.wasm",
       "text": "shared globals must have a shared value type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
       "line": 75,
-      "filename": "global-heap-types.11.wasm",
+      "filename": "types.11.wasm",
       "text": "type mismatch",
       "module_type": "binary"
     },
     {
       "type": "module",
       "line": 79,
-      "filename": "global-heap-types.12.wasm"
+      "filename": "types.12.wasm"
     },
     {
       "type": "assert_invalid",
       "line": 94,
-      "filename": "global-heap-types.13.wasm",
+      "filename": "types.13.wasm",
       "text": "shared globals must have a shared value type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
       "line": 97,
-      "filename": "global-heap-types.14.wasm",
+      "filename": "types.14.wasm",
       "text": "shared globals must have a shared value type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
       "line": 100,
-      "filename": "global-heap-types.15.wasm",
+      "filename": "types.15.wasm",
       "text": "type mismatch",
       "module_type": "binary"
     },
     {
       "type": "module",
       "line": 104,
-      "filename": "global-heap-types.16.wasm"
+      "filename": "types.16.wasm"
     },
     {
       "type": "assert_invalid",
       "line": 119,
-      "filename": "global-heap-types.17.wasm",
+      "filename": "types.17.wasm",
       "text": "shared globals must have a shared value type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
       "line": 122,
-      "filename": "global-heap-types.18.wasm",
+      "filename": "types.18.wasm",
       "text": "shared globals must have a shared value type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
       "line": 125,
-      "filename": "global-heap-types.19.wasm",
+      "filename": "types.19.wasm",
       "text": "type mismatch",
       "module_type": "binary"
     },
     {
       "type": "module",
       "line": 129,
-      "filename": "global-heap-types.20.wasm"
+      "filename": "types.20.wasm"
     },
     {
       "type": "assert_invalid",
       "line": 144,
-      "filename": "global-heap-types.21.wasm",
+      "filename": "types.21.wasm",
       "text": "shared globals must have a shared value type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
       "line": 147,
-      "filename": "global-heap-types.22.wasm",
+      "filename": "types.22.wasm",
       "text": "shared globals must have a shared value type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
       "line": 150,
-      "filename": "global-heap-types.23.wasm",
+      "filename": "types.23.wasm",
       "text": "type mismatch",
       "module_type": "binary"
     },
     {
       "type": "module",
       "line": 154,
-      "filename": "global-heap-types.24.wasm"
+      "filename": "types.24.wasm"
     },
     {
       "type": "assert_invalid",
       "line": 169,
-      "filename": "global-heap-types.25.wasm",
+      "filename": "types.25.wasm",
       "text": "shared globals must have a shared value type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
       "line": 172,
-      "filename": "global-heap-types.26.wasm",
+      "filename": "types.26.wasm",
       "text": "shared globals must have a shared value type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
       "line": 175,
-      "filename": "global-heap-types.27.wasm",
+      "filename": "types.27.wasm",
       "text": "type mismatch",
       "module_type": "binary"
     },
     {
       "type": "module",
       "line": 179,
-      "filename": "global-heap-types.28.wasm"
+      "filename": "types.28.wasm"
     },
     {
       "type": "assert_invalid",
       "line": 194,
-      "filename": "global-heap-types.29.wasm",
+      "filename": "types.29.wasm",
       "text": "shared globals must have a shared value type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
       "line": 197,
-      "filename": "global-heap-types.30.wasm",
+      "filename": "types.30.wasm",
       "text": "shared globals must have a shared value type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
       "line": 200,
-      "filename": "global-heap-types.31.wasm",
+      "filename": "types.31.wasm",
       "text": "type mismatch",
       "module_type": "binary"
     },
     {
       "type": "module",
       "line": 204,
-      "filename": "global-heap-types.32.wasm"
+      "filename": "types.32.wasm"
     },
     {
       "type": "assert_invalid",
       "line": 219,
-      "filename": "global-heap-types.33.wasm",
+      "filename": "types.33.wasm",
       "text": "shared globals must have a shared value type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
       "line": 222,
-      "filename": "global-heap-types.34.wasm",
+      "filename": "types.34.wasm",
       "text": "shared globals must have a shared value type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
       "line": 225,
-      "filename": "global-heap-types.35.wasm",
+      "filename": "types.35.wasm",
       "text": "type mismatch",
       "module_type": "binary"
     },
     {
       "type": "module",
       "line": 229,
-      "filename": "global-heap-types.36.wasm"
+      "filename": "types.36.wasm"
     },
     {
       "type": "assert_invalid",
       "line": 244,
-      "filename": "global-heap-types.37.wasm",
+      "filename": "types.37.wasm",
       "text": "shared globals must have a shared value type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
       "line": 247,
-      "filename": "global-heap-types.38.wasm",
+      "filename": "types.38.wasm",
       "text": "shared globals must have a shared value type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
       "line": 250,
-      "filename": "global-heap-types.39.wasm",
+      "filename": "types.39.wasm",
       "text": "type mismatch",
       "module_type": "binary"
     },
     {
       "type": "module",
       "line": 254,
-      "filename": "global-heap-types.40.wasm"
+      "filename": "types.40.wasm"
     },
     {
       "type": "assert_invalid",
       "line": 269,
-      "filename": "global-heap-types.41.wasm",
+      "filename": "types.41.wasm",
       "text": "shared globals must have a shared value type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
       "line": 272,
-      "filename": "global-heap-types.42.wasm",
+      "filename": "types.42.wasm",
       "text": "shared globals must have a shared value type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
       "line": 275,
-      "filename": "global-heap-types.43.wasm",
+      "filename": "types.43.wasm",
       "text": "type mismatch",
       "module_type": "binary"
     },
     {
       "type": "module",
       "line": 279,
-      "filename": "global-heap-types.44.wasm"
+      "filename": "types.44.wasm"
     },
     {
       "type": "assert_invalid",
       "line": 294,
-      "filename": "global-heap-types.45.wasm",
+      "filename": "types.45.wasm",
       "text": "shared globals must have a shared value type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
       "line": 297,
-      "filename": "global-heap-types.46.wasm",
+      "filename": "types.46.wasm",
       "text": "shared globals must have a shared value type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
       "line": 300,
-      "filename": "global-heap-types.47.wasm",
+      "filename": "types.47.wasm",
       "text": "type mismatch",
       "module_type": "binary"
     },
     {
       "type": "module",
       "line": 304,
-      "filename": "global-heap-types.48.wasm"
+      "filename": "types.48.wasm"
     },
     {
       "type": "assert_invalid",
       "line": 319,
-      "filename": "global-heap-types.49.wasm",
+      "filename": "types.49.wasm",
       "text": "shared globals must have a shared value type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
       "line": 324,
-      "filename": "global-heap-types.50.wasm",
+      "filename": "types.50.wasm",
       "text": "type mismatch",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
       "line": 329,
-      "filename": "global-heap-types.51.wasm",
+      "filename": "types.51.wasm",
       "text": "shared composite type must contain shared types",
       "module_type": "binary"
     },
     {
       "type": "module",
       "line": 333,
-      "filename": "global-heap-types.52.wasm"
+      "filename": "types.52.wasm"
     },
     {
       "type": "assert_invalid",
       "line": 348,
-      "filename": "global-heap-types.53.wasm",
+      "filename": "types.53.wasm",
       "text": "shared globals must have a shared value type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
       "line": 353,
-      "filename": "global-heap-types.54.wasm",
+      "filename": "types.54.wasm",
       "text": "type mismatch",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
       "line": 358,
-      "filename": "global-heap-types.55.wasm",
+      "filename": "types.55.wasm",
       "text": "shared composite type must contain shared types",
       "module_type": "binary"
     },
     {
       "type": "module",
       "line": 362,
-      "filename": "global-heap-types.56.wasm"
+      "filename": "types.56.wasm"
     },
     {
       "type": "assert_invalid",
       "line": 377,
-      "filename": "global-heap-types.57.wasm",
+      "filename": "types.57.wasm",
       "text": "shared globals must have a shared value type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
       "line": 382,
-      "filename": "global-heap-types.58.wasm",
+      "filename": "types.58.wasm",
       "text": "type mismatch",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
       "line": 387,
-      "filename": "global-heap-types.59.wasm",
+      "filename": "types.59.wasm",
       "text": "shared composite type must contain shared types",
       "module_type": "binary"
     }