Skip to content

Commit 4a02a11

Browse files
committed
Change how Instance stores instantiated memories in the runtime.
This commit changes `Instance` such that memories can be stored statically, with just a base pointer, size, maximum, and a callback to make memory accessible. Previously the memories were being stored as boxed trait objects, which would require the pooling allocator to do some unpleasant things to avoid allocations. With this change, the pooling allocator can simply define a memory for the instance without using a trait object.
1 parent 6eec398 commit 4a02a11

4 files changed

Lines changed: 121 additions & 12 deletions

File tree

crates/runtime/src/instance.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
55
use crate::export::Export;
66
use crate::externref::{StackMapRegistry, VMExternRefActivationsTable};
7-
use crate::memory::{RuntimeLinearMemory, RuntimeMemoryCreator};
7+
use crate::memory::{Memory, RuntimeMemoryCreator};
88
use crate::table::{Table, TableElement};
99
use crate::traphandlers::Trap;
1010
use crate::vmcontext::{
@@ -45,7 +45,7 @@ pub(crate) struct Instance {
4545
offsets: VMOffsets,
4646

4747
/// WebAssembly linear memory data.
48-
memories: PrimaryMap<DefinedMemoryIndex, Box<dyn RuntimeLinearMemory>>,
48+
memories: PrimaryMap<DefinedMemoryIndex, Memory>,
4949

5050
/// WebAssembly table data.
5151
tables: PrimaryMap<DefinedTableIndex, Table>,

crates/runtime/src/instance/allocator.rs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::externref::{StackMapRegistry, VMExternRefActivationsTable};
22
use crate::imports::Imports;
33
use crate::instance::{Instance, InstanceHandle, RuntimeMemoryCreator};
4-
use crate::memory::{DefaultMemoryCreator, RuntimeLinearMemory};
4+
use crate::memory::{DefaultMemoryCreator, Memory};
55
use crate::table::{Table, TableElement};
66
use crate::traphandlers::Trap;
77
use crate::vmcontext::{
@@ -296,8 +296,7 @@ impl OnDemandInstanceAllocator {
296296
fn create_memories(
297297
&self,
298298
module: &Module,
299-
) -> Result<PrimaryMap<DefinedMemoryIndex, Box<dyn RuntimeLinearMemory>>, InstantiationError>
300-
{
299+
) -> Result<PrimaryMap<DefinedMemoryIndex, Memory>, InstantiationError> {
301300
let creator = self
302301
.mem_creator
303302
.as_deref()
@@ -306,11 +305,8 @@ impl OnDemandInstanceAllocator {
306305
let mut memories: PrimaryMap<DefinedMemoryIndex, _> =
307306
PrimaryMap::with_capacity(module.memory_plans.len() - num_imports);
308307
for plan in &module.memory_plans.values().as_slice()[num_imports..] {
309-
memories.push(
310-
creator
311-
.new_memory(plan)
312-
.map_err(InstantiationError::Resource)?,
313-
);
308+
memories
309+
.push(Memory::new_dynamic(plan, creator).map_err(InstantiationError::Resource)?);
314310
}
315311
Ok(memories)
316312
}

crates/runtime/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ pub use crate::instance::{
4242
OnDemandInstanceAllocator,
4343
};
4444
pub use crate::jit_int::GdbJitImageRegistration;
45-
pub use crate::memory::{RuntimeLinearMemory, RuntimeMemoryCreator};
45+
pub use crate::memory::{Memory, RuntimeLinearMemory, RuntimeMemoryCreator};
4646
pub use crate::mmap::Mmap;
4747
pub use crate::table::{Table, TableElement};
4848
pub use crate::traphandlers::{

crates/runtime/src/memory.rs

Lines changed: 114 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
use crate::mmap::Mmap;
66
use crate::vmcontext::VMMemoryDefinition;
77
use more_asserts::{assert_ge, assert_le};
8-
use std::cell::RefCell;
8+
use std::cell::{Cell, RefCell};
99
use std::convert::TryFrom;
1010
use wasmtime_environ::{MemoryPlan, MemoryStyle, WASM_MAX_PAGES, WASM_PAGE_SIZE};
1111

@@ -170,3 +170,116 @@ impl RuntimeLinearMemory for MmapMemory {
170170
}
171171
}
172172
}
173+
174+
enum MemoryStorage {
175+
Static {
176+
base: *mut u8,
177+
size: Cell<u32>,
178+
maximum: u32,
179+
make_accessible: Option<fn(usize, usize) -> bool>,
180+
},
181+
Dynamic(Box<dyn RuntimeLinearMemory>),
182+
}
183+
184+
/// Represents an instantiation of a WebAssembly memory.
185+
pub struct Memory {
186+
storage: MemoryStorage,
187+
}
188+
189+
impl Memory {
190+
/// Create a new dynamic (movable) memory instance for the specified plan.
191+
pub fn new_dynamic(
192+
plan: &MemoryPlan,
193+
creator: &dyn RuntimeMemoryCreator,
194+
) -> Result<Self, String> {
195+
Ok(Self {
196+
storage: MemoryStorage::Dynamic(creator.new_memory(plan)?),
197+
})
198+
}
199+
200+
/// Create a new static (immovable) memory instance for the specified plan.
201+
pub fn new_static(
202+
plan: &MemoryPlan,
203+
base: *mut u8,
204+
maximum: u32,
205+
make_accessible: Option<fn(usize, usize) -> bool>,
206+
) -> Result<Self, String> {
207+
debug_assert!(plan.memory.maximum.unwrap_or(maximum) <= maximum);
208+
209+
if plan.memory.minimum > 0 {
210+
if let Some(make_accessible) = &make_accessible {
211+
if !make_accessible(0, plan.memory.minimum as usize * WASM_PAGE_SIZE as usize) {
212+
return Err("memory cannot be made accessible".into());
213+
}
214+
}
215+
}
216+
217+
Ok(Self {
218+
storage: MemoryStorage::Static {
219+
base,
220+
size: Cell::new(plan.memory.minimum),
221+
maximum,
222+
make_accessible,
223+
},
224+
})
225+
}
226+
227+
/// Returns the number of allocated wasm pages.
228+
pub fn size(&self) -> u32 {
229+
match &self.storage {
230+
MemoryStorage::Static { size, .. } => size.get(),
231+
MemoryStorage::Dynamic(mem) => mem.size(),
232+
}
233+
}
234+
235+
/// Grow memory by the specified amount of wasm pages.
236+
///
237+
/// Returns `None` if memory can't be grown by the specified amount
238+
/// of wasm pages.
239+
pub fn grow(&self, delta: u32) -> Option<u32> {
240+
match &self.storage {
241+
MemoryStorage::Static {
242+
size,
243+
maximum,
244+
make_accessible,
245+
..
246+
} => {
247+
let old_size = size.get();
248+
if delta == 0 {
249+
return Some(old_size);
250+
}
251+
252+
let new_size = old_size.checked_add(delta)?;
253+
254+
if new_size > *maximum || new_size >= WASM_MAX_PAGES {
255+
return None;
256+
}
257+
258+
let start = usize::try_from(old_size).unwrap() * WASM_PAGE_SIZE as usize;
259+
let len = usize::try_from(delta).unwrap() * WASM_PAGE_SIZE as usize;
260+
261+
if let Some(make_accessible) = make_accessible {
262+
if !make_accessible(start, len) {
263+
return None;
264+
}
265+
}
266+
267+
size.set(new_size);
268+
269+
Some(old_size)
270+
}
271+
MemoryStorage::Dynamic(mem) => mem.grow(delta),
272+
}
273+
}
274+
275+
/// Return a `VMMemoryDefinition` for exposing the memory to compiled wasm code.
276+
pub fn vmmemory(&self) -> VMMemoryDefinition {
277+
match &self.storage {
278+
MemoryStorage::Static { base, size, .. } => VMMemoryDefinition {
279+
base: *base,
280+
current_length: size.get() as usize,
281+
},
282+
MemoryStorage::Dynamic(mem) => mem.vmmemory(),
283+
}
284+
}
285+
}

0 commit comments

Comments
 (0)