Initial skeleton of some component model processing (#4005)

* Initial skeleton of some component model processing

This commit is the first of what will likely be many to implement the
component model proposal in Wasmtime. This will be structured as a
series of incremental commits, most of which haven't been written yet.
My hope is to make this incremental and over time to make this easier to
review and easier to test each step in isolation.

Here much of the skeleton of how components are going to work in
Wasmtime is sketched out. This is not a complete implementation of the
component model so it's not all that useful yet, but some things you can
do are:

* Process the type section into a representation amenable for working
  with in Wasmtime.
* Process the module section and register core wasm modules.
* Process the instance section for core wasm modules.
* Process core wasm module imports.
* Process core wasm instance aliasing.
* Ability to compile a component with core wasm embedded.
* Ability to instantiate a component with no imports.
* Ability to get functions from this component.

This is already starting to diverge from the previous module linking
representation where a `Component` will try to avoid unnecessary
metadata about the component and instead internally only have the bare
minimum necessary to instantiate the module. My hope is we can avoid
constructing most of the index spaces during instantiation only for it
to all ge thrown away. Additionally I'm predicting that we'll need to
see through processing where possible to know how to generate adapters
and where they are fused.

At this time you can't actually call a component's functions, and that's
the next PR that I would like to make.

* Add tests for the component model support

This commit uses the recently updated wasm-tools crates to add tests for
the component model added in the previous commit. This involved updating
the `wasmtime-wast` crate for component-model changes. Currently the
component support there is quite primitive, but enough to at least
instantiate components and verify the internals of Wasmtime are all
working correctly. Additionally some simple tests for the embedding API
have also been added.
This commit is contained in:
Alex Crichton
2022-05-20 15:33:18 -05:00
committed by GitHub
parent a75f383f96
commit fcf6208750
45 changed files with 2938 additions and 213 deletions

View File

@@ -111,3 +111,8 @@ memory-init-cow = ["wasmtime-runtime/memory-init-cow"]
#
# This is enabled by default.
wasm-backtrace = ["wasmtime-runtime/wasm-backtrace", "backtrace"]
# Enables in-progress support for the component model. Note that this feature is
# in-progress, buggy, and incomplete. This is primarily here for internal
# testing purposes.
component-model = ["wasmtime-environ/component-model"]

View File

@@ -0,0 +1,122 @@
use crate::{Engine, Module};
use anyhow::{bail, Context, Result};
use std::fs;
use std::path::Path;
use std::sync::Arc;
use wasmtime_environ::component::{ComponentTypes, ModuleUpvarIndex, Translation, Translator};
use wasmtime_environ::PrimaryMap;
/// A compiled WebAssembly Component.
//
// FIXME: need to write more docs here.
#[derive(Clone)]
pub struct Component {
inner: Arc<ComponentInner>,
}
struct ComponentInner {
component: wasmtime_environ::component::Component,
upvars: PrimaryMap<ModuleUpvarIndex, Module>,
types: Arc<ComponentTypes>,
}
impl Component {
/// Compiles a new WebAssembly component from the in-memory wasm image
/// provided.
//
// FIXME: need to write more docs here.
#[cfg(compiler)]
#[cfg_attr(nightlydoc, doc(cfg(feature = "cranelift")))] // see build.rs
pub fn new(engine: &Engine, bytes: impl AsRef<[u8]>) -> Result<Component> {
let bytes = bytes.as_ref();
#[cfg(feature = "wat")]
let bytes = wat::parse_bytes(bytes)?;
Component::from_binary(engine, &bytes)
}
/// Compiles a new WebAssembly component from a wasm file on disk pointed to
/// by `file`.
//
// FIXME: need to write more docs here.
#[cfg(compiler)]
#[cfg_attr(nightlydoc, doc(cfg(feature = "cranelift")))] // see build.rs
pub fn from_file(engine: &Engine, file: impl AsRef<Path>) -> Result<Component> {
match Self::new(
engine,
&fs::read(&file).with_context(|| "failed to read input file")?,
) {
Ok(m) => Ok(m),
Err(e) => {
cfg_if::cfg_if! {
if #[cfg(feature = "wat")] {
let mut e = e.downcast::<wat::Error>()?;
e.set_path(file);
bail!(e)
} else {
Err(e)
}
}
}
}
}
/// Compiles a new WebAssembly component from the in-memory wasm image
/// provided.
//
// FIXME: need to write more docs here.
#[cfg(compiler)]
#[cfg_attr(nightlydoc, doc(cfg(feature = "cranelift")))] // see build.rs
pub fn from_binary(engine: &Engine, binary: &[u8]) -> Result<Component> {
engine
.check_compatible_with_native_host()
.context("compilation settings are not compatible with the native host")?;
let tunables = &engine.config().tunables;
let mut validator =
wasmparser::Validator::new_with_features(engine.config().features.clone());
let mut types = Default::default();
let translation = Translator::new(tunables, &mut validator, &mut types)
.translate(binary)
.context("failed to parse WebAssembly module")?;
let types = Arc::new(types.finish());
let Translation {
component, upvars, ..
} = translation;
let upvars = upvars.into_iter().map(|(_, t)| t).collect::<Vec<_>>();
let upvars = engine
.run_maybe_parallel(upvars, |module| {
let (mmap, info) = Module::compile_functions(engine, module, types.module_types())?;
// FIXME: the `SignatureCollection` here is re-registering the
// entire list of wasm types within `types` on each invocation.
// That's ok semantically but is quite slow to do so. This
// should build up a mapping from `SignatureIndex` to
// `VMSharedSignatureIndex` once and then reuse that for each
// module somehow.
Module::from_parts(engine, mmap, info, types.clone())
})?
.into_iter()
.collect();
Ok(Component {
inner: Arc::new(ComponentInner {
component,
upvars,
types,
}),
})
}
pub(crate) fn env_component(&self) -> &wasmtime_environ::component::Component {
&self.inner.component
}
pub(crate) fn upvar(&self, idx: ModuleUpvarIndex) -> &Module {
&self.inner.upvars[idx]
}
pub(crate) fn types(&self) -> &Arc<ComponentTypes> {
&self.inner.types
}
}

