Skip to content

Commit

Permalink
Add a configuration option to force "static" memories (#3503)
Browse files Browse the repository at this point in the history
* Add a configuration option to force "static" memories

In poking around at some things earlier today I realized that one
configuration option for memories we haven't exposed from embeddings
like the CLI is to forcibly limit the size of memory growth and force
using a static memory style. This means that the CLI, for example, can't
limit memory growth by default and memories are only limited in size by
what the OS can give and the wasm's own memory type. This configuration
option means that the CLI can artificially limit the size of wasm linear
memories.

Additionally another motivation for this is for testing out various
codegen ramifications of static/dynamic memories. This is the only way
to force a static memory, by default, for wasm64 memories with no
maximum size listed for example.

* Review feedback
  • Loading branch information
alexcrichton authored Nov 3, 2021
1 parent 6428ac8 commit 6bcee7f
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 5 deletions.
18 changes: 13 additions & 5 deletions crates/runtime/src/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,21 +90,29 @@ pub struct MmapMemory {

impl MmapMemory {
/// Create a new linear memory instance with specified minimum and maximum number of wasm pages.
pub fn new(plan: &MemoryPlan, minimum: usize, maximum: Option<usize>) -> Result<Self> {
pub fn new(plan: &MemoryPlan, minimum: usize, mut maximum: Option<usize>) -> Result<Self> {
// It's a programmer error for these two configuration values to exceed
// the host available address space, so panic if such a configuration is
// found (mostly an issue for hypothetical 32-bit hosts).
let offset_guard_bytes = usize::try_from(plan.offset_guard_size).unwrap();
let pre_guard_bytes = usize::try_from(plan.pre_guard_size).unwrap();

let (alloc_bytes, extra_to_reserve_on_growth) = match plan.style {
// Dynamic memories start with the minimum size plus the `reserve`
// amount specified to grow into.
MemoryStyle::Dynamic { reserve } => (minimum, usize::try_from(reserve).unwrap()),

// Static memories will never move in memory and consequently get
// their entire allocation up-front with no extra room to grow into.
// Note that the `maximum` is adjusted here to whatever the smaller
// of the two is, the `maximum` given or the `bound` specified for
// this memory.
MemoryStyle::Static { bound } => {
assert_ge!(bound, plan.memory.minimum);
(
usize::try_from(bound.checked_mul(WASM_PAGE_SIZE_U64).unwrap()).unwrap(),
0,
)
let bound_bytes =
usize::try_from(bound.checked_mul(WASM_PAGE_SIZE_U64).unwrap()).unwrap();
maximum = Some(bound_bytes.min(maximum.unwrap_or(usize::MAX)));
(bound_bytes, 0)
}
};
let request_bytes = pre_guard_bytes
Expand Down
16 changes: 16 additions & 0 deletions crates/wasmtime/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1119,6 +1119,22 @@ impl Config {
self
}

/// Indicates that the "static" style of memory should always be used.
///
/// 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, irregardless of what the
/// actual maximum size is on the memory's original type.
///
/// For the difference between static and dynamic memories, see the
/// [`Config::static_memory_maximum_size`].
pub fn static_memory_forced(&mut self, force: bool) -> &mut Self {
self.tunables.static_memory_bound_is_maximum = force;
self
}

/// Configures the size, in bytes, of the guard region used at the end of a
/// static memory's address space reservation.
///
Expand Down
6 changes: 6 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,10 @@ struct CommonOptions {
#[structopt(long, value_name = "MAXIMUM")]
static_memory_maximum_size: Option<u64>,

/// Force using a "static" style for all wasm memories.
#[structopt(long)]
static_memory_forced: bool,

/// Byte size of the guard region after static memories are allocated.
#[structopt(long, value_name = "SIZE")]
static_memory_guard_size: Option<u64>,
Expand Down Expand Up @@ -319,6 +323,8 @@ impl CommonOptions {
config.static_memory_maximum_size(max);
}

config.static_memory_forced(self.static_memory_forced);

if let Some(size) = self.static_memory_guard_size {
config.static_memory_guard_size(size);
}
Expand Down
14 changes: 14 additions & 0 deletions tests/all/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -362,3 +362,17 @@ fn tiny_static_heap() -> Result<()> {
f.call(&mut store, ())?;
Ok(())
}

#[test]
fn static_forced_max() -> Result<()> {
let mut config = Config::new();
config.static_memory_maximum_size(5 * 65536);
config.static_memory_forced(true);
let engine = Engine::new(&config)?;
let mut store = Store::new(&engine, ());

let mem = Memory::new(&mut store, MemoryType::new(0, None))?;
mem.grow(&mut store, 5).unwrap();
assert!(mem.grow(&mut store, 1).is_err());
Ok(())
}

0 comments on commit 6bcee7f

Please sign in to comment.