Skip to main content

wasmtime_environ/
vmtypes.rs

1//! Centralized definitions of the various `VM*` types whose layout is shared
2//! between the runtime (which uses the actual structures) and the compiler
3//! (which uses the types' offsets and has per-type alias regions).
4//!
5//! To keep these in sync, the shape of each type is defined exactly once here,
6//! via the higher-order [`for_each_vm_type!`] macro, and each consumer
7//! generates its view of the type from that single source of truth.
8
9/// Invoke the given macro `$mac` once, passing it the definitions of each of the
10/// `VM*` types whose layout is shared between the runtime, compilation offsets,
11/// and Cranelift alias regions.
12///
13/// This is a higher-order macro: callers define a `macro_rules!` macro that
14/// matches the grammar defined below and pass its name as an argument to this
15/// macro's invocation, e.g. `for_each_vm_type!(define_vm_types)`.
16///
17/// # Grammar
18///
19/// Each type is emitted as a struct definition preceded by:
20///
21/// * Doc-comment attributes (`#[doc = "..."]`).
22///
23/// * An optional `#[derive(...)]` attribute.
24///
25/// * A `#[repr(...)]` attribute.
26///
27/// * A `#[snake_name = <ident>]` attribute giving the type's name in
28///   `snake_case`, used to generate accessor method names.
29///
30/// Each field may be preceded by doc-comment attributes and, optionally, these
31/// marker attributes, in this order:
32///
33/// * `#[aggregate]`: this field is a composite (a nested struct or array)
34///   rather than a single scalar. Compiled Wasm code accesses such a field's
35///   interior piecewise, so there is no one Cranelift type for the field as a
36///   whole and no alias-region accessor is generated for it. The field's offset
37///   is still generated, since that is what interior accesses are computed
38///   relative to.
39///
40/// * `#[readonly]` and/or `#[can_move]`: describe how Cranelift may treat loads
41///   and stores of that field.
42#[macro_export]
43macro_rules! for_each_vm_type {
44    ($mac:ident) => {
45        $mac! {
46            /// The fields compiled code needs to access to utilize a WebAssembly linear
47            /// memory defined within the instance, namely the start address and the
48            /// size in bytes.
49            #[derive(Debug)]
50            #[repr(C)]
51            #[snake_name = vm_memory_definition]
52            pub struct VMMemoryDefinition {
53                /// The start address.
54                pub base: VmPtr<u8>,
55
56                /// The current logical size of this linear memory in bytes.
57                ///
58                /// This is atomic because shared memories must be able to grow their length
59                /// atomically. For relaxed access, see
60                /// [`VMMemoryDefinition::current_length()`].
61                pub current_length: AtomicUsize,
62            }
63
64            /// The fields compiled code needs to access to utilize a WebAssembly table
65            /// defined within the instance.
66            #[derive(Debug, Copy, Clone)]
67            #[repr(C)]
68            #[snake_name = vm_table_definition]
69            pub struct VMTableDefinition {
70                /// Pointer to the table data.
71                pub base: VmPtr<u8>,
72
73                /// The current number of elements in the table.
74                pub current_elements: usize,
75            }
76
77            /// The storage for a WebAssembly global defined within the instance.
78            ///
79            /// TODO: Pack the globals more densely, rather than using the same size
80            /// for every type.
81            #[derive(Debug)]
82            #[repr(C, align(16))]
83            #[snake_name = vm_global_definition]
84            pub struct VMGlobalDefinition {
85                /// The raw storage for a global's value.
86                storage: [u8; 16],
87            }
88
89            /// A WebAssembly tag defined within the instance.
90            #[derive(Debug)]
91            #[repr(C)]
92            #[snake_name = vm_tag_definition]
93            pub struct VMTagDefinition {
94                /// Function signature's type id.
95                pub type_index: VMSharedTypeIndex,
96            }
97
98            /// The VM caller-checked "funcref" record, for caller-side signature checking.
99            ///
100            /// It consists of function pointer(s), a type id to be checked by the
101            /// caller, and the vmctx closure associated with this function.
102            #[derive(Debug, Clone)]
103            #[repr(C)]
104            #[snake_name = vm_func_ref]
105            pub struct VMFuncRef {
106                /// Function pointer for this funcref if being called via the "array"
107                /// calling convention that `Func::new` et al use.
108                pub array_call: VmPtr<VMArrayCallFunction>,
109
110                /// Function pointer for this funcref if being called via the calling
111                /// convention we use when compiling Wasm.
112                ///
113                /// Most functions come with a function pointer that we can use when they
114                /// are called from Wasm. The notable exception is when we `Func::wrap` a
115                /// host function, and we don't have a Wasm compiler on hand to compile a
116                /// Wasm-to-native trampoline for the function. In this case, we leave
117                /// `wasm_call` empty until the function is passed as an import to Wasm (or
118                /// otherwise exposed to Wasm via tables/globals). At this point, we look up
119                /// a Wasm-to-native trampoline for the function in the Wasm's compiled
120                /// module and use that fill in `VMFunctionImport::wasm_call`. **However**
121                /// there is no guarantee that the Wasm module has a trampoline for this
122                /// function's signature. The Wasm module only has trampolines for its
123                /// types, and if this function isn't of one of those types, then the Wasm
124                /// module will not have a trampoline for it. This is actually okay, because
125                /// it means that the Wasm cannot actually call this function. But it does
126                /// mean that this field needs to be an `Option` even though it is non-null
127                /// the vast vast vast majority of the time.
128                pub wasm_call: Option<VmPtr<VMWasmCallFunction>>,
129
130                /// Function signature's type id.
131                pub type_index: VMSharedTypeIndex,
132
133                /// The VM state associated with this function.
134                ///
135                /// The actual definition of what this pointer points to depends on the
136                /// function being referenced: for core Wasm functions, this is a `*mut
137                /// VMContext`, for host functions it is a `*mut VMHostFuncContext`, and for
138                /// component functions it is a `*mut VMComponentContext`.
139                pub vmctx: VmPtr<VMOpaqueContext>,
140            }
141
142            /// An imported function.
143            ///
144            /// Basically the same as `VMFuncRef`, except that `wasm_call` is not optional.
145            #[derive(Debug, Clone)]
146            #[repr(C)]
147            #[snake_name = vm_function_import]
148            pub struct VMFunctionImport {
149                /// Same as `VMFuncRef::array_call`.
150                #[readonly]
151                #[can_move]
152                pub array_call: VmPtr<VMArrayCallFunction>,
153
154                /// Same as `VMFuncRef::wasm_call`, except always non-null. Must be filled
155                /// in by the time Wasm is importing this function!
156                #[readonly]
157                #[can_move]
158                pub wasm_call: VmPtr<VMWasmCallFunction>,
159
160                /// Function signature's _actual_ type id.
161                ///
162                /// This is the type that the function was defined with, not the type that
163                /// it was imported as. These two can be different in the face of subtyping
164                /// and we need the former for to correctly implement dynamic downcasts.
165                #[readonly]
166                #[can_move]
167                pub type_index: VMSharedTypeIndex,
168
169                /// Same as `VMFuncRef::vmctx`.
170                #[readonly]
171                #[can_move]
172                pub vmctx: VmPtr<VMOpaqueContext>,
173            }
174
175            /// The fields compiled code needs to access to utilize a WebAssembly table
176            /// imported from another instance.
177            #[derive(Debug, Copy, Clone)]
178            #[repr(C)]
179            #[snake_name = vm_table_import]
180            pub struct VMTableImport {
181                /// A pointer to the imported table description.
182                #[readonly]
183                #[can_move]
184                pub from: VmPtr<VMTableDefinition>,
185
186                /// A pointer to the `VMContext` that owns the table description.
187                #[readonly]
188                #[can_move]
189                pub vmctx: VmPtr<VMContext>,
190
191                /// The table index, within `vmctx`, this definition resides at.
192                #[readonly]
193                #[can_move]
194                pub index: DefinedTableIndex,
195            }
196
197            /// The fields compiled code needs to access to utilize a WebAssembly linear
198            /// memory imported from another instance.
199            #[derive(Debug, Copy, Clone)]
200            #[repr(C)]
201            #[snake_name = vm_memory_import]
202            pub struct VMMemoryImport {
203                /// A pointer to the imported memory description.
204                #[readonly]
205                #[can_move]
206                pub from: VmPtr<VMMemoryDefinition>,
207
208                /// A pointer to the `VMContext` that owns the memory description.
209                #[readonly]
210                #[can_move]
211                pub vmctx: VmPtr<VMContext>,
212
213                /// The index of the memory in the containing `vmctx`.
214                #[readonly]
215                #[can_move]
216                pub index: DefinedMemoryIndex,
217            }
218
219            /// The fields compiled code needs to access to utilize a WebAssembly global
220            /// variable imported from another instance.
221            ///
222            /// Note that unlike with functions, tables, and memories, `VMGlobalImport`
223            /// doesn't include a `vmctx` pointer. Globals are never resized, and don't
224            /// require a `vmctx` pointer to access.
225            #[derive(Debug, Copy, Clone)]
226            #[repr(C)]
227            #[snake_name = vm_global_import]
228            pub struct VMGlobalImport {
229                /// A pointer to the imported global variable description.
230                #[readonly]
231                #[can_move]
232                pub from: VmPtr<VMGlobalDefinition>,
233
234                /// A pointer to the context that owns the global.
235                ///
236                /// Exactly what's stored here is dictated by `kind` below. This is `None`
237                /// for `VMGlobalKind::Host`, it's a `VMContext` for
238                /// `VMGlobalKind::Instance`, and it's `VMComponentContext` for
239                /// `VMGlobalKind::ComponentFlags`.
240                #[readonly]
241                #[can_move]
242                pub vmctx: Option<VmPtr<VMOpaqueContext>>,
243
244                /// The kind of global, and extra location information in addition to
245                /// `vmctx` above.
246                #[readonly]
247                #[can_move]
248                pub kind: VMGlobalKind,
249            }
250
251            /// The fields compiled code needs to access to utilize a WebAssembly
252            /// tag imported from another instance.
253            #[derive(Debug, Copy, Clone)]
254            #[repr(C)]
255            #[snake_name = vm_tag_import]
256            pub struct VMTagImport {
257                /// A pointer to the imported tag description.
258                #[readonly]
259                #[can_move]
260                pub from: VmPtr<VMTagDefinition>,
261
262                /// The instance that owns this tag.
263                #[readonly]
264                #[can_move]
265                pub vmctx: VmPtr<VMContext>,
266
267                /// The index of the tag in the containing `vmctx`.
268                #[readonly]
269                #[can_move]
270                pub index: DefinedTagIndex,
271            }
272
273            /// Structure that holds all mutable context that is shared across all instances
274            /// in a store, for example data related to fuel or epochs.
275            ///
276            /// `VMStoreContext`s are one-to-one with `wasmtime::Store`s, the same way that
277            /// `VMContext`s are one-to-one with `wasmtime::Instance`s. And the same way
278            /// that multiple `wasmtime::Instance`s may be associated with the same
279            /// `wasmtime::Store`, multiple `VMContext`s hold a pointer to the same
280            /// `VMStoreContext` when they are associated with the same `wasmtime::Store`.
281            #[derive(Debug)]
282            // NB: `align(8)` is forced rather than inferred because the i386
283            // System V ABI aligns 64-bit integers to 4 bytes, and `VMOffsets`
284            // can't tell that target apart from the ones that align them to 8,
285            // since it only knows the target's pointer width.
286            #[repr(C, align(8))]
287            #[snake_name = vm_store_context]
288            pub struct VMStoreContext {
289                // NB: 64-bit integer fields are located first with pointer-sized fields
290                // trailing afterwards. That makes the offsets in this structure easier to
291                // calculate on 32-bit platforms as we don't have to worry about the
292                // alignment of 64-bit integers.
293                //
294                /// Indicator of how much fuel has been consumed and is remaining to
295                /// WebAssembly.
296                ///
297                /// This field is typically negative and increments towards positive. Upon
298                /// turning positive a wasm trap will be generated. This field is only
299                /// modified if wasm is configured to consume fuel.
300                pub fuel_consumed: UnsafeCell<i64>,
301
302                /// Deadline epoch for interruption: if epoch-based interruption
303                /// is enabled and the global (per engine) epoch counter is
304                /// observed to reach or exceed this value, the guest code will
305                /// yield if running asynchronously.
306                pub epoch_deadline: UnsafeCell<u64>,
307
308                /// The "store version".
309                ///
310                /// This is used to test whether stack-frame handles referring to
311                /// suspended stack frames remain valid.
312                ///
313                /// The invariant that this upward-counting number must satisfy
314                /// is: the number must be incremented whenever execution starts
315                /// or resumes in the `Store` or when any stack is
316                /// dropped/freed. That way, if we take a reference to some
317                /// suspended stack frame and track the "version" at the time we
318                /// took that reference, if the version still matches, we can be
319                /// sure that nothing could have unwound the referenced Wasm
320                /// frame.
321                ///
322                /// This version number is incremented in exactly one place: the
323                /// Wasm-to-host trampolines, after return from host code. Note
324                /// that this captures both the normal "return into Wasm" case
325                /// (where Wasm frames can subsequently return normally and thus
326                /// invalidate frames), and the "trap/exception unwinds Wasm
327                /// frames" case, which is done internally via the `raise` libcall
328                /// invoked after the main hostcall returns an error, and after we
329                /// increment this version number.
330                ///
331                /// Note that this also handles the fiber/future-drop case because
332                /// because we *always* return into the trampoline to clean up;
333                /// that trampoline immediately raises an error and uses the
334                /// longjmp-like unwind within Cranelift frames to skip over all
335                /// the guest Wasm frames, but not before it increments the
336                /// store's execution version number.
337                ///
338                /// This field is in use only if guest debugging is enabled.
339                pub execution_version: u64,
340
341                /// Current stack limit of the wasm module.
342                ///
343                /// For more information see `crates/cranelift/src/lib.rs`.
344                pub stack_limit: UnsafeCell<usize>,
345
346                /// The `VMMemoryDefinition` for this store's GC heap.
347                #[aggregate]
348                pub gc_heap: UnsafeCell<VMMemoryDefinition>,
349
350                /// The value of the frame pointer register in the trampoline used
351                /// to call from Wasm to the host.
352                ///
353                /// Maintained by our Wasm-to-host trampoline, and cleared just
354                /// before calling into Wasm in `catch_traps`.
355                ///
356                /// This member is `0` when Wasm is actively running and has not called out
357                /// to the host.
358                ///
359                /// Used to find the start of a contiguous sequence of Wasm frames
360                /// when walking the stack. Note that we record the FP of the
361                /// *trampoline*'s frame, not the last Wasm frame, because we need
362                /// to know the SP (bottom of frame) of the last Wasm frame as
363                /// well in case we need to resume to an exception handler in that
364                /// frame. The FP of the last Wasm frame can be recovered by
365                /// loading the saved FP value at this FP address.
366                pub last_wasm_exit_trampoline_fp: UnsafeCell<usize>,
367
368                /// The last Wasm program counter before we called from Wasm to the host.
369                ///
370                /// Maintained by our Wasm-to-host trampoline, and cleared just before
371                /// calling into Wasm in `catch_traps`.
372                ///
373                /// This member is `0` when Wasm is actively running and has not called out
374                /// to the host.
375                ///
376                /// Used when walking a contiguous sequence of Wasm frames.
377                pub last_wasm_exit_pc: UnsafeCell<usize>,
378
379                /// The last host stack pointer before we called into Wasm from the host.
380                ///
381                /// Maintained by our host-to-Wasm trampoline. This member is `0` when Wasm
382                /// is not running, and it's set to nonzero once a host-to-wasm trampoline
383                /// is executed.
384                ///
385                /// When a host function is wrapped into a `wasmtime::Func`, and is then
386                /// called from the host, then this member is not changed meaning that the
387                /// previous activation in pointed to by `last_wasm_exit_trampoline_fp` is
388                /// still the last wasm set of frames on the stack.
389                ///
390                /// This field is saved/restored during fiber suspension/resumption
391                /// resumption as part of `CallThreadState::swap`.
392                ///
393                /// This field is used to find the end of a contiguous sequence of Wasm
394                /// frames when walking the stack. Additionally it's used when a trap is
395                /// raised as part of the set of parameters used to resume in the entry
396                /// trampoline's "catch" block.
397                pub last_wasm_entry_sp: UnsafeCell<usize>,
398
399                /// Same as `last_wasm_entry_sp`, but for the `fp` of the trampoline.
400                pub last_wasm_entry_fp: UnsafeCell<usize>,
401
402                /// The last trap handler from a host-to-wasm entry trampoline on the stack.
403                ///
404                /// This field is configured when the host calls into wasm by the trampoline
405                /// itself. It stores the `pc` of an exception handler suitable to handle
406                /// all traps (or uncaught exceptions).
407                pub last_wasm_entry_trap_handler: UnsafeCell<usize>,
408
409                /// Stack information used by stack switching instructions. See documentation
410                /// on `VMStackChain` for details.
411                #[aggregate]
412                pub stack_chain: UnsafeCell<VMStackChain>,
413
414                /// A pointer to the embedder's `T` inside a `Store<T>`, for use with the
415                /// `store-data-address` unsafe intrinsic.
416                pub store_data: VmPtr<()>,
417
418                /// The range, in addresses, of the guard page that is currently in use.
419                ///
420                /// This field is used when signal handlers are run to determine whether a
421                /// faulting address lies within the guard page of an async stack for
422                /// example. If this happens then the signal handler aborts with a stack
423                /// overflow message similar to what would happen had the stack overflow
424                /// happened on the main thread. This field is, by default a null..null
425                /// range indicating that no async guard is in use (aka no fiber). In such a
426                /// situation while this field is read it'll never classify a fault as an
427                /// guard page fault.
428                #[aggregate]
429                pub async_guard_range: Range<*mut u8>,
430
431                /// The `context.{get,set}` values for the current thread in the component
432                /// model. This is only used for `component-model-async` and slot[1] is only
433                /// used for `component-model-threading`. Despite the conditional use nature
434                /// this is unconditionally present as it avoids the need to make logic in
435                /// `VMOffsets` conditional.
436                ///
437                /// This is saved/restored when threads are swapped in the component model.
438                ///
439                /// NB: `UnsafeCell` because JIT code writes to the slots.
440                #[aggregate]
441                pub component_context: UnsafeCell<[u32; NUM_COMPONENT_CONTEXT_SLOTS]>,
442
443                /// JIT-visible current thread for the component model's sync-to-sync
444                /// adapter fast path.
445                ///
446                /// Like `component_context`, this is unconditionally present to keep
447                /// `VMOffsets` logic unconditional even though it is only used when
448                /// `component-model-async` is enabled.
449                ///
450                /// NB: `UnsafeCell` because JIT code writes to this field.
451                pub current_thread: UnsafeCell<VMLazyThread>,
452            }
453
454            /// JIT-visible representation of the store's current thread for the component
455            /// model, encoded as a single pointer-sized integer so that generated JIT code
456            /// can load, store, and compare it with a handful of instructions.
457            ///
458            /// This is the inline fast-path counterpart to the host-side `CurrentThread`: a
459            /// fused sync-to-sync adapter records a lazy deferred thread here (a pointer to
460            /// a `VMDeferredThread` on its own stack frame) instead of eagerly allocating a
461            /// `GuestTask`/`GuestThread` in the host. Host code promotes the deferred
462            /// thread into a real one only when it actually needs it; see
463            /// `StoreOpaque::force_current_thread`.
464            ///
465            /// This type is a bitpacked equivalent of the following logical `enum`:
466            ///
467            /// ```ignore
468            /// enum VMLazyThread {
469            ///     /// No thread.
470            ///     None,
471            ///
472            ///     /// The lazy thread was promoted and materialized; get it from
473            ///     /// `ConcurrentState::current_thread`.
474            ///     Forced,
475            ///
476            ///     /// The lazy thread has not been materialized, here is a pointer to the
477            ///     /// stack-allocated data needed to do force that promotion.
478            ///     Deferred(*mut VMDeferredThread),
479            /// }
480            /// ```
481            ///
482            /// Bitpacking details:
483            ///
484            /// * `None`: `0`
485            ///
486            /// * `Forced`: A non-zero value with its low-bit set.
487            ///
488            /// * `Deferred`: A non-zero value with its low-bit clear.
489            #[derive(Debug, Copy, Clone, PartialEq, Eq)]
490            #[repr(transparent)]
491            #[snake_name = vm_lazy_thread]
492            pub struct VMLazyThread {
493                /// The bitpacked thread representation described above.
494                ///
495                /// Private: use the `VMLazyThread::{none,forced,deferred}` constructors
496                /// and the `is_*`/`as_deferred` accessors instead of touching this
497                /// directly.
498                thread: Option<VmPtr<VMDeferredThread>>,
499            }
500
501            /// A deferred component-model thread.
502            ///
503            /// This is an on-stack record pushed by a fused sync-to-sync adapter's fast
504            /// path to defer the work that the `enter_sync_call` libcall would otherwise do
505            /// eagerly.
506            ///
507            /// The adapter allocates one of these in its own stack frame, links the
508            /// previous current-thread value to it via `parent`, and finally points
509            /// `VMStoreContext::current_thread` at it. When host code actually needs the
510            /// real thread, it walks the `parent` chain to materialize thread state (see
511            /// `StoreOpaque::force_current_thread`).
512            #[derive(Debug)]
513            #[repr(C)]
514            #[snake_name = vm_deferred_thread]
515            pub struct VMDeferredThread {
516                /// The previous value of `VMStoreContext::current_thread`.
517                pub parent: VMLazyThread,
518                /// The caller component instance (a deferred `enter_sync_call` argument).
519                pub caller_instance: u32,
520                /// Whether the callee is async-lifted (a deferred `enter_sync_call` arg).
521                pub callee_async: u32,
522                /// The callee component instance (a deferred `enter_sync_call` argument).
523                pub callee_instance: u32,
524                /// The caller thread's `context.{get,set}` slots, saved on entry and
525                /// restored on the fast-path exit (or recovered while forcing).
526                #[aggregate]
527                pub saved_context: [u32; NUM_COMPONENT_CONTEXT_SLOTS],
528            }
529        }
530    };
531}