wasmtime_environ/
fact.rs

1//! Wasmtime's Fused Adapter Compiler of Trampolines (FACT)
2//!
3//! This module contains a compiler which emits trampolines to implement fused
4//! adapters for the component model. A fused adapter is when a core wasm
5//! function is lifted from one component instance and then lowered into another
6//! component instance. This communication between components is well-defined by
7//! the spec and ends up creating what's called a "fused adapter".
8//!
9//! Adapters are currently implemented with WebAssembly modules. This submodule
10//! will generate a core wasm binary which contains the adapters specified
11//! during compilation. The actual wasm is then later processed by standard
12//! paths in Wasmtime to create native machine code and runtime representations
13//! of modules.
14//!
15//! Note that identification of precisely what goes into an adapter module is
16//! not handled in this file, instead that's all done in `translate/adapt.rs`.
17//! Otherwise this module is only responsible for taking a set of adapters and
18//! their imports and then generating a core wasm module to implement all of
19//! that.
20
21use crate::component::dfg::CoreDef;
22use crate::component::{
23    Adapter, AdapterOptions as AdapterOptionsDfg, ComponentTypesBuilder, FlatType, InterfaceType,
24    StringEncoding, Transcode, TypeFuncIndex,
25};
26use crate::fact::transcode::Transcoder;
27use crate::prelude::*;
28use crate::{EntityRef, FuncIndex, GlobalIndex, MemoryIndex, PrimaryMap};
29use std::borrow::Cow;
30use std::collections::HashMap;
31use wasm_encoder::*;
32
33mod core_types;
34mod signature;
35mod trampoline;
36mod transcode;
37mod traps;
38
39/// Representation of an adapter module.
40pub struct Module<'a> {
41    /// Whether or not debug code is inserted into the adapters themselves.
42    debug: bool,
43    /// Type information from the creator of this `Module`
44    types: &'a ComponentTypesBuilder,
45
46    /// Core wasm type section that's incrementally built
47    core_types: core_types::CoreTypes,
48
49    /// Core wasm import section which is built as adapters are inserted. Note
50    /// that imports here are intern'd to avoid duplicate imports of the same
51    /// item.
52    core_imports: ImportSection,
53    /// Final list of imports that this module ended up using, in the same order
54    /// as the imports in the import section.
55    imports: Vec<Import>,
56    /// Intern'd imports and what index they were assigned. Note that this map
57    /// covers all the index spaces for imports, not just one.
58    imported: HashMap<CoreDef, usize>,
59    /// Intern'd transcoders and what index they were assigned.
60    imported_transcoders: HashMap<Transcoder, FuncIndex>,
61
62    /// Cached versions of imported trampolines for working with resources.
63    imported_resource_transfer_own: Option<FuncIndex>,
64    imported_resource_transfer_borrow: Option<FuncIndex>,
65    imported_resource_enter_call: Option<FuncIndex>,
66    imported_resource_exit_call: Option<FuncIndex>,
67
68    // Current status of index spaces from the imports generated so far.
69    imported_funcs: PrimaryMap<FuncIndex, Option<CoreDef>>,
70    imported_memories: PrimaryMap<MemoryIndex, CoreDef>,
71    imported_globals: PrimaryMap<GlobalIndex, CoreDef>,
72
73    funcs: PrimaryMap<FunctionId, Function>,
74    helper_funcs: HashMap<Helper, FunctionId>,
75    helper_worklist: Vec<(FunctionId, Helper)>,
76}
77
78struct AdapterData {
79    /// Export name of this adapter
80    name: String,
81    /// Options specified during the `canon lift` operation
82    lift: AdapterOptions,
83    /// Options specified during the `canon lower` operation
84    lower: AdapterOptions,
85    /// The core wasm function that this adapter will be calling (the original
86    /// function that was `canon lift`'d)
87    callee: FuncIndex,
88    /// FIXME(#4185) should be plumbed and handled as part of the new reentrance
89    /// rules not yet implemented here.
90    called_as_export: bool,
91}
92
93/// Configuration options which apply at the "global adapter" level.
94///
95/// These options are typically unique per-adapter and generally aren't needed
96/// when translating recursive types within an adapter.
97struct AdapterOptions {
98    /// The ascribed type of this adapter.
99    ty: TypeFuncIndex,
100    /// The global that represents the instance flags for where this adapter
101    /// came from.
102    flags: GlobalIndex,
103    /// The configured post-return function, if any.
104    post_return: Option<FuncIndex>,
105    /// Other, more general, options configured.
106    options: Options,
107}
108
109/// This type is split out of `AdapterOptions` and is specifically used to
110/// deduplicate translation functions within a module. Consequently this has
111/// as few fields as possible to minimize the number of functions generated
112/// within an adapter module.
113#[derive(PartialEq, Eq, Hash, Copy, Clone)]
114struct Options {
115    /// The encoding that strings use from this adapter.
116    string_encoding: StringEncoding,
117    /// Whether or not the `memory` field, if present, is a 64-bit memory.
118    memory64: bool,
119    /// An optionally-specified memory where values may travel through for
120    /// types like lists.
121    memory: Option<MemoryIndex>,
122    /// An optionally-specified function to be used to allocate space for
123    /// types such as strings as they go into a module.
124    realloc: Option<FuncIndex>,
125}
126
127enum Context {
128    Lift,
129    Lower,
130}
131
132/// Representation of a "helper function" which may be generated as part of
133/// generating an adapter trampoline.
134///
135/// Helper functions are created when inlining the translation for a type in its
136/// entirety would make a function excessively large. This is currently done via
137/// a simple fuel/cost heuristic based on the type being translated but may get
138/// fancier over time.
139#[derive(Copy, Clone, PartialEq, Eq, Hash)]
140struct Helper {
141    /// Metadata about the source type of what's being translated.
142    src: HelperType,
143    /// Metadata about the destination type which is being translated to.
144    dst: HelperType,
145}
146
147/// Information about a source or destination type in a `Helper` which is
148/// generated.
149#[derive(Copy, Clone, PartialEq, Eq, Hash)]
150struct HelperType {
151    /// The concrete type being translated.
152    ty: InterfaceType,
153    /// The configuration options (memory, etc) for the adapter.
154    opts: Options,
155    /// Where the type is located (either the stack or in memory)
156    loc: HelperLocation,
157}
158
159/// Where a `HelperType` is located, dictating the signature of the helper
160/// function.
161#[derive(Copy, Clone, PartialEq, Eq, Hash)]
162enum HelperLocation {
163    /// Located on the stack in wasm locals.
164    Stack,
165    /// Located in linear memory as configured by `opts`.
166    Memory,
167}
168
169impl<'a> Module<'a> {
170    /// Creates an empty module.
171    pub fn new(types: &'a ComponentTypesBuilder, debug: bool) -> Module<'a> {
172        Module {
173            debug,
174            types,
175            core_types: Default::default(),
176            core_imports: Default::default(),
177            imported: Default::default(),
178            imports: Default::default(),
179            imported_transcoders: Default::default(),
180            imported_funcs: PrimaryMap::new(),
181            imported_memories: PrimaryMap::new(),
182            imported_globals: PrimaryMap::new(),
183            funcs: PrimaryMap::new(),
184            helper_funcs: HashMap::new(),
185            helper_worklist: Vec::new(),
186            imported_resource_transfer_own: None,
187            imported_resource_transfer_borrow: None,
188            imported_resource_enter_call: None,
189            imported_resource_exit_call: None,
190        }
191    }
192
193    /// Registers a new adapter within this adapter module.
194    ///
195    /// The `name` provided is the export name of the adapter from the final
196    /// module, and `adapter` contains all metadata necessary for compilation.
197    pub fn adapt(&mut self, name: &str, adapter: &Adapter) {
198        // Import any items required by the various canonical options
199        // (memories, reallocs, etc)
200        let mut lift = self.import_options(adapter.lift_ty, &adapter.lift_options);
201        let lower = self.import_options(adapter.lower_ty, &adapter.lower_options);
202
203        // Lowering options are not allowed to specify post-return as per the
204        // current canonical abi specification.
205        assert!(adapter.lower_options.post_return.is_none());
206
207        // Import the core wasm function which was lifted using its appropriate
208        // signature since the exported function this adapter generates will
209        // call the lifted function.
210        let signature = self.types.signature(&lift, Context::Lift);
211        let ty = self
212            .core_types
213            .function(&signature.params, &signature.results);
214        let callee = self.import_func("callee", name, ty, adapter.func.clone());
215
216        // Handle post-return specifically here where we have `core_ty` and the
217        // results of `core_ty` are the parameters to the post-return function.
218        lift.post_return = adapter.lift_options.post_return.as_ref().map(|func| {
219            let ty = self.core_types.function(&signature.results, &[]);
220            self.import_func("post_return", name, ty, func.clone())
221        });
222
223        // This will internally create the adapter as specified and append
224        // anything necessary to `self.funcs`.
225        trampoline::compile(
226            self,
227            &AdapterData {
228                name: name.to_string(),
229                lift,
230                lower,
231                callee,
232                // FIXME(#4185) should be plumbed and handled as part of the new
233                // reentrance rules not yet implemented here.
234                called_as_export: true,
235            },
236        );
237
238        while let Some((result, helper)) = self.helper_worklist.pop() {
239            trampoline::compile_helper(self, result, helper);
240        }
241    }
242
243    fn import_options(&mut self, ty: TypeFuncIndex, options: &AdapterOptionsDfg) -> AdapterOptions {
244        let AdapterOptionsDfg {
245            instance,
246            string_encoding,
247            memory,
248            memory64,
249            realloc,
250            post_return: _, // handled above
251        } = options;
252        let flags = self.import_global(
253            "flags",
254            &format!("instance{}", instance.as_u32()),
255            GlobalType {
256                val_type: ValType::I32,
257                mutable: true,
258                shared: false,
259            },
260            CoreDef::InstanceFlags(*instance),
261        );
262        let memory = memory.as_ref().map(|memory| {
263            self.import_memory(
264                "memory",
265                &format!("m{}", self.imported_memories.len()),
266                MemoryType {
267                    minimum: 0,
268                    maximum: None,
269                    shared: false,
270                    memory64: *memory64,
271                    page_size_log2: None,
272                },
273                memory.clone().into(),
274            )
275        });
276        let realloc = realloc.as_ref().map(|func| {
277            let ptr = if *memory64 {
278                ValType::I64
279            } else {
280                ValType::I32
281            };
282            let ty = self.core_types.function(&[ptr, ptr, ptr, ptr], &[ptr]);
283            self.import_func(
284                "realloc",
285                &format!("f{}", self.imported_funcs.len()),
286                ty,
287                func.clone(),
288            )
289        });
290
291        AdapterOptions {
292            ty,
293            flags,
294            post_return: None,
295            options: Options {
296                string_encoding: *string_encoding,
297                memory64: *memory64,
298                memory,
299                realloc,
300            },
301        }
302    }
303
304    fn import_func(&mut self, module: &str, name: &str, ty: u32, def: CoreDef) -> FuncIndex {
305        self.import(module, name, EntityType::Function(ty), def, |m| {
306            &mut m.imported_funcs
307        })
308    }
309
310    fn import_global(
311        &mut self,
312        module: &str,
313        name: &str,
314        ty: GlobalType,
315        def: CoreDef,
316    ) -> GlobalIndex {
317        self.import(module, name, EntityType::Global(ty), def, |m| {
318            &mut m.imported_globals
319        })
320    }
321
322    fn import_memory(
323        &mut self,
324        module: &str,
325        name: &str,
326        ty: MemoryType,
327        def: CoreDef,
328    ) -> MemoryIndex {
329        self.import(module, name, EntityType::Memory(ty), def, |m| {
330            &mut m.imported_memories
331        })
332    }
333
334    fn import<K: EntityRef, V: From<CoreDef>>(
335        &mut self,
336        module: &str,
337        name: &str,
338        ty: EntityType,
339        def: CoreDef,
340        map: impl FnOnce(&mut Self) -> &mut PrimaryMap<K, V>,
341    ) -> K {
342        if let Some(prev) = self.imported.get(&def) {
343            return K::new(*prev);
344        }
345        let idx = map(self).push(def.clone().into());
346        self.core_imports.import(module, name, ty);
347        self.imported.insert(def.clone(), idx.index());
348        self.imports.push(Import::CoreDef(def));
349        idx
350    }
351
352    fn import_transcoder(&mut self, transcoder: transcode::Transcoder) -> FuncIndex {
353        *self
354            .imported_transcoders
355            .entry(transcoder)
356            .or_insert_with(|| {
357                // Add the import to the core wasm import section...
358                let name = transcoder.name();
359                let ty = transcoder.ty(&mut self.core_types);
360                self.core_imports.import("transcode", &name, ty);
361
362                // ... and also record the metadata for what this import
363                // corresponds to.
364                let from = self.imported_memories[transcoder.from_memory].clone();
365                let to = self.imported_memories[transcoder.to_memory].clone();
366                self.imports.push(Import::Transcode {
367                    op: transcoder.op,
368                    from,
369                    from64: transcoder.from_memory64,
370                    to,
371                    to64: transcoder.to_memory64,
372                });
373
374                self.imported_funcs.push(None)
375            })
376    }
377
378    fn import_simple(
379        &mut self,
380        module: &str,
381        name: &str,
382        params: &[ValType],
383        results: &[ValType],
384        import: Import,
385        get: impl Fn(&mut Self) -> &mut Option<FuncIndex>,
386    ) -> FuncIndex {
387        if let Some(idx) = get(self) {
388            return *idx;
389        }
390        let ty = self.core_types.function(params, results);
391        let ty = EntityType::Function(ty);
392        self.core_imports.import(module, name, ty);
393
394        self.imports.push(import);
395        let idx = self.imported_funcs.push(None);
396        *get(self) = Some(idx);
397        idx
398    }
399
400    fn import_resource_transfer_own(&mut self) -> FuncIndex {
401        self.import_simple(
402            "resource",
403            "transfer-own",
404            &[ValType::I32, ValType::I32, ValType::I32],
405            &[ValType::I32],
406            Import::ResourceTransferOwn,
407            |me| &mut me.imported_resource_transfer_own,
408        )
409    }
410
411    fn import_resource_transfer_borrow(&mut self) -> FuncIndex {
412        self.import_simple(
413            "resource",
414            "transfer-borrow",
415            &[ValType::I32, ValType::I32, ValType::I32],
416            &[ValType::I32],
417            Import::ResourceTransferBorrow,
418            |me| &mut me.imported_resource_transfer_borrow,
419        )
420    }
421
422    fn import_resource_enter_call(&mut self) -> FuncIndex {
423        self.import_simple(
424            "resource",
425            "enter-call",
426            &[],
427            &[],
428            Import::ResourceEnterCall,
429            |me| &mut me.imported_resource_enter_call,
430        )
431    }
432
433    fn import_resource_exit_call(&mut self) -> FuncIndex {
434        self.import_simple(
435            "resource",
436            "exit-call",
437            &[],
438            &[],
439            Import::ResourceExitCall,
440            |me| &mut me.imported_resource_exit_call,
441        )
442    }
443
444    fn translate_helper(&mut self, helper: Helper) -> FunctionId {
445        *self.helper_funcs.entry(helper).or_insert_with(|| {
446            // Generate a fresh `Function` with a unique id for what we're about to
447            // generate.
448            let ty = helper.core_type(self.types, &mut self.core_types);
449            let id = self.funcs.push(Function::new(None, ty));
450            self.helper_worklist.push((id, helper));
451            id
452        })
453    }
454
455    /// Encodes this module into a WebAssembly binary.
456    pub fn encode(&mut self) -> Vec<u8> {
457        // Build the function/export sections of the wasm module in a first pass
458        // which will assign a final `FuncIndex` to all functions defined in
459        // `self.funcs`.
460        let mut funcs = FunctionSection::new();
461        let mut exports = ExportSection::new();
462        let mut id_to_index = PrimaryMap::<FunctionId, FuncIndex>::new();
463        for (id, func) in self.funcs.iter() {
464            assert!(func.filled_in);
465            let idx = FuncIndex::from_u32(self.imported_funcs.next_key().as_u32() + id.as_u32());
466            let id2 = id_to_index.push(idx);
467            assert_eq!(id2, id);
468
469            funcs.function(func.ty);
470
471            if let Some(name) = &func.export {
472                exports.export(name, ExportKind::Func, idx.as_u32());
473            }
474        }
475
476        // With all functions numbered the fragments of the body of each
477        // function can be assigned into one final adapter function.
478        let mut code = CodeSection::new();
479        let mut traps = traps::TrapSection::default();
480        for (id, func) in self.funcs.iter() {
481            let mut func_traps = Vec::new();
482            let mut body = Vec::new();
483
484            // Encode all locals used for this function
485            func.locals.len().encode(&mut body);
486            for (count, ty) in func.locals.iter() {
487                count.encode(&mut body);
488                ty.encode(&mut body);
489            }
490
491            // Then encode each "chunk" of a body which may have optional traps
492            // specified within it. Traps get offset by the current length of
493            // the body and otherwise our `Call` instructions are "relocated"
494            // here to the final function index.
495            for chunk in func.body.iter() {
496                match chunk {
497                    Body::Raw(code, traps) => {
498                        let start = body.len();
499                        body.extend_from_slice(code);
500                        for (offset, trap) in traps {
501                            func_traps.push((start + offset, *trap));
502                        }
503                    }
504                    Body::Call(id) => {
505                        Instruction::Call(id_to_index[*id].as_u32()).encode(&mut body);
506                    }
507                }
508            }
509            code.raw(&body);
510            traps.append(id_to_index[id].as_u32(), func_traps);
511        }
512
513        let traps = traps.finish();
514
515        let mut result = wasm_encoder::Module::new();
516        result.section(&self.core_types.section);
517        result.section(&self.core_imports);
518        result.section(&funcs);
519        result.section(&exports);
520        result.section(&code);
521        if self.debug {
522            result.section(&CustomSection {
523                name: "wasmtime-trampoline-traps".into(),
524                data: Cow::Borrowed(&traps),
525            });
526        }
527        result.finish()
528    }
529
530    /// Returns the imports that were used, in order, to create this adapter
531    /// module.
532    pub fn imports(&self) -> &[Import] {
533        &self.imports
534    }
535}
536
537/// Possible imports into an adapter module.
538#[derive(Clone)]
539pub enum Import {
540    /// A definition required in the configuration of an `Adapter`.
541    CoreDef(CoreDef),
542    /// A transcoding function from the host to convert between string encodings.
543    Transcode {
544        /// The transcoding operation this performs.
545        op: Transcode,
546        /// The memory being read
547        from: CoreDef,
548        /// Whether or not `from` is a 64-bit memory
549        from64: bool,
550        /// The memory being written
551        to: CoreDef,
552        /// Whether or not `to` is a 64-bit memory
553        to64: bool,
554    },
555    /// Transfers an owned resource from one table to another.
556    ResourceTransferOwn,
557    /// Transfers a borrowed resource from one table to another.
558    ResourceTransferBorrow,
559    /// Sets up entry metadata for a borrow resources when a call starts.
560    ResourceEnterCall,
561    /// Tears down a previous entry and handles checking borrow-related
562    /// metadata.
563    ResourceExitCall,
564}
565
566impl Options {
567    fn ptr(&self) -> ValType {
568        if self.memory64 {
569            ValType::I64
570        } else {
571            ValType::I32
572        }
573    }
574
575    fn ptr_size(&self) -> u8 {
576        if self.memory64 {
577            8
578        } else {
579            4
580        }
581    }
582
583    fn flat_types<'a>(
584        &self,
585        ty: &InterfaceType,
586        types: &'a ComponentTypesBuilder,
587    ) -> Option<&'a [FlatType]> {
588        let flat = types.flat_types(ty)?;
589        Some(if self.memory64 {
590            flat.memory64
591        } else {
592            flat.memory32
593        })
594    }
595}
596
597/// Temporary index which is not the same as `FuncIndex`.
598///
599/// This represents the nth generated function in the adapter module where the
600/// final index of the function is not known at the time of generation since
601/// more imports may be discovered (specifically string transcoders).
602#[derive(Debug, Copy, Clone, PartialEq, Eq)]
603struct FunctionId(u32);
604cranelift_entity::entity_impl!(FunctionId);
605
606/// A generated function to be added to an adapter module.
607///
608/// At least one function is created per-adapter and depending on the type
609/// hierarchy multiple functions may be generated per-adapter.
610struct Function {
611    /// Whether or not the `body` has been finished.
612    ///
613    /// Functions are added to a `Module` before they're defined so this is used
614    /// to assert that the function was in fact actually filled in by the
615    /// time we reach `Module::encode`.
616    filled_in: bool,
617
618    /// The type signature that this function has, as an index into the core
619    /// wasm type index space of the generated adapter module.
620    ty: u32,
621
622    /// The locals that are used by this function, organized by the number of
623    /// types of each local.
624    locals: Vec<(u32, ValType)>,
625
626    /// If specified, the export name of this function.
627    export: Option<String>,
628
629    /// The contents of the function.
630    ///
631    /// See `Body` for more information, and the `Vec` here represents the
632    /// concatenation of all the `Body` fragments.
633    body: Vec<Body>,
634}
635
636/// Representation of a fragment of the body of a core wasm function generated
637/// for adapters.
638///
639/// This variant comes in one of two flavors:
640///
641/// 1. First a `Raw` variant is used to contain general instructions for the
642///    wasm function. This is populated by `Compiler::instruction` primarily.
643///    This also comes with a list of traps. and the byte offset within the
644///    first vector of where the trap information applies to.
645///
646/// 2. A `Call` instruction variant for a `FunctionId` where the final
647///    `FuncIndex` isn't known until emission time.
648///
649/// The purpose of this representation is the `Body::Call` variant. This can't
650/// be encoded as an instruction when it's generated due to not knowing the
651/// final index of the function being called. During `Module::encode`, however,
652/// all indices are known and `Body::Call` is turned into a final
653/// `Instruction::Call`.
654///
655/// One other possible representation in the future would be to encode a `Call`
656/// instruction with a 5-byte leb to fill in later, but for now this felt
657/// easier to represent. A 5-byte leb may be more efficient at compile-time if
658/// necessary, however.
659enum Body {
660    Raw(Vec<u8>, Vec<(usize, traps::Trap)>),
661    Call(FunctionId),
662}
663
664impl Function {
665    fn new(export: Option<String>, ty: u32) -> Function {
666        Function {
667            filled_in: false,
668            ty,
669            locals: Vec::new(),
670            export,
671            body: Vec::new(),
672        }
673    }
674}
675
676impl Helper {
677    fn core_type(
678        &self,
679        types: &ComponentTypesBuilder,
680        core_types: &mut core_types::CoreTypes,
681    ) -> u32 {
682        let mut params = Vec::new();
683        let mut results = Vec::new();
684        // The source type being translated is always pushed onto the
685        // parameters first, either a pointer for memory or its flat
686        // representation.
687        self.src.push_flat(&mut params, types);
688
689        // The destination type goes into the parameter list if it's from
690        // memory or otherwise is the result of the function itself for a
691        // stack-based representation.
692        match self.dst.loc {
693            HelperLocation::Stack => self.dst.push_flat(&mut results, types),
694            HelperLocation::Memory => params.push(self.dst.opts.ptr()),
695        }
696
697        core_types.function(&params, &results)
698    }
699}
700
701impl HelperType {
702    fn push_flat(&self, dst: &mut Vec<ValType>, types: &ComponentTypesBuilder) {
703        match self.loc {
704            HelperLocation::Stack => {
705                for ty in self.opts.flat_types(&self.ty, types).unwrap() {
706                    dst.push((*ty).into());
707                }
708            }
709            HelperLocation::Memory => {
710                dst.push(self.opts.ptr());
711            }
712        }
713    }
714}