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