linera_wasmer_compiler/engine/
tunables.rs

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