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