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