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