* Compute instance exports on demand.

Instead having instances eagerly compute a Vec of Externs, and bumping
the refcount for each Extern, compute Externs on demand.

This also enables `Instance::get_export` to avoid doing a linear search.

This also means that the closure returned by `get0` and friends now
holds an `InstanceHandle` to dynamically hold the instance live rather
than being scoped to a lifetime.

* Compute module imports and exports on demand too.

And compute Extern::ty on demand too.

* Add a utility function for computing an ExternType.

* Add a utility function for looking up a function's signature.

* Add a utility function for computing the ValType of a Global.

* Rename wasmtime_environ::Export to EntityIndex.

This helps differentiate it from other Export types in the tree, and
describes what it is.

* Fix a typo in a comment.

* Simplify module imports and exports.

* Make `Instance::exports` return the export names.

This significantly simplifies the public API, as it's relatively common
to need the names, and this avoids the need to do a zip with
`Module::exports`.

This also changes `ImportType` and `ExportType` to have public members
instead of private members and accessors, as I find that simplifies the
usage particularly in cases where there are temporary instances.

* Remove `Instance::module`.

This doesn't quite remove `Instance`'s `module` member, it gets a step
closer.

* Use a InstanceHandle utility function.

* Don't consume self in the `Func::get*` methods.

Instead, just create a closure containing the instance handle and the
export for them to call.

* Use `ExactSizeIterator` to avoid needing separate `num_*` methods.

* Rename `Extern::func()` etc. to `into_func()` etc.

* Revise examples to avoid using `nth`.

* Add convenience methods to instance for getting specific extern types.

* Use the convenience functions in more tests and examples.

* Avoid cloning strings for `ImportType` and `ExportType`.

* Remove more obviated clone() calls.

* Simplify `Func`'s closure state.

* Make wasmtime::Export's fields private.

This makes them more consistent with ExportType.

* Fix compilation error.

* Make a lifetime parameter explicit, and use better lifetime names.

Instead of 'me, use 'instance and 'module to make it clear what the
lifetime is.

* More lifetime cleanups.
This commit is contained in:
Dan Gohman
2020-04-20 13:55:33 -07:00
committed by GitHub
parent 967827f4b5
commit 9364eb1d98
57 changed files with 788 additions and 875 deletions

View File

@@ -16,7 +16,7 @@ pub unsafe extern "C" fn resolve_vmctx_memory_ptr(p: *const u32) -> *const u8 {
);
let handle = InstanceHandle::from_vmctx(VMCTX_AND_MEMORY.0);
assert!(
VMCTX_AND_MEMORY.1 < handle.instance().module().local.memory_plans.len(),
VMCTX_AND_MEMORY.1 < handle.module().local.memory_plans.len(),
"memory index for debugger is out of bounds"
);
let index = MemoryIndex::new(VMCTX_AND_MEMORY.1);

View File