View File

@@ -0,0 +1,83 @@
use crate::component::instance::lookup;
use crate::store::{StoreOpaque, Stored};
use std::sync::Arc;
use wasmtime_environ::component::{
ComponentTypes, FuncTypeIndex, LiftedFunction, RuntimeInstanceIndex, StringEncoding,
};
use wasmtime_environ::PrimaryMap;
use wasmtime_runtime::{Export, ExportFunction, ExportMemory, VMTrampoline};
/// A WebAssembly component function.
//
// FIXME: write more docs here
#[derive(Copy, Clone, Debug)]
pub struct Func(Stored<FuncData>);
#[doc(hidden)]
#[allow(dead_code)] // FIXME: remove this when fields are actually used
pub struct FuncData {
trampoline: VMTrampoline,
export: ExportFunction,
ty: FuncTypeIndex,
types: Arc<ComponentTypes>,
options: Options,
}
#[derive(Clone)]
#[allow(dead_code)] // FIXME: remove this when fields are actually used
pub(crate) struct Options {
string_encoding: Option<StringEncoding>,
intrinsics: Option<Intrinsics>,
}
#[derive(Clone)]
#[allow(dead_code)] // FIXME: remove this when fields are actually used
struct Intrinsics {
memory: ExportMemory,
realloc: ExportFunction,
free: ExportFunction,
}
impl Func {
pub(crate) fn from_lifted_func(
store: &mut StoreOpaque,
types: &Arc<ComponentTypes>,
instances: &PrimaryMap<RuntimeInstanceIndex, crate::Instance>,
func: &LiftedFunction,
) -> Func {
let export = match lookup(store, instances, &func.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,
}
});
Func(store.store_data_mut().insert(FuncData {
trampoline,
export,
options: Options {
intrinsics,
string_encoding: func.options.string_encoding,
},
ty: func.ty,
types: types.clone(),
}))
}
}

View File

