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