From 4a02a118d35ce7ebe7640aefc6a6f7bf293695eb Mon Sep 17 00:00:00 2001 From: Peter Huene Date: Wed, 9 Dec 2020 10:15:32 -0800 Subject: [PATCH] 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. --- crates/runtime/src/instance.rs | 4 +- crates/runtime/src/instance/allocator.rs | 12 +-- crates/runtime/src/lib.rs | 2 +- crates/runtime/src/memory.rs | 115 ++++++++++++++++++++++- 4 files changed, 121 insertions(+), 12 deletions(-) diff --git a/crates/runtime/src/instance.rs b/crates/runtime/src/instance.rs index 0ba72863e62f..1082bc4f0341 100644 --- a/crates/runtime/src/instance.rs +++ b/crates/runtime/src/instance.rs @@ -4,7 +4,7 @@ use crate::export::Export; use crate::externref::{StackMapRegistry, VMExternRefActivationsTable}; -use crate::memory::{RuntimeLinearMemory, RuntimeMemoryCreator}; +use crate::memory::{Memory, RuntimeMemoryCreator}; use crate::table::{Table, TableElement}; use crate::traphandlers::Trap; use crate::vmcontext::{ @@ -45,7 +45,7 @@ pub(crate) struct Instance { offsets: VMOffsets, /// WebAssembly linear memory data. - memories: PrimaryMap>, + memories: PrimaryMap, /// WebAssembly table data. tables: PrimaryMap, diff --git a/crates/runtime/src/instance/allocator.rs b/crates/runtime/src/instance/allocator.rs index 6e56780d36ea..cf2ff8493603 100644 --- a/crates/runtime/src/instance/allocator.rs +++ b/crates/runtime/src/instance/allocator.rs @@ -1,7 +1,7 @@ use crate::externref::{StackMapRegistry, VMExternRefActivationsTable}; use crate::imports::Imports; use crate::instance::{Instance, InstanceHandle, RuntimeMemoryCreator}; -use crate::memory::{DefaultMemoryCreator, RuntimeLinearMemory}; +use crate::memory::{DefaultMemoryCreator, Memory}; use crate::table::{Table, TableElement}; use crate::traphandlers::Trap; use crate::vmcontext::{ @@ -296,8 +296,7 @@ impl OnDemandInstanceAllocator { fn create_memories( &self, module: &Module, - ) -> Result>, InstantiationError> - { + ) -> Result, InstantiationError> { let creator = self .mem_creator .as_deref() @@ -306,11 +305,8 @@ impl OnDemandInstanceAllocator { let mut memories: PrimaryMap = PrimaryMap::with_capacity(module.memory_plans.len() - num_imports); for plan in &module.memory_plans.values().as_slice()[num_imports..] { - memories.push( - creator - .new_memory(plan) - .map_err(InstantiationError::Resource)?, - ); + memories + .push(Memory::new_dynamic(plan, creator).map_err(InstantiationError::Resource)?); } Ok(memories) } diff --git a/crates/runtime/src/lib.rs b/crates/runtime/src/lib.rs index 3bafaddd42ce..f283e5760ee1 100644 --- a/crates/runtime/src/lib.rs +++ b/crates/runtime/src/lib.rs @@ -42,7 +42,7 @@ pub use crate::instance::{ OnDemandInstanceAllocator, }; pub use crate::jit_int::GdbJitImageRegistration; -pub use crate::memory::{RuntimeLinearMemory, RuntimeMemoryCreator}; +pub use crate::memory::{Memory, RuntimeLinearMemory, RuntimeMemoryCreator}; pub use crate::mmap::Mmap; pub use crate::table::{Table, TableElement}; pub use crate::traphandlers::{ diff --git a/crates/runtime/src/memory.rs b/crates/runtime/src/memory.rs index a2c03e1d31d3..c2faddac3de1 100644 --- a/crates/runtime/src/memory.rs +++ b/crates/runtime/src/memory.rs @@ -5,7 +5,7 @@ use crate::mmap::Mmap; use crate::vmcontext::VMMemoryDefinition; use more_asserts::{assert_ge, assert_le}; -use std::cell::RefCell; +use std::cell::{Cell, RefCell}; use std::convert::TryFrom; use wasmtime_environ::{MemoryPlan, MemoryStyle, WASM_MAX_PAGES, WASM_PAGE_SIZE}; @@ -170,3 +170,116 @@ impl RuntimeLinearMemory for MmapMemory { } } } + +enum MemoryStorage { + Static { + base: *mut u8, + size: Cell, + maximum: u32, + make_accessible: Option bool>, + }, + Dynamic(Box), +} + +/// Represents an instantiation of a WebAssembly memory. +pub struct Memory { + storage: MemoryStorage, +} + +impl Memory { + /// Create a new dynamic (movable) memory instance for the specified plan. + pub fn new_dynamic( + plan: &MemoryPlan, + creator: &dyn RuntimeMemoryCreator, + ) -> Result { + Ok(Self { + storage: MemoryStorage::Dynamic(creator.new_memory(plan)?), + }) + } + + /// Create a new static (immovable) memory instance for the specified plan. + pub fn new_static( + plan: &MemoryPlan, + base: *mut u8, + maximum: u32, + make_accessible: Option bool>, + ) -> Result { + debug_assert!(plan.memory.maximum.unwrap_or(maximum) <= maximum); + + if plan.memory.minimum > 0 { + if let Some(make_accessible) = &make_accessible { + if !make_accessible(0, plan.memory.minimum as usize * WASM_PAGE_SIZE as usize) { + return Err("memory cannot be made accessible".into()); + } + } + } + + Ok(Self { + storage: MemoryStorage::Static { + base, + size: Cell::new(plan.memory.minimum), + maximum, + make_accessible, + }, + }) + } + + /// Returns the number of allocated wasm pages. + pub fn size(&self) -> u32 { + match &self.storage { + MemoryStorage::Static { size, .. } => size.get(), + MemoryStorage::Dynamic(mem) => mem.size(), + } + } + + /// Grow memory by the specified amount of wasm pages. + /// + /// Returns `None` if memory can't be grown by the specified amount + /// of wasm pages. + pub fn grow(&self, delta: u32) -> Option { + match &self.storage { + MemoryStorage::Static { + size, + maximum, + make_accessible, + .. + } => { + let old_size = size.get(); + if delta == 0 { + return Some(old_size); + } + + let new_size = old_size.checked_add(delta)?; + + if new_size > *maximum || new_size >= WASM_MAX_PAGES { + return None; + } + + let start = usize::try_from(old_size).unwrap() * WASM_PAGE_SIZE as usize; + let len = usize::try_from(delta).unwrap() * WASM_PAGE_SIZE as usize; + + if let Some(make_accessible) = make_accessible { + if !make_accessible(start, len) { + return None; + } + } + + size.set(new_size); + + Some(old_size) + } + MemoryStorage::Dynamic(mem) => mem.grow(delta), + } + } + + /// Return a `VMMemoryDefinition` for exposing the memory to compiled wasm code. + pub fn vmmemory(&self) -> VMMemoryDefinition { + match &self.storage { + MemoryStorage::Static { base, size, .. } => VMMemoryDefinition { + base: *base, + current_length: size.get() as usize, + }, + MemoryStorage::Dynamic(mem) => mem.vmmemory(), + } + } +}