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