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