Skip to main content

wasmer_compiler/engine/
tunables.rs

1use crate::{DEFAULT_MAX_TABLE_ELEMENTS, engine::error::LinkError};
2use std::ptr::NonNull;
3use wasmer_types::{
4    FunctionType, GlobalType, LocalGlobalIndex, LocalMemoryIndex, LocalTableIndex, MemoryIndex,
5    MemoryType, ModuleInfo, TableIndex, TableType, TagKind, entity::PrimaryMap,
6};
7use wasmer_vm::{InternalStoreHandle, MemoryError, StoreObjects, VMTag};
8use wasmer_vm::{MemoryStyle, TableStyle};
9use wasmer_vm::{VMConfig, VMGlobal, VMGlobalDefinition, VMMemory, VMTable};
10use wasmer_vm::{VMMemoryDefinition, VMTableDefinition};
11
12/// An engine delegates the creation of memories, tables, and globals
13/// to a foreign implementor of this trait.
14pub trait Tunables {
15    /// Maximum total number of elements allowed across all of the module's local tables.
16    /// Cap the default total local table size at ~32 MiB (each table element occupies 4 x pointers).
17    fn max_table_elements(&self) -> u32 {
18        DEFAULT_MAX_TABLE_ELEMENTS
19    }
20
21    /// Construct a `MemoryStyle` for the provided `MemoryType`
22    fn memory_style(&self, memory: &MemoryType) -> MemoryStyle;
23
24    /// Construct a `TableStyle` for the provided `TableType`
25    fn table_style(&self, table: &TableType) -> TableStyle;
26
27    /// Create a memory owned by the host given a [`MemoryType`] and a [`MemoryStyle`].
28    fn create_host_memory(
29        &self,
30        ty: &MemoryType,
31        style: &MemoryStyle,
32    ) -> Result<VMMemory, MemoryError>;
33
34    /// Create a memory owned by the VM given a [`MemoryType`] and a [`MemoryStyle`].
35    ///
36    /// # Safety
37    /// - `vm_definition_location` must point to a valid location in VM memory.
38    unsafe fn create_vm_memory(
39        &self,
40        ty: &MemoryType,
41        style: &MemoryStyle,
42        vm_definition_location: NonNull<VMMemoryDefinition>,
43    ) -> Result<VMMemory, MemoryError>;
44
45    /// Create a table owned by the host given a [`TableType`] and a [`TableStyle`].
46    fn create_host_table(&self, ty: &TableType, style: &TableStyle) -> Result<VMTable, String>;
47
48    /// Create a table owned by the VM given a [`TableType`] and a [`TableStyle`].
49    ///
50    /// # Safety
51    /// - `vm_definition_location` must point to a valid location in VM memory.
52    unsafe fn create_vm_table(
53        &self,
54        ty: &TableType,
55        style: &TableStyle,
56        vm_definition_location: NonNull<VMTableDefinition>,
57    ) -> Result<VMTable, String>;
58
59    /// Create a global with an unset value.
60    fn create_global(&self, ty: GlobalType) -> Result<VMGlobal, String> {
61        Ok(VMGlobal::new(ty))
62    }
63
64    /// Create a global owned by the VM with backing storage in the `VMContext`.
65    ///
66    /// # Safety
67    /// - `vm_definition_location` must point to a valid location in VM memory.
68    unsafe fn create_vm_global(
69        &self,
70        ty: GlobalType,
71        vm_definition_location: NonNull<VMGlobalDefinition>,
72    ) -> Result<VMGlobal, String> {
73        unsafe { Ok(VMGlobal::new_instance(ty, vm_definition_location)) }
74    }
75
76    /// Create a new tag.
77    fn create_tag(&self, kind: TagKind, ty: FunctionType) -> Result<VMTag, String> {
78        Ok(VMTag::new(kind, ty))
79    }
80
81    /// Allocate memory for just the memories of the current module.
82    ///
83    /// # Safety
84    /// - `memory_definition_locations` must point to a valid locations in VM memory.
85    #[allow(clippy::result_large_err)]
86    unsafe fn create_memories(
87        &self,
88        context: &mut StoreObjects,
89        module: &ModuleInfo,
90        memory_styles: &PrimaryMap<MemoryIndex, MemoryStyle>,
91        memory_definition_locations: &[NonNull<VMMemoryDefinition>],
92    ) -> Result<PrimaryMap<LocalMemoryIndex, InternalStoreHandle<VMMemory>>, LinkError> {
93        unsafe {
94            let num_imports = module.num_imported_memories;
95            let mut memories: PrimaryMap<LocalMemoryIndex, _> =
96                PrimaryMap::with_capacity(module.memories.len() - num_imports);
97            for ((mi, ty), mdl) in module
98                .memories
99                .iter()
100                .skip(num_imports)
101                .zip(memory_definition_locations)
102            {
103                let style = &memory_styles[mi];
104                memories.push(InternalStoreHandle::new(
105                    context,
106                    self.create_vm_memory(ty, style, *mdl).map_err(|e| {
107                        LinkError::Resource(format!("Failed to create memory: {e}"))
108                    })?,
109                ));
110            }
111            Ok(memories)
112        }
113    }
114
115    /// Allocate memory for just the tables of the current module.
116    ///
117    /// # Safety
118    ///
119    /// To be done
120    #[allow(clippy::result_large_err)]
121    unsafe fn create_tables(
122        &self,
123        context: &mut StoreObjects,
124        module: &ModuleInfo,
125        table_styles: &PrimaryMap<TableIndex, TableStyle>,
126        table_definition_locations: &[NonNull<VMTableDefinition>],
127    ) -> Result<PrimaryMap<LocalTableIndex, InternalStoreHandle<VMTable>>, LinkError> {
128        let max_table_elements = self.max_table_elements();
129        let total_table_elements = module
130            .tables
131            .values()
132            .skip(module.num_imported_tables)
133            .fold(0u32, |total, ty| total.saturating_add(ty.minimum));
134        if total_table_elements > max_table_elements {
135            return Err(LinkError::Resource(format!(
136                "Total table ({total_table_elements}) is larger than maximum allowed size ({max_table_elements})!",
137            )));
138        }
139
140        let num_imports = module.num_imported_tables;
141        let mut tables: PrimaryMap<LocalTableIndex, _> =
142            PrimaryMap::with_capacity(module.tables.len() - num_imports);
143        for ((ti, ty), tdl) in module
144            .tables
145            .iter()
146            .skip(num_imports)
147            .zip(table_definition_locations)
148        {
149            let style = &table_styles[ti];
150            tables.push(InternalStoreHandle::new(context, unsafe {
151                self.create_vm_table(ty, style, *tdl)
152                    .map_err(LinkError::Resource)?
153            }));
154        }
155        Ok(tables)
156    }
157
158    /// Allocate memory for just the globals of the current module,
159    /// with initializers applied.
160    #[allow(clippy::result_large_err)]
161    fn create_globals(
162        &self,
163        context: &mut StoreObjects,
164        module: &ModuleInfo,
165        vm_definition_locations: &[NonNull<VMGlobalDefinition>],
166    ) -> Result<PrimaryMap<LocalGlobalIndex, InternalStoreHandle<VMGlobal>>, LinkError> {
167        let num_imports = module.num_imported_globals;
168        let mut vmctx_globals = PrimaryMap::with_capacity(module.globals.len() - num_imports);
169
170        for (i, &global_type) in module.globals.values().skip(num_imports).enumerate() {
171            let location = vm_definition_locations
172                .get(i)
173                .ok_or_else(|| LinkError::Resource("global definition location missing".into()))?;
174            vmctx_globals.push(InternalStoreHandle::new(context, unsafe {
175                self.create_vm_global(global_type, *location)
176                    .map_err(LinkError::Resource)?
177            }));
178        }
179
180        Ok(vmctx_globals)
181    }
182
183    /// Get the VMConfig for this tunables
184    /// Currently, VMConfig have optional Stack size
185    /// If wasm_stack_size is left to None (the default value)
186    /// then the global stack size will be use
187    /// Else the defined stack size will be used. Size is in byte
188    /// and the value might be rounded to sane value is needed.
189    fn vmconfig(&self) -> &VMConfig {
190        &VMConfig {
191            wasm_stack_size: None,
192        }
193    }
194}
195
196/// Tunable parameters for WebAssembly compilation.
197/// This is the reference implementation of the `Tunables` trait,
198/// used by default.
199///
200/// You can use this as a template for creating a custom Tunables
201/// implementation or use composition to wrap your Tunables around
202/// this one. The later approach is demonstrated in the
203/// tunables-limit-memory example.
204#[derive(Clone, Default)]
205pub struct BaseTunables {}
206
207impl BaseTunables {
208    /// Get the default `BaseTunables`.
209    pub fn new() -> Self {
210        Self {}
211    }
212}
213
214impl Tunables for BaseTunables {
215    /// Always return Static memory style.
216    fn memory_style(&self, _memory: &MemoryType) -> MemoryStyle {
217        MemoryStyle::Static
218    }
219
220    /// Get a [`TableStyle`] for the provided [`TableType`].
221    fn table_style(&self, _table: &TableType) -> TableStyle {
222        TableStyle::CallerChecksSignature
223    }
224
225    /// Create a memory owned by the host given a [`MemoryType`] and a [`MemoryStyle`].
226    fn create_host_memory(
227        &self,
228        ty: &MemoryType,
229        style: &MemoryStyle,
230    ) -> Result<VMMemory, MemoryError> {
231        VMMemory::new(ty, style)
232    }
233
234    /// Create a memory owned by the VM given a [`MemoryType`] and a [`MemoryStyle`].
235    ///
236    /// # Safety
237    /// - `vm_definition_location` must point to a valid, owned `VMMemoryDefinition`,
238    ///   for example in `VMContext`.
239    unsafe fn create_vm_memory(
240        &self,
241        ty: &MemoryType,
242        style: &MemoryStyle,
243        vm_definition_location: NonNull<VMMemoryDefinition>,
244    ) -> Result<VMMemory, MemoryError> {
245        unsafe { VMMemory::from_definition(ty, style, vm_definition_location) }
246    }
247
248    /// Create a table owned by the host given a [`TableType`] and a [`TableStyle`].
249    fn create_host_table(&self, ty: &TableType, style: &TableStyle) -> Result<VMTable, String> {
250        VMTable::new(ty, style)
251    }
252
253    /// Create a table owned by the VM given a [`TableType`] and a [`TableStyle`].
254    ///
255    /// # Safety
256    /// - `vm_definition_location` must point to a valid, owned `VMTableDefinition`,
257    ///   for example in `VMContext`.
258    unsafe fn create_vm_table(
259        &self,
260        ty: &TableType,
261        style: &TableStyle,
262        vm_definition_location: NonNull<VMTableDefinition>,
263    ) -> Result<VMTable, String> {
264        unsafe { VMTable::from_definition(ty, style, vm_definition_location) }
265    }
266}
267
268impl Tunables for Box<dyn Tunables + Send + Sync> {
269    fn max_table_elements(&self) -> u32 {
270        self.as_ref().max_table_elements()
271    }
272
273    fn memory_style(&self, memory: &MemoryType) -> MemoryStyle {
274        self.as_ref().memory_style(memory)
275    }
276
277    fn table_style(&self, table: &TableType) -> TableStyle {
278        self.as_ref().table_style(table)
279    }
280
281    fn create_host_memory(
282        &self,
283        ty: &MemoryType,
284        style: &MemoryStyle,
285    ) -> Result<VMMemory, MemoryError> {
286        self.as_ref().create_host_memory(ty, style)
287    }
288
289    unsafe fn create_vm_memory(
290        &self,
291        ty: &MemoryType,
292        style: &MemoryStyle,
293        vm_definition_location: NonNull<VMMemoryDefinition>,
294    ) -> Result<VMMemory, MemoryError> {
295        unsafe {
296            self.as_ref()
297                .create_vm_memory(ty, style, vm_definition_location)
298        }
299    }
300
301    fn create_host_table(&self, ty: &TableType, style: &TableStyle) -> Result<VMTable, String> {
302        self.as_ref().create_host_table(ty, style)
303    }
304
305    unsafe fn create_vm_table(
306        &self,
307        ty: &TableType,
308        style: &TableStyle,
309        vm_definition_location: NonNull<VMTableDefinition>,
310    ) -> Result<VMTable, String> {
311        unsafe {
312            self.as_ref()
313                .create_vm_table(ty, style, vm_definition_location)
314        }
315    }
316}
317
318impl Tunables for std::sync::Arc<dyn Tunables + Send + Sync> {
319    fn max_table_elements(&self) -> u32 {
320        self.as_ref().max_table_elements()
321    }
322
323    fn memory_style(&self, memory: &MemoryType) -> MemoryStyle {
324        self.as_ref().memory_style(memory)
325    }
326
327    fn table_style(&self, table: &TableType) -> TableStyle {
328        self.as_ref().table_style(table)
329    }
330
331    fn create_host_memory(
332        &self,
333        ty: &MemoryType,
334        style: &MemoryStyle,
335    ) -> Result<VMMemory, MemoryError> {
336        self.as_ref().create_host_memory(ty, style)
337    }
338
339    unsafe fn create_vm_memory(
340        &self,
341        ty: &MemoryType,
342        style: &MemoryStyle,
343        vm_definition_location: NonNull<VMMemoryDefinition>,
344    ) -> Result<VMMemory, MemoryError> {
345        unsafe {
346            self.as_ref()
347                .create_vm_memory(ty, style, vm_definition_location)
348        }
349    }
350
351    fn create_host_table(&self, ty: &TableType, style: &TableStyle) -> Result<VMTable, String> {
352        self.as_ref().create_host_table(ty, style)
353    }
354
355    unsafe fn create_vm_table(
356        &self,
357        ty: &TableType,
358        style: &TableStyle,
359        vm_definition_location: NonNull<VMTableDefinition>,
360    ) -> Result<VMTable, String> {
361        unsafe {
362            self.as_ref()
363                .create_vm_table(ty, style, vm_definition_location)
364        }
365    }
366}