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