Implement the module linking alias section (#2451)

This commit is intended to do almost everything necessary for processing
the alias section of module linking. Most of this is internal
refactoring, the highlights being:

* Type contents are now stored separately from a `wasmtime_env::Module`.
  Given that modules can freely alias types and have them used all over
  the place, it seemed best to have one canonical location to type
  storage which everywhere else points to (with indices). A new
  `TypeTables` structure is produced during compilation which is shared
  amongst all member modules in a wasm blob.

* Instantiation is heavily refactored to account for module linking. The
  main gotcha here is that imports are now listed as "initializers". We
  have a sort of pseudo-bytecode-interpreter which interprets the
  initialization of a module. This is more complicated than just
  matching imports at this point because in the module linking proposal
  the module, alias, import, and instance sections may all be
  interleaved. This means that imports aren't guaranteed to show up at
  the beginning of the address space for modules/instances.

Otherwise most of the changes here largely fell out from these two
design points. Aliases are recorded as initializers in this scheme.
Copying around type information and/or just knowing type information
during compilation is also pretty easy since everything is just a
pointer into a `TypeTables` and we don't have to actually copy any types
themselves. Lots of various refactorings were necessary to accomodate
these changes.

Tests are hoped to cover a breadth of functionality here, but not
necessarily a depth. There's still one more piece of the module linking
proposal missing which is exporting instances/modules, which will come
in a future PR.

It's also worth nothing that there's one large TODO which isn't
implemented in this change that I plan on opening an issue for.
With module linking when a set of modules comes back from compilation
each modules has all the trampolines for the entire set of modules. This
is quite a lot of duplicate trampolines across module-linking modules.
We'll want to refactor this at some point to instead have only one set
of trampolines per set of module linking modules and have them shared
from there. I figured it was best to separate out this change, however,
since it's purely related to resource usage, and doesn't impact
non-module-linking modules at all.

cc #2094
This commit is contained in:
Alex Crichton
2020-12-02 17:24:06 -06:00
committed by GitHub
parent a548516f97
commit 9ac7d01288
34 changed files with 1322 additions and 521 deletions

View File

@@ -115,6 +115,15 @@ impl Extern {
};
Store::same(my_store, store)
}
pub(crate) fn desc(&self) -> &'static str {
match self {
Extern::Func(_) => "function",
Extern::Table(_) => "table",
Extern::Memory(_) => "memory",
Extern::Global(_) => "global",
}
}
}
impl From<Func> for Extern {

View File

@@ -1,74 +1,158 @@
use crate::trampoline::StoreInstanceHandle;
use crate::{Engine, Export, Extern, Func, Global, Memory, Module, Store, Table, Trap};
use anyhow::{bail, Error, Result};
use anyhow::{bail, Context, Error, Result};
use std::mem;
use wasmtime_environ::entity::PrimaryMap;
use wasmtime_environ::wasm::{EntityIndex, FuncIndex, GlobalIndex, MemoryIndex, TableIndex};
use wasmtime_jit::CompiledModule;
use wasmtime_environ::wasm::{
EntityIndex, FuncIndex, GlobalIndex, InstanceIndex, MemoryIndex, ModuleIndex, TableIndex,
};
use wasmtime_environ::Initializer;
use wasmtime_jit::{CompiledModule, TypeTables};
use wasmtime_runtime::{
Imports, InstantiationError, StackMapRegistry, VMContext, VMExternRefActivationsTable,
VMFunctionBody, VMFunctionImport, VMGlobalImport, VMMemoryImport, VMTableImport,
};
fn instantiate(
store: &Store,
compiled_module: &CompiledModule,
all_modules: &[CompiledModule],
imports: &mut ImportsBuilder<'_>,
/// Performs all low-level steps necessary for instantiation.
///
/// This function will take all the arguments and attempt to do everything
/// necessary to instantiate the referenced instance. The trickiness of this
/// function stems from the implementation of the module-linking proposal where
/// we're handling nested instances, interleaved imports/aliases, etc. That's
/// all an internal implementation here ideally though!
///
/// * `store` - the store we're instantiating into
/// * `compiled_module` - the module that we're instantiating
/// * `all_modules` - the list of all modules that were part of the compilation
/// of `compiled_module`. This is only applicable in the module linking
/// proposal, otherwise this will just be a list containing `compiled_module`
/// itself.
/// * `type` - the type tables produced during compilation which
/// `compiled_module`'s metadata references.
/// * `parent_modules` - this is the list of compiled modules the parent has.
/// This is only applicable on recursive instantiations.
/// * `define_import` - this function, like the name implies, defines an import
/// into the provided builder. The expected entity that it's defining is also
/// passed in for the top-level case where type-checking is performed. This is
/// fallible because type checks may fail.
fn instantiate<'a>(
store: &'a Store,
compiled_module: &'a CompiledModule,
all_modules: &'a [CompiledModule],
types: &'a TypeTables,
parent_modules: &PrimaryMap<ModuleIndex, &'a CompiledModule>,
define_import: &mut dyn FnMut(&EntityIndex, &mut ImportsBuilder<'a>) -> Result<()>,
) -> Result<StoreInstanceHandle, Error> {
let env_module = compiled_module.module();
// The first part of instantiating any module is to first follow any
// `instantiate` instructions it has as part of the module linking
// proposal. Here we iterate overall those instructions and create the
// instances as necessary.
for instance in env_module.instances.values() {
let (module_idx, args) = match instance {
wasmtime_environ::Instance::Instantiate { module, args } => (*module, args),
wasmtime_environ::Instance::Import(_) => continue,
};
// Translate the `module_idx` to a top-level module `usize` and then
// use that to extract the child `&CompiledModule` itself. Then we can
// iterate over each of the arguments provided to satisfy its imports.
//
// Note that we directly reach into `imports` below based on indexes
// and push raw value into how to instantiate our submodule. This should
// be safe due to wasm validation ensuring that all our indices are
// in-bounds and all the expected types and such line up.
let module_idx = compiled_module.submodule_idx(module_idx);
let compiled_module = &all_modules[module_idx];
let mut builder = ImportsBuilder::new(compiled_module.module(), store);
for arg in args {
match *arg {
EntityIndex::Global(i) => {
builder.globals.push(imports.globals[i]);
}
EntityIndex::Table(i) => {
builder.tables.push(imports.tables[i]);
}
EntityIndex::Function(i) => {
builder.functions.push(imports.functions[i]);
}
EntityIndex::Memory(i) => {
builder.memories.push(imports.memories[i]);
}
EntityIndex::Module(_) => unimplemented!(),
EntityIndex::Instance(_) => unimplemented!(),
let mut imports = ImportsBuilder::new(env_module, types, store);
for initializer in env_module.initializers.iter() {
match initializer {
// Definition of an import depends on how our parent is providing
// imports, so we delegate to our custom closure. This will resolve
// to fetching from the import list for the top-level module and
// otherwise fetching from each nested instance's argument list for
// submodules.
Initializer::Import {
index,
module,
field,
} => {
define_import(index, &mut imports).with_context(|| match field {
Some(name) => format!("incompatible import type for `{}::{}`", module, name),
None => format!("incompatible import type for `{}`", module),
})?;
}
// This one's pretty easy, we're just picking up our parent's module
// and putting it into our own index space.
Initializer::AliasParentModule(idx) => {
imports.modules.push(parent_modules[*idx]);
}
// Turns out defining any kind of module is pretty easy, we're just
// slinging around pointers.
Initializer::DefineModule(idx) => {
imports.modules.push(&all_modules[*idx]);
}
// Here we lookup our instance handle, ask it for the nth export,
// and then push that item into our own index space. We eschew
// type-checking since only valid modules reach this point.
Initializer::AliasInstanceExport { instance, export } => {
let handle = &imports.instances[*instance];
let export_index = &handle.module().exports[*export];
let item = Extern::from_wasmtime_export(
handle.lookup_by_declaration(export_index),
handle.clone(),
);
imports.push_extern(&item);
}
// Oh boy a recursive instantiation! The recursive arguments here
// are pretty simple, and the only slightly-meaty one is how
// arguments are pulled from `args` and pushed directly into the
// builder specified, which should be an easy enough
// copy-the-pointer operation in all cases.
//
// Note that this recursive call shouldn't result in an infinite
// loop because of wasm module validation which requires everything
// to be a DAG. Additionally the recursion should also be bounded
// due to validation. We may one day need to make this an iterative
// loop, however.
Initializer::Instantiate { module, args } => {
let module_to_instantiate = imports.modules[*module];
let mut args = args.iter();
let handle = instantiate(
store,
module_to_instantiate,
all_modules,
types,
&imports.modules,
&mut |_, builder| {
match *args.next().unwrap() {
EntityIndex::Global(i) => {
builder.globals.push(imports.globals[i]);
}
EntityIndex::Function(i) => {
builder.functions.push(imports.functions[i]);
}
EntityIndex::Table(i) => {
builder.tables.push(imports.tables[i]);
}
EntityIndex::Memory(i) => {
builder.memories.push(imports.memories[i]);
}
EntityIndex::Module(i) => {
builder.modules.push(imports.modules[i]);
}
EntityIndex::Instance(i) => {
builder.instances.push(imports.instances[i].clone());
}
}
Ok(())
},
)?;
imports.instances.push(handle);
}
}
instantiate(store, compiled_module, all_modules, &mut builder)?;
}
// With the above initialization done we've now acquired the final set of
// imports in all the right index spaces and everything. Time to carry on
// with the creation of our own instance.
let imports = imports.imports();
// Register the module just before instantiation to ensure we have a
// trampoline registered for every signature and to preserve the module's
// compiled JIT code within the `Store`.
store.register_module(compiled_module);
store.register_module(compiled_module, types);
let config = store.engine().config();
let instance = unsafe {
let instance = compiled_module.instantiate(
imports.imports(),
&store.lookup_shared_signature(compiled_module.module()),
imports,
&store.lookup_shared_signature(types),
config.memory_creator.as_ref().map(|a| a as _),
store.interrupts(),
Box::new(()),
@@ -208,26 +292,38 @@ impl Instance {
bail!("cross-`Engine` instantiation is not currently supported");
}
let mut builder = ImportsBuilder::new(module.compiled_module().module(), store);
// Perform some pre-flight checks before we get into the meat of
// instantiation.
let expected = module
.compiled_module()
.module()
.initializers
.iter()
.filter(|e| match e {
Initializer::Import { .. } => true,
_ => false,
})
.count();
if expected != imports.len() {
bail!("expected {} imports, found {}", expected, imports.len());
}
for import in imports {
// For now we have a restriction that the `Store` that we're working
// with is the same for everything involved here.
if !import.comes_from_same_store(store) {
bail!("cross-`Store` instantiation is not currently supported");
}
match import {
Extern::Global(e) => builder.global(e)?,
Extern::Func(e) => builder.func(e)?,
Extern::Table(e) => builder.table(e)?,
Extern::Memory(e) => builder.memory(e)?,
}
}
builder.validate_all_imports_provided()?;
let mut imports = imports.iter();
let handle = instantiate(
store,
module.compiled_module(),
&module.compiled,
&mut builder,
module.all_compiled_modules(),
module.types(),
&PrimaryMap::new(),
&mut |idx, builder| {
let import = imports.next().expect("already checked the length");
builder.define_extern(idx, import)
},
)?;
Ok(Instance {
@@ -304,113 +400,105 @@ struct ImportsBuilder<'a> {
tables: PrimaryMap<TableIndex, VMTableImport>,
memories: PrimaryMap<MemoryIndex, VMMemoryImport>,
globals: PrimaryMap<GlobalIndex, VMGlobalImport>,
instances: PrimaryMap<InstanceIndex, StoreInstanceHandle>,
modules: PrimaryMap<ModuleIndex, &'a CompiledModule>,
module: &'a wasmtime_environ::Module,
imports: std::slice::Iter<'a, (String, Option<String>, EntityIndex)>,
store: &'a Store,
types: &'a TypeTables,
}
impl<'a> ImportsBuilder<'a> {
fn new(module: &'a wasmtime_environ::Module, store: &'a Store) -> ImportsBuilder<'a> {
fn new(
module: &'a wasmtime_environ::Module,
types: &'a TypeTables,
store: &'a Store,
) -> ImportsBuilder<'a> {
ImportsBuilder {
imports: module.imports.iter(),
module,
store,
types,
functions: PrimaryMap::with_capacity(module.num_imported_funcs),
tables: PrimaryMap::with_capacity(module.num_imported_tables),
memories: PrimaryMap::with_capacity(module.num_imported_memories),
globals: PrimaryMap::with_capacity(module.num_imported_globals),
instances: PrimaryMap::with_capacity(module.instances.len()),
modules: PrimaryMap::with_capacity(module.modules.len()),
}
}
fn next_import(
&mut self,
found: &str,
get: impl FnOnce(&wasmtime_environ::Module, &EntityIndex) -> Option<bool>,
) -> Result<()> {
match self.imports.next() {
Some((module, field, idx)) => {
let error = match get(self.module, idx) {
Some(true) => return Ok(()),
Some(false) => {
anyhow::anyhow!("{} types incompatible", found)
fn define_extern(&mut self, expected: &EntityIndex, actual: &Extern) -> Result<()> {
match *expected {
EntityIndex::Table(i) => {
self.tables.push(match actual {
Extern::Table(e) if e.matches_expected(&self.module.table_plans[i]) => {
e.vmimport()
}
None => {
let desc = match idx {
EntityIndex::Table(_) => "table",
EntityIndex::Function(_) => "func",
EntityIndex::Memory(_) => "memory",
EntityIndex::Global(_) => "global",
EntityIndex::Instance(_) => "instance",
EntityIndex::Module(_) => "module",
};
anyhow::anyhow!("expected {}, but found {}", desc, found)
}
};
let import_name = match field {
Some(name) => format!("{}/{}", module, name),
None => module.to_string(),
};
Err(error.context(format!("incompatible import type for {}", import_name)))
Extern::Table(_) => bail!("table types incompatible"),
_ => bail!("expected table, but found {}", actual.desc()),
});
}
None => bail!("too many imports provided"),
}
}
fn global(&mut self, global: &Global) -> Result<()> {
self.next_import("global", |m, e| match e {
EntityIndex::Global(i) => Some(global.matches_expected(&m.globals[*i])),
_ => None,
})?;
self.globals.push(global.vmimport());
Ok(())
}
fn memory(&mut self, mem: &Memory) -> Result<()> {
self.next_import("memory", |m, e| match e {
EntityIndex::Memory(i) => Some(mem.matches_expected(&m.memory_plans[*i])),
_ => None,
})?;
self.memories.push(mem.vmimport());
Ok(())
}
fn table(&mut self, table: &Table) -> Result<()> {
self.next_import("table", |m, e| match e {
EntityIndex::Table(i) => Some(table.matches_expected(&m.table_plans[*i])),
_ => None,
})?;
self.tables.push(table.vmimport());
Ok(())
}
fn func(&mut self, func: &Func) -> Result<()> {
let store = self.store;
self.next_import("func", |m, e| match e {
EntityIndex::Function(i) => Some(
EntityIndex::Memory(i) => {
self.memories.push(match actual {
Extern::Memory(e) if e.matches_expected(&self.module.memory_plans[i]) => {
e.vmimport()
}
Extern::Memory(_) => bail!("memory types incompatible"),
_ => bail!("expected memory, but found {}", actual.desc()),
});
}
EntityIndex::Global(i) => {
self.globals.push(match actual {
Extern::Global(e) if e.matches_expected(&self.module.globals[i]) => {
e.vmimport()
}
Extern::Global(_) => bail!("global types incompatible"),
_ => bail!("expected global, but found {}", actual.desc()),
});
}
EntityIndex::Function(i) => {
let func = match actual {
Extern::Func(e) => e,
_ => bail!("expected function, but found {}", actual.desc()),
};
// Look up the `i`th function's type from the module in our
// signature registry. If it's not present then we have no
// functions registered with that type, so `func` is guaranteed
// to not match.
match store
let ty = self
.store
.signatures()
.borrow()
.lookup(&m.signatures[m.functions[*i]])
{
Some(ty) => func.matches_expected(ty),
None => false,
},
),
_ => None,
})?;
self.functions.push(func.vmimport());
.lookup(&self.types.wasm_signatures[self.module.functions[i]])
.ok_or_else(|| anyhow::format_err!("function types incompatible"))?;
if !func.matches_expected(ty) {
bail!("function types incompatible");
}
self.functions.push(func.vmimport());
}
// FIXME(#2094)
EntityIndex::Module(_i) => unimplemented!(),
EntityIndex::Instance(_i) => unimplemented!(),
}
Ok(())
}
fn validate_all_imports_provided(&mut self) -> Result<()> {
if self.imports.next().is_some() {
bail!("not enough imports provided");
fn push_extern(&mut self, item: &Extern) {
match item {
Extern::Func(i) => {
self.functions.push(i.vmimport());
}
Extern::Global(i) => {
self.globals.push(i.vmimport());
}
Extern::Table(i) => {
self.tables.push(i.vmimport());
}
Extern::Memory(i) => {
self.memories.push(i.vmimport());
}
}
Ok(())
}
fn imports(&self) -> Imports<'_> {

View File

@@ -8,7 +8,7 @@ use std::sync::Arc;
use wasmparser::Validator;
#[cfg(feature = "cache")]
use wasmtime_cache::ModuleCacheEntry;
use wasmtime_jit::{CompilationArtifacts, CompiledModule};
use wasmtime_jit::{CompilationArtifacts, CompiledModule, TypeTables};
/// A compiled WebAssembly module, ready to be instantiated.
///
@@ -81,10 +81,15 @@ use wasmtime_jit::{CompilationArtifacts, CompiledModule};
#[derive(Clone)]
pub struct Module {
engine: Engine,
pub(crate) compiled: Arc<[CompiledModule]>,
data: Arc<ModuleData>,
index: usize,
}
pub(crate) struct ModuleData {
pub(crate) types: TypeTables,
pub(crate) modules: Vec<CompiledModule>,
}
impl Module {
/// Creates a new WebAssembly `Module` from the given in-memory `bytes`.
///
@@ -164,7 +169,7 @@ impl Module {
/// See [`Module::new`] for other details.
pub fn new_with_name(engine: &Engine, bytes: impl AsRef<[u8]>, name: &str) -> Result<Module> {
let mut module = Module::new(engine, bytes.as_ref())?;
Arc::get_mut(&mut module.compiled).unwrap()[module.index]
Arc::get_mut(&mut module.data).unwrap().modules[module.index]
.module_mut()
.expect("mutable module")
.name = Some(name.to_string());
@@ -240,14 +245,14 @@ impl Module {
/// ```
pub fn from_binary(engine: &Engine, binary: &[u8]) -> Result<Module> {
#[cfg(feature = "cache")]
let artifacts = ModuleCacheEntry::new("wasmtime", engine.cache_config())
let (artifacts, types) = ModuleCacheEntry::new("wasmtime", engine.cache_config())
.get_data((engine.compiler(), binary), |(compiler, binary)| {
CompilationArtifacts::build(compiler, binary)
})?;
#[cfg(not(feature = "cache"))]
let artifacts = CompilationArtifacts::build(engine.compiler(), binary)?;
let (artifacts, types) = CompilationArtifacts::build(engine.compiler(), binary)?;
let compiled = CompiledModule::from_artifacts_list(
let modules = CompiledModule::from_artifacts_list(
artifacts,
engine.compiler().isa(),
&*engine.config().profiler,
@@ -255,8 +260,8 @@ impl Module {
Ok(Module {
engine: engine.clone(),
index: compiled.len() - 1,
compiled: compiled.into(),
index: 0,
data: Arc::new(ModuleData { types, modules }),
})
}
@@ -290,10 +295,12 @@ impl Module {
pub fn serialize(&self) -> Result<Vec<u8>> {
let artifacts = (
compiler_fingerprint(&self.engine),
self.compiled
self.data
.modules
.iter()
.map(|i| i.compilation_artifacts())
.collect::<Vec<_>>(),
&self.data.types,
self.index,
);
@@ -313,14 +320,14 @@ impl Module {
pub fn deserialize(engine: &Engine, serialized: &[u8]) -> Result<Module> {
let expected_fingerprint = compiler_fingerprint(engine);
let (fingerprint, artifacts, index) = bincode_options()
.deserialize::<(u64, _, _)>(serialized)
let (fingerprint, artifacts, types, index) = bincode_options()
.deserialize::<(u64, _, _, _)>(serialized)
.context("Deserialize compilation artifacts")?;
if fingerprint != expected_fingerprint {
bail!("Incompatible compilation artifact");
}
let compiled = CompiledModule::from_artifacts_list(
let modules = CompiledModule::from_artifacts_list(
artifacts,
engine.compiler().isa(),
&*engine.config().profiler,
@@ -329,12 +336,20 @@ impl Module {
Ok(Module {
engine: engine.clone(),
index,
compiled: compiled.into(),
data: Arc::new(ModuleData { modules, types }),
})
}
pub(crate) fn compiled_module(&self) -> &CompiledModule {
&self.compiled[self.index]
&self.data.modules[self.index]
}
pub(crate) fn all_compiled_modules(&self) -> &[CompiledModule] {
&self.data.modules
}
pub(crate) fn types(&self) -> &TypeTables {
&self.data.types
}
/// Returns identifier/name that this [`Module`] has. This name
@@ -419,12 +434,21 @@ impl Module {
) -> impl ExactSizeIterator<Item = ImportType<'module>> + 'module {
let module = self.compiled_module().module();
module
.imports
.initializers
.iter()
.map(move |(module_name, name, entity_index)| {
let r#type = EntityType::new(entity_index, module);
ImportType::new(module_name, name.as_deref(), r#type)
.filter_map(move |initializer| match initializer {
wasmtime_environ::Initializer::Import {
module,
field,
index,
} => {
let ty = EntityType::new(index, self);
Some(ImportType::new(module, field.as_deref(), ty))
}
_ => None,
})
.collect::<Vec<_>>()
.into_iter()
}
/// Returns the list of exports that this [`Module`] has and will be
@@ -486,8 +510,8 @@ impl Module {
) -> impl ExactSizeIterator<Item = ExportType<'module>> + 'module {
let module = self.compiled_module().module();
module.exports.iter().map(move |(name, entity_index)| {
let r#type = EntityType::new(entity_index, module);
ExportType::new(name, r#type)
let ty = EntityType::new(entity_index, self);
ExportType::new(name, ty)
})
}
@@ -537,7 +561,7 @@ impl Module {
pub fn get_export<'module>(&'module self, name: &'module str) -> Option<ExternType> {
let module = self.compiled_module().module();
let entity_index = module.exports.get(name)?;
Some(EntityType::new(entity_index, module).extern_type())
Some(EntityType::new(entity_index, self).extern_type())
}
/// Returns the [`Engine`] that this [`Module`] was compiled by.

View File

@@ -11,7 +11,7 @@ use std::hash::{Hash, Hasher};
use std::rc::{Rc, Weak};
use std::sync::Arc;
use wasmtime_environ::wasm;
use wasmtime_jit::{CompiledModule, ModuleCode};
use wasmtime_jit::{CompiledModule, ModuleCode, TypeTables};
use wasmtime_runtime::{
InstanceHandle, RuntimeMemoryCreator, SignalHandler, StackMapRegistry, TrapInfo, VMExternRef,
VMExternRefActivationsTable, VMInterrupts, VMSharedSignatureIndex,
@@ -137,17 +137,17 @@ impl Store {
pub(crate) fn lookup_shared_signature<'a>(
&'a self,
module: &'a wasmtime_environ::Module,
types: &'a TypeTables,
) -> impl Fn(wasm::SignatureIndex) -> VMSharedSignatureIndex + 'a {
move |index| {
self.signatures()
.borrow()
.lookup(&module.signatures[index])
.lookup(&types.wasm_signatures[index])
.expect("signature not previously registered")
}
}
pub(crate) fn register_module(&self, module: &CompiledModule) {
pub(crate) fn register_module(&self, module: &CompiledModule, types: &TypeTables) {
// All modules register their JIT code in a store for two reasons
// currently:
//
@@ -169,7 +169,7 @@ impl Store {
// once-per-module (and once-per-signature). This allows us to create
// a `Func` wrapper for any function in the module, which requires that
// we know about the signature and trampoline for all instances.
self.register_signatures(module);
self.register_signatures(module, types);
// And finally with a module being instantiated into this `Store` we
// need to preserve its jit-code. References to this module's code and
@@ -205,11 +205,10 @@ impl Store {
}));
}
fn register_signatures(&self, module: &CompiledModule) {
fn register_signatures(&self, module: &CompiledModule, types: &TypeTables) {
let trampolines = module.trampolines();
let module = module.module();
let mut signatures = self.signatures().borrow_mut();
for (index, wasm) in module.signatures.iter() {
for (index, wasm) in types.wasm_signatures.iter() {
signatures.register(wasm, trampolines[index]);
}
}

View File

@@ -10,7 +10,7 @@ use wasmtime_environ::wasm::DefinedFuncIndex;
use wasmtime_environ::Module;
use wasmtime_runtime::{
Imports, InstanceHandle, StackMapRegistry, VMExternRefActivationsTable, VMFunctionBody,
VMFunctionImport,
VMFunctionImport, VMSharedSignatureIndex,
};
pub(crate) fn create_handle(
@@ -19,11 +19,11 @@ pub(crate) fn create_handle(
finished_functions: PrimaryMap<DefinedFuncIndex, *mut [VMFunctionBody]>,
state: Box<dyn Any>,
func_imports: &[VMFunctionImport],
shared_signature_id: Option<VMSharedSignatureIndex>,
) -> Result<StoreInstanceHandle> {
let mut imports = Imports::default();
imports.functions = func_imports;
let module = Arc::new(module);
let module2 = module.clone();
unsafe {
let handle = InstanceHandle::new(
@@ -31,7 +31,7 @@ pub(crate) fn create_handle(
&finished_functions,
imports,
store.memory_creator(),
&store.lookup_shared_signature(&module2),
&|_| shared_signature_id.unwrap(),
state,
store.interrupts(),
store.externref_activations_table() as *const VMExternRefActivationsTable as *mut _,

View File

@@ -10,7 +10,8 @@ use std::mem;
use std::panic::{self, AssertUnwindSafe};
use wasmtime_environ::entity::PrimaryMap;
use wasmtime_environ::isa::TargetIsa;
use wasmtime_environ::{ir, wasm, CompiledFunction, Module};
use wasmtime_environ::wasm::SignatureIndex;
use wasmtime_environ::{ir, wasm, CompiledFunction, Module, ModuleType};
use wasmtime_jit::trampoline::ir::{
ExternalName, Function, InstBuilder, MemFlags, StackSlotData, StackSlotKind,
};
@@ -223,7 +224,8 @@ pub fn create_handle_with_function(
// First up we manufacture a trampoline which has the ABI specified by `ft`
// and calls into `stub_fn`...
let sig_id = module.signatures.push(wft.clone());
let sig_id = SignatureIndex::from_u32(u32::max_value() - 1);
module.types.push(ModuleType::Function(sig_id));
let func_id = module.functions.push(sig_id);
module
.exports
@@ -241,7 +243,7 @@ pub fn create_handle_with_function(
&sig,
mem::size_of::<u128>(),
)?;
store.signatures().borrow_mut().register(wft, trampoline);
let shared_signature_id = store.signatures().borrow_mut().register(wft, trampoline);
// Next up we wrap everything up into an `InstanceHandle` by publishing our
// code memory (makes it executable) and ensuring all our various bits of
@@ -254,6 +256,7 @@ pub fn create_handle_with_function(
finished_functions,
Box::new(trampoline_state),
&[],
Some(shared_signature_id),
)
.map(|instance| (instance, trampoline))
}
@@ -270,13 +273,21 @@ pub unsafe fn create_handle_with_raw_function(
let mut module = Module::new();
let mut finished_functions = PrimaryMap::new();
let sig_id = module.signatures.push(wft.clone());
let sig_id = SignatureIndex::from_u32(u32::max_value() - 1);
module.types.push(ModuleType::Function(sig_id));
let func_id = module.functions.push(sig_id);
module
.exports
.insert(String::new(), wasm::EntityIndex::Function(func_id));
finished_functions.push(func);
store.signatures().borrow_mut().register(wft, trampoline);
let shared_signature_id = store.signatures().borrow_mut().register(wft, trampoline);
create_handle(module, store, finished_functions, state, &[])
create_handle(
module,
store,
finished_functions,
state,
&[],
Some(shared_signature_id),
)
}

View File

@@ -3,13 +3,17 @@ use crate::trampoline::StoreInstanceHandle;
use crate::{GlobalType, Mutability, Store, Val};
use anyhow::Result;
use wasmtime_environ::entity::PrimaryMap;
use wasmtime_environ::{wasm, Module};
use wasmtime_environ::{
wasm::{self, SignatureIndex},
Module, ModuleType,
};
use wasmtime_runtime::VMFunctionImport;
pub fn create_global(store: &Store, gt: &GlobalType, val: Val) -> Result<StoreInstanceHandle> {
let mut module = Module::new();
let mut func_imports = Vec::new();
let mut externref_init = None;
let mut shared_signature_id = None;
let global = wasm::Global {
wasm_ty: gt.content().to_wasm_type(),
@@ -35,17 +39,19 @@ pub fn create_global(store: &Store, gt: &GlobalType, val: Val) -> Result<StoreIn
Val::FuncRef(Some(f)) => {
// Add a function import to the stub module, and then initialize
// our global with a `ref.func` to grab that imported function.
let signatures = store.signatures().borrow();
let shared_sig_index = f.sig_index();
let (wasm, _) = signatures
.lookup_shared(shared_sig_index)
.expect("signature not registered");
let local_sig_index = module.signatures.push(wasm.clone());
let func_index = module.functions.push(local_sig_index);
shared_signature_id = Some(shared_sig_index);
let sig_id = SignatureIndex::from_u32(u32::max_value() - 1);
module.types.push(ModuleType::Function(sig_id));
let func_index = module.functions.push(sig_id);
module.num_imported_funcs = 1;
module
.imports
.push(("".into(), None, wasm::EntityIndex::Function(func_index)));
.initializers
.push(wasmtime_environ::Initializer::Import {
module: "".into(),
field: None,
index: wasm::EntityIndex::Function(func_index),
});
let f = f.caller_checked_anyfunc();
let f = unsafe { f.as_ref() };
@@ -70,6 +76,7 @@ pub fn create_global(store: &Store, gt: &GlobalType, val: Val) -> Result<StoreIn
PrimaryMap::new(),
Box::new(()),
&func_imports,
shared_signature_id,
)?;
if let Some(x) = externref_init {

View File

@@ -29,7 +29,7 @@ pub fn create_handle_with_memory(
.exports
.insert(String::new(), wasm::EntityIndex::Memory(memory_id));
create_handle(module, store, PrimaryMap::new(), Box::new(()), &[])
create_handle(module, store, PrimaryMap::new(), Box::new(()), &[], None)
}
struct LinearMemoryProxy {

View File

@@ -27,5 +27,5 @@ pub fn create_handle_with_table(store: &Store, table: &TableType) -> Result<Stor
.exports
.insert(String::new(), wasm::EntityIndex::Table(table_id));
create_handle(module, store, PrimaryMap::new(), Box::new(()), &[])
create_handle(module, store, PrimaryMap::new(), Box::new(()), &[], None)
}

View File

@@ -1,3 +1,4 @@
use crate::Module;
use std::fmt;
use wasmtime_environ::wasm::WasmFuncType;
use wasmtime_environ::{ir, wasm};
@@ -195,33 +196,23 @@ impl ExternType {
(Instance(InstanceType) instance unwrap_instance)
}
fn from_wasmtime(
module: &wasmtime_environ::Module,
ty: &wasmtime_environ::wasm::EntityType,
) -> ExternType {
fn from_wasmtime(module: &Module, ty: &wasmtime_environ::wasm::EntityType) -> ExternType {
use wasmtime_environ::wasm::EntityType;
match ty {
EntityType::Function(idx) => {
let sig = module.types[*idx].unwrap_function();
let sig = &module.signatures[sig];
let sig = &module.types().wasm_signatures[*idx];
FuncType::from_wasm_func_type(sig).into()
}
EntityType::Global(ty) => GlobalType::from_wasmtime_global(ty).into(),
EntityType::Memory(ty) => MemoryType::from_wasmtime_memory(ty).into(),
EntityType::Table(ty) => TableType::from_wasmtime_table(ty).into(),
EntityType::Module(ty) => {
let (imports, exports) = match &module.types[*ty] {
wasmtime_environ::ModuleType::Module { imports, exports } => (imports, exports),
_ => unreachable!("not possible in valid wasm modules"),
};
ModuleType::from_wasmtime(module, imports, exports).into()
let ty = &module.types().module_signatures[*ty];
ModuleType::from_wasmtime(module, ty).into()
}
EntityType::Instance(ty) => {
let exports = match &module.types[*ty] {
wasmtime_environ::ModuleType::Instance { exports } => exports,
_ => unreachable!("not possible in valid wasm modules"),
};
InstanceType::from_wasmtime(module, exports).into()
let ty = &module.types().instance_signatures[*ty];
InstanceType::from_wasmtime(module, ty).into()
}
EntityType::Event(_) => unimplemented!("wasm event support"),
}
@@ -499,16 +490,17 @@ impl ModuleType {
}
pub(crate) fn from_wasmtime(
module: &wasmtime_environ::Module,
imports: &[(String, Option<String>, wasmtime_environ::wasm::EntityType)],
exports: &[(String, wasmtime_environ::wasm::EntityType)],
module: &Module,
ty: &wasmtime_environ::ModuleSignature,
) -> ModuleType {
let exports = &module.types().instance_signatures[ty.exports].exports;
ModuleType {
exports: exports
.iter()
.map(|(name, ty)| (name.to_string(), ExternType::from_wasmtime(module, ty)))
.collect(),
imports: imports
imports: ty
.imports
.iter()
.map(|(m, name, ty)| {
(
@@ -556,11 +548,12 @@ impl InstanceType {
}
pub(crate) fn from_wasmtime(
module: &wasmtime_environ::Module,
exports: &[(String, wasmtime_environ::wasm::EntityType)],
module: &Module,
ty: &wasmtime_environ::InstanceSignature,
) -> InstanceType {
InstanceType {
exports: exports
exports: ty
.exports
.iter()
.map(|(name, ty)| (name.to_string(), ExternType::from_wasmtime(module, ty)))
.collect(),
@@ -577,13 +570,12 @@ pub(crate) enum EntityType<'module> {
Memory(&'module wasm::Memory),
Global(&'module wasm::Global),
Module {
imports: &'module [(String, Option<String>, wasmtime_environ::wasm::EntityType)],
exports: &'module [(String, wasmtime_environ::wasm::EntityType)],
module: &'module wasmtime_environ::Module,
ty: &'module wasmtime_environ::ModuleSignature,
module: &'module Module,
},
Instance {
exports: &'module [(String, wasmtime_environ::wasm::EntityType)],
module: &'module wasmtime_environ::Module,
ty: &'module wasmtime_environ::InstanceSignature,
module: &'module Module,
},
}
@@ -591,51 +583,31 @@ impl<'module> EntityType<'module> {
/// Translate from a `EntityIndex` into an `ExternType`.
pub(crate) fn new(
entity_index: &wasm::EntityIndex,
module: &'module wasmtime_environ::Module,
module: &'module Module,
) -> EntityType<'module> {
let env_module = module.compiled_module().module();
match entity_index {
wasm::EntityIndex::Function(func_index) => {
let sig = module.wasm_func_type(*func_index);
EntityType::Function(&sig)
let sig_index = env_module.functions[*func_index];
let sig = &module.types().wasm_signatures[sig_index];
EntityType::Function(sig)
}
wasm::EntityIndex::Table(table_index) => {
EntityType::Table(&module.table_plans[*table_index].table)
EntityType::Table(&env_module.table_plans[*table_index].table)
}
wasm::EntityIndex::Memory(memory_index) => {
EntityType::Memory(&module.memory_plans[*memory_index].memory)
EntityType::Memory(&env_module.memory_plans[*memory_index].memory)
}
wasm::EntityIndex::Global(global_index) => {
EntityType::Global(&module.globals[*global_index])
EntityType::Global(&env_module.globals[*global_index])
}
wasm::EntityIndex::Module(idx) => {
let (imports, exports) = match &module.types[module.modules[*idx]] {
wasmtime_environ::ModuleType::Module { imports, exports } => (imports, exports),
_ => unreachable!("valid modules should never hit this"),
};
EntityType::Module {
imports,
exports,
module,
}
let ty = &module.types().module_signatures[env_module.modules[*idx]];
EntityType::Module { ty, module }
}
wasm::EntityIndex::Instance(idx) => {
// Get the type, either a pointer to an instance for an import
// or a module for an instantiation.
let ty = match module.instances[*idx] {
wasmtime_environ::Instance::Import(ty) => ty,
wasmtime_environ::Instance::Instantiate { module: idx, .. } => {
module.modules[idx]
}
};
// Get the exports of whatever our type specifies, ignoring
// imports in the module case since we're instantiating the
// module.
let exports = match &module.types[ty] {
wasmtime_environ::ModuleType::Instance { exports } => exports,
wasmtime_environ::ModuleType::Module { exports, .. } => exports,
_ => unreachable!("valid modules should never hit this"),
};
EntityType::Instance { exports, module }
let ty = &module.types().instance_signatures[env_module.instances[*idx]];
EntityType::Instance { ty, module }
}
}
}
@@ -647,14 +619,8 @@ impl<'module> EntityType<'module> {
EntityType::Table(table) => TableType::from_wasmtime_table(table).into(),
EntityType::Memory(memory) => MemoryType::from_wasmtime_memory(memory).into(),
EntityType::Global(global) => GlobalType::from_wasmtime_global(global).into(),
EntityType::Instance { exports, module } => {
InstanceType::from_wasmtime(module, exports).into()
}
EntityType::Module {
imports,
exports,
module,
} => ModuleType::from_wasmtime(module, imports, exports).into(),
EntityType::Instance { module, ty } => InstanceType::from_wasmtime(module, ty).into(),
EntityType::Module { module, ty } => ModuleType::from_wasmtime(module, ty).into(),
}
}
}