Previously each module in a module-linking-using-module would compile all the trampolines for all signatures for all modules. In forest-like situations with lots of modules this would cause quite a few trampolines to get compiled. The original intention was to have one global list of trampolines for all modules in the module-linking graph that they could all share. With the current design of module linking, however, the intention is for modules to be relatively isolated from one another which would make achieving this difficult. In lieu of total sharing (which would be good for the global scope anyway but we also don't do that right now) this commit implements an alternative strategy where each module simply compiles its own trampolines that it itself can reach. This should mean that module-linking modules behave more similarly to standalone modules in terms of trampoline duplication. If we ever do global trampoline deduplication we can likely batch this all together into one, but for now this should fix the performance issues seen in fuzzing. Closes #2525
74 lines
2.6 KiB
Rust
74 lines
2.6 KiB
Rust
//! Object file generation.
|
|
|
|
use super::trampoline::build_trampoline;
|
|
use cranelift_frontend::FunctionBuilderContext;
|
|
use object::write::Object;
|
|
use serde::{Deserialize, Serialize};
|
|
use std::collections::BTreeSet;
|
|
use wasmtime_debug::DwarfSection;
|
|
use wasmtime_environ::isa::{unwind::UnwindInfo, TargetIsa};
|
|
use wasmtime_environ::wasm::{FuncIndex, SignatureIndex};
|
|
use wasmtime_environ::{CompiledFunctions, ModuleTranslation, ModuleType, TypeTables};
|
|
use wasmtime_obj::{ObjectBuilder, ObjectBuilderTarget};
|
|
|
|
pub use wasmtime_obj::utils;
|
|
|
|
/// Unwind information for object files functions (including trampolines).
|
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
pub enum ObjectUnwindInfo {
|
|
Func(FuncIndex, UnwindInfo),
|
|
Trampoline(SignatureIndex, UnwindInfo),
|
|
}
|
|
|
|
// Builds ELF image from the module `Compilation`.
|
|
pub(crate) fn build_object(
|
|
isa: &dyn TargetIsa,
|
|
translation: &ModuleTranslation,
|
|
types: &TypeTables,
|
|
funcs: &CompiledFunctions,
|
|
dwarf_sections: Vec<DwarfSection>,
|
|
) -> Result<(Object, Vec<ObjectUnwindInfo>), anyhow::Error> {
|
|
const CODE_SECTION_ALIGNMENT: u64 = 0x1000;
|
|
|
|
let mut unwind_info = Vec::new();
|
|
|
|
// Preserve function unwind info.
|
|
unwind_info.extend(funcs.iter().filter_map(|(index, func)| {
|
|
func.unwind_info
|
|
.as_ref()
|
|
.map(|info| ObjectUnwindInfo::Func(translation.module.func_index(index), info.clone()))
|
|
}));
|
|
|
|
// Build trampolines for every signature that can be used by this module.
|
|
let signatures = translation
|
|
.module
|
|
.types
|
|
.values()
|
|
.filter_map(|t| match t {
|
|
ModuleType::Function(f) => Some(*f),
|
|
_ => None,
|
|
})
|
|
.collect::<BTreeSet<_>>();
|
|
let mut trampolines = Vec::with_capacity(signatures.len());
|
|
let mut cx = FunctionBuilderContext::new();
|
|
for i in signatures {
|
|
let native_sig = &types.native_signatures[i];
|
|
let func = build_trampoline(isa, &mut cx, native_sig, std::mem::size_of::<u128>())?;
|
|
// Preserve trampoline function unwind info.
|
|
if let Some(info) = &func.unwind_info {
|
|
unwind_info.push(ObjectUnwindInfo::Trampoline(i, info.clone()))
|
|
}
|
|
trampolines.push((i, func));
|
|
}
|
|
|
|
let target = ObjectBuilderTarget::new(isa.triple().architecture)?;
|
|
let mut builder = ObjectBuilder::new(target, &translation.module, funcs);
|
|
builder
|
|
.set_code_alignment(CODE_SECTION_ALIGNMENT)
|
|
.set_trampolines(trampolines)
|
|
.set_dwarf_sections(dwarf_sections);
|
|
let obj = builder.build()?;
|
|
|
|
Ok((obj, unwind_info))
|
|
}
|