use crate::component::*;
use crate::{EntityIndex, EntityRef, PrimaryMap, SignatureIndex};
use indexmap::IndexMap;
use std::collections::HashMap;
use std::hash::Hash;
use std::ops::Index;
#[derive(Default)]
#[allow(missing_docs)]
pub struct ComponentDfg {
pub import_types: PrimaryMap<ImportIndex, (String, TypeDef)>,
pub imports: PrimaryMap<RuntimeImportIndex, (ImportIndex, Vec<String>)>,
pub exports: IndexMap<String, Export>,
pub lowerings: Intern<LowerImportId, LowerImport>,
pub always_trap: Intern<AlwaysTrapId, SignatureIndex>,
pub reallocs: Intern<ReallocId, CoreDef>,
pub post_returns: Intern<PostReturnId, CoreDef>,
pub memories: Intern<MemoryId, CoreExport<MemoryIndex>>,
pub adapters: Intern<AdapterId, Adapter>,
pub transcoders: Intern<TranscoderId, Transcoder>,
pub instances: Intern<InstanceId, Instance>,
pub num_runtime_component_instances: u32,
pub adapter_modules: PrimaryMap<AdapterModuleId, (StaticModuleIndex, Vec<CoreDef>)>,
pub adapter_paritionings: PrimaryMap<AdapterId, (AdapterModuleId, EntityIndex)>,
}
macro_rules! id {
($(pub struct $name:ident(u32);)*) => ($(
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)]
#[allow(missing_docs)]
pub struct $name(u32);
cranelift_entity::entity_impl!($name);
)*)
}
id! {
pub struct InstanceId(u32);
pub struct LowerImportId(u32);
pub struct MemoryId(u32);
pub struct ReallocId(u32);
pub struct AdapterId(u32);
pub struct PostReturnId(u32);
pub struct AlwaysTrapId(u32);
pub struct AdapterModuleId(u32);
pub struct TranscoderId(u32);
}
#[allow(missing_docs)]
pub enum Instance {
Static(StaticModuleIndex, Box<[CoreDef]>),
Import(
RuntimeImportIndex,
IndexMap<String, IndexMap<String, CoreDef>>,
),
}
#[allow(missing_docs)]
pub enum Export {
LiftedFunction {
ty: TypeFuncIndex,
func: CoreDef,
options: CanonicalOptions,
},
ModuleStatic(StaticModuleIndex),
ModuleImport(RuntimeImportIndex),
Instance(IndexMap<String, Export>),
Type(TypeDef),
}
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
#[allow(missing_docs)]
pub enum CoreDef {
Export(CoreExport<EntityIndex>),
Lowered(LowerImportId),
AlwaysTrap(AlwaysTrapId),
InstanceFlags(RuntimeComponentInstanceIndex),
Transcoder(TranscoderId),
Adapter(AdapterId),
}
impl<T> From<CoreExport<T>> for CoreDef
where
EntityIndex: From<T>,
{
fn from(export: CoreExport<T>) -> CoreDef {
CoreDef::Export(export.map_index(|i| i.into()))
}
}
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
#[allow(missing_docs)]
pub struct CoreExport<T> {
pub instance: InstanceId,
pub item: ExportItem<T>,
}
impl<T> CoreExport<T> {
#[allow(missing_docs)]
pub fn map_index<U>(self, f: impl FnOnce(T) -> U) -> CoreExport<U> {
CoreExport {
instance: self.instance,
item: match self.item {
ExportItem::Index(i) => ExportItem::Index(f(i)),
ExportItem::Name(s) => ExportItem::Name(s),
},
}
}
}
#[derive(Hash, Eq, PartialEq, Clone)]
#[allow(missing_docs)]
pub struct LowerImport {
pub import: RuntimeImportIndex,
pub canonical_abi: SignatureIndex,
pub options: CanonicalOptions,
}
#[derive(Clone, Hash, Eq, PartialEq)]
#[allow(missing_docs)]
pub struct CanonicalOptions {
pub instance: RuntimeComponentInstanceIndex,
pub string_encoding: StringEncoding,
pub memory: Option<MemoryId>,
pub realloc: Option<ReallocId>,
pub post_return: Option<PostReturnId>,
}
#[derive(Clone, Hash, Eq, PartialEq)]
#[allow(missing_docs)]
pub struct Transcoder {
pub op: Transcode,
pub from: MemoryId,
pub from64: bool,
pub to: MemoryId,
pub to64: bool,
pub signature: SignatureIndex,
}
pub struct Intern<K: EntityRef, V> {
intern_map: HashMap<V, K>,
key_map: PrimaryMap<K, V>,
}
impl<K, V> Intern<K, V>
where
K: EntityRef,
{
pub fn push(&mut self, value: V) -> K {
self.key_map.push(value)
}
pub fn push_uniq(&mut self, value: V) -> K
where
V: Hash + Eq + Clone,
{
*self
.intern_map
.entry(value.clone())
.or_insert_with(|| self.key_map.push(value))
}
pub fn iter(&self) -> impl Iterator<Item = (K, &V)> {
self.key_map.iter()
}
}
impl<K: EntityRef, V> Index<K> for Intern<K, V> {
type Output = V;
fn index(&self, key: K) -> &V {
&self.key_map[key]
}
}
impl<K: EntityRef, V> Default for Intern<K, V> {
fn default() -> Intern<K, V> {
Intern {
intern_map: HashMap::new(),
key_map: PrimaryMap::new(),
}
}
}
impl ComponentDfg {
pub fn finish(self) -> Component {
let mut linearize = LinearizeDfg {
dfg: &self,
initializers: Vec::new(),
num_runtime_modules: 0,
runtime_memories: Default::default(),
runtime_post_return: Default::default(),
runtime_reallocs: Default::default(),
runtime_instances: Default::default(),
runtime_always_trap: Default::default(),
runtime_lowerings: Default::default(),
runtime_transcoders: Default::default(),
};
for (id, instance) in linearize.dfg.instances.key_map.iter() {
linearize.instantiate(id, instance);
}
let exports = self
.exports
.iter()
.map(|(name, export)| (name.clone(), linearize.export(export)))
.collect();
Component {
exports,
initializers: linearize.initializers,
num_runtime_modules: linearize.num_runtime_modules,
num_runtime_memories: linearize.runtime_memories.len() as u32,
num_runtime_post_returns: linearize.runtime_post_return.len() as u32,
num_runtime_reallocs: linearize.runtime_reallocs.len() as u32,
num_runtime_instances: linearize.runtime_instances.len() as u32,
num_always_trap: linearize.runtime_always_trap.len() as u32,
num_lowerings: linearize.runtime_lowerings.len() as u32,
num_transcoders: linearize.runtime_transcoders.len() as u32,
imports: self.imports,
import_types: self.import_types,
num_runtime_component_instances: self.num_runtime_component_instances,
}
}
}
struct LinearizeDfg<'a> {
dfg: &'a ComponentDfg,
initializers: Vec<GlobalInitializer>,
num_runtime_modules: u32,
runtime_memories: HashMap<MemoryId, RuntimeMemoryIndex>,
runtime_reallocs: HashMap<ReallocId, RuntimeReallocIndex>,
runtime_post_return: HashMap<PostReturnId, RuntimePostReturnIndex>,
runtime_instances: HashMap<RuntimeInstance, RuntimeInstanceIndex>,
runtime_always_trap: HashMap<AlwaysTrapId, RuntimeAlwaysTrapIndex>,
runtime_lowerings: HashMap<LowerImportId, LoweredIndex>,
runtime_transcoders: HashMap<TranscoderId, RuntimeTranscoderIndex>,
}
#[derive(Copy, Clone, Hash, Eq, PartialEq)]
enum RuntimeInstance {
Normal(InstanceId),
Adapter(AdapterModuleId),
}
impl LinearizeDfg<'_> {
fn instantiate(&mut self, instance: InstanceId, args: &Instance) {
log::trace!("creating instance {instance:?}");
let instantiation = match args {
Instance::Static(index, args) => InstantiateModule::Static(
*index,
args.iter().map(|def| self.core_def(def)).collect(),
),
Instance::Import(index, args) => InstantiateModule::Import(
*index,
args.iter()
.map(|(module, values)| {
let values = values
.iter()
.map(|(name, def)| (name.clone(), self.core_def(def)))
.collect();
(module.clone(), values)
})
.collect(),
),
};
let index = RuntimeInstanceIndex::new(self.runtime_instances.len());
self.initializers
.push(GlobalInitializer::InstantiateModule(instantiation));
let prev = self
.runtime_instances
.insert(RuntimeInstance::Normal(instance), index);
assert!(prev.is_none());
}
fn export(&mut self, export: &Export) -> info::Export {
match export {
Export::LiftedFunction { ty, func, options } => {
let func = self.core_def(func);
let options = self.options(options);
info::Export::LiftedFunction {
ty: *ty,
func,
options,
}
}
Export::ModuleStatic(i) => {
let index = RuntimeModuleIndex::from_u32(self.num_runtime_modules);
self.num_runtime_modules += 1;
self.initializers
.push(GlobalInitializer::SaveStaticModule(*i));
info::Export::Module(index)
}
Export::ModuleImport(i) => {
let index = RuntimeModuleIndex::from_u32(self.num_runtime_modules);
self.num_runtime_modules += 1;
self.initializers
.push(GlobalInitializer::SaveModuleImport(*i));
info::Export::Module(index)
}
Export::Instance(map) => info::Export::Instance(
map.iter()
.map(|(name, export)| (name.clone(), self.export(export)))
.collect(),
),
Export::Type(def) => info::Export::Type(*def),
}
}
fn options(&mut self, options: &CanonicalOptions) -> info::CanonicalOptions {
let memory = options.memory.map(|mem| self.runtime_memory(mem));
let realloc = options.realloc.map(|mem| self.runtime_realloc(mem));
let post_return = options.post_return.map(|mem| self.runtime_post_return(mem));
info::CanonicalOptions {
instance: options.instance,
string_encoding: options.string_encoding,
memory,
realloc,
post_return,
}
}
fn runtime_memory(&mut self, mem: MemoryId) -> RuntimeMemoryIndex {
self.intern(
mem,
|me| &mut me.runtime_memories,
|me, mem| me.core_export(&me.dfg.memories[mem]),
|index, export| GlobalInitializer::ExtractMemory(ExtractMemory { index, export }),
)
}
fn runtime_realloc(&mut self, realloc: ReallocId) -> RuntimeReallocIndex {
self.intern(
realloc,
|me| &mut me.runtime_reallocs,
|me, realloc| me.core_def(&me.dfg.reallocs[realloc]),
|index, def| GlobalInitializer::ExtractRealloc(ExtractRealloc { index, def }),
)
}
fn runtime_post_return(&mut self, post_return: PostReturnId) -> RuntimePostReturnIndex {
self.intern(
post_return,
|me| &mut me.runtime_post_return,
|me, post_return| me.core_def(&me.dfg.post_returns[post_return]),
|index, def| GlobalInitializer::ExtractPostReturn(ExtractPostReturn { index, def }),
)
}
fn core_def(&mut self, def: &CoreDef) -> info::CoreDef {
match def {
CoreDef::Export(e) => info::CoreDef::Export(self.core_export(e)),
CoreDef::AlwaysTrap(id) => info::CoreDef::AlwaysTrap(self.runtime_always_trap(*id)),
CoreDef::Lowered(id) => info::CoreDef::Lowered(self.runtime_lowering(*id)),
CoreDef::InstanceFlags(i) => info::CoreDef::InstanceFlags(*i),
CoreDef::Adapter(id) => info::CoreDef::Export(self.adapter(*id)),
CoreDef::Transcoder(id) => info::CoreDef::Transcoder(self.runtime_transcoder(*id)),
}
}
fn runtime_always_trap(&mut self, id: AlwaysTrapId) -> RuntimeAlwaysTrapIndex {
self.intern(
id,
|me| &mut me.runtime_always_trap,
|me, id| me.dfg.always_trap[id],
|index, canonical_abi| {
GlobalInitializer::AlwaysTrap(AlwaysTrap {
index,
canonical_abi,
})
},
)
}
fn runtime_lowering(&mut self, id: LowerImportId) -> LoweredIndex {
self.intern(
id,
|me| &mut me.runtime_lowerings,
|me, id| {
let info = &me.dfg.lowerings[id];
let options = me.options(&info.options);
(info.import, info.canonical_abi, options)
},
|index, (import, canonical_abi, options)| {
GlobalInitializer::LowerImport(info::LowerImport {
index,
import,
canonical_abi,
options,
})
},
)
}
fn runtime_transcoder(&mut self, id: TranscoderId) -> RuntimeTranscoderIndex {
self.intern(
id,
|me| &mut me.runtime_transcoders,
|me, id| {
let info = &me.dfg.transcoders[id];
(
info.op,
me.runtime_memory(info.from),
info.from64,
me.runtime_memory(info.to),
info.to64,
info.signature,
)
},
|index, (op, from, from64, to, to64, signature)| {
GlobalInitializer::Transcoder(info::Transcoder {
index,
op,
from,
from64,
to,
to64,
signature,
})
},
)
}
fn core_export<T>(&mut self, export: &CoreExport<T>) -> info::CoreExport<T>
where
T: Clone,
{
let instance = export.instance;
log::trace!("referencing export of {instance:?}");
info::CoreExport {
instance: self.runtime_instances[&RuntimeInstance::Normal(instance)],
item: export.item.clone(),
}
}
fn adapter(&mut self, adapter: AdapterId) -> info::CoreExport<EntityIndex> {
let (adapter_module, entity_index) = self.dfg.adapter_paritionings[adapter];
let instance = self.adapter_module(adapter_module);
info::CoreExport {
instance,
item: ExportItem::Index(entity_index),
}
}
fn adapter_module(&mut self, adapter_module: AdapterModuleId) -> RuntimeInstanceIndex {
self.intern(
RuntimeInstance::Adapter(adapter_module),
|me| &mut me.runtime_instances,
|me, _| {
log::debug!("instantiating {adapter_module:?}");
let (module_index, args) = &me.dfg.adapter_modules[adapter_module];
let args = args.iter().map(|arg| me.core_def(arg)).collect();
let instantiate = InstantiateModule::Static(*module_index, args);
GlobalInitializer::InstantiateModule(instantiate)
},
|_, init| init,
)
}
fn intern<K, V, T>(
&mut self,
key: K,
map: impl Fn(&mut Self) -> &mut HashMap<K, V>,
gen: impl FnOnce(&mut Self, K) -> T,
init: impl FnOnce(V, T) -> GlobalInitializer,
) -> V
where
K: Hash + Eq + Copy,
V: EntityRef,
{
if let Some(val) = map(self).get(&key) {
return val.clone();
}
let tmp = gen(self, key);
let index = V::new(map(self).len());
self.initializers.push(init(index, tmp));
let prev = map(self).insert(key, index);
assert!(prev.is_none());
index
}
}