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::{VMContinuationStack, VMStackChain, VMStackState};
11use core::cell::UnsafeCell;
12use core::ffi::c_void;
13use core::fmt;
14use core::marker::{self, PhantomPinned};
15use core::mem::{self, ManuallyDrop, MaybeUninit};
16#[cfg(feature = "gc-null")]
17use core::num::NonZeroU32;
18use core::ops::Range;
19use core::ptr::{self, NonNull};
20use core::sync::atomic::{AtomicUsize, Ordering};
21use wasmtime_environ::{
22 BuiltinFunctionIndex, DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex,
23 DefinedTagIndex, NUM_COMPONENT_CONTEXT_SLOTS, VMCONTEXT_MAGIC, VMSharedTypeIndex,
24};
25
26/// A function pointer that exposes the array calling convention.
27///
28/// Regardless of the underlying Wasm function type, all functions using the
29/// array calling convention have the same Rust signature.
30///
31/// Arguments:
32///
33/// * Callee `vmctx` for the function itself.
34///
35/// * Caller's `vmctx` (so that host functions can access the linear memory of
36/// their Wasm callers).
37///
38/// * A pointer to a buffer of `ValRaw`s where both arguments are passed into
39/// this function, and where results are returned from this function.
40///
41/// * The capacity of the `ValRaw` buffer. Must always be at least
42/// `max(len(wasm_params), len(wasm_results))`.
43///
44/// Return value:
45///
46/// * `true` if this call succeeded.
47/// * `false` if this call failed and a trap was recorded in TLS.
48pub type VMArrayCallNative = unsafe extern "C" fn(
49 NonNull<VMOpaqueContext>,
50 NonNull<VMContext>,
51 NonNull<ValRaw>,
52 usize,
53) -> bool;
54
55/// An opaque function pointer which might be `VMArrayCallNative` or it might be
56/// pulley bytecode. Requires external knowledge to determine what kind of
57/// function pointer this is.
58#[repr(transparent)]
59pub struct VMArrayCallFunction(VMFunctionBody);
60
61/// A function pointer that exposes the Wasm calling convention.
62///
63/// In practice, different Wasm function types end up mapping to different Rust
64/// function types, so this isn't simply a type alias the way that
65/// `VMArrayCallFunction` is. However, the exact details of the calling
66/// convention are left to the Wasm compiler (e.g. Cranelift or Winch). Runtime
67/// code never does anything with these function pointers except shuffle them
68/// around and pass them back to Wasm.
69#[repr(transparent)]
70pub struct VMWasmCallFunction(VMFunctionBody);
71
72// SAFETY: `VMFunctionImport` is generated with `#[repr(C)]` and only contains
73// `VmSafe` fields.
74unsafe impl VmSafe for VMFunctionImport {}
75
76impl VMFunctionImport {
77 /// Convert `&VMFunctionImport` into `&VMFuncRef`.
78 pub fn as_func_ref(&self) -> &VMFuncRef {
79 // Safety: `VMFunctionImport` and `VMFuncRef` have the same
80 // representation.
81 unsafe { Self::as_non_null_func_ref(NonNull::from(self)).as_ref() }
82 }
83
84 /// Convert `NonNull<VMFunctionImport>` into `NonNull<VMFuncRef>`.
85 pub fn as_non_null_func_ref(p: NonNull<VMFunctionImport>) -> NonNull<VMFuncRef> {
86 p.cast()
87 }
88
89 /// Convert `*mut VMFunctionImport` into `*mut VMFuncRef`.
90 pub fn as_func_ref_ptr(p: *mut VMFunctionImport) -> *mut VMFuncRef {
91 p.cast()
92 }
93}
94
95#[cfg(test)]
96mod test_vmfunction_import {
97 use super::{VMFuncRef, VMFunctionImport};
98 use core::mem::offset_of;
99 use std::mem::size_of;
100
101 #[test]
102 fn vmfunction_import_and_vmfunc_ref_have_same_layout() {
103 assert_eq!(size_of::<VMFunctionImport>(), size_of::<VMFuncRef>());
104 assert_eq!(
105 offset_of!(VMFunctionImport, array_call),
106 offset_of!(VMFuncRef, array_call),
107 );
108 assert_eq!(
109 offset_of!(VMFunctionImport, wasm_call),
110 offset_of!(VMFuncRef, wasm_call),
111 );
112 assert_eq!(
113 offset_of!(VMFunctionImport, type_index),
114 offset_of!(VMFuncRef, type_index),
115 );
116 assert_eq!(
117 offset_of!(VMFunctionImport, vmctx),
118 offset_of!(VMFuncRef, vmctx),
119 );
120 }
121}
122
123/// A placeholder byte-sized type which is just used to provide some amount of type
124/// safety when dealing with pointers to JIT-compiled function bodies. Note that it's
125/// deliberately not Copy, as we shouldn't be carelessly copying function body bytes
126/// around.
127#[repr(C)]
128pub struct VMFunctionBody(u8);
129
130// SAFETY: this structure is never read and is safe to pass to jit code.
131unsafe impl VmSafe for VMFunctionBody {}
132
133#[cfg(test)]
134mod test_vmfunction_body {
135 use super::VMFunctionBody;
136 use std::mem::size_of;
137
138 #[test]
139 fn check_vmfunction_body_offsets() {
140 assert_eq!(size_of::<VMFunctionBody>(), 1);
141 }
142}
143
144// SAFETY: `VMTableImport` is generated with `#[repr(C)]` and only contains
145// `VmSafe` fields.
146unsafe impl VmSafe for VMTableImport {}
147
148#[cfg(test)]
149mod test_vmtable {
150 use wasmtime_environ::component::{Component, VMComponentOffsets};
151 use wasmtime_environ::{HostPtr, Module, PtrSize, StaticModuleIndex, VMOffsets};
152
153 #[test]
154 fn ensure_sizes_match() {
155 // Because we use `VMTableImport` for recording tables used by components, we
156 // want to make sure that the size calculations between `VMOffsets` and
157 // `VMComponentOffsets` stay the same.
158 let module = Module::new(StaticModuleIndex::from_u32(0));
159 let vm_offsets = VMOffsets::new(HostPtr, &module);
160 let component = Component::default();
161 let vm_component_offsets = VMComponentOffsets::new(HostPtr, &component);
162 assert_eq!(
163 vm_offsets.ptr.vm_table_import().size(),
164 vm_component_offsets.ptr.vm_table_import().size()
165 );
166 }
167}
168
169// SAFETY: `VMMemoryImport` is generated with `#[repr(C)]` and only contains
170// `VmSafe` fields.
171unsafe impl VmSafe for VMMemoryImport {}
172
173// SAFETY: `VMGlobalImport` is generated with `#[repr(C)]` and only contains
174// `VmSafe` fields.
175unsafe impl VmSafe for VMGlobalImport {}
176
177/// The kinds of globals that Wasmtime has.
178#[derive(Debug, Copy, Clone)]
179#[repr(C, u32)]
180pub enum VMGlobalKind {
181 /// Host globals, stored in a `StoreOpaque`.
182 Host(DefinedGlobalIndex),
183 /// Instance globals, stored in `VMContext`s
184 Instance(DefinedGlobalIndex),
185 /// Flags for a component instance, stored in `VMComponentContext`.
186 #[cfg(feature = "component-model")]
187 ComponentFlags(wasmtime_environ::component::RuntimeComponentInstanceIndex),
188}
189
190// SAFETY: the above enum is repr(C) and stores nothing else
191unsafe impl VmSafe for VMGlobalKind {}
192
193// SAFETY: `VMTagImport` is generated with `#[repr(C)]` and only contains
194// `VmSafe` fields.
195unsafe impl VmSafe for VMTagImport {}
196
197/// Define the runtime definitions of the shared `VM*` types.
198#[allow(
199 unused_macro_rules,
200 reason = "the `@test` arms are only expanded inside the `#[cfg(test)]` \
201 layout-test module, and the enum arms go unused whenever no \
202 `VM*` type is an enum"
203)]
204macro_rules! define_vm_types {
205 // Emit the layout test for one `struct` VM type: its size and alignment,
206 // plus each field's offset, checked against the real Rust type.
207 (@test [ $($cfg:tt)* ] $Name:ident $snake:ident [ $($fname:ident)* ]) => {
208 $($cfg)*
209 #[test]
210 fn $snake() {
211 use super::$Name;
212
213 let host = HostPtr;
214 let offsets = host.$snake();
215
216 let expected = usize::from(offsets.size());
217 let actual = size_of::<$Name>();
218 assert_eq!(
219 expected,
220 actual,
221 "size of {} failed: {expected} (expected) != {actual} (actual)",
222 stringify!($Name),
223 );
224
225 let expected = usize::from(offsets.align());
226 let actual = align_of::<$Name>();
227 assert_eq!(
228 expected,
229 actual,
230 "alignment of {} failed: {expected} (expected) != {actual} (actual)",
231 stringify!($Name),
232 );
233
234 $(
235 let expected = usize::from(offsets.$fname());
236 let actual = offset_of!($Name, $fname);
237 assert_eq!(
238 expected,
239 actual,
240 "offset of {}::{} failed: {expected} (expected) != {actual} (actual)",
241 stringify!($Name),
242 stringify!($fname),
243 );
244 )*
245 }
246 };
247
248 // Emit the layout test for one `#[repr(C, $tag)]` enum VM type.
249 //
250 // `offset_of!` does not work on enum variants, so instead of measuring a
251 // variant field's offset this checks the generated offsets against the
252 // layout rule `#[repr(C, $tag)]` guarantees, evaluated with each field
253 // type's real `size_of`/`align_of`.
254 (@test_enum [ $($cfg:tt)* ] $Name:ident $snake:ident $tag:ty [
255 $( { $vsnake:ident $Variant:ident [ $( $fname:ident : $fty:ty , )* ] } )*
256 ]) => {
257 $($cfg)*
258 #[test]
259 fn $snake() {
260 use super::$Name;
261
262 let host = HostPtr;
263 let offsets = host.$snake();
264
265 let expected = usize::from(offsets.size());
266 let actual = size_of::<$Name>();
267 assert_eq!(
268 expected,
269 actual,
270 "size of {} failed: {expected} (expected) != {actual} (actual)",
271 stringify!($Name),
272 );
273
274 let expected = usize::from(offsets.align());
275 let actual = align_of::<$Name>();
276 assert_eq!(
277 expected,
278 actual,
279 "alignment of {} failed: {expected} (expected) != {actual} (actual)",
280 stringify!($Name),
281 );
282
283 let expected = usize::from(offsets.tag_size());
284 let actual = size_of::<$tag>();
285 assert_eq!(
286 expected,
287 actual,
288 "tag size of {} failed: {expected} (expected) != {actual} (actual)",
289 stringify!($Name),
290 );
291
292 // The payload of a `#[repr(C, $tag)]` enum starts just past the
293 // tag, rounded up to the enum's alignment.
294 let expected = usize::from(offsets.payload());
295 let actual = align_up(size_of::<$tag>(), align_of::<$Name>());
296 assert_eq!(
297 expected,
298 actual,
299 "payload offset of {} failed: {expected} (expected) != {actual} (actual)",
300 stringify!($Name),
301 );
302
303 $(
304 // A variant with no fields leaves both untouched.
305 #[allow(
306 unused_assignments,
307 unused_mut,
308 unused_variables,
309 reason = "a variant may have no fields, and the last \
310 field's size is accumulated but never read"
311 )]
312 {
313 let variant = offsets.$vsnake();
314 let mut o = usize::from(offsets.payload());
315 $(
316 o = align_up(o, align_of::<$fty>());
317 let expected = usize::from(variant.$fname());
318 assert_eq!(
319 expected,
320 o,
321 "offset of {}::{}::{} failed: \
322 {expected} (expected) != {o} (actual)",
323 stringify!($Name),
324 stringify!($Variant),
325 stringify!($fname),
326 );
327 o += size_of::<$fty>();
328 )*
329 }
330 )*
331 }
332 };
333
334 // Munched one at a time rather than matched as a repetition because a
335 // `struct` and an `enum` do not share a body shape.
336 (@munch { $($defs:tt)* } { $($tests:tt)* }) => {
337 $($defs)*
338
339 #[cfg(test)]
340 #[allow(
341 unused_imports,
342 dead_code,
343 reason = "the glob import and `align_up` are only used by the enum \
344 tests, and not every `VM*` type is an enum"
345 )]
346 mod test_vm_type_layouts {
347 // The enum tests name variant fields' types, so the module needs
348 // everything the definitions above were written against.
349 use super::*;
350 use core::mem::{align_of, offset_of, size_of};
351 use wasmtime_environ::{HostPtr, PtrSize};
352
353 /// Round `offset` up to a multiple of `align`.
354 fn align_up(offset: usize, align: usize) -> usize {
355 offset.next_multiple_of(align)
356 }
357
358 $($tests)*
359 }
360 };
361
362 (@munch { $($defs:tt)* } { $($tests:tt)* }
363 $(#[doc = $sdoc:literal])*
364 $(#[cfg($($scfg:tt)*)])?
365 $(#[derive($($d:ident),*)])?
366 #[repr(C, $tag:ident)]
367 #[snake_name = $snake:ident]
368 $svis:vis enum $Name:ident {
369 $(
370 $(#[doc = $vdoc:literal])*
371 #[snake_name = $vsnake:ident]
372 $Variant:ident {
373 $(
374 $(#[doc = $fdoc:literal])*
375 $(#[aggregate])?
376 $(#[indexed])?
377 $(#[readonly])?
378 $(#[can_move])?
379 $fname:ident : $fty:ty ,
380 )*
381 } = $disc:literal ,
382 )*
383 }
384 $($rest:tt)*
385 ) => {
386 define_vm_types!(
387 @munch
388 {
389 $($defs)*
390
391 $(#[doc = $sdoc])*
392 $(#[cfg($($scfg)*)])?
393 $(#[derive($($d),*)])?
394 #[repr(C, $tag)]
395 $svis enum $Name {
396 $(
397 $(#[doc = $vdoc])*
398 $Variant {
399 $(
400 $(#[doc = $fdoc])*
401 $fname: $fty,
402 )*
403 } = $disc,
404 )*
405 }
406 }
407 {
408 $($tests)*
409
410 define_vm_types!(
411 @test_enum
412 [ $(#[cfg($($scfg)*)])? ]
413 $Name $snake $tag
414 [ $( { $vsnake $Variant [ $( $fname : $fty , )* ] } )* ]
415 );
416 }
417 $($rest)*
418 );
419 };
420
421 (@munch { $($defs:tt)* } { $($tests:tt)* }
422 $(#[doc = $sdoc:literal])*
423 $(#[cfg($($scfg:tt)*)])?
424 $(#[derive($($d:ident),*)])?
425 #[repr($($repr:tt)*)]
426 #[snake_name = $snake:ident]
427 $svis:vis struct $Name:ident {
428 $(
429 $(#[doc = $fdoc:literal])*
430 $(#[aggregate])?
431 $(#[indexed])?
432 $(#[readonly])?
433 $(#[can_move])?
434 // Unlike the offsets and alias-region consumers, this one only
435 // ever re-emits a field's type verbatim, so it can capture the
436 // whole type as one fragment instead of taking it apart.
437 $fvis:vis $fname:ident : $fty:ty ,
438 )*
439 }
440 $($rest:tt)*
441 ) => {
442 define_vm_types!(
443 @munch
444 {
445 $($defs)*
446
447 $(#[doc = $sdoc])*
448 $(#[cfg($($scfg)*)])?
449 $(#[derive($($d),*)])?
450 #[repr($($repr)*)]
451 $svis struct $Name {
452 $(
453 $(#[doc = $fdoc])*
454 $fvis $fname: $fty,
455 )*
456 }
457 }
458 {
459 $($tests)*
460
461 define_vm_types!(
462 @test
463 [ $(#[cfg($($scfg)*)])? ]
464 $Name $snake
465 [ $($fname)* ]
466 );
467 }
468 $($rest)*
469 );
470 };
471
472 ( $($defs:tt)* ) => {
473 define_vm_types!(@munch {} {} $($defs)*);
474 };
475}
476wasmtime_environ::for_each_vm_type!(define_vm_types);
477
478// SAFETY: `VMMemoryDefinition` is generated with `#[repr(C)]` and each field
479// individually implements `VmSafe`, which satisfies the requirements of this
480// trait.
481unsafe impl VmSafe for VMMemoryDefinition {}
482
483impl VMMemoryDefinition {
484 /// Return the current length (in bytes) of the [`VMMemoryDefinition`] by
485 /// performing a relaxed load; do not use this function for situations in
486 /// which a precise length is needed. Owned memories (i.e., non-shared) will
487 /// always return a precise result (since no concurrent modification is
488 /// possible) but shared memories may see an imprecise value--a
489 /// `current_length` potentially smaller than what some other thread
490 /// observes. Since Wasm memory only grows, this under-estimation may be
491 /// acceptable in certain cases.
492 #[inline]
493 pub fn current_length(&self) -> usize {
494 self.current_length.load(Ordering::Relaxed)
495 }
496
497 /// Return a copy of the [`VMMemoryDefinition`] using the relaxed value of
498 /// `current_length`; see [`VMMemoryDefinition::current_length()`].
499 #[inline]
500 pub unsafe fn load(ptr: *mut Self) -> Self {
501 let other = unsafe { &*ptr };
502 VMMemoryDefinition {
503 base: other.base,
504 current_length: other.current_length().into(),
505 }
506 }
507}
508
509// SAFETY: `VMTableDefinition` is generated with `#[repr(C)]` and only contains
510// `VmSafe` fields.
511unsafe impl VmSafe for VMTableDefinition {}
512
513// SAFETY: `VMGlobalDefinition` is `#[repr(C)]` and only contains `VmSafe`
514// fields.
515unsafe impl VmSafe for VMGlobalDefinition {}
516
517#[cfg(test)]
518mod test_vmglobal_definition {
519 use super::VMGlobalDefinition;
520 use std::mem::{align_of, size_of};
521 use wasmtime_environ::{HostPtr, Module, PtrSize, StaticModuleIndex, VMOffsets};
522
523 #[test]
524 fn check_vmglobal_definition_size() {
525 // The compiler lays out globals using this size; see
526 // `vmctx_field_size!` in `wasmtime-environ`.
527 assert_eq!(size_of::<VMGlobalDefinition>(), 16);
528 assert_eq!(
529 size_of::<VMGlobalDefinition>(),
530 usize::from(HostPtr.maximum_value_size())
531 );
532 }
533
534 #[test]
535 fn check_vmglobal_definition_alignment() {
536 assert_eq!(align_of::<VMGlobalDefinition>(), 16);
537 assert!(align_of::<VMGlobalDefinition>() >= align_of::<i32>());
538 assert!(align_of::<VMGlobalDefinition>() >= align_of::<i64>());
539 assert!(align_of::<VMGlobalDefinition>() >= align_of::<f32>());
540 assert!(align_of::<VMGlobalDefinition>() >= align_of::<f64>());
541 assert!(align_of::<VMGlobalDefinition>() >= align_of::<[u8; 16]>());
542 assert!(align_of::<VMGlobalDefinition>() >= align_of::<[f32; 4]>());
543 assert!(align_of::<VMGlobalDefinition>() >= align_of::<[f64; 2]>());
544 }
545
546 #[test]
547 fn check_vmglobal_begins_aligned() {
548 let module = Module::new(StaticModuleIndex::from_u32(0));
549 let offsets = VMOffsets::new(HostPtr, &module);
550 assert_eq!(offsets.globals().begin() % 16, 0);
551 }
552
553 #[test]
554 #[cfg(feature = "gc")]
555 fn check_vmglobal_can_contain_gc_ref() {
556 assert!(size_of::<crate::runtime::vm::VMGcRef>() <= size_of::<VMGlobalDefinition>());
557 }
558}
559
560/// The storage for a WebAssembly global defined within the instance.
561///
562/// Compiled code reads and writes this storage directly, as the field that
563/// matches the global's type. Scalars are stored in native-endian order, except
564/// `v128`, which is little-endian.
565///
566/// TODO: Pack the globals more densely, rather than using the same size for
567/// every type.
568#[repr(C, align(16))]
569pub union VMGlobalDefinition {
570 // These values are all stored in native-endian
571 i32: i32,
572 i64: i64,
573 f32: u32,
574 f64: u64,
575 gc_ref: ManuallyDrop<Option<VMGcRef>>,
576 func_ref: Option<VmPtr<VMFuncRef>>,
577 // This is stored in little-endian
578 v128: [u8; 16],
579}
580
581impl VMGlobalDefinition {
582 /// Construct a `VMGlobalDefinition`.
583 pub fn new() -> Self {
584 Self { v128: [0; 16] }
585 }
586
587 /// Returns the value of this `i32` global.
588 ///
589 /// # Safety
590 ///
591 /// This function requires that the type of this global is `i32`.
592 pub unsafe fn get_i32(&self) -> i32 {
593 unsafe { self.i32 }
594 }
595
596 /// Sets the value of this `i32` global.
597 ///
598 /// # Safety
599 ///
600 /// This function requires that the type of this global is `i32`.
601 pub unsafe fn set_i32(&mut self, val: i32) {
602 self.i32 = val;
603 }
604
605 /// Returns the value of this `i64` global.
606 ///
607 /// # Safety
608 ///
609 /// This function requires that the type of this global is `i64`.
610 pub unsafe fn get_i64(&self) -> i64 {
611 unsafe { self.i64 }
612 }
613
614 /// Sets the value of this `i64` global.
615 ///
616 /// # Safety
617 ///
618 /// This function requires that the type of this global is `i64`.
619 pub unsafe fn set_i64(&mut self, val: i64) {
620 self.i64 = val;
621 }
622
623 /// Returns the bits of this `f32` global.
624 ///
625 /// # Safety
626 ///
627 /// This function requires that the type of this global is `f32`.
628 pub unsafe fn get_f32_bits(&self) -> u32 {
629 unsafe { self.f32 }
630 }
631
632 /// Sets the bits of this `f32` global.
633 ///
634 /// # Safety
635 ///
636 /// This function requires that the type of this global is `f32`.
637 pub unsafe fn set_f32_bits(&mut self, bits: u32) {
638 self.f32 = bits;
639 }
640
641 /// Returns the bits of this `f64` global.
642 ///
643 /// # Safety
644 ///
645 /// This function requires that the type of this global is `f64`.
646 pub unsafe fn get_f64_bits(&self) -> u64 {
647 unsafe { self.f64 }
648 }
649
650 /// Sets the bits of this `f64` global.
651 ///
652 /// # Safety
653 ///
654 /// This function requires that the type of this global is `f64`.
655 pub unsafe fn set_f64_bits(&mut self, bits: u64) {
656 self.f64 = bits;
657 }
658
659 /// Returns the value of this `v128` global.
660 ///
661 /// # Safety
662 ///
663 /// This function requires that the type of this global is `v128`.
664 pub unsafe fn get_u128(&self) -> u128 {
665 u128::from_le_bytes(unsafe { self.v128 })
666 }
667
668 /// Sets the value of this `v128` global.
669 ///
670 /// # Safety
671 ///
672 /// This function requires that the type of this global is `v128`.
673 pub unsafe fn set_u128(&mut self, val: u128) {
674 self.v128 = val.to_le_bytes();
675 }
676
677 /// Returns the value of this GC reference global.
678 ///
679 /// # Safety
680 ///
681 /// This function requires that the type of this global is a GC reference,
682 /// for example `extrenref`, `anyref`, `exnref`, etc.
683 pub unsafe fn as_gc_ref(&self) -> Option<&VMGcRef> {
684 let ret = unsafe { (*self.gc_ref).as_ref() };
685 assert!(cfg!(feature = "gc") || ret.is_none());
686 ret
687 }
688
689 /// Returns the value of this GC reference global, mutably.
690 ///
691 /// # Safety
692 ///
693 /// This function requires that the type of this global is a GC reference,
694 /// for example `extrenref`, `anyref`, `exnref`, etc.
695 pub unsafe fn as_gc_ref_mut(&mut self) -> Option<&mut VMGcRef> {
696 let ret = unsafe { (*self.gc_ref).as_mut() };
697 assert!(cfg!(feature = "gc") || ret.is_none());
698 ret
699 }
700
701 /// Initialize this GC reference global to the given GC reference.
702 ///
703 /// # Safety
704 ///
705 /// This function requires that the type of this global is a GC reference,
706 /// for example `extrenref`, `anyref`, `exnref`, etc.
707 pub unsafe fn init_gc_ref(
708 &mut self,
709 store: &mut StoreOpaque,
710 gc_ref: Option<&VMGcRef>,
711 ) -> Result<()> {
712 // SAFETY: `MaybeUninit<T>` has the same layout as `T`, and this global
713 // is uninitialized, so it must not be read as a `VMGcRef`.
714 let dest = unsafe { &mut *(&raw mut self.gc_ref).cast::<MaybeUninit<Option<VMGcRef>>>() };
715 store.init_gc_ref(dest, gc_ref)
716 }
717
718 /// Write a GC reference into this GC reference global.
719 pub unsafe fn write_gc_ref(
720 &mut self,
721 store: &mut StoreOpaque,
722 gc_ref: Option<&VMGcRef>,
723 ) -> Result<()> {
724 let dest = unsafe { &mut *self.gc_ref };
725 store.write_gc_ref(dest, gc_ref)
726 }
727
728 /// Returns the value of this funcref global.
729 ///
730 /// # Safety
731 ///
732 /// This function requires that the type of this global is a `funcref` or
733 /// a concrete function type.
734 pub unsafe fn get_func_ref(&self) -> Option<NonNull<VMFuncRef>> {
735 unsafe { self.func_ref.map(|p| p.as_non_null()) }
736 }
737
738 /// Sets the value of this funcref global.
739 ///
740 /// # Safety
741 ///
742 /// This function requires that the type of this global is a `funcref` or
743 /// a concrete function type.
744 pub unsafe fn set_func_ref(&mut self, func_ref: Option<NonNull<VMFuncRef>>) {
745 self.func_ref = func_ref.map(VmPtr::from);
746 }
747}
748
749#[cfg(test)]
750mod test_vmshared_type_index {
751 use super::VMSharedTypeIndex;
752 use std::mem::size_of;
753 use wasmtime_environ::{HostPtr, Module, StaticModuleIndex, VMOffsets};
754
755 #[test]
756 fn check_vmshared_type_index() {
757 let module = Module::new(StaticModuleIndex::from_u32(0));
758 let offsets = VMOffsets::new(HostPtr, &module);
759 assert_eq!(
760 size_of::<VMSharedTypeIndex>(),
761 usize::from(offsets.size_of_vmshared_type_index())
762 );
763 }
764}
765
766impl VMTagDefinition {
767 pub fn new(type_index: VMSharedTypeIndex) -> Self {
768 Self { type_index }
769 }
770}
771
772// SAFETY: `VMTagDefinition` is generated with `#[repr(C)]` and only contains
773// `VmSafe` fields.
774unsafe impl VmSafe for VMTagDefinition {}
775
776#[cfg(test)]
777mod test_vmtag_definition {
778 use wasmtime_environ::{HostPtr, Module, StaticModuleIndex, VMOffsets};
779
780 #[test]
781 fn check_vmtag_begins_aligned() {
782 let module = Module::new(StaticModuleIndex::from_u32(0));
783 let offsets = VMOffsets::new(HostPtr, &module);
784 assert_eq!(offsets.tags().begin() % 16, 0);
785 }
786}
787
788// SAFETY: `VMFuncRef` is generated with `#[repr(C)]` and only contains
789// `VmSafe` fields.
790unsafe impl VmSafe for VMFuncRef {}
791
792impl VMFuncRef {
793 /// Invokes the `array_call` field of this `VMFuncRef` with the supplied
794 /// arguments.
795 ///
796 /// This will invoke the function pointer in the `array_call` field with:
797 ///
798 /// * the `callee` vmctx as `self.vmctx`
799 /// * the `caller` as `caller` specified here
800 /// * the args pointer as `args_and_results`
801 /// * the args length as `args_and_results`
802 ///
803 /// The `args_and_results` area must be large enough to both load all
804 /// arguments from and store all results to.
805 ///
806 /// Returns whether a trap was recorded in TLS for raising.
807 ///
808 /// # Unsafety
809 ///
810 /// This method is unsafe because it can be called with any pointers. They
811 /// must all be valid for this wasm function call to proceed. For example
812 /// the `caller` must be valid machine code if `pulley` is `None` or it must
813 /// be valid bytecode if `pulley` is `Some`. Additionally `args_and_results`
814 /// must be large enough to handle all the arguments/results for this call.
815 ///
816 /// Note that the unsafety invariants to maintain here are not currently
817 /// exhaustively documented.
818 #[inline]
819 pub unsafe fn array_call(
820 me: NonNull<VMFuncRef>,
821 pulley: Option<InterpreterRef<'_>>,
822 caller: NonNull<VMContext>,
823 args_and_results: NonNull<[ValRaw]>,
824 ) -> bool {
825 match pulley {
826 Some(vm) => unsafe { Self::array_call_interpreted(me, vm, caller, args_and_results) },
827 None => unsafe { Self::array_call_native(me, caller, args_and_results) },
828 }
829 }
830
831 unsafe fn array_call_interpreted(
832 me: NonNull<VMFuncRef>,
833 vm: InterpreterRef<'_>,
834 caller: NonNull<VMContext>,
835 args_and_results: NonNull<[ValRaw]>,
836 ) -> bool {
837 // If `caller` is actually a `VMArrayCallHostFuncContext` then skip the
838 // interpreter, even though it's available, as `array_call` will be
839 // native code.
840 unsafe {
841 if me.as_ref().vmctx.as_non_null().as_ref().magic
842 == wasmtime_environ::VM_ARRAY_CALL_HOST_FUNC_MAGIC
843 {
844 return Self::array_call_native(me, caller, args_and_results);
845 }
846 vm.call(
847 me.as_ref().array_call.as_non_null().cast(),
848 me.as_ref().vmctx.as_non_null(),
849 caller,
850 args_and_results,
851 )
852 }
853 }
854
855 #[inline]
856 unsafe fn array_call_native(
857 me: NonNull<VMFuncRef>,
858 caller: NonNull<VMContext>,
859 args_and_results: NonNull<[ValRaw]>,
860 ) -> bool {
861 unsafe {
862 union GetNativePointer {
863 native: VMArrayCallNative,
864 ptr: NonNull<VMArrayCallFunction>,
865 }
866 let native = GetNativePointer {
867 ptr: me.as_ref().array_call.as_non_null(),
868 }
869 .native;
870 native(
871 me.as_ref().vmctx.as_non_null(),
872 caller,
873 args_and_results.cast(),
874 args_and_results.len(),
875 )
876 }
877 }
878
879 pub(crate) fn as_vm_function_import(&self) -> Option<&VMFunctionImport> {
880 if self.wasm_call.is_some() {
881 // Safety: `VMFuncRef` and `VMFunctionImport` have the same layout
882 // and `wasm_call` is non-null.
883 Some(unsafe { NonNull::from(self).cast::<VMFunctionImport>().as_ref() })
884 } else {
885 None
886 }
887 }
888}
889
890macro_rules! define_builtin_array {
891 (
892 $(
893 $( #[$attr:meta] )*
894 $name:ident( $( $pname:ident: $param:ident ),* ) $( -> $result:ident )?;
895 )*
896 ) => {
897 /// An array that stores addresses of builtin functions. We translate code
898 /// to use indirect calls. This way, we don't have to patch the code.
899 #[repr(C)]
900 #[allow(improper_ctypes_definitions, reason = "__m128i known not FFI-safe")]
901 pub struct VMBuiltinFunctionsArray {
902 $(
903 $name: unsafe extern "C" fn(
904 $(define_builtin_array!(@ty $param)),*
905 ) $( -> define_builtin_array!(@ty $result))?,
906 )*
907 }
908
909 impl VMBuiltinFunctionsArray {
910 pub const INIT: VMBuiltinFunctionsArray = VMBuiltinFunctionsArray {
911 $(
912 $name: crate::runtime::vm::libcalls::raw::$name,
913 )*
914 };
915
916 /// Helper to call `expose_provenance()` on all contained pointers.
917 ///
918 /// This is required to be called at least once before entering wasm
919 /// to inform the compiler that these function pointers may all be
920 /// loaded/stored and used on the "other end" to reacquire
921 /// provenance in Pulley. Pulley models hostcalls with a host
922 /// pointer as the first parameter that's a function pointer under
923 /// the hood, and this call ensures that the use of the function
924 /// pointer is considered valid.
925 pub fn expose_provenance(&self) -> NonNull<Self>{
926 $(
927 (self.$name as *mut u8).expose_provenance();
928 )*
929 NonNull::from(self)
930 }
931 }
932 };
933
934 (@ty u32) => (u32);
935 (@ty u64) => (u64);
936 (@ty f32) => (f32);
937 (@ty f64) => (f64);
938 (@ty u8) => (u8);
939 (@ty i8x16) => (i8x16);
940 (@ty f32x4) => (f32x4);
941 (@ty f64x2) => (f64x2);
942 (@ty bool) => (bool);
943 (@ty pointer) => (*mut u8);
944 (@ty size) => (usize);
945 (@ty vmctx) => (NonNull<VMContext>);
946}
947
948// SAFETY: the above structure is repr(C) and only contains `VmSafe` fields.
949unsafe impl VmSafe for VMBuiltinFunctionsArray {}
950
951wasmtime_environ::foreach_builtin_function!(define_builtin_array);
952
953const _: () = {
954 assert!(
955 mem::size_of::<VMBuiltinFunctionsArray>()
956 == mem::size_of::<usize>() * (BuiltinFunctionIndex::len() as usize)
957 )
958};
959
960impl VMStoreContext {
961 /// From the current saved trampoline FP, get the FP of the last
962 /// Wasm frame. If the current saved trampoline FP is null, return
963 /// null.
964 ///
965 /// We store only the trampoline FP, because (i) we need the
966 /// trampoline FP, so we know the size (bottom) of the last Wasm
967 /// frame; and (ii) the last Wasm frame, just above the trampoline
968 /// frame, can be recovered via the FP chain.
969 ///
970 /// # Safety
971 ///
972 /// This function requires that the `last_wasm_exit_trampoline_fp`
973 /// field either points to an active trampoline frame or is a null
974 /// pointer.
975 pub(crate) unsafe fn last_wasm_exit_fp(&self) -> usize {
976 // SAFETY: the unsafe cell is safe to load (no other threads
977 // will be writing our store when we have control), and the
978 // helper function's safety condition is the same as ours.
979 unsafe {
980 let trampoline_fp = *self.last_wasm_exit_trampoline_fp.get();
981 Self::wasm_exit_fp_from_trampoline_fp(trampoline_fp)
982 }
983 }
984
985 /// From any saved trampoline FP, get the FP of the last Wasm
986 /// frame. If the given trampoline FP is null, return null.
987 ///
988 /// This differs from `last_wasm_exit_fp()` above in that it
989 /// allows accessing activations further up the stack as well,
990 /// e.g. via `CallThreadState::old_state`.
991 ///
992 /// # Safety
993 ///
994 /// This function requires that the provided FP value is valid,
995 /// and points to an active trampoline frame, or is null.
996 ///
997 /// This function depends on the invariant that on all supported
998 /// architectures, we store the previous FP value under the
999 /// current FP. This is a property of our ABI that we control and
1000 /// ensure.
1001 pub(crate) unsafe fn wasm_exit_fp_from_trampoline_fp(trampoline_fp: usize) -> usize {
1002 if trampoline_fp != 0 {
1003 // SAFETY: We require that trampoline_fp points to a valid
1004 // frame, which will (by definition) contain an old FP value
1005 // that we can load.
1006 unsafe { *(trampoline_fp as *const usize) }
1007 } else {
1008 0
1009 }
1010 }
1011
1012 #[cfg(feature = "component-model-async")]
1013 pub(crate) fn component_context_mut(&mut self) -> &mut [u32; NUM_COMPONENT_CONTEXT_SLOTS] {
1014 self.component_context.get_mut()
1015 }
1016
1017 #[cfg(feature = "component-model-async")]
1018 pub(crate) fn current_thread_mut(&mut self) -> &mut VMLazyThread {
1019 self.current_thread.get_mut()
1020 }
1021}
1022
1023// The `VMStoreContext` type is a pod-type with no destructor, and we don't
1024// access any fields from other threads, so add in these trait impls which are
1025// otherwise not available due to the `fuel_consumed` and `epoch_deadline`
1026// variables in `VMStoreContext`.
1027unsafe impl Send for VMStoreContext {}
1028unsafe impl Sync for VMStoreContext {}
1029
1030// SAFETY: the above structure is repr(C) and only contains `VmSafe` fields.
1031unsafe impl VmSafe for VMStoreContext {}
1032
1033impl Default for VMStoreContext {
1034 fn default() -> VMStoreContext {
1035 VMStoreContext {
1036 fuel_consumed: UnsafeCell::new(0),
1037 epoch_deadline: UnsafeCell::new(0),
1038 execution_version: 0,
1039 stack_limit: UnsafeCell::new(usize::MAX),
1040 gc_heap: UnsafeCell::new(VMMemoryDefinition {
1041 base: NonNull::dangling().into(),
1042 current_length: AtomicUsize::new(0),
1043 }),
1044 last_wasm_exit_trampoline_fp: UnsafeCell::new(0),
1045 last_wasm_exit_pc: UnsafeCell::new(0),
1046 last_wasm_entry_fp: UnsafeCell::new(0),
1047 last_wasm_entry_sp: UnsafeCell::new(0),
1048 last_wasm_entry_trap_handler: UnsafeCell::new(0),
1049 stack_chain: UnsafeCell::new(VMStackChain::Absent),
1050 async_guard_range: ptr::null_mut()..ptr::null_mut(),
1051 store_data: VmPtr::dangling(),
1052 component_context: UnsafeCell::new([0; NUM_COMPONENT_CONTEXT_SLOTS]),
1053 current_thread: UnsafeCell::new(VMLazyThread::none()),
1054 }
1055 }
1056}
1057
1058#[cfg(test)]
1059mod test_vmstore_context {
1060 use super::{VMMemoryDefinition, VMStoreContext};
1061 use core::mem::offset_of;
1062 use wasmtime_environ::{HostPtr, Module, PtrSize, StaticModuleIndex, VMOffsets};
1063
1064 /// Check the `VMStoreContext` offsets that `for_each_vm_type!` does *not*
1065 /// generate: the offsets reaching into the inlined `gc_heap`.
1066 ///
1067 /// Every field offset, plus the size and alignment of the type, is already
1068 /// checked by the generated `test_vm_type_layouts::vm_store_context`.
1069 #[test]
1070 fn derived_field_offsets() {
1071 let module = Module::new(StaticModuleIndex::from_u32(0));
1072 let offsets = VMOffsets::new(HostPtr, &module);
1073 assert_eq!(
1074 offset_of!(VMStoreContext, gc_heap) + offset_of!(VMMemoryDefinition, base),
1075 usize::from(offsets.ptr.vm_store_context().gc_heap_base())
1076 );
1077 assert_eq!(
1078 offset_of!(VMStoreContext, gc_heap) + offset_of!(VMMemoryDefinition, current_length),
1079 usize::from(offsets.ptr.vm_store_context().gc_heap_current_length())
1080 );
1081 }
1082}
1083
1084#[cfg(test)]
1085mod test_vm_cont_ref {
1086 use wasmtime_environ::PtrSize;
1087
1088 /// Check the one `VMContRef` property that `for_each_vm_type!` does *not*
1089 /// generate an assertion for: that `revision` lands at an eight-aligned
1090 /// offset for every target pointer width, not just the host's.
1091 ///
1092 /// Some 32-bit platforms need it to be 8-byte aligned and some don't, so we
1093 /// make sure that it always is, without padding to get there.
1094 ///
1095 /// Every field offset, plus the size and alignment of the type, is already
1096 /// checked by the generated `test_vm_type_layouts::vm_cont_ref`.
1097 #[test]
1098 fn revision_is_eight_aligned() {
1099 assert_eq!(4u8.vm_cont_ref().revision() % 8, 0);
1100 assert_eq!(8u8.vm_cont_ref().revision() % 8, 0);
1101 }
1102}
1103
1104impl VMLazyThread {
1105 const _ASSERT_SIZE: () = assert!(
1106 core::mem::size_of::<VMLazyThread>() == core::mem::size_of::<*mut VMDeferredThread>()
1107 );
1108 const _ASSERT_ALIGN: () = assert!(
1109 core::mem::align_of::<VMLazyThread>() == core::mem::align_of::<*mut VMDeferredThread>()
1110 );
1111
1112 const FORCED: VmPtr<VMDeferredThread> = VmPtr::<u8>::dangling().cast();
1113
1114 /// There is no current thread.
1115 pub const fn none() -> Self {
1116 Self { thread: None }
1117 }
1118
1119 /// A lazy thread that has already been promoted.
1120 pub const fn forced() -> Self {
1121 Self {
1122 thread: Some(Self::FORCED),
1123 }
1124 }
1125
1126 /// A deferred thread referencing the given on-stack [`VMDeferredThread`].
1127 pub fn deferred(ptr: NonNull<VMDeferredThread>) -> Self {
1128 debug_assert_eq!(ptr.addr().get() & Self::FORCED.addr().get(), 0);
1129 Self {
1130 thread: Some(ptr.into()),
1131 }
1132 }
1133
1134 /// Returns `true` if there is no current thread.
1135 pub fn is_none(self) -> bool {
1136 self.thread.is_none()
1137 }
1138
1139 /// Returns `true` if a deferred thread has been forced/promoted.
1140 pub fn is_forced(self) -> bool {
1141 self.thread.is_some_and(|p| p == Self::FORCED)
1142 }
1143
1144 /// Returns `true` if this is a deferred thread (i.e. neither `None` nor
1145 /// forced).
1146 pub fn is_deferred(self) -> bool {
1147 self.thread.is_some_and(|p| p != Self::FORCED)
1148 }
1149
1150 /// Returns the deferred [`VMDeferredThread`] pointer if this is a deferred
1151 /// thread.
1152 pub fn as_deferred(self) -> Option<VmPtr<VMDeferredThread>> {
1153 self.thread
1154 .and_then(|p| if p == Self::FORCED { None } else { Some(p) })
1155 }
1156}
1157
1158#[cfg(test)]
1159mod test_vmlazy_thread {
1160 use super::*;
1161
1162 #[test]
1163 fn vmlazy_thread_forced() {
1164 assert_eq!(
1165 VMLazyThread::forced().thread.unwrap().addr().get(),
1166 usize::try_from(wasmtime_environ::VM_LAZY_THREAD_FORCED).unwrap()
1167 );
1168 }
1169}
1170
1171/// The VM "context", which is pointed to by the `vmctx` arg in Cranelift.
1172/// This has information about globals, memories, tables, and other runtime
1173/// state associated with the current instance.
1174///
1175/// The struct here is empty, as the sizes of these fields are dynamic, and
1176/// we can't describe them in Rust's type system. Sufficient memory is
1177/// allocated at runtime.
1178#[derive(Debug)]
1179#[repr(C, align(16))] // align 16 since globals are aligned to that and contained inside
1180pub struct VMContext {
1181 _magic: u32,
1182}
1183
1184impl VMContext {
1185 /// Helper function to cast between context types using a debug assertion to
1186 /// protect against some mistakes.
1187 #[inline]
1188 pub unsafe fn from_opaque(opaque: NonNull<VMOpaqueContext>) -> NonNull<VMContext> {
1189 // Note that in general the offset of the "magic" field is stored in
1190 // `VMContext::magic`. Given though that this is a sanity check
1191 // about converting this pointer to another type we ideally don't want
1192 // to read the offset from potentially corrupt memory. Instead it would
1193 // be better to catch errors here as soon as possible.
1194 //
1195 // To accomplish this the `VMContext` structure is laid out with the
1196 // magic field at a statically known offset (here it's 0 for now). This
1197 // static offset is asserted in `VMOffsets::from` and needs to be kept
1198 // in sync with this line for this debug assertion to work.
1199 //
1200 // Also note that this magic is only ever invalid in the presence of
1201 // bugs, meaning we don't actually read the magic and act differently
1202 // at runtime depending what it is, so this is a debug assertion as
1203 // opposed to a regular assertion.
1204 unsafe {
1205 debug_assert_eq!(opaque.as_ref().magic, VMCONTEXT_MAGIC);
1206 }
1207 opaque.cast()
1208 }
1209}
1210
1211/// A "raw" and unsafe representation of a WebAssembly value.
1212///
1213/// This is provided for use with the `Func::new_unchecked` and
1214/// `Func::call_unchecked` APIs. In general it's unlikely you should be using
1215/// this from Rust, rather using APIs like `Func::wrap` and `TypedFunc::call`.
1216///
1217/// This is notably an "unsafe" way to work with `Val` and it's recommended to
1218/// instead use `Val` where possible. An important note about this union is that
1219/// fields are all stored in little-endian format, regardless of the endianness
1220/// of the host system.
1221#[repr(C)]
1222#[derive(Copy, Clone)]
1223pub union ValRaw {
1224 /// A WebAssembly `i32` value.
1225 ///
1226 /// Note that the payload here is a Rust `i32` but the WebAssembly `i32`
1227 /// type does not assign an interpretation of the upper bit as either signed
1228 /// or unsigned. The Rust type `i32` is simply chosen for convenience.
1229 ///
1230 /// This value is always stored in a little-endian format.
1231 i32: i32,
1232
1233 /// A WebAssembly `i64` value.
1234 ///
1235 /// Note that the payload here is a Rust `i64` but the WebAssembly `i64`
1236 /// type does not assign an interpretation of the upper bit as either signed
1237 /// or unsigned. The Rust type `i64` is simply chosen for convenience.
1238 ///
1239 /// This value is always stored in a little-endian format.
1240 i64: i64,
1241
1242 /// A WebAssembly `f32` value.
1243 ///
1244 /// Note that the payload here is a Rust `u32`. This is to allow passing any
1245 /// representation of NaN into WebAssembly without risk of changing NaN
1246 /// payload bits as its gets passed around the system. Otherwise though this
1247 /// `u32` value is the return value of `f32::to_bits` in Rust.
1248 ///
1249 /// This value is always stored in a little-endian format.
1250 f32: u32,
1251
1252 /// A WebAssembly `f64` value.
1253 ///
1254 /// Note that the payload here is a Rust `u64`. This is to allow passing any
1255 /// representation of NaN into WebAssembly without risk of changing NaN
1256 /// payload bits as its gets passed around the system. Otherwise though this
1257 /// `u64` value is the return value of `f64::to_bits` in Rust.
1258 ///
1259 /// This value is always stored in a little-endian format.
1260 f64: u64,
1261
1262 /// A WebAssembly `v128` value.
1263 ///
1264 /// The payload here is a Rust `[u8; 16]` which has the same number of bits
1265 /// but note that `v128` in WebAssembly is often considered a vector type
1266 /// such as `i32x4` or `f64x2`. This means that the actual interpretation
1267 /// of the underlying bits is left up to the instructions which consume
1268 /// this value.
1269 ///
1270 /// This value is always stored in a little-endian format.
1271 v128: [u8; 16],
1272
1273 /// A WebAssembly `funcref` value (or one of its subtypes).
1274 ///
1275 /// The payload here is a pointer which is runtime-defined. This is one of
1276 /// the main points of unsafety about the `ValRaw` type as the validity of
1277 /// the pointer here is not easily verified and must be preserved by
1278 /// carefully calling the correct functions throughout the runtime.
1279 ///
1280 /// This value is always stored in a little-endian format.
1281 funcref: *mut c_void,
1282
1283 /// A WebAssembly `externref` value (or one of its subtypes).
1284 ///
1285 /// The payload here is a compressed pointer value which is
1286 /// runtime-defined. This is one of the main points of unsafety about the
1287 /// `ValRaw` type as the validity of the pointer here is not easily verified
1288 /// and must be preserved by carefully calling the correct functions
1289 /// throughout the runtime.
1290 ///
1291 /// This value is always stored in a little-endian format.
1292 externref: u32,
1293
1294 /// A WebAssembly `anyref` value (or one of its subtypes).
1295 ///
1296 /// The payload here is a compressed pointer value which is
1297 /// runtime-defined. This is one of the main points of unsafety about the
1298 /// `ValRaw` type as the validity of the pointer here is not easily verified
1299 /// and must be preserved by carefully calling the correct functions
1300 /// throughout the runtime.
1301 ///
1302 /// This value is always stored in a little-endian format.
1303 anyref: u32,
1304
1305 /// A WebAssembly `exnref` value (or one of its subtypes).
1306 ///
1307 /// The payload here is a compressed pointer value which is
1308 /// runtime-defined. This is one of the main points of unsafety about the
1309 /// `ValRaw` type as the validity of the pointer here is not easily verified
1310 /// and must be preserved by carefully calling the correct functions
1311 /// throughout the runtime.
1312 ///
1313 /// This value is always stored in a little-endian format.
1314 exnref: u32,
1315}
1316
1317// The `ValRaw` type is matched as `wasmtime_val_raw_t` in the C API so these
1318// are some simple assertions about the shape of the type which are additionally
1319// matched in C.
1320const _: () = {
1321 assert!(mem::size_of::<ValRaw>() == 16);
1322 assert!(mem::align_of::<ValRaw>() == mem::align_of::<u64>());
1323};
1324
1325// This type is just a bag-of-bits so it's up to the caller to figure out how
1326// to safely deal with threading concerns and safely access interior bits.
1327unsafe impl Send for ValRaw {}
1328unsafe impl Sync for ValRaw {}
1329
1330impl fmt::Debug for ValRaw {
1331 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1332 struct Hex<T>(T);
1333 impl<T: fmt::LowerHex> fmt::Debug for Hex<T> {
1334 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1335 let bytes = mem::size_of::<T>();
1336 let hex_digits_per_byte = 2;
1337 let hex_digits = bytes * hex_digits_per_byte;
1338 write!(f, "0x{:0width$x}", self.0, width = hex_digits)
1339 }
1340 }
1341
1342 unsafe {
1343 f.debug_struct("ValRaw")
1344 .field("i32", &Hex(self.i32))
1345 .field("i64", &Hex(self.i64))
1346 .field("f32", &Hex(self.f32))
1347 .field("f64", &Hex(self.f64))
1348 .field("v128", &Hex(u128::from_le_bytes(self.v128)))
1349 .field("funcref", &self.funcref)
1350 .field("externref", &Hex(self.externref))
1351 .field("anyref", &Hex(self.anyref))
1352 .field("exnref", &Hex(self.exnref))
1353 .finish()
1354 }
1355 }
1356}
1357
1358impl ValRaw {
1359 /// Create a null reference that is compatible with any of
1360 /// `{any,extern,func,exn}ref`.
1361 pub fn null() -> ValRaw {
1362 unsafe {
1363 let raw = mem::MaybeUninit::<Self>::zeroed().assume_init();
1364 debug_assert_eq!(raw.get_anyref(), 0);
1365 debug_assert_eq!(raw.get_exnref(), 0);
1366 debug_assert_eq!(raw.get_externref(), 0);
1367 debug_assert_eq!(raw.get_funcref(), ptr::null_mut());
1368 raw
1369 }
1370 }
1371
1372 /// Creates a WebAssembly `i32` value
1373 #[inline]
1374 pub fn i32(i: i32) -> ValRaw {
1375 // Note that this is intentionally not setting the `i32` field, instead
1376 // setting the `i64` field with a zero-extended version of `i`. For more
1377 // information on this see the comments on `Lower for Result` in the
1378 // `wasmtime` crate. Otherwise though all `ValRaw` constructors are
1379 // otherwise constrained to guarantee that the initial 64-bits are
1380 // always initialized.
1381 ValRaw::u64(i.cast_unsigned().into())
1382 }
1383
1384 /// Creates a WebAssembly `i64` value
1385 #[inline]
1386 pub fn i64(i: i64) -> ValRaw {
1387 ValRaw { i64: i.to_le() }
1388 }
1389
1390 /// Creates a WebAssembly `i32` value
1391 #[inline]
1392 pub fn u32(i: u32) -> ValRaw {
1393 // See comments in `ValRaw::i32` for why this is setting the upper
1394 // 32-bits as well.
1395 ValRaw::u64(i.into())
1396 }
1397
1398 /// Creates a WebAssembly `i64` value
1399 #[inline]
1400 pub fn u64(i: u64) -> ValRaw {
1401 ValRaw::i64(i as i64)
1402 }
1403
1404 /// Creates a WebAssembly `f32` value
1405 #[inline]
1406 pub fn f32(i: u32) -> ValRaw {
1407 // See comments in `ValRaw::i32` for why this is setting the upper
1408 // 32-bits as well.
1409 ValRaw::u64(i.into())
1410 }
1411
1412 /// Creates a WebAssembly `f64` value
1413 #[inline]
1414 pub fn f64(i: u64) -> ValRaw {
1415 ValRaw { f64: i.to_le() }
1416 }
1417
1418 /// Creates a WebAssembly `v128` value
1419 #[inline]
1420 pub fn v128(i: u128) -> ValRaw {
1421 ValRaw {
1422 v128: i.to_le_bytes(),
1423 }
1424 }
1425
1426 /// Creates a WebAssembly `funcref` value
1427 #[inline]
1428 pub fn funcref(i: *mut c_void) -> ValRaw {
1429 ValRaw {
1430 funcref: i.map_addr(|i| i.to_le()),
1431 }
1432 }
1433
1434 /// Creates a WebAssembly `externref` value
1435 #[inline]
1436 pub fn externref(e: u32) -> ValRaw {
1437 assert!(cfg!(feature = "gc") || e == 0);
1438 ValRaw {
1439 externref: e.to_le(),
1440 }
1441 }
1442
1443 /// Creates a WebAssembly `anyref` value
1444 #[inline]
1445 pub fn anyref(r: u32) -> ValRaw {
1446 assert!(cfg!(feature = "gc") || r == 0);
1447 ValRaw { anyref: r.to_le() }
1448 }
1449
1450 /// Creates a WebAssembly `exnref` value
1451 #[inline]
1452 pub fn exnref(r: u32) -> ValRaw {
1453 assert!(cfg!(feature = "gc") || r == 0);
1454 ValRaw { exnref: r.to_le() }
1455 }
1456
1457 #[inline]
1458 pub(crate) fn vmgcref(r: Option<VMGcRef>) -> ValRaw {
1459 let raw = r.map_or(0, |r| r.as_raw_u32());
1460
1461 // NB: All `VMGcRef`-based `ValRaw`s are the same.
1462 debug_assert_eq!(raw, ValRaw::anyref(raw).get_exnref());
1463 debug_assert_eq!(raw, ValRaw::exnref(raw).get_externref());
1464 debug_assert_eq!(raw, ValRaw::externref(raw).get_anyref());
1465
1466 ValRaw::anyref(raw)
1467 }
1468
1469 /// Gets the WebAssembly `i32` value
1470 #[inline]
1471 pub fn get_i32(&self) -> i32 {
1472 unsafe { i32::from_le(self.i32) }
1473 }
1474
1475 /// Gets the WebAssembly `i64` value
1476 #[inline]
1477 pub fn get_i64(&self) -> i64 {
1478 unsafe { i64::from_le(self.i64) }
1479 }
1480
1481 /// Gets the WebAssembly `i32` value
1482 #[inline]
1483 pub fn get_u32(&self) -> u32 {
1484 self.get_i32().cast_unsigned()
1485 }
1486
1487 /// Gets the WebAssembly `i64` value
1488 #[inline]
1489 pub fn get_u64(&self) -> u64 {
1490 self.get_i64().cast_unsigned()
1491 }
1492
1493 /// Gets the WebAssembly `f32` value
1494 #[inline]
1495 pub fn get_f32(&self) -> u32 {
1496 unsafe { u32::from_le(self.f32) }
1497 }
1498
1499 /// Gets the WebAssembly `f64` value
1500 #[inline]
1501 pub fn get_f64(&self) -> u64 {
1502 unsafe { u64::from_le(self.f64) }
1503 }
1504
1505 /// Gets the WebAssembly `v128` value
1506 #[inline]
1507 pub fn get_v128(&self) -> u128 {
1508 unsafe { u128::from_le_bytes(self.v128) }
1509 }
1510
1511 /// Gets the WebAssembly `funcref` value
1512 #[inline]
1513 pub fn get_funcref(&self) -> *mut c_void {
1514 let addr = unsafe { usize::from_le(self.funcref.addr()) };
1515 core::ptr::with_exposed_provenance_mut(addr)
1516 }
1517
1518 /// Gets the WebAssembly `externref` value
1519 #[inline]
1520 pub fn get_externref(&self) -> u32 {
1521 let externref = u32::from_le(unsafe { self.externref });
1522 assert!(cfg!(feature = "gc") || externref == 0);
1523 externref
1524 }
1525
1526 /// Gets the WebAssembly `anyref` value
1527 #[inline]
1528 pub fn get_anyref(&self) -> u32 {
1529 let anyref = u32::from_le(unsafe { self.anyref });
1530 assert!(cfg!(feature = "gc") || anyref == 0);
1531 anyref
1532 }
1533
1534 /// Gets the WebAssembly `exnref` value
1535 #[inline]
1536 pub fn get_exnref(&self) -> u32 {
1537 let exnref = u32::from_le(unsafe { self.exnref });
1538 assert!(cfg!(feature = "gc") || exnref == 0);
1539 exnref
1540 }
1541
1542 /// Get the inner `VMGcRef`.
1543 pub(crate) fn get_vmgcref(&self) -> Option<crate::vm::VMGcRef> {
1544 debug_assert_eq!(self.get_anyref(), self.get_exnref());
1545 debug_assert_eq!(self.get_anyref(), self.get_externref());
1546 VMGcRef::from_raw_u32(self.get_anyref())
1547 }
1548}
1549
1550/// An "opaque" version of `VMContext` which must be explicitly casted to a
1551/// target context.
1552///
1553/// This context is used to represent that contexts specified in
1554/// `VMFuncRef` can have any type and don't have an implicit
1555/// structure. Neither wasmtime nor cranelift-generated code can rely on the
1556/// structure of an opaque context in general and only the code which configured
1557/// the context is able to rely on a particular structure. This is because the
1558/// context pointer configured for `VMFuncRef` is guaranteed to be
1559/// the first parameter passed.
1560///
1561/// Note that Wasmtime currently has a layout where all contexts that are casted
1562/// to an opaque context start with a 32-bit "magic" which can be used in debug
1563/// mode to debug-assert that the casts here are correct and have at least a
1564/// little protection against incorrect casts.
1565pub struct VMOpaqueContext {
1566 pub(crate) magic: u32,
1567 _marker: marker::PhantomPinned,
1568}
1569
1570impl VMOpaqueContext {
1571 /// Helper function to clearly indicate that casts are desired.
1572 #[inline]
1573 pub fn from_vmcontext(ptr: NonNull<VMContext>) -> NonNull<VMOpaqueContext> {
1574 ptr.cast()
1575 }
1576
1577 /// Helper function to clearly indicate that casts are desired.
1578 #[inline]
1579 pub fn from_vm_array_call_host_func_context(
1580 ptr: NonNull<VMArrayCallHostFuncContext>,
1581 ) -> NonNull<VMOpaqueContext> {
1582 ptr.cast()
1583 }
1584}