wasmer_compiler/engine/
tunables.rs

1use crate::engine::error::LinkError;
2use crate::types::target::{PointerWidth, Target};
3use std::ptr::NonNull;
4use wasmer_types::entity::{EntityRef, PrimaryMap};
5use wasmer_types::{
6    GlobalType, LocalGlobalIndex, LocalMemoryIndex, LocalTableIndex, MemoryIndex, MemoryType,
7    ModuleInfo, Pages, TableIndex, TableType,
8};
9use wasmer_vm::{InternalStoreHandle, MemoryError, StoreObjects};
10use wasmer_vm::{MemoryStyle, TableStyle};
11use wasmer_vm::{VMConfig, VMGlobal, VMMemory, VMTable};
12use wasmer_vm::{VMMemoryDefinition, VMTableDefinition};
13
14/// An engine delegates the creation of memories, tables, and globals
15/// to a foreign implementor of this trait.
16pub trait Tunables {
17    /// Construct a `MemoryStyle` for the provided `MemoryType`
18    fn memory_style(&self, memory: &MemoryType) -> MemoryStyle;
19
20    /// Construct a `TableStyle` for the provided `TableType`
21    fn table_style(&self, table: &TableType) -> TableStyle;
22
23    /// Create a memory owned by the host given a [`MemoryType`] and a [`MemoryStyle`].
24    fn create_host_memory(
25        &self,
26        ty: &MemoryType,
27        style: &MemoryStyle,
28    ) -> Result<VMMemory, MemoryError>;
29
30    /// Create a memory owned by the VM given a [`MemoryType`] and a [`MemoryStyle`].
31    ///
32    /// # Safety
33    /// - `vm_definition_location` must point to a valid location in VM memory.
34    unsafe fn create_vm_memory(
35        &self,
36        ty: &MemoryType,
37        style: &MemoryStyle,
38        vm_definition_location: NonNull<VMMemoryDefinition>,
39    ) -> Result<VMMemory, MemoryError>;
40
41    /// Create a table owned by the host given a [`TableType`] and a [`TableStyle`].
42    fn create_host_table(&self, ty: &TableType, style: &TableStyle) -> Result<VMTable, String>;
43
44    /// Create a table owned by the VM given a [`TableType`] and a [`TableStyle`].
45    ///
46    /// # Safety
47    /// - `vm_definition_location` must point to a valid location in VM memory.
48    unsafe fn create_vm_table(
49        &self,
50        ty: &TableType,
51        style: &TableStyle,
52        vm_definition_location: NonNull<VMTableDefinition>,
53    ) -> Result<VMTable, String>;
54
55    /// Create a global with an unset value.
56    fn create_global(&self, ty: GlobalType) -> Result<VMGlobal, String> {
57        Ok(VMGlobal::new(ty))
58    }
59
60    /// Allocate memory for just the memories of the current module.
61    ///
62    /// # Safety
63    /// - `memory_definition_locations` must point to a valid locations in VM memory.
64    #[allow(clippy::result_large_err)]
65    unsafe fn create_memories(
66        &self,
67        context: &mut StoreObjects,
68        module: &ModuleInfo,
69        memory_styles: &PrimaryMap<MemoryIndex, MemoryStyle>,
70        memory_definition_locations: &[NonNull<VMMemoryDefinition>],
71    ) -> Result<PrimaryMap<LocalMemoryIndex, InternalStoreHandle<VMMemory>>, LinkError> {
72        let num_imports = module.num_imported_memories;
73        let mut memories: PrimaryMap<LocalMemoryIndex, _> =
74            PrimaryMap::with_capacity(module.memories.len() - num_imports);
75        for (index, mdl) in memory_definition_locations
76            .iter()
77            .enumerate()
78            .take(module.memories.len())
79            .skip(num_imports)
80        {
81            let mi = MemoryIndex::new(index);
82            let ty = &module.memories[mi];
83            let style = &memory_styles[mi];
84            memories.push(InternalStoreHandle::new(
85                context,
86                self.create_vm_memory(ty, style, *mdl)
87                    .map_err(|e| LinkError::Resource(format!("Failed to create memory: {}", e)))?,
88            ));
89        }
90        Ok(memories)
91    }
92
93    /// Allocate memory for just the tables of the current module.
94    ///
95    /// # Safety
96    ///
97    /// To be done
98    #[allow(clippy::result_large_err)]
99    unsafe fn create_tables(
100        &self,
101        context: &mut StoreObjects,
102        module: &ModuleInfo,
103        table_styles: &PrimaryMap<TableIndex, TableStyle>,
104        table_definition_locations: &[NonNull<VMTableDefinition>],
105    ) -> Result<PrimaryMap<LocalTableIndex, InternalStoreHandle<VMTable>>, LinkError> {
106        let num_imports = module.num_imported_tables;
107        let mut tables: PrimaryMap<LocalTableIndex, _> =
108            PrimaryMap::with_capacity(module.tables.len() - num_imports);
109        for (index, tdl) in table_definition_locations
110            .iter()
111            .enumerate()
112            .take(module.tables.len())
113            .skip(num_imports)
114        {
115            let ti = TableIndex::new(index);
116            let ty = &module.tables[ti];
117            let style = &table_styles[ti];
118            tables.push(InternalStoreHandle::new(
119                context,
120                self.create_vm_table(ty, style, *tdl)
121                    .map_err(LinkError::Resource)?,
122            ));
123        }
124        Ok(tables)
125    }
126
127    /// Allocate memory for just the globals of the current module,
128    /// with initializers applied.
129    #[allow(clippy::result_large_err)]
130    fn create_globals(
131        &self,
132        context: &mut StoreObjects,
133        module: &ModuleInfo,
134    ) -> Result<PrimaryMap<LocalGlobalIndex, InternalStoreHandle<VMGlobal>>, LinkError> {
135        let num_imports = module.num_imported_globals;
136        let mut vmctx_globals = PrimaryMap::with_capacity(module.globals.len() - num_imports);
137
138        for &global_type in module.globals.values().skip(num_imports) {
139            vmctx_globals.push(InternalStoreHandle::new(
140                context,
141                self.create_global(global_type)
142                    .map_err(LinkError::Resource)?,
143            ));
144        }
145
146        Ok(vmctx_globals)
147    }
148
149    /// Get the VMConfig for this tunables
150    /// Currently, VMConfig have optional Stack size
151    /// If wasm_stack_size is left to None (the default value)
152    /// then the global stack size will be use
153    /// Else the defined stack size will be used. Size is in byte
154    /// and the value might be rounded to sane value is needed.
155    fn vmconfig(&self) -> &VMConfig {
156        &VMConfig {
157            wasm_stack_size: None,
158        }
159    }
160}
161
162/// Tunable parameters for WebAssembly compilation.
163/// This is the reference implementation of the `Tunables` trait,
164/// used by default.
165///
166/// You can use this as a template for creating a custom Tunables
167/// implementation or use composition to wrap your Tunables around
168/// this one. The later approach is demonstrated in the
169/// tunables-limit-memory example.
170#[derive(Clone)]
171pub struct BaseTunables {
172    /// For static heaps, the size in wasm pages of the heap protected by bounds checking.
173    pub static_memory_bound: Pages,
174
175    /// The size in bytes of the offset guard for static heaps.
176    pub static_memory_offset_guard_size: u64,
177
178    /// The size in bytes of the offset guard for dynamic heaps.
179    pub dynamic_memory_offset_guard_size: u64,
180}
181
182impl BaseTunables {
183    /// Get the `BaseTunables` for a specific Target
184    pub fn for_target(target: &Target) -> Self {
185        let triple = target.triple();
186        let pointer_width: PointerWidth = triple.pointer_width().unwrap();
187        let (static_memory_bound, static_memory_offset_guard_size): (Pages, u64) =
188            match pointer_width {
189                PointerWidth::U16 => (0x400.into(), 0x1000),
190                PointerWidth::U32 => (0x4000.into(), 0x1_0000),
191                // Static Memory Bound:
192                //   Allocating 4 GiB of address space let us avoid the
193                //   need for explicit bounds checks.
194                // Static Memory Guard size:
195                //   Allocating 2 GiB of address space lets us translate wasm
196                //   offsets into x86 offsets as aggressively as we can.
197                PointerWidth::U64 => (0x1_0000.into(), 0x8000_0000),
198            };
199
200        // Allocate a small guard to optimize common cases but without
201        // wasting too much memory.
202        // The Windows memory manager seems more laxed than the other ones
203        // And a guard of just 1 page may not be enough is some borderline cases
204        // So using 2 pages for guard on this platform
205        #[cfg(target_os = "windows")]
206        let dynamic_memory_offset_guard_size: u64 = 0x2_0000;
207        #[cfg(not(target_os = "windows"))]
208        let dynamic_memory_offset_guard_size: u64 = 0x1_0000;
209
210        Self {
211            static_memory_bound,
212            static_memory_offset_guard_size,
213            dynamic_memory_offset_guard_size,
214        }
215    }
216}
217
218impl Tunables for BaseTunables {
219    /// Get a `MemoryStyle` for the provided `MemoryType`
220    fn memory_style(&self, memory: &MemoryType) -> MemoryStyle {
221        // A heap with a maximum that doesn't exceed the static memory bound specified by the
222        // tunables make it static.
223        //
224        // If the module doesn't declare an explicit maximum treat it as 4GiB.
225        let maximum = memory.maximum.unwrap_or_else(Pages::max_value);
226        if maximum <= self.static_memory_bound {
227            MemoryStyle::Static {
228                // Bound can be larger than the maximum for performance reasons
229                bound: self.static_memory_bound,
230                offset_guard_size: self.static_memory_offset_guard_size,
231            }
232        } else {
233            MemoryStyle::Dynamic {
234                offset_guard_size: self.dynamic_memory_offset_guard_size,
235            }
236        }
237    }
238
239    /// Get a [`TableStyle`] for the provided [`TableType`].
240    fn table_style(&self, _table: &TableType) -> TableStyle {
241        TableStyle::CallerChecksSignature
242    }
243
244    /// Create a memory owned by the host given a [`MemoryType`] and a [`MemoryStyle`].
245    fn create_host_memory(
246        &self,
247        ty: &MemoryType,
248        style: &MemoryStyle,
249    ) -> Result<VMMemory, MemoryError> {
250        VMMemory::new(ty, style)
251    }
252
253    /// Create a memory owned by the VM given a [`MemoryType`] and a [`MemoryStyle`].
254    ///
255    /// # Safety
256    /// - `vm_definition_location` must point to a valid, owned `VMMemoryDefinition`,
257    ///   for example in `VMContext`.
258    unsafe fn create_vm_memory(
259        &self,
260        ty: &MemoryType,
261        style: &MemoryStyle,
262        vm_definition_location: NonNull<VMMemoryDefinition>,
263    ) -> Result<VMMemory, MemoryError> {
264        VMMemory::from_definition(ty, style, vm_definition_location)
265    }
266
267    /// Create a table owned by the host given a [`TableType`] and a [`TableStyle`].
268    fn create_host_table(&self, ty: &TableType, style: &TableStyle) -> Result<VMTable, String> {
269        VMTable::new(ty, style)
270    }
271
272    /// Create a table owned by the VM given a [`TableType`] and a [`TableStyle`].
273    ///
274    /// # Safety
275    /// - `vm_definition_location` must point to a valid, owned `VMTableDefinition`,
276    ///   for example in `VMContext`.
277    unsafe fn create_vm_table(
278        &self,
279        ty: &TableType,
280        style: &TableStyle,
281        vm_definition_location: NonNull<VMTableDefinition>,
282    ) -> Result<VMTable, String> {
283        VMTable::from_definition(ty, style, vm_definition_location)
284    }
285}
286
287impl Tunables for Box<dyn Tunables + Send + Sync> {
288    fn memory_style(&self, memory: &MemoryType) -> MemoryStyle {
289        self.as_ref().memory_style(memory)
290    }
291
292    fn table_style(&self, table: &TableType) -> TableStyle {
293        self.as_ref().table_style(table)
294    }
295
296    fn create_host_memory(
297        &self,
298        ty: &MemoryType,
299        style: &MemoryStyle,
300    ) -> Result<VMMemory, MemoryError> {
301        self.as_ref().create_host_memory(ty, style)
302    }
303
304    unsafe fn create_vm_memory(
305        &self,
306        ty: &MemoryType,
307        style: &MemoryStyle,
308        vm_definition_location: NonNull<VMMemoryDefinition>,
309    ) -> Result<VMMemory, MemoryError> {
310        self.as_ref()
311            .create_vm_memory(ty, style, vm_definition_location)
312    }
313
314    fn create_host_table(&self, ty: &TableType, style: &TableStyle) -> Result<VMTable, String> {
315        self.as_ref().create_host_table(ty, style)
316    }
317
318    unsafe fn create_vm_table(
319        &self,
320        ty: &TableType,
321        style: &TableStyle,
322        vm_definition_location: NonNull<VMTableDefinition>,
323    ) -> Result<VMTable, String> {
324        self.as_ref()
325            .create_vm_table(ty, style, vm_definition_location)
326    }
327}
328
329impl Tunables for std::sync::Arc<dyn Tunables + Send + Sync> {
330    fn memory_style(&self, memory: &MemoryType) -> MemoryStyle {
331        self.as_ref().memory_style(memory)
332    }
333
334    fn table_style(&self, table: &TableType) -> TableStyle {
335        self.as_ref().table_style(table)
336    }
337
338    fn create_host_memory(
339        &self,
340        ty: &MemoryType,
341        style: &MemoryStyle,
342    ) -> Result<VMMemory, MemoryError> {
343        self.as_ref().create_host_memory(ty, style)
344    }
345
346    unsafe fn create_vm_memory(
347        &self,
348        ty: &MemoryType,
349        style: &MemoryStyle,
350        vm_definition_location: NonNull<VMMemoryDefinition>,
351    ) -> Result<VMMemory, MemoryError> {
352        self.as_ref()
353            .create_vm_memory(ty, style, vm_definition_location)
354    }
355
356    fn create_host_table(&self, ty: &TableType, style: &TableStyle) -> Result<VMTable, String> {
357        self.as_ref().create_host_table(ty, style)
358    }
359
360    unsafe fn create_vm_table(
361        &self,
362        ty: &TableType,
363        style: &TableStyle,
364        vm_definition_location: NonNull<VMTableDefinition>,
365    ) -> Result<VMTable, String> {
366        self.as_ref()
367            .create_vm_table(ty, style, vm_definition_location)
368    }
369}