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 array {
233 #[aggregate]
234 func_refs[num_escaped_funcs; FuncRefIndex]: VMFuncRef
235 }
236
237 optional {
238 #[aggregate]
239 startup_func_ref[if has_startup_func]: VMFuncRef
240 }
241
242 array {
243 runtime_data_bases[num_runtime_data; RuntimeDataIndex]: VmPtr<u8>
244 }
245
246 array {
247 runtime_data_lengths[num_runtime_data; RuntimeDataIndex]: u32
248 }
249 }
250 }
251
252 {
253 VMComponentContext vmcomponent
254
255 static {
256 // NB: `magic` must be at offset zero; this is relied upon by
257 // `VMComponentContext::from_opaque`.
258 field { #[readonly] #[can_move] magic: u32 }
259
260 align { ptr }
261
262 field {
263 #[readonly]
264 #[pointee(
265 #[readonly]
266 #[can_move]
267 ComponentBuiltinFunctionsArray as builtins_array[ComponentBuiltinFunctionIndex]:
268 unsafe extern "C" fn
269 )]
270 builtins: VmPtr<VMComponentBuiltins>
271 }
272
273 field { #[readonly] #[can_move] store_context: VmPtr<VMStoreContext> }
274 }
275
276 dynamic {
277 align { 16 }
278
279 // Each of these flags gets a whole `VMGlobalDefinition`'s
280 // worth of space, but only its first four bytes are ever
281 // accessed.
282 //
283 // NB: these flags come first, before any field whose offset
284 // depends on the component's shape, so that their offsets
285 // are a function of the target pointer size alone, as marked
286 // by `#[ptr_size_offset]`. Core Wasm compilation does not
287 // have the enclosing `VMComponentContext`'s offsets on hand,
288 // but must still be able to compute these flags' offsets to
289 // build the alias regions for accessing them.
290
291 array {
292 #[ptr_size_offset]
293 #[access_as = u32]
294 may_leave[num_runtime_component_instances; RuntimeComponentInstanceIndex]: VMGlobalDefinition
295 }
296
297 align { ptr }
298
299 array {
300 #[aggregate]
301 trampoline_func_refs[num_trampolines; TrampolineIndex]: VMFuncRef
302 }
303
304 array {
305 #[aggregate]
306 intrinsic_func_refs[num_unsafe_intrinsics; UnsafeIntrinsic]: VMFuncRef
307 }
308
309 array {
310 #[aggregate]
311 lowerings[num_lowerings; LoweredIndex]: VMLowering
312 }
313
314 array {
315 memories
316 [num_runtime_memories; RuntimeMemoryIndex]: VmPtr<VMMemoryDefinition>
317 }
318
319 array {
320 #[aggregate]
321 tables[num_runtime_tables; RuntimeTableIndex]: VMTableImport
322 }
323
324 array {
325 reallocs[num_runtime_reallocs; RuntimeReallocIndex]: VmPtr<VMFuncRef>
326 }
327
328 array {
329 callbacks[num_runtime_callbacks; RuntimeCallbackIndex]: VmPtr<VMFuncRef>
330 }
331
332 array {
333 post_returns[num_runtime_post_returns; RuntimePostReturnIndex]: VmPtr<VMFuncRef>
334 }
335
336 array {
337 #[readonly]
338 resource_destructors[num_resources; ResourceIndex]: VmPtr<VMFuncRef>
339 }
340 }
341 }
342 }
343 };
344}
345
346/// Round `offset` up to a multiple of `align`.
347#[inline]
348pub(crate) fn align_up(offset: u32, align: u32) -> u32 {
349 debug_assert!(align.is_power_of_two());
350 (offset + (align - 1)) & !(align - 1)
351}
352
353/// Add two offsets, panicking on overflow.
354#[inline]
355pub(crate) fn cadd(a: u32, b: u32) -> u32 {
356 a.checked_add(b).unwrap()
357}
358
359/// Multiply an element count by an element size, panicking on overflow.
360#[inline]
361pub(crate) fn cmul(count: u32, size: u32) -> u32 {
362 count.checked_mul(size).unwrap()
363}
364
365/// Classify one of the field types accepted by `for_each_vmctx_type!` to its
366/// size in bytes as a `u32`, given a `PtrSize`.
367#[allow(
368 unused_macro_rules,
369 reason = "some element types only appear in `VMComponentContext`, and so are \
370 unused when the `component-model` feature is disabled"
371)]
372macro_rules! vmctx_field_size {
373 (($p:expr) u32) => {
374 4u32
375 };
376 (($p:expr) VmPtr < $g:ident >) => {
377 u32::from($p)
378 };
379 // `VMLowering` is a pair of pointers, and is not itself defined by
380 // `for_each_vm_type!`.
381 (($p:expr) VMLowering) => {
382 2u32 * u32::from($p)
383 };
384 // Anything else names one of the `VM*` types, and so has an
385 // `offsets::VMFoo` generated for it by `for_each_vm_type!`. Deferring to
386 // that keeps this macro from having to mirror the list of `VM*` types, and
387 // a type that has no such entry fails to resolve here.
388 (($p:expr) $Name:ident) => {
389 u32::from(crate::vmoffsets::offsets::$Name($p).size())
390 };
391}
392
393/// Classify a `for_each_vmctx_type!` alignment step to its alignment in bytes
394/// as a `u32`, given a `PtrSize`.
395macro_rules! vmctx_align_value {
396 (($p:expr) ptr) => {
397 u32::from($p)
398 };
399 (($p:expr) $n:literal) => {
400 $n
401 };
402}
403
404/// Generate the accessors for, and the layout computation of, the
405/// dynamically-positioned fields of one of the vmctx types.
406#[allow(
407 unused_macro_rules,
408 reason = "single dynamically-positioned fields only appear in \
409 `VMComponentContext`, and so are unused when the `component-model` \
410 feature is disabled"
411)]
412macro_rules! define_vmctx_dynamic_offsets {
413 (@accessors ($s:ident) [ $($kind:ident $entry:tt)* ]) => {
414 $( define_vmctx_dynamic_offsets!(@accessor ($s) $kind $entry); )*
415 };
416
417 (@accessor ($s:ident) align { $al:tt }) => {};
418
419 (@accessor ($s:ident) field { $(# $fattr:tt)* $fname:ident : $($fty:tt)* }) => {
420 #[doc = concat!("The offset of the `", stringify!($fname), "` field.")]
421 #[inline]
422 pub fn $fname(&$s) -> u32 {
423 $s.$fname
424 }
425 };
426
427 (@accessor ($s:ident) array {
428 $(# $fattr:tt)* $fname:ident [ $count:ident ; $Index:ident ] : $($fty:tt)*
429 }) => {
430 #[doc = concat!("The offsets of the `", stringify!($fname), "` array.")]
431 #[inline]
432 pub fn $fname(&$s) -> $crate::ArrayOffsets<$Index> {
433 $crate::ArrayOffsets::new(
434 $s.$fname,
435 vmctx_field_size!(($s.ptr.size()) $($fty)*),
436 $s.$count,
437 )
438 }
439 };
440
441 (@accessor ($s:ident) optional {
442 $(# $fattr:tt)* $fname:ident [ if $flag:ident ] : $($fty:tt)*
443 }) => {
444 #[doc = concat!(
445 "The offset of the `", stringify!($fname), "` field.\n\n",
446 "Panics if `", stringify!($flag), "` is false, in which case this \
447 field is not present at all."
448 )]
449 #[inline]
450 pub fn $fname(&$s) -> u32 {
451 assert!($s.$flag);
452 $s.$fname
453 }
454 };
455
456 (@compute_fn ($s:ident, $next:ident) $snake:ident [ $($kind:ident $entry:tt)* ]) => {
457 /// Compute the offset of each dynamically-positioned field, and this
458 /// vmctx's total size.
459 fn compute_field_offsets(&mut $s) {
460 let mut $next = u32::from($s.ptr.$snake().end_of_static_fields());
461 $( define_vmctx_dynamic_offsets!(@compute ($s, $next) $kind $entry); )*
462 $s.size = $next;
463 }
464 };
465
466 (@compute ($s:ident, $next:ident) align { $al:tt }) => {
467 $next = crate::vmctxtypes::align_up($next, vmctx_align_value!(($s.ptr.size()) $al));
468 };
469
470 (@compute ($s:ident, $next:ident) field {
471 $(# $fattr:tt)* $fname:ident : $($fty:tt)*
472 }) => {
473 $s.$fname = $next;
474 $next = crate::vmctxtypes::cadd($next, vmctx_field_size!(($s.ptr.size()) $($fty)*));
475 };
476
477 (@compute ($s:ident, $next:ident) array {
478 $(# $fattr:tt)* $fname:ident [ $count:ident ; $Index:ident ] : $($fty:tt)*
479 }) => {
480 $s.$fname = $next;
481 $next = crate::vmctxtypes::cadd(
482 $next,
483 crate::vmctxtypes::cmul($s.$count, vmctx_field_size!(($s.ptr.size()) $($fty)*)),
484 );
485 };
486
487 (@compute ($s:ident, $next:ident) optional {
488 $(# $fattr:tt)* $fname:ident [ if $flag:ident ] : $($fty:tt)*
489 }) => {
490 $s.$fname = $next;
491 $next = crate::vmctxtypes::cadd(
492 $next,
493 if $s.$flag {
494 vmctx_field_size!(($s.ptr.size()) $($fty)*)
495 } else {
496 0
497 },
498 );
499 };
500}
501
502/// The offsets of one array field within a vmctx.
503#[derive(Debug, Clone, Copy)]
504pub struct ArrayOffsets<I> {
505 begin: u32,
506 stride: u32,
507 count: u32,
508 _index: core::marker::PhantomData<I>,
509}
510
511impl<I> ArrayOffsets<I> {
512 /// Create the offsets for an array of `count` elements which begins at
513 /// `begin` within its vmctx and whose elements are `stride` bytes apart.
514 #[inline]
515 pub fn new(begin: u32, stride: u32, count: u32) -> Self {
516 ArrayOffsets {
517 begin,
518 stride,
519 count,
520 _index: core::marker::PhantomData,
521 }
522 }
523
524 /// The offset of the start of this array within its vmctx.
525 #[inline]
526 pub fn begin(&self) -> u32 {
527 self.begin
528 }
529
530 /// The number of bytes between the start of consecutive elements of this
531 /// array.
532 #[inline]
533 pub fn stride(&self) -> u32 {
534 self.stride
535 }
536
537 /// The number of elements in this array.
538 #[inline]
539 pub fn count(&self) -> u32 {
540 self.count
541 }
542}
543
544impl<I: VmctxArrayIndex> ArrayOffsets<I> {
545 /// The offset of the given element within this array's vmctx.
546 ///
547 /// Panics if `index` is out of bounds for this array.
548 #[inline]
549 pub fn at(&self, index: I) -> u32 {
550 let index = index.vmctx_array_index();
551 assert!(index < self.count);
552 self.begin + index * self.stride
553 }
554}
555
556/// An index type that can be used to index one of a vmctx's arrays.
557pub trait VmctxArrayIndex: Copy {
558 /// This index's position within its array.
559 fn vmctx_array_index(self) -> u32;
560}
561
562/// Implement `VmctxArrayIndex` for entity references.
563macro_rules! impl_vmctx_array_index {
564 ($($ty:ty),* $(,)?) => {
565 $(
566 impl $crate::VmctxArrayIndex for $ty {
567 #[inline]
568 fn vmctx_array_index(self) -> u32 {
569 self.as_u32()
570 }
571 }
572 )*
573 };
574}