wasmtime/runtime/vm/vmcontext.rs
1//! This file declares `VMContext` and several related structs which contain
2//! fields that compiled wasm code accesses directly.
3
4mod vm_host_func_context;
5
6pub use self::vm_host_func_context::VMArrayCallHostFuncContext;
7use crate::prelude::*;
8use crate::runtime::vm::{InterpreterRef, VMGcRef, VmPtr, VmSafe, f32x4, f64x2, i8x16};
9use crate::store::StoreOpaque;
10use crate::vm::stack_switching::VMStackChain;
11use core::cell::UnsafeCell;
12use core::ffi::c_void;
13use core::fmt;
14use core::marker;
15use core::mem::{self, MaybeUninit};
16use core::ops::Range;
17use core::ptr::{self, NonNull};
18use core::sync::atomic::{AtomicUsize, Ordering};
19use wasmtime_environ::{
20 BuiltinFunctionIndex, DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex,
21 DefinedTagIndex, NUM_COMPONENT_CONTEXT_SLOTS, VMCONTEXT_MAGIC, VMSharedTypeIndex,
22 WasmHeapTopType, WasmValType,
23};
24
25/// A function pointer that exposes the array calling convention.
26///
27/// Regardless of the underlying Wasm function type, all functions using the
28/// array calling convention have the same Rust signature.
29///
30/// Arguments:
31///
32/// * Callee `vmctx` for the function itself.
33///
34/// * Caller's `vmctx` (so that host functions can access the linear memory of
35/// their Wasm callers).
36///
37/// * A pointer to a buffer of `ValRaw`s where both arguments are passed into
38/// this function, and where results are returned from this function.
39///
40/// * The capacity of the `ValRaw` buffer. Must always be at least
41/// `max(len(wasm_params), len(wasm_results))`.
42///
43/// Return value:
44///
45/// * `true` if this call succeeded.
46/// * `false` if this call failed and a trap was recorded in TLS.
47pub type VMArrayCallNative = unsafe extern "C" fn(
48 NonNull<VMOpaqueContext>,
49 NonNull<VMContext>,
50 NonNull<ValRaw>,
51 usize,
52) -> bool;
53
54/// An opaque function pointer which might be `VMArrayCallNative` or it might be
55/// pulley bytecode. Requires external knowledge to determine what kind of
56/// function pointer this is.
57#[repr(transparent)]
58pub struct VMArrayCallFunction(VMFunctionBody);
59
60/// A function pointer that exposes the Wasm calling convention.
61///
62/// In practice, different Wasm function types end up mapping to different Rust
63/// function types, so this isn't simply a type alias the way that
64/// `VMArrayCallFunction` is. However, the exact details of the calling
65/// convention are left to the Wasm compiler (e.g. Cranelift or Winch). Runtime
66/// code never does anything with these function pointers except shuffle them
67/// around and pass them back to Wasm.
68#[repr(transparent)]
69pub struct VMWasmCallFunction(VMFunctionBody);
70
71/// An imported function.
72///
73/// Basically the same as `VMFuncRef`, except that `wasm_call` is not optional.
74#[derive(Debug, Clone)]
75#[repr(C)]
76pub struct VMFunctionImport {
77 /// Same as `VMFuncRef::array_call`.
78 pub array_call: VmPtr<VMArrayCallFunction>,
79
80 /// Same as `VMFuncRef::wasm_call`, except always non-null. Must be filled
81 /// in by the time Wasm is importing this function!
82 pub wasm_call: VmPtr<VMWasmCallFunction>,
83
84 /// Function signature's _actual_ type id.
85 ///
86 /// This is the type that the function was defined with, not the type that
87 /// it was imported as. These two can be different in the face of subtyping
88 /// and we need the former for to correctly implement dynamic downcasts.
89 pub type_index: VMSharedTypeIndex,
90
91 /// Same as `VMFuncRef::vmctx`.
92 pub vmctx: VmPtr<VMOpaqueContext>,
93 // If more elements are added here, remember to add offset_of tests below!
94}
95
96// SAFETY: the above structure is repr(C) and only contains `VmSafe` fields.
97unsafe impl VmSafe for VMFunctionImport {}
98
99impl VMFunctionImport {
100 /// Convert `&VMFunctionImport` into `&VMFuncRef`.
101 pub fn as_func_ref(&self) -> &VMFuncRef {
102 // Safety: `VMFunctionImport` and `VMFuncRef` have the same
103 // representation.
104 unsafe { Self::as_non_null_func_ref(NonNull::from(self)).as_ref() }
105 }
106
107 /// Convert `NonNull<VMFunctionImport>` into `NonNull<VMFuncRef>`.
108 pub fn as_non_null_func_ref(p: NonNull<VMFunctionImport>) -> NonNull<VMFuncRef> {
109 p.cast()
110 }
111
112 /// Convert `*mut VMFunctionImport` into `*mut VMFuncRef`.
113 pub fn as_func_ref_ptr(p: *mut VMFunctionImport) -> *mut VMFuncRef {
114 p.cast()
115 }
116}
117
118#[cfg(test)]
119mod test_vmfunction_import {
120 use super::{VMFuncRef, VMFunctionImport};
121 use core::mem::offset_of;
122 use std::mem::size_of;
123 use wasmtime_environ::{HostPtr, Module, StaticModuleIndex, VMOffsets};
124
125 #[test]
126 fn check_vmfunction_import_offsets() {
127 let module = Module::new(StaticModuleIndex::from_u32(0));
128 let offsets = VMOffsets::new(HostPtr, &module);
129 assert_eq!(
130 size_of::<VMFunctionImport>(),
131 usize::from(offsets.size_of_vmfunction_import())
132 );
133 assert_eq!(
134 offset_of!(VMFunctionImport, array_call),
135 usize::from(offsets.vmfunction_import_array_call())
136 );
137 assert_eq!(
138 offset_of!(VMFunctionImport, wasm_call),
139 usize::from(offsets.vmfunction_import_wasm_call())
140 );
141 assert_eq!(
142 offset_of!(VMFunctionImport, type_index),
143 usize::from(offsets.vmfunction_import_type_index())
144 );
145 assert_eq!(
146 offset_of!(VMFunctionImport, vmctx),
147 usize::from(offsets.vmfunction_import_vmctx())
148 );
149 }
150
151 #[test]
152 fn vmfunction_import_and_vmfunc_ref_have_same_layout() {
153 assert_eq!(size_of::<VMFunctionImport>(), size_of::<VMFuncRef>());
154 assert_eq!(
155 offset_of!(VMFunctionImport, array_call),
156 offset_of!(VMFuncRef, array_call),
157 );
158 assert_eq!(
159 offset_of!(VMFunctionImport, wasm_call),
160 offset_of!(VMFuncRef, wasm_call),
161 );
162 assert_eq!(
163 offset_of!(VMFunctionImport, type_index),
164 offset_of!(VMFuncRef, type_index),
165 );
166 assert_eq!(
167 offset_of!(VMFunctionImport, vmctx),
168 offset_of!(VMFuncRef, vmctx),
169 );
170 }
171}
172
173/// A placeholder byte-sized type which is just used to provide some amount of type
174/// safety when dealing with pointers to JIT-compiled function bodies. Note that it's
175/// deliberately not Copy, as we shouldn't be carelessly copying function body bytes
176/// around.
177#[repr(C)]
178pub struct VMFunctionBody(u8);
179
180// SAFETY: this structure is never read and is safe to pass to jit code.
181unsafe impl VmSafe for VMFunctionBody {}
182
183#[cfg(test)]
184mod test_vmfunction_body {
185 use super::VMFunctionBody;
186 use std::mem::size_of;
187
188 #[test]
189 fn check_vmfunction_body_offsets() {
190 assert_eq!(size_of::<VMFunctionBody>(), 1);
191 }
192}
193
194/// The fields compiled code needs to access to utilize a WebAssembly table
195/// imported from another instance.
196#[derive(Debug, Copy, Clone)]
197#[repr(C)]
198pub struct VMTableImport {
199 /// A pointer to the imported table description.
200 pub from: VmPtr<VMTableDefinition>,
201
202 /// A pointer to the `VMContext` that owns the table description.
203 pub vmctx: VmPtr<VMContext>,
204
205 /// The table index, within `vmctx`, this definition resides at.
206 pub index: DefinedTableIndex,
207}
208
209// SAFETY: the above structure is repr(C) and only contains `VmSafe` fields.
210unsafe impl VmSafe for VMTableImport {}
211
212#[cfg(test)]
213mod test_vmtable {
214 use super::VMTableImport;
215 use core::mem::offset_of;
216 use std::mem::size_of;
217 use wasmtime_environ::component::{Component, VMComponentOffsets};
218 use wasmtime_environ::{HostPtr, Module, StaticModuleIndex, VMOffsets};
219
220 #[test]
221 fn check_vmtable_offsets() {
222 let module = Module::new(StaticModuleIndex::from_u32(0));
223 let offsets = VMOffsets::new(HostPtr, &module);
224 assert_eq!(
225 size_of::<VMTableImport>(),
226 usize::from(offsets.size_of_vmtable_import())
227 );
228 assert_eq!(
229 offset_of!(VMTableImport, from),
230 usize::from(offsets.vmtable_import_from())
231 );
232 assert_eq!(
233 offset_of!(VMTableImport, vmctx),
234 usize::from(offsets.vmtable_import_vmctx())
235 );
236 assert_eq!(
237 offset_of!(VMTableImport, index),
238 usize::from(offsets.vmtable_import_index())
239 );
240 }
241
242 #[test]
243 fn ensure_sizes_match() {
244 // Because we use `VMTableImport` for recording tables used by components, we
245 // want to make sure that the size calculations between `VMOffsets` and
246 // `VMComponentOffsets` stay the same.
247 let module = Module::new(StaticModuleIndex::from_u32(0));
248 let vm_offsets = VMOffsets::new(HostPtr, &module);
249 let component = Component::default();
250 let vm_component_offsets = VMComponentOffsets::new(HostPtr, &component);
251 assert_eq!(
252 vm_offsets.size_of_vmtable_import(),
253 vm_component_offsets.size_of_vmtable_import()
254 );
255 }
256}
257
258/// The fields compiled code needs to access to utilize a WebAssembly linear
259/// memory imported from another instance.
260#[derive(Debug, Copy, Clone)]
261#[repr(C)]
262pub struct VMMemoryImport {
263 /// A pointer to the imported memory description.
264 pub from: VmPtr<VMMemoryDefinition>,
265
266 /// A pointer to the `VMContext` that owns the memory description.
267 pub vmctx: VmPtr<VMContext>,
268
269 /// The index of the memory in the containing `vmctx`.
270 pub index: DefinedMemoryIndex,
271}
272
273// SAFETY: the above structure is repr(C) and only contains `VmSafe` fields.
274unsafe impl VmSafe for VMMemoryImport {}
275
276#[cfg(test)]
277mod test_vmmemory_import {
278 use super::VMMemoryImport;
279 use core::mem::offset_of;
280 use std::mem::size_of;
281 use wasmtime_environ::{HostPtr, Module, StaticModuleIndex, VMOffsets};
282
283 #[test]
284 fn check_vmmemory_import_offsets() {
285 let module = Module::new(StaticModuleIndex::from_u32(0));
286 let offsets = VMOffsets::new(HostPtr, &module);
287 assert_eq!(
288 size_of::<VMMemoryImport>(),
289 usize::from(offsets.size_of_vmmemory_import())
290 );
291 assert_eq!(
292 offset_of!(VMMemoryImport, from),
293 usize::from(offsets.vmmemory_import_from())
294 );
295 assert_eq!(
296 offset_of!(VMMemoryImport, vmctx),
297 usize::from(offsets.vmmemory_import_vmctx())
298 );
299 assert_eq!(
300 offset_of!(VMMemoryImport, index),
301 usize::from(offsets.vmmemory_import_index())
302 );
303 }
304}
305
306/// The fields compiled code needs to access to utilize a WebAssembly global
307/// variable imported from another instance.
308///
309/// Note that unlike with functions, tables, and memories, `VMGlobalImport`
310/// doesn't include a `vmctx` pointer. Globals are never resized, and don't
311/// require a `vmctx` pointer to access.
312#[derive(Debug, Copy, Clone)]
313#[repr(C)]
314pub struct VMGlobalImport {
315 /// A pointer to the imported global variable description.
316 pub from: VmPtr<VMGlobalDefinition>,
317
318 /// A pointer to the context that owns the global.
319 ///
320 /// Exactly what's stored here is dictated by `kind` below. This is `None`
321 /// for `VMGlobalKind::Host`, it's a `VMContext` for
322 /// `VMGlobalKind::Instance`, and it's `VMComponentContext` for
323 /// `VMGlobalKind::ComponentFlags`.
324 pub vmctx: Option<VmPtr<VMOpaqueContext>>,
325
326 /// The kind of global, and extra location information in addition to
327 /// `vmctx` above.
328 pub kind: VMGlobalKind,
329}
330
331// SAFETY: the above structure is repr(C) and only contains `VmSafe` fields.
332unsafe impl VmSafe for VMGlobalImport {}
333
334/// The kinds of globals that Wasmtime has.
335#[derive(Debug, Copy, Clone)]
336#[repr(C, u32)]
337pub enum VMGlobalKind {
338 /// Host globals, stored in a `StoreOpaque`.
339 Host(DefinedGlobalIndex),
340 /// Instance globals, stored in `VMContext`s
341 Instance(DefinedGlobalIndex),
342 /// Flags for a component instance, stored in `VMComponentContext`.
343 #[cfg(feature = "component-model")]
344 ComponentFlags(wasmtime_environ::component::RuntimeComponentInstanceIndex),
345 #[cfg(feature = "component-model")]
346 TaskMayBlock,
347}
348
349// SAFETY: the above enum is repr(C) and stores nothing else
350unsafe impl VmSafe for VMGlobalKind {}
351
352#[cfg(test)]
353mod test_vmglobal_import {
354 use super::VMGlobalImport;
355 use core::mem::offset_of;
356 use std::mem::size_of;
357 use wasmtime_environ::{HostPtr, Module, StaticModuleIndex, VMOffsets};
358
359 #[test]
360 fn check_vmglobal_import_offsets() {
361 let module = Module::new(StaticModuleIndex::from_u32(0));
362 let offsets = VMOffsets::new(HostPtr, &module);
363 assert_eq!(
364 size_of::<VMGlobalImport>(),
365 usize::from(offsets.size_of_vmglobal_import())
366 );
367 assert_eq!(
368 offset_of!(VMGlobalImport, from),
369 usize::from(offsets.vmglobal_import_from())
370 );
371 }
372}
373
374/// The fields compiled code needs to access to utilize a WebAssembly
375/// tag imported from another instance.
376#[derive(Debug, Copy, Clone)]
377#[repr(C)]
378pub struct VMTagImport {
379 /// A pointer to the imported tag description.
380 pub from: VmPtr<VMTagDefinition>,
381
382 /// The instance that owns this tag.
383 pub vmctx: VmPtr<VMContext>,
384
385 /// The index of the tag in the containing `vmctx`.
386 pub index: DefinedTagIndex,
387}
388
389// SAFETY: the above structure is repr(C) and only contains `VmSafe` fields.
390unsafe impl VmSafe for VMTagImport {}
391
392#[cfg(test)]
393mod test_vmtag_import {
394 use super::VMTagImport;
395 use core::mem::{offset_of, size_of};
396 use wasmtime_environ::{HostPtr, Module, StaticModuleIndex, VMOffsets};
397
398 #[test]
399 fn check_vmtag_import_offsets() {
400 let module = Module::new(StaticModuleIndex::from_u32(0));
401 let offsets = VMOffsets::new(HostPtr, &module);
402 assert_eq!(
403 size_of::<VMTagImport>(),
404 usize::from(offsets.size_of_vmtag_import())
405 );
406 assert_eq!(
407 offset_of!(VMTagImport, from),
408 usize::from(offsets.vmtag_import_from())
409 );
410 assert_eq!(
411 offset_of!(VMTagImport, vmctx),
412 usize::from(offsets.vmtag_import_vmctx())
413 );
414 assert_eq!(
415 offset_of!(VMTagImport, index),
416 usize::from(offsets.vmtag_import_index())
417 );
418 }
419}
420
421/// The fields compiled code needs to access to utilize a WebAssembly linear
422/// memory defined within the instance, namely the start address and the
423/// size in bytes.
424#[derive(Debug)]
425#[repr(C)]
426pub struct VMMemoryDefinition {
427 /// The start address.
428 pub base: VmPtr<u8>,
429
430 /// The current logical size of this linear memory in bytes.
431 ///
432 /// This is atomic because shared memories must be able to grow their length
433 /// atomically. For relaxed access, see
434 /// [`VMMemoryDefinition::current_length()`].
435 pub current_length: AtomicUsize,
436}
437
438// SAFETY: the above definition has `repr(C)` and each field individually
439// implements `VmSafe`, which satisfies the requirements of this trait.
440unsafe impl VmSafe for VMMemoryDefinition {}
441
442impl VMMemoryDefinition {
443 /// Return the current length (in bytes) of the [`VMMemoryDefinition`] by
444 /// performing a relaxed load; do not use this function for situations in
445 /// which a precise length is needed. Owned memories (i.e., non-shared) will
446 /// always return a precise result (since no concurrent modification is
447 /// possible) but shared memories may see an imprecise value--a
448 /// `current_length` potentially smaller than what some other thread
449 /// observes. Since Wasm memory only grows, this under-estimation may be
450 /// acceptable in certain cases.
451 #[inline]
452 pub fn current_length(&self) -> usize {
453 self.current_length.load(Ordering::Relaxed)
454 }
455
456 /// Return a copy of the [`VMMemoryDefinition`] using the relaxed value of
457 /// `current_length`; see [`VMMemoryDefinition::current_length()`].
458 #[inline]
459 pub unsafe fn load(ptr: *mut Self) -> Self {
460 let other = unsafe { &*ptr };
461 VMMemoryDefinition {
462 base: other.base,
463 current_length: other.current_length().into(),
464 }
465 }
466}
467
468#[cfg(test)]
469mod test_vmmemory_definition {
470 use super::VMMemoryDefinition;
471 use core::mem::offset_of;
472 use std::mem::size_of;
473 use wasmtime_environ::{HostPtr, Module, PtrSize, StaticModuleIndex, VMOffsets};
474
475 #[test]
476 fn check_vmmemory_definition_offsets() {
477 let module = Module::new(StaticModuleIndex::from_u32(0));
478 let offsets = VMOffsets::new(HostPtr, &module);
479 assert_eq!(
480 size_of::<VMMemoryDefinition>(),
481 usize::from(offsets.ptr.size_of_vmmemory_definition())
482 );
483 assert_eq!(
484 offset_of!(VMMemoryDefinition, base),
485 usize::from(offsets.ptr.vmmemory_definition_base())
486 );
487 assert_eq!(
488 offset_of!(VMMemoryDefinition, current_length),
489 usize::from(offsets.ptr.vmmemory_definition_current_length())
490 );
491 /* TODO: Assert that the size of `current_length` matches.
492 assert_eq!(
493 size_of::<VMMemoryDefinition::current_length>(),
494 usize::from(offsets.size_of_vmmemory_definition_current_length())
495 );
496 */
497 }
498}
499
500/// The fields compiled code needs to access to utilize a WebAssembly table
501/// defined within the instance.
502#[derive(Debug, Copy, Clone)]
503#[repr(C)]
504pub struct VMTableDefinition {
505 /// Pointer to the table data.
506 pub base: VmPtr<u8>,
507
508 /// The current number of elements in the table.
509 pub current_elements: usize,
510}
511
512// SAFETY: the above structure is repr(C) and only contains `VmSafe` fields.
513unsafe impl VmSafe for VMTableDefinition {}
514
515#[cfg(test)]
516mod test_vmtable_definition {
517 use super::VMTableDefinition;
518 use core::mem::offset_of;
519 use std::mem::size_of;
520 use wasmtime_environ::{HostPtr, Module, StaticModuleIndex, VMOffsets};
521
522 #[test]
523 fn check_vmtable_definition_offsets() {
524 let module = Module::new(StaticModuleIndex::from_u32(0));
525 let offsets = VMOffsets::new(HostPtr, &module);
526 assert_eq!(
527 size_of::<VMTableDefinition>(),
528 usize::from(offsets.size_of_vmtable_definition())
529 );
530 assert_eq!(
531 offset_of!(VMTableDefinition, base),
532 usize::from(offsets.vmtable_definition_base())
533 );
534 assert_eq!(
535 offset_of!(VMTableDefinition, current_elements),
536 usize::from(offsets.vmtable_definition_current_elements())
537 );
538 }
539}
540
541/// The storage for a WebAssembly global defined within the instance.
542///
543/// TODO: Pack the globals more densely, rather than using the same size
544/// for every type.
545#[derive(Debug)]
546#[repr(C, align(16))]
547pub struct VMGlobalDefinition {
548 storage: [u8; 16],
549 // If more elements are added here, remember to add offset_of tests below!
550}
551
552// SAFETY: the above structure is repr(C) and only contains `VmSafe` fields.
553unsafe impl VmSafe for VMGlobalDefinition {}
554
555#[cfg(test)]
556mod test_vmglobal_definition {
557 use super::VMGlobalDefinition;
558 use std::mem::{align_of, size_of};
559 use wasmtime_environ::{HostPtr, Module, PtrSize, StaticModuleIndex, VMOffsets};
560
561 #[test]
562 fn check_vmglobal_definition_alignment() {
563 assert!(align_of::<VMGlobalDefinition>() >= align_of::<i32>());
564 assert!(align_of::<VMGlobalDefinition>() >= align_of::<i64>());
565 assert!(align_of::<VMGlobalDefinition>() >= align_of::<f32>());
566 assert!(align_of::<VMGlobalDefinition>() >= align_of::<f64>());
567 assert!(align_of::<VMGlobalDefinition>() >= align_of::<[u8; 16]>());
568 assert!(align_of::<VMGlobalDefinition>() >= align_of::<[f32; 4]>());
569 assert!(align_of::<VMGlobalDefinition>() >= align_of::<[f64; 2]>());
570 }
571
572 #[test]
573 fn check_vmglobal_definition_offsets() {
574 let module = Module::new(StaticModuleIndex::from_u32(0));
575 let offsets = VMOffsets::new(HostPtr, &module);
576 assert_eq!(
577 size_of::<VMGlobalDefinition>(),
578 usize::from(offsets.ptr.size_of_vmglobal_definition())
579 );
580 }
581
582 #[test]
583 fn check_vmglobal_begins_aligned() {
584 let module = Module::new(StaticModuleIndex::from_u32(0));
585 let offsets = VMOffsets::new(HostPtr, &module);
586 assert_eq!(offsets.vmctx_globals_begin() % 16, 0);
587 }
588
589 #[test]
590 #[cfg(feature = "gc")]
591 fn check_vmglobal_can_contain_gc_ref() {
592 assert!(size_of::<crate::runtime::vm::VMGcRef>() <= size_of::<VMGlobalDefinition>());
593 }
594}
595
596impl VMGlobalDefinition {
597 /// Construct a `VMGlobalDefinition`.
598 pub fn new() -> Self {
599 Self { storage: [0; 16] }
600 }
601
602 /// Create a `VMGlobalDefinition` from a `ValRaw`.
603 ///
604 /// # Unsafety
605 ///
606 /// This raw value's type must match the given `WasmValType`.
607 pub unsafe fn from_val_raw(
608 store: &mut StoreOpaque,
609 wasm_ty: WasmValType,
610 raw: ValRaw,
611 ) -> Result<Self> {
612 let mut global = Self::new();
613 unsafe {
614 match wasm_ty {
615 WasmValType::I32 => *global.as_i32_mut() = raw.get_i32(),
616 WasmValType::I64 => *global.as_i64_mut() = raw.get_i64(),
617 WasmValType::F32 => *global.as_f32_bits_mut() = raw.get_f32(),
618 WasmValType::F64 => *global.as_f64_bits_mut() = raw.get_f64(),
619 WasmValType::V128 => global.set_u128(raw.get_v128()),
620 WasmValType::Ref(r) => match r.heap_type.top() {
621 WasmHeapTopType::Extern => {
622 let r = VMGcRef::from_raw_u32(raw.get_externref());
623 global.init_gc_ref(store, r.as_ref())
624 }
625 WasmHeapTopType::Any => {
626 let r = VMGcRef::from_raw_u32(raw.get_anyref());
627 global.init_gc_ref(store, r.as_ref())
628 }
629 WasmHeapTopType::Func => *global.as_func_ref_mut() = raw.get_funcref().cast(),
630 WasmHeapTopType::Cont => *global.as_func_ref_mut() = raw.get_funcref().cast(), // TODO(#10248): temporary hack.
631 WasmHeapTopType::Exn => {
632 let r = VMGcRef::from_raw_u32(raw.get_exnref());
633 global.init_gc_ref(store, r.as_ref())
634 }
635 },
636 }
637 }
638 Ok(global)
639 }
640
641 /// Get this global's value as a `ValRaw`.
642 ///
643 /// # Unsafety
644 ///
645 /// This global's value's type must match the given `WasmValType`.
646 pub unsafe fn to_val_raw(
647 &self,
648 store: &mut StoreOpaque,
649 wasm_ty: WasmValType,
650 ) -> Result<ValRaw> {
651 unsafe {
652 Ok(match wasm_ty {
653 WasmValType::I32 => ValRaw::i32(*self.as_i32()),
654 WasmValType::I64 => ValRaw::i64(*self.as_i64()),
655 WasmValType::F32 => ValRaw::f32(*self.as_f32_bits()),
656 WasmValType::F64 => ValRaw::f64(*self.as_f64_bits()),
657 WasmValType::V128 => ValRaw::v128(self.get_u128()),
658 WasmValType::Ref(r) => match r.heap_type.top() {
659 WasmHeapTopType::Extern => ValRaw::externref(match self.as_gc_ref() {
660 Some(r) => store.clone_gc_ref(r).as_raw_u32(),
661 None => 0,
662 }),
663 WasmHeapTopType::Any => ValRaw::anyref({
664 match self.as_gc_ref() {
665 Some(r) => store.clone_gc_ref(r).as_raw_u32(),
666 None => 0,
667 }
668 }),
669 WasmHeapTopType::Exn => ValRaw::exnref({
670 match self.as_gc_ref() {
671 Some(r) => store.clone_gc_ref(r).as_raw_u32(),
672 None => 0,
673 }
674 }),
675 WasmHeapTopType::Func => ValRaw::funcref(self.as_func_ref().cast()),
676 WasmHeapTopType::Cont => todo!(), // FIXME: #10248 stack switching support.
677 },
678 })
679 }
680 }
681
682 /// Return a reference to the value as an i32.
683 pub unsafe fn as_i32(&self) -> &i32 {
684 unsafe { &*(self.storage.as_ref().as_ptr().cast::<i32>()) }
685 }
686
687 /// Return a mutable reference to the value as an i32.
688 pub unsafe fn as_i32_mut(&mut self) -> &mut i32 {
689 unsafe { &mut *(self.storage.as_mut().as_mut_ptr().cast::<i32>()) }
690 }
691
692 /// Return a reference to the value as a u32.
693 pub unsafe fn as_u32(&self) -> &u32 {
694 unsafe { &*(self.storage.as_ref().as_ptr().cast::<u32>()) }
695 }
696
697 /// Return a mutable reference to the value as an u32.
698 pub unsafe fn as_u32_mut(&mut self) -> &mut u32 {
699 unsafe { &mut *(self.storage.as_mut().as_mut_ptr().cast::<u32>()) }
700 }
701
702 /// Return a reference to the value as an i64.
703 pub unsafe fn as_i64(&self) -> &i64 {
704 unsafe { &*(self.storage.as_ref().as_ptr().cast::<i64>()) }
705 }
706
707 /// Return a mutable reference to the value as an i64.
708 pub unsafe fn as_i64_mut(&mut self) -> &mut i64 {
709 unsafe { &mut *(self.storage.as_mut().as_mut_ptr().cast::<i64>()) }
710 }
711
712 /// Return a reference to the value as an u64.
713 pub unsafe fn as_u64(&self) -> &u64 {
714 unsafe { &*(self.storage.as_ref().as_ptr().cast::<u64>()) }
715 }
716
717 /// Return a mutable reference to the value as an u64.
718 pub unsafe fn as_u64_mut(&mut self) -> &mut u64 {
719 unsafe { &mut *(self.storage.as_mut().as_mut_ptr().cast::<u64>()) }
720 }
721
722 /// Return a reference to the value as an f32.
723 pub unsafe fn as_f32(&self) -> &f32 {
724 unsafe { &*(self.storage.as_ref().as_ptr().cast::<f32>()) }
725 }
726
727 /// Return a mutable reference to the value as an f32.
728 pub unsafe fn as_f32_mut(&mut self) -> &mut f32 {
729 unsafe { &mut *(self.storage.as_mut().as_mut_ptr().cast::<f32>()) }
730 }
731
732 /// Return a reference to the value as f32 bits.
733 pub unsafe fn as_f32_bits(&self) -> &u32 {
734 unsafe { &*(self.storage.as_ref().as_ptr().cast::<u32>()) }
735 }
736
737 /// Return a mutable reference to the value as f32 bits.
738 pub unsafe fn as_f32_bits_mut(&mut self) -> &mut u32 {
739 unsafe { &mut *(self.storage.as_mut().as_mut_ptr().cast::<u32>()) }
740 }
741
742 /// Return a reference to the value as an f64.
743 pub unsafe fn as_f64(&self) -> &f64 {
744 unsafe { &*(self.storage.as_ref().as_ptr().cast::<f64>()) }
745 }
746
747 /// Return a mutable reference to the value as an f64.
748 pub unsafe fn as_f64_mut(&mut self) -> &mut f64 {
749 unsafe { &mut *(self.storage.as_mut().as_mut_ptr().cast::<f64>()) }
750 }
751
752 /// Return a reference to the value as f64 bits.
753 pub unsafe fn as_f64_bits(&self) -> &u64 {
754 unsafe { &*(self.storage.as_ref().as_ptr().cast::<u64>()) }
755 }
756
757 /// Return a mutable reference to the value as f64 bits.
758 pub unsafe fn as_f64_bits_mut(&mut self) -> &mut u64 {
759 unsafe { &mut *(self.storage.as_mut().as_mut_ptr().cast::<u64>()) }
760 }
761
762 /// Gets the underlying 128-bit vector value.
763 //
764 // Note that vectors are stored in little-endian format while other types
765 // are stored in native-endian format.
766 pub unsafe fn get_u128(&self) -> u128 {
767 unsafe { u128::from_le(*(self.storage.as_ref().as_ptr().cast::<u128>())) }
768 }
769
770 /// Sets the 128-bit vector values.
771 //
772 // Note that vectors are stored in little-endian format while other types
773 // are stored in native-endian format.
774 pub unsafe fn set_u128(&mut self, val: u128) {
775 unsafe {
776 *self.storage.as_mut().as_mut_ptr().cast::<u128>() = val.to_le();
777 }
778 }
779
780 /// Return a reference to the value as u128 bits.
781 pub unsafe fn as_u128_bits(&self) -> &[u8; 16] {
782 unsafe { &*(self.storage.as_ref().as_ptr().cast::<[u8; 16]>()) }
783 }
784
785 /// Return a mutable reference to the value as u128 bits.
786 pub unsafe fn as_u128_bits_mut(&mut self) -> &mut [u8; 16] {
787 unsafe { &mut *(self.storage.as_mut().as_mut_ptr().cast::<[u8; 16]>()) }
788 }
789
790 /// Return a reference to the global value as a borrowed GC reference.
791 pub unsafe fn as_gc_ref(&self) -> Option<&VMGcRef> {
792 let raw_ptr = self.storage.as_ref().as_ptr().cast::<Option<VMGcRef>>();
793 let ret = unsafe { (*raw_ptr).as_ref() };
794 assert!(cfg!(feature = "gc") || ret.is_none());
795 ret
796 }
797
798 /// Initialize a global to the given GC reference.
799 pub unsafe fn init_gc_ref(&mut self, store: &mut StoreOpaque, gc_ref: Option<&VMGcRef>) {
800 let dest = unsafe {
801 &mut *(self
802 .storage
803 .as_mut()
804 .as_mut_ptr()
805 .cast::<MaybeUninit<Option<VMGcRef>>>())
806 };
807
808 store.init_gc_ref(dest, gc_ref)
809 }
810
811 /// Write a GC reference into this global value.
812 pub unsafe fn write_gc_ref(&mut self, store: &mut StoreOpaque, gc_ref: Option<&VMGcRef>) {
813 let dest = unsafe { &mut *(self.storage.as_mut().as_mut_ptr().cast::<Option<VMGcRef>>()) };
814 store.write_gc_ref(dest, gc_ref)
815 }
816
817 /// Return a reference to the value as a `VMFuncRef`.
818 pub unsafe fn as_func_ref(&self) -> *mut VMFuncRef {
819 unsafe { *(self.storage.as_ref().as_ptr().cast::<*mut VMFuncRef>()) }
820 }
821
822 /// Return a mutable reference to the value as a `VMFuncRef`.
823 pub unsafe fn as_func_ref_mut(&mut self) -> &mut *mut VMFuncRef {
824 unsafe { &mut *(self.storage.as_mut().as_mut_ptr().cast::<*mut VMFuncRef>()) }
825 }
826}
827
828#[cfg(test)]
829mod test_vmshared_type_index {
830 use super::VMSharedTypeIndex;
831 use std::mem::size_of;
832 use wasmtime_environ::{HostPtr, Module, StaticModuleIndex, VMOffsets};
833
834 #[test]
835 fn check_vmshared_type_index() {
836 let module = Module::new(StaticModuleIndex::from_u32(0));
837 let offsets = VMOffsets::new(HostPtr, &module);
838 assert_eq!(
839 size_of::<VMSharedTypeIndex>(),
840 usize::from(offsets.size_of_vmshared_type_index())
841 );
842 }
843}
844
845/// A WebAssembly tag defined within the instance.
846///
847#[derive(Debug)]
848#[repr(C)]
849pub struct VMTagDefinition {
850 /// Function signature's type id.
851 pub type_index: VMSharedTypeIndex,
852}
853
854impl VMTagDefinition {
855 pub fn new(type_index: VMSharedTypeIndex) -> Self {
856 Self { type_index }
857 }
858}
859
860// SAFETY: the above structure is repr(C) and only contains VmSafe
861// fields.
862unsafe impl VmSafe for VMTagDefinition {}
863
864#[cfg(test)]
865mod test_vmtag_definition {
866 use super::VMTagDefinition;
867 use std::mem::size_of;
868 use wasmtime_environ::{HostPtr, Module, PtrSize, StaticModuleIndex, VMOffsets};
869
870 #[test]
871 fn check_vmtag_definition_offsets() {
872 let module = Module::new(StaticModuleIndex::from_u32(0));
873 let offsets = VMOffsets::new(HostPtr, &module);
874 assert_eq!(
875 size_of::<VMTagDefinition>(),
876 usize::from(offsets.ptr.size_of_vmtag_definition())
877 );
878 }
879
880 #[test]
881 fn check_vmtag_begins_aligned() {
882 let module = Module::new(StaticModuleIndex::from_u32(0));
883 let offsets = VMOffsets::new(HostPtr, &module);
884 assert_eq!(offsets.vmctx_tags_begin() % 16, 0);
885 }
886}
887
888/// The VM caller-checked "funcref" record, for caller-side signature checking.
889///
890/// It consists of function pointer(s), a type id to be checked by the
891/// caller, and the vmctx closure associated with this function.
892#[derive(Debug, Clone)]
893#[repr(C)]
894pub struct VMFuncRef {
895 /// Function pointer for this funcref if being called via the "array"
896 /// calling convention that `Func::new` et al use.
897 pub array_call: VmPtr<VMArrayCallFunction>,
898
899 /// Function pointer for this funcref if being called via the calling
900 /// convention we use when compiling Wasm.
901 ///
902 /// Most functions come with a function pointer that we can use when they
903 /// are called from Wasm. The notable exception is when we `Func::wrap` a
904 /// host function, and we don't have a Wasm compiler on hand to compile a
905 /// Wasm-to-native trampoline for the function. In this case, we leave
906 /// `wasm_call` empty until the function is passed as an import to Wasm (or
907 /// otherwise exposed to Wasm via tables/globals). At this point, we look up
908 /// a Wasm-to-native trampoline for the function in the Wasm's compiled
909 /// module and use that fill in `VMFunctionImport::wasm_call`. **However**
910 /// there is no guarantee that the Wasm module has a trampoline for this
911 /// function's signature. The Wasm module only has trampolines for its
912 /// types, and if this function isn't of one of those types, then the Wasm
913 /// module will not have a trampoline for it. This is actually okay, because
914 /// it means that the Wasm cannot actually call this function. But it does
915 /// mean that this field needs to be an `Option` even though it is non-null
916 /// the vast vast vast majority of the time.
917 pub wasm_call: Option<VmPtr<VMWasmCallFunction>>,
918
919 /// Function signature's type id.
920 pub type_index: VMSharedTypeIndex,
921
922 /// The VM state associated with this function.
923 ///
924 /// The actual definition of what this pointer points to depends on the
925 /// function being referenced: for core Wasm functions, this is a `*mut
926 /// VMContext`, for host functions it is a `*mut VMHostFuncContext`, and for
927 /// component functions it is a `*mut VMComponentContext`.
928 pub vmctx: VmPtr<VMOpaqueContext>,
929 // If more elements are added here, remember to add offset_of tests below!
930}
931
932// SAFETY: the above structure is repr(C) and only contains `VmSafe` fields.
933unsafe impl VmSafe for VMFuncRef {}
934
935impl VMFuncRef {
936 /// Invokes the `array_call` field of this `VMFuncRef` with the supplied
937 /// arguments.
938 ///
939 /// This will invoke the function pointer in the `array_call` field with:
940 ///
941 /// * the `callee` vmctx as `self.vmctx`
942 /// * the `caller` as `caller` specified here
943 /// * the args pointer as `args_and_results`
944 /// * the args length as `args_and_results`
945 ///
946 /// The `args_and_results` area must be large enough to both load all
947 /// arguments from and store all results to.
948 ///
949 /// Returns whether a trap was recorded in TLS for raising.
950 ///
951 /// # Unsafety
952 ///
953 /// This method is unsafe because it can be called with any pointers. They
954 /// must all be valid for this wasm function call to proceed. For example
955 /// the `caller` must be valid machine code if `pulley` is `None` or it must
956 /// be valid bytecode if `pulley` is `Some`. Additionally `args_and_results`
957 /// must be large enough to handle all the arguments/results for this call.
958 ///
959 /// Note that the unsafety invariants to maintain here are not currently
960 /// exhaustively documented.
961 #[inline]
962 pub unsafe fn array_call(
963 me: NonNull<VMFuncRef>,
964 pulley: Option<InterpreterRef<'_>>,
965 caller: NonNull<VMContext>,
966 args_and_results: NonNull<[ValRaw]>,
967 ) -> bool {
968 match pulley {
969 Some(vm) => unsafe { Self::array_call_interpreted(me, vm, caller, args_and_results) },
970 None => unsafe { Self::array_call_native(me, caller, args_and_results) },
971 }
972 }
973
974 unsafe fn array_call_interpreted(
975 me: NonNull<VMFuncRef>,
976 vm: InterpreterRef<'_>,
977 caller: NonNull<VMContext>,
978 args_and_results: NonNull<[ValRaw]>,
979 ) -> bool {
980 // If `caller` is actually a `VMArrayCallHostFuncContext` then skip the
981 // interpreter, even though it's available, as `array_call` will be
982 // native code.
983 unsafe {
984 if me.as_ref().vmctx.as_non_null().as_ref().magic
985 == wasmtime_environ::VM_ARRAY_CALL_HOST_FUNC_MAGIC
986 {
987 return Self::array_call_native(me, caller, args_and_results);
988 }
989 vm.call(
990 me.as_ref().array_call.as_non_null().cast(),
991 me.as_ref().vmctx.as_non_null(),
992 caller,
993 args_and_results,
994 )
995 }
996 }
997
998 #[inline]
999 unsafe fn array_call_native(
1000 me: NonNull<VMFuncRef>,
1001 caller: NonNull<VMContext>,
1002 args_and_results: NonNull<[ValRaw]>,
1003 ) -> bool {
1004 unsafe {
1005 union GetNativePointer {
1006 native: VMArrayCallNative,
1007 ptr: NonNull<VMArrayCallFunction>,
1008 }
1009 let native = GetNativePointer {
1010 ptr: me.as_ref().array_call.as_non_null(),
1011 }
1012 .native;
1013 native(
1014 me.as_ref().vmctx.as_non_null(),
1015 caller,
1016 args_and_results.cast(),
1017 args_and_results.len(),
1018 )
1019 }
1020 }
1021
1022 pub(crate) fn as_vm_function_import(&self) -> Option<&VMFunctionImport> {
1023 if self.wasm_call.is_some() {
1024 // Safety: `VMFuncRef` and `VMFunctionImport` have the same layout
1025 // and `wasm_call` is non-null.
1026 Some(unsafe { NonNull::from(self).cast::<VMFunctionImport>().as_ref() })
1027 } else {
1028 None
1029 }
1030 }
1031}
1032
1033#[cfg(test)]
1034mod test_vm_func_ref {
1035 use super::VMFuncRef;
1036 use core::mem::offset_of;
1037 use std::mem::size_of;
1038 use wasmtime_environ::{HostPtr, Module, PtrSize, StaticModuleIndex, VMOffsets};
1039
1040 #[test]
1041 fn check_vm_func_ref_offsets() {
1042 let module = Module::new(StaticModuleIndex::from_u32(0));
1043 let offsets = VMOffsets::new(HostPtr, &module);
1044 assert_eq!(
1045 size_of::<VMFuncRef>(),
1046 usize::from(offsets.ptr.size_of_vm_func_ref())
1047 );
1048 assert_eq!(
1049 offset_of!(VMFuncRef, array_call),
1050 usize::from(offsets.ptr.vm_func_ref_array_call())
1051 );
1052 assert_eq!(
1053 offset_of!(VMFuncRef, wasm_call),
1054 usize::from(offsets.ptr.vm_func_ref_wasm_call())
1055 );
1056 assert_eq!(
1057 offset_of!(VMFuncRef, type_index),
1058 usize::from(offsets.ptr.vm_func_ref_type_index())
1059 );
1060 assert_eq!(
1061 offset_of!(VMFuncRef, vmctx),
1062 usize::from(offsets.ptr.vm_func_ref_vmctx())
1063 );
1064 }
1065}
1066
1067macro_rules! define_builtin_array {
1068 (
1069 $(
1070 $( #[$attr:meta] )*
1071 $name:ident( $( $pname:ident: $param:ident ),* ) $( -> $result:ident )?;
1072 )*
1073 ) => {
1074 /// An array that stores addresses of builtin functions. We translate code
1075 /// to use indirect calls. This way, we don't have to patch the code.
1076 #[repr(C)]
1077 #[allow(improper_ctypes_definitions, reason = "__m128i known not FFI-safe")]
1078 pub struct VMBuiltinFunctionsArray {
1079 $(
1080 $name: unsafe extern "C" fn(
1081 $(define_builtin_array!(@ty $param)),*
1082 ) $( -> define_builtin_array!(@ty $result))?,
1083 )*
1084 }
1085
1086 impl VMBuiltinFunctionsArray {
1087 pub const INIT: VMBuiltinFunctionsArray = VMBuiltinFunctionsArray {
1088 $(
1089 $name: crate::runtime::vm::libcalls::raw::$name,
1090 )*
1091 };
1092
1093 /// Helper to call `expose_provenance()` on all contained pointers.
1094 ///
1095 /// This is required to be called at least once before entering wasm
1096 /// to inform the compiler that these function pointers may all be
1097 /// loaded/stored and used on the "other end" to reacquire
1098 /// provenance in Pulley. Pulley models hostcalls with a host
1099 /// pointer as the first parameter that's a function pointer under
1100 /// the hood, and this call ensures that the use of the function
1101 /// pointer is considered valid.
1102 pub fn expose_provenance(&self) -> NonNull<Self>{
1103 $(
1104 (self.$name as *mut u8).expose_provenance();
1105 )*
1106 NonNull::from(self)
1107 }
1108 }
1109 };
1110
1111 (@ty u32) => (u32);
1112 (@ty u64) => (u64);
1113 (@ty f32) => (f32);
1114 (@ty f64) => (f64);
1115 (@ty u8) => (u8);
1116 (@ty i8x16) => (i8x16);
1117 (@ty f32x4) => (f32x4);
1118 (@ty f64x2) => (f64x2);
1119 (@ty bool) => (bool);
1120 (@ty pointer) => (*mut u8);
1121 (@ty size) => (usize);
1122 (@ty vmctx) => (NonNull<VMContext>);
1123}
1124
1125// SAFETY: the above structure is repr(C) and only contains `VmSafe` fields.
1126unsafe impl VmSafe for VMBuiltinFunctionsArray {}
1127
1128wasmtime_environ::foreach_builtin_function!(define_builtin_array);
1129
1130const _: () = {
1131 assert!(
1132 mem::size_of::<VMBuiltinFunctionsArray>()
1133 == mem::size_of::<usize>() * (BuiltinFunctionIndex::len() as usize)
1134 )
1135};
1136
1137/// Structure that holds all mutable context that is shared across all instances
1138/// in a store, for example data related to fuel or epochs.
1139///
1140/// `VMStoreContext`s are one-to-one with `wasmtime::Store`s, the same way that
1141/// `VMContext`s are one-to-one with `wasmtime::Instance`s. And the same way
1142/// that multiple `wasmtime::Instance`s may be associated with the same
1143/// `wasmtime::Store`, multiple `VMContext`s hold a pointer to the same
1144/// `VMStoreContext` when they are associated with the same `wasmtime::Store`.
1145#[derive(Debug)]
1146#[repr(C)]
1147pub struct VMStoreContext {
1148 // NB: 64-bit integer fields are located first with pointer-sized fields
1149 // trailing afterwards. That makes the offsets in this structure easier to
1150 // calculate on 32-bit platforms as we don't have to worry about the
1151 // alignment of 64-bit integers.
1152 //
1153 /// Indicator of how much fuel has been consumed and is remaining to
1154 /// WebAssembly.
1155 ///
1156 /// This field is typically negative and increments towards positive. Upon
1157 /// turning positive a wasm trap will be generated. This field is only
1158 /// modified if wasm is configured to consume fuel.
1159 pub fuel_consumed: UnsafeCell<i64>,
1160
1161 /// Deadline epoch for interruption: if epoch-based interruption
1162 /// is enabled and the global (per engine) epoch counter is
1163 /// observed to reach or exceed this value, the guest code will
1164 /// yield if running asynchronously.
1165 pub epoch_deadline: UnsafeCell<u64>,
1166
1167 /// The "store version".
1168 ///
1169 /// This is used to test whether stack-frame handles referring to
1170 /// suspended stack frames remain valid.
1171 ///
1172 /// The invariant that this upward-counting number must satisfy
1173 /// is: the number must be incremented whenever execution starts
1174 /// or resumes in the `Store` or when any stack is
1175 /// dropped/freed. That way, if we take a reference to some
1176 /// suspended stack frame and track the "version" at the time we
1177 /// took that reference, if the version still matches, we can be
1178 /// sure that nothing could have unwound the referenced Wasm
1179 /// frame.
1180 ///
1181 /// This version number is incremented in exactly one place: the
1182 /// Wasm-to-host trampolines, after return from host code. Note
1183 /// that this captures both the normal "return into Wasm" case
1184 /// (where Wasm frames can subsequently return normally and thus
1185 /// invalidate frames), and the "trap/exception unwinds Wasm
1186 /// frames" case, which is done internally via the `raise` libcall
1187 /// invoked after the main hostcall returns an error, and after we
1188 /// increment this version number.
1189 ///
1190 /// Note that this also handles the fiber/future-drop case because
1191 /// because we *always* return into the trampoline to clean up;
1192 /// that trampoline immediately raises an error and uses the
1193 /// longjmp-like unwind within Cranelift frames to skip over all
1194 /// the guest Wasm frames, but not before it increments the
1195 /// store's execution version number.
1196 ///
1197 /// This field is in use only if guest debugging is enabled.
1198 pub execution_version: u64,
1199
1200 /// Current stack limit of the wasm module.
1201 ///
1202 /// For more information see `crates/cranelift/src/lib.rs`.
1203 pub stack_limit: UnsafeCell<usize>,
1204
1205 /// The `VMMemoryDefinition` for this store's GC heap.
1206 pub gc_heap: VMMemoryDefinition,
1207
1208 /// The value of the frame pointer register in the trampoline used
1209 /// to call from Wasm to the host.
1210 ///
1211 /// Maintained by our Wasm-to-host trampoline, and cleared just
1212 /// before calling into Wasm in `catch_traps`.
1213 ///
1214 /// This member is `0` when Wasm is actively running and has not called out
1215 /// to the host.
1216 ///
1217 /// Used to find the start of a contiguous sequence of Wasm frames
1218 /// when walking the stack. Note that we record the FP of the
1219 /// *trampoline*'s frame, not the last Wasm frame, because we need
1220 /// to know the SP (bottom of frame) of the last Wasm frame as
1221 /// well in case we need to resume to an exception handler in that
1222 /// frame. The FP of the last Wasm frame can be recovered by
1223 /// loading the saved FP value at this FP address.
1224 pub last_wasm_exit_trampoline_fp: UnsafeCell<usize>,
1225
1226 /// The last Wasm program counter before we called from Wasm to the host.
1227 ///
1228 /// Maintained by our Wasm-to-host trampoline, and cleared just before
1229 /// calling into Wasm in `catch_traps`.
1230 ///
1231 /// This member is `0` when Wasm is actively running and has not called out
1232 /// to the host.
1233 ///
1234 /// Used when walking a contiguous sequence of Wasm frames.
1235 pub last_wasm_exit_pc: UnsafeCell<usize>,
1236
1237 /// The last host stack pointer before we called into Wasm from the host.
1238 ///
1239 /// Maintained by our host-to-Wasm trampoline. This member is `0` when Wasm
1240 /// is not running, and it's set to nonzero once a host-to-wasm trampoline
1241 /// is executed.
1242 ///
1243 /// When a host function is wrapped into a `wasmtime::Func`, and is then
1244 /// called from the host, then this member is not changed meaning that the
1245 /// previous activation in pointed to by `last_wasm_exit_trampoline_fp` is
1246 /// still the last wasm set of frames on the stack.
1247 ///
1248 /// This field is saved/restored during fiber suspension/resumption
1249 /// resumption as part of `CallThreadState::swap`.
1250 ///
1251 /// This field is used to find the end of a contiguous sequence of Wasm
1252 /// frames when walking the stack. Additionally it's used when a trap is
1253 /// raised as part of the set of parameters used to resume in the entry
1254 /// trampoline's "catch" block.
1255 pub last_wasm_entry_sp: UnsafeCell<usize>,
1256
1257 /// Same as `last_wasm_entry_sp`, but for the `fp` of the trampoline.
1258 pub last_wasm_entry_fp: UnsafeCell<usize>,
1259
1260 /// The last trap handler from a host-to-wasm entry trampoline on the stack.
1261 ///
1262 /// This field is configured when the host calls into wasm by the trampoline
1263 /// itself. It stores the `pc` of an exception handler suitable to handle
1264 /// all traps (or uncaught exceptions).
1265 pub last_wasm_entry_trap_handler: UnsafeCell<usize>,
1266
1267 /// Stack information used by stack switching instructions. See documentation
1268 /// on `VMStackChain` for details.
1269 pub stack_chain: UnsafeCell<VMStackChain>,
1270
1271 /// A pointer to the embedder's `T` inside a `Store<T>`, for use with the
1272 /// `store-data-address` unsafe intrinsic.
1273 pub store_data: VmPtr<()>,
1274
1275 /// The range, in addresses, of the guard page that is currently in use.
1276 ///
1277 /// This field is used when signal handlers are run to determine whether a
1278 /// faulting address lies within the guard page of an async stack for
1279 /// example. If this happens then the signal handler aborts with a stack
1280 /// overflow message similar to what would happen had the stack overflow
1281 /// happened on the main thread. This field is, by default a null..null
1282 /// range indicating that no async guard is in use (aka no fiber). In such a
1283 /// situation while this field is read it'll never classify a fault as an
1284 /// guard page fault.
1285 pub async_guard_range: Range<*mut u8>,
1286
1287 /// The `context.{get,set}` values for the current thread in the component
1288 /// model. This is only used for `component-model-async` and slot[1] is only
1289 /// used for `component-model-threading`. Despite the conditional use nature
1290 /// this is unconditionally present as it avoids the need to make logic in
1291 /// `VMOffsets` conditional.
1292 ///
1293 /// This is saved/restored when threads are swapped in the component model.
1294 pub component_context: [u32; NUM_COMPONENT_CONTEXT_SLOTS],
1295}
1296
1297impl VMStoreContext {
1298 /// From the current saved trampoline FP, get the FP of the last
1299 /// Wasm frame. If the current saved trampoline FP is null, return
1300 /// null.
1301 ///
1302 /// We store only the trampoline FP, because (i) we need the
1303 /// trampoline FP, so we know the size (bottom) of the last Wasm
1304 /// frame; and (ii) the last Wasm frame, just above the trampoline
1305 /// frame, can be recovered via the FP chain.
1306 ///
1307 /// # Safety
1308 ///
1309 /// This function requires that the `last_wasm_exit_trampoline_fp`
1310 /// field either points to an active trampoline frame or is a null
1311 /// pointer.
1312 pub(crate) unsafe fn last_wasm_exit_fp(&self) -> usize {
1313 // SAFETY: the unsafe cell is safe to load (no other threads
1314 // will be writing our store when we have control), and the
1315 // helper function's safety condition is the same as ours.
1316 unsafe {
1317 let trampoline_fp = *self.last_wasm_exit_trampoline_fp.get();
1318 Self::wasm_exit_fp_from_trampoline_fp(trampoline_fp)
1319 }
1320 }
1321
1322 /// From any saved trampoline FP, get the FP of the last Wasm
1323 /// frame. If the given trampoline FP is null, return null.
1324 ///
1325 /// This differs from `last_wasm_exit_fp()` above in that it
1326 /// allows accessing activations further up the stack as well,
1327 /// e.g. via `CallThreadState::old_state`.
1328 ///
1329 /// # Safety
1330 ///
1331 /// This function requires that the provided FP value is valid,
1332 /// and points to an active trampoline frame, or is null.
1333 ///
1334 /// This function depends on the invariant that on all supported
1335 /// architectures, we store the previous FP value under the
1336 /// current FP. This is a property of our ABI that we control and
1337 /// ensure.
1338 pub(crate) unsafe fn wasm_exit_fp_from_trampoline_fp(trampoline_fp: usize) -> usize {
1339 if trampoline_fp != 0 {
1340 // SAFETY: We require that trampoline_fp points to a valid
1341 // frame, which will (by definition) contain an old FP value
1342 // that we can load.
1343 unsafe { *(trampoline_fp as *const usize) }
1344 } else {
1345 0
1346 }
1347 }
1348}
1349
1350// The `VMStoreContext` type is a pod-type with no destructor, and we don't
1351// access any fields from other threads, so add in these trait impls which are
1352// otherwise not available due to the `fuel_consumed` and `epoch_deadline`
1353// variables in `VMStoreContext`.
1354unsafe impl Send for VMStoreContext {}
1355unsafe impl Sync for VMStoreContext {}
1356
1357// SAFETY: the above structure is repr(C) and only contains `VmSafe` fields.
1358unsafe impl VmSafe for VMStoreContext {}
1359
1360impl Default for VMStoreContext {
1361 fn default() -> VMStoreContext {
1362 VMStoreContext {
1363 fuel_consumed: UnsafeCell::new(0),
1364 epoch_deadline: UnsafeCell::new(0),
1365 execution_version: 0,
1366 stack_limit: UnsafeCell::new(usize::max_value()),
1367 gc_heap: VMMemoryDefinition {
1368 base: NonNull::dangling().into(),
1369 current_length: AtomicUsize::new(0),
1370 },
1371 last_wasm_exit_trampoline_fp: UnsafeCell::new(0),
1372 last_wasm_exit_pc: UnsafeCell::new(0),
1373 last_wasm_entry_fp: UnsafeCell::new(0),
1374 last_wasm_entry_sp: UnsafeCell::new(0),
1375 last_wasm_entry_trap_handler: UnsafeCell::new(0),
1376 stack_chain: UnsafeCell::new(VMStackChain::Absent),
1377 async_guard_range: ptr::null_mut()..ptr::null_mut(),
1378 store_data: VmPtr::dangling(),
1379 component_context: [0; NUM_COMPONENT_CONTEXT_SLOTS],
1380 }
1381 }
1382}
1383
1384#[cfg(test)]
1385mod test_vmstore_context {
1386 use super::{VMMemoryDefinition, VMStoreContext};
1387 use core::mem::offset_of;
1388 use wasmtime_environ::{HostPtr, Module, PtrSize, StaticModuleIndex, VMOffsets};
1389
1390 #[test]
1391 fn field_offsets() {
1392 let module = Module::new(StaticModuleIndex::from_u32(0));
1393 let offsets = VMOffsets::new(HostPtr, &module);
1394 assert_eq!(
1395 offset_of!(VMStoreContext, stack_limit),
1396 usize::from(offsets.ptr.vmstore_context_stack_limit())
1397 );
1398 assert_eq!(
1399 offset_of!(VMStoreContext, fuel_consumed),
1400 usize::from(offsets.ptr.vmstore_context_fuel_consumed())
1401 );
1402 assert_eq!(
1403 offset_of!(VMStoreContext, epoch_deadline),
1404 usize::from(offsets.ptr.vmstore_context_epoch_deadline())
1405 );
1406 assert_eq!(
1407 offset_of!(VMStoreContext, execution_version),
1408 usize::from(offsets.ptr.vmstore_context_execution_version())
1409 );
1410 assert_eq!(
1411 offset_of!(VMStoreContext, gc_heap),
1412 usize::from(offsets.ptr.vmstore_context_gc_heap())
1413 );
1414 assert_eq!(
1415 offset_of!(VMStoreContext, gc_heap) + offset_of!(VMMemoryDefinition, base),
1416 usize::from(offsets.ptr.vmstore_context_gc_heap_base())
1417 );
1418 assert_eq!(
1419 offset_of!(VMStoreContext, gc_heap) + offset_of!(VMMemoryDefinition, current_length),
1420 usize::from(offsets.ptr.vmstore_context_gc_heap_current_length())
1421 );
1422 assert_eq!(
1423 offset_of!(VMStoreContext, last_wasm_exit_trampoline_fp),
1424 usize::from(offsets.ptr.vmstore_context_last_wasm_exit_trampoline_fp())
1425 );
1426 assert_eq!(
1427 offset_of!(VMStoreContext, last_wasm_exit_pc),
1428 usize::from(offsets.ptr.vmstore_context_last_wasm_exit_pc())
1429 );
1430 assert_eq!(
1431 offset_of!(VMStoreContext, last_wasm_entry_fp),
1432 usize::from(offsets.ptr.vmstore_context_last_wasm_entry_fp())
1433 );
1434 assert_eq!(
1435 offset_of!(VMStoreContext, last_wasm_entry_sp),
1436 usize::from(offsets.ptr.vmstore_context_last_wasm_entry_sp())
1437 );
1438 assert_eq!(
1439 offset_of!(VMStoreContext, last_wasm_entry_trap_handler),
1440 usize::from(offsets.ptr.vmstore_context_last_wasm_entry_trap_handler())
1441 );
1442 assert_eq!(
1443 offset_of!(VMStoreContext, stack_chain),
1444 usize::from(offsets.ptr.vmstore_context_stack_chain())
1445 );
1446 assert_eq!(
1447 offset_of!(VMStoreContext, store_data),
1448 usize::from(offsets.ptr.vmstore_context_store_data())
1449 );
1450 assert_eq!(
1451 offset_of!(VMStoreContext, component_context),
1452 usize::from(offsets.ptr.vmstore_context_component_context_slot(0))
1453 );
1454
1455 // Make sure that the calculation for the size of a slot is also
1456 // accurate.
1457 let slot_width = offsets.ptr.vmstore_context_component_context_slot(1)
1458 - offsets.ptr.vmstore_context_component_context_slot(0);
1459 let default = VMStoreContext::default();
1460 assert_eq!(
1461 size_of_val(&default.component_context[0]),
1462 usize::from(slot_width)
1463 );
1464 }
1465}
1466
1467/// The VM "context", which is pointed to by the `vmctx` arg in Cranelift.
1468/// This has information about globals, memories, tables, and other runtime
1469/// state associated with the current instance.
1470///
1471/// The struct here is empty, as the sizes of these fields are dynamic, and
1472/// we can't describe them in Rust's type system. Sufficient memory is
1473/// allocated at runtime.
1474#[derive(Debug)]
1475#[repr(C, align(16))] // align 16 since globals are aligned to that and contained inside
1476pub struct VMContext {
1477 _magic: u32,
1478}
1479
1480impl VMContext {
1481 /// Helper function to cast between context types using a debug assertion to
1482 /// protect against some mistakes.
1483 #[inline]
1484 pub unsafe fn from_opaque(opaque: NonNull<VMOpaqueContext>) -> NonNull<VMContext> {
1485 // Note that in general the offset of the "magic" field is stored in
1486 // `VMOffsets::vmctx_magic`. Given though that this is a sanity check
1487 // about converting this pointer to another type we ideally don't want
1488 // to read the offset from potentially corrupt memory. Instead it would
1489 // be better to catch errors here as soon as possible.
1490 //
1491 // To accomplish this the `VMContext` structure is laid out with the
1492 // magic field at a statically known offset (here it's 0 for now). This
1493 // static offset is asserted in `VMOffsets::from` and needs to be kept
1494 // in sync with this line for this debug assertion to work.
1495 //
1496 // Also note that this magic is only ever invalid in the presence of
1497 // bugs, meaning we don't actually read the magic and act differently
1498 // at runtime depending what it is, so this is a debug assertion as
1499 // opposed to a regular assertion.
1500 unsafe {
1501 debug_assert_eq!(opaque.as_ref().magic, VMCONTEXT_MAGIC);
1502 }
1503 opaque.cast()
1504 }
1505}
1506
1507/// A "raw" and unsafe representation of a WebAssembly value.
1508///
1509/// This is provided for use with the `Func::new_unchecked` and
1510/// `Func::call_unchecked` APIs. In general it's unlikely you should be using
1511/// this from Rust, rather using APIs like `Func::wrap` and `TypedFunc::call`.
1512///
1513/// This is notably an "unsafe" way to work with `Val` and it's recommended to
1514/// instead use `Val` where possible. An important note about this union is that
1515/// fields are all stored in little-endian format, regardless of the endianness
1516/// of the host system.
1517#[repr(C)]
1518#[derive(Copy, Clone)]
1519pub union ValRaw {
1520 /// A WebAssembly `i32` value.
1521 ///
1522 /// Note that the payload here is a Rust `i32` but the WebAssembly `i32`
1523 /// type does not assign an interpretation of the upper bit as either signed
1524 /// or unsigned. The Rust type `i32` is simply chosen for convenience.
1525 ///
1526 /// This value is always stored in a little-endian format.
1527 i32: i32,
1528
1529 /// A WebAssembly `i64` value.
1530 ///
1531 /// Note that the payload here is a Rust `i64` but the WebAssembly `i64`
1532 /// type does not assign an interpretation of the upper bit as either signed
1533 /// or unsigned. The Rust type `i64` is simply chosen for convenience.
1534 ///
1535 /// This value is always stored in a little-endian format.
1536 i64: i64,
1537
1538 /// A WebAssembly `f32` value.
1539 ///
1540 /// Note that the payload here is a Rust `u32`. This is to allow passing any
1541 /// representation of NaN into WebAssembly without risk of changing NaN
1542 /// payload bits as its gets passed around the system. Otherwise though this
1543 /// `u32` value is the return value of `f32::to_bits` in Rust.
1544 ///
1545 /// This value is always stored in a little-endian format.
1546 f32: u32,
1547
1548 /// A WebAssembly `f64` value.
1549 ///
1550 /// Note that the payload here is a Rust `u64`. This is to allow passing any
1551 /// representation of NaN into WebAssembly without risk of changing NaN
1552 /// payload bits as its gets passed around the system. Otherwise though this
1553 /// `u64` value is the return value of `f64::to_bits` in Rust.
1554 ///
1555 /// This value is always stored in a little-endian format.
1556 f64: u64,
1557
1558 /// A WebAssembly `v128` value.
1559 ///
1560 /// The payload here is a Rust `[u8; 16]` which has the same number of bits
1561 /// but note that `v128` in WebAssembly is often considered a vector type
1562 /// such as `i32x4` or `f64x2`. This means that the actual interpretation
1563 /// of the underlying bits is left up to the instructions which consume
1564 /// this value.
1565 ///
1566 /// This value is always stored in a little-endian format.
1567 v128: [u8; 16],
1568
1569 /// A WebAssembly `funcref` value (or one of its subtypes).
1570 ///
1571 /// The payload here is a pointer which is runtime-defined. This is one of
1572 /// the main points of unsafety about the `ValRaw` type as the validity of
1573 /// the pointer here is not easily verified and must be preserved by
1574 /// carefully calling the correct functions throughout the runtime.
1575 ///
1576 /// This value is always stored in a little-endian format.
1577 funcref: *mut c_void,
1578
1579 /// A WebAssembly `externref` value (or one of its subtypes).
1580 ///
1581 /// The payload here is a compressed pointer value which is
1582 /// runtime-defined. This is one of the main points of unsafety about the
1583 /// `ValRaw` type as the validity of the pointer here is not easily verified
1584 /// and must be preserved by carefully calling the correct functions
1585 /// throughout the runtime.
1586 ///
1587 /// This value is always stored in a little-endian format.
1588 externref: u32,
1589
1590 /// A WebAssembly `anyref` value (or one of its subtypes).
1591 ///
1592 /// The payload here is a compressed pointer value which is
1593 /// runtime-defined. This is one of the main points of unsafety about the
1594 /// `ValRaw` type as the validity of the pointer here is not easily verified
1595 /// and must be preserved by carefully calling the correct functions
1596 /// throughout the runtime.
1597 ///
1598 /// This value is always stored in a little-endian format.
1599 anyref: u32,
1600
1601 /// A WebAssembly `exnref` value (or one of its subtypes).
1602 ///
1603 /// The payload here is a compressed pointer value which is
1604 /// runtime-defined. This is one of the main points of unsafety about the
1605 /// `ValRaw` type as the validity of the pointer here is not easily verified
1606 /// and must be preserved by carefully calling the correct functions
1607 /// throughout the runtime.
1608 ///
1609 /// This value is always stored in a little-endian format.
1610 exnref: u32,
1611}
1612
1613// The `ValRaw` type is matched as `wasmtime_val_raw_t` in the C API so these
1614// are some simple assertions about the shape of the type which are additionally
1615// matched in C.
1616const _: () = {
1617 assert!(mem::size_of::<ValRaw>() == 16);
1618 assert!(mem::align_of::<ValRaw>() == mem::align_of::<u64>());
1619};
1620
1621// This type is just a bag-of-bits so it's up to the caller to figure out how
1622// to safely deal with threading concerns and safely access interior bits.
1623unsafe impl Send for ValRaw {}
1624unsafe impl Sync for ValRaw {}
1625
1626impl fmt::Debug for ValRaw {
1627 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1628 struct Hex<T>(T);
1629 impl<T: fmt::LowerHex> fmt::Debug for Hex<T> {
1630 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1631 let bytes = mem::size_of::<T>();
1632 let hex_digits_per_byte = 2;
1633 let hex_digits = bytes * hex_digits_per_byte;
1634 write!(f, "0x{:0width$x}", self.0, width = hex_digits)
1635 }
1636 }
1637
1638 unsafe {
1639 f.debug_struct("ValRaw")
1640 .field("i32", &Hex(self.i32))
1641 .field("i64", &Hex(self.i64))
1642 .field("f32", &Hex(self.f32))
1643 .field("f64", &Hex(self.f64))
1644 .field("v128", &Hex(u128::from_le_bytes(self.v128)))
1645 .field("funcref", &self.funcref)
1646 .field("externref", &Hex(self.externref))
1647 .field("anyref", &Hex(self.anyref))
1648 .field("exnref", &Hex(self.exnref))
1649 .finish()
1650 }
1651 }
1652}
1653
1654impl ValRaw {
1655 /// Create a null reference that is compatible with any of
1656 /// `{any,extern,func,exn}ref`.
1657 pub fn null() -> ValRaw {
1658 unsafe {
1659 let raw = mem::MaybeUninit::<Self>::zeroed().assume_init();
1660 debug_assert_eq!(raw.get_anyref(), 0);
1661 debug_assert_eq!(raw.get_exnref(), 0);
1662 debug_assert_eq!(raw.get_externref(), 0);
1663 debug_assert_eq!(raw.get_funcref(), ptr::null_mut());
1664 raw
1665 }
1666 }
1667
1668 /// Creates a WebAssembly `i32` value
1669 #[inline]
1670 pub fn i32(i: i32) -> ValRaw {
1671 // Note that this is intentionally not setting the `i32` field, instead
1672 // setting the `i64` field with a zero-extended version of `i`. For more
1673 // information on this see the comments on `Lower for Result` in the
1674 // `wasmtime` crate. Otherwise though all `ValRaw` constructors are
1675 // otherwise constrained to guarantee that the initial 64-bits are
1676 // always initialized.
1677 ValRaw::u64(i.cast_unsigned().into())
1678 }
1679
1680 /// Creates a WebAssembly `i64` value
1681 #[inline]
1682 pub fn i64(i: i64) -> ValRaw {
1683 ValRaw { i64: i.to_le() }
1684 }
1685
1686 /// Creates a WebAssembly `i32` value
1687 #[inline]
1688 pub fn u32(i: u32) -> ValRaw {
1689 // See comments in `ValRaw::i32` for why this is setting the upper
1690 // 32-bits as well.
1691 ValRaw::u64(i.into())
1692 }
1693
1694 /// Creates a WebAssembly `i64` value
1695 #[inline]
1696 pub fn u64(i: u64) -> ValRaw {
1697 ValRaw::i64(i as i64)
1698 }
1699
1700 /// Creates a WebAssembly `f32` value
1701 #[inline]
1702 pub fn f32(i: u32) -> ValRaw {
1703 // See comments in `ValRaw::i32` for why this is setting the upper
1704 // 32-bits as well.
1705 ValRaw::u64(i.into())
1706 }
1707
1708 /// Creates a WebAssembly `f64` value
1709 #[inline]
1710 pub fn f64(i: u64) -> ValRaw {
1711 ValRaw { f64: i.to_le() }
1712 }
1713
1714 /// Creates a WebAssembly `v128` value
1715 #[inline]
1716 pub fn v128(i: u128) -> ValRaw {
1717 ValRaw {
1718 v128: i.to_le_bytes(),
1719 }
1720 }
1721
1722 /// Creates a WebAssembly `funcref` value
1723 #[inline]
1724 pub fn funcref(i: *mut c_void) -> ValRaw {
1725 ValRaw {
1726 funcref: i.map_addr(|i| i.to_le()),
1727 }
1728 }
1729
1730 /// Creates a WebAssembly `externref` value
1731 #[inline]
1732 pub fn externref(e: u32) -> ValRaw {
1733 assert!(cfg!(feature = "gc") || e == 0);
1734 ValRaw {
1735 externref: e.to_le(),
1736 }
1737 }
1738
1739 /// Creates a WebAssembly `anyref` value
1740 #[inline]
1741 pub fn anyref(r: u32) -> ValRaw {
1742 assert!(cfg!(feature = "gc") || r == 0);
1743 ValRaw { anyref: r.to_le() }
1744 }
1745
1746 /// Creates a WebAssembly `exnref` value
1747 #[inline]
1748 pub fn exnref(r: u32) -> ValRaw {
1749 assert!(cfg!(feature = "gc") || r == 0);
1750 ValRaw { exnref: r.to_le() }
1751 }
1752
1753 #[inline]
1754 pub(crate) fn vmgcref(r: Option<VMGcRef>) -> ValRaw {
1755 let raw = r.map_or(0, |r| r.as_raw_u32());
1756
1757 // NB: All `VMGcRef`-based `ValRaw`s are the same.
1758 debug_assert_eq!(raw, ValRaw::anyref(raw).get_exnref());
1759 debug_assert_eq!(raw, ValRaw::exnref(raw).get_externref());
1760 debug_assert_eq!(raw, ValRaw::externref(raw).get_anyref());
1761
1762 ValRaw::anyref(raw)
1763 }
1764
1765 /// Gets the WebAssembly `i32` value
1766 #[inline]
1767 pub fn get_i32(&self) -> i32 {
1768 unsafe { i32::from_le(self.i32) }
1769 }
1770
1771 /// Gets the WebAssembly `i64` value
1772 #[inline]
1773 pub fn get_i64(&self) -> i64 {
1774 unsafe { i64::from_le(self.i64) }
1775 }
1776
1777 /// Gets the WebAssembly `i32` value
1778 #[inline]
1779 pub fn get_u32(&self) -> u32 {
1780 self.get_i32().cast_unsigned()
1781 }
1782
1783 /// Gets the WebAssembly `i64` value
1784 #[inline]
1785 pub fn get_u64(&self) -> u64 {
1786 self.get_i64().cast_unsigned()
1787 }
1788
1789 /// Gets the WebAssembly `f32` value
1790 #[inline]
1791 pub fn get_f32(&self) -> u32 {
1792 unsafe { u32::from_le(self.f32) }
1793 }
1794
1795 /// Gets the WebAssembly `f64` value
1796 #[inline]
1797 pub fn get_f64(&self) -> u64 {
1798 unsafe { u64::from_le(self.f64) }
1799 }
1800
1801 /// Gets the WebAssembly `v128` value
1802 #[inline]
1803 pub fn get_v128(&self) -> u128 {
1804 unsafe { u128::from_le_bytes(self.v128) }
1805 }
1806
1807 /// Gets the WebAssembly `funcref` value
1808 #[inline]
1809 pub fn get_funcref(&self) -> *mut c_void {
1810 let addr = unsafe { usize::from_le(self.funcref.addr()) };
1811 core::ptr::with_exposed_provenance_mut(addr)
1812 }
1813
1814 /// Gets the WebAssembly `externref` value
1815 #[inline]
1816 pub fn get_externref(&self) -> u32 {
1817 let externref = u32::from_le(unsafe { self.externref });
1818 assert!(cfg!(feature = "gc") || externref == 0);
1819 externref
1820 }
1821
1822 /// Gets the WebAssembly `anyref` value
1823 #[inline]
1824 pub fn get_anyref(&self) -> u32 {
1825 let anyref = u32::from_le(unsafe { self.anyref });
1826 assert!(cfg!(feature = "gc") || anyref == 0);
1827 anyref
1828 }
1829
1830 /// Gets the WebAssembly `exnref` value
1831 #[inline]
1832 pub fn get_exnref(&self) -> u32 {
1833 let exnref = u32::from_le(unsafe { self.exnref });
1834 assert!(cfg!(feature = "gc") || exnref == 0);
1835 exnref
1836 }
1837
1838 /// Get the inner `VMGcRef`.
1839 pub(crate) fn get_vmgcref(&self) -> Option<crate::vm::VMGcRef> {
1840 debug_assert_eq!(self.get_anyref(), self.get_exnref());
1841 debug_assert_eq!(self.get_anyref(), self.get_externref());
1842 VMGcRef::from_raw_u32(self.get_anyref())
1843 }
1844}
1845
1846/// An "opaque" version of `VMContext` which must be explicitly casted to a
1847/// target context.
1848///
1849/// This context is used to represent that contexts specified in
1850/// `VMFuncRef` can have any type and don't have an implicit
1851/// structure. Neither wasmtime nor cranelift-generated code can rely on the
1852/// structure of an opaque context in general and only the code which configured
1853/// the context is able to rely on a particular structure. This is because the
1854/// context pointer configured for `VMFuncRef` is guaranteed to be
1855/// the first parameter passed.
1856///
1857/// Note that Wasmtime currently has a layout where all contexts that are casted
1858/// to an opaque context start with a 32-bit "magic" which can be used in debug
1859/// mode to debug-assert that the casts here are correct and have at least a
1860/// little protection against incorrect casts.
1861pub struct VMOpaqueContext {
1862 pub(crate) magic: u32,
1863 _marker: marker::PhantomPinned,
1864}
1865
1866impl VMOpaqueContext {
1867 /// Helper function to clearly indicate that casts are desired.
1868 #[inline]
1869 pub fn from_vmcontext(ptr: NonNull<VMContext>) -> NonNull<VMOpaqueContext> {
1870 ptr.cast()
1871 }
1872
1873 /// Helper function to clearly indicate that casts are desired.
1874 #[inline]
1875 pub fn from_vm_array_call_host_func_context(
1876 ptr: NonNull<VMArrayCallHostFuncContext>,
1877 ) -> NonNull<VMOpaqueContext> {
1878 ptr.cast()
1879 }
1880}