@@ -0,0 +1,173 @@
use crate::component::{Component, Func};
use crate::instance::OwnedImports;
use crate::store::{StoreOpaque, Stored};
use crate::{AsContextMut, Module, StoreContextMut};
use anyhow::Result;
use wasmtime_environ::component::{
CoreExport, Export, ExportItem, Instantiation, RuntimeInstanceIndex,
};
use wasmtime_environ::{EntityIndex, PrimaryMap};
/// An instantiated component.
///
/// This is similar to [`crate::Instance`] except that it represents an
/// instantiated component instead of an instantiated module. Otherwise though
/// the two behave similarly.
//
// FIXME: need to write more docs here.
#[derive(Copy, Clone)]
pub struct Instance(Stored<Option<Box<InstanceData>>>);
pub(crate) struct InstanceData {
instances: PrimaryMap<RuntimeInstanceIndex, crate::Instance>,
// FIXME: shouldn't store the entire component here which keeps upvars
// alive and things like that, instead only the bare minimum necessary
// should be kept alive here (mostly just `wasmtime_environ::Component`.
component: Component,
}
impl Instance {
/// Instantiates the `component` provided within the given `store`.
///
/// Does not support components which have imports at this time.
//
// FIXME: need to write more docs here.
pub fn new(mut store: impl AsContextMut, component: &Component) -> Result<Instance> {
let mut store = store.as_context_mut();
let mut instantiator = Instantiator::new(component);
instantiator.run(&mut store)?;
let data = Box::new(InstanceData {
instances: instantiator.instances,
component: component.clone(),
});
Ok(Instance(store.0.store_data_mut().insert(Some(data))))
}
/// Looks up a function by name within this [`Instance`].
///
/// The `store` specified must be the store that this instance lives within
/// and `name` is the name of the function to lookup. If the function is
/// found `Some` is returned otherwise `None` is returned.
///
/// # Panics
///
/// Panics if `store` does not own this instance.
pub fn get_func(&self, mut store: impl AsContextMut, name: &str) -> Option<Func> {
self._get_func(store.as_context_mut().0, name)
}
fn _get_func(&self, store: &mut StoreOpaque, name: &str) -> Option<Func> {
// FIXME: this movement in ownership is unfortunate and feels like there
// should be a better solution. The reason for this is that we need to
// simultaneously look at lots of pieces of `InstanceData` while also
// inserting into `store`, but `InstanceData` is stored within `store`.
// By moving it out we appease the borrow-checker but take a runtime
// hit.
let data = store[self.0].take().unwrap();
let result = data.get_func(store, name);
store[self.0] = Some(data);
return result;
}
}
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,
)),
}
}
}
struct Instantiator<'a> {
component: &'a Component,
instances: PrimaryMap<RuntimeInstanceIndex, crate::Instance>,
imports: OwnedImports,
}
impl<'a> Instantiator<'a> {
fn new(component: &'a Component) -> Instantiator<'a> {
let env_component = component.env_component();
if env_component.imports.len() > 0 {
unimplemented!("component imports");
}
Instantiator {
component,
instances: PrimaryMap::with_capacity(env_component.instances.len()),
imports: OwnedImports::empty(),
}
}
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");
}
};
// 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);
}
Ok(())
}
fn build_imports(
&mut self,
store: &mut StoreOpaque,
module: &Module,
args: &[CoreExport<EntityIndex>],
) -> &OwnedImports {
self.imports.clear();
self.imports.reserve(module);
for arg in args {
let export = lookup(store, &self.instances, arg);
// The unsafety here should be ok since the `export` is loaded
// directly from an instance which should only give us valid export
// items.
unsafe {
self.imports.push_export(&export);
}
}
&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)
}

View File

@@ -0,0 +1,14 @@
//! In-progress implementation of the WebAssembly component model
//!
//! This module is a work-in-progress and currently represents an incomplete and
//! probably buggy implementation of the component model.
mod component;
mod func;
mod instance;
mod store;
pub use self::component::Component;
pub use self::func::Func;
pub use self::instance::Instance;
pub(crate) use self::store::ComponentStoreData;

View File

@@ -0,0 +1,28 @@
use crate::store::{StoreData, StoredData};
macro_rules! component_store_data {
($($field:ident => $t:ty,)*) => (
#[derive(Default)]
pub struct ComponentStoreData {
$($field: Vec<$t>,)*
}
$(
impl StoredData for $t {
#[inline]
fn list(data: &StoreData) -> &Vec<Self> {
&data.components.$field
}
#[inline]
fn list_mut(data: &mut StoreData) -> &mut Vec<Self> {
&mut data.components.$field
}
}
)*
)
}
component_store_data! {
funcs => crate::component::func::FuncData,
instances => Option<Box<crate::component::instance::InstanceData>>,
}

View File

@@ -619,6 +619,20 @@ impl Config {
self
}
/// Configures whether the WebAssembly component-model [proposal] will
/// be enabled for compilation.
///
/// Note that this feature is a work-in-progress and is incomplete.
///
/// This is `false` by default.
///
/// [proposal]: https://github.com/webassembly/component-model
#[cfg(feature = "component-model")]
pub fn wasm_component_model(&mut self, enable: bool) -> &mut Self {
self.features.component_model = enable;
self
}
/// Configures which compilation strategy will be used for wasm modules.
///
/// This method can be used to configure which compiler is used for wasm

