From 3bc157803c055e428965ec5a3ba1d69ab80c41e4 Mon Sep 17 00:00:00 2001 From: grovesNL Date: Fri, 26 Jan 2024 22:36:02 -0330 Subject: [PATCH] Add `ModuleExport` to skip repeated string lookups --- crates/wasmtime/src/instance.rs | 37 +++++++++++++++++++- crates/wasmtime/src/lib.rs | 2 +- crates/wasmtime/src/module.rs | 60 +++++++++++++++++++++++++++++++-- 3 files changed, 95 insertions(+), 4 deletions(-) diff --git a/crates/wasmtime/src/instance.rs b/crates/wasmtime/src/instance.rs index af070727af1b..88f495867bea 100644 --- a/crates/wasmtime/src/instance.rs +++ b/crates/wasmtime/src/instance.rs @@ -1,4 +1,5 @@ use crate::linker::{Definition, DefinitionType}; +use crate::module::ModuleExport; use crate::store::{InstanceId, StoreOpaque, Stored}; use crate::types::matching; use crate::{ @@ -9,7 +10,9 @@ use anyhow::{anyhow, bail, Context, Result}; use std::mem; use std::ptr::NonNull; use std::sync::Arc; -use wasmtime_environ::{EntityType, FuncIndex, GlobalIndex, MemoryIndex, PrimaryMap, TableIndex}; +use wasmtime_environ::{ + EntityType, FuncIndex, GlobalIndex, MemoryIndex, PrimaryMap, TableIndex, +}; use wasmtime_runtime::{ Imports, InstanceAllocationRequest, StorePtr, VMContext, VMFuncRef, VMFunctionImport, VMGlobalImport, VMMemoryImport, VMNativeCallFunction, VMOpaqueContext, VMTableImport, @@ -450,6 +453,38 @@ impl Instance { Some(item) } + /// Looks up exported [`Extern`] value by a [`ModuleExport`] value. + /// + /// This is similar to [`Instance::get_export`] but uses a [`ModuleExport`] value to avoid + /// string lookups where possible. + /// + /// This method will search the module for an export with a matching entity index and return + /// the value, if found. + /// + /// Returns `None` if there was no export with a matching entity index. + /// + /// # Panics + /// + /// Panics if `store` does not own this instance. + pub fn get_module_export( + &self, + mut store: impl AsContextMut, + export: &ModuleExport, + ) -> Option { + let store = store.as_context_mut().0; + let data = &store[self.0]; + + let id = data.id; + let instance = store.instance_mut(id); + + let item = unsafe { + Extern::from_wasmtime_export(instance.get_export_by_index(export.entity), store) + }; + let data = &mut store[self.0]; + data.exports[export.export_name_index] = Some(item.clone()); + Some(item) + } + /// Looks up an exported [`Func`] value by name. /// /// Returns `None` if there was no export named `name`, or if there was but diff --git a/crates/wasmtime/src/lib.rs b/crates/wasmtime/src/lib.rs index 28a455b60271..983466341302 100644 --- a/crates/wasmtime/src/lib.rs +++ b/crates/wasmtime/src/lib.rs @@ -454,7 +454,7 @@ pub use crate::instantiate::CompiledModule; pub use crate::limits::*; pub use crate::linker::*; pub use crate::memory::*; -pub use crate::module::Module; +pub use crate::module::{Module, ModuleExport}; #[cfg(feature = "profiling")] pub use crate::profiling::GuestProfiler; pub use crate::r#ref::ExternRef; diff --git a/crates/wasmtime/src/module.rs b/crates/wasmtime/src/module.rs index d27122f6c6de..548406e35cec 100644 --- a/crates/wasmtime/src/module.rs +++ b/crates/wasmtime/src/module.rs @@ -17,8 +17,7 @@ use std::ptr::NonNull; use std::sync::Arc; use wasmparser::{Parser, ValidPayload, Validator}; use wasmtime_environ::{ - CompiledModuleInfo, DefinedFuncIndex, DefinedMemoryIndex, HostPtr, ModuleEnvironment, - ModuleTypes, ObjectKind, VMOffsets, + CompiledModuleInfo, DefinedFuncIndex, DefinedMemoryIndex, EntityIndex, HostPtr, ModuleEnvironment, ModuleTypes, ObjectKind, VMOffsets }; use wasmtime_runtime::{ CompiledModuleId, MemoryImage, MmapVec, ModuleMemoryImages, VMArrayCallFunction, @@ -897,6 +896,52 @@ impl Module { )) } + /// Looks up an export in this [`Module`] by name to get its index. + /// + /// This function will return the index of an export with the given name. This can be useful + /// to avoid the cost of looking up the export by name multiple times. Instead the index can be + /// stored, and used to look up + /// + /// # Examples + /// + /// There may be no export with that name: + /// + /// ``` + /// # use wasmtime::*; + /// # fn main() -> anyhow::Result<()> { + /// # let engine = Engine::default(); + /// let module = Module::new(&engine, "(module)")?; + /// assert!(module.get_export("foo").is_none()); + /// # Ok(()) + /// # } + /// ``` + /// + /// When there is an export with that name, it is returned: + /// + /// ``` + /// # use wasmtime::*; + /// # fn main() -> anyhow::Result<()> { + /// # let engine = Engine::default(); + /// let wat = r#" + /// (module + /// (func (export "foo")) + /// (memory (export "memory") 1) + /// ) + /// "#; + /// let module = Module::new(&engine, wat)?; + /// let foo = module.get_export_index("foo"); + /// assert!(foo.is_some()); + /// ``` + pub fn get_export_index(&self, name: &str) -> Option { + let compiled_module = self.compiled_module(); + let module = compiled_module.module(); + module.exports.get_full(name).map(|(export_name_index, _, &entity)| ModuleExport { + module: compiled_module.unique_id(), + entity, + export_name_index, + }) + } + /// Returns the [`Engine`] that this [`Module`] was compiled by. pub fn engine(&self) -> &Engine { &self.inner.engine @@ -1117,6 +1162,17 @@ impl Drop for ModuleInner { } } +/// Describes the location of an export in a module. +#[derive(Copy, Clone)] +pub struct ModuleExport { + /// The module that this export is defined in. + pub module: CompiledModuleId, + /// A raw index into the wasm module. + pub entity: EntityIndex, + /// The index of the export name. + pub export_name_index: usize, +} + fn _assert_send_sync() { fn _assert() {} _assert::();