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/// Generate the [`offsets`] module: a `struct VMFoo<P: PtrSize>(P)` for each
66/// `VM*` type, with a method per field returning that field's offset, plus
67/// `size` and `align` methods.
68macro_rules! define_vm_type_offsets {
69    // `UnsafeCell<T>` is `repr(transparent)`, so it has exactly `T`'s layout;
70    // delegate to the inner type.
71    (@size ($p:expr) UnsafeCell < $inner:tt >) => { define_vm_type_offsets!(@size ($p) $inner) };
72
73    // Classify a field type to its size in bytes as a `u32`, given `$p` (the
74    // target pointer size as a `u8`). All `VmPtr<_>` and `Option<VmPtr<_>>`
75    // fields are pointer-sized; the `Defined*Index` types are `u32` entity
76    // references; and `VMGlobalKind` is a `repr(C, u32)` enum with a `u32`
77    // payload.
78    (@size ($p:expr) VmPtr < $g:ty >) => { u32::from($p) };
79    (@size ($p:expr) Option < VmPtr < $g:ty >>) => { u32::from($p) };
80    (@size ($p:expr) AtomicUsize) => { u32::from($p) };
81    (@size ($p:expr) usize) => { u32::from($p) };
82    (@size ($p:expr) i64) => { 8u32 };
83    (@size ($p:expr) u64) => { 8u32 };
84    (@size ($p:expr) u32) => { 4u32 };
85    (@size ($p:expr) [u8; 16]) => { 16u32 };
86    (@size ($p:expr) [u32; $n:expr]) => { 4u32 * u32::try_from($n).unwrap() };
87    (@size ($p:expr) VMSharedTypeIndex) => { u32::from(($p).size_of_vmshared_type_index()) };
88    (@size ($p:expr) DefinedTableIndex) => { 4u32 };
89    (@size ($p:expr) DefinedMemoryIndex) => { 4u32 };
90    (@size ($p:expr) DefinedTagIndex) => { 4u32 };
91    (@size ($p:expr) VMGlobalKind) => { 8u32 };
92    // `Range<T>` is a two-field `{ start: T, end: T }` struct.
93    (@size ($p:expr) Range < *mut u8 >) => { 2u32 * u32::from($p) };
94    // Nested `VM*` types recurse through their own generated offsets, keeping
95    // this macro the single source of truth for their layout too.
96    (@size ($p:expr) VMMemoryDefinition) => { u32::from(($p).vm_memory_definition().size()) };
97    (@size ($p:expr) VMLazyThread) => { u32::from(($p).vm_lazy_thread().size()) };
98    // `VMStackChain` is a `repr(usize, C)` enum, and is not itself defined by
99    // `for_each_vm_type!`.
100    (@size ($p:expr) VMStackChain) => { u32::from(($p).size_of_vmstack_chain()) };
101
102    // As with `@size` above, `UnsafeCell<T>` has exactly `T`'s alignment.
103    (@align ($p:expr) UnsafeCell < $inner:tt >) => { define_vm_type_offsets!(@align ($p) $inner) };
104
105    // Classify a field type to its alignment in bytes as a `u32`, given `$p`
106    // (the target pointer size as a `u8`).
107    //
108    // NB: 64-bit integers are assumed to be 8-aligned, which holds everywhere except
109    // `i686-unknown-linux-gnu`, and the pointer size alone can't tell those apart. Types
110    // with 64-bit fields must therefore put them first and force their own alignment with
111    // `#[repr(C, align(8))]`, as `VMStoreContext` does.
112    (@align ($p:expr) VmPtr < $g:ty >) => { u32::from($p) };
113    (@align ($p:expr) Option < VmPtr < $g:ty >>) => { u32::from($p) };
114    (@align ($p:expr) AtomicUsize) => { u32::from($p) };
115    (@align ($p:expr) usize) => { u32::from($p) };
116    (@align ($p:expr) i64) => { 8u32 };
117    (@align ($p:expr) u64) => { 8u32 };
118    (@align ($p:expr) u32) => { 4u32 };
119    (@align ($p:expr) [u8; 16]) => { 16u32 };
120    (@align ($p:expr) [u32; $n:expr]) => { 4u32 };
121    (@align ($p:expr) VMSharedTypeIndex) => { u32::from(($p).align_of_vmshared_type_index()) };
122    (@align ($p:expr) DefinedTableIndex) => { 4u32 };
123    (@align ($p:expr) DefinedMemoryIndex) => { 4u32 };
124    (@align ($p:expr) DefinedTagIndex) => { 4u32 };
125    (@align ($p:expr) VMGlobalKind) => { 4u32 };
126    (@align ($p:expr) Range < *mut u8 >) => { u32::from($p) };
127    (@align ($p:expr) VMMemoryDefinition) => { u32::from(($p).vm_memory_definition().align()) };
128    (@align ($p:expr) VMLazyThread) => { u32::from(($p).vm_lazy_thread().align()) };
129    (@align ($p:expr) VMStackChain) => { u32::from($p) };
130
131    // Classify a `#[repr(...)]` to the minimum alignment it forces, as a `u32`.
132    (@repr_align C) => { 1u32 };
133    (@repr_align transparent) => { 1u32 };
134    (@repr_align C, align($n:literal)) => {{ let align: u32 = $n; align }};
135
136    // Emit a `pub fn` per field returning that field's offset, computed by
137    // accumulating the aligned size of each preceding field. `$p`/`$o` are
138    // caller-minted identifiers (for the pointer size and running offset) that
139    // are threaded through the recursion so their hygiene stays consistent
140    // across the emitted `let` bindings.
141    //
142    // Fields arrive pre-split into `[ $fname : $fty... ]` groups (see `@impl`
143    // below), so each field's type is a raw token sequence that the `@size`
144    // and `@align` classifiers can match against structurally.
145    (@fields $Name:ident ($p:ident, $o:ident) prefix( $($prefix:tt)* )) => {};
146    (@fields $Name:ident ($p:ident, $o:ident) prefix( $($prefix:tt)* )
147        [ $fname:ident : $($fty:tt)* ]
148        $($rest:tt)*
149    ) => {
150        #[doc = concat!(
151            "The offset of the `", stringify!($fname),
152            "` field of `", stringify!($Name), "`."
153        )]
154        #[inline]
155        pub fn $fname(&self) -> u8 {
156            let $p = self.0.size();
157            let $o: u32 = 0;
158            $($prefix)*
159            let $o = align($o, define_vm_type_offsets!(@align ($p) $($fty)*));
160            let _ = $p;
161            u8::try_from($o).unwrap()
162        }
163        define_vm_type_offsets!(@fields $Name ($p, $o)
164            prefix(
165                $($prefix)*
166                let $o = align($o, define_vm_type_offsets!(@align ($p) $($fty)*));
167                let $o = $o + define_vm_type_offsets!(@size ($p) $($fty)*);
168            )
169            $($rest)*
170        );
171    };
172
173    // Emit an `offsets::VMFoo` type's inherent `impl`. Fields are peeled off
174    // the raw struct body one at a time into `[ $fname : $fty... ]` groups
175    // accumulated in the `{ ... }` list; once the body is exhausted, the
176    // terminal arm emits the per-field offset methods plus `align`/`size`.
177    //
178    // Splitting the body by hand (rather than matching `$fty:tt $(< $fgen:ty
179    // >)?` within a repetition) is what lets each field's type reach the
180    // `@size`/`@align` classifiers as raw tokens, so those classifiers can
181    // require `Option`s to specifically be `Option<VmPtr<_>>`.
182    (@impl $Name:ident [$($repr:tt)*] { $( [ $fname:ident : $($fty:tt)* ] )* }) => {
183        impl<P: PtrSize> $Name<P> {
184            define_vm_type_offsets!(@fields $Name (p, o) prefix()
185                $( [ $fname : $($fty)* ] )*
186            );
187
188            #[doc = concat!("The alignment of the `", stringify!($Name), "` type.")]
189            #[inline]
190            pub fn align(&self) -> u8 {
191                let p = self.0.size();
192                let a: u32 = define_vm_type_offsets!(@repr_align $($repr)*);
193                $(
194                    let a = core::cmp::max(
195                        a,
196                        define_vm_type_offsets!(@align (p) $($fty)*),
197                    );
198                )*
199                let _ = p;
200                u8::try_from(a).unwrap()
201            }
202
203            #[doc = concat!("The size of the `", stringify!($Name), "` type.")]
204            #[inline]
205            pub fn size(&self) -> u8 {
206                let p = self.0.size();
207                let o: u32 = 0;
208                $(
209                    let o = align(o, define_vm_type_offsets!(@align (p) $($fty)*));
210                    let o = o + define_vm_type_offsets!(@size (p) $($fty)*);
211                )*
212                let o = align(o, u32::from(self.align()));
213                let _ = p;
214                u8::try_from(o).unwrap()
215            }
216        }
217    };
218    // Consume one field's attributes, visibility, and name, then collect its
219    // type tokens. None of the field attributes (doc comments and the
220    // `#[aggregate]`/`#[readonly]`/`#[can_move]` markers) affect layout, so they
221    // are all discarded here.
222    (@impl $Name:ident $repr:tt { $($groups:tt)* }
223        $(#[$($attr:tt)*])* $fvis:vis $fname:ident : $($rest:tt)*
224    ) => {
225        define_vm_type_offsets!(@impl_ty $Name $repr { $($groups)* } $fname [] $($rest)*);
226    };
227    // Accumulate one field's type tokens up to its terminating comma, then
228    // append the completed `[ $fname : $fty... ]` group and resume `@impl`.
229    (@impl_ty $Name:ident $repr:tt { $($groups:tt)* } $fname:ident [ $($fty:tt)* ] , $($rest:tt)*) => {
230        define_vm_type_offsets!(@impl $Name $repr { $($groups)* [ $fname : $($fty)* ] } $($rest)*);
231    };
232    (@impl_ty $Name:ident $repr:tt { $($groups:tt)* } $fname:ident [ $($fty:tt)* ] $tok:tt $($rest:tt)*) => {
233        define_vm_type_offsets!(@impl_ty $Name $repr { $($groups)* } $fname [ $($fty)* $tok ] $($rest)*);
234    };
235
236    // Top-level entry: the list of `VM*` type definitions.
237    ( $(
238        $(#[doc = $sdoc:literal])*
239        $(#[derive($($d:ident),*)])?
240        #[repr($($repr:tt)*)]
241        #[snake_name = $snake:ident]
242        $svis:vis struct $Name:ident {
243            $($body:tt)*
244        }
245    )* ) => {
246        /// Offsets of fields within the various `VM*` types, parameterized over
247        /// a target [`PtrSize`] so that they can be computed during cross
248        /// compilation.
249        ///
250        /// These types are namespaced within their own module so that they never
251        /// collide with the real definitions of the `VM*` types themselves.
252        pub mod offsets {
253            use super::{align, PtrSize, NUM_COMPONENT_CONTEXT_SLOTS};
254
255            $(
256                #[doc = concat!("Offsets of fields within the `", stringify!($Name), "` type.")]
257                pub struct $Name<P: PtrSize>(pub P);
258
259                define_vm_type_offsets!(@impl $Name [$($repr)*] {} $($body)*);
260            )*
261        }
262    };
263}
264for_each_vm_type!(define_vm_type_offsets);
265
266/// The size, in bytes, of one `context.{get,set}` slot. These slots are `u32`s,
267/// both in `VMStoreContext::component_context` and in
268/// `VMDeferredThread::saved_context`.
269const COMPONENT_CONTEXT_SLOT_SIZE: u8 = 4;
270
271/// Offsets within a `VMStoreContext` that are not simply the offset of one of
272/// its fields, and so are not generated by `for_each_vm_type!`.
273impl<P: PtrSize> offsets::VMStoreContext<P> {
274    /// The offset of the `gc_heap.base` field within a `VMStoreContext`.
275    pub fn gc_heap_base(&self) -> u8 {
276        let offset = self.gc_heap() + self.0.vm_memory_definition().base();
277        debug_assert!(offset < self.last_wasm_exit_trampoline_fp());
278        offset
279    }
280
281    /// The offset of the `gc_heap.current_length` field within a
282    /// `VMStoreContext`.
283    pub fn gc_heap_current_length(&self) -> u8 {
284        let offset = self.gc_heap() + self.0.vm_memory_definition().current_length();
285        debug_assert!(offset < self.last_wasm_exit_trampoline_fp());
286        offset
287    }
288
289    /// The offset of the `component_context[i]` slot within a `VMStoreContext`.
290    pub fn component_context_slot(&self, i: u8) -> u8 {
291        assert!(usize::from(i) < NUM_COMPONENT_CONTEXT_SLOTS);
292        self.component_context() + i * COMPONENT_CONTEXT_SLOT_SIZE
293    }
294}
295
296/// Offsets within a `VMDeferredThread` that are not simply the offset of one of
297/// its fields, and so are not generated by `for_each_vm_type!`.
298impl<P: PtrSize> offsets::VMDeferredThread<P> {
299    /// The offset of the `saved_context[i]` slot within a `VMDeferredThread`.
300    pub fn saved_context_slot(&self, i: u8) -> u8 {
301        assert!(usize::from(i) < NUM_COMPONENT_CONTEXT_SLOTS);
302        self.saved_context() + i * COMPONENT_CONTEXT_SLOT_SIZE
303    }
304}
305
306/// Add a `fn vm_foo(&self) -> offsets::VMFoo<&Self>` accessor to [`PtrSize`] for
307/// each `VM*` type, using the `#[snake_name = ...]` attribute for the method
308/// name.
309macro_rules! define_ptr_size_vm_type_accessors {
310    ( $(
311        $(#[doc = $sdoc:literal])*
312        $(#[derive($($d:ident),*)])?
313        #[repr($($repr:tt)*)]
314        #[snake_name = $snake:ident]
315        $svis:vis struct $Name:ident {
316            // This macro only needs each type's name and snake name, so the
317            // body is captured raw rather than parsed into fields.
318            $($body:tt)*
319        }
320    )* ) => {
321        $(
322            #[doc = concat!("Get the [`offsets::", stringify!($Name), "`] offsets for this pointer size.")]
323            #[inline]
324            fn $snake(&self) -> offsets::$Name<&Self> {
325                offsets::$Name(self)
326            }
327        )*
328    };
329}
330
331/// This class computes offsets to fields within `VMContext` and other
332/// related structs that JIT code accesses directly.
333#[derive(Debug, Clone, Copy)]
334pub struct VMOffsets<P> {
335    /// The size in bytes of a pointer on the target.
336    pub ptr: P,
337    /// The number of imported functions in the module.
338    pub num_imported_functions: u32,
339    /// The number of imported tables in the module.
340    pub num_imported_tables: u32,
341    /// The number of imported memories in the module.
342    pub num_imported_memories: u32,
343    /// The number of imported globals in the module.
344    pub num_imported_globals: u32,
345    /// The number of imported tags in the module.
346    pub num_imported_tags: u32,
347    /// The number of defined tables in the module.
348    pub num_defined_tables: u32,
349    /// The number of defined memories in the module.
350    pub num_defined_memories: u32,
351    /// The number of memories owned by the module instance.
352    pub num_owned_memories: u32,
353    /// The number of defined globals in the module.
354    pub num_defined_globals: u32,
355    /// The number of defined tags in the module.
356    pub num_defined_tags: u32,
357    /// The number of escaped functions in the module, the size of the func_refs
358    /// array.
359    pub num_escaped_funcs: u32,
360    /// The number of runtime data segments in the module.
361    pub num_runtime_data: u32,
362    /// Whether or not the module has a start function.
363    pub has_startup_func: bool,
364
365    // precalculated offsets of various member fields
366    imported_functions: u32,
367    imported_tables: u32,
368    imported_memories: u32,
369    imported_globals: u32,
370    imported_tags: u32,
371    defined_tables: u32,
372    defined_memories: u32,
373    owned_memories: u32,
374    defined_globals: u32,
375    defined_tags: u32,
376    defined_func_refs: u32,
377    startup_func_ref: u32,
378    runtime_data_bases: u32,
379    runtime_data_lengths: u32,
380    size: u32,
381}
382
383/// Trait used for the `ptr` representation of the field of `VMOffsets`
384pub trait PtrSize {
385    /// Returns the pointer size, in bytes, for the target.
386    fn size(&self) -> u8;
387
388    // Generate a `fn vm_foo(&self) -> offsets::VMFoo<&Self>` accessor for each
389    // `VM*` type, giving access to that type's field offsets, size, and
390    // alignment for this pointer size.
391    for_each_vm_type!(define_ptr_size_vm_type_accessors);
392
393    /// The offset of the `VMContext::store_context` field
394    fn vmcontext_store_context(&self) -> u8 {
395        u8::try_from(align(
396            u32::try_from(core::mem::size_of::<u32>()).unwrap(),
397            u32::from(self.size()),
398        ))
399        .unwrap()
400    }
401
402    /// The offset of the `VMContext::builtin_functions` field
403    fn vmcontext_builtin_functions(&self) -> u8 {
404        self.vmcontext_store_context() + self.size()
405    }
406
407    /// Return the size of `VMSharedTypeIndex`.
408    #[inline]
409    fn size_of_vmshared_type_index(&self) -> u8 {
410        4
411    }
412
413    /// Return the alignment of `VMSharedTypeIndex`.
414    #[inline]
415    fn align_of_vmshared_type_index(&self) -> u8 {
416        4
417    }
418
419    /// This is the size of the largest value type (i.e. a V128).
420    #[inline]
421    fn maximum_value_size(&self) -> u8 {
422        self.vm_global_definition().size()
423    }
424
425    /// Return the size of `*mut VMMemoryDefinition`.
426    #[inline]
427    fn size_of_vmmemory_pointer(&self) -> u8 {
428        self.size()
429    }
430
431    // Offsets within `VMArrayCallHostFuncContext`.
432
433    /// Return the offset of `VMArrayCallHostFuncContext::func_ref`.
434    fn vmarray_call_host_func_context_func_ref(&self) -> u8 {
435        u8::try_from(align(
436            u32::try_from(core::mem::size_of::<u32>()).unwrap(),
437            u32::from(self.size()),
438        ))
439        .unwrap()
440    }
441
442    /// Return the size of `VMStackChain`.
443    fn size_of_vmstack_chain(&self) -> u8 {
444        2 * self.size()
445    }
446
447    // Offsets within `VMStackLimits`
448
449    /// Return the offset of `VMStackLimits::stack_limit`.
450    fn vmstack_limits_stack_limit(&self) -> u8 {
451        0
452    }
453
454    /// Return the offset of `VMStackLimits::last_wasm_entry_fp`.
455    fn vmstack_limits_last_wasm_entry_fp(&self) -> u8 {
456        self.size()
457    }
458
459    /// Return the offset of `VMStackLimits::last_wasm_entry_sp`.
460    fn vmstack_limits_last_wasm_entry_sp(&self) -> u8 {
461        self.vmstack_limits_last_wasm_entry_fp() + self.size()
462    }
463
464    /// Return the offset of `VMStackLimits::last_wasm_entry_trap_handler`.
465    fn vmstack_limits_last_wasm_entry_trap_handler(&self) -> u8 {
466        self.vmstack_limits_last_wasm_entry_sp() + self.size()
467    }
468
469    // Offsets within `VMHostArray`
470
471    /// Return the offset of `VMHostArray::length`.
472    fn vmhostarray_length(&self) -> u8 {
473        0
474    }
475
476    /// Return the offset of `VMHostArray::capacity`.
477    fn vmhostarray_capacity(&self) -> u8 {
478        4
479    }
480
481    /// Return the offset of `VMHostArray::data`.
482    fn vmhostarray_data(&self) -> u8 {
483        8
484    }
485
486    /// Return the size of `VMHostArray`.
487    fn size_of_vmhostarray(&self) -> u8 {
488        8 + self.size()
489    }
490
491    // Offsets within `VMCommonStackInformation`
492
493    /// Return the offset of `VMCommonStackInformation::limits`.
494    fn vmcommon_stack_information_limits(&self) -> u8 {
495        0 * self.size()
496    }
497
498    /// Return the offset of `VMCommonStackInformation::state`.
499    fn vmcommon_stack_information_state(&self) -> u8 {
500        4 * self.size()
501    }
502
503    /// Return the offset of `VMCommonStackInformation::handlers`.
504    fn vmcommon_stack_information_handlers(&self) -> u8 {
505        u8::try_from(align(
506            self.vmcommon_stack_information_state() as u32 + 4,
507            u32::from(self.size()),
508        ))
509        .unwrap()
510    }
511
512    /// Return the offset of `VMCommonStackInformation::first_switch_handler_index`.
513    fn vmcommon_stack_information_first_switch_handler_index(&self) -> u8 {
514        self.vmcommon_stack_information_handlers() + self.size_of_vmhostarray()
515    }
516
517    /// Return the size of `VMCommonStackInformation`.
518    fn size_of_vmcommon_stack_information(&self) -> u8 {
519        u8::try_from(align(
520            self.vmcommon_stack_information_first_switch_handler_index() as u32 + 4,
521            u32::from(self.size()),
522        ))
523        .unwrap()
524    }
525
526    // Offsets within `VMContObj`
527
528    /// Return the offset of `VMContObj::contref`
529    fn vmcontobj_contref(&self) -> u8 {
530        0
531    }
532
533    /// Return the offset of `VMContObj::revision`
534    fn vmcontobj_revision(&self) -> u8 {
535        self.size()
536    }
537
538    /// Return the size of `VMContObj`.
539    fn size_of_vmcontobj(&self) -> u8 {
540        u8::try_from(align(
541            u32::from(self.vmcontobj_revision())
542                + u32::try_from(core::mem::size_of::<usize>()).unwrap(),
543            u32::from(self.size()),
544        ))
545        .unwrap()
546    }
547
548    // Offsets within `VMContRef`
549
550    /// Return the offset of `VMContRef::common_stack_information`.
551    fn vmcontref_common_stack_information(&self) -> u8 {
552        0 * self.size()
553    }
554
555    /// Return the offset of `VMContRef::parent_chain`.
556    fn vmcontref_parent_chain(&self) -> u8 {
557        u8::try_from(align(
558            (self.vmcontref_common_stack_information() + self.size_of_vmcommon_stack_information())
559                as u32,
560            u32::from(self.size()),
561        ))
562        .unwrap()
563    }
564
565    /// Return the offset of `VMContRef::last_ancestor`.
566    fn vmcontref_last_ancestor(&self) -> u8 {
567        self.vmcontref_parent_chain() + 2 * self.size()
568    }
569
570    /// Return the offset of `VMContRef::revision`.
571    fn vmcontref_revision(&self) -> u8 {
572        self.vmcontref_last_ancestor() + self.size()
573    }
574
575    /// Return the offset of `VMContRef::stack`.
576    fn vmcontref_stack(&self) -> u8 {
577        self.vmcontref_revision() + self.size()
578    }
579
580    /// Return the offset of `VMContRef::args`.
581    fn vmcontref_args(&self) -> u8 {
582        self.vmcontref_stack() + 3 * self.size()
583    }
584
585    /// Return the offset of `VMContRef::values`.
586    fn vmcontref_values(&self) -> u8 {
587        self.vmcontref_args() + self.size_of_vmhostarray()
588    }
589
590    /// Return the offset to the `magic` value in this `VMContext`.
591    #[inline]
592    fn vmctx_magic(&self) -> u8 {
593        // This is required by the implementation of `VMContext::instance` and
594        // `VMContext::instance_mut`. If this value changes then those locations
595        // need to be updated.
596        0
597    }
598
599    /// Return the offset to the `VMStoreContext` structure
600    #[inline]
601    fn vmctx_store_context(&self) -> u8 {
602        self.vmctx_magic() + self.size()
603    }
604
605    /// Return the offset to the `VMBuiltinFunctionsArray` structure
606    #[inline]
607    fn vmctx_builtin_functions(&self) -> u8 {
608        self.vmctx_store_context() + self.size()
609    }
610
611    /// Return the offset to the `*const AtomicU64` epoch-counter
612    /// pointer.
613    #[inline]
614    fn vmctx_epoch_ptr(&self) -> u8 {
615        self.vmctx_builtin_functions() + self.size()
616    }
617
618    /// Return the offset to the `*mut T` collector-specific data.
619    ///
620    /// This is a pointer that different collectors can use however they see
621    /// fit.
622    #[inline]
623    fn vmctx_gc_heap_data(&self) -> u8 {
624        self.vmctx_epoch_ptr() + self.size()
625    }
626
627    /// Return the offset of the `over_approximated_stack_roots` field within
628    /// `VMDrcHeapData`.
629    #[inline]
630    fn vmdrc_heap_data_over_approximated_stack_roots(&self) -> u8 {
631        0
632    }
633
634    /// Return the offset of the `current_over_approximated_stack_roots_len`
635    /// field within `VMDrcHeapData`.
636    #[inline]
637    fn vmdrc_heap_data_current_over_approximated_stack_roots_len(&self) -> u8 {
638        4
639    }
640
641    /// Return the offset of the
642    /// `over_approximated_stack_roots_len_after_last_gc` field within
643    /// `VMDrcHeapData`.
644    #[inline]
645    fn vmdrc_heap_data_over_approximated_stack_roots_len_after_last_gc(&self) -> u8 {
646        8
647    }
648
649    /// Return the size of `VMDrcHeapData`.
650    #[inline]
651    fn size_of_vmdrc_heap_data(&self) -> u8 {
652        12
653    }
654
655    /// Return the alignment of `VMDrcHeapData`.
656    #[inline]
657    fn align_of_vmdrc_heap_data(&self) -> u8 {
658        4
659    }
660
661    /// Return the offset of the `bump_ptr` field within `VMCopyingHeapData`.
662    #[inline]
663    fn vmcopying_heap_data_bump_ptr(&self) -> u8 {
664        0
665    }
666
667    /// Return the offset of the `active_space_end` field within
668    /// `VMCopyingHeapData`.
669    #[inline]
670    fn vmcopying_heap_data_active_space_end(&self) -> u8 {
671        4
672    }
673
674    /// Return the size of `VMCopyingHeapData`.
675    #[inline]
676    fn size_of_vmcopying_heap_data(&self) -> u8 {
677        8
678    }
679
680    /// Return the alignment of `VMCopyingHeapData`.
681    #[inline]
682    fn align_of_vmcopying_heap_data(&self) -> u8 {
683        4
684    }
685
686    /// The offset of the `type_ids` array pointer.
687    #[inline]
688    fn vmctx_type_ids_array(&self) -> u8 {
689        self.vmctx_gc_heap_data() + self.size()
690    }
691
692    /// The end of statically known offsets in `VMContext`.
693    ///
694    /// Data after this is dynamically sized.
695    #[inline]
696    fn vmctx_dynamic_data_start(&self) -> u8 {
697        self.vmctx_type_ids_array() + self.size()
698    }
699}
700
701/// A trait to abstract over various types that contain a `P: PtrSize`.
702pub trait GetPtrSize {
703    /// The type that implements `PtrSize`.
704    type Ptr: PtrSize;
705
706    /// Get a `&P` where `P: PtrSize`.
707    fn get_ptr_size(&self) -> &Self::Ptr;
708}
709
710impl<P> GetPtrSize for P
711where
712    P: PtrSize,
713{
714    type Ptr = Self;
715
716    #[inline]
717    fn get_ptr_size(&self) -> &Self::Ptr {
718        self
719    }
720}
721
722/// Type representing the size of a pointer for the current compilation host
723#[derive(Clone, Copy)]
724pub struct HostPtr;
725
726impl PtrSize for HostPtr {
727    #[inline]
728    fn size(&self) -> u8 {
729        core::mem::size_of::<usize>() as u8
730    }
731}
732
733impl PtrSize for u8 {
734    #[inline]
735    fn size(&self) -> u8 {
736        *self
737    }
738}
739
740impl<P> PtrSize for &'_ P
741where
742    P: PtrSize + ?Sized,
743{
744    #[inline]
745    fn size(&self) -> u8 {
746        (**self).size()
747    }
748}
749
750/// Used to construct a `VMOffsets`
751#[derive(Debug, Clone, Copy)]
752pub struct VMOffsetsFields<P> {
753    /// The size in bytes of a pointer on the target.
754    pub ptr: P,
755    /// The number of imported functions in the module.
756    pub num_imported_functions: u32,
757    /// The number of imported tables in the module.
758    pub num_imported_tables: u32,
759    /// The number of imported memories in the module.
760    pub num_imported_memories: u32,
761    /// The number of imported globals in the module.
762    pub num_imported_globals: u32,
763    /// The number of imported tags in the module.
764    pub num_imported_tags: u32,
765    /// The number of defined tables in the module.
766    pub num_defined_tables: u32,
767    /// The number of defined memories in the module.
768    pub num_defined_memories: u32,
769    /// The number of memories owned by the module instance.
770    pub num_owned_memories: u32,
771    /// The number of defined globals in the module.
772    pub num_defined_globals: u32,
773    /// The number of defined tags in the module.
774    pub num_defined_tags: u32,
775    /// The number of escaped functions in the module, the size of the function
776    /// references array.
777    pub num_escaped_funcs: u32,
778    /// The number of runtime data segments in the module.
779    pub num_runtime_data: u32,
780    /// Whether or not the module has a start function.
781    pub has_startup_func: bool,
782}
783
784impl<P: PtrSize> VMOffsets<P> {
785    /// Return a new `VMOffsets` instance, for a given pointer size.
786    pub fn new(ptr: P, module: &Module) -> Self {
787        let num_owned_memories = module
788            .memories
789            .iter()
790            .skip(module.num_imported_memories)
791            .filter(|p| !p.1.shared)
792            .count()
793            .try_into()
794            .unwrap();
795        VMOffsets::from(VMOffsetsFields {
796            ptr,
797            num_imported_functions: cast_to_u32(module.num_imported_funcs),
798            num_imported_tables: cast_to_u32(module.num_imported_tables),
799            num_imported_memories: cast_to_u32(module.num_imported_memories),
800            num_imported_globals: cast_to_u32(module.num_imported_globals),
801            num_imported_tags: cast_to_u32(module.num_imported_tags),
802            num_defined_tables: cast_to_u32(module.num_defined_tables()),
803            num_defined_memories: cast_to_u32(module.num_defined_memories()),
804            num_owned_memories,
805            num_defined_globals: cast_to_u32(module.globals.len() - module.num_imported_globals),
806            num_defined_tags: cast_to_u32(module.tags.len() - module.num_imported_tags),
807            num_escaped_funcs: cast_to_u32(module.num_escaped_funcs),
808            num_runtime_data: cast_to_u32(module.runtime_data.len()),
809            has_startup_func: !module.startup.is_none(),
810        })
811    }
812
813    /// Returns the size, in bytes, of the target
814    #[inline]
815    pub fn pointer_size(&self) -> u8 {
816        self.ptr.size()
817    }
818
819    /// Returns an iterator which provides a human readable description and a
820    /// byte size. The iterator returned will iterate over the bytes allocated
821    /// to the entire `VMOffsets` structure to explain where each byte size is
822    /// coming from.
823    pub fn region_sizes(&self) -> impl Iterator<Item = (&str, u32)> {
824        macro_rules! calculate_sizes {
825            ($($name:ident: $desc:tt,)*) => {{
826                let VMOffsets {
827                    // These fields are metadata not talking about specific
828                    // offsets of specific fields.
829                    ptr: _,
830                    num_imported_functions: _,
831                    num_imported_tables: _,
832                    num_imported_memories: _,
833                    num_imported_globals: _,
834                    num_imported_tags: _,
835                    num_defined_tables: _,
836                    num_defined_globals: _,
837                    num_defined_memories: _,
838                    num_defined_tags: _,
839                    num_owned_memories: _,
840                    num_escaped_funcs: _,
841                    num_runtime_data: _,
842                    has_startup_func: _,
843
844                    // used as the initial size below
845                    size,
846
847                    // exhaustively match the rest of the fields with input from
848                    // the macro
849                    $($name,)*
850                } = *self;
851
852                // calculate the size of each field by relying on the inputs to
853                // the macro being in reverse order and determining the size of
854                // the field as the offset from the field to the last field.
855                let mut last = size;
856                $(
857                    assert!($name <= last);
858                    let tmp = $name;
859                    let $name = last - $name;
860                    last = tmp;
861                )*
862                assert_ne!(last, 0);
863                IntoIterator::into_iter([
864                    $(($desc, $name),)*
865                    ("static vmctx data", last),
866                ])
867            }};
868        }
869
870        calculate_sizes! {
871            runtime_data_lengths: "runtime data lengths",
872            runtime_data_bases: "runtime data base pointers",
873            startup_func_ref: "startup funcref",
874            defined_func_refs: "module functions",
875            defined_tags: "defined tags",
876            defined_globals: "defined globals",
877            defined_tables: "defined tables",
878            imported_tags: "imported tags",
879            imported_globals: "imported globals",
880            imported_tables: "imported tables",
881            imported_functions: "imported functions",
882            owned_memories: "owned memories",
883            defined_memories: "defined memories",
884            imported_memories: "imported memories",
885        }
886    }
887}
888
889impl<P: PtrSize> GetPtrSize for VMOffsets<P> {
890    type Ptr = P;
891
892    #[inline]
893    fn get_ptr_size(&self) -> &Self::Ptr {
894        &self.ptr
895    }
896}
897
898impl<P: PtrSize> From<VMOffsetsFields<P>> for VMOffsets<P> {
899    fn from(fields: VMOffsetsFields<P>) -> VMOffsets<P> {
900        let mut ret = Self {
901            ptr: fields.ptr,
902            num_imported_functions: fields.num_imported_functions,
903            num_imported_tables: fields.num_imported_tables,
904            num_imported_memories: fields.num_imported_memories,
905            num_imported_globals: fields.num_imported_globals,
906            num_imported_tags: fields.num_imported_tags,
907            num_defined_tables: fields.num_defined_tables,
908            num_defined_memories: fields.num_defined_memories,
909            num_owned_memories: fields.num_owned_memories,
910            num_defined_globals: fields.num_defined_globals,
911            num_defined_tags: fields.num_defined_tags,
912            num_escaped_funcs: fields.num_escaped_funcs,
913            num_runtime_data: fields.num_runtime_data,
914            has_startup_func: fields.has_startup_func,
915            imported_functions: 0,
916            imported_tables: 0,
917            imported_memories: 0,
918            imported_globals: 0,
919            imported_tags: 0,
920            defined_tables: 0,
921            defined_memories: 0,
922            owned_memories: 0,
923            defined_globals: 0,
924            defined_tags: 0,
925            defined_func_refs: 0,
926            startup_func_ref: 0,
927            runtime_data_bases: 0,
928            runtime_data_lengths: 0,
929            size: 0,
930        };
931
932        // Convenience functions for checked addition and multiplication.
933        // As side effect this reduces binary size by using only a single
934        // `#[track_caller]` location for each function instead of one for
935        // each individual invocation.
936        #[inline]
937        fn cadd(count: u32, size: u32) -> u32 {
938            count.checked_add(size).unwrap()
939        }
940
941        #[inline]
942        fn cmul(count: u32, size: u8) -> u32 {
943            count.checked_mul(u32::from(size)).unwrap()
944        }
945
946        let mut next_field_offset = u32::from(ret.ptr.vmctx_dynamic_data_start());
947
948        macro_rules! fields {
949            (size($field:ident) = $size:expr, $($rest:tt)*) => {
950                ret.$field = next_field_offset;
951                next_field_offset = cadd(next_field_offset, u32::from($size));
952                fields!($($rest)*);
953            };
954            (align($align:expr), $($rest:tt)*) => {
955                next_field_offset = align(next_field_offset, $align);
956                fields!($($rest)*);
957            };
958            () => {};
959        }
960
961        fields! {
962            size(imported_memories)
963                = cmul(ret.num_imported_memories, ret.ptr.vm_memory_import().size()),
964            size(defined_memories)
965                = cmul(ret.num_defined_memories, ret.ptr.size_of_vmmemory_pointer()),
966            size(owned_memories)
967                = cmul(ret.num_owned_memories, ret.ptr.vm_memory_definition().size()),
968            size(imported_functions)
969                = cmul(ret.num_imported_functions, ret.ptr.vm_function_import().size()),
970            size(imported_tables)
971                = cmul(ret.num_imported_tables, ret.ptr.vm_table_import().size()),
972            size(imported_globals)
973                = cmul(ret.num_imported_globals, ret.ptr.vm_global_import().size()),
974            size(imported_tags)
975                = cmul(ret.num_imported_tags, ret.ptr.vm_tag_import().size()),
976            size(defined_tables)
977                = cmul(ret.num_defined_tables, ret.ptr.vm_table_definition().size()),
978            align(16),
979            size(defined_globals)
980                = cmul(ret.num_defined_globals, ret.ptr.vm_global_definition().size()),
981            size(defined_tags)
982                = cmul(ret.num_defined_tags, ret.ptr.vm_tag_definition().size()),
983            size(defined_func_refs) = cmul(
984                ret.num_escaped_funcs,
985                ret.ptr.vm_func_ref().size(),
986            ),
987            size(startup_func_ref) = if ret.has_startup_func {
988                ret.ptr.vm_func_ref().size()
989            } else {
990                0
991            },
992            size(runtime_data_bases) = cmul(ret.num_runtime_data, ret.ptr.size()),
993            size(runtime_data_lengths) = cmul(ret.num_runtime_data, 4),
994        }
995
996        ret.size = next_field_offset;
997
998        return ret;
999    }
1000}
1001
1002/// Offsets for `*const VMFunctionBody`.
1003impl<P: PtrSize> VMOffsets<P> {
1004    /// The size of the `current_elements` field.
1005    pub fn size_of_vmfunction_body_ptr(&self) -> u8 {
1006        1 * self.pointer_size()
1007    }
1008}
1009
1010/// Offsets for `VMTableDefinition`.
1011impl<P: PtrSize> VMOffsets<P> {
1012    /// The size of the `current_elements` field.
1013    #[inline]
1014    pub fn size_of_vmtable_definition_current_elements(&self) -> u8 {
1015        self.pointer_size()
1016    }
1017}
1018
1019/// Offsets for `VMSharedTypeIndex`.
1020impl<P: PtrSize> VMOffsets<P> {
1021    /// Return the size of `VMSharedTypeIndex`.
1022    #[inline]
1023    pub fn size_of_vmshared_type_index(&self) -> u8 {
1024        self.ptr.size_of_vmshared_type_index()
1025    }
1026}
1027
1028/// Offsets for `VMContext`.
1029impl<P: PtrSize> VMOffsets<P> {
1030    /// The offset of the `tables` array.
1031    #[inline]
1032    pub fn vmctx_imported_functions_begin(&self) -> u32 {
1033        self.imported_functions
1034    }
1035
1036    /// The offset of the `tables` array.
1037    #[inline]
1038    pub fn vmctx_imported_tables_begin(&self) -> u32 {
1039        self.imported_tables
1040    }
1041
1042    /// The offset of the `memories` array.
1043    #[inline]
1044    pub fn vmctx_imported_memories_begin(&self) -> u32 {
1045        self.imported_memories
1046    }
1047
1048    /// The offset of the `globals` array.
1049    #[inline]
1050    pub fn vmctx_imported_globals_begin(&self) -> u32 {
1051        self.imported_globals
1052    }
1053
1054    /// The offset of the `tags` array.
1055    #[inline]
1056    pub fn vmctx_imported_tags_begin(&self) -> u32 {
1057        self.imported_tags
1058    }
1059
1060    /// The offset of the `tables` array.
1061    #[inline]
1062    pub fn vmctx_tables_begin(&self) -> u32 {
1063        self.defined_tables
1064    }
1065
1066    /// The offset of the `memories` array.
1067    #[inline]
1068    pub fn vmctx_memories_begin(&self) -> u32 {
1069        self.defined_memories
1070    }
1071
1072    /// The offset of the `owned_memories` array.
1073    #[inline]
1074    pub fn vmctx_owned_memories_begin(&self) -> u32 {
1075        self.owned_memories
1076    }
1077
1078    /// The offset of the `globals` array.
1079    #[inline]
1080    pub fn vmctx_globals_begin(&self) -> u32 {
1081        self.defined_globals
1082    }
1083
1084    /// The offset of the `tags` array.
1085    #[inline]
1086    pub fn vmctx_tags_begin(&self) -> u32 {
1087        self.defined_tags
1088    }
1089
1090    /// The offset of the `func_refs` array.
1091    #[inline]
1092    pub fn vmctx_func_refs_begin(&self) -> u32 {
1093        self.defined_func_refs
1094    }
1095
1096    /// The offset of the `runtime_data_bases` array.
1097    #[inline]
1098    pub fn vmctx_runtime_data_bases_begin(&self) -> u32 {
1099        self.runtime_data_bases
1100    }
1101
1102    /// The offset of the `runtime_data_lengths` array.
1103    #[inline]
1104    pub fn vmctx_runtime_data_lengths_begin(&self) -> u32 {
1105        self.runtime_data_lengths
1106    }
1107
1108    /// Return the size of the `VMContext` allocation.
1109    #[inline]
1110    pub fn size_of_vmctx(&self) -> u32 {
1111        self.size
1112    }
1113
1114    /// Return the offset to `VMFunctionImport` index `index`.
1115    #[inline]
1116    pub fn vmctx_vmfunction_import(&self, index: FuncIndex) -> u32 {
1117        assert!(index.as_u32() < self.num_imported_functions);
1118        self.vmctx_imported_functions_begin()
1119            + index.as_u32() * u32::from(self.ptr.vm_function_import().size())
1120    }
1121
1122    /// Return the offset to `VMTable` index `index`.
1123    #[inline]
1124    pub fn vmctx_vmtable_import(&self, index: TableIndex) -> u32 {
1125        assert!(index.as_u32() < self.num_imported_tables);
1126        self.vmctx_imported_tables_begin()
1127            + index.as_u32() * u32::from(self.ptr.vm_table_import().size())
1128    }
1129
1130    /// Return the offset to `VMMemoryImport` index `index`.
1131    #[inline]
1132    pub fn vmctx_vmmemory_import(&self, index: MemoryIndex) -> u32 {
1133        assert!(index.as_u32() < self.num_imported_memories);
1134        self.vmctx_imported_memories_begin()
1135            + index.as_u32() * u32::from(self.ptr.vm_memory_import().size())
1136    }
1137
1138    /// Return the offset to `VMGlobalImport` index `index`.
1139    #[inline]
1140    pub fn vmctx_vmglobal_import(&self, index: GlobalIndex) -> u32 {
1141        assert!(index.as_u32() < self.num_imported_globals);
1142        self.vmctx_imported_globals_begin()
1143            + index.as_u32() * u32::from(self.ptr.vm_global_import().size())
1144    }
1145
1146    /// Return the offset to `VMTagImport` index `index`.
1147    #[inline]
1148    pub fn vmctx_vmtag_import(&self, index: TagIndex) -> u32 {
1149        assert!(index.as_u32() < self.num_imported_tags);
1150        self.vmctx_imported_tags_begin()
1151            + index.as_u32() * u32::from(self.ptr.vm_tag_import().size())
1152    }
1153
1154    /// Return the offset to `VMTableDefinition` index `index`.
1155    #[inline]
1156    pub fn vmctx_vmtable_definition(&self, index: DefinedTableIndex) -> u32 {
1157        assert!(index.as_u32() < self.num_defined_tables);
1158        self.vmctx_tables_begin()
1159            + index.as_u32() * u32::from(self.ptr.vm_table_definition().size())
1160    }
1161
1162    /// Return the offset to the `*mut VMMemoryDefinition` at index `index`.
1163    #[inline]
1164    pub fn vmctx_vmmemory_pointer(&self, index: DefinedMemoryIndex) -> u32 {
1165        assert!(index.as_u32() < self.num_defined_memories);
1166        self.vmctx_memories_begin()
1167            + index.as_u32() * u32::from(self.ptr.size_of_vmmemory_pointer())
1168    }
1169
1170    /// Return the offset to the owned `VMMemoryDefinition` at index `index`.
1171    #[inline]
1172    pub fn vmctx_vmmemory_definition(&self, index: OwnedMemoryIndex) -> u32 {
1173        assert!(index.as_u32() < self.num_owned_memories);
1174        self.vmctx_owned_memories_begin()
1175            + index.as_u32() * u32::from(self.ptr.vm_memory_definition().size())
1176    }
1177
1178    /// Return the offset to the `VMGlobalDefinition` index `index`.
1179    #[inline]
1180    pub fn vmctx_vmglobal_definition(&self, index: DefinedGlobalIndex) -> u32 {
1181        assert!(index.as_u32() < self.num_defined_globals);
1182        self.vmctx_globals_begin()
1183            + index.as_u32() * u32::from(self.ptr.vm_global_definition().size())
1184    }
1185
1186    /// Return the offset to the `VMTagDefinition` index `index`.
1187    #[inline]
1188    pub fn vmctx_vmtag_definition(&self, index: DefinedTagIndex) -> u32 {
1189        assert!(index.as_u32() < self.num_defined_tags);
1190        self.vmctx_tags_begin() + index.as_u32() * u32::from(self.ptr.vm_tag_definition().size())
1191    }
1192
1193    /// Return the offset to the `VMFuncRef` for the given function
1194    /// index (either imported or defined).
1195    #[inline]
1196    pub fn vmctx_func_ref(&self, index: FuncRefIndex) -> u32 {
1197        assert!(!index.is_reserved_value());
1198        assert!(index.as_u32() < self.num_escaped_funcs);
1199        self.vmctx_func_refs_begin() + index.as_u32() * u32::from(self.ptr.vm_func_ref().size())
1200    }
1201
1202    /// Returns the offset to the `VMFuncRef` for the module startup function.
1203    ///
1204    /// Panics if this module does not have a startup function.
1205    #[inline]
1206    pub fn vmctx_startup_func_ref(&self) -> u32 {
1207        assert!(self.has_startup_func);
1208        self.startup_func_ref
1209    }
1210
1211    /// Return the offset to the base of the runtime data segment at `index`.
1212    #[inline]
1213    pub fn vmctx_runtime_data_base(&self, index: RuntimeDataIndex) -> u32 {
1214        assert!(!index.is_reserved_value());
1215        assert!(index.as_u32() < self.num_runtime_data);
1216        self.vmctx_runtime_data_bases_begin() + index.as_u32() * u32::from(self.ptr.size())
1217    }
1218
1219    /// Return the offset to the length of the runtime data segment at `index`.
1220    #[inline]
1221    pub fn vmctx_runtime_data_length(&self, index: RuntimeDataIndex) -> u32 {
1222        assert!(!index.is_reserved_value());
1223        assert!(index.as_u32() < self.num_runtime_data);
1224        self.vmctx_runtime_data_lengths_begin() + index.as_u32() * 4
1225    }
1226
1227    /// Return the offset to the `wasm_call` field in `*const VMFunctionBody` index `index`.
1228    #[inline]
1229    pub fn vmctx_vmfunction_import_wasm_call(&self, index: FuncIndex) -> u32 {
1230        self.vmctx_vmfunction_import(index) + u32::from(self.ptr.vm_function_import().wasm_call())
1231    }
1232
1233    /// Return the offset to the `array_call` field in `*const VMFunctionBody` index `index`.
1234    #[inline]
1235    pub fn vmctx_vmfunction_import_array_call(&self, index: FuncIndex) -> u32 {
1236        self.vmctx_vmfunction_import(index) + u32::from(self.ptr.vm_function_import().array_call())
1237    }
1238
1239    /// Return the offset to the `vmctx` field in `*const VMFunctionBody` index `index`.
1240    #[inline]
1241    pub fn vmctx_vmfunction_import_vmctx(&self, index: FuncIndex) -> u32 {
1242        self.vmctx_vmfunction_import(index) + u32::from(self.ptr.vm_function_import().vmctx())
1243    }
1244
1245    /// Return the offset to the `from` field in the imported `VMTable` at index
1246    /// `index`.
1247    #[inline]
1248    pub fn vmctx_vmtable_from(&self, index: TableIndex) -> u32 {
1249        self.vmctx_vmtable_import(index) + u32::from(self.ptr.vm_table_import().from())
1250    }
1251
1252    /// Return the offset to the `base` field in `VMTableDefinition` index `index`.
1253    #[inline]
1254    pub fn vmctx_vmtable_definition_base(&self, index: DefinedTableIndex) -> u32 {
1255        self.vmctx_vmtable_definition(index) + u32::from(self.ptr.vm_table_definition().base())
1256    }
1257
1258    /// Return the offset to the `current_elements` field in `VMTableDefinition` index `index`.
1259    #[inline]
1260    pub fn vmctx_vmtable_definition_current_elements(&self, index: DefinedTableIndex) -> u32 {
1261        self.vmctx_vmtable_definition(index)
1262            + u32::from(self.ptr.vm_table_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.ptr.vm_memory_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.vm_memory_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.vm_memory_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.ptr.vm_global_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.ptr.vm_tag_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.ptr.vm_tag_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.ptr.vm_tag_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}
1374
1375/// The bit pattern of `VMLazyThread::forced()`.
1376pub const VM_LAZY_THREAD_FORCED: u64 = 1;