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