Remove global state for trap registration (#909)
* Remove global state for trap registration There's a number of changes brought about in this commit, motivated by a few things. One motivation was to remove an instance of using `lazy_static!` in an effort to remove global state and encapsulate it wherever possible. A second motivation came when investigating a slowly-compiling wasm module (a bit too slowly) where a good chunk of time was spent in managing trap registrations. The specific change made here is that `TrapRegistry` is now stored inside of a `Compiler` instead of inside a global. Additionally traps are "bulk registered" for a module rather than one-by-one. This form of bulk-registration allows optimizing the locks used here, where a lock is only held for a module at-a-time instead of once-per-function. With these changes the "unregister" logic has also been tweaked a bit here and there to continue to work. As a nice side effect the `Compiler` type now has one fewer field that requires actual mutability and has been updated for multi-threaded compilation, nudging us closer to a world where we can support multi-threaded compilation. Yay! In terms of performance improvements, a local wasm test file that previously took 3 seconds to compile is now 10% faster to compile, taking ~2.7 seconds now. * Perform trap resolution after unwinding This avoids taking locks in signal handlers which feels a bit iffy... * Remove `TrapRegistration::dummy()` Avoid an case where you're trying to lookup trap information from a dummy module for something that happened in a different module. * Tweak some comments
This commit is contained in:
@@ -21,8 +21,8 @@ use wasmtime_environ::{
|
||||
VMOffsets,
|
||||
};
|
||||
use wasmtime_runtime::{
|
||||
get_mut_trap_registry, jit_function_registry, InstantiationError, SignatureRegistry,
|
||||
TrapRegistrationGuard, VMFunctionBody,
|
||||
jit_function_registry, InstantiationError, SignatureRegistry, TrapRegistration, TrapRegistry,
|
||||
VMFunctionBody,
|
||||
};
|
||||
|
||||
/// Select which kind of compilation to use.
|
||||
@@ -51,8 +51,8 @@ pub struct Compiler {
|
||||
isa: Box<dyn TargetIsa>,
|
||||
|
||||
code_memory: CodeMemory,
|
||||
trap_registration_guards: Vec<TrapRegistrationGuard>,
|
||||
jit_function_ranges: Vec<(usize, usize)>,
|
||||
trap_registry: TrapRegistry,
|
||||
trampoline_park: HashMap<*const VMFunctionBody, *const VMFunctionBody>,
|
||||
signatures: SignatureRegistry,
|
||||
strategy: CompilationStrategy,
|
||||
@@ -67,28 +67,18 @@ impl Compiler {
|
||||
Self {
|
||||
isa,
|
||||
code_memory: CodeMemory::new(),
|
||||
trap_registration_guards: Vec::new(),
|
||||
jit_function_ranges: Vec::new(),
|
||||
trampoline_park: HashMap::new(),
|
||||
signatures: SignatureRegistry::new(),
|
||||
fn_builder_ctx: FunctionBuilderContext::new(),
|
||||
strategy,
|
||||
trap_registry: TrapRegistry::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Compiler {
|
||||
fn drop(&mut self) {
|
||||
// We must deregister traps before freeing the code memory.
|
||||
// Otherwise, we have a race:
|
||||
// - Compiler #1 dropped code memory, but hasn't deregistered the trap yet
|
||||
// - Compiler #2 allocated code memory and tries to register a trap,
|
||||
// but the trap at certain address happens to be already registered,
|
||||
// since Compiler #1 hasn't deregistered it yet => assertion in trap registry fails.
|
||||
// Having a custom drop implementation we are independent from the field order
|
||||
// in the struct what reduces potential human error.
|
||||
self.trap_registration_guards.clear();
|
||||
|
||||
for (start, end) in self.jit_function_ranges.iter() {
|
||||
jit_function_registry::unregister(*start, *end);
|
||||
}
|
||||
@@ -119,6 +109,7 @@ impl Compiler {
|
||||
PrimaryMap<DefinedFuncIndex, ir::JumpTableOffsets>,
|
||||
Relocations,
|
||||
Option<Vec<u8>>,
|
||||
TrapRegistration,
|
||||
),
|
||||
SetupError,
|
||||
> {
|
||||
@@ -156,11 +147,7 @@ impl Compiler {
|
||||
)))
|
||||
})?;
|
||||
|
||||
register_traps(
|
||||
&allocated_functions,
|
||||
&traps,
|
||||
&mut self.trap_registration_guards,
|
||||
);
|
||||
let trap_registration = register_traps(&allocated_functions, &traps, &self.trap_registry);
|
||||
|
||||
for (i, allocated) in allocated_functions.iter() {
|
||||
let ptr = (*allocated) as *const VMFunctionBody;
|
||||
@@ -217,7 +204,13 @@ impl Compiler {
|
||||
|
||||
let jt_offsets = compilation.get_jt_offsets();
|
||||
|
||||
Ok((allocated_functions, jt_offsets, relocations, dbg))
|
||||
Ok((
|
||||
allocated_functions,
|
||||
jt_offsets,
|
||||
relocations,
|
||||
dbg,
|
||||
trap_registration,
|
||||
))
|
||||
}
|
||||
|
||||
/// Create a trampoline for invoking a function.
|
||||
@@ -267,6 +260,11 @@ impl Compiler {
|
||||
pub fn signatures(&self) -> &SignatureRegistry {
|
||||
&self.signatures
|
||||
}
|
||||
|
||||
/// Shared registration of trap information
|
||||
pub fn trap_registry(&self) -> &TrapRegistry {
|
||||
&self.trap_registry
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a trampoline for invoking a function.
|
||||
@@ -408,19 +406,21 @@ fn allocate_functions(
|
||||
fn register_traps(
|
||||
allocated_functions: &PrimaryMap<DefinedFuncIndex, *mut [VMFunctionBody]>,
|
||||
traps: &Traps,
|
||||
trap_registration_guards: &mut Vec<TrapRegistrationGuard>,
|
||||
) {
|
||||
let mut trap_registry = get_mut_trap_registry();
|
||||
for (func_addr, func_traps) in allocated_functions.values().zip(traps.values()) {
|
||||
for trap_desc in func_traps.iter() {
|
||||
let func_addr = *func_addr as *const u8 as usize;
|
||||
let offset = usize::try_from(trap_desc.code_offset).unwrap();
|
||||
let trap_addr = func_addr + offset;
|
||||
let guard =
|
||||
trap_registry.register_trap(trap_addr, trap_desc.source_loc, trap_desc.trap_code);
|
||||
trap_registration_guards.push(guard);
|
||||
}
|
||||
}
|
||||
registry: &TrapRegistry,
|
||||
) -> TrapRegistration {
|
||||
let traps =
|
||||
allocated_functions
|
||||
.values()
|
||||
.zip(traps.values())
|
||||
.flat_map(|(func_addr, func_traps)| {
|
||||
func_traps.iter().map(move |trap_desc| {
|
||||
let func_addr = *func_addr as *const u8 as usize;
|
||||
let offset = usize::try_from(trap_desc.code_offset).unwrap();
|
||||
let trap_addr = func_addr + offset;
|
||||
(trap_addr, trap_desc.source_loc, trap_desc.trap_code)
|
||||
})
|
||||
});
|
||||
registry.register_traps(traps)
|
||||
}
|
||||
|
||||
/// We don't expect trampoline compilation to produce any relocations, so
|
||||
|
||||
Reference in New Issue
Block a user