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