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

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

View File

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

View File

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

View File

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