Implement variant translation in fused adapters (#4534)

* Implement variant translation in fused adapters

This commit implements the most general case of variants for fused
adapter trampolines. Additionally a number of other primitive types are
filled out here to assist with testing variants. The implementation
internally was relatively straightforward given the shape of variants,
but there's room for future optimization as necessary especially around
converting locals to various types.

This commit also introduces a "one off" fuzzer for adapters to ensure
that the generated adapter is valid. I hope to extend this fuzz
generator as more types are implemented to assist in various corner
cases that might arise. For now the fuzzer simply tests that the output
wasm module is valid, not that it actually executes correctly. I hope to
integrate with a fuzzer along the lines of #4307 one day to test the
run-time-correctness of the generated adapters as well, at which point
this fuzzer would become obsolete.

Finally this commit also fixes an issue with `u8` translation where
upper bits weren't zero'd out and were passed raw across modules.
Instead smaller-than-32 types now all mask out their upper bits and do
sign-extension as appropriate for unsigned/signed variants.

* Fuzz memory64 in the new trampoline fuzzer

Currently memory64 isn't supported elsewhere in the component model
implementation of Wasmtime but the trampoline compiler seems as good a
place as any to ensure that it at least works in isolation. This plumbs
through fuzz input into a `memory64` boolean which gets fed into
compilation. Some miscellaneous bugs were fixed as a result to ensure
that memory64 trampolines all validate correctly.

* Tweak manifest for doc build
This commit is contained in:
Alex Crichton
2022-07-27 09:14:43 -05:00
committed by GitHub
parent 799e8919fe
commit 285bc5ce24
15 changed files with 1315 additions and 98 deletions

View File

@@ -159,6 +159,8 @@ pub struct AdapterOptions {
pub string_encoding: StringEncoding,
/// An optional memory definition supplied.
pub memory: Option<CoreExport<MemoryIndex>>,
/// If `memory` is specified, whether it's a 64-bit memory.
pub memory64: bool,
/// An optional definition of `realloc` to used.
pub realloc: Option<CoreDef>,
/// An optional definition of a `post-return` to use.
@@ -563,6 +565,7 @@ impl DefinedItems {
let AdapterOptions {
instance: _,
string_encoding: _,
memory64: _,
memory,
realloc,
post_return,

View File

@@ -47,7 +47,7 @@
use crate::component::translate::adapt::{Adapter, AdapterOptions, Adapters};
use crate::component::translate::*;
use crate::{PrimaryMap, SignatureIndex};
use crate::{EntityType, PrimaryMap, SignatureIndex};
use indexmap::IndexMap;
pub(super) fn run(
@@ -67,6 +67,7 @@ pub(super) fn run(
runtime_post_return_interner: Default::default(),
runtime_memory_interner: Default::default(),
runtime_always_trap_interner: Default::default(),
runtime_instances: PrimaryMap::default(),
};
// The initial arguments to the root component are all host imports. This
@@ -145,6 +146,9 @@ struct Inliner<'a> {
runtime_post_return_interner: HashMap<CoreDef, RuntimePostReturnIndex>,
runtime_memory_interner: HashMap<CoreExport<MemoryIndex>, RuntimeMemoryIndex>,
runtime_always_trap_interner: HashMap<SignatureIndex, RuntimeAlwaysTrapIndex>,
/// Origin information about where each runtime instance came from
runtime_instances: PrimaryMap<RuntimeInstanceIndex, InstanceModule>,
}
/// A "stack frame" as part of the inlining process, or the progress through
@@ -540,6 +544,7 @@ impl<'a> Inliner<'a> {
// and an initializer is recorded to indicate that it's being
// instantiated.
ModuleInstantiate(module, args) => {
let instance_module;
let init = match &frame.modules[*module] {
ModuleDef::Static(idx) => {
let mut defs = Vec::new();
@@ -549,6 +554,7 @@ impl<'a> Inliner<'a> {
self.core_def_of_module_instance_export(frame, instance, name),
);
}
instance_module = InstanceModule::Static(*idx);
InstantiateModule::Static(*idx, defs.into())
}
ModuleDef::Import(path, ty) => {
@@ -562,12 +568,15 @@ impl<'a> Inliner<'a> {
.insert(name.to_string(), def);
}
let index = self.runtime_import(path);
instance_module = InstanceModule::Import(*ty);
InstantiateModule::Import(index, defs)
}
};
let idx = RuntimeInstanceIndex::from_u32(self.result.num_runtime_instances);
self.result.num_runtime_instances += 1;
let idx2 = self.runtime_instances.push(instance_module);
assert_eq!(idx, idx2);
self.result
.initializers
.push(GlobalInitializer::InstantiateModule(init));
@@ -822,12 +831,32 @@ impl<'a> Inliner<'a> {
_ => unreachable!(),
})
});
let memory64 = match &memory {
Some(memory) => match &self.runtime_instances[memory.instance] {
InstanceModule::Static(idx) => match &memory.item {
ExportItem::Index(i) => {
let plan = &self.nested_modules[*idx].module.memory_plans[*i];
plan.memory.memory64
}
ExportItem::Name(_) => unreachable!(),
},
InstanceModule::Import(ty) => match &memory.item {
ExportItem::Name(name) => match self.types[*ty].exports[name] {
EntityType::Memory(m) => m.memory64,
_ => unreachable!(),
},
ExportItem::Index(_) => unreachable!(),
},
},
None => false,
};
let realloc = options.realloc.map(|i| frame.funcs[i].clone());
let post_return = options.post_return.map(|i| frame.funcs[i].clone());
AdapterOptions {
instance: frame.instance,
string_encoding: options.string_encoding,
memory,
memory64,
realloc,
post_return,
}
@@ -1064,3 +1093,8 @@ impl<'a> ComponentItemDef<'a> {
Ok(item)
}
}
enum InstanceModule {
Static(StaticModuleIndex),
Import(TypeModuleIndex),
}

View File

@@ -574,7 +574,7 @@ impl ComponentTypesBuilder {
.collect(),
result: self.valtype(&ty.result),
};
intern(&mut self.functions, &mut self.component_types.functions, ty)
self.add_func_type(ty)
}
fn defined_type(&mut self, ty: &wasmparser::ComponentDefinedType<'_>) -> InterfaceType {
@@ -636,7 +636,7 @@ impl ComponentTypesBuilder {
})
.collect(),
};
intern(&mut self.records, &mut self.component_types.records, record)
self.add_record_type(record)
}
fn variant_type(&mut self, cases: &[wasmparser::VariantCase<'_>]) -> TypeVariantIndex {
@@ -654,18 +654,14 @@ impl ComponentTypesBuilder {
})
.collect(),
};
intern(
&mut self.variants,
&mut self.component_types.variants,
variant,
)
self.add_variant_type(variant)
}
fn tuple_type(&mut self, types: &[wasmparser::ComponentValType]) -> TypeTupleIndex {
let tuple = TypeTuple {
types: types.iter().map(|ty| self.valtype(ty)).collect(),
};
intern(&mut self.tuples, &mut self.component_types.tuples, tuple)
self.add_tuple_type(tuple)
}
fn flags_type(&mut self, flags: &[&str]) -> TypeFlagsIndex {
@@ -704,6 +700,26 @@ impl ComponentTypesBuilder {
expected,
)
}
/// Interns a new function type within this type information.
pub fn add_func_type(&mut self, ty: TypeFunc) -> TypeFuncIndex {
intern(&mut self.functions, &mut self.component_types.functions, ty)
}
/// Interns a new record type within this type information.
pub fn add_record_type(&mut self, ty: TypeRecord) -> TypeRecordIndex {
intern(&mut self.records, &mut self.component_types.records, ty)
}
/// Interns a new tuple type within this type information.
pub fn add_tuple_type(&mut self, ty: TypeTuple) -> TypeTupleIndex {
intern(&mut self.tuples, &mut self.component_types.tuples, ty)
}
/// Interns a new variant type within this type information.
pub fn add_variant_type(&mut self, ty: TypeVariant) -> TypeVariantIndex {
intern(&mut self.variants, &mut self.component_types.variants, ty)
}
}
// Forward the indexing impl to the internal `TypeTables`