Add dataflow processing to component translation for imports (#4205)
This commit enhances the processing of components to track all the dataflow for the processing of `canon.lower`'d functions. At the same time this fills out a few other missing details to component processing such as aliasing from some kinds of component instances and similar. The major changes contained within this are the updates the `info` submodule which has the AST of component type information. This has been significantly refactored to prepare for representing lowered functions and implementing those. The major change is from an `Instantiation` list to an `Initializer` list which abstractly represents a few other initialization actions. This work is split off from my main work to implement component imports of host functions. This is incomplete in the sense that it doesn't actually finish everything necessary to define host functions and import them into components. Instead this is only the changes necessary at the translation layer (so far). Consequently this commit does not have tests and also namely doesn't actually include the `VMComponentContext` initialization and usage. The full body of work is still a bit too messy to PR just yet so I'm hoping that this is a slimmed-down-enough piece to adequately be reviewed.
This commit is contained in:
@@ -1,13 +1,13 @@
|
||||
use crate::component::instance::lookup;
|
||||
use crate::component::instance::InstanceData;
|
||||
use crate::store::{StoreOpaque, Stored};
|
||||
use crate::{AsContext, StoreContextMut};
|
||||
use anyhow::{bail, Context, Result};
|
||||
use std::convert::TryFrom;
|
||||
use std::sync::Arc;
|
||||
use wasmtime_environ::component::{
|
||||
ComponentTypes, FuncTypeIndex, LiftedFunction, RuntimeInstanceIndex, StringEncoding,
|
||||
CanonicalOptions, ComponentTypes, CoreExport, FuncTypeIndex, StringEncoding,
|
||||
};
|
||||
use wasmtime_environ::PrimaryMap;
|
||||
use wasmtime_environ::FuncIndex;
|
||||
use wasmtime_runtime::{Export, ExportFunction, ExportMemory, VMTrampoline};
|
||||
|
||||
mod typed;
|
||||
@@ -36,50 +36,35 @@ pub(crate) struct Options {
|
||||
struct Intrinsics {
|
||||
memory: ExportMemory,
|
||||
realloc: ExportFunction,
|
||||
#[allow(dead_code)] // FIXME: remove this when actually used
|
||||
free: ExportFunction,
|
||||
}
|
||||
|
||||
impl Func {
|
||||
pub(crate) fn from_lifted_func(
|
||||
store: &mut StoreOpaque,
|
||||
types: &Arc<ComponentTypes>,
|
||||
instances: &PrimaryMap<RuntimeInstanceIndex, crate::Instance>,
|
||||
func: &LiftedFunction,
|
||||
instance: &InstanceData,
|
||||
ty: FuncTypeIndex,
|
||||
func: &CoreExport<FuncIndex>,
|
||||
options: &CanonicalOptions,
|
||||
) -> Func {
|
||||
let export = match lookup(store, instances, &func.func) {
|
||||
let export = match instance.lookup_export(store, func) {
|
||||
Export::Function(f) => f,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let trampoline = store.lookup_trampoline(unsafe { export.anyfunc.as_ref() });
|
||||
let intrinsics = func.options.intrinsics.as_ref().map(|i| {
|
||||
let memory = match lookup(store, instances, &i.memory) {
|
||||
Export::Memory(m) => m,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let realloc = match lookup(store, instances, &i.canonical_abi_realloc) {
|
||||
Export::Function(f) => f,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let free = match lookup(store, instances, &i.canonical_abi_free) {
|
||||
Export::Function(f) => f,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
Intrinsics {
|
||||
memory,
|
||||
realloc,
|
||||
free,
|
||||
}
|
||||
let intrinsics = options.memory.map(|i| {
|
||||
let memory = instance.runtime_memory(i);
|
||||
let realloc = instance.runtime_realloc(options.realloc.unwrap());
|
||||
Intrinsics { memory, realloc }
|
||||
});
|
||||
Func(store.store_data_mut().insert(FuncData {
|
||||
trampoline,
|
||||
export,
|
||||
options: Options {
|
||||
intrinsics,
|
||||
string_encoding: func.options.string_encoding,
|
||||
string_encoding: options.string_encoding,
|
||||
},
|
||||
ty: func.ty,
|
||||
types: types.clone(),
|
||||
ty,
|
||||
types: instance.component_types().clone(),
|
||||
}))
|
||||
}
|
||||
|
||||
|
||||
@@ -3,8 +3,10 @@ use crate::instance::OwnedImports;
|
||||
use crate::store::{StoreOpaque, Stored};
|
||||
use crate::{AsContextMut, Module, StoreContextMut};
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use std::sync::Arc;
|
||||
use wasmtime_environ::component::{
|
||||
CoreExport, Export, ExportItem, Instantiation, RuntimeInstanceIndex,
|
||||
ComponentTypes, CoreDef, CoreExport, Export, ExportItem, Initializer, ModuleToInstantiate,
|
||||
RuntimeInstanceIndex, RuntimeMemoryIndex, RuntimeReallocIndex,
|
||||
};
|
||||
use wasmtime_environ::{EntityIndex, PrimaryMap};
|
||||
|
||||
@@ -24,6 +26,10 @@ pub(crate) struct InstanceData {
|
||||
// alive and things like that, instead only the bare minimum necessary
|
||||
// should be kept alive here (mostly just `wasmtime_environ::Component`.
|
||||
component: Component,
|
||||
|
||||
// TODO: move these to `VMComponentContext`
|
||||
memories: PrimaryMap<RuntimeMemoryIndex, wasmtime_runtime::ExportMemory>,
|
||||
reallocs: PrimaryMap<RuntimeReallocIndex, wasmtime_runtime::ExportFunction>,
|
||||
}
|
||||
|
||||
impl Instance {
|
||||
@@ -38,10 +44,7 @@ impl Instance {
|
||||
let mut instantiator = Instantiator::new(component);
|
||||
instantiator.run(&mut store)?;
|
||||
|
||||
let data = Box::new(InstanceData {
|
||||
instances: instantiator.instances,
|
||||
component: component.clone(),
|
||||
});
|
||||
let data = Box::new(instantiator.data);
|
||||
Ok(Instance(store.0.store_data_mut().insert(Some(data))))
|
||||
}
|
||||
|
||||
@@ -103,19 +106,56 @@ impl Instance {
|
||||
impl InstanceData {
|
||||
fn get_func(&self, store: &mut StoreOpaque, name: &str) -> Option<Func> {
|
||||
match self.component.env_component().exports.get(name)? {
|
||||
Export::LiftedFunction(func) => Some(Func::from_lifted_func(
|
||||
store,
|
||||
self.component.types(),
|
||||
&self.instances,
|
||||
func,
|
||||
)),
|
||||
Export::LiftedFunction { ty, func, options } => {
|
||||
Some(Func::from_lifted_func(store, self, *ty, func, options))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn lookup_def(&self, store: &mut StoreOpaque, item: &CoreDef) -> wasmtime_runtime::Export {
|
||||
match item {
|
||||
CoreDef::Lowered(_) => unimplemented!(),
|
||||
CoreDef::Export(e) => self.lookup_export(store, e),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lookup_export<T>(
|
||||
&self,
|
||||
store: &mut StoreOpaque,
|
||||
item: &CoreExport<T>,
|
||||
) -> wasmtime_runtime::Export
|
||||
where
|
||||
T: Copy + Into<EntityIndex>,
|
||||
{
|
||||
let instance = &self.instances[item.instance];
|
||||
let id = instance.id(store);
|
||||
let instance = store.instance_mut(id);
|
||||
let idx = match &item.item {
|
||||
ExportItem::Index(idx) => (*idx).into(),
|
||||
ExportItem::Name(name) => instance.module().exports[name],
|
||||
};
|
||||
instance.get_export_by_index(idx)
|
||||
}
|
||||
|
||||
pub fn component_types(&self) -> &Arc<ComponentTypes> {
|
||||
self.component.types()
|
||||
}
|
||||
|
||||
pub fn runtime_memory(&self, memory: RuntimeMemoryIndex) -> wasmtime_runtime::ExportMemory {
|
||||
self.memories[memory].clone()
|
||||
}
|
||||
|
||||
pub fn runtime_realloc(
|
||||
&self,
|
||||
realloc: RuntimeReallocIndex,
|
||||
) -> wasmtime_runtime::ExportFunction {
|
||||
self.reallocs[realloc].clone()
|
||||
}
|
||||
}
|
||||
|
||||
struct Instantiator<'a> {
|
||||
component: &'a Component,
|
||||
instances: PrimaryMap<RuntimeInstanceIndex, crate::Instance>,
|
||||
data: InstanceData,
|
||||
imports: OwnedImports,
|
||||
}
|
||||
|
||||
@@ -127,33 +167,61 @@ impl<'a> Instantiator<'a> {
|
||||
}
|
||||
Instantiator {
|
||||
component,
|
||||
instances: PrimaryMap::with_capacity(env_component.instances.len()),
|
||||
imports: OwnedImports::empty(),
|
||||
data: InstanceData {
|
||||
instances: PrimaryMap::with_capacity(env_component.num_runtime_instances as usize),
|
||||
component: component.clone(),
|
||||
memories: Default::default(),
|
||||
reallocs: Default::default(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn run<T>(&mut self, store: &mut StoreContextMut<'_, T>) -> Result<()> {
|
||||
let env_component = self.component.env_component();
|
||||
for (index, instantiation) in env_component.instances.iter() {
|
||||
let (module, args) = match instantiation {
|
||||
Instantiation::ModuleUpvar { module, args } => {
|
||||
(self.component.upvar(*module), args)
|
||||
}
|
||||
Instantiation::ModuleImport { import_index, args } => {
|
||||
drop((import_index, args));
|
||||
unimplemented!("component module imports");
|
||||
}
|
||||
};
|
||||
for initializer in env_component.initializers.iter() {
|
||||
match initializer {
|
||||
Initializer::InstantiateModule {
|
||||
instance,
|
||||
module,
|
||||
args,
|
||||
} => {
|
||||
let module = match module {
|
||||
ModuleToInstantiate::Upvar(module) => self.component.upvar(*module),
|
||||
ModuleToInstantiate::Import(idx) => {
|
||||
drop(idx);
|
||||
unimplemented!("component module imports");
|
||||
}
|
||||
};
|
||||
|
||||
// Note that the unsafety here should be ok because the
|
||||
// validity of the component means that type-checks have
|
||||
// already been performed. This maens that the unsafety due
|
||||
// to imports having the wrong type should not happen here.
|
||||
let imports = self.build_imports(store.0, module, args);
|
||||
let instance =
|
||||
unsafe { crate::Instance::new_started(store, module, imports.as_ref())? };
|
||||
let idx = self.instances.push(instance);
|
||||
assert_eq!(idx, index);
|
||||
// Note that the unsafety here should be ok because the
|
||||
// validity of the component means that type-checks have
|
||||
// already been performed. This maens that the unsafety due
|
||||
// to imports having the wrong type should not happen here.
|
||||
let imports = self.build_imports(store.0, module, args);
|
||||
let i =
|
||||
unsafe { crate::Instance::new_started(store, module, imports.as_ref())? };
|
||||
let idx = self.data.instances.push(i);
|
||||
assert_eq!(idx, *instance);
|
||||
}
|
||||
Initializer::LowerImport(_) => unimplemented!(),
|
||||
|
||||
Initializer::ExtractMemory { index, export } => {
|
||||
let memory = match self.data.lookup_export(store.0, export) {
|
||||
wasmtime_runtime::Export::Memory(m) => m,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
assert_eq!(*index, self.data.memories.push(memory));
|
||||
}
|
||||
|
||||
Initializer::ExtractRealloc { index, def } => {
|
||||
let func = match self.data.lookup_def(store.0, def) {
|
||||
wasmtime_runtime::Export::Function(f) => f,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
assert_eq!(*index, self.data.reallocs.push(func));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -162,13 +230,13 @@ impl<'a> Instantiator<'a> {
|
||||
&mut self,
|
||||
store: &mut StoreOpaque,
|
||||
module: &Module,
|
||||
args: &[CoreExport<EntityIndex>],
|
||||
args: &[CoreDef],
|
||||
) -> &OwnedImports {
|
||||
self.imports.clear();
|
||||
self.imports.reserve(module);
|
||||
|
||||
for arg in args {
|
||||
let export = lookup(store, &self.instances, arg);
|
||||
let export = self.data.lookup_def(store, arg);
|
||||
|
||||
// The unsafety here should be ok since the `export` is loaded
|
||||
// directly from an instance which should only give us valid export
|
||||
@@ -181,21 +249,3 @@ impl<'a> Instantiator<'a> {
|
||||
&self.imports
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn lookup<T>(
|
||||
store: &mut StoreOpaque,
|
||||
instances: &PrimaryMap<RuntimeInstanceIndex, crate::Instance>,
|
||||
item: &CoreExport<T>,
|
||||
) -> wasmtime_runtime::Export
|
||||
where
|
||||
T: Copy + Into<EntityIndex>,
|
||||
{
|
||||
let instance = &instances[item.instance];
|
||||
let id = instance.id(store);
|
||||
let instance = store.instance_mut(id);
|
||||
let idx = match &item.item {
|
||||
ExportItem::Index(idx) => (*idx).into(),
|
||||
ExportItem::Name(name) => instance.module().exports[name],
|
||||
};
|
||||
instance.get_export_by_index(idx)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user