Skip to main content

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