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/// Bit flag for indicating async-lifted exports
40///
41/// This flag may be passed to the `async-start` built-in function (which is
42/// called from both async->async and async->sync adapters) to indicate that the
43/// callee is an async-lifted export.
44pub const START_FLAG_ASYNC_CALLEE: i32 = 1 << 0;
45
46/// Fixed parameter types for the `prepare_call` built-in function.
47///
48/// Note that `prepare_call` also takes a variable number of parameters in
49/// addition to these, determined by the signature of the function for which
50/// we're generating an adapter.
51pub static PREPARE_CALL_FIXED_PARAMS: &[ValType] = &[
52 ValType::FUNCREF, // start
53 ValType::FUNCREF, // return
54 ValType::I32, // caller_instance
55 ValType::I32, // callee_instance
56 ValType::I32, // task_return_type
57 ValType::I32, // string_encoding
58 ValType::I32, // result_count_or_max_if_async
59];
60
61/// Representation of an adapter module.
62pub struct Module<'a> {
63 /// Whether or not debug code is inserted into the adapters themselves.
64 debug: bool,
65 /// Type information from the creator of this `Module`
66 types: &'a ComponentTypesBuilder,
67
68 /// Core wasm type section that's incrementally built
69 core_types: core_types::CoreTypes,
70
71 /// Core wasm import section which is built as adapters are inserted. Note
72 /// that imports here are intern'd to avoid duplicate imports of the same
73 /// item.
74 core_imports: ImportSection,
75 /// Final list of imports that this module ended up using, in the same order
76 /// as the imports in the import section.
77 imports: Vec<Import>,
78 /// Intern'd imports and what index they were assigned. Note that this map
79 /// covers all the index spaces for imports, not just one.
80 imported: HashMap<CoreDef, usize>,
81 /// Intern'd transcoders and what index they were assigned.
82 imported_transcoders: HashMap<Transcoder, FuncIndex>,
83
84 /// Cached versions of imported trampolines for working with resources.
85 imported_resource_transfer_own: Option<FuncIndex>,
86 imported_resource_transfer_borrow: Option<FuncIndex>,
87 imported_resource_enter_call: Option<FuncIndex>,
88 imported_resource_exit_call: Option<FuncIndex>,
89
90 // Cached versions of imported trampolines for working with the async ABI.
91 imported_async_start_calls: HashMap<(Option<FuncIndex>, Option<FuncIndex>), FuncIndex>,
92
93 // Cached versions of imported trampolines for working with `stream`s,
94 // `future`s, and `error-context`s.
95 imported_future_transfer: Option<FuncIndex>,
96 imported_stream_transfer: Option<FuncIndex>,
97 imported_error_context_transfer: Option<FuncIndex>,
98
99 // Current status of index spaces from the imports generated so far.
100 imported_funcs: PrimaryMap<FuncIndex, Option<CoreDef>>,
101 imported_memories: PrimaryMap<MemoryIndex, CoreDef>,
102 imported_globals: PrimaryMap<GlobalIndex, CoreDef>,
103
104 funcs: PrimaryMap<FunctionId, Function>,
105 helper_funcs: HashMap<Helper, FunctionId>,
106 helper_worklist: Vec<(FunctionId, Helper)>,
107
108 exports: Vec<(u32, String)>,
109}
110
111struct AdapterData {
112 /// Export name of this adapter
113 name: String,
114 /// Options specified during the `canon lift` operation
115 lift: AdapterOptions,
116 /// Options specified during the `canon lower` operation
117 lower: AdapterOptions,
118 /// The core wasm function that this adapter will be calling (the original
119 /// function that was `canon lift`'d)
120 callee: FuncIndex,
121 /// FIXME(#4185) should be plumbed and handled as part of the new reentrance
122 /// rules not yet implemented here.
123 called_as_export: bool,
124}
125
126/// Configuration options which apply at the "global adapter" level.
127///
128/// These options are typically unique per-adapter and generally aren't needed
129/// when translating recursive types within an adapter.
130struct AdapterOptions {
131 instance: RuntimeComponentInstanceIndex,
132 /// The ascribed type of this adapter.
133 ty: TypeFuncIndex,
134 /// The global that represents the instance flags for where this adapter
135 /// came from.
136 flags: GlobalIndex,
137 /// The configured post-return function, if any.
138 post_return: Option<FuncIndex>,
139 /// Other, more general, options configured.
140 options: Options,
141}
142
143#[derive(PartialEq, Eq, Hash, Copy, Clone)]
144/// Linear memory.
145struct LinearMemoryOptions {
146 /// Whether or not the `memory` field, if present, is a 64-bit memory.
147 memory64: bool,
148 /// An optionally-specified memory where values may travel through for
149 /// types like lists.
150 memory: Option<MemoryIndex>,
151 /// An optionally-specified function to be used to allocate space for
152 /// types such as strings as they go into a module.
153 realloc: Option<FuncIndex>,
154}
155
156impl LinearMemoryOptions {
157 fn ptr(&self) -> ValType {
158 if self.memory64 {
159 ValType::I64
160 } else {
161 ValType::I32
162 }
163 }
164
165 fn ptr_size(&self) -> u8 {
166 if self.memory64 { 8 } else { 4 }
167 }
168}
169
170/// The data model for objects passed through an adapter.
171#[derive(PartialEq, Eq, Hash, Copy, Clone)]
172enum DataModel {
173 Gc {},
174 LinearMemory(LinearMemoryOptions),
175}
176
177impl DataModel {
178 #[track_caller]
179 fn unwrap_memory(&self) -> &LinearMemoryOptions {
180 match self {
181 DataModel::Gc {} => panic!("`unwrap_memory` on GC"),
182 DataModel::LinearMemory(opts) => opts,
183 }
184 }
185}
186
187/// This type is split out of `AdapterOptions` and is specifically used to
188/// deduplicate translation functions within a module. Consequently this has
189/// as few fields as possible to minimize the number of functions generated
190/// within an adapter module.
191#[derive(PartialEq, Eq, Hash, Copy, Clone)]
192struct Options {
193 /// The encoding that strings use from this adapter.
194 string_encoding: StringEncoding,
195 callback: Option<FuncIndex>,
196 async_: bool,
197 core_type: ModuleInternedTypeIndex,
198 data_model: DataModel,
199}
200
201/// Representation of a "helper function" which may be generated as part of
202/// generating an adapter trampoline.
203///
204/// Helper functions are created when inlining the translation for a type in its
205/// entirety would make a function excessively large. This is currently done via
206/// a simple fuel/cost heuristic based on the type being translated but may get
207/// fancier over time.
208#[derive(Copy, Clone, PartialEq, Eq, Hash)]
209struct Helper {
210 /// Metadata about the source type of what's being translated.
211 src: HelperType,
212 /// Metadata about the destination type which is being translated to.
213 dst: HelperType,
214}
215
216/// Information about a source or destination type in a `Helper` which is
217/// generated.
218#[derive(Copy, Clone, PartialEq, Eq, Hash)]
219struct HelperType {
220 /// The concrete type being translated.
221 ty: InterfaceType,
222 /// The configuration options (memory, etc) for the adapter.
223 opts: Options,
224 /// Where the type is located (either the stack or in memory)
225 loc: HelperLocation,
226}
227
228/// Where a `HelperType` is located, dictating the signature of the helper
229/// function.
230#[derive(Copy, Clone, PartialEq, Eq, Hash)]
231enum HelperLocation {
232 /// Located on the stack in wasm locals.
233 Stack,
234 /// Located in linear memory as configured by `opts`.
235 Memory,
236 /// Located in a GC struct field.
237 #[expect(dead_code, reason = "CM+GC is still WIP")]
238 StructField,
239 /// Located in a GC array element.
240 #[expect(dead_code, reason = "CM+GC is still WIP")]
241 ArrayElement,
242}
243
244impl<'a> Module<'a> {
245 /// Creates an empty module.
246 pub fn new(types: &'a ComponentTypesBuilder, debug: bool) -> Module<'a> {
247 Module {
248 debug,
249 types,
250 core_types: Default::default(),
251 core_imports: Default::default(),
252 imported: Default::default(),
253 imports: Default::default(),
254 imported_transcoders: Default::default(),
255 imported_funcs: PrimaryMap::new(),
256 imported_memories: PrimaryMap::new(),
257 imported_globals: PrimaryMap::new(),
258 funcs: PrimaryMap::new(),
259 helper_funcs: HashMap::new(),
260 helper_worklist: Vec::new(),
261 imported_resource_transfer_own: None,
262 imported_resource_transfer_borrow: None,
263 imported_resource_enter_call: None,
264 imported_resource_exit_call: None,
265 imported_async_start_calls: HashMap::new(),
266 imported_future_transfer: None,
267 imported_stream_transfer: None,
268 imported_error_context_transfer: None,
269 exports: Vec::new(),
270 }
271 }
272
273 /// Registers a new adapter within this adapter module.
274 ///
275 /// The `name` provided is the export name of the adapter from the final
276 /// module, and `adapter` contains all metadata necessary for compilation.
277 pub fn adapt(&mut self, name: &str, adapter: &Adapter) {
278 // Import any items required by the various canonical options
279 // (memories, reallocs, etc)
280 let mut lift = self.import_options(adapter.lift_ty, &adapter.lift_options);
281 let lower = self.import_options(adapter.lower_ty, &adapter.lower_options);
282
283 // Lowering options are not allowed to specify post-return as per the
284 // current canonical abi specification.
285 assert!(adapter.lower_options.post_return.is_none());
286
287 // Import the core wasm function which was lifted using its appropriate
288 // signature since the exported function this adapter generates will
289 // call the lifted function.
290 let signature = self.types.signature(&lift);
291 let ty = self
292 .core_types
293 .function(&signature.params, &signature.results);
294 let callee = self.import_func("callee", name, ty, adapter.func.clone());
295
296 // Handle post-return specifically here where we have `core_ty` and the
297 // results of `core_ty` are the parameters to the post-return function.
298 lift.post_return = adapter.lift_options.post_return.as_ref().map(|func| {
299 let ty = self.core_types.function(&signature.results, &[]);
300 self.import_func("post_return", name, ty, func.clone())
301 });
302
303 // This will internally create the adapter as specified and append
304 // anything necessary to `self.funcs`.
305 trampoline::compile(
306 self,
307 &AdapterData {
308 name: name.to_string(),
309 lift,
310 lower,
311 callee,
312 // FIXME(#4185) should be plumbed and handled as part of the new
313 // reentrance rules not yet implemented here.
314 called_as_export: true,
315 },
316 );
317
318 while let Some((result, helper)) = self.helper_worklist.pop() {
319 trampoline::compile_helper(self, result, helper);
320 }
321 }
322
323 fn import_options(&mut self, ty: TypeFuncIndex, options: &AdapterOptionsDfg) -> AdapterOptions {
324 let AdapterOptionsDfg {
325 instance,
326 string_encoding,
327 post_return: _, // handled above
328 callback,
329 async_,
330 core_type,
331 data_model,
332 } = options;
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 translate_helper(&mut self, helper: Helper) -> FunctionId {
721 *self.helper_funcs.entry(helper).or_insert_with(|| {
722 // Generate a fresh `Function` with a unique id for what we're about to
723 // generate.
724 let ty = helper.core_type(self.types, &mut self.core_types);
725 let id = self.funcs.push(Function::new(None, ty));
726 self.helper_worklist.push((id, helper));
727 id
728 })
729 }
730
731 /// Encodes this module into a WebAssembly binary.
732 pub fn encode(&mut self) -> Vec<u8> {
733 // Build the function/export sections of the wasm module in a first pass
734 // which will assign a final `FuncIndex` to all functions defined in
735 // `self.funcs`.
736 let mut funcs = FunctionSection::new();
737 let mut exports = ExportSection::new();
738 let mut id_to_index = PrimaryMap::<FunctionId, FuncIndex>::new();
739 for (id, func) in self.funcs.iter() {
740 assert!(func.filled_in);
741 let idx = FuncIndex::from_u32(self.imported_funcs.next_key().as_u32() + id.as_u32());
742 let id2 = id_to_index.push(idx);
743 assert_eq!(id2, id);
744
745 funcs.function(func.ty);
746
747 if let Some(name) = &func.export {
748 exports.export(name, ExportKind::Func, idx.as_u32());
749 }
750 }
751 for (idx, name) in &self.exports {
752 exports.export(name, ExportKind::Func, *idx);
753 }
754
755 // With all functions numbered the fragments of the body of each
756 // function can be assigned into one final adapter function.
757 let mut code = CodeSection::new();
758 let mut traps = traps::TrapSection::default();
759 for (id, func) in self.funcs.iter() {
760 let mut func_traps = Vec::new();
761 let mut body = Vec::new();
762
763 // Encode all locals used for this function
764 func.locals.len().encode(&mut body);
765 for (count, ty) in func.locals.iter() {
766 count.encode(&mut body);
767 ty.encode(&mut body);
768 }
769
770 // Then encode each "chunk" of a body which may have optional traps
771 // specified within it. Traps get offset by the current length of
772 // the body and otherwise our `Call` instructions are "relocated"
773 // here to the final function index.
774 for chunk in func.body.iter() {
775 match chunk {
776 Body::Raw(code, traps) => {
777 let start = body.len();
778 body.extend_from_slice(code);
779 for (offset, trap) in traps {
780 func_traps.push((start + offset, *trap));
781 }
782 }
783 Body::Call(id) => {
784 Instruction::Call(id_to_index[*id].as_u32()).encode(&mut body);
785 }
786 Body::RefFunc(id) => {
787 Instruction::RefFunc(id_to_index[*id].as_u32()).encode(&mut body);
788 }
789 }
790 }
791 code.raw(&body);
792 traps.append(id_to_index[id].as_u32(), func_traps);
793 }
794
795 let traps = traps.finish();
796
797 let mut result = wasm_encoder::Module::new();
798 result.section(&self.core_types.section);
799 result.section(&self.core_imports);
800 result.section(&funcs);
801 result.section(&exports);
802 result.section(&code);
803 if self.debug {
804 result.section(&CustomSection {
805 name: "wasmtime-trampoline-traps".into(),
806 data: Cow::Borrowed(&traps),
807 });
808 }
809 result.finish()
810 }
811
812 /// Returns the imports that were used, in order, to create this adapter
813 /// module.
814 pub fn imports(&self) -> &[Import] {
815 &self.imports
816 }
817}
818
819/// Possible imports into an adapter module.
820#[derive(Clone)]
821pub enum Import {
822 /// A definition required in the configuration of an `Adapter`.
823 CoreDef(CoreDef),
824 /// A transcoding function from the host to convert between string encodings.
825 Transcode {
826 /// The transcoding operation this performs.
827 op: Transcode,
828 /// The memory being read
829 from: CoreDef,
830 /// Whether or not `from` is a 64-bit memory
831 from64: bool,
832 /// The memory being written
833 to: CoreDef,
834 /// Whether or not `to` is a 64-bit memory
835 to64: bool,
836 },
837 /// Transfers an owned resource from one table to another.
838 ResourceTransferOwn,
839 /// Transfers a borrowed resource from one table to another.
840 ResourceTransferBorrow,
841 /// Sets up entry metadata for a borrow resources when a call starts.
842 ResourceEnterCall,
843 /// Tears down a previous entry and handles checking borrow-related
844 /// metadata.
845 ResourceExitCall,
846 /// An intrinsic used by FACT-generated modules to begin a call involving
847 /// an async-lowered import and/or an async-lifted export.
848 PrepareCall {
849 /// The memory used to verify that the memory specified for the
850 /// `task.return` that is called at runtime (if any) matches the one
851 /// specified in the lifted export.
852 memory: Option<CoreDef>,
853 },
854 /// An intrinsic used by FACT-generated modules to complete a call involving
855 /// a sync-lowered import and async-lifted export.
856 SyncStartCall {
857 /// The callee's callback function, if any.
858 callback: Option<CoreDef>,
859 },
860 /// An intrinsic used by FACT-generated modules to complete a call involving
861 /// an async-lowered import function.
862 AsyncStartCall {
863 /// The callee's callback function, if any.
864 callback: Option<CoreDef>,
865
866 /// The callee's post-return function, if any.
867 post_return: Option<CoreDef>,
868 },
869 /// An intrinisic used by FACT-generated modules to (partially or entirely) transfer
870 /// ownership of a `future`.
871 FutureTransfer,
872 /// An intrinisic used by FACT-generated modules to (partially or entirely) transfer
873 /// ownership of a `stream`.
874 StreamTransfer,
875 /// An intrinisic used by FACT-generated modules to (partially or entirely) transfer
876 /// ownership of an `error-context`.
877 ErrorContextTransfer,
878}
879
880impl Options {
881 fn flat_types<'a>(
882 &self,
883 ty: &InterfaceType,
884 types: &'a ComponentTypesBuilder,
885 ) -> Option<&'a [FlatType]> {
886 let flat = types.flat_types(ty)?;
887 match self.data_model {
888 DataModel::Gc {} => todo!("CM+GC"),
889 DataModel::LinearMemory(mem_opts) => Some(if mem_opts.memory64 {
890 flat.memory64
891 } else {
892 flat.memory32
893 }),
894 }
895 }
896}
897
898/// Temporary index which is not the same as `FuncIndex`.
899///
900/// This represents the nth generated function in the adapter module where the
901/// final index of the function is not known at the time of generation since
902/// more imports may be discovered (specifically string transcoders).
903#[derive(Debug, Copy, Clone, PartialEq, Eq)]
904struct FunctionId(u32);
905cranelift_entity::entity_impl!(FunctionId);
906
907/// A generated function to be added to an adapter module.
908///
909/// At least one function is created per-adapter and depending on the type
910/// hierarchy multiple functions may be generated per-adapter.
911struct Function {
912 /// Whether or not the `body` has been finished.
913 ///
914 /// Functions are added to a `Module` before they're defined so this is used
915 /// to assert that the function was in fact actually filled in by the
916 /// time we reach `Module::encode`.
917 filled_in: bool,
918
919 /// The type signature that this function has, as an index into the core
920 /// wasm type index space of the generated adapter module.
921 ty: u32,
922
923 /// The locals that are used by this function, organized by the number of
924 /// types of each local.
925 locals: Vec<(u32, ValType)>,
926
927 /// If specified, the export name of this function.
928 export: Option<String>,
929
930 /// The contents of the function.
931 ///
932 /// See `Body` for more information, and the `Vec` here represents the
933 /// concatenation of all the `Body` fragments.
934 body: Vec<Body>,
935}
936
937/// Representation of a fragment of the body of a core wasm function generated
938/// for adapters.
939///
940/// This variant comes in one of two flavors:
941///
942/// 1. First a `Raw` variant is used to contain general instructions for the
943/// wasm function. This is populated by `Compiler::instruction` primarily.
944/// This also comes with a list of traps. and the byte offset within the
945/// first vector of where the trap information applies to.
946///
947/// 2. A `Call` instruction variant for a `FunctionId` where the final
948/// `FuncIndex` isn't known until emission time.
949///
950/// The purpose of this representation is the `Body::Call` variant. This can't
951/// be encoded as an instruction when it's generated due to not knowing the
952/// final index of the function being called. During `Module::encode`, however,
953/// all indices are known and `Body::Call` is turned into a final
954/// `Instruction::Call`.
955///
956/// One other possible representation in the future would be to encode a `Call`
957/// instruction with a 5-byte leb to fill in later, but for now this felt
958/// easier to represent. A 5-byte leb may be more efficient at compile-time if
959/// necessary, however.
960enum Body {
961 Raw(Vec<u8>, Vec<(usize, traps::Trap)>),
962 Call(FunctionId),
963 RefFunc(FunctionId),
964}
965
966impl Function {
967 fn new(export: Option<String>, ty: u32) -> Function {
968 Function {
969 filled_in: false,
970 ty,
971 locals: Vec::new(),
972 export,
973 body: Vec::new(),
974 }
975 }
976}
977
978impl Helper {
979 fn core_type(
980 &self,
981 types: &ComponentTypesBuilder,
982 core_types: &mut core_types::CoreTypes,
983 ) -> u32 {
984 let mut params = Vec::new();
985 let mut results = Vec::new();
986 // The source type being translated is always pushed onto the
987 // parameters first, either a pointer for memory or its flat
988 // representation.
989 self.src.push_flat(&mut params, types);
990
991 // The destination type goes into the parameter list if it's from
992 // memory or otherwise is the result of the function itself for a
993 // stack-based representation.
994 match self.dst.loc {
995 HelperLocation::Stack => self.dst.push_flat(&mut results, types),
996 HelperLocation::Memory => params.push(self.dst.opts.data_model.unwrap_memory().ptr()),
997 HelperLocation::StructField | HelperLocation::ArrayElement => todo!("CM+GC"),
998 }
999
1000 core_types.function(¶ms, &results)
1001 }
1002}
1003
1004impl HelperType {
1005 fn push_flat(&self, dst: &mut Vec<ValType>, types: &ComponentTypesBuilder) {
1006 match self.loc {
1007 HelperLocation::Stack => {
1008 for ty in self.opts.flat_types(&self.ty, types).unwrap() {
1009 dst.push((*ty).into());
1010 }
1011 }
1012 HelperLocation::Memory => {
1013 dst.push(self.opts.data_model.unwrap_memory().ptr());
1014 }
1015 HelperLocation::StructField | HelperLocation::ArrayElement => todo!("CM+GC"),
1016 }
1017 }
1018}