diff --git a/Cargo.toml b/Cargo.toml
index 796d74dbdff1..ed703cbe3341 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -96,3 +96,6 @@ harness = false
 
 [profile.dev.package.backtrace]
 debug = false # FIXME(#1813)
+
+[patch.crates-io]
+wasmparser = { git = 'https://github.com/alexcrichton/wasm-tools', branch = 'fix-validate-submodule' }
diff --git a/cranelift/wasm/src/translation_utils.rs b/cranelift/wasm/src/translation_utils.rs
index e16103323884..c8493d4b51e5 100644
--- a/cranelift/wasm/src/translation_utils.rs
+++ b/cranelift/wasm/src/translation_utils.rs
@@ -110,7 +110,7 @@ pub struct InstanceTypeIndex(u32);
 entity_impl!(InstanceTypeIndex);
 
 /// An index of an entity.
-#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
+#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
 #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
 pub enum EntityIndex {
     /// Function index.
diff --git a/crates/c-api/src/extern.rs b/crates/c-api/src/extern.rs
index 0b395dd2fa76..6a08a6e13ea2 100644
--- a/crates/c-api/src/extern.rs
+++ b/crates/c-api/src/extern.rs
@@ -16,6 +16,10 @@ pub extern "C" fn wasm_extern_kind(e: &wasm_extern_t) -> wasm_externkind_t {
         Extern::Global(_) => crate::WASM_EXTERN_GLOBAL,
         Extern::Table(_) => crate::WASM_EXTERN_TABLE,
         Extern::Memory(_) => crate::WASM_EXTERN_MEMORY,
+
+        // FIXME(#2094)
+        Extern::Instance(_) => unimplemented!(),
+        Extern::Module(_) => unimplemented!(),
     }
 }
 
diff --git a/crates/environ/src/module.rs b/crates/environ/src/module.rs
index d78efadf0f33..c555a4435b8f 100644
--- a/crates/environ/src/module.rs
+++ b/crates/environ/src/module.rs
@@ -346,6 +346,30 @@ impl Module {
     pub fn is_imported_global(&self, index: GlobalIndex) -> bool {
         index.index() < self.num_imported_globals
     }
+
+    /// Test whether the given global index is for an imported global.
+    pub fn imports(&self) -> impl Iterator<Item = (&str, Option<&str>, EntityType)> {
+        self.initializers.iter().filter_map(move |i| match i {
+            Initializer::Import {
+                module,
+                field,
+                index,
+            } => Some((module.as_str(), field.as_deref(), self.type_of(*index))),
+            _ => None,
+        })
+    }
+
+    /// Returns the type of an item based on its index
+    pub fn type_of(&self, index: EntityIndex) -> EntityType {
+        match index {
+            EntityIndex::Global(i) => EntityType::Global(self.globals[i]),
+            EntityIndex::Table(i) => EntityType::Table(self.table_plans[i].table),
+            EntityIndex::Memory(i) => EntityType::Memory(self.memory_plans[i].memory),
+            EntityIndex::Function(i) => EntityType::Function(self.functions[i]),
+            EntityIndex::Instance(i) => EntityType::Instance(self.instances[i]),
+            EntityIndex::Module(i) => EntityType::Module(self.modules[i]),
+        }
+    }
 }
 
 /// All types which are recorded for the entirety of a translation.
