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/// A trait to abstract over various types that contain a `P: PtrSize`.
569pub trait GetPtrSize {
570    /// The type that implements `PtrSize`.
571    type Ptr: PtrSize;
572
573    /// Get a `&P` where `P: PtrSize`.
574    fn get_ptr_size(&self) -> &Self::Ptr;
575}
576
577impl<P> GetPtrSize for P
578where
579    P: PtrSize,
580{
581    type Ptr = Self;
582
583    #[inline]
584    fn get_ptr_size(&self) -> &Self::Ptr {
585        self
586    }
587}
588
589/// Type representing the size of a pointer for the current compilation host
590#[derive(Clone, Copy)]
591pub struct HostPtr;
592
593impl PtrSize for HostPtr {
594    #[inline]
595    fn size(&self) -> u8 {
596        core::mem::size_of::<usize>() as u8
597    }
598}
599
600impl PtrSize for u8 {
601    #[inline]
602    fn size(&self) -> u8 {
603        *self
604    }
605}
606
607/// Used to construct a `VMOffsets`
608#[derive(Debug, Clone, Copy)]
609pub struct VMOffsetsFields<P> {
610    /// The size in bytes of a pointer on the target.
611    pub ptr: P,
612    /// The number of imported functions in the module.
613    pub num_imported_functions: u32,
614    /// The number of imported tables in the module.
615    pub num_imported_tables: u32,
616    /// The number of imported memories in the module.
617    pub num_imported_memories: u32,
618    /// The number of imported globals in the module.
619    pub num_imported_globals: u32,
620    /// The number of imported tags in the module.
621    pub num_imported_tags: u32,
622    /// The number of defined tables in the module.
623    pub num_defined_tables: u32,
624    /// The number of defined memories in the module.
625    pub num_defined_memories: u32,
626    /// The number of memories owned by the module instance.
627    pub num_owned_memories: u32,
628    /// The number of defined globals in the module.
629    pub num_defined_globals: u32,
630    /// The number of defined tags in the module.
631    pub num_defined_tags: u32,
632    /// The number of escaped functions in the module, the size of the function
633    /// references array.
634    pub num_escaped_funcs: u32,
635    /// The number of runtime data segments in the module.
636    pub num_runtime_data: u32,
637    /// Whether or not the module has a start function.
638    pub has_startup_func: bool,
639}
640
641impl<P: PtrSize> VMOffsets<P> {
642    /// Return a new `VMOffsets` instance, for a given pointer size.
643    pub fn new(ptr: P, module: &Module) -> Self {
644        let num_owned_memories = module
645            .memories
646            .iter()
647            .skip(module.num_imported_memories)
648            .filter(|p| !p.1.shared)
649            .count()
650            .try_into()
651            .unwrap();
652        VMOffsets::from(VMOffsetsFields {
653            ptr,
654            num_imported_functions: cast_to_u32(module.num_imported_funcs),
655            num_imported_tables: cast_to_u32(module.num_imported_tables),
656            num_imported_memories: cast_to_u32(module.num_imported_memories),
657            num_imported_globals: cast_to_u32(module.num_imported_globals),
658            num_imported_tags: cast_to_u32(module.num_imported_tags),
659            num_defined_tables: cast_to_u32(module.num_defined_tables()),
660            num_defined_memories: cast_to_u32(module.num_defined_memories()),
661            num_owned_memories,
662            num_defined_globals: cast_to_u32(module.globals.len() - module.num_imported_globals),
663            num_defined_tags: cast_to_u32(module.tags.len() - module.num_imported_tags),
664            num_escaped_funcs: cast_to_u32(module.num_escaped_funcs),
665            num_runtime_data: cast_to_u32(module.runtime_data.len()),
666            has_startup_func: !module.startup.is_none(),
667        })
668    }
669
670    /// Returns the size, in bytes, of the target
671    #[inline]
672    pub fn pointer_size(&self) -> u8 {
673        self.ptr.size()
674    }
675
676    /// Returns an iterator which provides a human readable description and a
677    /// byte size. The iterator returned will iterate over the bytes allocated
678    /// to the entire `VMOffsets` structure to explain where each byte size is
679    /// coming from.
680    pub fn region_sizes(&self) -> impl Iterator<Item = (&str, u32)> {
681        macro_rules! calculate_sizes {
682            ($($name:ident: $desc:tt,)*) => {{
683                let VMOffsets {
684                    // These fields are metadata not talking about specific
685                    // offsets of specific fields.
686                    ptr: _,
687                    num_imported_functions: _,
688                    num_imported_tables: _,
689                    num_imported_memories: _,
690                    num_imported_globals: _,
691                    num_imported_tags: _,
692                    num_defined_tables: _,
693                    num_defined_globals: _,
694                    num_defined_memories: _,
695                    num_defined_tags: _,
696                    num_owned_memories: _,
697                    num_escaped_funcs: _,
698                    num_runtime_data: _,
699                    has_startup_func: _,
700
701                    // used as the initial size below
702                    size,
703
704                    // exhaustively match the rest of the fields with input from
705                    // the macro
706                    $($name,)*
707                } = *self;
708
709                // calculate the size of each field by relying on the inputs to
710                // the macro being in reverse order and determining the size of
711                // the field as the offset from the field to the last field.
712                let mut last = size;
713                $(
714                    assert!($name <= last);
715                    let tmp = $name;
716                    let $name = last - $name;
717                    last = tmp;
718                )*
719                assert_ne!(last, 0);
720                IntoIterator::into_iter([
721                    $(($desc, $name),)*
722                    ("static vmctx data", last),
723                ])
724            }};
725        }
726
727        calculate_sizes! {
728            runtime_data_lengths: "runtime data lengths",
729            runtime_data_bases: "runtime data base pointers",
730            startup_func_ref: "startup funcref",
731            defined_func_refs: "module functions",
732            defined_tags: "defined tags",
733            defined_globals: "defined globals",
734            defined_tables: "defined tables",
735            imported_tags: "imported tags",
736            imported_globals: "imported globals",
737            imported_tables: "imported tables",
738            imported_functions: "imported functions",
739            owned_memories: "owned memories",
740            defined_memories: "defined memories",
741            imported_memories: "imported memories",
742        }
743    }
744}
745
746impl<P: PtrSize> GetPtrSize for VMOffsets<P> {
747    type Ptr = P;
748
749    #[inline]
750    fn get_ptr_size(&self) -> &Self::Ptr {
751        &self.ptr
752    }
753}
754
755impl<P: PtrSize> From<VMOffsetsFields<P>> for VMOffsets<P> {
756    fn from(fields: VMOffsetsFields<P>) -> VMOffsets<P> {
757        let mut ret = Self {
758            ptr: fields.ptr,
759            num_imported_functions: fields.num_imported_functions,
760            num_imported_tables: fields.num_imported_tables,
761            num_imported_memories: fields.num_imported_memories,
762            num_imported_globals: fields.num_imported_globals,
763            num_imported_tags: fields.num_imported_tags,
764            num_defined_tables: fields.num_defined_tables,
765            num_defined_memories: fields.num_defined_memories,
766            num_owned_memories: fields.num_owned_memories,
767            num_defined_globals: fields.num_defined_globals,
768            num_defined_tags: fields.num_defined_tags,
769            num_escaped_funcs: fields.num_escaped_funcs,
770            num_runtime_data: fields.num_runtime_data,
771            has_startup_func: fields.has_startup_func,
772            imported_functions: 0,
773            imported_tables: 0,
774            imported_memories: 0,
775            imported_globals: 0,
776            imported_tags: 0,
777            defined_tables: 0,
778            defined_memories: 0,
779            owned_memories: 0,
780            defined_globals: 0,
781            defined_tags: 0,
782            defined_func_refs: 0,
783            startup_func_ref: 0,
784            runtime_data_bases: 0,
785            runtime_data_lengths: 0,
786            size: 0,
787        };
788
789        // Convenience functions for checked addition and multiplication.
790        // As side effect this reduces binary size by using only a single
791        // `#[track_caller]` location for each function instead of one for
792        // each individual invocation.
793        #[inline]
794        fn cadd(count: u32, size: u32) -> u32 {
795            count.checked_add(size).unwrap()
796        }
797
798        #[inline]
799        fn cmul(count: u32, size: u8) -> u32 {
800            count.checked_mul(u32::from(size)).unwrap()
801        }
802
803        let mut next_field_offset = u32::from(ret.ptr.vmctx_dynamic_data_start());
804
805        macro_rules! fields {
806            (size($field:ident) = $size:expr, $($rest:tt)*) => {
807                ret.$field = next_field_offset;
808                next_field_offset = cadd(next_field_offset, u32::from($size));
809                fields!($($rest)*);
810            };
811            (align($align:expr), $($rest:tt)*) => {
812                next_field_offset = align(next_field_offset, $align);
813                fields!($($rest)*);
814            };
815            () => {};
816        }
817
818        fields! {
819            size(imported_memories)
820                = cmul(ret.num_imported_memories, ret.size_of_vmmemory_import()),
821            size(defined_memories)
822                = cmul(ret.num_defined_memories, ret.ptr.size_of_vmmemory_pointer()),
823            size(owned_memories)
824                = cmul(ret.num_owned_memories, ret.ptr.size_of_vmmemory_definition()),
825            size(imported_functions)
826                = cmul(ret.num_imported_functions, ret.size_of_vmfunction_import()),
827            size(imported_tables)
828                = cmul(ret.num_imported_tables, ret.size_of_vmtable_import()),
829            size(imported_globals)
830                = cmul(ret.num_imported_globals, ret.size_of_vmglobal_import()),
831            size(imported_tags)
832                = cmul(ret.num_imported_tags, ret.size_of_vmtag_import()),
833            size(defined_tables)
834                = cmul(ret.num_defined_tables, ret.size_of_vmtable_definition()),
835            align(16),
836            size(defined_globals)
837                = cmul(ret.num_defined_globals, ret.ptr.size_of_vmglobal_definition()),
838            size(defined_tags)
839                = cmul(ret.num_defined_tags, ret.ptr.size_of_vmtag_definition()),
840            size(defined_func_refs) = cmul(
841                ret.num_escaped_funcs,
842                ret.ptr.size_of_vm_func_ref(),
843            ),
844            size(startup_func_ref) = if ret.has_startup_func {
845                ret.ptr.size_of_vm_func_ref()
846            } else {
847                0
848            },
849            size(runtime_data_bases) = cmul(ret.num_runtime_data, ret.ptr.size()),
850            size(runtime_data_lengths) = cmul(ret.num_runtime_data, 4),
851        }
852
853        ret.size = next_field_offset;
854
855        return ret;
856    }
857}
858
859impl<P: PtrSize> VMOffsets<P> {
860    /// The offset of the `VMFunctionImport::array_call` field.
861    #[inline]
862    pub fn vmfunction_import_array_call(&self) -> u8 {
863        0 * self.pointer_size()
864    }
865
866    /// The offset of the `VMFunctionImport::wasm_call` field.
867    #[inline]
868    pub fn vmfunction_import_wasm_call(&self) -> u8 {
869        1 * self.pointer_size()
870    }
871
872    /// The offset of the `VMFunctionImport::type_index` field.
873    #[inline]
874    pub fn vmfunction_import_type_index(&self) -> u8 {
875        2 * self.pointer_size()
876    }
877
878    /// The offset of the `VMFunctionImport::vmctx` field.
879    #[inline]
880    pub fn vmfunction_import_vmctx(&self) -> u8 {
881        3 * self.pointer_size()
882    }
883
884    /// Return the size of `VMFunctionImport`.
885    #[inline]
886    pub fn size_of_vmfunction_import(&self) -> u8 {
887        4 * self.pointer_size()
888    }
889}
890
891/// Offsets for `*const VMFunctionBody`.
892impl<P: PtrSize> VMOffsets<P> {
893    /// The size of the `current_elements` field.
894    pub fn size_of_vmfunction_body_ptr(&self) -> u8 {
895        1 * self.pointer_size()
896    }
897}
898
899/// Offsets for `VMTableImport`.
900impl<P: PtrSize> VMOffsets<P> {
901    /// The offset of the `from` field.
902    #[inline]
903    pub fn vmtable_import_from(&self) -> u8 {
904        0 * self.pointer_size()
905    }
906
907    /// The offset of the `vmctx` field.
908    #[inline]
909    pub fn vmtable_import_vmctx(&self) -> u8 {
910        1 * self.pointer_size()
911    }
912
913    /// The offset of the `index` field.
914    #[inline]
915    pub fn vmtable_import_index(&self) -> u8 {
916        2 * self.pointer_size()
917    }
918
919    /// Return the size of `VMTableImport`.
920    #[inline]
921    pub fn size_of_vmtable_import(&self) -> u8 {
922        3 * self.pointer_size()
923    }
924}
925
926/// Offsets for `VMTableDefinition`.
927impl<P: PtrSize> VMOffsets<P> {
928    /// The offset of the `base` field.
929    #[inline]
930    pub fn vmtable_definition_base(&self) -> u8 {
931        0 * self.pointer_size()
932    }
933
934    /// The offset of the `current_elements` field.
935    pub fn vmtable_definition_current_elements(&self) -> u8 {
936        1 * self.pointer_size()
937    }
938
939    /// The size of the `current_elements` field.
940    #[inline]
941    pub fn size_of_vmtable_definition_current_elements(&self) -> u8 {
942        self.pointer_size()
943    }
944
945    /// Return the size of `VMTableDefinition`.
946    #[inline]
947    pub fn size_of_vmtable_definition(&self) -> u8 {
948        2 * self.pointer_size()
949    }
950}
951
952/// Offsets for `VMMemoryImport`.
953impl<P: PtrSize> VMOffsets<P> {
954    /// The offset of the `from` field.
955    #[inline]
956    pub fn vmmemory_import_from(&self) -> u8 {
957        0 * self.pointer_size()
958    }
959
960    /// The offset of the `vmctx` field.
961    #[inline]
962    pub fn vmmemory_import_vmctx(&self) -> u8 {
963        1 * self.pointer_size()
964    }
965
966    /// The offset of the `index` field.
967    #[inline]
968    pub fn vmmemory_import_index(&self) -> u8 {
969        2 * self.pointer_size()
970    }
971
972    /// Return the size of `VMMemoryImport`.
973    #[inline]
974    pub fn size_of_vmmemory_import(&self) -> u8 {
975        3 * self.pointer_size()
976    }
977}
978
979/// Offsets for `VMGlobalImport`.
980impl<P: PtrSize> VMOffsets<P> {
981    /// The offset of the `from` field.
982    #[inline]
983    pub fn vmglobal_import_from(&self) -> u8 {
984        0 * self.pointer_size()
985    }
986
987    /// Return the size of `VMGlobalImport`.
988    #[inline]
989    pub fn size_of_vmglobal_import(&self) -> u8 {
990        // `VMGlobalImport` has two pointers plus 8 bytes for `VMGlobalKind`
991        2 * self.pointer_size() + 8
992    }
993}
994
995/// Offsets for `VMSharedTypeIndex`.
996impl<P: PtrSize> VMOffsets<P> {
997    /// Return the size of `VMSharedTypeIndex`.
998    #[inline]
999    pub fn size_of_vmshared_type_index(&self) -> u8 {
1000        4
1001    }
1002}
1003
1004/// Offsets for `VMTagImport`.
1005impl<P: PtrSize> VMOffsets<P> {
1006    /// The offset of the `from` field.
1007    #[inline]
1008    pub fn vmtag_import_from(&self) -> u8 {
1009        0 * self.pointer_size()
1010    }
1011
1012    /// The offset of the `vmctx` field.
1013    #[inline]
1014    pub fn vmtag_import_vmctx(&self) -> u8 {
1015        1 * self.pointer_size()
1016    }
1017
1018    /// The offset of the `index` field.
1019    #[inline]
1020    pub fn vmtag_import_index(&self) -> u8 {
1021        2 * self.pointer_size()
1022    }
1023
1024    /// Return the size of `VMTagImport`.
1025    #[inline]
1026    pub fn size_of_vmtag_import(&self) -> u8 {
1027        3 * self.pointer_size()
1028    }
1029}
1030
1031/// Offsets for `VMContext`.
1032impl<P: PtrSize> VMOffsets<P> {
1033    /// The offset of the `tables` array.
1034    #[inline]
1035    pub fn vmctx_imported_functions_begin(&self) -> u32 {
1036        self.imported_functions
1037    }
1038
1039    /// The offset of the `tables` array.
1040    #[inline]
1041    pub fn vmctx_imported_tables_begin(&self) -> u32 {
1042        self.imported_tables
1043    }
1044
1045    /// The offset of the `memories` array.
1046    #[inline]
1047    pub fn vmctx_imported_memories_begin(&self) -> u32 {
1048        self.imported_memories
1049    }
1050
1051    /// The offset of the `globals` array.
1052    #[inline]
1053    pub fn vmctx_imported_globals_begin(&self) -> u32 {
1054        self.imported_globals
1055    }
1056
1057    /// The offset of the `tags` array.
1058    #[inline]
1059    pub fn vmctx_imported_tags_begin(&self) -> u32 {
1060        self.imported_tags
1061    }
1062
1063    /// The offset of the `tables` array.
1064    #[inline]
1065    pub fn vmctx_tables_begin(&self) -> u32 {
1066        self.defined_tables
1067    }
1068
1069    /// The offset of the `memories` array.
1070    #[inline]
1071    pub fn vmctx_memories_begin(&self) -> u32 {
1072        self.defined_memories
1073    }
1074
1075    /// The offset of the `owned_memories` array.
1076    #[inline]
1077    pub fn vmctx_owned_memories_begin(&self) -> u32 {
1078        self.owned_memories
1079    }
1080
1081    /// The offset of the `globals` array.
1082    #[inline]
1083    pub fn vmctx_globals_begin(&self) -> u32 {
1084        self.defined_globals
1085    }
1086
1087    /// The offset of the `tags` array.
1088    #[inline]
1089    pub fn vmctx_tags_begin(&self) -> u32 {
1090        self.defined_tags
1091    }
1092
1093    /// The offset of the `func_refs` array.
1094    #[inline]
1095    pub fn vmctx_func_refs_begin(&self) -> u32 {
1096        self.defined_func_refs
1097    }
1098
1099    /// The offset of the `runtime_data_bases` array.
1100    #[inline]
1101    pub fn vmctx_runtime_data_bases_begin(&self) -> u32 {
1102        self.runtime_data_bases
1103    }
1104
1105    /// The offset of the `runtime_data_lengths` array.
1106    #[inline]
1107    pub fn vmctx_runtime_data_lengths_begin(&self) -> u32 {
1108        self.runtime_data_lengths
1109    }
1110
1111    /// Return the size of the `VMContext` allocation.
1112    #[inline]
1113    pub fn size_of_vmctx(&self) -> u32 {
1114        self.size
1115    }
1116
1117    /// Return the offset to `VMFunctionImport` index `index`.
1118    #[inline]
1119    pub fn vmctx_vmfunction_import(&self, index: FuncIndex) -> u32 {
1120        assert!(index.as_u32() < self.num_imported_functions);
1121        self.vmctx_imported_functions_begin()
1122            + index.as_u32() * u32::from(self.size_of_vmfunction_import())
1123    }
1124
1125    /// Return the offset to `VMTable` index `index`.
1126    #[inline]
1127    pub fn vmctx_vmtable_import(&self, index: TableIndex) -> u32 {
1128        assert!(index.as_u32() < self.num_imported_tables);
1129        self.vmctx_imported_tables_begin()
1130            + index.as_u32() * u32::from(self.size_of_vmtable_import())
1131    }
1132
1133    /// Return the offset to `VMMemoryImport` index `index`.
1134    #[inline]
1135    pub fn vmctx_vmmemory_import(&self, index: MemoryIndex) -> u32 {
1136        assert!(index.as_u32() < self.num_imported_memories);
1137        self.vmctx_imported_memories_begin()
1138            + index.as_u32() * u32::from(self.size_of_vmmemory_import())
1139    }
1140
1141    /// Return the offset to `VMGlobalImport` index `index`.
1142    #[inline]
1143    pub fn vmctx_vmglobal_import(&self, index: GlobalIndex) -> u32 {
1144        assert!(index.as_u32() < self.num_imported_globals);
1145        self.vmctx_imported_globals_begin()
1146            + index.as_u32() * u32::from(self.size_of_vmglobal_import())
1147    }
1148
1149    /// Return the offset to `VMTagImport` index `index`.
1150    #[inline]
1151    pub fn vmctx_vmtag_import(&self, index: TagIndex) -> u32 {
1152        assert!(index.as_u32() < self.num_imported_tags);
1153        self.vmctx_imported_tags_begin() + index.as_u32() * u32::from(self.size_of_vmtag_import())
1154    }
1155
1156    /// Return the offset to `VMTableDefinition` index `index`.
1157    #[inline]
1158    pub fn vmctx_vmtable_definition(&self, index: DefinedTableIndex) -> u32 {
1159        assert!(index.as_u32() < self.num_defined_tables);
1160        self.vmctx_tables_begin() + index.as_u32() * u32::from(self.size_of_vmtable_definition())
1161    }
1162
1163    /// Return the offset to the `*mut VMMemoryDefinition` at index `index`.
1164    #[inline]
1165    pub fn vmctx_vmmemory_pointer(&self, index: DefinedMemoryIndex) -> u32 {
1166        assert!(index.as_u32() < self.num_defined_memories);
1167        self.vmctx_memories_begin()
1168            + index.as_u32() * u32::from(self.ptr.size_of_vmmemory_pointer())
1169    }
1170
1171    /// Return the offset to the owned `VMMemoryDefinition` at index `index`.
1172    #[inline]
1173    pub fn vmctx_vmmemory_definition(&self, index: OwnedMemoryIndex) -> u32 {
1174        assert!(index.as_u32() < self.num_owned_memories);
1175        self.vmctx_owned_memories_begin()
1176            + index.as_u32() * u32::from(self.ptr.size_of_vmmemory_definition())
1177    }
1178
1179    /// Return the offset to the `VMGlobalDefinition` index `index`.
1180    #[inline]
1181    pub fn vmctx_vmglobal_definition(&self, index: DefinedGlobalIndex) -> u32 {
1182        assert!(index.as_u32() < self.num_defined_globals);
1183        self.vmctx_globals_begin()
1184            + index.as_u32() * u32::from(self.ptr.size_of_vmglobal_definition())
1185    }
1186
1187    /// Return the offset to the `VMTagDefinition` index `index`.
1188    #[inline]
1189    pub fn vmctx_vmtag_definition(&self, index: DefinedTagIndex) -> u32 {
1190        assert!(index.as_u32() < self.num_defined_tags);
1191        self.vmctx_tags_begin() + index.as_u32() * u32::from(self.ptr.size_of_vmtag_definition())
1192    }
1193
1194    /// Return the offset to the `VMFuncRef` for the given function
1195    /// index (either imported or defined).
1196    #[inline]
1197    pub fn vmctx_func_ref(&self, index: FuncRefIndex) -> u32 {
1198        assert!(!index.is_reserved_value());
1199        assert!(index.as_u32() < self.num_escaped_funcs);
1200        self.vmctx_func_refs_begin() + index.as_u32() * u32::from(self.ptr.size_of_vm_func_ref())
1201    }
1202
1203    /// Returns the offset to the `VMFuncRef` for the module startup function.
1204    ///
1205    /// Panics if this module does not have a startup function.
1206    #[inline]
1207    pub fn vmctx_startup_func_ref(&self) -> u32 {
1208        assert!(self.has_startup_func);
1209        self.startup_func_ref
1210    }
1211
1212    /// Return the offset to the base of the runtime data segment at `index`.
1213    #[inline]
1214    pub fn vmctx_runtime_data_base(&self, index: RuntimeDataIndex) -> u32 {
1215        assert!(!index.is_reserved_value());
1216        assert!(index.as_u32() < self.num_runtime_data);
1217        self.vmctx_runtime_data_bases_begin() + index.as_u32() * u32::from(self.ptr.size())
1218    }
1219
1220    /// Return the offset to the length of the runtime data segment at `index`.
1221    #[inline]
1222    pub fn vmctx_runtime_data_length(&self, index: RuntimeDataIndex) -> u32 {
1223        assert!(!index.is_reserved_value());
1224        assert!(index.as_u32() < self.num_runtime_data);
1225        self.vmctx_runtime_data_lengths_begin() + index.as_u32() * 4
1226    }
1227
1228    /// Return the offset to the `wasm_call` field in `*const VMFunctionBody` index `index`.
1229    #[inline]
1230    pub fn vmctx_vmfunction_import_wasm_call(&self, index: FuncIndex) -> u32 {
1231        self.vmctx_vmfunction_import(index) + u32::from(self.vmfunction_import_wasm_call())
1232    }
1233
1234    /// Return the offset to the `array_call` field in `*const VMFunctionBody` index `index`.
1235    #[inline]
1236    pub fn vmctx_vmfunction_import_array_call(&self, index: FuncIndex) -> u32 {
1237        self.vmctx_vmfunction_import(index) + u32::from(self.vmfunction_import_array_call())
1238    }
1239
1240    /// Return the offset to the `vmctx` field in `*const VMFunctionBody` index `index`.
1241    #[inline]
1242    pub fn vmctx_vmfunction_import_vmctx(&self, index: FuncIndex) -> u32 {
1243        self.vmctx_vmfunction_import(index) + u32::from(self.vmfunction_import_vmctx())
1244    }
1245
1246    /// Return the offset to the `from` field in the imported `VMTable` at index
1247    /// `index`.
1248    #[inline]
1249    pub fn vmctx_vmtable_from(&self, index: TableIndex) -> u32 {
1250        self.vmctx_vmtable_import(index) + u32::from(self.vmtable_import_from())
1251    }
1252
1253    /// Return the offset to the `base` field in `VMTableDefinition` index `index`.
1254    #[inline]
1255    pub fn vmctx_vmtable_definition_base(&self, index: DefinedTableIndex) -> u32 {
1256        self.vmctx_vmtable_definition(index) + u32::from(self.vmtable_definition_base())
1257    }
1258
1259    /// Return the offset to the `current_elements` field in `VMTableDefinition` index `index`.
1260    #[inline]
1261    pub fn vmctx_vmtable_definition_current_elements(&self, index: DefinedTableIndex) -> u32 {
1262        self.vmctx_vmtable_definition(index) + u32::from(self.vmtable_definition_current_elements())
1263    }
1264
1265    /// Return the offset to the `from` field in `VMMemoryImport` index `index`.
1266    #[inline]
1267    pub fn vmctx_vmmemory_import_from(&self, index: MemoryIndex) -> u32 {
1268        self.vmctx_vmmemory_import(index) + u32::from(self.vmmemory_import_from())
1269    }
1270
1271    /// Return the offset to the `base` field in `VMMemoryDefinition` index `index`.
1272    #[inline]
1273    pub fn vmctx_vmmemory_definition_base(&self, index: OwnedMemoryIndex) -> u32 {
1274        self.vmctx_vmmemory_definition(index) + u32::from(self.ptr.vmmemory_definition_base())
1275    }
1276
1277    /// Return the offset to the `current_length` field in `VMMemoryDefinition` index `index`.
1278    #[inline]
1279    pub fn vmctx_vmmemory_definition_current_length(&self, index: OwnedMemoryIndex) -> u32 {
1280        self.vmctx_vmmemory_definition(index)
1281            + u32::from(self.ptr.vmmemory_definition_current_length())
1282    }
1283
1284    /// Return the offset to the `from` field in `VMGlobalImport` index `index`.
1285    #[inline]
1286    pub fn vmctx_vmglobal_import_from(&self, index: GlobalIndex) -> u32 {
1287        self.vmctx_vmglobal_import(index) + u32::from(self.vmglobal_import_from())
1288    }
1289
1290    /// Return the offset to the `from` field in `VMTagImport` index `index`.
1291    #[inline]
1292    pub fn vmctx_vmtag_import_from(&self, index: TagIndex) -> u32 {
1293        self.vmctx_vmtag_import(index) + u32::from(self.vmtag_import_from())
1294    }
1295
1296    /// Return the offset to the `vmctx` field in `VMTagImport` index `index`.
1297    #[inline]
1298    pub fn vmctx_vmtag_import_vmctx(&self, index: TagIndex) -> u32 {
1299        self.vmctx_vmtag_import(index) + u32::from(self.vmtag_import_vmctx())
1300    }
1301
1302    /// Return the offset to the `index` field in `VMTagImport` index `index`.
1303    #[inline]
1304    pub fn vmctx_vmtag_import_index(&self, index: TagIndex) -> u32 {
1305        self.vmctx_vmtag_import(index) + u32::from(self.vmtag_import_index())
1306    }
1307}
1308
1309/// Offsets for `VMGcHeader`.
1310impl<P: PtrSize> VMOffsets<P> {
1311    /// Return the offset for the `VMGcHeader::kind` field.
1312    #[inline]
1313    pub fn vm_gc_header_kind(&self) -> u32 {
1314        0
1315    }
1316
1317    /// Return the offset for the `VMGcHeader`'s reserved bits.
1318    #[inline]
1319    pub fn vm_gc_header_reserved_bits(&self) -> u32 {
1320        // NB: The reserved bits are the unused `VMGcKind` bits.
1321        self.vm_gc_header_kind()
1322    }
1323
1324    /// Return the offset for the `VMGcHeader::ty` field.
1325    #[inline]
1326    pub fn vm_gc_header_ty(&self) -> u32 {
1327        self.vm_gc_header_kind() + 4
1328    }
1329}
1330
1331/// Offsets for `VMDrcHeader`.
1332///
1333/// Should only be used when the DRC collector is enabled.
1334impl<P: PtrSize> VMOffsets<P> {
1335    /// Return the offset for `VMDrcHeader::ref_count`.
1336    #[inline]
1337    pub fn vm_drc_header_ref_count(&self) -> u32 {
1338        8
1339    }
1340
1341    /// Return the offset for `VMDrcHeader::next_over_approximated_stack_root`.
1342    #[inline]
1343    pub fn vm_drc_header_next_over_approximated_stack_root(&self) -> u32 {
1344        self.vm_drc_header_ref_count() + 8
1345    }
1346}
1347
1348/// Magic value for core Wasm VM contexts.
1349///
1350/// This is stored at the start of all `VMContext` structures.
1351pub const VMCONTEXT_MAGIC: u32 = u32::from_le_bytes(*b"core");
1352
1353/// Equivalent of `VMCONTEXT_MAGIC` except for array-call host functions.
1354///
1355/// This is stored at the start of all `VMArrayCallHostFuncContext` structures
1356/// and double-checked on `VMArrayCallHostFuncContext::from_opaque`.
1357pub const VM_ARRAY_CALL_HOST_FUNC_MAGIC: u32 = u32::from_le_bytes(*b"ACHF");
1358
1359#[cfg(test)]
1360mod tests {
1361    use crate::vmoffsets::align;
1362
1363    #[test]
1364    fn alignment() {
1365        fn is_aligned(x: u32) -> bool {
1366            x % 16 == 0
1367        }
1368        assert!(is_aligned(align(0, 16)));
1369        assert!(is_aligned(align(32, 16)));
1370        assert!(is_aligned(align(33, 16)));
1371        assert!(is_aligned(align(31, 16)));
1372    }
1373}