1d3132c9dSAlex Crichton //! Support for implementing the [`RuntimeLinearMemory`] trait in terms of a
2d3132c9dSAlex Crichton //! platform memory allocation primitive (e.g. `malloc`)
3d3132c9dSAlex Crichton //!
4d3132c9dSAlex Crichton //! Note that memory is allocated here using `Vec::try_reserve` to explicitly
5d3132c9dSAlex Crichton //! handle memory allocation failures.
6d3132c9dSAlex Crichton
7d3132c9dSAlex Crichton use crate::prelude::*;
8d3132c9dSAlex Crichton use crate::runtime::vm::SendSyncPtr;
990ac295eSAlex Crichton use crate::runtime::vm::memory::{MemoryBase, RuntimeLinearMemory};
10d3132c9dSAlex Crichton use core::mem;
11d3132c9dSAlex Crichton use core::ptr::NonNull;
12d3132c9dSAlex Crichton use wasmtime_environ::Tunables;
13d3132c9dSAlex Crichton
14d3132c9dSAlex Crichton #[repr(C, align(16))]
15d3132c9dSAlex Crichton #[derive(Copy, Clone)]
16d3132c9dSAlex Crichton pub struct Align16(u128);
17d3132c9dSAlex Crichton
18d3132c9dSAlex Crichton /// An instance of linear memory backed by the default system allocator.
19d3132c9dSAlex Crichton pub struct MallocMemory {
20d3132c9dSAlex Crichton storage: Vec<Align16>,
21d3132c9dSAlex Crichton base_ptr: SendSyncPtr<u8>,
22d3132c9dSAlex Crichton byte_len: usize,
23d3132c9dSAlex Crichton }
24d3132c9dSAlex Crichton
25d3132c9dSAlex Crichton impl MallocMemory {
new( _ty: &wasmtime_environ::Memory, tunables: &Tunables, minimum: usize, ) -> Result<Self>26d3132c9dSAlex Crichton pub fn new(
27d3132c9dSAlex Crichton _ty: &wasmtime_environ::Memory,
28d3132c9dSAlex Crichton tunables: &Tunables,
29d3132c9dSAlex Crichton minimum: usize,
30d3132c9dSAlex Crichton ) -> Result<Self> {
31d3132c9dSAlex Crichton if tunables.memory_guard_size > 0 {
32d3132c9dSAlex Crichton bail!("malloc memory is only compatible if guard pages aren't used");
33d3132c9dSAlex Crichton }
34d3132c9dSAlex Crichton if tunables.memory_reservation > 0 {
35d3132c9dSAlex Crichton bail!("malloc memory is only compatible with no ahead-of-time memory reservation");
36d3132c9dSAlex Crichton }
3734504fedSAlex Crichton if tunables.memory_init_cow {
3834504fedSAlex Crichton bail!("malloc memory cannot be used with CoW images");
3934504fedSAlex Crichton }
40d3132c9dSAlex Crichton
4127ce0babSAlex Crichton let initial_allocation_byte_size = minimum
429034e101SAlex Crichton .checked_add(tunables.memory_reservation_for_growth.try_into()?)
43d3132c9dSAlex Crichton .context("memory allocation size too large")?;
44d3132c9dSAlex Crichton
4527ce0babSAlex Crichton let initial_allocation_len = byte_size_to_element_len(initial_allocation_byte_size);
46d3132c9dSAlex Crichton let mut storage = Vec::new();
47c656ed3bSAlex Crichton storage
48c656ed3bSAlex Crichton .try_reserve(initial_allocation_len)
49c656ed3bSAlex Crichton .with_context(|| {
50c656ed3bSAlex Crichton format!(
51c656ed3bSAlex Crichton "failed to allocate {initial_allocation_byte_size:#x} \
52c656ed3bSAlex Crichton bytes ({minimum:#x} minimum + {:#x} memory_reservation_for_growth)",
53c656ed3bSAlex Crichton tunables.memory_reservation_for_growth,
54c656ed3bSAlex Crichton )
55c656ed3bSAlex Crichton })?;
5627ce0babSAlex Crichton
5727ce0babSAlex Crichton let initial_len = byte_size_to_element_len(minimum);
5827ce0babSAlex Crichton if initial_len > 0 {
5927ce0babSAlex Crichton grow_storage_to(&mut storage, initial_len);
6027ce0babSAlex Crichton }
61d3132c9dSAlex Crichton Ok(MallocMemory {
62d3132c9dSAlex Crichton base_ptr: SendSyncPtr::new(NonNull::new(storage.as_mut_ptr()).unwrap()).cast(),
63d3132c9dSAlex Crichton storage,
64d3132c9dSAlex Crichton byte_len: minimum,
65d3132c9dSAlex Crichton })
66d3132c9dSAlex Crichton }
67d3132c9dSAlex Crichton }
68d3132c9dSAlex Crichton
69d3132c9dSAlex Crichton impl RuntimeLinearMemory for MallocMemory {
byte_size(&self) -> usize70d3132c9dSAlex Crichton fn byte_size(&self) -> usize {
71d3132c9dSAlex Crichton self.byte_len
72d3132c9dSAlex Crichton }
73d3132c9dSAlex Crichton
byte_capacity(&self) -> usize74d3132c9dSAlex Crichton fn byte_capacity(&self) -> usize {
75d3132c9dSAlex Crichton self.storage.capacity() * mem::size_of::<Align16>()
76d3132c9dSAlex Crichton }
77d3132c9dSAlex Crichton
grow_to(&mut self, new_size: usize) -> Result<()>78d3132c9dSAlex Crichton fn grow_to(&mut self, new_size: usize) -> Result<()> {
79d3132c9dSAlex Crichton let new_element_len = byte_size_to_element_len(new_size);
80d3132c9dSAlex Crichton if new_element_len > self.storage.len() {
81d3132c9dSAlex Crichton self.storage
82c656ed3bSAlex Crichton .try_reserve(new_element_len - self.storage.len())
83c656ed3bSAlex Crichton .with_context(|| format!("failed to grow memory to {new_size:#x} bytes"))?;
8427ce0babSAlex Crichton grow_storage_to(&mut self.storage, new_element_len);
85d3132c9dSAlex Crichton self.base_ptr =
86d3132c9dSAlex Crichton SendSyncPtr::new(NonNull::new(self.storage.as_mut_ptr()).unwrap()).cast();
87d3132c9dSAlex Crichton }
88d3132c9dSAlex Crichton self.byte_len = new_size;
89d3132c9dSAlex Crichton Ok(())
90d3132c9dSAlex Crichton }
91d3132c9dSAlex Crichton
base(&self) -> MemoryBase92d5ee2a04SRain fn base(&self) -> MemoryBase {
93d5ee2a04SRain MemoryBase::Raw(self.base_ptr)
94d3132c9dSAlex Crichton }
95046a51caSNick Fitzgerald
vmmemory(&self) -> crate::vm::VMMemoryDefinition96046a51caSNick Fitzgerald fn vmmemory(&self) -> crate::vm::VMMemoryDefinition {
97046a51caSNick Fitzgerald let base = self.base_ptr.as_non_null();
98046a51caSNick Fitzgerald crate::vm::VMMemoryDefinition {
99046a51caSNick Fitzgerald base: base.into(),
100046a51caSNick Fitzgerald current_length: self.byte_len.into(),
101046a51caSNick Fitzgerald }
102046a51caSNick Fitzgerald }
103d3132c9dSAlex Crichton }
104d3132c9dSAlex Crichton
byte_size_to_element_len(byte_size: usize) -> usize105d3132c9dSAlex Crichton fn byte_size_to_element_len(byte_size: usize) -> usize {
106d3132c9dSAlex Crichton let align = mem::align_of::<Align16>();
107d3132c9dSAlex Crichton
108d3132c9dSAlex Crichton // Round up the requested byte size to the size of each vector element.
109d3132c9dSAlex Crichton let byte_size_rounded_up =
110d3132c9dSAlex Crichton byte_size.checked_add(align - 1).unwrap_or(usize::MAX) & !(align - 1);
111d3132c9dSAlex Crichton
112d3132c9dSAlex Crichton // Next divide this aligned size by the size of each element to get the
113d3132c9dSAlex Crichton // element length of our vector.
114d3132c9dSAlex Crichton byte_size_rounded_up / align
115d3132c9dSAlex Crichton }
11627ce0babSAlex Crichton
11727ce0babSAlex Crichton /// Helper that is the equivalent of `storage.resize(new_len, Align16(0))`
11827ce0babSAlex Crichton /// except it's also optimized to perform well in debug mode. Just using
11927ce0babSAlex Crichton /// `resize` leads to a per-element iteration which can be quite slow in debug
12027ce0babSAlex Crichton /// mode as it's not optimized to a memcpy, so it's manually optimized here
12127ce0babSAlex Crichton /// instead.
grow_storage_to(storage: &mut Vec<Align16>, new_len: usize)12227ce0babSAlex Crichton fn grow_storage_to(storage: &mut Vec<Align16>, new_len: usize) {
12327ce0babSAlex Crichton debug_assert!(new_len > storage.len());
12427ce0babSAlex Crichton assert!(new_len <= storage.capacity());
12527ce0babSAlex Crichton let capacity_to_set = new_len - storage.len();
12627ce0babSAlex Crichton let slice_to_initialize = &mut storage.spare_capacity_mut()[..capacity_to_set];
12727ce0babSAlex Crichton let byte_size = mem::size_of_val(slice_to_initialize);
12827ce0babSAlex Crichton
12927ce0babSAlex Crichton // SAFETY: The `slice_to_initialize` is guaranteed to be in the capacity of
13027ce0babSAlex Crichton // the vector via the slicing above, so it's all owned memory by the
13127ce0babSAlex Crichton // vector. Additionally the `byte_size` is the exact size of the
13227ce0babSAlex Crichton // `slice_to_initialize` itself, so this `memset` should be in-bounds.
13327ce0babSAlex Crichton // Finally the `Align16` is a simple wrapper around `u128` for which 0
13427ce0babSAlex Crichton // is a valid byte pattern. This should make the initial `write_bytes` safe.
13527ce0babSAlex Crichton //
13627ce0babSAlex Crichton // Afterwards the `set_len` call should also be safe because we've
13727ce0babSAlex Crichton // initialized the tail end of the vector with zeros so it's safe to
13827ce0babSAlex Crichton // consider it having a new length now.
13927ce0babSAlex Crichton unsafe {
14027ce0babSAlex Crichton core::ptr::write_bytes(slice_to_initialize.as_mut_ptr().cast::<u8>(), 0, byte_size);
14127ce0babSAlex Crichton storage.set_len(new_len);
14227ce0babSAlex Crichton }
14327ce0babSAlex Crichton }
14427ce0babSAlex Crichton
14527ce0babSAlex Crichton #[cfg(test)]
14627ce0babSAlex Crichton mod tests {
14727ce0babSAlex Crichton use super::*;
14827ce0babSAlex Crichton
14927ce0babSAlex Crichton // This is currently required by the constructor but otherwise ignored in
15027ce0babSAlex Crichton // the creation of a `MallocMemory`, so just have a single one used in
15127ce0babSAlex Crichton // tests below.
15227ce0babSAlex Crichton const TY: wasmtime_environ::Memory = wasmtime_environ::Memory {
15327ce0babSAlex Crichton idx_type: wasmtime_environ::IndexType::I32,
15427ce0babSAlex Crichton limits: wasmtime_environ::Limits { min: 0, max: None },
15527ce0babSAlex Crichton shared: false,
15627ce0babSAlex Crichton page_size_log2: 16,
15727ce0babSAlex Crichton };
15827ce0babSAlex Crichton
15927ce0babSAlex Crichton // Valid tunables that can be used to create a `MallocMemory`.
tunables() -> Tunables160*b8f882d1SDemilade Sonuga fn tunables() -> Tunables {
161*b8f882d1SDemilade Sonuga Tunables {
16227ce0babSAlex Crichton memory_reservation: 0,
16327ce0babSAlex Crichton memory_guard_size: 0,
16427ce0babSAlex Crichton memory_init_cow: false,
16527ce0babSAlex Crichton ..Tunables::default_miri()
166*b8f882d1SDemilade Sonuga }
167*b8f882d1SDemilade Sonuga }
16827ce0babSAlex Crichton
16927ce0babSAlex Crichton #[test]
simple()17027ce0babSAlex Crichton fn simple() {
171*b8f882d1SDemilade Sonuga let mut memory = MallocMemory::new(&TY, &tunables(), 10).unwrap();
17227ce0babSAlex Crichton assert_eq!(memory.storage.len(), 1);
17327ce0babSAlex Crichton assert_valid(&memory);
17427ce0babSAlex Crichton
17527ce0babSAlex Crichton memory.grow_to(11).unwrap();
17627ce0babSAlex Crichton assert_eq!(memory.storage.len(), 1);
17727ce0babSAlex Crichton assert_valid(&memory);
17827ce0babSAlex Crichton
17927ce0babSAlex Crichton memory.grow_to(16).unwrap();
18027ce0babSAlex Crichton assert_eq!(memory.storage.len(), 1);
18127ce0babSAlex Crichton assert_valid(&memory);
18227ce0babSAlex Crichton
18327ce0babSAlex Crichton memory.grow_to(17).unwrap();
18427ce0babSAlex Crichton assert_eq!(memory.storage.len(), 2);
18527ce0babSAlex Crichton assert_valid(&memory);
18627ce0babSAlex Crichton
18727ce0babSAlex Crichton memory.grow_to(65).unwrap();
18827ce0babSAlex Crichton assert_eq!(memory.storage.len(), 5);
18927ce0babSAlex Crichton assert_valid(&memory);
19027ce0babSAlex Crichton }
19127ce0babSAlex Crichton
19227ce0babSAlex Crichton #[test]
reservation_not_initialized()19327ce0babSAlex Crichton fn reservation_not_initialized() {
19427ce0babSAlex Crichton let tunables = Tunables {
19527ce0babSAlex Crichton memory_reservation_for_growth: 1 << 20,
196*b8f882d1SDemilade Sonuga ..tunables()
19727ce0babSAlex Crichton };
19827ce0babSAlex Crichton let mut memory = MallocMemory::new(&TY, &tunables, 10).unwrap();
19927ce0babSAlex Crichton assert_eq!(memory.storage.len(), 1);
20027ce0babSAlex Crichton assert_eq!(
20127ce0babSAlex Crichton memory.storage.capacity(),
20227ce0babSAlex Crichton (tunables.memory_reservation_for_growth / 16) as usize + 1,
20327ce0babSAlex Crichton );
20427ce0babSAlex Crichton assert_valid(&memory);
20527ce0babSAlex Crichton
20627ce0babSAlex Crichton memory.grow_to(100).unwrap();
20727ce0babSAlex Crichton assert_eq!(memory.storage.len(), 7);
20827ce0babSAlex Crichton assert_eq!(
20927ce0babSAlex Crichton memory.storage.capacity(),
21027ce0babSAlex Crichton (tunables.memory_reservation_for_growth / 16) as usize + 1,
21127ce0babSAlex Crichton );
21227ce0babSAlex Crichton assert_valid(&memory);
21327ce0babSAlex Crichton }
21427ce0babSAlex Crichton
assert_valid(mem: &MallocMemory)21527ce0babSAlex Crichton fn assert_valid(mem: &MallocMemory) {
21627ce0babSAlex Crichton assert_eq!(mem.storage.as_ptr().cast::<u8>(), mem.base_ptr.as_ptr());
21727ce0babSAlex Crichton assert!(mem.byte_len <= mem.storage.len() * 16);
21827ce0babSAlex Crichton for slot in mem.storage.iter() {
21927ce0babSAlex Crichton assert_eq!(slot.0, 0);
22027ce0babSAlex Crichton }
22127ce0babSAlex Crichton }
22227ce0babSAlex Crichton }
223