Track type information during component translation (#4448)

This commit augments the current translation phase of components with
extra machinery to track the type information of component items such as
instances, components, and functions. The end goal of this commit is to
enable the `Lower` instruction to know the type of the component
function being lowered. Currently during the inlining pass where
component fusion is detected the type of the lifted function is known,
but to implement fusion entirely the type of the lowered function must
be known. Note that these two types are expected to be different to
allow for the subtyping rules specified by the component model.

For now nothing is actually done with this information other than noting
its presence in the face of a lifted-then-lowered function. My hope
though was to split this out for a separate review to avoid making a
future component-adapter-compiler-containing-PR too large.
This commit is contained in:
Alex Crichton
2022-07-18 09:21:40 -05:00
committed by GitHub
parent 791af15413
commit 3032e3fcfb
2 changed files with 187 additions and 19 deletions

View File

@@ -3,6 +3,7 @@ use crate::{
EntityIndex, ModuleEnvironment, ModuleTranslation, PrimaryMap, SignatureIndex, Tunables,
};
use anyhow::{bail, Result};
use indexmap::IndexMap;
use std::collections::HashMap;
use std::mem;
use wasmparser::{Chunk, Encoding, Parser, Payload, Validator};
@@ -54,6 +55,11 @@ pub struct Translator<'a, 'data> {
/// As frames are popped from `lexical_scopes` their completed component
/// will be pushed onto this list.
static_components: PrimaryMap<StaticComponentIndex, Translation<'data>>,
/// Storage for type information used by `ComponentInstanceType::Synthetic`
/// which is thrown away after compilation is finished.
synthetic_instance_types:
PrimaryMap<SyntheticInstanceTypeIndex, HashMap<&'data str, ComponentItemType>>,
}
/// Representation of the syntactic scope of a component meaning where it is
@@ -138,14 +144,14 @@ struct Translation<'data> {
/// The list of exports from this component, as pairs of names and an
/// index into an index space of what's being exported.
exports: Vec<(&'data str, ComponentItem)>,
exports: IndexMap<&'data str, ComponentItem>,
/// Type information from wasmparser about this component, available after
/// the component has been completely translated.
types: Option<wasmparser::types::Types>,
/// The types of all core wasm functions defined within this component.
/// Type information (in Wasmtime's representation) for various
/// component-model index spaces.
funcs: PrimaryMap<FuncIndex, SignatureIndex>,
components: PrimaryMap<ComponentIndex, ComponentType>,
component_funcs: PrimaryMap<ComponentFuncIndex, TypeFuncIndex>,
component_instances: PrimaryMap<ComponentInstanceIndex, ComponentInstanceType>,
}
#[allow(missing_docs)]
@@ -219,6 +225,44 @@ struct LocalCanonicalOptions {
post_return: Option<FuncIndex>,
}
#[derive(Copy, Clone)]
enum ComponentType {
/// A component was imported from so its type is listed by an index.
Index(TypeComponentIndex),
/// A component was defined statically inline so its type information can be
/// found in the `Translation` itself.
Static(StaticComponentIndex),
}
#[derive(Copy, Clone)]
enum ComponentInstanceType {
/// An instance was imported so its type information is listed by index.
Index(TypeComponentInstanceIndex),
/// An instance was created through instantiating an imported component, so
/// the type of the index is inferred from the type of the component.
InstantiatedIndex(TypeComponentIndex),
/// An instance was created through instantiation of a statically defined
/// component, so the type information is inferred from a `Translation`.
InstantiatedStatic(StaticComponentIndex),
/// An instance was created from a list of other items, and the list can be
/// found in the `synthetic_instance_types` map.
Synthetic(SyntheticInstanceTypeIndex),
}
/// Only used as part of `synthetic_instance_types` which is used to infer type
/// information for component-model items. This intentionally doesn't cover
/// everything (e.g. modules are missing at this time since they aren't needed).
#[derive(Copy, Clone)]
enum ComponentItemType {
Func(TypeFuncIndex),
Component(ComponentType),
Instance(ComponentInstanceType),
}
#[derive(Copy, Clone, PartialEq, Eq)]
struct SyntheticInstanceTypeIndex(u32);
cranelift_entity::entity_impl!(SyntheticInstanceTypeIndex);
enum Action {
KeepGoing,
Skip(usize),
@@ -241,6 +285,7 @@ impl<'a, 'data> Translator<'a, 'data> {
lexical_scopes: Vec::new(),
static_components: Default::default(),
static_modules: Default::default(),
synthetic_instance_types: Default::default(),
}
}
@@ -355,22 +400,17 @@ impl<'a, 'data> Translator<'a, 'data> {
// type of the function is then intern'd to get a
// `SignatureIndex` which is later used at runtime for a
// `VMSharedSignatureIndex`.
for init in self.result.initializers.iter() {
match init {
LocalInitializer::Lower(..) | LocalInitializer::AliasExportFunc(..) => {}
_ => continue,
}
let idx = self.result.funcs.next_key();
let lowered_function_type = types
.function_at(idx.as_u32())
.expect("should be in-bounds");
for idx in 0.. {
let lowered_function_type = match types.function_at(idx) {
Some(ty) => ty,
None => break,
};
let ty = self
.types
.module_types_builder()
.wasm_func_type(lowered_function_type.clone().try_into()?);
self.result.funcs.push(ty);
}
self.result.types = Some(types);
// When leaving a module be sure to pop the types scope to
// ensure that when we go back to the previous module outer
@@ -395,6 +435,9 @@ impl<'a, 'data> Translator<'a, 'data> {
self.result
.initializers
.push(LocalInitializer::ComponentStatic(static_idx, closure_args));
self.result
.components
.push(ComponentType::Static(static_idx));
}
// When we see a type section the types are validated and then
@@ -428,6 +471,7 @@ impl<'a, 'data> Translator<'a, 'data> {
for import in s {
let import = import?;
let ty = self.types.component_type_ref(&import.ty);
self.result.push_typedef(ty);
self.result
.initializers
.push(LocalInitializer::Import(import.name, ty));
@@ -456,6 +500,7 @@ impl<'a, 'data> Translator<'a, 'data> {
self.result
.initializers
.push(LocalInitializer::Lift(ty, func, options));
self.result.component_funcs.push(ty);
}
wasmparser::CanonicalFunction::Lower {
func_index,
@@ -559,7 +604,8 @@ impl<'a, 'data> Translator<'a, 'data> {
for export in s {
let export = export?;
let item = self.kind_to_item(export.kind, export.index);
self.result.exports.push((export.name, item));
let prev = self.result.exports.insert(export.name, item);
assert!(prev.is_none());
}
}
@@ -608,6 +654,7 @@ impl<'a, 'data> Translator<'a, 'data> {
} => {
let instance = ComponentInstanceIndex::from_u32(instance_index);
drop(kind);
self.alias_component_instance_export(instance, name);
LocalInitializer::AliasComponentExport(instance, name)
}
wasmparser::ComponentAlias::Outer { kind, count, index } => {
@@ -701,6 +748,13 @@ impl<'a, 'data> Translator<'a, 'data> {
args.insert(arg.name, idx);
}
self.result
.component_instances
.push(match self.result.components[component] {
ComponentType::Index(i) => ComponentInstanceType::InstantiatedIndex(i),
ComponentType::Static(i) => ComponentInstanceType::InstantiatedStatic(i),
});
LocalInitializer::ComponentInstantiate(component, args)
}
@@ -711,10 +765,32 @@ impl<'a, 'data> Translator<'a, 'data> {
exports: &[wasmparser::ComponentExport<'data>],
) -> LocalInitializer<'data> {
let mut map = HashMap::with_capacity(exports.len());
let mut types = HashMap::with_capacity(exports.len());
for export in exports {
let idx = self.kind_to_item(export.kind, export.index);
let ty = match idx {
ComponentItem::Func(i) => {
Some(ComponentItemType::Func(self.result.component_funcs[i]))
}
ComponentItem::Component(i) => {
Some(ComponentItemType::Component(self.result.components[i]))
}
ComponentItem::ComponentInstance(i) => Some(ComponentItemType::Instance(
self.result.component_instances[i],
)),
ComponentItem::Module(_) => None,
};
map.insert(export.name, idx);
if let Some(ty) = ty {
types.insert(export.name, ty);
}
}
let index = self.synthetic_instance_types.push(types);
self.result
.component_instances
.push(ComponentInstanceType::Synthetic(index));
LocalInitializer::ComponentSynthetic(map)
}
@@ -762,6 +838,71 @@ impl<'a, 'data> Translator<'a, 'data> {
}
}
fn alias_component_instance_export(
&mut self,
instance: ComponentInstanceIndex,
name: &'data str,
) {
match self.result.component_instances[instance] {
// An imported component instance is being aliased, so the type of
// the aliased item is directly available from the instance type.
ComponentInstanceType::Index(ty) => {
self.result.push_typedef(self.types[ty].exports[name])
}
// An imported component was instantiated so the type of the aliased
// export is available through the type of the export on the
// original component.
ComponentInstanceType::InstantiatedIndex(ty) => {
self.result.push_typedef(self.types[ty].exports[name])
}
// A static nested component was instantiated which means that the
// type of the export can be looked up directly from the translation
// that was finished prior.
ComponentInstanceType::InstantiatedStatic(idx) => {
let translation = &self.static_components[idx];
match translation.exports[name] {
ComponentItem::Func(idx) => {
self.result
.component_funcs
.push(translation.component_funcs[idx]);
}
ComponentItem::Component(idx) => {
self.result.components.push(translation.components[idx]);
}
ComponentItem::ComponentInstance(idx) => {
self.result
.component_instances
.push(translation.component_instances[idx]);
}
// ignored during this type pass
ComponentItem::Module(_) => {}
}
}
// A synthetic instance is being aliased which means the global list
// of synthetic instance types can be consulted for the type
// information.
ComponentInstanceType::Synthetic(index) => {
let map = &self.synthetic_instance_types[index];
match map[name] {
ComponentItemType::Func(ty) => {
self.result.component_funcs.push(ty);
}
ComponentItemType::Component(ty) => {
self.result.components.push(ty);
}
ComponentItemType::Instance(ty) => {
self.result.component_instances.push(ty);
}
}
}
}
}
fn alias_component_outer(
&mut self,
kind: wasmparser::ComponentOuterAliasKind,
@@ -818,10 +959,15 @@ impl<'a, 'data> Translator<'a, 'data> {
component =
ClosedOverComponent::Upvar(frame.closure_args.components.push(component));
}
let component_ty = match self.lexical_scopes.get(depth) {
Some(frame) => frame.translation.components[index],
None => self.result.components[index],
};
self.result
.initializers
.push(LocalInitializer::AliasComponent(component));
self.result.components.push(component_ty);
}
}
}
@@ -861,3 +1007,23 @@ impl<'a, 'data> Translator<'a, 'data> {
return ret;
}
}
impl Translation<'_> {
fn push_typedef(&mut self, ty: TypeDef) {
match ty {
TypeDef::ComponentInstance(idx) => {
self.component_instances
.push(ComponentInstanceType::Index(idx));
}
TypeDef::ComponentFunc(idx) => {
self.component_funcs.push(idx);
}
TypeDef::Component(idx) => {
self.components.push(ComponentType::Index(idx));
}
// not processed here
TypeDef::Interface(_) | TypeDef::CoreFunc(_) | TypeDef::Module(_) => {}
}
}
}

View File

@@ -390,6 +390,7 @@ impl<'a> Inliner<'a> {
// NB: at this time only lowered imported functions are supported.
Lower(func, options) => {
let canonical_abi = frame.translation.funcs[frame.funcs.next_key()];
let lower_ty = frame.translation.component_funcs[*func];
let options_lower = self.canonical_options(frame, options);
let func = match &frame.component_funcs[*func] {
@@ -476,13 +477,14 @@ impl<'a> Inliner<'a> {
// the return values, copy them from `options_lift` to
// `options_lower`, and then return.
ComponentFuncDef::Lifted {
ty,
ty: lift_ty,
func,
options: options_lift,
} => {
// These are the various compilation options for lifting
// and lowering.
drop(ty); // component-model function type
drop(lift_ty); // type used when lifting the core function
drop(lower_ty); // type used when lowering the core function
drop(func); // original core wasm function that was lifted
drop(options_lift); // options during `canon lift`
drop(options_lower); // options during `canon lower`