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

@@ -23,6 +23,7 @@ use crate::component::{
};
use crate::{FuncIndex, GlobalIndex, MemoryIndex};
use std::collections::HashMap;
use std::mem;
use wasm_encoder::*;
mod core_types;
@@ -90,6 +91,7 @@ enum Context {
}
impl<'a> Module<'a> {
/// Creates an empty module.
pub fn new(types: &'a ComponentTypes, debug: bool) -> Module<'a> {
Module {
debug,
@@ -110,20 +112,24 @@ impl<'a> Module<'a> {
/// The `name` provided is the export name of the adapter from the final
/// module, and `adapter` contains all metadata necessary for compilation.
pub fn adapt(&mut self, name: &str, adapter: &Adapter) {
// Import core wasm function which was lifted using its appropriate
// Import any items required by the various canonical options
// (memories, reallocs, etc)
let mut lift = self.import_options(adapter.lift_ty, &adapter.lift_options);
let lower = self.import_options(adapter.lower_ty, &adapter.lower_options);
// Lowering options are not allowed to specify post-return as per the
// current canonical abi specification.
assert!(adapter.lower_options.post_return.is_none());
// Import the core wasm function which was lifted using its appropriate
// signature since the exported function this adapter generates will
// call the lifted function.
let signature = self.signature(adapter.lift_ty, Context::Lift);
let signature = self.signature(&lift, Context::Lift);
let ty = self
.core_types
.function(&signature.params, &signature.results);
let callee = self.import_func("callee", name, ty, adapter.func.clone());
// Next import any items required by the various canonical options
// (memories, reallocs, etc)
let mut lift = self.import_options(adapter.lift_ty, &adapter.lift_options);
let lower = self.import_options(adapter.lower_ty, &adapter.lower_options);
// Handle post-return specifically here where we have `core_ty` and the
// results of `core_ty` are the parameters to the post-return function.
lift.post_return = adapter.lift_options.post_return.as_ref().map(|func| {
@@ -131,10 +137,6 @@ impl<'a> Module<'a> {
self.import_func("post_return", name, ty, func.clone())
});
// Lowering options are not allowed to specify post-return as per the
// current canonical abi specification.
assert!(adapter.lower_options.post_return.is_none());
self.adapters.push(AdapterData {
name: name.to_string(),
lift,
@@ -151,10 +153,10 @@ impl<'a> Module<'a> {
instance,
string_encoding,
memory,
memory64,
realloc,
post_return: _, // handled above
} = options;
let memory64 = false; // FIXME(#4311) should be plumbed from somewhere
let flags = self.import_global(
"flags",
&format!("instance{}", instance.as_u32()),
@@ -172,13 +174,17 @@ impl<'a> Module<'a> {
minimum: 0,
maximum: None,
shared: false,
memory64,
memory64: *memory64,
},
memory.clone().into(),
)
});
let realloc = realloc.as_ref().map(|func| {
let ptr = if memory64 { ValType::I64 } else { ValType::I32 };
let ptr = if *memory64 {
ValType::I64
} else {
ValType::I32
};
let ty = self.core_types.function(&[ptr, ptr, ptr, ptr], &[ptr]);
self.import_func("realloc", "", ty, func.clone())
});
@@ -186,7 +192,7 @@ impl<'a> Module<'a> {
ty,
string_encoding: *string_encoding,
flags,
memory64,
memory64: *memory64,
memory,
realloc,
post_return: None,
@@ -245,26 +251,27 @@ impl<'a> Module<'a> {
ret
}
/// Encodes this module into a WebAssembly binary.
pub fn encode(&mut self) -> Vec<u8> {
let mut funcs = FunctionSection::new();
let mut code = CodeSection::new();
let mut exports = ExportSection::new();
let mut traps = traps::TrapSection::default();
let mut types = mem::take(&mut self.core_types);
for adapter in self.adapters.iter() {
let idx = self.core_funcs + funcs.len();
exports.export(&adapter.name, ExportKind::Func, idx);
let signature = self.signature(adapter.lower.ty, Context::Lower);
let ty = self
.core_types
.function(&signature.params, &signature.results);
let signature = self.signature(&adapter.lower, Context::Lower);
let ty = types.function(&signature.params, &signature.results);
funcs.function(ty);
let (function, func_traps) = trampoline::compile(self, adapter);
let (function, func_traps) = trampoline::compile(self, &mut types, adapter);
code.raw(&function);
traps.append(idx, func_traps);
}
self.core_types = types;
let traps = traps.finish();
let mut result = wasm_encoder::Module::new();
@@ -288,3 +295,13 @@ impl<'a> Module<'a> {
&self.imports
}
}
impl Options {
fn ptr(&self) -> ValType {
if self.memory64 {
ValType::I64
} else {
ValType::I32
}
}
}