wasmtime_environ/component/
dfg.rs

1//! A dataflow-graph-like intermediate representation of a component
2//!
3//! This module contains `ComponentDfg` which is an intermediate step towards
4//! becoming a full-fledged `Component`. The main purpose for the existence of
5//! this representation of a component is to track dataflow between various
6//! items within a component and support edits to them after the initial inlined
7//! translation of a component.
8//!
9//! Currently fused adapters are represented with a core WebAssembly module
10//! which gets "injected" into the final component as-if the component already
11//! bundled it. In doing so the adapter modules need to be partitioned and
12//! inserted into the final sequence of modules to instantiate. While this is
13//! possible to do with a flat `GlobalInitializer` list it gets unwieldy really
14//! quickly especially when other translation features are added.
15//!
16//! This module is largely a duplicate of the `component::info` module in this
17//! crate. The hierarchy here uses `*Id` types instead of `*Index` types to
18//! represent that they don't have any necessary implicit ordering. Additionally
19//! nothing is kept in an ordered list and instead this is worked with in a
20//! general dataflow fashion where dependencies are walked during processing.
21//!
22//! The `ComponentDfg::finish` method will convert the dataflow graph to a
23//! linearized `GlobalInitializer` list which is intended to not be edited after
24//! it's created.
25//!
26//! The `ComponentDfg` is created as part of the `component::inline` phase of
27//! translation where the dataflow performed there allows identification of
28//! fused adapters, what arguments make their way to core wasm modules, etc.
29
30use crate::component::*;
31use crate::prelude::*;
32use crate::{EntityIndex, EntityRef, PrimaryMap, WasmValType};
33use anyhow::Result;
34use indexmap::IndexMap;
35use std::collections::HashMap;
36use std::hash::Hash;
37use std::ops::Index;
38use wasmparser::types::ComponentCoreModuleTypeId;
39use wasmtime_types::ModuleInternedTypeIndex;
40
41#[derive(Default)]
42#[allow(missing_docs)]
43pub struct ComponentDfg {
44    /// Same as `Component::import_types`
45    pub import_types: PrimaryMap<ImportIndex, (String, TypeDef)>,
46
47    /// Same as `Component::imports`
48    pub imports: PrimaryMap<RuntimeImportIndex, (ImportIndex, Vec<String>)>,
49
50    /// Same as `Component::exports`
51    pub exports: IndexMap<String, Export>,
52
53    /// All trampolines and their type signature which will need to get
54    /// compiled by Cranelift.
55    pub trampolines: Intern<TrampolineIndex, (ModuleInternedTypeIndex, Trampoline)>,
56
57    /// Know reallocation functions which are used by `lowerings` (e.g. will be
58    /// used by the host)
59    pub reallocs: Intern<ReallocId, CoreDef>,
60
61    /// Same as `reallocs`, but for post-return.
62    pub post_returns: Intern<PostReturnId, CoreDef>,
63
64    /// Same as `reallocs`, but for post-return.
65    pub memories: Intern<MemoryId, CoreExport<MemoryIndex>>,
66
67    /// Metadata about identified fused adapters.
68    ///
69    /// Note that this list is required to be populated in-order where the
70    /// "left" adapters cannot depend on "right" adapters. Currently this falls
71    /// out of the inlining pass of translation.
72    pub adapters: Intern<AdapterId, Adapter>,
73
74    /// Metadata about all known core wasm instances created.
75    ///
76    /// This is mostly an ordered list and is not deduplicated based on contents
77    /// unlike the items above. Creation of an `Instance` is side-effectful and
78    /// all instances here are always required to be created. These are
79    /// considered "roots" in dataflow.
80    pub instances: PrimaryMap<InstanceId, Instance>,
81
82    /// Number of component instances that were created during the inlining
83    /// phase (this is not edited after creation).
84    pub num_runtime_component_instances: u32,
85
86    /// Known adapter modules and how they are instantiated.
87    ///
88    /// This map is not filled in on the initial creation of a `ComponentDfg`.
89    /// Instead these modules are filled in by the `inline::adapt` phase where
90    /// adapter modules are identified and filled in here.
91    ///
92    /// The payload here is the static module index representing the core wasm
93    /// adapter module that was generated as well as the arguments to the
94    /// instantiation of the adapter module.
95    pub adapter_modules: PrimaryMap<AdapterModuleId, (StaticModuleIndex, Vec<CoreDef>)>,
96
97    /// Metadata about where adapters can be found within their respective
98    /// adapter modules.
99    ///
100    /// Like `adapter_modules` this is not filled on the initial creation of
101    /// `ComponentDfg` but rather is created alongside `adapter_modules` during
102    /// the `inline::adapt` phase of translation.
103    ///
104    /// The values here are the module that the adapter is present within along
105    /// as the core wasm index of the export corresponding to the lowered
106    /// version of the adapter.
107    pub adapter_paritionings: PrimaryMap<AdapterId, (AdapterModuleId, EntityIndex)>,
108
109    /// Defined resources in this component sorted by index with metadata about
110    /// each resource.
111    ///
112    /// Note that each index here is a unique resource, and that may mean it was
113    /// the same component instantiated twice for example.
114    pub resources: PrimaryMap<DefinedResourceIndex, Resource>,
115
116    /// Metadata about all imported resources into this component. This records
117    /// both how many imported resources there are (the size of this map) along
118    /// with what the corresponding runtime import is.
119    pub imported_resources: PrimaryMap<ResourceIndex, RuntimeImportIndex>,
120
121    /// The total number of resource tables that will be used by this component,
122    /// currently the number of unique `TypeResourceTableIndex` allocations for
123    /// this component.
124    pub num_resource_tables: usize,
125
126    /// An ordered list of side effects induced by instantiating this component.
127    ///
128    /// Currently all side effects are either instantiating core wasm modules or
129    /// declaring a resource. These side effects affect the dataflow processing
130    /// of this component by idnicating what order operations should be
131    /// performed during instantiation.
132    pub side_effects: Vec<SideEffect>,
133}
134
135/// Possible side effects that are possible with instantiating this component.
136pub enum SideEffect {
137    /// A core wasm instance was created.
138    ///
139    /// Instantiation is side-effectful due to the presence of constructs such
140    /// as traps and the core wasm `start` function which may call component
141    /// imports. Instantiation order from the original component must be done in
142    /// the same order.
143    Instance(InstanceId),
144
145    /// A resource was declared in this component.
146    ///
147    /// This is a bit less side-effectful than instantiation but this serves as
148    /// the order in which resources are initialized in a component with their
149    /// destructors. Destructors are loaded from core wasm instances (or
150    /// lowerings) which are produced by prior side-effectful operations.
151    Resource(DefinedResourceIndex),
152}
153
154macro_rules! id {
155    ($(pub struct $name:ident(u32);)*) => ($(
156        #[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)]
157        #[allow(missing_docs)]
158        pub struct $name(u32);
159        cranelift_entity::entity_impl!($name);
160    )*)
161}
162
163id! {
164    pub struct InstanceId(u32);
165    pub struct MemoryId(u32);
166    pub struct ReallocId(u32);
167    pub struct AdapterId(u32);
168    pub struct PostReturnId(u32);
169    pub struct AdapterModuleId(u32);
170}
171
172/// Same as `info::InstantiateModule`
173#[allow(missing_docs)]
174pub enum Instance {
175    Static(StaticModuleIndex, Box<[CoreDef]>),
176    Import(
177        RuntimeImportIndex,
178        IndexMap<String, IndexMap<String, CoreDef>>,
179    ),
180}
181
182/// Same as `info::Export`
183#[allow(missing_docs)]
184pub enum Export {
185    LiftedFunction {
186        ty: TypeFuncIndex,
187        func: CoreDef,
188        options: CanonicalOptions,
189    },
190    ModuleStatic {
191        ty: ComponentCoreModuleTypeId,
192        index: StaticModuleIndex,
193    },
194    ModuleImport {
195        ty: TypeModuleIndex,
196        import: RuntimeImportIndex,
197    },
198    Instance {
199        ty: TypeComponentInstanceIndex,
200        exports: IndexMap<String, Export>,
201    },
202    Type(TypeDef),
203}
204
205/// Same as `info::CoreDef`, except has an extra `Adapter` variant.
206#[derive(Debug, Clone, Hash, Eq, PartialEq)]
207#[allow(missing_docs)]
208pub enum CoreDef {
209    Export(CoreExport<EntityIndex>),
210    InstanceFlags(RuntimeComponentInstanceIndex),
211    Trampoline(TrampolineIndex),
212    /// This is a special variant not present in `info::CoreDef` which
213    /// represents that this definition refers to a fused adapter function. This
214    /// adapter is fully processed after the initial translation and
215    /// identificatino of adapters.
216    ///
217    /// During translation into `info::CoreDef` this variant is erased and
218    /// replaced by `info::CoreDef::Export` since adapters are always
219    /// represented as the exports of a core wasm instance.
220    Adapter(AdapterId),
221}
222
223impl<T> From<CoreExport<T>> for CoreDef
224where
225    EntityIndex: From<T>,
226{
227    fn from(export: CoreExport<T>) -> CoreDef {
228        CoreDef::Export(export.map_index(|i| i.into()))
229    }
230}
231
232/// Same as `info::CoreExport`
233#[derive(Debug, Clone, Hash, Eq, PartialEq)]
234#[allow(missing_docs)]
235pub struct CoreExport<T> {
236    pub instance: InstanceId,
237    pub item: ExportItem<T>,
238}
239
240impl<T> CoreExport<T> {
241    #[allow(missing_docs)]
242    pub fn map_index<U>(self, f: impl FnOnce(T) -> U) -> CoreExport<U> {
243        CoreExport {
244            instance: self.instance,
245            item: match self.item {
246                ExportItem::Index(i) => ExportItem::Index(f(i)),
247                ExportItem::Name(s) => ExportItem::Name(s),
248            },
249        }
250    }
251}
252
253/// Same as `info::Trampoline`
254#[derive(Clone, PartialEq, Eq, Hash)]
255#[allow(missing_docs)]
256pub enum Trampoline {
257    LowerImport {
258        import: RuntimeImportIndex,
259        options: CanonicalOptions,
260        lower_ty: TypeFuncIndex,
261    },
262    Transcoder {
263        op: Transcode,
264        from: MemoryId,
265        from64: bool,
266        to: MemoryId,
267        to64: bool,
268    },
269    AlwaysTrap,
270    ResourceNew(TypeResourceTableIndex),
271    ResourceRep(TypeResourceTableIndex),
272    ResourceDrop(TypeResourceTableIndex),
273    ResourceTransferOwn,
274    ResourceTransferBorrow,
275    ResourceEnterCall,
276    ResourceExitCall,
277}
278
279/// Same as `info::CanonicalOptions`
280#[derive(Clone, Hash, Eq, PartialEq)]
281#[allow(missing_docs)]
282pub struct CanonicalOptions {
283    pub instance: RuntimeComponentInstanceIndex,
284    pub string_encoding: StringEncoding,
285    pub memory: Option<MemoryId>,
286    pub realloc: Option<ReallocId>,
287    pub post_return: Option<PostReturnId>,
288}
289
290/// Same as `info::Resource`
291#[allow(missing_docs)]
292pub struct Resource {
293    pub rep: WasmValType,
294    pub dtor: Option<CoreDef>,
295    pub instance: RuntimeComponentInstanceIndex,
296}
297
298/// A helper structure to "intern" and deduplicate values of type `V` with an
299/// identifying key `K`.
300///
301/// Note that this can also be used where `V` can't be intern'd to represent a
302/// flat list of items.
303pub struct Intern<K: EntityRef, V> {
304    intern_map: HashMap<V, K>,
305    key_map: PrimaryMap<K, V>,
306}
307
308impl<K, V> Intern<K, V>
309where
310    K: EntityRef,
311{
312    /// Inserts the `value` specified into this set, returning either a fresh
313    /// key `K` if this value hasn't been seen before or otherwise returning the
314    /// previous `K` used to represent value.
315    ///
316    /// Note that this should only be used for component model items where the
317    /// creation of `value` is not side-effectful.
318    pub fn push(&mut self, value: V) -> K
319    where
320        V: Hash + Eq + Clone,
321    {
322        *self
323            .intern_map
324            .entry(value.clone())
325            .or_insert_with(|| self.key_map.push(value))
326    }
327
328    /// Returns an iterator of all the values contained within this set.
329    pub fn iter(&self) -> impl Iterator<Item = (K, &V)> {
330        self.key_map.iter()
331    }
332}
333
334impl<K: EntityRef, V> Index<K> for Intern<K, V> {
335    type Output = V;
336    fn index(&self, key: K) -> &V {
337        &self.key_map[key]
338    }
339}
340
341impl<K: EntityRef, V> Default for Intern<K, V> {
342    fn default() -> Intern<K, V> {
343        Intern {
344            intern_map: HashMap::new(),
345            key_map: PrimaryMap::new(),
346        }
347    }
348}
349
350impl ComponentDfg {
351    /// Consumes the intermediate `ComponentDfg` to produce a final `Component`
352    /// with a linear innitializer list.
353    pub fn finish(
354        self,
355        wasmtime_types: &mut ComponentTypesBuilder,
356        wasmparser_types: wasmparser::types::TypesRef<'_>,
357    ) -> Result<ComponentTranslation> {
358        let mut linearize = LinearizeDfg {
359            dfg: &self,
360            initializers: Vec::new(),
361            runtime_memories: Default::default(),
362            runtime_post_return: Default::default(),
363            runtime_reallocs: Default::default(),
364            runtime_instances: Default::default(),
365            num_lowerings: 0,
366            trampolines: Default::default(),
367            trampoline_defs: Default::default(),
368            trampoline_map: Default::default(),
369        };
370
371        // Handle all side effects of this component in the order that they're
372        // defined. This will, for example, process all instantiations necessary
373        // of core wasm modules.
374        for item in linearize.dfg.side_effects.iter() {
375            linearize.side_effect(item);
376        }
377
378        // Next the exports of the instance are handled which will likely end up
379        // creating some lowered imports, perhaps some saved modules, etc.
380        let mut export_items = PrimaryMap::new();
381        let mut exports = NameMap::default();
382        for (name, export) in self.exports.iter() {
383            let export =
384                linearize.export(export, &mut export_items, wasmtime_types, wasmparser_types)?;
385            exports.insert(name, &mut NameMapNoIntern, false, export)?;
386        }
387
388        // With all those pieces done the results of the dataflow-based
389        // linearization are recorded into the `Component`. The number of
390        // runtime values used for each index space is used from the `linearize`
391        // result.
392        Ok(ComponentTranslation {
393            trampolines: linearize.trampoline_defs,
394            component: Component {
395                exports,
396                export_items,
397                initializers: linearize.initializers,
398                trampolines: linearize.trampolines,
399                num_lowerings: linearize.num_lowerings,
400
401                num_runtime_memories: linearize.runtime_memories.len() as u32,
402                num_runtime_post_returns: linearize.runtime_post_return.len() as u32,
403                num_runtime_reallocs: linearize.runtime_reallocs.len() as u32,
404                num_runtime_instances: linearize.runtime_instances.len() as u32,
405                imports: self.imports,
406                import_types: self.import_types,
407                num_runtime_component_instances: self.num_runtime_component_instances,
408                num_resource_tables: self.num_resource_tables,
409                num_resources: (self.resources.len() + self.imported_resources.len()) as u32,
410                imported_resources: self.imported_resources,
411                defined_resource_instances: self
412                    .resources
413                    .iter()
414                    .map(|(_, r)| r.instance)
415                    .collect(),
416            },
417        })
418    }
419
420    /// Converts the provided defined index into a normal index, adding in the
421    /// number of imported resources.
422    pub fn resource_index(&self, defined: DefinedResourceIndex) -> ResourceIndex {
423        ResourceIndex::from_u32(defined.as_u32() + (self.imported_resources.len() as u32))
424    }
425}
426
427struct LinearizeDfg<'a> {
428    dfg: &'a ComponentDfg,
429    initializers: Vec<GlobalInitializer>,
430    trampolines: PrimaryMap<TrampolineIndex, ModuleInternedTypeIndex>,
431    trampoline_defs: PrimaryMap<TrampolineIndex, info::Trampoline>,
432    trampoline_map: HashMap<TrampolineIndex, TrampolineIndex>,
433    runtime_memories: HashMap<MemoryId, RuntimeMemoryIndex>,
434    runtime_reallocs: HashMap<ReallocId, RuntimeReallocIndex>,
435    runtime_post_return: HashMap<PostReturnId, RuntimePostReturnIndex>,
436    runtime_instances: HashMap<RuntimeInstance, RuntimeInstanceIndex>,
437    num_lowerings: u32,
438}
439
440#[derive(Copy, Clone, Hash, Eq, PartialEq)]
441enum RuntimeInstance {
442    Normal(InstanceId),
443    Adapter(AdapterModuleId),
444}
445
446impl LinearizeDfg<'_> {
447    fn side_effect(&mut self, effect: &SideEffect) {
448        match effect {
449            SideEffect::Instance(i) => {
450                self.instantiate(*i, &self.dfg.instances[*i]);
451            }
452            SideEffect::Resource(i) => {
453                self.resource(*i, &self.dfg.resources[*i]);
454            }
455        }
456    }
457
458    fn instantiate(&mut self, instance: InstanceId, args: &Instance) {
459        log::trace!("creating instance {instance:?}");
460        let instantiation = match args {
461            Instance::Static(index, args) => InstantiateModule::Static(
462                *index,
463                args.iter().map(|def| self.core_def(def)).collect(),
464            ),
465            Instance::Import(index, args) => InstantiateModule::Import(
466                *index,
467                args.iter()
468                    .map(|(module, values)| {
469                        let values = values
470                            .iter()
471                            .map(|(name, def)| (name.clone(), self.core_def(def)))
472                            .collect();
473                        (module.clone(), values)
474                    })
475                    .collect(),
476            ),
477        };
478        let index = RuntimeInstanceIndex::new(self.runtime_instances.len());
479        self.initializers
480            .push(GlobalInitializer::InstantiateModule(instantiation));
481        let prev = self
482            .runtime_instances
483            .insert(RuntimeInstance::Normal(instance), index);
484        assert!(prev.is_none());
485    }
486
487    fn resource(&mut self, index: DefinedResourceIndex, resource: &Resource) {
488        let dtor = resource.dtor.as_ref().map(|dtor| self.core_def(dtor));
489        self.initializers
490            .push(GlobalInitializer::Resource(info::Resource {
491                dtor,
492                index,
493                rep: resource.rep,
494                instance: resource.instance,
495            }));
496    }
497
498    fn export(
499        &mut self,
500        export: &Export,
501        items: &mut PrimaryMap<ExportIndex, info::Export>,
502        wasmtime_types: &mut ComponentTypesBuilder,
503        wasmparser_types: wasmparser::types::TypesRef<'_>,
504    ) -> Result<ExportIndex> {
505        let item = match export {
506            Export::LiftedFunction { ty, func, options } => {
507                let func = self.core_def(func);
508                let options = self.options(options);
509                info::Export::LiftedFunction {
510                    ty: *ty,
511                    func,
512                    options,
513                }
514            }
515            Export::ModuleStatic { ty, index } => info::Export::ModuleStatic {
516                ty: wasmtime_types.convert_module(wasmparser_types, *ty)?,
517                index: *index,
518            },
519            Export::ModuleImport { ty, import } => info::Export::ModuleImport {
520                ty: *ty,
521                import: *import,
522            },
523            Export::Instance { ty, exports } => info::Export::Instance {
524                ty: *ty,
525                exports: {
526                    let mut map = NameMap::default();
527                    for (name, export) in exports {
528                        let export =
529                            self.export(export, items, wasmtime_types, wasmparser_types)?;
530                        map.insert(name, &mut NameMapNoIntern, false, export)?;
531                    }
532                    map
533                },
534            },
535            Export::Type(def) => info::Export::Type(*def),
536        };
537        Ok(items.push(item))
538    }
539
540    fn options(&mut self, options: &CanonicalOptions) -> info::CanonicalOptions {
541        let memory = options.memory.map(|mem| self.runtime_memory(mem));
542        let realloc = options.realloc.map(|mem| self.runtime_realloc(mem));
543        let post_return = options.post_return.map(|mem| self.runtime_post_return(mem));
544        info::CanonicalOptions {
545            instance: options.instance,
546            string_encoding: options.string_encoding,
547            memory,
548            realloc,
549            post_return,
550        }
551    }
552
553    fn runtime_memory(&mut self, mem: MemoryId) -> RuntimeMemoryIndex {
554        self.intern(
555            mem,
556            |me| &mut me.runtime_memories,
557            |me, mem| me.core_export(&me.dfg.memories[mem]),
558            |index, export| GlobalInitializer::ExtractMemory(ExtractMemory { index, export }),
559        )
560    }
561
562    fn runtime_realloc(&mut self, realloc: ReallocId) -> RuntimeReallocIndex {
563        self.intern(
564            realloc,
565            |me| &mut me.runtime_reallocs,
566            |me, realloc| me.core_def(&me.dfg.reallocs[realloc]),
567            |index, def| GlobalInitializer::ExtractRealloc(ExtractRealloc { index, def }),
568        )
569    }
570
571    fn runtime_post_return(&mut self, post_return: PostReturnId) -> RuntimePostReturnIndex {
572        self.intern(
573            post_return,
574            |me| &mut me.runtime_post_return,
575            |me, post_return| me.core_def(&me.dfg.post_returns[post_return]),
576            |index, def| GlobalInitializer::ExtractPostReturn(ExtractPostReturn { index, def }),
577        )
578    }
579
580    fn core_def(&mut self, def: &CoreDef) -> info::CoreDef {
581        match def {
582            CoreDef::Export(e) => info::CoreDef::Export(self.core_export(e)),
583            CoreDef::InstanceFlags(i) => info::CoreDef::InstanceFlags(*i),
584            CoreDef::Adapter(id) => info::CoreDef::Export(self.adapter(*id)),
585            CoreDef::Trampoline(index) => info::CoreDef::Trampoline(self.trampoline(*index)),
586        }
587    }
588
589    fn trampoline(&mut self, index: TrampolineIndex) -> TrampolineIndex {
590        if let Some(idx) = self.trampoline_map.get(&index) {
591            return *idx;
592        }
593        let (signature, trampoline) = &self.dfg.trampolines[index];
594        let trampoline = match trampoline {
595            Trampoline::LowerImport {
596                import,
597                options,
598                lower_ty,
599            } => {
600                let index = LoweredIndex::from_u32(self.num_lowerings);
601                self.num_lowerings += 1;
602                self.initializers.push(GlobalInitializer::LowerImport {
603                    index,
604                    import: *import,
605                });
606                info::Trampoline::LowerImport {
607                    index,
608                    options: self.options(options),
609                    lower_ty: *lower_ty,
610                }
611            }
612            Trampoline::Transcoder {
613                op,
614                from,
615                from64,
616                to,
617                to64,
618            } => info::Trampoline::Transcoder {
619                op: *op,
620                from: self.runtime_memory(*from),
621                from64: *from64,
622                to: self.runtime_memory(*to),
623                to64: *to64,
624            },
625            Trampoline::AlwaysTrap => info::Trampoline::AlwaysTrap,
626            Trampoline::ResourceNew(ty) => info::Trampoline::ResourceNew(*ty),
627            Trampoline::ResourceDrop(ty) => info::Trampoline::ResourceDrop(*ty),
628            Trampoline::ResourceRep(ty) => info::Trampoline::ResourceRep(*ty),
629            Trampoline::ResourceTransferOwn => info::Trampoline::ResourceTransferOwn,
630            Trampoline::ResourceTransferBorrow => info::Trampoline::ResourceTransferBorrow,
631            Trampoline::ResourceEnterCall => info::Trampoline::ResourceEnterCall,
632            Trampoline::ResourceExitCall => info::Trampoline::ResourceExitCall,
633        };
634        let i1 = self.trampolines.push(*signature);
635        let i2 = self.trampoline_defs.push(trampoline);
636        assert_eq!(i1, i2);
637        self.trampoline_map.insert(index, i1);
638        i1
639    }
640
641    fn core_export<T>(&mut self, export: &CoreExport<T>) -> info::CoreExport<T>
642    where
643        T: Clone,
644    {
645        let instance = export.instance;
646        log::trace!("referencing export of {instance:?}");
647        info::CoreExport {
648            instance: self.runtime_instances[&RuntimeInstance::Normal(instance)],
649            item: export.item.clone(),
650        }
651    }
652
653    fn adapter(&mut self, adapter: AdapterId) -> info::CoreExport<EntityIndex> {
654        let (adapter_module, entity_index) = self.dfg.adapter_paritionings[adapter];
655
656        // Instantiates the adapter module if it hasn't already been
657        // instantiated or otherwise returns the index that the module was
658        // already instantiated at.
659        let instance = self.adapter_module(adapter_module);
660
661        // This adapter is always an export of the instance.
662        info::CoreExport {
663            instance,
664            item: ExportItem::Index(entity_index),
665        }
666    }
667
668    fn adapter_module(&mut self, adapter_module: AdapterModuleId) -> RuntimeInstanceIndex {
669        self.intern(
670            RuntimeInstance::Adapter(adapter_module),
671            |me| &mut me.runtime_instances,
672            |me, _| {
673                log::debug!("instantiating {adapter_module:?}");
674                let (module_index, args) = &me.dfg.adapter_modules[adapter_module];
675                let args = args.iter().map(|arg| me.core_def(arg)).collect();
676                let instantiate = InstantiateModule::Static(*module_index, args);
677                GlobalInitializer::InstantiateModule(instantiate)
678            },
679            |_, init| init,
680        )
681    }
682
683    /// Helper function to manage interning of results to avoid duplicate
684    /// initializers being inserted into the final list.
685    ///
686    /// * `key` - the key being referenced which is used to deduplicate.
687    /// * `map` - a closure to access the interning map on `Self`
688    /// * `gen` - a closure to generate an intermediate value with `Self` from
689    ///   `K`. This is only used if `key` hasn't previously been seen. This
690    ///   closure can recursively intern other values possibly.
691    /// * `init` - a closure to use the result of `gen` to create the final
692    ///   initializer now that the index `V` of the runtime item is known.
693    ///
694    /// This is used by all the other interning methods above to lazily append
695    /// initializers on-demand and avoid pushing more than one initializer at a
696    /// time.
697    fn intern<K, V, T>(
698        &mut self,
699        key: K,
700        map: impl Fn(&mut Self) -> &mut HashMap<K, V>,
701        gen: impl FnOnce(&mut Self, K) -> T,
702        init: impl FnOnce(V, T) -> GlobalInitializer,
703    ) -> V
704    where
705        K: Hash + Eq + Copy,
706        V: EntityRef,
707    {
708        if let Some(val) = map(self).get(&key) {
709            return *val;
710        }
711        let tmp = gen(self, key);
712        let index = V::new(map(self).len());
713        self.initializers.push(init(index, tmp));
714        let prev = map(self).insert(key, index);
715        assert!(prev.is_none());
716        index
717    }
718}