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