diff --git a/crates/c-api/include/wasmtime/config.h b/crates/c-api/include/wasmtime/config.h index 2130d4ff6c91..21df3271ebec 100644 --- a/crates/c-api/include/wasmtime/config.h +++ b/crates/c-api/include/wasmtime/config.h @@ -316,22 +316,24 @@ WASMTIME_CONFIG_PROP(void, cranelift_opt_level, wasmtime_opt_level_t) WASMTIME_CONFIG_PROP(void, profiler, wasmtime_profiling_strategy_t) /** - * \brief Configures the “static” style of memory to always be used. + * \brief Configures whether `memory_reservation` is the maximal size of linear + * memory. * * This setting is `false` by default. * * For more information see the Rust documentation at - * https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Config.html#method.static_memory_forced. + * https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Config.html#method.memory_may_move. */ -WASMTIME_CONFIG_PROP(void, static_memory_forced, bool) +WASMTIME_CONFIG_PROP(void, memory_may_move, bool) /** - * \brief Configures the maximum size for memory to be considered "static" + * \brief Configures the size, in bytes, of initial memory reservation size for + * linear memories. * * For more information see the Rust documentation at - * https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Config.html#method.static_memory_maximum_size. + * https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Config.html#method.memory_reservation. */ -WASMTIME_CONFIG_PROP(void, static_memory_maximum_size, uint64_t) +WASMTIME_CONFIG_PROP(void, memory_reservation, uint64_t) /** * \brief Configures the guard region size for linear memory. @@ -343,12 +345,12 @@ WASMTIME_CONFIG_PROP(void, memory_guard_size, uint64_t) /** * \brief Configures the size, in bytes, of the extra virtual memory space - * reserved after a “dynamic” memory for growing into. + * reserved for memories to grow into after being relocated. * * For more information see the Rust documentation at - * https://docs.wasmtime.dev/api/wasmtime/struct.Config.html#method.dynamic_memory_reserved_for_growth + * https://docs.wasmtime.dev/api/wasmtime/struct.Config.html#method.memory_reservation_for_growth */ -WASMTIME_CONFIG_PROP(void, dynamic_memory_reserved_for_growth, uint64_t) +WASMTIME_CONFIG_PROP(void, memory_reservation_for_growth, uint64_t) /** * \brief Configures whether to generate native unwind information (e.g. diff --git a/crates/c-api/src/config.rs b/crates/c-api/src/config.rs index f2c149d29076..96dd3384a7a9 100644 --- a/crates/c-api/src/config.rs +++ b/crates/c-api/src/config.rs @@ -222,13 +222,13 @@ pub unsafe extern "C" fn wasmtime_config_cache_config_load( } #[no_mangle] -pub extern "C" fn wasmtime_config_static_memory_forced_set(c: &mut wasm_config_t, enable: bool) { - c.config.static_memory_forced(enable); +pub extern "C" fn wasmtime_config_memory_may_move_set(c: &mut wasm_config_t, enable: bool) { + c.config.memory_may_move(enable); } #[no_mangle] -pub extern "C" fn wasmtime_config_static_memory_maximum_size_set(c: &mut wasm_config_t, size: u64) { - c.config.static_memory_maximum_size(size); +pub extern "C" fn wasmtime_config_memory_reservation_set(c: &mut wasm_config_t, size: u64) { + c.config.memory_reservation(size); } #[no_mangle] @@ -237,11 +237,11 @@ pub extern "C" fn wasmtime_config_memory_guard_size_set(c: &mut wasm_config_t, s } #[no_mangle] -pub extern "C" fn wasmtime_config_dynamic_memory_reserved_for_growth_set( +pub extern "C" fn wasmtime_config_memory_reservation_reserved_for_growth_set( c: &mut wasm_config_t, size: u64, ) { - c.config.dynamic_memory_reserved_for_growth(size); + c.config.memory_reservation_for_growth(size); } #[no_mangle] diff --git a/crates/cli-flags/src/lib.rs b/crates/cli-flags/src/lib.rs index 746302699f97..586e32420d3d 100644 --- a/crates/cli-flags/src/lib.rs +++ b/crates/cli-flags/src/lib.rs @@ -42,20 +42,19 @@ wasmtime_option_group! { /// Optimization level of generated code (0-2, s; default: 2) pub opt_level: Option, - /// Force using a "static" style for all wasm memories - pub static_memory_forced: Option, + /// Do not allow Wasm linear memories to move in the host process's + /// address space. + pub memory_may_move: Option, - /// Maximum size in bytes of wasm memory before it becomes dynamically - /// relocatable instead of up-front-reserved. - pub static_memory_maximum_size: Option, + /// Initial virtual memory allocation size for memories. + pub memory_reservation: Option, + + /// Bytes to reserve at the end of linear memory for growth into. + pub memory_reservation_for_growth: Option, /// Size, in bytes, of guard pages for linear memories. pub memory_guard_size: Option, - /// Bytes to reserve at the end of linear memory for growth for dynamic - /// memories. - pub dynamic_memory_reserved_for_growth: Option, - /// Indicates whether an unmapped region of memory is placed before all /// linear memories. pub guard_before_linear_memory: Option, @@ -170,6 +169,15 @@ wasmtime_option_group! { /// DEPRECATED: Use `-Cmemory-guard-size=N` instead. pub static_memory_guard_size: Option, + + /// DEPRECATED: Use `-Cmemory-may-move` instead. + pub static_memory_forced: Option, + + /// DEPRECATED: Use `-Cmemory-reservation=N` instead. + pub static_memory_maximum_size: Option, + + /// DEPRECATED: Use `-Cmemory-reservation-for-growth=N` instead. + pub dynamic_memory_reserved_for_growth: Option, } enum Optimize { @@ -631,12 +639,19 @@ impl CommonOptions { true => err, } - if let Some(max) = self.opts.static_memory_maximum_size { - config.static_memory_maximum_size(max); + if let Some(max) = self + .opts + .memory_reservation + .or(self.opts.static_memory_maximum_size) + { + config.memory_reservation(max); } if let Some(enable) = self.opts.static_memory_forced { - config.static_memory_forced(enable); + config.memory_may_move(!enable); + } + if let Some(enable) = self.opts.memory_may_move { + config.memory_may_move(enable); } if let Some(size) = self @@ -648,8 +663,12 @@ impl CommonOptions { config.memory_guard_size(size); } - if let Some(size) = self.opts.dynamic_memory_reserved_for_growth { - config.dynamic_memory_reserved_for_growth(size); + if let Some(size) = self + .opts + .memory_reservation_for_growth + .or(self.opts.dynamic_memory_reserved_for_growth) + { + config.memory_reservation_for_growth(size); } if let Some(enable) = self.opts.guard_before_linear_memory { config.guard_before_linear_memory(enable); diff --git a/crates/environ/src/module.rs b/crates/environ/src/module.rs index 176e9dd30f1f..18cb2db05b43 100644 --- a/crates/environ/src/module.rs +++ b/crates/environ/src/module.rs @@ -35,28 +35,29 @@ impl MemoryStyle { && tunables.signals_based_traps && match memory.maximum_byte_size() { Ok(mut maximum) => { - if tunables.static_memory_bound_is_maximum { - maximum = maximum.min(tunables.static_memory_reservation); + if !tunables.memory_may_move { + maximum = maximum.min(tunables.memory_reservation); } - // Ensure the minimum is less than the maximum; the minimum might exceed the maximum - // when the memory is artificially bounded via `static_memory_bound_is_maximum` above + // Ensure the minimum is less than the maximum; the minimum + // might exceed the maximum when the memory is artificially + // bounded via `memory_may_move` above memory.minimum_byte_size().unwrap() <= maximum - && maximum <= tunables.static_memory_reservation + && maximum <= tunables.memory_reservation } // If the maximum size of this memory is not representable with - // `u64` then use the `static_memory_bound_is_maximum` to indicate - // whether it's a static memory or not. It should be ok to discard - // the linear memory's maximum size here as growth to the maximum is - // always fallible and never guaranteed. - Err(_) => tunables.static_memory_bound_is_maximum, + // `u64` then use the `memory_may_move` to indicate whether + // it's a static memory or not. It should be ok to discard the + // linear memory's maximum size here as growth to the maximum + // is always fallible and never guaranteed. + Err(_) => !tunables.memory_may_move, }; if is_static { return ( Self::Static { - byte_reservation: tunables.static_memory_reservation, + byte_reservation: tunables.memory_reservation, }, tunables.memory_guard_size, ); @@ -65,7 +66,7 @@ impl MemoryStyle { // Otherwise, make it dynamic. ( Self::Dynamic { - reserve: tunables.dynamic_memory_growth_reserve, + reserve: tunables.memory_reservation_for_growth, }, tunables.memory_guard_size, ) diff --git a/crates/environ/src/tunables.rs b/crates/environ/src/tunables.rs index ca46268c3fb6..63268ae1cd71 100644 --- a/crates/environ/src/tunables.rs +++ b/crates/environ/src/tunables.rs @@ -62,18 +62,15 @@ define_tunables! { /// GC objects and barriers that must be emitted in Wasm code. pub collector: Option, - /// For static heaps, the size in bytes of virtual memory reservation for - /// the heap. - pub static_memory_reservation: u64, + /// Initial size, in bytes, to be allocated for linear memories. + pub memory_reservation: u64, /// The size, in bytes, of the guard page region for linear memories. pub memory_guard_size: u64, - /// The size, in bytes, of reserved memory at the end of a "dynamic" memory, - /// before the guard page, that memory can grow into. This is intended to - /// amortize the cost of `memory.grow` in the same manner that `Vec` has - /// space not in use to grow into. - pub dynamic_memory_growth_reserve: u64, + /// The size, in bytes, to allocate at the end of a relocated linear + /// memory for growth. + pub memory_reservation_for_growth: u64, /// Whether or not to generate native DWARF debug information. pub generate_native_debuginfo: bool, @@ -88,9 +85,9 @@ define_tunables! { /// Whether or not we use epoch-based interruption. pub epoch_interruption: bool, - /// Whether or not to treat the static memory bound as the maximum for - /// unbounded heaps. - pub static_memory_bound_is_maximum: bool, + /// Whether or not linear memories are allowed to be reallocated after + /// initial allocation at runtime. + pub memory_may_move: bool, /// Whether or not linear memory allocations will have a guard region at the /// beginning of the allocation in addition to the end. @@ -160,9 +157,9 @@ impl Tunables { // No virtual memory tricks are available on miri so make these // limits quite conservative. - static_memory_reservation: 1 << 20, + memory_reservation: 1 << 20, memory_guard_size: 0, - dynamic_memory_growth_reserve: 0, + memory_reservation_for_growth: 0, // General options which have the same defaults regardless of // architecture. @@ -170,7 +167,7 @@ impl Tunables { parse_wasm_debuginfo: true, consume_fuel: false, epoch_interruption: false, - static_memory_bound_is_maximum: false, + memory_may_move: true, guard_before_linear_memory: true, table_lazy_init: true, generate_address_map: true, @@ -187,9 +184,9 @@ impl 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_reservation: 10 * (1 << 20), + memory_reservation: 10 * (1 << 20), memory_guard_size: 0x1_0000, - dynamic_memory_growth_reserve: 1 << 20, // 1MB + memory_reservation_for_growth: 1 << 20, // 1MB ..Tunables::default_miri() } @@ -204,14 +201,14 @@ impl Tunables { // // Coupled with a 2 GiB address space guard it lets us translate // wasm offsets into x86 offsets as aggressively as we can. - static_memory_reservation: 1 << 32, + memory_reservation: 1 << 32, memory_guard_size: 0x8000_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: 2 << 30, // 2GB + memory_reservation_for_growth: 2 << 30, // 2GB ..Tunables::default_miri() } diff --git a/crates/fuzzing/src/generators/config.rs b/crates/fuzzing/src/generators/config.rs index 1f79cda5a473..d14e34cf923f 100644 --- a/crates/fuzzing/src/generators/config.rs +++ b/crates/fuzzing/src/generators/config.rs @@ -92,7 +92,7 @@ impl Config { pooling.core_instance_size = 1_000_000; if let MemoryConfig::Normal(cfg) = &mut self.wasmtime.memory_config { - match &mut cfg.static_memory_maximum_size { + match &mut cfg.memory_reservation { Some(size) => *size = (*size).max(pooling.max_memory_size as u64), other @ None => *other = Some(pooling.max_memory_size as u64), } @@ -266,9 +266,9 @@ impl Config { // supported when bounds checks are elided. let memory_config = if pcc { MemoryConfig::Normal(NormalMemoryConfig { - static_memory_maximum_size: Some(4 << 30), // 4 GiB - memory_guard_size: Some(2 << 30), // 2 GiB - dynamic_memory_reserved_for_growth: Some(0), + memory_reservation: Some(4 << 30), // 4 GiB + memory_guard_size: Some(2 << 30), // 2 GiB + memory_reservation_for_growth: Some(0), guard_before_linear_memory: false, memory_init_cow: true, // Doesn't matter, only using virtual memory. @@ -284,9 +284,9 @@ impl Config { } MemoryConfig::CustomUnaligned => { cfg.with_host_memory(Arc::new(UnalignedMemoryCreator)) - .static_memory_maximum_size(0) + .memory_reservation(0) .memory_guard_size(0) - .dynamic_memory_reserved_for_growth(0) + .memory_reservation_for_growth(0) .guard_before_linear_memory(false) .memory_init_cow(false); } @@ -519,7 +519,7 @@ impl WasmtimeConfig { .min(config.max_memory64_bytes.try_into().unwrap_or(u64::MAX)); let mut min = min_bytes.min(pooling.max_memory_size as u64); if let MemoryConfig::Normal(cfg) = &self.memory_config { - min = min.min(cfg.static_memory_maximum_size.unwrap_or(0)); + min = min.min(cfg.memory_reservation.unwrap_or(0)); } pooling.max_memory_size = min as usize; config.max_memory32_bytes = min; @@ -534,7 +534,7 @@ impl WasmtimeConfig { config.max_memory32_bytes = 1 << 16; config.max_memory64_bytes = 1 << 16; if let MemoryConfig::Normal(cfg) = &mut self.memory_config { - match &mut cfg.static_memory_maximum_size { + match &mut cfg.memory_reservation { Some(size) => *size = (*size).max(pooling.max_memory_size as u64), size @ None => *size = Some(pooling.max_memory_size as u64), } diff --git a/crates/fuzzing/src/generators/memory.rs b/crates/fuzzing/src/generators/memory.rs index d6eb7554f3bf..a5f542875a6e 100644 --- a/crates/fuzzing/src/generators/memory.rs +++ b/crates/fuzzing/src/generators/memory.rs @@ -135,9 +135,9 @@ pub enum MemoryConfig { #[derive(Clone, Debug, Eq, Hash, PartialEq)] #[allow(missing_docs)] pub struct NormalMemoryConfig { - pub static_memory_maximum_size: Option, + pub memory_reservation: Option, pub memory_guard_size: Option, - pub dynamic_memory_reserved_for_growth: Option, + pub memory_reservation_for_growth: Option, pub guard_before_linear_memory: bool, pub cranelift_enable_heap_access_spectre_mitigations: Option, pub memory_init_cow: bool, @@ -148,9 +148,9 @@ impl<'a> Arbitrary<'a> for NormalMemoryConfig { // This attempts to limit memory and guard sizes to 32-bit ranges so // we don't exhaust a 64-bit address space easily. Ok(Self { - static_memory_maximum_size: as Arbitrary>::arbitrary(u)?.map(Into::into), + memory_reservation: as Arbitrary>::arbitrary(u)?.map(Into::into), memory_guard_size: as Arbitrary>::arbitrary(u)?.map(Into::into), - dynamic_memory_reserved_for_growth: as Arbitrary>::arbitrary(u)? + memory_reservation_for_growth: as Arbitrary>::arbitrary(u)? .map(Into::into), guard_before_linear_memory: u.arbitrary()?, cranelift_enable_heap_access_spectre_mitigations: u.arbitrary()?, @@ -163,11 +163,9 @@ impl NormalMemoryConfig { /// Apply this memory configuration to the given `wasmtime::Config`. pub fn apply_to(&self, config: &mut wasmtime::Config) { config - .static_memory_maximum_size(self.static_memory_maximum_size.unwrap_or(0)) + .memory_reservation(self.memory_reservation.unwrap_or(0)) .memory_guard_size(self.memory_guard_size.unwrap_or(0)) - .dynamic_memory_reserved_for_growth( - self.dynamic_memory_reserved_for_growth.unwrap_or(0), - ) + .memory_reservation_for_growth(self.memory_reservation_for_growth.unwrap_or(0)) .guard_before_linear_memory(self.guard_before_linear_memory) .memory_init_cow(self.memory_init_cow); diff --git a/crates/misc/component-test-util/src/lib.rs b/crates/misc/component-test-util/src/lib.rs index c5f42b874f1c..eddb7654722f 100644 --- a/crates/misc/component-test-util/src/lib.rs +++ b/crates/misc/component-test-util/src/lib.rs @@ -55,7 +55,7 @@ pub fn config() -> Config { // component model tests create a disproportionate number of instances so // try to cut down on virtual memory usage by avoiding 4G reservations. if std::env::var("WASMTIME_TEST_NO_HOG_MEMORY").is_ok() { - config.static_memory_maximum_size(0); + config.memory_reservation(0); config.memory_guard_size(0); } config diff --git a/crates/wasmtime/src/config.rs b/crates/wasmtime/src/config.rs index 62f24389cc21..4c0de5b0e810 100644 --- a/crates/wasmtime/src/config.rs +++ b/crates/wasmtime/src/config.rs @@ -1010,7 +1010,7 @@ impl Config { /// type. /// /// This is part of the transition plan in - /// https://github.com/WebAssembly/component-model/issues/370. + /// . #[cfg(feature = "component-model")] pub fn wasm_component_model_more_flags(&mut self, enable: bool) -> &mut Self { self.wasm_feature(WasmFeatures::COMPONENT_MODEL_MORE_FLAGS, enable); @@ -1020,7 +1020,7 @@ impl Config { /// Configures whether components support more than one return value for functions. /// /// This is part of the transition plan in - /// https://github.com/WebAssembly/component-model/pull/368. + /// . #[cfg(feature = "component-model")] pub fn wasm_component_model_multiple_returns(&mut self, enable: bool) -> &mut Self { self.wasm_feature(WasmFeatures::COMPONENT_MODEL_MULTIPLE_RETURNS, enable); @@ -1291,11 +1291,8 @@ impl Config { /// Sets the instance allocation strategy to use. /// - /// When using the pooling instance allocation strategy, all linear memories - /// will be created as "static" and the - /// [`Config::static_memory_maximum_size`] and - /// [`Config::memory_guard_size`] options will be used to configure - /// the virtual memory allocations of linear memories. + /// This is notably used in conjunction with + /// [`InstanceAllocationStrategy::Pooling`] and [`PoolingAllocationConfig`]. pub fn allocation_strategy( &mut self, strategy: impl Into, @@ -1304,116 +1301,217 @@ impl Config { self } - /// Configures the maximum size, in bytes, where a linear memory is - /// considered static, above which it'll be considered dynamic. + /// Specifies the capacity of linear memories, in bytes, in their initial + /// allocation. /// /// > Note: this value has important performance ramifications, be sure to - /// > understand what this value does before tweaking it and benchmarking. + /// > benchmark when setting this to a non-default value and read over this + /// > documentation. + /// + /// This function will change the size of the initial memory allocation made + /// for linear memories. This setting is only applicable when the initial + /// size of a linear memory is below this threshold. Linear memories are + /// allocated in the virtual address space of the host process with OS APIs + /// such as `mmap` and this setting affects how large the allocation will + /// be. + /// + /// ## Background: WebAssembly Linear Memories + /// + /// WebAssembly linear memories always start with a minimum size and can + /// possibly grow up to a maximum size. The minimum size is always specified + /// in a WebAssembly module itself and the maximum size can either be + /// optionally specified in the module or inherently limited by the index + /// type. For example for this module: + /// + /// ```wasm + /// (module + /// (memory $a 4) + /// (memory $b 4096 4096 (pagesize 1)) + /// (memory $c i64 10) + /// ) + /// ``` /// - /// This function configures the threshold for wasm memories whether they're - /// implemented as a dynamically relocatable chunk of memory or a statically - /// located chunk of memory. The `max_size` parameter here is the size, in - /// bytes, where if the maximum size of a linear memory is below `max_size` - /// then it will be statically allocated with enough space to never have to - /// move. If the maximum size of a linear memory is larger than `max_size` - /// then wasm memory will be dynamically located and may move in memory - /// through growth operations. - /// - /// Specifying a `max_size` of 0 means that all memories will be dynamic and - /// may be relocated through `memory.grow`. Also note that if any wasm - /// memory's maximum size is below `max_size` then it will still reserve - /// `max_size` bytes in the virtual memory space. - /// - /// ## Static vs Dynamic Memory - /// - /// Linear memories represent contiguous arrays of bytes, but they can also - /// be grown through the API and wasm instructions. When memory is grown if - /// space hasn't been preallocated then growth may involve relocating the - /// base pointer in memory. Memories in Wasmtime are classified in two - /// different ways: - /// - /// * **static** - these memories preallocate all space necessary they'll - /// ever need, meaning that the base pointer of these memories is never - /// moved. Static memories may take more virtual memory space because of - /// pre-reserving space for memories. - /// - /// * **dynamic** - these memories are not preallocated and may move during - /// growth operations. Dynamic memories consume less virtual memory space - /// because they don't need to preallocate space for future growth. - /// - /// Static memories can be optimized better in JIT code because once the - /// base address is loaded in a function it's known that we never need to - /// reload it because it never changes, `memory.grow` is generally a pretty - /// fast operation because the wasm memory is never relocated, and under - /// some conditions bounds checks can be elided on memory accesses. - /// - /// Dynamic memories can't be quite as heavily optimized because the base - /// address may need to be reloaded more often, they may require relocating - /// lots of data on `memory.grow`, and dynamic memories require - /// unconditional bounds checks on all memory accesses. - /// - /// ## Should you use static or dynamic memory? - /// - /// In general you probably don't need to change the value of this property. - /// The defaults here are optimized for each target platform to consume a - /// reasonable amount of physical memory while also generating speedy - /// machine code. - /// - /// One of the main reasons you may want to configure this today is if your - /// environment can't reserve virtual memory space for each wasm linear - /// memory. On 64-bit platforms wasm memories require a 6GB reservation by - /// default, and system limits may prevent this in some scenarios. In this - /// case you may wish to force memories to be allocated dynamically meaning - /// that the virtual memory footprint of creating a wasm memory should be - /// exactly what's used by the wasm itself. - /// - /// For 32-bit memories a static memory must contain at least 4GB of - /// reserved address space plus a guard page to elide any bounds checks at - /// all. Smaller static memories will use similar bounds checks as dynamic - /// memories. + /// * Memory `$a` initially allocates 4 WebAssembly pages (256KiB) and can + /// grow up to 4GiB, the limit of the 32-bit index space. + /// * Memory `$b` initially allocates 4096 WebAssembly pages, but in this + /// case its page size is 1, so it's 4096 bytes. Memory can also grow no + /// further meaning that it will always be 4096 bytes. + /// * Memory `$c` is a 64-bit linear memory which starts with 640KiB of + /// memory and can theoretically grow up to 2^64 bytes, although most + /// hosts will run out of memory long before that. /// - /// ## Default + /// All operations on linear memories done by wasm are required to be + /// in-bounds. Any access beyond the end of a linear memory is considered a + /// trap. /// - /// The default value for this property depends on the host platform. For - /// 64-bit platforms there's lots of address space available, so the default - /// configured here is 4GB. WebAssembly linear memories currently max out at - /// 4GB which means that on 64-bit platforms Wasmtime by default always uses - /// a static memory. This, coupled with a sufficiently sized guard region, - /// should produce the fastest JIT code on 64-bit platforms, but does - /// require a large address space reservation for each wasm memory. + /// ## What this setting affects: Virtual Memory /// - /// For 32-bit platforms this value defaults to 1GB. This means that wasm - /// memories whose maximum size is less than 1GB will be allocated - /// statically, otherwise they'll be considered dynamic. + /// This setting is used to configure the behavior of the size of the linear + /// memory allocation performed for each of these memories. For example the + /// initial linear memory allocation looks like this: /// - /// ## Static Memory and Pooled Instance Allocation + /// ```text + /// memory_reservation + /// | + /// ◄─────────┴────────────────► + /// ┌───────┬─────────┬──────────────────┬───────┐ + /// │ guard │ initial │ ... capacity ... │ guard │ + /// └───────┴─────────┴──────────────────┴───────┘ + /// ◄──┬──► ◄──┬──► + /// │ │ + /// │ memory_guard_size + /// │ + /// │ + /// memory_guard_size (if guard_before_linear_memory) + /// ``` /// - /// When using the pooling instance allocator memories are considered to - /// always be static memories, they are never dynamic. This setting - /// configures the size of linear memory to reserve for each memory in the - /// pooling allocator. + /// Memory in the `initial` range is accessible to the instance and can be + /// read/written by wasm code. Memory in the `guard` regions is never + /// accesible to wasm code and memory in `capacity` is initially + /// inaccessible but may become accesible through `memory.grow` instructions + /// for example. + /// + /// This means that this setting is the size of the initial chunk of virtual + /// memory that a linear memory may grow into. + /// + /// ## What this setting affects: Runtime Speed + /// + /// This is a performance-sensitive setting which is taken into account + /// during the compilation process of a WebAssembly module. For example if a + /// 32-bit WebAssembly linear memory has a `memory_reservation` size of 4GiB + /// then bounds checks can be elided because `capacity` will be guaranteed + /// to be unmapped for all addressible bytes that wasm can access (modulo a + /// few details). + /// + /// If `memory_reservation` was something smaller like 256KiB then that + /// would have a much smaller impact on virtual memory but the compile code + /// would then need to have explicit bounds checks to ensure that + /// loads/stores are in-bounds. + /// + /// The goal of this setting is to enable skipping bounds checks in most + /// modules by default. Some situations which require explicit bounds checks + /// though are: + /// + /// * When `memory_reservation` is smaller than the addressible size of the + /// linear memory. For example if 64-bit linear memories always need + /// bounds checks as they can address the entire virtual address spacce. + /// For 32-bit linear memories a `memory_reservation` minimum size of 4GiB + /// is required to elide bounds checks. + /// + /// * When linear memories have a page size of 1 then bounds checks are + /// required. In this situation virtual memory can't be relied upon + /// because that operates at the host page size granularity where wasm + /// requires a per-byte level granularity. + /// + /// * Configuration settings such as [`Config::signals_based_traps`] can be + /// used to disable the use of signal handlers and virtual memory so + /// explicit bounds checks are required. + /// + /// * When [`Config::memory_guard_size`] is too small a bounds check may be + /// required. For 32-bit wasm addresses are actually 33-bit effective + /// addresses because loads/stores have a 32-bit static offset to add to + /// the dynamic 32-bit address. If the static offset is larger than the + /// size of the guard region then an explicit bounds check is required. + /// + /// ## What this setting affects: Memory Growth Behavior + /// + /// In addition to affecting bounds checks emitted in compiled code this + /// setting also affects how WebAssembly linear memories are grown. The + /// `memory.grow` instruction can be used to make a linear memory larger and + /// this is also affected by APIs such as + /// [`Memory::grow`](crate::Memory::grow). + /// + /// In these situations when the amount being grown is small enough to fit + /// within the remaining capacity then the linear memory doesn't have to be + /// moved at runtime. If the capacity runs out though then a new linear + /// memory allocation must be made and the contents of linear memory is + /// copied over. + /// + /// For example here's a situation where a copy happens: + /// + /// * The `memory_reservation` setting is configured to 128KiB. + /// * A WebAssembly linear memory starts with a single 64KiB page. + /// * This memory can be grown by one page to contain the full 128KiB of + /// memory. + /// * If grown by one more page, though, then a 192KiB allocation must be + /// made and the previous 128KiB of contents are copied into the new + /// allocation. + /// + /// This growth behavior can have a significant performance impact if lots + /// of data needs to be copied on growth. Conversely if memory growth never + /// needs to happen because the capacity will always be large enough then + /// optimizations can be applied to cache the base pointer of linear memory. + /// + /// When memory is grown then the + /// [`Config::memory_reservation_for_growth`] is used for the new + /// memory allocation to have memory to grow into. + /// + /// When using the pooling allocator via [`PoolingAllocationConfig`] then + /// memories are never allowed to move so requests for growth are instead + /// rejected with an error. + /// + /// ## When this setting is not used + /// + /// This setting is ignored and unused when the initial size of linear + /// memory is larger than this threshold. For example if this setting is set + /// to 1MiB but a wasm module requires a 2MiB minimum allocation then this + /// setting is ignored. In this situation the minimum size of memory will be + /// allocated along with [`Config::memory_reservation_for_growth`] + /// after it to grow into. + /// + /// That means that this value can be set to zero. That can be useful in + /// benchmarking to see the overhead of bounds checks for example. + /// Additionally it can be used to minimize the virtual memory allocated by + /// Wasmtime. + /// + /// ## Default Value /// - /// Note that the pooling allocator can reduce the amount of memory needed - /// for pooling allocation by using memory protection; see - /// `PoolingAllocatorConfig::memory_protection_keys` for details. - pub fn static_memory_maximum_size(&mut self, max_size: u64) -> &mut Self { - self.tunables.static_memory_reservation = Some(max_size); + /// The default value for this property depends on the host platform. For + /// 64-bit platforms there's lots of address space available, so the default + /// configured here is 4GiB. When coupled with the default size of + /// [`Config::memory_guard_size`] this means that 32-bit WebAssembly linear + /// memories with 64KiB page sizes will skip almost all bounds checks by + /// default. + /// + /// For 32-bit platforms this value defaults to 10MiB. This means that + /// bounds checks will be required on 32-bit platforms. + pub fn memory_reservation(&mut self, bytes: u64) -> &mut Self { + self.tunables.memory_reservation = Some(bytes); self } - /// Indicates that the "static" style of memory should always be used. + /// Indicates whether linear memories may relocate their base pointer at + /// runtime. + /// + /// WebAssembly linear memories either have a maximum size that's explicitly + /// listed in the type of a memory or inherently limited by the index type + /// of the memory (e.g. 4GiB for 32-bit linear memories). Depending on how + /// the linear memory is allocated (see [`Config::memory_reservation`]) it + /// may be necessary to move the memory in the host's virtual address space + /// during growth. This option controls whether this movement is allowed or + /// not. + /// + /// An example of a linear memory needing to move is when + /// [`Config::memory_reservation`] is 0 then a linear memory will be + /// allocated as the minimum size of the memory plus + /// [`Config::memory_reservation_for_growth`]. When memory grows beyond the + /// reservation for growth then the memory needs to be relocated. + /// + /// When this option is set to `false` then it can have a number of impacts + /// on how memories work at runtime: /// - /// This configuration option enables selecting the "static" option for all - /// linear memories created within this `Config`. This means that all - /// memories will be allocated up-front and will never move. Additionally - /// this means that all memories are synthetically limited by the - /// [`Config::static_memory_maximum_size`] option, regardless of what the - /// actual maximum size is on the memory's original type. + /// * Modules can be compiled with static knowledge the base pointer of + /// linear memory never changes to enable optimizations such as + /// loop invariant code motion (hoisting the base pointer out of a loop). /// - /// 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 = Some(force); + /// * Memories cannot grow in excess of their original allocation. This + /// means that [`Config::memory_reservation`] and + /// [`Config::memory_reservation_for_growth`] may need tuning to ensure + /// the memory configuration works at runtime. + /// + /// The default value for this option is `true`. + pub fn memory_may_move(&mut self, enable: bool) -> &mut Self { + self.tunables.memory_may_move = Some(enable); self } @@ -1423,82 +1521,110 @@ impl Config { /// > Note: this value has important performance ramifications, be sure to /// > understand what this value does before tweaking it and benchmarking. /// - /// All WebAssembly loads/stores are bounds-checked and generate a trap if - /// they're out-of-bounds. Loads and stores are often very performance - /// critical, so we want the bounds check to be as fast as possible! - /// Accelerating these memory accesses is the motivation for a guard after a - /// memory allocation. + /// This setting controls how many bytes are guaranteed to be unmapped after + /// the virtual memory allocation of a linear memory. When + /// combined with sufficiently large values of + /// [`Config::memory_reservation`] (e.g. 4GiB for 32-bit linear memories) + /// then a guard region can be used to eliminate bounds checks in generated + /// code. /// - /// Memories can be configured with a guard at the end of them which - /// consists of unmapped virtual memory. This unmapped memory will trigger - /// a memory access violation (e.g. segfault) if accessed. This allows JIT - /// code to elide bounds checks if it can prove that an access, if out of - /// bounds, would hit the guard region. This means that having such a guard - /// of unmapped memory can remove the need for bounds checks in JIT code. + /// This setting additionally can be used to help deduplicate bounds checks + /// in code that otherwise requires bounds checks. For example with a 4KiB + /// guard region then a 64-bit linear memory which accesses addresses `x+8` + /// and `x+16` only needs to perform a single bounds check on `x`. If that + /// bounds check passes then the offset is guaranteed to either reside in + /// linear memory or the guard region, resulting in deterministic behavior + /// either way. /// /// ## How big should the guard be? /// - /// In general, like with configuring `static_memory_maximum_size`, you - /// probably don't want to change this value from the defaults. Otherwise, - /// though, the size of the guard region affects the number of bounds checks - /// needed for generated wasm code. More specifically, loads/stores with - /// immediate offsets will generate bounds checks based on how big the guard - /// page is. + /// In general, like with configuring [`Config::memory_reservation`], you + /// probably don't want to change this value from the defaults. Removing + /// bounds checks is dependent on a number of factors where the size of the + /// guard region is only one piece of the equation. Other factors include: /// - /// For 32-bit wasm memories a 4GB static memory is required to even start - /// removing bounds checks. A 4GB guard size will guarantee that the module - /// has zero bounds checks for memory accesses. A 2GB guard size will - /// eliminate all bounds checks with an immediate offset less than 2GB. A - /// guard size of zero means that all memory accesses will still have bounds - /// checks. + /// * [`Config::memory_reservation`] + /// * The index type of the linear memory (e.g. 32-bit or 64-bit) + /// * The page size of the linear memory + /// * Other settings such as [`Config::signals_based_traps`] + /// + /// Embeddings using virtual memory almost always want at least some guard + /// region, but otherwise changes from the default should be profiled + /// locally to see the performance impact. /// /// ## Default /// - /// The default value for this property is 2GB on 64-bit platforms. This + /// The default value for this property is 2GiB on 64-bit platforms. This /// allows eliminating almost all bounds checks on loads/stores with an - /// immediate offset of less than 2GB. On 32-bit platforms this defaults to - /// 64KB. - pub fn memory_guard_size(&mut self, guard_size: u64) -> &mut Self { - self.tunables.memory_guard_size = Some(guard_size); + /// immediate offset of less than 2GiB. On 32-bit platforms this defaults to + /// 64KiB. + pub fn memory_guard_size(&mut self, bytes: u64) -> &mut Self { + self.tunables.memory_guard_size = Some(bytes); self } /// Configures the size, in bytes, of the extra virtual memory space - /// reserved after a "dynamic" memory for growing into. - /// - /// For the difference between static and dynamic memories, see the - /// [`Config::static_memory_maximum_size`] - /// - /// Dynamic memories can be relocated in the process's virtual address space - /// on growth and do not always reserve their entire space up-front. This - /// means that a growth of the memory may require movement in the address - /// space, which in the worst case can copy a large number of bytes from one - /// region to another. - /// - /// This setting configures how many bytes are reserved after the initial - /// reservation for a dynamic memory for growing into. A value of 0 here - /// means that no extra bytes are reserved and all calls to `memory.grow` - /// will need to relocate the wasm linear memory (copying all the bytes). A - /// value of 1 megabyte, however, means that `memory.grow` can allocate up - /// to a megabyte of extra memory before the memory needs to be moved in - /// linear memory. + /// reserved after a linear memory is relocated. + /// + /// This setting is used in conjunction with [`Config::memory_reservation`] + /// to configure what happens after a linear memory is relocated in the host + /// address space. If the initial size of a linear memory exceeds + /// [`Config::memory_reservation`] or if it grows beyond that size + /// throughout its lifetime then this setting will be used. + /// + /// When a linear memory is relocated it will initially look like this: + /// + /// ```text + /// memory.size + /// │ + /// ◄──────┴─────► + /// ┌───────┬──────────────┬───────┐ + /// │ guard │ accessible │ guard │ + /// └───────┴──────────────┴───────┘ + /// ◄──┬──► + /// │ + /// memory_guard_size + /// ``` + /// + /// where `accessible` needs to be grown but there's no more memory to grow + /// into. A new region of the virtual address space will be allocated that + /// looks like this: + /// + /// ```text + /// memory_reservation_for_growth + /// │ + /// memory.size │ + /// │ │ + /// ◄──────┴─────► ◄─────────────┴───────────► + /// ┌───────┬──────────────┬───────────────────────────┬───────┐ + /// │ guard │ accessible │ .. reserved for growth .. │ guard │ + /// └───────┴──────────────┴───────────────────────────┴───────┘ + /// ◄──┬──► + /// │ + /// memory_guard_size + /// ``` + /// + /// This means that up to `memory_reservation_for_growth` bytes can be + /// allocated again before the entire linear memory needs to be moved again + /// when another `memory_reservation_for_growth` bytes will be appended to + /// the size of the allocation. /// /// Note that this is a currently simple heuristic for optimizing the growth /// of dynamic memories, primarily implemented for the memory64 proposal - /// where all memories are currently "dynamic". This is unlikely to be a - /// one-size-fits-all style approach and if you're an embedder running into - /// issues with dynamic memories and growth and are interested in having + /// where the maximum size of memory is larger than 4GiB. This setting is + /// unlikely to be a one-size-fits-all style approach and if you're an + /// embedder running into issues with growth and are interested in having /// other growth strategies available here please feel free to [open an /// issue on the Wasmtime repository][issue]! /// - /// [issue]: https://github.com/bytecodealliance/wasmtime/issues/ne + /// [issue]: https://github.com/bytecodealliance/wasmtime/issues/new /// /// ## Default /// - /// 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 = Some(reserved); + /// For 64-bit platforms this defaults to 2GiB, and for 32-bit platforms + /// this defaults to 1MiB. + pub fn memory_reservation_for_growth(&mut self, bytes: u64) -> &mut Self { + self.tunables.memory_reservation_for_growth = Some(bytes); self } @@ -1516,14 +1642,13 @@ impl Config { /// /// The size of the guard region before linear memory is the same as the /// guard size that comes after linear memory, which is configured by - /// [`Config::static_memory_guard_size`] and - /// [`Config::dynamic_memory_guard_size`]. + /// [`Config::memory_guard_size`]. /// /// ## Default /// /// This value defaults to `true`. - pub fn guard_before_linear_memory(&mut self, guard: bool) -> &mut Self { - self.tunables.guard_before_linear_memory = Some(guard); + pub fn guard_before_linear_memory(&mut self, enable: bool) -> &mut Self { + self.tunables.guard_before_linear_memory = Some(enable); self } @@ -2561,16 +2686,17 @@ pub enum WasmBacktraceDetails { /// /// Another benefit of pooled allocation is that it's possible to configure /// things such that no virtual memory management is required at all in a steady -/// state. For example a pooling allocator can be configured with -/// [`Config::memory_init_cow`] disabledd, dynamic bounds checks enabled -/// through -/// [`Config::static_memory_maximum_size(0)`](Config::static_memory_maximum_size), -/// and sufficient space through -/// [`PoolingAllocationConfig::table_keep_resident`] / -/// [`PoolingAllocationConfig::linear_memory_keep_resident`]. With all these -/// options in place no virtual memory tricks are used at all and everything is -/// manually managed by Wasmtime (for example resetting memory is a -/// `memset(0)`). This is not as fast in a single-threaded scenario but can +/// state. For example a pooling allocator can be configured with: +/// +/// * [`Config::memory_init_cow`] disabled +/// * [`Config::memory_guard_size`] disabled +/// * [`Config::memory_reservation`] shrunk to minimal size +/// * [`PoolingAllocationConfig::table_keep_resident`] sufficiently large +/// * [`PoolingAllocationConfig::linear_memory_keep_resident`] sufficiently large +/// +/// With all these options in place no virtual memory tricks are used at all and +/// everything is manually managed by Wasmtime (for example resetting memory is +/// a `memset(0)`). This is not as fast in a single-threaded scenario but can /// provide benefits in high-parallelism situations as no virtual memory locks /// or IPIs need happen. /// @@ -3041,8 +3167,8 @@ impl PoolingAllocationConfig { /// [`memory_protection_keys`](PoolingAllocationConfig::memory_protection_keys). /// /// The virtual memory reservation size of each linear memory is controlled - /// by the [`Config::static_memory_maximum_size`] setting and this method's - /// configuration cannot exceed [`Config::static_memory_maximum_size`]. + /// by the [`Config::memory_reservation`] setting and this method's + /// configuration cannot exceed [`Config::memory_reservation`]. pub fn max_memory_size(&mut self, bytes: usize) -> &mut Self { self.config.limits.max_memory_size = bytes; self diff --git a/crates/wasmtime/src/engine/serialization.rs b/crates/wasmtime/src/engine/serialization.rs index 1c1818b102c2..f4bdd79eb0ce 100644 --- a/crates/wasmtime/src/engine/serialization.rs +++ b/crates/wasmtime/src/engine/serialization.rs @@ -364,20 +364,20 @@ impl Metadata<'_> { fn check_tunables(&mut self, other: &Tunables) -> Result<()> { let Tunables { collector, - static_memory_reservation, + memory_reservation, memory_guard_size, generate_native_debuginfo, parse_wasm_debuginfo, consume_fuel, epoch_interruption, - static_memory_bound_is_maximum, + memory_may_move, guard_before_linear_memory, table_lazy_init, relaxed_simd_deterministic, winch_callable, signals_based_traps, // This doesn't affect compilation, it's just a runtime setting. - dynamic_memory_growth_reserve: _, + memory_reservation_for_growth: _, // This does technically affect compilation but modules with/without // trap information can be loaded into engines with the opposite @@ -391,9 +391,9 @@ impl Metadata<'_> { Self::check_collector(collector, other.collector)?; Self::check_int( - static_memory_reservation, - other.static_memory_reservation, - "static memory reservation", + memory_reservation, + other.memory_reservation, + "memory reservation", )?; Self::check_int( memory_guard_size, @@ -416,11 +416,7 @@ impl Metadata<'_> { other.epoch_interruption, "epoch interruption", )?; - Self::check_bool( - static_memory_bound_is_maximum, - other.static_memory_bound_is_maximum, - "pooling allocation support", - )?; + Self::check_bool(memory_may_move, other.memory_may_move, "memory may move")?; Self::check_bool( guard_before_linear_memory, other.guard_before_linear_memory, diff --git a/crates/wasmtime/src/runtime/memory.rs b/crates/wasmtime/src/runtime/memory.rs index 8450e388443b..65bbc077a46f 100644 --- a/crates/wasmtime/src/runtime/memory.rs +++ b/crates/wasmtime/src/runtime/memory.rs @@ -1048,7 +1048,7 @@ mod tests { #[test] fn respect_tunables() { let mut cfg = Config::new(); - cfg.static_memory_maximum_size(0).memory_guard_size(0); + cfg.memory_reservation(0).memory_guard_size(0); let mut store = Store::new(&Engine::new(&cfg).unwrap(), ()); let ty = MemoryType::new(1, None); let mem = Memory::new(&mut store, ty).unwrap(); diff --git a/crates/wasmtime/src/runtime/vm/cow.rs b/crates/wasmtime/src/runtime/vm/cow.rs index 7cf3c1672198..0ac6e5d6bea5 100644 --- a/crates/wasmtime/src/runtime/vm/cow.rs +++ b/crates/wasmtime/src/runtime/vm/cow.rs @@ -798,7 +798,7 @@ mod test { fn instantiate_no_image() { let ty = dummy_memory(); let tunables = Tunables { - static_memory_reservation: 4 << 30, + memory_reservation: 4 << 30, ..Tunables::default_miri() }; // 4 MiB mmap'd area, not accessible @@ -838,7 +838,7 @@ mod test { let page_size = host_page_size(); let ty = dummy_memory(); let tunables = Tunables { - static_memory_reservation: 4 << 30, + memory_reservation: 4 << 30, ..Tunables::default_miri() }; // 4 MiB mmap'd area, not accessible @@ -911,7 +911,7 @@ mod test { let page_size = host_page_size(); let ty = dummy_memory(); let tunables = Tunables { - static_memory_reservation: 100 << 16, + memory_reservation: 100 << 16, ..Tunables::default_miri() }; let mut mmap = Mmap::accessible_reserved(0, 4 << 20).unwrap(); @@ -964,8 +964,8 @@ mod test { let page_size = host_page_size(); let ty = dummy_memory(); let tunables = Tunables { - static_memory_reservation: 0, - dynamic_memory_growth_reserve: 200, + memory_reservation: 0, + memory_reservation_for_growth: 200, ..Tunables::default_miri() }; diff --git a/crates/wasmtime/src/runtime/vm/instance/allocator/pooling.rs b/crates/wasmtime/src/runtime/vm/instance/allocator/pooling.rs index dbad7bbd9fb7..75e41b562399 100644 --- a/crates/wasmtime/src/runtime/vm/instance/allocator/pooling.rs +++ b/crates/wasmtime/src/runtime/vm/instance/allocator/pooling.rs @@ -136,7 +136,7 @@ pub struct InstanceLimits { pub max_memories_per_module: u32, /// Maximum byte size of a linear memory, must be smaller than - /// `static_memory_reservation` in `Tunables`. + /// `memory_reservation` in `Tunables`. pub max_memory_size: usize, /// The total number of GC heaps in the pool, across all instances. @@ -716,14 +716,14 @@ mod test { PoolingInstanceAllocator::new( &config, &Tunables { - static_memory_reservation: 0x10000, + memory_reservation: 0x10000, ..Tunables::default_host() }, ) .map_err(|e| e.to_string()) .expect_err("expected a failure constructing instance allocator"), "maximum memory size of 0x100010000 bytes exceeds the configured \ - static memory reservation of 0x10000 bytes" + memory reservation of 0x10000 bytes" ); } diff --git a/crates/wasmtime/src/runtime/vm/instance/allocator/pooling/memory_pool.rs b/crates/wasmtime/src/runtime/vm/instance/allocator/pooling/memory_pool.rs index 904a7c49c643..b993e2814a99 100644 --- a/crates/wasmtime/src/runtime/vm/instance/allocator/pooling/memory_pool.rs +++ b/crates/wasmtime/src/runtime/vm/instance/allocator/pooling/memory_pool.rs @@ -139,14 +139,12 @@ pub struct MemoryPool { impl MemoryPool { /// Create a new `MemoryPool`. pub fn new(config: &PoolingInstanceAllocatorConfig, tunables: &Tunables) -> Result { - if u64::try_from(config.limits.max_memory_size).unwrap() - > tunables.static_memory_reservation - { + if u64::try_from(config.limits.max_memory_size).unwrap() > tunables.memory_reservation { bail!( "maximum memory size of {:#x} bytes exceeds the configured \ - static memory reservation of {:#x} bytes", + memory reservation of {:#x} bytes", config.limits.max_memory_size, - tunables.static_memory_reservation + tunables.memory_reservation ); } let pkeys = match config.memory_protection_keys { @@ -556,8 +554,8 @@ impl SlabConstraints { tunables: &Tunables, num_pkeys_available: usize, ) -> Result { - // `static_memory_reservation` is the configured number of bytes for a - // static memory slot (see `Config::static_memory_maximum_size`); even + // `memory_reservation` is the configured number of bytes for a + // static memory slot (see `Config::memory_reservation`); even // if the memory never grows to this size (e.g., it has a lower memory // maximum), codegen will assume that this unused memory is mapped // `PROT_NONE`. Typically `static_memory_bound` is 4GiB which helps @@ -566,9 +564,9 @@ impl SlabConstraints { // MPK-protected stripes, the slot size can be lower than the // `static_memory_bound`. let expected_slot_bytes: usize = tunables - .static_memory_reservation + .memory_reservation .try_into() - .context("static memory bound is too large")?; + .context("memory reservation is too large")?; let expected_slot_bytes = round_usize_up_to_host_pages(expected_slot_bytes)?; let guard_bytes: usize = tunables @@ -781,7 +779,7 @@ mod tests { ..Default::default() }, &Tunables { - static_memory_reservation: WASM_PAGE_SIZE as u64, + memory_reservation: WASM_PAGE_SIZE as u64, memory_guard_size: 0, ..Tunables::default_host() }, diff --git a/examples/mpk.rs b/examples/mpk.rs index 74a708bc120b..236fec3159ac 100644 --- a/examples/mpk.rs +++ b/examples/mpk.rs @@ -69,10 +69,10 @@ struct Args { memory_size: usize, /// The maximum number of bytes a memory is considered static; see - /// `Config::static_memory_maximum_size` for more details and the default + /// `Config::memory_reservation` for more details and the default /// value if unset. #[arg(long, value_parser = parse_byte_size)] - static_memory_maximum_size: Option, + memory_reservation: Option, /// The size in bytes of the guard region to expect between static memory /// slots; see [`Config::memory_guard_size`] for more details and the @@ -192,8 +192,8 @@ fn build_engine(args: &Args, num_memories: u32, enable_mpk: MpkEnabled) -> Resul // Configure the engine itself. let mut config = Config::new(); - if let Some(static_memory_maximum_size) = args.static_memory_maximum_size { - config.static_memory_maximum_size(static_memory_maximum_size); + if let Some(memory_reservation) = args.memory_reservation { + config.memory_reservation(memory_reservation); } if let Some(memory_guard_size) = args.memory_guard_size { config.memory_guard_size(memory_guard_size); diff --git a/src/commands/serve.rs b/src/commands/serve.rs index f3b4643f26ad..02ce3327199f 100644 --- a/src/commands/serve.rs +++ b/src/commands/serve.rs @@ -642,7 +642,7 @@ fn use_pooling_allocator_by_default() -> Result> { const BITS_TO_TEST: u32 = 42; let mut config = Config::new(); config.wasm_memory64(true); - config.static_memory_maximum_size(1 << BITS_TO_TEST); + config.memory_reservation(1 << BITS_TO_TEST); let engine = Engine::new(&config)?; let mut store = Store::new(&engine, ()); // NB: the maximum size is in wasm pages to take out the 16-bits of wasm diff --git a/tests/all/async_functions.rs b/tests/all/async_functions.rs index ae3973b9bb6a..fc891e6ea19a 100644 --- a/tests/all/async_functions.rs +++ b/tests/all/async_functions.rs @@ -353,7 +353,7 @@ async fn async_with_pooling_stacks() { config.async_support(true); config.allocation_strategy(InstanceAllocationStrategy::Pooling(pool)); config.memory_guard_size(0); - config.static_memory_maximum_size(1 << 16); + config.memory_reservation(1 << 16); let engine = Engine::new(&config).unwrap(); let mut store = Store::new(&engine, ()); @@ -377,7 +377,7 @@ async fn async_host_func_with_pooling_stacks() -> Result<()> { config.async_support(true); config.allocation_strategy(InstanceAllocationStrategy::Pooling(pooling)); config.memory_guard_size(0); - config.static_memory_maximum_size(1 << 16); + config.memory_reservation(1 << 16); let mut store = Store::new(&Engine::new(&config)?, ()); let mut linker = Linker::new(store.engine()); @@ -409,7 +409,7 @@ async fn async_mpk_protection() -> Result<()> { let mut config = Config::new(); config.async_support(true); config.allocation_strategy(InstanceAllocationStrategy::Pooling(pooling)); - config.static_memory_maximum_size(1 << 26); + config.memory_reservation(1 << 26); config.epoch_interruption(true); let engine = Engine::new(&config)?; diff --git a/tests/all/fuel.rs b/tests/all/fuel.rs index 1d209a30954a..201af9aa5e39 100644 --- a/tests/all/fuel.rs +++ b/tests/all/fuel.rs @@ -182,7 +182,7 @@ fn manual_edge_cases(config: &mut Config) { #[cfg_attr(miri, ignore)] fn unconditionally_trapping_memory_accesses_save_fuel_before_trapping(config: &mut Config) { config.consume_fuel(true); - config.static_memory_maximum_size(0x1_0000); + config.memory_reservation(0x1_0000); let engine = Engine::new(&config).unwrap(); diff --git a/tests/all/memory.rs b/tests/all/memory.rs index 16dbd1d6e375..6c8f1c651924 100644 --- a/tests/all/memory.rs +++ b/tests/all/memory.rs @@ -97,10 +97,10 @@ fn offsets_static_dynamic_oh_my(config: &mut Config) -> Result<()> { let mut engines = Vec::new(); let sizes = [0, 1 * GB, 4 * GB]; - for &static_memory_maximum_size in sizes.iter() { + for &memory_reservation in sizes.iter() { for &guard_size in sizes.iter() { for &guard_before_linear_memory in [true, false].iter() { - config.static_memory_maximum_size(static_memory_maximum_size); + config.memory_reservation(memory_reservation); config.memory_guard_size(guard_size); config.guard_before_linear_memory(guard_before_linear_memory); config.cranelift_debug_verifier(true); @@ -139,7 +139,7 @@ fn guards_present() -> Result<()> { const GUARD_SIZE: u64 = 65536; let mut config = Config::new(); - config.static_memory_maximum_size(1 << 20); + config.memory_reservation(1 << 20); config.memory_guard_size(GUARD_SIZE); config.guard_before_linear_memory(true); let engine = Engine::new(&config)?; @@ -190,7 +190,7 @@ fn guards_present_pooling(config: &mut Config) -> Result<()> { pool.total_memories(2) .max_memory_size(10 << 16) .memory_protection_keys(MpkEnabled::Disable); - config.static_memory_maximum_size(1 << 20); + config.memory_reservation(1 << 20); config.memory_guard_size(GUARD_SIZE); config.guard_before_linear_memory(true); config.allocation_strategy(InstanceAllocationStrategy::Pooling(pool)); @@ -251,7 +251,7 @@ fn guards_present_pooling_mpk(config: &mut Config) -> Result<()> { .max_memory_size(10 << 16) .memory_protection_keys(MpkEnabled::Enable) .max_memory_protection_keys(2); - config.static_memory_maximum_size(1 << 20); + config.memory_reservation(1 << 20); config.memory_guard_size(GUARD_SIZE); config.guard_before_linear_memory(true); config.allocation_strategy(InstanceAllocationStrategy::Pooling(pool)); @@ -387,7 +387,7 @@ fn tiny_static_heap(config: &mut Config) -> Result<()> { // the static memory size limit in the configuration. This is intended to // specifically test that a load of all the valid addresses of the memory // all pass bounds-checks in cranelift to help weed out any off-by-one bugs. - config.static_memory_maximum_size(1 << 16); + config.memory_reservation(1 << 16); let engine = Engine::new(&config)?; let mut store = Store::new(&engine, ()); @@ -420,8 +420,8 @@ fn tiny_static_heap(config: &mut Config) -> Result<()> { #[test] fn static_forced_max() -> Result<()> { let mut config = Config::new(); - config.static_memory_maximum_size(5 << 16); - config.static_memory_forced(true); + config.memory_reservation(5 << 16); + config.memory_may_move(false); let engine = Engine::new(&config)?; let mut store = Store::new(&engine, ()); @@ -434,9 +434,9 @@ fn static_forced_max() -> Result<()> { #[wasmtime_test] fn dynamic_extra_growth_unchanged_pointer(config: &mut Config) -> Result<()> { const EXTRA_PAGES: u64 = 5; - config.static_memory_maximum_size(0); + config.memory_reservation(0); // 5 wasm pages extra - config.dynamic_memory_reserved_for_growth(EXTRA_PAGES * (1 << 16)); + config.memory_reservation_for_growth(EXTRA_PAGES * (1 << 16)); let engine = Engine::new(&config)?; let mut store = Store::new(&engine, ()); @@ -673,8 +673,8 @@ fn init_with_negative_segment(_: &mut Config) -> Result<()> { #[test] fn non_page_aligned_static_memory() -> Result<()> { let mut config = Config::new(); - config.static_memory_maximum_size(100_000); - config.static_memory_forced(true); + config.memory_reservation(100_000); + config.memory_may_move(false); let engine = Engine::new(&config)?; let ty = MemoryType::new(1, None); Memory::new(&mut Store::new(&engine, ()), ty)?; diff --git a/tests/all/memory_creator.rs b/tests/all/memory_creator.rs index 5829f5b0e372..6d79e3c0d3db 100644 --- a/tests/all/memory_creator.rs +++ b/tests/all/memory_creator.rs @@ -129,7 +129,7 @@ mod not_for_windows { let mut config = Config::new(); config .with_host_memory(mem_creator.clone()) - .static_memory_maximum_size(0) + .memory_reservation(0) .memory_guard_size(0); (Store::new(&Engine::new(&config).unwrap(), ()), mem_creator) } diff --git a/tests/all/module.rs b/tests/all/module.rs index 61852409ddb6..7dc7cc9ccd9d 100644 --- a/tests/all/module.rs +++ b/tests/all/module.rs @@ -37,7 +37,7 @@ fn caches_across_engines() { // differ in runtime settings let res = Module::deserialize( - &Engine::new(Config::new().static_memory_maximum_size(0)).unwrap(), + &Engine::new(Config::new().memory_reservation(0)).unwrap(), &bytes, ); assert!(res.is_err()); diff --git a/tests/all/module_serialize.rs b/tests/all/module_serialize.rs index 92f202c56ad6..e3d28576ef8e 100644 --- a/tests/all/module_serialize.rs +++ b/tests/all/module_serialize.rs @@ -68,7 +68,7 @@ fn test_module_serialize_fail() -> Result<()> { )?; let mut config = Config::new(); - config.static_memory_maximum_size(0); + config.memory_reservation(0); let mut store = Store::new(&Engine::new(&config)?, ()); match unsafe { deserialize_and_instantiate(&mut store, &buffer) } { Ok(_) => bail!("expected failure at deserialization"), diff --git a/tests/all/pooling_allocator.rs b/tests/all/pooling_allocator.rs index 162aee1dad35..14474401a735 100644 --- a/tests/all/pooling_allocator.rs +++ b/tests/all/pooling_allocator.rs @@ -7,7 +7,7 @@ fn successful_instantiation() -> Result<()> { let mut config = Config::new(); config.allocation_strategy(pool); config.memory_guard_size(0); - config.static_memory_maximum_size(1 << 16); + config.memory_reservation(1 << 16); let engine = Engine::new(&config)?; let module = Module::new(&engine, r#"(module (memory 1) (table 10 funcref))"#)?; @@ -27,7 +27,7 @@ fn memory_limit() -> Result<()> { let mut config = Config::new(); config.allocation_strategy(pool); config.memory_guard_size(1 << 16); - config.static_memory_maximum_size(3 << 16); + config.memory_reservation(3 << 16); config.wasm_multi_memory(true); let engine = Engine::new(&config)?; @@ -200,7 +200,7 @@ fn memory_zeroed() -> Result<()> { let mut config = Config::new(); config.allocation_strategy(pool); config.memory_guard_size(0); - config.static_memory_maximum_size(1 << 16); + config.memory_reservation(1 << 16); let engine = Engine::new(&config)?; @@ -237,7 +237,7 @@ fn table_limit() -> Result<()> { let mut config = Config::new(); config.allocation_strategy(pool); config.memory_guard_size(0); - config.static_memory_maximum_size(1 << 16); + config.memory_reservation(1 << 16); let engine = Engine::new(&config)?; @@ -372,7 +372,7 @@ fn table_zeroed() -> Result<()> { let mut config = Config::new(); config.allocation_strategy(pool); config.memory_guard_size(0); - config.static_memory_maximum_size(1 << 16); + config.memory_reservation(1 << 16); let engine = Engine::new(&config)?; @@ -407,7 +407,7 @@ fn total_core_instances_limit() -> Result<()> { let mut config = Config::new(); config.allocation_strategy(pool); config.memory_guard_size(0); - config.static_memory_maximum_size(1 << 16); + config.memory_reservation(1 << 16); let engine = Engine::new(&config)?; let module = Module::new(&engine, r#"(module)"#)?; @@ -695,7 +695,7 @@ fn dynamic_memory_pooling_allocator() -> Result<()> { let mut pool = crate::small_pool_config(); pool.max_memory_size(max_size as usize); let mut config = Config::new(); - config.static_memory_maximum_size(max_size); + config.memory_reservation(max_size); config.memory_guard_size(guard_size); config.allocation_strategy(pool); diff --git a/tests/pcc_memory.rs b/tests/pcc_memory.rs index 7b6e3e58d30d..8a992fc85309 100644 --- a/tests/pcc_memory.rs +++ b/tests/pcc_memory.rs @@ -75,18 +75,14 @@ mod pcc_memory_tests { } for test in &bodies { - for static_memory_maximum_size in [4 * GIB] { + for memory_reservation in [4 * GIB] { for guard_size in [2 * GIB] { for enable_spectre in [true /* not yet supported by PCC: false */] { for _memory_bits in [32 /* not yet supported by PCC: 64 */] { log::trace!("test:\n{}\n", test); - log::trace!( - "static {:x} guard {:x}", - static_memory_maximum_size, - guard_size - ); + log::trace!("static {:x} guard {:x}", memory_reservation, guard_size); let mut cfg = Config::new(); - cfg.static_memory_maximum_size(static_memory_maximum_size); + cfg.memory_reservation(memory_reservation); cfg.memory_guard_size(guard_size); cfg.cranelift_pcc(true); unsafe { diff --git a/tests/wast.rs b/tests/wast.rs index dc92dcc4df5f..858690ffe865 100644 --- a/tests/wast.rs +++ b/tests/wast.rs @@ -360,11 +360,11 @@ fn run_wast(wast: &Path, config: WastConfig) -> anyhow::Result<()> { // also don't reserve lots of memory after dynamic memories for growth // (makes growth slower). if use_shared_memory { - cfg.static_memory_maximum_size(2 * u64::from(Memory::DEFAULT_PAGE_SIZE)); + cfg.memory_reservation(2 * u64::from(Memory::DEFAULT_PAGE_SIZE)); } else { - cfg.static_memory_maximum_size(0); + cfg.memory_reservation(0); } - cfg.dynamic_memory_reserved_for_growth(0); + cfg.memory_reservation_for_growth(0); let small_guard = 64 * 1024; cfg.memory_guard_size(small_guard); @@ -390,8 +390,8 @@ fn run_wast(wast: &Path, config: WastConfig) -> anyhow::Result<()> { // impact. let max_memory_size = 805 << 16; if multi_memory { - cfg.static_memory_maximum_size(max_memory_size as u64); - cfg.dynamic_memory_reserved_for_growth(0); + cfg.memory_reservation(max_memory_size as u64); + cfg.memory_reservation_for_growth(0); cfg.memory_guard_size(0); }