Path: blob/main/crates/environ/src/tunables.rs
3068 views
use crate::prelude::*;1use crate::{IndexType, Limits, Memory, TripleExt};2use core::{fmt, str::FromStr};3use serde_derive::{Deserialize, Serialize};4use target_lexicon::{PointerWidth, Triple};56macro_rules! define_tunables {7(8$(#[$outer_attr:meta])*9pub struct $tunables:ident {10$(11$(#[$field_attr:meta])*12pub $field:ident : $field_ty:ty,13)*14}1516pub struct $config_tunables:ident {17...18}19) => {20$(#[$outer_attr])*21pub struct $tunables {22$(23$(#[$field_attr])*24pub $field: $field_ty,25)*26}2728/// Optional tunable configuration options used in `wasmtime::Config`29#[derive(Default, Clone)]30#[expect(missing_docs, reason = "macro-generated fields")]31pub struct $config_tunables {32$(pub $field: Option<$field_ty>,)*33}3435impl $config_tunables {36/// Formats configured fields into `f`.37pub fn format(&self, f: &mut fmt::DebugStruct<'_,'_>) {38$(39if let Some(val) = &self.$field {40f.field(stringify!($field), val);41}42)*43}4445/// Configure the `Tunables` provided.46pub fn configure(&self, tunables: &mut Tunables) {47$(48if let Some(val) = self.$field {49tunables.$field = val;50}51)*52}53}54};55}5657define_tunables! {58/// Tunable parameters for WebAssembly compilation.59#[derive(Clone, Hash, Serialize, Deserialize, Debug)]60pub struct Tunables {61/// The garbage collector implementation to use, which implies the layout of62/// GC objects and barriers that must be emitted in Wasm code.63pub collector: Option<Collector>,6465/// Initial size, in bytes, to be allocated for linear memories.66pub memory_reservation: u64,6768/// The size, in bytes, of the guard page region for linear memories.69pub memory_guard_size: u64,7071/// The size, in bytes, to allocate at the end of a relocated linear72/// memory for growth.73pub memory_reservation_for_growth: u64,7475/// Whether or not to generate native DWARF debug information.76pub debug_native: bool,7778/// Whether we are enabling precise Wasm-level debugging in79/// the guest.80pub debug_guest: bool,8182/// Whether or not to retain DWARF sections in compiled modules.83pub parse_wasm_debuginfo: bool,8485/// Whether or not fuel is enabled for generated code, meaning that fuel86/// will be consumed every time a wasm instruction is executed.87pub consume_fuel: bool,8889/// Whether or not we use epoch-based interruption.90pub epoch_interruption: bool,9192/// Whether or not linear memories are allowed to be reallocated after93/// initial allocation at runtime.94pub memory_may_move: bool,9596/// Whether or not linear memory allocations will have a guard region at the97/// beginning of the allocation in addition to the end.98pub guard_before_linear_memory: bool,99100/// Whether to initialize tables lazily, so that instantiation is fast but101/// indirect calls are a little slower. If false, tables are initialized102/// eagerly from any active element segments that apply to them during103/// instantiation.104pub table_lazy_init: bool,105106/// Indicates whether an address map from compiled native code back to wasm107/// offsets in the original file is generated.108pub generate_address_map: bool,109110/// Flag for the component module whether adapter modules have debug111/// assertions baked into them.112pub debug_adapter_modules: bool,113114/// Whether or not lowerings for relaxed simd instructions are forced to115/// be deterministic.116pub relaxed_simd_deterministic: bool,117118/// Whether or not Wasm functions target the winch abi.119pub winch_callable: bool,120121/// Whether or not the host will be using native signals (e.g. SIGILL,122/// SIGSEGV, etc) to implement traps.123pub signals_based_traps: bool,124125/// Whether CoW images might be used to initialize linear memories.126pub memory_init_cow: bool,127128/// Whether to enable inlining in Wasmtime's compilation orchestration129/// or not.130pub inlining: bool,131132/// Whether to inline calls within the same core Wasm module or not.133pub inlining_intra_module: IntraModuleInlining,134135/// The size of "small callees" that can be inlined regardless of the136/// caller's size.137pub inlining_small_callee_size: u32,138139/// The general size threshold for the sum of the caller's and callee's140/// sizes, past which we will generally not inline calls anymore.141pub inlining_sum_size_threshold: u32,142143/// Whether any component model feature related to concurrency is144/// enabled.145pub concurrency_support: bool,146}147148pub struct ConfigTunables {149...150}151}152153impl Tunables {154/// Returns a `Tunables` configuration assumed for running code on the host.155pub fn default_host() -> Self {156if cfg!(miri) {157Tunables::default_miri()158} else if cfg!(target_pointer_width = "32") {159Tunables::default_u32()160} else if cfg!(target_pointer_width = "64") {161Tunables::default_u64()162} else {163panic!("unsupported target_pointer_width");164}165}166167/// Returns the default set of tunables for the given target triple.168pub fn default_for_target(target: &Triple) -> Result<Self> {169if cfg!(miri) {170return Ok(Tunables::default_miri());171}172let mut ret = match target173.pointer_width()174.map_err(|_| format_err!("failed to retrieve target pointer width"))?175{176PointerWidth::U32 => Tunables::default_u32(),177PointerWidth::U64 => Tunables::default_u64(),178_ => bail!("unsupported target pointer width"),179};180181// Pulley targets never use signals-based-traps and also can't benefit182// from guard pages, so disable them.183if target.is_pulley() {184ret.signals_based_traps = false;185ret.memory_guard_size = 0;186}187Ok(ret)188}189190/// Returns the default set of tunables for running under MIRI.191pub const fn default_miri() -> Tunables {192Tunables {193collector: None,194195// No virtual memory tricks are available on miri so make these196// limits quite conservative.197memory_reservation: 1 << 20,198memory_guard_size: 0,199memory_reservation_for_growth: 0,200201// General options which have the same defaults regardless of202// architecture.203debug_native: false,204parse_wasm_debuginfo: true,205consume_fuel: false,206epoch_interruption: false,207memory_may_move: true,208guard_before_linear_memory: true,209table_lazy_init: true,210generate_address_map: true,211debug_adapter_modules: false,212relaxed_simd_deterministic: false,213winch_callable: false,214signals_based_traps: false,215memory_init_cow: true,216inlining: false,217inlining_intra_module: IntraModuleInlining::WhenUsingGc,218inlining_small_callee_size: 50,219inlining_sum_size_threshold: 2000,220debug_guest: false,221concurrency_support: true,222}223}224225/// Returns the default set of tunables for running under a 32-bit host.226pub const fn default_u32() -> Tunables {227Tunables {228// For 32-bit we scale way down to 10MB of reserved memory. This229// impacts performance severely but allows us to have more than a230// few instances running around.231memory_reservation: 10 * (1 << 20),232memory_guard_size: 0x1_0000,233memory_reservation_for_growth: 1 << 20, // 1MB234signals_based_traps: true,235236..Tunables::default_miri()237}238}239240/// Returns the default set of tunables for running under a 64-bit host.241pub const fn default_u64() -> Tunables {242Tunables {243// 64-bit has tons of address space to static memories can have 4gb244// address space reservations liberally by default, allowing us to245// help eliminate bounds checks.246//247// A 32MiB default guard size is then allocated so we can remove248// explicit bounds checks if any static offset is less than this249// value. SpiderMonkey found, for example, that in a large corpus of250// wasm modules 20MiB was the maximum offset so this is the251// power-of-two-rounded up from that and matches SpiderMonkey.252memory_reservation: 1 << 32,253memory_guard_size: 32 << 20,254255// We've got lots of address space on 64-bit so use a larger256// grow-into-this area, but on 32-bit we aren't as lucky. Miri is257// not exactly fast so reduce memory consumption instead of trying258// to avoid memory movement.259memory_reservation_for_growth: 2 << 30, // 2GB260261signals_based_traps: true,262..Tunables::default_miri()263}264}265266/// Get the GC heap's memory type, given our configured tunables.267pub fn gc_heap_memory_type(&self) -> Memory {268Memory {269idx_type: IndexType::I32,270limits: Limits { min: 0, max: None },271shared: false,272// We *could* try to match the target architecture's page size, but that273// would require exercising a page size for memories that we don't274// otherwise support for Wasm; we conservatively avoid that, and just275// use the default Wasm page size, for now.276page_size_log2: 16,277}278}279}280281/// The garbage collector implementation to use.282#[derive(Clone, Copy, Hash, Serialize, Deserialize, Debug, PartialEq, Eq)]283pub enum Collector {284/// The deferred reference-counting collector.285DeferredReferenceCounting,286/// The null collector.287Null,288}289290impl fmt::Display for Collector {291fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {292match self {293Collector::DeferredReferenceCounting => write!(f, "deferred reference-counting"),294Collector::Null => write!(f, "null"),295}296}297}298299/// Whether to inline function calls within the same module.300#[derive(Clone, Copy, Hash, Serialize, Deserialize, Debug, PartialEq, Eq)]301#[expect(missing_docs, reason = "self-describing variants")]302pub enum IntraModuleInlining {303Yes,304No,305WhenUsingGc,306}307308impl FromStr for IntraModuleInlining {309type Err = Error;310311fn from_str(s: &str) -> Result<Self, Self::Err> {312match s {313"y" | "yes" | "true" => Ok(Self::Yes),314"n" | "no" | "false" => Ok(Self::No),315"gc" => Ok(Self::WhenUsingGc),316_ => bail!(317"invalid intra-module inlining option string: `{s}`, \318only yes,no,gc accepted"319),320}321}322}323324325