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
|
||||
|
||||
@@ -18,7 +18,7 @@ use wasmtime_environ::{
|
||||
CompileError, DataInitializer, DataInitializerLocation, Module, ModuleEnvironment,
|
||||
};
|
||||
use wasmtime_runtime::{
|
||||
GdbJitImageRegistration, InstanceHandle, InstantiationError, VMFunctionBody,
|
||||
GdbJitImageRegistration, InstanceHandle, InstantiationError, TrapRegistration, VMFunctionBody,
|
||||
VMSharedSignatureIndex,
|
||||
};
|
||||
|
||||
@@ -52,6 +52,7 @@ struct RawCompiledModule<'data> {
|
||||
data_initializers: Box<[DataInitializer<'data>]>,
|
||||
signatures: BoxedSlice<SignatureIndex, VMSharedSignatureIndex>,
|
||||
dbg_jit_registration: Option<GdbJitImageRegistration>,
|
||||
trap_registration: TrapRegistration,
|
||||
}
|
||||
|
||||
impl<'data> RawCompiledModule<'data> {
|
||||
@@ -73,12 +74,13 @@ impl<'data> RawCompiledModule<'data> {
|
||||
None
|
||||
};
|
||||
|
||||
let (allocated_functions, jt_offsets, relocations, dbg_image) = compiler.compile(
|
||||
&translation.module,
|
||||
translation.module_translation.as_ref().unwrap(),
|
||||
translation.function_body_inputs,
|
||||
debug_data,
|
||||
)?;
|
||||
let (allocated_functions, jt_offsets, relocations, dbg_image, trap_registration) = compiler
|
||||
.compile(
|
||||
&translation.module,
|
||||
translation.module_translation.as_ref().unwrap(),
|
||||
translation.function_body_inputs,
|
||||
debug_data,
|
||||
)?;
|
||||
|
||||
link_module(
|
||||
&translation.module,
|
||||
@@ -127,6 +129,7 @@ impl<'data> RawCompiledModule<'data> {
|
||||
data_initializers: translation.data_initializers.into_boxed_slice(),
|
||||
signatures: signatures.into_boxed_slice(),
|
||||
dbg_jit_registration,
|
||||
trap_registration,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -138,6 +141,7 @@ pub struct CompiledModule {
|
||||
data_initializers: Box<[OwnedDataInitializer]>,
|
||||
signatures: BoxedSlice<SignatureIndex, VMSharedSignatureIndex>,
|
||||
dbg_jit_registration: Option<Rc<GdbJitImageRegistration>>,
|
||||
trap_registration: TrapRegistration,
|
||||
}
|
||||
|
||||
impl CompiledModule {
|
||||
@@ -159,6 +163,7 @@ impl CompiledModule {
|
||||
.into_boxed_slice(),
|
||||
raw.signatures.clone(),
|
||||
raw.dbg_jit_registration,
|
||||
raw.trap_registration,
|
||||
))
|
||||
}
|
||||
|
||||
@@ -169,6 +174,7 @@ impl CompiledModule {
|
||||
data_initializers: Box<[OwnedDataInitializer]>,
|
||||
signatures: BoxedSlice<SignatureIndex, VMSharedSignatureIndex>,
|
||||
dbg_jit_registration: Option<GdbJitImageRegistration>,
|
||||
trap_registration: TrapRegistration,
|
||||
) -> Self {
|
||||
Self {
|
||||
module: Arc::new(module),
|
||||
@@ -176,6 +182,7 @@ impl CompiledModule {
|
||||
data_initializers,
|
||||
signatures,
|
||||
dbg_jit_registration: dbg_jit_registration.map(Rc::new),
|
||||
trap_registration,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,6 +210,7 @@ impl CompiledModule {
|
||||
let imports = resolve_imports(&self.module, resolver)?;
|
||||
InstanceHandle::new(
|
||||
Arc::clone(&self.module),
|
||||
self.trap_registration.clone(),
|
||||
self.finished_functions.clone(),
|
||||
imports,
|
||||
&data_initializers,
|
||||
|
||||
Reference in New Issue
Block a user