Repository: bytecodealliance/wasmtime
Pull request #2118 (2020-08-07 19:12:58)

Enable ref types and bulk memory operations by default
Both have spent a bit of time enabled in our fuzzers by default and we haven't run into any issues in a while. The one catch was the potential slowdown due to #1883, but

* I measured and, while it did exist, it was on the order of milliseconds when dealing with a 270K wasm module, and
* I've added a commit that fixes the slowdown (and is otherwise generally a tiny speed up by doing a little manual LICM that the compiler failed to do because of a trait method call).

alexcrichton: The only prior experience we have for this is multi-value, and we didn't actually enable that by default until it was merged into the upstream spec itself. In that sense this is much earlier to enable-by-default than our precedent.

Given that, I wonder if we might want to take this moment to discuss/agree about when to enable new features by default? I personally feel that merging into the spec is probably too slow, and stage 4 (which these proposals are at) feels like it's the right time. It's what Firefox did!

If others agree, could this also add some documentation in the book to when we turn a feature on by default? I would say the requirements for a Wasmtime wasm feature to be on by default would be:

* Upstream proposal in stage 4
* Complete implementation in Wasmtime
* No major bugs or design work needs to be done
* Fuzzing enabled for awhile (week or so?)

In terms of the content of this PR, r=me, just wanted to point out the procedural point!
alexcrichton: Actually some other requirements I would add are:

* Implemented in the `wasmtime` crate's API
* Implemented in the C API
* Implemented in at least one language binding (e.g. Go, Python, .NET)
fitzgen: This is a good point to bring up, thanks Alex. Once we find consensus on what is required here, we should probably document it in our contributing guide.

--------------------------

You proposed requirements sound good to me, but I would even expand the last point about fuzzing to include a review of the ways that we've exposed the feature to the fuzzers.

* Is it just enabled in the "compile" fuzz target's config or did we write custom fuzz targets to explicitly exercise *running* (rather than just compiling) this feature?
* Did we add seeds to our corpora that use the feature?
* Generally, how confident are we that the fuzzers have actually exercised this stuff? (e.g. does introducing a panic on purpose to some corner case get caught by the fuzzer pretty quickly?) 

In particular, if we aren't pretty confident about that last point, I don't think we are ready to turn the feature on by default yet.

According to these standards, I feel good about reference types, and okay about bulk memory. I think bulk memory could probably use more runtime testing, rather than just "can we compile modules that use it" testing. For reference types, we have specialized fuzz targets that actually run the modules, not just compile them.
alexcrichton: Oh yeah and to be clear I also feel good about the implementation we have of reference types and bulk memory myself.

Also, as an aside, would you be up (after this lands) to send a similar PR to `wasm-tools` to update `Default for WasmFeatures` in wasmparser?
github-actions[bot]: #### Subscribe to Label Action

cc @bnjbvr, @peterhuene

<details>
This issue or pull request has been labeled: "cranelift", "wasmtime:api"

Thus the following users have been cc'd because of the following labels:

* bnjbvr: cranelift
* peterhuene: wasmtime:api

To subscribe or unsubscribe from this label, edit the <code>.github/subscribe-to-label.json</code> configuration file.

