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:
Alex Crichton
2020-02-06 12:40:50 -06:00
committed by GitHub
parent 9dffaf9d57
commit 348c597a8e
14 changed files with 233 additions and 131 deletions

View File

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

View File

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