Skip to main content

wasmtime_environ/
vmctxtypes.rs

1//! Centralized definitions of the layouts of Wasmtime's two "vmctx" types:
2//! `VMContext`, the runtime context for a core Wasm instance, and
3//! `VMComponentContext`, the runtime context for a component.
4//!
5//! Unlike the `VM*` types defined by `for_each_vm_type!`, neither of these has
6//! a corresponding `#[repr(C)]` Rust `struct`: their sizes depend on the module
7//! or component being instantiated, so they are dynamically laid out and
8//! accessed exclusively through the computed offsets in `VMOffsets` and
9//! `VMComponentOffsets`. That layout is defined exactly once here, via the
10//! higher-order `for_each_vmctx_type!` macro, and each consumer generates its
11//! view of it from that single source of truth.
12
13/// Invoke the given macro `$mac` once, passing it the layout of each of
14/// Wasmtime's "vmctx" types.
15///
16/// This is a higher-order macro: callers define a `macro_rules!` macro that
17/// matches the grammar described below and pass its name as an argument to this
18/// macro's invocation, e.g. `for_each_vmctx_type!(define_vmctx_offsets)`.
19///
20/// # Grammar
21///
22/// The layout below is written in exactly the same grammar that it is handed to
23/// `$mac` in: this macro has a single rule, and that rule does nothing but
24/// forward those tokens along. Writing the layout in the grammar that consumers
25/// match makes it more verbose than a bespoke input syntax would be, but in
26/// exchange there is no normalization pass in between, so what a consumer matches
27/// is exactly what a reader of the layout sees.
28///
29/// `$mac` receives one brace-delimited group per vmctx type:
30///
31/// ```ignore
32/// {
33///     VMContext vmctx
34///     static { ...entries... }
35///     dynamic { ...entries... }
36/// }
37/// ```
38///
39/// where `vmctx` is the name used for accessor methods of that type, `static`
40/// holds the fixed-width prefix whose offsets depend only on the target pointer
41/// size, and `dynamic` holds the rest, whose offsets additionally depend on the
42/// module or component being compiled.
43///
44/// Each entry within a section is a keyword naming the entry's shape followed by
45/// a single delimited group, so that a consumer can iterate over a section with
46/// `$( ...$kind:ident $entry:tt... )*` and dispatch on one whole entry at a time
47/// instead of munching the section token by token. An entry is one of:
48///
49/// * `align { ptr }` or `align { N }`: round the running offset up to the target
50///   pointer size, or to `N` bytes. Alignment is *always* explicit: it is never
51///   derived from a field's type, because the layout being described here does
52///   not necessarily align every field to its natural alignment, and inserting
53///   padding that the layout does not actually have would silently corrupt every
54///   subsequent offset.
55///
56/// * `field { <attrs> <name>: <ty> }`: a single field.
57///
58/// * `array { <attrs> <name>[<count>; <IndexType>]: <ty> }` (`dynamic` only): an
59///   array of `self.<count>` elements, indexed by `<IndexType>`.
60///
61/// * `optional { <attrs> <name>[if <flag>]: <ty> }` (`dynamic` only): a field that
62///   is present when `self.<flag>` is true and absent (zero-sized) otherwise.
63///
64/// A field's type is always the last thing in its entry. Consumers capture it
65/// as a trailing `$($fty:tt)*` and re-dispatch on its tokens only where they
66/// actually need to classify it into a size or a Cranelift type; a `:ty`
67/// capture would be opaque forever, and so could never be classified at
68/// all.
69///
70/// `<attrs>` is a possibly-empty sequence of marker attributes, which consumers
71/// match with `$(# $fattr:tt)*`. They do not affect the layout, but they do affect
72/// the accesses generated for the field:
73///
74/// * `#[aggregate]`: this field is a composite (a nested struct, or an array of
75///   them) rather than a single scalar. Compiled code accesses such a field's
76///   interior piecewise, so there is no one Cranelift type for the field as a
77///   whole and no alias-region accessor is generated for it. Its offset is still
78///   generated, since that is what interior accesses are computed relative to.
79///
80/// * `#[readonly]` and/or `#[can_move]`: describe how Cranelift may treat loads
81///   and stores of this field.
82///
83/// * `#[access_as = Type]`: this field is *declared* with one type (because that
84///   is what determines its size and stride) but *accessed* as another. For
85///   example, the component context's `may_leave` flags are each stored in a
86///   whole `VMGlobalDefinition` but only ever accessed as a `u32`.
87///
88/// * `#[ptr_size_offset]` (`dynamic` only): this field's offset is a function
89///   of the target pointer size alone, even though it lives in the `dynamic`
90///   section, because it and everything before it have sizes that do not depend
91///   on the vmctx's shape. These fields get generated accessors that do not
92///   need to be parameterized over `VMOffsets`, only `GetPtrSize`.
93///
94/// * `#[pointee(<attrs> <Region> as <name> $([<IndexType>])? : <ty>)]`: this
95///   field is a pointer to memory that lives *outside* the vmctx and so is not
96///   part of its layout at all, but that compiled code reaches only by loading
97///   this field first. `<Region>` names the alias region that memory belongs
98///   to, `<name>` is the accessor generated for it, and `<ty>` is the type it is
99///   accessed as. With an `[<IndexType>]`, the pointee is an array of `<ty>`
100///   indexed by `<IndexType>` and each element gets its own alias region;
101///   without one, it is a single `<ty>`. The pointee's `<attrs>` are its own:
102///   how compiled code may treat a load of the pointee is independent of how it
103///   may treat a load of the pointer.
104///
105/// Doc comments are deliberately *not* accepted on fields; use `//` comments for
106/// prose about the layout. Accessor documentation is synthesized from the field
107/// names instead, so that there is only one place a field can be described.
108///
109/// A consumer that only cares about one of the two types can filter with a
110/// literal-name arm followed by a catch-all, e.g.
111///
112/// ```ignore
113/// (@one VMContext $snake:ident dynamic { $($dyn:tt)* }) => { ...generate... };
114/// (@one $other:ident $snake:ident dynamic { $($dyn:tt)* }) => {};
115/// ```
116#[macro_export]
117macro_rules! for_each_vmctx_type {
118    ($mac:ident) => {
119        $mac! {
120            {
121                VMContext vmctx
122
123                // Fixed-width data comes first so that the calculation of these
124                // fields' offsets is a compile-time constant when using
125                // `HostPtr`.
126                static {
127                    field { #[readonly] #[can_move] magic: u32 }
128
129                    // NB: this is where the four bytes of padding after `magic`
130                    // live on targets with eight-byte pointers.
131                    align { ptr }
132
133                    field { #[readonly] #[can_move] store_context: VmPtr<VMStoreContext> }
134
135                    // NB: `VMBuiltinFunctionsArray` is a `repr(C)` struct of
136                    // `unsafe extern "C" fn` fields rather than a true array,
137                    // so its elements are pointer-width and pointer-aligned.
138                    field {
139                        #[readonly]
140                        #[can_move]
141                        #[pointee(
142                            #[readonly]
143                            #[can_move]
144                            BuiltinFunctionsArray as builtin_functions_array[BuiltinFunctionIndex]:
145                                unsafe extern "C" fn
146                        )]
147                        builtin_functions: VmPtr<VMBuiltinFunctionsArray>
148                    }
149
150                    field {
151                        #[pointee(EpochCounter as epoch_counter: AtomicU64)]
152                        epoch_ptr: VmPtr<AtomicU64>
153                    }
154
155                    // A pointer that different collectors use however they see
156                    // fit.
157                    field { #[readonly] #[can_move] gc_heap_data: VmPtr<u8> }
158
159                    field {
160                        #[readonly]
161                        #[can_move]
162                        #[pointee(
163                            #[readonly]
164                            #[can_move]
165                            TypeIdsArray as type_ids_array[ModuleInternedTypeIndex]:
166                                VMSharedTypeIndex
167                        )]
168                        type_ids: VmPtr<VMSharedTypeIndex>
169                    }
170                }
171
172                // Variable-width fields come after the fixed-width fields
173                // above. Memory-related items are placed first as they are some
174                // of the most frequently accessed items, and minimizing their
175                // offset can shrink the size of load/store instruction offset
176                // immediates on platforms like x64 and Pulley (e.g. fit in an
177                // 8-bit offset instead of needing a 32-bit offset).
178                dynamic {
179                    array {
180                        #[aggregate]
181                        imported_memories[num_imported_memories; MemoryIndex]: VMMemoryImport
182                    }
183
184                    array {
185                        #[readonly]
186                        #[can_move]
187                        memories[num_defined_memories; DefinedMemoryIndex]: VmPtr<VMMemoryDefinition>
188                    }
189
190                    array {
191                        #[aggregate]
192                        owned_memories[num_owned_memories; OwnedMemoryIndex]: VMMemoryDefinition
193                    }
194
195                    array {
196                        #[aggregate]
197                        imported_functions[num_imported_functions; FuncIndex]: VMFunctionImport
198                    }
199
200                    array {
201                        #[aggregate]
202                        imported_tables[num_imported_tables; TableIndex]: VMTableImport
203                    }
204
205                    array {
206                        #[aggregate]
207                        imported_globals[num_imported_globals; GlobalIndex]: VMGlobalImport
208                    }
209
210                    array {
211                        #[aggregate]
212                        imported_tags[num_imported_tags; TagIndex]: VMTagImport
213                    }
214
215                    array {
216                        #[aggregate]
217                        tables[num_defined_tables; DefinedTableIndex]: VMTableDefinition
218                    }
219
220                    align { 16 }
221
222                    array {
223                        #[aggregate]
224                        globals[num_defined_globals; DefinedGlobalIndex]: VMGlobalDefinition
225                    }
226
227                    array {
228                        #[aggregate]
229                        tags[num_defined_tags; DefinedTagIndex]: VMTagDefinition
230                    }
231
232                    align { ptr }
233
234                    array {
235                        #[aggregate]
236                        func_refs[num_escaped_funcs; FuncRefIndex]: VMFuncRef
237                    }
238
239                    optional {
240                        #[aggregate]
241                        startup_func_ref[if has_startup_func]: VMFuncRef
242                    }
243
244                    array {
245                        runtime_data_bases[num_runtime_data; RuntimeDataIndex]: VmPtr<u8>
246                    }
247
248                    array {
249                        runtime_data_lengths[num_runtime_data; RuntimeDataIndex]: u32
250                    }
251                }
252            }
253
254            {
255                VMComponentContext vmcomponent
256
257                static {
258                    // NB: `magic` must be at offset zero; this is relied upon by
259                    // `VMComponentContext::from_opaque`.
260                    field { #[readonly] #[can_move] magic: u32 }
261
262                    align { ptr }
263
264                    field {
265                        #[readonly]
266                        #[pointee(
267                            #[readonly]
268                            #[can_move]
269                            ComponentBuiltinFunctionsArray as builtins_array[ComponentBuiltinFunctionIndex]:
270                                unsafe extern "C" fn
271                        )]
272                        builtins: VmPtr<VMComponentBuiltins>
273                    }
274
275                    field { #[readonly] #[can_move] store_context: VmPtr<VMStoreContext> }
276                }
277
278                dynamic {
279                    align { 16 }
280
281                    // Each of these flags gets a whole `VMGlobalDefinition`'s
282                    // worth of space, but only its first four bytes are ever
283                    // accessed.
284                    //
285                    // NB: these flags come first, before any field whose offset
286                    // depends on the component's shape, so that their offsets
287                    // are a function of the target pointer size alone, as marked
288                    // by `#[ptr_size_offset]`. Core Wasm compilation does not
289                    // have the enclosing `VMComponentContext`'s offsets on hand,
290                    // but must still be able to compute these flags' offsets to
291                    // build the alias regions for accessing them.
292
293                    array {
294                        #[ptr_size_offset]
295                        #[access_as = u32]
296                        may_leave[num_runtime_component_instances; RuntimeComponentInstanceIndex]: VMGlobalDefinition
297                    }
298
299                    align { ptr }
300
301                    array {
302                        #[aggregate]
303                        trampoline_func_refs[num_trampolines; TrampolineIndex]: VMFuncRef
304                    }
305
306                    array {
307                        #[aggregate]
308                        intrinsic_func_refs[num_unsafe_intrinsics; UnsafeIntrinsic]: VMFuncRef
309                    }
310
311                    array {
312                        #[aggregate]
313                        lowerings[num_lowerings; LoweredIndex]: VMLowering
314                    }
315
316                    array {
317                        memories
318                            [num_runtime_memories; RuntimeMemoryIndex]: VmPtr<VMMemoryDefinition>
319                    }
320
321                    array {
322                        #[aggregate]
323                        tables[num_runtime_tables; RuntimeTableIndex]: VMTableImport
324                    }
325
326                    array {
327                        reallocs[num_runtime_reallocs; RuntimeReallocIndex]: VmPtr<VMFuncRef>
328                    }
329
330                    array {
331                        callbacks[num_runtime_callbacks; RuntimeCallbackIndex]: VmPtr<VMFuncRef>
332                    }
333
334                    array {
335                        post_returns[num_runtime_post_returns; RuntimePostReturnIndex]: VmPtr<VMFuncRef>
336                    }
337
338                    array {
339                        #[readonly]
340                        resource_destructors[num_resources; ResourceIndex]: VmPtr<VMFuncRef>
341                    }
342                }
343            }
344        }
345    };
346}
347
348/// Round `offset` up to a multiple of `align`.
349#[inline]
350pub(crate) fn align_up(offset: u32, align: u32) -> u32 {
351    debug_assert!(align.is_power_of_two());
352    (offset + (align - 1)) & !(align - 1)
353}
354
355/// Add two offsets, panicking on overflow.
356#[inline]
357pub(crate) fn cadd(a: u32, b: u32) -> u32 {
358    a.checked_add(b).unwrap()
359}
360
361/// Multiply an element count by an element size, panicking on overflow.
362#[inline]
363pub(crate) fn cmul(count: u32, size: u32) -> u32 {
364    count.checked_mul(size).unwrap()
365}
366
367/// Classify one of the field types accepted by `for_each_vmctx_type!` to its
368/// size in bytes as a `u32`, given a `PtrSize`.
369#[allow(
370    unused_macro_rules,
371    reason = "some element types only appear in `VMComponentContext`, and so are \
372              unused when the `component-model` feature is disabled"
373)]
374macro_rules! vmctx_field_size {
375    (($p:expr) u32) => {
376        4u32
377    };
378    (($p:expr) VmPtr < $g:ident >) => {
379        u32::from($p)
380    };
381    // `VMLowering` is a pair of pointers, and is not itself defined by
382    // `for_each_vm_type!`.
383    (($p:expr) VMLowering) => {
384        2u32 * u32::from($p)
385    };
386    // Anything else names one of the `VM*` types, and so has an
387    // `offsets::VMFoo` generated for it by `for_each_vm_type!`. Deferring to
388    // that keeps this macro from having to mirror the list of `VM*` types, and
389    // a type that has no such entry fails to resolve here.
390    (($p:expr) $Name:ident) => {
391        u32::from(crate::vmoffsets::offsets::$Name($p).size())
392    };
393}
394
395/// Classify a `for_each_vmctx_type!` alignment step to its alignment in bytes
396/// as a `u32`, given a `PtrSize`.
397macro_rules! vmctx_align_value {
398    (($p:expr) ptr) => {
399        u32::from($p)
400    };
401    (($p:expr) $n:literal) => {
402        $n
403    };
404}
405
406/// Generate the accessors for, and the layout computation of, the
407/// dynamically-positioned fields of one of the vmctx types.
408#[allow(
409    unused_macro_rules,
410    reason = "single dynamically-positioned fields only appear in \
411              `VMComponentContext`, and so are unused when the `component-model` \
412              feature is disabled"
413)]
414macro_rules! define_vmctx_dynamic_offsets {
415    (@accessors ($s:ident) [ $($kind:ident $entry:tt)* ]) => {
416        $( define_vmctx_dynamic_offsets!(@accessor ($s) $kind $entry); )*
417    };
418
419    (@accessor ($s:ident) align { $al:tt }) => {};
420
421    (@accessor ($s:ident) field { $(# $fattr:tt)* $fname:ident : $($fty:tt)* }) => {
422        #[doc = concat!("The offset of the `", stringify!($fname), "` field.")]
423        #[inline]
424        pub fn $fname(&$s) -> u32 {
425            $s.$fname
426        }
427    };
428
429    (@accessor ($s:ident) array {
430        $(# $fattr:tt)* $fname:ident [ $count:ident ; $Index:ident ] : $($fty:tt)*
431    }) => {
432        #[doc = concat!("The offsets of the `", stringify!($fname), "` array.")]
433        #[inline]
434        pub fn $fname(&$s) -> $crate::ArrayOffsets<$Index> {
435            $crate::ArrayOffsets::new(
436                $s.$fname,
437                vmctx_field_size!(($s.ptr.size()) $($fty)*),
438                $s.$count,
439            )
440        }
441    };
442
443    (@accessor ($s:ident) optional {
444        $(# $fattr:tt)* $fname:ident [ if $flag:ident ] : $($fty:tt)*
445    }) => {
446        #[doc = concat!(
447            "The offset of the `", stringify!($fname), "` field.\n\n",
448            "Panics if `", stringify!($flag), "` is false, in which case this \
449             field is not present at all."
450        )]
451        #[inline]
452        pub fn $fname(&$s) -> u32 {
453            assert!($s.$flag);
454            $s.$fname
455        }
456    };
457
458    (@compute_fn ($s:ident, $next:ident) $snake:ident [ $($kind:ident $entry:tt)* ]) => {
459        /// Compute the offset of each dynamically-positioned field, and this
460        /// vmctx's total size.
461        fn compute_field_offsets(&mut $s) {
462            let mut $next = u32::from($s.ptr.$snake().end_of_static_fields());
463            $( define_vmctx_dynamic_offsets!(@compute ($s, $next) $kind $entry); )*
464            $s.size = $next;
465        }
466    };
467
468    (@compute ($s:ident, $next:ident) align { $al:tt }) => {
469        $next = crate::vmctxtypes::align_up($next, vmctx_align_value!(($s.ptr.size()) $al));
470    };
471
472    (@compute ($s:ident, $next:ident) field {
473        $(# $fattr:tt)* $fname:ident : $($fty:tt)*
474    }) => {
475        $s.$fname = $next;
476        $next = crate::vmctxtypes::cadd($next, vmctx_field_size!(($s.ptr.size()) $($fty)*));
477    };
478
479    (@compute ($s:ident, $next:ident) array {
480        $(# $fattr:tt)* $fname:ident [ $count:ident ; $Index:ident ] : $($fty:tt)*
481    }) => {
482        $s.$fname = $next;
483        $next = crate::vmctxtypes::cadd(
484            $next,
485            crate::vmctxtypes::cmul($s.$count, vmctx_field_size!(($s.ptr.size()) $($fty)*)),
486        );
487    };
488
489    (@compute ($s:ident, $next:ident) optional {
490        $(# $fattr:tt)* $fname:ident [ if $flag:ident ] : $($fty:tt)*
491    }) => {
492        $s.$fname = $next;
493        $next = crate::vmctxtypes::cadd(
494            $next,
495            if $s.$flag {
496                vmctx_field_size!(($s.ptr.size()) $($fty)*)
497            } else {
498                0
499            },
500        );
501    };
502}
503
504/// The offsets of one array field within a vmctx.
505#[derive(Debug, Clone, Copy)]
506pub struct ArrayOffsets<I> {
507    begin: u32,
508    stride: u32,
509    count: u32,
510    _index: core::marker::PhantomData<I>,
511}
512
513impl<I> ArrayOffsets<I> {
514    /// Create the offsets for an array of `count` elements which begins at
515    /// `begin` within its vmctx and whose elements are `stride` bytes apart.
516    #[inline]
517    pub fn new(begin: u32, stride: u32, count: u32) -> Self {
518        ArrayOffsets {
519            begin,
520            stride,
521            count,
522            _index: core::marker::PhantomData,
523        }
524    }
525
526    /// The offset of the start of this array within its vmctx.
527    #[inline]
528    pub fn begin(&self) -> u32 {
529        self.begin
530    }
531
532    /// The number of bytes between the start of consecutive elements of this
533    /// array.
534    #[inline]
535    pub fn stride(&self) -> u32 {
536        self.stride
537    }
538
539    /// The number of elements in this array.
540    #[inline]
541    pub fn count(&self) -> u32 {
542        self.count
543    }
544}
545
546impl<I: VmctxArrayIndex> ArrayOffsets<I> {
547    /// The offset of the given element within this array's vmctx.
548    ///
549    /// Panics if `index` is out of bounds for this array.
550    #[inline]
551    pub fn at(&self, index: I) -> u32 {
552        let index = index.vmctx_array_index();
553        assert!(index < self.count);
554        self.begin + index * self.stride
555    }
556}
557
558/// An index type that can be used to index one of a vmctx's arrays.
559pub trait VmctxArrayIndex: Copy {
560    /// This index's position within its array.
561    fn vmctx_array_index(self) -> u32;
562}
563
564/// Implement `VmctxArrayIndex` for entity references.
565macro_rules! impl_vmctx_array_index {
566    ($($ty:ty),* $(,)?) => {
567        $(
568            impl $crate::VmctxArrayIndex for $ty {
569                #[inline]
570                fn vmctx_array_index(self) -> u32 {
571                    self.as_u32()
572                }
573            }
574        )*
575    };
576}