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