Skip to main content

wasmtime_environ/
fact.rs

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