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