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#[cfg(target_pointer_width = "32")]
20fn cast_to_u32(sz: usize) -> u32 {
21    u32::try_from(sz).unwrap()
22}
23#[cfg(target_pointer_width = "64")]
24fn cast_to_u32(sz: usize) -> u32 {
25    u32::try_from(sz).expect("overflow in cast from usize to u32")
26}
27
28/// Align an offset used in this module to a specific byte-width by rounding up
29#[inline]
30fn align(offset: u32, width: u32) -> u32 {
31    (offset + (width - 1)) / width * width
32}
33
34/// Generate the [`offsets`] module: a `struct VMFoo<P: PtrSize>(P)` for each
35/// `VM*` type, with a method per field returning that field's offset, plus
36/// `size` and `align` methods.
37macro_rules! define_vm_type_offsets {
38    // `UnsafeCell<T>` is `repr(transparent)`, so it has exactly `T`'s layout;
39    // delegate to the inner type.
40    (@size ($p:expr) UnsafeCell < $inner:tt >) => { define_vm_type_offsets!(@size ($p) $inner) };
41
42    // Classify a field type to its size in bytes as a `u32`, given `$p` (the
43    // target pointer size as a `u8`). All `VmPtr<_>` and `Option<VmPtr<_>>`
44    // fields are pointer-sized; the `Defined*Index` types are `u32` entity
45    // references; and `VMGlobalKind` is a `repr(C, u32)` enum with a `u32`
46    // payload.
47    (@size ($p:expr) VmPtr < $g:ty >) => { u32::from($p) };
48    (@size ($p:expr) Option < VmPtr < $g:ty >>) => { u32::from($p) };
49    (@size ($p:expr) AtomicUsize) => { u32::from($p) };
50    (@size ($p:expr) usize) => { u32::from($p) };
51    (@size ($p:expr) i64) => { 8u32 };
52    (@size ($p:expr) u64) => { 8u32 };
53    (@size ($p:expr) u32) => { 4u32 };
54    // `VMGcRef` is a `NonZeroU32` newtype, so `Option<VMGcRef>` is niche-packed
55    // back down into four bytes.
56    (@size ($p:expr) NonZeroU32) => { 4u32 };
57    (@size ($p:expr) Option < VMGcRef >) => { 4u32 };
58    (@size ($p:expr) [u8; 16]) => { 16u32 };
59    (@size ($p:expr) [u32; $n:expr]) => { 4u32 * u32::try_from($n).unwrap() };
60    (@size ($p:expr) VMSharedTypeIndex) => { u32::from(($p).size_of_vmshared_type_index()) };
61    (@size ($p:expr) DefinedTableIndex) => { 4u32 };
62    (@size ($p:expr) DefinedMemoryIndex) => { 4u32 };
63    (@size ($p:expr) DefinedTagIndex) => { 4u32 };
64    (@size ($p:expr) VMGlobalKind) => { 8u32 };
65    // `Range<T>` is a two-field `{ start: T, end: T }` struct.
66    (@size ($p:expr) Range < *mut u8 >) => { 2u32 * u32::from($p) };
67    // Nested `VM*` types recurse through their own generated offsets, keeping
68    // this macro the single source of truth for their layout too.
69    (@size ($p:expr) VMMemoryDefinition) => { u32::from(($p).vm_memory_definition().size()) };
70    (@size ($p:expr) VMLazyThread) => { u32::from(($p).vm_lazy_thread().size()) };
71    (@size ($p:expr) VMStackLimits) => { u32::from(($p).vm_stack_limits().size()) };
72    (@size ($p:expr) VMHostArray) => { u32::from(($p).vm_host_array().size()) };
73    (@size ($p:expr) VMPayloads) => { u32::from(($p).vm_payloads().size()) };
74    (@size ($p:expr) VMCommonStackInformation) => {
75        u32::from(($p).vm_common_stack_information().size())
76    };
77    (@size ($p:expr) VMGcHeader) => { u32::from(($p).vm_gc_header().size()) };
78    // `VMStackChain` is a `repr(usize, C)` enum, and is not itself defined by
79    // `for_each_vm_type!`.
80    (@size ($p:expr) VMStackChain) => { u32::from(($p).size_of_vmstack_chain()) };
81    (@size ($p:expr) VMStackState) => { 4u32 };
82    // `VMContinuationStack` is a platform-specific `repr(C)` struct private to
83    // the runtime, but every implementation of it is a pointer, a `usize`, and a
84    // byte-sized tag.
85    (@size ($p:expr) VMContinuationStack) => { 3u32 * u32::from($p) };
86    (@size ($p:expr) PhantomPinned) => { 0u32 };
87
88    // As with `@size` above, `UnsafeCell<T>` has exactly `T`'s alignment.
89    (@align ($p:expr) UnsafeCell < $inner:tt >) => { define_vm_type_offsets!(@align ($p) $inner) };
90
91    // Classify a field type to its alignment in bytes as a `u32`, given `$p`
92    // (the target pointer size as a `u8`).
93    //
94    // NB: 64-bit integers are assumed to be 8-aligned, which holds everywhere except
95    // `i686-unknown-linux-gnu`, and the pointer size alone can't tell those apart. Types
96    // with 64-bit fields must therefore put them first and force their own alignment with
97    // `#[repr(C, align(8))]`, as `VMStoreContext` does.
98    (@align ($p:expr) VmPtr < $g:ty >) => { u32::from($p) };
99    (@align ($p:expr) Option < VmPtr < $g:ty >>) => { u32::from($p) };
100    (@align ($p:expr) AtomicUsize) => { u32::from($p) };
101    (@align ($p:expr) usize) => { u32::from($p) };
102    (@align ($p:expr) i64) => { 8u32 };
103    (@align ($p:expr) u64) => { 8u32 };
104    (@align ($p:expr) u32) => { 4u32 };
105    (@align ($p:expr) NonZeroU32) => { 4u32 };
106    (@align ($p:expr) Option < VMGcRef >) => { 4u32 };
107    (@align ($p:expr) [u8; 16]) => { 16u32 };
108    (@align ($p:expr) [u32; $n:expr]) => { 4u32 };
109    (@align ($p:expr) VMSharedTypeIndex) => { u32::from(($p).align_of_vmshared_type_index()) };
110    (@align ($p:expr) DefinedTableIndex) => { 4u32 };
111    (@align ($p:expr) DefinedMemoryIndex) => { 4u32 };
112    (@align ($p:expr) DefinedTagIndex) => { 4u32 };
113    (@align ($p:expr) VMGlobalKind) => { 4u32 };
114    (@align ($p:expr) Range < *mut u8 >) => { u32::from($p) };
115    (@align ($p:expr) VMMemoryDefinition) => { u32::from(($p).vm_memory_definition().align()) };
116    (@align ($p:expr) VMLazyThread) => { u32::from(($p).vm_lazy_thread().align()) };
117    (@align ($p:expr) VMStackLimits) => { u32::from(($p).vm_stack_limits().align()) };
118    (@align ($p:expr) VMHostArray) => { u32::from(($p).vm_host_array().align()) };
119    (@align ($p:expr) VMPayloads) => { u32::from(($p).vm_payloads().align()) };
120    (@align ($p:expr) VMCommonStackInformation) => {
121        u32::from(($p).vm_common_stack_information().align())
122    };
123    (@align ($p:expr) VMGcHeader) => { u32::from(($p).vm_gc_header().align()) };
124    (@align ($p:expr) VMStackChain) => { u32::from($p) };
125    (@align ($p:expr) VMStackState) => { 4u32 };
126    (@align ($p:expr) VMContinuationStack) => { u32::from($p) };
127    (@align ($p:expr) PhantomPinned) => { 1u32 };
128
129    // Classify a `#[repr(...)]` to the minimum alignment it forces, as a `u32`.
130    (@repr_align C) => { 1u32 };
131    (@repr_align transparent) => { 1u32 };
132    (@repr_align C, align($n:literal)) => {{ let align: u32 = $n; align }};
133
134    // Emit a `pub fn` per field returning that field's offset, computed by
135    // accumulating the aligned size of each preceding field. `$p`/`$o` are
136    // caller-minted identifiers (for the pointer size and running offset) that
137    // are threaded through the recursion so their hygiene stays consistent
138    // across the emitted `let` bindings.
139    //
140    // Fields arrive pre-split into `[ $fname : $fty... ]` groups (see `@impl`
141    // below), so each field's type is a raw token sequence that the `@size`
142    // and `@align` classifiers can match against structurally.
143    (@fields $Name:ident ($p:ident, $o:ident) prefix( $($prefix:tt)* )) => {};
144    (@fields $Name:ident ($p:ident, $o:ident) prefix( $($prefix:tt)* )
145        [ $fname:ident : $($fty:tt)* ]
146        $($rest:tt)*
147    ) => {
148        #[doc = concat!(
149            "The offset of the `", stringify!($fname),
150            "` field of `", stringify!($Name), "`."
151        )]
152        #[inline]
153        pub fn $fname(&self) -> u8 {
154            let $p = self.0.size();
155            let $o: u32 = 0;
156            $($prefix)*
157            let $o = align($o, define_vm_type_offsets!(@align ($p) $($fty)*));
158            let _ = $p;
159            u8::try_from($o).unwrap()
160        }
161        define_vm_type_offsets!(@fields $Name ($p, $o)
162            prefix(
163                $($prefix)*
164                let $o = align($o, define_vm_type_offsets!(@align ($p) $($fty)*));
165                let $o = $o + define_vm_type_offsets!(@size ($p) $($fty)*);
166            )
167            $($rest)*
168        );
169    };
170
171    // Emit an `offsets::VMFoo` type's inherent `impl`. Fields are peeled off
172    // the raw struct body one at a time into `[ $fname : $fty... ]` groups
173    // accumulated in the `{ ... }` list; once the body is exhausted, the
174    // terminal arm emits the per-field offset methods plus `align`/`size`.
175    //
176    // Splitting the body by hand (rather than matching `$fty:tt $(< $fgen:ty
177    // >)?` within a repetition) is what lets each field's type reach the
178    // `@size`/`@align` classifiers as raw tokens, so those classifiers can
179    // require `Option`s to specifically be `Option<VmPtr<_>>`.
180    (@impl $Name:ident [$($repr:tt)*] { $( [ $fname:ident : $($fty:tt)* ] )* }) => {
181        impl<P: PtrSize> $Name<P> {
182            define_vm_type_offsets!(@fields $Name (p, o) prefix()
183                $( [ $fname : $($fty)* ] )*
184            );
185
186            #[doc = concat!("The alignment of the `", stringify!($Name), "` type.")]
187            #[inline]
188            pub fn align(&self) -> u8 {
189                let p = self.0.size();
190                let a: u32 = define_vm_type_offsets!(@repr_align $($repr)*);
191                $(
192                    let a = core::cmp::max(
193                        a,
194                        define_vm_type_offsets!(@align (p) $($fty)*),
195                    );
196                )*
197                let _ = p;
198                u8::try_from(a).unwrap()
199            }
200
201            #[doc = concat!("The size of the `", stringify!($Name), "` type.")]
202            #[inline]
203            pub fn size(&self) -> u8 {
204                let p = self.0.size();
205                let o: u32 = 0;
206                $(
207                    let o = align(o, define_vm_type_offsets!(@align (p) $($fty)*));
208                    let o = o + define_vm_type_offsets!(@size (p) $($fty)*);
209                )*
210                let o = align(o, u32::from(self.align()));
211                let _ = p;
212                u8::try_from(o).unwrap()
213            }
214        }
215    };
216    // Consume one field's attributes, visibility, and name, then collect its
217    // type tokens. None of the field attributes (doc comments and the
218    // `#[aggregate]`/`#[readonly]`/`#[can_move]` markers) affect layout, so they
219    // are all discarded here.
220    (@impl $Name:ident $repr:tt { $($groups:tt)* }
221        $(#[$($attr:tt)*])* $fvis:vis $fname:ident : $($rest:tt)*
222    ) => {
223        define_vm_type_offsets!(@impl_ty $Name $repr { $($groups)* } $fname [] $($rest)*);
224    };
225    // Accumulate one field's type tokens up to its terminating comma, then
226    // append the completed `[ $fname : $fty... ]` group and resume `@impl`.
227    (@impl_ty $Name:ident $repr:tt { $($groups:tt)* } $fname:ident [ $($fty:tt)* ] , $($rest:tt)*) => {
228        define_vm_type_offsets!(@impl $Name $repr { $($groups)* [ $fname : $($fty)* ] } $($rest)*);
229    };
230    (@impl_ty $Name:ident $repr:tt { $($groups:tt)* } $fname:ident [ $($fty:tt)* ] $tok:tt $($rest:tt)*) => {
231        define_vm_type_offsets!(@impl_ty $Name $repr { $($groups)* } $fname [ $($fty)* $tok ] $($rest)*);
232    };
233
234    // Top-level entry: the list of `VM*` type definitions.
235    ( $(
236        $(#[doc = $sdoc:literal])*
237        $(#[cfg($($scfg:tt)*)])?
238        $(#[derive($($d:ident),*)])?
239        #[repr($($repr:tt)*)]
240        #[snake_name = $snake:ident]
241        $svis:vis struct $Name:ident {
242            $($body:tt)*
243        }
244    )* ) => {
245        $(
246            #[doc = concat!("Offsets of fields within the `", stringify!($Name), "` type.")]
247            pub struct $Name<P: PtrSize>(pub P);
248
249            define_vm_type_offsets!(@impl $Name [$($repr)*] {} $($body)*);
250        )*
251    };
252}
253
254/// Generate a `struct VMContext<P: PtrSize>(P)`-style wrapper for each vmctx
255/// type, with a method per statically-positioned field returning that field's
256/// offset.
257#[allow(
258    unused_macro_rules,
259    reason = "only `VMComponentContext` has a `#[ptr_size_offset]` prefix in its \
260              `dynamic` section, so those arms go unused for `VMContext`"
261)]
262macro_rules! define_vmctx_static_offsets {
263    // Munch the `static` section, threading a closed-form expression for the
264    // running offset. Once it is exhausted, keep going into the `dynamic`
265    // section's `#[ptr_size_offset]` prefix, whose offsets are closed-form too.
266    (@chain $p:ident [ $($prev:tt)* ] [] [ $($dyn:tt)* ]) => {
267        /// The offset just past this type's last statically-positioned field.
268        ///
269        /// Everything after this point is dynamically sized.
270        #[inline]
271        pub fn end_of_static_fields(&self) -> u8 {
272            let $p = self.0.size();
273            let _ = $p;
274            u8::try_from($($prev)*).unwrap()
275        }
276
277        define_vmctx_static_offsets!(@dyn_chain $p [ $($prev)* ] [ $($dyn)* ]);
278    };
279    (@chain $p:ident [ $($prev:tt)* ] [ align { $al:tt } $($rest:tt)* ] $dyn:tt) => {
280        define_vmctx_static_offsets!(
281            @chain $p
282            [ crate::vmctxtypes::align_up($($prev)*, vmctx_align_value!(($p) $al)) ]
283            [ $($rest)* ]
284            $dyn
285        );
286    };
287    (@chain $p:ident [ $($prev:tt)* ] [
288        field { $(# $fattr:tt)* $fname:ident : $($fty:tt)* } $($rest:tt)*
289    ] $dyn:tt) => {
290        #[doc = concat!("The offset of the `", stringify!($fname), "` field.")]
291        #[inline]
292        pub fn $fname(&self) -> u8 {
293            let $p = self.0.size();
294            let _ = $p;
295            u8::try_from($($prev)*).unwrap()
296        }
297        define_vmctx_static_offsets!(
298            @chain $p
299            [ $($prev)* + vmctx_field_size!(($p) $($fty)*) ]
300            [ $($rest)* ]
301            $dyn
302        );
303    };
304
305    // Munch the `dynamic` section's leading run of `#[ptr_size_offset]` entries,
306    // continuing to thread the closed-form running offset.
307    (@dyn_chain $p:ident [ $($prev:tt)* ] [ align { $al:tt } $($rest:tt)* ]) => {
308        define_vmctx_static_offsets!(
309            @dyn_chain $p
310            [ crate::vmctxtypes::align_up($($prev)*, vmctx_align_value!(($p) $al)) ]
311            [ $($rest)* ]
312        );
313    };
314    (@dyn_chain $p:ident [ $($prev:tt)* ] [
315        field { #[ptr_size_offset] $(# $fattr:tt)* $fname:ident : $($fty:tt)* } $($rest:tt)*
316    ]) => {
317        #[doc = concat!("The offset of the `", stringify!($fname), "` field.")]
318        #[inline]
319        pub fn $fname(&self) -> u32 {
320            let $p = self.0.size();
321            let _ = $p;
322            $($prev)*
323        }
324        define_vmctx_static_offsets!(
325            @dyn_chain $p
326            [ $($prev)* + vmctx_field_size!(($p) $($fty)*) ]
327            [ $($rest)* ]
328        );
329    };
330    (@dyn_chain $p:ident [ $($prev:tt)* ] [
331        array {
332            #[ptr_size_offset] $(# $fattr:tt)*
333            $fname:ident [ $count:ident ; $Index:ident ] : $($fty:tt)*
334        } $($rest:tt)*
335    ]) => {
336        // NB: this takes `impl VmctxArrayIndex` rather than the declared
337        // `$Index` because these wrappers are compiled even when the
338        // `component-model` feature is off, and some of the index types are not.
339        #[doc = concat!(
340            "The offset of the `index`th element of the `", stringify!($fname),
341            "` array.\n\nThis is not bounds checked: the array's length is a \
342             property of the particular module or component being compiled, which \
343             is exactly what this wrapper does not know."
344        )]
345        #[inline]
346        pub fn $fname(&self, index: impl crate::VmctxArrayIndex) -> u32 {
347            let $p = self.0.size();
348            let _ = $p;
349            let index = index.vmctx_array_index();
350            ($($prev)*) + vmctx_field_size!(($p) $($fty)*) * index
351        }
352        // An array's size depends on how many elements this particular module or
353        // component needs, so nothing after it has a closed-form offset and the
354        // chain necessarily stops here.
355    };
356    // Anything else ends the closed-form prefix.
357    (@dyn_chain $p:ident [ $($prev:tt)* ] [ $($rest:tt)* ]) => {};
358
359    ( $(
360        {
361            $Name:ident $snake:ident
362            static { $($stat:tt)* }
363            dynamic { $($dyn:tt)* }
364        }
365    )* ) => {
366        $(
367            #[doc = concat!("Offsets of the statically-positioned fields within the `",
368                            stringify!($Name), "` type.")]
369            pub struct $Name<P: PtrSize>(pub P);
370
371            impl<P: PtrSize> $Name<P> {
372                define_vmctx_static_offsets!(@chain ptr [ 0u32 ] [ $($stat)* ] [ $($dyn)* ]);
373            }
374        )*
375    };
376}
377
378/// Offsets of fields within the various `VM*` types and within the two vmctx
379/// types, parameterized over a target `PtrSize` so that they can be computed
380/// during cross compilation.
381///
382/// These types are namespaced within their own module so that they never collide
383/// with the real definitions of the `VM*` types themselves.
384pub mod offsets {
385    use super::{NUM_COMPONENT_CONTEXT_SLOTS, PtrSize, align};
386
387    for_each_vm_type!(define_vm_type_offsets);
388    for_each_vmctx_type!(define_vmctx_static_offsets);
389}
390
391/// Offsets within a `VMStoreContext` that are not simply the offset of one of
392/// its fields, and so are not generated by `for_each_vm_type!`.
393impl<P: PtrSize> offsets::VMStoreContext<P> {
394    /// The offset of the `gc_heap.base` field within a `VMStoreContext`.
395    pub fn gc_heap_base(&self) -> u8 {
396        let offset = self.gc_heap() + self.0.vm_memory_definition().base();
397        debug_assert!(offset < self.last_wasm_exit_trampoline_fp());
398        offset
399    }
400
401    /// The offset of the `gc_heap.current_length` field within a
402    /// `VMStoreContext`.
403    pub fn gc_heap_current_length(&self) -> u8 {
404        let offset = self.gc_heap() + self.0.vm_memory_definition().current_length();
405        debug_assert!(offset < self.last_wasm_exit_trampoline_fp());
406        offset
407    }
408}
409
410/// Add a `fn vm_foo(&self) -> offsets::VMFoo<&Self>` accessor to `PtrSize` for
411/// each `VM*` type.
412macro_rules! define_ptr_size_vm_type_accessors {
413    ( $(
414        $(#[doc = $sdoc:literal])*
415        $(#[cfg($($scfg:tt)*)])?
416        $(#[derive($($d:ident),*)])?
417        #[repr($($repr:tt)*)]
418        #[snake_name = $snake:ident]
419        $svis:vis struct $Name:ident {
420            $($body:tt)*
421        }
422    )* ) => {
423        $(
424            #[doc = concat!("Get the [`offsets::", stringify!($Name), "`] offsets for this pointer size.")]
425            #[inline]
426            fn $snake(&self) -> offsets::$Name<&Self> {
427                offsets::$Name(self)
428            }
429        )*
430    };
431}
432
433/// Add a `fn vmctx(&self) -> offsets::VMContext<&Self>` accessor to `PtrSize`
434/// for each vmctx type.
435macro_rules! define_ptr_size_vmctx_type_accessors {
436    ( $(
437        {
438            $Name:ident $snake:ident
439            static { $($stat:tt)* }
440            dynamic { $($dyn:tt)* }
441        }
442    )* ) => {
443        $(
444            #[doc = concat!("Get the [`offsets::", stringify!($Name),
445                            "`] offsets for this pointer size.")]
446            #[inline]
447            fn $snake(&self) -> offsets::$Name<&Self> {
448                offsets::$Name(self)
449            }
450        )*
451    };
452}
453
454/// This class computes offsets to fields within `VMContext` and other
455/// related structs that JIT code accesses directly.
456#[derive(Debug, Clone, Copy)]
457pub struct VMOffsets<P> {
458    /// The size in bytes of a pointer on the target.
459    pub ptr: P,
460    /// The number of imported functions in the module.
461    pub num_imported_functions: u32,
462    /// The number of imported tables in the module.
463    pub num_imported_tables: u32,
464    /// The number of imported memories in the module.
465    pub num_imported_memories: u32,
466    /// The number of imported globals in the module.
467    pub num_imported_globals: u32,
468    /// The number of imported tags in the module.
469    pub num_imported_tags: u32,
470    /// The number of defined tables in the module.
471    pub num_defined_tables: u32,
472    /// The number of defined memories in the module.
473    pub num_defined_memories: u32,
474    /// The number of memories owned by the module instance.
475    pub num_owned_memories: u32,
476    /// The number of defined globals in the module.
477    pub num_defined_globals: u32,
478    /// The number of defined tags in the module.
479    pub num_defined_tags: u32,
480    /// The number of escaped functions in the module, the size of the func_refs
481    /// array.
482    pub num_escaped_funcs: u32,
483    /// The number of runtime data segments in the module.
484    pub num_runtime_data: u32,
485    /// Whether or not the module has a start function.
486    pub has_startup_func: bool,
487
488    // Precalculated offsets of the dynamically-positioned fields.
489    imported_memories: u32,
490    memories: u32,
491    owned_memories: u32,
492    imported_functions: u32,
493    imported_tables: u32,
494    imported_globals: u32,
495    imported_tags: u32,
496    tables: u32,
497    globals: u32,
498    tags: u32,
499    func_refs: u32,
500    startup_func_ref: u32,
501    runtime_data_bases: u32,
502    runtime_data_lengths: u32,
503    size: u32,
504}
505
506/// Trait used for the `ptr` representation of the field of `VMOffsets`
507pub trait PtrSize {
508    /// Returns the pointer size, in bytes, for the target.
509    fn size(&self) -> u8;
510
511    // Generate a `fn vm_foo(&self) -> offsets::VMFoo<&Self>` accessor for each
512    // `VM*` type.
513    for_each_vm_type!(define_ptr_size_vm_type_accessors);
514
515    // Generate a `fn vmctx(&self) -> offsets::VMContext<&Self>` accessor for
516    // each vmctx type.
517    for_each_vmctx_type!(define_ptr_size_vmctx_type_accessors);
518
519    /// Return the size of `VMSharedTypeIndex`.
520    #[inline]
521    fn size_of_vmshared_type_index(&self) -> u8 {
522        4
523    }
524
525    /// Return the alignment of `VMSharedTypeIndex`.
526    #[inline]
527    fn align_of_vmshared_type_index(&self) -> u8 {
528        4
529    }
530
531    /// This is the size of the largest value type (i.e. a V128).
532    #[inline]
533    fn maximum_value_size(&self) -> u8 {
534        self.vm_global_definition().size()
535    }
536
537    /// Return the size of `*mut VMMemoryDefinition`.
538    #[inline]
539    fn size_of_vmmemory_pointer(&self) -> u8 {
540        self.size()
541    }
542
543    // Offsets within `VMArrayCallHostFuncContext`.
544
545    /// Return the offset of `VMArrayCallHostFuncContext::func_ref`.
546    fn vmarray_call_host_func_context_func_ref(&self) -> u8 {
547        u8::try_from(align(
548            u32::try_from(core::mem::size_of::<u32>()).unwrap(),
549            u32::from(self.size()),
550        ))
551        .unwrap()
552    }
553
554    /// Return the size of `VMStackChain`.
555    fn size_of_vmstack_chain(&self) -> u8 {
556        2 * self.size()
557    }
558
559    // Offsets within `VMContObj`
560
561    /// Return the offset of `VMContObj::contref`
562    fn vmcontobj_contref(&self) -> u8 {
563        0
564    }
565
566    /// Return the offset of `VMContObj::revision`
567    fn vmcontobj_revision(&self) -> u8 {
568        self.size()
569    }
570
571    /// Return the size of `VMContObj`.
572    fn size_of_vmcontobj(&self) -> u8 {
573        u8::try_from(align(
574            u32::from(self.vmcontobj_revision())
575                + u32::try_from(core::mem::size_of::<usize>()).unwrap(),
576            u32::from(self.size()),
577        ))
578        .unwrap()
579    }
580}
581
582/// A trait to abstract over various types that contain a `P: PtrSize`.
583pub trait GetPtrSize {
584    /// The type that implements `PtrSize`.
585    type Ptr: PtrSize;
586
587    /// Get a `&P` where `P: PtrSize`.
588    fn get_ptr_size(&self) -> &Self::Ptr;
589}
590
591impl<P> GetPtrSize for P
592where
593    P: PtrSize,
594{
595    type Ptr = Self;
596
597    #[inline]
598    fn get_ptr_size(&self) -> &Self::Ptr {
599        self
600    }
601}
602
603/// Type representing the size of a pointer for the current compilation host
604#[derive(Clone, Copy)]
605pub struct HostPtr;
606
607impl PtrSize for HostPtr {
608    #[inline]
609    fn size(&self) -> u8 {
610        core::mem::size_of::<usize>() as u8
611    }
612}
613
614impl PtrSize for u8 {
615    #[inline]
616    fn size(&self) -> u8 {
617        *self
618    }
619}
620
621impl<P> PtrSize for &'_ P
622where
623    P: PtrSize + ?Sized,
624{
625    #[inline]
626    fn size(&self) -> u8 {
627        (**self).size()
628    }
629}
630
631/// Used to construct a `VMOffsets`
632#[derive(Debug, Clone, Copy)]
633pub struct VMOffsetsFields<P> {
634    /// The size in bytes of a pointer on the target.
635    pub ptr: P,
636    /// The number of imported functions in the module.
637    pub num_imported_functions: u32,
638    /// The number of imported tables in the module.
639    pub num_imported_tables: u32,
640    /// The number of imported memories in the module.
641    pub num_imported_memories: u32,
642    /// The number of imported globals in the module.
643    pub num_imported_globals: u32,
644    /// The number of imported tags in the module.
645    pub num_imported_tags: u32,
646    /// The number of defined tables in the module.
647    pub num_defined_tables: u32,
648    /// The number of defined memories in the module.
649    pub num_defined_memories: u32,
650    /// The number of memories owned by the module instance.
651    pub num_owned_memories: u32,
652    /// The number of defined globals in the module.
653    pub num_defined_globals: u32,
654    /// The number of defined tags in the module.
655    pub num_defined_tags: u32,
656    /// The number of escaped functions in the module, the size of the function
657    /// references array.
658    pub num_escaped_funcs: u32,
659    /// The number of runtime data segments in the module.
660    pub num_runtime_data: u32,
661    /// Whether or not the module has a start function.
662    pub has_startup_func: bool,
663}
664
665impl<P: PtrSize> VMOffsets<P> {
666    /// Return a new `VMOffsets` instance, for a given pointer size.
667    pub fn new(ptr: P, module: &Module) -> Self {
668        let num_owned_memories = module
669            .memories
670            .iter()
671            .skip(module.num_imported_memories)
672            .filter(|p| !p.1.shared)
673            .count()
674            .try_into()
675            .unwrap();
676        VMOffsets::from(VMOffsetsFields {
677            ptr,
678            num_imported_functions: cast_to_u32(module.num_imported_funcs),
679            num_imported_tables: cast_to_u32(module.num_imported_tables),
680            num_imported_memories: cast_to_u32(module.num_imported_memories),
681            num_imported_globals: cast_to_u32(module.num_imported_globals),
682            num_imported_tags: cast_to_u32(module.num_imported_tags),
683            num_defined_tables: cast_to_u32(module.num_defined_tables()),
684            num_defined_memories: cast_to_u32(module.num_defined_memories()),
685            num_owned_memories,
686            num_defined_globals: cast_to_u32(module.globals.len() - module.num_imported_globals),
687            num_defined_tags: cast_to_u32(module.tags.len() - module.num_imported_tags),
688            num_escaped_funcs: cast_to_u32(module.num_escaped_funcs),
689            num_runtime_data: cast_to_u32(module.runtime_data.len()),
690            has_startup_func: !module.startup.is_none(),
691        })
692    }
693
694    /// Returns the size, in bytes, of the target
695    #[inline]
696    pub fn pointer_size(&self) -> u8 {
697        self.ptr.size()
698    }
699
700    /// Returns an iterator which provides a human readable description and a
701    /// byte size. The iterator returned will iterate over the bytes allocated
702    /// to the entire `VMOffsets` structure to explain where each byte size is
703    /// coming from.
704    pub fn region_sizes(&self) -> impl Iterator<Item = (&str, u32)> {
705        macro_rules! calculate_sizes {
706            ($($name:ident: $desc:tt,)*) => {{
707                let VMOffsets {
708                    // These fields are metadata not talking about specific
709                    // offsets of specific fields.
710                    ptr: _,
711                    num_imported_functions: _,
712                    num_imported_tables: _,
713                    num_imported_memories: _,
714                    num_imported_globals: _,
715                    num_imported_tags: _,
716                    num_defined_tables: _,
717                    num_defined_globals: _,
718                    num_defined_memories: _,
719                    num_defined_tags: _,
720                    num_owned_memories: _,
721                    num_escaped_funcs: _,
722                    num_runtime_data: _,
723                    has_startup_func: _,
724
725                    // used as the initial size below
726                    size,
727
728                    // exhaustively match the rest of the fields with input from
729                    // the macro
730                    $($name,)*
731                } = *self;
732
733                // calculate the size of each field by relying on the inputs to
734                // the macro being in reverse order and determining the size of
735                // the field as the offset from the field to the last field.
736                let mut last = size;
737                $(
738                    assert!($name <= last);
739                    let tmp = $name;
740                    let $name = last - $name;
741                    last = tmp;
742                )*
743                assert_ne!(last, 0);
744                IntoIterator::into_iter([
745                    $(($desc, $name),)*
746                    ("static vmctx data", last),
747                ])
748            }};
749        }
750
751        calculate_sizes! {
752            runtime_data_lengths: "runtime data lengths",
753            runtime_data_bases: "runtime data base pointers",
754            startup_func_ref: "startup funcref",
755            func_refs: "module functions",
756            tags: "defined tags",
757            globals: "defined globals",
758            tables: "defined tables",
759            imported_tags: "imported tags",
760            imported_globals: "imported globals",
761            imported_tables: "imported tables",
762            imported_functions: "imported functions",
763            owned_memories: "owned memories",
764            memories: "defined memories",
765            imported_memories: "imported memories",
766        }
767    }
768}
769
770impl<P: PtrSize> GetPtrSize for VMOffsets<P> {
771    type Ptr = P;
772
773    #[inline]
774    fn get_ptr_size(&self) -> &Self::Ptr {
775        &self.ptr
776    }
777}
778
779impl<P: PtrSize> From<VMOffsetsFields<P>> for VMOffsets<P> {
780    fn from(fields: VMOffsetsFields<P>) -> VMOffsets<P> {
781        let mut ret = Self {
782            ptr: fields.ptr,
783            num_imported_functions: fields.num_imported_functions,
784            num_imported_tables: fields.num_imported_tables,
785            num_imported_memories: fields.num_imported_memories,
786            num_imported_globals: fields.num_imported_globals,
787            num_imported_tags: fields.num_imported_tags,
788            num_defined_tables: fields.num_defined_tables,
789            num_defined_memories: fields.num_defined_memories,
790            num_owned_memories: fields.num_owned_memories,
791            num_defined_globals: fields.num_defined_globals,
792            num_defined_tags: fields.num_defined_tags,
793            num_escaped_funcs: fields.num_escaped_funcs,
794            num_runtime_data: fields.num_runtime_data,
795            has_startup_func: fields.has_startup_func,
796            imported_memories: 0,
797            memories: 0,
798            owned_memories: 0,
799            imported_functions: 0,
800            imported_tables: 0,
801            imported_globals: 0,
802            imported_tags: 0,
803            tables: 0,
804            globals: 0,
805            tags: 0,
806            func_refs: 0,
807            startup_func_ref: 0,
808            runtime_data_bases: 0,
809            runtime_data_lengths: 0,
810            size: 0,
811        };
812        ret.compute_field_offsets();
813        ret
814    }
815}
816
817/// Offsets for `*const VMFunctionBody`.
818impl<P: PtrSize> VMOffsets<P> {
819    /// The size of the `current_elements` field.
820    pub fn size_of_vmfunction_body_ptr(&self) -> u8 {
821        1 * self.pointer_size()
822    }
823}
824
825/// Offsets for `VMTableDefinition`.
826impl<P: PtrSize> VMOffsets<P> {
827    /// The size of the `current_elements` field.
828    #[inline]
829    pub fn size_of_vmtable_definition_current_elements(&self) -> u8 {
830        self.pointer_size()
831    }
832}
833
834/// Offsets for `VMSharedTypeIndex`.
835impl<P: PtrSize> VMOffsets<P> {
836    /// Return the size of `VMSharedTypeIndex`.
837    #[inline]
838    pub fn size_of_vmshared_type_index(&self) -> u8 {
839        self.ptr.size_of_vmshared_type_index()
840    }
841}
842
843impl_vmctx_array_index! {
844    MemoryIndex,
845    DefinedMemoryIndex,
846    OwnedMemoryIndex,
847    FuncIndex,
848    TableIndex,
849    DefinedTableIndex,
850    GlobalIndex,
851    DefinedGlobalIndex,
852    TagIndex,
853    DefinedTagIndex,
854    FuncRefIndex,
855    RuntimeDataIndex,
856    ModuleInternedTypeIndex,
857}
858
859/// Generate the accessors for the offsets of `VMContext`'s
860/// dynamically-positioned fields.
861macro_rules! define_vmoffsets_dynamic_offsets {
862    (@one VMContext $snake:ident { $($dyn:tt)* }) => {
863        /// Offsets of the dynamically-positioned fields of `VMContext`.
864        impl<P: PtrSize> VMOffsets<P> {
865            define_vmctx_dynamic_offsets!(@accessors (self) [ $($dyn)* ]);
866            define_vmctx_dynamic_offsets!(@compute_fn (self, next) $snake [ $($dyn)* ]);
867
868            /// Return the size of the `VMContext` allocation.
869            #[inline]
870            pub fn size_of_vmctx(&self) -> u32 {
871                self.size
872            }
873        }
874    };
875    (@one $other:ident $snake:ident { $($dyn:tt)* }) => {};
876
877    ( $(
878        {
879            $Name:ident $snake:ident
880            static { $($stat:tt)* }
881            dynamic { $($dyn:tt)* }
882        }
883    )* ) => {
884        $( define_vmoffsets_dynamic_offsets!(@one $Name $snake { $($dyn)* }); )*
885    };
886}
887for_each_vmctx_type!(define_vmoffsets_dynamic_offsets);
888
889/// Magic value for core Wasm VM contexts.
890///
891/// This is stored at the start of all `VMContext` structures.
892pub const VMCONTEXT_MAGIC: u32 = u32::from_le_bytes(*b"core");
893
894/// Equivalent of `VMCONTEXT_MAGIC` except for array-call host functions.
895///
896/// This is stored at the start of all `VMArrayCallHostFuncContext` structures
897/// and double-checked on `VMArrayCallHostFuncContext::from_opaque`.
898pub const VM_ARRAY_CALL_HOST_FUNC_MAGIC: u32 = u32::from_le_bytes(*b"ACHF");
899
900#[cfg(test)]
901mod tests {
902    use crate::vmoffsets::align;
903
904    #[test]
905    fn alignment() {
906        fn is_aligned(x: u32) -> bool {
907            x % 16 == 0
908        }
909        assert!(is_aligned(align(0, 16)));
910        assert!(is_aligned(align(32, 16)));
911        assert!(is_aligned(align(33, 16)));
912        assert!(is_aligned(align(31, 16)));
913    }
914}
915
916/// The bit pattern of `VMLazyThread::forced()`.
917pub const VM_LAZY_THREAD_FORCED: u64 = 1;