@@ -376,7 +400,7 @@ pub struct ModuleSignature {
 #[derive(Debug, Clone, Serialize, Deserialize)]
 pub struct InstanceSignature {
     /// The name of what's being exported as well as its type signature.
-    pub exports: Vec<(String, EntityType)>,
+    pub exports: IndexMap<String, EntityType>,
 }
 
 mod passive_data_serde {
diff --git a/crates/environ/src/module_environ.rs b/crates/environ/src/module_environ.rs
index f1af5c4ea023..6d36b4e7a9f7 100644
--- a/crates/environ/src/module_environ.rs
+++ b/crates/environ/src/module_environ.rs
@@ -857,7 +857,7 @@ and for re-adding support for interface types you can see this issue:
             // instance.
             Alias::Child { instance, export } => {
                 let ty = self.result.module.instances[instance];
-                match &self.types.instance_signatures[ty].exports[export].1 {
+                match &self.types.instance_signatures[ty].exports[export] {
                     EntityType::Global(g) => {
                         self.result.module.globals.push(g.clone());
                         self.result.module.num_imported_globals += 1;
diff --git a/crates/runtime/src/export.rs b/crates/runtime/src/export.rs
index edfa51e1f290..0161e56a6809 100644
--- a/crates/runtime/src/export.rs
+++ b/crates/runtime/src/export.rs
@@ -1,13 +1,14 @@
 use crate::vmcontext::{
     VMCallerCheckedAnyfunc, VMContext, VMGlobalDefinition, VMMemoryDefinition, VMTableDefinition,
 };
+use crate::InstanceHandle;
+use std::any::Any;
 use std::ptr::NonNull;
 use wasmtime_environ::wasm::Global;
 use wasmtime_environ::{MemoryPlan, TablePlan};
 
 /// The value of an export passed from one instance to another.
-#[derive(Debug, Clone)]
-pub enum Export {
+pub enum Export<'a> {
     /// A function export value.
     Function(ExportFunction),
 
@@ -19,6 +20,12 @@ pub enum Export {
 
     /// A global export value.
     Global(ExportGlobal),
+
+    /// An instance
+    Instance(&'a InstanceHandle),
+
+    /// A module
+    Module(&'a dyn Any),
 }
 
 /// A function export value.
@@ -31,8 +38,8 @@ pub struct ExportFunction {
     pub anyfunc: NonNull<VMCallerCheckedAnyfunc>,
 }
 
-impl From<ExportFunction> for Export {
-    fn from(func: ExportFunction) -> Export {
+impl<'a> From<ExportFunction> for Export<'a> {
+    fn from(func: ExportFunction) -> Export<'a> {
         Export::Function(func)
     }
 }
@@ -48,8 +55,8 @@ pub struct ExportTable {
     pub table: TablePlan,
 }
 
-impl From<ExportTable> for Export {
-    fn from(func: ExportTable) -> Export {
+impl<'a> From<ExportTable> for Export<'a> {
+    fn from(func: ExportTable) -> Export<'a> {
         Export::Table(func)
     }
 }
@@ -65,8 +72,8 @@ pub struct ExportMemory {
     pub memory: MemoryPlan,
 }
 
-impl From<ExportMemory> for Export {
-    fn from(func: ExportMemory) -> Export {
+impl<'a> From<ExportMemory> for Export<'a> {
+    fn from(func: ExportMemory) -> Export<'a> {
         Export::Memory(func)
     }
 }
@@ -82,8 +89,8 @@ pub struct ExportGlobal {
     pub global: Global,
 }
 
-impl From<ExportGlobal> for Export {
-    fn from(func: ExportGlobal) -> Export {
+impl<'a> From<ExportGlobal> for Export<'a> {
+    fn from(func: ExportGlobal) -> Export<'a> {
         Export::Global(func)
     }
 }
diff --git a/crates/runtime/src/imports.rs b/crates/runtime/src/imports.rs
index 2f85ba220199..19696307503a 100644
--- a/crates/runtime/src/imports.rs
+++ b/crates/runtime/src/imports.rs
@@ -1,12 +1,21 @@
 use crate::vmcontext::{VMFunctionImport, VMGlobalImport, VMMemoryImport, VMTableImport};
+use crate::InstanceHandle;
+use std::any::Any;
+use wasmtime_environ::entity::PrimaryMap;
+use wasmtime_environ::wasm::{InstanceIndex, ModuleIndex};
 
 /// Resolved import pointers.
 ///
-/// Note that each of these fields are slices, not `PrimaryMap`. They should be
+/// Note that some of these fields are slices, not `PrimaryMap`. They should be
 /// stored in index-order as with the module that we're providing the imports
 /// for, and indexing is all done the same way as the main module's index
 /// spaces.
-#[derive(Clone, Default)]
+///
+/// Also note that the way we compile modules means that for the module linking
+/// proposal all `alias` directives should map to imported items. This means
+/// that each of these items aren't necessarily directly imported, but may be
+/// aliased.
+#[derive(Default)]
 pub struct Imports<'a> {
     /// Resolved addresses for imported functions.
     pub functions: &'a [VMFunctionImport],
@@ -19,4 +28,15 @@ pub struct Imports<'a> {
 
     /// Resolved addresses for imported globals.
     pub globals: &'a [VMGlobalImport],
+
+    /// Resolved imported instances.
+    pub instances: PrimaryMap<InstanceIndex, InstanceHandle>,
+
+    /// Resolved imported modules.
+    ///
+    /// Note that `Box<Any>` here is chosen to allow the embedder of this crate
+    /// to pick an appropriate representation of what module type should be. For
+    /// example for the `wasmtime` crate it's `wasmtime::Module` but that's not
+    /// defined way down here in this low crate.
+    pub modules: PrimaryMap<ModuleIndex, Box<dyn Any>>,
 }
diff --git a/crates/runtime/src/instance.rs b/crates/runtime/src/instance.rs
index 1f39b171a032..9ff7c4ee6db6 100644
--- a/crates/runtime/src/instance.rs
+++ b/crates/runtime/src/instance.rs
@@ -28,8 +28,8 @@ use thiserror::Error;
 use wasmtime_environ::entity::{packed_option::ReservedValue, BoxedSlice, EntityRef, PrimaryMap};
 use wasmtime_environ::wasm::{
     DataIndex, DefinedFuncIndex, DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex,
-    ElemIndex, EntityIndex, FuncIndex, GlobalIndex, GlobalInit, MemoryIndex, SignatureIndex,
-    TableElementType, TableIndex, WasmType,
+    ElemIndex, EntityIndex, FuncIndex, GlobalIndex, GlobalInit, InstanceIndex, MemoryIndex,
+    ModuleIndex, SignatureIndex, TableElementType, TableIndex, WasmType,
 };
 use wasmtime_environ::{ir, DataInitializer, Module, ModuleType, TableElements, VMOffsets};
 
@@ -50,6 +50,15 @@ pub(crate) struct Instance {
     /// WebAssembly table data.
     tables: BoxedSlice<DefinedTableIndex, Table>,
 
+    /// Instances our module defined and their handles.
+    instances: PrimaryMap<InstanceIndex, InstanceHandle>,
+
+    /// Modules that are located in our index space.
+    ///
+    /// For now these are `Box<Any>` so the caller can define the type of what a
+    /// module looks like.
+    modules: PrimaryMap<ModuleIndex, Box<dyn Any>>,
+
     /// Passive elements in this instantiation. As `elem.drop`s happen, these
     /// entries get removed. A missing entry is considered equivalent to an
     /// empty slice.
@@ -268,7 +277,7 @@ impl Instance {
     }
 
     /// Lookup an export with the given export declaration.
-    pub fn lookup_by_declaration(&self, export: &EntityIndex) -> Export {
+    pub fn lookup_by_declaration(&self, export: &EntityIndex) -> Export<'_> {
         match export {
             EntityIndex::Function(index) => {
                 let anyfunc = self.get_caller_checked_anyfunc(*index).unwrap();
@@ -317,9 +326,8 @@ impl Instance {
             }
             .into(),
 
-            // FIXME(#2094)
-            EntityIndex::Instance(_index) => unimplemented!(),
-            EntityIndex::Module(_index) => unimplemented!(),
+            EntityIndex::Instance(index) => Export::Instance(&self.instances[*index]),
+            EntityIndex::Module(index) => Export::Module(&*self.modules[*index]),
         }
     }
 
@@ -847,6 +855,8 @@ impl InstanceHandle {
                 passive_elements: Default::default(),
                 passive_data,
                 host_state,
+                instances: imports.instances,
+                modules: imports.modules,
                 vmctx: VMContext {},
             };
             let layout = instance.alloc_layout();
diff --git a/crates/wasmtime/Cargo.toml b/crates/wasmtime/Cargo.toml
index 28de26001ebc..c0d0ddb579d5 100644
--- a/crates/wasmtime/Cargo.toml
+++ b/crates/wasmtime/Cargo.toml
@@ -29,6 +29,7 @@ wat = { version = "1.0.18", optional = true }
 smallvec = "1.4.0"
 serde = { version = "1.0.94", features = ["derive"] }
 bincode = "1.2.1"
+indexmap = "1.6"
 
 [target.'cfg(target_os = "windows")'.dependencies]
 winapi = "0.3.7"
diff --git a/crates/wasmtime/src/externals.rs b/crates/wasmtime/src/externals.rs
index 63f2e2678f4d..9a075e308efc 100644
--- a/crates/wasmtime/src/externals.rs
+++ b/crates/wasmtime/src/externals.rs
@@ -3,8 +3,8 @@ use crate::trampoline::{
 };
 use crate::values::{from_checked_anyfunc, into_checked_anyfunc, Val};
 use crate::{
-    ExternRef, ExternType, Func, GlobalType, MemoryType, Mutability, Store, TableType, Trap,
-    ValType,
+    ExternRef, ExternType, Func, GlobalType, Instance, MemoryType, Module, Mutability, Store,
+    TableType, Trap, ValType,
 };
 use anyhow::{anyhow, bail, Result};
 use std::mem;
@@ -33,6 +33,10 @@ pub enum Extern {
     Table(Table),
     /// A WebAssembly linear memory.
     Memory(Memory),
+    /// A WebAssembly instance.
+    Instance(Instance),
+    /// A WebAssembly module.
+    Module(Module),
 }
 
 impl Extern {
@@ -76,6 +80,26 @@ impl Extern {
         }
     }
 
+    /// Returns the underlying `Instance`, if this external is a instance.
+    ///
+    /// Returns `None` if this is not a instance.
+    pub fn into_instance(self) -> Option<Instance> {
+        match self {
+            Extern::Instance(instance) => Some(instance),
+            _ => None,
+        }
+    }
+
+    /// Returns the underlying `Module`, if this external is a module.
+    ///
+    /// Returns `None` if this is not a module.
+    pub fn into_module(self) -> Option<Module> {
+        match self {
+            Extern::Module(module) => Some(module),
+            _ => None,
+        }
+    }
+
     /// Returns the type associated with this `Extern`.
     pub fn ty(&self) -> ExternType {
         match self {
@@ -83,6 +107,8 @@ impl Extern {
             Extern::Memory(ft) => ExternType::Memory(ft.ty()),
             Extern::Table(tt) => ExternType::Table(tt.ty()),
             Extern::Global(gt) => ExternType::Global(gt.ty()),
+            Extern::Instance(i) => ExternType::Instance(i.ty()),
+            Extern::Module(m) => ExternType::Module(m.ty()),
         }
     }
 
@@ -103,6 +129,13 @@ impl Extern {
             wasmtime_runtime::Export::Table(t) => {
                 Extern::Table(Table::from_wasmtime_table(t, instance))
             }
+            wasmtime_runtime::Export::Instance(i) => {
+                let handle = unsafe { instance.store.existing_instance_handle(i.clone()) };
+                Extern::Instance(Instance::from_wasmtime(handle))
+            }
+            wasmtime_runtime::Export::Module(m) => {
+                Extern::Module(m.downcast_ref::<Module>().unwrap().clone())
+            }
         }
     }
 
@@ -112,6 +145,10 @@ impl Extern {
             Extern::Global(g) => &g.instance.store,
             Extern::Memory(m) => &m.instance.store,
             Extern::Table(t) => &t.instance.store,
+            Extern::Instance(i) => i.store(),
+            // Modules don't live in stores right now, so they're compatible
+            // with all stores.
+            Extern::Module(_) => return true,
         };
         Store::same(my_store, store)
     }
@@ -122,6 +159,8 @@ impl Extern {
             Extern::Table(_) => "table",
             Extern::Memory(_) => "memory",
             Extern::Global(_) => "global",
+            Extern::Instance(_) => "instance",
+            Extern::Module(_) => "module",
         }
     }
 }
@@ -150,6 +189,18 @@ impl From<Table> for Extern {
     }
 }
 
+impl From<Instance> for Extern {
+    fn from(r: Instance) -> Self {
+        Extern::Instance(r)
+    }
+}
+
+impl From<Module> for Extern {
+    fn from(r: Module) -> Self {
+        Extern::Module(r)
+    }
+}
+
 /// A WebAssembly `global` value which can be read and written to.
 ///
 /// A `global` in WebAssembly is sort of like a global variable within an
@@ -294,11 +345,8 @@ impl Global {
         }
     }
 
-    pub(crate) fn matches_expected(&self, expected: &wasmtime_environ::wasm::Global) -> bool {
-        let actual = &self.wasmtime_export.global;
-        expected.ty == actual.ty
-            && expected.wasm_ty == actual.wasm_ty
-            && expected.mutability == actual.mutability
+    pub(crate) fn wasmtime_ty(&self) -> &wasmtime_environ::wasm::Global {
+        &self.wasmtime_export.global
     }
 
     pub(crate) fn vmimport(&self) -> wasmtime_runtime::VMGlobalImport {
@@ -538,19 +586,8 @@ impl Table {
         }
     }
 
-    pub(crate) fn matches_expected(&self, ty: &wasmtime_environ::TablePlan) -> bool {
-        let expected = &ty.table;
-        let actual = &self.wasmtime_export.table.table;
-        expected.wasm_ty == actual.wasm_ty
-            && expected.ty == actual.ty
-            && expected.minimum <= actual.minimum
-            && match expected.maximum {
-                Some(expected) => match actual.maximum {
-                    Some(actual) => expected >= actual,
-                    None => false,
-                },
-                None => true,
-            }
+    pub(crate) fn wasmtime_ty(&self) -> &wasmtime_environ::wasm::Table {
+        &self.wasmtime_export.table.table
     }
 
     pub(crate) fn vmimport(&self) -> wasmtime_runtime::VMTableImport {
@@ -960,18 +997,8 @@ impl Memory {
         }
     }
 
-    pub(crate) fn matches_expected(&self, ty: &wasmtime_environ::MemoryPlan) -> bool {
-        let expected = &ty.memory;
-        let actual = &self.wasmtime_export.memory.memory;
-        expected.shared == actual.shared
-            && expected.minimum <= actual.minimum
-            && match expected.maximum {
-                Some(expected) => match actual.maximum {
-                    Some(actual) => expected >= actual,
-                    None => false,
-                },
-                None => true,
-            }
+    pub(crate) fn wasmtime_ty(&self) -> &wasmtime_environ::wasm::Memory {
+        &self.wasmtime_export.memory.memory
     }
 
     pub(crate) fn vmimport(&self) -> wasmtime_runtime::VMMemoryImport {
diff --git a/crates/wasmtime/src/func.rs b/crates/wasmtime/src/func.rs
index b57936d67d98..630c5f736d24 100644
--- a/crates/wasmtime/src/func.rs
+++ b/crates/wasmtime/src/func.rs
@@ -798,10 +798,6 @@ impl Func {
         &self.instance.store
     }
 
-    pub(crate) fn matches_expected(&self, expected: VMSharedSignatureIndex) -> bool {
-        self.sig_index() == expected
-    }
-
     pub(crate) fn vmimport(&self) -> wasmtime_runtime::VMFunctionImport {
         unsafe {
             let f = self.caller_checked_anyfunc();
@@ -1503,7 +1499,6 @@ impl Caller<'_> {
                 return None;
             }
             let instance = InstanceHandle::from_vmctx(self.caller_vmctx);
-            let export = instance.lookup(name)?;
             // Our `Weak` pointer is used only to break a cycle where `Store`
             // stores instance handles which have this weak pointer as their
             // custom host data. This function should only be invoke-able while
@@ -1511,6 +1506,7 @@ impl Caller<'_> {
             debug_assert!(self.store.upgrade().is_some());
             let handle =
                 Store::from_inner(self.store.upgrade()?).existing_instance_handle(instance);
+            let export = handle.lookup(name)?;
             match export {
                 Export::Memory(m) => Some(Extern::Memory(Memory::from_wasmtime_memory(m, handle))),
                 Export::Function(f) => Some(Extern::Func(Func::from_wasmtime_function(f, handle))),
diff --git a/crates/wasmtime/src/instance.rs b/crates/wasmtime/src/instance.rs
index 066a61864036..678ced4f52bd 100644
--- a/crates/wasmtime/src/instance.rs
+++ b/crates/wasmtime/src/instance.rs
@@ -1,16 +1,23 @@
 use crate::trampoline::StoreInstanceHandle;
-use crate::{Engine, Export, Extern, Func, Global, Memory, Module, Store, Table, Trap};
+use crate::types::matching;
+use crate::{
+    Engine, Export, Extern, ExternType, Func, Global, InstanceType, Memory, Module, Store, Table,
+    Trap,
+};
 use anyhow::{bail, Context, Error, Result};
 use std::mem;
+use std::sync::Arc;
 use wasmtime_environ::entity::PrimaryMap;
 use wasmtime_environ::wasm::{
-    EntityIndex, FuncIndex, GlobalIndex, InstanceIndex, MemoryIndex, ModuleIndex, TableIndex,
+    EntityIndex, EntityType, FuncIndex, GlobalIndex, InstanceIndex, MemoryIndex, ModuleIndex,
+    TableIndex,
 };
 use wasmtime_environ::Initializer;
-use wasmtime_jit::{CompiledModule, TypeTables};
+use wasmtime_jit::TypeTables;
 use wasmtime_runtime::{
-    Imports, InstantiationError, StackMapRegistry, VMContext, VMExternRefActivationsTable,
-    VMFunctionBody, VMFunctionImport, VMGlobalImport, VMMemoryImport, VMTableImport,
+    Imports, InstanceHandle, InstantiationError, StackMapRegistry, VMContext,
+    VMExternRefActivationsTable, VMFunctionBody, VMFunctionImport, VMGlobalImport, VMMemoryImport,
+    VMTableImport,
 };
 
 /// Performs all low-level steps necessary for instantiation.
@@ -35,17 +42,16 @@ use wasmtime_runtime::{
 ///   into the provided builder. The expected entity that it's defining is also
 ///   passed in for the top-level case where type-checking is performed. This is
 ///   fallible because type checks may fail.
-fn instantiate<'a>(
-    store: &'a Store,
-    compiled_module: &'a CompiledModule,
-    all_modules: &'a [CompiledModule],
-    types: &'a TypeTables,
-    parent_modules: &PrimaryMap<ModuleIndex, &'a CompiledModule>,
-    define_import: &mut dyn FnMut(&EntityIndex, &mut ImportsBuilder<'a>) -> Result<()>,
+fn instantiate(
+    store: &Store,
+    module: &Module,
+    parent_modules: &PrimaryMap<ModuleIndex, Module>,
+    define_import: &mut dyn FnMut(&EntityIndex, &mut ImportsBuilder<'_>) -> Result<()>,
 ) -> Result<StoreInstanceHandle, Error> {
+    let compiled_module = module.compiled_module();
     let env_module = compiled_module.module();
 
-    let mut imports = ImportsBuilder::new(env_module, types, store);
+    let mut imports = ImportsBuilder::new(store, module);
     for initializer in env_module.initializers.iter() {
         match initializer {
             // Definition of an import depends on how our parent is providing
@@ -67,24 +73,28 @@ fn instantiate<'a>(
             // This one's pretty easy, we're just picking up our parent's module
             // and putting it into our own index space.
             Initializer::AliasParentModule(idx) => {
-                imports.modules.push(parent_modules[*idx]);
+                imports.modules.push(parent_modules[*idx].clone());
             }
 
             // Turns out defining any kind of module is pretty easy, we're just
             // slinging around pointers.
             Initializer::DefineModule(idx) => {
-                imports.modules.push(&all_modules[*idx]);
+                imports.modules.push(module.submodule(*idx));
             }
 
             // Here we lookup our instance handle, ask it for the nth export,
             // and then push that item into our own index space. We eschew
             // type-checking since only valid modules reach this point.
+            //
+            // Note that the unsafety here is because we're asserting that the
+            // handle comes from our same store, but this should be true because
+            // we acquired fthe handle from an instance in the store.
             Initializer::AliasInstanceExport { instance, export } => {
                 let handle = &imports.instances[*instance];
                 let export_index = &handle.module().exports[*export];
                 let item = Extern::from_wasmtime_export(
                     handle.lookup_by_declaration(export_index),
-                    handle.clone(),
+                    unsafe { store.existing_instance_handle(handle.clone()) },
                 );
                 imports.push_extern(&item);
             }
@@ -100,14 +110,16 @@ fn instantiate<'a>(
             // to be a DAG. Additionally the recursion should also be bounded
             // due to validation. We may one day need to make this an iterative
             // loop, however.
+            //
+            // Also note that there's some unsafety here around cloning
+            // `InstanceHandle` because the handle may not live long enough, but
+            // we're doing all of this in the context of our `Store` argument
+            // above so we should be safe here.
             Initializer::Instantiate { module, args } => {
-                let module_to_instantiate = imports.modules[*module];
                 let mut args = args.iter();
                 let handle = instantiate(
                     store,
-                    module_to_instantiate,
-                    all_modules,
-                    types,
+                    &imports.modules[*module],
                     &imports.modules,
                     &mut |_, builder| {
                         match *args.next().unwrap() {
@@ -124,16 +136,18 @@ fn instantiate<'a>(
                                 builder.memories.push(imports.memories[i]);
                             }
                             EntityIndex::Module(i) => {
-                                builder.modules.push(imports.modules[i]);
+                                builder.modules.push(imports.modules[i].clone());
                             }
                             EntityIndex::Instance(i) => {
-                                builder.instances.push(imports.instances[i].clone());
+                                builder
+                                    .instances
+                                    .push(unsafe { imports.instances[i].clone() });
                             }
                         }
                         Ok(())
                     },
                 )?;
-                imports.instances.push(handle);
+                imports.instances.push(unsafe { (*handle).clone() });
             }
         }
     }
@@ -146,16 +160,16 @@ fn instantiate<'a>(
     // Register the module just before instantiation to ensure we have a
     // trampoline registered for every signature and to preserve the module's
     // compiled JIT code within the `Store`.
-    store.register_module(compiled_module, types);
+    store.register_module(module);
 
     let config = store.engine().config();
     let instance = unsafe {
         let instance = compiled_module.instantiate(
             imports,
-            &store.lookup_shared_signature(types),
+            &store.lookup_shared_signature(module.types()),
             config.memory_creator.as_ref().map(|a| a as _),
             store.interrupts(),
-            Box::new(()),
+            Box::new(module.types().clone()),
             store.externref_activations_table() as *const VMExternRefActivationsTable as *mut _,
             store.stack_map_registry() as *const StackMapRegistry as *mut _,
         )?;
@@ -230,7 +244,6 @@ fn instantiate<'a>(
 #[derive(Clone)]
 pub struct Instance {
     pub(crate) handle: StoreInstanceHandle,
-    module: Module,
 }
 
 impl Instance {
@@ -294,16 +307,7 @@ impl Instance {
 
         // Perform some pre-flight checks before we get into the meat of
         // instantiation.
-        let expected = module
-            .compiled_module()
-            .module()
-            .initializers
-            .iter()
-            .filter(|e| match e {
-                Initializer::Import { .. } => true,
-                _ => false,
-            })
-            .count();
+        let expected = module.compiled_module().module().imports().count();
         if expected != imports.len() {
             bail!("expected {} imports, found {}", expected, imports.len());
         }
@@ -314,22 +318,34 @@ impl Instance {
         }
 
         let mut imports = imports.iter();
-        let handle = instantiate(
-            store,
-            module.compiled_module(),
-            module.all_compiled_modules(),
-            module.types(),
-            &PrimaryMap::new(),
-            &mut |idx, builder| {
-                let import = imports.next().expect("already checked the length");
-                builder.define_extern(idx, import)
-            },
-        )?;
+        let handle = instantiate(store, module, &PrimaryMap::new(), &mut |idx, builder| {
+            let import = imports.next().expect("already checked the length");
+            builder.define_extern(idx, import)
+        })?;
 
-        Ok(Instance {
-            handle,
-            module: module.clone(),
-        })
+        Ok(Instance { handle })
+    }
+
+    pub(crate) fn from_wasmtime(handle: StoreInstanceHandle) -> Instance {
+        Instance { handle }
+    }
+
+    /// Returns the type signature of this instance.
+    pub fn ty(&self) -> InstanceType {
+        let mut ty = InstanceType::new();
+        let module = self.handle.module();
+        let types = self
+            .handle
+            .host_state()
+            .downcast_ref::<Arc<TypeTables>>()
+            .unwrap();
+        for (name, index) in module.exports.iter() {
+            ty.add_named_export(
+                name,
+                ExternType::from_wasmtime(types, &module.type_of(*index)),
+            );
+        }
+        return ty;
     }
 
     /// Returns the associated [`Store`] that this `Instance` is compiled into.
@@ -400,24 +416,20 @@ struct ImportsBuilder<'a> {
     tables: PrimaryMap<TableIndex, VMTableImport>,
     memories: PrimaryMap<MemoryIndex, VMMemoryImport>,
     globals: PrimaryMap<GlobalIndex, VMGlobalImport>,
-    instances: PrimaryMap<InstanceIndex, StoreInstanceHandle>,
-    modules: PrimaryMap<ModuleIndex, &'a CompiledModule>,
+    instances: PrimaryMap<InstanceIndex, InstanceHandle>,
+    modules: PrimaryMap<ModuleIndex, Module>,
 
     module: &'a wasmtime_environ::Module,
-    store: &'a Store,
-    types: &'a TypeTables,
+    matcher: matching::MatchCx<'a>,
 }
 
 impl<'a> ImportsBuilder<'a> {
-    fn new(
-        module: &'a wasmtime_environ::Module,
-        types: &'a TypeTables,
-        store: &'a Store,
-    ) -> ImportsBuilder<'a> {
+    fn new(store: &'a Store, module: &'a Module) -> ImportsBuilder<'a> {
+        let types = module.types();
+        let module = module.compiled_module().module();
         ImportsBuilder {
             module,
-            store,
-            types,
+            matcher: matching::MatchCx { store, types },
             functions: PrimaryMap::with_capacity(module.num_imported_funcs),
             tables: PrimaryMap::with_capacity(module.num_imported_tables),
             memories: PrimaryMap::with_capacity(module.num_imported_memories),
@@ -428,59 +440,38 @@ impl<'a> ImportsBuilder<'a> {
     }
 
     fn define_extern(&mut self, expected: &EntityIndex, actual: &Extern) -> Result<()> {
-        match *expected {
-            EntityIndex::Table(i) => {
-                self.tables.push(match actual {
-                    Extern::Table(e) if e.matches_expected(&self.module.table_plans[i]) => {
-                        e.vmimport()
-                    }
-                    Extern::Table(_) => bail!("table types incompatible"),
-                    _ => bail!("expected table, but found {}", actual.desc()),
-                });
-            }
-            EntityIndex::Memory(i) => {
-                self.memories.push(match actual {
-                    Extern::Memory(e) if e.matches_expected(&self.module.memory_plans[i]) => {
-                        e.vmimport()
-                    }
-                    Extern::Memory(_) => bail!("memory types incompatible"),
-                    _ => bail!("expected memory, but found {}", actual.desc()),
-                });
-            }
-            EntityIndex::Global(i) => {
-                self.globals.push(match actual {
-                    Extern::Global(e) if e.matches_expected(&self.module.globals[i]) => {
-                        e.vmimport()
-                    }
-                    Extern::Global(_) => bail!("global types incompatible"),
-                    _ => bail!("expected global, but found {}", actual.desc()),
-                });
-            }
-            EntityIndex::Function(i) => {
-                let func = match actual {
-                    Extern::Func(e) => e,
-                    _ => bail!("expected function, but found {}", actual.desc()),
-                };
-                // Look up the `i`th function's type from the module in our
-                // signature registry. If it's not present then we have no
-                // functions registered with that type, so `func` is guaranteed
-                // to not match.
-                let ty = self
-                    .store
-                    .signatures()
-                    .borrow()
-                    .lookup(&self.types.wasm_signatures[self.module.functions[i]])
-                    .ok_or_else(|| anyhow::format_err!("function types incompatible"))?;
-                if !func.matches_expected(ty) {
-                    bail!("function types incompatible");
-                }
-                self.functions.push(func.vmimport());
-            }
-
-            // FIXME(#2094)
-            EntityIndex::Module(_i) => unimplemented!(),
-            EntityIndex::Instance(_i) => unimplemented!(),
+        let expected_ty = self.module.type_of(*expected);
+        let compatible = match &expected_ty {
+            EntityType::Table(i) => match actual {
+                Extern::Table(e) => self.matcher.table(i, e),
+                _ => bail!("expected table, but found {}", actual.desc()),
+            },
+            EntityType::Memory(i) => match actual {
+                Extern::Memory(e) => self.matcher.memory(i, e),
+                _ => bail!("expected memory, but found {}", actual.desc()),
+            },
+            EntityType::Global(i) => match actual {
+                Extern::Global(e) => self.matcher.global(i, e),
+                _ => bail!("expected global, but found {}", actual.desc()),
+            },
+            EntityType::Function(i) => match actual {
+                Extern::Func(e) => self.matcher.func(*i, e),
+                _ => bail!("expected func, but found {}", actual.desc()),
+            },
+            EntityType::Instance(i) => match actual {
+                Extern::Instance(e) => self.matcher.instance(*i, e),
+                _ => bail!("expected instance, but found {}", actual.desc()),
+            },
+            EntityType::Module(i) => match actual {
+                Extern::Module(e) => self.matcher.module(*i, e),
+                _ => bail!("expected module, but found {}", actual.desc()),
+            },
+            EntityType::Event(_) => unimplemented!(),
+        };
+        if !compatible {
+            bail!("{} types incompatible", actual.desc());
         }
+        self.push_extern(actual);
         Ok(())
     }
 
@@ -498,15 +489,27 @@ impl<'a> ImportsBuilder<'a> {
             Extern::Memory(i) => {
                 self.memories.push(i.vmimport());
             }
+            Extern::Instance(i) => {
+                debug_assert!(Store::same(i.store(), self.matcher.store));
+                self.instances.push(unsafe { (*i.handle).clone() });
+            }
+            Extern::Module(m) => {
+                self.modules.push(m.clone());
+            }
         }
     }
 
-    fn imports(&self) -> Imports<'_> {
+    fn imports(&mut self) -> Imports<'_> {
         Imports {
             tables: self.tables.values().as_slice(),
             globals: self.globals.values().as_slice(),
             memories: self.memories.values().as_slice(),
             functions: self.functions.values().as_slice(),
+            instances: mem::take(&mut self.instances),
+            modules: mem::take(&mut self.modules)
+                .into_iter()
+                .map(|(_, m)| Box::new(m) as Box<_>)
+                .collect(),
         }
     }
 }
diff --git a/crates/wasmtime/src/linker.rs b/crates/wasmtime/src/linker.rs
index e04052789898..763d0fa3cb3d 100644
--- a/crates/wasmtime/src/linker.rs
+++ b/crates/wasmtime/src/linker.rs
@@ -58,6 +58,8 @@ enum ImportKind {
     Global(GlobalType),
     Memory,
     Table,
+    Module,
+    Instance,
 }
 
 impl Linker {
@@ -516,10 +518,8 @@ impl Linker {
             ExternType::Global(f) => ImportKind::Global(f),
             ExternType::Memory(_) => ImportKind::Memory,
             ExternType::Table(_) => ImportKind::Table,
-
-            // FIXME(#2094)
-            ExternType::Module(_) => unimplemented!(),
-            ExternType::Instance(_) => unimplemented!(),
+            ExternType::Module(_) => ImportKind::Module,
+            ExternType::Instance(_) => ImportKind::Instance,
         }
     }
 
diff --git a/crates/wasmtime/src/module.rs b/crates/wasmtime/src/module.rs
index 37f068af6c14..b3d19f5ff496 100644
--- a/crates/wasmtime/src/module.rs
+++ b/crates/wasmtime/src/module.rs
@@ -1,5 +1,5 @@
-use crate::types::{EntityType, ExportType, ExternType, ImportType};
-use crate::Engine;
+use crate::types::{ExportType, ExternType, ImportType};
+use crate::{Engine, ModuleType};
 use anyhow::{bail, Context, Result};
 use bincode::Options;
 use std::hash::Hash;
@@ -86,7 +86,7 @@ pub struct Module {
 }
 
 pub(crate) struct ModuleData {
-    pub(crate) types: TypeTables,
+    pub(crate) types: Arc<TypeTables>,
     pub(crate) modules: Vec<CompiledModule>,
 }
 
@@ -258,6 +258,7 @@ impl Module {
             &*engine.config().profiler,
         )?;
 
+        let types = Arc::new(types);
         Ok(Module {
             engine: engine.clone(),
             index: 0,
@@ -291,6 +292,23 @@ impl Module {
         Ok(())
     }
 
+    /// Returns the type signature of this module.
+    pub fn ty(&self) -> ModuleType {
+        let mut sig = ModuleType::new();
+        let env_module = self.compiled_module().module();
+        let types = self.types();
+        for (module, field, ty) in env_module.imports() {
+            sig.add_named_import(module, field, ExternType::from_wasmtime(types, &ty));
+        }
+        for (name, index) in env_module.exports.iter() {
+            sig.add_named_export(
+                name,
+                ExternType::from_wasmtime(types, &env_module.type_of(*index)),
+            );
+        }
+        return sig;
+    }
+
     /// Serialize compilation artifacts to the buffer. See also `deseriaize`.
     pub fn serialize(&self) -> Result<Vec<u8>> {
         let artifacts = (
@@ -300,7 +318,7 @@ impl Module {
                 .iter()
                 .map(|i| i.compilation_artifacts())
                 .collect::<Vec<_>>(),
-            &self.data.types,
+            &*self.data.types,
             self.index,
         );
 
@@ -333,6 +351,7 @@ impl Module {
             &*engine.config().profiler,
         )?;
 
+        let types = Arc::new(types);
         Ok(Module {
             engine: engine.clone(),
             index,
@@ -344,11 +363,16 @@ impl Module {
         &self.data.modules[self.index]
     }
 
-    pub(crate) fn all_compiled_modules(&self) -> &[CompiledModule] {
-        &self.data.modules
+    pub(crate) fn submodule(&self, index: usize) -> Module {
+        assert!(index < self.data.modules.len());
+        Module {
+            engine: self.engine.clone(),
+            data: self.data.clone(),
+            index,
+        }
     }
 
-    pub(crate) fn types(&self) -> &TypeTables {
+    pub(crate) fn types(&self) -> &Arc<TypeTables> {
         &self.data.types
     }
 
@@ -433,20 +457,10 @@ impl Module {
         &'module self,
     ) -> impl ExactSizeIterator<Item = ImportType<'module>> + 'module {
         let module = self.compiled_module().module();
+        let types = self.types();
         module
-            .initializers
-            .iter()
-            .filter_map(move |initializer| match initializer {
-                wasmtime_environ::Initializer::Import {
-                    module,
-                    field,
-                    index,
-                } => {
-                    let ty = EntityType::new(index, self);
-                    Some(ImportType::new(module, field.as_deref(), ty))
-                }
-                _ => None,
-            })
+            .imports()
+            .map(move |(module, field, ty)| ImportType::new(module, field, ty, types))
             .collect::<Vec<_>>()
             .into_iter()
     }
@@ -509,9 +523,9 @@ impl Module {
         &'module self,
     ) -> impl ExactSizeIterator<Item = ExportType<'module>> + 'module {
         let module = self.compiled_module().module();
+        let types = self.types();
         module.exports.iter().map(move |(name, entity_index)| {
-            let ty = EntityType::new(entity_index, self);
-            ExportType::new(name, ty)
+            ExportType::new(name, module.type_of(*entity_index), types)
         })
     }
 
@@ -561,7 +575,10 @@ impl Module {
     pub fn get_export<'module>(&'module self, name: &'module str) -> Option<ExternType> {
         let module = self.compiled_module().module();
         let entity_index = module.exports.get(name)?;
-        Some(EntityType::new(entity_index, self).extern_type())
+        Some(ExternType::from_wasmtime(
+            self.types(),
+            &module.type_of(*entity_index),
+        ))
     }
 
     /// Returns the [`Engine`] that this [`Module`] was compiled by.
diff --git a/crates/wasmtime/src/store.rs b/crates/wasmtime/src/store.rs
index 9761b40b9190..6b314f6ac16b 100644
--- a/crates/wasmtime/src/store.rs
+++ b/crates/wasmtime/src/store.rs
@@ -1,7 +1,7 @@
 use crate::frame_info::StoreFrameInfo;
 use crate::sig_registry::SignatureRegistry;
 use crate::trampoline::StoreInstanceHandle;
-use crate::Engine;
+use crate::{Engine, Module};
 use anyhow::{bail, Result};
 use std::any::Any;
 use std::cell::RefCell;
@@ -147,7 +147,7 @@ impl Store {
         }
     }
 
-    pub(crate) fn register_module(&self, module: &CompiledModule, types: &TypeTables) {
+    pub(crate) fn register_module(&self, module: &Module) {
         // All modules register their JIT code in a store for two reasons
         // currently:
         //
@@ -158,18 +158,18 @@ impl Store {
         // * Second when generating a backtrace we'll use this mapping to
         //   only generate wasm frames for instruction pointers that fall
         //   within jit code.
-        self.register_jit_code(module);
+        self.register_jit_code(module.compiled_module());
 
         // We need to know about all the stack maps of all instantiated modules
         // so when performing a GC we know about all wasm frames that we find
         // on the stack.
-        self.register_stack_maps(module);
+        self.register_stack_maps(module.compiled_module());
 
         // Signatures are loaded into our `SignatureRegistry` here
         // once-per-module (and once-per-signature). This allows us to create
         // a `Func` wrapper for any function in the module, which requires that
         // we know about the signature and trampoline for all instances.
-        self.register_signatures(module, types);
+        self.register_signatures(module);
 
         // And finally with a module being instantiated into this `Store` we
         // need to preserve its jit-code. References to this module's code and
@@ -178,7 +178,7 @@ impl Store {
         self.inner
             .modules
             .borrow_mut()
-            .insert(ArcModuleCode(module.code().clone()));
+            .insert(ArcModuleCode(module.compiled_module().code().clone()));
     }
 
     fn register_jit_code(&self, module: &CompiledModule) {
@@ -205,10 +205,10 @@ impl Store {
             }));
     }
 
-    fn register_signatures(&self, module: &CompiledModule, types: &TypeTables) {
-        let trampolines = module.trampolines();
+    fn register_signatures(&self, module: &Module) {
+        let trampolines = module.compiled_module().trampolines();
         let mut signatures = self.signatures().borrow_mut();
-        for (index, wasm) in types.wasm_signatures.iter() {
+        for (index, wasm) in module.types().wasm_signatures.iter() {
             signatures.register(wasm, trampolines[index]);
         }
     }
diff --git a/crates/wasmtime/src/types.rs b/crates/wasmtime/src/types.rs
index 9e992aa8e89c..3a7076a764aa 100644
--- a/crates/wasmtime/src/types.rs
+++ b/crates/wasmtime/src/types.rs
@@ -1,7 +1,9 @@
-use crate::Module;
 use std::fmt;
-use wasmtime_environ::wasm::WasmFuncType;
+use wasmtime_environ::wasm::{EntityType, WasmFuncType};
 use wasmtime_environ::{ir, wasm};
+use wasmtime_jit::TypeTables;
+
+pub(crate) mod matching;
 
 // Type Representations
 
@@ -196,23 +198,25 @@ impl ExternType {
         (Instance(InstanceType) instance unwrap_instance)
     }
 
-    fn from_wasmtime(module: &Module, ty: &wasmtime_environ::wasm::EntityType) -> ExternType {
-        use wasmtime_environ::wasm::EntityType;
+    pub(crate) fn from_wasmtime(
+        types: &TypeTables,
+        ty: &wasmtime_environ::wasm::EntityType,
+    ) -> ExternType {
         match ty {
             EntityType::Function(idx) => {
-                let sig = &module.types().wasm_signatures[*idx];
+                let sig = &types.wasm_signatures[*idx];
                 FuncType::from_wasm_func_type(sig).into()
             }
             EntityType::Global(ty) => GlobalType::from_wasmtime_global(ty).into(),
             EntityType::Memory(ty) => MemoryType::from_wasmtime_memory(ty).into(),
             EntityType::Table(ty) => TableType::from_wasmtime_table(ty).into(),
             EntityType::Module(ty) => {
-                let ty = &module.types().module_signatures[*ty];
-                ModuleType::from_wasmtime(module, ty).into()
+                let ty = &types.module_signatures[*ty];
+                ModuleType::from_wasmtime(types, ty).into()
             }
             EntityType::Instance(ty) => {
-                let ty = &module.types().instance_signatures[*ty];
-                InstanceType::from_wasmtime(module, ty).into()
+                let ty = &types.instance_signatures[*ty];
+                InstanceType::from_wasmtime(types, ty).into()
             }
             EntityType::Event(_) => unimplemented!("wasm event support"),
         }
@@ -490,14 +494,14 @@ impl ModuleType {
     }
 
     pub(crate) fn from_wasmtime(
-        module: &Module,
+        types: &TypeTables,
         ty: &wasmtime_environ::ModuleSignature,
     ) -> ModuleType {
-        let exports = &module.types().instance_signatures[ty.exports].exports;
+        let exports = &types.instance_signatures[ty.exports].exports;
         ModuleType {
             exports: exports
                 .iter()
-                .map(|(name, ty)| (name.to_string(), ExternType::from_wasmtime(module, ty)))
+                .map(|(name, ty)| (name.to_string(), ExternType::from_wasmtime(types, ty)))
                 .collect(),
             imports: ty
                 .imports
@@ -506,7 +510,7 @@ impl ModuleType {
                     (
                         m.to_string(),
                         name.as_ref().map(|n| n.to_string()),
-                        ExternType::from_wasmtime(module, ty),
+                        ExternType::from_wasmtime(types, ty),
                     )
                 })
                 .collect(),
@@ -548,83 +552,19 @@ impl InstanceType {
     }
 
     pub(crate) fn from_wasmtime(
-        module: &Module,
+        types: &TypeTables,
         ty: &wasmtime_environ::InstanceSignature,
     ) -> InstanceType {
         InstanceType {
             exports: ty
                 .exports
                 .iter()
-                .map(|(name, ty)| (name.to_string(), ExternType::from_wasmtime(module, ty)))
+                .map(|(name, ty)| (name.to_string(), ExternType::from_wasmtime(types, ty)))
                 .collect(),
         }
     }
 }
 
-// Entity Types
-
-#[derive(Clone)]
-pub(crate) enum EntityType<'module> {
-    Function(&'module wasm::WasmFuncType),
-    Table(&'module wasm::Table),
-    Memory(&'module wasm::Memory),
-    Global(&'module wasm::Global),
-    Module {
-        ty: &'module wasmtime_environ::ModuleSignature,
-        module: &'module Module,
-    },
-    Instance {
-        ty: &'module wasmtime_environ::InstanceSignature,
-        module: &'module Module,
-    },
-}
-
-impl<'module> EntityType<'module> {
-    /// Translate from a `EntityIndex` into an `ExternType`.
-    pub(crate) fn new(
-        entity_index: &wasm::EntityIndex,
-        module: &'module Module,
-    ) -> EntityType<'module> {
-        let env_module = module.compiled_module().module();
-        match entity_index {
-            wasm::EntityIndex::Function(func_index) => {
-                let sig_index = env_module.functions[*func_index];
-                let sig = &module.types().wasm_signatures[sig_index];
-                EntityType::Function(sig)
-            }
-            wasm::EntityIndex::Table(table_index) => {
-                EntityType::Table(&env_module.table_plans[*table_index].table)
-            }
-            wasm::EntityIndex::Memory(memory_index) => {
-                EntityType::Memory(&env_module.memory_plans[*memory_index].memory)
-            }
-            wasm::EntityIndex::Global(global_index) => {
-                EntityType::Global(&env_module.globals[*global_index])
-            }
-            wasm::EntityIndex::Module(idx) => {
-                let ty = &module.types().module_signatures[env_module.modules[*idx]];
-                EntityType::Module { ty, module }
-            }
-            wasm::EntityIndex::Instance(idx) => {
-                let ty = &module.types().instance_signatures[env_module.instances[*idx]];
-                EntityType::Instance { ty, module }
-            }
-        }
-    }
-
-    /// Convert this `EntityType` to an `ExternType`.
-    pub(crate) fn extern_type(&self) -> ExternType {
-        match self {
-            EntityType::Function(sig) => FuncType::from_wasm_func_type(sig).into(),
-            EntityType::Table(table) => TableType::from_wasmtime_table(table).into(),
-            EntityType::Memory(memory) => MemoryType::from_wasmtime_memory(memory).into(),
-            EntityType::Global(global) => GlobalType::from_wasmtime_global(global).into(),
-            EntityType::Instance { module, ty } => InstanceType::from_wasmtime(module, ty).into(),
-            EntityType::Module { module, ty } => ModuleType::from_wasmtime(module, ty).into(),
-        }
-    }
-}
-
 // Import Types
 
 /// A descriptor for an imported value into a wasm module.
@@ -647,7 +587,7 @@ pub struct ImportType<'module> {
 
 #[derive(Clone)]
 enum EntityOrExtern<'a> {
-    Entity(EntityType<'a>),
+    Entity(EntityType, &'a TypeTables),
     Extern(&'a ExternType),
 }
 
@@ -657,12 +597,13 @@ impl<'module> ImportType<'module> {
     pub(crate) fn new(
         module: &'module str,
         name: Option<&'module str>,
-        ty: EntityType<'module>,
+        ty: EntityType,
+        types: &'module TypeTables,
     ) -> ImportType<'module> {
         ImportType {
             module,
             name,
-            ty: EntityOrExtern::Entity(ty),
+            ty: EntityOrExtern::Entity(ty, types),
         }
     }
 
@@ -683,7 +624,7 @@ impl<'module> ImportType<'module> {
     /// Returns the expected type of this import.
     pub fn ty(&self) -> ExternType {
         match &self.ty {
-            EntityOrExtern::Entity(e) => e.extern_type(),
+            EntityOrExtern::Entity(e, types) => ExternType::from_wasmtime(types, e),
             EntityOrExtern::Extern(e) => (*e).clone(),
         }
     }
@@ -719,10 +660,14 @@ pub struct ExportType<'module> {
 impl<'module> ExportType<'module> {
     /// Creates a new export which is exported with the given `name` and has the
     /// given `ty`.
-    pub(crate) fn new(name: &'module str, ty: EntityType<'module>) -> ExportType<'module> {
+    pub(crate) fn new(
+        name: &'module str,
+        ty: EntityType,
+        types: &'module TypeTables,
+    ) -> ExportType<'module> {
         ExportType {
             name,
-            ty: EntityOrExtern::Entity(ty),
+            ty: EntityOrExtern::Entity(ty, types),
         }
     }
 
@@ -734,7 +679,7 @@ impl<'module> ExportType<'module> {
     /// Returns the type of this export.
     pub fn ty(&self) -> ExternType {
         match &self.ty {
-            EntityOrExtern::Entity(e) => e.extern_type(),
+            EntityOrExtern::Entity(e, types) => ExternType::from_wasmtime(types, e),
             EntityOrExtern::Extern(e) => (*e).clone(),
         }
     }
diff --git a/crates/wasmtime/src/types/matching.rs b/crates/wasmtime/src/types/matching.rs
new file mode 100644
index 000000000000..c9ef03c26c12
--- /dev/null
+++ b/crates/wasmtime/src/types/matching.rs
@@ -0,0 +1,195 @@
+use crate::Store;
+use std::sync::Arc;
+use wasmtime_environ::wasm::{
+    EntityType, Global, InstanceTypeIndex, Memory, ModuleTypeIndex, SignatureIndex, Table,
+};
+use wasmtime_jit::TypeTables;
+
+pub struct MatchCx<'a> {
+    pub types: &'a TypeTables,
+    pub store: &'a Store,
+}
+
+impl MatchCx<'_> {
+    pub fn global(&self, expected: &Global, actual: &crate::Global) -> bool {
+        self.global_ty(expected, actual.wasmtime_ty())
+    }
+
+    fn global_ty(&self, expected: &Global, actual: &Global) -> bool {
+        expected.ty == actual.ty
+            && expected.wasm_ty == actual.wasm_ty
+            && expected.mutability == actual.mutability
+    }
+
+    pub fn table(&self, expected: &Table, actual: &crate::Table) -> bool {
+        self.table_ty(expected, actual.wasmtime_ty())
+    }
+
+    fn table_ty(&self, expected: &Table, actual: &Table) -> bool {
+        expected.wasm_ty == actual.wasm_ty
+            && expected.ty == actual.ty
+            && expected.minimum <= actual.minimum
+            && match expected.maximum {
+                Some(expected) => match actual.maximum {
+                    Some(actual) => expected >= actual,
+                    None => false,
+                },
+                None => true,
+            }
+    }
+
+    pub fn memory(&self, expected: &Memory, actual: &crate::Memory) -> bool {
+        self.memory_ty(expected, actual.wasmtime_ty())
+    }
+
+    fn memory_ty(&self, expected: &Memory, actual: &Memory) -> bool {
+        expected.shared == actual.shared
+            && expected.minimum <= actual.minimum
+            && match expected.maximum {
+                Some(expected) => match actual.maximum {
+                    Some(actual) => expected >= actual,
+                    None => false,
+                },
+                None => true,
+            }
+    }
+
+    pub fn func(&self, expected: SignatureIndex, actual: &crate::Func) -> bool {
+        match self
+            .store
+            .signatures()
+            .borrow()
+            .lookup(&self.types.wasm_signatures[expected])
+        {
+            Some(idx) => actual.sig_index() == idx,
+            // If our expected signature isn't registered, then there's no way
+            // that `actual` can match it.
+            None => false,
+        }
+    }
+
+    pub fn instance(&self, expected: InstanceTypeIndex, actual: &crate::Instance) -> bool {
+        let module = actual.handle.module();
+        self.exports_match(
+            expected,
+            actual
+                .handle
+                .host_state()
+                .downcast_ref::<Arc<TypeTables>>()
+                .unwrap(),
+            |name| module.exports.get(name).map(|idx| module.type_of(*idx)),
+        )
+    }
+
+    /// Validates that the type signature of `actual` matches the `expected`
+    /// module type signature.
+    pub fn module(&self, expected: ModuleTypeIndex, actual: &crate::Module) -> bool {
+        let expected_sig = &self.types.module_signatures[expected];
+        let module = actual.compiled_module().module();
+        self.imports_match(expected, actual.types(), module.imports())
+            && self.exports_match(expected_sig.exports, actual.types(), |name| {
+                module.exports.get(name).map(|idx| module.type_of(*idx))
+            })
+    }
+
+    /// Validaates that the `actual_imports` list of module imports matches the
+    /// `expected` module type signature.
+    ///
+    /// Types specified in `actual_imports` are relative to `actual_types`.
+    fn imports_match<'a>(
+        &self,
+        expected: ModuleTypeIndex,
+        actual_types: &TypeTables,
+        mut actual_imports: impl Iterator<Item = (&'a str, Option<&'a str>, EntityType)>,
+    ) -> bool {
+        let expected_sig = &self.types.module_signatures[expected];
+        for (_, _, expected) in expected_sig.imports.iter() {
+            let (_, _, ty) = match actual_imports.next() {
+                Some(e) => e,
+                None => return false,
+            };
+            if !self.extern_ty_matches(expected, &ty, actual_types) {
+                return false;
+            }
+        }
+        actual_imports.next().is_none()
+    }
+
+    /// Validates that all exports in `expected` are defined by `lookup` within
+    /// `actual_types`.
+    fn exports_match(
+        &self,
+        expected: InstanceTypeIndex,
+        actual_types: &TypeTables,
+        lookup: impl Fn(&str) -> Option<EntityType>,
+    ) -> bool {
+        // The `expected` type must be a subset of `actual`, meaning that all
+        // names in `expected` must be present in `actual`. Note that we do
+        // name-based lookup here instead of index-based lookup.
+        self.types.instance_signatures[expected].exports.iter().all(
+            |(name, expected)| match lookup(name) {
+                Some(ty) => self.extern_ty_matches(expected, &ty, actual_types),
+                None => false,
+            },
+        )
+    }
+
+    /// Validates that the `expected` entity matches the `actual_ty` defined
+    /// within `actual_types`.
+    fn extern_ty_matches(
+        &self,
+        expected: &EntityType,
+        actual_ty: &EntityType,
+        actual_types: &TypeTables,
+    ) -> bool {
+        match expected {
+            EntityType::Global(expected) => match actual_ty {
+                EntityType::Global(actual) => self.global_ty(expected, actual),
+                _ => false,
+            },
+            EntityType::Table(expected) => match actual_ty {
+                EntityType::Table(actual) => self.table_ty(expected, actual),
+                _ => false,
+            },
+            EntityType::Memory(expected) => match actual_ty {
+                EntityType::Memory(actual) => self.memory_ty(expected, actual),
+                _ => false,
+            },
+            EntityType::Function(expected) => match *actual_ty {
+                EntityType::Function(actual) => {
+                    self.types.wasm_signatures[*expected] == actual_types.wasm_signatures[actual]
+                }
+                _ => false,
+            },
+            EntityType::Instance(expected) => match actual_ty {
+                EntityType::Instance(actual) => {
+                    let sig = &actual_types.instance_signatures[*actual];
+                    self.exports_match(*expected, actual_types, |name| {
+                        sig.exports.get(name).cloned()
+                    })
+                }
+                _ => false,
+            },
+            EntityType::Module(expected) => match actual_ty {
+                EntityType::Module(actual) => {
+                    let expected_module_sig = &self.types.module_signatures[*expected];
+                    let actual_module_sig = &actual_types.module_signatures[*actual];
+                    let actual_instance_sig =
+                        &actual_types.instance_signatures[actual_module_sig.exports];
+
+                    self.imports_match(
+                        *expected,
+                        actual_types,
+                        actual_module_sig.imports.iter().map(|(module, field, ty)| {
+                            (module.as_str(), field.as_deref(), ty.clone())
+                        }),
+                    ) && self.exports_match(expected_module_sig.exports, actual_types, |name| {
+                        actual_instance_sig.exports.get(name).cloned()
+                    })
+                }
+                _ => false,
+            },
+            EntityType::Event(_) => unimplemented!(),
+        }
+    }
+}
diff --git a/tests/all/wast.rs b/tests/all/wast.rs
index 9b069c2ebe0f..9227079a0f85 100644
--- a/tests/all/wast.rs
+++ b/tests/all/wast.rs
@@ -23,8 +23,8 @@ fn run_wast(wast: &str, strategy: Strategy) -> anyhow::Result<()> {
     let mut cfg = Config::new();
     cfg.wasm_simd(simd)
         .wasm_bulk_memory(bulk_mem)
-        .wasm_reference_types(reftypes)
-        .wasm_multi_memory(multi_memory)
+        .wasm_reference_types(reftypes || module_linking)
+        .wasm_multi_memory(multi_memory || module_linking)
         .wasm_module_linking(module_linking)
         .strategy(strategy)?
         .cranelift_debug_verifier(true);
diff --git a/tests/misc_testsuite/module-linking/alias.wast b/tests/misc_testsuite/module-linking/alias.wast
index 73324b251b9f..0c69b4084c7c 100644
--- a/tests/misc_testsuite/module-linking/alias.wast
+++ b/tests/misc_testsuite/module-linking/alias.wast
@@ -54,7 +54,38 @@
 )
 (assert_return (invoke "get") (i32.const 4))
 
-;; TODO instances/modules -- needs import/export of modules/instances to work
+;; modules
+(module
+  (module $m
+    (module $sub (export "module")
+      (func $f (export "") (result i32)
+        i32.const 5))
+  )
+  (instance $a (instantiate $m))
+  (instance $b (instantiate $a.$sub))
+  (alias $b.$f (instance $b) (func 0))
+
+  (func (export "get") (result i32)
+    call $b.$f)
+)
+(assert_return (invoke "get") (i32.const 5))
+
+;; instances
+(module
+  (module $m
+    (module $sub
+      (func $f (export "") (result i32)
+        i32.const 6))
+    (instance $i (export "") (instantiate $sub))
+  )
+  (instance $a (instantiate $m))
+  (alias $a.$i (instance $a) (instance 0))
+  (alias $a.$i.$f (instance $a.$i) (func 0))
+
+  (func (export "get") (result i32)
+    call $a.$i.$f)
+)
+(assert_return (invoke "get") (i32.const 6))
 
 ;; alias parent -- type
 (module
diff --git a/tests/misc_testsuite/module-linking/import-subtyping.wast b/tests/misc_testsuite/module-linking/import-subtyping.wast
new file mode 100644
index 000000000000..4ac16580706c
--- /dev/null
+++ b/tests/misc_testsuite/module-linking/import-subtyping.wast
@@ -0,0 +1,348 @@
+;; subsets of imports
+(module $a
+  (module (export "m")
+    (func (export ""))
+    (func (export "a"))
+    (global (export "b") i32 (i32.const 0))
+  )
+)
+
+(module
+  (import "a" "m" (module))
+  (import "a" "m" (module (export "" (func))))
+  (import "a" "m" (module (export "a" (func))))
+  (import "a" "m" (module (export "b" (global i32))))
+  (import "a" "m" (module
+    (export "" (func))
+    (export "a" (func))
+  ))
+  (import "a" "m" (module
+    (export "a" (func))
+    (export "" (func))
+  ))
+  (import "a" "m" (module
+    (export "a" (func))
+    (export "" (func))
+    (export "b" (global i32))
+  ))
+  (import "a" "m" (module
+    (export "b" (global i32))
+    (export "a" (func))
+    (export "" (func))
+  ))
+)
+
+;; functions
+(module $a
+  (module (export "m")
+    (func (export ""))))
+
+(module
+  (import "a" "m" (module))
+  (import "a" "m" (module (export "" (func))))
+)
+(assert_unlinkable
+  (module (import "a" "m" (module (export "" (func (param i32))))))
+  "module types incompatible")
+(assert_unlinkable
+  (module (import "a" "m" (module (export "" (func (result i32))))))
+  "module types incompatible")
+(assert_unlinkable
+  (module (import "a" "m" (module (export "" (global i32)))))
+  "module types incompatible")
+(assert_unlinkable
+  (module (import "a" "m" (module (export "" (table 1 funcref)))))
+  "module types incompatible")
+(assert_unlinkable
+  (module (import "a" "m" (module (export "" (memory 1)))))
+  "module types incompatible")
+(assert_unlinkable
+  (module (import "a" "m" (module (export "" (module)))))
+  "module types incompatible")
+(assert_unlinkable
+  (module (import "a" "m" (module (export "" (instance)))))
+  "module types incompatible")
+
+(module $a
+  (module (export "m")
+    (global (export "") i32 (i32.const 0))))
+
+;; globals
+(module
+  (import "a" "m" (module))
+  (import "a" "m" (module (export "" (global i32))))
+)
+(assert_unlinkable
+  (module
+    (import "a" "m" (module (export "" (global (mut i32)))))
+  )
+  "module types incompatible")
+(assert_unlinkable
+  (module (import "a" "m" (module (export "" (global f32)))))
+  "module types incompatible")
+(assert_unlinkable
+  (module (import "a" "m" (module (export "" (func)))))
+  "module types incompatible")
+(assert_unlinkable
+  (module (import "a" "m" (module (export "" (table 1 funcref)))))
+  "module types incompatible")
+(assert_unlinkable
+  (module (import "a" "m" (module (export "" (memory 1)))))
+  "module types incompatible")
+(assert_unlinkable
+  (module (import "a" "m" (module (export "" (module)))))
+  "module types incompatible")
+(assert_unlinkable
+  (module (import "a" "m" (module (export "" (instance)))))
+  "module types incompatible")
+
+;; tables
+(module $a
+  (module (export "m")
+    (table (export "") 1 funcref)
+    (table (export "max") 1 10 funcref)
+  )
+)
+(module
+  (import "a" "m" (module))
+  (import "a" "m" (module (export "" (table 1 funcref))))
+  (import "a" "m" (module (export "" (table 0 funcref))))
+  (import "a" "m" (module (export "max" (table 1 10 funcref))))
+  (import "a" "m" (module (export "max" (table 0 10 funcref))))
+  (import "a" "m" (module (export "max" (table 0 11 funcref))))
+  (import "a" "m" (module (export "max" (table 0 funcref))))
+)
+(assert_unlinkable
+  (module (import "a" "m" (module (export "" (global f32)))))
+  "module types incompatible")
+(assert_unlinkable
+  (module (import "a" "m" (module (export "" (func)))))
+  "module types incompatible")
+(assert_unlinkable
+  (module (import "a" "m" (module (export "" (table 2 funcref)))))
+  "module types incompatible")
+(assert_unlinkable
+  (module (import "a" "m" (module (export "" (table 1 10 funcref)))))
+  "module types incompatible")
+(assert_unlinkable
+  (module (import "a" "m" (module (export "max" (table 2 10 funcref)))))
+  "module types incompatible")
+(assert_unlinkable
+  (module (import "a" "m" (module (export "max" (table 1 9 funcref)))))
+  "module types incompatible")
+(assert_unlinkable
+  (module (import "a" "m" (module (export "" (memory 1)))))
+  "module types incompatible")
+(assert_unlinkable
+  (module (import "a" "m" (module (export "" (module)))))
+  "module types incompatible")
+(assert_unlinkable
+  (module (import "a" "m" (module (export "" (instance)))))
+  "module types incompatible")
+
+;; memories
+(module $a
+  (module (export "m")
+    (memory (export "") 1)
+    (memory (export "max") 1 10)
+  )
+)
+(module
+  (import "a" "m" (module))
+  (import "a" "m" (module (export "" (memory 1))))
+  (import "a" "m" (module (export "" (memory 0))))
+  (import "a" "m" (module (export "max" (memory 1 10))))
+  (import "a" "m" (module (export "max" (memory 0 10))))
+  (import "a" "m" (module (export "max" (memory 0 11))))
+  (import "a" "m" (module (export "max" (memory 0))))
+)
+(assert_unlinkable
+  (module (import "a" "m" (module (export "" (global f32)))))
+  "module types incompatible")
+(assert_unlinkable
+  (module (import "a" "m" (module (export "" (func)))))
+  "module types incompatible")
+(assert_unlinkable
+  (module (import "a" "m" (module (export "" (table 1 funcref)))))
+  "module types incompatible")
+(assert_unlinkable
+  (module (import "a" "m" (module (export "" (memory 2)))))
+  "module types incompatible")
+(assert_unlinkable
+  (module (import "a" "m" (module (export "" (memory 1 10)))))
+  "module types incompatible")
+(assert_unlinkable
+  (module (import "a" "m" (module (export "max" (memory 2 10)))))
+  "module types incompatible")
+(assert_unlinkable
+  (module (import "a" "m" (module (export "max" (memory 2)))))
+  "module types incompatible")
+(assert_unlinkable
+  (module (import "a" "m" (module (export "" (module)))))
+  "module types incompatible")
+(assert_unlinkable
+  (module (import "a" "m" (module (export "" (instance)))))
+  "module types incompatible")
+
+;; modules
+(module $a
+  (module (export "m")
+    ;; export nothing
+    (module (export "a"))
+    ;; export one thing
+    (module (export "b")
+      (func (export ""))
+    )
+    ;; export a mixture
+    (module (export "c")
+      (func (export "a"))
+      (func (export "b") (result i32)
+        i32.const 0)
+      (global (export "c") i32 (i32.const 0))
+    )
+    ;; import one thing
+    (module (export "d")
+      (import "" (func))
+    )
+    ;; import a mixture
+    (module (export "e")
+      (import "" (func))
+      (import "" (func))
+      (import "" (global i32))
+    )
+  )
+)
+(module
+  (import "a" "m" (module))
+  (import "a" "m" (module (export "a" (module))))
+  (import "a" "m" (module (export "b" (module))))
+  (import "a" "m" (module (export "b" (module (export "" (func))))))
+  (import "a" "m" (module (export "c" (module))))
+  (import "a" "m" (module (export "c" (module
+    (export "a" (func))
+  ))))
+  (import "a" "m" (module (export "c" (module
+    (export "a" (func))
+    (export "b" (func (result i32)))
+  ))))
+  (import "a" "m" (module (export "c" (module
+    (export "c" (global i32))
+  ))))
+  (import "a" "m" (module (export "c" (module
+    (export "c" (global i32))
+    (export "a" (func))
+  ))))
+
+  ;; for now import strings aren't matched at all, imports must simply pairwise
+  ;; line up
+  (import "a" "m" (module (export "d" (module (import "" (func))))))
+  (import "a" "m" (module (export "d" (module (import "x" (func))))))
+  (import "a" "m" (module (export "d" (module (import "x" "y" (func))))))
+
+  (import "a" "m" (module (export "e" (module
+    (import "x" "y" (func))
+    (import "a" (func))
+    (import "z" (global i32))
+  ))))
+)
+(assert_unlinkable
+  (module (import "a" "m" (module (export "" (module (export "a" (func)))))))
+  "module types incompatible")
+(assert_unlinkable
+  (module (import "a" "m" (module (export "d" (module)))))
+  "module types incompatible")
+(assert_unlinkable
+  (module (import "a" "m" (module (export "d" (module (import "" (module)))))))
+  "module types incompatible")
+(assert_unlinkable
+  (module (import "a" "m" (module (export "" (global f32)))))
+  "module types incompatible")
+(assert_unlinkable
+  (module (import "a" "m" (module (export "" (func)))))
+  "module types incompatible")
+(assert_unlinkable
+  (module (import "a" "m" (module (export "" (table 1 funcref)))))
+  "module types incompatible")
+(assert_unlinkable
+  (module (import "a" "m" (module (export "" (memory 2)))))
+  "module types incompatible")
+(assert_unlinkable
+  (module (import "a" "m" (module (export "" (module (export "foo" (func)))))))
+  "module types incompatible")
+(assert_unlinkable
+  (module (import "a" "m" (module (export "" (instance)))))
+  "module types incompatible")
+
+;; instances
+(module $a
+  ;; export nothing
+  (module $m1)
+  (instance (export "a") (instantiate $m1))
+  ;; export one thing
+  (module $m2
+    (func (export ""))
+  )
+  (instance (export "b") (instantiate $m2))
+  ;; export a mixture
+  (module $m3
+    (func (export "a"))
+    (func (export "b") (result i32)
+      i32.const 0)
+    (global (export "c") i32 (i32.const 0))
+  )
+  (instance (export "c") (instantiate $m3))
+
+  (module (export "m")
+    ;; export one thing
+    (module $m2
+      (func (export ""))
+    )
+    (instance (export "i") (instantiate $m2))
+  )
+
+)
+(module
+  (import "a" "a" (instance))
+  (import "a" "b" (instance))
+  (import "a" "b" (instance (export "" (func))))
+  (import "a" "c" (instance))
+  (import "a" "c" (instance (export "a" (func))))
+  (import "a" "c" (instance (export "b" (func (result i32)))))
+  (import "a" "c" (instance (export "c" (global i32))))
+  (import "a" "c" (instance
+    (export "a" (func))
+    (export "b" (func (result i32)))
+    (export "c" (global i32))
+  ))
+  (import "a" "c" (instance
+    (export "c" (global i32))
+    (export "a" (func))
+  ))
+
+  (import "a" "m" (module (export "i" (instance))))
+  (import "a" "m" (module (export "i" (instance (export "" (func))))))
+)
+(assert_unlinkable
+  (module (import "a" "a" (instance (export "" (global f32)))))
+  "instance types incompatible")
+(assert_unlinkable
+  (module (import "a" "m" (module (export "i" (instance (export "x" (func)))))))
+  "module types incompatible")
+(assert_unlinkable
+  (module (import "a" "m" (module (export "" (func)))))
+  "module types incompatible")
+(assert_unlinkable
+  (module (import "a" "m" (module (export "" (table 1 funcref)))))
+  "module types incompatible")
+(assert_unlinkable
+  (module (import "a" "m" (module (export "" (memory 2)))))
+  "module types incompatible")
+(assert_unlinkable
+  (module (import "a" "m" (module (export "" (memory 1 10)))))
+  "module types incompatible")
+(assert_unlinkable
+  (module (import "a" "m" (module (export "max" (memory 2 10)))))
+  "module types incompatible")
+(assert_unlinkable
+  (module (import "a" "m" (module (export "" (module)))))
+  "module types incompatible")
diff --git a/tests/misc_testsuite/module-linking/instantiate.wast b/tests/misc_testsuite/module-linking/instantiate.wast
index a0e24c7a7bac..c04929257f56 100644
--- a/tests/misc_testsuite/module-linking/instantiate.wast
+++ b/tests/misc_testsuite/module-linking/instantiate.wast
@@ -117,6 +117,24 @@
 )
 (assert_return (invoke "get") (i32.const 5))
 
+;; imported modules again
+(module
+  (module $m
+    (import "" (module $m (export "get" (func (result i32)))))
+    (instance $i (instantiate $m))
+    (alias $f (instance $i) (func 0))
+    (export "" (func $f))
+  )
+  (module $m2
+    (func (export "get") (result i32)
+      i32.const 6))
+  (instance $a (instantiate $m (module $m2)))
+
+  (func (export "get") (result i32)
+    call $a.$f)
+)
+(assert_return (invoke "get") (i32.const 6))
+
 ;; all at once
 (module
   (import "a" "inc" (func $f))
@@ -195,3 +213,23 @@
   (instance (instantiate 0 (func 0)))
 )
 (assert_return (invoke $a "get") (i32.const 1))
+
+;; module/instance top-level imports work
+(module $b
+  (module (export "m"))
+  (instance (export "i") (instantiate 0))
+)
+(module
+  (import "b" "m" (module))
+  (import "b" "i" (instance))
+)
+(assert_unlinkable
+  (module
+    (import "b" "m" (module (import "" (func))))
+  )
+  "module types incompatible")
+(assert_unlinkable
+  (module
+    (import "b" "i" (instance (export "" (func))))
+  )
+  "instance types incompatible")