Pre-generate trampoline functions (#957)
* Refactor wasmtime_runtime::Export Instead of an enumeration with variants that have data fields have an enumeration where each variant has a struct, and each struct has the data fields. This allows us to store the structs in the `wasmtime` API and avoid lots of `panic!` calls and various extraneous matches. * Pre-generate trampoline functions The `wasmtime` crate supports calling arbitrary function signatures in wasm code, and to do this it generates "trampoline functions" which have a known ABI that then internally convert to a particular signature's ABI and call it. These trampoline functions are currently generated on-the-fly and are cached in the global `Store` structure. This, however, is suboptimal for a few reasons: * Due to how code memory is managed each trampoline resides in its own 64kb allocation of memory. This means if you have N trampolines you're using N * 64kb of memory, which is quite a lot of overhead! * Trampolines are never free'd, even if the referencing module goes away. This is similar to #925. * Trampolines are a source of shared state which prevents `Store` from being easily thread safe. This commit refactors how trampolines are managed inside of the `wasmtime` crate and jit/runtime internals. All trampolines are now allocated in the same pass of `CodeMemory` that the main module is allocated into. A trampoline is generated per-signature in a module as well, instead of per-function. This cache of trampolines is stored directly inside of an `Instance`. Trampolines are stored based on `VMSharedSignatureIndex` so they can be looked up from the internals of the `ExportFunction` value. The `Func` API has been updated with various bits and pieces to ensure the right trampolines are registered in the right places. Overall this should ensure that all trampolines necessary are generated up-front rather than lazily. This allows us to remove the trampoline cache from the `Compiler` type, and move one step closer to making `Compiler` threadsafe for usage across multiple threads. Note that as one small caveat the `Func::wrap*` family of functions don't need to generate a trampoline at runtime, they actually generate the trampoline at compile time which gets passed in. Also in addition to shuffling a lot of code around this fixes one minor bug found in `code_memory.rs`, where `self.position` was loaded before allocation, but the allocation may push a new chunk which would cause `self.position` to be zero instead. * Pass the `SignatureRegistry` as an argument to where it's needed. This avoids the need for storing it in an `Arc`. * Ignore tramoplines for functions with lots of arguments Co-authored-by: Dan Gohman <sunfish@mozilla.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
use crate::vmcontext::{
|
||||
VMContext, VMFunctionBody, VMGlobalDefinition, VMMemoryDefinition, VMTableDefinition,
|
||||
VMContext, VMFunctionBody, VMGlobalDefinition, VMMemoryDefinition, VMSharedSignatureIndex,
|
||||
VMTableDefinition,
|
||||
};
|
||||
use wasmtime_environ::ir;
|
||||
use wasmtime_environ::wasm::Global;
|
||||
use wasmtime_environ::{MemoryPlan, TablePlan};
|
||||
|
||||
@@ -9,96 +9,84 @@ use wasmtime_environ::{MemoryPlan, TablePlan};
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Export {
|
||||
/// A function export value.
|
||||
Function {
|
||||
/// The address of the native-code function.
|
||||
address: *const VMFunctionBody,
|
||||
/// Pointer to the containing `VMContext`.
|
||||
vmctx: *mut VMContext,
|
||||
/// The function signature declaration, used for compatibilty checking.
|
||||
signature: ir::Signature,
|
||||
},
|
||||
Function(ExportFunction),
|
||||
|
||||
/// A table export value.
|
||||
Table {
|
||||
/// The address of the table descriptor.
|
||||
definition: *mut VMTableDefinition,
|
||||
/// Pointer to the containing `VMContext`.
|
||||
vmctx: *mut VMContext,
|
||||
/// The table declaration, used for compatibilty checking.
|
||||
table: TablePlan,
|
||||
},
|
||||
Table(ExportTable),
|
||||
|
||||
/// A memory export value.
|
||||
Memory {
|
||||
/// The address of the memory descriptor.
|
||||
definition: *mut VMMemoryDefinition,
|
||||
/// Pointer to the containing `VMContext`.
|
||||
vmctx: *mut VMContext,
|
||||
/// The memory declaration, used for compatibilty checking.
|
||||
memory: MemoryPlan,
|
||||
},
|
||||
Memory(ExportMemory),
|
||||
|
||||
/// A global export value.
|
||||
Global {
|
||||
/// The address of the global storage.
|
||||
definition: *mut VMGlobalDefinition,
|
||||
/// Pointer to the containing `VMContext`.
|
||||
vmctx: *mut VMContext,
|
||||
/// The global declaration, used for compatibilty checking.
|
||||
global: Global,
|
||||
},
|
||||
Global(ExportGlobal),
|
||||
}
|
||||
|
||||
impl Export {
|
||||
/// Construct a function export value.
|
||||
pub fn function(
|
||||
address: *const VMFunctionBody,
|
||||
vmctx: *mut VMContext,
|
||||
signature: ir::Signature,
|
||||
) -> Self {
|
||||
Self::Function {
|
||||
address,
|
||||
vmctx,
|
||||
signature,
|
||||
}
|
||||
}
|
||||
/// A function export value.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ExportFunction {
|
||||
/// The address of the native-code function.
|
||||
pub address: *const VMFunctionBody,
|
||||
/// Pointer to the containing `VMContext`.
|
||||
pub vmctx: *mut VMContext,
|
||||
/// The function signature declaration, used for compatibilty checking.
|
||||
///
|
||||
/// Note that this indexes within the module associated with `vmctx`.
|
||||
pub signature: VMSharedSignatureIndex,
|
||||
}
|
||||
|
||||
/// Construct a table export value.
|
||||
pub fn table(
|
||||
definition: *mut VMTableDefinition,
|
||||
vmctx: *mut VMContext,
|
||||
table: TablePlan,
|
||||
) -> Self {
|
||||
Self::Table {
|
||||
definition,
|
||||
vmctx,
|
||||
table,
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct a memory export value.
|
||||
pub fn memory(
|
||||
definition: *mut VMMemoryDefinition,
|
||||
vmctx: *mut VMContext,
|
||||
memory: MemoryPlan,
|
||||
) -> Self {
|
||||
Self::Memory {
|
||||
definition,
|
||||
vmctx,
|
||||
memory,
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct a global export value.
|
||||
pub fn global(
|
||||
definition: *mut VMGlobalDefinition,
|
||||
vmctx: *mut VMContext,
|
||||
global: Global,
|
||||
) -> Self {
|
||||
Self::Global {
|
||||
definition,
|
||||
vmctx,
|
||||
global,
|
||||
}
|
||||
impl From<ExportFunction> for Export {
|
||||
fn from(func: ExportFunction) -> Export {
|
||||
Export::Function(func)
|
||||
}
|
||||
}
|
||||
|
||||
/// A table export value.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ExportTable {
|
||||
/// The address of the table descriptor.
|
||||
pub definition: *mut VMTableDefinition,
|
||||
/// Pointer to the containing `VMContext`.
|
||||
pub vmctx: *mut VMContext,
|
||||
/// The table declaration, used for compatibilty checking.
|
||||
pub table: TablePlan,
|
||||
}
|
||||
|
||||
impl From<ExportTable> for Export {
|
||||
fn from(func: ExportTable) -> Export {
|
||||
Export::Table(func)
|
||||
}
|
||||
}
|
||||
|
||||
/// A memory export value.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ExportMemory {
|
||||
/// The address of the memory descriptor.
|
||||
pub definition: *mut VMMemoryDefinition,
|
||||
/// Pointer to the containing `VMContext`.
|
||||
pub vmctx: *mut VMContext,
|
||||
/// The memory declaration, used for compatibilty checking.
|
||||
pub memory: MemoryPlan,
|
||||
}
|
||||
|
||||
impl From<ExportMemory> for Export {
|
||||
fn from(func: ExportMemory) -> Export {
|
||||
Export::Memory(func)
|
||||
}
|
||||
}
|
||||
|
||||
/// A global export value.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ExportGlobal {
|
||||
/// The address of the global storage.
|
||||
pub definition: *mut VMGlobalDefinition,
|
||||
/// Pointer to the containing `VMContext`.
|
||||
pub vmctx: *mut VMContext,
|
||||
/// The global declaration, used for compatibilty checking.
|
||||
pub global: Global,
|
||||
}
|
||||
|
||||
impl From<ExportGlobal> for Export {
|
||||
fn from(func: ExportGlobal) -> Export {
|
||||
Export::Global(func)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user