Repository: bytecodealliance/wasmtime
Pull request #7844 (2024-01-29 22:45:23)

Fix handling of `Tunables` on cross-compiles
This commit fixes how Wasmtime handles `Tunables` when targetting non-host platforms (or namely platforms with different pointer widths). Previously the host's `Tunables` would always be used instead of the target's tunables which meant that modules couldn't be loaded on the other platform due to the host having differing tunables by default.

This commit updates tunables in `wasmtime::Config` to all be optional and loading the actual `Tunables` is deferred until the target is known during `Engine`-creation time.

<!--
Please make sure you include the following information:

- If this work has been discussed elsewhere, please include a link to that
  conversation. If it was discussed in an issue, just mention "issue #...".

- Explain why this change is needed. If the details are in an issue already,
  this can be brief.

Our development process is documented in the Wasmtime book:
https://docs.wasmtime.dev/contributing-development-process.html

Please ensure all communication follows the code of conduct:
https://github.com/bytecodealliance/wasmtime/blob/main/CODE_OF_CONDUCT.md
-->


github-actions[bot]: #### Subscribe to Label Action

cc @peterhuene, @saulecabrera

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

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

* peterhuene: wasmtime:api
* saulecabrera: winch

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>
github-actions[bot]: #### Label Messager: wasmtime:config

It looks like you are changing Wasmtime's configuration options. Make sure to
complete this check list:

* [ ] If you added a new `Config` method, you wrote extensive documentation for
      it.

  <details>

  Our documentation should be of the following form:

  ```text
  Short, simple summary sentence.

  More details. These details can be multiple paragraphs. There should be
  information about not just the method, but its parameters and results as
  well.

  Is this method fallible? If so, when can it return an error?

  Can this method panic? If so, when does it panic?

  # Example

  Optional example here.
  ```

  </details>

* [ ] If you added a new `Config` method, or modified an existing one, you
  ensured that this configuration is exercised by the fuzz targets.

  <details>

  For example, if you expose a new strategy for allocating the next instance
  slot inside the pooling allocator, you should ensure that at least one of our
  fuzz targets exercises that new strategy.

  Often, all that is required of you is to ensure that there is a knob for this
  configuration option in [`wasmtime_fuzzing::Config`][fuzzing-config] (or one
  of its nested `struct`s).

  Rarely, this may require authoring a new fuzz target to specifically test this
  configuration. See [our docs on fuzzing][fuzzing-docs] for more details.

  </details>

* [ ] If you are enabling a configuration option by default, make sure that it
  has been fuzzed for at least two weeks before turning it on by default.

[fuzzing-config]: https://github.com/bytecodealliance/wasmtime/blob/ca0e8d0a1d8cefc0496dba2f77a670571d8fdcab/crates/fuzzing/src/generators.rs#L182-L194
[fuzzing-docs]: https://docs.wasmtime.dev/contributing-fuzzing.html


--------------------------------------------------------------------------------

<details>

To modify this label's message, edit the <code>.github/label-messager/wasmtime-config.md</code> file.

To add new label messages or remove existing label messages, edit the
<code>.github/label-messager.json</code> configuration file.

