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// The `VMContext` layout is not defined here: it is defined once, alongside
5// `VMComponentContext`'s, in `for_each_vmctx_type!`. Everything in this module
6// is either generated from that definition or is an offset that is not simply
7// the offset of one of the layout's fields.
8
9use crate::{
10    DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex, DefinedTagIndex, FuncIndex,
11    FuncRefIndex, GlobalIndex, MemoryIndex, Module, ModuleInternedTypeIndex, OwnedMemoryIndex,
12    RuntimeDataIndex, TableIndex, TagIndex,
13};
14
15/// Number of slots in for `component_context` in the `VMStoreContext`. This is
16/// defined by the component model's `context.{get,set}` intrinsics.
17pub const NUM_COMPONENT_CONTEXT_SLOTS: usize = 2;
18
19/// Capacity of the `lazy_lenders` buffer in the `VMStoreContext`.
20///
21/// Fast paths usually lend only one or two handles, so this is small; a lend
22/// that does not fit forces the deferred thread and records in its
23/// `CallContext` instead.
24pub const LAZY_LENDERS_CAPACITY: usize = 8;
25
26#[cfg(target_pointer_width = "32")]
27fn cast_to_u32(sz: usize) -> u32 {
28    u32::try_from(sz).unwrap()
29}
30#[cfg(target_pointer_width = "64")]
31fn cast_to_u32(sz: usize) -> u32 {
32    u32::try_from(sz).expect("overflow in cast from usize to u32")
33}
34
35/// Align an offset used in this module to a specific byte-width by rounding up
36#[inline]
37fn align(offset: u32, width: u32) -> u32 {
38    (offset + (width - 1)) / width * width
39}
40
41/// Generate the [`offsets`] module: a `struct VMFoo<P: PtrSize>(P)` for each
42/// `VM*` type, with a method per field returning that field's offset, plus
43/// `size` and `align` methods.
44///
45/// A `#[repr(C, $tag)]` enum additionally gets `tag`/`tag_size`/`payload` and
46/// a nested `offsets::<snake_name>::Variant<P>` type per variant, holding that
47/// variant's fields' offsets within the enum as a whole.
48#[allow(
49    unused_macro_rules,
50    reason = "the enum arms go unused whenever no `VM*` type is an enum, and \
51              the type classifiers are written for all field types rather than \
52              only those some type happens to use"
53)]
54macro_rules! define_vm_type_offsets {
55    // `UnsafeCell<T>` is `repr(transparent)`, so it has exactly `T`'s layout;
56    // delegate to the inner type.
57    (@size ($p:expr) UnsafeCell < $inner:tt >) => { define_vm_type_offsets!(@size ($p) $inner) };
58
59    // Classify a field type to its size in bytes as a `u32`, given `$p` (the
60    // target pointer size as a `u8`). All `VmPtr<_>` and `Option<VmPtr<_>>`
61    // fields are pointer-sized; the `Defined*Index` types are `u32` entity
62    // references; and `VMGlobalKind` is a `repr(C, u32)` enum with a `u32`
63    // payload.
64    (@size ($p:expr) VmPtr < $g:ty >) => { u32::from($p) };
65    (@size ($p:expr) VmPtr < VmPtr < $g:ty >>) => { u32::from($p) };
66    (@size ($p:expr) Option < VmPtr < $g:ty >>) => { u32::from($p) };
67    (@size ($p:expr) AtomicUsize) => { u32::from($p) };
68    (@size ($p:expr) usize) => { u32::from($p) };
69    (@size ($p:expr) i64) => { 8u32 };
70    (@size ($p:expr) u64) => { 8u32 };
71    (@size ($p:expr) u32) => { 4u32 };
72    (@size ($p:expr) u16) => { 2u32 };
73    (@size ($p:expr) u8) => { 1u32 };
74    // `VMGcRef` is a `NonZeroU32` newtype, so `Option<VMGcRef>` is niche-packed
75    // back down into four bytes.
76    (@size ($p:expr) NonZeroU32) => { 4u32 };
77    (@size ($p:expr) Option < VMGcRef >) => { 4u32 };
78    (@size ($p:expr) [u32; $n:expr]) => { 4u32 * u32::try_from($n).unwrap() };
79    (@size ($p:expr) VMSharedTypeIndex) => { u32::from(($p).size_of_vmshared_type_index()) };
80    (@size ($p:expr) DefinedTableIndex) => { 4u32 };
81    (@size ($p:expr) DefinedMemoryIndex) => { 4u32 };
82    (@size ($p:expr) DefinedTagIndex) => { 4u32 };
83    (@size ($p:expr) VMGlobalKind) => { 8u32 };
84    // `Range<T>` is a two-field `{ start: T, end: T }` struct.
85    (@size ($p:expr) Range < *mut u8 >) => { 2u32 * u32::from($p) };
86    // Nested `VM*` types recurse through their own generated offsets, keeping
87    // this macro the single source of truth for their layout too.
88    (@size ($p:expr) VMMemoryDefinition) => { u32::from(($p).vm_memory_definition().size()) };
89    (@size ($p:expr) VMLazyThread) => { u32::from(($p).vm_lazy_thread().size()) };
90    (@size ($p:expr) VMStackLimits) => { u32::from(($p).vm_stack_limits().size()) };
91    (@size ($p:expr) VMHostArray) => { u32::from(($p).vm_host_array().size()) };
92    (@size ($p:expr) VMPayloads) => { u32::from(($p).vm_payloads().size()) };
93    (@size ($p:expr) VMAsanStackSupport) => {
94        u32::from(($p).vm_asan_stack_support().size())
95    };
96    (@size ($p:expr) VMCommonStackInformation) => {
97        u32::from(($p).vm_common_stack_information().size())
98    };
99    (@size ($p:expr) VMGcHeader) => { u32::from(($p).vm_gc_header().size()) };
100    (@size ($p:expr) VMTypedResource) => { u32::from(($p).vm_typed_resource().size()) };
101    (@size ($p:expr) VMScope) => { u32::from(($p).vm_scope().size()) };
102    (@size ($p:expr) VMTransmitLocalState) => {
103        u32::from(($p).vm_transmit_local_state().size())
104    };
105    // `VMStackChain` is a `repr(usize, C)` enum, and is not itself defined by
106    // `for_each_vm_type!`.
107    (@size ($p:expr) VMStackChain) => { u32::from(($p).size_of_vmstack_chain()) };
108    (@size ($p:expr) VMStackState) => { 4u32 };
109    // `VMContinuationStack` is a platform-specific `repr(C)` struct private to
110    // the runtime, but every implementation of it is a pointer, a `usize`, and a
111    // byte-sized tag.
112    (@size ($p:expr) VMContinuationStack) => { 3u32 * u32::from($p) };
113    (@size ($p:expr) PhantomPinned) => { 0u32 };
114
115    // As with `@size` above, `UnsafeCell<T>` has exactly `T`'s alignment.
116    (@align ($p:expr) UnsafeCell < $inner:tt >) => { define_vm_type_offsets!(@align ($p) $inner) };
117
118    // Classify a field type to its alignment in bytes as a `u32`, given `$p`
119    // (the target pointer size as a `u8`).
120    //
121    // NB: 64-bit integers are assumed to be 8-aligned, which holds everywhere except
122    // `i686-unknown-linux-gnu`, and the pointer size alone can't tell those apart. Types
123    // with 64-bit fields must therefore put them first and force their own alignment with
124    // `#[repr(C, align(8))]`, as `VMStoreContext` does.
125    (@align ($p:expr) VmPtr < $g:ty >) => { u32::from($p) };
126    (@align ($p:expr) VmPtr < VmPtr < $g:ty >>) => { u32::from($p) };
127    (@align ($p:expr) Option < VmPtr < $g:ty >>) => { u32::from($p) };
128    (@align ($p:expr) AtomicUsize) => { u32::from($p) };
129    (@align ($p:expr) usize) => { u32::from($p) };
130    (@align ($p:expr) i64) => { 8u32 };
131    (@align ($p:expr) u64) => { 8u32 };
132    (@align ($p:expr) u32) => { 4u32 };
133    (@align ($p:expr) u16) => { 2u32 };
134    (@align ($p:expr) u8) => { 1u32 };
135    (@align ($p:expr) NonZeroU32) => { 4u32 };
136    (@align ($p:expr) Option < VMGcRef >) => { 4u32 };
137    (@align ($p:expr) [u32; $n:expr]) => { 4u32 };
138    (@align ($p:expr) VMSharedTypeIndex) => { u32::from(($p).align_of_vmshared_type_index()) };
139    (@align ($p:expr) DefinedTableIndex) => { 4u32 };
140    (@align ($p:expr) DefinedMemoryIndex) => { 4u32 };
141    (@align ($p:expr) DefinedTagIndex) => { 4u32 };
142    (@align ($p:expr) VMGlobalKind) => { 4u32 };
143    (@align ($p:expr) Range < *mut u8 >) => { u32::from($p) };
144    (@align ($p:expr) VMMemoryDefinition) => { u32::from(($p).vm_memory_definition().align()) };
145    (@align ($p:expr) VMLazyThread) => { u32::from(($p).vm_lazy_thread().align()) };
146    (@align ($p:expr) VMStackLimits) => { u32::from(($p).vm_stack_limits().align()) };
147    (@align ($p:expr) VMHostArray) => { u32::from(($p).vm_host_array().align()) };
148    (@align ($p:expr) VMPayloads) => { u32::from(($p).vm_payloads().align()) };
149    (@align ($p:expr) VMAsanStackSupport) => {
150        u32::from(($p).vm_asan_stack_support().align())
151    };
152    (@align ($p:expr) VMCommonStackInformation) => {
153        u32::from(($p).vm_common_stack_information().align())
154    };
155    (@align ($p:expr) VMGcHeader) => { u32::from(($p).vm_gc_header().align()) };
156    (@align ($p:expr) VMTypedResource) => { u32::from(($p).vm_typed_resource().align()) };
157    (@align ($p:expr) VMScope) => { u32::from(($p).vm_scope().align()) };
158    (@align ($p:expr) VMTransmitLocalState) => {
159        u32::from(($p).vm_transmit_local_state().align())
160    };
161    (@align ($p:expr) VMStackChain) => { u32::from($p) };
162    (@align ($p:expr) VMStackState) => { 4u32 };
163    (@align ($p:expr) VMContinuationStack) => { u32::from($p) };
164    (@align ($p:expr) PhantomPinned) => { 1u32 };
165
166    // Classify a `#[repr(...)]` to the minimum alignment it forces, as a `u32`.
167    (@repr_align C) => { 1u32 };
168    (@repr_align transparent) => { 1u32 };
169    (@repr_align C, align($n:literal)) => {{ let align: u32 = $n; align }};
170
171    // Classify a `#[repr(C, $tag)]` enum's tag type to its size in bytes, which
172    // for these types is also its alignment.
173    (@tag_size u8) => { 1u32 };
174    (@tag_size u16) => { 2u32 };
175    (@tag_size u32) => { 4u32 };
176    (@tag_size u64) => { 8u32 };
177
178    // Emit a `pub fn` per field returning that field's offset, computed by
179    // accumulating the aligned size of each preceding field. `$p`/`$o` are
180    // caller-minted identifiers (for the pointer size and running offset) that
181    // are threaded through the recursion so their hygiene stays consistent
182    // across the emitted `let` bindings.
183    //
184    // Fields arrive pre-split into `[ $fname : $fty... ]` groups (see `@impl`
185    // below), so each field's type is a raw token sequence that the `@size`
186    // and `@align` classifiers can match against structurally.
187    (@fields $Name:ident ($p:ident, $o:ident) prefix( $($prefix:tt)* )) => {};
188    (@fields $Name:ident ($p:ident, $o:ident) prefix( $($prefix:tt)* )
189        [ $fname:ident : $($fty:tt)* ]
190        $($rest:tt)*
191    ) => {
192        #[doc = concat!(
193            "The offset of the `", stringify!($fname),
194            "` field of `", stringify!($Name), "`."
195        )]
196        #[inline]
197        pub fn $fname(&self) -> u8 {
198            let $p = self.0.size();
199            let $o: u32 = 0;
200            $($prefix)*
201            let $o = align($o, define_vm_type_offsets!(@align ($p) $($fty)*));
202            let _ = $p;
203            u8::try_from($o).unwrap()
204        }
205        define_vm_type_offsets!(@fields $Name ($p, $o)
206            prefix(
207                $($prefix)*
208                let $o = align($o, define_vm_type_offsets!(@align ($p) $($fty)*));
209                let $o = $o + define_vm_type_offsets!(@size ($p) $($fty)*);
210            )
211            $($rest)*
212        );
213    };
214
215    // Emit an `offsets::VMFoo` type's inherent `impl`. Fields are peeled off
216    // the raw struct body one at a time into `[ $fname : $fty... ]` groups
217    // accumulated in the `{ ... }` list; once the body is exhausted, the
218    // terminal arm emits the per-field offset methods plus `align`/`size`.
219    //
220    // Splitting the body by hand (rather than matching `$fty:tt $(< $fgen:ty
221    // >)?` within a repetition) is what lets each field's type reach the
222    // `@size`/`@align` classifiers as raw tokens, so those classifiers can
223    // require `Option`s to specifically be `Option<VmPtr<_>>`.
224    (@impl $Name:ident [$($repr:tt)*] { $( [ $fname:ident : $($fty:tt)* ] )* }) => {
225        impl<P: PtrSize> $Name<P> {
226            define_vm_type_offsets!(@fields $Name (p, o) prefix()
227                $( [ $fname : $($fty)* ] )*
228            );
229
230            #[doc = concat!("The alignment of the `", stringify!($Name), "` type.")]
231            #[inline]
232            pub fn align(&self) -> u8 {
233                let p = self.0.size();
234                let a: u32 = define_vm_type_offsets!(@repr_align $($repr)*);
235                $(
236                    let a = core::cmp::max(
237                        a,
238                        define_vm_type_offsets!(@align (p) $($fty)*),
239                    );
240                )*
241                let _ = p;
242                u8::try_from(a).unwrap()
243            }
244
245            #[doc = concat!("The size of the `", stringify!($Name), "` type.")]
246            #[inline]
247            pub fn size(&self) -> u8 {
248                let p = self.0.size();
249                let o: u32 = 0;
250                $(
251                    let o = align(o, define_vm_type_offsets!(@align (p) $($fty)*));
252                    let o = o + define_vm_type_offsets!(@size (p) $($fty)*);
253                )*
254                let o = align(o, u32::from(self.align()));
255                let _ = p;
256                u8::try_from(o).unwrap()
257            }
258        }
259    };
260    // Consume one field's attributes, visibility, and name, then collect its
261    // type tokens. None of the field attributes (doc comments and the
262    // `#[aggregate]`/`#[readonly]`/`#[can_move]` markers) affect layout, so they
263    // are all discarded here.
264    (@impl $Name:ident $repr:tt { $($groups:tt)* }
265        $(#[$($attr:tt)*])* $fvis:vis $fname:ident : $($rest:tt)*
266    ) => {
267        define_vm_type_offsets!(@impl_ty $Name $repr { $($groups)* } $fname [] $($rest)*);
268    };
269    // Accumulate one field's type tokens up to its terminating comma, then
270    // append the completed `[ $fname : $fty... ]` group and resume `@impl`.
271    (@impl_ty $Name:ident $repr:tt { $($groups:tt)* } $fname:ident [ $($fty:tt)* ] , $($rest:tt)*) => {
272        define_vm_type_offsets!(@impl $Name $repr { $($groups)* [ $fname : $($fty)* ] } $($rest)*);
273    };
274    (@impl_ty $Name:ident $repr:tt { $($groups:tt)* } $fname:ident [ $($fty:tt)* ] $tok:tt $($rest:tt)*) => {
275        define_vm_type_offsets!(@impl_ty $Name $repr { $($groups)* } $fname [ $($fty)* $tok ] $($rest)*);
276    };
277
278    // Dispatch on `struct` vs `enum`.
279    (@def struct $Name:ident $snake:ident $repr:tt { $($body:tt)* }) => {
280        define_vm_type_offsets!(@impl $Name $repr {} $($body)*);
281    };
282    // `C` guarantees the tag-plus-union layout.
283    (@def enum $Name:ident $snake:ident [ C, $tag:ident ] { $($body:tt)* }) => {
284        define_vm_type_offsets!(@enum_variants $Name $snake $tag {} $($body)*);
285    };
286
287    // Peel variants off an enum's body one at a time. Each variant's own body
288    // is split by `@enum_fields`, so the enum body's unconsumed remainder rides
289    // along in brackets until that recursion hands it back.
290    (@enum_variants $Name:ident $snake:ident $tag:ident { $($vs:tt)* }) => {
291        define_vm_type_offsets!(@enum_emit $Name $snake $tag { $($vs)* });
292    };
293    (@enum_variants $Name:ident $snake:ident $tag:ident { $($vs:tt)* }
294        $(#[doc = $vdoc:literal])*
295        #[snake_name = $vsnake:ident]
296        $(#[$vattr:meta])*
297        $Variant:ident { $($vbody:tt)* } = $disc:literal ,
298        $($rest:tt)*
299    ) => {
300        define_vm_type_offsets!(
301            @enum_fields $Name $snake $tag { $($vs)* }
302            $vsnake $Variant $disc {}
303            [ $($vbody)* ] [ $($rest)* ]
304        );
305    };
306
307    // As `@impl`/`@impl_ty` for a struct body, resuming `@enum_variants` on the
308    // enum body's remainder once the variant is exhausted.
309    (@enum_fields $Name:ident $snake:ident $tag:ident { $($vs:tt)* }
310        $vsnake:ident $Variant:ident $disc:literal { $($g:tt)* }
311        [] [ $($rest:tt)* ]
312    ) => {
313        define_vm_type_offsets!(
314            @enum_variants $Name $snake $tag
315            { $($vs)* { $vsnake $Variant $disc { $($g)* } } }
316            $($rest)*
317        );
318    };
319    (@enum_fields $Name:ident $snake:ident $tag:ident { $($vs:tt)* }
320        $vsnake:ident $Variant:ident $disc:literal { $($g:tt)* }
321        [ $(#[$($fattr:tt)*])* $fname:ident : $($vrest:tt)* ] $rest:tt
322    ) => {
323        define_vm_type_offsets!(
324            @enum_field_ty $Name $snake $tag { $($vs)* }
325            $vsnake $Variant $disc { $($g)* }
326            $fname [] [ $($vrest)* ] $rest
327        );
328    };
329    (@enum_field_ty $Name:ident $snake:ident $tag:ident { $($vs:tt)* }
330        $vsnake:ident $Variant:ident $disc:literal { $($g:tt)* }
331        $fname:ident [ $($fty:tt)* ] [ , $($vrest:tt)* ] $rest:tt
332    ) => {
333        define_vm_type_offsets!(
334            @enum_fields $Name $snake $tag { $($vs)* }
335            $vsnake $Variant $disc { $($g)* [ $fname : $($fty)* ] }
336            [ $($vrest)* ] $rest
337        );
338    };
339    (@enum_field_ty $Name:ident $snake:ident $tag:ident { $($vs:tt)* }
340        $vsnake:ident $Variant:ident $disc:literal { $($g:tt)* }
341        $fname:ident [ $($fty:tt)* ] [ $tok:tt $($vrest:tt)* ] $rest:tt
342    ) => {
343        define_vm_type_offsets!(
344            @enum_field_ty $Name $snake $tag { $($vs)* }
345            $vsnake $Variant $disc { $($g)* }
346            $fname [ $($fty)* $tok ] [ $($vrest)* ] $rest
347        );
348    };
349
350    // Emit an enum's `offsets::VMFoo` inherent `impl`, plus a `mod $snake`
351    // holding one `$Variant<P>` offsets type per variant.
352    //
353    // The tag is at offset zero, every variant's fields start at the same
354    // payload offset (the tag's size rounded up to the enum's alignment), and
355    // within a variant the fields are laid out as a struct's are.
356    (@enum_emit $Name:ident $snake:ident $tag:ident {
357        $(
358            { $vsnake:ident $Variant:ident $disc:literal {
359                $( [ $fname:ident : $($fty:tt)* ] )*
360            } }
361        )*
362    }) => {
363        impl<P: PtrSize> $Name<P> {
364            #[doc = concat!(
365                "The offset of the `", stringify!($tag), "` tag of `",
366                stringify!($Name), "`."
367            )]
368            #[inline]
369            pub fn tag(&self) -> u8 {
370                0
371            }
372
373            #[doc = concat!(
374                "The size, in bytes, of the `", stringify!($tag), "` tag of `",
375                stringify!($Name), "`."
376            )]
377            #[inline]
378            pub fn tag_size(&self) -> u8 {
379                u8::try_from(define_vm_type_offsets!(@tag_size $tag)).unwrap()
380            }
381
382            #[doc = concat!(
383                "The offset at which every variant of `", stringify!($Name),
384                "` lays out its fields."
385            )]
386            #[inline]
387            pub fn payload(&self) -> u8 {
388                let tag_size = u32::from(self.tag_size());
389                u8::try_from(align(tag_size, u32::from(self.align()))).unwrap()
390            }
391
392            #[doc = concat!("The alignment of the `", stringify!($Name), "` type.")]
393            #[inline]
394            pub fn align(&self) -> u8 {
395                let p = self.0.size();
396                // The tag sets the floor on the enum's alignment.
397                let a: u32 = define_vm_type_offsets!(@tag_size $tag);
398                $($(
399                    let a = core::cmp::max(
400                        a,
401                        define_vm_type_offsets!(@align (p) $($fty)*),
402                    );
403                )*)*
404                let _ = p;
405                u8::try_from(a).unwrap()
406            }
407
408            #[doc = concat!("The size of the `", stringify!($Name), "` type.")]
409            #[inline]
410            pub fn size(&self) -> u8 {
411                let p = self.0.size();
412                let payload = u32::from(self.payload());
413                let s: u32 = payload;
414                $(
415                    let o: u32 = payload;
416                    $(
417                        let o = align(o, define_vm_type_offsets!(@align (p) $($fty)*));
418                        let o = o + define_vm_type_offsets!(@size (p) $($fty)*);
419                    )*
420                    let s = core::cmp::max(s, o);
421                )*
422                let s = align(s, u32::from(self.align()));
423                let _ = p;
424                u8::try_from(s).unwrap()
425            }
426
427            $(
428                #[doc = concat!(
429                    "Get the offsets of the `", stringify!($Variant),
430                    "` variant's fields."
431                )]
432                #[inline]
433                pub fn $vsnake(&self) -> $snake::$Variant<&P> {
434                    $snake::$Variant($Name(&self.0))
435                }
436            )*
437        }
438
439        #[doc = concat!(
440            "Offsets of the fields of each variant of the `", stringify!($Name),
441            "` enum."
442        )]
443        pub mod $snake {
444            use super::{$Name, PtrSize, align};
445
446            $(
447                #[doc = concat!(
448                    "Offsets of fields within the `", stringify!($Name), "::",
449                    stringify!($Variant), "` variant."
450                )]
451                pub struct $Variant<P: PtrSize>(pub $Name<P>);
452
453                impl<P: PtrSize> $Variant<P> {
454                    #[doc = concat!(
455                        "The value of the `u32` tag that identifies the `",
456                        stringify!($Variant), "` variant."
457                    )]
458                    #[inline]
459                    pub fn tag_value(&self) -> u32 {
460                        $disc
461                    }
462
463                    define_vm_type_offsets!(@vfields $Name $Variant (p, o) prefix()
464                        $( [ $fname : $($fty)* ] )*
465                    );
466                }
467            )*
468        }
469    };
470
471    // As `@fields` above, except that the running offset starts at the enum's
472    // payload offset.
473    (@vfields $Name:ident $Variant:ident ($p:ident, $o:ident) prefix( $($prefix:tt)* )) => {};
474    (@vfields $Name:ident $Variant:ident ($p:ident, $o:ident) prefix( $($prefix:tt)* )
475        [ $fname:ident : $($fty:tt)* ]
476        $($rest:tt)*
477    ) => {
478        #[doc = concat!(
479            "The offset of the `", stringify!($fname),
480            "` field of `", stringify!($Name), "::", stringify!($Variant), "`."
481        )]
482        #[inline]
483        pub fn $fname(&self) -> u8 {
484            let $p = (self.0).0.size();
485            let $o: u32 = u32::from(self.0.payload());
486            $($prefix)*
487            let $o = align($o, define_vm_type_offsets!(@align ($p) $($fty)*));
488            let _ = $p;
489            u8::try_from($o).unwrap()
490        }
491        define_vm_type_offsets!(@vfields $Name $Variant ($p, $o)
492            prefix(
493                $($prefix)*
494                let $o = align($o, define_vm_type_offsets!(@align ($p) $($fty)*));
495                let $o = $o + define_vm_type_offsets!(@size ($p) $($fty)*);
496            )
497            $($rest)*
498        );
499    };
500
501    // Top-level entry: the list of `VM*` type definitions. Each is either a
502    // `struct` or a `#[repr(C, u32)]` `enum`; `@def` dispatches on which.
503    ( $(
504        $(#[doc = $sdoc:literal])*
505        $(#[cfg($($scfg:tt)*)])?
506        $(#[derive($($d:ident),*)])?
507        #[repr($($repr:tt)*)]
508        #[snake_name = $snake:ident]
509        $svis:vis $kind:ident $Name:ident {
510            $($body:tt)*
511        }
512    )* ) => {
513        $(
514            #[doc = concat!("Offsets of fields within the `", stringify!($Name), "` type.")]
515            pub struct $Name<P: PtrSize>(pub P);
516
517            define_vm_type_offsets!(@def $kind $Name $snake [$($repr)*] { $($body)* });
518        )*
519    };
520}
521
522/// Generate a `struct VMContext<P: PtrSize>(P)`-style wrapper for each vmctx
523/// type, with a method per statically-positioned field returning that field's
524/// offset.
525#[allow(
526    unused_macro_rules,
527    reason = "only `VMComponentContext` has a `#[ptr_size_offset]` prefix in its \
528              `dynamic` section, so those arms go unused for `VMContext`"
529)]
530macro_rules! define_vmctx_static_offsets {
531    // Munch the `static` section, threading a closed-form expression for the
532    // running offset. Once it is exhausted, keep going into the `dynamic`
533    // section's `#[ptr_size_offset]` prefix, whose offsets are closed-form too.
534    (@chain $p:ident [ $($prev:tt)* ] [] [ $($dyn:tt)* ]) => {
535        /// The offset just past this type's last statically-positioned field.
536        ///
537        /// Everything after this point is dynamically sized.
538        #[inline]
539        pub fn end_of_static_fields(&self) -> u8 {
540            let $p = self.0.size();
541            let _ = $p;
542            u8::try_from($($prev)*).unwrap()
543        }
544
545        define_vmctx_static_offsets!(@dyn_chain $p [ $($prev)* ] [ $($dyn)* ]);
546    };
547    (@chain $p:ident [ $($prev:tt)* ] [ align { $al:tt } $($rest:tt)* ] $dyn:tt) => {
548        define_vmctx_static_offsets!(
549            @chain $p
550            [ crate::vmctxtypes::align_up($($prev)*, vmctx_align_value!(($p) $al)) ]
551            [ $($rest)* ]
552            $dyn
553        );
554    };
555    (@chain $p:ident [ $($prev:tt)* ] [
556        field { $(# $fattr:tt)* $fname:ident : $($fty:tt)* } $($rest:tt)*
557    ] $dyn:tt) => {
558        #[doc = concat!("The offset of the `", stringify!($fname), "` field.")]
559        #[inline]
560        pub fn $fname(&self) -> u8 {
561            let $p = self.0.size();
562            let _ = $p;
563            u8::try_from($($prev)*).unwrap()
564        }
565        define_vmctx_static_offsets!(
566            @chain $p
567            [ $($prev)* + vmctx_field_size!(($p) $($fty)*) ]
568            [ $($rest)* ]
569            $dyn
570        );
571    };
572
573    // Munch the `dynamic` section's leading run of `#[ptr_size_offset]` entries,
574    // continuing to thread the closed-form running offset.
575    (@dyn_chain $p:ident [ $($prev:tt)* ] [ align { $al:tt } $($rest:tt)* ]) => {
576        define_vmctx_static_offsets!(
577            @dyn_chain $p
578            [ crate::vmctxtypes::align_up($($prev)*, vmctx_align_value!(($p) $al)) ]
579            [ $($rest)* ]
580        );
581    };
582    (@dyn_chain $p:ident [ $($prev:tt)* ] [
583        field { #[ptr_size_offset] $(# $fattr:tt)* $fname:ident : $($fty:tt)* } $($rest:tt)*
584    ]) => {
585        #[doc = concat!("The offset of the `", stringify!($fname), "` field.")]
586        #[inline]
587        pub fn $fname(&self) -> u32 {
588            let $p = self.0.size();
589            let _ = $p;
590            $($prev)*
591        }
592        define_vmctx_static_offsets!(
593            @dyn_chain $p
594            [ $($prev)* + vmctx_field_size!(($p) $($fty)*) ]
595            [ $($rest)* ]
596        );
597    };
598    (@dyn_chain $p:ident [ $($prev:tt)* ] [
599        array {
600            #[ptr_size_offset] $(# $fattr:tt)*
601            $fname:ident [ $count:ident ; $Index:ident ] : $($fty:tt)*
602        } $($rest:tt)*
603    ]) => {
604        // NB: this takes `impl VmctxArrayIndex` rather than the declared
605        // `$Index` because these wrappers are compiled even when the
606        // `component-model` feature is off, and some of the index types are not.
607        #[doc = concat!(
608            "The offset of the `index`th element of the `", stringify!($fname),
609            "` array.\n\nThis is not bounds checked: the array's length is a \
610             property of the particular module or component being compiled, which \
611             is exactly what this wrapper does not know."
612        )]
613        #[inline]
614        pub fn $fname(&self, index: impl crate::VmctxArrayIndex) -> u32 {
615            let $p = self.0.size();
616            let _ = $p;
617            let index = index.vmctx_array_index();
618            ($($prev)*) + vmctx_field_size!(($p) $($fty)*) * index
619        }
620        // An array's size depends on how many elements this particular module or
621        // component needs, so nothing after it has a closed-form offset and the
622        // chain necessarily stops here.
623    };
624    // Anything else ends the closed-form prefix.
625    (@dyn_chain $p:ident [ $($prev:tt)* ] [ $($rest:tt)* ]) => {};
626
627    ( $(
628        {
629            $Name:ident $snake:ident
630            static { $($stat:tt)* }
631            dynamic { $($dyn:tt)* }
632        }
633    )* ) => {
634        $(
635            #[doc = concat!("Offsets of the statically-positioned fields within the `",
636                            stringify!($Name), "` type.")]
637            pub struct $Name<P: PtrSize>(pub P);
638
639            impl<P: PtrSize> $Name<P> {
640                define_vmctx_static_offsets!(@chain ptr [ 0u32 ] [ $($stat)* ] [ $($dyn)* ]);
641            }
642        )*
643    };
644}
645
646/// Offsets of fields within the various `VM*` types and within the two vmctx
647/// types, parameterized over a target `PtrSize` so that they can be computed
648/// during cross compilation.
649///
650/// These types are namespaced within their own module so that they never collide
651/// with the real definitions of the `VM*` types themselves.
652pub mod offsets {
653    use super::{NUM_COMPONENT_CONTEXT_SLOTS, PtrSize, align};
654
655    for_each_vm_type!(define_vm_type_offsets);
656    for_each_vmctx_type!(define_vmctx_static_offsets);
657}
658
659/// Offsets within a `VMStoreContext` that are not simply the offset of one of
660/// its fields, and so are not generated by `for_each_vm_type!`.
661impl<P: PtrSize> offsets::VMStoreContext<P> {
662    /// The offset of the `gc_heap.base` field within a `VMStoreContext`.
663    pub fn gc_heap_base(&self) -> u8 {
664        let offset = self.gc_heap() + self.0.vm_memory_definition().base();
665        debug_assert!(offset < self.last_wasm_exit_trampoline_fp());
666        offset
667    }
668
669    /// The offset of the `gc_heap.current_length` field within a
670    /// `VMStoreContext`.
671    pub fn gc_heap_current_length(&self) -> u8 {
672        let offset = self.gc_heap() + self.0.vm_memory_definition().current_length();
673        debug_assert!(offset < self.last_wasm_exit_trampoline_fp());
674        offset
675    }
676}
677
678/// Add a `fn vm_foo(&self) -> offsets::VMFoo<&Self>` accessor to `PtrSize` for
679/// each `VM*` type.
680macro_rules! define_ptr_size_vm_type_accessors {
681    ( $(
682        $(#[doc = $sdoc:literal])*
683        $(#[cfg($($scfg:tt)*)])?
684        $(#[derive($($d:ident),*)])?
685        #[repr($($repr:tt)*)]
686        #[snake_name = $snake:ident]
687        $svis:vis $kind:ident $Name:ident {
688            $($body:tt)*
689        }
690    )* ) => {
691        $(
692            #[doc = concat!("Get the [`offsets::", stringify!($Name), "`] offsets for this pointer size.")]
693            #[inline]
694            fn $snake(&self) -> offsets::$Name<&Self> {
695                offsets::$Name(self)
696            }
697        )*
698    };
699}
700
701/// Add a `fn vmctx(&self) -> offsets::VMContext<&Self>` accessor to `PtrSize`
702/// for each vmctx type.
703macro_rules! define_ptr_size_vmctx_type_accessors {
704    ( $(
705        {
706            $Name:ident $snake:ident
707            static { $($stat:tt)* }
708            dynamic { $($dyn:tt)* }
709        }
710    )* ) => {
711        $(
712            #[doc = concat!("Get the [`offsets::", stringify!($Name),
713                            "`] offsets for this pointer size.")]
714            #[inline]
715            fn $snake(&self) -> offsets::$Name<&Self> {
716                offsets::$Name(self)
717            }
718        )*
719    };
720}
721
722/// This class computes offsets to fields within `VMContext` and other
723/// related structs that JIT code accesses directly.
724#[derive(Debug, Clone, Copy)]
725pub struct VMOffsets<P> {
726    /// The size in bytes of a pointer on the target.
727    pub ptr: P,
728    /// The number of imported functions in the module.
729    pub num_imported_functions: u32,
730    /// The number of imported tables in the module.
731    pub num_imported_tables: u32,
732    /// The number of imported memories in the module.
733    pub num_imported_memories: u32,
734    /// The number of imported globals in the module.
735    pub num_imported_globals: u32,
736    /// The number of imported tags in the module.
737    pub num_imported_tags: u32,
738    /// The number of defined tables in the module.
739    pub num_defined_tables: u32,
740    /// The number of defined memories in the module.
741    pub num_defined_memories: u32,
742    /// The number of memories owned by the module instance.
743    pub num_owned_memories: u32,
744    /// The number of shadow memory pointers: one per memory, imported and
745    /// defined, if the module's code checks memory shadows for watchpoints,
746    /// and otherwise none.
747    pub num_shadow_memories: u32,
748    /// The number of defined globals in the module.
749    pub num_defined_globals: u32,
750    /// The number of defined tags in the module.
751    pub num_defined_tags: u32,
752    /// The number of escaped functions in the module, the size of the func_refs
753    /// array.
754    pub num_escaped_funcs: u32,
755    /// The number of runtime data segments in the module.
756    pub num_runtime_data: u32,
757    /// Whether or not the module has a start function.
758    pub has_startup_func: bool,
759
760    // Precalculated offsets of the dynamically-positioned fields.
761    imported_memories: u32,
762    memories: u32,
763    owned_memories: u32,
764    shadow_memories: u32,
765    imported_functions: u32,
766    imported_tables: u32,
767    imported_globals: u32,
768    imported_tags: u32,
769    tables: u32,
770    globals: u32,
771    tags: u32,
772    func_refs: u32,
773    startup_func_ref: u32,
774    runtime_data_bases: u32,
775    runtime_data_lengths: u32,
776    size: u32,
777}
778
779/// Trait used for the `ptr` representation of the field of `VMOffsets`
780pub trait PtrSize {
781    /// Returns the pointer size, in bytes, for the target.
782    fn size(&self) -> u8;
783
784    // Generate a `fn vm_foo(&self) -> offsets::VMFoo<&Self>` accessor for each
785    // `VM*` type.
786    for_each_vm_type!(define_ptr_size_vm_type_accessors);
787
788    // Generate a `fn vmctx(&self) -> offsets::VMContext<&Self>` accessor for
789    // each vmctx type.
790    for_each_vmctx_type!(define_ptr_size_vmctx_type_accessors);
791
792    /// Return the size of `VMSharedTypeIndex`.
793    #[inline]
794    fn size_of_vmshared_type_index(&self) -> u8 {
795        4
796    }
797
798    /// Return the alignment of `VMSharedTypeIndex`.
799    #[inline]
800    fn align_of_vmshared_type_index(&self) -> u8 {
801        4
802    }
803
804    /// This is the size of the largest value type (i.e. a V128).
805    #[inline]
806    fn maximum_value_size(&self) -> u8 {
807        16
808    }
809
810    /// Return the size of `*mut VMMemoryDefinition`.
811    #[inline]
812    fn size_of_vmmemory_pointer(&self) -> u8 {
813        self.size()
814    }
815
816    // Offsets within `VMArrayCallHostFuncContext`.
817
818    /// Return the offset of `VMArrayCallHostFuncContext::func_ref`.
819    fn vmarray_call_host_func_context_func_ref(&self) -> u8 {
820        u8::try_from(align(
821            u32::try_from(core::mem::size_of::<u32>()).unwrap(),
822            u32::from(self.size()),
823        ))
824        .unwrap()
825    }
826
827    /// Return the size of `VMStackChain`.
828    fn size_of_vmstack_chain(&self) -> u8 {
829        2 * self.size()
830    }
831
832    // Offsets within `VMContObj`
833
834    /// Return the offset of `VMContObj::contref`
835    fn vmcontobj_contref(&self) -> u8 {
836        0
837    }
838
839    /// Return the offset of `VMContObj::revision`
840    fn vmcontobj_revision(&self) -> u8 {
841        self.size()
842    }
843
844    /// Return the size of `VMContObj`.
845    fn size_of_vmcontobj(&self) -> u8 {
846        u8::try_from(align(
847            u32::from(self.vmcontobj_revision())
848                + u32::try_from(core::mem::size_of::<usize>()).unwrap(),
849            u32::from(self.size()),
850        ))
851        .unwrap()
852    }
853}
854
855/// A trait to abstract over various types that contain a `P: PtrSize`.
856pub trait GetPtrSize {
857    /// The type that implements `PtrSize`.
858    type Ptr: PtrSize;
859
860    /// Get a `&P` where `P: PtrSize`.
861    fn get_ptr_size(&self) -> &Self::Ptr;
862}
863
864impl<P> GetPtrSize for P
865where
866    P: PtrSize,
867{
868    type Ptr = Self;
869
870    #[inline]
871    fn get_ptr_size(&self) -> &Self::Ptr {
872        self
873    }
874}
875
876/// Type representing the size of a pointer for the current compilation host
877#[derive(Clone, Copy)]
878pub struct HostPtr;
879
880impl PtrSize for HostPtr {
881    #[inline]
882    fn size(&self) -> u8 {
883        core::mem::size_of::<usize>() as u8
884    }
885}
886
887impl PtrSize for u8 {
888    #[inline]
889    fn size(&self) -> u8 {
890        *self
891    }
892}
893
894impl<P> PtrSize for &'_ P
895where
896    P: PtrSize + ?Sized,
897{
898    #[inline]
899    fn size(&self) -> u8 {
900        (**self).size()
901    }
902}
903
904/// Used to construct a `VMOffsets`
905#[derive(Debug, Clone, Copy)]
906pub struct VMOffsetsFields<P> {
907    /// The size in bytes of a pointer on the target.
908    pub ptr: P,
909    /// The number of imported functions in the module.
910    pub num_imported_functions: u32,
911    /// The number of imported tables in the module.
912    pub num_imported_tables: u32,
913    /// The number of imported memories in the module.
914    pub num_imported_memories: u32,
915    /// The number of imported globals in the module.
916    pub num_imported_globals: u32,
917    /// The number of imported tags in the module.
918    pub num_imported_tags: u32,
919    /// The number of defined tables in the module.
920    pub num_defined_tables: u32,
921    /// The number of defined memories in the module.
922    pub num_defined_memories: u32,
923    /// The number of memories owned by the module instance.
924    pub num_owned_memories: u32,
925    /// The number of shadow memory pointers: one per memory, imported and
926    /// defined, if the module's code checks memory shadows for watchpoints,
927    /// and otherwise none.
928    pub num_shadow_memories: u32,
929    /// The number of defined globals in the module.
930    pub num_defined_globals: u32,
931    /// The number of defined tags in the module.
932    pub num_defined_tags: u32,
933    /// The number of escaped functions in the module, the size of the function
934    /// references array.
935    pub num_escaped_funcs: u32,
936    /// The number of runtime data segments in the module.
937    pub num_runtime_data: u32,
938    /// Whether or not the module has a start function.
939    pub has_startup_func: bool,
940}
941
942impl<P: PtrSize> VMOffsets<P> {
943    /// Return a new `VMOffsets` instance, for a given pointer size.
944    pub fn new(ptr: P, module: &Module) -> Self {
945        let num_owned_memories = module
946            .memories
947            .iter()
948            .skip(module.num_imported_memories)
949            .filter(|p| !p.1.shared)
950            .count()
951            .try_into()
952            .unwrap();
953        VMOffsets::from(VMOffsetsFields {
954            ptr,
955            num_imported_functions: cast_to_u32(module.num_imported_funcs),
956            num_imported_tables: cast_to_u32(module.num_imported_tables),
957            num_imported_memories: cast_to_u32(module.num_imported_memories),
958            num_imported_globals: cast_to_u32(module.num_imported_globals),
959            num_imported_tags: cast_to_u32(module.num_imported_tags),
960            num_defined_tables: cast_to_u32(module.num_defined_tables()),
961            num_defined_memories: cast_to_u32(module.num_defined_memories()),
962            num_owned_memories,
963            num_shadow_memories: if module.has_shadow_memories {
964                cast_to_u32(module.memories.len())
965            } else {
966                0
967            },
968            num_defined_globals: cast_to_u32(module.globals.len() - module.num_imported_globals),
969            num_defined_tags: cast_to_u32(module.tags.len() - module.num_imported_tags),
970            num_escaped_funcs: cast_to_u32(module.num_escaped_funcs),
971            num_runtime_data: cast_to_u32(module.runtime_data.len()),
972            has_startup_func: !module.startup.is_none(),
973        })
974    }
975
976    /// Returns the size, in bytes, of the target
977    #[inline]
978    pub fn pointer_size(&self) -> u8 {
979        self.ptr.size()
980    }
981
982    /// Returns an iterator which provides a human readable description and a
983    /// byte size. The iterator returned will iterate over the bytes allocated
984    /// to the entire `VMOffsets` structure to explain where each byte size is
985    /// coming from.
986    pub fn region_sizes(&self) -> impl Iterator<Item = (&str, u32)> {
987        macro_rules! calculate_sizes {
988            ($($name:ident: $desc:tt,)*) => {{
989                let VMOffsets {
990                    // These fields are metadata not talking about specific
991                    // offsets of specific fields.
992                    ptr: _,
993                    num_imported_functions: _,
994                    num_imported_tables: _,
995                    num_imported_memories: _,
996                    num_imported_globals: _,
997                    num_imported_tags: _,
998                    num_defined_tables: _,
999                    num_defined_globals: _,
1000                    num_defined_memories: _,
1001                    num_defined_tags: _,
1002                    num_owned_memories: _,
1003                    num_shadow_memories: _,
1004                    num_escaped_funcs: _,
1005                    num_runtime_data: _,
1006                    has_startup_func: _,
1007
1008                    // used as the initial size below
1009                    size,
1010
1011                    // exhaustively match the rest of the fields with input from
1012                    // the macro
1013                    $($name,)*
1014                } = *self;
1015
1016                // calculate the size of each field by relying on the inputs to
1017                // the macro being in reverse order and determining the size of
1018                // the field as the offset from the field to the last field.
1019                let mut last = size;
1020                $(
1021                    assert!($name <= last);
1022                    let tmp = $name;
1023                    let $name = last - $name;
1024                    last = tmp;
1025                )*
1026                assert_ne!(last, 0);
1027                IntoIterator::into_iter([
1028                    $(($desc, $name),)*
1029                    ("static vmctx data", last),
1030                ])
1031            }};
1032        }
1033
1034        calculate_sizes! {
1035            runtime_data_lengths: "runtime data lengths",
1036            runtime_data_bases: "runtime data base pointers",
1037            startup_func_ref: "startup funcref",
1038            func_refs: "module functions",
1039            tags: "defined tags",
1040            globals: "defined globals",
1041            tables: "defined tables",
1042            imported_tags: "imported tags",
1043            imported_globals: "imported globals",
1044            imported_tables: "imported tables",
1045            imported_functions: "imported functions",
1046            shadow_memories: "shadow memories",
1047            owned_memories: "owned memories",
1048            memories: "defined memories",
1049            imported_memories: "imported memories",
1050        }
1051    }
1052}
1053
1054impl<P: PtrSize> GetPtrSize for VMOffsets<P> {
1055    type Ptr = P;
1056
1057    #[inline]
1058    fn get_ptr_size(&self) -> &Self::Ptr {
1059        &self.ptr
1060    }
1061}
1062
1063impl<P: PtrSize> From<VMOffsetsFields<P>> for VMOffsets<P> {
1064    fn from(fields: VMOffsetsFields<P>) -> VMOffsets<P> {
1065        let mut ret = Self {
1066            ptr: fields.ptr,
1067            num_imported_functions: fields.num_imported_functions,
1068            num_imported_tables: fields.num_imported_tables,
1069            num_imported_memories: fields.num_imported_memories,
1070            num_imported_globals: fields.num_imported_globals,
1071            num_imported_tags: fields.num_imported_tags,
1072            num_defined_tables: fields.num_defined_tables,
1073            num_defined_memories: fields.num_defined_memories,
1074            num_owned_memories: fields.num_owned_memories,
1075            num_shadow_memories: fields.num_shadow_memories,
1076            num_defined_globals: fields.num_defined_globals,
1077            num_defined_tags: fields.num_defined_tags,
1078            num_escaped_funcs: fields.num_escaped_funcs,
1079            num_runtime_data: fields.num_runtime_data,
1080            has_startup_func: fields.has_startup_func,
1081            imported_memories: 0,
1082            memories: 0,
1083            owned_memories: 0,
1084            shadow_memories: 0,
1085            imported_functions: 0,
1086            imported_tables: 0,
1087            imported_globals: 0,
1088            imported_tags: 0,
1089            tables: 0,
1090            globals: 0,
1091            tags: 0,
1092            func_refs: 0,
1093            startup_func_ref: 0,
1094            runtime_data_bases: 0,
1095            runtime_data_lengths: 0,
1096            size: 0,
1097        };
1098        ret.compute_field_offsets();
1099        ret
1100    }
1101}
1102
1103/// Offsets for `*const VMFunctionBody`.
1104impl<P: PtrSize> VMOffsets<P> {
1105    /// The size of the `current_elements` field.
1106    pub fn size_of_vmfunction_body_ptr(&self) -> u8 {
1107        1 * self.pointer_size()
1108    }
1109}
1110
1111/// Offsets for `VMTableDefinition`.
1112impl<P: PtrSize> VMOffsets<P> {
1113    /// The size of the `current_elements` field.
1114    #[inline]
1115    pub fn size_of_vmtable_definition_current_elements(&self) -> u8 {
1116        self.pointer_size()
1117    }
1118}
1119
1120/// Offsets for `VMSharedTypeIndex`.
1121impl<P: PtrSize> VMOffsets<P> {
1122    /// Return the size of `VMSharedTypeIndex`.
1123    #[inline]
1124    pub fn size_of_vmshared_type_index(&self) -> u8 {
1125        self.ptr.size_of_vmshared_type_index()
1126    }
1127}
1128
1129impl_vmctx_array_index! {
1130    MemoryIndex,
1131    DefinedMemoryIndex,
1132    OwnedMemoryIndex,
1133    FuncIndex,
1134    TableIndex,
1135    DefinedTableIndex,
1136    GlobalIndex,
1137    DefinedGlobalIndex,
1138    TagIndex,
1139    DefinedTagIndex,
1140    FuncRefIndex,
1141    RuntimeDataIndex,
1142    ModuleInternedTypeIndex,
1143}
1144
1145/// Generate the accessors for the offsets of `VMContext`'s
1146/// dynamically-positioned fields.
1147macro_rules! define_vmoffsets_dynamic_offsets {
1148    (@one VMContext $snake:ident { $($dyn:tt)* }) => {
1149        /// Offsets of the dynamically-positioned fields of `VMContext`.
1150        impl<P: PtrSize> VMOffsets<P> {
1151            define_vmctx_dynamic_offsets!(@accessors (self) [ $($dyn)* ]);
1152            define_vmctx_dynamic_offsets!(@compute_fn (self, next) $snake [ $($dyn)* ]);
1153
1154            /// Return the size of the `VMContext` allocation.
1155            #[inline]
1156            pub fn size_of_vmctx(&self) -> u32 {
1157                self.size
1158            }
1159        }
1160    };
1161    (@one $other:ident $snake:ident { $($dyn:tt)* }) => {};
1162
1163    ( $(
1164        {
1165            $Name:ident $snake:ident
1166            static { $($stat:tt)* }
1167            dynamic { $($dyn:tt)* }
1168        }
1169    )* ) => {
1170        $( define_vmoffsets_dynamic_offsets!(@one $Name $snake { $($dyn)* }); )*
1171    };
1172}
1173for_each_vmctx_type!(define_vmoffsets_dynamic_offsets);
1174
1175/// Magic value for core Wasm VM contexts.
1176///
1177/// This is stored at the start of all `VMContext` structures.
1178pub const VMCONTEXT_MAGIC: u32 = u32::from_le_bytes(*b"core");
1179
1180/// Equivalent of `VMCONTEXT_MAGIC` except for array-call host functions.
1181///
1182/// This is stored at the start of all `VMArrayCallHostFuncContext` structures
1183/// and double-checked on `VMArrayCallHostFuncContext::from_opaque`.
1184pub const VM_ARRAY_CALL_HOST_FUNC_MAGIC: u32 = u32::from_le_bytes(*b"ACHF");
1185
1186#[cfg(test)]
1187mod tests {
1188    use crate::vmoffsets::{VMOffsets, VMOffsetsFields, align};
1189
1190    #[test]
1191    fn alignment() {
1192        fn is_aligned(x: u32) -> bool {
1193            x % 16 == 0
1194        }
1195        assert!(is_aligned(align(0, 16)));
1196        assert!(is_aligned(align(32, 16)));
1197        assert!(is_aligned(align(33, 16)));
1198        assert!(is_aligned(align(31, 16)));
1199    }
1200
1201    #[test]
1202    fn pointer_fields_aligned_after_tags() {
1203        for ptr in [4u8, 8] {
1204            for num_defined_tags in 0..4 {
1205                let offsets = VMOffsets::from(VMOffsetsFields {
1206                    ptr,
1207                    num_imported_functions: 0,
1208                    num_imported_tables: 0,
1209                    num_imported_memories: 0,
1210                    num_imported_globals: 0,
1211                    num_imported_tags: 0,
1212                    num_defined_tables: 0,
1213                    num_defined_memories: 0,
1214                    num_owned_memories: 0,
1215                    num_shadow_memories: 0,
1216                    num_defined_globals: 0,
1217                    num_defined_tags,
1218                    num_escaped_funcs: 1,
1219                    num_runtime_data: 1,
1220                    has_startup_func: true,
1221                });
1222                let ptr = u32::from(ptr);
1223                assert_eq!(offsets.func_refs % ptr, 0);
1224                assert_eq!(offsets.startup_func_ref % ptr, 0);
1225                assert_eq!(offsets.runtime_data_bases % ptr, 0);
1226            }
1227        }
1228    }
1229}
1230
1231/// The bit pattern of `VMLazyThread::forced()`.
1232pub const VM_LAZY_THREAD_FORCED: u64 = 1;