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