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:
Alex Crichton
2021-08-30 10:35:17 -05:00
committed by GitHub
parent c73be1f13a
commit a237e73b5a
26 changed files with 271 additions and 288 deletions

View File

@@ -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)),

View File

@@ -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);

View File

@@ -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()
);
}
}
});

View File

@@ -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()),

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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 {

View File

@@ -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)
}