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