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