@@ -31,7 +31,7 @@ use wasmtime_environ::wasm::{
DataIndex, DefinedFuncIndex, DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex,
ElemIndex, FuncIndex, GlobalIndex, GlobalInit, MemoryIndex, SignatureIndex, TableIndex,
};
use wasmtime_environ::{ir, DataInitializer, Module, TableElements, VMOffsets};
use wasmtime_environ::{ir, DataInitializer, EntityIndex, Module, TableElements, VMOffsets};
cfg_if::cfg_if! {
if #[cfg(unix)] {
@@ -296,9 +296,9 @@ impl Instance {
}
/// Lookup an export with the given export declaration.
pub fn lookup_by_declaration(&self, export: &wasmtime_environ::Export) -> Export {
pub fn lookup_by_declaration(&self, export: &EntityIndex) -> Export {
match export {
wasmtime_environ::Export::Function(index) => {
EntityIndex::Function(index) => {
let signature = self.signature_id(self.module.local.functions[*index]);
let (address, vmctx) =
if let Some(def_index) = self.module.local.defined_func_index(*index) {
@@ -317,7 +317,7 @@ impl Instance {
}
.into()
}
wasmtime_environ::Export::Table(index) => {
EntityIndex::Table(index) => {
let (definition, vmctx) =
if let Some(def_index) = self.module.local.defined_table_index(*index) {
(self.table_ptr(def_index), self.vmctx_ptr())
@@ -332,7 +332,7 @@ impl Instance {
}
.into()
}
wasmtime_environ::Export::Memory(index) => {
EntityIndex::Memory(index) => {
let (definition, vmctx) =
if let Some(def_index) = self.module.local.defined_memory_index(*index) {
(self.memory_ptr(def_index), self.vmctx_ptr())
@@ -347,7 +347,7 @@ impl Instance {
}
.into()
}
wasmtime_environ::Export::Global(index) => ExportGlobal {
EntityIndex::Global(index) => ExportGlobal {
definition: if let Some(def_index) = self.module.local.defined_global_index(*index)
{
self.global_ptr(def_index)
@@ -363,10 +363,10 @@ impl Instance {
/// Return an iterator over the exports of this instance.
///
/// Specifically, it provides access to the key-value pairs, where they keys
/// Specifically, it provides access to the key-value pairs, where the keys
/// are export names, and the values are export declarations which can be
/// resolved `lookup_by_declaration`.
pub fn exports(&self) -> indexmap::map::Iter<String, wasmtime_environ::Export> {
pub fn exports(&self) -> indexmap::map::Iter<String, EntityIndex> {
self.module.exports.iter()
}
@@ -383,22 +383,35 @@ impl Instance {
None => return Ok(()),
};
let (callee_address, callee_vmctx) = match self.module.local.defined_func_index(start_index)
{
Some(defined_index) => {
let body = *self
.finished_functions
.get(defined_index)
.expect("function index is out of bounds");
(body as *const _, self.vmctx_ptr())
}
None => {
assert_lt!(start_index.index(), self.module.imported_funcs.len());
let import = self.imported_function(start_index);
(import.body, import.vmctx)
}
};
self.invoke_function_index(start_index)
.map_err(InstantiationError::StartTrap)
}
fn invoke_function_index(&self, callee_index: FuncIndex) -> Result<(), Trap> {
let (callee_address, callee_vmctx) =
match self.module.local.defined_func_index(callee_index) {
Some(defined_index) => {
let body = *self
.finished_functions
.get(defined_index)
.expect("function index is out of bounds");
(body as *const _, self.vmctx_ptr())
}
None => {
assert_lt!(callee_index.index(), self.module.local.num_imported_funcs);
let import = self.imported_function(callee_index);
(import.body, import.vmctx)
}
};
self.invoke_function(callee_vmctx, callee_address)
}
fn invoke_function(
&self,
callee_vmctx: *mut VMContext,
callee_address: *const VMFunctionBody,
) -> Result<(), Trap> {
// Make the call.
unsafe {
catch_traps(callee_vmctx, || {
@@ -407,7 +420,6 @@ impl Instance {
unsafe extern "C" fn(*mut VMContext, *mut VMContext),
>(callee_address)(callee_vmctx, self.vmctx_ptr())
})
.map_err(InstantiationError::StartTrap)
}
}
@@ -1019,7 +1031,7 @@ impl InstanceHandle {
}
/// Lookup an export with the given export declaration.
pub fn lookup_by_declaration(&self, export: &wasmtime_environ::Export) -> Export {
pub fn lookup_by_declaration(&self, export: &EntityIndex) -> Export {
self.instance().lookup_by_declaration(export)
}
@@ -1028,7 +1040,7 @@ impl InstanceHandle {
/// Specifically, it provides access to the key-value pairs, where the keys
/// are export names, and the values are export declarations which can be
/// resolved `lookup_by_declaration`.
pub fn exports(&self) -> indexmap::map::Iter<String, wasmtime_environ::Export> {
pub fn exports(&self) -> indexmap::map::Iter<String, EntityIndex> {
self.instance().exports()
}
@@ -1204,7 +1216,7 @@ fn check_memory_init_bounds(
/// Allocate memory for just the tables of the current module.
fn create_tables(module: &Module) -> BoxedSlice<DefinedTableIndex, Table> {
let num_imports = module.imported_tables.len();
let num_imports = module.local.num_imported_tables;
let mut tables: PrimaryMap<DefinedTableIndex, _> =
PrimaryMap::with_capacity(module.local.table_plans.len() - num_imports);
for table in &module.local.table_plans.values().as_slice()[num_imports..] {
@@ -1291,7 +1303,7 @@ fn create_memories(
module: &Module,
mem_creator: &dyn RuntimeMemoryCreator,
) -> Result<BoxedSlice<DefinedMemoryIndex, Box<dyn RuntimeLinearMemory>>, InstantiationError> {
let num_imports = module.imported_memories.len();
let num_imports = module.local.num_imported_memories;
let mut memories: PrimaryMap<DefinedMemoryIndex, _> =
PrimaryMap::with_capacity(module.local.memory_plans.len() - num_imports);
for plan in &module.local.memory_plans.values().as_slice()[num_imports..] {
@@ -1336,7 +1348,7 @@ fn initialize_memories(
/// Allocate memory for just the globals of the current module,
/// with initializers applied.
fn create_globals(module: &Module) -> BoxedSlice<DefinedGlobalIndex, VMGlobalDefinition> {
let num_imports = module.imported_globals.len();
let num_imports = module.local.num_imported_globals;
let mut vmctx_globals = PrimaryMap::with_capacity(module.local.globals.len() - num_imports);
for _ in &module.local.globals.values().as_slice()[num_imports..] {
@@ -1348,7 +1360,7 @@ fn create_globals(module: &Module) -> BoxedSlice<DefinedGlobalIndex, VMGlobalDef
fn initialize_globals(instance: &Instance) {
let module = Arc::clone(&instance.module);
let num_imports = module.imported_globals.len();
let num_imports = module.local.num_imported_globals;
for (index, global) in module.local.globals.iter().skip(num_imports) {
let def_index = module.local.defined_global_index(index).unwrap();
unsafe {
@@ -1394,7 +1406,7 @@ pub enum InstantiationError {
#[error("Trap occurred during instantiation")]
Trap(Trap),
/// A compilation error occured.
/// A trap occurred while running the wasm start function.
#[error("Trap occurred while invoking start function")]
StartTrap(Trap),
}