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