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:
@@ -2,8 +2,8 @@
|
||||
//! module.
|
||||
|
||||
use crate::{
|
||||
DefinedFuncIndex, FilePos, FunctionBodyData, ModuleTranslation, PrimaryMap, StackMap, Tunables,
|
||||
TypeTables, WasmError, WasmFuncType,
|
||||
DefinedFuncIndex, FilePos, FunctionBodyData, ModuleTranslation, PrimaryMap, SignatureIndex,
|
||||
StackMap, Tunables, TypeTables, WasmError, WasmFuncType,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use object::write::Object;
|
||||
@@ -22,6 +22,25 @@ use thiserror::Error;
|
||||
pub struct FunctionInfo {
|
||||
pub start_srcloc: FilePos,
|
||||
pub stack_maps: Vec<StackMapInformation>,
|
||||
|
||||
/// Offset in the text section of where this function starts.
|
||||
pub start: u64,
|
||||
/// The size of the compiled function, in bytes.
|
||||
pub length: u32,
|
||||
}
|
||||
|
||||
/// Information about a compiled trampoline which the host can call to enter
|
||||
/// wasm.
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct Trampoline {
|
||||
/// The signature this trampoline is for
|
||||
pub signature: SignatureIndex,
|
||||
|
||||
/// Offset in the text section of where this function starts.
|
||||
pub start: u64,
|
||||
/// The size of the compiled function, in bytes.
|
||||
pub length: u32,
|
||||
}
|
||||
|
||||
/// The offset within a function of a GC safepoint, and its associated stack
|
||||
@@ -154,19 +173,21 @@ pub trait Compiler: Send + Sync {
|
||||
funcs: PrimaryMap<DefinedFuncIndex, Box<dyn Any + Send>>,
|
||||
emit_dwarf: bool,
|
||||
obj: &mut Object,
|
||||
) -> Result<PrimaryMap<DefinedFuncIndex, FunctionInfo>>;
|
||||
) -> Result<(PrimaryMap<DefinedFuncIndex, FunctionInfo>, Vec<Trampoline>)>;
|
||||
|
||||
/// Inserts two functions for host-to-wasm and wasm-to-host trampolines into
|
||||
/// the `obj` provided.
|
||||
///
|
||||
/// This will configure the same sections as `emit_obj`, but will likely be
|
||||
/// much smaller.
|
||||
/// much smaller. The two returned `Trampoline` structures describe where to
|
||||
/// find the host-to-wasm and wasm-to-host trampolines in the text section,
|
||||
/// respectively.
|
||||
fn emit_trampoline_obj(
|
||||
&self,
|
||||
ty: &WasmFuncType,
|
||||
host_fn: usize,
|
||||
obj: &mut Object,
|
||||
) -> Result<()>;
|
||||
) -> Result<(Trampoline, Trampoline)>;
|
||||
|
||||
/// Creates a new `Object` file which is used to build the results of a
|
||||
/// compilation into.
|
||||
|
||||
@@ -43,6 +43,7 @@ pub use crate::stack_map::StackMap;
|
||||
pub use crate::trap_encoding::*;
|
||||
pub use crate::tunables::Tunables;
|
||||
pub use crate::vmoffsets::*;
|
||||
pub use object;
|
||||
|
||||
// Reexport all of these type-level since they're quite commonly used and it's
|
||||
// much easier to refer to everything through one crate rather than importing
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
use crate::{EntityRef, ModuleTranslation, PrimaryMap, Tunables, WASM_PAGE_SIZE};
|
||||
use indexmap::IndexMap;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::collections::BTreeMap;
|
||||
use std::convert::TryFrom;
|
||||
use std::ops::Range;
|
||||
use wasmtime_types::*;
|
||||
@@ -422,10 +422,6 @@ pub struct Module {
|
||||
|
||||
/// The type of each nested wasm module this module contains.
|
||||
pub modules: PrimaryMap<ModuleIndex, ModuleTypeIndex>,
|
||||
|
||||
/// The set of defined functions within this module which are located in
|
||||
/// element segments.
|
||||
pub possibly_exported_funcs: BTreeSet<DefinedFuncIndex>,
|
||||
}
|
||||
|
||||
/// Initialization routines for creating an instance, encompassing imports,
|
||||
|
||||
@@ -10,7 +10,7 @@ use crate::{
|
||||
};
|
||||
use cranelift_entity::packed_option::ReservedValue;
|
||||
use std::borrow::Cow;
|
||||
use std::collections::{hash_map::Entry, HashMap};
|
||||
use std::collections::{hash_map::Entry, HashMap, HashSet};
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
use std::mem;
|
||||
use std::path::PathBuf;
|
||||
@@ -61,6 +61,23 @@ pub struct ModuleTranslation<'data> {
|
||||
/// References to the function bodies.
|
||||
pub function_body_inputs: PrimaryMap<DefinedFuncIndex, FunctionBodyData<'data>>,
|
||||
|
||||
/// The set of defined functions within this module which are "possibly
|
||||
/// exported" which means that the host can possibly call them. This
|
||||
/// includes functions such as:
|
||||
///
|
||||
/// * Exported functions
|
||||
/// * Functions in element segments
|
||||
/// * Functions via `ref.func` instructions
|
||||
///
|
||||
/// This set is used to determine the set of type signatures that need
|
||||
/// trampolines for the host to call into.
|
||||
pub escaped_funcs: HashSet<DefinedFuncIndex>,
|
||||
|
||||
/// A list of type signatures which are considered exported from this
|
||||
/// module, or those that can possibly be called. This list is sorted, and
|
||||
/// trampolines for each of these signatures are required.
|
||||
pub exported_signatures: Vec<SignatureIndex>,
|
||||
|
||||
/// DWARF debug information, if enabled, parsed from the module.
|
||||
pub debuginfo: DebugInfoData<'data>,
|
||||
|
||||
@@ -221,6 +238,22 @@ impl<'data> ModuleEnvironment<'data> {
|
||||
Payload::End => {
|
||||
validator.end()?;
|
||||
|
||||
// With the `escaped_funcs` set of functions finished
|
||||
// we can calculate the set of signatures that are exported as
|
||||
// the set of exported functions' signatures.
|
||||
self.result.exported_signatures = self
|
||||
.result
|
||||
.module
|
||||
.functions
|
||||
.iter()
|
||||
.filter_map(|(i, sig)| match self.result.module.defined_func_index(i) {
|
||||
Some(i) if !self.result.escaped_funcs.contains(&i) => None,
|
||||
_ => Some(*sig),
|
||||
})
|
||||
.collect();
|
||||
self.result.exported_signatures.sort_unstable();
|
||||
self.result.exported_signatures.dedup();
|
||||
|
||||
self.result.creation_artifacts.shrink_to_fit();
|
||||
self.result.creation_modules.shrink_to_fit();
|
||||
|
||||
@@ -417,7 +450,7 @@ impl<'data> ModuleEnvironment<'data> {
|
||||
Operator::RefNull { ty: _ } => GlobalInit::RefNullConst,
|
||||
Operator::RefFunc { function_index } => {
|
||||
let index = FuncIndex::from_u32(function_index);
|
||||
self.flag_func_possibly_exported(index);
|
||||
self.flag_func_escaped(index);
|
||||
GlobalInit::RefFunc(index)
|
||||
}
|
||||
Operator::GlobalGet { global_index } => {
|
||||
@@ -446,7 +479,7 @@ impl<'data> ModuleEnvironment<'data> {
|
||||
let entity = match kind {
|
||||
ExternalKind::Function => {
|
||||
let index = FuncIndex::from_u32(index);
|
||||
self.flag_func_possibly_exported(index);
|
||||
self.flag_func_escaped(index);
|
||||
EntityIndex::Function(index)
|
||||
}
|
||||
ExternalKind::Table => EntityIndex::Table(TableIndex::from_u32(index)),
|
||||
@@ -471,7 +504,7 @@ impl<'data> ModuleEnvironment<'data> {
|
||||
validator.start_section(func, &range)?;
|
||||
|
||||
let func_index = FuncIndex::from_u32(func);
|
||||
self.flag_func_possibly_exported(func_index);
|
||||
self.flag_func_escaped(func_index);
|
||||
debug_assert!(self.result.module.start_func.is_none());
|
||||
self.result.module.start_func = Some(func_index);
|
||||
}
|
||||
@@ -497,7 +530,7 @@ impl<'data> ModuleEnvironment<'data> {
|
||||
elements.push(match item? {
|
||||
ElementItem::Func(f) => {
|
||||
let f = FuncIndex::from_u32(f);
|
||||
self.flag_func_possibly_exported(f);
|
||||
self.flag_func_escaped(f);
|
||||
f
|
||||
}
|
||||
ElementItem::Null(_ty) => FuncIndex::reserved_value(),
|
||||
@@ -1113,9 +1146,9 @@ and for re-adding support for interface types you can see this issue:
|
||||
.push(ModuleSignature { imports, exports })
|
||||
}
|
||||
|
||||
fn flag_func_possibly_exported(&mut self, func: FuncIndex) {
|
||||
fn flag_func_escaped(&mut self, func: FuncIndex) {
|
||||
if let Some(idx) = self.result.module.defined_func_index(func) {
|
||||
self.result.module.possibly_exported_funcs.insert(idx);
|
||||
self.result.escaped_funcs.insert(idx);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user