Skip to main content

wasmtime_environ/
vmoffsets.rs

1//! Offsets and sizes of various structs in `wasmtime::runtime::vm::*` that are
2//! accessed directly by compiled Wasm code.
3
4// Currently the `VMContext` allocation by field looks like this:
5//
6// struct VMContext {
7//      // Fixed-width data comes first so the calculation of the offset of
8//      // these fields is a compile-time constant when using `HostPtr`.
9//      magic: u32,
10//      _padding: u32, // (On 64-bit systems)
11//      vm_store_context: *const VMStoreContext,
12//      builtin_functions: *mut VMBuiltinFunctionsArray,
13//      epoch_ptr: *mut AtomicU64,
14//      gc_heap_data: *mut T, // Collector-specific pointer
15//      type_ids: *const VMSharedTypeIndex,
16//
17//      // Variable-width fields come after the fixed-width fields above. Place
18//      // memory-related items first as they're some of the most frequently
19//      // accessed items and minimizing their offset in this structure can
20//      // shrink the size of load/store instruction offset immediates on
21//      // platforms like x64 and Pulley (e.g. fit in an 8-bit offset instead
22//      // of needing a 32-bit offset)
23//      imported_memories: [VMMemoryImport; module.num_imported_memories],
24//      memories: [*mut VMMemoryDefinition; module.num_defined_memories],
25//      owned_memories: [VMMemoryDefinition; module.num_owned_memories],
26//      imported_functions: [VMFunctionImport; module.num_imported_functions],
27//      imported_tables: [VMTableImport; module.num_imported_tables],
28//      imported_globals: [VMGlobalImport; module.num_imported_globals],
29//      imported_tags: [VMTagImport; module.num_imported_tags],
30//      tables: [VMTableDefinition; module.num_defined_tables],
31//      globals: [VMGlobalDefinition; module.num_defined_globals],
32//      tags: [VMTagDefinition; module.num_defined_tags],
33//      func_refs: [VMFuncRef; module.num_escaped_funcs],
34// }
35
36use crate::{
37    DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex, DefinedTagIndex, FuncIndex,
38    FuncRefIndex, GlobalIndex, MemoryIndex, Module, OwnedMemoryIndex, TableIndex, TagIndex,
39};
40use cranelift_entity::packed_option::ReservedValue;
41
42#[cfg(target_pointer_width = "32")]
43fn cast_to_u32(sz: usize) -> u32 {
44    u32::try_from(sz).unwrap()
45}
46#[cfg(target_pointer_width = "64")]
47fn cast_to_u32(sz: usize) -> u32 {
48    u32::try_from(sz).expect("overflow in cast from usize to u32")
49}
50
51/// Align an offset used in this module to a specific byte-width by rounding up
52#[inline]
53fn align(offset: u32, width: u32) -> u32 {
54    (offset + (width - 1)) / width * width
55}
56
57/// This class computes offsets to fields within `VMContext` and other
58/// related structs that JIT code accesses directly.
59#[derive(Debug, Clone, Copy)]
60pub struct VMOffsets<P> {
61    /// The size in bytes of a pointer on the target.
62    pub ptr: P,
63    /// The number of imported functions in the module.
64    pub num_imported_functions: u32,
65    /// The number of imported tables in the module.
66    pub num_imported_tables: u32,
67    /// The number of imported memories in the module.
68    pub num_imported_memories: u32,
69    /// The number of imported globals in the module.
70    pub num_imported_globals: u32,
71    /// The number of imported tags in the module.
72    pub num_imported_tags: u32,
73    /// The number of defined tables in the module.
74    pub num_defined_tables: u32,
75    /// The number of defined memories in the module.
76    pub num_defined_memories: u32,
77    /// The number of memories owned by the module instance.
78    pub num_owned_memories: u32,
79    /// The number of defined globals in the module.
80    pub num_defined_globals: u32,
81    /// The number of defined tags in the module.
82    pub num_defined_tags: u32,
83    /// The number of escaped functions in the module, the size of the func_refs
84    /// array.
85    pub num_escaped_funcs: u32,
86
87    // precalculated offsets of various member fields
88    imported_functions: u32,
89    imported_tables: u32,
90    imported_memories: u32,
91    imported_globals: u32,
92    imported_tags: u32,
93    defined_tables: u32,
94    defined_memories: u32,
95    owned_memories: u32,
96    defined_globals: u32,
97    defined_tags: u32,
98    defined_func_refs: u32,
99    size: u32,
100}
101
102/// Trait used for the `ptr` representation of the field of `VMOffsets`
103pub trait PtrSize {
104    /// Returns the pointer size, in bytes, for the target.
105    fn size(&self) -> u8;
106
107    /// The offset of the `VMContext::store_context` field
108    fn vmcontext_store_context(&self) -> u8 {
109        u8::try_from(align(
110            u32::try_from(core::mem::size_of::<u32>()).unwrap(),
111            u32::from(self.size()),
112        ))
113        .unwrap()
114    }
115
116    /// The offset of the `VMContext::builtin_functions` field
117    fn vmcontext_builtin_functions(&self) -> u8 {
118        self.vmcontext_store_context() + self.size()
119    }
120
121    /// The offset of the `array_call` field.
122    #[inline]
123    fn vm_func_ref_array_call(&self) -> u8 {
124        0 * self.size()
125    }
126
127    /// The offset of the `wasm_call` field.
128    #[inline]
129    fn vm_func_ref_wasm_call(&self) -> u8 {
130        1 * self.size()
131    }
132
133    /// The offset of the `type_index` field.
134    #[inline]
135    fn vm_func_ref_type_index(&self) -> u8 {
136        2 * self.size()
137    }
138
139    /// The offset of the `vmctx` field.
140    #[inline]
141    fn vm_func_ref_vmctx(&self) -> u8 {
142        3 * self.size()
143    }
144
145    /// Return the size of `VMFuncRef`.
146    #[inline]
147    fn size_of_vm_func_ref(&self) -> u8 {
148        4 * self.size()
149    }
150
151    /// Return the size of `VMGlobalDefinition`; this is the size of the largest value type (i.e. a
152    /// V128).
153    #[inline]
154    fn size_of_vmglobal_definition(&self) -> u8 {
155        16
156    }
157
158    /// Return the size of `VMTagDefinition`.
159    #[inline]
160    fn size_of_vmtag_definition(&self) -> u8 {
161        4
162    }
163
164    /// This is the size of the largest value type (i.e. a V128).
165    #[inline]
166    fn maximum_value_size(&self) -> u8 {
167        self.size_of_vmglobal_definition()
168    }
169
170    // Offsets within `VMStoreContext`
171
172    /// Return the offset of the `fuel_consumed` field of `VMStoreContext`
173    #[inline]
174    fn vmstore_context_fuel_consumed(&self) -> u8 {
175        0
176    }
177
178    /// Return the offset of the `epoch_deadline` field of `VMStoreContext`
179    #[inline]
180    fn vmstore_context_epoch_deadline(&self) -> u8 {
181        self.vmstore_context_fuel_consumed() + 8
182    }
183
184    /// Return the offset of the `execution_version` field of
185    /// `VMStoreContext`
186    #[inline]
187    fn vmstore_context_execution_version(&self) -> u8 {
188        self.vmstore_context_epoch_deadline() + 8
189    }
190
191    /// Return the offset of the `stack_limit` field of `VMStoreContext`
192    #[inline]
193    fn vmstore_context_stack_limit(&self) -> u8 {
194        self.vmstore_context_execution_version() + 8
195    }
196
197    /// Return the offset of the `gc_heap` field of `VMStoreContext`.
198    #[inline]
199    fn vmstore_context_gc_heap(&self) -> u8 {
200        self.vmstore_context_stack_limit() + self.size()
201    }
202
203    /// Return the offset of the `gc_heap.base` field within a `VMStoreContext`.
204    fn vmstore_context_gc_heap_base(&self) -> u8 {
205        let offset = self.vmstore_context_gc_heap() + self.vmmemory_definition_base();
206        debug_assert!(offset < self.vmstore_context_last_wasm_exit_trampoline_fp());
207        offset
208    }
209
210    /// Return the offset of the `gc_heap.current_length` field within a `VMStoreContext`.
211    fn vmstore_context_gc_heap_current_length(&self) -> u8 {
212        let offset = self.vmstore_context_gc_heap() + self.vmmemory_definition_current_length();
213        debug_assert!(offset < self.vmstore_context_last_wasm_exit_trampoline_fp());
214        offset
215    }
216
217    /// Return the offset of the `last_wasm_exit_trampoline_fp` field
218    /// of `VMStoreContext`.
219    fn vmstore_context_last_wasm_exit_trampoline_fp(&self) -> u8 {
220        self.vmstore_context_gc_heap() + self.size_of_vmmemory_definition()
221    }
222
223    /// Return the offset of the `last_wasm_exit_pc` field of `VMStoreContext`.
224    fn vmstore_context_last_wasm_exit_pc(&self) -> u8 {
225        self.vmstore_context_last_wasm_exit_trampoline_fp() + self.size()
226    }
227
228    /// Return the offset of the `last_wasm_entry_sp` field of `VMStoreContext`.
229    fn vmstore_context_last_wasm_entry_sp(&self) -> u8 {
230        self.vmstore_context_last_wasm_exit_pc() + self.size()
231    }
232
233    /// Return the offset of the `last_wasm_entry_fp` field of `VMStoreContext`.
234    fn vmstore_context_last_wasm_entry_fp(&self) -> u8 {
235        self.vmstore_context_last_wasm_entry_sp() + self.size()
236    }
237
238    /// Return the offset of the `last_wasm_entry_trap_handler` field of `VMStoreContext`.
239    fn vmstore_context_last_wasm_entry_trap_handler(&self) -> u8 {
240        self.vmstore_context_last_wasm_entry_fp() + self.size()
241    }
242
243    /// Return the offset of the `stack_chain` field of `VMStoreContext`.
244    fn vmstore_context_stack_chain(&self) -> u8 {
245        self.vmstore_context_last_wasm_entry_trap_handler() + self.size()
246    }
247
248    /// Return the offset of the `stack_chain` field of `VMStoreContext`.
249    fn vmstore_context_store_data(&self) -> u8 {
250        self.vmstore_context_stack_chain() + self.size_of_vmstack_chain()
251    }
252
253    // Offsets within `VMMemoryDefinition`
254
255    /// The offset of the `base` field.
256    #[inline]
257    fn vmmemory_definition_base(&self) -> u8 {
258        0 * self.size()
259    }
260
261    /// The offset of the `current_length` field.
262    #[inline]
263    fn vmmemory_definition_current_length(&self) -> u8 {
264        1 * self.size()
265    }
266
267    /// Return the size of `VMMemoryDefinition`.
268    #[inline]
269    fn size_of_vmmemory_definition(&self) -> u8 {
270        2 * self.size()
271    }
272
273    /// Return the size of `*mut VMMemoryDefinition`.
274    #[inline]
275    fn size_of_vmmemory_pointer(&self) -> u8 {
276        self.size()
277    }
278
279    // Offsets within `VMArrayCallHostFuncContext`.
280
281    /// Return the offset of `VMArrayCallHostFuncContext::func_ref`.
282    fn vmarray_call_host_func_context_func_ref(&self) -> u8 {
283        u8::try_from(align(
284            u32::try_from(core::mem::size_of::<u32>()).unwrap(),
285            u32::from(self.size()),
286        ))
287        .unwrap()
288    }
289
290    /// Return the size of `VMStackChain`.
291    fn size_of_vmstack_chain(&self) -> u8 {
292        2 * self.size()
293    }
294
295    // Offsets within `VMStackLimits`
296
297    /// Return the offset of `VMStackLimits::stack_limit`.
298    fn vmstack_limits_stack_limit(&self) -> u8 {
299        0
300    }
301
302    /// Return the offset of `VMStackLimits::last_wasm_entry_fp`.
303    fn vmstack_limits_last_wasm_entry_fp(&self) -> u8 {
304        self.size()
305    }
306
307    // Offsets within `VMHostArray`
308
309    /// Return the offset of `VMHostArray::length`.
310    fn vmhostarray_length(&self) -> u8 {
311        0
312    }
313
314    /// Return the offset of `VMHostArray::capacity`.
315    fn vmhostarray_capacity(&self) -> u8 {
316        4
317    }
318
319    /// Return the offset of `VMHostArray::data`.
320    fn vmhostarray_data(&self) -> u8 {
321        8
322    }
323
324    /// Return the size of `VMHostArray`.
325    fn size_of_vmhostarray(&self) -> u8 {
326        8 + self.size()
327    }
328
329    // Offsets within `VMCommonStackInformation`
330
331    /// Return the offset of `VMCommonStackInformation::limits`.
332    fn vmcommon_stack_information_limits(&self) -> u8 {
333        0 * self.size()
334    }
335
336    /// Return the offset of `VMCommonStackInformation::state`.
337    fn vmcommon_stack_information_state(&self) -> u8 {
338        2 * self.size()
339    }
340
341    /// Return the offset of `VMCommonStackInformation::handlers`.
342    fn vmcommon_stack_information_handlers(&self) -> u8 {
343        u8::try_from(align(
344            self.vmcommon_stack_information_state() as u32 + 4,
345            u32::from(self.size()),
346        ))
347        .unwrap()
348    }
349
350    /// Return the offset of `VMCommonStackInformation::first_switch_handler_index`.
351    fn vmcommon_stack_information_first_switch_handler_index(&self) -> u8 {
352        self.vmcommon_stack_information_handlers() + self.size_of_vmhostarray()
353    }
354
355    /// Return the size of `VMCommonStackInformation`.
356    fn size_of_vmcommon_stack_information(&self) -> u8 {
357        u8::try_from(align(
358            self.vmcommon_stack_information_first_switch_handler_index() as u32 + 4,
359            u32::from(self.size()),
360        ))
361        .unwrap()
362    }
363
364    // Offsets within `VMContObj`
365
366    /// Return the offset of `VMContObj::contref`
367    fn vmcontobj_contref(&self) -> u8 {
368        0
369    }
370
371    /// Return the offset of `VMContObj::revision`
372    fn vmcontobj_revision(&self) -> u8 {
373        self.size()
374    }
375
376    /// Return the size of `VMContObj`.
377    fn size_of_vmcontobj(&self) -> u8 {
378        u8::try_from(align(
379            u32::from(self.vmcontobj_revision())
380                + u32::try_from(core::mem::size_of::<usize>()).unwrap(),
381            u32::from(self.size()),
382        ))
383        .unwrap()
384    }
385
386    // Offsets within `VMContRef`
387
388    /// Return the offset of `VMContRef::common_stack_information`.
389    fn vmcontref_common_stack_information(&self) -> u8 {
390        0 * self.size()
391    }
392
393    /// Return the offset of `VMContRef::parent_chain`.
394    fn vmcontref_parent_chain(&self) -> u8 {
395        u8::try_from(align(
396            (self.vmcontref_common_stack_information() + self.size_of_vmcommon_stack_information())
397                as u32,
398            u32::from(self.size()),
399        ))
400        .unwrap()
401    }
402
403    /// Return the offset of `VMContRef::last_ancestor`.
404    fn vmcontref_last_ancestor(&self) -> u8 {
405        self.vmcontref_parent_chain() + 2 * self.size()
406    }
407
408    /// Return the offset of `VMContRef::revision`.
409    fn vmcontref_revision(&self) -> u8 {
410        self.vmcontref_last_ancestor() + self.size()
411    }
412
413    /// Return the offset of `VMContRef::stack`.
414    fn vmcontref_stack(&self) -> u8 {
415        self.vmcontref_revision() + self.size()
416    }
417
418    /// Return the offset of `VMContRef::args`.
419    fn vmcontref_args(&self) -> u8 {
420        self.vmcontref_stack() + 3 * self.size()
421    }
422
423    /// Return the offset of `VMContRef::values`.
424    fn vmcontref_values(&self) -> u8 {
425        self.vmcontref_args() + self.size_of_vmhostarray()
426    }
427
428    /// Return the offset to the `magic` value in this `VMContext`.
429    #[inline]
430    fn vmctx_magic(&self) -> u8 {
431        // This is required by the implementation of `VMContext::instance` and
432        // `VMContext::instance_mut`. If this value changes then those locations
433        // need to be updated.
434        0
435    }
436
437    /// Return the offset to the `VMStoreContext` structure
438    #[inline]
439    fn vmctx_store_context(&self) -> u8 {
440        self.vmctx_magic() + self.size()
441    }
442
443    /// Return the offset to the `VMBuiltinFunctionsArray` structure
444    #[inline]
445    fn vmctx_builtin_functions(&self) -> u8 {
446        self.vmctx_store_context() + self.size()
447    }
448
449    /// Return the offset to the `*const AtomicU64` epoch-counter
450    /// pointer.
451    #[inline]
452    fn vmctx_epoch_ptr(&self) -> u8 {
453        self.vmctx_builtin_functions() + self.size()
454    }
455
456    /// Return the offset to the `*mut T` collector-specific data.
457    ///
458    /// This is a pointer that different collectors can use however they see
459    /// fit.
460    #[inline]
461    fn vmctx_gc_heap_data(&self) -> u8 {
462        self.vmctx_epoch_ptr() + self.size()
463    }
464
465    /// The offset of the `type_ids` array pointer.
466    #[inline]
467    fn vmctx_type_ids_array(&self) -> u8 {
468        self.vmctx_gc_heap_data() + self.size()
469    }
470
471    /// The end of statically known offsets in `VMContext`.
472    ///
473    /// Data after this is dynamically sized.
474    #[inline]
475    fn vmctx_dynamic_data_start(&self) -> u8 {
476        self.vmctx_type_ids_array() + self.size()
477    }
478}
479
480/// Type representing the size of a pointer for the current compilation host
481#[derive(Clone, Copy)]
482pub struct HostPtr;
483
484impl PtrSize for HostPtr {
485    #[inline]
486    fn size(&self) -> u8 {
487        core::mem::size_of::<usize>() as u8
488    }
489}
490
491impl PtrSize for u8 {
492    #[inline]
493    fn size(&self) -> u8 {
494        *self
495    }
496}
497
498/// Used to construct a `VMOffsets`
499#[derive(Debug, Clone, Copy)]
500pub struct VMOffsetsFields<P> {
501    /// The size in bytes of a pointer on the target.
502    pub ptr: P,
503    /// The number of imported functions in the module.
504    pub num_imported_functions: u32,
505    /// The number of imported tables in the module.
506    pub num_imported_tables: u32,
507    /// The number of imported memories in the module.
508    pub num_imported_memories: u32,
509    /// The number of imported globals in the module.
510    pub num_imported_globals: u32,
511    /// The number of imported tags in the module.
512    pub num_imported_tags: u32,
513    /// The number of defined tables in the module.
514    pub num_defined_tables: u32,
515    /// The number of defined memories in the module.
516    pub num_defined_memories: u32,
517    /// The number of memories owned by the module instance.
518    pub num_owned_memories: u32,
519    /// The number of defined globals in the module.
520    pub num_defined_globals: u32,
521    /// The number of defined tags in the module.
522    pub num_defined_tags: u32,
523    /// The number of escaped functions in the module, the size of the function
524    /// references array.
525    pub num_escaped_funcs: u32,
526}
527
528impl<P: PtrSize> VMOffsets<P> {
529    /// Return a new `VMOffsets` instance, for a given pointer size.
530    pub fn new(ptr: P, module: &Module) -> Self {
531        let num_owned_memories = module
532            .memories
533            .iter()
534            .skip(module.num_imported_memories)
535            .filter(|p| !p.1.shared)
536            .count()
537            .try_into()
538            .unwrap();
539        VMOffsets::from(VMOffsetsFields {
540            ptr,
541            num_imported_functions: cast_to_u32(module.num_imported_funcs),
542            num_imported_tables: cast_to_u32(module.num_imported_tables),
543            num_imported_memories: cast_to_u32(module.num_imported_memories),
544            num_imported_globals: cast_to_u32(module.num_imported_globals),
545            num_imported_tags: cast_to_u32(module.num_imported_tags),
546            num_defined_tables: cast_to_u32(module.num_defined_tables()),
547            num_defined_memories: cast_to_u32(module.num_defined_memories()),
548            num_owned_memories,
549            num_defined_globals: cast_to_u32(module.globals.len() - module.num_imported_globals),
550            num_defined_tags: cast_to_u32(module.tags.len() - module.num_imported_tags),
551            num_escaped_funcs: cast_to_u32(module.num_escaped_funcs),
552        })
553    }
554
555    /// Returns the size, in bytes, of the target
556    #[inline]
557    pub fn pointer_size(&self) -> u8 {
558        self.ptr.size()
559    }
560
561    /// Returns an iterator which provides a human readable description and a
562    /// byte size. The iterator returned will iterate over the bytes allocated
563    /// to the entire `VMOffsets` structure to explain where each byte size is
564    /// coming from.
565    pub fn region_sizes(&self) -> impl Iterator<Item = (&str, u32)> {
566        macro_rules! calculate_sizes {
567            ($($name:ident: $desc:tt,)*) => {{
568                let VMOffsets {
569                    // These fields are metadata not talking about specific
570                    // offsets of specific fields.
571                    ptr: _,
572                    num_imported_functions: _,
573                    num_imported_tables: _,
574                    num_imported_memories: _,
575                    num_imported_globals: _,
576                    num_imported_tags: _,
577                    num_defined_tables: _,
578                    num_defined_globals: _,
579                    num_defined_memories: _,
580                    num_defined_tags: _,
581                    num_owned_memories: _,
582                    num_escaped_funcs: _,
583
584                    // used as the initial size below
585                    size,
586
587                    // exhaustively match the rest of the fields with input from
588                    // the macro
589                    $($name,)*
590                } = *self;
591
592                // calculate the size of each field by relying on the inputs to
593                // the macro being in reverse order and determining the size of
594                // the field as the offset from the field to the last field.
595                let mut last = size;
596                $(
597                    assert!($name <= last);
598                    let tmp = $name;
599                    let $name = last - $name;
600                    last = tmp;
601                )*
602                assert_ne!(last, 0);
603                IntoIterator::into_iter([
604                    $(($desc, $name),)*
605                    ("static vmctx data", last),
606                ])
607            }};
608        }
609
610        calculate_sizes! {
611            defined_func_refs: "module functions",
612            defined_tags: "defined tags",
613            defined_globals: "defined globals",
614            defined_tables: "defined tables",
615            imported_tags: "imported tags",
616            imported_globals: "imported globals",
617            imported_tables: "imported tables",
618            imported_functions: "imported functions",
619            owned_memories: "owned memories",
620            defined_memories: "defined memories",
621            imported_memories: "imported memories",
622        }
623    }
624}
625
626impl<P: PtrSize> From<VMOffsetsFields<P>> for VMOffsets<P> {
627    fn from(fields: VMOffsetsFields<P>) -> VMOffsets<P> {
628        let mut ret = Self {
629            ptr: fields.ptr,
630            num_imported_functions: fields.num_imported_functions,
631            num_imported_tables: fields.num_imported_tables,
632            num_imported_memories: fields.num_imported_memories,
633            num_imported_globals: fields.num_imported_globals,
634            num_imported_tags: fields.num_imported_tags,
635            num_defined_tables: fields.num_defined_tables,
636            num_defined_memories: fields.num_defined_memories,
637            num_owned_memories: fields.num_owned_memories,
638            num_defined_globals: fields.num_defined_globals,
639            num_defined_tags: fields.num_defined_tags,
640            num_escaped_funcs: fields.num_escaped_funcs,
641            imported_functions: 0,
642            imported_tables: 0,
643            imported_memories: 0,
644            imported_globals: 0,
645            imported_tags: 0,
646            defined_tables: 0,
647            defined_memories: 0,
648            owned_memories: 0,
649            defined_globals: 0,
650            defined_tags: 0,
651            defined_func_refs: 0,
652            size: 0,
653        };
654
655        // Convenience functions for checked addition and multiplication.
656        // As side effect this reduces binary size by using only a single
657        // `#[track_caller]` location for each function instead of one for
658        // each individual invocation.
659        #[inline]
660        fn cadd(count: u32, size: u32) -> u32 {
661            count.checked_add(size).unwrap()
662        }
663
664        #[inline]
665        fn cmul(count: u32, size: u8) -> u32 {
666            count.checked_mul(u32::from(size)).unwrap()
667        }
668
669        let mut next_field_offset = u32::from(ret.ptr.vmctx_dynamic_data_start());
670
671        macro_rules! fields {
672            (size($field:ident) = $size:expr, $($rest:tt)*) => {
673                ret.$field = next_field_offset;
674                next_field_offset = cadd(next_field_offset, u32::from($size));
675                fields!($($rest)*);
676            };
677            (align($align:expr), $($rest:tt)*) => {
678                next_field_offset = align(next_field_offset, $align);
679                fields!($($rest)*);
680            };
681            () => {};
682        }
683
684        fields! {
685            size(imported_memories)
686                = cmul(ret.num_imported_memories, ret.size_of_vmmemory_import()),
687            size(defined_memories)
688                = cmul(ret.num_defined_memories, ret.ptr.size_of_vmmemory_pointer()),
689            size(owned_memories)
690                = cmul(ret.num_owned_memories, ret.ptr.size_of_vmmemory_definition()),
691            size(imported_functions)
692                = cmul(ret.num_imported_functions, ret.size_of_vmfunction_import()),
693            size(imported_tables)
694                = cmul(ret.num_imported_tables, ret.size_of_vmtable_import()),
695            size(imported_globals)
696                = cmul(ret.num_imported_globals, ret.size_of_vmglobal_import()),
697            size(imported_tags)
698                = cmul(ret.num_imported_tags, ret.size_of_vmtag_import()),
699            size(defined_tables)
700                = cmul(ret.num_defined_tables, ret.size_of_vmtable_definition()),
701            align(16),
702            size(defined_globals)
703                = cmul(ret.num_defined_globals, ret.ptr.size_of_vmglobal_definition()),
704            size(defined_tags)
705                = cmul(ret.num_defined_tags, ret.ptr.size_of_vmtag_definition()),
706            size(defined_func_refs) = cmul(
707                ret.num_escaped_funcs,
708                ret.ptr.size_of_vm_func_ref(),
709            ),
710        }
711
712        ret.size = next_field_offset;
713
714        return ret;
715    }
716}
717
718impl<P: PtrSize> VMOffsets<P> {
719    /// The offset of the `wasm_call` field.
720    #[inline]
721    pub fn vmfunction_import_wasm_call(&self) -> u8 {
722        0 * self.pointer_size()
723    }
724
725    /// The offset of the `array_call` field.
726    #[inline]
727    pub fn vmfunction_import_array_call(&self) -> u8 {
728        1 * self.pointer_size()
729    }
730
731    /// The offset of the `vmctx` field.
732    #[inline]
733    pub fn vmfunction_import_vmctx(&self) -> u8 {
734        2 * self.pointer_size()
735    }
736
737    /// Return the size of `VMFunctionImport`.
738    #[inline]
739    pub fn size_of_vmfunction_import(&self) -> u8 {
740        3 * self.pointer_size()
741    }
742}
743
744/// Offsets for `*const VMFunctionBody`.
745impl<P: PtrSize> VMOffsets<P> {
746    /// The size of the `current_elements` field.
747    pub fn size_of_vmfunction_body_ptr(&self) -> u8 {
748        1 * self.pointer_size()
749    }
750}
751
752/// Offsets for `VMTableImport`.
753impl<P: PtrSize> VMOffsets<P> {
754    /// The offset of the `from` field.
755    #[inline]
756    pub fn vmtable_import_from(&self) -> u8 {
757        0 * self.pointer_size()
758    }
759
760    /// The offset of the `vmctx` field.
761    #[inline]
762    pub fn vmtable_import_vmctx(&self) -> u8 {
763        1 * self.pointer_size()
764    }
765
766    /// The offset of the `index` field.
767    #[inline]
768    pub fn vmtable_import_index(&self) -> u8 {
769        2 * self.pointer_size()
770    }
771
772    /// Return the size of `VMTableImport`.
773    #[inline]
774    pub fn size_of_vmtable_import(&self) -> u8 {
775        3 * self.pointer_size()
776    }
777}
778
779/// Offsets for `VMTableDefinition`.
780impl<P: PtrSize> VMOffsets<P> {
781    /// The offset of the `base` field.
782    #[inline]
783    pub fn vmtable_definition_base(&self) -> u8 {
784        0 * self.pointer_size()
785    }
786
787    /// The offset of the `current_elements` field.
788    pub fn vmtable_definition_current_elements(&self) -> u8 {
789        1 * self.pointer_size()
790    }
791
792    /// The size of the `current_elements` field.
793    #[inline]
794    pub fn size_of_vmtable_definition_current_elements(&self) -> u8 {
795        self.pointer_size()
796    }
797
798    /// Return the size of `VMTableDefinition`.
799    #[inline]
800    pub fn size_of_vmtable_definition(&self) -> u8 {
801        2 * self.pointer_size()
802    }
803}
804
805/// Offsets for `VMMemoryImport`.
806impl<P: PtrSize> VMOffsets<P> {
807    /// The offset of the `from` field.
808    #[inline]
809    pub fn vmmemory_import_from(&self) -> u8 {
810        0 * self.pointer_size()
811    }
812
813    /// The offset of the `vmctx` field.
814    #[inline]
815    pub fn vmmemory_import_vmctx(&self) -> u8 {
816        1 * self.pointer_size()
817    }
818
819    /// The offset of the `index` field.
820    #[inline]
821    pub fn vmmemory_import_index(&self) -> u8 {
822        2 * self.pointer_size()
823    }
824
825    /// Return the size of `VMMemoryImport`.
826    #[inline]
827    pub fn size_of_vmmemory_import(&self) -> u8 {
828        3 * self.pointer_size()
829    }
830}
831
832/// Offsets for `VMGlobalImport`.
833impl<P: PtrSize> VMOffsets<P> {
834    /// The offset of the `from` field.
835    #[inline]
836    pub fn vmglobal_import_from(&self) -> u8 {
837        0 * self.pointer_size()
838    }
839
840    /// Return the size of `VMGlobalImport`.
841    #[inline]
842    pub fn size_of_vmglobal_import(&self) -> u8 {
843        // `VMGlobalImport` has two pointers plus 8 bytes for `VMGlobalKind`
844        2 * self.pointer_size() + 8
845    }
846}
847
848/// Offsets for `VMSharedTypeIndex`.
849impl<P: PtrSize> VMOffsets<P> {
850    /// Return the size of `VMSharedTypeIndex`.
851    #[inline]
852    pub fn size_of_vmshared_type_index(&self) -> u8 {
853        4
854    }
855}
856
857/// Offsets for `VMTagImport`.
858impl<P: PtrSize> VMOffsets<P> {
859    /// The offset of the `from` field.
860    #[inline]
861    pub fn vmtag_import_from(&self) -> u8 {
862        0 * self.pointer_size()
863    }
864
865    /// The offset of the `vmctx` field.
866    #[inline]
867    pub fn vmtag_import_vmctx(&self) -> u8 {
868        1 * self.pointer_size()
869    }
870
871    /// The offset of the `index` field.
872    #[inline]
873    pub fn vmtag_import_index(&self) -> u8 {
874        2 * self.pointer_size()
875    }
876
877    /// Return the size of `VMTagImport`.
878    #[inline]
879    pub fn size_of_vmtag_import(&self) -> u8 {
880        3 * self.pointer_size()
881    }
882}
883
884/// Offsets for `VMContext`.
885impl<P: PtrSize> VMOffsets<P> {
886    /// The offset of the `tables` array.
887    #[inline]
888    pub fn vmctx_imported_functions_begin(&self) -> u32 {
889        self.imported_functions
890    }
891
892    /// The offset of the `tables` array.
893    #[inline]
894    pub fn vmctx_imported_tables_begin(&self) -> u32 {
895        self.imported_tables
896    }
897
898    /// The offset of the `memories` array.
899    #[inline]
900    pub fn vmctx_imported_memories_begin(&self) -> u32 {
901        self.imported_memories
902    }
903
904    /// The offset of the `globals` array.
905    #[inline]
906    pub fn vmctx_imported_globals_begin(&self) -> u32 {
907        self.imported_globals
908    }
909
910    /// The offset of the `tags` array.
911    #[inline]
912    pub fn vmctx_imported_tags_begin(&self) -> u32 {
913        self.imported_tags
914    }
915
916    /// The offset of the `tables` array.
917    #[inline]
918    pub fn vmctx_tables_begin(&self) -> u32 {
919        self.defined_tables
920    }
921
922    /// The offset of the `memories` array.
923    #[inline]
924    pub fn vmctx_memories_begin(&self) -> u32 {
925        self.defined_memories
926    }
927
928    /// The offset of the `owned_memories` array.
929    #[inline]
930    pub fn vmctx_owned_memories_begin(&self) -> u32 {
931        self.owned_memories
932    }
933
934    /// The offset of the `globals` array.
935    #[inline]
936    pub fn vmctx_globals_begin(&self) -> u32 {
937        self.defined_globals
938    }
939
940    /// The offset of the `tags` array.
941    #[inline]
942    pub fn vmctx_tags_begin(&self) -> u32 {
943        self.defined_tags
944    }
945
946    /// The offset of the `func_refs` array.
947    #[inline]
948    pub fn vmctx_func_refs_begin(&self) -> u32 {
949        self.defined_func_refs
950    }
951
952    /// Return the size of the `VMContext` allocation.
953    #[inline]
954    pub fn size_of_vmctx(&self) -> u32 {
955        self.size
956    }
957
958    /// Return the offset to `VMFunctionImport` index `index`.
959    #[inline]
960    pub fn vmctx_vmfunction_import(&self, index: FuncIndex) -> u32 {
961        assert!(index.as_u32() < self.num_imported_functions);
962        self.vmctx_imported_functions_begin()
963            + index.as_u32() * u32::from(self.size_of_vmfunction_import())
964    }
965
966    /// Return the offset to `VMTable` index `index`.
967    #[inline]
968    pub fn vmctx_vmtable_import(&self, index: TableIndex) -> u32 {
969        assert!(index.as_u32() < self.num_imported_tables);
970        self.vmctx_imported_tables_begin()
971            + index.as_u32() * u32::from(self.size_of_vmtable_import())
972    }
973
974    /// Return the offset to `VMMemoryImport` index `index`.
975    #[inline]
976    pub fn vmctx_vmmemory_import(&self, index: MemoryIndex) -> u32 {
977        assert!(index.as_u32() < self.num_imported_memories);
978        self.vmctx_imported_memories_begin()
979            + index.as_u32() * u32::from(self.size_of_vmmemory_import())
980    }
981
982    /// Return the offset to `VMGlobalImport` index `index`.
983    #[inline]
984    pub fn vmctx_vmglobal_import(&self, index: GlobalIndex) -> u32 {
985        assert!(index.as_u32() < self.num_imported_globals);
986        self.vmctx_imported_globals_begin()
987            + index.as_u32() * u32::from(self.size_of_vmglobal_import())
988    }
989
990    /// Return the offset to `VMTagImport` index `index`.
991    #[inline]
992    pub fn vmctx_vmtag_import(&self, index: TagIndex) -> u32 {
993        assert!(index.as_u32() < self.num_imported_tags);
994        self.vmctx_imported_tags_begin() + index.as_u32() * u32::from(self.size_of_vmtag_import())
995    }
996
997    /// Return the offset to `VMTableDefinition` index `index`.
998    #[inline]
999    pub fn vmctx_vmtable_definition(&self, index: DefinedTableIndex) -> u32 {
1000        assert!(index.as_u32() < self.num_defined_tables);
1001        self.vmctx_tables_begin() + index.as_u32() * u32::from(self.size_of_vmtable_definition())
1002    }
1003
1004    /// Return the offset to the `*mut VMMemoryDefinition` at index `index`.
1005    #[inline]
1006    pub fn vmctx_vmmemory_pointer(&self, index: DefinedMemoryIndex) -> u32 {
1007        assert!(index.as_u32() < self.num_defined_memories);
1008        self.vmctx_memories_begin()
1009            + index.as_u32() * u32::from(self.ptr.size_of_vmmemory_pointer())
1010    }
1011
1012    /// Return the offset to the owned `VMMemoryDefinition` at index `index`.
1013    #[inline]
1014    pub fn vmctx_vmmemory_definition(&self, index: OwnedMemoryIndex) -> u32 {
1015        assert!(index.as_u32() < self.num_owned_memories);
1016        self.vmctx_owned_memories_begin()
1017            + index.as_u32() * u32::from(self.ptr.size_of_vmmemory_definition())
1018    }
1019
1020    /// Return the offset to the `VMGlobalDefinition` index `index`.
1021    #[inline]
1022    pub fn vmctx_vmglobal_definition(&self, index: DefinedGlobalIndex) -> u32 {
1023        assert!(index.as_u32() < self.num_defined_globals);
1024        self.vmctx_globals_begin()
1025            + index.as_u32() * u32::from(self.ptr.size_of_vmglobal_definition())
1026    }
1027
1028    /// Return the offset to the `VMTagDefinition` index `index`.
1029    #[inline]
1030    pub fn vmctx_vmtag_definition(&self, index: DefinedTagIndex) -> u32 {
1031        assert!(index.as_u32() < self.num_defined_tags);
1032        self.vmctx_tags_begin() + index.as_u32() * u32::from(self.ptr.size_of_vmtag_definition())
1033    }
1034
1035    /// Return the offset to the `VMFuncRef` for the given function
1036    /// index (either imported or defined).
1037    #[inline]
1038    pub fn vmctx_func_ref(&self, index: FuncRefIndex) -> u32 {
1039        assert!(!index.is_reserved_value());
1040        assert!(index.as_u32() < self.num_escaped_funcs);
1041        self.vmctx_func_refs_begin() + index.as_u32() * u32::from(self.ptr.size_of_vm_func_ref())
1042    }
1043
1044    /// Return the offset to the `wasm_call` field in `*const VMFunctionBody` index `index`.
1045    #[inline]
1046    pub fn vmctx_vmfunction_import_wasm_call(&self, index: FuncIndex) -> u32 {
1047        self.vmctx_vmfunction_import(index) + u32::from(self.vmfunction_import_wasm_call())
1048    }
1049
1050    /// Return the offset to the `array_call` field in `*const VMFunctionBody` index `index`.
1051    #[inline]
1052    pub fn vmctx_vmfunction_import_array_call(&self, index: FuncIndex) -> u32 {
1053        self.vmctx_vmfunction_import(index) + u32::from(self.vmfunction_import_array_call())
1054    }
1055
1056    /// Return the offset to the `vmctx` field in `*const VMFunctionBody` index `index`.
1057    #[inline]
1058    pub fn vmctx_vmfunction_import_vmctx(&self, index: FuncIndex) -> u32 {
1059        self.vmctx_vmfunction_import(index) + u32::from(self.vmfunction_import_vmctx())
1060    }
1061
1062    /// Return the offset to the `from` field in the imported `VMTable` at index
1063    /// `index`.
1064    #[inline]
1065    pub fn vmctx_vmtable_from(&self, index: TableIndex) -> u32 {
1066        self.vmctx_vmtable_import(index) + u32::from(self.vmtable_import_from())
1067    }
1068
1069    /// Return the offset to the `base` field in `VMTableDefinition` index `index`.
1070    #[inline]
1071    pub fn vmctx_vmtable_definition_base(&self, index: DefinedTableIndex) -> u32 {
1072        self.vmctx_vmtable_definition(index) + u32::from(self.vmtable_definition_base())
1073    }
1074
1075    /// Return the offset to the `current_elements` field in `VMTableDefinition` index `index`.
1076    #[inline]
1077    pub fn vmctx_vmtable_definition_current_elements(&self, index: DefinedTableIndex) -> u32 {
1078        self.vmctx_vmtable_definition(index) + u32::from(self.vmtable_definition_current_elements())
1079    }
1080
1081    /// Return the offset to the `from` field in `VMMemoryImport` index `index`.
1082    #[inline]
1083    pub fn vmctx_vmmemory_import_from(&self, index: MemoryIndex) -> u32 {
1084        self.vmctx_vmmemory_import(index) + u32::from(self.vmmemory_import_from())
1085    }
1086
1087    /// Return the offset to the `base` field in `VMMemoryDefinition` index `index`.
1088    #[inline]
1089    pub fn vmctx_vmmemory_definition_base(&self, index: OwnedMemoryIndex) -> u32 {
1090        self.vmctx_vmmemory_definition(index) + u32::from(self.ptr.vmmemory_definition_base())
1091    }
1092
1093    /// Return the offset to the `current_length` field in `VMMemoryDefinition` index `index`.
1094    #[inline]
1095    pub fn vmctx_vmmemory_definition_current_length(&self, index: OwnedMemoryIndex) -> u32 {
1096        self.vmctx_vmmemory_definition(index)
1097            + u32::from(self.ptr.vmmemory_definition_current_length())
1098    }
1099
1100    /// Return the offset to the `from` field in `VMGlobalImport` index `index`.
1101    #[inline]
1102    pub fn vmctx_vmglobal_import_from(&self, index: GlobalIndex) -> u32 {
1103        self.vmctx_vmglobal_import(index) + u32::from(self.vmglobal_import_from())
1104    }
1105
1106    /// Return the offset to the `from` field in `VMTagImport` index `index`.
1107    #[inline]
1108    pub fn vmctx_vmtag_import_from(&self, index: TagIndex) -> u32 {
1109        self.vmctx_vmtag_import(index) + u32::from(self.vmtag_import_from())
1110    }
1111
1112    /// Return the offset to the `vmctx` field in `VMTagImport` index `index`.
1113    #[inline]
1114    pub fn vmctx_vmtag_import_vmctx(&self, index: TagIndex) -> u32 {
1115        self.vmctx_vmtag_import(index) + u32::from(self.vmtag_import_vmctx())
1116    }
1117
1118    /// Return the offset to the `index` field in `VMTagImport` index `index`.
1119    #[inline]
1120    pub fn vmctx_vmtag_import_index(&self, index: TagIndex) -> u32 {
1121        self.vmctx_vmtag_import(index) + u32::from(self.vmtag_import_index())
1122    }
1123}
1124
1125/// Offsets for `VMGcHeader`.
1126impl<P: PtrSize> VMOffsets<P> {
1127    /// Return the offset for the `VMGcHeader::kind` field.
1128    #[inline]
1129    pub fn vm_gc_header_kind(&self) -> u32 {
1130        0
1131    }
1132
1133    /// Return the offset for the `VMGcHeader`'s reserved bits.
1134    #[inline]
1135    pub fn vm_gc_header_reserved_bits(&self) -> u32 {
1136        // NB: The reserved bits are the unused `VMGcKind` bits.
1137        self.vm_gc_header_kind()
1138    }
1139
1140    /// Return the offset for the `VMGcHeader::ty` field.
1141    #[inline]
1142    pub fn vm_gc_header_ty(&self) -> u32 {
1143        self.vm_gc_header_kind() + 4
1144    }
1145}
1146
1147/// Offsets for `VMDrcHeader`.
1148///
1149/// Should only be used when the DRC collector is enabled.
1150impl<P: PtrSize> VMOffsets<P> {
1151    /// Return the offset for `VMDrcHeader::ref_count`.
1152    #[inline]
1153    pub fn vm_drc_header_ref_count(&self) -> u32 {
1154        8
1155    }
1156
1157    /// Return the offset for `VMDrcHeader::next_over_approximated_stack_root`.
1158    #[inline]
1159    pub fn vm_drc_header_next_over_approximated_stack_root(&self) -> u32 {
1160        self.vm_drc_header_ref_count() + 8
1161    }
1162}
1163
1164/// Magic value for core Wasm VM contexts.
1165///
1166/// This is stored at the start of all `VMContext` structures.
1167pub const VMCONTEXT_MAGIC: u32 = u32::from_le_bytes(*b"core");
1168
1169/// Equivalent of `VMCONTEXT_MAGIC` except for array-call host functions.
1170///
1171/// This is stored at the start of all `VMArrayCallHostFuncContext` structures
1172/// and double-checked on `VMArrayCallHostFuncContext::from_opaque`.
1173pub const VM_ARRAY_CALL_HOST_FUNC_MAGIC: u32 = u32::from_le_bytes(*b"ACHF");
1174
1175#[cfg(test)]
1176mod tests {
1177    use crate::vmoffsets::align;
1178
1179    #[test]
1180    fn alignment() {
1181        fn is_aligned(x: u32) -> bool {
1182            x % 16 == 0
1183        }
1184        assert!(is_aligned(align(0, 16)));
1185        assert!(is_aligned(align(32, 16)));
1186        assert!(is_aligned(align(33, 16)));
1187        assert!(is_aligned(align(31, 16)));
1188    }
1189}