Skip to content

Commit

Permalink
Merge static/dynamic guard size options
Browse files Browse the repository at this point in the history
This commit is the first of what will likely be a few to refactor the
memory-related configuration options in Wasmtime. The end goal of these
refactorings is to fix some preexisting issues and additionally make the
configuration easier to understand for both users and implementors
alike. First on the chopping block here is to merge the
`dynamic_memory_guard_size` and `static_memory_guard_size` options into
one option. AFAIK there's not a strong reason to have separate
configuration options for these so it's hopefully simpler to have a
single `memory_guard_size` option which applies to all linear memories
equally.

I'll note that the old CLI options are preserved but are documented as
deprecated. We don't currently warn on using "deprecated options" so for
now the old options are just documented as deprecated and are otherwise
silently accepted.
  • Loading branch information
alexcrichton committed Oct 31, 2024
1 parent 16eb9fa commit d33922a
Show file tree
Hide file tree
Showing 15 changed files with 68 additions and 146 deletions.
25 changes: 15 additions & 10 deletions crates/cli-flags/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,18 +42,15 @@ wasmtime_option_group! {
/// Optimization level of generated code (0-2, s; default: 2)
pub opt_level: Option<wasmtime::OptLevel>,

/// Byte size of the guard region after dynamic memories are allocated
pub dynamic_memory_guard_size: Option<u64>,

/// Force using a "static" style for all wasm memories
pub static_memory_forced: Option<bool>,

/// Maximum size in bytes of wasm memory before it becomes dynamically
/// relocatable instead of up-front-reserved.
pub static_memory_maximum_size: Option<u64>,

/// Byte size of the guard region after static memories are allocated
pub static_memory_guard_size: Option<u64>,
/// Size, in bytes, of guard pages for linear memories.
pub memory_guard_size: Option<u64>,

/// Bytes to reserve at the end of linear memory for growth for dynamic
/// memories.
Expand Down Expand Up @@ -167,6 +164,12 @@ wasmtime_option_group! {

/// Enable or disable the use of host signal handlers for traps.
pub signals_based_traps: Option<bool>,

/// DEPRECATED: Use `-Cmemory-guard-size=N` instead.
pub dynamic_memory_guard_size: Option<u64>,

/// DEPRECATED: Use `-Cmemory-guard-size=N` instead.
pub static_memory_guard_size: Option<u64>,
}

enum Optimize {
Expand Down Expand Up @@ -621,13 +624,15 @@ impl CommonOptions {
config.static_memory_forced(enable);
}

if let Some(size) = self.opts.static_memory_guard_size {
config.static_memory_guard_size(size);
if let Some(size) = self
.opts
.static_memory_guard_size
.or(self.opts.dynamic_memory_guard_size)
.or(self.opts.memory_guard_size)
{
config.memory_guard_size(size);
}

if let Some(size) = self.opts.dynamic_memory_guard_size {
config.dynamic_memory_guard_size(size);
}
if let Some(size) = self.opts.dynamic_memory_reserved_for_growth {
config.dynamic_memory_reserved_for_growth(size);
}
Expand Down
4 changes: 2 additions & 2 deletions crates/environ/src/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ impl MemoryStyle {
Self::Static {
byte_reservation: tunables.static_memory_reservation,
},
tunables.static_memory_offset_guard_size,
tunables.memory_guard_size,
);
}

Expand All @@ -67,7 +67,7 @@ impl MemoryStyle {
Self::Dynamic {
reserve: tunables.dynamic_memory_growth_reserve,
},
tunables.dynamic_memory_offset_guard_size,
tunables.memory_guard_size,
)
}
}
Expand Down
21 changes: 5 additions & 16 deletions crates/environ/src/tunables.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,8 @@ pub struct Tunables {
/// the heap.
pub static_memory_reservation: u64,

/// The size in bytes of the offset guard for static heaps.
pub static_memory_offset_guard_size: u64,

/// The size in bytes of the offset guard for dynamic heaps.
pub dynamic_memory_offset_guard_size: 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
Expand Down Expand Up @@ -100,8 +97,7 @@ impl Tunables {
// No virtual memory tricks are available on miri so make these
// limits quite conservative.
static_memory_reservation: 1 << 20,
static_memory_offset_guard_size: 0,
dynamic_memory_offset_guard_size: 0,
memory_guard_size: 0,
dynamic_memory_growth_reserve: 0,

// General options which have the same defaults regardless of
Expand All @@ -128,8 +124,7 @@ impl Tunables {
// impacts performance severely but allows us to have more than a
// few instances running around.
static_memory_reservation: 10 * (1 << 20),
static_memory_offset_guard_size: 0x1_0000,
dynamic_memory_offset_guard_size: 0x1_0000,
memory_guard_size: 0x1_0000,
dynamic_memory_growth_reserve: 1 << 20, // 1MB

..Tunables::default_miri()
Expand All @@ -146,13 +141,7 @@ 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,
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: 0x1_0000,
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
Expand Down
2 changes: 1 addition & 1 deletion crates/misc/component-test-util/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ pub fn config() -> Config {
// 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.dynamic_memory_guard_size(0);
config.memory_guard_size(0);
}
config
}
Expand Down
78 changes: 14 additions & 64 deletions crates/wasmtime/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,7 @@ pub struct Config {
#[derive(Default, Clone)]
struct ConfigTunables {
static_memory_reservation: Option<u64>,
static_memory_offset_guard_size: Option<u64>,
dynamic_memory_offset_guard_size: Option<u64>,
memory_guard_size: Option<u64>,
dynamic_memory_growth_reserve: Option<u64>,
generate_native_debuginfo: Option<bool>,
parse_wasm_debuginfo: Option<bool>,
Expand Down Expand Up @@ -1288,7 +1287,7 @@ impl Config {
/// When using the pooling instance allocation strategy, all linear memories
/// will be created as "static" and the
/// [`Config::static_memory_maximum_size`] and
/// [`Config::static_memory_guard_size`] options will be used to configure
/// [`Config::memory_guard_size`] options will be used to configure
/// the virtual memory allocations of linear memories.
pub fn allocation_strategy(&mut self, strategy: InstanceAllocationStrategy) -> &mut Self {
self.allocation_strategy = strategy;
Expand Down Expand Up @@ -1409,7 +1408,7 @@ impl Config {
}

/// Configures the size, in bytes, of the guard region used at the end of a
/// static memory's address space reservation.
/// linear memory's address space reservation.
///
/// > Note: this value has important performance ramifications, be sure to
/// > understand what this value does before tweaking it and benchmarking.
Expand All @@ -1420,16 +1419,12 @@ impl Config {
/// Accelerating these memory accesses is the motivation for a guard after a
/// memory allocation.
///
/// Memories (both static and dynamic) 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.
///
/// For the difference between static and dynamic memories, see the
/// [`Config::static_memory_maximum_size`].
/// 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.
///
/// ## How big should the guard be?
///
Expand All @@ -1453,45 +1448,8 @@ impl Config {
/// 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.
///
/// ## Errors
///
/// The `Engine::new` method will return an error if this option is smaller
/// than the value configured for [`Config::dynamic_memory_guard_size`].
pub fn static_memory_guard_size(&mut self, guard_size: u64) -> &mut Self {
self.tunables.static_memory_offset_guard_size = Some(guard_size);
self
}

/// Configures the size, in bytes, of the guard region used at the end of a
/// dynamic memory's address space reservation.
///
/// For the difference between static and dynamic memories, see the
/// [`Config::static_memory_maximum_size`]
///
/// For more information about what a guard is, see the documentation on
/// [`Config::static_memory_guard_size`].
///
/// Note that the size of the guard region for dynamic memories is not super
/// critical for performance. Making it reasonably-sized can improve
/// generated code slightly, but for maximum performance you'll want to lean
/// towards static memories rather than dynamic anyway.
///
/// Also note that the dynamic memory guard size must be smaller than the
/// static memory guard size, so if a large dynamic memory guard is
/// specified then the static memory guard size will also be automatically
/// increased.
///
/// ## Default
///
/// This value defaults to 64KB.
///
/// ## Errors
///
/// The `Engine::new` method will return an error if this option is larger
/// than the value configured for [`Config::static_memory_guard_size`].
pub fn dynamic_memory_guard_size(&mut self, guard_size: u64) -> &mut Self {
self.tunables.dynamic_memory_offset_guard_size = Some(guard_size);
pub fn memory_guard_size(&mut self, guard_size: u64) -> &mut Self {
self.tunables.memory_guard_size = Some(guard_size);
self
}

Expand Down Expand Up @@ -1957,8 +1915,7 @@ impl Config {

set_fields! {
static_memory_reservation
static_memory_offset_guard_size
dynamic_memory_offset_guard_size
memory_guard_size
dynamic_memory_growth_reserve
generate_native_debuginfo
parse_wasm_debuginfo
Expand All @@ -1979,10 +1936,6 @@ impl Config {
tunables.winch_callable = self.compiler_config.strategy == Some(Strategy::Winch);
}

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, features))
}

Expand Down Expand Up @@ -2329,11 +2282,8 @@ impl fmt::Debug for Config {
if let Some(size) = self.tunables.static_memory_reservation {
f.field("static_memory_maximum_reservation", &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(size) = self.tunables.memory_guard_size {
f.field("memory_guard_size", &size);
}
if let Some(enable) = self.tunables.guard_before_linear_memory {
f.field("guard_before_linear_memory", &enable);
Expand Down
18 changes: 6 additions & 12 deletions crates/wasmtime/src/engine/serialization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -364,8 +364,7 @@ impl Metadata<'_> {
fn check_tunables(&mut self, other: &Tunables) -> Result<()> {
let Tunables {
static_memory_reservation,
static_memory_offset_guard_size,
dynamic_memory_offset_guard_size,
memory_guard_size,
generate_native_debuginfo,
parse_wasm_debuginfo,
consume_fuel,
Expand Down Expand Up @@ -395,14 +394,9 @@ impl Metadata<'_> {
"static memory reservation",
)?;
Self::check_int(
static_memory_offset_guard_size,
other.static_memory_offset_guard_size,
"static memory guard size",
)?;
Self::check_int(
dynamic_memory_offset_guard_size,
other.dynamic_memory_offset_guard_size,
"dynamic memory guard size",
memory_guard_size,
other.memory_guard_size,
"memory guard size",
)?;
Self::check_bool(
generate_native_debuginfo,
Expand Down Expand Up @@ -696,11 +690,11 @@ Caused by:
let engine = Engine::default();
let mut metadata = Metadata::new(&engine);

metadata.tunables.static_memory_offset_guard_size = 0;
metadata.tunables.memory_guard_size = 0;

match metadata.check_compatible(&engine) {
Ok(_) => unreachable!(),
Err(e) => assert_eq!(e.to_string(), "Module was compiled with a static memory guard size of '0' but '2147483648' is expected for the host"),
Err(e) => assert_eq!(e.to_string(), "Module was compiled with a memory guard size of '0' but '2147483648' is expected for the host"),
}

Ok(())
Expand Down
3 changes: 1 addition & 2 deletions crates/wasmtime/src/runtime/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1049,8 +1049,7 @@ mod tests {
#[test]
fn respect_tunables() {
let mut cfg = Config::new();
cfg.static_memory_maximum_size(0)
.dynamic_memory_guard_size(0);
cfg.static_memory_maximum_size(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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -577,7 +577,7 @@ impl SlabConstraints {
let expected_slot_bytes = round_usize_up_to_host_pages(expected_slot_bytes)?;

let guard_bytes: usize = tunables
.static_memory_offset_guard_size
.memory_guard_size
.try_into()
.context("guard region is too large")?;
let guard_bytes = round_usize_up_to_host_pages(guard_bytes)?;
Expand Down Expand Up @@ -608,7 +608,7 @@ struct SlabLayout {
/// guard region after the memory to catch OOB access. On these guard
/// regions, note that:
/// - users can configure how aggressively (or not) to elide bounds checks
/// via `Config::static_memory_guard_size` (see also:
/// via `Config::memory_guard_size` (see also:
/// `memory_and_guard_size`)
/// - memory protection keys can compress the size of the guard region by
/// placing slots from a different key (i.e., a stripe) in the guard
Expand Down Expand Up @@ -786,7 +786,7 @@ mod tests {
},
&Tunables {
static_memory_reservation: WASM_PAGE_SIZE as u64,
static_memory_offset_guard_size: 0,
memory_guard_size: 0,
..Tunables::default_host()
},
)?;
Expand Down
8 changes: 4 additions & 4 deletions examples/mpk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,10 @@ struct Args {
static_memory_maximum_size: Option<u64>,

/// The size in bytes of the guard region to expect between static memory
/// slots; see [`Config::static_memory_guard_size`] for more details and the
/// slots; see [`Config::memory_guard_size`] for more details and the
/// default value if unset.
#[arg(long, value_parser = parse_byte_size)]
static_memory_guard_size: Option<u64>,
memory_guard_size: Option<u64>,
}

/// Parse a human-readable byte size--e.g., "512 MiB"--into the correct number
Expand Down Expand Up @@ -195,8 +195,8 @@ fn build_engine(args: &Args, num_memories: u32, enable_mpk: MpkEnabled) -> Resul
if let Some(static_memory_maximum_size) = args.static_memory_maximum_size {
config.static_memory_maximum_size(static_memory_maximum_size);
}
if let Some(static_memory_guard_size) = args.static_memory_guard_size {
config.static_memory_guard_size(static_memory_guard_size);
if let Some(memory_guard_size) = args.memory_guard_size {
config.memory_guard_size(memory_guard_size);
}
config.allocation_strategy(InstanceAllocationStrategy::Pooling(pool));

Expand Down
6 changes: 2 additions & 4 deletions tests/all/async_functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -352,8 +352,7 @@ async fn async_with_pooling_stacks() {
let mut config = Config::new();
config.async_support(true);
config.allocation_strategy(InstanceAllocationStrategy::Pooling(pool));
config.dynamic_memory_guard_size(0);
config.static_memory_guard_size(0);
config.memory_guard_size(0);
config.static_memory_maximum_size(1 << 16);

let engine = Engine::new(&config).unwrap();
Expand All @@ -377,8 +376,7 @@ async fn async_host_func_with_pooling_stacks() -> Result<()> {
let mut config = Config::new();
config.async_support(true);
config.allocation_strategy(InstanceAllocationStrategy::Pooling(pooling));
config.dynamic_memory_guard_size(0);
config.static_memory_guard_size(0);
config.memory_guard_size(0);
config.static_memory_maximum_size(1 << 16);

let mut store = Store::new(&Engine::new(&config)?, ());
Expand Down
Loading

0 comments on commit d33922a

Please sign in to comment.