Path: blob/main/crates/environ/src/tunables.rs
1692 views
use crate::{IndexType, Limits, Memory, TripleExt};1use anyhow::{Error, Result, anyhow, bail};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 generate_native_debuginfo: bool,7778/// Whether or not to retain DWARF sections in compiled modules.79pub parse_wasm_debuginfo: bool,8081/// Whether or not fuel is enabled for generated code, meaning that fuel82/// will be consumed every time a wasm instruction is executed.83pub consume_fuel: bool,8485/// Whether or not we use epoch-based interruption.86pub epoch_interruption: bool,8788/// Whether or not linear memories are allowed to be reallocated after89/// initial allocation at runtime.90pub memory_may_move: bool,9192/// Whether or not linear memory allocations will have a guard region at the93/// beginning of the allocation in addition to the end.94pub guard_before_linear_memory: bool,9596/// Whether to initialize tables lazily, so that instantiation is fast but97/// indirect calls are a little slower. If false, tables are initialized98/// eagerly from any active element segments that apply to them during99/// instantiation.100pub table_lazy_init: bool,101102/// Indicates whether an address map from compiled native code back to wasm103/// offsets in the original file is generated.104pub generate_address_map: bool,105106/// Flag for the component module whether adapter modules have debug107/// assertions baked into them.108pub debug_adapter_modules: bool,109110/// Whether or not lowerings for relaxed simd instructions are forced to111/// be deterministic.112pub relaxed_simd_deterministic: bool,113114/// Whether or not Wasm functions target the winch abi.115pub winch_callable: bool,116117/// Whether or not the host will be using native signals (e.g. SIGILL,118/// SIGSEGV, etc) to implement traps.119pub signals_based_traps: bool,120121/// Whether CoW images might be used to initialize linear memories.122pub memory_init_cow: bool,123124/// Whether to enable inlining in Wasmtime's compilation orchestration125/// or not.126pub inlining: bool,127128/// Whether to inline calls within the same core Wasm module or not.129pub inlining_intra_module: IntraModuleInlining,130131/// The size of "small callees" that can be inlined regardless of the132/// caller's size.133pub inlining_small_callee_size: u32,134135/// The general size threshold for the sum of the caller's and callee's136/// sizes, past which we will generally not inline calls anymore.137pub inlining_sum_size_threshold: u32,138}139140pub struct ConfigTunables {141...142}143}144145impl Tunables {146/// Returns a `Tunables` configuration assumed for running code on the host.147pub fn default_host() -> Self {148if cfg!(miri) {149Tunables::default_miri()150} else if cfg!(target_pointer_width = "32") {151Tunables::default_u32()152} else if cfg!(target_pointer_width = "64") {153Tunables::default_u64()154} else {155panic!("unsupported target_pointer_width");156}157}158159/// Returns the default set of tunables for the given target triple.160pub fn default_for_target(target: &Triple) -> Result<Self> {161if cfg!(miri) {162return Ok(Tunables::default_miri());163}164let mut ret = match target165.pointer_width()166.map_err(|_| anyhow!("failed to retrieve target pointer width"))?167{168PointerWidth::U32 => Tunables::default_u32(),169PointerWidth::U64 => Tunables::default_u64(),170_ => bail!("unsupported target pointer width"),171};172173// Pulley targets never use signals-based-traps and also can't benefit174// from guard pages, so disable them.175if target.is_pulley() {176ret.signals_based_traps = false;177ret.memory_guard_size = 0;178}179Ok(ret)180}181182/// Returns the default set of tunables for running under MIRI.183pub const fn default_miri() -> Tunables {184Tunables {185collector: None,186187// No virtual memory tricks are available on miri so make these188// limits quite conservative.189memory_reservation: 1 << 20,190memory_guard_size: 0,191memory_reservation_for_growth: 0,192193// General options which have the same defaults regardless of194// architecture.195generate_native_debuginfo: false,196parse_wasm_debuginfo: true,197consume_fuel: false,198epoch_interruption: false,199memory_may_move: true,200guard_before_linear_memory: true,201table_lazy_init: true,202generate_address_map: true,203debug_adapter_modules: false,204relaxed_simd_deterministic: false,205winch_callable: false,206signals_based_traps: false,207memory_init_cow: true,208inlining: false,209inlining_intra_module: IntraModuleInlining::WhenUsingGc,210inlining_small_callee_size: 50,211inlining_sum_size_threshold: 2000,212}213}214215/// Returns the default set of tunables for running under a 32-bit host.216pub const fn default_u32() -> Tunables {217Tunables {218// For 32-bit we scale way down to 10MB of reserved memory. This219// impacts performance severely but allows us to have more than a220// few instances running around.221memory_reservation: 10 * (1 << 20),222memory_guard_size: 0x1_0000,223memory_reservation_for_growth: 1 << 20, // 1MB224signals_based_traps: true,225226..Tunables::default_miri()227}228}229230/// Returns the default set of tunables for running under a 64-bit host.231pub const fn default_u64() -> Tunables {232Tunables {233// 64-bit has tons of address space to static memories can have 4gb234// address space reservations liberally by default, allowing us to235// help eliminate bounds checks.236//237// A 32MiB default guard size is then allocated so we can remove238// explicit bounds checks if any static offset is less than this239// value. SpiderMonkey found, for example, that in a large corpus of240// wasm modules 20MiB was the maximum offset so this is the241// power-of-two-rounded up from that and matches SpiderMonkey.242memory_reservation: 1 << 32,243memory_guard_size: 32 << 20,244245// We've got lots of address space on 64-bit so use a larger246// grow-into-this area, but on 32-bit we aren't as lucky. Miri is247// not exactly fast so reduce memory consumption instead of trying248// to avoid memory movement.249memory_reservation_for_growth: 2 << 30, // 2GB250251signals_based_traps: true,252..Tunables::default_miri()253}254}255256/// Get the GC heap's memory type, given our configured tunables.257pub fn gc_heap_memory_type(&self) -> Memory {258Memory {259idx_type: IndexType::I32,260limits: Limits { min: 0, max: None },261shared: false,262// We *could* try to match the target architecture's page size, but that263// would require exercising a page size for memories that we don't264// otherwise support for Wasm; we conservatively avoid that, and just265// use the default Wasm page size, for now.266page_size_log2: 16,267}268}269}270271/// The garbage collector implementation to use.272#[derive(Clone, Copy, Hash, Serialize, Deserialize, Debug, PartialEq, Eq)]273pub enum Collector {274/// The deferred reference-counting collector.275DeferredReferenceCounting,276/// The null collector.277Null,278}279280impl fmt::Display for Collector {281fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {282match self {283Collector::DeferredReferenceCounting => write!(f, "deferred reference-counting"),284Collector::Null => write!(f, "null"),285}286}287}288289/// Whether to inline function calls within the same module.290#[derive(Clone, Copy, Hash, Serialize, Deserialize, Debug, PartialEq, Eq)]291#[expect(missing_docs, reason = "self-describing variants")]292pub enum IntraModuleInlining {293Yes,294No,295WhenUsingGc,296}297298impl FromStr for IntraModuleInlining {299type Err = Error;300301fn from_str(s: &str) -> Result<Self, Self::Err> {302match s {303"y" | "yes" | "true" => Ok(Self::Yes),304"n" | "no" | "false" => Ok(Self::No),305"gc" => Ok(Self::WhenUsingGc),306_ => bail!(307"invalid intra-module inlining option string: `{s}`, \308only yes,no,gc accepted"309),310}311}312}313314315