Skip to main content

for_each_vmctx_type

Macro for_each_vmctx_type 

Source
macro_rules! for_each_vmctx_type {
    ($mac:ident) => { ... };
}
Expand description

Invoke the given macro $mac once, passing it the layout of each of Wasmtime’s “vmctx” types.

This is a higher-order macro: callers define a macro_rules! macro that matches the grammar described below and pass its name as an argument to this macro’s invocation, e.g. for_each_vmctx_type!(define_vmctx_offsets).

§Grammar

The layout below is written in exactly the same grammar that it is handed to $mac in: this macro has a single rule, and that rule does nothing but forward those tokens along. Writing the layout in the grammar that consumers match makes it more verbose than a bespoke input syntax would be, but in exchange there is no normalization pass in between, so what a consumer matches is exactly what a reader of the layout sees.

$mac receives one brace-delimited group per vmctx type:

{
    VMContext vmctx
    static { ...entries... }
    dynamic { ...entries... }
}

where vmctx is the name used for accessor methods of that type, static holds the fixed-width prefix whose offsets depend only on the target pointer size, and dynamic holds the rest, whose offsets additionally depend on the module or component being compiled.

Each entry within a section is a keyword naming the entry’s shape followed by a single delimited group, so that a consumer can iterate over a section with $( ...$kind:ident $entry:tt... )* and dispatch on one whole entry at a time instead of munching the section token by token. An entry is one of:

  • align { ptr } or align { N }: round the running offset up to the target pointer size, or to N bytes. Alignment is always explicit: it is never derived from a field’s type, because the layout being described here does not necessarily align every field to its natural alignment, and inserting padding that the layout does not actually have would silently corrupt every subsequent offset.

  • field { <attrs> <name>: <ty> }: a single field.

  • array { <attrs> <name>[<count>; <IndexType>]: <ty> } (dynamic only): an array of self.<count> elements, indexed by <IndexType>.

  • optional { <attrs> <name>[if <flag>]: <ty> } (dynamic only): a field that is present when self.<flag> is true and absent (zero-sized) otherwise.

A field’s type is always the last thing in its entry. Consumers capture it as a trailing $($fty:tt)* and re-dispatch on its tokens only where they actually need to classify it into a size or a Cranelift type; a :ty capture would be opaque forever, and so could never be classified at all.

<attrs> is a possibly-empty sequence of marker attributes, which consumers match with $(# $fattr:tt)*. They do not affect the layout, but they do affect the accesses generated for the field:

  • #[aggregate]: this field is a composite (a nested struct, or an array of them) rather than a single scalar. Compiled code accesses such a field’s interior piecewise, so there is no one Cranelift type for the field as a whole and no alias-region accessor is generated for it. Its offset is still generated, since that is what interior accesses are computed relative to.

  • #[readonly] and/or #[can_move]: describe how Cranelift may treat loads and stores of this field.

  • #[access_as = Type]: this field is declared with one type (because that is what determines its size and stride) but accessed as another. For example, the component context’s may_leave flags are each stored in a whole VMGlobalDefinition but only ever accessed as a u32.

Doc comments are deliberately not accepted on fields; use // comments for prose about the layout. Accessor documentation is synthesized from the field names instead, so that there is only one place a field can be described.

A consumer that only cares about one of the two types can filter with a literal-name arm followed by a catch-all, e.g.

(@one VMContext $snake:ident dynamic { $($dyn:tt)* }) => { ...generate... };
(@one $other:ident $snake:ident dynamic { $($dyn:tt)* }) => {};