Remove some allocations in CodeMemory (#3253)
* Remove some allocations in `CodeMemory` This commit removes the `FinishedFunctions` type as well as allocations associated with trampolines when allocating inside of a `CodeMemory`. The main goal of this commit is to improve the time spent in `CodeMemory` where currently today a good portion of time is spent simply parsing symbol names and trying to extract function indices from them. Instead this commit implements a new strategy (different from #3236) where compilation records offset/length information for all functions/trampolines so this doesn't need to be re-learned from the object file later. A consequence of this commit is that this offset information will be decoded/encoded through `bincode` unconditionally, but we can also optimize that later if necessary as well. Internally this involved quite a bit of refactoring since the previous map for `FinishedFunctions` was relatively heavily relied upon. * comments
This commit is contained in:
@@ -732,7 +732,8 @@ impl<'a> Instantiator<'a> {
|
||||
.allocator()
|
||||
.allocate(InstanceAllocationRequest {
|
||||
module: compiled_module.module().clone(),
|
||||
finished_functions: compiled_module.finished_functions(),
|
||||
image_base: compiled_module.code().range().0,
|
||||
functions: compiled_module.functions(),
|
||||
imports: self.cur.build(),
|
||||
shared_signatures: self.cur.module.signatures().as_module_map().into(),
|
||||
host_state: Box::new(Instance(instance_to_be)),
|
||||
|
||||
@@ -398,7 +398,7 @@ impl Module {
|
||||
.collect();
|
||||
|
||||
let mut obj = engine.compiler().object()?;
|
||||
let funcs = engine.compiler().emit_obj(
|
||||
let (funcs, trampolines) = engine.compiler().emit_obj(
|
||||
&translation,
|
||||
&types,
|
||||
funcs,
|
||||
@@ -412,7 +412,8 @@ impl Module {
|
||||
translation.try_paged_init();
|
||||
}
|
||||
|
||||
let (mmap, info) = wasmtime_jit::finish_compile(translation, obj, funcs, tunables)?;
|
||||
let (mmap, info) =
|
||||
wasmtime_jit::finish_compile(translation, obj, funcs, trampolines, tunables)?;
|
||||
Ok((mmap, Some(info)))
|
||||
})?;
|
||||
|
||||
@@ -486,7 +487,7 @@ impl Module {
|
||||
let signatures = Arc::new(SignatureCollection::new_for_module(
|
||||
engine.signatures(),
|
||||
&types.wasm_signatures,
|
||||
modules.iter().flat_map(|m| m.trampolines().iter().cloned()),
|
||||
modules.iter().flat_map(|m| m.trampolines()),
|
||||
));
|
||||
|
||||
let module = modules.remove(main_module);
|
||||
|
||||
@@ -52,7 +52,7 @@ impl ModuleRegistry {
|
||||
// and for schemes like uffd this performs lazy initialization which
|
||||
// could use the module in the future. For that reason we continue to
|
||||
// register empty modules and retain them.
|
||||
if compiled_module.finished_functions().is_empty() {
|
||||
if compiled_module.finished_functions().len() == 0 {
|
||||
self.modules_without_code.push(compiled_module.clone());
|
||||
return;
|
||||
}
|
||||
@@ -539,13 +539,19 @@ fn test_frame_info() -> Result<(), anyhow::Error> {
|
||||
GlobalModuleRegistry::with(|modules| {
|
||||
for (i, alloc) in module.compiled_module().finished_functions() {
|
||||
let (start, end) = unsafe {
|
||||
let ptr = (**alloc).as_ptr();
|
||||
let len = (**alloc).len();
|
||||
let ptr = (*alloc).as_ptr();
|
||||
let len = (*alloc).len();
|
||||
(ptr as usize, ptr as usize + len)
|
||||
};
|
||||
for pc in start..end {
|
||||
let (frame, _, _) = modules.lookup_frame_info(pc).unwrap();
|
||||
assert!(frame.func_index() == i.as_u32());
|
||||
assert!(
|
||||
frame.func_index() == i.as_u32(),
|
||||
"lookup of {:#x} returned {}, expected {}",
|
||||
pc,
|
||||
frame.func_index(),
|
||||
i.as_u32()
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -194,7 +194,7 @@ impl<T> Store<T> {
|
||||
/// tables created to 10,000. This can be overridden with the
|
||||
/// [`Store::limiter`] configuration method.
|
||||
pub fn new(engine: &Engine, data: T) -> Self {
|
||||
let finished_functions = &Default::default();
|
||||
let functions = &Default::default();
|
||||
// Wasmtime uses the callee argument to host functions to learn about
|
||||
// the original pointer to the `Store` itself, allowing it to
|
||||
// reconstruct a `StoreContextMut<T>`. When we initially call a `Func`,
|
||||
@@ -206,7 +206,8 @@ impl<T> Store<T> {
|
||||
OnDemandInstanceAllocator::default()
|
||||
.allocate(InstanceAllocationRequest {
|
||||
host_state: Box::new(()),
|
||||
finished_functions,
|
||||
image_base: 0,
|
||||
functions,
|
||||
shared_signatures: None.into(),
|
||||
imports: Default::default(),
|
||||
module: Arc::new(wasmtime_environ::Module::default()),
|
||||
|
||||
@@ -16,24 +16,22 @@ use crate::{GlobalType, MemoryType, TableType, Val};
|
||||
use anyhow::Result;
|
||||
use std::any::Any;
|
||||
use std::sync::Arc;
|
||||
use wasmtime_environ::{
|
||||
DefinedFuncIndex, EntityIndex, GlobalIndex, MemoryIndex, Module, PrimaryMap, TableIndex,
|
||||
};
|
||||
use wasmtime_environ::{EntityIndex, GlobalIndex, MemoryIndex, Module, TableIndex};
|
||||
use wasmtime_runtime::{
|
||||
Imports, InstanceAllocationRequest, InstanceAllocator, OnDemandInstanceAllocator,
|
||||
VMFunctionBody, VMFunctionImport, VMSharedSignatureIndex,
|
||||
VMFunctionImport, VMSharedSignatureIndex,
|
||||
};
|
||||
|
||||
fn create_handle(
|
||||
module: Module,
|
||||
store: &mut StoreOpaque<'_>,
|
||||
finished_functions: PrimaryMap<DefinedFuncIndex, *mut [VMFunctionBody]>,
|
||||
host_state: Box<dyn Any + Send + Sync>,
|
||||
func_imports: &[VMFunctionImport],
|
||||
shared_signature_id: Option<VMSharedSignatureIndex>,
|
||||
) -> Result<InstanceId> {
|
||||
let mut imports = Imports::default();
|
||||
imports.functions = func_imports;
|
||||
let functions = &Default::default();
|
||||
|
||||
unsafe {
|
||||
let config = store.engine().config();
|
||||
@@ -43,7 +41,8 @@ fn create_handle(
|
||||
let handle = OnDemandInstanceAllocator::new(config.mem_creator.clone(), 0).allocate(
|
||||
InstanceAllocationRequest {
|
||||
module: Arc::new(module),
|
||||
finished_functions: &finished_functions,
|
||||
functions,
|
||||
image_base: 0,
|
||||
imports,
|
||||
shared_signatures: shared_signature_id.into(),
|
||||
host_state,
|
||||
|
||||
@@ -77,22 +77,23 @@ pub fn create_function(
|
||||
engine: &Engine,
|
||||
) -> Result<(InstanceHandle, VMTrampoline)> {
|
||||
let mut obj = engine.compiler().object()?;
|
||||
engine
|
||||
.compiler()
|
||||
.emit_trampoline_obj(ft.as_wasm_func_type(), stub_fn as usize, &mut obj)?;
|
||||
let (t1, t2) = engine.compiler().emit_trampoline_obj(
|
||||
ft.as_wasm_func_type(),
|
||||
stub_fn as usize,
|
||||
&mut obj,
|
||||
)?;
|
||||
let obj = obj.write()?;
|
||||
|
||||
// Copy the results of JIT compilation into executable memory, and this will
|
||||
// also take care of unwind table registration.
|
||||
let mut code_memory = CodeMemory::new();
|
||||
let alloc = code_memory.allocate_for_object_unparsed(&obj)?;
|
||||
let mut trampolines = alloc.trampolines();
|
||||
let (host_i, host_trampoline) = trampolines.next().unwrap();
|
||||
assert_eq!(host_i.as_u32(), 0);
|
||||
let (wasm_i, wasm_trampoline) = trampolines.next().unwrap();
|
||||
assert_eq!(wasm_i.as_u32(), 1);
|
||||
assert!(trampolines.next().is_none());
|
||||
let host_trampoline = host_trampoline.as_ptr();
|
||||
let wasm_trampoline = wasm_trampoline as *mut [_];
|
||||
drop(trampolines);
|
||||
let (alloc, _obj) = code_memory.allocate_for_object_unparsed(&obj)?;
|
||||
|
||||
// Extract the host/wasm trampolines from the results of compilation since
|
||||
// we know their start/length.
|
||||
let host_trampoline = alloc[t1.start as usize..][..t1.length as usize].as_ptr();
|
||||
let wasm_trampoline = &mut alloc[t2.start as usize..][..t2.length as usize];
|
||||
let wasm_trampoline = wasm_trampoline as *mut [u8] as *mut [VMFunctionBody];
|
||||
|
||||
code_memory.publish();
|
||||
|
||||
@@ -104,8 +105,7 @@ pub fn create_function(
|
||||
sig,
|
||||
Box::new(TrampolineState { func, code_memory }),
|
||||
)?;
|
||||
let host_trampoline =
|
||||
std::mem::transmute::<*const VMFunctionBody, VMTrampoline>(host_trampoline);
|
||||
let host_trampoline = std::mem::transmute::<*const u8, VMTrampoline>(host_trampoline);
|
||||
Ok((instance, host_trampoline))
|
||||
}
|
||||
}
|
||||
@@ -116,7 +116,8 @@ pub unsafe fn create_raw_function(
|
||||
host_state: Box<dyn Any + Send + Sync>,
|
||||
) -> Result<InstanceHandle> {
|
||||
let mut module = Module::new();
|
||||
let mut finished_functions = PrimaryMap::new();
|
||||
let mut functions = PrimaryMap::new();
|
||||
functions.push(Default::default());
|
||||
|
||||
let sig_id = SignatureIndex::from_u32(u32::max_value() - 1);
|
||||
module.types.push(ModuleType::Function(sig_id));
|
||||
@@ -124,12 +125,12 @@ pub unsafe fn create_raw_function(
|
||||
module
|
||||
.exports
|
||||
.insert(String::new(), EntityIndex::Function(func_id));
|
||||
finished_functions.push(func);
|
||||
|
||||
Ok(
|
||||
OnDemandInstanceAllocator::default().allocate(InstanceAllocationRequest {
|
||||
module: Arc::new(module),
|
||||
finished_functions: &finished_functions,
|
||||
functions: &functions,
|
||||
image_base: (*func).as_ptr() as usize,
|
||||
imports: Imports::default(),
|
||||
shared_signatures: sig.into(),
|
||||
host_state,
|
||||
|
||||
@@ -2,9 +2,7 @@ use crate::store::{InstanceId, StoreOpaque};
|
||||
use crate::trampoline::create_handle;
|
||||
use crate::{GlobalType, Mutability, Val};
|
||||
use anyhow::Result;
|
||||
use wasmtime_environ::{
|
||||
EntityIndex, Global, GlobalInit, Module, ModuleType, PrimaryMap, SignatureIndex,
|
||||
};
|
||||
use wasmtime_environ::{EntityIndex, Global, GlobalInit, Module, ModuleType, SignatureIndex};
|
||||
use wasmtime_runtime::VMFunctionImport;
|
||||
|
||||
pub fn create_global(store: &mut StoreOpaque<'_>, gt: &GlobalType, val: Val) -> Result<InstanceId> {
|
||||
@@ -69,7 +67,6 @@ pub fn create_global(store: &mut StoreOpaque<'_>, gt: &GlobalType, val: Val) ->
|
||||
let id = create_handle(
|
||||
module,
|
||||
store,
|
||||
PrimaryMap::new(),
|
||||
Box::new(()),
|
||||
&func_imports,
|
||||
shared_signature_id,
|
||||
|
||||
@@ -5,7 +5,7 @@ use crate::MemoryType;
|
||||
use anyhow::{anyhow, Result};
|
||||
use std::convert::TryFrom;
|
||||
use std::sync::Arc;
|
||||
use wasmtime_environ::{EntityIndex, MemoryPlan, MemoryStyle, Module, PrimaryMap, WASM_PAGE_SIZE};
|
||||
use wasmtime_environ::{EntityIndex, MemoryPlan, MemoryStyle, Module, WASM_PAGE_SIZE};
|
||||
use wasmtime_runtime::{RuntimeLinearMemory, RuntimeMemoryCreator, VMMemoryDefinition};
|
||||
|
||||
pub fn create_memory(store: &mut StoreOpaque<'_>, memory: &MemoryType) -> Result<InstanceId> {
|
||||
@@ -20,7 +20,7 @@ pub fn create_memory(store: &mut StoreOpaque<'_>, memory: &MemoryType) -> Result
|
||||
.exports
|
||||
.insert(String::new(), EntityIndex::Memory(memory_id));
|
||||
|
||||
create_handle(module, store, PrimaryMap::new(), Box::new(()), &[], None)
|
||||
create_handle(module, store, Box::new(()), &[], None)
|
||||
}
|
||||
|
||||
struct LinearMemoryProxy {
|
||||
|
||||
@@ -2,7 +2,7 @@ use crate::store::{InstanceId, StoreOpaque};
|
||||
use crate::trampoline::create_handle;
|
||||
use crate::TableType;
|
||||
use anyhow::Result;
|
||||
use wasmtime_environ::{EntityIndex, Module, PrimaryMap};
|
||||
use wasmtime_environ::{EntityIndex, Module};
|
||||
|
||||
pub fn create_table(store: &mut StoreOpaque<'_>, table: &TableType) -> Result<InstanceId> {
|
||||
let mut module = Module::new();
|
||||
@@ -16,5 +16,5 @@ pub fn create_table(store: &mut StoreOpaque<'_>, table: &TableType) -> Result<In
|
||||
.exports
|
||||
.insert(String::new(), EntityIndex::Table(table_id));
|
||||
|
||||
create_handle(module, store, PrimaryMap::new(), Box::new(()), &[], None)
|
||||
create_handle(module, store, Box::new(()), &[], None)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user