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:
@@ -71,10 +71,9 @@ impl CodeMemory {
|
||||
) -> Result<&mut [VMFunctionBody], String> {
|
||||
let size = Self::function_allocation_size(func);
|
||||
|
||||
let start = self.position as u32;
|
||||
let (buf, table) = self.allocate(size)?;
|
||||
let (buf, table, start) = self.allocate(size)?;
|
||||
|
||||
let (_, _, _, vmfunc) = Self::copy_function(func, start, buf, table);
|
||||
let (_, _, _, vmfunc) = Self::copy_function(func, start as u32, buf, table);
|
||||
|
||||
Ok(vmfunc)
|
||||
}
|
||||
@@ -90,9 +89,9 @@ impl CodeMemory {
|
||||
.into_iter()
|
||||
.fold(0, |acc, func| acc + Self::function_allocation_size(func));
|
||||
|
||||
let mut start = self.position as u32;
|
||||
let (mut buf, mut table) = self.allocate(total_len)?;
|
||||
let (mut buf, mut table, start) = self.allocate(total_len)?;
|
||||
let mut result = Vec::with_capacity(compilation.len());
|
||||
let mut start = start as u32;
|
||||
|
||||
for func in compilation.into_iter() {
|
||||
let (next_start, next_buf, next_table, vmfunc) =
|
||||
@@ -134,8 +133,14 @@ impl CodeMemory {
|
||||
/// that it can be written to and patched, though we make it readonly before
|
||||
/// actually executing from it.
|
||||
///
|
||||
/// A few values are returned:
|
||||
///
|
||||
/// * A mutable slice which references the allocated memory
|
||||
/// * A function table instance where unwind information is registered
|
||||
/// * The offset within the current mmap that the slice starts at
|
||||
///
|
||||
/// TODO: Add an alignment flag.
|
||||
fn allocate(&mut self, size: usize) -> Result<(&mut [u8], &mut FunctionTable), String> {
|
||||
fn allocate(&mut self, size: usize) -> Result<(&mut [u8], &mut FunctionTable, usize), String> {
|
||||
if self.current.mmap.len() - self.position < size {
|
||||
self.push_current(cmp::max(0x10000, size))?;
|
||||
}
|
||||
@@ -146,6 +151,7 @@ impl CodeMemory {
|
||||
Ok((
|
||||
&mut self.current.mmap.as_mut_slice()[old_position..self.position],
|
||||
&mut self.current.table,
|
||||
old_position,
|
||||
))
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ use wasmtime_environ::{
|
||||
use wasmtime_profiling::ProfilingAgent;
|
||||
use wasmtime_runtime::{
|
||||
InstantiationError, SignatureRegistry, TrapRegistration, TrapRegistry, VMFunctionBody,
|
||||
VMSharedSignatureIndex,
|
||||
VMSharedSignatureIndex, VMTrampoline,
|
||||
};
|
||||
|
||||
/// Select which kind of compilation to use.
|
||||
@@ -53,13 +53,9 @@ pub struct Compiler {
|
||||
|
||||
code_memory: CodeMemory,
|
||||
trap_registry: TrapRegistry,
|
||||
trampoline_park: HashMap<VMSharedSignatureIndex, *const VMFunctionBody>,
|
||||
signatures: SignatureRegistry,
|
||||
strategy: CompilationStrategy,
|
||||
cache_config: CacheConfig,
|
||||
|
||||
/// The `FunctionBuilderContext`, shared between trampline function compilations.
|
||||
fn_builder_ctx: FunctionBuilderContext,
|
||||
}
|
||||
|
||||
impl Compiler {
|
||||
@@ -72,9 +68,7 @@ impl Compiler {
|
||||
Self {
|
||||
isa,
|
||||
code_memory: CodeMemory::new(),
|
||||
trampoline_park: HashMap::new(),
|
||||
signatures: SignatureRegistry::new(),
|
||||
fn_builder_ctx: FunctionBuilderContext::new(),
|
||||
strategy,
|
||||
trap_registry: TrapRegistry::default(),
|
||||
cache_config,
|
||||
@@ -103,6 +97,7 @@ impl Compiler {
|
||||
) -> Result<
|
||||
(
|
||||
PrimaryMap<DefinedFuncIndex, *mut [VMFunctionBody]>,
|
||||
HashMap<VMSharedSignatureIndex, VMTrampoline>,
|
||||
PrimaryMap<DefinedFuncIndex, ir::JumpTableOffsets>,
|
||||
Relocations,
|
||||
Option<Vec<u8>>,
|
||||
@@ -145,6 +140,8 @@ impl Compiler {
|
||||
}
|
||||
.map_err(SetupError::Compile)?;
|
||||
|
||||
// Allocate all of the compiled functions into executable memory,
|
||||
// copying over their contents.
|
||||
let allocated_functions =
|
||||
allocate_functions(&mut self.code_memory, &compilation).map_err(|message| {
|
||||
SetupError::Instantiate(InstantiationError::Resource(format!(
|
||||
@@ -153,8 +150,45 @@ impl Compiler {
|
||||
)))
|
||||
})?;
|
||||
|
||||
// Create a registration value for all traps in our allocated
|
||||
// functions. This registration will allow us to map a trapping PC
|
||||
// value to what the trap actually means if it came from JIT code.
|
||||
let trap_registration = register_traps(&allocated_functions, &traps, &self.trap_registry);
|
||||
|
||||
// Eagerly generate a entry trampoline for every type signature in the
|
||||
// module. This should be "relatively lightweight" for most modules and
|
||||
// guarantees that all functions (including indirect ones through
|
||||
// tables) have a trampoline when invoked through the wasmtime API.
|
||||
let mut cx = FunctionBuilderContext::new();
|
||||
let mut trampolines = HashMap::new();
|
||||
for sig in module.local.signatures.values() {
|
||||
let index = self.signatures.register(sig);
|
||||
if trampolines.contains_key(&index) {
|
||||
continue;
|
||||
}
|
||||
// FIXME(#1303) we should be generating a trampoline for all
|
||||
// functions in a module, not just those with less than 40
|
||||
// arguments. Currently though cranelift dies in spec tests when one
|
||||
// function has 100 arguments. This looks to be a cranelift bug, so
|
||||
// let's work around it for now by skipping generating a trampoline
|
||||
// for that massive function. The trampoline isn't actually needed
|
||||
// at this time, and we'll hopefully get the cranelift bug fixed
|
||||
// soon enough to remove this condition.
|
||||
if sig.params.len() > 40 {
|
||||
continue;
|
||||
}
|
||||
trampolines.insert(
|
||||
index,
|
||||
make_trampoline(
|
||||
&*self.isa,
|
||||
&mut self.code_memory,
|
||||
&mut cx,
|
||||
sig,
|
||||
std::mem::size_of::<u128>(),
|
||||
)?,
|
||||
);
|
||||
}
|
||||
|
||||
// Translate debug info (DWARF) only if at least one function is present.
|
||||
let dbg = if debug_data.is_some() && !allocated_functions.is_empty() {
|
||||
let target_config = self.isa.frontend_config();
|
||||
@@ -199,6 +233,7 @@ impl Compiler {
|
||||
|
||||
Ok((
|
||||
allocated_functions,
|
||||
trampolines,
|
||||
jt_offsets,
|
||||
relocations,
|
||||
dbg,
|
||||
@@ -206,38 +241,6 @@ impl Compiler {
|
||||
))
|
||||
}
|
||||
|
||||
/// Create a trampoline for invoking a function.
|
||||
pub(crate) fn get_trampoline(
|
||||
&mut self,
|
||||
signature: &ir::Signature,
|
||||
value_size: usize,
|
||||
) -> Result<*const VMFunctionBody, SetupError> {
|
||||
let index = self.signatures.register(signature);
|
||||
if let Some(trampoline) = self.trampoline_park.get(&index) {
|
||||
return Ok(*trampoline);
|
||||
}
|
||||
let body = make_trampoline(
|
||||
&*self.isa,
|
||||
&mut self.code_memory,
|
||||
&mut self.fn_builder_ctx,
|
||||
signature,
|
||||
value_size,
|
||||
)?;
|
||||
self.trampoline_park.insert(index, body);
|
||||
return Ok(body);
|
||||
}
|
||||
|
||||
/// Create and publish a trampoline for invoking a function.
|
||||
pub fn get_published_trampoline(
|
||||
&mut self,
|
||||
signature: &ir::Signature,
|
||||
value_size: usize,
|
||||
) -> Result<*const VMFunctionBody, SetupError> {
|
||||
let result = self.get_trampoline(signature, value_size)?;
|
||||
self.publish_compiled_code();
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Make memory containing compiled code executable.
|
||||
pub(crate) fn publish_compiled_code(&mut self) {
|
||||
self.code_memory.publish();
|
||||
@@ -265,13 +268,13 @@ impl Compiler {
|
||||
}
|
||||
|
||||
/// Create a trampoline for invoking a function.
|
||||
fn make_trampoline(
|
||||
pub fn make_trampoline(
|
||||
isa: &dyn TargetIsa,
|
||||
code_memory: &mut CodeMemory,
|
||||
fn_builder_ctx: &mut FunctionBuilderContext,
|
||||
signature: &ir::Signature,
|
||||
value_size: usize,
|
||||
) -> Result<*const VMFunctionBody, SetupError> {
|
||||
) -> Result<VMTrampoline, SetupError> {
|
||||
let pointer_type = isa.pointer_type();
|
||||
let mut wrapper_sig = ir::Signature::new(isa.frontend_config().default_call_conv);
|
||||
|
||||
@@ -373,14 +376,15 @@ fn make_trampoline(
|
||||
|
||||
let unwind_info = CompiledFunctionUnwindInfo::new(isa, &context);
|
||||
|
||||
Ok(code_memory
|
||||
let ptr = code_memory
|
||||
.allocate_for_function(&CompiledFunction {
|
||||
body: code_buf,
|
||||
jt_offsets: context.func.jt_offsets,
|
||||
unwind_info,
|
||||
})
|
||||
.map_err(|message| SetupError::Instantiate(InstantiationError::Resource(message)))?
|
||||
.as_ptr())
|
||||
.as_ptr();
|
||||
Ok(unsafe { std::mem::transmute::<*const VMFunctionBody, VMTrampoline>(ptr) })
|
||||
}
|
||||
|
||||
fn allocate_functions(
|
||||
|
||||
@@ -7,27 +7,28 @@ use wasmtime_environ::entity::PrimaryMap;
|
||||
use wasmtime_environ::wasm::{Global, GlobalInit, Memory, Table, TableElementType};
|
||||
use wasmtime_environ::{MemoryPlan, MemoryStyle, Module, TablePlan};
|
||||
use wasmtime_runtime::{
|
||||
Export, Imports, InstanceHandle, LinkError, VMFunctionImport, VMGlobalImport, VMMemoryImport,
|
||||
VMTableImport,
|
||||
Export, Imports, InstanceHandle, LinkError, SignatureRegistry, VMFunctionImport,
|
||||
VMGlobalImport, VMMemoryImport, VMTableImport,
|
||||
};
|
||||
|
||||
/// This function allows to match all imports of a `Module` with concrete definitions provided by
|
||||
/// a `Resolver`.
|
||||
///
|
||||
/// If all imports are satisfied returns an `Imports` instance required for a module instantiation.
|
||||
pub fn resolve_imports(module: &Module, resolver: &mut dyn Resolver) -> Result<Imports, LinkError> {
|
||||
pub fn resolve_imports(
|
||||
module: &Module,
|
||||
signatures: &SignatureRegistry,
|
||||
resolver: &mut dyn Resolver,
|
||||
) -> Result<Imports, LinkError> {
|
||||
let mut dependencies = HashSet::new();
|
||||
|
||||
let mut function_imports = PrimaryMap::with_capacity(module.imported_funcs.len());
|
||||
for (index, (module_name, field, import_idx)) in module.imported_funcs.iter() {
|
||||
match resolver.resolve(*import_idx, module_name, field) {
|
||||
Some(export_value) => match export_value {
|
||||
Export::Function {
|
||||
address,
|
||||
signature,
|
||||
vmctx,
|
||||
} => {
|
||||
Export::Function(f) => {
|
||||
let import_signature = &module.local.signatures[module.local.functions[index]];
|
||||
let signature = signatures.lookup(f.signature).unwrap();
|
||||
if signature != *import_signature {
|
||||
// TODO: If the difference is in the calling convention,
|
||||
// we could emit a wrapper function to fix it up.
|
||||
@@ -37,13 +38,13 @@ pub fn resolve_imports(module: &Module, resolver: &mut dyn Resolver) -> Result<I
|
||||
module_name, field, signature, import_signature
|
||||
)));
|
||||
}
|
||||
dependencies.insert(unsafe { InstanceHandle::from_vmctx(vmctx) });
|
||||
dependencies.insert(unsafe { InstanceHandle::from_vmctx(f.vmctx) });
|
||||
function_imports.push(VMFunctionImport {
|
||||
body: address,
|
||||
vmctx,
|
||||
body: f.address,
|
||||
vmctx: f.vmctx,
|
||||
});
|
||||
}
|
||||
Export::Table { .. } | Export::Memory { .. } | Export::Global { .. } => {
|
||||
Export::Table(_) | Export::Memory(_) | Export::Global(_) => {
|
||||
return Err(LinkError(format!(
|
||||
"{}/{}: incompatible import type: export incompatible with function import",
|
||||
module_name, field
|
||||
@@ -63,26 +64,22 @@ pub fn resolve_imports(module: &Module, resolver: &mut dyn Resolver) -> Result<I
|
||||
for (index, (module_name, field, import_idx)) in module.imported_tables.iter() {
|
||||
match resolver.resolve(*import_idx, module_name, field) {
|
||||
Some(export_value) => match export_value {
|
||||
Export::Table {
|
||||
definition,
|
||||
vmctx,
|
||||
table,
|
||||
} => {
|
||||
Export::Table(t) => {
|
||||
let import_table = &module.local.table_plans[index];
|
||||
if !is_table_compatible(&table, import_table) {
|
||||
if !is_table_compatible(&t.table, import_table) {
|
||||
return Err(LinkError(format!(
|
||||
"{}/{}: incompatible import type: exported table incompatible with \
|
||||
table import",
|
||||
module_name, field,
|
||||
)));
|
||||
}
|
||||
dependencies.insert(unsafe { InstanceHandle::from_vmctx(vmctx) });
|
||||
dependencies.insert(unsafe { InstanceHandle::from_vmctx(t.vmctx) });
|
||||
table_imports.push(VMTableImport {
|
||||
from: definition,
|
||||
vmctx,
|
||||
from: t.definition,
|
||||
vmctx: t.vmctx,
|
||||
});
|
||||
}
|
||||
Export::Global { .. } | Export::Memory { .. } | Export::Function { .. } => {
|
||||
Export::Global(_) | Export::Memory(_) | Export::Function(_) => {
|
||||
return Err(LinkError(format!(
|
||||
"{}/{}: incompatible import type: export incompatible with table import",
|
||||
module_name, field
|
||||
@@ -102,13 +99,9 @@ pub fn resolve_imports(module: &Module, resolver: &mut dyn Resolver) -> Result<I
|
||||
for (index, (module_name, field, import_idx)) in module.imported_memories.iter() {
|
||||
match resolver.resolve(*import_idx, module_name, field) {
|
||||
Some(export_value) => match export_value {
|
||||
Export::Memory {
|
||||
definition,
|
||||
vmctx,
|
||||
memory,
|
||||
} => {
|
||||
Export::Memory(m) => {
|
||||
let import_memory = &module.local.memory_plans[index];
|
||||
if !is_memory_compatible(&memory, import_memory) {
|
||||
if !is_memory_compatible(&m.memory, import_memory) {
|
||||
return Err(LinkError(format!(
|
||||
"{}/{}: incompatible import type: exported memory incompatible with \
|
||||
memory import",
|
||||
@@ -123,19 +116,19 @@ pub fn resolve_imports(module: &Module, resolver: &mut dyn Resolver) -> Result<I
|
||||
MemoryStyle::Static {
|
||||
bound: import_bound,
|
||||
},
|
||||
) = (memory.style, &import_memory.style)
|
||||
) = (m.memory.style, &import_memory.style)
|
||||
{
|
||||
assert_ge!(bound, *import_bound);
|
||||
}
|
||||
assert_ge!(memory.offset_guard_size, import_memory.offset_guard_size);
|
||||
assert_ge!(m.memory.offset_guard_size, import_memory.offset_guard_size);
|
||||
|
||||
dependencies.insert(unsafe { InstanceHandle::from_vmctx(vmctx) });
|
||||
dependencies.insert(unsafe { InstanceHandle::from_vmctx(m.vmctx) });
|
||||
memory_imports.push(VMMemoryImport {
|
||||
from: definition,
|
||||
vmctx,
|
||||
from: m.definition,
|
||||
vmctx: m.vmctx,
|
||||
});
|
||||
}
|
||||
Export::Table { .. } | Export::Global { .. } | Export::Function { .. } => {
|
||||
Export::Table(_) | Export::Global(_) | Export::Function(_) => {
|
||||
return Err(LinkError(format!(
|
||||
"{}/{}: incompatible import type: export incompatible with memory import",
|
||||
module_name, field
|
||||
@@ -155,28 +148,24 @@ pub fn resolve_imports(module: &Module, resolver: &mut dyn Resolver) -> Result<I
|
||||
for (index, (module_name, field, import_idx)) in module.imported_globals.iter() {
|
||||
match resolver.resolve(*import_idx, module_name, field) {
|
||||
Some(export_value) => match export_value {
|
||||
Export::Table { .. } | Export::Memory { .. } | Export::Function { .. } => {
|
||||
Export::Table(_) | Export::Memory(_) | Export::Function(_) => {
|
||||
return Err(LinkError(format!(
|
||||
"{}/{}: incompatible import type: exported global incompatible with \
|
||||
global import",
|
||||
module_name, field
|
||||
)));
|
||||
}
|
||||
Export::Global {
|
||||
definition,
|
||||
vmctx,
|
||||
global,
|
||||
} => {
|
||||
Export::Global(g) => {
|
||||
let imported_global = module.local.globals[index];
|
||||
if !is_global_compatible(&global, &imported_global) {
|
||||
if !is_global_compatible(&g.global, &imported_global) {
|
||||
return Err(LinkError(format!(
|
||||
"{}/{}: incompatible import type: exported global incompatible with \
|
||||
global import",
|
||||
module_name, field
|
||||
)));
|
||||
}
|
||||
dependencies.insert(unsafe { InstanceHandle::from_vmctx(vmctx) });
|
||||
global_imports.push(VMGlobalImport { from: definition });
|
||||
dependencies.insert(unsafe { InstanceHandle::from_vmctx(g.vmctx) });
|
||||
global_imports.push(VMGlobalImport { from: g.definition });
|
||||
}
|
||||
},
|
||||
None => {
|
||||
|
||||
@@ -7,6 +7,7 @@ use crate::compiler::Compiler;
|
||||
use crate::imports::resolve_imports;
|
||||
use crate::link::link_module;
|
||||
use crate::resolver::Resolver;
|
||||
use std::collections::HashMap;
|
||||
use std::io::Write;
|
||||
use std::rc::Rc;
|
||||
use std::sync::{Arc, Mutex};
|
||||
@@ -19,8 +20,8 @@ use wasmtime_environ::{
|
||||
};
|
||||
use wasmtime_profiling::ProfilingAgent;
|
||||
use wasmtime_runtime::{
|
||||
GdbJitImageRegistration, InstanceHandle, InstantiationError, TrapRegistration, VMFunctionBody,
|
||||
VMSharedSignatureIndex,
|
||||
GdbJitImageRegistration, InstanceHandle, InstantiationError, SignatureRegistry,
|
||||
TrapRegistration, VMFunctionBody, VMSharedSignatureIndex, VMTrampoline,
|
||||
};
|
||||
|
||||
/// An error condition while setting up a wasm instance, be it validation,
|
||||
@@ -50,6 +51,7 @@ pub enum SetupError {
|
||||
struct RawCompiledModule<'data> {
|
||||
module: Module,
|
||||
finished_functions: BoxedSlice<DefinedFuncIndex, *mut [VMFunctionBody]>,
|
||||
trampolines: HashMap<VMSharedSignatureIndex, VMTrampoline>,
|
||||
data_initializers: Box<[DataInitializer<'data>]>,
|
||||
signatures: BoxedSlice<SignatureIndex, VMSharedSignatureIndex>,
|
||||
dbg_jit_registration: Option<GdbJitImageRegistration>,
|
||||
@@ -78,13 +80,19 @@ impl<'data> RawCompiledModule<'data> {
|
||||
None
|
||||
};
|
||||
|
||||
let (finished_functions, jt_offsets, relocations, dbg_image, trap_registration) = compiler
|
||||
.compile(
|
||||
&translation.module,
|
||||
translation.module_translation.as_ref().unwrap(),
|
||||
translation.function_body_inputs,
|
||||
debug_data,
|
||||
)?;
|
||||
let (
|
||||
finished_functions,
|
||||
trampolines,
|
||||
jt_offsets,
|
||||
relocations,
|
||||
dbg_image,
|
||||
trap_registration,
|
||||
) = compiler.compile(
|
||||
&translation.module,
|
||||
translation.module_translation.as_ref().unwrap(),
|
||||
translation.function_body_inputs,
|
||||
debug_data,
|
||||
)?;
|
||||
|
||||
link_module(
|
||||
&translation.module,
|
||||
@@ -135,6 +143,7 @@ impl<'data> RawCompiledModule<'data> {
|
||||
Ok(Self {
|
||||
module: translation.module,
|
||||
finished_functions: finished_functions.into_boxed_slice(),
|
||||
trampolines,
|
||||
data_initializers: translation.data_initializers.into_boxed_slice(),
|
||||
signatures: signatures.into_boxed_slice(),
|
||||
dbg_jit_registration,
|
||||
@@ -147,6 +156,7 @@ impl<'data> RawCompiledModule<'data> {
|
||||
pub struct CompiledModule {
|
||||
module: Arc<Module>,
|
||||
finished_functions: BoxedSlice<DefinedFuncIndex, *mut [VMFunctionBody]>,
|
||||
trampolines: HashMap<VMSharedSignatureIndex, VMTrampoline>,
|
||||
data_initializers: Box<[OwnedDataInitializer]>,
|
||||
signatures: BoxedSlice<SignatureIndex, VMSharedSignatureIndex>,
|
||||
dbg_jit_registration: Option<Rc<GdbJitImageRegistration>>,
|
||||
@@ -166,6 +176,7 @@ impl CompiledModule {
|
||||
Ok(Self::from_parts(
|
||||
raw.module,
|
||||
raw.finished_functions,
|
||||
raw.trampolines,
|
||||
raw.data_initializers
|
||||
.iter()
|
||||
.map(OwnedDataInitializer::new)
|
||||
@@ -181,6 +192,7 @@ impl CompiledModule {
|
||||
pub fn from_parts(
|
||||
module: Module,
|
||||
finished_functions: BoxedSlice<DefinedFuncIndex, *mut [VMFunctionBody]>,
|
||||
trampolines: HashMap<VMSharedSignatureIndex, VMTrampoline>,
|
||||
data_initializers: Box<[OwnedDataInitializer]>,
|
||||
signatures: BoxedSlice<SignatureIndex, VMSharedSignatureIndex>,
|
||||
dbg_jit_registration: Option<GdbJitImageRegistration>,
|
||||
@@ -189,6 +201,7 @@ impl CompiledModule {
|
||||
Self {
|
||||
module: Arc::new(module),
|
||||
finished_functions,
|
||||
trampolines,
|
||||
data_initializers,
|
||||
signatures,
|
||||
dbg_jit_registration: dbg_jit_registration.map(Rc::new),
|
||||
@@ -209,6 +222,7 @@ impl CompiledModule {
|
||||
&self,
|
||||
is_bulk_memory: bool,
|
||||
resolver: &mut dyn Resolver,
|
||||
sig_registry: &SignatureRegistry,
|
||||
) -> Result<InstanceHandle, InstantiationError> {
|
||||
let data_initializers = self
|
||||
.data_initializers
|
||||
@@ -218,11 +232,12 @@ impl CompiledModule {
|
||||
data: &*init.data,
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let imports = resolve_imports(&self.module, resolver)?;
|
||||
let imports = resolve_imports(&self.module, &sig_registry, resolver)?;
|
||||
InstanceHandle::new(
|
||||
Arc::clone(&self.module),
|
||||
self.trap_registration.clone(),
|
||||
self.finished_functions.clone(),
|
||||
self.trampolines.clone(),
|
||||
imports,
|
||||
&data_initializers,
|
||||
self.signatures.clone(),
|
||||
@@ -284,7 +299,10 @@ pub unsafe fn instantiate(
|
||||
is_bulk_memory: bool,
|
||||
profiler: Option<&Arc<Mutex<Box<dyn ProfilingAgent + Send>>>>,
|
||||
) -> Result<InstanceHandle, SetupError> {
|
||||
let instance = CompiledModule::new(compiler, data, debug_info, profiler)?
|
||||
.instantiate(is_bulk_memory, resolver)?;
|
||||
let instance = CompiledModule::new(compiler, data, debug_info, profiler)?.instantiate(
|
||||
is_bulk_memory,
|
||||
resolver,
|
||||
compiler.signatures(),
|
||||
)?;
|
||||
Ok(instance)
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ pub mod native;
|
||||
pub mod trampoline;
|
||||
|
||||
pub use crate::code_memory::CodeMemory;
|
||||
pub use crate::compiler::{CompilationStrategy, Compiler};
|
||||
pub use crate::compiler::{make_trampoline, CompilationStrategy, Compiler};
|
||||
pub use crate::instantiate::{instantiate, CompiledModule, SetupError};
|
||||
pub use crate::link::link_module;
|
||||
pub use crate::resolver::{NullResolver, Resolver};
|
||||
|
||||
Reference in New Issue
Block a user