Optimize codegen slightly calling wasm functions

Currently wasm-calls work with `Result<T, Trap>` internally but `Trap`
is an enum defined in `wasmtime-runtime` which is actually quite large.
Since traps are supposed to be rare this commit changes these functions
to return a `Box<Trap>` which is un-boxed later up in the `wasmtime`
crate within a `#[cold]` function.
This commit is contained in:
Alex Crichton
2021-08-31 08:34:31 -07:00
parent 25755ff23a
commit c8f55ed688
7 changed files with 38 additions and 27 deletions

View File

@@ -173,7 +173,7 @@ pub unsafe fn catch_traps<'a, F>(
signal_handler: Option<*const SignalHandler<'static>>, signal_handler: Option<*const SignalHandler<'static>>,
callee: *mut VMContext, callee: *mut VMContext,
mut closure: F, mut closure: F,
) -> Result<(), Trap> ) -> Result<(), Box<Trap>>
where where
F: FnMut(*mut VMContext), F: FnMut(*mut VMContext),
{ {
@@ -227,26 +227,31 @@ impl CallThreadState {
self, self,
interrupts: *mut VMInterrupts, interrupts: *mut VMInterrupts,
closure: impl FnOnce(&CallThreadState) -> i32, closure: impl FnOnce(&CallThreadState) -> i32,
) -> Result<(), Trap> { ) -> Result<(), Box<Trap>> {
let ret = tls::set(&self, || closure(&self))?; let ret = tls::set(&self, || closure(&self))?;
if ret != 0 { if ret != 0 {
return Ok(()); Ok(())
} else {
Err(unsafe { self.read_trap(interrupts) })
} }
match unsafe { (*self.unwind.get()).as_ptr().read() } { }
UnwindReason::UserTrap(data) => Err(Trap::User(data)),
UnwindReason::LibTrap(trap) => Err(trap), #[cold]
unsafe fn read_trap(&self, interrupts: *mut VMInterrupts) -> Box<Trap> {
Box::new(match (*self.unwind.get()).as_ptr().read() {
UnwindReason::UserTrap(data) => Trap::User(data),
UnwindReason::LibTrap(trap) => trap,
UnwindReason::JitTrap { backtrace, pc } => { UnwindReason::JitTrap { backtrace, pc } => {
let maybe_interrupted = unsafe { let maybe_interrupted =
(*interrupts).stack_limit.load(SeqCst) == wasmtime_environ::INTERRUPTED (*interrupts).stack_limit.load(SeqCst) == wasmtime_environ::INTERRUPTED;
}; Trap::Jit {
Err(Trap::Jit {
pc, pc,
backtrace, backtrace,
maybe_interrupted, maybe_interrupted,
}) }
} }
UnwindReason::Panic(panic) => std::panic::resume_unwind(panic), UnwindReason::Panic(panic) => std::panic::resume_unwind(panic),
} })
} }
fn unwind_with(&self, reason: UnwindReason) -> ! { fn unwind_with(&self, reason: UnwindReason) -> ! {
@@ -372,17 +377,16 @@ mod tls {
thread_local!(static PTR: Cell<(Ptr, bool)> = Cell::new((ptr::null(), false))); thread_local!(static PTR: Cell<(Ptr, bool)> = Cell::new((ptr::null(), false)));
#[inline(never)] // see module docs for why this is here #[inline(never)] // see module docs for why this is here
pub fn replace(val: Ptr) -> Result<Ptr, Trap> { pub fn replace(val: Ptr) -> Result<Ptr, Box<Trap>> {
PTR.with(|p| { PTR.with(|p| {
// When a new value is configured that means that we may be // When a new value is configured that means that we may be
// entering WebAssembly so check to see if this thread has // entering WebAssembly so check to see if this thread has
// performed per-thread initialization for traps. // performed per-thread initialization for traps.
let (prev, mut initialized) = p.get(); let (prev, initialized) = p.get();
if !initialized { if !initialized {
super::super::sys::lazy_per_thread_init()?; super::super::sys::lazy_per_thread_init()?;
initialized = true;
} }
p.set((val, initialized)); p.set((val, true));
Ok(prev) Ok(prev)
}) })
} }
@@ -390,7 +394,7 @@ mod tls {
#[inline(never)] #[inline(never)]
/// Eagerly initialize thread-local runtime functionality. This will be performed /// Eagerly initialize thread-local runtime functionality. This will be performed
/// lazily by the runtime if users do not perform it eagerly. /// lazily by the runtime if users do not perform it eagerly.
pub fn initialize() -> Result<(), Trap> { pub fn initialize() -> Result<(), Box<Trap>> {
PTR.with(|p| { PTR.with(|p| {
let (state, initialized) = p.get(); let (state, initialized) = p.get();
if initialized { if initialized {
@@ -420,7 +424,7 @@ mod tls {
/// ///
/// This is not a safe operation since it's intended to only be used /// This is not a safe operation since it's intended to only be used
/// with stack switching found with fibers and async wasmtime. /// with stack switching found with fibers and async wasmtime.
pub unsafe fn take() -> Result<TlsRestore, Trap> { pub unsafe fn take() -> Result<TlsRestore, Box<Trap>> {
// Our tls pointer must be set at this time, and it must not be // Our tls pointer must be set at this time, and it must not be
// null. We need to restore the previous pointer since we're // null. We need to restore the previous pointer since we're
// removing ourselves from the call-stack, and in the process we // removing ourselves from the call-stack, and in the process we
@@ -437,7 +441,7 @@ mod tls {
/// ///
/// This is unsafe because it's intended to only be used within the /// This is unsafe because it's intended to only be used within the
/// context of stack switching within wasmtime. /// context of stack switching within wasmtime.
pub unsafe fn replace(self) -> Result<(), super::Trap> { pub unsafe fn replace(self) -> Result<(), Box<super::Trap>> {
// We need to configure our previous TLS pointer to whatever is in // We need to configure our previous TLS pointer to whatever is in
// TLS at this time, and then we set the current state to ourselves. // TLS at this time, and then we set the current state to ourselves.
let prev = raw::get(); let prev = raw::get();
@@ -452,7 +456,7 @@ mod tls {
/// execution of `closure` any call to `with` will yield `ptr`, unless this /// execution of `closure` any call to `with` will yield `ptr`, unless this
/// is recursively called again. /// is recursively called again.
#[inline] #[inline]
pub fn set<R>(state: &CallThreadState, closure: impl FnOnce() -> R) -> Result<R, Trap> { pub fn set<R>(state: &CallThreadState, closure: impl FnOnce() -> R) -> Result<R, Box<Trap>> {
struct Reset<'a>(&'a CallThreadState); struct Reset<'a>(&'a CallThreadState);
impl Drop for Reset<'_> { impl Drop for Reset<'_> {

View File

@@ -409,7 +409,8 @@ unsafe extern "C" fn unwind(wasm_pc: *const u8) -> ! {
/// unhandled thread-level exceptions get automatically forwarded to the /// unhandled thread-level exceptions get automatically forwarded to the
/// task-level port which is where we'd expected things like breakpad/crashpad /// task-level port which is where we'd expected things like breakpad/crashpad
/// exception handlers to get registered. /// exception handlers to get registered.
pub fn lazy_per_thread_init() -> Result<(), Trap> { #[cold]
pub fn lazy_per_thread_init() -> Result<(), Box<Trap>> {
unsafe { unsafe {
assert!(WASMTIME_PORT != MACH_PORT_NULL); assert!(WASMTIME_PORT != MACH_PORT_NULL);
let this_thread = mach_thread_self(); let this_thread = mach_thread_self();

View File

@@ -251,7 +251,8 @@ unsafe fn set_pc(cx: *mut libc::c_void, pc: usize, arg1: usize) {
/// always large enough for our signal handling code. Override it by creating /// always large enough for our signal handling code. Override it by creating
/// and registering our own alternate stack that is large enough and has a guard /// and registering our own alternate stack that is large enough and has a guard
/// page. /// page.
pub fn lazy_per_thread_init() -> Result<(), Trap> { #[cold]
pub fn lazy_per_thread_init() -> Result<(), Box<Trap>> {
// This thread local is purely used to register a `Stack` to get deallocated // This thread local is purely used to register a `Stack` to get deallocated
// when the thread exists. Otherwise this function is only ever called at // when the thread exists. Otherwise this function is only ever called at
// most once per-thread. // most once per-thread.
@@ -273,7 +274,7 @@ pub fn lazy_per_thread_init() -> Result<(), Trap> {
Ok(()) Ok(())
}); });
unsafe fn allocate_sigaltstack() -> Result<Option<Stack>, Trap> { unsafe fn allocate_sigaltstack() -> Result<Option<Stack>, Box<Trap>> {
// Check to see if the existing sigaltstack, if it exists, is big // Check to see if the existing sigaltstack, if it exists, is big
// enough. If so we don't need to allocate our own. // enough. If so we don't need to allocate our own.
let mut old_stack = mem::zeroed(); let mut old_stack = mem::zeroed();
@@ -303,7 +304,7 @@ pub fn lazy_per_thread_init() -> Result<(), Trap> {
0, 0,
); );
if ptr == libc::MAP_FAILED { if ptr == libc::MAP_FAILED {
return Err(Trap::oom()); return Err(Box::new(Trap::oom()));
} }
// Prepare the stack with readable/writable memory and then register it // Prepare the stack with readable/writable memory and then register it

View File

@@ -74,7 +74,7 @@ unsafe extern "system" fn exception_handler(exception_info: PEXCEPTION_POINTERS)
}) })
} }
pub fn lazy_per_thread_init() -> Result<(), Trap> { pub fn lazy_per_thread_init() -> Result<(), Box<Trap>> {
// Unused on Windows // Unused on Windows
Ok(()) Ok(())
} }

View File

@@ -87,7 +87,7 @@ impl Engine {
/// latency of WebAssembly calls are extra-important, which is not /// latency of WebAssembly calls are extra-important, which is not
/// necessarily true of all embeddings. /// necessarily true of all embeddings.
pub fn tls_eager_initialize() -> Result<(), Trap> { pub fn tls_eager_initialize() -> Result<(), Trap> {
wasmtime_runtime::tls_eager_initialize().map_err(Trap::from_runtime) wasmtime_runtime::tls_eager_initialize().map_err(Trap::from_runtime_box)
} }
/// Returns the configuration settings that this engine is using. /// Returns the configuration settings that this engine is using.

View File

@@ -1056,7 +1056,7 @@ pub(crate) fn invoke_wasm_and_catch_traps<T>(
); );
exit_wasm(store, exit); exit_wasm(store, exit);
store.0.entering_native_hook()?; store.0.entering_native_hook()?;
result.map_err(Trap::from_runtime) result.map_err(Trap::from_runtime_box)
} }
} }

View File

@@ -161,6 +161,11 @@ impl Trap {
) )
} }
#[cold] // see Trap::new
pub(crate) fn from_runtime_box(runtime_trap: Box<wasmtime_runtime::Trap>) -> Self {
Self::from_runtime(*runtime_trap)
}
#[cold] // see Trap::new #[cold] // see Trap::new
pub(crate) fn from_runtime(runtime_trap: wasmtime_runtime::Trap) -> Self { pub(crate) fn from_runtime(runtime_trap: wasmtime_runtime::Trap) -> Self {
match runtime_trap { match runtime_trap {