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 = &params[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