View File

@@ -813,13 +813,22 @@ impl Func {
) -> Result<(), Trap> {
let mut store = store.as_context_mut();
let data = &store.0.store_data()[self.0];
let trampoline = data.trampoline();
let anyfunc = data.export().anyfunc;
invoke_wasm_and_catch_traps(&mut store, |callee| {
let trampoline = data.trampoline();
Self::call_unchecked_raw(&mut store, anyfunc, trampoline, params_and_returns)
}
pub(crate) unsafe fn call_unchecked_raw<T>(
store: &mut StoreContextMut<'_, T>,
anyfunc: NonNull<VMCallerCheckedAnyfunc>,
trampoline: VMTrampoline,
params_and_returns: *mut ValRaw,
) -> Result<(), Trap> {
invoke_wasm_and_catch_traps(store, |callee| {
trampoline(
(*anyfunc.as_ptr()).vmctx,
anyfunc.as_ref().vmctx,
callee,
(*anyfunc.as_ptr()).func_ptr.as_ptr(),
anyfunc.as_ref().func_ptr.as_ptr(),
params_and_returns,
)
})
@@ -2063,7 +2072,6 @@ impl HostFunc {
/// `Engine`. This happens automatically during the above two constructors.
fn _new(engine: &Engine, mut instance: InstanceHandle, trampoline: VMTrampoline) -> Self {
let export = instance.get_exported_func(FuncIndex::from_u32(0));
HostFunc {
instance,
trampoline,

View File

@@ -1,11 +1,11 @@
use super::{invoke_wasm_and_catch_traps, HostAbi};
use crate::store::{AutoAssertNoGc, StoreOpaque};
use crate::{AsContextMut, ExternRef, Func, StoreContextMut, Trap, ValRaw, ValType};
use crate::{AsContextMut, ExternRef, Func, FuncType, StoreContextMut, Trap, ValRaw, ValType};
use anyhow::{bail, Result};
use std::marker;
use std::mem::{self, MaybeUninit};
use std::ptr;
use wasmtime_runtime::{VMContext, VMFunctionBody};
use wasmtime_runtime::{VMCallerCheckedAnyfunc, VMContext, VMFunctionBody, VMSharedSignatureIndex};
/// A statically typed WebAssembly function.
///
@@ -76,7 +76,8 @@ where
!store.0.async_support(),
"must use `call_async` with async stores"
);
unsafe { self._call(&mut store, params) }
let func = self.func.caller_checked_anyfunc(store.0);
unsafe { Self::call_raw(&mut store, func, params) }
}
/// Invokes this WebAssembly function with the specified parameters.
@@ -106,15 +107,24 @@ where
"must use `call` with non-async stores"
);
store
.on_fiber(|store| unsafe { self._call(store, params) })
.on_fiber(|store| {
let func = self.func.caller_checked_anyfunc(store.0);
unsafe { Self::call_raw(store, func, params) }
})
.await?
}
unsafe fn _call<T>(
&self,
pub(crate) unsafe fn call_raw<T>(
store: &mut StoreContextMut<'_, T>,
func: ptr::NonNull<VMCallerCheckedAnyfunc>,
params: Params,
) -> Result<Results, Trap> {
// double-check that params/results match for this function's type in
// debug mode.
if cfg!(debug_assertions) {
Self::debug_typecheck(store.0, func.as_ref().type_index);
}
// See the comment in `Func::call_impl`'s `write_params` function.
if params.externrefs_count()
> store
@@ -150,12 +160,7 @@ where
// efficient to move in memory. This closure is actually invoked on the
// other side of a C++ shim, so it can never be inlined enough to make
// the memory go away, so the size matters here for performance.
let mut captures = (
self.func.caller_checked_anyfunc(store.0),
MaybeUninit::uninit(),
params,
false,
);
let mut captures = (func, MaybeUninit::uninit(), params, false);
let result = invoke_wasm_and_catch_traps(store, |callee| {
let (anyfunc, ret, params, returned) = &mut captures;
@@ -174,6 +179,19 @@ where
result?;
Ok(Results::from_abi(store.0, ret.assume_init()))
}
/// Purely a debug-mode assertion, not actually used in release builds.
fn debug_typecheck(store: &StoreOpaque, func: VMSharedSignatureIndex) {
let ty = FuncType::from_wasm_func_type(
store
.engine()
.signatures()
.lookup_type(func)
.expect("signature should be registered"),
);
Params::typecheck(ty.params()).expect("params should match");
Results::typecheck(ty.results()).expect("results should match");
}
}
/// A trait implemented for types which can be arguments and results for

View File

@@ -165,7 +165,7 @@ impl Instance {
/// Internal function to create an instance and run the start function.
///
/// This function's unsafety is the same as `Instance::new_raw`.
unsafe fn new_started<T>(
pub(crate) unsafe fn new_started<T>(
store: &mut StoreContextMut<'_, T>,
module: &Module,
imports: Imports<'_>,
@@ -506,9 +506,14 @@ impl Instance {
pub fn get_global(&self, store: impl AsContextMut, name: &str) -> Option<Global> {
self.get_export(store, name)?.into_global()
}
#[cfg(feature = "component-model")]
pub(crate) fn id(&self, store: &StoreOpaque) -> InstanceId {
store[self.0].id
}
}
struct OwnedImports {
pub(crate) struct OwnedImports {
functions: PrimaryMap<FuncIndex, VMFunctionImport>,
tables: PrimaryMap<TableIndex, VMTableImport>,
memories: PrimaryMap<MemoryIndex, VMMemoryImport>,
@@ -517,15 +522,36 @@ struct OwnedImports {
impl OwnedImports {
fn new(module: &Module) -> OwnedImports {
let raw = module.compiled_module().module();
let mut ret = OwnedImports::empty();
ret.reserve(module);
return ret;
}
pub(crate) fn empty() -> OwnedImports {
OwnedImports {
functions: PrimaryMap::with_capacity(raw.num_imported_funcs),
tables: PrimaryMap::with_capacity(raw.num_imported_tables),
memories: PrimaryMap::with_capacity(raw.num_imported_memories),
globals: PrimaryMap::with_capacity(raw.num_imported_globals),
functions: PrimaryMap::new(),
tables: PrimaryMap::new(),
memories: PrimaryMap::new(),
globals: PrimaryMap::new(),
}
}
pub(crate) fn reserve(&mut self, module: &Module) {
let raw = module.compiled_module().module();
self.functions.reserve(raw.num_imported_funcs);
self.tables.reserve(raw.num_imported_tables);
self.memories.reserve(raw.num_imported_memories);
self.globals.reserve(raw.num_imported_globals);
}
#[cfg(feature = "component-model")]
pub(crate) fn clear(&mut self) {
self.functions.clear();
self.tables.clear();
self.memories.clear();
self.globals.clear();
}
fn push(&mut self, item: &Extern, store: &mut StoreOpaque) {
match item {
Extern::Func(i) => {
@@ -543,7 +569,37 @@ impl OwnedImports {
}
}
fn as_ref(&self) -> Imports<'_> {
/// Note that this is unsafe as the validity of `item` is not verified and
/// it contains a bunch of raw pointers.
#[cfg(feature = "component-model")]
pub(crate) unsafe fn push_export(&mut self, item: &wasmtime_runtime::Export) {
match item {
wasmtime_runtime::Export::Function(f) => {
let f = f.anyfunc.as_ref();
self.functions.push(VMFunctionImport {
body: f.func_ptr,
vmctx: f.vmctx,
});
}
wasmtime_runtime::Export::Global(g) => {
self.globals.push(VMGlobalImport { from: g.definition });
}
wasmtime_runtime::Export::Table(t) => {
self.tables.push(VMTableImport {
from: t.definition,
vmctx: t.vmctx,
});
}
wasmtime_runtime::Export::Memory(m) => {
self.memories.push(VMMemoryImport {
from: m.definition,
vmctx: m.vmctx,
});
}
}
}
pub(crate) fn as_ref(&self) -> Imports<'_> {
Imports {
tables: self.tables.values().as_slice(),
globals: self.globals.values().as_slice(),

View File

@@ -430,6 +430,9 @@ pub use crate::trap::*;
pub use crate::types::*;
pub use crate::values::*;
#[cfg(feature = "component-model")]
pub mod component;
cfg_if::cfg_if! {
if #[cfg(all(target_os = "macos", not(feature = "posix-signals-on-macos")))] {
// no extensions for macOS at this time

View File

@@ -11,9 +11,11 @@ use std::ops::Range;
use std::path::Path;
use std::sync::Arc;
use wasmparser::{Parser, ValidPayload, Validator};
#[cfg(feature = "component-model")]
use wasmtime_environ::component::ComponentTypes;
use wasmtime_environ::{
DefinedFuncIndex, DefinedMemoryIndex, FunctionInfo, ModuleEnvironment, PrimaryMap,
SignatureIndex, TypeTables,
DefinedFuncIndex, DefinedMemoryIndex, FunctionInfo, ModuleEnvironment, ModuleTranslation,
ModuleTypes, PrimaryMap, SignatureIndex,
};
use wasmtime_jit::{CompiledModule, CompiledModuleInfo};
use wasmtime_runtime::{
@@ -105,7 +107,7 @@ struct ModuleInner {
/// executed.
module: Arc<CompiledModule>,
/// Type information of this module.
types: TypeTables,
types: Types,
/// Registered shared signature for the module.
signatures: SignatureCollection,
/// A set of initialization images for memories, if any.
@@ -340,26 +342,41 @@ impl Module {
pub(crate) fn build_artifacts(
engine: &Engine,
wasm: &[u8],
) -> Result<(MmapVec, Option<CompiledModuleInfo>, TypeTables)> {
) -> Result<(MmapVec, Option<CompiledModuleInfo>, ModuleTypes)> {
let tunables = &engine.config().tunables;
// First a `ModuleEnvironment` is created which records type information
// about the wasm module. This is where the WebAssembly is parsed and
// validated. Afterwards `types` will have all the type information for
// this module.
let (mut translation, types) = ModuleEnvironment::new(tunables, &engine.config().features)
.translate(wasm)
let mut validator =
wasmparser::Validator::new_with_features(engine.config().features.clone());
let parser = wasmparser::Parser::new(0);
let mut types = Default::default();
let translation = ModuleEnvironment::new(tunables, &mut validator, &mut types)
.translate(parser, wasm)
.context("failed to parse WebAssembly module")?;
let types = types.finish();
let (mmap, info) = Module::compile_functions(engine, translation, &types)?;
Ok((mmap, info, types))
}
// Next compile all functions in parallel using rayon. This will perform
// the actual validation of all the function bodies.
#[cfg(compiler)]
pub(crate) fn compile_functions(
engine: &Engine,
mut translation: ModuleTranslation<'_>,
types: &ModuleTypes,
) -> Result<(MmapVec, Option<CompiledModuleInfo>)> {
// Compile all functions in parallel using rayon. This will also perform
// validation of function bodies.
let tunables = &engine.config().tunables;
let functions = mem::take(&mut translation.function_body_inputs);
let functions = functions.into_iter().collect::<Vec<_>>();
let funcs = engine
.run_maybe_parallel(functions, |(index, func)| {
engine
.compiler()
.compile_function(&translation, index, func, tunables, &types)
.compile_function(&translation, index, func, tunables, types)
})?
.into_iter()
.collect();
@@ -369,7 +386,7 @@ impl Module {
let (funcs, trampolines) =
engine
.compiler()
.emit_obj(&translation, &types, funcs, tunables, &mut obj)?;
.emit_obj(&translation, types, funcs, tunables, &mut obj)?;
// If configured attempt to use static memory initialization which
// can either at runtime be implemented as a single memcpy to
@@ -389,7 +406,7 @@ impl Module {
let (mmap, info) =
wasmtime_jit::finish_compile(translation, obj, funcs, trampolines, tunables)?;
Ok((mmap, Some(info), types))
Ok((mmap, Some(info)))
}
/// Deserializes an in-memory compiled module previously created with
@@ -467,11 +484,11 @@ impl Module {
module.into_module(engine)
}
fn from_parts(
pub(crate) fn from_parts(
engine: &Engine,
mmap: MmapVec,
info: Option<CompiledModuleInfo>,
types: TypeTables,
types: impl Into<Types>,
) -> Result<Self> {
let module = Arc::new(CompiledModule::from_artifacts(
mmap,
@@ -483,9 +500,10 @@ impl Module {
// Validate the module can be used with the current allocator
engine.allocator().validate(module.module())?;
let types = types.into();
let signatures = SignatureCollection::new_for_module(
engine.signatures(),
&types,
types.module_types(),
module.trampolines().map(|(idx, f, _)| (idx, f)),
);
@@ -530,9 +548,15 @@ impl Module {
let mut functions = Vec::new();
for payload in Parser::new(0).parse_all(binary) {
if let ValidPayload::Func(a, b) = validator.payload(&payload?)? {
let payload = payload?;
if let ValidPayload::Func(a, b) = validator.payload(&payload)? {
functions.push((a, b));
}
if let wasmparser::Payload::Version { encoding, .. } = &payload {
if let wasmparser::Encoding::Component = encoding {
bail!("component passed to module validation");
}
}
}
engine.run_maybe_parallel(functions, |(mut validator, body)| validator.validate(&body))?;
@@ -562,8 +586,8 @@ impl Module {
self.compiled_module().module()
}
pub(crate) fn types(&self) -> &TypeTables {
&self.inner.types
pub(crate) fn types(&self) -> &ModuleTypes {
self.inner.types.module_types()
}
pub(crate) fn signatures(&self) -> &SignatureCollection {
@@ -1037,6 +1061,35 @@ impl wasmtime_runtime::ModuleRuntimeInfo for BareModuleInfo {
}
}
pub(crate) enum Types {
Module(ModuleTypes),
#[cfg(feature = "component-model")]
Component(Arc<ComponentTypes>),
}
impl Types {
fn module_types(&self) -> &ModuleTypes {
match self {
Types::Module(m) => m,
#[cfg(feature = "component-model")]
Types::Component(c) => c.module_types(),
}
}
}
impl From<ModuleTypes> for Types {
fn from(types: ModuleTypes) -> Types {
Types::Module(types)
}
}
#[cfg(feature = "component-model")]
impl From<Arc<ComponentTypes>> for Types {
fn from(types: Arc<ComponentTypes>) -> Types {
Types::Component(types)
}
}
/// Helper method to construct a `ModuleMemoryImages` for an associated
/// `CompiledModule`.
fn memory_images(engine: &Engine, module: &CompiledModule) -> Result<Option<ModuleMemoryImages>> {

View File

@@ -7,7 +7,7 @@
//! There are two main pieces of data associated with a binary artifact:
//!
//! 1. The compiled module image, currently an ELF file.
//! 2. Compilation metadata for the module, including the `TypeTables`
//! 2. Compilation metadata for the module, including the `ModuleTypes`
//! information. This metadata is validated for compilation settings.
//!
//! Compiled modules are, at this time, represented as an ELF file. This ELF
@@ -47,7 +47,7 @@ use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
use std::path::Path;
use std::str::FromStr;
use wasmtime_environ::{FlagValue, Tunables, TypeTables};
use wasmtime_environ::{FlagValue, ModuleTypes, Tunables};
use wasmtime_jit::{subslice_range, CompiledModuleInfo};
use wasmtime_runtime::MmapVec;
@@ -166,7 +166,7 @@ struct Metadata<'a> {
isa_flags: BTreeMap<String, FlagValue>,
tunables: Tunables,
features: WasmFeatures,
types: MyCow<'a, TypeTables>,
types: MyCow<'a, ModuleTypes>,
}
impl<'a> SerializedModule<'a> {
@@ -180,7 +180,7 @@ impl<'a> SerializedModule<'a> {
}
#[cfg(compiler)]
pub fn from_artifacts(engine: &Engine, artifacts: &'a MmapVec, types: &'a TypeTables) -> Self {
pub fn from_artifacts(engine: &Engine, artifacts: &'a MmapVec, types: &'a ModuleTypes) -> Self {
Self::with_data(engine, MyCow::Borrowed(artifacts), MyCow::Borrowed(types))
}
@@ -188,7 +188,7 @@ impl<'a> SerializedModule<'a> {
fn with_data(
engine: &Engine,
artifacts: MyCow<'a, MmapVec>,
types: MyCow<'a, TypeTables>,
types: MyCow<'a, ModuleTypes>,
) -> Self {
Self {
artifacts,
@@ -211,7 +211,7 @@ impl<'a> SerializedModule<'a> {
pub fn into_parts(
mut self,
engine: &Engine,
) -> Result<(MmapVec, Option<CompiledModuleInfo>, TypeTables)> {
) -> Result<(MmapVec, Option<CompiledModuleInfo>, ModuleTypes)> {
// Verify that the compilation settings in the engine match the
// compilation settings of the module that's being loaded.
self.check_triple(engine)?;

View File

@@ -6,7 +6,7 @@ use std::{
sync::RwLock,
};
use std::{convert::TryFrom, sync::Arc};
use wasmtime_environ::{PrimaryMap, SignatureIndex, TypeTables, WasmFuncType};
use wasmtime_environ::{ModuleTypes, PrimaryMap, SignatureIndex, WasmFuncType};
use wasmtime_runtime::{VMSharedSignatureIndex, VMTrampoline};
/// Represents a collection of shared signatures.
@@ -27,7 +27,7 @@ impl SignatureCollection {
/// and trampolines.
pub fn new_for_module(
registry: &SignatureRegistry,
types: &TypeTables,
types: &ModuleTypes,
trampolines: impl Iterator<Item = (SignatureIndex, VMTrampoline)>,
) -> Self {
let (signatures, trampolines) = registry
@@ -89,7 +89,7 @@ struct SignatureRegistryInner {
impl SignatureRegistryInner {
fn register_for_module(
&mut self,
types: &TypeTables,
types: &ModuleTypes,
trampolines: impl Iterator<Item = (SignatureIndex, VMTrampoline)>,
) -> (
PrimaryMap<SignatureIndex, VMSharedSignatureIndex>,

View File

@@ -20,6 +20,8 @@ pub struct StoreData {
globals: Vec<wasmtime_runtime::ExportGlobal>,
instances: Vec<crate::instance::InstanceData>,
memories: Vec<wasmtime_runtime::ExportMemory>,
#[cfg(feature = "component-model")]
pub(crate) components: crate::component::ComponentStoreData,
}
pub trait StoredData: Sized {
@@ -65,6 +67,8 @@ impl StoreData {
globals: Vec::new(),
instances: Vec::new(),
memories: Vec::new(),
#[cfg(feature = "component-model")]
components: Default::default(),
}
}

View File

@@ -1,5 +1,5 @@
use std::fmt;
use wasmtime_environ::{EntityType, Global, Memory, Table, TypeTables, WasmFuncType, WasmType};
use wasmtime_environ::{EntityType, Global, Memory, ModuleTypes, Table, WasmFuncType, WasmType};
pub(crate) mod matching;
@@ -147,7 +147,7 @@ impl ExternType {
(Memory(MemoryType) memory unwrap_memory)
}
pub(crate) fn from_wasmtime(types: &TypeTables, ty: &EntityType) -> ExternType {
pub(crate) fn from_wasmtime(types: &ModuleTypes, ty: &EntityType) -> ExternType {
match ty {
EntityType::Function(idx) => FuncType::from_wasm_func_type(types[*idx].clone()).into(),
EntityType::Global(ty) => GlobalType::from_wasmtime_global(ty).into(),
@@ -427,7 +427,7 @@ pub struct ImportType<'module> {
/// The type of the import.
ty: EntityType,
types: &'module TypeTables,
types: &'module ModuleTypes,
}
impl<'module> ImportType<'module> {
@@ -437,7 +437,7 @@ impl<'module> ImportType<'module> {
module: &'module str,
name: &'module str,
ty: EntityType,
types: &'module TypeTables,
types: &'module ModuleTypes,
) -> ImportType<'module> {
ImportType {
module,
@@ -489,7 +489,7 @@ pub struct ExportType<'module> {
/// The type of the export.
ty: EntityType,
types: &'module TypeTables,
types: &'module ModuleTypes,
}
impl<'module> ExportType<'module> {
@@ -498,7 +498,7 @@ impl<'module> ExportType<'module> {
pub(crate) fn new(
name: &'module str,
ty: EntityType,
types: &'module TypeTables,
types: &'module ModuleTypes,
) -> ExportType<'module> {
ExportType { name, ty, types }
}

View File

@@ -3,13 +3,13 @@ use crate::store::StoreOpaque;
use crate::{signatures::SignatureCollection, Engine, Extern};
use anyhow::{bail, Result};
use wasmtime_environ::{
EntityType, Global, Memory, SignatureIndex, Table, TypeTables, WasmFuncType, WasmType,
EntityType, Global, Memory, ModuleTypes, SignatureIndex, Table, WasmFuncType, WasmType,
};
use wasmtime_runtime::VMSharedSignatureIndex;
pub struct MatchCx<'a> {
pub signatures: &'a SignatureCollection,
pub types: &'a TypeTables,
pub types: &'a ModuleTypes,
pub store: &'a StoreOpaque,
pub engine: &'a Engine,
}