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}