Skip to content

Commit

Permalink
Implement imported/exported modules/instances
Browse files Browse the repository at this point in the history
This commit implements the final piece of the module linking proposal
which is to flesh out the support for importing/exporting instances and
modules. This ended up having a few changes:

* Two more `PrimaryMap` instances are now stored in an `Instance`. The value
  for instances is `InstanceHandle` (pretty easy) and for modules it's
  `Box<dyn Any>` (less easy).

* The custom host state for `InstanceHandle` for `wasmtime` is now
  `Arc<TypeTables` to be able to fully reconstruct an instance's types
  just from its instance.

* Type matching for imports now has been updated to take
  instances/modules into account.

One of the main downsides of this implementation is that type matching
of imports is duplicated between wasmparser and wasmtime, leading to
posssible bugs especially in the subtelties of module linking. I'm not
sure how best to unify these two pieces of validation, however, and it
may be more trouble than it's worth.

cc bytecodealliance#2094
  • Loading branch information
alexcrichton committed Dec 2, 2020
1 parent 9ac7d01 commit 423be4e
Show file tree
Hide file tree
Showing 21 changed files with 968 additions and 299 deletions.
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,6 @@ harness = false

[profile.dev.package.backtrace]
debug = false # FIXME(#1813)

[patch.crates-io]
wasmparser = { git = 'https://github.com/alexcrichton/wasm-tools', branch = 'fix-validate-submodule' }
2 changes: 1 addition & 1 deletion cranelift/wasm/src/translation_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ pub struct InstanceTypeIndex(u32);
entity_impl!(InstanceTypeIndex);

/// An index of an entity.
#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub enum EntityIndex {
/// Function index.
Expand Down
4 changes: 4 additions & 0 deletions crates/c-api/src/extern.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ pub extern "C" fn wasm_extern_kind(e: &wasm_extern_t) -> wasm_externkind_t {
Extern::Global(_) => crate::WASM_EXTERN_GLOBAL,
Extern::Table(_) => crate::WASM_EXTERN_TABLE,
Extern::Memory(_) => crate::WASM_EXTERN_MEMORY,

// FIXME(#2094)
Extern::Instance(_) => unimplemented!(),
Extern::Module(_) => unimplemented!(),
}
}

Expand Down
26 changes: 25 additions & 1 deletion crates/environ/src/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,30 @@ impl Module {
pub fn is_imported_global(&self, index: GlobalIndex) -> bool {
index.index() < self.num_imported_globals
}

/// Test whether the given global index is for an imported global.
pub fn imports(&self) -> impl Iterator<Item = (&str, Option<&str>, EntityType)> {
self.initializers.iter().filter_map(move |i| match i {
Initializer::Import {
module,
field,
index,
} => Some((module.as_str(), field.as_deref(), self.type_of(*index))),
_ => None,
})
}

/// Returns the type of an item based on its index
pub fn type_of(&self, index: EntityIndex) -> EntityType {
match index {
EntityIndex::Global(i) => EntityType::Global(self.globals[i]),
EntityIndex::Table(i) => EntityType::Table(self.table_plans[i].table),
EntityIndex::Memory(i) => EntityType::Memory(self.memory_plans[i].memory),
EntityIndex::Function(i) => EntityType::Function(self.functions[i]),
EntityIndex::Instance(i) => EntityType::Instance(self.instances[i]),
EntityIndex::Module(i) => EntityType::Module(self.modules[i]),
}
}
}

