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