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_start_calls: HashMap<(Option<FuncIndex>, Option<FuncIndex>, Vec<ValType>), 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_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 previously set up
598    /// with `prepare-call`.
599    ///
600    /// For async-lowered imports `results` will be `[i32]`, and for
601    /// sync-lowered imports it'll be the function's normal return type.
602    ///
603    /// Note that this could potentially be combined with the `prepare-call`
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 `start-call` to have the
607    /// host do it.
608    fn import_start_call(
609        &mut self,
610        suffix: &str,
611        callback: Option<FuncIndex>,
612        post_return: Option<FuncIndex>,
613        results: &[ValType],
614    ) -> FuncIndex {
615        let key = (callback, post_return, results.to_vec());
616        self.import_simple_get_and_set(
617            "async",
618            &format!("[start-call]{suffix}"),
619            &[ValType::FUNCREF, ValType::I32, ValType::I32, ValType::I32],
620            results,
621            Import::StartCall {
622                callback: callback
623                    .map(|callback| self.imported_funcs.get(callback).unwrap().clone().unwrap()),
624                post_return: post_return.map(|post_return| {
625                    self.imported_funcs
626                        .get(post_return)
627                        .unwrap()
628                        .clone()
629                        .unwrap()
630                }),
631            },
632            |me| me.imported_start_calls.get(&key).copied(),
633            |me, v| assert!(me.imported_start_calls.insert(key.clone(), v).is_none()),
634        )
635    }
636
637    fn import_future_transfer(&mut self) -> FuncIndex {
638        self.import_simple(
639            "future",
640            "transfer",
641            &[ValType::I32; 3],
642            &[ValType::I32],
643            Import::FutureTransfer,
644            |me| &mut me.imported_future_transfer,
645        )
646    }
647
648    fn import_stream_transfer(&mut self) -> FuncIndex {
649        self.import_simple(
650            "stream",
651            "transfer",
652            &[ValType::I32; 3],
653            &[ValType::I32],
654            Import::StreamTransfer,
655            |me| &mut me.imported_stream_transfer,
656        )
657    }
658
659    fn import_error_context_transfer(&mut self) -> FuncIndex {
660        self.import_simple(
661            "error-context",
662            "transfer",
663            &[ValType::I32; 3],
664            &[ValType::I32],
665            Import::ErrorContextTransfer,
666            |me| &mut me.imported_error_context_transfer,
667        )
668    }
669
670    fn import_resource_transfer_own(&mut self) -> FuncIndex {
671        self.import_simple(
672            "resource",
673            "transfer-own",
674            &[ValType::I32, ValType::I32, ValType::I32],
675            &[ValType::I32],
676            Import::ResourceTransferOwn,
677            |me| &mut me.imported_resource_transfer_own,
678        )
679    }
680
681    fn import_resource_transfer_borrow(&mut self) -> FuncIndex {
682        self.import_simple(
683            "resource",
684            "transfer-borrow",
685            &[ValType::I32, ValType::I32, ValType::I32],
686            &[ValType::I32],
687            Import::ResourceTransferBorrow,
688            |me| &mut me.imported_resource_transfer_borrow,
689        )
690    }
691
692    fn import_enter_sync_call(&mut self) -> FuncIndex {
693        self.import_simple(
694            "async",
695            "enter-sync-call",
696            &[ValType::I32; 2],
697            &[],
698            Import::EnterSyncCall,
699            |me| &mut me.imported_enter_sync_call,
700        )
701    }
702
703    fn import_exit_sync_call(&mut self) -> FuncIndex {
704        self.import_simple(
705            "async",
706            "exit-sync-call",
707            &[],
708            &[],
709            Import::ExitSyncCall,
710            |me| &mut me.imported_exit_sync_call,
711        )
712    }
713
714    /// Imports the `context.get` intrinsic for the `slot`th context slot.
715    fn import_context_get(&mut self, slot: usize) -> FuncIndex {
716        let intrinsic = match slot {
717            0 => UnsafeIntrinsic::ContextGetI32_0,
718            1 => UnsafeIntrinsic::ContextGetI32_1,
719            _ => unreachable!(),
720        };
721        self.import_unsafe_intrinsic(intrinsic, &format!("get{slot}"))
722    }
723
724    /// Imports the `context.set` intrinsic for the `slot`th context slot.
725    fn import_context_set(&mut self, slot: usize) -> FuncIndex {
726        let intrinsic = match slot {
727            0 => UnsafeIntrinsic::ContextSetI32_0,
728            1 => UnsafeIntrinsic::ContextSetI32_1,
729            _ => unreachable!(),
730        };
731        self.import_unsafe_intrinsic(intrinsic, &format!("set{slot}"))
732    }
733
734    fn import_unsafe_intrinsic(&mut self, intrinsic: UnsafeIntrinsic, name: &str) -> FuncIndex {
735        let map = |ty: &WasmValType| match ty {
736            crate::WasmValType::I32 => ValType::I32,
737            crate::WasmValType::I64 => ValType::I64,
738            crate::WasmValType::F32 => ValType::F32,
739            crate::WasmValType::F64 => ValType::F64,
740            crate::WasmValType::V128 => ValType::V128,
741            crate::WasmValType::Ref(_) => unreachable!(),
742        };
743        let params = intrinsic.core_params().iter().map(map).collect::<Vec<_>>();
744        let results = intrinsic.core_results().iter().map(map).collect::<Vec<_>>();
745
746        self.import_simple_get_and_set(
747            "context",
748            name,
749            &params,
750            &results,
751            Import::UnsafeIntrinsic(intrinsic),
752            |me| me.imported_unsafe_intrinsics.get(&intrinsic).copied(),
753            |me, idx| {
754                me.imported_unsafe_intrinsics.insert(intrinsic, idx);
755            },
756        )
757    }
758
759    fn import_trap(&mut self, trap: Trap) -> FuncIndex {
760        let name = format!("trap{}", trap as u8);
761        self.import_simple_get_and_set(
762            "runtime",
763            &name,
764            &[],
765            &[],
766            Import::Trap(trap),
767            |me| me.imported_traps.get(&trap).copied(),
768            |me, idx| {
769                me.imported_traps.insert(trap, idx);
770            },
771        )
772    }
773
774    fn translate_helper(&mut self, helper: Helper) -> FunctionId {
775        *self.helper_funcs.entry(helper).or_insert_with(|| {
776            // Generate a fresh `Function` with a unique id for what we're about to
777            // generate.
778            let ty = helper.core_type(self.types, &mut self.core_types);
779            let id = self.funcs.push(Function::new(None, ty));
780            self.helper_worklist.push((id, helper));
781            id
782        })
783    }
784
785    /// Encodes this module into a WebAssembly binary.
786    pub fn encode(&mut self) -> Vec<u8> {
787        // Build the function/export sections of the wasm module in a first pass
788        // which will assign a final `FuncIndex` to all functions defined in
789        // `self.funcs`.
790        let mut funcs = FunctionSection::new();
791        let mut exports = ExportSection::new();
792        let mut id_to_index = PrimaryMap::<FunctionId, FuncIndex>::new();
793        for (id, func) in self.funcs.iter() {
794            assert!(func.filled_in);
795            let idx = FuncIndex::from_u32(self.imported_funcs.next_key().as_u32() + id.as_u32());
796            let id2 = id_to_index.push(idx);
797            assert_eq!(id2, id);
798
799            funcs.function(func.ty);
800
801            if let Some(name) = &func.export {
802                exports.export(name, ExportKind::Func, idx.as_u32());
803            }
804        }
805        for (idx, name) in &self.exports {
806            exports.export(name, ExportKind::Func, *idx);
807        }
808
809        // With all functions numbered the fragments of the body of each
810        // function can be assigned into one final adapter function.
811        let mut code = CodeSection::new();
812        for (_, func) in self.funcs.iter() {
813            let mut body = Vec::new();
814
815            // Encode all locals used for this function
816            func.locals.len().encode(&mut body);
817            for (count, ty) in func.locals.iter() {
818                count.encode(&mut body);
819                ty.encode(&mut body);
820            }
821
822            // Then encode each "chunk" of a body which may have optional traps
823            // specified within it. Traps get offset by the current length of
824            // the body and otherwise our `Call` instructions are "relocated"
825            // here to the final function index.
826            for chunk in func.body.iter() {
827                match chunk {
828                    Body::Raw(code) => {
829                        body.extend_from_slice(code);
830                    }
831                    Body::Call(id) => {
832                        Instruction::Call(id_to_index[*id].as_u32()).encode(&mut body);
833                    }
834                    Body::RefFunc(id) => {
835                        Instruction::RefFunc(id_to_index[*id].as_u32()).encode(&mut body);
836                    }
837                }
838            }
839            code.raw(&body);
840        }
841
842        let mut result = wasm_encoder::Module::new();
843        result.section(&self.core_types.section);
844        result.section(&self.core_imports);
845        result.section(&funcs);
846        result.section(&exports);
847        result.section(&code);
848        result.finish()
849    }
850
851    /// Returns the imports that were used, in order, to create this adapter
852    /// module.
853    pub fn imports(&self) -> &[Import] {
854        &self.imports
855    }
856}
857
858/// Possible imports into an adapter module.
859#[derive(Clone)]
860pub enum Import {
861    /// A definition required in the configuration of an `Adapter`.
862    CoreDef(CoreDef),
863    /// A transcoding function from the host to convert between string encodings.
864    Transcode {
865        /// The transcoding operation this performs.
866        op: Transcode,
867        /// The memory being read
868        from: CoreDef,
869        /// Whether or not `from` is a 64-bit memory
870        from64: bool,
871        /// The memory being written
872        to: CoreDef,
873        /// Whether or not `to` is a 64-bit memory
874        to64: bool,
875    },
876    /// Transfers an owned resource from one table to another.
877    ResourceTransferOwn,
878    /// Transfers a borrowed resource from one table to another.
879    ResourceTransferBorrow,
880    /// An intrinsic used by FACT-generated modules to begin a call involving
881    /// an async-lowered import and/or an async-lifted export.
882    PrepareCall {
883        /// The memory used to verify that the memory specified for the
884        /// `task.return` that is called at runtime (if any) matches the one
885        /// specified in the lifted export.
886        memory: Option<CoreDef>,
887    },
888    /// An intrinsic used by FACT-generated modules to start a call previously
889    /// set up with `PrepareCall`.
890    StartCall {
891        /// The callee's callback function, if any.
892        callback: Option<CoreDef>,
893
894        /// The callee's post-return function, if any.
895        post_return: Option<CoreDef>,
896    },
897    /// An intrinisic used by FACT-generated modules to (partially or entirely) transfer
898    /// ownership of a `future`.
899    FutureTransfer,
900    /// An intrinisic used by FACT-generated modules to (partially or entirely) transfer
901    /// ownership of a `stream`.
902    StreamTransfer,
903    /// An intrinisic used by FACT-generated modules to (partially or entirely) transfer
904    /// ownership of an `error-context`.
905    ErrorContextTransfer,
906    /// An intrinsic for trapping the instance with a specific trap code.
907    Trap(Trap),
908    /// An intrinsic used by FACT-generated modules to check whether an instance
909    /// may be entered for a sync-to-sync call and push a task onto the stack if
910    /// so.
911    EnterSyncCall,
912    /// An intrinsic used by FACT-generated modules to pop the task previously
913    /// pushed by `EnterSyncCall`.
914    ExitSyncCall,
915    /// An unsafe intrinsic, such as reading/writing `context.{get,set}` slots.
916    UnsafeIntrinsic(UnsafeIntrinsic),
917}
918
919impl Options {
920    fn flat_types<'a>(
921        &self,
922        ty: &InterfaceType,
923        types: &'a ComponentTypesBuilder,
924    ) -> Option<&'a [FlatType]> {
925        let flat = types.flat_types(ty)?;
926        match self.data_model {
927            DataModel::Gc {} => todo!("CM+GC"),
928            DataModel::LinearMemory(mem_opts) => Some(if mem_opts.memory64() {
929                flat.memory64
930            } else {
931                flat.memory32
932            }),
933        }
934    }
935}
936
937/// Temporary index which is not the same as `FuncIndex`.
938///
939/// This represents the nth generated function in the adapter module where the
940/// final index of the function is not known at the time of generation since
941/// more imports may be discovered (specifically string transcoders).
942#[derive(Debug, Copy, Clone, PartialEq, Eq)]
943struct FunctionId(u32);
944cranelift_entity::entity_impl!(FunctionId);
945
946/// A generated function to be added to an adapter module.
947///
948/// At least one function is created per-adapter and depending on the type
949/// hierarchy multiple functions may be generated per-adapter.
950struct Function {
951    /// Whether or not the `body` has been finished.
952    ///
953    /// Functions are added to a `Module` before they're defined so this is used
954    /// to assert that the function was in fact actually filled in by the
955    /// time we reach `Module::encode`.
956    filled_in: bool,
957
958    /// The type signature that this function has, as an index into the core
959    /// wasm type index space of the generated adapter module.
960    ty: u32,
961
962    /// The locals that are used by this function, organized by the number of
963    /// types of each local.
964    locals: Vec<(u32, ValType)>,
965
966    /// If specified, the export name of this function.
967    export: Option<String>,
968
969    /// The contents of the function.
970    ///
971    /// See `Body` for more information, and the `Vec` here represents the
972    /// concatenation of all the `Body` fragments.
973    body: Vec<Body>,
974}
975
976/// Representation of a fragment of the body of a core wasm function generated
977/// for adapters.
978///
979/// This variant comes in one of two flavors:
980///
981/// 1. First a `Raw` variant is used to contain general instructions for the
982///    wasm function. This is populated by `Compiler::instruction` primarily.
983///
984/// 2. A `Call` instruction variant for a `FunctionId` where the final
985///    `FuncIndex` isn't known until emission time.
986///
987/// The purpose of this representation is the `Body::Call` variant. This can't
988/// be encoded as an instruction when it's generated due to not knowing the
989/// final index of the function being called. During `Module::encode`, however,
990/// all indices are known and `Body::Call` is turned into a final
991/// `Instruction::Call`.
992///
993/// One other possible representation in the future would be to encode a `Call`
994/// instruction with a 5-byte leb to fill in later, but for now this felt
995/// easier to represent. A 5-byte leb may be more efficient at compile-time if
996/// necessary, however.
997enum Body {
998    Raw(Vec<u8>),
999    Call(FunctionId),
1000    RefFunc(FunctionId),
1001}
1002
1003impl Function {
1004    fn new(export: Option<String>, ty: u32) -> Function {
1005        Function {
1006            filled_in: false,
1007            ty,
1008            locals: Vec::new(),
1009            export,
1010            body: Vec::new(),
1011        }
1012    }
1013}
1014
1015impl Helper {
1016    fn core_type(
1017        &self,
1018        types: &ComponentTypesBuilder,
1019        core_types: &mut core_types::CoreTypes,
1020    ) -> u32 {
1021        let mut params = Vec::new();
1022        let mut results = Vec::new();
1023        // The source type being translated is always pushed onto the
1024        // parameters first, either a pointer for memory or its flat
1025        // representation.
1026        self.src.push_flat(&mut params, types);
1027
1028        // The destination type goes into the parameter list if it's from
1029        // memory or otherwise is the result of the function itself for a
1030        // stack-based representation.
1031        match self.dst.loc {
1032            HelperLocation::Stack => self.dst.push_flat(&mut results, types),
1033            HelperLocation::Memory => params.push(self.dst.opts.data_model.unwrap_memory().ptr()),
1034            HelperLocation::StructField | HelperLocation::ArrayElement => todo!("CM+GC"),
1035        }
1036
1037        core_types.function(&params, &results)
1038    }
1039}
1040
1041impl HelperType {
1042    fn push_flat(&self, dst: &mut Vec<ValType>, types: &ComponentTypesBuilder) {
1043        match self.loc {
1044            HelperLocation::Stack => {
1045                for ty in self.opts.flat_types(&self.ty, types).unwrap() {
1046                    dst.push((*ty).into());
1047                }
1048            }
1049            HelperLocation::Memory => {
1050                dst.push(self.opts.data_model.unwrap_memory().ptr());
1051            }
1052            HelperLocation::StructField | HelperLocation::ArrayElement => todo!("CM+GC"),
1053        }
1054    }
1055}