/// All types which are recorded for the entirety of a translation.
Expand Down Expand Up @@ -376,7 +400,7 @@ pub struct ModuleSignature {
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InstanceSignature {
/// The name of what's being exported as well as its type signature.
pub exports: Vec<(String, EntityType)>,
pub exports: IndexMap<String, EntityType>,
}

mod passive_data_serde {
Expand Down
2 changes: 1 addition & 1 deletion crates/environ/src/module_environ.rs
Original file line number Diff line number Diff line change
Expand Up @@ -857,7 +857,7 @@ and for re-adding support for interface types you can see this issue:
// instance.
Alias::Child { instance, export } => {
let ty = self.result.module.instances[instance];
match &self.types.instance_signatures[ty].exports[export].1 {
match &self.types.instance_signatures[ty].exports[export] {
EntityType::Global(g) => {
self.result.module.globals.push(g.clone());
self.result.module.num_imported_globals += 1;
Expand Down
27 changes: 17 additions & 10 deletions crates/runtime/src/export.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
use crate::vmcontext::{
VMCallerCheckedAnyfunc, VMContext, VMGlobalDefinition, VMMemoryDefinition, VMTableDefinition,
};
use crate::InstanceHandle;
use std::any::Any;
use std::ptr::NonNull;
use wasmtime_environ::wasm::Global;
use wasmtime_environ::{MemoryPlan, TablePlan};

/// The value of an export passed from one instance to another.
#[derive(Debug, Clone)]
pub enum Export {
pub enum Export<'a> {
/// A function export value.
Function(ExportFunction),

Expand All @@ -19,6 +20,12 @@ pub enum Export {

/// A global export value.
Global(ExportGlobal),

/// An instance
Instance(&'a InstanceHandle),

/// A module
Module(&'a dyn Any),
}

/// A function export value.
Expand All @@ -31,8 +38,8 @@ pub struct ExportFunction {
pub anyfunc: NonNull<VMCallerCheckedAnyfunc>,
}

impl From<ExportFunction> for Export {
fn from(func: ExportFunction) -> Export {
impl<'a> From<ExportFunction> for Export<'a> {
fn from(func: ExportFunction) -> Export<'a> {
Export::Function(func)
}
}
Expand All @@ -48,8 +55,8 @@ pub struct ExportTable {
pub table: TablePlan,
}

impl From<ExportTable> for Export {
fn from(func: ExportTable) -> Export {
impl<'a> From<ExportTable> for Export<'a> {
fn from(func: ExportTable) -> Export<'a> {
Export::Table(func)
}
}
Expand All @@ -65,8 +72,8 @@ pub struct ExportMemory {
pub memory: MemoryPlan,
}

impl From<ExportMemory> for Export {
fn from(func: ExportMemory) -> Export {
impl<'a> From<ExportMemory> for Export<'a> {
fn from(func: ExportMemory) -> Export<'a> {
Export::Memory(func)
}
}
Expand All @@ -82,8 +89,8 @@ pub struct ExportGlobal {
pub global: Global,
}

impl From<ExportGlobal> for Export {
fn from(func: ExportGlobal) -> Export {
impl<'a> From<ExportGlobal> for Export<'a> {
fn from(func: ExportGlobal) -> Export<'a> {
Export::Global(func)
}
}
24 changes: 22 additions & 2 deletions crates/runtime/src/imports.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
use crate::vmcontext::{VMFunctionImport, VMGlobalImport, VMMemoryImport, VMTableImport};
use crate::InstanceHandle;
use std::any::Any;
use wasmtime_environ::entity::PrimaryMap;
use wasmtime_environ::wasm::{InstanceIndex, ModuleIndex};

/// Resolved import pointers.
///
/// Note that each of these fields are slices, not `PrimaryMap`. They should be
/// Note that some of these fields are slices, not `PrimaryMap`. They should be
/// stored in index-order as with the module that we're providing the imports
/// for, and indexing is all done the same way as the main module's index
/// spaces.
#[derive(Clone, Default)]
///
/// Also note that the way we compile modules means that for the module linking
/// proposal all `alias` directives should map to imported items. This means
/// that each of these items aren't necessarily directly imported, but may be
/// aliased.
#[derive(Default)]
pub struct Imports<'a> {
/// Resolved addresses for imported functions.
pub functions: &'a [VMFunctionImport],
Expand All @@ -19,4 +28,15 @@ pub struct Imports<'a> {

/// Resolved addresses for imported globals.
pub globals: &'a [VMGlobalImport],

/// Resolved imported instances.
pub instances: PrimaryMap<InstanceIndex, InstanceHandle>,

/// Resolved imported modules.
///
/// Note that `Box<Any>` here is chosen to allow the embedder of this crate
/// to pick an appropriate representation of what module type should be. For
/// example for the `wasmtime` crate it's `wasmtime::Module` but that's not
/// defined way down here in this low crate.
pub modules: PrimaryMap<ModuleIndex, Box<dyn Any>>,
}
22 changes: 16 additions & 6 deletions crates/runtime/src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ use thiserror::Error;
use wasmtime_environ::entity::{packed_option::ReservedValue, BoxedSlice, EntityRef, PrimaryMap};
use wasmtime_environ::wasm::{
DataIndex, DefinedFuncIndex, DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex,
ElemIndex, EntityIndex, FuncIndex, GlobalIndex, GlobalInit, MemoryIndex, SignatureIndex,
TableElementType, TableIndex, WasmType,
ElemIndex, EntityIndex, FuncIndex, GlobalIndex, GlobalInit, InstanceIndex, MemoryIndex,
ModuleIndex, SignatureIndex, TableElementType, TableIndex, WasmType,
};
use wasmtime_environ::{ir, DataInitializer, Module, ModuleType, TableElements, VMOffsets};

Expand All @@ -50,6 +50,15 @@ pub(crate) struct Instance {
/// WebAssembly table data.
tables: BoxedSlice<DefinedTableIndex, Table>,

/// Instances our module defined and their handles.
instances: PrimaryMap<InstanceIndex, InstanceHandle>,

/// Modules that are located in our index space.
///
/// For now these are `Box<Any>` so the caller can define the type of what a
/// module looks like.
modules: PrimaryMap<ModuleIndex, Box<dyn Any>>,

/// Passive elements in this instantiation. As `elem.drop`s happen, these
/// entries get removed. A missing entry is considered equivalent to an
/// empty slice.
Expand Down Expand Up @@ -268,7 +277,7 @@ impl Instance {
}

/// Lookup an export with the given export declaration.
pub fn lookup_by_declaration(&self, export: &EntityIndex) -> Export {
pub fn lookup_by_declaration(&self, export: &EntityIndex) -> Export<'_> {
match export {
EntityIndex::Function(index) => {
let anyfunc = self.get_caller_checked_anyfunc(*index).unwrap();
Expand Down Expand Up @@ -317,9 +326,8 @@ impl Instance {
}
.into(),

// FIXME(#2094)
EntityIndex::Instance(_index) => unimplemented!(),
EntityIndex::Module(_index) => unimplemented!(),
EntityIndex::Instance(index) => Export::Instance(&self.instances[*index]),
EntityIndex::Module(index) => Export::Module(&*self.modules[*index]),
}
}

Expand Down Expand Up @@ -847,6 +855,8 @@ impl InstanceHandle {
passive_elements: Default::default(),
passive_data,
host_state,
instances: imports.instances,
modules: imports.modules,
vmctx: VMContext {},
};
let layout = instance.alloc_layout();
Expand Down
1 change: 1 addition & 0 deletions crates/wasmtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ wat = { version = "1.0.18", optional = true }
smallvec = "1.4.0"
serde = { version = "1.0.94", features = ["derive"] }
bincode = "1.2.1"
indexmap = "1.6"

[target.'cfg(target_os = "windows")'.dependencies]
winapi = "0.3.7"
Expand Down
Loading

0 comments on commit 423be4e

Please sign in to comment.