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