[Learn more.](https://github.com/bytecodealliance/label-messager-action)

</details>

Patch:
diff --git a/.github/actions/binary-compatible-builds/action.yml b/.github/actions/binary-compatible-builds/action.yml
index b5a190c4a..8abc72d63 100644
--- a/.github/actions/binary-compatible-builds/action.yml
+++ b/.github/actions/binary-compatible-builds/action.yml
@@ -2,7 +2,7 @@ name: 'Set up a CentOS 6 container to build releases in'
 description: 'Set up a CentOS 6 container to build releases in'
 
 runs:
-  using: node16
+  using: node20
   main: 'main.js'
 inputs:
   name:
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 0562a7f9d..4d6062754 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -247,7 +247,7 @@ jobs:
         mv crates/c-api/html gh-pages/c-api
         mv target/doc gh-pages/api
         tar czf gh-pages.tar.gz gh-pages
-    - uses: actions/upload-artifact@v3
+    - uses: actions/upload-artifact@v4
       with:
         name: gh-pages
         path: gh-pages.tar.gz
@@ -322,8 +322,13 @@ jobs:
 
     # Check whether `wasmtime` cross-compiles to aarch64-linux-android
     - run: rustup target add aarch64-linux-android
+    - name: Set up JDK 17
+      uses: actions/setup-java@v3
+      with:
+        java-version: '17'
+        distribution: 'temurin'
     - name: Setup Android SDK
-      uses: android-actions/setup-android@v2
+      uses: android-actions/setup-android@v3
     - uses: actions/cache@v4
       with:
         path: ${{ runner.tool_cache }}/cargo-ndk
@@ -557,7 +562,7 @@ jobs:
       env:
         VERSION: ${{ github.sha }}
 
-    - uses: actions/upload-artifact@v3
+    - uses: actions/upload-artifact@v4
       with:
         name: bins-wasi-preview1-component-adapter
         path: target/wasm32-unknown-unknown/release/wasi_snapshot_preview1.*.wasm
@@ -740,7 +745,7 @@ jobs:
     # unconditionally to this workflow's files so we have a copy of them.
     - run: ./ci/build-tarballs.sh "${{ matrix.build }}" "${{ matrix.target }}"
 
-    - uses: actions/upload-artifact@v3
+    - uses: actions/upload-artifact@v4
       with:
         name: bins-${{ matrix.build }}
         path: dist
diff --git a/crates/cranelift/src/builder.rs b/crates/cranelift/src/builder.rs
index 026960dd4..2c8583237 100644
--- a/crates/cranelift/src/builder.rs
+++ b/crates/cranelift/src/builder.rs
@@ -16,7 +16,7 @@ use wasmtime_cranelift_shared::isa_builder::IsaBuilder;
 use wasmtime_environ::{CacheStore, CompilerBuilder, Setting, Tunables};
 
 struct Builder {
-    tunables: Tunables,
+    tunables: Option<Tunables>,
     inner: IsaBuilder<CodegenResult<OwnedTargetIsa>>,
     linkopts: LinkOptions,
     cache_store: Option<Arc<dyn CacheStore>>,
@@ -39,7 +39,7 @@ pub struct LinkOptions {
 
 pub fn builder(triple: Option<Triple>) -> Result<Box<dyn CompilerBuilder>> {
     Ok(Box::new(Builder {
-        tunables: Tunables::default(),
+        tunables: None,
         inner: IsaBuilder::new(triple, |triple| isa::lookup(triple).map_err(|e| e.into()))?,
         linkopts: LinkOptions::default(),
         cache_store: None,
@@ -82,14 +82,17 @@ impl CompilerBuilder for Builder {
     }
 
     fn set_tunables(&mut self, tunables: Tunables) -> Result<()> {
-        self.tunables = tunables;
+        self.tunables = Some(tunables);
         Ok(())
     }
 
     fn build(&self) -> Result<Box<dyn wasmtime_environ::Compiler>> {
         let isa = self.inner.build()?;
         Ok(Box::new(crate::compiler::Compiler::new(
-            self.tunables.clone(),
+            self.tunables
+                .as_ref()
+                .expect("set_tunables not called")
+                .clone(),
             isa,
             self.cache_store.clone(),
             self.linkopts.clone(),
diff --git a/crates/environ/src/lib.rs b/crates/environ/src/lib.rs
index 9f34dd940..4df17658a 100644
--- a/crates/environ/src/lib.rs
+++ b/crates/environ/src/lib.rs
@@ -33,7 +33,7 @@ pub use crate::ref_bits::*;
 pub use crate::scopevec::ScopeVec;
 pub use crate::stack_map::StackMap;
 pub use crate::trap_encoding::*;
-pub use crate::tunables::Tunables;
+pub use crate::tunables::*;
 pub use crate::vmoffsets::*;
 pub use object;
 
diff --git a/crates/environ/src/tunables.rs b/crates/environ/src/tunables.rs
index 7ba3e6fdf..4ff360fcb 100644
--- a/crates/environ/src/tunables.rs
+++ b/crates/environ/src/tunables.rs
@@ -1,7 +1,7 @@
 use serde_derive::{Deserialize, Serialize};
 
 /// Tunable parameters for WebAssembly compilation.
-#[derive(Clone, Hash, Serialize, Deserialize)]
+#[derive(Clone, Hash, Serialize, Deserialize, Debug)]
 pub struct Tunables {
     /// For static heaps, the size in wasm pages of the heap protected by bounds
     /// checking.
@@ -56,62 +56,85 @@ pub struct Tunables {
     pub tail_callable: bool,
 }
 
-impl Default for Tunables {
-    fn default() -> Self {
-        let (static_memory_bound, static_memory_offset_guard_size) = if cfg!(miri) {
+impl Tunables {
+    /// Returns a `Tunables` configuration assumed for running code on the host.
+    pub fn default_host() -> Self {
+        if cfg!(miri) {
+            Tunables::default_miri()
+        } else if cfg!(target_pointer_width = "32") {
+            Tunables::default_u32()
+        } else if cfg!(target_pointer_width = "64") {
+            Tunables::default_u64()
+        } else {
+            panic!("unsupported target_pointer_width");
+        }
+    }
+
+    /// Returns the default set of tunables for running under MIRI.
+    pub fn default_miri() -> Tunables {
+        Tunables {
             // No virtual memory tricks are available on miri so make these
             // limits quite conservative.
-            ((1 << 20) / crate::WASM_PAGE_SIZE as u64, 0)
-        } else if cfg!(target_pointer_width = "64") {
+            static_memory_bound: (1 << 20) / crate::WASM_PAGE_SIZE as u64,
+            static_memory_offset_guard_size: 0,
+            dynamic_memory_offset_guard_size: 0,
+            dynamic_memory_growth_reserve: 0,
+
+            // General options which have the same defaults regardless of
+            // architecture.
+            generate_native_debuginfo: false,
+            parse_wasm_debuginfo: true,
+            consume_fuel: false,
+            epoch_interruption: false,
+            static_memory_bound_is_maximum: false,
+            guard_before_linear_memory: true,
+            generate_address_map: true,
+            debug_adapter_modules: false,
+            relaxed_simd_deterministic: false,
+            tail_callable: false,
+        }
+    }
+
+    /// Returns the default set of tunables for running under a 32-bit host.
+    pub fn default_u32() -> Tunables {
+        Tunables {
+            // For 32-bit we scale way down to 10MB of reserved memory. This
+            // impacts performance severely but allows us to have more than a
+            // few instances running around.
+            static_memory_bound: (10 * (1 << 20)) / crate::WASM_PAGE_SIZE as u64,
+            static_memory_offset_guard_size: 0x1_0000,
+            dynamic_memory_offset_guard_size: 0x1_0000,
+            dynamic_memory_growth_reserve: 1 << 20, // 1MB
+
+            ..Tunables::default_miri()
+        }
+    }
+
+    /// Returns the default set of tunables for running under a 64-bit host.
+    pub fn default_u64() -> Tunables {
+        Tunables {
             // 64-bit has tons of address space to static memories can have 4gb
             // address space reservations liberally by default, allowing us to
             // help eliminate bounds checks.
             //
             // Coupled with a 2 GiB address space guard it lets us translate
             // wasm offsets into x86 offsets as aggressively as we can.
-            (0x1_0000, 0x8000_0000)
-        } else if cfg!(target_pointer_width = "32") {
-            // For 32-bit we scale way down to 10MB of reserved memory. This
-            // impacts performance severely but allows us to have more than a
-            // few instances running around.
-            ((10 * (1 << 20)) / crate::WASM_PAGE_SIZE as u64, 0x1_0000)
-        } else {
-            panic!("unsupported target_pointer_width");
-        };
-        Self {
-            static_memory_bound,
-            static_memory_offset_guard_size,
+            static_memory_bound: 0x1_0000,
+            static_memory_offset_guard_size: 0x8000_0000,
 
             // Size in bytes of the offset guard for dynamic memories.
             //
             // Allocate a small guard to optimize common cases but without
             // wasting too much memory.
-            dynamic_memory_offset_guard_size: if cfg!(miri) { 0 } else { 0x1_0000 },
+            dynamic_memory_offset_guard_size: 0x1_0000,
 
             // We've got lots of address space on 64-bit so use a larger
             // grow-into-this area, but on 32-bit we aren't as lucky. Miri is
             // not exactly fast so reduce memory consumption instead of trying
             // to avoid memory movement.
-            dynamic_memory_growth_reserve: if cfg!(miri) {
-                0
-            } else if cfg!(target_pointer_width = "64") {
-                2 << 30 // 2GB
-            } else if cfg!(target_pointer_width = "32") {
-                1 << 20 // 1MB
-            } else {
-                panic!("unsupported target_pointer_width");
-            },
+            dynamic_memory_growth_reserve: 2 << 30, // 2GB
 
-            generate_native_debuginfo: false,
-            parse_wasm_debuginfo: true,
-            consume_fuel: false,
-            epoch_interruption: false,
-            static_memory_bound_is_maximum: false,
-            guard_before_linear_memory: true,
-            generate_address_map: true,
-            debug_adapter_modules: false,
-            relaxed_simd_deterministic: false,
-            tail_callable: false,
+            ..Tunables::default_miri()
         }
     }
 }
diff --git a/crates/runtime/src/instance/allocator/pooling.rs b/crates/runtime/src/instance/allocator/pooling.rs
index 928069b5e..a7154c7c9 100644
--- a/crates/runtime/src/instance/allocator/pooling.rs
+++ b/crates/runtime/src/instance/allocator/pooling.rs
@@ -579,7 +579,7 @@ mod test {
                 &config,
                 &Tunables {
                     static_memory_bound: 1,
-                    ..Tunables::default()
+                    ..Tunables::default_host()
                 },
             )
             .map_err(|e| e.to_string())
@@ -603,7 +603,7 @@ mod test {
             async_stack_zeroing: true,
             ..PoolingInstanceAllocatorConfig::default()
         };
-        let allocator = PoolingInstanceAllocator::new(&config, &Tunables::default())?;
+        let allocator = PoolingInstanceAllocator::new(&config, &Tunables::default_host())?;
 
         unsafe {
             for _ in 0..255 {
@@ -637,7 +637,7 @@ mod test {
             async_stack_zeroing: false,
             ..PoolingInstanceAllocatorConfig::default()
         };
-        let allocator = PoolingInstanceAllocator::new(&config, &Tunables::default())?;
+        let allocator = PoolingInstanceAllocator::new(&config, &Tunables::default_host())?;
 
         unsafe {
             for i in 0..255 {
diff --git a/crates/runtime/src/instance/allocator/pooling/memory_pool.rs b/crates/runtime/src/instance/allocator/pooling/memory_pool.rs
index 9658e43d8..414ecb797 100644
--- a/crates/runtime/src/instance/allocator/pooling/memory_pool.rs
+++ b/crates/runtime/src/instance/allocator/pooling/memory_pool.rs
@@ -774,7 +774,7 @@ mod tests {
             &Tunables {
                 static_memory_bound: 1,
                 static_memory_offset_guard_size: 0,
-                ..Tunables::default()
+                ..Tunables::default_host()
             },
         )?;
 
@@ -808,7 +808,7 @@ mod tests {
             &Tunables {
                 static_memory_bound: 1,
                 static_memory_offset_guard_size: 0,
-                ..Tunables::default()
+                ..Tunables::default_host()
             },
         )
         .unwrap();
@@ -828,7 +828,7 @@ mod tests {
             memory_protection_keys: MpkEnabled::Enable,
             ..PoolingInstanceAllocatorConfig::default()
         };
-        let pool = MemoryPool::new(&config, &Tunables::default()).unwrap();
+        let pool = MemoryPool::new(&config, &Tunables::default_host()).unwrap();
         assert!(pool.stripes.len() >= 2);
 
         let max_memory_slots = config.limits.total_memories;
diff --git a/crates/wasi-nn/Cargo.toml b/crates/wasi-nn/Cargo.toml
index 64fe8604d..62f582cce 100644
--- a/crates/wasi-nn/Cargo.toml
+++ b/crates/wasi-nn/Cargo.toml
@@ -17,10 +17,10 @@ workspace = true
 [dependencies]
 # These dependencies are necessary for the WITX-generation macros to work:
 anyhow = { workspace = true }
-wiggle = { workspace = true }
+wiggle = { workspace = true, features = ["wasmtime"] }
 
 # This dependency is necessary for the WIT-generation macros to work:
-wasmtime = { workspace = true, features = ["component-model"] }
+wasmtime = { workspace = true, features = ["component-model", "runtime"] }
 
 # These dependencies are necessary for the wasi-nn implementation:
 tracing = { workspace = true }
diff --git a/crates/wasmtime/src/compile.rs b/crates/wasmtime/src/compile.rs
index 71bc5a96c..b8811619d 100644
--- a/crates/wasmtime/src/compile.rs
+++ b/crates/wasmtime/src/compile.rs
@@ -54,7 +54,7 @@ pub(crate) fn build_artifacts<T: FinishedObject>(
     engine: &Engine,
     wasm: &[u8],
 ) -> Result<(T, Option<(CompiledModuleInfo, ModuleTypes)>)> {
-    let tunables = &engine.config().tunables;
+    let tunables = engine.tunables();
 
     // First a `ModuleEnvironment` is created which records type information
     // about the wasm module. This is where the WebAssembly is parsed and
@@ -117,7 +117,7 @@ pub(crate) fn build_component_artifacts<T: FinishedObject>(
     use wasmtime_environ::component::{CompiledComponentInfo, ComponentArtifacts};
     use wasmtime_environ::ScopeVec;
 
-    let tunables = &engine.config().tunables;
+    let tunables = engine.tunables();
     let compiler = engine.compiler();
 
     let scope = ScopeVec::new();
@@ -610,7 +610,7 @@ impl FunctionIndices {
         // `symbol_ids_and_locs[i]` is the symbol ID and function location of
         // `compiled_funcs[i]`.
         let compiler = engine.compiler();
-        let tunables = &engine.config().tunables;
+        let tunables = engine.tunables();
         let symbol_ids_and_locs = compiler.append_code(
             &mut obj,
             &compiled_funcs,
diff --git a/crates/wasmtime/src/config.rs b/crates/wasmtime/src/config.rs
index 75a1c616f..944afd255 100644
--- a/crates/wasmtime/src/config.rs
+++ b/crates/wasmtime/src/config.rs
@@ -6,7 +6,7 @@ use std::fmt;
 use std::path::Path;
 use std::str::FromStr;
 use std::sync::Arc;
-use target_lexicon::Architecture;
+use target_lexicon::{Architecture, PointerWidth};
 use wasmparser::WasmFeatures;
 #[cfg(feature = "cache")]
 use wasmtime_cache::CacheConfig;
@@ -103,8 +103,8 @@ pub struct Config {
     #[cfg(any(feature = "cranelift", feature = "winch"))]
     compiler_config: CompilerConfig,
     profiling_strategy: ProfilingStrategy,
+    tunables: ConfigTunables,
 
-    pub(crate) tunables: Tunables,
     #[cfg(feature = "cache")]
     pub(crate) cache_config: CacheConfig,
     #[cfg(feature = "runtime")]
@@ -130,6 +130,24 @@ pub struct Config {
     pub(crate) macos_use_mach_ports: bool,
 }
 
+#[derive(Default, Clone)]
+struct ConfigTunables {
+    static_memory_bound: Option<u64>,
+    static_memory_offset_guard_size: Option<u64>,
+    dynamic_memory_offset_guard_size: Option<u64>,
+    dynamic_memory_growth_reserve: Option<u64>,
+    generate_native_debuginfo: Option<bool>,
+    parse_wasm_debuginfo: Option<bool>,
+    consume_fuel: Option<bool>,
+    epoch_interruption: Option<bool>,
+    static_memory_bound_is_maximum: Option<bool>,
+    guard_before_linear_memory: Option<bool>,
+    generate_address_map: Option<bool>,
+    debug_adapter_modules: Option<bool>,
+    relaxed_simd_deterministic: Option<bool>,
+    tail_callable: Option<bool>,
+}
+
 /// User-provided configuration for the compiler.
 #[cfg(any(feature = "cranelift", feature = "winch"))]
 #[derive(Debug, Clone)]
@@ -190,7 +208,7 @@ impl Config {
     /// specified.
     pub fn new() -> Self {
         let mut ret = Self {
-            tunables: Tunables::default(),
+            tunables: ConfigTunables::default(),
             #[cfg(any(feature = "cranelift", feature = "winch"))]
             compiler_config: CompilerConfig::default(),
             #[cfg(feature = "cache")]
@@ -386,7 +404,7 @@ impl Config {
     ///
     /// By default this option is `false`.
     pub fn debug_info(&mut self, enable: bool) -> &mut Self {
-        self.tunables.generate_native_debuginfo = enable;
+        self.tunables.generate_native_debuginfo = Some(enable);
         self
     }
 
@@ -433,13 +451,13 @@ impl Config {
     pub fn wasm_backtrace_details(&mut self, enable: WasmBacktraceDetails) -> &mut Self {
         self.wasm_backtrace_details_env_used = false;
         self.tunables.parse_wasm_debuginfo = match enable {
-            WasmBacktraceDetails::Enable => true,
-            WasmBacktraceDetails::Disable => false,
+            WasmBacktraceDetails::Enable => Some(true),
+            WasmBacktraceDetails::Disable => Some(false),
             WasmBacktraceDetails::Environment => {
                 self.wasm_backtrace_details_env_used = true;
                 std::env::var("WASMTIME_BACKTRACE_DETAILS")
-                    .map(|s| s == "1")
-                    .unwrap_or(false)
+                    .map(|s| Some(s == "1"))
+                    .unwrap_or(Some(false))
             }
         };
         self
@@ -482,7 +500,7 @@ impl Config {
     ///
     /// [`Store`]: crate::Store
     pub fn consume_fuel(&mut self, enable: bool) -> &mut Self {
-        self.tunables.consume_fuel = enable;
+        self.tunables.consume_fuel = Some(enable);
         self
     }
 
@@ -576,7 +594,7 @@ impl Config {
     /// - [`Store::epoch_deadline_callback`](crate::Store::epoch_deadline_callback)
     /// - [`Store::epoch_deadline_async_yield_and_update`](crate::Store::epoch_deadline_async_yield_and_update)
     pub fn epoch_interruption(&mut self, enable: bool) -> &mut Self {
-        self.tunables.epoch_interruption = enable;
+        self.tunables.epoch_interruption = Some(enable);
         self
     }
 
@@ -667,7 +685,7 @@ impl Config {
     /// [WebAssembly tail calls proposal]: https://github.com/WebAssembly/tail-call
     pub fn wasm_tail_call(&mut self, enable: bool) -> &mut Self {
         self.features.tail_call = enable;
-        self.tunables.tail_callable = enable;
+        self.tunables.tail_callable = Some(enable);
         self
     }
 
@@ -794,7 +812,7 @@ impl Config {
     ///
     /// [proposal]: https://github.com/webassembly/relaxed-simd
     pub fn relaxed_simd_deterministic(&mut self, enable: bool) -> &mut Self {
-        self.tunables.relaxed_simd_deterministic = enable;
+        self.tunables.relaxed_simd_deterministic = Some(enable);
         self
     }
 
@@ -1239,7 +1257,7 @@ impl Config {
     /// `PoolingAllocatorConfig::memory_protection_keys` for details.
     pub fn static_memory_maximum_size(&mut self, max_size: u64) -> &mut Self {
         let max_pages = max_size / u64::from(wasmtime_environ::WASM_PAGE_SIZE);
-        self.tunables.static_memory_bound = max_pages;
+        self.tunables.static_memory_bound = Some(max_pages);
         self
     }
 
@@ -1255,7 +1273,7 @@ impl Config {
     /// For the difference between static and dynamic memories, see the
     /// [`Config::static_memory_maximum_size`].
     pub fn static_memory_forced(&mut self, force: bool) -> &mut Self {
-        self.tunables.static_memory_bound_is_maximum = force;
+        self.tunables.static_memory_bound_is_maximum = Some(force);
         self
     }
 
@@ -1311,7 +1329,7 @@ impl Config {
     /// than the value configured for [`Config::dynamic_memory_guard_size`].
     pub fn static_memory_guard_size(&mut self, guard_size: u64) -> &mut Self {
         let guard_size = round_up_to_pages(guard_size);
-        self.tunables.static_memory_offset_guard_size = guard_size;
+        self.tunables.static_memory_offset_guard_size = Some(guard_size);
         self
     }
 
@@ -1344,7 +1362,7 @@ impl Config {
     /// than the value configured for [`Config::static_memory_guard_size`].
     pub fn dynamic_memory_guard_size(&mut self, guard_size: u64) -> &mut Self {
         let guard_size = round_up_to_pages(guard_size);
-        self.tunables.dynamic_memory_offset_guard_size = guard_size;
+        self.tunables.dynamic_memory_offset_guard_size = Some(guard_size);
         self
     }
 
@@ -1383,7 +1401,7 @@ impl Config {
     /// For 64-bit platforms this defaults to 2GB, and for 32-bit platforms this
     /// defaults to 1MB.
     pub fn dynamic_memory_reserved_for_growth(&mut self, reserved: u64) -> &mut Self {
-        self.tunables.dynamic_memory_growth_reserve = round_up_to_pages(reserved);
+        self.tunables.dynamic_memory_growth_reserve = Some(round_up_to_pages(reserved));
         self
     }
 
@@ -1408,7 +1426,7 @@ impl Config {
     ///
     /// This value defaults to `true`.
     pub fn guard_before_linear_memory(&mut self, guard: bool) -> &mut Self {
-        self.tunables.guard_before_linear_memory = guard;
+        self.tunables.guard_before_linear_memory = Some(guard);
         self
     }
 
@@ -1455,7 +1473,7 @@ impl Config {
     /// numbers if so configured as well (and the original wasm module has DWARF
     /// debugging information present).
     pub fn generate_address_map(&mut self, generate: bool) -> &mut Self {
-        self.tunables.generate_address_map = generate;
+        self.tunables.generate_address_map = Some(generate);
         self
     }
 
@@ -1589,7 +1607,7 @@ impl Config {
         self
     }
 
-    pub(crate) fn validate(&self) -> Result<()> {
+    pub(crate) fn validate(&self) -> Result<Tunables> {
         if self.features.reference_types && !self.features.bulk_memory {
             bail!("feature 'reference_types' requires 'bulk_memory' to be enabled");
         }
@@ -1603,27 +1621,73 @@ impl Config {
         if self.max_wasm_stack == 0 {
             bail!("max_wasm_stack size cannot be zero");
         }
-        if self.tunables.static_memory_offset_guard_size
-            < self.tunables.dynamic_memory_offset_guard_size
-        {
-            bail!("static memory guard size cannot be smaller than dynamic memory guard size");
-        }
         #[cfg(not(feature = "wmemcheck"))]
         if self.wmemcheck {
             bail!("wmemcheck (memory checker) was requested but is not enabled in this build");
         }
 
-        Ok(())
+        #[cfg(not(any(feature = "cranelift", feature = "winch")))]
+        let mut tunables = Tunables::default_host();
+        #[cfg(any(feature = "cranelift", feature = "winch"))]
+        let mut tunables = match &self.compiler_config.target.as_ref() {
+            Some(target) => match target.pointer_width() {
+                Ok(PointerWidth::U32) => Tunables::default_u32(),
+                Ok(PointerWidth::U64) => Tunables::default_u64(),
+                _ => bail!("unknown pointer width"),
+            },
+            None => Tunables::default_host(),
+        };
+
+        macro_rules! set_fields {
+            ($($field:ident)*) => (
+                let ConfigTunables {
+                    $($field,)*
+                } = &self.tunables;
+
+                $(
+                    if let Some(e) = $field {
+                        tunables.$field = *e;
+                    }
+                )*
+            )
+        }
+
+        set_fields! {
+            static_memory_bound
+            static_memory_offset_guard_size
+            dynamic_memory_offset_guard_size
+            dynamic_memory_growth_reserve
+            generate_native_debuginfo
+            parse_wasm_debuginfo
+            consume_fuel
+            epoch_interruption
+            static_memory_bound_is_maximum
+            guard_before_linear_memory
+            generate_address_map
+            debug_adapter_modules
+            relaxed_simd_deterministic
+            tail_callable
+        }
+
+        if tunables.static_memory_offset_guard_size < tunables.dynamic_memory_offset_guard_size {
+            bail!("static memory guard size cannot be smaller than dynamic memory guard size");
+        }
+        Ok(tunables)
     }
 
     #[cfg(feature = "runtime")]
-    pub(crate) fn build_allocator(&self) -> Result<Box<dyn InstanceAllocator + Send + Sync>> {
+    pub(crate) fn build_allocator(
+        &self,
+        tunables: &Tunables,
+    ) -> Result<Box<dyn InstanceAllocator + Send + Sync>> {
         #[cfg(feature = "async")]
         let stack_size = self.async_stack_size;
 
         #[cfg(not(feature = "async"))]
         let stack_size = 0;
 
+        let _ = tunables;
+
         match &self.allocation_strategy {
             InstanceAllocationStrategy::OnDemand => {
                 #[allow(unused_mut)]
@@ -1642,8 +1706,7 @@ impl Config {
                 let mut config = config.config;
                 config.stack_size = stack_size;
                 Ok(Box::new(wasmtime_runtime::PoolingInstanceAllocator::new(
-                    &config,
-                    &self.tunables,
+                    &config, tunables,
                 )?))
             }
         }
@@ -1660,7 +1723,10 @@ impl Config {
     }
 
     #[cfg(any(feature = "cranelift", feature = "winch"))]
-    pub(crate) fn build_compiler(mut self) -> Result<(Self, Box<dyn wasmtime_environ::Compiler>)> {
+    pub(crate) fn build_compiler(
+        mut self,
+        tunables: &Tunables,
+    ) -> Result<(Self, Box<dyn wasmtime_environ::Compiler>)> {
         let target = self.compiler_config.target.clone();
 
         let mut compiler = match self.compiler_config.strategy {
@@ -1767,7 +1833,7 @@ impl Config {
             compiler.enable_incremental_compilation(cache_store.clone())?;
         }
 
-        compiler.set_tunables(self.tunables.clone())?;
+        compiler.set_tunables(tunables.clone())?;
         compiler.wmemcheck(self.compiler_config.wmemcheck);
 
         Ok((self, compiler.build()?))
@@ -1778,7 +1844,7 @@ impl Config {
     /// then are necessary.
     #[cfg(feature = "component-model")]
     pub fn debug_adapter_modules(&mut self, debug: bool) -> &mut Self {
-        self.tunables.debug_adapter_modules = debug;
+        self.tunables.debug_adapter_modules = Some(debug);
         self
     }
 
@@ -1849,7 +1915,6 @@ impl fmt::Debug for Config {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         let mut f = f.debug_struct("Config");
         f.field("debug_info", &self.tunables.generate_native_debuginfo)
-            .field("parse_wasm_debuginfo", &self.tunables.parse_wasm_debuginfo)
             .field("wasm_threads", &self.features.threads)
             .field("wasm_reference_types", &self.features.reference_types)
             .field(
@@ -1860,28 +1925,30 @@ impl fmt::Debug for Config {
             .field("wasm_simd", &self.features.simd)
             .field("wasm_relaxed_simd", &self.features.relaxed_simd)
             .field("wasm_multi_value", &self.features.multi_value)
-            .field(
-                "static_memory_maximum_size",
-                &(u64::from(self.tunables.static_memory_bound)
-                    * u64::from(wasmtime_environ::WASM_PAGE_SIZE)),
-            )
-            .field(
-                "static_memory_guard_size",
-                &self.tunables.static_memory_offset_guard_size,
-            )
-            .field(
-                "dynamic_memory_guard_size",
-                &self.tunables.dynamic_memory_offset_guard_size,
-            )
-            .field(
-                "guard_before_linear_memory",
-                &self.tunables.guard_before_linear_memory,
-            )
             .field("parallel_compilation", &self.parallel_compilation);
         #[cfg(any(feature = "cranelift", feature = "winch"))]
         {
             f.field("compiler_config", &self.compiler_config);
         }
+
+        if let Some(enable) = self.tunables.parse_wasm_debuginfo {
+            f.field("parse_wasm_debuginfo", &enable);
+        }
+        if let Some(size) = self.tunables.static_memory_bound {
+            f.field(
+                "static_memory_maximum_size",
+                &(u64::from(size) * u64::from(wasmtime_environ::WASM_PAGE_SIZE)),
+            );
+        }
+        if let Some(size) = self.tunables.static_memory_offset_guard_size {
+            f.field("static_memory_guard_size", &size);
+        }
+        if let Some(size) = self.tunables.dynamic_memory_offset_guard_size {
+            f.field("dynamic_memory_guard_size", &size);
+        }
+        if let Some(enable) = self.tunables.guard_before_linear_memory {
+            f.field("guard_before_linear_memory", &enable);
+        }
         f.finish()
     }
 }
diff --git a/crates/wasmtime/src/engine.rs b/crates/wasmtime/src/engine.rs
index 567838f2f..2a6aba471 100644
--- a/crates/wasmtime/src/engine.rs
+++ b/crates/wasmtime/src/engine.rs
@@ -9,7 +9,7 @@ use std::path::Path;
 use std::sync::atomic::{AtomicU64, Ordering};
 use std::sync::Arc;
 use wasmtime_environ::obj;
-use wasmtime_environ::{FlagValue, ObjectKind};
+use wasmtime_environ::{FlagValue, ObjectKind, Tunables};
 
 mod serialization;
 
@@ -37,27 +37,28 @@ mod serialization;
 /// default settings.
 #[derive(Clone)]
 pub struct Engine {
-    pub(crate) inner: Arc<EngineInner>,
+    inner: Arc<EngineInner>,
 }
 
-pub(crate) struct EngineInner {
-    pub(crate) config: Config,
+struct EngineInner {
+    config: Config,
+    tunables: Tunables,
     #[cfg(any(feature = "cranelift", feature = "winch"))]
-    pub(crate) compiler: Box<dyn wasmtime_environ::Compiler>,
+    compiler: Box<dyn wasmtime_environ::Compiler>,
     #[cfg(feature = "runtime")]
-    pub(crate) allocator: Box<dyn wasmtime_runtime::InstanceAllocator + Send + Sync>,
+    allocator: Box<dyn wasmtime_runtime::InstanceAllocator + Send + Sync>,
     #[cfg(feature = "runtime")]
-    pub(crate) profiler: Box<dyn crate::profiling_agent::ProfilingAgent>,
+    profiler: Box<dyn crate::profiling_agent::ProfilingAgent>,
     #[cfg(feature = "runtime")]
-    pub(crate) signatures: TypeRegistry,
+    signatures: TypeRegistry,
     #[cfg(feature = "runtime")]
-    pub(crate) epoch: AtomicU64,
+    epoch: AtomicU64,
     #[cfg(feature = "runtime")]
-    pub(crate) unique_id_allocator: wasmtime_runtime::CompiledModuleIdAllocator,
+    unique_id_allocator: wasmtime_runtime::CompiledModuleIdAllocator,
 
     /// One-time check of whether the compiler's settings, if present, are
     /// compatible with the native host.
-    pub(crate) compatible_with_native_host: OnceCell<Result<(), String>>,
+    compatible_with_native_host: OnceCell<Result<(), String>>,
 }
 
 impl Default for Engine {
@@ -94,17 +95,17 @@ impl Engine {
         }
 
         let config = config.clone();
-        config.validate()?;
+        let tunables = config.validate()?;
 
         #[cfg(any(feature = "cranelift", feature = "winch"))]
-        let (config, compiler) = config.build_compiler()?;
+        let (config, compiler) = config.build_compiler(&tunables)?;
 
         Ok(Engine {
             inner: Arc::new(EngineInner {
                 #[cfg(any(feature = "cranelift", feature = "winch"))]
                 compiler,
                 #[cfg(feature = "runtime")]
-                allocator: config.build_allocator()?,
+                allocator: config.build_allocator(&tunables)?,
                 #[cfg(feature = "runtime")]
                 profiler: config.build_profiler()?,
                 #[cfg(feature = "runtime")]
@@ -115,6 +116,7 @@ impl Engine {
                 unique_id_allocator: wasmtime_runtime::CompiledModuleIdAllocator::new(),
                 compatible_with_native_host: OnceCell::new(),
                 config,
+                tunables,
             }),
         })
     }
@@ -161,6 +163,10 @@ impl Engine {
         }
     }
 
+    pub(crate) fn tunables(&self) -> &Tunables {
+        &self.inner.tunables
+    }
+
     /// Returns whether the engine `a` and `b` refer to the same configuration.
     pub fn same(a: &Engine, b: &Engine) -> bool {
         Arc::ptr_eq(&a.inner, &b.inner)
diff --git a/crates/wasmtime/src/engine/serialization.rs b/crates/wasmtime/src/engine/serialization.rs
index 410776d45..f4063bd6e 100644
--- a/crates/wasmtime/src/engine/serialization.rs
+++ b/crates/wasmtime/src/engine/serialization.rs
@@ -231,7 +231,7 @@ impl Metadata<'_> {
             target: engine.compiler().triple().to_string(),
             shared_flags: engine.compiler().flags(),
             isa_flags: engine.compiler().isa_flags(),
-            tunables: engine.config().tunables.clone(),
+            tunables: engine.tunables().clone(),
             features: WasmFeatures {
                 reference_types,
                 multi_value,
@@ -254,7 +254,7 @@ impl Metadata<'_> {
         self.check_triple(engine)?;
         self.check_shared_flags(engine)?;
         self.check_isa_flags(engine)?;
-        self.check_tunables(&engine.config().tunables)?;
+        self.check_tunables(&engine.tunables())?;
         self.check_features(&engine.config().features)?;
         Ok(())
     }
diff --git a/crates/wasmtime/src/runtime/memory.rs b/crates/wasmtime/src/runtime/memory.rs
index d79736958..0a625ee21 100644
--- a/crates/wasmtime/src/runtime/memory.rs
+++ b/crates/wasmtime/src/runtime/memory.rs
@@ -732,7 +732,7 @@ impl SharedMemory {
         }
         debug_assert!(ty.maximum().is_some());
 
-        let tunables = &engine.config().tunables;
+        let tunables = engine.tunables();
         let plan = MemoryPlan::for_memory(ty.wasmtime_memory().clone(), tunables);
         let memory = wasmtime_runtime::SharedMemory::new(plan)?;
         Ok(Self(memory, engine.clone()))
diff --git a/crates/wasmtime/src/runtime/module.rs b/crates/wasmtime/src/runtime/module.rs
index 564bacd21..94c9470b6 100644
--- a/crates/wasmtime/src/runtime/module.rs
+++ b/crates/wasmtime/src/runtime/module.rs
@@ -1105,7 +1105,7 @@ impl std::hash::Hash for HashedEngineCompileEnv<'_> {
 
         // Hash configuration state read for compilation
         let config = self.0.config();
-        config.tunables.hash(hasher);
+        self.0.tunables().hash(hasher);
         config.features.hash(hasher);
         config.wmemcheck.hash(hasher);
 
diff --git a/crates/wasmtime/src/runtime/store.rs b/crates/wasmtime/src/runtime/store.rs
index 5a5c1e94e..631f49b3b 100644
--- a/crates/wasmtime/src/runtime/store.rs
+++ b/crates/wasmtime/src/runtime/store.rs
@@ -1390,7 +1390,7 @@ impl StoreOpaque {
 
     pub fn get_fuel(&self) -> Result<u64> {
         anyhow::ensure!(
-            self.engine().config().tunables.consume_fuel,
+            self.engine().tunables().consume_fuel,
             "fuel is not configured in this store"
         );
         let injected_fuel = unsafe { *self.runtime_limits.fuel_consumed.get() };
@@ -1408,7 +1408,7 @@ impl StoreOpaque {
 
     pub fn set_fuel(&mut self, fuel: u64) -> Result<()> {
         anyhow::ensure!(
-            self.engine().config().tunables.consume_fuel,
+            self.engine().tunables().consume_fuel,
             "fuel is not configured in this store"
         );
         let injected_fuel = unsafe { &mut *self.runtime_limits.fuel_consumed.get() };
@@ -1423,7 +1423,7 @@ impl StoreOpaque {
 
     pub fn fuel_async_yield_interval(&mut self, interval: Option<u64>) -> Result<()> {
         anyhow::ensure!(
-            self.engine().config().tunables.consume_fuel,
+            self.engine().tunables().consume_fuel,
             "fuel is not configured in this store"
         );
         anyhow::ensure!(
diff --git a/crates/wasmtime/src/runtime/trampoline/func.rs b/crates/wasmtime/src/runtime/trampoline/func.rs
index ab90d5dcf..c6c0f49fb 100644
--- a/crates/wasmtime/src/runtime/trampoline/func.rs
+++ b/crates/wasmtime/src/runtime/trampoline/func.rs
@@ -94,10 +94,7 @@ where
             &mut obj,
         )?;
     engine.append_bti(&mut obj);
-    let obj = finish_object(wasmtime_environ::ObjectBuilder::new(
-        obj,
-        &engine.config().tunables,
-    ))?;
+    let obj = finish_object(wasmtime_environ::ObjectBuilder::new(obj, engine.tunables()))?;
 
     // Copy the results of JIT compilation into executable memory, and this will
     // also take care of unwind table registration.
diff --git a/crates/wasmtime/src/runtime/trampoline/memory.rs b/crates/wasmtime/src/runtime/trampoline/memory.rs
index 72c7eb891..9d2583757 100644
--- a/crates/wasmtime/src/runtime/trampoline/memory.rs
+++ b/crates/wasmtime/src/runtime/trampoline/memory.rs
@@ -40,7 +40,7 @@ pub fn create_memory(
     // preallocated (i.e., shared memory) or allocated manually below.
     let plan = wasmtime_environ::MemoryPlan::for_memory(
         memory_ty.wasmtime_memory().clone(),
-        &store.engine().config().tunables,
+        store.engine().tunables(),
     );
     let memory_id = module.memory_plans.push(plan.clone());
 
diff --git a/crates/wasmtime/src/runtime/trampoline/table.rs b/crates/wasmtime/src/runtime/trampoline/table.rs
index f351a2609..b6034dc3e 100644
--- a/crates/wasmtime/src/runtime/trampoline/table.rs
+++ b/crates/wasmtime/src/runtime/trampoline/table.rs
@@ -8,7 +8,7 @@ pub fn create_table(store: &mut StoreOpaque, table: &TableType) -> Result<Instan
     let mut module = Module::new();
     let table_plan = wasmtime_environ::TablePlan::for_table(
         table.wasmtime_table().clone(),
-        &store.engine().config().tunables,
+        store.engine().tunables(),
     );
     let table_id = module.table_plans.push(table_plan);
     // TODO: can this `exports.insert` get removed?
diff --git a/winch/src/compile.rs b/winch/src/compile.rs
index ec2b12acf..276055820 100644
--- a/winch/src/compile.rs
+++ b/winch/src/compile.rs
@@ -32,7 +32,7 @@ pub fn run(opt: &Options) -> Result<()> {
     let mut validator = Validator::new();
     let parser = WasmParser::new(0);
     let mut types = Default::default();
-    let tunables = Tunables::default();
+    let tunables = Tunables::default_host();
     let mut translation = ModuleEnvironment::new(&tunables, &mut validator, &mut types)
         .translate(parser, &bytes)
         .context("Failed to translate WebAssembly module")?;


Test patch:
diff --git a/winch/filetests/src/lib.rs b/winch/filetests/src/lib.rs
index 68e5a6b7a..85606d1d2 100644
--- a/winch/filetests/src/lib.rs
+++ b/winch/filetests/src/lib.rs
@@ -104,7 +104,7 @@ mod test {
         let mut validator = Validator::new();
         let parser = WasmParser::new(0);
         let mut types = Default::default();
-        let tunables = Tunables::default();
+        let tunables = Tunables::default_host();
         let mut translation = ModuleEnvironment::new(&tunables, &mut validator, &mut types)
             .translate(parser, &wasm)
             .context("Failed to translate WebAssembly module")