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:
@@ -21,13 +21,13 @@ use cranelift_wasm::{
|
||||
use object::write::Object;
|
||||
use std::any::Any;
|
||||
use std::cmp;
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::collections::BTreeMap;
|
||||
use std::convert::TryFrom;
|
||||
use std::mem;
|
||||
use std::sync::Mutex;
|
||||
use wasmtime_environ::{
|
||||
AddressMapSection, CompileError, FilePos, FlagValue, FunctionBodyData, FunctionInfo,
|
||||
InstructionAddressMap, Module, ModuleTranslation, StackMapInformation, TrapCode,
|
||||
InstructionAddressMap, Module, ModuleTranslation, StackMapInformation, Trampoline, TrapCode,
|
||||
TrapEncodingBuilder, TrapInformation, Tunables, TypeTables, VMOffsets,
|
||||
};
|
||||
|
||||
@@ -120,12 +120,12 @@ impl wasmtime_environ::Compiler for Compiler {
|
||||
let func_index = module.func_index(func_index);
|
||||
let mut context = Context::new();
|
||||
context.func.name = get_func_name(func_index);
|
||||
context.func.signature = func_signature(isa, module, types, func_index);
|
||||
context.func.signature = func_signature(isa, translation, types, func_index);
|
||||
if tunables.generate_native_debuginfo {
|
||||
context.func.collect_debug_info();
|
||||
}
|
||||
|
||||
let mut func_env = FuncEnvironment::new(isa, module, types, tunables);
|
||||
let mut func_env = FuncEnvironment::new(isa, translation, types, tunables);
|
||||
|
||||
// We use these as constant offsets below in
|
||||
// `stack_limit_from_arguments`, so assert their values here. This
|
||||
@@ -201,6 +201,7 @@ impl wasmtime_environ::Compiler for Compiler {
|
||||
None
|
||||
};
|
||||
|
||||
let length = u32::try_from(code_buf.len()).unwrap();
|
||||
Ok(Box::new(CompiledFunction {
|
||||
body: code_buf,
|
||||
jt_offsets: context.func.jt_offsets,
|
||||
@@ -212,6 +213,8 @@ impl wasmtime_environ::Compiler for Compiler {
|
||||
info: FunctionInfo {
|
||||
start_srcloc: address_transform.start_srcloc,
|
||||
stack_maps: stack_map_sink.finish(),
|
||||
start: 0,
|
||||
length,
|
||||
},
|
||||
address_map: address_transform,
|
||||
}))
|
||||
@@ -224,40 +227,38 @@ impl wasmtime_environ::Compiler for Compiler {
|
||||
funcs: PrimaryMap<DefinedFuncIndex, Box<dyn Any + Send>>,
|
||||
emit_dwarf: bool,
|
||||
obj: &mut Object,
|
||||
) -> Result<PrimaryMap<DefinedFuncIndex, FunctionInfo>> {
|
||||
) -> Result<(PrimaryMap<DefinedFuncIndex, FunctionInfo>, Vec<Trampoline>)> {
|
||||
const CODE_SECTION_ALIGNMENT: u64 = 0x1000;
|
||||
let funcs: crate::CompiledFunctions = funcs
|
||||
.into_iter()
|
||||
.map(|(_i, f)| *f.downcast().unwrap())
|
||||
.collect();
|
||||
|
||||
// Build trampolines for every signature that can be used by this module.
|
||||
let signatures = translation
|
||||
.module
|
||||
.functions
|
||||
.iter()
|
||||
.filter_map(|(i, sig)| match translation.module.defined_func_index(i) {
|
||||
Some(i) if !translation.module.possibly_exported_funcs.contains(&i) => None,
|
||||
_ => Some(*sig),
|
||||
})
|
||||
.collect::<BTreeSet<_>>();
|
||||
let mut trampolines = Vec::with_capacity(signatures.len());
|
||||
for i in signatures {
|
||||
let func = self.host_to_wasm_trampoline(&types.wasm_signatures[i])?;
|
||||
trampolines.push((i, func));
|
||||
}
|
||||
|
||||
let mut builder = ObjectBuilder::new(obj, &translation.module);
|
||||
let mut addrs = AddressMapSection::default();
|
||||
let mut traps = TrapEncodingBuilder::default();
|
||||
let compiled_trampolines = translation
|
||||
.exported_signatures
|
||||
.iter()
|
||||
.map(|i| self.host_to_wasm_trampoline(&types.wasm_signatures[*i]))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
let mut func_starts = Vec::with_capacity(funcs.len());
|
||||
for (i, func) in funcs.iter() {
|
||||
let range = builder.func(i, func);
|
||||
addrs.push(range.clone(), &func.address_map.instructions);
|
||||
traps.push(range, &func.traps);
|
||||
traps.push(range.clone(), &func.traps);
|
||||
func_starts.push(range.start);
|
||||
}
|
||||
for (i, func) in trampolines.iter() {
|
||||
builder.trampoline(*i, func);
|
||||
|
||||
// Build trampolines for every signature that can be used by this module.
|
||||
let mut trampolines = Vec::with_capacity(translation.exported_signatures.len());
|
||||
for (i, func) in translation
|
||||
.exported_signatures
|
||||
.iter()
|
||||
.zip(&compiled_trampolines)
|
||||
{
|
||||
trampolines.push(builder.trampoline(*i, &func));
|
||||
}
|
||||
builder.align_text_to(CODE_SECTION_ALIGNMENT);
|
||||
|
||||
@@ -295,7 +296,17 @@ impl wasmtime_environ::Compiler for Compiler {
|
||||
addrs.append_to(obj);
|
||||
traps.append_to(obj);
|
||||
|
||||
Ok(funcs.into_iter().map(|(_, f)| f.info).collect())
|
||||
Ok((
|
||||
funcs
|
||||
.into_iter()
|
||||
.zip(func_starts)
|
||||
.map(|((_, mut f), start)| {
|
||||
f.info.start = start;
|
||||
f.info
|
||||
})
|
||||
.collect(),
|
||||
trampolines,
|
||||
))
|
||||
}
|
||||
|
||||
fn emit_trampoline_obj(
|
||||
@@ -303,15 +314,15 @@ impl wasmtime_environ::Compiler for Compiler {
|
||||
ty: &WasmFuncType,
|
||||
host_fn: usize,
|
||||
obj: &mut Object,
|
||||
) -> Result<()> {
|
||||
) -> Result<(Trampoline, Trampoline)> {
|
||||
let host_to_wasm = self.host_to_wasm_trampoline(ty)?;
|
||||
let wasm_to_host = self.wasm_to_host_trampoline(ty, host_fn)?;
|
||||
let module = Module::new();
|
||||
let mut builder = ObjectBuilder::new(obj, &module);
|
||||
builder.trampoline(SignatureIndex::new(0), &host_to_wasm);
|
||||
builder.trampoline(SignatureIndex::new(1), &wasm_to_host);
|
||||
let a = builder.trampoline(SignatureIndex::new(0), &host_to_wasm);
|
||||
let b = builder.trampoline(SignatureIndex::new(1), &wasm_to_host);
|
||||
builder.finish(&*self.isa)?;
|
||||
Ok(())
|
||||
Ok((a, b))
|
||||
}
|
||||
|
||||
fn triple(&self) -> &target_lexicon::Triple {
|
||||
|
||||
@@ -16,8 +16,8 @@ use std::convert::TryFrom;
|
||||
use std::mem;
|
||||
use wasmparser::Operator;
|
||||
use wasmtime_environ::{
|
||||
BuiltinFunctionIndex, MemoryPlan, MemoryStyle, Module, TableStyle, Tunables, TypeTables,
|
||||
VMOffsets, INTERRUPTED, WASM_PAGE_SIZE,
|
||||
BuiltinFunctionIndex, MemoryPlan, MemoryStyle, Module, ModuleTranslation, TableStyle, Tunables,
|
||||
TypeTables, VMOffsets, INTERRUPTED, WASM_PAGE_SIZE,
|
||||
};
|
||||
|
||||
/// Compute an `ir::ExternalName` for a given wasm function index.
|
||||
@@ -111,6 +111,7 @@ wasmtime_environ::foreach_builtin_function!(declare_function_signatures);
|
||||
pub struct FuncEnvironment<'module_environment> {
|
||||
isa: &'module_environment (dyn TargetIsa + 'module_environment),
|
||||
module: &'module_environment Module,
|
||||
translation: &'module_environment ModuleTranslation<'module_environment>,
|
||||
types: &'module_environment TypeTables,
|
||||
|
||||
/// The Cranelift global holding the vmctx address.
|
||||
@@ -142,7 +143,7 @@ pub struct FuncEnvironment<'module_environment> {
|
||||
impl<'module_environment> FuncEnvironment<'module_environment> {
|
||||
pub fn new(
|
||||
isa: &'module_environment (dyn TargetIsa + 'module_environment),
|
||||
module: &'module_environment Module,
|
||||
translation: &'module_environment ModuleTranslation<'module_environment>,
|
||||
types: &'module_environment TypeTables,
|
||||
tunables: &'module_environment Tunables,
|
||||
) -> Self {
|
||||
@@ -157,11 +158,12 @@ impl<'module_environment> FuncEnvironment<'module_environment> {
|
||||
);
|
||||
Self {
|
||||
isa,
|
||||
module,
|
||||
module: &translation.module,
|
||||
translation,
|
||||
types,
|
||||
vmctx: None,
|
||||
builtin_function_signatures,
|
||||
offsets: VMOffsets::new(isa.pointer_bytes(), module),
|
||||
offsets: VMOffsets::new(isa.pointer_bytes(), &translation.module),
|
||||
tunables,
|
||||
fuel_var: Variable::new(0),
|
||||
vminterrupts_ptr: Variable::new(0),
|
||||
@@ -1289,7 +1291,7 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
|
||||
func: &mut ir::Function,
|
||||
index: FuncIndex,
|
||||
) -> WasmResult<ir::FuncRef> {
|
||||
let sig = crate::func_signature(self.isa, self.module, self.types, index);
|
||||
let sig = crate::func_signature(self.isa, self.translation, self.types, index);
|
||||
let signature = func.import_signature(sig);
|
||||
let name = get_func_name(index);
|
||||
Ok(func.import_function(ir::ExtFuncData {
|
||||
|
||||
@@ -95,7 +95,7 @@ use cranelift_entity::PrimaryMap;
|
||||
use cranelift_wasm::{DefinedFuncIndex, FuncIndex, WasmFuncType, WasmType};
|
||||
use target_lexicon::CallingConvention;
|
||||
use wasmtime_environ::{
|
||||
FilePos, FunctionInfo, InstructionAddressMap, Module, TrapInformation, TypeTables,
|
||||
FilePos, FunctionInfo, InstructionAddressMap, ModuleTranslation, TrapInformation, TypeTables,
|
||||
};
|
||||
|
||||
pub use builder::builder;
|
||||
@@ -257,16 +257,16 @@ fn indirect_signature(isa: &dyn TargetIsa, wasm: &WasmFuncType) -> ir::Signature
|
||||
/// use a custom theoretically faster calling convention instead of the default.
|
||||
fn func_signature(
|
||||
isa: &dyn TargetIsa,
|
||||
module: &Module,
|
||||
translation: &ModuleTranslation,
|
||||
types: &TypeTables,
|
||||
index: FuncIndex,
|
||||
) -> ir::Signature {
|
||||
let call_conv = match module.defined_func_index(index) {
|
||||
let call_conv = match translation.module.defined_func_index(index) {
|
||||
// If this is a defined function in the module and it's never possibly
|
||||
// exported, then we can optimize this function to use the fastest
|
||||
// calling convention since it's purely an internal implementation
|
||||
// detail of the module itself.
|
||||
Some(idx) if !module.possibly_exported_funcs.contains(&idx) => CallConv::Fast,
|
||||
Some(idx) if !translation.escaped_funcs.contains(&idx) => CallConv::Fast,
|
||||
|
||||
// ... otherwise if it's an imported function or if it's a possibly
|
||||
// exported function then we use the default ABI wasmtime would
|
||||
@@ -277,7 +277,7 @@ fn func_signature(
|
||||
push_types(
|
||||
isa,
|
||||
&mut sig,
|
||||
&types.wasm_signatures[module.functions[index]],
|
||||
&types.wasm_signatures[translation.module.functions[index]],
|
||||
);
|
||||
return sig;
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ use std::convert::TryFrom;
|
||||
use std::ops::Range;
|
||||
use wasmtime_environ::obj;
|
||||
use wasmtime_environ::{
|
||||
DefinedFuncIndex, EntityRef, FuncIndex, Module, PrimaryMap, SignatureIndex,
|
||||
DefinedFuncIndex, EntityRef, FuncIndex, Module, PrimaryMap, SignatureIndex, Trampoline,
|
||||
};
|
||||
|
||||
const TEXT_SECTION_NAME: &[u8] = b".text";
|
||||
@@ -230,9 +230,14 @@ impl<'a> ObjectBuilder<'a> {
|
||||
range
|
||||
}
|
||||
|
||||
pub fn trampoline(&mut self, sig: SignatureIndex, func: &'a CompiledFunction) {
|
||||
pub fn trampoline(&mut self, sig: SignatureIndex, func: &'a CompiledFunction) -> Trampoline {
|
||||
let name = obj::trampoline_symbol_name(sig);
|
||||
self.append_func(name.into_bytes(), func);
|
||||
let (_, range) = self.append_func(name.into_bytes(), func);
|
||||
Trampoline {
|
||||
signature: sig,
|
||||
start: range.start,
|
||||
length: u32::try_from(range.end - range.start).unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn align_text_to(&mut self, align: u64) {
|
||||
|
||||
Reference in New Issue
Block a user