[Learn more.](https://github.com/bytecodealliance/subscribe-to-label-action)
</details>
alexcrichton: If you want to add some docs to https://bytecodealliance.github.io/wasmtime/stability.html (or  that section, maybe not exactly that page), that might be a good place to add some info?

Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 4cf980b0f..dc4d2d228 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2373,7 +2373,6 @@ dependencies = [
  "target-lexicon",
  "tempfile",
  "wasmparser 0.59.0",
- "wasmtime-cache",
  "wasmtime-environ",
  "wasmtime-jit",
  "wasmtime-profiling",
@@ -2405,30 +2404,6 @@ dependencies = [
  "quote",
 ]
 
-[[package]]
-name = "wasmtime-cache"
-version = "0.19.0"
-dependencies = [
- "anyhow",
- "base64 0.12.3",
- "bincode",
- "directories",
- "errno",
- "file-per-thread-logger",
- "filetime",
- "lazy_static",
- "libc",
- "log",
- "more-asserts",
- "pretty_env_logger",
- "serde",
- "sha2",
- "tempfile",
- "toml",
- "winapi",
- "zstd",
-]
-
 [[package]]
 name = "wasmtime-cli"
 version = "0.19.0"
@@ -2450,7 +2425,6 @@ dependencies = [
  "test-programs",
  "wasi-common",
  "wasmtime",
- "wasmtime-cache",
  "wasmtime-debug",
  "wasmtime-environ",
  "wasmtime-fuzzing",
@@ -2481,19 +2455,33 @@ name = "wasmtime-environ"
 version = "0.19.0"
 dependencies = [
  "anyhow",
+ "base64 0.12.3",
+ "bincode",
  "cfg-if",
  "cranelift-codegen",
  "cranelift-entity",
  "cranelift-frontend",
  "cranelift-wasm",
+ "directories",
+ "errno",
+ "file-per-thread-logger",
+ "filetime",
  "gimli 0.21.0",
  "indexmap",
+ "lazy_static",
+ "libc",
  "lightbeam",
  "log",
  "more-asserts",
+ "pretty_env_logger",
  "serde",
+ "sha2",
+ "tempfile",
  "thiserror",
+ "toml",
  "wasmparser 0.59.0",
+ "winapi",
+ "zstd",
 ]
 
 [[package]]
diff --git a/Cargo.toml b/Cargo.toml
index 4e665313e..c7b56dc0e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -22,8 +22,7 @@ doc = false
 
 [dependencies]
 # Enable all supported architectures by default.
-wasmtime = { path = "crates/wasmtime", version = "0.19.0", default-features = false, features = ['cache'] }
-wasmtime-cache = { path = "crates/cache", version = "0.19.0" }
+wasmtime = { path = "crates/wasmtime", version = "0.19.0", default-features = false }
 wasmtime-debug = { path = "crates/debug", version = "0.19.0" }
 wasmtime-environ = { path = "crates/environ", version = "0.19.0" }
 wasmtime-jit = { path = "crates/jit", version = "0.19.0" }
diff --git a/cranelift/codegen/src/regalloc/context.rs b/cranelift/codegen/src/regalloc/context.rs
index 8aa1003d4..505b1d127 100644
--- a/cranelift/codegen/src/regalloc/context.rs
+++ b/cranelift/codegen/src/regalloc/context.rs
@@ -203,17 +203,22 @@ impl Context {
             &mut self.tracker,
         );
 
-        // This function runs after register allocation has taken
-        // place, meaning values have locations assigned already.
-        if isa.flags().enable_safepoints() {
-            emit_stack_maps(func, domtree, &self.liveness, &mut self.tracker, isa);
-        } else {
-            // Make sure no references are used.
-            for val in func.dfg.values() {
-                let ty = func.dfg.value_type(val);
-                if ty.lane_type().is_ref() {
-                    panic!("reference types were found but safepoints were not enabled.");
-                }
+        // If there are any reference types used, encode safepoints and emit
+        // stack maps.
+        //
+        // This function runs after register allocation has taken place, meaning
+        // values have locations assigned already, which is necessary for
+        // creating the stack maps.
+        let safepoints_enabled = isa.flags().enable_safepoints();
+        for val in func.dfg.values() {
+            let ty = func.dfg.value_type(val);
+            if ty.lane_type().is_ref() {
+                assert!(
+                    safepoints_enabled,
+                    "reference types were found but safepoints were not enabled"
+                );
+                emit_stack_maps(func, domtree, &self.liveness, &mut self.tracker, isa);
+                break;
             }
         }
 
diff --git a/crates/c-api/Cargo.toml b/crates/c-api/Cargo.toml
index fc4704e28..e59532cd9 100644
--- a/crates/c-api/Cargo.toml
+++ b/crates/c-api/Cargo.toml
@@ -31,8 +31,7 @@ wasi-common = { path = "../wasi-common", optional = true }
 wasmtime-wasi = { path = "../wasi", optional = true }
 
 [features]
-default = ['jitdump', 'wat', 'wasi', 'cache']
+default = ['jitdump', 'wat', 'wasi']
 lightbeam = ["wasmtime/lightbeam"]
 jitdump = ["wasmtime/jitdump"]
-cache = ["wasmtime/cache"]
 wasi = ['wasi-common', 'wasmtime-wasi']
diff --git a/crates/c-api/src/config.rs b/crates/c-api/src/config.rs
index 49dce1984..aaee8c4b3 100644
--- a/crates/c-api/src/config.rs
+++ b/crates/c-api/src/config.rs
@@ -1,7 +1,3 @@
-// Don't worry about unused imports if we're frobbing features, only worry about
-// them with the default set of features enabled.
-#![cfg_attr(not(feature = "cache"), allow(unused_imports))]
-
 use crate::{handle_result, wasmtime_error_t};
 use std::ffi::CStr;
 use std::os::raw::c_char;
@@ -134,7 +130,6 @@ pub extern "C" fn wasmtime_config_profiler_set(
 }
 
 #[no_mangle]
-#[cfg(feature = "cache")]
 pub unsafe extern "C" fn wasmtime_config_cache_config_load(
     c: &mut wasm_config_t,
     filename: *const c_char,
diff --git a/crates/cache/Cargo.toml b/crates/cache/Cargo.toml
deleted file mode 100644
index b4c736772..000000000
--- a/crates/cache/Cargo.toml
+++ /dev/null
@@ -1,35 +0,0 @@
-[package]
-name = "wasmtime-cache"
-version = "0.19.0"
-authors = ["The Wasmtime Project Developers"]
-description = "Support for automatic module caching with Wasmtime"
-license = "Apache-2.0 WITH LLVM-exception"
-repository = "https://github.com/bytecodealliance/wasmtime"
-documentation = "https://docs.rs/wasmtime-cache/"
-edition = "2018"
-
-[dependencies]
-anyhow = "1.0"
-base64 = "0.12.0"
-bincode = "1.1.4"
-directories = "2.0.1"
-file-per-thread-logger = "0.1.1"
-log = { version = "0.4.8", default-features = false }
-serde = { version = "1.0.94", features = ["derive"] }
-sha2 = "0.8.0"
-toml = "0.5.5"
-zstd = "0.5"
-
-[target.'cfg(target_os = "windows")'.dependencies]
-winapi = "0.3.7"
-
-[target.'cfg(not(target_os = "windows"))'.dependencies]
-errno = "0.2.4"
-libc = "0.2.60"
-
-[dev-dependencies]
-filetime = "0.2.7"
-lazy_static = "1.3.0"
-more-asserts = "0.2.1"
-pretty_env_logger = "0.4.0"
-tempfile = "3"
diff --git a/crates/environ/Cargo.toml b/crates/environ/Cargo.toml
index 0dac39f98..19b8f08d0 100644
--- a/crates/environ/Cargo.toml
+++ b/crates/environ/Cargo.toml
@@ -21,11 +21,31 @@ wasmparser = "0.59.0"
 lightbeam = { path = "../lightbeam", optional = true, version = "0.19.0" }
 indexmap = { version = "1.0.2", features = ["serde-1"] }
 thiserror = "1.0.4"
+directories = "2.0.1"
+sha2 = "0.8.0"
+base64 = "0.12.0"
 serde = { version = "1.0.94", features = ["derive"] }
+bincode = "1.1.4"
 log = { version = "0.4.8", default-features = false }
+zstd = "0.5"
+toml = "0.5.5"
+file-per-thread-logger = "0.1.1"
 more-asserts = "0.2.1"
 cfg-if = "0.1.9"
 gimli = "0.21"
 
+[target.'cfg(target_os = "windows")'.dependencies]
+winapi = "0.3.7"
+
+[target.'cfg(not(target_os = "windows"))'.dependencies]
+libc = "0.2.60"
+errno = "0.2.4"
+
+[dev-dependencies]
+tempfile = "3"
+pretty_env_logger = "0.4.0"
+filetime = "0.2.7"
+lazy_static = "1.3.0"
+
 [badges]
 maintenance = { status = "actively-developed" }
diff --git a/crates/cache/src/worker/tests/system_time_stub.rs b/crates/environ/src/cache/worker/tests/system_time_stub.rs
similarity index 100%
rename from crates/cache/src/worker/tests/system_time_stub.rs
rename to crates/environ/src/cache/worker/tests/system_time_stub.rs
diff --git a/crates/environ/src/lib.rs b/crates/environ/src/lib.rs
index d07792632..a0148ff7f 100644
--- a/crates/environ/src/lib.rs
+++ b/crates/environ/src/lib.rs
@@ -33,11 +33,15 @@ mod module_environ;
 mod tunables;
 mod vmoffsets;
 
+mod cache;
+
 pub mod cranelift;
 #[cfg(feature = "lightbeam")]
 pub mod lightbeam;
 
 pub use crate::address_map::*;
+pub use crate::cache::create_new_config as cache_create_new_config;
+pub use crate::cache::{CacheConfig, ModuleCacheEntry};
 pub use crate::compilation::*;
 pub use crate::cranelift::Cranelift;
 pub use crate::data_structures::*;
diff --git a/crates/jit/src/imports.rs b/crates/jit/src/imports.rs
new file mode 100644
index 000000000..3c00ce4ae
--- /dev/null
+++ b/crates/jit/src/imports.rs
@@ -0,0 +1,263 @@
+//! Module imports resolving logic.
+
+use crate::resolver::Resolver;
+use more_asserts::assert_ge;
+use std::convert::TryInto;
+use wasmtime_environ::entity::PrimaryMap;
+use wasmtime_environ::wasm::{Global, GlobalInit, Memory, Table, TableElementType};
+use wasmtime_environ::{EntityIndex, MemoryPlan, MemoryStyle, Module, TablePlan};
+use wasmtime_runtime::{
+    Export, Imports, LinkError, SignatureRegistry, VMFunctionImport, VMGlobalImport,
+    VMMemoryImport, VMTableImport,
+};
+
+/// This function allows to match all imports of a `Module` with concrete definitions provided by
+/// a `Resolver`.
+///
+/// If all imports are satisfied returns an `Imports` instance required for a module instantiation.
+pub fn resolve_imports(
+    module: &Module,
+    signatures: &SignatureRegistry,
+    resolver: &mut dyn Resolver,
+) -> Result<Imports, LinkError> {
+    let mut function_imports = PrimaryMap::with_capacity(module.num_imported_funcs);
+    let mut table_imports = PrimaryMap::with_capacity(module.num_imported_tables);
+    let mut memory_imports = PrimaryMap::with_capacity(module.num_imported_memories);
+    let mut global_imports = PrimaryMap::with_capacity(module.num_imported_globals);
+
+    for (import_idx, (module_name, field_name, import)) in module.imports.iter().enumerate() {
+        let import_idx = import_idx.try_into().unwrap();
+        let export = resolver.resolve(import_idx, module_name, field_name);
+
+        match (import, &export) {
+            (EntityIndex::Function(func_index), Some(Export::Function(f))) => {
+                let import_signature = module.native_func_signature(*func_index);
+                let signature = signatures
+                    .lookup_native(unsafe { f.anyfunc.as_ref().type_index })
+                    .unwrap();
+                if signature != *import_signature {
+                    // TODO: If the difference is in the calling convention,
+                    // we could emit a wrapper function to fix it up.
+                    return Err(LinkError(format!(
+                        "{}/{}: incompatible import type: exported function with signature {} \
+                         incompatible with function import with signature {}",
+                        module_name, field_name, signature, import_signature
+                    )));
+                }
+                function_imports.push(VMFunctionImport {
+                    body: unsafe { f.anyfunc.as_ref().func_ptr },
+                    vmctx: unsafe { f.anyfunc.as_ref().vmctx },
+                });
+            }
+            (EntityIndex::Function(_), Some(_)) => {
+                return Err(LinkError(format!(
+                    "{}/{}: incompatible import type: export incompatible with function import",
+                    module_name, field_name
+                )));
+            }
+            (EntityIndex::Function(_), None) => {
+                return Err(LinkError(format!(
+                    "{}/{}: unknown import function: function not provided",
+                    module_name, field_name
+                )));
+            }
+
+            (EntityIndex::Table(table_index), Some(Export::Table(t))) => {
+                let import_table = &module.table_plans[*table_index];
+                if !is_table_compatible(&t.table, import_table) {
+                    return Err(LinkError(format!(
+                        "{}/{}: incompatible import type: exported table incompatible with \
+                             table import",
+                        module_name, field_name,
+                    )));
+                }
+                table_imports.push(VMTableImport {
+                    from: t.definition,
+                    vmctx: t.vmctx,
+                });
+            }
+            (EntityIndex::Table(_), Some(_)) => {
+                return Err(LinkError(format!(
+                    "{}/{}: incompatible import type: export incompatible with table import",
+                    module_name, field_name
+                )));
+            }
+            (EntityIndex::Table(_), None) => {
+                return Err(LinkError(format!(
+                    "{}/{}: unknown import table: table not provided",
+                    module_name, field_name
+                )));
+            }
+
+            (EntityIndex::Memory(memory_index), Some(Export::Memory(m))) => {
+                let import_memory = &module.memory_plans[*memory_index];
+                if !is_memory_compatible(&m.memory, import_memory) {
+                    return Err(LinkError(format!(
+                        "{}/{}: incompatible import type: exported memory incompatible with \
+                             memory import",
+                        module_name, field_name
+                    )));
+                }
+
+                // Sanity-check: Ensure that the imported memory has at least
+                // guard-page protections the importing module expects it to have.
+                if let (
+                    MemoryStyle::Static { bound },
+                    MemoryStyle::Static {
+                        bound: import_bound,
+                    },
+                ) = (&m.memory.style, &import_memory.style)
+                {
+                    assert_ge!(*bound, *import_bound);
+                }
+                assert_ge!(m.memory.offset_guard_size, import_memory.offset_guard_size);
+
+                memory_imports.push(VMMemoryImport {
+                    from: m.definition,
+                    vmctx: m.vmctx,
+                });
+            }
+            (EntityIndex::Memory(_), Some(_)) => {
+                return Err(LinkError(format!(
+                    "{}/{}: incompatible import type: export incompatible with memory import",
+                    module_name, field_name
+                )));
+            }
+            (EntityIndex::Memory(_), None) => {
+                return Err(LinkError(format!(
+                    "{}/{}: unknown import memory: memory not provided",
+                    module_name, field_name
+                )));
+            }
+
+            (EntityIndex::Global(global_index), Some(Export::Global(g))) => {
+                let imported_global = module.globals[*global_index];
+                if !is_global_compatible(&g.global, &imported_global) {
+                    return Err(LinkError(format!(
+                        "{}/{}: incompatible import type: exported global incompatible with \
+                             global import",
+                        module_name, field_name
+                    )));
+                }
+                global_imports.push(VMGlobalImport { from: g.definition });
+            }
+            (EntityIndex::Global(_), Some(_)) => {
+                return Err(LinkError(format!(
+                    "{}/{}: incompatible import type: export incompatible with global import",
+                    module_name, field_name
+                )));
+            }
+            (EntityIndex::Global(_), None) => {
+                return Err(LinkError(format!(
+                    "{}/{}: unknown import global: global not provided",
+                    module_name, field_name
+                )));
+            }
+        }
+    }
+
+    Ok(Imports::new(
+        function_imports,
+        table_imports,
+        memory_imports,
+        global_imports,
+    ))
+}
+
+fn is_global_compatible(exported: &Global, imported: &Global) -> bool {
+    match imported.initializer {
+        GlobalInit::Import => (),
+        _ => panic!("imported Global should have an Imported initializer"),
+    }
+
+    let Global {
+        wasm_ty: exported_wasm_ty,
+        ty: exported_ty,
+        mutability: exported_mutability,
+        initializer: _exported_initializer,
+    } = exported;
+    let Global {
+        wasm_ty: imported_wasm_ty,
+        ty: imported_ty,
+        mutability: imported_mutability,
+        initializer: _imported_initializer,
+    } = imported;
+    exported_wasm_ty == imported_wasm_ty
+        && exported_ty == imported_ty
+        && imported_mutability == exported_mutability
+}
+
+fn is_table_element_type_compatible(
+    exported_type: TableElementType,
+    imported_type: TableElementType,
+) -> bool {
+    match exported_type {
+        TableElementType::Func => match imported_type {
+            TableElementType::Func => true,
+            _ => false,
+        },
+        TableElementType::Val(exported_val_ty) => match imported_type {
+            TableElementType::Val(imported_val_ty) => exported_val_ty == imported_val_ty,
+            _ => false,
+        },
+    }
+}
+
+fn is_table_compatible(exported: &TablePlan, imported: &TablePlan) -> bool {
+    let TablePlan {
+        table:
+            Table {
+                wasm_ty: exported_wasm_ty,
+                ty: exported_ty,
+                minimum: exported_minimum,
+                maximum: exported_maximum,
+            },
+        style: _exported_style,
+    } = exported;
+    let TablePlan {
+        table:
+            Table {
+                wasm_ty: imported_wasm_ty,
+                ty: imported_ty,
+                minimum: imported_minimum,
+                maximum: imported_maximum,
+            },
+        style: _imported_style,
+    } = imported;
+
+    exported_wasm_ty == imported_wasm_ty
+        && is_table_element_type_compatible(*exported_ty, *imported_ty)
+        && imported_minimum <= exported_minimum
+        && (imported_maximum.is_none()
+            || (!exported_maximum.is_none()
+                && imported_maximum.unwrap() >= exported_maximum.unwrap()))
+}
+
+fn is_memory_compatible(exported: &MemoryPlan, imported: &MemoryPlan) -> bool {
+    let MemoryPlan {
+        memory:
+            Memory {
+                minimum: exported_minimum,
+                maximum: exported_maximum,
+                shared: exported_shared,
+            },
+        style: _exported_style,
+        offset_guard_size: _exported_offset_guard_size,
+    } = exported;
+    let MemoryPlan {
+        memory:
+            Memory {
+                minimum: imported_minimum,
+                maximum: imported_maximum,
+                shared: imported_shared,
+            },
+        style: _imported_style,
+        offset_guard_size: _imported_offset_guard_size,
+    } = imported;
+
+    imported_minimum <= exported_minimum
+        && (imported_maximum.is_none()
+            || (!exported_maximum.is_none()
+                && imported_maximum.unwrap() >= exported_maximum.unwrap()))
+        && exported_shared == imported_shared
+}
diff --git a/crates/jit/src/instantiate.rs b/crates/jit/src/instantiate.rs
index 71f81cb34..ee1bffdeb 100644
--- a/crates/jit/src/instantiate.rs
+++ b/crates/jit/src/instantiate.rs
@@ -5,8 +5,10 @@
 
 use crate::code_memory::CodeMemory;
 use crate::compiler::{Compilation, Compiler};
+use crate::imports::resolve_imports;
 use crate::link::link_module;
 use crate::object::ObjectUnwindInfo;
+use crate::resolver::Resolver;
 use object::File as ObjectFile;
 use serde::{Deserialize, Serialize};
 use std::any::Any;
@@ -22,10 +24,10 @@ use wasmtime_environ::{
     ModuleEnvironment, ModuleTranslation, StackMapInformation, TrapInformation,
 };
 use wasmtime_profiling::ProfilingAgent;
+use wasmtime_runtime::VMInterrupts;
 use wasmtime_runtime::{
-    GdbJitImageRegistration, Imports, InstanceHandle, InstantiationError, RuntimeMemoryCreator,
-    SignatureRegistry, StackMapRegistry, VMExternRefActivationsTable, VMFunctionBody, VMInterrupts,
-    VMTrampoline,
+    GdbJitImageRegistration, InstanceHandle, InstantiationError, RuntimeMemoryCreator,
+    SignatureRegistry, StackMapRegistry, VMExternRefActivationsTable, VMFunctionBody, VMTrampoline,
 };
 
 /// An error condition while setting up a wasm instance, be it validation,
@@ -243,7 +245,7 @@ impl CompiledModule {
     /// See `InstanceHandle::new`
     pub unsafe fn instantiate(
         &self,
-        imports: Imports<'_>,
+        resolver: &mut dyn Resolver,
         signature_registry: &mut SignatureRegistry,
         mem_creator: Option<&dyn RuntimeMemoryCreator>,
         interrupts: Arc<VMInterrupts>,
@@ -269,6 +271,7 @@ impl CompiledModule {
 
         let finished_functions = self.finished_functions.0.clone();
 
+        let imports = resolve_imports(&self.module, signature_registry, resolver)?;
         InstanceHandle::new(
             self.module.clone(),
             self.code.clone(),
diff --git a/crates/jit/src/lib.rs b/crates/jit/src/lib.rs
index 28c1d701c..ca7d9fde0 100644
--- a/crates/jit/src/lib.rs
+++ b/crates/jit/src/lib.rs
@@ -23,9 +23,11 @@
 
 mod code_memory;
 mod compiler;
+mod imports;
 mod instantiate;
 mod link;
 mod object;
+mod resolver;
 mod unwind;
 
 pub mod native;
@@ -35,6 +37,7 @@ pub use crate::code_memory::CodeMemory;
 pub use crate::compiler::{Compilation, CompilationStrategy, Compiler};
 pub use crate::instantiate::{CompilationArtifacts, CompiledModule, SetupError};
 pub use crate::link::link_module;
+pub use crate::resolver::{NullResolver, Resolver};
 
 /// Version number of this crate.
 pub const VERSION: &str = env!("CARGO_PKG_VERSION");
diff --git a/crates/jit/src/resolver.rs b/crates/jit/src/resolver.rs
new file mode 100644
index 000000000..34f052c45
--- /dev/null
+++ b/crates/jit/src/resolver.rs
@@ -0,0 +1,26 @@
+//! Define the `Resolver` trait, allowing custom resolution for external
+//! references.
+
+use wasmtime_runtime::Export;
+
+/// Import resolver connects imports with available exported values.
+pub trait Resolver {
+    /// Resolves an import a WebAssembly module to an export it's hooked up to.
+    ///
+    /// The `index` provided is the index of the import in the wasm module
+    /// that's being resolved. For example 1 means that it's the second import
+    /// listed in the wasm module.
+    ///
+    /// The `module` and `field` arguments provided are the module/field names
+    /// listed on the import itself.
+    fn resolve(&mut self, index: u32, module: &str, field: &str) -> Option<Export>;
+}
+
+/// `Resolver` implementation that always resolves to `None`.
+pub struct NullResolver {}
+
+impl Resolver for NullResolver {
+    fn resolve(&mut self, _idx: u32, _module: &str, _field: &str) -> Option<Export> {
+        None
+    }
+}
diff --git a/crates/runtime/src/imports.rs b/crates/runtime/src/imports.rs
index 2f85ba220..14360a703 100644
--- a/crates/runtime/src/imports.rs
+++ b/crates/runtime/src/imports.rs
@@ -1,22 +1,46 @@
 use crate::vmcontext::{VMFunctionImport, VMGlobalImport, VMMemoryImport, VMTableImport};
+use wasmtime_environ::entity::{BoxedSlice, PrimaryMap};
+use wasmtime_environ::wasm::{FuncIndex, GlobalIndex, MemoryIndex, TableIndex};
 
 /// Resolved import pointers.
-///
-/// Note that each 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)]
-pub struct Imports<'a> {
+#[derive(Clone)]
+pub struct Imports {
     /// Resolved addresses for imported functions.
-    pub functions: &'a [VMFunctionImport],
+    pub functions: BoxedSlice<FuncIndex, VMFunctionImport>,
 
     /// Resolved addresses for imported tables.
-    pub tables: &'a [VMTableImport],
+    pub tables: BoxedSlice<TableIndex, VMTableImport>,
 
     /// Resolved addresses for imported memories.
-    pub memories: &'a [VMMemoryImport],
+    pub memories: BoxedSlice<MemoryIndex, VMMemoryImport>,
 
     /// Resolved addresses for imported globals.
-    pub globals: &'a [VMGlobalImport],
+    pub globals: BoxedSlice<GlobalIndex, VMGlobalImport>,
+}
+
+impl Imports {
+    /// Construct a new `Imports` instance.
+    pub fn new(
+        function_imports: PrimaryMap<FuncIndex, VMFunctionImport>,
+        table_imports: PrimaryMap<TableIndex, VMTableImport>,
+        memory_imports: PrimaryMap<MemoryIndex, VMMemoryImport>,
+        global_imports: PrimaryMap<GlobalIndex, VMGlobalImport>,
+    ) -> Self {
+        Self {
+            functions: function_imports.into_boxed_slice(),
+            tables: table_imports.into_boxed_slice(),
+            memories: memory_imports.into_boxed_slice(),
+            globals: global_imports.into_boxed_slice(),
+        }
+    }
+
+    /// Construct a new `Imports` instance with no imports.
+    pub fn none() -> Self {
+        Self {
+            functions: PrimaryMap::new().into_boxed_slice(),
+            tables: PrimaryMap::new().into_boxed_slice(),
+            memories: PrimaryMap::new().into_boxed_slice(),
+            globals: PrimaryMap::new().into_boxed_slice(),
+        }
+    }
 }
diff --git a/crates/runtime/src/instance.rs b/crates/runtime/src/instance.rs
index e53b147cb..eac1954c7 100644
--- a/crates/runtime/src/instance.rs
+++ b/crates/runtime/src/instance.rs
@@ -897,36 +897,28 @@ impl InstanceHandle {
         };
         let instance = handle.instance();
 
-        debug_assert_eq!(vmshared_signatures.len(), handle.module().signatures.len());
         ptr::copy(
             vmshared_signatures.values().as_slice().as_ptr(),
             instance.signature_ids_ptr() as *mut VMSharedSignatureIndex,
             vmshared_signatures.len(),
         );
-        debug_assert_eq!(imports.functions.len(), handle.module().num_imported_funcs);
         ptr::copy(
-            imports.functions.as_ptr(),
+            imports.functions.values().as_slice().as_ptr(),
             instance.imported_functions_ptr() as *mut VMFunctionImport,
             imports.functions.len(),
         );
-        debug_assert_eq!(imports.tables.len(), handle.module().num_imported_tables);
         ptr::copy(
-            imports.tables.as_ptr(),
+            imports.tables.values().as_slice().as_ptr(),
             instance.imported_tables_ptr() as *mut VMTableImport,
             imports.tables.len(),
         );
-        debug_assert_eq!(
-            imports.memories.len(),
-            handle.module().num_imported_memories
-        );
         ptr::copy(
-            imports.memories.as_ptr(),
+            imports.memories.values().as_slice().as_ptr(),
             instance.imported_memories_ptr() as *mut VMMemoryImport,
             imports.memories.len(),
         );
-        debug_assert_eq!(imports.globals.len(), handle.module().num_imported_globals);
         ptr::copy(
-            imports.globals.as_ptr(),
+            imports.globals.values().as_slice().as_ptr(),
             instance.imported_globals_ptr() as *mut VMGlobalImport,
             imports.globals.len(),
         );
diff --git a/crates/runtime/src/sig_registry.rs b/crates/runtime/src/sig_registry.rs
index ca7ba5743..30e5514bb 100644
--- a/crates/runtime/src/sig_registry.rs
+++ b/crates/runtime/src/sig_registry.rs
@@ -50,11 +50,6 @@ impl SignatureRegistry {
         }
     }
 
-    /// Looks up a shared index from the wasm signature itself.
-    pub fn lookup(&self, wasm: &WasmFuncType) -> Option<VMSharedSignatureIndex> {
-        self.wasm2index.get(wasm).cloned()
-    }
-
     /// Looks up a shared native signature within this registry.
     ///
     /// Note that for this operation to be semantically correct the `idx` must
diff --git a/crates/wasmtime/Cargo.toml b/crates/wasmtime/Cargo.toml
index 83efbb8e1..54a15a42f 100644
--- a/crates/wasmtime/Cargo.toml
+++ b/crates/wasmtime/Cargo.toml
@@ -13,7 +13,6 @@ edition = "2018"
 wasmtime-runtime = { path = "../runtime", version = "0.19.0" }
 wasmtime-environ = { path = "../environ", version = "0.19.0" }
 wasmtime-jit = { path = "../jit", version = "0.19.0" }
-wasmtime-cache = { path = "../cache", version = "0.19.0", optional = true }
 wasmtime-profiling = { path = "../profiling", version = "0.19.0" }
 wasmparser = "0.59.0"
 target-lexicon = { version = "0.10.0", default-features = false }
@@ -41,7 +40,7 @@ wasmtime-wasi = { path = "../wasi" }
 maintenance = { status = "actively-developed" }
 
 [features]
-default = ['cache', 'wat', 'jitdump', 'parallel-compilation']
+default = ['wat', 'jitdump', 'parallel-compilation']
 
 # Enables experimental support for the lightbeam codegen backend, an alternative
 # to cranelift. Requires Nightly Rust currently, and this is not enabled by
@@ -56,6 +55,3 @@ vtune = ["wasmtime-jit/vtune"]
 
 # Enables parallel compilation of WebAssembly code
 parallel-compilation = ["wasmtime-jit/parallel-compilation"]
-
-# Enables support for automatic cache configuration to be enabled in `Config`.
-cache = ["wasmtime-cache"]
diff --git a/crates/wasmtime/src/externals.rs b/crates/wasmtime/src/externals.rs
index 096cfd45c..ad25a6370 100644
--- a/crates/wasmtime/src/externals.rs
+++ b/crates/wasmtime/src/externals.rs
@@ -86,6 +86,15 @@ impl Extern {
         }
     }
 
+    pub(crate) fn get_wasmtime_export(&self) -> wasmtime_runtime::Export {
+        match self {
+            Extern::Func(f) => f.wasmtime_function().clone().into(),
+            Extern::Global(g) => g.wasmtime_export.clone().into(),
+            Extern::Memory(m) => m.wasmtime_export.clone().into(),
+            Extern::Table(t) => t.wasmtime_export.clone().into(),
+        }
+    }
+
     pub(crate) fn from_wasmtime_export(
         wasmtime_export: wasmtime_runtime::Export,
         instance: StoreInstanceHandle,
@@ -115,15 +124,6 @@ impl Extern {
         };
         Store::same(my_store, store)
     }
-
-    pub(crate) fn desc(&self) -> &'static str {
-        match self {
-            Extern::Func(_) => "function",
-            Extern::Table(_) => "table",
-            Extern::Memory(_) => "memory",
-            Extern::Global(_) => "global",
-        }
-    }
 }
 
 impl From<Func> for Extern {
@@ -293,19 +293,6 @@ impl Global {
             wasmtime_export,
         }
     }
-
-    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 vmimport(&self) -> wasmtime_runtime::VMGlobalImport {
-        wasmtime_runtime::VMGlobalImport {
-            from: self.wasmtime_export.definition,
-        }
-    }
 }
 
 /// A WebAssembly `table`, or an array of values.
@@ -537,28 +524,6 @@ impl Table {
             wasmtime_export,
         }
     }
-
-    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 vmimport(&self) -> wasmtime_runtime::VMTableImport {
-        wasmtime_runtime::VMTableImport {
-            from: self.wasmtime_export.definition,
-            vmctx: self.wasmtime_export.vmctx,
-        }
-    }
 }
 
 /// A WebAssembly linear memory.
@@ -959,27 +924,6 @@ impl Memory {
             wasmtime_export,
         }
     }
-
-    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 vmimport(&self) -> wasmtime_runtime::VMMemoryImport {
-        wasmtime_runtime::VMMemoryImport {
-            from: self.wasmtime_export.definition,
-            vmctx: self.wasmtime_export.vmctx,
-        }
-    }
 }
 
 /// A linear memory. This trait provides an interface for raw memory buffers which are used
diff --git a/crates/wasmtime/src/func.rs b/crates/wasmtime/src/func.rs
index fbed5174b..3ec3ddbb9 100644
--- a/crates/wasmtime/src/func.rs
+++ b/crates/wasmtime/src/func.rs
@@ -635,6 +635,10 @@ impl Func {
         Ok(results.into())
     }
 
+    pub(crate) fn wasmtime_function(&self) -> &wasmtime_runtime::ExportFunction {
+        &self.export
+    }
+
     pub(crate) fn caller_checked_anyfunc(
         &self,
     ) -> NonNull<wasmtime_runtime::VMCallerCheckedAnyfunc> {
@@ -785,20 +789,6 @@ impl Func {
     pub fn store(&self) -> &Store {
         &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();
-            wasmtime_runtime::VMFunctionImport {
-                body: f.as_ref().func_ptr,
-                vmctx: f.as_ref().vmctx,
-            }
-        }
-    }
 }
 
 impl fmt::Debug for Func {
diff --git a/crates/wasmtime/src/instance.rs b/crates/wasmtime/src/instance.rs
index c36fef4d1..f4ecffe97 100644
--- a/crates/wasmtime/src/instance.rs
+++ b/crates/wasmtime/src/instance.rs
@@ -1,25 +1,53 @@
 use crate::trampoline::StoreInstanceHandle;
 use crate::{Engine, Export, Extern, Func, Global, Memory, Module, Store, Table, Trap};
-use anyhow::{anyhow, bail, Context, Error, Result};
+use anyhow::{bail, Error, Result};
 use std::any::Any;
 use std::mem;
 use wasmtime_environ::EntityIndex;
-use wasmtime_jit::CompiledModule;
+use wasmtime_jit::{CompiledModule, Resolver};
 use wasmtime_runtime::{
-    Imports, InstantiationError, StackMapRegistry, VMContext, VMExternRefActivationsTable,
-    VMFunctionBody,
+    InstantiationError, StackMapRegistry, VMContext, VMExternRefActivationsTable, VMFunctionBody,
 };
 
+struct SimpleResolver<'a> {
+    imports: &'a [Extern],
+}
+
+impl Resolver for SimpleResolver<'_> {
+    fn resolve(&mut self, idx: u32, _name: &str, _field: &str) -> Option<wasmtime_runtime::Export> {
+        self.imports
+            .get(idx as usize)
+            .map(|i| i.get_wasmtime_export())
+    }
+}
+
 fn instantiate(
     store: &Store,
     compiled_module: &CompiledModule,
-    imports: Imports<'_>,
+    imports: &[Extern],
     host: Box<dyn Any>,
 ) -> Result<StoreInstanceHandle, Error> {
+    // For now we have a restriction that the `Store` that we're working
+    // with is the same for everything involved here.
+    for import in imports {
+        if !import.comes_from_same_store(store) {
+            bail!("cross-`Store` instantiation is not currently supported");
+        }
+    }
+
+    if imports.len() != compiled_module.module().imports.len() {
+        bail!(
+            "wrong number of imports provided, {} != {}",
+            imports.len(),
+            compiled_module.module().imports.len()
+        );
+    }
+
+    let mut resolver = SimpleResolver { imports };
     let config = store.engine().config();
     let instance = unsafe {
         let instance = compiled_module.instantiate(
-            imports,
+            &mut resolver,
             &mut store.signatures_mut(),
             config.memory_creator.as_ref().map(|a| a as _),
             store.interrupts().clone(),
@@ -168,9 +196,7 @@ impl Instance {
             frame_info_registration
         });
 
-        let handle = with_imports(store, module.compiled_module(), imports, |imports| {
-            instantiate(store, module.compiled_module(), imports, host_info)
-        })?;
+        let handle = instantiate(store, module.compiled_module(), imports, host_info)?;
 
         Ok(Instance {
             handle,
@@ -241,82 +267,3 @@ impl Instance {
         self.get_export(name)?.into_global()
     }
 }
-
-fn with_imports<R>(
-    store: &Store,
-    module: &CompiledModule,
-    externs: &[Extern],
-    f: impl FnOnce(Imports<'_>) -> Result<R>,
-) -> Result<R> {
-    let m = module.module();
-    if externs.len() != m.imports.len() {
-        bail!(
-            "wrong number of imports provided, {} != {}",
-            externs.len(),
-            m.imports.len()
-        );
-    }
-
-    let mut tables = Vec::new();
-    let mut functions = Vec::new();
-    let mut globals = Vec::new();
-    let mut memories = Vec::new();
-
-    let mut process = |expected: &EntityIndex, actual: &Extern| {
-        // For now we have a restriction that the `Store` that we're working
-        // with is the same for everything involved here.
-        if !actual.comes_from_same_store(store) {
-            bail!("cross-`Store` instantiation is not currently supported");
-        }
-
-        match *expected {
-            EntityIndex::Table(i) => tables.push(match actual {
-                Extern::Table(e) if e.matches_expected(&m.table_plans[i]) => e.vmimport(),
-                Extern::Table(_) => bail!("table types incompatible"),
-                _ => bail!("expected table, but found {}", actual.desc()),
-            }),
-            EntityIndex::Memory(i) => memories.push(match actual {
-                Extern::Memory(e) if e.matches_expected(&m.memory_plans[i]) => e.vmimport(),
-                Extern::Memory(_) => bail!("memory types incompatible"),
-                _ => bail!("expected memory, but found {}", actual.desc()),
-            }),
-            EntityIndex::Global(i) => globals.push(match actual {
-                Extern::Global(e) if e.matches_expected(&m.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 = store
-                    .signatures_mut()
-                    .lookup(&m.signatures[m.functions[i]].0)
-                    .ok_or_else(|| anyhow!("function types incompatible"))?;
-                if !func.matches_expected(ty) {
-                    bail!("function types incompatible");
-                }
-                functions.push(func.vmimport());
-            }
-        }
-        Ok(())
-    };
-
-    for (expected, actual) in m.imports.iter().zip(externs) {
-        process(&expected.2, actual).with_context(|| {
-            format!("incompatible import type for {}/{}", expected.0, expected.1)
-        })?;
-    }
-
-    return f(Imports {
-        tables: &tables,
-        functions: &functions,
-        globals: &globals,
-        memories: &memories,
-    });
-}
diff --git a/crates/wasmtime/src/module.rs b/crates/wasmtime/src/module.rs
index dd306289f..40f346cba 100644
--- a/crates/wasmtime/src/module.rs
+++ b/crates/wasmtime/src/module.rs
@@ -4,8 +4,7 @@ use crate::types::{EntityType, ExportType, ExternType, ImportType};
 use anyhow::{bail, Context, Result};
 use std::path::Path;
 use std::sync::{Arc, Mutex};
-#[cfg(feature = "cache")]
-use wasmtime_cache::ModuleCacheEntry;
+use wasmtime_environ::ModuleCacheEntry;
 use wasmtime_jit::{CompilationArtifacts, CompiledModule};
 
 /// A compiled WebAssembly module, ready to be instantiated.
@@ -302,13 +301,11 @@ impl Module {
     }
 
     unsafe fn compile(engine: &Engine, binary: &[u8]) -> Result<Self> {
-        #[cfg(feature = "cache")]
-        let artifacts = ModuleCacheEntry::new("wasmtime", engine.cache_config())
+        let cache_entry = ModuleCacheEntry::new("wasmtime", engine.cache_config());
+        let artifacts = cache_entry
             .get_data((engine.compiler(), binary), |(compiler, binary)| {
                 CompilationArtifacts::build(compiler, binary)
             })?;
-        #[cfg(not(feature = "cache"))]
-        let artifacts = CompilationArtifacts::build(engine.compiler(), binary)?;
 
         let compiled = CompiledModule::from_artifacts(
             artifacts,
diff --git a/crates/wasmtime/src/runtime.rs b/crates/wasmtime/src/runtime.rs
index c45098f58..b6bff866e 100644
--- a/crates/wasmtime/src/runtime.rs
+++ b/crates/wasmtime/src/runtime.rs
@@ -7,16 +7,13 @@ use std::cmp;
 use std::convert::TryFrom;
 use std::fmt;
 use std::hash::{Hash, Hasher};
-#[cfg(feature = "cache")]
 use std::path::Path;
 use std::rc::{Rc, Weak};
 use std::sync::Arc;
 use target_lexicon::Triple;
 use wasmparser::Validator;
-#[cfg(feature = "cache")]
-use wasmtime_cache::CacheConfig;
 use wasmtime_environ::settings::{self, Configurable, SetError};
-use wasmtime_environ::{ir, isa, isa::TargetIsa, wasm, Tunables};
+use wasmtime_environ::{ir, isa, isa::TargetIsa, wasm, CacheConfig, Tunables};
 use wasmtime_jit::{native, CompilationStrategy, Compiler};
 use wasmtime_profiling::{JitDumpAgent, NullProfilerAgent, ProfilingAgent, VTuneAgent};
 use wasmtime_runtime::{
@@ -40,7 +37,6 @@ pub struct Config {
     pub(crate) isa_flags: isa::Builder,
     pub(crate) tunables: Tunables,
     pub(crate) strategy: CompilationStrategy,
-    #[cfg(feature = "cache")]
     pub(crate) cache_config: CacheConfig,
     pub(crate) profiler: Arc<dyn ProfilingAgent>,
     pub(crate) memory_creator: Option<MemoryCreatorProxy>,
@@ -93,14 +89,13 @@ impl Config {
             flags,
             isa_flags: native::builder(),
             strategy: CompilationStrategy::Auto,
-            #[cfg(feature = "cache")]
             cache_config: CacheConfig::new_cache_disabled(),
             profiler: Arc::new(NullProfilerAgent),
             memory_creator: None,
             max_wasm_stack: 1 << 20,
             wasm_threads: false,
-            wasm_reference_types: false,
-            wasm_bulk_memory: false,
+            wasm_reference_types: cfg!(target_arch = "x86_64"),
+            wasm_bulk_memory: true,
             wasm_simd: false,
             wasm_multi_value: true,
         }
@@ -175,25 +170,17 @@ impl Config {
         self
     }
 
-    /// Configures whether the WebAssembly reference types proposal will be
-    /// enabled for compilation.
+    /// Configures whether the [WebAssembly reference types proposal][proposal]
+    /// will be enabled for compilation.
     ///
-    /// The [WebAssembly reference types proposal][proposal] is not currently
-    /// fully standardized and is undergoing development. Additionally the
-    /// support in wasmtime itself is still being worked on. Support for this
-    /// feature can be enabled through this method for appropriate wasm
-    /// modules.
-    ///
-    /// This feature gates items such as the `externref` type and multiple tables
-    /// being in a module. Note that enabling the reference types feature will
-    /// also enable the bulk memory feature.
+    /// This feature gates items such as the `externref` and `funcref` types as
+    /// well as allowing a module to define multiple tables.
     ///
-    /// This is `false` by default.
+    /// Note that enabling the reference types feature will also enable the bulk
+    /// memory feature.
     ///
-    /// > **Note**: Wasmtime does not implement everything for the reference
-    /// > types proposal spec at this time, so bugs, panics, and possibly
-    /// > segfaults should be expected. This should not be enabled in a
-    /// > production setting right now.
+    /// This is `true` by default on x86-64, and `false` by default on other
+    /// architectures.
     ///
     /// [proposal]: https://github.com/webassembly/reference-types
     pub fn wasm_reference_types(&mut self, enable: bool) -> &mut Self {
@@ -240,19 +227,13 @@ impl Config {
         self
     }
 
-    /// Configures whether the WebAssembly bulk memory operations proposal will
-    /// be enabled for compilation.
-    ///
-    /// The [WebAssembly bulk memory operations proposal][proposal] is not
-    /// currently fully standardized and is undergoing development.
-    /// Additionally the support in wasmtime itself is still being worked on.
-    /// Support for this feature can be enabled through this method for
-    /// appropriate wasm modules.
+    /// Configures whether the [WebAssembly bulk memory operations
+    /// proposal][proposal] will be enabled for compilation.
     ///
     /// This feature gates items such as the `memory.copy` instruction, passive
     /// data/table segments, etc, being in a module.
     ///
-    /// This is `false` by default.
+    /// This is `true` by default.
     ///
     /// [proposal]: https://github.com/webassembly/bulk-memory-operations
     pub fn wasm_bulk_memory(&mut self, enable: bool) -> &mut Self {
@@ -400,18 +381,14 @@ impl Config {
     ///
     /// By default cache configuration is not enabled or loaded.
     ///
-    /// This method is only available when the `cache` feature of this crate is
-    /// enabled.
-    ///
     /// # Errors
     ///
     /// This method can fail due to any error that happens when loading the file
     /// pointed to by `path` and attempting to load the cache configuration.
     ///
     /// [docs]: https://bytecodealliance.github.io/wasmtime/cli-cache.html
-    #[cfg(feature = "cache")]
     pub fn cache_config_load(&mut self, path: impl AsRef<Path>) -> Result<&mut Self> {
-        self.cache_config = CacheConfig::from_file(Some(path.as_ref()))?;
+        self.cache_config = wasmtime_environ::CacheConfig::from_file(Some(path.as_ref()))?;
         Ok(self)
     }
 
@@ -425,9 +402,6 @@ impl Config {
     ///
     /// By default cache configuration is not enabled or loaded.
     ///
-    /// This method is only available when the `cache` feature of this crate is
-    /// enabled.
-    ///
     /// # Errors
     ///
     /// This method can fail due to any error that happens when loading the
@@ -436,9 +410,8 @@ impl Config {
     /// for an enabled cache are applied.
     ///
     /// [docs]: https://bytecodealliance.github.io/wasmtime/cli-cache.html
-    #[cfg(feature = "cache")]
     pub fn cache_config_load_default(&mut self) -> Result<&mut Self> {
-        self.cache_config = CacheConfig::from_file(None)?;
+        self.cache_config = wasmtime_environ::CacheConfig::from_file(None)?;
         Ok(self)
     }
 
@@ -806,7 +779,6 @@ impl Engine {
         &self.inner.compiler
     }
 
-    #[cfg(feature = "cache")]
     pub(crate) fn cache_config(&self) -> &CacheConfig {
         &self.config().cache_config
     }
diff --git a/crates/wasmtime/src/trampoline/create_handle.rs b/crates/wasmtime/src/trampoline/create_handle.rs
index e87624090..b8fde1622 100644
--- a/crates/wasmtime/src/trampoline/create_handle.rs
+++ b/crates/wasmtime/src/trampoline/create_handle.rs
@@ -7,7 +7,7 @@ use std::any::Any;
 use std::collections::HashMap;
 use std::sync::Arc;
 use wasmtime_environ::entity::PrimaryMap;
-use wasmtime_environ::wasm::DefinedFuncIndex;
+use wasmtime_environ::wasm::{DefinedFuncIndex, FuncIndex};
 use wasmtime_environ::Module;
 use wasmtime_runtime::{
     Imports, InstanceHandle, StackMapRegistry, VMExternRefActivationsTable, VMFunctionBody,
@@ -20,10 +20,14 @@ pub(crate) fn create_handle(
     finished_functions: PrimaryMap<DefinedFuncIndex, *mut [VMFunctionBody]>,
     trampolines: HashMap<VMSharedSignatureIndex, VMTrampoline>,
     state: Box<dyn Any>,
-    func_imports: &[VMFunctionImport],
+    func_imports: PrimaryMap<FuncIndex, VMFunctionImport>,
 ) -> Result<StoreInstanceHandle> {
-    let mut imports = Imports::default();
-    imports.functions = func_imports;
+    let imports = Imports::new(
+        func_imports,
+        PrimaryMap::new(),
+        PrimaryMap::new(),
+        PrimaryMap::new(),
+    );
 
     // Compute indices into the shared signature table.
     let signatures = module
diff --git a/crates/wasmtime/src/trampoline/func.rs b/crates/wasmtime/src/trampoline/func.rs
index 85ef15359..81baa99b2 100644
--- a/crates/wasmtime/src/trampoline/func.rs
+++ b/crates/wasmtime/src/trampoline/func.rs
@@ -262,7 +262,7 @@ pub fn create_handle_with_function(
         finished_functions,
         trampolines,
         Box::new(trampoline_state),
-        &[],
+        PrimaryMap::new(),
     )
     .map(|instance| (instance, trampoline))
 }
@@ -298,5 +298,12 @@ pub unsafe fn create_handle_with_raw_function(
     let sig_id = store.register_signature(ft.to_wasm_func_type(), sig);
     trampolines.insert(sig_id, trampoline);
 
-    create_handle(module, store, finished_functions, trampolines, state, &[])
+    create_handle(
+        module,
+        store,
+        finished_functions,
+        trampolines,
+        state,
+        PrimaryMap::new(),
+    )
 }
diff --git a/crates/wasmtime/src/trampoline/global.rs b/crates/wasmtime/src/trampoline/global.rs
index b0f060cc4..7093e6516 100644
--- a/crates/wasmtime/src/trampoline/global.rs
+++ b/crates/wasmtime/src/trampoline/global.rs
@@ -8,7 +8,7 @@ use wasmtime_runtime::VMFunctionImport;
 
 pub fn create_global(store: &Store, gt: &GlobalType, val: Val) -> Result<StoreInstanceHandle> {
     let mut module = Module::new();
-    let mut func_imports = Vec::new();
+    let mut func_imports = PrimaryMap::new();
     let mut externref_init = None;
 
     let global = wasm::Global {
@@ -68,7 +68,7 @@ pub fn create_global(store: &Store, gt: &GlobalType, val: Val) -> Result<StoreIn
         PrimaryMap::new(),
         Default::default(),
         Box::new(()),
-        &func_imports,
+        func_imports,
     )?;
 
     if let Some(x) = externref_init {
diff --git a/crates/wasmtime/src/trampoline/memory.rs b/crates/wasmtime/src/trampoline/memory.rs
index f8ed9a256..ebc035c53 100644
--- a/crates/wasmtime/src/trampoline/memory.rs
+++ b/crates/wasmtime/src/trampoline/memory.rs
@@ -35,7 +35,7 @@ pub fn create_handle_with_memory(
         PrimaryMap::new(),
         Default::default(),
         Box::new(()),
-        &[],
+        PrimaryMap::new(),
     )
 }
 
diff --git a/crates/wasmtime/src/trampoline/table.rs b/crates/wasmtime/src/trampoline/table.rs
index 002a49558..14433c03a 100644
--- a/crates/wasmtime/src/trampoline/table.rs
+++ b/crates/wasmtime/src/trampoline/table.rs
@@ -33,6 +33,6 @@ pub fn create_handle_with_table(store: &Store, table: &TableType) -> Result<Stor
         PrimaryMap::new(),
         Default::default(),
         Box::new(()),
-        &[],
+        PrimaryMap::new(),
     )
 }
diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md
index cb6c41961..32b71d3cd 100644
--- a/docs/SUMMARY.md
+++ b/docs/SUMMARY.md
@@ -5,26 +5,26 @@
   - [Creating `hello-world.wasm`](./tutorial-create-hello-world.md)
   - [Running `hello-world.wasm`](./tutorial-run-hello-world.md)
 - [Examples](./examples.md)
-  - [Markdown parser](./examples-markdown.md)
+  - [Markdown Parser](./examples-markdown.md)
   - [Profiling WebAssembly](./examples-profiling.md)
     - [Profiling with Perf](./examples-profiling-perf.md)
     - [Profiling with VTune](./examples-profiling-vtune.md)
   - [Embedding in Rust](./examples-rust-embed.md)
     - [Hello, world!](./examples-rust-hello-world.md)
     - [Calculating the GCD](./examples-rust-gcd.md)
-    - [Using linear memory](./examples-rust-memory.md)
+    - [Using Linear Memory](./examples-rust-memory.md)
     - [WASI](./examples-rust-wasi.md)
-    - [Linking modules](./examples-rust-linking.md)
+    - [Linking Modules](./examples-rust-linking.md)
     - [Debugging](./examples-rust-debugging.md)
-    - [Using multi-value](./examples-rust-multi-value.md)
+    - [Using Multi-Value](./examples-rust-multi-value.md)
   - [Embedding in C](./examples-c-embed.md)
-    - [Hello, world!](./examples-c-hello-world.md)
+    - [Hello, World!](./examples-c-hello-world.md)
     - [Calculating the GCD](./examples-c-gcd.md)
-    - [Using linear memory](./examples-c-memory.md)
+    - [Using Linear Memory](./examples-c-memory.md)
     - [WASI](./examples-c-wasi.md)
-    - [Linking modules](./examples-c-linking.md)
+    - [Linking Modules](./examples-c-linking.md)
     - [Debugging](./examples-c-debugging.md)
-    - [Using multi-value](./examples-c-multi-value.md)
+    - [Using Multi-Value](./examples-c-multi-value.md)
 - [Using WebAssembly from your language](./lang.md)
   - [Rust](./lang-rust.md)
   - [C](./lang-c.md)
@@ -45,6 +45,7 @@
 - [Stability](stability.md)
   - [Release Process](./stability-release.md)
   - [Platform Support](./stability-platform-support.md)
+  - [Wasm Proposals Support](./stability-wasm-proposals-support.md)
 - [Security](security.md)
   - [Disclosure Policy](./security-disclosure.md)
   - [Sandboxing](./security-sandboxing.md)
@@ -53,8 +54,9 @@
   - [Testing](./contributing-testing.md)
   - [Fuzzing](./contributing-fuzzing.md)
   - [CI](./contributing-ci.md)
-  - [Coding guidelines](./contributing-coding-guidelines.md)
-  - [Development process](./contributing-development-process.md)
+  - [Coding Guidelines](./contributing-coding-guidelines.md)
+  - [Development Process](./contributing-development-process.md)
   - [Release Process](./contributing-release-process.md)
+  - [Implementing Wasm Proposals](./contributing-implementing-wasm-proposals.md)
   - [Governance](./contributing-governance.md)
   - [Code of Conduct](./contributing-coc.md)
diff --git a/docs/contributing-coding-guidelines.md b/docs/contributing-coding-guidelines.md
index 0fe5133df..41fcfc961 100644
--- a/docs/contributing-coding-guidelines.md
+++ b/docs/contributing-coding-guidelines.md
@@ -6,7 +6,7 @@ be aware of.
 
 [pull request]: https://help.github.com/articles/about-pull-requests/
 
-### rustfmt
+### `rustfmt`
 
 All PRs must be formatted according to rustfmt, and this is checked in the
 continuous integration tests. You can format code locally with:
@@ -19,7 +19,7 @@ at the root of the repository. You can find [more information about rustfmt
 online](https://github.com/rust-lang/rustfmt) too, such as how to configure
 your editor.
 
-### Rustc version support
+### Minimum Supported `rustc` Version
 
 Wasmtime supports the current stable version of Rust.
 
diff --git a/docs/contributing-implementing-wasm-proposals.md b/docs/contributing-implementing-wasm-proposals.md
new file mode 100644
index 000000000..bce9db61d
--- /dev/null
+++ b/docs/contributing-implementing-wasm-proposals.md
@@ -0,0 +1,118 @@
+# Implementing WebAssembly Proposals
+
+## Adding New Support for a Wasm Proposal
+
+The following checkboxes enumerate the steps required to add support for a new
+WebAssembly proposal to Wasmtime. They can be completed over the course of
+multiple pull requests.
+
+* <input type="checkbox"/> Add support to the
+  [`wasmparser`](https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wasmparser)
+  crate.
+
+* <input type="checkbox"/> Add support to the
+  [`wat`](https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wat)
+  and
+  [`wast`](https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wast)
+  crates.
+
+* <input type="checkbox"/> Add support to the
+  [`wasmprinter`](https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wasmprinter)
+  crate.
+
+* <input type="checkbox"/> Add a `wasmtime::Config::enable_foo_bar` method to
+  the `wasmtime` crate.
+
+* <input type="checkbox"/> Add a `--enable-foo-bar` command line flag to the
+  `wasmtime` binary.
+
+* <input type="checkbox"/> Enable the spec tests in
+  [`build.rs`](https://github.com/bytecodealliance/wasmtime/blob/c7cd70fcec3eee66c9d7b5aa6fb4580d5a802218/build.rs#L41-L52)
+  but [mark them as
+  ignored](https://github.com/bytecodealliance/wasmtime/blob/c7cd70fcec3eee66c9d7b5aa6fb4580d5a802218/build.rs#L196)
+  for now.
+
+* <input type="checkbox"/> Stop ignoring individual spec tests and get them
+  passing one by one.
+
+* <input type="checkbox"/> Enable the proposal in [the fuzz
+  targets](./contributing-fuzzing.html).
+
+  * <input type="checkbox"/> Add examples from the spec tests to [the relevant
+    corpora](https://github.com/bytecodealliance/wasmtime-libfuzzer-corpus).
+
+    > The `wast2json` tool from [WABT] is useful for this.
+
+  * <input type="checkbox"/> Write a custom fuzz target, oracle, and/or test
+    case generator for fuzzing this proposal in particular.
+
+    > For example, we wrote a [custom
+    > generator](https://github.com/bytecodealliance/wasmtime/blob/c7cd70fcec3eee66c9d7b5aa6fb4580d5a802218/crates/fuzzing/src/generators/table_ops.rs),
+    > [oracle](https://github.com/bytecodealliance/wasmtime/blob/c7cd70fcec3eee66c9d7b5aa6fb4580d5a802218/crates/fuzzing/src/oracles.rs#L417-L467),
+    > and [fuzz
+    > target](https://github.com/bytecodealliance/wasmtime/blob/c7cd70fcec3eee66c9d7b5aa6fb4580d5a802218/fuzz/fuzz_targets/table_ops.rs)
+    > for exercising `table.{get,set}` instructions and their interaction with
+    > GC while implementing the reference types proposal.
+
+* <input type="checkbox"/> Expose the proposal's new functionality in the
+  `wasmtime` crate's API.
+
+  > For example, the bulk memory operations proposal introduced a `table.copy`
+  > instruction, and we exposed its functionality as the `wasmtime::Table::copy`
+  > method.
+
+* <input type="checkbox"/> Expose the proposal's new functionality in the C API.
+
+  > This may require extensions to the standard C API, and if so, should be
+  > defined in
+  > [`wasmtime.h`](https://github.com/bytecodealliance/wasmtime/blob/c7cd70fcec3eee66c9d7b5aa6fb4580d5a802218/crates/c-api/include/wasmtime.h)
+  > and prefixed with `wasmtime_`.
+
+* <input type="checkbox"/> Use the C API to expose the proposal's new
+  functionality in the other language embedding APIs:
+
+  * <input type="checkbox"/> [Python](https://github.com/bytecodealliance/wasmtime-py/)
+
+  * <input type="checkbox"/> [Go](https://github.com/bytecodealliance/wasmtime-go/)
+
+  * <input type="checkbox"/> [.NET](https://github.com/bytecodealliance/wasmtime-dotnet/)
+
+* <input type="checkbox"/> Document support for the proposal in
+  `wasmtime/docs/stability-wasm-proposals-support.md`.
+
+## Enabling Support for a Proposal by Default
+
+These are the standards that must be met to enable support for a proposal by
+default in Wasmtime, and can be used as a review checklist.
+
+* <input type="checkbox"/> The proposal must be in phase 4, or greater, of [the
+  WebAssembly standardization process][phases].
+
+* <input type="checkbox"/> All spec tests must be passing in Wasmtime.
+
+* <input type="checkbox"/> No open questions, design concerns, or serious known
+  bugs.
+
+* <input type="checkbox"/> Has been fuzzed for at least a week minimum.
+
+* <input type="checkbox"/> We are confident that the fuzzers are fully
+  exercising the proposal's functionality.
+
+  > For example, it would *not* have been enough to simply enable reference
+  > types in the `compile` fuzz target to enable that proposal by
+  > default. Compiling a module that uses reference types but not instantiating
+  > it nor running any of its functions doesn't exercise any of the GC
+  > implementation and does not run the inline fast paths for `table` operations
+  > emitted by the JIT. Exercising these things was the motivation for writing
+  > the custom fuzz target for `table.{get,set}` instructions.
+
+* <input type="checkbox"/> The proposal's functionality is exposed in the
+  `wasmtime` crate's API.
+
+* <input type="checkbox"/> The proposal's functionality is exposed in the C API.
+
+* <input type="checkbox"/> The proposal's functionality is exposed in at least
+  one of the other languages' APIs.
+
+[phases]: https://github.com/WebAssembly/meetings/blob/master/process/phases.md
+[WABT]: https://github.com/WebAssembly/wabt/
diff --git a/docs/stability-wasm-proposals-support.md b/docs/stability-wasm-proposals-support.md
new file mode 100644
index 000000000..d38db6f3d
--- /dev/null
+++ b/docs/stability-wasm-proposals-support.md
@@ -0,0 +1,34 @@
+# WebAssembly Proposals Support
+
+The following table summarizes Wasmtime's support for WebAssembly proposals as
+well as the command line flag and [`wasmtime::Config`][config] method you can
+use to enable or disable support for a proposal.
+
+If a proposal is not listed, then it is not supported by Wasmtime.
+
+Wasmtime will never enable a proposal by default unless it has reached phase 4
+of [the WebAssembly standardizations process][phases] and its implementation in
+Wasmtime has been [thoroughly
+vetted](./contributing-supporting-new-wasm-proposals.html).
+
+| WebAssembly Proposal                        | Supported in Wasmtime?           | Command Line Flag      | [`Config`][config] Method |
+|---------------------------------------------|----------------------------------|------------------------|---------------------------|
+| **[Import and Export Mutable Globals]**     | **Yes.**<br/>Always enabled.     | (none)                 | (none)                    |
+| **[Sign-Extension Operations]**             | **Yes.**<br/>Always enabled.     | (none)                 | (none)                    |
+| **[Non-Trapping Float-to-Int Conversions]** | **Yes.**<br/>Always enabled.     | (none)                 | (none)                    |
+| **[Multi-Value]**                           | **Yes.**<br/>Enabled by default. | `--enable-multi-value` | [`wasm_multi_value`](https://docs.rs/wasmtime/*/wasmtime/struct.Config.html#method.wasm_multi_value) |
+| **[Bulk Memory Operations]**                | **Yes.**<br/>Enabled by default. | `--enable-bulk-memory` | [`wasm_bulk_memory`](https://docs.rs/wasmtime/*/wasmtime/struct.Config.html#method.wasm_bulk_memory) |
+| **[Reference Types]**                       | **Yes.**<br/>Enabled by default on x86_64. Aarch64 support in progress. | `--enable-reference-types` | [`wasm_reference_types`](https://docs.rs/wasmtime/*/wasmtime/struct.Config.html#method.wasm_reference_types) |
+| **[Fixed-Width SIMD]**                      | **In progress.**                 | `--enable-simd`        | [`wasm_simd`](https://docs.rs/wasmtime/*/wasmtime/struct.Config.html#method.wasm_simd) |
+| **[Threads and Atomics]**                   | **In progress.**                 | `--enable-threads`     | [`wasm_threads`](https://docs.rs/wasmtime/*/wasmtime/struct.Config.html#method.wasm_threads) |
+
+[config]: https://docs.rs/wasmtime/*/wasmtime/struct.Config.html
+[Multi-Value]: https://github.com/WebAssembly/spec/blob/master/proposals/multi-value/Overview.md
+[Bulk Memory Operations]: https://github.com/WebAssembly/bulk-memory-operations/blob/master/proposals/bulk-memory-operations/Overview.md
+[Import and Export Mutable Globals]: https://github.com/WebAssembly/mutable-global/blob/master/proposals/mutable-global/Overview.md
+[Reference Types]: https://github.com/WebAssembly/reference-types/blob/master/proposals/reference-types/Overview.md
+[Non-Trapping Float-to-Int Conversions]: https://github.com/WebAssembly/spec/blob/master/proposals/nontrapping-float-to-int-conversion/Overview.md
+[Sign-Extension Operations]: https://github.com/WebAssembly/spec/blob/master/proposals/sign-extension-ops/Overview.md
+[Fixed-Width SIMD]: https://github.com/WebAssembly/simd/blob/master/proposals/simd/SIMD.md
+[phases]: https://github.com/WebAssembly/meetings/blob/master/process/phases.md
+[Threads and Atomics]: https://github.com/WebAssembly/threads/blob/master/proposals/threads/Overview.md
diff --git a/scripts/publish.rs b/scripts/publish.rs
index c61870edb..af2f694b3 100644
--- a/scripts/publish.rs
+++ b/scripts/publish.rs
@@ -58,7 +58,6 @@ const CRATES_TO_PUBLISH: &[&str] = &[
     "wasmtime-profiling",
     "wasmtime-obj",
     "wasmtime-jit",
-    "wasmtime-cache",
     "wasmtime",
     "wasmtime-wiggle",
     "wasmtime-wasi",
diff --git a/src/commands/config.rs b/src/commands/config.rs
index 14184bd8e..5507bf502 100644
--- a/src/commands/config.rs
+++ b/src/commands/config.rs
@@ -1,7 +1,8 @@
 //! The module that implements the `wasmtime config` command.
 
-use anyhow::Result;
+use anyhow::{anyhow, Result};
 use structopt::StructOpt;
+use wasmtime_environ::cache_create_new_config;
 
 const CONFIG_NEW_AFTER_HELP: &str =
     "If no file path is specified, the system configuration file path will be used.";
@@ -36,7 +37,7 @@ pub struct ConfigNewCommand {
 impl ConfigNewCommand {
     /// Executes the command.
     pub fn execute(&self) -> Result<()> {
-        let path = wasmtime_cache::create_new_config(self.path.as_ref())?;
+        let path = cache_create_new_config(self.path.as_ref()).map_err(|e| anyhow!(e))?;
 
         println!(
             "Successfully created a new configuation file at '{}'.",
diff --git a/src/lib.rs b/src/lib.rs
index d254a95e5..22bc9cbfc 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -110,7 +110,7 @@ struct CommonOptions {
 
     /// Enable support for reference types
     #[structopt(long)]
-    enable_reference_types: bool,
+    enable_reference_types: Option<bool>,
 
     /// Enable support for multi-value functions
     #[structopt(long)]
@@ -122,7 +122,7 @@ struct CommonOptions {
 
     /// Enable support for bulk memory instructions
     #[structopt(long)]
-    enable_bulk_memory: bool,
+    enable_bulk_memory: Option<bool>,
 
     /// Enable all experimental Wasm features
     #[structopt(long)]
@@ -185,9 +185,13 @@ impl CommonOptions {
         config
             .cranelift_debug_verifier(self.enable_cranelift_debug_verifier)
             .debug_info(self.debug_info)
-            .wasm_bulk_memory(self.enable_bulk_memory || self.enable_all)
             .wasm_simd(self.enable_simd || self.enable_all)
-            .wasm_reference_types(self.enable_reference_types || self.enable_all)
+            .wasm_bulk_memory(self.enable_bulk_memory.unwrap_or(true) || self.enable_all)
+            .wasm_reference_types(
+                self.enable_reference_types
+                    .unwrap_or(cfg!(target_arch = "x86_64"))
+                    || self.enable_all,
+            )
             .wasm_multi_value(self.enable_multi_value.unwrap_or(true) || self.enable_all)
             .wasm_threads(self.enable_threads || self.enable_all)
             .cranelift_opt_level(self.opt_level())


Test patch:
diff --git a/crates/cache/build.rs b/crates/environ/build.rs
similarity index 100%
rename from crates/cache/build.rs
rename to crates/environ/build.rs
diff --git a/crates/cache/src/lib.rs b/crates/environ/src/cache.rs
similarity index 100%
rename from crates/cache/src/lib.rs
rename to crates/environ/src/cache.rs
diff --git a/crates/cache/src/config.rs b/crates/environ/src/cache/config.rs
similarity index 100%
rename from crates/cache/src/config.rs
rename to crates/environ/src/cache/config.rs
diff --git a/crates/cache/src/config/tests.rs b/crates/environ/src/cache/config/tests.rs
similarity index 100%
rename from crates/cache/src/config/tests.rs
rename to crates/environ/src/cache/config/tests.rs
diff --git a/crates/cache/src/tests.rs b/crates/environ/src/cache/tests.rs
similarity index 100%
rename from crates/cache/src/tests.rs
rename to crates/environ/src/cache/tests.rs
diff --git a/crates/cache/src/worker.rs b/crates/environ/src/cache/worker.rs
similarity index 100%
rename from crates/cache/src/worker.rs
rename to crates/environ/src/cache/worker.rs
diff --git a/crates/cache/src/worker/tests.rs b/crates/environ/src/cache/worker/tests.rs
similarity index 99%
rename from crates/cache/src/worker/tests.rs
rename to crates/environ/src/cache/worker/tests.rs
index 493973de8..43f0c0671 100644
--- a/crates/cache/src/worker/tests.rs
+++ b/crates/environ/src/cache/worker/tests.rs
@@ -1,5 +1,5 @@
 use super::*;
-use crate::config::tests::test_prolog;
+use crate::cache::config::tests::test_prolog;
 use more_asserts::{assert_ge, assert_gt, assert_lt};
 use std::iter::repeat;
 use std::process;
diff --git a/crates/cache/tests/cache_write_default_config.rs b/crates/environ/tests/cache_write_default_config.rs
similarity index 70%
rename from crates/cache/tests/cache_write_default_config.rs
rename to crates/environ/tests/cache_write_default_config.rs
index 6ef0b19a7..1f1592fdb 100644
--- a/crates/cache/tests/cache_write_default_config.rs
+++ b/crates/environ/tests/cache_write_default_config.rs
@@ -1,11 +1,12 @@
-use wasmtime_cache::create_new_config;
+use tempfile;
+use wasmtime_environ::cache_create_new_config;
 
 #[test]
 fn test_cache_write_default_config() {
     let dir = tempfile::tempdir().expect("Can't create temporary directory");
     let config_path = dir.path().join("cache-config.toml");
 
-    let result = create_new_config(Some(&config_path));
+    let result = cache_create_new_config(Some(&config_path));
     assert!(result.is_ok());
     assert!(config_path.exists());
     assert_eq!(config_path, result.unwrap());