wasmtime_environ/fact.rs
1//! Wasmtime's Fused Adapter Compiler of Trampolines (FACT)
2//!
3//! This module contains a compiler which emits trampolines to implement fused
4//! adapters for the component model. A fused adapter is when a core wasm
5//! function is lifted from one component instance and then lowered into another
6//! component instance. This communication between components is well-defined by
7//! the spec and ends up creating what's called a "fused adapter".
8//!
9//! Adapters are currently implemented with WebAssembly modules. This submodule
10//! will generate a core wasm binary which contains the adapters specified
11//! during compilation. The actual wasm is then later processed by standard
12//! paths in Wasmtime to create native machine code and runtime representations
13//! of modules.
14//!
15//! Note that identification of precisely what goes into an adapter module is
16//! not handled in this file, instead that's all done in `translate/adapt.rs`.
17//! Otherwise this module is only responsible for taking a set of adapters and
18//! their imports and then generating a core wasm module to implement all of
19//! that.
20
21use crate::component::dfg::CoreDef;
22use crate::component::{
23 Adapter, AdapterOptions as AdapterOptionsDfg, CanonicalAbiInfo, ComponentTypesBuilder,
24 FlatType, InterfaceType, RuntimeComponentInstanceIndex, StringEncoding, Transcode,
25 TypeFuncIndex,
26};
27use crate::fact::transcode::Transcoder;
28use crate::prelude::*;
29use crate::{
30 EntityRef, FuncIndex, GlobalIndex, IndexType, Memory, MemoryIndex, ModuleInternedTypeIndex,
31 PrimaryMap, Tunables,
32};
33use std::collections::HashMap;
34use wasm_encoder::*;
35use wasmparser::WasmFeatures;
36
37mod core_types;
38mod signature;
39mod trampoline;
40mod transcode;
41
42/// Fixed parameter types for the `prepare_call` built-in function.
43///
44/// Note that `prepare_call` also takes a variable number of parameters in
45/// addition to these, determined by the signature of the function for which
46/// we're generating an adapter.
47pub static PREPARE_CALL_FIXED_PARAMS: &[ValType] = &[
48 ValType::FUNCREF, // start
49 ValType::FUNCREF, // return
50 ValType::I32, // caller_instance
51 ValType::I32, // callee_instance
52 ValType::I32, // task_return_type
53 ValType::I32, // callee_async
54 ValType::I32, // string_encoding
55 ValType::I32, // result_count_or_max_if_async
56];
57
58/// Representation of an adapter module.
59pub struct Module<'a> {
60 /// Compilation configuration
61 tunables: &'a Tunables,
62 /// Type information from the creator of this `Module`
63 types: &'a ComponentTypesBuilder,
64
65 /// The Wasm features enabled for validation of this module.
66 features: WasmFeatures,
67
68 /// Core wasm type section that's incrementally built
69 core_types: core_types::CoreTypes,
70
71 /// Core wasm import section which is built as adapters are inserted. Note
72 /// that imports here are intern'd to avoid duplicate imports of the same
73 /// item.
74 core_imports: ImportSection,
75 /// Final list of imports that this module ended up using, in the same order
76 /// as the imports in the import section.
77 imports: Vec<Import>,
78 /// Intern'd imports and what index they were assigned. Note that this map
79 /// covers all the index spaces for imports, not just one.
80 imported: HashMap<CoreDef, usize>,
81 /// Intern'd transcoders and what index they were assigned.
82 imported_transcoders: HashMap<Transcoder, FuncIndex>,
83
84 /// Cached versions of imported trampolines for working with resources.
85 imported_resource_transfer_own: Option<FuncIndex>,
86 imported_resource_transfer_borrow: Option<FuncIndex>,
87
88 // Cached versions of imported trampolines for working with the async ABI.
89 imported_async_start_calls: HashMap<(Option<FuncIndex>, Option<FuncIndex>), FuncIndex>,
90
91 // Cached versions of imported trampolines for working with `stream`s,
92 // `future`s, and `error-context`s.
93 imported_future_transfer: Option<FuncIndex>,
94 imported_stream_transfer: Option<FuncIndex>,
95 imported_error_context_transfer: Option<FuncIndex>,
96
97 imported_enter_sync_call: Option<FuncIndex>,
98 imported_exit_sync_call: Option<FuncIndex>,
99
100 imported_trap: Option<FuncIndex>,
101
102 // Current status of index spaces from the imports generated so far.
103 imported_funcs: PrimaryMap<FuncIndex, Option<CoreDef>>,
104 imported_memories: PrimaryMap<MemoryIndex, CoreDef>,
105 imported_globals: PrimaryMap<GlobalIndex, CoreDef>,
106
107 funcs: PrimaryMap<FunctionId, Function>,
108 helper_funcs: HashMap<Helper, FunctionId>,
109 helper_worklist: Vec<(FunctionId, Helper)>,
110
111 exports: Vec<(u32, String)>,
112
113 task_may_block: Option<GlobalIndex>,
114}
115
116struct AdapterData {
117 /// Export name of this adapter
118 name: String,
119 /// Options specified during the `canon lift` operation
120 lift: AdapterOptions,
121 /// Options specified during the `canon lower` operation
122 lower: AdapterOptions,
123 /// The core wasm function that this adapter will be calling (the original
124 /// function that was `canon lift`'d)
125 callee: FuncIndex,
126}
127
128/// Configuration options which apply at the "global adapter" level.
129///
130/// These options are typically unique per-adapter and generally aren't needed
131/// when translating recursive types within an adapter.
132struct AdapterOptions {
133 /// The Wasmtime-assigned component instance index where the options were
134 /// originally specified.
135 instance: RuntimeComponentInstanceIndex,
136 /// The ancestors (i.e. chain of instantiating instances) of the instance
137 /// specified in the `instance` field.
138 ancestors: Vec<RuntimeComponentInstanceIndex>,
139 /// The ascribed type of this adapter.
140 ty: TypeFuncIndex,
141 /// The global that represents the instance flags for where this adapter
142 /// came from.
143 flags: GlobalIndex,
144 /// The configured post-return function, if any.
145 post_return: Option<FuncIndex>,
146 /// Other, more general, options configured.
147 options: Options,
148}
149
150#[derive(PartialEq, Eq, Hash, Copy, Clone)]
151/// Linear memory.
152struct LinearMemoryOptions {
153 /// An optionally-specified memory where values may travel through for
154 /// types like lists.
155 memory: Option<(MemoryIndex, Memory)>,
156 /// An optionally-specified function to be used to allocate space for
157 /// types such as strings as they go into a module.
158 realloc: Option<FuncIndex>,
159}
160
161impl LinearMemoryOptions {
162 fn ptr(&self) -> ValType {
163 if self.memory64() {
164 ValType::I64
165 } else {
166 ValType::I32
167 }
168 }
169
170 fn ptr_size(&self) -> u8 {
171 if self.memory64() { 8 } else { 4 }
172 }
173
174 fn memory64(&self) -> bool {
175 self.memory
176 .as_ref()
177 .map(|(_, ty)| ty.idx_type == IndexType::I64)
178 .unwrap_or(false)
179 }
180
181 fn sizealign(&self, abi: &CanonicalAbiInfo) -> (u32, u32) {
182 if self.memory64() {
183 (abi.size64, abi.align64)
184 } else {
185 (abi.size32, abi.align32)
186 }
187 }
188}
189
190/// The data model for objects passed through an adapter.
191#[derive(PartialEq, Eq, Hash, Copy, Clone)]
192enum DataModel {
193 Gc {},
194 LinearMemory(LinearMemoryOptions),
195}
196
197impl DataModel {
198 #[track_caller]
199 fn unwrap_memory(&self) -> &LinearMemoryOptions {
200 match self {
201 DataModel::Gc {} => panic!("`unwrap_memory` on GC"),
202 DataModel::LinearMemory(opts) => opts,
203 }
204 }
205}
206
207/// This type is split out of `AdapterOptions` and is specifically used to
208/// deduplicate translation functions within a module. Consequently this has
209/// as few fields as possible to minimize the number of functions generated
210/// within an adapter module.
211#[derive(PartialEq, Eq, Hash, Copy, Clone)]
212struct Options {
213 /// The encoding that strings use from this adapter.
214 string_encoding: StringEncoding,
215 callback: Option<FuncIndex>,
216 async_: bool,
217 core_type: ModuleInternedTypeIndex,
218 data_model: DataModel,
219}
220
221/// Representation of a "helper function" which may be generated as part of
222/// generating an adapter trampoline.
223///
224/// Helper functions are created when inlining the translation for a type in its
225/// entirety would make a function excessively large. This is currently done via
226/// a simple fuel/cost heuristic based on the type being translated but may get
227/// fancier over time.
228#[derive(Copy, Clone, PartialEq, Eq, Hash)]
229struct Helper {
230 /// Metadata about the source type of what's being translated.
231 src: HelperType,
232 /// Metadata about the destination type which is being translated to.
233 dst: HelperType,
234}
235
236/// Information about a source or destination type in a `Helper` which is
237/// generated.
238#[derive(Copy, Clone, PartialEq, Eq, Hash)]
239struct HelperType {
240 /// The concrete type being translated.
241 ty: InterfaceType,
242 /// The configuration options (memory, etc) for the adapter.
243 opts: Options,
244 /// Where the type is located (either the stack or in memory)
245 loc: HelperLocation,
246}
247
248/// Where a `HelperType` is located, dictating the signature of the helper
249/// function.
250#[derive(Copy, Clone, PartialEq, Eq, Hash)]
251enum HelperLocation {
252 /// Located on the stack in wasm locals.
253 Stack,
254 /// Located in linear memory as configured by `opts`.
255 Memory,
256 /// Located in a GC struct field.
257 #[expect(dead_code, reason = "CM+GC is still WIP")]
258 StructField,
259 /// Located in a GC array element.
260 #[expect(dead_code, reason = "CM+GC is still WIP")]
261 ArrayElement,
262}
263
264impl<'a> Module<'a> {
265 /// Creates an empty module.
266 pub fn new(
267 types: &'a ComponentTypesBuilder,
268 tunables: &'a Tunables,
269 features: WasmFeatures,
270 ) -> Module<'a> {
271 Module {
272 tunables,
273 types,
274 features,
275 core_types: Default::default(),
276 core_imports: Default::default(),
277 imported: Default::default(),
278 imports: Default::default(),
279 imported_transcoders: Default::default(),
280 imported_funcs: PrimaryMap::new(),
281 imported_memories: PrimaryMap::new(),
282 imported_globals: PrimaryMap::new(),
283 funcs: PrimaryMap::new(),
284 helper_funcs: HashMap::new(),
285 helper_worklist: Vec::new(),
286 imported_resource_transfer_own: None,
287 imported_resource_transfer_borrow: None,
288 imported_async_start_calls: HashMap::new(),
289 imported_future_transfer: None,
290 imported_stream_transfer: None,
291 imported_error_context_transfer: None,
292 imported_enter_sync_call: None,
293 imported_exit_sync_call: None,
294 imported_trap: None,
295 exports: Vec::new(),
296 task_may_block: None,
297 }
298 }
299
300 /// Registers a new adapter within this adapter module.
301 ///
302 /// The `name` provided is the export name of the adapter from the final
303 /// module, and `adapter` contains all metadata necessary for compilation.
304 pub fn adapt(&mut self, name: &str, adapter: &Adapter) {
305 // Import any items required by the various canonical options
306 // (memories, reallocs, etc)
307 let mut lift = self.import_options(adapter.lift_ty, &adapter.lift_options);
308 let lower = self.import_options(adapter.lower_ty, &adapter.lower_options);
309
310 // Lowering options are not allowed to specify post-return as per the
311 // current canonical abi specification.
312 assert!(adapter.lower_options.post_return.is_none());
313
314 // Import the core wasm function which was lifted using its appropriate
315 // signature since the exported function this adapter generates will
316 // call the lifted function.
317 let signature = self.types.signature(&lift);
318 let ty = self
319 .core_types
320 .function(&signature.params, &signature.results);
321 let callee = self.import_func("callee", name, ty, adapter.func.clone());
322
323 // Handle post-return specifically here where we have `core_ty` and the
324 // results of `core_ty` are the parameters to the post-return function.
325 lift.post_return = adapter.lift_options.post_return.as_ref().map(|func| {
326 let ty = self.core_types.function(&signature.results, &[]);
327 self.import_func("post_return", name, ty, func.clone())
328 });
329
330 // This will internally create the adapter as specified and append
331 // anything necessary to `self.funcs`.
332 trampoline::compile(
333 self,
334 &AdapterData {
335 name: name.to_string(),
336 lift,
337 lower,
338 callee,
339 },
340 );
341
342 while let Some((result, helper)) = self.helper_worklist.pop() {
343 trampoline::compile_helper(self, result, helper);
344 }
345 }
346
347 fn import_options(&mut self, ty: TypeFuncIndex, options: &AdapterOptionsDfg) -> AdapterOptions {
348 let AdapterOptionsDfg {
349 instance,
350 ancestors,
351 string_encoding,
352 post_return: _, // handled above
353 callback,
354 async_,
355 core_type,
356 data_model,
357 cancellable,
358 } = options;
359 assert!(!cancellable);
360
361 let flags = self.import_global(
362 "flags",
363 &format!("instance{}", instance.as_u32()),
364 GlobalType {
365 val_type: ValType::I32,
366 mutable: true,
367 shared: false,
368 },
369 CoreDef::InstanceFlags(*instance),
370 );
371
372 let data_model = match data_model {
373 crate::component::DataModel::Gc {} => DataModel::Gc {},
374 crate::component::DataModel::LinearMemory { memory, realloc } => {
375 let memory = memory.as_ref().map(|(memory, ty)| {
376 (
377 self.import_memory(
378 "memory",
379 &format!("m{}", self.imported_memories.len()),
380 MemoryType {
381 minimum: 0,
382 maximum: None,
383 shared: ty.shared,
384 memory64: ty.idx_type == IndexType::I64,
385 page_size_log2: if ty.page_size_log2 == 16 {
386 None
387 } else {
388 Some(ty.page_size_log2.into())
389 },
390 },
391 memory.clone().into(),
392 ),
393 *ty,
394 )
395 });
396 let realloc = realloc.as_ref().map(|func| {
397 let ptr = match memory.as_ref().unwrap().1.idx_type {
398 IndexType::I32 => ValType::I32,
399 IndexType::I64 => ValType::I64,
400 };
401 let ty = self.core_types.function(&[ptr, ptr, ptr, ptr], &[ptr]);
402 self.import_func(
403 "realloc",
404 &format!("f{}", self.imported_funcs.len()),
405 ty,
406 func.clone(),
407 )
408 });
409 DataModel::LinearMemory(LinearMemoryOptions { memory, realloc })
410 }
411 };
412
413 let callback = callback.as_ref().map(|func| {
414 let ty = self
415 .core_types
416 .function(&[ValType::I32, ValType::I32, ValType::I32], &[ValType::I32]);
417 self.import_func(
418 "callback",
419 &format!("f{}", self.imported_funcs.len()),
420 ty,
421 func.clone(),
422 )
423 });
424
425 AdapterOptions {
426 instance: *instance,
427 ancestors: ancestors.clone(),
428 ty,
429 flags,
430 post_return: None,
431 options: Options {
432 string_encoding: *string_encoding,
433 callback,
434 async_: *async_,
435 core_type: *core_type,
436 data_model,
437 },
438 }
439 }
440
441 fn import_func(&mut self, module: &str, name: &str, ty: u32, def: CoreDef) -> FuncIndex {
442 self.import(module, name, EntityType::Function(ty), def, |m| {
443 &mut m.imported_funcs
444 })
445 }
446
447 fn import_global(
448 &mut self,
449 module: &str,
450 name: &str,
451 ty: GlobalType,
452 def: CoreDef,
453 ) -> GlobalIndex {
454 self.import(module, name, EntityType::Global(ty), def, |m| {
455 &mut m.imported_globals
456 })
457 }
458
459 fn import_memory(
460 &mut self,
461 module: &str,
462 name: &str,
463 ty: MemoryType,
464 def: CoreDef,
465 ) -> MemoryIndex {
466 self.import(module, name, EntityType::Memory(ty), def, |m| {
467 &mut m.imported_memories
468 })
469 }
470
471 fn import<K: EntityRef, V: From<CoreDef>>(
472 &mut self,
473 module: &str,
474 name: &str,
475 ty: EntityType,
476 def: CoreDef,
477 map: impl FnOnce(&mut Self) -> &mut PrimaryMap<K, V>,
478 ) -> K {
479 if let Some(prev) = self.imported.get(&def) {
480 return K::new(*prev);
481 }
482 let idx = map(self).push(def.clone().into());
483 self.core_imports.import(module, name, ty);
484 self.imported.insert(def.clone(), idx.index());
485 self.imports.push(Import::CoreDef(def));
486 idx
487 }
488
489 fn import_task_may_block(&mut self) -> GlobalIndex {
490 if let Some(task_may_block) = self.task_may_block {
491 task_may_block
492 } else {
493 let task_may_block = self.import_global(
494 "instance",
495 "task_may_block",
496 GlobalType {
497 val_type: ValType::I32,
498 mutable: true,
499 shared: false,
500 },
501 CoreDef::TaskMayBlock,
502 );
503 self.task_may_block = Some(task_may_block);
504 task_may_block
505 }
506 }
507
508 fn import_transcoder(&mut self, transcoder: transcode::Transcoder) -> FuncIndex {
509 *self
510 .imported_transcoders
511 .entry(transcoder)
512 .or_insert_with(|| {
513 // Add the import to the core wasm import section...
514 let name = transcoder.name();
515 let ty = transcoder.ty(&mut self.core_types);
516 self.core_imports.import("transcode", &name, ty);
517
518 // ... and also record the metadata for what this import
519 // corresponds to.
520 let from = self.imported_memories[transcoder.from_memory].clone();
521 let to = self.imported_memories[transcoder.to_memory].clone();
522 self.imports.push(Import::Transcode {
523 op: transcoder.op,
524 from,
525 from64: transcoder.from_memory64,
526 to,
527 to64: transcoder.to_memory64,
528 });
529
530 self.imported_funcs.push(None)
531 })
532 }
533
534 fn import_simple(
535 &mut self,
536 module: &str,
537 name: &str,
538 params: &[ValType],
539 results: &[ValType],
540 import: Import,
541 get: impl Fn(&mut Self) -> &mut Option<FuncIndex>,
542 ) -> FuncIndex {
543 self.import_simple_get_and_set(
544 module,
545 name,
546 params,
547 results,
548 import,
549 |me| *get(me),
550 |me, v| *get(me) = Some(v),
551 )
552 }
553
554 fn import_simple_get_and_set(
555 &mut self,
556 module: &str,
557 name: &str,
558 params: &[ValType],
559 results: &[ValType],
560 import: Import,
561 get: impl Fn(&mut Self) -> Option<FuncIndex>,
562 set: impl Fn(&mut Self, FuncIndex),
563 ) -> FuncIndex {
564 if let Some(idx) = get(self) {
565 return idx;
566 }
567 let ty = self.core_types.function(params, results);
568 let ty = EntityType::Function(ty);
569 self.core_imports.import(module, name, ty);
570
571 self.imports.push(import);
572 let idx = self.imported_funcs.push(None);
573 set(self, idx);
574 idx
575 }
576
577 /// Import a host built-in function to set up a subtask for a sync-lowered
578 /// import call to an async-lifted export.
579 ///
580 /// Given that the callee may exert backpressure before the host can copy
581 /// the parameters, the adapter must use this function to set up the subtask
582 /// and stash the parameters as part of that subtask until any backpressure
583 /// has cleared.
584 fn import_prepare_call(
585 &mut self,
586 suffix: &str,
587 params: &[ValType],
588 memory: Option<MemoryIndex>,
589 ) -> FuncIndex {
590 let ty = self.core_types.function(
591 &PREPARE_CALL_FIXED_PARAMS
592 .iter()
593 .copied()
594 .chain(params.iter().copied())
595 .collect::<Vec<_>>(),
596 &[],
597 );
598 self.core_imports.import(
599 "sync",
600 &format!("[prepare-call]{suffix}"),
601 EntityType::Function(ty),
602 );
603 let import = Import::PrepareCall {
604 memory: memory.map(|v| self.imported_memories[v].clone()),
605 };
606 self.imports.push(import);
607 self.imported_funcs.push(None)
608 }
609
610 /// Import a host built-in function to start a subtask for a sync-lowered
611 /// import call to an async-lifted export.
612 ///
613 /// This call with block until the subtask has produced result(s) via the
614 /// `task.return` intrinsic.
615 ///
616 /// Note that this could potentially be combined with the `sync-prepare`
617 /// built-in into a single built-in function that does both jobs. However,
618 /// we've kept them separate to allow a future optimization where the caller
619 /// calls the callee directly rather than using `sync-start` to have the host
620 /// do it.
621 fn import_sync_start_call(
622 &mut self,
623 suffix: &str,
624 callback: Option<FuncIndex>,
625 results: &[ValType],
626 ) -> FuncIndex {
627 let ty = self
628 .core_types
629 .function(&[ValType::FUNCREF, ValType::I32], results);
630 self.core_imports.import(
631 "sync",
632 &format!("[start-call]{suffix}"),
633 EntityType::Function(ty),
634 );
635 let import = Import::SyncStartCall {
636 callback: callback
637 .map(|callback| self.imported_funcs.get(callback).unwrap().clone().unwrap()),
638 };
639 self.imports.push(import);
640 self.imported_funcs.push(None)
641 }
642
643 /// Import a host built-in function to start a subtask for an async-lowered
644 /// import call to an async- or sync-lifted export.
645 ///
646 /// Note that this could potentially be combined with the `async-prepare`
647 /// built-in into a single built-in function that does both jobs. However,
648 /// we've kept them separate to allow a future optimization where the caller
649 /// calls the callee directly rather than using `async-start` to have the
650 /// host do it.
651 fn import_async_start_call(
652 &mut self,
653 suffix: &str,
654 callback: Option<FuncIndex>,
655 post_return: Option<FuncIndex>,
656 ) -> FuncIndex {
657 self.import_simple_get_and_set(
658 "async",
659 &format!("[start-call]{suffix}"),
660 &[ValType::FUNCREF, ValType::I32, ValType::I32, ValType::I32],
661 &[ValType::I32],
662 Import::AsyncStartCall {
663 callback: callback
664 .map(|callback| self.imported_funcs.get(callback).unwrap().clone().unwrap()),
665 post_return: post_return.map(|post_return| {
666 self.imported_funcs
667 .get(post_return)
668 .unwrap()
669 .clone()
670 .unwrap()
671 }),
672 },
673 |me| {
674 me.imported_async_start_calls
675 .get(&(callback, post_return))
676 .copied()
677 },
678 |me, v| {
679 assert!(
680 me.imported_async_start_calls
681 .insert((callback, post_return), v)
682 .is_none()
683 )
684 },
685 )
686 }
687
688 fn import_future_transfer(&mut self) -> FuncIndex {
689 self.import_simple(
690 "future",
691 "transfer",
692 &[ValType::I32; 3],
693 &[ValType::I32],
694 Import::FutureTransfer,
695 |me| &mut me.imported_future_transfer,
696 )
697 }
698
699 fn import_stream_transfer(&mut self) -> FuncIndex {
700 self.import_simple(
701 "stream",
702 "transfer",
703 &[ValType::I32; 3],
704 &[ValType::I32],
705 Import::StreamTransfer,
706 |me| &mut me.imported_stream_transfer,
707 )
708 }
709
710 fn import_error_context_transfer(&mut self) -> FuncIndex {
711 self.import_simple(
712 "error-context",
713 "transfer",
714 &[ValType::I32; 3],
715 &[ValType::I32],
716 Import::ErrorContextTransfer,
717 |me| &mut me.imported_error_context_transfer,
718 )
719 }
720
721 fn import_resource_transfer_own(&mut self) -> FuncIndex {
722 self.import_simple(
723 "resource",
724 "transfer-own",
725 &[ValType::I32, ValType::I32, ValType::I32],
726 &[ValType::I32],
727 Import::ResourceTransferOwn,
728 |me| &mut me.imported_resource_transfer_own,
729 )
730 }
731
732 fn import_resource_transfer_borrow(&mut self) -> FuncIndex {
733 self.import_simple(
734 "resource",
735 "transfer-borrow",
736 &[ValType::I32, ValType::I32, ValType::I32],
737 &[ValType::I32],
738 Import::ResourceTransferBorrow,
739 |me| &mut me.imported_resource_transfer_borrow,
740 )
741 }
742
743 fn import_enter_sync_call(&mut self) -> FuncIndex {
744 self.import_simple(
745 "async",
746 "enter-sync-call",
747 &[ValType::I32; 3],
748 &[],
749 Import::EnterSyncCall,
750 |me| &mut me.imported_enter_sync_call,
751 )
752 }
753
754 fn import_exit_sync_call(&mut self) -> FuncIndex {
755 self.import_simple(
756 "async",
757 "exit-sync-call",
758 &[],
759 &[],
760 Import::ExitSyncCall,
761 |me| &mut me.imported_exit_sync_call,
762 )
763 }
764
765 fn import_trap(&mut self) -> FuncIndex {
766 self.import_simple(
767 "runtime",
768 "trap",
769 &[ValType::I32],
770 &[],
771 Import::Trap,
772 |me| &mut me.imported_trap,
773 )
774 }
775
776 fn translate_helper(&mut self, helper: Helper) -> FunctionId {
777 *self.helper_funcs.entry(helper).or_insert_with(|| {
778 // Generate a fresh `Function` with a unique id for what we're about to
779 // generate.
780 let ty = helper.core_type(self.types, &mut self.core_types);
781 let id = self.funcs.push(Function::new(None, ty));
782 self.helper_worklist.push((id, helper));
783 id
784 })
785 }
786
787 /// Encodes this module into a WebAssembly binary.
788 pub fn encode(&mut self) -> Vec<u8> {
789 // Build the function/export sections of the wasm module in a first pass
790 // which will assign a final `FuncIndex` to all functions defined in
791 // `self.funcs`.
792 let mut funcs = FunctionSection::new();
793 let mut exports = ExportSection::new();
794 let mut id_to_index = PrimaryMap::<FunctionId, FuncIndex>::new();
795 for (id, func) in self.funcs.iter() {
796 assert!(func.filled_in);
797 let idx = FuncIndex::from_u32(self.imported_funcs.next_key().as_u32() + id.as_u32());
798 let id2 = id_to_index.push(idx);
799 assert_eq!(id2, id);
800
801 funcs.function(func.ty);
802
803 if let Some(name) = &func.export {
804 exports.export(name, ExportKind::Func, idx.as_u32());
805 }
806 }
807 for (idx, name) in &self.exports {
808 exports.export(name, ExportKind::Func, *idx);
809 }
810
811 // With all functions numbered the fragments of the body of each
812 // function can be assigned into one final adapter function.
813 let mut code = CodeSection::new();
814 for (_, func) in self.funcs.iter() {
815 let mut body = Vec::new();
816
817 // Encode all locals used for this function
818 func.locals.len().encode(&mut body);
819 for (count, ty) in func.locals.iter() {
820 count.encode(&mut body);
821 ty.encode(&mut body);
822 }
823
824 // Then encode each "chunk" of a body which may have optional traps
825 // specified within it. Traps get offset by the current length of
826 // the body and otherwise our `Call` instructions are "relocated"
827 // here to the final function index.
828 for chunk in func.body.iter() {
829 match chunk {
830 Body::Raw(code) => {
831 body.extend_from_slice(code);
832 }
833 Body::Call(id) => {
834 Instruction::Call(id_to_index[*id].as_u32()).encode(&mut body);
835 }
836 Body::RefFunc(id) => {
837 Instruction::RefFunc(id_to_index[*id].as_u32()).encode(&mut body);
838 }
839 }
840 }
841 code.raw(&body);
842 }
843
844 let mut result = wasm_encoder::Module::new();
845 result.section(&self.core_types.section);
846 result.section(&self.core_imports);
847 result.section(&funcs);
848 result.section(&exports);
849 result.section(&code);
850 result.finish()
851 }
852
853 /// Returns the imports that were used, in order, to create this adapter
854 /// module.
855 pub fn imports(&self) -> &[Import] {
856 &self.imports
857 }
858}
859
860/// Possible imports into an adapter module.
861#[derive(Clone)]
862pub enum Import {
863 /// A definition required in the configuration of an `Adapter`.
864 CoreDef(CoreDef),
865 /// A transcoding function from the host to convert between string encodings.
866 Transcode {
867 /// The transcoding operation this performs.
868 op: Transcode,
869 /// The memory being read
870 from: CoreDef,
871 /// Whether or not `from` is a 64-bit memory
872 from64: bool,
873 /// The memory being written
874 to: CoreDef,
875 /// Whether or not `to` is a 64-bit memory
876 to64: bool,
877 },
878 /// Transfers an owned resource from one table to another.
879 ResourceTransferOwn,
880 /// Transfers a borrowed resource from one table to another.
881 ResourceTransferBorrow,
882 /// An intrinsic used by FACT-generated modules to begin a call involving
883 /// an async-lowered import and/or an async-lifted export.
884 PrepareCall {
885 /// The memory used to verify that the memory specified for the
886 /// `task.return` that is called at runtime (if any) matches the one
887 /// specified in the lifted export.
888 memory: Option<CoreDef>,
889 },
890 /// An intrinsic used by FACT-generated modules to complete a call involving
891 /// a sync-lowered import and async-lifted export.
892 SyncStartCall {
893 /// The callee's callback function, if any.
894 callback: Option<CoreDef>,
895 },
896 /// An intrinsic used by FACT-generated modules to complete a call involving
897 /// an async-lowered import function.
898 AsyncStartCall {
899 /// The callee's callback function, if any.
900 callback: Option<CoreDef>,
901
902 /// The callee's post-return function, if any.
903 post_return: Option<CoreDef>,
904 },
905 /// An intrinisic used by FACT-generated modules to (partially or entirely) transfer
906 /// ownership of a `future`.
907 FutureTransfer,
908 /// An intrinisic used by FACT-generated modules to (partially or entirely) transfer
909 /// ownership of a `stream`.
910 StreamTransfer,
911 /// An intrinisic used by FACT-generated modules to (partially or entirely) transfer
912 /// ownership of an `error-context`.
913 ErrorContextTransfer,
914 /// An intrinsic for trapping the instance with a specific trap code.
915 Trap,
916 /// An intrinsic used by FACT-generated modules to check whether an instance
917 /// may be entered for a sync-to-sync call and push a task onto the stack if
918 /// so.
919 EnterSyncCall,
920 /// An intrinsic used by FACT-generated modules to pop the task previously
921 /// pushed by `EnterSyncCall`.
922 ExitSyncCall,
923}
924
925impl Options {
926 fn flat_types<'a>(
927 &self,
928 ty: &InterfaceType,
929 types: &'a ComponentTypesBuilder,
930 ) -> Option<&'a [FlatType]> {
931 let flat = types.flat_types(ty)?;
932 match self.data_model {
933 DataModel::Gc {} => todo!("CM+GC"),
934 DataModel::LinearMemory(mem_opts) => Some(if mem_opts.memory64() {
935 flat.memory64
936 } else {
937 flat.memory32
938 }),
939 }
940 }
941}
942
943/// Temporary index which is not the same as `FuncIndex`.
944///
945/// This represents the nth generated function in the adapter module where the
946/// final index of the function is not known at the time of generation since
947/// more imports may be discovered (specifically string transcoders).
948#[derive(Debug, Copy, Clone, PartialEq, Eq)]
949struct FunctionId(u32);
950cranelift_entity::entity_impl!(FunctionId);
951
952/// A generated function to be added to an adapter module.
953///
954/// At least one function is created per-adapter and depending on the type
955/// hierarchy multiple functions may be generated per-adapter.
956struct Function {
957 /// Whether or not the `body` has been finished.
958 ///
959 /// Functions are added to a `Module` before they're defined so this is used
960 /// to assert that the function was in fact actually filled in by the
961 /// time we reach `Module::encode`.
962 filled_in: bool,
963
964 /// The type signature that this function has, as an index into the core
965 /// wasm type index space of the generated adapter module.
966 ty: u32,
967
968 /// The locals that are used by this function, organized by the number of
969 /// types of each local.
970 locals: Vec<(u32, ValType)>,
971
972 /// If specified, the export name of this function.
973 export: Option<String>,
974
975 /// The contents of the function.
976 ///
977 /// See `Body` for more information, and the `Vec` here represents the
978 /// concatenation of all the `Body` fragments.
979 body: Vec<Body>,
980}
981
982/// Representation of a fragment of the body of a core wasm function generated
983/// for adapters.
984///
985/// This variant comes in one of two flavors:
986///
987/// 1. First a `Raw` variant is used to contain general instructions for the
988/// wasm function. This is populated by `Compiler::instruction` primarily.
989///
990/// 2. A `Call` instruction variant for a `FunctionId` where the final
991/// `FuncIndex` isn't known until emission time.
992///
993/// The purpose of this representation is the `Body::Call` variant. This can't
994/// be encoded as an instruction when it's generated due to not knowing the
995/// final index of the function being called. During `Module::encode`, however,
996/// all indices are known and `Body::Call` is turned into a final
997/// `Instruction::Call`.
998///
999/// One other possible representation in the future would be to encode a `Call`
1000/// instruction with a 5-byte leb to fill in later, but for now this felt
1001/// easier to represent. A 5-byte leb may be more efficient at compile-time if
1002/// necessary, however.
1003enum Body {
1004 Raw(Vec<u8>),
1005 Call(FunctionId),
1006 RefFunc(FunctionId),
1007}
1008
1009impl Function {
1010 fn new(export: Option<String>, ty: u32) -> Function {
1011 Function {
1012 filled_in: false,
1013 ty,
1014 locals: Vec::new(),
1015 export,
1016 body: Vec::new(),
1017 }
1018 }
1019}
1020
1021impl Helper {
1022 fn core_type(
1023 &self,
1024 types: &ComponentTypesBuilder,
1025 core_types: &mut core_types::CoreTypes,
1026 ) -> u32 {
1027 let mut params = Vec::new();
1028 let mut results = Vec::new();
1029 // The source type being translated is always pushed onto the
1030 // parameters first, either a pointer for memory or its flat
1031 // representation.
1032 self.src.push_flat(&mut params, types);
1033
1034 // The destination type goes into the parameter list if it's from
1035 // memory or otherwise is the result of the function itself for a
1036 // stack-based representation.
1037 match self.dst.loc {
1038 HelperLocation::Stack => self.dst.push_flat(&mut results, types),
1039 HelperLocation::Memory => params.push(self.dst.opts.data_model.unwrap_memory().ptr()),
1040 HelperLocation::StructField | HelperLocation::ArrayElement => todo!("CM+GC"),
1041 }
1042
1043 core_types.function(¶ms, &results)
1044 }
1045}
1046
1047impl HelperType {
1048 fn push_flat(&self, dst: &mut Vec<ValType>, types: &ComponentTypesBuilder) {
1049 match self.loc {
1050 HelperLocation::Stack => {
1051 for ty in self.opts.flat_types(&self.ty, types).unwrap() {
1052 dst.push((*ty).into());
1053 }
1054 }
1055 HelperLocation::Memory => {
1056 dst.push(self.opts.data_model.unwrap_memory().ptr());
1057 }
1058 HelperLocation::StructField | HelperLocation::ArrayElement => todo!("CM+GC"),
1059 }
1060 }
1061}