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

@@ -2,7 +2,6 @@
//! signalhandling mechanisms.
use crate::instance::{InstanceHandle, SignalHandler};
use crate::trap_registry::get_trap_registry;
use crate::trap_registry::TrapDescription;
use crate::vmcontext::{VMContext, VMFunctionBody};
use backtrace::Backtrace;
@@ -78,8 +77,7 @@ cfg_if::cfg_if! {
/// Only safe to call when wasm code is on the stack, aka `wasmtime_call` or
/// `wasmtime_call_trampoline` must have been previously called.
pub unsafe fn raise_user_trap(data: Box<dyn Error + Send + Sync>) -> ! {
let trap = Trap::User(data);
tls::with(|info| info.unwrap().unwind_with(UnwindReason::Trap(trap)))
tls::with(|info| info.unwrap().unwind_with(UnwindReason::UserTrap(data)))
}
/// Carries a Rust panic across wasm code and resumes the panic on the other
@@ -187,7 +185,8 @@ pub struct CallThreadState {
enum UnwindReason {
None,
Panic(Box<dyn Any + Send>),
Trap(Trap),
UserTrap(Box<dyn Error + Send + Sync>),
Trap { backtrace: Backtrace, pc: usize },
}
impl CallThreadState {
@@ -210,9 +209,25 @@ impl CallThreadState {
debug_assert_eq!(ret, 1);
Ok(())
}
UnwindReason::Trap(trap) => {
UnwindReason::UserTrap(data) => {
debug_assert_eq!(ret, 0);
Err(trap)
Err(Trap::User(data))
}
UnwindReason::Trap { backtrace, pc } => {
debug_assert_eq!(ret, 0);
let instance = unsafe { InstanceHandle::from_vmctx(self.vmctx) };
Err(Trap::Wasm {
desc: instance
.instance()
.trap_registration
.get_trap(pc)
.unwrap_or_else(|| TrapDescription {
source_loc: ir::SourceLoc::default(),
trap_code: ir::TrapCode::StackOverflow,
}),
backtrace,
})
}
UnwindReason::Panic(panic) => {
debug_assert_eq!(ret, 0);
@@ -289,20 +304,12 @@ impl CallThreadState {
if self.jmp_buf.get().is_null() {
return ptr::null();
}
let registry = get_trap_registry();
let trap = Trap::Wasm {
desc: registry
.get_trap(pc as usize)
.unwrap_or_else(|| TrapDescription {
source_loc: ir::SourceLoc::default(),
trap_code: ir::TrapCode::StackOverflow,
}),
backtrace: Backtrace::new_unresolved(),
};
let backtrace = Backtrace::new_unresolved();
self.reset_guard_page.set(reset_guard_page);
self.unwind.replace(UnwindReason::Trap(trap));
self.unwind.replace(UnwindReason::Trap {
backtrace,
pc: pc as usize,
});
self.jmp_buf.get()
}
}