1 //! Compilation support for the component model. 2 3 use crate::{TRAP_CANNOT_LEAVE_COMPONENT, TRAP_INTERNAL_ASSERT, compiler::Compiler}; 4 use cranelift_codegen::ir::condcodes::IntCC; 5 use cranelift_codegen::ir::{self, InstBuilder, MemFlags, Value}; 6 use cranelift_codegen::isa::{CallConv, TargetIsa}; 7 use cranelift_frontend::FunctionBuilder; 8 use wasmtime_environ::error::{Result, bail}; 9 use wasmtime_environ::{ 10 Abi, CompiledFunctionBody, EntityRef, FuncKey, HostCall, PtrSize, TrapSentinel, Tunables, 11 WasmFuncType, WasmValType, component::*, fact::PREPARE_CALL_FIXED_PARAMS, 12 }; 13 14 struct TrampolineCompiler<'a> { 15 compiler: &'a Compiler, 16 isa: &'a (dyn TargetIsa + 'static), 17 builder: FunctionBuilder<'a>, 18 component: &'a Component, 19 types: &'a ComponentTypesBuilder, 20 offsets: VMComponentOffsets<u8>, 21 block0: ir::Block, 22 signature: &'a WasmFuncType, 23 } 24 25 /// What host functions can be called, used in `translate_hostcall` below. 26 enum HostCallee { 27 /// Call a host-lowered function specified by this index. 28 Lowering(LoweredIndex), 29 /// Call a host libcall, specified by this accessor. 30 Libcall(GetLibcallFn), 31 } 32 33 type GetLibcallFn = 34 fn(&dyn TargetIsa, &mut ir::Function) -> (ir::SigRef, ComponentBuiltinFunctionIndex); 35 36 impl From<LoweredIndex> for HostCallee { 37 fn from(index: LoweredIndex) -> HostCallee { 38 HostCallee::Lowering(index) 39 } 40 } 41 42 impl From<GetLibcallFn> for HostCallee { 43 fn from(f: GetLibcallFn) -> HostCallee { 44 HostCallee::Libcall(f) 45 } 46 } 47 48 /// How to interpret the results of a host function. 49 enum HostResult { 50 /// The host function returns the sentinel specified which is interpreted 51 /// and translated to the real return value. 52 Sentinel(TrapSentinel), 53 54 /// The host function returns a `bool` indicating whether it succeeded or 55 /// not. 56 /// 57 /// After the return value is interpreted the host function also filled in 58 /// `ptr` and `len` with wasm return values which need to be returned. 59 /// 60 /// If `ptr` and `len` are not specified then this must be used with 61 /// `WasmArgs::ValRawList` and that ptr/len is used. 62 MultiValue { 63 /// The base pointer of the `ValRaw` list on the stack. 64 ptr: Option<ir::Value>, 65 /// The length of the `ValRaw` list on the stack. 66 len: Option<ir::Value>, 67 }, 68 } 69 70 impl From<TrapSentinel> for HostResult { 71 fn from(sentinel: TrapSentinel) -> HostResult { 72 HostResult::Sentinel(sentinel) 73 } 74 } 75 76 /// Different means of passing WebAssembly arguments to host calls. 77 #[derive(Debug, Copy, Clone)] 78 enum WasmArgs { 79 /// All wasm arguments to the host are passed directly as values, typically 80 /// through registers. 81 InRegisters, 82 83 /// All wasm arguments to the host are passed indirectly by spilling them 84 /// to the stack as a sequence of contiguous `ValRaw`s. 85 ValRawList, 86 87 /// The first `n` arguments are passed in registers, but everything after 88 /// that is spilled to the stack. 89 InRegistersUpTo(usize), 90 } 91 92 impl<'a> TrampolineCompiler<'a> { 93 fn new( 94 compiler: &'a Compiler, 95 func_compiler: &'a mut super::FunctionCompiler<'_>, 96 component: &'a Component, 97 types: &'a ComponentTypesBuilder, 98 signature: &'a WasmFuncType, 99 ) -> TrampolineCompiler<'a> { 100 let isa = &*compiler.isa; 101 let func = ir::Function::with_name_signature( 102 ir::UserFuncName::user(0, 0), 103 crate::wasm_call_signature(isa, signature, &compiler.tunables), 104 ); 105 let (builder, block0) = func_compiler.builder(func); 106 TrampolineCompiler { 107 compiler, 108 isa, 109 builder, 110 component, 111 types, 112 offsets: VMComponentOffsets::new(isa.pointer_bytes(), component), 113 block0, 114 signature, 115 } 116 } 117 118 fn translate(&mut self, trampoline: &Trampoline) { 119 self.check_may_leave(trampoline); 120 121 match trampoline { 122 Trampoline::Transcoder { 123 op, 124 from, 125 from64, 126 to, 127 to64, 128 } => { 129 self.translate_transcode(*op, *from, *from64, *to, *to64); 130 } 131 Trampoline::LowerImport { 132 index, 133 options, 134 lower_ty, 135 } => { 136 let pointer_type = self.isa.pointer_type(); 137 self.translate_hostcall( 138 HostCallee::Lowering(*index), 139 HostResult::MultiValue { 140 ptr: None, 141 len: None, 142 }, 143 WasmArgs::ValRawList, 144 |me, params| { 145 let vmctx = params[0]; 146 params.extend([ 147 me.builder.ins().load( 148 pointer_type, 149 MemFlags::trusted(), 150 vmctx, 151 i32::try_from(me.offsets.lowering_data(*index)).unwrap(), 152 ), 153 me.index_value(*lower_ty), 154 me.index_value(*options), 155 ]); 156 }, 157 ); 158 } 159 Trampoline::ResourceNew { instance, ty } => { 160 // Currently this only supports resources represented by `i32` 161 assert_eq!(self.signature.params()[0], WasmValType::I32); 162 self.translate_libcall( 163 host::resource_new32, 164 TrapSentinel::NegativeOne, 165 WasmArgs::InRegisters, 166 |me, params| { 167 params.push(me.index_value(*instance)); 168 params.push(me.index_value(*ty)); 169 }, 170 ); 171 } 172 Trampoline::ResourceRep { instance, ty } => { 173 // Currently this only supports resources represented by `i32` 174 assert_eq!(self.signature.returns()[0], WasmValType::I32); 175 self.translate_libcall( 176 host::resource_rep32, 177 TrapSentinel::NegativeOne, 178 WasmArgs::InRegisters, 179 |me, params| { 180 params.push(me.index_value(*instance)); 181 params.push(me.index_value(*ty)); 182 }, 183 ); 184 } 185 Trampoline::ResourceDrop { instance, ty } => { 186 self.translate_resource_drop(*instance, *ty); 187 } 188 Trampoline::BackpressureInc { instance } => { 189 self.translate_libcall( 190 host::backpressure_modify, 191 TrapSentinel::Falsy, 192 WasmArgs::InRegisters, 193 |me, params| { 194 params.push(me.index_value(*instance)); 195 params.push(me.builder.ins().iconst(ir::types::I8, 1)); 196 }, 197 ); 198 } 199 Trampoline::BackpressureDec { instance } => { 200 self.translate_libcall( 201 host::backpressure_modify, 202 TrapSentinel::Falsy, 203 WasmArgs::InRegisters, 204 |me, params| { 205 params.push(me.index_value(*instance)); 206 params.push(me.builder.ins().iconst(ir::types::I8, 0)); 207 }, 208 ); 209 } 210 Trampoline::TaskReturn { 211 instance, 212 results, 213 options, 214 } => { 215 self.translate_libcall( 216 host::task_return, 217 TrapSentinel::Falsy, 218 WasmArgs::ValRawList, 219 |me, params| { 220 params.push(me.index_value(*instance)); 221 params.push(me.index_value(*results)); 222 params.push(me.index_value(*options)); 223 }, 224 ); 225 } 226 Trampoline::TaskCancel { instance } => { 227 self.translate_libcall( 228 host::task_cancel, 229 TrapSentinel::Falsy, 230 WasmArgs::InRegisters, 231 |me, params| { 232 params.push(me.index_value(*instance)); 233 }, 234 ); 235 } 236 Trampoline::WaitableSetNew { instance } => { 237 self.translate_libcall( 238 host::waitable_set_new, 239 TrapSentinel::NegativeOne, 240 WasmArgs::InRegisters, 241 |me, params| { 242 params.push(me.index_value(*instance)); 243 }, 244 ); 245 } 246 Trampoline::WaitableSetWait { instance, options } => { 247 self.translate_libcall( 248 host::waitable_set_wait, 249 TrapSentinel::NegativeOne, 250 WasmArgs::InRegisters, 251 |me, params| { 252 params.push(me.index_value(*instance)); 253 params.push(me.index_value(*options)); 254 }, 255 ); 256 } 257 Trampoline::WaitableSetPoll { instance, options } => { 258 self.translate_libcall( 259 host::waitable_set_poll, 260 TrapSentinel::NegativeOne, 261 WasmArgs::InRegisters, 262 |me, params| { 263 params.push(me.index_value(*instance)); 264 params.push(me.index_value(*options)); 265 }, 266 ); 267 } 268 Trampoline::WaitableSetDrop { instance } => { 269 self.translate_libcall( 270 host::waitable_set_drop, 271 TrapSentinel::Falsy, 272 WasmArgs::InRegisters, 273 |me, params| { 274 params.push(me.index_value(*instance)); 275 }, 276 ); 277 } 278 Trampoline::WaitableJoin { instance } => { 279 self.translate_libcall( 280 host::waitable_join, 281 TrapSentinel::Falsy, 282 WasmArgs::InRegisters, 283 |me, params| { 284 params.push(me.index_value(*instance)); 285 }, 286 ); 287 } 288 Trampoline::ThreadYield { 289 instance, 290 cancellable, 291 } => { 292 self.translate_libcall( 293 host::thread_yield, 294 TrapSentinel::NegativeOne, 295 WasmArgs::InRegisters, 296 |me, params| { 297 params.push(me.index_value(*instance)); 298 params.push( 299 me.builder 300 .ins() 301 .iconst(ir::types::I8, i64::from(*cancellable)), 302 ); 303 }, 304 ); 305 } 306 Trampoline::SubtaskDrop { instance } => { 307 self.translate_libcall( 308 host::subtask_drop, 309 TrapSentinel::Falsy, 310 WasmArgs::InRegisters, 311 |me, params| { 312 params.push(me.index_value(*instance)); 313 }, 314 ); 315 } 316 Trampoline::SubtaskCancel { instance, async_ } => { 317 self.translate_libcall( 318 host::subtask_cancel, 319 TrapSentinel::NegativeOne, 320 WasmArgs::InRegisters, 321 |me, params| { 322 params.push(me.index_value(*instance)); 323 params.push(me.builder.ins().iconst(ir::types::I8, i64::from(*async_))); 324 }, 325 ); 326 } 327 Trampoline::StreamNew { instance, ty } => { 328 self.translate_libcall( 329 host::stream_new, 330 TrapSentinel::NegativeOne, 331 WasmArgs::InRegisters, 332 |me, params| { 333 params.push(me.index_value(*instance)); 334 params.push(me.index_value(*ty)); 335 }, 336 ); 337 } 338 Trampoline::StreamRead { 339 instance, 340 ty, 341 options, 342 } => { 343 if let Some(info) = self.flat_stream_element_info(*ty).cloned() { 344 self.translate_libcall( 345 host::flat_stream_read, 346 TrapSentinel::NegativeOne, 347 WasmArgs::InRegisters, 348 |me, params| { 349 params.extend([ 350 me.index_value(*instance), 351 me.index_value(*ty), 352 me.index_value(*options), 353 me.builder 354 .ins() 355 .iconst(ir::types::I32, i64::from(info.size32)), 356 me.builder 357 .ins() 358 .iconst(ir::types::I32, i64::from(info.align32)), 359 ]); 360 }, 361 ); 362 } else { 363 self.translate_libcall( 364 host::stream_read, 365 TrapSentinel::NegativeOne, 366 WasmArgs::InRegisters, 367 |me, params| { 368 params.push(me.index_value(*instance)); 369 params.push(me.index_value(*ty)); 370 params.push(me.index_value(*options)); 371 }, 372 ); 373 } 374 } 375 Trampoline::StreamWrite { 376 instance, 377 ty, 378 options, 379 } => { 380 if let Some(info) = self.flat_stream_element_info(*ty).cloned() { 381 self.translate_libcall( 382 host::flat_stream_write, 383 TrapSentinel::NegativeOne, 384 WasmArgs::InRegisters, 385 |me, params| { 386 params.extend([ 387 me.index_value(*instance), 388 me.index_value(*ty), 389 me.index_value(*options), 390 me.builder 391 .ins() 392 .iconst(ir::types::I32, i64::from(info.size32)), 393 me.builder 394 .ins() 395 .iconst(ir::types::I32, i64::from(info.align32)), 396 ]); 397 }, 398 ); 399 } else { 400 self.translate_libcall( 401 host::stream_write, 402 TrapSentinel::NegativeOne, 403 WasmArgs::InRegisters, 404 |me, params| { 405 params.push(me.index_value(*instance)); 406 params.push(me.index_value(*ty)); 407 params.push(me.index_value(*options)); 408 }, 409 ); 410 } 411 } 412 Trampoline::StreamCancelRead { 413 instance, 414 ty, 415 async_, 416 } => { 417 self.translate_libcall( 418 host::stream_cancel_read, 419 TrapSentinel::NegativeOne, 420 WasmArgs::InRegisters, 421 |me, params| { 422 params.push(me.index_value(*instance)); 423 params.push(me.index_value(*ty)); 424 params.push(me.builder.ins().iconst(ir::types::I8, i64::from(*async_))); 425 }, 426 ); 427 } 428 Trampoline::StreamCancelWrite { 429 instance, 430 ty, 431 async_, 432 } => { 433 self.translate_libcall( 434 host::stream_cancel_write, 435 TrapSentinel::NegativeOne, 436 WasmArgs::InRegisters, 437 |me, params| { 438 params.push(me.index_value(*instance)); 439 params.push(me.index_value(*ty)); 440 params.push(me.builder.ins().iconst(ir::types::I8, i64::from(*async_))); 441 }, 442 ); 443 } 444 Trampoline::StreamDropReadable { instance, ty } => { 445 self.translate_libcall( 446 host::stream_drop_readable, 447 TrapSentinel::Falsy, 448 WasmArgs::InRegisters, 449 |me, params| { 450 params.push(me.index_value(*instance)); 451 params.push(me.index_value(*ty)); 452 }, 453 ); 454 } 455 Trampoline::StreamDropWritable { instance, ty } => { 456 self.translate_libcall( 457 host::stream_drop_writable, 458 TrapSentinel::Falsy, 459 WasmArgs::InRegisters, 460 |me, params| { 461 params.push(me.index_value(*instance)); 462 params.push(me.index_value(*ty)); 463 }, 464 ); 465 } 466 Trampoline::FutureNew { instance, ty } => { 467 self.translate_libcall( 468 host::future_new, 469 TrapSentinel::NegativeOne, 470 WasmArgs::InRegisters, 471 |me, params| { 472 params.push(me.index_value(*instance)); 473 params.push(me.index_value(*ty)); 474 }, 475 ); 476 } 477 Trampoline::FutureRead { 478 instance, 479 ty, 480 options, 481 } => { 482 self.translate_libcall( 483 host::future_read, 484 TrapSentinel::NegativeOne, 485 WasmArgs::InRegisters, 486 |me, params| { 487 params.push(me.index_value(*instance)); 488 params.push(me.index_value(*ty)); 489 params.push(me.index_value(*options)); 490 }, 491 ); 492 } 493 Trampoline::FutureWrite { 494 instance, 495 ty, 496 options, 497 } => { 498 self.translate_libcall( 499 host::future_write, 500 TrapSentinel::NegativeOne, 501 WasmArgs::InRegisters, 502 |me, params| { 503 params.push(me.index_value(*instance)); 504 params.push(me.index_value(*ty)); 505 params.push(me.index_value(*options)); 506 }, 507 ); 508 } 509 Trampoline::FutureCancelRead { 510 instance, 511 ty, 512 async_, 513 } => { 514 self.translate_libcall( 515 host::future_cancel_read, 516 TrapSentinel::NegativeOne, 517 WasmArgs::InRegisters, 518 |me, params| { 519 params.push(me.index_value(*instance)); 520 params.push(me.index_value(*ty)); 521 params.push(me.builder.ins().iconst(ir::types::I8, i64::from(*async_))); 522 }, 523 ); 524 } 525 Trampoline::FutureCancelWrite { 526 instance, 527 ty, 528 async_, 529 } => { 530 self.translate_libcall( 531 host::future_cancel_write, 532 TrapSentinel::NegativeOne, 533 WasmArgs::InRegisters, 534 |me, params| { 535 params.push(me.index_value(*instance)); 536 params.push(me.index_value(*ty)); 537 params.push(me.builder.ins().iconst(ir::types::I8, i64::from(*async_))); 538 }, 539 ); 540 } 541 Trampoline::FutureDropReadable { instance, ty } => { 542 self.translate_libcall( 543 host::future_drop_readable, 544 TrapSentinel::Falsy, 545 WasmArgs::InRegisters, 546 |me, params| { 547 params.push(me.index_value(*instance)); 548 params.push(me.index_value(*ty)); 549 }, 550 ); 551 } 552 Trampoline::FutureDropWritable { instance, ty } => { 553 self.translate_libcall( 554 host::future_drop_writable, 555 TrapSentinel::Falsy, 556 WasmArgs::InRegisters, 557 |me, params| { 558 params.push(me.index_value(*instance)); 559 params.push(me.index_value(*ty)); 560 }, 561 ); 562 } 563 Trampoline::ErrorContextNew { 564 instance, 565 ty, 566 options, 567 } => { 568 self.translate_libcall( 569 host::error_context_new, 570 TrapSentinel::NegativeOne, 571 WasmArgs::InRegisters, 572 |me, params| { 573 params.push(me.index_value(*instance)); 574 params.push(me.index_value(*ty)); 575 params.push(me.index_value(*options)); 576 }, 577 ); 578 } 579 Trampoline::ErrorContextDebugMessage { 580 instance, 581 ty, 582 options, 583 } => { 584 self.translate_libcall( 585 host::error_context_debug_message, 586 TrapSentinel::Falsy, 587 WasmArgs::InRegisters, 588 |me, params| { 589 params.push(me.index_value(*instance)); 590 params.push(me.index_value(*ty)); 591 params.push(me.index_value(*options)); 592 }, 593 ); 594 } 595 Trampoline::ErrorContextDrop { instance, ty } => { 596 self.translate_libcall( 597 host::error_context_drop, 598 TrapSentinel::Falsy, 599 WasmArgs::InRegisters, 600 |me, params| { 601 params.push(me.index_value(*instance)); 602 params.push(me.index_value(*ty)); 603 }, 604 ); 605 } 606 Trampoline::ResourceTransferOwn => { 607 self.translate_libcall( 608 host::resource_transfer_own, 609 TrapSentinel::NegativeOne, 610 WasmArgs::InRegisters, 611 |_, _| {}, 612 ); 613 } 614 Trampoline::ResourceTransferBorrow => { 615 self.translate_libcall( 616 host::resource_transfer_borrow, 617 TrapSentinel::NegativeOne, 618 WasmArgs::InRegisters, 619 |_, _| {}, 620 ); 621 } 622 Trampoline::PrepareCall { memory } => { 623 self.translate_libcall( 624 host::prepare_call, 625 TrapSentinel::Falsy, 626 WasmArgs::InRegistersUpTo(PREPARE_CALL_FIXED_PARAMS.len()), 627 |me, params| { 628 let vmctx = params[0]; 629 params.push(me.load_optional_memory(vmctx, *memory)); 630 }, 631 ); 632 } 633 Trampoline::SyncStartCall { callback } => { 634 let pointer_type = self.isa.pointer_type(); 635 let (values_vec_ptr, len) = self.compiler.allocate_stack_array_and_spill_args( 636 &WasmFuncType::new( 637 Box::new([]), 638 self.signature.returns().iter().copied().collect(), 639 ), 640 &mut self.builder, 641 &[], 642 ); 643 let values_vec_len = self.builder.ins().iconst(pointer_type, i64::from(len)); 644 self.translate_libcall( 645 host::sync_start, 646 HostResult::MultiValue { 647 ptr: Some(values_vec_ptr), 648 len: Some(values_vec_len), 649 }, 650 WasmArgs::InRegisters, 651 |me, params| { 652 let vmctx = params[0]; 653 params.push(me.load_callback(vmctx, *callback)); 654 params.push(values_vec_ptr); 655 params.push(values_vec_len); 656 }, 657 ); 658 } 659 Trampoline::AsyncStartCall { 660 callback, 661 post_return, 662 } => { 663 self.translate_libcall( 664 host::async_start, 665 TrapSentinel::NegativeOne, 666 WasmArgs::InRegisters, 667 |me, params| { 668 let vmctx = params[0]; 669 params.extend([ 670 me.load_callback(vmctx, *callback), 671 me.load_post_return(vmctx, *post_return), 672 ]); 673 }, 674 ); 675 } 676 Trampoline::FutureTransfer => { 677 self.translate_libcall( 678 host::future_transfer, 679 TrapSentinel::NegativeOne, 680 WasmArgs::InRegisters, 681 |_, _| {}, 682 ); 683 } 684 Trampoline::StreamTransfer => { 685 self.translate_libcall( 686 host::stream_transfer, 687 TrapSentinel::NegativeOne, 688 WasmArgs::InRegisters, 689 |_, _| {}, 690 ); 691 } 692 Trampoline::ErrorContextTransfer => { 693 self.translate_libcall( 694 host::error_context_transfer, 695 TrapSentinel::NegativeOne, 696 WasmArgs::InRegisters, 697 |_, _| {}, 698 ); 699 } 700 Trampoline::Trap => { 701 self.translate_libcall( 702 host::trap, 703 TrapSentinel::Falsy, 704 WasmArgs::InRegisters, 705 |_, _| {}, 706 ); 707 } 708 Trampoline::EnterSyncCall => { 709 self.translate_libcall( 710 host::enter_sync_call, 711 TrapSentinel::Falsy, 712 WasmArgs::InRegisters, 713 |_, _| {}, 714 ); 715 } 716 Trampoline::ExitSyncCall => { 717 self.translate_libcall( 718 host::exit_sync_call, 719 TrapSentinel::Falsy, 720 WasmArgs::InRegisters, 721 |_, _| {}, 722 ); 723 } 724 Trampoline::ContextGet { instance, slot } => { 725 self.translate_libcall( 726 host::context_get, 727 TrapSentinel::NegativeOne, 728 WasmArgs::InRegisters, 729 |me, params| { 730 params.push(me.index_value(*instance)); 731 params.push(me.builder.ins().iconst(ir::types::I32, i64::from(*slot))); 732 }, 733 ); 734 } 735 Trampoline::ContextSet { instance, slot } => { 736 self.translate_libcall( 737 host::context_set, 738 TrapSentinel::Falsy, 739 WasmArgs::InRegisters, 740 |me, params| { 741 params.push(me.index_value(*instance)); 742 params.push(me.builder.ins().iconst(ir::types::I32, i64::from(*slot))); 743 }, 744 ); 745 } 746 Trampoline::ThreadIndex => { 747 self.translate_libcall( 748 host::thread_index, 749 TrapSentinel::NegativeOne, 750 WasmArgs::InRegisters, 751 |_, _| {}, 752 ); 753 } 754 Trampoline::ThreadNewIndirect { 755 instance, 756 start_func_table_idx, 757 start_func_ty_idx, 758 } => { 759 self.translate_libcall( 760 host::thread_new_indirect, 761 TrapSentinel::NegativeOne, 762 WasmArgs::InRegisters, 763 |me, params| { 764 params.push(me.index_value(*instance)); 765 params.push(me.index_value(*start_func_table_idx)); 766 params.push(me.index_value(*start_func_ty_idx)); 767 }, 768 ); 769 } 770 Trampoline::ThreadSuspendToSuspended { 771 instance, 772 cancellable, 773 } => { 774 self.translate_libcall( 775 host::thread_suspend_to_suspended, 776 TrapSentinel::NegativeOne, 777 WasmArgs::InRegisters, 778 |me, params| { 779 params.push(me.index_value(*instance)); 780 params.push( 781 me.builder 782 .ins() 783 .iconst(ir::types::I8, i64::from(*cancellable)), 784 ); 785 }, 786 ); 787 } 788 Trampoline::ThreadSuspendTo { 789 instance, 790 cancellable, 791 } => { 792 self.translate_libcall( 793 host::thread_suspend_to, 794 TrapSentinel::NegativeOne, 795 WasmArgs::InRegisters, 796 |me, params| { 797 params.push(me.index_value(*instance)); 798 params.push( 799 me.builder 800 .ins() 801 .iconst(ir::types::I8, i64::from(*cancellable)), 802 ); 803 }, 804 ); 805 } 806 Trampoline::ThreadSuspend { 807 instance, 808 cancellable, 809 } => { 810 self.translate_libcall( 811 host::thread_suspend, 812 TrapSentinel::NegativeOne, 813 WasmArgs::InRegisters, 814 |me, params| { 815 params.push(me.index_value(*instance)); 816 params.push( 817 me.builder 818 .ins() 819 .iconst(ir::types::I8, i64::from(*cancellable)), 820 ); 821 }, 822 ); 823 } 824 Trampoline::ThreadUnsuspend { instance } => { 825 self.translate_libcall( 826 host::thread_unsuspend, 827 TrapSentinel::Falsy, 828 WasmArgs::InRegisters, 829 |me, params| { 830 params.push(me.index_value(*instance)); 831 }, 832 ); 833 } 834 Trampoline::ThreadYieldToSuspended { 835 instance, 836 cancellable, 837 } => { 838 self.translate_libcall( 839 host::thread_yield_to_suspended, 840 TrapSentinel::NegativeOne, 841 WasmArgs::InRegisters, 842 |me, params| { 843 params.push(me.index_value(*instance)); 844 params.push( 845 me.builder 846 .ins() 847 .iconst(ir::types::I8, i64::from(*cancellable)), 848 ); 849 }, 850 ); 851 } 852 } 853 } 854 855 /// Determine whether the specified type can be optimized as a stream 856 /// payload by lifting and lowering with a simple `memcpy`. 857 /// 858 /// Any type containing only "flat", primitive data for which all bit 859 /// patterns are valid (i.e. no pointers, handles, bools, or chars) should 860 /// qualify for this optimization, but it's also okay to conservatively 861 /// return `None` here; the fallback slow path will always work -- it just 862 /// won't be as efficient. 863 fn flat_stream_element_info(&self, ty: TypeStreamTableIndex) -> Option<&CanonicalAbiInfo> { 864 let payload = self.types[self.types[ty].ty].payload; 865 match payload { 866 None => Some(&CanonicalAbiInfo::ZERO), 867 Some( 868 // Note that we exclude `Bool` and `Char` from this list because 869 // not all bit patterns are valid for those types. 870 payload @ (InterfaceType::S8 871 | InterfaceType::U8 872 | InterfaceType::S16 873 | InterfaceType::U16 874 | InterfaceType::S32 875 | InterfaceType::U32 876 | InterfaceType::S64 877 | InterfaceType::U64 878 | InterfaceType::Float32 879 | InterfaceType::Float64), 880 ) => Some(self.types.canonical_abi(&payload)), 881 // TODO: Recursively check for other "flat" types (i.e. those without pointers or handles), 882 // e.g. `record`s, `variant`s, etc. which contain only flat types. 883 _ => None, 884 } 885 } 886 887 /// Helper function to spill the wasm arguments `args` to this function into 888 /// a stack-allocated array. 889 fn store_wasm_arguments(&mut self, args: &[Value]) -> (Value, Value) { 890 let pointer_type = self.isa.pointer_type(); 891 892 let (ptr, len) = self.compiler.allocate_stack_array_and_spill_args( 893 self.signature, 894 &mut self.builder, 895 args, 896 ); 897 let len = self.builder.ins().iconst(pointer_type, i64::from(len)); 898 (ptr, len) 899 } 900 901 /// Convenience wrapper around `translate_hostcall` to enable type inference 902 /// on the `get_libcall` parameter here. 903 fn translate_libcall( 904 &mut self, 905 get_libcall: GetLibcallFn, 906 host_result: impl Into<HostResult>, 907 wasm_args: WasmArgs, 908 extra_host_args: impl FnOnce(&mut Self, &mut Vec<ir::Value>), 909 ) { 910 self.translate_hostcall( 911 HostCallee::Libcall(get_libcall), 912 host_result.into(), 913 wasm_args, 914 extra_host_args, 915 ) 916 } 917 918 /// Translates an invocation of a host function and interpret the result. 919 /// 920 /// This is intended to be a relatively narrow waist which most intrinsics 921 /// go through. The configuration supported here is: 922 /// 923 /// * `host_callee` - what's being called, either a libcall or a lowered 924 /// function 925 /// * `host_result` - how to interpret the return value to see if it's a 926 /// trap 927 /// * `wasm_args` - how to pass wasm args to the host, either in registers 928 /// or on the stack 929 /// * `extra_host_args` - a closure used to push extra arguments just before 930 /// the wasm arguments are forwarded. 931 fn translate_hostcall( 932 &mut self, 933 host_callee: HostCallee, 934 host_result: impl Into<HostResult>, 935 wasm_args: WasmArgs, 936 extra_host_args: impl FnOnce(&mut Self, &mut Vec<ir::Value>), 937 ) { 938 let pointer_type = self.isa.pointer_type(); 939 940 // Load all parameters in an ABI-agnostic fashion, of which the 941 // `VMComponentContext` will be the first. 942 let params = self.abi_load_params(); 943 let vmctx = params[0]; 944 let wasm_params = ¶ms[2..]; 945 946 // Start building up arguments to the host. The first is always the 947 // vmctx. After is whatever `extra_host_args` appends, and then finally 948 // is what `WasmArgs` specifies. 949 let mut host_args = vec![vmctx]; 950 extra_host_args(self, &mut host_args); 951 let mut val_raw_ptr = None; 952 let mut val_raw_len = None; 953 match wasm_args { 954 // Wasm params are passed through as values themselves. 955 WasmArgs::InRegisters => host_args.extend(wasm_params.iter().copied()), 956 957 // Wasm params are spilled and then the ptr/len is passed. 958 WasmArgs::ValRawList => { 959 let (ptr, len) = self.store_wasm_arguments(wasm_params); 960 val_raw_ptr = Some(ptr); 961 val_raw_len = Some(len); 962 host_args.push(ptr); 963 host_args.push(len); 964 } 965 966 // A mixture of the above two. 967 WasmArgs::InRegistersUpTo(n) => { 968 let (values_vec_ptr, len) = self.compiler.allocate_stack_array_and_spill_args( 969 &WasmFuncType::new( 970 self.signature.params().iter().skip(n).copied().collect(), 971 Box::new([]), 972 ), 973 &mut self.builder, 974 &wasm_params[n..], 975 ); 976 let values_vec_len = self.builder.ins().iconst(pointer_type, i64::from(len)); 977 978 host_args.extend(wasm_params[..n].iter().copied()); 979 host_args.push(values_vec_ptr); 980 host_args.push(values_vec_len); 981 } 982 } 983 984 // Next perform the actual invocation of the host with `host_args`. 985 let call = match host_callee { 986 HostCallee::Libcall(get_libcall) => self.call_libcall(vmctx, get_libcall, &host_args), 987 HostCallee::Lowering(index) => { 988 // Load host function pointer from the vmcontext and then call that 989 // indirect function pointer with the list of arguments. 990 let host_fn = self.builder.ins().load( 991 pointer_type, 992 MemFlags::trusted(), 993 vmctx, 994 i32::try_from(self.offsets.lowering_callee(index)).unwrap(), 995 ); 996 let host_sig = { 997 let mut sig = ir::Signature::new(CallConv::triple_default(self.isa.triple())); 998 for param in host_args.iter() { 999 let ty = self.builder.func.dfg.value_type(*param); 1000 sig.params.push(ir::AbiParam::new(ty)); 1001 } 1002 // return value is a bool whether a trap was raised or not 1003 sig.returns.push(ir::AbiParam::new(ir::types::I8)); 1004 self.builder.import_signature(sig) 1005 }; 1006 self.compiler.call_indirect_host( 1007 &mut self.builder, 1008 HostCall::ComponentLowerImport, 1009 host_sig, 1010 host_fn, 1011 &host_args, 1012 ) 1013 } 1014 }; 1015 1016 // Acquire the result of this function (if any) and interpret it 1017 // according to `host_result`. 1018 // 1019 // Note that all match arms here end with `abi_store_results` which 1020 // accounts for the ABI of this function when storing results. 1021 let result = self.builder.func.dfg.inst_results(call).get(0).copied(); 1022 let result_ty = result.map(|v| self.builder.func.dfg.value_type(v)); 1023 let expected = self.signature.returns(); 1024 match host_result.into() { 1025 HostResult::Sentinel(TrapSentinel::NegativeOne) => { 1026 assert_eq!(expected.len(), 1); 1027 let (result, result_ty) = (result.unwrap(), result_ty.unwrap()); 1028 let result = match (result_ty, expected[0]) { 1029 (ir::types::I64, WasmValType::I32) => { 1030 self.raise_if_negative_one_and_truncate(result) 1031 } 1032 (ir::types::I64, WasmValType::I64) | (ir::types::I32, WasmValType::I32) => { 1033 self.raise_if_negative_one(result) 1034 } 1035 other => panic!("unsupported NegativeOne combo {other:?}"), 1036 }; 1037 self.abi_store_results(&[result]); 1038 } 1039 HostResult::Sentinel(TrapSentinel::Falsy) => { 1040 assert_eq!(expected.len(), 0); 1041 self.raise_if_host_trapped(result.unwrap()); 1042 self.abi_store_results(&[]); 1043 } 1044 HostResult::Sentinel(_) => todo!("support additional return types if/when necessary"), 1045 1046 HostResult::MultiValue { ptr, len } => { 1047 let ptr = ptr.or(val_raw_ptr).unwrap(); 1048 let len = len.or(val_raw_len).unwrap(); 1049 self.raise_if_host_trapped(result.unwrap()); 1050 let results = self.compiler.load_values_from_array( 1051 self.signature.returns(), 1052 &mut self.builder, 1053 ptr, 1054 len, 1055 ); 1056 self.abi_store_results(&results); 1057 } 1058 } 1059 } 1060 1061 fn index_value(&mut self, index: impl EntityRef) -> ir::Value { 1062 self.builder 1063 .ins() 1064 .iconst(ir::types::I32, i64::try_from(index.index()).unwrap()) 1065 } 1066 1067 fn translate_resource_drop( 1068 &mut self, 1069 instance: RuntimeComponentInstanceIndex, 1070 resource: TypeResourceTableIndex, 1071 ) { 1072 let args = self.abi_load_params(); 1073 let vmctx = args[0]; 1074 let caller_vmctx = args[1]; 1075 let pointer_type = self.isa.pointer_type(); 1076 1077 // The arguments this shim passes along to the libcall are: 1078 // 1079 // * the vmctx 1080 // * the calling component instance index 1081 // * a constant value for this `ResourceDrop` intrinsic 1082 // * the wasm handle index to drop 1083 let mut host_args = Vec::new(); 1084 host_args.push(vmctx); 1085 host_args.push( 1086 self.builder 1087 .ins() 1088 .iconst(ir::types::I32, i64::from(instance.as_u32())), 1089 ); 1090 host_args.push( 1091 self.builder 1092 .ins() 1093 .iconst(ir::types::I32, i64::from(resource.as_u32())), 1094 ); 1095 host_args.push(args[2]); 1096 1097 let call = self.call_libcall(vmctx, host::resource_drop, &host_args); 1098 1099 // Immediately raise a trap if requested by the host 1100 let should_run_destructor = 1101 self.raise_if_negative_one(self.builder.func.dfg.inst_results(call)[0]); 1102 1103 let resource_ty = self.types[resource].unwrap_concrete_ty(); 1104 let resource_def = self 1105 .component 1106 .defined_resource_index(resource_ty) 1107 .map(|idx| { 1108 self.component 1109 .initializers 1110 .iter() 1111 .filter_map(|i| match i { 1112 GlobalInitializer::Resource(r) if r.index == idx => Some(r), 1113 _ => None, 1114 }) 1115 .next() 1116 .unwrap() 1117 }); 1118 let has_destructor = match resource_def { 1119 Some(def) => def.dtor.is_some(), 1120 None => true, 1121 }; 1122 // Synthesize the following: 1123 // 1124 // ... 1125 // brif should_run_destructor, run_destructor_block, return_block 1126 // 1127 // run_destructor_block: 1128 // ;; test may_leave, but only if the component instances 1129 // ;; differ 1130 // flags = load.i32 vmctx+$instance_flags_offset 1131 // masked = band flags, $FLAG_MAY_LEAVE 1132 // trapz masked, $TRAP_CANNOT_LEAVE_COMPONENT 1133 // 1134 // ;; set may_block to false, saving the old value to restore 1135 // ;; later, but only if the component instances differ and 1136 // ;; concurrency is enabled 1137 // old_may_block = load.i32 vmctx+$may_block_offset 1138 // store 0, vmctx+$may_block_offset 1139 // 1140 // ;; call enter_sync_call, but only if the component instances 1141 // ;; differ and concurrency is enabled 1142 // ... 1143 // 1144 // ;; ============================================================ 1145 // ;; this is conditionally emitted based on whether the resource 1146 // ;; has a destructor or not, and can be statically omitted 1147 // ;; because that information is known at compile time here. 1148 // rep = ushr.i64 rep, 1 1149 // rep = ireduce.i32 rep 1150 // dtor = load.ptr vmctx+$offset 1151 // func_addr = load.ptr dtor+$offset 1152 // callee_vmctx = load.ptr dtor+$offset 1153 // call_indirect func_addr, callee_vmctx, vmctx, rep 1154 // ;; ============================================================ 1155 // 1156 // ;; restore old value of may_block 1157 // store old_may_block, vmctx+$may_block_offset 1158 // 1159 // ;; if needed, call exit_sync_call 1160 // ... 1161 // 1162 // ;; if needed, restore the old value of may_block 1163 // store old_may_block, vmctx+$may_block_offset 1164 // 1165 // jump return_block 1166 // 1167 // return_block: 1168 // return 1169 // 1170 // This will decode `should_run_destructor` and run the destructor 1171 // funcref if one is specified for this resource. Note that not all 1172 // resources have destructors, hence the null check. 1173 self.builder.ensure_inserted_block(); 1174 let current_block = self.builder.current_block().unwrap(); 1175 let run_destructor_block = self.builder.create_block(); 1176 self.builder 1177 .insert_block_after(run_destructor_block, current_block); 1178 let return_block = self.builder.create_block(); 1179 self.builder 1180 .insert_block_after(return_block, run_destructor_block); 1181 1182 self.builder.ins().brif( 1183 should_run_destructor, 1184 run_destructor_block, 1185 &[], 1186 return_block, 1187 &[], 1188 ); 1189 1190 let trusted = ir::MemFlags::trusted().with_readonly(); 1191 1192 self.builder.switch_to_block(run_destructor_block); 1193 1194 // If this is a component-defined resource, the `may_leave` flag must be 1195 // checked. Additionally, if concurrency is enabled, the `may_block` 1196 // field must be updated and `enter_sync_call` called. Note though that 1197 // all of that may be elided if the resource table resides in the same 1198 // component instance that defined the resource as the component is 1199 // calling itself. 1200 let old_may_block = if let Some(def) = resource_def { 1201 if self.types[resource].unwrap_concrete_instance() != def.instance { 1202 self.check_may_leave_instance(self.types[resource].unwrap_concrete_instance()); 1203 1204 if self.compiler.tunables.concurrency_support { 1205 // Stash the old value of `may_block` and then set it to false. 1206 let old_may_block = self.builder.ins().load( 1207 ir::types::I32, 1208 trusted, 1209 vmctx, 1210 i32::try_from(self.offsets.task_may_block()).unwrap(), 1211 ); 1212 let zero = self.builder.ins().iconst(ir::types::I32, i64::from(0)); 1213 self.builder.ins().store( 1214 ir::MemFlags::trusted(), 1215 zero, 1216 vmctx, 1217 i32::try_from(self.offsets.task_may_block()).unwrap(), 1218 ); 1219 1220 // Call `enter_sync_call` 1221 // 1222 // FIXME: Apply the optimizations described in #12311. 1223 let host_args = vec![ 1224 vmctx, 1225 self.builder 1226 .ins() 1227 .iconst(ir::types::I32, i64::from(instance.as_u32())), 1228 self.builder.ins().iconst(ir::types::I32, i64::from(0)), 1229 self.builder 1230 .ins() 1231 .iconst(ir::types::I32, i64::from(def.instance.as_u32())), 1232 ]; 1233 let call = self.call_libcall(vmctx, host::enter_sync_call, &host_args); 1234 let result = self.builder.func.dfg.inst_results(call).get(0).copied(); 1235 self.raise_if_host_trapped(result.unwrap()); 1236 1237 Some(old_may_block) 1238 } else { 1239 None 1240 } 1241 } else { 1242 None 1243 } 1244 } else { 1245 None 1246 }; 1247 1248 // Conditionally emit destructor-execution code based on whether we 1249 // statically know that a destructor exists or not. 1250 if has_destructor { 1251 let rep = self.builder.ins().ushr_imm(should_run_destructor, 1); 1252 let rep = self.builder.ins().ireduce(ir::types::I32, rep); 1253 let index = self.types[resource].unwrap_concrete_ty(); 1254 // NB: despite the vmcontext storing nullable funcrefs for function 1255 // pointers we know this is statically never null due to the 1256 // `has_destructor` check above. 1257 let dtor_func_ref = self.builder.ins().load( 1258 pointer_type, 1259 trusted, 1260 vmctx, 1261 i32::try_from(self.offsets.resource_destructor(index)).unwrap(), 1262 ); 1263 if self.compiler.emit_debug_checks { 1264 self.builder 1265 .ins() 1266 .trapz(dtor_func_ref, TRAP_INTERNAL_ASSERT); 1267 } 1268 let func_addr = self.builder.ins().load( 1269 pointer_type, 1270 trusted, 1271 dtor_func_ref, 1272 i32::from(self.offsets.ptr.vm_func_ref_wasm_call()), 1273 ); 1274 let callee_vmctx = self.builder.ins().load( 1275 pointer_type, 1276 trusted, 1277 dtor_func_ref, 1278 i32::from(self.offsets.ptr.vm_func_ref_vmctx()), 1279 ); 1280 1281 let sig = crate::wasm_call_signature(self.isa, self.signature, &self.compiler.tunables); 1282 let sig_ref = self.builder.import_signature(sig); 1283 1284 // NB: note that the "caller" vmctx here is the caller of this 1285 // intrinsic itself, not the `VMComponentContext`. This effectively 1286 // takes ourselves out of the chain here but that's ok since the 1287 // caller is only used for store/limits and that same info is 1288 // stored, but elsewhere, in the component context. 1289 self.builder.ins().call_indirect( 1290 sig_ref, 1291 func_addr, 1292 &[callee_vmctx, caller_vmctx, rep], 1293 ); 1294 } 1295 1296 if let Some(old_may_block) = old_may_block { 1297 // Call `exit_sync_call` 1298 // 1299 // FIXME: Apply the optimizations described in #12311. 1300 let call = self.call_libcall(vmctx, host::exit_sync_call, &[vmctx]); 1301 let result = self.builder.func.dfg.inst_results(call).get(0).copied(); 1302 self.raise_if_host_trapped(result.unwrap()); 1303 1304 // Restore the old value of `may_block` 1305 self.builder.ins().store( 1306 ir::MemFlags::trusted(), 1307 old_may_block, 1308 vmctx, 1309 i32::try_from(self.offsets.task_may_block()).unwrap(), 1310 ); 1311 } 1312 1313 self.builder.ins().jump(return_block, &[]); 1314 self.builder.seal_block(run_destructor_block); 1315 1316 self.builder.switch_to_block(return_block); 1317 self.builder.seal_block(return_block); 1318 self.abi_store_results(&[]); 1319 } 1320 1321 fn load_optional_memory( 1322 &mut self, 1323 vmctx: ir::Value, 1324 memory: Option<RuntimeMemoryIndex>, 1325 ) -> ir::Value { 1326 match memory { 1327 Some(idx) => self.load_memory(vmctx, idx), 1328 None => self.builder.ins().iconst(self.isa.pointer_type(), 0), 1329 } 1330 } 1331 1332 fn load_memory(&mut self, vmctx: ir::Value, memory: RuntimeMemoryIndex) -> ir::Value { 1333 self.builder.ins().load( 1334 self.isa.pointer_type(), 1335 MemFlags::trusted(), 1336 vmctx, 1337 i32::try_from(self.offsets.runtime_memory(memory)).unwrap(), 1338 ) 1339 } 1340 1341 fn load_callback( 1342 &mut self, 1343 vmctx: ir::Value, 1344 callback: Option<RuntimeCallbackIndex>, 1345 ) -> ir::Value { 1346 let pointer_type = self.isa.pointer_type(); 1347 match callback { 1348 Some(idx) => self.builder.ins().load( 1349 pointer_type, 1350 MemFlags::trusted(), 1351 vmctx, 1352 i32::try_from(self.offsets.runtime_callback(idx)).unwrap(), 1353 ), 1354 None => self.builder.ins().iconst(pointer_type, 0), 1355 } 1356 } 1357 1358 fn load_post_return( 1359 &mut self, 1360 vmctx: ir::Value, 1361 post_return: Option<RuntimePostReturnIndex>, 1362 ) -> ir::Value { 1363 let pointer_type = self.isa.pointer_type(); 1364 match post_return { 1365 Some(idx) => self.builder.ins().load( 1366 pointer_type, 1367 MemFlags::trusted(), 1368 vmctx, 1369 i32::try_from(self.offsets.runtime_post_return(idx)).unwrap(), 1370 ), 1371 None => self.builder.ins().iconst(pointer_type, 0), 1372 } 1373 } 1374 1375 /// Loads a host function pointer for a libcall stored at the `offset` 1376 /// provided in the libcalls array. 1377 /// 1378 /// The offset is calculated in the `host` module below. 1379 fn load_libcall( 1380 &mut self, 1381 vmctx: ir::Value, 1382 index: ComponentBuiltinFunctionIndex, 1383 ) -> ir::Value { 1384 let pointer_type = self.isa.pointer_type(); 1385 // First load the pointer to the builtins structure which is static 1386 // per-process. 1387 let builtins_array = self.builder.ins().load( 1388 pointer_type, 1389 MemFlags::trusted().with_readonly(), 1390 vmctx, 1391 i32::try_from(self.offsets.builtins()).unwrap(), 1392 ); 1393 // Next load the function pointer at `offset` and return that. 1394 self.builder.ins().load( 1395 pointer_type, 1396 MemFlags::trusted().with_readonly(), 1397 builtins_array, 1398 i32::try_from(index.index() * u32::from(self.offsets.ptr.size())).unwrap(), 1399 ) 1400 } 1401 1402 /// Get a function's parameters regardless of the ABI in use. 1403 /// 1404 /// This emits code to load the parameters from the array-call's ABI's values 1405 /// vector, if necessary. 1406 fn abi_load_params(&mut self) -> Vec<ir::Value> { 1407 self.builder.func.dfg.block_params(self.block0).to_vec() 1408 } 1409 1410 /// Emit code to return the given result values, regardless of the ABI in use. 1411 fn abi_store_results(&mut self, results: &[ir::Value]) { 1412 self.builder.ins().return_(results); 1413 } 1414 1415 fn raise_if_host_trapped(&mut self, succeeded: ir::Value) { 1416 let caller_vmctx = self.builder.func.dfg.block_params(self.block0)[1]; 1417 self.compiler 1418 .raise_if_host_trapped(&mut self.builder, caller_vmctx, succeeded); 1419 } 1420 1421 fn raise_if_transcode_trapped(&mut self, amount_copied: ir::Value) { 1422 let pointer_type = self.isa.pointer_type(); 1423 let minus_one = self.builder.ins().iconst(pointer_type, -1); 1424 let succeeded = self 1425 .builder 1426 .ins() 1427 .icmp(IntCC::NotEqual, amount_copied, minus_one); 1428 self.raise_if_host_trapped(succeeded); 1429 } 1430 1431 fn raise_if_negative_one_and_truncate(&mut self, ret: ir::Value) -> ir::Value { 1432 let ret = self.raise_if_negative_one(ret); 1433 self.builder.ins().ireduce(ir::types::I32, ret) 1434 } 1435 1436 fn raise_if_negative_one(&mut self, ret: ir::Value) -> ir::Value { 1437 let result_ty = self.builder.func.dfg.value_type(ret); 1438 let minus_one = self.builder.ins().iconst(result_ty, -1); 1439 let succeeded = self.builder.ins().icmp(IntCC::NotEqual, ret, minus_one); 1440 self.raise_if_host_trapped(succeeded); 1441 ret 1442 } 1443 1444 fn call_libcall( 1445 &mut self, 1446 vmctx: ir::Value, 1447 get_libcall: GetLibcallFn, 1448 args: &[ir::Value], 1449 ) -> ir::Inst { 1450 let (host_sig, index) = get_libcall(self.isa, &mut self.builder.func); 1451 let host_fn = self.load_libcall(vmctx, index); 1452 self.compiler 1453 .call_indirect_host(&mut self.builder, index, host_sig, host_fn, args) 1454 } 1455 1456 fn check_may_leave(&mut self, trampoline: &Trampoline) { 1457 let instance = match trampoline { 1458 // These intrinsics explicitly do not check the may-leave flag. 1459 Trampoline::ResourceRep { .. } 1460 | Trampoline::ThreadIndex 1461 | Trampoline::BackpressureInc { .. } 1462 | Trampoline::BackpressureDec { .. } 1463 | Trampoline::ContextGet { .. } 1464 | Trampoline::ContextSet { .. } => return, 1465 1466 // Intrinsics used in adapters generated by FACT that aren't called 1467 // directly from guest wasm, so no check is needed. 1468 Trampoline::ResourceTransferOwn 1469 | Trampoline::ResourceTransferBorrow 1470 | Trampoline::PrepareCall { .. } 1471 | Trampoline::SyncStartCall { .. } 1472 | Trampoline::AsyncStartCall { .. } 1473 | Trampoline::FutureTransfer 1474 | Trampoline::StreamTransfer 1475 | Trampoline::ErrorContextTransfer 1476 | Trampoline::Trap 1477 | Trampoline::EnterSyncCall 1478 | Trampoline::ExitSyncCall 1479 | Trampoline::Transcoder { .. } => return, 1480 1481 Trampoline::LowerImport { options, .. } => self.component.options[*options].instance, 1482 1483 Trampoline::ResourceNew { instance, .. } 1484 | Trampoline::ResourceDrop { instance, .. } 1485 | Trampoline::TaskReturn { instance, .. } 1486 | Trampoline::TaskCancel { instance } 1487 | Trampoline::WaitableSetNew { instance } 1488 | Trampoline::WaitableSetWait { instance, .. } 1489 | Trampoline::WaitableSetPoll { instance, .. } 1490 | Trampoline::WaitableSetDrop { instance } 1491 | Trampoline::WaitableJoin { instance } 1492 | Trampoline::ThreadYield { instance, .. } 1493 | Trampoline::ThreadNewIndirect { instance, .. } 1494 | Trampoline::ThreadSuspend { instance, .. } 1495 | Trampoline::ThreadSuspendToSuspended { instance, .. } 1496 | Trampoline::ThreadSuspendTo { instance, .. } 1497 | Trampoline::ThreadUnsuspend { instance, .. } 1498 | Trampoline::ThreadYieldToSuspended { instance, .. } 1499 | Trampoline::SubtaskDrop { instance } 1500 | Trampoline::SubtaskCancel { instance, .. } 1501 | Trampoline::ErrorContextNew { instance, .. } 1502 | Trampoline::ErrorContextDebugMessage { instance, .. } 1503 | Trampoline::ErrorContextDrop { instance, .. } 1504 | Trampoline::StreamNew { instance, .. } 1505 | Trampoline::StreamRead { instance, .. } 1506 | Trampoline::StreamWrite { instance, .. } 1507 | Trampoline::StreamCancelRead { instance, .. } 1508 | Trampoline::StreamCancelWrite { instance, .. } 1509 | Trampoline::StreamDropReadable { instance, .. } 1510 | Trampoline::StreamDropWritable { instance, .. } 1511 | Trampoline::FutureNew { instance, .. } 1512 | Trampoline::FutureRead { instance, .. } 1513 | Trampoline::FutureWrite { instance, .. } 1514 | Trampoline::FutureCancelRead { instance, .. } 1515 | Trampoline::FutureCancelWrite { instance, .. } 1516 | Trampoline::FutureDropReadable { instance, .. } 1517 | Trampoline::FutureDropWritable { instance, .. } => *instance, 1518 }; 1519 1520 self.check_may_leave_instance(instance) 1521 } 1522 1523 fn check_may_leave_instance(&mut self, instance: RuntimeComponentInstanceIndex) { 1524 let vmctx = self.builder.func.dfg.block_params(self.block0)[0]; 1525 1526 let flags = self.builder.ins().load( 1527 ir::types::I32, 1528 ir::MemFlags::trusted(), 1529 vmctx, 1530 i32::try_from(self.offsets.instance_flags(instance)).unwrap(), 1531 ); 1532 let may_leave_bit = self 1533 .builder 1534 .ins() 1535 .band_imm(flags, i64::from(FLAG_MAY_LEAVE)); 1536 self.builder 1537 .ins() 1538 .trapz(may_leave_bit, TRAP_CANNOT_LEAVE_COMPONENT); 1539 } 1540 } 1541 1542 impl ComponentCompiler for Compiler { 1543 fn compile_trampoline( 1544 &self, 1545 component: &ComponentTranslation, 1546 types: &ComponentTypesBuilder, 1547 key: FuncKey, 1548 abi: Abi, 1549 _tunables: &Tunables, 1550 symbol: &str, 1551 ) -> Result<CompiledFunctionBody> { 1552 let (abi2, trampoline_index) = key.unwrap_component_trampoline(); 1553 debug_assert_eq!(abi, abi2); 1554 let sig = types[component.component.trampolines[trampoline_index]].unwrap_func(); 1555 1556 match abi { 1557 // Fall through to the trampoline compiler. 1558 Abi::Wasm => {} 1559 1560 // Implement the array-abi trampoline in terms of calling the 1561 // wasm-abi trampoline. 1562 Abi::Array => { 1563 let offsets = 1564 VMComponentOffsets::new(self.isa.pointer_bytes(), &component.component); 1565 return Ok(self.array_to_wasm_trampoline( 1566 key, 1567 FuncKey::ComponentTrampoline(Abi::Wasm, trampoline_index), 1568 sig, 1569 symbol, 1570 offsets.vm_store_context(), 1571 wasmtime_environ::component::VMCOMPONENT_MAGIC, 1572 )?); 1573 } 1574 1575 Abi::Patchable => unreachable!( 1576 "We should not be compiling a patchable-ABI trampoline for a component function" 1577 ), 1578 } 1579 1580 let mut compiler = self.function_compiler(); 1581 let mut c = TrampolineCompiler::new(self, &mut compiler, &component.component, types, sig); 1582 1583 // If we are crossing the Wasm-to-native boundary, we need to save the 1584 // exit FP and return address for stack walking purposes. However, we 1585 // always debug assert that our vmctx is a component context, regardless 1586 // whether we are actually crossing that boundary because it should 1587 // always hold. 1588 let vmctx = c.builder.block_params(c.block0)[0]; 1589 let pointer_type = self.isa.pointer_type(); 1590 self.debug_assert_vmctx_kind( 1591 &mut c.builder, 1592 vmctx, 1593 wasmtime_environ::component::VMCOMPONENT_MAGIC, 1594 ); 1595 let vm_store_context = c.builder.ins().load( 1596 pointer_type, 1597 MemFlags::trusted(), 1598 vmctx, 1599 i32::try_from(c.offsets.vm_store_context()).unwrap(), 1600 ); 1601 super::save_last_wasm_exit_fp_and_pc( 1602 &mut c.builder, 1603 pointer_type, 1604 &c.offsets.ptr, 1605 vm_store_context, 1606 ); 1607 1608 c.translate(&component.trampolines[trampoline_index]); 1609 c.builder.finalize(); 1610 compiler.cx.abi = Some(abi); 1611 1612 Ok(CompiledFunctionBody { 1613 code: super::box_dyn_any_compiler_context(Some(compiler.cx)), 1614 needs_gc_heap: false, 1615 }) 1616 } 1617 1618 fn compile_intrinsic( 1619 &self, 1620 _tunables: &Tunables, 1621 component: &ComponentTranslation, 1622 types: &ComponentTypesBuilder, 1623 intrinsic: UnsafeIntrinsic, 1624 abi: Abi, 1625 symbol: &str, 1626 ) -> Result<CompiledFunctionBody> { 1627 let wasm_func_ty = WasmFuncType::new( 1628 intrinsic.core_params().into(), 1629 intrinsic.core_results().into(), 1630 ); 1631 1632 match abi { 1633 // Fall through to the trampoline compiler. 1634 Abi::Wasm => {} 1635 1636 // Implement the array-abi trampoline in terms of calling the 1637 // wasm-abi trampoline. 1638 Abi::Array => { 1639 let offsets = 1640 VMComponentOffsets::new(self.isa.pointer_bytes(), &component.component); 1641 return Ok(self.array_to_wasm_trampoline( 1642 FuncKey::UnsafeIntrinsic(abi, intrinsic), 1643 FuncKey::UnsafeIntrinsic(Abi::Wasm, intrinsic), 1644 &wasm_func_ty, 1645 symbol, 1646 offsets.vm_store_context(), 1647 wasmtime_environ::component::VMCOMPONENT_MAGIC, 1648 )?); 1649 } 1650 1651 Abi::Patchable => { 1652 unreachable!( 1653 "We should not be compiling a patchable trampoline for a component intrinsic" 1654 ) 1655 } 1656 } 1657 1658 let mut compiler = self.function_compiler(); 1659 let mut c = TrampolineCompiler::new( 1660 self, 1661 &mut compiler, 1662 &component.component, 1663 &types, 1664 &wasm_func_ty, 1665 ); 1666 1667 match intrinsic { 1668 UnsafeIntrinsic::U8NativeLoad 1669 | UnsafeIntrinsic::U16NativeLoad 1670 | UnsafeIntrinsic::U32NativeLoad 1671 | UnsafeIntrinsic::U64NativeLoad => c.translate_load_intrinsic(intrinsic)?, 1672 UnsafeIntrinsic::U8NativeStore 1673 | UnsafeIntrinsic::U16NativeStore 1674 | UnsafeIntrinsic::U32NativeStore 1675 | UnsafeIntrinsic::U64NativeStore => c.translate_store_intrinsic(intrinsic)?, 1676 UnsafeIntrinsic::StoreDataAddress => { 1677 let [callee_vmctx, _caller_vmctx] = *c.abi_load_params() else { 1678 unreachable!() 1679 }; 1680 let pointer_type = self.isa.pointer_type(); 1681 1682 // Load the `*mut VMStoreContext` out of our vmctx. 1683 let store_ctx = c.builder.ins().load( 1684 pointer_type, 1685 ir::MemFlags::trusted() 1686 .with_readonly() 1687 .with_alias_region(Some(ir::AliasRegion::Vmctx)) 1688 .with_can_move(), 1689 callee_vmctx, 1690 i32::try_from(c.offsets.vm_store_context()).unwrap(), 1691 ); 1692 1693 // Load the `*mut T` out of the `VMStoreContext`. 1694 let data_address = c.builder.ins().load( 1695 pointer_type, 1696 ir::MemFlags::trusted() 1697 .with_readonly() 1698 .with_alias_region(Some(ir::AliasRegion::Vmctx)) 1699 .with_can_move(), 1700 store_ctx, 1701 i32::from(c.offsets.ptr.vmstore_context_store_data()), 1702 ); 1703 1704 // Zero-extend the address if we are on a 32-bit architecture. 1705 let data_address = match pointer_type.bits() { 1706 32 => c.builder.ins().uextend(ir::types::I64, data_address), 1707 64 => data_address, 1708 p => bail!("unsupported architecture: no support for {p}-bit pointers"), 1709 }; 1710 1711 c.abi_store_results(&[data_address]); 1712 } 1713 } 1714 1715 c.builder.finalize(); 1716 compiler.cx.abi = Some(abi); 1717 1718 Ok(CompiledFunctionBody { 1719 code: super::box_dyn_any_compiler_context(Some(compiler.cx)), 1720 needs_gc_heap: false, 1721 }) 1722 } 1723 } 1724 1725 macro_rules! unsafe_intrinsic_clif_params_results { 1726 ( 1727 $( 1728 $symbol:expr => $variant:ident : $ctor:ident ( $( $param:ident : $param_ty:ident ),* ) $( -> $result_ty:ident )? ; 1729 )* 1730 ) => { 1731 fn unsafe_intrinsic_clif_params(intrinsic: UnsafeIntrinsic) -> &'static [ir::types::Type] { 1732 match intrinsic { 1733 $( 1734 UnsafeIntrinsic::$variant => &[ $( unsafe_intrinsic_clif_params_results!(@clif_type $param_ty) ),* ], 1735 )* 1736 } 1737 } 1738 1739 fn unsafe_intrinsic_clif_results(intrinsic: UnsafeIntrinsic) -> &'static [ir::types::Type] { 1740 match intrinsic { 1741 $( 1742 UnsafeIntrinsic::$variant => &[ $( unsafe_intrinsic_clif_params_results!(@clif_type $result_ty) )? ], 1743 )* 1744 } 1745 } 1746 }; 1747 1748 (@clif_type u8) => { ir::types::I8 }; 1749 (@clif_type u16) => { ir::types::I16 }; 1750 (@clif_type u32) => { ir::types::I32 }; 1751 (@clif_type u64) => { ir::types::I64 }; 1752 } 1753 1754 wasmtime_environ::for_each_unsafe_intrinsic!(unsafe_intrinsic_clif_params_results); 1755 1756 impl TrampolineCompiler<'_> { 1757 fn translate_transcode( 1758 &mut self, 1759 op: Transcode, 1760 from: RuntimeMemoryIndex, 1761 from64: bool, 1762 to: RuntimeMemoryIndex, 1763 to64: bool, 1764 ) { 1765 let pointer_type = self.isa.pointer_type(); 1766 let vmctx = self.builder.func.dfg.block_params(self.block0)[0]; 1767 1768 // Determine the static signature of the host libcall for this transcode 1769 // operation and additionally calculate the static offset within the 1770 // transode libcalls array. 1771 let get_libcall = match op { 1772 Transcode::Copy(FixedEncoding::Utf8) => host::utf8_to_utf8, 1773 Transcode::Copy(FixedEncoding::Utf16) => host::utf16_to_utf16, 1774 Transcode::Copy(FixedEncoding::Latin1) => host::latin1_to_latin1, 1775 Transcode::Latin1ToUtf16 => host::latin1_to_utf16, 1776 Transcode::Latin1ToUtf8 => host::latin1_to_utf8, 1777 Transcode::Utf16ToCompactProbablyUtf16 => host::utf16_to_compact_probably_utf16, 1778 Transcode::Utf16ToCompactUtf16 => host::utf16_to_compact_utf16, 1779 Transcode::Utf16ToLatin1 => host::utf16_to_latin1, 1780 Transcode::Utf16ToUtf8 => host::utf16_to_utf8, 1781 Transcode::Utf8ToCompactUtf16 => host::utf8_to_compact_utf16, 1782 Transcode::Utf8ToLatin1 => host::utf8_to_latin1, 1783 Transcode::Utf8ToUtf16 => host::utf8_to_utf16, 1784 }; 1785 1786 // Load the base pointers for the from/to linear memories. 1787 let from_base = self.load_runtime_memory_base(vmctx, from); 1788 let to_base = self.load_runtime_memory_base(vmctx, to); 1789 1790 let mut args = Vec::new(); 1791 args.push(vmctx); 1792 1793 let uses_retptr = match op { 1794 Transcode::Utf16ToUtf8 1795 | Transcode::Latin1ToUtf8 1796 | Transcode::Utf8ToLatin1 1797 | Transcode::Utf16ToLatin1 => true, 1798 _ => false, 1799 }; 1800 1801 // Most transcoders share roughly the same signature despite doing very 1802 // different things internally, so most libcalls are lumped together 1803 // here. 1804 match op { 1805 Transcode::Copy(_) 1806 | Transcode::Latin1ToUtf16 1807 | Transcode::Utf16ToCompactProbablyUtf16 1808 | Transcode::Utf8ToLatin1 1809 | Transcode::Utf16ToLatin1 1810 | Transcode::Utf8ToUtf16 => { 1811 args.push(self.ptr_param(0, from64, from_base)); 1812 args.push(self.len_param(1, from64)); 1813 args.push(self.ptr_param(2, to64, to_base)); 1814 } 1815 1816 Transcode::Utf16ToUtf8 | Transcode::Latin1ToUtf8 => { 1817 args.push(self.ptr_param(0, from64, from_base)); 1818 args.push(self.len_param(1, from64)); 1819 args.push(self.ptr_param(2, to64, to_base)); 1820 args.push(self.len_param(3, to64)); 1821 } 1822 1823 Transcode::Utf8ToCompactUtf16 | Transcode::Utf16ToCompactUtf16 => { 1824 args.push(self.ptr_param(0, from64, from_base)); 1825 args.push(self.len_param(1, from64)); 1826 args.push(self.ptr_param(2, to64, to_base)); 1827 args.push(self.len_param(3, to64)); 1828 args.push(self.len_param(4, to64)); 1829 } 1830 }; 1831 if uses_retptr { 1832 let slot = self 1833 .builder 1834 .func 1835 .create_sized_stack_slot(ir::StackSlotData::new( 1836 ir::StackSlotKind::ExplicitSlot, 1837 pointer_type.bytes(), 1838 0, 1839 )); 1840 args.push(self.builder.ins().stack_addr(pointer_type, slot, 0)); 1841 } 1842 let call = self.call_libcall(vmctx, get_libcall, &args); 1843 let mut results = self.builder.func.dfg.inst_results(call).to_vec(); 1844 if uses_retptr { 1845 results.push(self.builder.ins().load( 1846 pointer_type, 1847 ir::MemFlags::trusted(), 1848 *args.last().unwrap(), 1849 0, 1850 )); 1851 } 1852 let mut raw_results = Vec::new(); 1853 1854 // Like the arguments the results are fairly similar across libcalls, so 1855 // they're lumped into various buckets here. 1856 match op { 1857 Transcode::Copy(_) | Transcode::Latin1ToUtf16 => { 1858 self.raise_if_host_trapped(results[0]); 1859 } 1860 1861 Transcode::Utf8ToUtf16 1862 | Transcode::Utf16ToCompactProbablyUtf16 1863 | Transcode::Utf8ToCompactUtf16 1864 | Transcode::Utf16ToCompactUtf16 => { 1865 self.raise_if_transcode_trapped(results[0]); 1866 raw_results.push(self.cast_from_pointer(results[0], to64)); 1867 } 1868 1869 Transcode::Latin1ToUtf8 1870 | Transcode::Utf16ToUtf8 1871 | Transcode::Utf8ToLatin1 1872 | Transcode::Utf16ToLatin1 => { 1873 self.raise_if_transcode_trapped(results[0]); 1874 raw_results.push(self.cast_from_pointer(results[0], from64)); 1875 raw_results.push(self.cast_from_pointer(results[1], to64)); 1876 } 1877 }; 1878 1879 self.builder.ins().return_(&raw_results); 1880 } 1881 1882 // Helper function to cast an input parameter to the host pointer type. 1883 fn len_param(&mut self, param: usize, is64: bool) -> ir::Value { 1884 let val = self.builder.func.dfg.block_params(self.block0)[2 + param]; 1885 self.cast_to_pointer(val, is64) 1886 } 1887 1888 // Helper function to interpret an input parameter as a pointer into 1889 // linear memory. This will cast the input parameter to the host integer 1890 // type and then add that value to the base. 1891 // 1892 // Note that bounds-checking happens in adapter modules, and this 1893 // trampoline is simply calling the host libcall. 1894 fn ptr_param(&mut self, param: usize, is64: bool, base: ir::Value) -> ir::Value { 1895 let val = self.len_param(param, is64); 1896 self.builder.ins().iadd(base, val) 1897 } 1898 1899 // Helper function to cast a core wasm input to a host pointer type 1900 // which will go into the host libcall. 1901 fn cast_to_pointer(&mut self, val: ir::Value, is64: bool) -> ir::Value { 1902 let pointer_type = self.isa.pointer_type(); 1903 let host64 = pointer_type == ir::types::I64; 1904 if is64 == host64 { 1905 val 1906 } else if !is64 { 1907 assert!(host64); 1908 self.builder.ins().uextend(pointer_type, val) 1909 } else { 1910 assert!(!host64); 1911 self.builder.ins().ireduce(pointer_type, val) 1912 } 1913 } 1914 1915 // Helper to cast a host pointer integer type to the destination type. 1916 fn cast_from_pointer(&mut self, val: ir::Value, is64: bool) -> ir::Value { 1917 let host64 = self.isa.pointer_type() == ir::types::I64; 1918 if is64 == host64 { 1919 val 1920 } else if !is64 { 1921 assert!(host64); 1922 self.builder.ins().ireduce(ir::types::I32, val) 1923 } else { 1924 assert!(!host64); 1925 self.builder.ins().uextend(ir::types::I64, val) 1926 } 1927 } 1928 1929 fn load_runtime_memory_base(&mut self, vmctx: ir::Value, mem: RuntimeMemoryIndex) -> ir::Value { 1930 let pointer_type = self.isa.pointer_type(); 1931 let from_vmmemory_definition = self.load_memory(vmctx, mem); 1932 self.builder.ins().load( 1933 pointer_type, 1934 MemFlags::trusted(), 1935 from_vmmemory_definition, 1936 i32::from(self.offsets.ptr.vmmemory_definition_base()), 1937 ) 1938 } 1939 1940 fn translate_load_intrinsic(&mut self, intrinsic: UnsafeIntrinsic) -> Result<()> { 1941 // Emit code for a native-load intrinsic. 1942 debug_assert_eq!(intrinsic.core_params(), &[WasmValType::I64]); 1943 debug_assert_eq!(intrinsic.core_results().len(), 1); 1944 1945 let wasm_ty = intrinsic.core_results()[0]; 1946 let clif_ty = unsafe_intrinsic_clif_results(intrinsic)[0]; 1947 1948 let [_callee_vmctx, _caller_vmctx, pointer] = *self.abi_load_params() else { 1949 unreachable!() 1950 }; 1951 1952 // Truncate the pointer, if necessary. 1953 debug_assert_eq!(self.builder.func.dfg.value_type(pointer), ir::types::I64); 1954 let pointer = match self.isa.pointer_bits() { 1955 32 => self.builder.ins().ireduce(ir::types::I32, pointer), 1956 64 => pointer, 1957 p => bail!("unsupported architecture: no support for {p}-bit pointers"), 1958 }; 1959 1960 // Do the load! 1961 let mut value = self 1962 .builder 1963 .ins() 1964 .load(clif_ty, ir::MemFlags::trusted(), pointer, 0); 1965 1966 // Extend the value, if necessary. When implementing the 1967 // `u8-native-load` intrinsic, for example, we will load a Cranelift 1968 // value of type `i8` but we need to extend it to an `i32` because 1969 // Wasm doesn't have an `i8` core value type. 1970 let wasm_clif_ty = crate::value_type(self.isa, wasm_ty); 1971 if clif_ty != wasm_clif_ty { 1972 assert!(clif_ty.bytes() < wasm_clif_ty.bytes()); 1973 // NB: all of our unsafe intrinsics for native loads are 1974 // unsigned, so we always zero-extend. 1975 value = self.builder.ins().uextend(wasm_clif_ty, value); 1976 } 1977 1978 self.abi_store_results(&[value]); 1979 Ok(()) 1980 } 1981 1982 fn translate_store_intrinsic(&mut self, intrinsic: UnsafeIntrinsic) -> Result<()> { 1983 debug_assert!(intrinsic.core_results().is_empty()); 1984 debug_assert!(matches!(intrinsic.core_params(), [WasmValType::I64, _])); 1985 1986 let wasm_ty = intrinsic.core_params()[1]; 1987 let clif_ty = unsafe_intrinsic_clif_params(intrinsic)[1]; 1988 1989 let [_callee_vmctx, _caller_vmctx, pointer, mut value] = *self.abi_load_params() else { 1990 unreachable!() 1991 }; 1992 1993 // Truncate the pointer, if necessary. 1994 debug_assert_eq!(self.builder.func.dfg.value_type(pointer), ir::types::I64); 1995 let pointer = match self.isa.pointer_bits() { 1996 32 => self.builder.ins().ireduce(ir::types::I32, pointer), 1997 64 => pointer, 1998 p => bail!("unsupported architecture: no support for {p}-bit pointers"), 1999 }; 2000 2001 // Truncate the value, if necessary. For example, with 2002 // `u8-native-store` we will be given an `i32` from Wasm (because 2003 // core Wasm does not have an 8-bit integer value type) and we need 2004 // to reduce that into an `i8`. 2005 let wasm_ty = crate::value_type(self.isa, wasm_ty); 2006 if clif_ty != wasm_ty { 2007 assert!(clif_ty.bytes() < wasm_ty.bytes()); 2008 value = self.builder.ins().ireduce(clif_ty, value); 2009 } 2010 2011 // Do the store! 2012 self.builder 2013 .ins() 2014 .store(ir::MemFlags::trusted(), value, pointer, 0); 2015 2016 self.abi_store_results(&[]); 2017 Ok(()) 2018 } 2019 } 2020 2021 /// Module with macro-generated contents that will return the signature and 2022 /// offset for each of the host transcoder functions. 2023 /// 2024 /// Note that a macro is used here to keep this in sync with the actual 2025 /// transcoder functions themselves which are also defined via a macro. 2026 mod host { 2027 use cranelift_codegen::ir::{self, AbiParam}; 2028 use cranelift_codegen::isa::{CallConv, TargetIsa}; 2029 use wasmtime_environ::component::ComponentBuiltinFunctionIndex; 2030 2031 macro_rules! define { 2032 ( 2033 $( 2034 $( #[$attr:meta] )* 2035 $name:ident( $( $pname:ident: $param:ident ),* ) $( -> $result:ident )?; 2036 )* 2037 ) => { 2038 $( 2039 pub(super) fn $name(isa: &dyn TargetIsa, func: &mut ir::Function) -> (ir::SigRef, ComponentBuiltinFunctionIndex) { 2040 let pointer_type = isa.pointer_type(); 2041 let sig = build_sig( 2042 isa, 2043 func, 2044 &[$( define!(@ty pointer_type $param) ),*], 2045 &[$( define!(@ty pointer_type $result) ),*], 2046 ); 2047 2048 return (sig, ComponentBuiltinFunctionIndex::$name()) 2049 } 2050 )* 2051 }; 2052 2053 (@ty $ptr:ident size) => ($ptr); 2054 (@ty $ptr:ident ptr_u8) => ($ptr); 2055 (@ty $ptr:ident ptr_u16) => ($ptr); 2056 (@ty $ptr:ident ptr_size) => ($ptr); 2057 (@ty $ptr:ident bool) => (ir::types::I8); 2058 (@ty $ptr:ident u8) => (ir::types::I8); 2059 (@ty $ptr:ident u32) => (ir::types::I32); 2060 (@ty $ptr:ident u64) => (ir::types::I64); 2061 (@ty $ptr:ident vmctx) => ($ptr); 2062 } 2063 2064 wasmtime_environ::foreach_builtin_component_function!(define); 2065 2066 fn build_sig( 2067 isa: &dyn TargetIsa, 2068 func: &mut ir::Function, 2069 params: &[ir::Type], 2070 returns: &[ir::Type], 2071 ) -> ir::SigRef { 2072 let mut sig = ir::Signature { 2073 params: params.iter().map(|ty| AbiParam::new(*ty)).collect(), 2074 returns: returns.iter().map(|ty| AbiParam::new(*ty)).collect(), 2075 call_conv: CallConv::triple_default(isa.triple()), 2076 }; 2077 2078 // Once we're declaring the signature of a host function we must respect 2079 // the default ABI of the platform which is where argument extension of 2080 // params/results may come into play. 2081 let extension = isa.default_argument_extension(); 2082 for arg in sig.params.iter_mut().chain(sig.returns.iter_mut()) { 2083 if arg.value_type.is_int() { 2084 arg.extension = extension; 2085 } 2086 } 2087 func.import_signature(sig) 2088 } 2089 } 2090