1 use crate::component::instance::Instance; 2 use crate::component::matching::InstanceType; 3 use crate::component::storage::storage_as_slice; 4 use crate::component::types::Type; 5 use crate::component::values::Val; 6 use crate::prelude::*; 7 use crate::runtime::vm::component::{ComponentInstance, ResourceTables}; 8 use crate::runtime::vm::{Export, ExportFunction}; 9 use crate::store::StoreOpaque; 10 use crate::{AsContext, AsContextMut, StoreContextMut, ValRaw}; 11 use core::mem::{self, MaybeUninit}; 12 use core::ptr::NonNull; 13 use wasmtime_environ::component::{ 14 CanonicalOptionsDataModel, ExportIndex, InterfaceType, MAX_FLAT_PARAMS, MAX_FLAT_RESULTS, 15 TypeFuncIndex, TypeTuple, 16 }; 17 18 mod host; 19 mod options; 20 mod typed; 21 pub use self::host::*; 22 pub use self::options::*; 23 pub use self::typed::*; 24 25 #[repr(C)] 26 union ParamsAndResults<Params: Copy, Return: Copy> { 27 params: Params, 28 ret: Return, 29 } 30 31 /// A WebAssembly component function which can be called. 32 /// 33 /// This type is the dual of [`wasmtime::Func`](crate::Func) for component 34 /// functions. An instance of [`Func`] represents a component function from a 35 /// component [`Instance`](crate::component::Instance). Like with 36 /// [`wasmtime::Func`](crate::Func) it's possible to call functions either 37 /// synchronously or asynchronously and either typed or untyped. 38 #[derive(Copy, Clone, Debug)] 39 #[repr(C)] // here for the C API. 40 pub struct Func { 41 instance: Instance, 42 index: ExportIndex, 43 } 44 45 // Double-check that the C representation in `component/instance.h` matches our 46 // in-Rust representation here in terms of size/alignment/etc. 47 const _: () = { 48 #[repr(C)] 49 struct T(u64, u32); 50 #[repr(C)] 51 struct C(T, u32); 52 assert!(core::mem::size_of::<C>() == core::mem::size_of::<Func>()); 53 assert!(core::mem::align_of::<C>() == core::mem::align_of::<Func>()); 54 assert!(core::mem::offset_of!(Func, instance) == 0); 55 }; 56 57 impl Func { 58 pub(crate) fn from_lifted_func(instance: Instance, index: ExportIndex) -> Func { 59 Func { instance, index } 60 } 61 62 /// Attempt to cast this [`Func`] to a statically typed [`TypedFunc`] with 63 /// the provided `Params` and `Return`. 64 /// 65 /// This function will perform a type-check at runtime that the [`Func`] 66 /// takes `Params` as parameters and returns `Return`. If the type-check 67 /// passes then a [`TypedFunc`] will be returned which can be used to 68 /// invoke the function in an efficient, statically-typed, and ergonomic 69 /// manner. 70 /// 71 /// The `Params` type parameter here is a tuple of the parameters to the 72 /// function. A function which takes no arguments should use `()`, a 73 /// function with one argument should use `(T,)`, etc. Note that all 74 /// `Params` must also implement the [`Lower`] trait since they're going 75 /// into wasm. 76 /// 77 /// The `Return` type parameter is the return value of this function. A 78 /// return value of `()` means that there's no return (similar to a Rust 79 /// unit return) and otherwise a type `T` can be specified. Note that the 80 /// `Return` must also implement the [`Lift`] trait since it's coming from 81 /// wasm. 82 /// 83 /// Types specified here must implement the [`ComponentType`] trait. This 84 /// trait is implemented for built-in types to Rust such as integer 85 /// primitives, floats, `Option<T>`, `Result<T, E>`, strings, `Vec<T>`, and 86 /// more. As parameters you'll be passing native Rust types. 87 /// 88 /// See the documentation for [`ComponentType`] for more information about 89 /// supported types. 90 /// 91 /// # Errors 92 /// 93 /// If the function does not actually take `Params` as its parameters or 94 /// return `Return` then an error will be returned. 95 /// 96 /// # Panics 97 /// 98 /// This function will panic if `self` is not owned by the `store` 99 /// specified. 100 /// 101 /// # Examples 102 /// 103 /// Calling a function which takes no parameters and has no return value: 104 /// 105 /// ``` 106 /// # use wasmtime::component::Func; 107 /// # use wasmtime::Store; 108 /// # fn foo(func: &Func, store: &mut Store<()>) -> anyhow::Result<()> { 109 /// let typed = func.typed::<(), ()>(&store)?; 110 /// typed.call(store, ())?; 111 /// # Ok(()) 112 /// # } 113 /// ``` 114 /// 115 /// Calling a function which takes one string parameter and returns a 116 /// string: 117 /// 118 /// ``` 119 /// # use wasmtime::component::Func; 120 /// # use wasmtime::Store; 121 /// # fn foo(func: &Func, mut store: Store<()>) -> anyhow::Result<()> { 122 /// let typed = func.typed::<(&str,), (String,)>(&store)?; 123 /// let ret = typed.call(&mut store, ("Hello, ",))?.0; 124 /// println!("returned string was: {}", ret); 125 /// # Ok(()) 126 /// # } 127 /// ``` 128 /// 129 /// Calling a function which takes multiple parameters and returns a boolean: 130 /// 131 /// ``` 132 /// # use wasmtime::component::Func; 133 /// # use wasmtime::Store; 134 /// # fn foo(func: &Func, mut store: Store<()>) -> anyhow::Result<()> { 135 /// let typed = func.typed::<(u32, Option<&str>, &[u8]), (bool,)>(&store)?; 136 /// let ok: bool = typed.call(&mut store, (1, Some("hello"), b"bytes!"))?.0; 137 /// println!("return value was: {ok}"); 138 /// # Ok(()) 139 /// # } 140 /// ``` 141 pub fn typed<Params, Return>(&self, store: impl AsContext) -> Result<TypedFunc<Params, Return>> 142 where 143 Params: ComponentNamedList + Lower, 144 Return: ComponentNamedList + Lift, 145 { 146 self._typed(store.as_context().0, None) 147 } 148 149 pub(crate) fn _typed<Params, Return>( 150 &self, 151 store: &StoreOpaque, 152 instance: Option<&ComponentInstance>, 153 ) -> Result<TypedFunc<Params, Return>> 154 where 155 Params: ComponentNamedList + Lower, 156 Return: ComponentNamedList + Lift, 157 { 158 self.typecheck::<Params, Return>(store, instance)?; 159 unsafe { Ok(TypedFunc::new_unchecked(*self)) } 160 } 161 162 fn typecheck<Params, Return>( 163 &self, 164 store: &StoreOpaque, 165 instance: Option<&ComponentInstance>, 166 ) -> Result<()> 167 where 168 Params: ComponentNamedList + Lower, 169 Return: ComponentNamedList + Lift, 170 { 171 let cx = InstanceType::new(instance.unwrap_or_else(|| self.instance.id().get(store))); 172 let ty = &cx.types[self.ty(store)]; 173 174 Params::typecheck(&InterfaceType::Tuple(ty.params), &cx) 175 .context("type mismatch with parameters")?; 176 Return::typecheck(&InterfaceType::Tuple(ty.results), &cx) 177 .context("type mismatch with results")?; 178 179 Ok(()) 180 } 181 182 /// Get the parameter names and types for this function. 183 pub fn params(&self, store: impl AsContext) -> Box<[(String, Type)]> { 184 let store = store.as_context(); 185 let instance = self.instance.id().get(store.0); 186 let types = instance.component().types(); 187 let func_ty = &types[self.ty(store.0)]; 188 types[func_ty.params] 189 .types 190 .iter() 191 .zip(&func_ty.param_names) 192 .map(|(ty, name)| (name.clone(), Type::from(ty, &InstanceType::new(instance)))) 193 .collect() 194 } 195 196 /// Get the result types for this function. 197 pub fn results(&self, store: impl AsContext) -> Box<[Type]> { 198 let store = store.as_context(); 199 let instance = self.instance.id().get(store.0); 200 let types = instance.component().types(); 201 let ty = self.ty(store.0); 202 types[types[ty].results] 203 .types 204 .iter() 205 .map(|ty| Type::from(ty, &InstanceType::new(instance))) 206 .collect() 207 } 208 209 fn ty(&self, store: &StoreOpaque) -> TypeFuncIndex { 210 let instance = self.instance.id().get(store); 211 let (ty, _, _) = instance.component().export_lifted_function(self.index); 212 ty 213 } 214 215 /// Invokes this function with the `params` given and returns the result. 216 /// 217 /// The `params` provided must match the parameters that this function takes 218 /// in terms of their types and the number of parameters. Results will be 219 /// written to the `results` slice provided if the call completes 220 /// successfully. The initial types of the values in `results` are ignored 221 /// and values are overwritten to write the result. It's required that the 222 /// size of `results` exactly matches the number of results that this 223 /// function produces. 224 /// 225 /// Note that after a function is invoked the embedder needs to invoke 226 /// [`Func::post_return`] to execute any final cleanup required by the 227 /// guest. This function call is required to either call the function again 228 /// or to call another function. 229 /// 230 /// For more detailed information see the documentation of 231 /// [`TypedFunc::call`]. 232 /// 233 /// # Errors 234 /// 235 /// Returns an error in situations including but not limited to: 236 /// 237 /// * `params` is not the right size or if the values have the wrong type 238 /// * `results` is not the right size 239 /// * A trap occurs while executing the function 240 /// * The function calls a host function which returns an error 241 /// 242 /// See [`TypedFunc::call`] for more information in addition to 243 /// [`wasmtime::Func::call`](crate::Func::call). 244 /// 245 /// # Panics 246 /// 247 /// Panics if this is called on a function in an asynchronous store. This 248 /// only works with functions defined within a synchronous store. Also 249 /// panics if `store` does not own this function. 250 pub fn call( 251 &self, 252 mut store: impl AsContextMut, 253 params: &[Val], 254 results: &mut [Val], 255 ) -> Result<()> { 256 let mut store = store.as_context_mut(); 257 assert!( 258 !store.0.async_support(), 259 "must use `call_async` when async support is enabled on the config" 260 ); 261 self.call_impl(&mut store.as_context_mut(), params, results) 262 } 263 264 /// Exactly like [`Self::call`] except for use on async stores. 265 /// 266 /// Note that after this [`Func::post_return_async`] will be used instead of 267 /// the synchronous version at [`Func::post_return`]. 268 /// 269 /// # Panics 270 /// 271 /// Panics if this is called on a function in a synchronous store. This 272 /// only works with functions defined within an asynchronous store. Also 273 /// panics if `store` does not own this function. 274 #[cfg(feature = "async")] 275 pub async fn call_async( 276 &self, 277 mut store: impl AsContextMut<Data: Send>, 278 params: &[Val], 279 results: &mut [Val], 280 ) -> Result<()> { 281 let mut store = store.as_context_mut(); 282 assert!( 283 store.0.async_support(), 284 "cannot use `call_async` without enabling async support in the config" 285 ); 286 store 287 .on_fiber(|store| self.call_impl(store, params, results)) 288 .await? 289 } 290 291 fn call_impl( 292 &self, 293 mut store: impl AsContextMut, 294 params: &[Val], 295 results: &mut [Val], 296 ) -> Result<()> { 297 let store = &mut store.as_context_mut(); 298 299 let param_tys = self.params(&store); 300 let result_tys = self.results(&store); 301 302 if param_tys.len() != params.len() { 303 bail!( 304 "expected {} argument(s), got {}", 305 param_tys.len(), 306 params.len() 307 ); 308 } 309 if result_tys.len() != results.len() { 310 bail!( 311 "expected {} results(s), got {}", 312 result_tys.len(), 313 results.len() 314 ); 315 } 316 317 self.call_raw( 318 store, 319 params, 320 |cx, params, params_ty, dst: &mut MaybeUninit<[ValRaw; MAX_FLAT_PARAMS]>| { 321 let params_ty = match params_ty { 322 InterfaceType::Tuple(i) => &cx.types[i], 323 _ => unreachable!(), 324 }; 325 if params_ty.abi.flat_count(MAX_FLAT_PARAMS).is_some() { 326 let dst = &mut unsafe { 327 mem::transmute::<_, &mut [MaybeUninit<ValRaw>; MAX_FLAT_PARAMS]>(dst) 328 } 329 .iter_mut(); 330 331 params 332 .iter() 333 .zip(params_ty.types.iter()) 334 .try_for_each(|(param, ty)| param.lower(cx, *ty, dst)) 335 } else { 336 self.store_args(cx, ¶ms_ty, params, dst) 337 } 338 }, 339 |cx, results_ty, src: &[ValRaw; MAX_FLAT_RESULTS]| { 340 let results_ty = match results_ty { 341 InterfaceType::Tuple(i) => &cx.types[i], 342 _ => unreachable!(), 343 }; 344 if results_ty.abi.flat_count(MAX_FLAT_RESULTS).is_some() { 345 let mut flat = src.iter(); 346 for (ty, slot) in results_ty.types.iter().zip(results) { 347 *slot = Val::lift(cx, *ty, &mut flat)?; 348 } 349 Ok(()) 350 } else { 351 Self::load_results(cx, results_ty, results, &mut src.iter()) 352 } 353 }, 354 ) 355 } 356 357 /// Invokes the underlying wasm function, lowering arguments and lifting the 358 /// result. 359 /// 360 /// The `lower` function and `lift` function provided here are what actually 361 /// do the lowering and lifting. The `LowerParams` and `LowerReturn` types 362 /// are what will be allocated on the stack for this function call. They 363 /// should be appropriately sized for the lowering/lifting operation 364 /// happening. 365 fn call_raw<T, Params: ?Sized, Return, LowerParams, LowerReturn>( 366 &self, 367 store: &mut StoreContextMut<'_, T>, 368 params: &Params, 369 lower: impl FnOnce( 370 &mut LowerContext<'_, T>, 371 &Params, 372 InterfaceType, 373 &mut MaybeUninit<LowerParams>, 374 ) -> Result<()>, 375 lift: impl FnOnce(&mut LiftContext<'_>, InterfaceType, &LowerReturn) -> Result<Return>, 376 ) -> Result<Return> 377 where 378 LowerParams: Copy, 379 LowerReturn: Copy, 380 { 381 let vminstance = self.instance.id().get(store.0); 382 let component = vminstance.component().clone(); 383 let (ty, def, options) = component.export_lifted_function(self.index); 384 385 let mem_opts = match options.data_model { 386 CanonicalOptionsDataModel::Gc {} => todo!("CM+GC"), 387 CanonicalOptionsDataModel::LinearMemory(opts) => opts, 388 }; 389 390 let export = match self.instance.lookup_vmdef(store.0, def) { 391 Export::Function(f) => f, 392 _ => unreachable!(), 393 }; 394 let vminstance = self.instance.id().get(store.0); 395 let component_instance = options.instance; 396 let memory = mem_opts 397 .memory 398 .map(|i| NonNull::new(vminstance.runtime_memory(i)).unwrap()); 399 let realloc = mem_opts.realloc.map(|i| vminstance.runtime_realloc(i)); 400 let options = 401 unsafe { Options::new(store.0.id(), memory, realloc, options.string_encoding) }; 402 403 let space = &mut MaybeUninit::<ParamsAndResults<LowerParams, LowerReturn>>::uninit(); 404 405 // Double-check the size/alignment of `space`, just in case. 406 // 407 // Note that this alone is not enough to guarantee the validity of the 408 // `unsafe` block below, but it's definitely required. In any case LLVM 409 // should be able to trivially see through these assertions and remove 410 // them in release mode. 411 let val_size = mem::size_of::<ValRaw>(); 412 let val_align = mem::align_of::<ValRaw>(); 413 assert!(mem::size_of_val(space) % val_size == 0); 414 assert!(mem::size_of_val(map_maybe_uninit!(space.params)) % val_size == 0); 415 assert!(mem::size_of_val(map_maybe_uninit!(space.ret)) % val_size == 0); 416 assert!(mem::align_of_val(space) == val_align); 417 assert!(mem::align_of_val(map_maybe_uninit!(space.params)) == val_align); 418 assert!(mem::align_of_val(map_maybe_uninit!(space.ret)) == val_align); 419 420 let types = component.types(); 421 let mut flags = vminstance.instance_flags(component_instance); 422 423 unsafe { 424 // Test the "may enter" flag which is a "lock" on this instance. 425 // This is immediately set to `false` afterwards and note that 426 // there's no on-cleanup setting this flag back to true. That's an 427 // intentional design aspect where if anything goes wrong internally 428 // from this point on the instance is considered "poisoned" and can 429 // never be entered again. The only time this flag is set to `true` 430 // again is after post-return logic has completed successfully. 431 if !flags.may_enter() { 432 bail!(crate::Trap::CannotEnterComponent); 433 } 434 flags.set_may_enter(false); 435 436 debug_assert!(flags.may_leave()); 437 flags.set_may_leave(false); 438 let mut cx = LowerContext::new(store.as_context_mut(), &options, &types, self.instance); 439 cx.enter_call(); 440 let result = lower( 441 &mut cx, 442 params, 443 InterfaceType::Tuple(types[ty].params), 444 map_maybe_uninit!(space.params), 445 ); 446 flags.set_may_leave(true); 447 result?; 448 449 // This is unsafe as we are providing the guarantee that all the 450 // inputs are valid. The various pointers passed in for the function 451 // are all valid since they're coming from our store, and the 452 // `params_and_results` should have the correct layout for the core 453 // wasm function we're calling. Note that this latter point relies 454 // on the correctness of this module and `ComponentType` 455 // implementations, hence `ComponentType` being an `unsafe` trait. 456 crate::Func::call_unchecked_raw( 457 store, 458 export.func_ref, 459 NonNull::new(core::ptr::slice_from_raw_parts_mut( 460 space.as_mut_ptr().cast(), 461 mem::size_of_val(space) / mem::size_of::<ValRaw>(), 462 )) 463 .unwrap(), 464 )?; 465 466 // Note that `.assume_init_ref()` here is unsafe but we're relying 467 // on the correctness of the structure of `LowerReturn` and the 468 // type-checking performed to acquire the `TypedFunc` to make this 469 // safe. It should be the case that `LowerReturn` is the exact 470 // representation of the return value when interpreted as 471 // `[ValRaw]`, and additionally they should have the correct types 472 // for the function we just called (which filled in the return 473 // values). 474 let ret = map_maybe_uninit!(space.ret).assume_init_ref(); 475 476 // Lift the result into the host while managing post-return state 477 // here as well. 478 // 479 // After a successful lift the return value of the function, which 480 // is currently required to be 0 or 1 values according to the 481 // canonical ABI, is saved within the `Store`'s `FuncData`. This'll 482 // later get used in post-return. 483 flags.set_needs_post_return(true); 484 let val = lift( 485 &mut LiftContext::new(store.0, &options, &types, self.instance), 486 InterfaceType::Tuple(types[ty].results), 487 ret, 488 )?; 489 let ret_slice = storage_as_slice(ret); 490 self.instance.id().get_mut(store.0).post_return_arg_set( 491 self.index, 492 match ret_slice.len() { 493 0 => ValRaw::i32(0), 494 1 => ret_slice[0], 495 _ => unreachable!(), 496 }, 497 ); 498 return Ok(val); 499 } 500 } 501 502 /// Invokes the `post-return` canonical ABI option, if specified, after a 503 /// [`Func::call`] has finished. 504 /// 505 /// This function is a required method call after a [`Func::call`] completes 506 /// successfully. After the embedder has finished processing the return 507 /// value then this function must be invoked. 508 /// 509 /// # Errors 510 /// 511 /// This function will return an error in the case of a WebAssembly trap 512 /// happening during the execution of the `post-return` function, if 513 /// specified. 514 /// 515 /// # Panics 516 /// 517 /// This function will panic if it's not called under the correct 518 /// conditions. This can only be called after a previous invocation of 519 /// [`Func::call`] completes successfully, and this function can only 520 /// be called for the same [`Func`] that was `call`'d. 521 /// 522 /// If this function is called when [`Func::call`] was not previously 523 /// called, then it will panic. If a different [`Func`] for the same 524 /// component instance was invoked then this function will also panic 525 /// because the `post-return` needs to happen for the other function. 526 /// 527 /// Panics if this is called on a function in an asynchronous store. 528 /// This only works with functions defined within a synchronous store. 529 #[inline] 530 pub fn post_return(&self, mut store: impl AsContextMut) -> Result<()> { 531 let store = store.as_context_mut(); 532 assert!( 533 !store.0.async_support(), 534 "must use `post_return_async` when async support is enabled on the config" 535 ); 536 self.post_return_impl(store) 537 } 538 539 /// Exactly like [`Self::post_return`] except for use on async stores. 540 /// 541 /// # Panics 542 /// 543 /// Panics if this is called on a function in a synchronous store. This 544 /// only works with functions defined within an asynchronous store. 545 #[cfg(feature = "async")] 546 pub async fn post_return_async(&self, mut store: impl AsContextMut<Data: Send>) -> Result<()> { 547 let mut store = store.as_context_mut(); 548 assert!( 549 store.0.async_support(), 550 "cannot use `call_async` without enabling async support in the config" 551 ); 552 // Future optimization opportunity: conditionally use a fiber here since 553 // some func's post_return will not need the async context (i.e. end up 554 // calling async host functionality) 555 store.on_fiber(|store| self.post_return_impl(store)).await? 556 } 557 558 fn post_return_impl(&self, mut store: impl AsContextMut) -> Result<()> { 559 let mut store = store.as_context_mut(); 560 let index = self.index; 561 let vminstance = self.instance.id().get(store.0); 562 let (_ty, _def, options) = vminstance.component().export_lifted_function(index); 563 let post_return = options.post_return.map(|i| { 564 let func_ref = vminstance.runtime_post_return(i); 565 ExportFunction { func_ref } 566 }); 567 let mut flags = vminstance.instance_flags(options.instance); 568 let mut instance = self.instance.id().get_mut(store.0); 569 let post_return_arg = instance.as_mut().post_return_arg_take(index); 570 571 unsafe { 572 // First assert that the instance is in a "needs post return" state. 573 // This will ensure that the previous action on the instance was a 574 // function call above. This flag is only set after a component 575 // function returns so this also can't be called (as expected) 576 // during a host import for example. 577 // 578 // Note, though, that this assert is not sufficient because it just 579 // means some function on this instance needs its post-return 580 // called. We need a precise post-return for a particular function 581 // which is the second assert here (the `.expect`). That will assert 582 // that this function itself needs to have its post-return called. 583 // 584 // The theory at least is that these two asserts ensure component 585 // model semantics are upheld where the host properly calls 586 // `post_return` on the right function despite the call being a 587 // separate step in the API. 588 assert!( 589 flags.needs_post_return(), 590 "post_return can only be called after a function has previously been called", 591 ); 592 let post_return_arg = post_return_arg.expect("calling post_return on wrong function"); 593 594 // This is a sanity-check assert which shouldn't ever trip. 595 assert!(!flags.may_enter()); 596 597 // Unset the "needs post return" flag now that post-return is being 598 // processed. This will cause future invocations of this method to 599 // panic, even if the function call below traps. 600 flags.set_needs_post_return(false); 601 602 // If the function actually had a `post-return` configured in its 603 // canonical options that's executed here. 604 // 605 // Note that if this traps (returns an error) this function 606 // intentionally leaves the instance in a "poisoned" state where it 607 // can no longer be entered because `may_enter` is `false`. 608 if let Some(func) = post_return { 609 crate::Func::call_unchecked_raw( 610 &mut store, 611 func.func_ref, 612 NonNull::new(core::ptr::slice_from_raw_parts(&post_return_arg, 1).cast_mut()) 613 .unwrap(), 614 )?; 615 } 616 617 // And finally if everything completed successfully then the "may 618 // enter" flag is set to `true` again here which enables further use 619 // of the component. 620 flags.set_may_enter(true); 621 622 let (calls, host_table, _, instance) = store 623 .0 624 .component_resource_state_with_instance(self.instance); 625 ResourceTables { 626 host_table: Some(host_table), 627 calls, 628 guest: Some(instance.guest_tables()), 629 } 630 .exit_call()?; 631 } 632 Ok(()) 633 } 634 635 fn store_args<T>( 636 &self, 637 cx: &mut LowerContext<'_, T>, 638 params_ty: &TypeTuple, 639 args: &[Val], 640 dst: &mut MaybeUninit<[ValRaw; MAX_FLAT_PARAMS]>, 641 ) -> Result<()> { 642 let size = usize::try_from(params_ty.abi.size32).unwrap(); 643 let ptr = cx.realloc(0, 0, params_ty.abi.align32, size)?; 644 let mut offset = ptr; 645 for (ty, arg) in params_ty.types.iter().zip(args) { 646 let abi = cx.types.canonical_abi(ty); 647 arg.store(cx, *ty, abi.next_field32_size(&mut offset))?; 648 } 649 650 map_maybe_uninit!(dst[0]).write(ValRaw::i64(ptr as i64)); 651 652 Ok(()) 653 } 654 655 fn load_results( 656 cx: &mut LiftContext<'_>, 657 results_ty: &TypeTuple, 658 results: &mut [Val], 659 src: &mut core::slice::Iter<'_, ValRaw>, 660 ) -> Result<()> { 661 // FIXME(#4311): needs to read an i64 for memory64 662 let ptr = usize::try_from(src.next().unwrap().get_u32())?; 663 if ptr % usize::try_from(results_ty.abi.align32)? != 0 { 664 bail!("return pointer not aligned"); 665 } 666 667 let bytes = cx 668 .memory() 669 .get(ptr..) 670 .and_then(|b| b.get(..usize::try_from(results_ty.abi.size32).unwrap())) 671 .ok_or_else(|| anyhow::anyhow!("pointer out of bounds of memory"))?; 672 673 let mut offset = 0; 674 for (ty, slot) in results_ty.types.iter().zip(results) { 675 let abi = cx.types.canonical_abi(ty); 676 let offset = abi.next_field32_size(&mut offset); 677 *slot = Val::load(cx, *ty, &bytes[offset..][..abi.size32 as usize])?; 678 } 679 Ok(()) 680 } 681 } 682