Skip to main content

wasmer_types/
vmoffsets.rs

1// This file contains code from external sources.
2// Attributions: https://github.com/wasmerio/wasmer/blob/main/docs/ATTRIBUTIONS.md
3
4//! Offsets and sizes of various structs in wasmer-vm's vmcontext
5//! module.
6
7#![deny(rustdoc::broken_intra_doc_links)]
8
9use crate::{
10    FunctionIndex, GlobalIndex, LocalGlobalIndex, LocalMemoryIndex, LocalTableIndex, MemoryIndex,
11    ModuleInfo, TableIndex, TableType, WasmError, WasmResult, entity::EntityRef,
12};
13use more_asserts::assert_lt;
14use std::convert::TryFrom;
15
16/// An index type for builtin functions.
17#[derive(Copy, Clone, Debug)]
18pub struct VMBuiltinFunctionIndex(u32);
19
20impl VMBuiltinFunctionIndex {
21    /// Returns an index for wasm's `memory.grow` builtin function.
22    pub const fn get_memory32_grow_index() -> Self {
23        Self(0)
24    }
25    /// Returns an index for wasm's imported `memory.grow` builtin function.
26    pub const fn get_imported_memory32_grow_index() -> Self {
27        Self(1)
28    }
29    /// Returns an index for wasm's `memory.size` builtin function.
30    pub const fn get_memory32_size_index() -> Self {
31        Self(2)
32    }
33    /// Returns an index for wasm's imported `memory.size` builtin function.
34    pub const fn get_imported_memory32_size_index() -> Self {
35        Self(3)
36    }
37    /// Returns an index for wasm's `table.copy` when both tables are locally
38    /// defined.
39    pub const fn get_table_copy_index() -> Self {
40        Self(4)
41    }
42    /// Returns an index for wasm's `table.init`.
43    pub const fn get_table_init_index() -> Self {
44        Self(5)
45    }
46    /// Returns an index for wasm's `elem.drop`.
47    pub const fn get_elem_drop_index() -> Self {
48        Self(6)
49    }
50    /// Returns an index for wasm's `memory.copy`.
51    pub const fn get_memory_copy_index() -> Self {
52        Self(7)
53    }
54    /// Returns an index for wasm's `memory.fill` for locally defined memories.
55    pub const fn get_memory_fill_index() -> Self {
56        Self(8)
57    }
58    /// Returns an index for wasm's `memory.fill` for imported memories.
59    pub const fn get_imported_memory_fill_index() -> Self {
60        Self(9)
61    }
62    /// Returns an index for wasm's `memory.init` instruction.
63    pub const fn get_memory_init_index() -> Self {
64        Self(10)
65    }
66    /// Returns an index for wasm's `data.drop` instruction.
67    pub const fn get_data_drop_index() -> Self {
68        Self(11)
69    }
70    /// Returns an index for wasm's `raise_trap` instruction.
71    pub const fn get_raise_trap_index() -> Self {
72        Self(12)
73    }
74    /// Returns an index for wasm's `table.size` instruction for local tables.
75    pub const fn get_table_size_index() -> Self {
76        Self(13)
77    }
78    /// Returns an index for wasm's `table.size` instruction for imported tables.
79    pub const fn get_imported_table_size_index() -> Self {
80        Self(14)
81    }
82    /// Returns an index for wasm's `table.grow` instruction for local tables.
83    pub const fn get_table_grow_index() -> Self {
84        Self(15)
85    }
86    /// Returns an index for wasm's `table.grow` instruction for imported tables.
87    pub const fn get_imported_table_grow_index() -> Self {
88        Self(16)
89    }
90    /// Returns an index for wasm's `table.get` instruction for local tables.
91    pub const fn get_table_get_index() -> Self {
92        Self(17)
93    }
94    /// Returns an index for wasm's `table.get` instruction for imported tables.
95    pub const fn get_imported_table_get_index() -> Self {
96        Self(18)
97    }
98    /// Returns an index for wasm's `table.set` instruction for local tables.
99    pub const fn get_table_set_index() -> Self {
100        Self(19)
101    }
102    /// Returns an index for wasm's `table.set` instruction for imported tables.
103    pub const fn get_imported_table_set_index() -> Self {
104        Self(20)
105    }
106    /// Returns an index for wasm's `func.ref` instruction.
107    pub const fn get_func_ref_index() -> Self {
108        Self(21)
109    }
110    /// Returns an index for wasm's `table.fill` instruction for local tables.
111    pub const fn get_table_fill_index() -> Self {
112        Self(22)
113    }
114    /// Returns an index for wasm's local `memory.atomic.wait32` builtin function.
115    pub const fn get_memory_atomic_wait32_index() -> Self {
116        Self(23)
117    }
118    /// Returns an index for wasm's imported `memory.atomic.wait32` builtin function.
119    pub const fn get_imported_memory_atomic_wait32_index() -> Self {
120        Self(24)
121    }
122    /// Returns an index for wasm's local `memory.atomic.wait64` builtin function.
123    pub const fn get_memory_atomic_wait64_index() -> Self {
124        Self(25)
125    }
126    /// Returns an index for wasm's imported `memory.atomic.wait64` builtin function.
127    pub const fn get_imported_memory_atomic_wait64_index() -> Self {
128        Self(26)
129    }
130    /// Returns an index for wasm's local `memory.atomic.notify` builtin function.
131    pub const fn get_memory_atomic_notify_index() -> Self {
132        Self(27)
133    }
134
135    /// Returns an index for wasm's imported `memory.atomic.notify` builtin function.
136    pub const fn get_imported_memory_atomic_notify_index() -> Self {
137        Self(28)
138    }
139
140    /// Returns an index for wasm's imported `debug_usize` builtin function.
141    pub const fn get_imported_debug_usize_index() -> Self {
142        Self(29)
143    }
144
145    /// Returns an index for wasm's imported `debug_str` builtin function.
146    pub const fn get_imported_debug_str_index() -> Self {
147        Self(30)
148    }
149
150    /// Returns an index for wasm's imported `wasmer_eh_personality2` builtin function.
151    pub const fn get_imported_personality2_index() -> Self {
152        Self(31)
153    }
154
155    /// Returns an index for wasm's imported `alloc_exception` builtin function.
156    pub const fn get_imported_alloc_exception_index() -> Self {
157        Self(32)
158    }
159
160    /// Returns an index for wasm's imported `throw` builtin function.
161    pub const fn get_imported_throw_index() -> Self {
162        Self(33)
163    }
164
165    /// Returns an index for wasm's imported `read_exnref` builtin function.
166    pub const fn get_imported_read_exnref_index() -> Self {
167        Self(34)
168    }
169
170    /// Returns an index for wasm's imported `exception_into_exnref` builtin function.
171    pub const fn get_imported_exception_into_exnref_index() -> Self {
172        Self(35)
173    }
174
175    /// Returns the total number of builtin functions.
176    pub const fn builtin_functions_total_number() -> u32 {
177        36
178    }
179
180    /// Return the index as an u32 number.
181    pub const fn index(self) -> u32 {
182        self.0
183    }
184}
185
186#[cfg(target_pointer_width = "32")]
187fn cast_to_u32(sz: usize) -> u32 {
188    u32::try_from(sz).unwrap()
189}
190#[cfg(target_pointer_width = "64")]
191fn cast_to_u32(sz: usize) -> u32 {
192    u32::try_from(sz).expect("overflow in cast from usize to u32")
193}
194
195/// Align an offset used in this module to a specific byte-width by rounding up
196#[inline]
197const fn align(offset: u32, width: u32) -> u32 {
198    offset.div_ceil(width) * width
199}
200
201/// This class computes offsets to fields within VMContext and other
202/// related structs that JIT code accesses directly.
203#[derive(Clone, Debug)]
204pub struct VMOffsets {
205    /// The size in bytes of a pointer on the target.
206    pointer_size: u8,
207    /// The number of imported functions in the module.
208    num_imported_functions: u32,
209    /// The number of imported tables in the module.
210    num_imported_tables: u32,
211    /// The number of imported memories in the module.
212    num_imported_memories: u32,
213    /// The number of tags in the module.
214    num_tag_ids: u32,
215    /// The number of imported globals in the module.
216    num_imported_globals: u32,
217    /// The number of defined tables in the module.
218    num_local_tables: u32,
219    /// The number of defined memories in the module.
220    num_local_memories: u32,
221    /// The number of defined globals in the module.
222    num_local_globals: u32,
223    /// Relative offsets of inline `VMCallerCheckedAnyfunc` arrays for local fixed `funcref`
224    /// tables. Non-fixed tables do not have inline storage.
225    local_fixed_funcref_table_offsets: Vec<Option<u32>>,
226    /// Total size in bytes of all inline fixed `funcref` table storage in `VMContext`.
227    size_of_local_fixed_funcref_tables: u32,
228
229    vmctx_imported_functions_begin: u32,
230    vmctx_imported_tables_begin: u32,
231    vmctx_imported_memories_begin: u32,
232    vmctx_tag_ids_begin: u32,
233    vmctx_imported_globals_begin: u32,
234    vmctx_tables_begin: u32,
235    vmctx_fixed_funcref_tables_begin: u32,
236    vmctx_memories_begin: u32,
237    vmctx_globals_begin: u32,
238    vmctx_builtin_functions_begin: u32,
239    vmctx_trap_handler_begin: u32,
240    vmctx_gas_limiter_pointer: u32,
241    vmctx_stack_limit_begin: u32,
242    vmctx_stack_limit_initial_begin: u32,
243    size_of_vmctx: u32,
244}
245
246impl VMOffsets {
247    #[deprecated(note = "Use try_new function instead")]
248    /// Return a new `VMOffsets` instance, for a given pointer size
249    /// (panics if the VM offsets cannot be properly created).
250    pub fn new(pointer_size: u8, module: &ModuleInfo) -> Self {
251        Self::try_new(pointer_size, module).unwrap()
252    }
253
254    /// Return a new `VMOffsets` instance, for a given pointer size.
255    pub fn try_new(pointer_size: u8, module: &ModuleInfo) -> Result<Self, String> {
256        let mut local_fixed_funcref_table_offsets =
257            vec![None; module.tables.len() - module.num_imported_tables];
258        let mut size_of_local_fixed_funcref_tables: u32 = 0;
259        // TODO: ensure it matches size_of::<VMCallerCheckedAnyfunc>
260        let size_of_vmcaller_checked_anyfunc = 4 * u32::from(pointer_size);
261
262        let fixed_table_err = |table: &TableType| {
263            format!(
264                "cannot allocate fixed Table with {} elements",
265                table.minimum,
266            )
267        };
268        for (table_index, table) in module.tables.iter() {
269            if let Some(local_table_index) = module.local_table_index(table_index)
270                && table.is_fixed_funcref_table()
271            {
272                local_fixed_funcref_table_offsets[local_table_index.index()] =
273                    Some(size_of_local_fixed_funcref_tables);
274                size_of_local_fixed_funcref_tables = size_of_local_fixed_funcref_tables
275                    .checked_add(
276                        table
277                            .minimum
278                            .checked_mul(size_of_vmcaller_checked_anyfunc)
279                            .ok_or_else(|| fixed_table_err(table))?,
280                    )
281                    .ok_or_else(|| fixed_table_err(table))?;
282            }
283        }
284
285        let mut ret = Self {
286            pointer_size,
287            num_imported_functions: cast_to_u32(module.num_imported_functions),
288            num_imported_tables: cast_to_u32(module.num_imported_tables),
289            num_imported_memories: cast_to_u32(module.num_imported_memories),
290            num_tag_ids: cast_to_u32(module.tags.len()),
291            num_imported_globals: cast_to_u32(module.num_imported_globals),
292            num_local_tables: cast_to_u32(module.tables.len()),
293            num_local_memories: cast_to_u32(module.memories.len()),
294            num_local_globals: cast_to_u32(module.globals.len()),
295            local_fixed_funcref_table_offsets,
296            size_of_local_fixed_funcref_tables,
297            vmctx_imported_functions_begin: 0,
298            vmctx_imported_tables_begin: 0,
299            vmctx_imported_memories_begin: 0,
300            vmctx_tag_ids_begin: 0,
301            vmctx_imported_globals_begin: 0,
302            vmctx_tables_begin: 0,
303            vmctx_fixed_funcref_tables_begin: 0,
304            vmctx_memories_begin: 0,
305            vmctx_globals_begin: 0,
306            vmctx_builtin_functions_begin: 0,
307            vmctx_trap_handler_begin: 0,
308            vmctx_gas_limiter_pointer: 0,
309            vmctx_stack_limit_begin: 0,
310            vmctx_stack_limit_initial_begin: 0,
311            size_of_vmctx: 0,
312        };
313        ret.precompute()?;
314        Ok(ret)
315    }
316
317    /// Return a new `VMOffsets` instance, for a given pointer size
318    /// skipping the `ModuleInfo`.
319    ///
320    /// Note: This should only when generating code for trampolines.
321    pub fn new_for_trampolines(pointer_size: u8) -> Self {
322        Self {
323            pointer_size,
324            num_imported_functions: 0,
325            num_imported_tables: 0,
326            num_imported_memories: 0,
327            num_tag_ids: 0,
328            num_imported_globals: 0,
329            num_local_tables: 0,
330            num_local_memories: 0,
331            num_local_globals: 0,
332            local_fixed_funcref_table_offsets: Vec::new(),
333            size_of_local_fixed_funcref_tables: 0,
334            vmctx_imported_functions_begin: 0,
335            vmctx_imported_tables_begin: 0,
336            vmctx_imported_memories_begin: 0,
337            vmctx_tag_ids_begin: 0,
338            vmctx_imported_globals_begin: 0,
339            vmctx_tables_begin: 0,
340            vmctx_fixed_funcref_tables_begin: 0,
341            vmctx_memories_begin: 0,
342            vmctx_globals_begin: 0,
343            vmctx_builtin_functions_begin: 0,
344            vmctx_trap_handler_begin: 0,
345            vmctx_gas_limiter_pointer: 0,
346            vmctx_stack_limit_begin: 0,
347            vmctx_stack_limit_initial_begin: 0,
348            size_of_vmctx: 0,
349        }
350    }
351
352    /// Number of local tables defined in the module
353    pub fn num_local_tables(&self) -> u32 {
354        self.num_local_tables
355    }
356
357    /// Number of local memories defined in the module
358    pub fn num_local_memories(&self) -> u32 {
359        self.num_local_memories
360    }
361
362    /// Number of local globals defined in the module
363    pub fn num_local_globals(&self) -> u32 {
364        self.num_local_globals
365    }
366
367    fn precompute(&mut self) -> Result<(), &'static str> {
368        const ERR_MESSAGE: &str = "precompute exceeds u32 type";
369
370        /// Offset base by num_items items of size item_size, panicking on overflow
371        fn offset_by(base: u32, num_items: u32, item_size: u32) -> Result<u32, &'static str> {
372            base.checked_add(num_items.checked_mul(item_size).ok_or(ERR_MESSAGE)?)
373                .ok_or(ERR_MESSAGE)
374        }
375        // Offset base by num_items items of size item_size, panicking on overflow
376        // Also, will align the value on pointer size boundary,
377        // to avoid misalignment issue
378        let pointer_size = self.pointer_size as u32;
379        let offset_by_aligned =
380            |base: u32, num_items: u32, item_size: u32| -> Result<u32, &'static str> {
381                Ok(align(
382                    base.checked_add(num_items.checked_mul(item_size).ok_or(ERR_MESSAGE)?)
383                        .ok_or(ERR_MESSAGE)?,
384                    pointer_size,
385                ))
386            };
387
388        self.vmctx_imported_functions_begin = 0;
389        self.vmctx_imported_tables_begin = offset_by_aligned(
390            self.vmctx_imported_functions_begin,
391            self.num_imported_functions,
392            u32::from(self.size_of_vmfunction_import()),
393        )?;
394        self.vmctx_imported_memories_begin = offset_by_aligned(
395            self.vmctx_imported_tables_begin,
396            self.num_imported_tables,
397            u32::from(self.size_of_vmtable_import()),
398        )?;
399
400        self.vmctx_tag_ids_begin = offset_by_aligned(
401            self.vmctx_imported_memories_begin,
402            self.num_imported_memories,
403            u32::from(self.size_of_vmmemory_import()),
404        )?;
405
406        self.vmctx_imported_globals_begin = offset_by_aligned(
407            self.vmctx_tag_ids_begin,
408            self.num_tag_ids,
409            u32::from(self.size_of_vmshared_tag_index()),
410        )?;
411
412        self.vmctx_tables_begin = offset_by_aligned(
413            self.vmctx_imported_globals_begin,
414            self.num_imported_globals,
415            u32::from(self.size_of_vmglobal_import()),
416        )?;
417        self.vmctx_fixed_funcref_tables_begin = offset_by_aligned(
418            self.vmctx_tables_begin,
419            self.num_local_tables,
420            u32::from(self.size_of_vmtable_definition()),
421        )?;
422        self.vmctx_memories_begin = align(
423            self.vmctx_fixed_funcref_tables_begin
424                .checked_add(self.size_of_local_fixed_funcref_tables)
425                .ok_or(ERR_MESSAGE)?,
426            pointer_size,
427        );
428        self.vmctx_globals_begin = align(
429            offset_by(
430                self.vmctx_memories_begin,
431                self.num_local_memories,
432                u32::from(self.size_of_vmmemory_definition()),
433            )?,
434            16,
435        );
436        self.vmctx_builtin_functions_begin = offset_by(
437            self.vmctx_globals_begin,
438            self.num_local_globals,
439            u32::from(self.size_of_vmglobal_local()),
440        )?;
441        self.vmctx_trap_handler_begin = offset_by(
442            self.vmctx_builtin_functions_begin,
443            VMBuiltinFunctionIndex::builtin_functions_total_number(),
444            u32::from(self.pointer_size),
445        )?;
446        self.vmctx_gas_limiter_pointer = offset_by(
447            self.vmctx_trap_handler_begin,
448            1,
449            u32::from(self.pointer_size),
450        )?;
451        self.vmctx_stack_limit_begin = offset_by(
452            self.vmctx_gas_limiter_pointer,
453            1,
454            u32::from(self.pointer_size),
455        )?;
456        self.vmctx_stack_limit_initial_begin = self
457            .vmctx_stack_limit_begin
458            .checked_add(4)
459            .ok_or(ERR_MESSAGE)?;
460        self.size_of_vmctx = self
461            .vmctx_stack_limit_begin
462            .checked_add(4)
463            .ok_or(ERR_MESSAGE)?;
464
465        Ok(())
466    }
467}
468
469/// Offsets for `VMFunctionImport`.
470impl VMOffsets {
471    /// The offset of the `body` field.
472    #[allow(clippy::erasing_op)]
473    pub const fn vmfunction_import_body(&self) -> u8 {
474        0 * self.pointer_size
475    }
476
477    /// The offset of the `vmctx` field.
478    #[allow(clippy::identity_op)]
479    pub const fn vmfunction_import_vmctx(&self) -> u8 {
480        1 * self.pointer_size
481    }
482
483    /// The offset of the `handle` field.
484    pub const fn vmfunction_import_handle(&self) -> u8 {
485        2 * self.pointer_size
486    }
487
488    /// The offset of the `include_m0_param` field.
489    pub const fn vmfunction_import_include_m0_param(&self) -> u8 {
490        3 * self.pointer_size
491    }
492
493    /// Return the size of `VMFunctionImport`.
494    pub const fn size_of_vmfunction_import(&self) -> u8 {
495        4 * self.pointer_size
496    }
497}
498
499/// Offsets for `VMDynamicFunctionContext`.
500impl VMOffsets {
501    /// The offset of the `address` field.
502    #[allow(clippy::erasing_op)]
503    pub const fn vmdynamicfunction_import_context_address(&self) -> u8 {
504        0 * self.pointer_size
505    }
506
507    /// The offset of the `ctx` field.
508    #[allow(clippy::identity_op)]
509    pub const fn vmdynamicfunction_import_context_ctx(&self) -> u8 {
510        1 * self.pointer_size
511    }
512
513    /// Return the size of `VMDynamicFunctionContext`.
514    pub const fn size_of_vmdynamicfunction_import_context(&self) -> u8 {
515        2 * self.pointer_size
516    }
517}
518
519/// Offsets for `*const VMFunctionBody`.
520impl VMOffsets {
521    /// The size of the `current_elements` field.
522    #[allow(clippy::identity_op)]
523    pub const fn size_of_vmfunction_body_ptr(&self) -> u8 {
524        1 * self.pointer_size
525    }
526}
527
528/// Offsets for `VMTableImport`.
529impl VMOffsets {
530    /// The offset of the `definition` field.
531    #[allow(clippy::erasing_op)]
532    pub const fn vmtable_import_definition(&self) -> u8 {
533        0 * self.pointer_size
534    }
535
536    /// The offset of the `handle` field.
537    #[allow(clippy::identity_op)]
538    pub const fn vmtable_import_handle(&self) -> u8 {
539        1 * self.pointer_size
540    }
541
542    /// Return the size of `VMTableImport`.
543    pub const fn size_of_vmtable_import(&self) -> u8 {
544        2 * self.pointer_size
545    }
546}
547
548/// Offsets for `VMTableDefinition`.
549impl VMOffsets {
550    /// The offset of the `base` field.
551    #[allow(clippy::erasing_op)]
552    pub const fn vmtable_definition_base(&self) -> u8 {
553        0 * self.pointer_size
554    }
555
556    /// The offset of the `current_elements` field.
557    #[allow(clippy::identity_op)]
558    pub const fn vmtable_definition_current_elements(&self) -> u8 {
559        1 * self.pointer_size
560    }
561
562    /// The size of the `current_elements` field.
563    pub const fn size_of_vmtable_definition_current_elements(&self) -> u8 {
564        4
565    }
566
567    /// Return the size of `VMTableDefinition`.
568    pub const fn size_of_vmtable_definition(&self) -> u8 {
569        2 * self.pointer_size
570    }
571}
572
573/// Offsets for `VMMemoryImport`.
574impl VMOffsets {
575    /// The offset of the `from` field.
576    #[allow(clippy::erasing_op)]
577    pub const fn vmmemory_import_definition(&self) -> u8 {
578        0 * self.pointer_size
579    }
580
581    /// The offset of the `handle` field.
582    #[allow(clippy::identity_op)]
583    pub const fn vmmemory_import_handle(&self) -> u8 {
584        1 * self.pointer_size
585    }
586
587    /// Return the size of `VMMemoryImport`.
588    pub const fn size_of_vmmemory_import(&self) -> u8 {
589        2 * self.pointer_size
590    }
591}
592
593/// Offsets for `VMMemoryDefinition`.
594impl VMOffsets {
595    /// The offset of the `base` field.
596    #[allow(clippy::erasing_op)]
597    pub const fn vmmemory_definition_base(&self) -> u8 {
598        0 * self.pointer_size
599    }
600
601    /// The offset of the `current_length` field.
602    #[allow(clippy::identity_op)]
603    pub const fn vmmemory_definition_current_length(&self) -> u8 {
604        1 * self.pointer_size
605    }
606
607    /// The size of the `current_length` field.
608    pub const fn size_of_vmmemory_definition_current_length(&self) -> u8 {
609        4
610    }
611
612    /// Return the size of `VMMemoryDefinition`.
613    pub const fn size_of_vmmemory_definition(&self) -> u8 {
614        2 * self.pointer_size
615    }
616}
617
618/// Offsets for `VMGlobalImport`.
619impl VMOffsets {
620    /// The offset of the `definition` field.
621    #[allow(clippy::erasing_op)]
622    pub const fn vmglobal_import_definition(&self) -> u8 {
623        0 * self.pointer_size
624    }
625
626    /// The offset of the `handle` field.
627    #[allow(clippy::identity_op)]
628    pub const fn vmglobal_import_handle(&self) -> u8 {
629        1 * self.pointer_size
630    }
631
632    /// Return the size of `VMGlobalImport`.
633    #[allow(clippy::identity_op)]
634    pub const fn size_of_vmglobal_import(&self) -> u8 {
635        2 * self.pointer_size
636    }
637}
638
639/// Offsets for a `VMGlobalDefinition` used as a local global.
640impl VMOffsets {
641    /// Return the size of a `VMGlobalDefinition`.
642    ///
643    /// Local globals are stored inline in the `VMContext`, and the definition
644    /// is a 16-byte `RawValue` aligned to 16.
645    pub const fn size_of_vmglobal_local(&self) -> u8 {
646        16
647    }
648}
649
650/// Offsets for `VMCallerCheckedAnyfunc`.
651impl VMOffsets {
652    /// The offset of the `func_ptr` field.
653    #[allow(clippy::erasing_op)]
654    pub const fn vmcaller_checked_anyfunc_func_ptr(&self) -> u8 {
655        0 * self.pointer_size
656    }
657
658    /// The offset of the `VMSignatureHash` field.
659    #[allow(clippy::identity_op)]
660    pub const fn vmcaller_checked_anyfunc_signature_hash(&self) -> u8 {
661        1 * self.pointer_size
662    }
663
664    /// The offset of the `vmctx` field.
665    pub const fn vmcaller_checked_anyfunc_vmctx(&self) -> u8 {
666        2 * self.pointer_size
667    }
668
669    /// The offset of the `call_trampoline` field.
670    pub const fn vmcaller_checked_anyfunc_call_trampoline(&self) -> u8 {
671        3 * self.pointer_size
672    }
673
674    /// Return the size of `VMCallerCheckedAnyfunc`.
675    pub const fn size_of_vmcaller_checked_anyfunc(&self) -> u8 {
676        4 * self.pointer_size
677    }
678}
679
680/// Offsets for `VMFuncRef`.
681impl VMOffsets {
682    /// The offset to the pointer to the anyfunc inside the ref.
683    #[allow(clippy::erasing_op)]
684    pub const fn vm_funcref_anyfunc_ptr(&self) -> u8 {
685        0 * self.pointer_size
686    }
687
688    /// Return the size of `VMFuncRef`.
689    #[allow(clippy::identity_op)]
690    pub const fn size_of_vm_funcref(&self) -> u8 {
691        1 * self.pointer_size
692    }
693}
694
695/// Offsets for `VMSharedTagIndex`.
696impl VMOffsets {
697    /// Return the size of `VMSharedTagIndex`.
698    pub const fn size_of_vmshared_tag_index(&self) -> u8 {
699        4
700    }
701}
702
703/// Convert a `VMContext` offset to the signed displacement used by compilers.
704pub fn vmctx_offset(offset: u32) -> WasmResult<i32> {
705    i32::try_from(offset)
706        .map_err(|_| WasmError::Generic(format!("VMContext offset {offset} exceeds i32::MAX")))
707}
708
709// TODO: make a breaking change where the return type will be `i32` instead!
710
711/// Offsets for `VMContext`.
712impl VMOffsets {
713    /// The offset of the `tables` array.
714    #[allow(clippy::erasing_op)]
715    pub fn vmctx_imported_functions_begin(&self) -> u32 {
716        self.vmctx_imported_functions_begin
717    }
718
719    /// The offset of the `tables` array.
720    #[allow(clippy::identity_op)]
721    pub fn vmctx_imported_tables_begin(&self) -> u32 {
722        self.vmctx_imported_tables_begin
723    }
724
725    /// The offset of the `memories` array.
726    pub fn vmctx_imported_memories_begin(&self) -> u32 {
727        self.vmctx_imported_memories_begin
728    }
729
730    /// The offset of the `globals` array.
731    pub fn vmctx_imported_globals_begin(&self) -> u32 {
732        self.vmctx_imported_globals_begin
733    }
734
735    /// The offset of the `tags` array.
736    pub fn vmctx_tag_ids_begin(&self) -> u32 {
737        self.vmctx_tag_ids_begin
738    }
739
740    /// The offset of the `tables` array.
741    pub fn vmctx_tables_begin(&self) -> u32 {
742        self.vmctx_tables_begin
743    }
744
745    /// The offset of the inline fixed `funcref` table storage.
746    pub fn vmctx_fixed_funcref_tables_begin(&self) -> u32 {
747        self.vmctx_fixed_funcref_tables_begin
748    }
749
750    /// The offset of the `memories` array.
751    pub fn vmctx_memories_begin(&self) -> u32 {
752        self.vmctx_memories_begin
753    }
754
755    /// The offset of the `globals` array.
756    pub fn vmctx_globals_begin(&self) -> u32 {
757        self.vmctx_globals_begin
758    }
759
760    /// The offset of the builtin functions array.
761    pub fn vmctx_builtin_functions_begin(&self) -> u32 {
762        self.vmctx_builtin_functions_begin
763    }
764
765    /// Return the size of the `VMContext` allocation.
766    pub fn size_of_vmctx(&self) -> u32 {
767        self.size_of_vmctx
768    }
769
770    /// Return the offset to `VMFunctionImport` index `index`.
771    pub fn vmctx_vmfunction_import(&self, index: FunctionIndex) -> u32 {
772        assert_lt!(index.as_u32(), self.num_imported_functions);
773        self.vmctx_imported_functions_begin
774            + index.as_u32() * u32::from(self.size_of_vmfunction_import())
775    }
776
777    /// Return the offset to `VMTableImport` index `index`.
778    pub fn vmctx_vmtable_import(&self, index: TableIndex) -> u32 {
779        assert_lt!(index.as_u32(), self.num_imported_tables);
780        self.vmctx_imported_tables_begin + index.as_u32() * u32::from(self.size_of_vmtable_import())
781    }
782
783    /// Return the offset to `VMMemoryImport` index `index`.
784    pub fn vmctx_vmmemory_import(&self, index: MemoryIndex) -> u32 {
785        assert_lt!(index.as_u32(), self.num_imported_memories);
786        self.vmctx_imported_memories_begin
787            + index.as_u32() * u32::from(self.size_of_vmmemory_import())
788    }
789
790    /// Return the offset to `VMGlobalImport` index `index`.
791    pub fn vmctx_vmglobal_import(&self, index: GlobalIndex) -> u32 {
792        assert_lt!(index.as_u32(), self.num_imported_globals);
793        self.vmctx_imported_globals_begin
794            + index.as_u32() * u32::from(self.size_of_vmglobal_import())
795    }
796
797    /// Return the offset to `VMTableDefinition` index `index`.
798    pub fn vmctx_vmtable_definition(&self, index: LocalTableIndex) -> u32 {
799        assert_lt!(index.as_u32(), self.num_local_tables);
800        self.vmctx_tables_begin + index.as_u32() * u32::from(self.size_of_vmtable_definition())
801    }
802
803    /// Return the offset to `VMMemoryDefinition` index `index`.
804    pub fn vmctx_vmmemory_definition(&self, index: LocalMemoryIndex) -> u32 {
805        assert_lt!(index.as_u32(), self.num_local_memories);
806        self.vmctx_memories_begin + index.as_u32() * u32::from(self.size_of_vmmemory_definition())
807    }
808
809    /// Return the offset to the `VMGlobalDefinition` index `index`.
810    pub fn vmctx_vmglobal_definition(&self, index: LocalGlobalIndex) -> u32 {
811        assert_lt!(index.as_u32(), self.num_local_globals);
812        self.vmctx_globals_begin + index.as_u32() * u32::from(self.size_of_vmglobal_local())
813    }
814
815    /// Return the offset to the `body` field in `*const VMFunctionBody` index `index`.
816    /// Remember updating precompute upon changes
817    pub fn vmctx_vmfunction_import_body(&self, index: FunctionIndex) -> u32 {
818        self.vmctx_vmfunction_import(index) + u32::from(self.vmfunction_import_body())
819    }
820
821    /// Return the offset to the `vmctx` field in `*const VMFunctionBody` index `index`.
822    /// Remember updating precompute upon changes
823    pub fn vmctx_vmfunction_import_vmctx(&self, index: FunctionIndex) -> u32 {
824        self.vmctx_vmfunction_import(index) + u32::from(self.vmfunction_import_vmctx())
825    }
826
827    /// Return the offset to the `definition` field in `VMTableImport` index `index`.
828    /// Remember updating precompute upon changes
829    pub fn vmctx_vmtable_import_definition(&self, index: TableIndex) -> u32 {
830        self.vmctx_vmtable_import(index) + u32::from(self.vmtable_import_definition())
831    }
832
833    /// Return the offset to the `base` field in `VMTableDefinition` index `index`.
834    /// Remember updating precompute upon changes
835    pub fn vmctx_vmtable_definition_base(&self, index: LocalTableIndex) -> u32 {
836        self.vmctx_vmtable_definition(index) + u32::from(self.vmtable_definition_base())
837    }
838
839    /// Return the offset to the `current_elements` field in `VMTableDefinition` index `index`.
840    /// Remember updating precompute upon changes
841    pub fn vmctx_vmtable_definition_current_elements(&self, index: LocalTableIndex) -> u32 {
842        self.vmctx_vmtable_definition(index) + u32::from(self.vmtable_definition_current_elements())
843    }
844
845    /// Return the offset to the inline `VMCallerCheckedAnyfunc` array for a local fixed
846    /// `funcref` table.
847    pub fn vmctx_fixed_funcref_table_anyfuncs(&self, index: LocalTableIndex) -> Option<u32> {
848        assert_lt!(index.as_u32(), self.num_local_tables);
849        self.local_fixed_funcref_table_offsets[index.index()]
850            .map(|offset| self.vmctx_fixed_funcref_tables_begin + offset)
851    }
852
853    /// Return the offset to the `from` field in `VMMemoryImport` index `index`.
854    /// Remember updating precompute upon changes
855    pub fn vmctx_vmmemory_import_definition(&self, index: MemoryIndex) -> u32 {
856        self.vmctx_vmmemory_import(index) + u32::from(self.vmmemory_import_definition())
857    }
858
859    /// Return the offset to the `vmctx` field in `VMMemoryImport` index `index`.
860    /// Remember updating precompute upon changes
861    pub fn vmctx_vmmemory_import_handle(&self, index: MemoryIndex) -> u32 {
862        self.vmctx_vmmemory_import(index) + u32::from(self.vmmemory_import_handle())
863    }
864
865    /// Return the offset to the `base` field in `VMMemoryDefinition` index `index`.
866    /// Remember updating precompute upon changes
867    pub fn vmctx_vmmemory_definition_base(&self, index: LocalMemoryIndex) -> u32 {
868        self.vmctx_vmmemory_definition(index) + u32::from(self.vmmemory_definition_base())
869    }
870
871    /// Return the offset to the `current_length` field in `VMMemoryDefinition` index `index`.
872    /// Remember updating precompute upon changes
873    pub fn vmctx_vmmemory_definition_current_length(&self, index: LocalMemoryIndex) -> u32 {
874        self.vmctx_vmmemory_definition(index) + u32::from(self.vmmemory_definition_current_length())
875    }
876
877    /// Return the offset to the `from` field in `VMGlobalImport` index `index`.
878    /// Remember updating precompute upon changes
879    pub fn vmctx_vmglobal_import_definition(&self, index: GlobalIndex) -> u32 {
880        self.vmctx_vmglobal_import(index) + u32::from(self.vmglobal_import_definition())
881    }
882
883    /// Return the offset to builtin function in `VMBuiltinFunctionsArray` index `index`.
884    /// Remember updating precompute upon changes
885    pub fn vmctx_builtin_function(&self, index: VMBuiltinFunctionIndex) -> u32 {
886        self.vmctx_builtin_functions_begin + index.index() * u32::from(self.pointer_size)
887    }
888}
889
890#[cfg(test)]
891mod tests {
892    use crate::vmoffsets::align;
893
894    #[test]
895    fn alignment() {
896        fn is_aligned(x: u32) -> bool {
897            x.is_multiple_of(16)
898        }
899        assert!(is_aligned(align(0, 16)));
900        assert!(is_aligned(align(32, 16)));
901        assert!(is_aligned(align(33, 16)));
902        assert!(is_aligned(align(31, 16)));
903    }
904}