From 66910067642ce2ddf5509845306508f89a24fc9e Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 26 Nov 2024 15:27:47 -0700 Subject: [PATCH] Update the host ABI of Wasmtime to return failure (#9675) * Update the host ABI of Wasmtime to return failure This commit updates the "array call" ABI of Wasmtime used to transition from wasm to the host to explicitly return a `bool` indicating whether the call succeeded or not. Previously functions would implicitly unwind via `longjmp` and thus no explicit checks were necessary. The burden of unwinding is now placed on Cranelift-compiled code itself instead of the caller. There are a few pieces of rationale for this change: * Primarily I was implementing initial integration of Pulley where the host `setjmp` and `longjmp` cannot be used to maintain the Pulley interpreter state. My initial plan for handling this was to handle traps a bit differently in Pulley where having direct access to whether a function trapped or not in the interpreter bytecode is easier to deal with. * Additionally it's generally not safe to call `longjmp` from Rust as it will not run on-stack destructors. This is ok today in the sense that we shouldn't have these in Wasmtime, but directly returning to compiled code improves our safety story here a bit where we just won't have to deal with the problem of skipping destructors. * In the future I'd like to move away from `setjmp` and `longjmp` entirely in the host to a Cranelift-native solution. This change would be required for such a migration anyway so it's my hope to make such a Cranelift-based implementation easier in the future. This might be necessary, for example, when implementing the `exception-handling` proposal for Wasmtime. Functionally this commit does not remove all usages of call-`longjmp`-from-Rust. Notably all libcalls and builtins still use this helper in the trampolines generated in Rust. I plan on going through the libcalls and updating their ABIs and signatures to reflect this in follow-up commits. As a result a few preexisting functions that should go away are marked `#[deprecated]` for internal use in this commit. I'll be cleaning that up as follow-ups. For now this commit handles the "hard part" of host functions by ensuring that the new `bool` return value is plumbed in all the locations correctly. prtest:full * Hack around Windows MinGW miscompile (?) * Run clang-format --- crates/cranelift/src/compiler.rs | 109 +++++++++--- crates/cranelift/src/compiler/component.rs | 19 +- crates/cranelift/src/func_environ.rs | 4 + crates/cranelift/src/lib.rs | 2 + crates/environ/src/builtin.rs | 6 +- .../src/runtime/component/func/host.rs | 17 +- crates/wasmtime/src/runtime/func.rs | 14 +- crates/wasmtime/src/runtime/func/typed.rs | 2 +- .../wasmtime/src/runtime/trampoline/func.rs | 33 +--- crates/wasmtime/src/runtime/vm/component.rs | 6 +- .../src/runtime/vm/component/libcalls.rs | 1 + crates/wasmtime/src/runtime/vm/helpers.c | 61 ++++++- crates/wasmtime/src/runtime/vm/libcalls.rs | 9 + .../src/runtime/vm/sys/custom/capi.rs | 8 +- .../src/runtime/vm/sys/custom/traphandlers.rs | 8 +- .../src/runtime/vm/sys/miri/traphandlers.rs | 7 +- .../src/runtime/vm/sys/unix/traphandlers.rs | 4 +- .../runtime/vm/sys/windows/traphandlers.rs | 4 +- .../wasmtime/src/runtime/vm/traphandlers.rs | 164 +++++++++++++++--- .../src/runtime/vm/traphandlers/signals.rs | 20 +-- crates/wasmtime/src/runtime/vm/vmcontext.rs | 15 +- .../embedding/wasmtime-platform.c | 11 +- .../embedding/wasmtime-platform.h | 12 +- pulley/src/interp.rs | 1 + pulley/src/lib.rs | 42 +++-- tests/disas/epoch-interruption-x86.wat | 4 +- .../winch/x64/call_indirect/call_indirect.wat | 4 +- .../winch/x64/call_indirect/local_arg.wat | 2 +- tests/disas/winch/x64/load/grow_load.wat | 2 +- tests/disas/winch/x64/table/fill.wat | 4 +- tests/disas/winch/x64/table/get.wat | 2 +- tests/disas/winch/x64/table/grow.wat | 2 +- .../disas/winch/x64/table/init_copy_drop.wat | 20 +-- tests/disas/winch/x64/table/set.wat | 2 +- 34 files changed, 428 insertions(+), 193 deletions(-) diff --git a/crates/cranelift/src/compiler.rs b/crates/cranelift/src/compiler.rs index 71e99216e7b2..f33fb4f34bba 100644 --- a/crates/cranelift/src/compiler.rs +++ b/crates/cranelift/src/compiler.rs @@ -400,7 +400,12 @@ impl wasmtime_environ::Compiler for Compiler { values_vec_len, ); - builder.ins().return_(&[]); + // At this time wasm functions always signal traps with longjmp or some + // similar sort of routine, so if we got this far that means that the + // function did not trap, so return a "true" value here to indicate that + // to satisfy the ABI of the array-call signature. + let true_return = builder.ins().iconst(ir::types::I8, 1); + builder.ins().return_(&[true_return]); builder.finalize(); Ok(Box::new(compiler.finish()?)) @@ -459,13 +464,14 @@ impl wasmtime_environ::Compiler for Compiler { // Do an indirect call to the callee. let callee_signature = builder.func.import_signature(array_call_sig); - self.call_indirect_host( + let call = self.call_indirect_host( &mut builder, callee_signature, callee, &[callee_vmctx, caller_vmctx, args_base, args_len], ); - + let succeeded = builder.func.dfg.inst_results(call)[0]; + self.raise_if_host_trapped(&mut builder, caller_vmctx, succeeded); let results = self.load_values_from_array(wasm_func_ty.returns(), &mut builder, args_base, args_len); builder.ins().return_(&results); @@ -637,27 +643,11 @@ impl wasmtime_environ::Compiler for Compiler { ); save_last_wasm_exit_fp_and_pc(&mut builder, pointer_type, &ptr_size, limits); - // Now it's time to delegate to the actual builtin. Builtins are stored - // in an array in all `VMContext`s. First load the base pointer of the - // array and then load the entry of the array that corresponds to this - // builtin. - let mem_flags = ir::MemFlags::trusted().with_readonly(); - let array_addr = builder.ins().load( - pointer_type, - mem_flags, - vmctx, - i32::from(ptr_size.vmcontext_builtin_functions()), - ); - let body_offset = i32::try_from(index.index() * pointer_type.bytes()).unwrap(); - let func_addr = builder - .ins() - .load(pointer_type, mem_flags, array_addr, body_offset); - - // Forward all our own arguments to the libcall itself, and then return - // all the same results as the libcall. - let block_params = builder.block_params(block0).to_vec(); - let host_sig = builder.func.import_signature(host_sig); - let call = self.call_indirect_host(&mut builder, host_sig, func_addr, &block_params); + // Now it's time to delegate to the actual builtin. Forward all our own + // arguments to the libcall itself, and then return all the same results + // as the libcall. + let args = builder.block_params(block0).to_vec(); + let call = self.call_builtin(&mut builder, vmctx, &args, index, host_sig); let results = builder.func.dfg.inst_results(call).to_vec(); builder.ins().return_(&results); builder.finalize(); @@ -877,6 +867,77 @@ impl Compiler { }), } } + + /// Invokes the `raise` libcall in `vmctx` if the `succeeded` value + /// indicates if a trap happened. + /// + /// This helper is used when the host returns back to WebAssembly. The host + /// returns a `bool` indicating whether the call succeeded. If the call + /// failed then Cranelift needs to unwind back to the original invocation + /// point. The unwind right now is then implemented in Wasmtime with a + /// `longjmp`, but one day this might be implemented differently with an + /// unwind inside of Cranelift. + /// + /// Additionally in the future for pulley this will emit a special trap + /// opcode for Pulley itself to cease interpretation and exit the + /// interpreter. + fn raise_if_host_trapped( + &self, + builder: &mut FunctionBuilder<'_>, + vmctx: ir::Value, + succeeded: ir::Value, + ) { + let trapped_block = builder.create_block(); + let continuation_block = builder.create_block(); + builder.set_cold_block(trapped_block); + builder + .ins() + .brif(succeeded, continuation_block, &[], trapped_block, &[]); + + builder.seal_block(trapped_block); + builder.seal_block(continuation_block); + + builder.switch_to_block(trapped_block); + let sigs = BuiltinFunctionSignatures::new(&*self.isa, &self.tunables); + let sig = sigs.host_signature(BuiltinFunctionIndex::raise()); + self.call_builtin(builder, vmctx, &[vmctx], BuiltinFunctionIndex::raise(), sig); + builder.ins().trap(TRAP_INTERNAL_ASSERT); + + builder.switch_to_block(continuation_block); + } + + /// Helper to load the core `builtin` from `vmctx` and invoke it with + /// `args`. + fn call_builtin( + &self, + builder: &mut FunctionBuilder<'_>, + vmctx: ir::Value, + args: &[ir::Value], + builtin: BuiltinFunctionIndex, + sig: ir::Signature, + ) -> ir::Inst { + let isa = &*self.isa; + let ptr_size = isa.pointer_bytes(); + let pointer_type = isa.pointer_type(); + + // Builtins are stored in an array in all `VMContext`s. First load the + // base pointer of the array and then load the entry of the array that + // corresponds to this builtin. + let mem_flags = ir::MemFlags::trusted().with_readonly(); + let array_addr = builder.ins().load( + pointer_type, + mem_flags, + vmctx, + i32::from(ptr_size.vmcontext_builtin_functions()), + ); + let body_offset = i32::try_from(builtin.index() * pointer_type.bytes()).unwrap(); + let func_addr = builder + .ins() + .load(pointer_type, mem_flags, array_addr, body_offset); + + let sig = builder.func.import_signature(sig); + self.call_indirect_host(builder, sig, func_addr, args) + } } struct FunctionCompiler<'a> { diff --git a/crates/cranelift/src/compiler/component.rs b/crates/cranelift/src/compiler/component.rs index 8f15f003ca68..1ab7064fc86c 100644 --- a/crates/cranelift/src/compiler/component.rs +++ b/crates/cranelift/src/compiler/component.rs @@ -120,6 +120,7 @@ impl<'a> TrampolineCompiler<'a> { let pointer_type = self.isa.pointer_type(); let args = self.builder.func.dfg.block_params(self.block0).to_vec(); let vmctx = args[0]; + let caller_vmctx = args[1]; let wasm_func_ty = self.types[self.signature].unwrap_func(); // Start off by spilling all the wasm arguments into a stack slot to be @@ -228,6 +229,9 @@ impl<'a> TrampolineCompiler<'a> { host_sig.params.push(ir::AbiParam::new(pointer_type)); callee_args.push(values_vec_len); + // return value is a bool whether a trap was raised or not + host_sig.returns.push(ir::AbiParam::new(ir::types::I8)); + // Load host function pointer from the vmcontext and then call that // indirect function pointer with the list of arguments. let host_fn = self.builder.ins().load( @@ -237,11 +241,15 @@ impl<'a> TrampolineCompiler<'a> { i32::try_from(self.offsets.lowering_callee(index)).unwrap(), ); let host_sig = self.builder.import_signature(host_sig); - self.compiler - .call_indirect_host(&mut self.builder, host_sig, host_fn, &callee_args); + let call = + self.compiler + .call_indirect_host(&mut self.builder, host_sig, host_fn, &callee_args); + let succeeded = self.builder.func.dfg.inst_results(call)[0]; match self.abi { Abi::Wasm => { + self.compiler + .raise_if_host_trapped(&mut self.builder, caller_vmctx, succeeded); // After the host function has returned the results are loaded from // `values_vec_ptr` and then returned. let results = self.compiler.load_values_from_array( @@ -253,7 +261,7 @@ impl<'a> TrampolineCompiler<'a> { self.builder.ins().return_(&results); } Abi::Array => { - self.builder.ins().return_(&[]); + self.builder.ins().return_(&[succeeded]); } } } @@ -522,8 +530,8 @@ impl<'a> TrampolineCompiler<'a> { self.builder.seal_block(run_destructor_block); self.builder.switch_to_block(return_block); - self.builder.ins().return_(&[]); self.builder.seal_block(return_block); + self.abi_store_results(&[]); } /// Invokes a host libcall and returns the result. @@ -624,7 +632,8 @@ impl<'a> TrampolineCompiler<'a> { ptr, len, ); - self.builder.ins().return_(&[]); + let true_value = self.builder.ins().iconst(ir::types::I8, 1); + self.builder.ins().return_(&[true_value]); } } } diff --git a/crates/cranelift/src/func_environ.rs b/crates/cranelift/src/func_environ.rs index 71ca33f1eefe..499411e6e093 100644 --- a/crates/cranelift/src/func_environ.rs +++ b/crates/cranelift/src/func_environ.rs @@ -171,6 +171,10 @@ impl<'module_environment> FuncEnvironment<'module_environment> { ) -> Self { let builtin_functions = BuiltinFunctions::new(isa, tunables); + // This isn't used during translation, so squash the warning about this + // being unused from the compiler. + let _ = BuiltinFunctions::raise; + // Avoid unused warning in default build. #[cfg(not(feature = "wmemcheck"))] let _ = wmemcheck; diff --git a/crates/cranelift/src/lib.rs b/crates/cranelift/src/lib.rs index 43d5452f3ca5..df4757b59bd3 100644 --- a/crates/cranelift/src/lib.rs +++ b/crates/cranelift/src/lib.rs @@ -151,6 +151,8 @@ fn array_call_signature(isa: &dyn TargetIsa) -> ir::Signature { // of `ValRaw`. sig.params.push(ir::AbiParam::new(isa.pointer_type())); sig.params.push(ir::AbiParam::new(isa.pointer_type())); + // boolean return value of whether this function trapped + sig.returns.push(ir::AbiParam::new(ir::types::I8)); sig } diff --git a/crates/environ/src/builtin.rs b/crates/environ/src/builtin.rs index f529dac9cf45..435cf6df9dfa 100644 --- a/crates/environ/src/builtin.rs +++ b/crates/environ/src/builtin.rs @@ -196,8 +196,12 @@ macro_rules! foreach_builtin_function { #[cfg(feature = "gc")] table_fill_gc_ref(vmctx: vmctx, table: i32, dst: i64, val: reference, len: i64); - // Raises an unconditional trap. + // Raises an unconditional trap with the specified code. trap(vmctx: vmctx, code: u8); + + // Raises an unconditional trap where the trap information must have + // been previously filled in. + raise(vmctx: vmctx); } }; } diff --git a/crates/wasmtime/src/runtime/component/func/host.rs b/crates/wasmtime/src/runtime/component/func/host.rs index 92d533ef2dce..f53df1b6b6f6 100644 --- a/crates/wasmtime/src/runtime/component/func/host.rs +++ b/crates/wasmtime/src/runtime/component/func/host.rs @@ -48,7 +48,8 @@ impl HostFunc { string_encoding: StringEncoding, storage: *mut MaybeUninit, storage_len: usize, - ) where + ) -> bool + where F: Fn(StoreContextMut, P) -> Result, P: ComponentNamedList + Lift + 'static, R: ComponentNamedList + Lower + 'static, @@ -295,24 +296,19 @@ unsafe fn call_host_and_handle_result( &Arc, StoreContextMut<'_, T>, ) -> Result<()>, -) { +) -> bool { let cx = VMComponentContext::from_opaque(cx); let instance = (*cx).instance(); let types = (*instance).component_types(); let raw_store = (*instance).store(); let mut store = StoreContextMut(&mut *raw_store.cast()); - let res = crate::runtime::vm::catch_unwind_and_longjmp(|| { + crate::runtime::vm::catch_unwind_and_record_trap(|| { store.0.call_hook(CallHook::CallingHost)?; let res = func(instance, types, store.as_context_mut()); store.0.call_hook(CallHook::ReturningFromHost)?; res - }); - - match res { - Ok(()) => {} - Err(e) => crate::runtime::vm::raise_user_trap(e), - } + }) } unsafe fn call_host_dynamic( @@ -435,7 +431,8 @@ extern "C" fn dynamic_entrypoint( string_encoding: StringEncoding, storage: *mut MaybeUninit, storage_len: usize, -) where +) -> bool +where F: Fn(StoreContextMut<'_, T>, &[Val], &mut [Val]) -> Result<()> + Send + Sync + 'static, { let data = data as *const F; diff --git a/crates/wasmtime/src/runtime/func.rs b/crates/wasmtime/src/runtime/func.rs index b9e215d2e03e..2a8748de2615 100644 --- a/crates/wasmtime/src/runtime/func.rs +++ b/crates/wasmtime/src/runtime/func.rs @@ -1592,7 +1592,7 @@ impl Func { /// can pass to the called wasm function, if desired. pub(crate) fn invoke_wasm_and_catch_traps( store: &mut StoreContextMut<'_, T>, - closure: impl FnMut(*mut VMContext), + closure: impl FnMut(*mut VMContext) -> bool, ) -> Result<()> { unsafe { let exit = enter_wasm(store); @@ -2296,7 +2296,8 @@ impl HostContext { caller_vmctx: *mut VMOpaqueContext, args: *mut ValRaw, args_len: usize, - ) where + ) -> bool + where F: Fn(Caller<'_, T>, P) -> R + 'static, P: WasmTyList, R: WasmRet, @@ -2356,15 +2357,10 @@ impl HostContext { // With nothing else on the stack move `run` into this // closure and then run it as part of `Caller::with`. - let result = crate::runtime::vm::catch_unwind_and_longjmp(move || { + crate::runtime::vm::catch_unwind_and_record_trap(move || { let caller_vmctx = VMContext::from_opaque(caller_vmctx); Caller::with(caller_vmctx, run) - }); - - match result { - Ok(val) => val, - Err(err) => crate::runtime::vm::raise_user_trap(err), - } + }) } } diff --git a/crates/wasmtime/src/runtime/func/typed.rs b/crates/wasmtime/src/runtime/func/typed.rs index 40df74368b25..5d233b8a1945 100644 --- a/crates/wasmtime/src/runtime/func/typed.rs +++ b/crates/wasmtime/src/runtime/func/typed.rs @@ -219,7 +219,7 @@ where let storage = core::ptr::slice_from_raw_parts_mut(storage, storage_len); func_ref .as_ref() - .array_call(VMOpaqueContext::from_vmcontext(caller), storage); + .array_call(VMOpaqueContext::from_vmcontext(caller), storage) }); let (_, storage) = captures; diff --git a/crates/wasmtime/src/runtime/trampoline/func.rs b/crates/wasmtime/src/runtime/trampoline/func.rs index 5df84eb7a080..60f40745e34a 100644 --- a/crates/wasmtime/src/runtime/trampoline/func.rs +++ b/crates/wasmtime/src/runtime/trampoline/func.rs @@ -25,23 +25,13 @@ unsafe extern "C" fn array_call_shim( caller_vmctx: *mut VMOpaqueContext, values_vec: *mut ValRaw, values_vec_len: usize, -) where +) -> bool +where F: Fn(*mut VMContext, &mut [ValRaw]) -> Result<()> + 'static, { - // Here we are careful to use `catch_unwind` to ensure Rust panics don't - // unwind past us. The primary reason for this is that Rust considers it UB - // to unwind past an `extern "C"` function. Here we are in an `extern "C"` - // function and the cross into wasm was through an `extern "C"` function at - // the base of the stack as well. We'll need to wait for assorted RFCs and - // language features to enable this to be done in a sound and stable fashion - // before avoiding catching the panic here. - // - // Also note that there are intentionally no local variables on this stack - // frame. The reason for that is that some of the "raise" functions we have - // below will trigger a longjmp, which won't run local destructors if we - // have any. To prevent leaks we avoid having any local destructors by - // avoiding local variables. - let result = crate::runtime::vm::catch_unwind_and_longjmp(|| { + // Be sure to catch Rust panics to manually shepherd them across the wasm + // boundary, and then otherwise delegate as normal. + crate::runtime::vm::catch_unwind_and_record_trap(|| { let vmctx = VMArrayCallHostFuncContext::from_opaque(vmctx); // Double-check ourselves in debug mode, but we control // the `Any` here so an unsafe downcast should also @@ -51,18 +41,7 @@ unsafe extern "C" fn array_call_shim( let state = &*(state as *const _ as *const TrampolineState); let values_vec = core::slice::from_raw_parts_mut(values_vec, values_vec_len); (state.func)(VMContext::from_opaque(caller_vmctx), values_vec) - }); - - match result { - Ok(()) => {} - - // If a trap was raised (an error returned from the imported function) - // then we smuggle the trap through `Box` through to the - // call-site, which gets unwrapped in `Trap::from_runtime` later on as we - // convert from the internal `Trap` type to our own `Trap` type in this - // crate. - Err(err) => crate::runtime::vm::raise_user_trap(err), - } + }) } pub fn create_array_call_function( diff --git a/crates/wasmtime/src/runtime/vm/component.rs b/crates/wasmtime/src/runtime/vm/component.rs index 21f3648be892..b06296f85802 100644 --- a/crates/wasmtime/src/runtime/vm/component.rs +++ b/crates/wasmtime/src/runtime/vm/component.rs @@ -97,6 +97,10 @@ pub struct ComponentInstance { /// signature that this callee corresponds to. /// * `nargs_and_results` - the size, in units of `ValRaw`, of /// `args_and_results`. +/// +/// This function returns a `bool` which indicates whether the call succeeded +/// or not. On failure this function records trap information in TLS which +/// should be suitable for reading later. // // FIXME: 9 arguments is probably too many. The `data` through `string-encoding` // parameters should probably get packaged up into the `VMComponentContext`. @@ -112,7 +116,7 @@ pub type VMLoweringCallee = extern "C" fn( string_encoding: StringEncoding, args_and_results: *mut mem::MaybeUninit, nargs_and_results: usize, -); +) -> bool; /// Structure describing a lowered host function stored within a /// `VMComponentContext` per-lowering. diff --git a/crates/wasmtime/src/runtime/vm/component/libcalls.rs b/crates/wasmtime/src/runtime/vm/component/libcalls.rs index 6fd7920b3933..7135524bf0bf 100644 --- a/crates/wasmtime/src/runtime/vm/component/libcalls.rs +++ b/crates/wasmtime/src/runtime/vm/component/libcalls.rs @@ -115,6 +115,7 @@ mod trampolines { )* ) => ( $( + #[allow(deprecated)] // FIXME: need to update this pub unsafe extern "C" fn $name( $($pname : signature!(@ty $param),)* ) $( -> signature!(@ty $result))? { diff --git a/crates/wasmtime/src/runtime/vm/helpers.c b/crates/wasmtime/src/runtime/vm/helpers.c index 2c3dd99ad9f3..84711450a2f8 100644 --- a/crates/wasmtime/src/runtime/vm/helpers.c +++ b/crates/wasmtime/src/runtime/vm/helpers.c @@ -61,16 +61,65 @@ typedef sigjmp_buf platform_jmp_buf; #define CONCAT(a, b) CONCAT2(a, b) #define VERSIONED_SYMBOL(a) CONCAT(a, VERSIONED_SUFFIX) -int VERSIONED_SYMBOL(wasmtime_setjmp)(void **buf_storage, - void (*body)(void *, void *), - void *payload, void *callee) { +// Define one function here, `wasmtime_setjmp_inverted`, which returns the +// negation of whether the call succeeded. Define then the actual import below +// of `wasmtime_setjmp_*` which returns the negation of this negation which +// means it returns whether the function invocation succeeded or not. +// +// Why in the world would we do this? For now: MinGW. In +// bytecodealliance/wasmtime#9675 that PR was originally failing CI only on +// MinGW and seems to be fixed by this. In that PR the signature of `body` here +// changed from a `void` return to a `bool` returned. That means that the body +// of this function changed from the historical: +// +// body(payload, callee); +// return 1; +// +// to what we actually want: +// +// return body(payload, callee); +// +// For some reason though this causes issues when unwinding via `longjmp` on +// Windows. Tests would exit with the error message: +// +// code 0xc0000028: An invalid or unaligned stack was encountered during an +// unwind operation. (os error 543) +// +// Debugging revealed that if this: +// +// return body(payload, callee); +// +// were written as: +// +// bool ret = body(payload, callee); +// return ret; +// +// then the bug would be "fixed". This "fix" didn't work in release mode +// however, leading to the current fix. For whatever reason it seems that +// unwinding is broken if there's not code between the `body(...)` indirect +// call and the function return. The `!` here below, inverting the return value, +// is the source of that "code". +// +// Ideally this `*_inverted` shim would go away and get past CI. It's unclear +// whether we're dealing with a miscompile in GCC, bad unwinding information +// generated by Cranelift for JIT code, or what. For now "this seems to work" +// but we'll also be in the process of forwarding this to some other Windows +// folks to see better what's going on. +static bool wasmtime_setjmp_inverted(void **buf_storage, + bool (*body)(void *, void *), + void *payload, void *callee) { platform_jmp_buf buf; if (platform_setjmp(buf) != 0) { - return 0; + return true; } *buf_storage = &buf; - body(payload, callee); - return 1; + return !body(payload, callee); +} + +bool VERSIONED_SYMBOL(wasmtime_setjmp)(void **buf_storage, + bool (*body)(void *, void *), + void *payload, void *callee) { + return !wasmtime_setjmp_inverted(buf_storage, body, payload, callee); } void VERSIONED_SYMBOL(wasmtime_longjmp)(void *JmpBuf) { diff --git a/crates/wasmtime/src/runtime/vm/libcalls.rs b/crates/wasmtime/src/runtime/vm/libcalls.rs index 04d1f8b95778..f7f3728d9239 100644 --- a/crates/wasmtime/src/runtime/vm/libcalls.rs +++ b/crates/wasmtime/src/runtime/vm/libcalls.rs @@ -110,6 +110,7 @@ pub mod raw { ) $( -> libcall!(@ty $result))? { $(#[cfg($attr)])? { + #[allow(deprecated)] // FIXME: need to update this let ret = crate::runtime::vm::traphandlers::catch_unwind_and_longjmp(|| { InstanceAndStore::from_vmctx(vmctx, |pair| { { @@ -172,6 +173,7 @@ pub mod raw { unsafe fn convert(self) -> T { match self { Ok(t) => t, + #[allow(deprecated)] // FIXME: need to update this Err(e) => crate::runtime::vm::traphandlers::raise_trap(e.into()), } } @@ -1255,6 +1257,13 @@ fn trap(_store: &mut dyn VMStore, _instance: &mut Instance, code: u8) -> Result< )) } +fn raise(_store: &mut dyn VMStore, _instance: &mut Instance) -> Result<(), TrapReason> { + // SAFETY: this is only called from compiled wasm so we know that wasm has + // already been entered. It's a dynamic safety precondition that the trap + // information has already been arranged to be present. + unsafe { crate::runtime::vm::traphandlers::raise_preexisting_trap() } +} + /// This module contains functions which are used for resolving relocations at /// runtime if necessary. /// diff --git a/crates/wasmtime/src/runtime/vm/sys/custom/capi.rs b/crates/wasmtime/src/runtime/vm/sys/custom/capi.rs index 1745f2d6f25d..80429ac71cd8 100644 --- a/crates/wasmtime/src/runtime/vm/sys/custom/capi.rs +++ b/crates/wasmtime/src/runtime/vm/sys/custom/capi.rs @@ -114,14 +114,14 @@ extern "C" { /// configured. /// * `payload` and `callee` - the two arguments to pass to `callback`. /// - /// Returns 0 if `wasmtime_longjmp` was used to return to this function. - /// Returns 1 if `wasmtime_longjmp` was not called and `callback` returned. + /// Returns false if `wasmtime_longjmp` was used to return to this function. + /// Returns true if `wasmtime_longjmp` was not called and `callback` returned. pub fn wasmtime_setjmp( jmp_buf: *mut *const u8, - callback: extern "C" fn(*mut u8, *mut u8), + callback: extern "C" fn(*mut u8, *mut u8) -> bool, payload: *mut u8, callee: *mut u8, - ) -> i32; + ) -> bool; /// Paired with `wasmtime_setjmp` this is used to jump back to the `setjmp` /// point. diff --git a/crates/wasmtime/src/runtime/vm/sys/custom/traphandlers.rs b/crates/wasmtime/src/runtime/vm/sys/custom/traphandlers.rs index 0344ac98bad9..ec3781c238ab 100644 --- a/crates/wasmtime/src/runtime/vm/sys/custom/traphandlers.rs +++ b/crates/wasmtime/src/runtime/vm/sys/custom/traphandlers.rs @@ -9,13 +9,13 @@ pub type SignalHandler = Box; pub unsafe fn wasmtime_setjmp( jmp_buf: *mut *const u8, - callback: extern "C" fn(*mut u8, *mut VMContext), + callback: extern "C" fn(*mut u8, *mut VMContext) -> bool, payload: *mut u8, callee: *mut VMContext, -) -> i32 { +) -> bool { let callback = mem::transmute::< - extern "C" fn(*mut u8, *mut VMContext), - extern "C" fn(*mut u8, *mut u8), + extern "C" fn(*mut u8, *mut VMContext) -> bool, + extern "C" fn(*mut u8, *mut u8) -> bool, >(callback); capi::wasmtime_setjmp(jmp_buf, callback, payload, callee.cast()) } diff --git a/crates/wasmtime/src/runtime/vm/sys/miri/traphandlers.rs b/crates/wasmtime/src/runtime/vm/sys/miri/traphandlers.rs index a1f4cae050b6..f95bbbd782b9 100644 --- a/crates/wasmtime/src/runtime/vm/sys/miri/traphandlers.rs +++ b/crates/wasmtime/src/runtime/vm/sys/miri/traphandlers.rs @@ -14,12 +14,11 @@ use crate::runtime::vm::VMContext; pub fn wasmtime_setjmp( _jmp_buf: *mut *const u8, - callback: extern "C" fn(*mut u8, *mut VMContext), + callback: extern "C" fn(*mut u8, *mut VMContext) -> bool, payload: *mut u8, callee: *mut VMContext, -) -> i32 { - callback(payload, callee); - 1 +) -> bool { + callback(payload, callee) } pub fn wasmtime_longjmp(_jmp_buf: *const u8) -> ! { diff --git a/crates/wasmtime/src/runtime/vm/sys/unix/traphandlers.rs b/crates/wasmtime/src/runtime/vm/sys/unix/traphandlers.rs index ffd179d49ac0..c186e3528d9a 100644 --- a/crates/wasmtime/src/runtime/vm/sys/unix/traphandlers.rs +++ b/crates/wasmtime/src/runtime/vm/sys/unix/traphandlers.rs @@ -6,10 +6,10 @@ extern "C" { #[allow(improper_ctypes)] pub fn wasmtime_setjmp( jmp_buf: *mut *const u8, - callback: extern "C" fn(*mut u8, *mut VMContext), + callback: extern "C" fn(*mut u8, *mut VMContext) -> bool, payload: *mut u8, callee: *mut VMContext, - ) -> i32; + ) -> bool; #[wasmtime_versioned_export_macros::versioned_link] pub fn wasmtime_longjmp(jmp_buf: *const u8) -> !; diff --git a/crates/wasmtime/src/runtime/vm/sys/windows/traphandlers.rs b/crates/wasmtime/src/runtime/vm/sys/windows/traphandlers.rs index e331299d9ffc..e107763a45b9 100644 --- a/crates/wasmtime/src/runtime/vm/sys/windows/traphandlers.rs +++ b/crates/wasmtime/src/runtime/vm/sys/windows/traphandlers.rs @@ -13,10 +13,10 @@ extern "C" { #[allow(improper_ctypes)] pub fn wasmtime_setjmp( jmp_buf: *mut *const u8, - callback: extern "C" fn(*mut u8, *mut VMContext), + callback: extern "C" fn(*mut u8, *mut VMContext) -> bool, payload: *mut u8, callee: *mut VMContext, - ) -> i32; + ) -> bool; #[wasmtime_versioned_export_macros::versioned_link] pub fn wasmtime_longjmp(jmp_buf: *const u8) -> !; diff --git a/crates/wasmtime/src/runtime/vm/traphandlers.rs b/crates/wasmtime/src/runtime/vm/traphandlers.rs index ede294392696..a211b4efebf7 100644 --- a/crates/wasmtime/src/runtime/vm/traphandlers.rs +++ b/crates/wasmtime/src/runtime/vm/traphandlers.rs @@ -20,8 +20,7 @@ use crate::runtime::store::StoreOpaque; use crate::runtime::vm::sys::traphandlers; use crate::runtime::vm::{Instance, VMContext, VMRuntimeLimits}; use crate::{StoreContextMut, WasmBacktrace}; -use core::cell::{Cell, UnsafeCell}; -use core::mem::MaybeUninit; +use core::cell::Cell; use core::ops::Range; use core::ptr; @@ -40,28 +39,42 @@ fn lazy_per_thread_init() { /// This function performs as-if a wasm trap was just executed. This trap /// payload is then returned from `catch_traps` below. /// +/// FIXME: this function should get removed in favor of explicitly calling the +/// `raise` libcall from wasm. +/// /// # Safety /// /// Only safe to call when wasm code is on the stack, aka `catch_traps` must /// have been previously called. Additionally no Rust destructors can be on the /// stack. They will be skipped and not executed. +#[deprecated(note = "move to `raise_preexisting_trap` or `catch_unwind_and_record_trap` instead")] +#[allow(deprecated)] pub unsafe fn raise_trap(reason: TrapReason) -> ! { tls::with(|info| info.unwrap().unwind_with(UnwindReason::Trap(reason))) } -/// Raises a user-defined trap immediately. +/// Raises a preexisting trap and unwinds. /// -/// This function performs as-if a wasm trap was just executed, only the trap -/// has a dynamic payload associated with it which is user-provided. This trap -/// payload is then returned from `catch_traps` below. +/// This function will execute the `longjmp` to make its way back to the +/// original `setjmp` performed when wasm was entered. This is currently +/// only called from the `raise` builtin of Wasmtime. This builtin is only used +/// when the host returns back to wasm and indicates that a trap should be +/// raised. In this situation the host has already stored trap information +/// within the `CallThreadState` and this is the low-level operation to actually +/// perform an unwind. +/// +/// This function won't be use with Pulley, for example, as the interpreter +/// halts differently than native code. Additionally one day this will ideally +/// be implemented by Cranelift itself without need of a libcall when Cranelift +/// implements the exception handling proposal for example. /// /// # Safety /// /// Only safe to call when wasm code is on the stack, aka `catch_traps` must /// have been previously called. Additionally no Rust destructors can be on the /// stack. They will be skipped and not executed. -pub unsafe fn raise_user_trap(error: Error) -> ! { - raise_trap(TrapReason::User(error)) +pub(super) unsafe fn raise_preexisting_trap() -> ! { + tls::with(|info| info.unwrap().unwind()) } /// Invokes the closure `f` and returns the result. @@ -71,11 +84,16 @@ pub unsafe fn raise_user_trap(error: Error) -> ! { /// the nearest `setjmp`. The panic will then be resumed from where it is /// caught. /// +/// FIXME: this function should get removed in favor of +/// `catch_unwind_and_record_trap` or a variant thereof. +/// /// # Safety /// /// Only safe to call when wasm code is on the stack, aka `catch_traps` must /// have been previously called. Additionally no Rust destructors can be on the /// stack. They will be skipped and not executed in the case that `f` panics. +#[deprecated(note = "move to `catch_unwind_and_record_trap` instead")] +#[allow(deprecated)] pub unsafe fn catch_unwind_and_longjmp(f: impl FnOnce() -> R) -> R { // With `panic=unwind` use `std::panic::catch_unwind` to catch possible // panics to rethrow. @@ -97,6 +115,56 @@ pub unsafe fn catch_unwind_and_longjmp(f: impl FnOnce() -> R) -> R { } } +/// Invokes the closure `f` and returns a `bool` if it succeeded. +/// +/// This will invoke the closure `f` which returns a `Result<()>`. The results +/// of executing this function are handled as: +/// +/// * Returns `Ok(())` - this means that this function returns `true` with no +/// other action taken. +/// * Returns `Err(e)` - this records trap information in the current +/// `CallThreadState` and returns `false`. +/// * Panics - this records unwind information in the current `CallThreadState` +/// and returns `false`. +/// +/// The purpose of this helper is to be used at the wasm->host boundary. This +/// is used to implement the "array call" ABI of the host where wasm itself +/// will initiate the unwind when it sees a `false` return value from the host. +/// +/// The return value of this function is typically directly returned back to +/// wasm to get handled. +pub fn catch_unwind_and_record_trap(f: impl FnOnce() -> Result<()>) -> bool { + let f = move || match f() { + Ok(()) => true, + Err(error) => { + let reason = UnwindReason::Trap(TrapReason::User(error)); + tls::with(|info| info.unwrap().record_unwind(reason)); + false + } + }; + // With `panic=unwind` use `std::panic::catch_unwind` to catch possible + // panics to rethrow. + #[cfg(all(feature = "std", panic = "unwind"))] + { + match std::panic::catch_unwind(std::panic::AssertUnwindSafe(f)) { + Ok(result) => result, + Err(err) => { + tls::with(|info| info.unwrap().record_unwind(UnwindReason::Panic(err))); + false + } + } + } + + // With `panic=abort` there's no use in using `std::panic::catch_unwind` + // since it won't actually catch anything. Note that + // `std::panic::catch_unwind` will technically optimize to this but having + // this branch avoids using the `std::panic` module entirely. + #[cfg(not(all(feature = "std", panic = "unwind")))] + { + f() + } +} + /// Stores trace message with backtrace. #[derive(Debug)] pub struct Trap { @@ -164,13 +232,16 @@ impl From for TrapReason { /// Catches any wasm traps that happen within the execution of `closure`, /// returning them as a `Result`. /// -/// Highly unsafe since `closure` won't have any dtors run. +/// # Unsafety +/// +/// This function is unsafe because during the execution of `closure` it may be +/// longjmp'd over and none of its destructors on the stack may be run. pub unsafe fn catch_traps( store: &mut StoreContextMut<'_, T>, mut closure: F, ) -> Result<(), Box> where - F: FnMut(*mut VMContext), + F: FnMut(*mut VMContext) -> bool, { let caller = store.0.default_caller(); @@ -194,9 +265,9 @@ where Err((UnwindReason::Panic(panic), _, _)) => std::panic::resume_unwind(panic), }; - extern "C" fn call_closure(payload: *mut u8, caller: *mut VMContext) + extern "C" fn call_closure(payload: *mut u8, caller: *mut VMContext) -> bool where - F: FnMut(*mut VMContext), + F: FnMut(*mut VMContext) -> bool, { unsafe { (*(payload as *mut F))(caller) } } @@ -210,8 +281,7 @@ mod call_thread_state { /// Temporary state stored on the stack which is registered in the `tls` module /// below for calls into wasm. pub struct CallThreadState { - pub(super) unwind: - UnsafeCell, Option)>>, + pub(super) unwind: Cell, Option)>>, pub(super) jmp_buf: Cell<*const u8>, #[cfg(all(feature = "signals-based-traps", not(miri)))] pub(super) signal_handler: Option<*const SignalHandler>, @@ -240,6 +310,10 @@ mod call_thread_state { impl Drop for CallThreadState { fn drop(&mut self) { + // Unwind information should not be present as it should have + // already been processed. + debug_assert!(self.unwind.replace(None).is_none()); + unsafe { *(*self.limits).last_wasm_exit_fp.get() = self.old_last_wasm_exit_fp.get(); *(*self.limits).last_wasm_exit_pc.get() = self.old_last_wasm_exit_pc.get(); @@ -258,7 +332,7 @@ mod call_thread_state { let _: Range<_> = store.async_guard_range(); CallThreadState { - unwind: UnsafeCell::new(MaybeUninit::uninit()), + unwind: Cell::new(None), jmp_buf: Cell::new(ptr::null()), #[cfg(all(feature = "signals-based-traps", not(miri)))] signal_handler: store.signal_handler(), @@ -321,22 +395,40 @@ impl CallThreadState { #[inline] fn with( mut self, - closure: impl FnOnce(&CallThreadState) -> i32, + closure: impl FnOnce(&CallThreadState) -> bool, ) -> Result<(), (UnwindReason, Option, Option)> { - let ret = tls::set(&mut self, |me| closure(me)); - if ret != 0 { + let succeeded = tls::set(&mut self, |me| closure(me)); + if succeeded { Ok(()) } else { - Err(unsafe { self.read_unwind() }) + Err(self.read_unwind()) } } #[cold] - unsafe fn read_unwind(&self) -> (UnwindReason, Option, Option) { - (*self.unwind.get()).as_ptr().read() + fn read_unwind(&self) -> (UnwindReason, Option, Option) { + self.unwind.replace(None).unwrap() } - fn unwind_with(&self, reason: UnwindReason) -> ! { + /// Records the unwind information provided within this `CallThreadState`, + /// optionally capturing a backtrace at this time. + /// + /// This function is used to stash metadata for why an unwind is about to + /// happen. The actual unwind is expected to happen after this function is + /// called using, for example, the `unwind` function below. + /// + /// Note that this is a relatively low-level function and will panic if + /// mis-used. + /// + /// # Panics + /// + /// Panics if unwind information has already been recorded as that should + /// have been processed first. + fn record_unwind(&self, reason: UnwindReason) { + if cfg!(debug_assertions) { + let prev = self.unwind.replace(None); + assert!(prev.is_none()); + } let (backtrace, coredump) = match &reason { // Panics don't need backtraces. There is nowhere to attach the // hypothetical backtrace to and it doesn't really make sense to try @@ -357,12 +449,28 @@ impl CallThreadState { self.capture_coredump(self.limits, None), ), }; - unsafe { - (*self.unwind.get()) - .as_mut_ptr() - .write((reason, backtrace, coredump)); - traphandlers::wasmtime_longjmp(self.jmp_buf.get()); - } + self.unwind.set(Some((reason, backtrace, coredump))); + } + + /// Helper function to perform an actual unwinding operation. + /// + /// This must be preceded by a `record_unwind` operation above to be + /// processed correctly on the other side. + /// + /// # Unsafety + /// + /// This function is not safe if the corresponding setjmp wasn't already + /// called. Additionally this isn't safe as it will skip all Rust + /// destructors on the stack, if there are any. + unsafe fn unwind(&self) -> ! { + debug_assert!(!self.jmp_buf.get().is_null()); + traphandlers::wasmtime_longjmp(self.jmp_buf.get()); + } + + #[deprecated(note = "move to `record_unwind` or `unwind` instead")] + fn unwind_with(&self, reason: UnwindReason) -> ! { + self.record_unwind(reason); + unsafe { self.unwind() } } fn capture_backtrace( diff --git a/crates/wasmtime/src/runtime/vm/traphandlers/signals.rs b/crates/wasmtime/src/runtime/vm/traphandlers/signals.rs index 7783b9de2a41..1a9e8da94d0e 100644 --- a/crates/wasmtime/src/runtime/vm/traphandlers/signals.rs +++ b/crates/wasmtime/src/runtime/vm/traphandlers/signals.rs @@ -151,16 +151,14 @@ impl CallThreadState { ) { let backtrace = self.capture_backtrace(self.limits, Some((pc, fp))); let coredump = self.capture_coredump(self.limits, Some((pc, fp))); - unsafe { - (*self.unwind.get()).as_mut_ptr().write(( - UnwindReason::Trap(TrapReason::Jit { - pc, - faulting_addr, - trap, - }), - backtrace, - coredump, - )); - } + self.unwind.set(Some(( + UnwindReason::Trap(TrapReason::Jit { + pc, + faulting_addr, + trap, + }), + backtrace, + coredump, + ))) } } diff --git a/crates/wasmtime/src/runtime/vm/vmcontext.rs b/crates/wasmtime/src/runtime/vm/vmcontext.rs index 23cebb1d8891..3b95d8a76e2d 100644 --- a/crates/wasmtime/src/runtime/vm/vmcontext.rs +++ b/crates/wasmtime/src/runtime/vm/vmcontext.rs @@ -37,8 +37,13 @@ use wasmtime_environ::{ /// /// * The capacity of the `ValRaw` buffer. Must always be at least /// `max(len(wasm_params), len(wasm_results))`. +/// +/// Return value: +/// +/// * `true` if this call succeeded. +/// * `false` if this call failed and a trap was recorded in TLS. pub type VMArrayCallNative = - unsafe extern "C" fn(*mut VMOpaqueContext, *mut VMOpaqueContext, *mut ValRaw, usize); + unsafe extern "C" fn(*mut VMOpaqueContext, *mut VMOpaqueContext, *mut ValRaw, usize) -> bool; /// An opaque function pointer which might be `VMArrayCallNative` or it might be /// pulley bytecode. Requires external knowledge to determine what kind of @@ -710,11 +715,17 @@ impl VMFuncRef { /// The `args_and_results` area must be large enough to both load all /// arguments from and store all results to. /// + /// Returns whether a trap was recorded in TLS for raising. + /// /// # Unsafety /// /// This method is unsafe because it can be called with any pointers. They /// must all be valid for this wasm function call to proceed. - pub unsafe fn array_call(&self, caller: *mut VMOpaqueContext, args_and_results: *mut [ValRaw]) { + pub unsafe fn array_call( + &self, + caller: *mut VMOpaqueContext, + args_and_results: *mut [ValRaw], + ) -> bool { union GetNativePointer { native: VMArrayCallNative, ptr: NonNull, diff --git a/examples/min-platform/embedding/wasmtime-platform.c b/examples/min-platform/embedding/wasmtime-platform.c index c6e7c38c46eb..023aa3da9778 100644 --- a/examples/min-platform/embedding/wasmtime-platform.c +++ b/examples/min-platform/embedding/wasmtime-platform.c @@ -57,15 +57,14 @@ uintptr_t wasmtime_page_size(void) { return sysconf(_SC_PAGESIZE); } #endif // WASMTIME_SIGNALS_BASED_TRAPS -int32_t wasmtime_setjmp(const uint8_t **jmp_buf_out, - void (*callback)(uint8_t *, uint8_t *), - uint8_t *payload, uint8_t *callee) { +bool wasmtime_setjmp(const uint8_t **jmp_buf_out, + bool (*callback)(uint8_t *, uint8_t *), uint8_t *payload, + uint8_t *callee) { jmp_buf buf; if (setjmp(buf) != 0) - return 0; + return false; *jmp_buf_out = (uint8_t *)&buf; - callback(payload, callee); - return 1; + return callback(payload, callee); } void wasmtime_longjmp(const uint8_t *jmp_buf_ptr) { diff --git a/examples/min-platform/embedding/wasmtime-platform.h b/examples/min-platform/embedding/wasmtime-platform.h index a4996bdf9237..80870afba4ff 100644 --- a/examples/min-platform/embedding/wasmtime-platform.h +++ b/examples/min-platform/embedding/wasmtime-platform.h @@ -173,13 +173,13 @@ extern uintptr_t wasmtime_page_size(void); * configured. * * `payload` and `callee` - the two arguments to pass to `callback`. * - * Returns 0 if `wasmtime_longjmp` was used to return to this function. - * Returns 1 if `wasmtime_longjmp` was not called and `callback` returned. + * Returns false if `wasmtime_longjmp` was used to return to this function. + * Returns true if `wasmtime_longjmp` was not called and `callback` returned. */ -extern int32_t wasmtime_setjmp(const uint8_t **jmp_buf, - void (*callback)(uint8_t*, uint8_t*), - uint8_t *payload, - uint8_t *callee); +extern bool wasmtime_setjmp(const uint8_t **jmp_buf, + bool (*callback)(uint8_t*, uint8_t*), + uint8_t *payload, + uint8_t *callee); /** * Paired with `wasmtime_setjmp` this is used to jump back to the `setjmp` diff --git a/pulley/src/interp.rs b/pulley/src/interp.rs index 0cf782917a21..4d39244571c8 100644 --- a/pulley/src/interp.rs +++ b/pulley/src/interp.rs @@ -1304,6 +1304,7 @@ impl ExtendedOpVisitor for Interpreter<'_> { (@get I32 $reg:ident) => ($reg.get_i32()); (@get I64 $reg:ident) => ($reg.get_i64()); + (@set I8 $dst:ident $val:ident) => ($dst.set_i32($val.into());); (@set I32 $dst:ident $val:ident) => ($dst.set_i32($val);); (@set I64 $dst:ident $val:ident) => ($dst.set_i64($val);); diff --git a/pulley/src/lib.rs b/pulley/src/lib.rs index c1e805d6e81b..a306cc37316c 100644 --- a/pulley/src/lib.rs +++ b/pulley/src/lib.rs @@ -231,37 +231,41 @@ macro_rules! for_each_extended_op { macro_rules! for_each_host_signature { ($m:ident) => { $m! { - fn(I64); - fn(I64, I32); - fn(I64, I32) -> I32; - fn(I64, I32, I32) -> I32; fn(I64, I32, I32, I32) -> I32; + fn(I64, I32, I32) -> I32; + fn(I64, I32, I32) -> I64; + fn(I64, I64, I32, I64, I64, I64, I8, I64, I64) -> I8; + fn(I64, I64, I64, I64, I64) -> I64; + fn(I64, I64, I64, I64) -> I64; + fn(I64, I64, I64) -> I64; + fn(I64, I64, I64); + fn(I64); fn(I64, I32, I32, I32, I32, I32); - fn(I64, I32, I32, I32, I32) -> I32; + fn(I64, I32) -> I32; fn(I64, I32, I32, I32, I32, I32, I32); - fn(I64, I32, I32) -> I64; + fn(I64, I32, I32, I32, I32) -> I32; fn(I64, I32, I32, I64, I32, I32); fn(I64, I32, I32, I64, I64, I64); - fn(I64, I32) -> I64; - fn(I64, I32, I64, I32) -> I32; + fn(I64, I32, I32) -> I32; + fn(I64, I32, I32) -> I64; fn(I64, I32, I64, I32, I64); fn(I64, I32, I64, I32) -> I64; - fn(I64, I32, I64, I32, I64) -> I32; fn(I64, I32, I64, I32, I64, I64); - fn(I64, I32, I64) -> I64; + fn(I64, I32, I64, I32, I64) -> I32; + fn(I64, I32, I64, I32, I64); + fn(I64, I32, I64, I32) -> I32; + fn(I64, I32, I64, I64, I64) -> I32; fn(I64, I32, I64, I64, I64); fn(I64, I32, I64, I64) -> I64; - fn(I64, I32, I64, I64, I64) -> I32; - fn(I64) -> I64; - fn(I64, I64) -> I32; + fn(I64, I32, I64) -> I64; + fn(I64, I32) -> I64; + fn(I64, I32); fn(I64, I64, I32) -> I64; - fn(I64, I64, I32, I64, I64, I64, I8, I64, I64); - fn(I64, I64, I64); - fn(I64, I64, I64, I64); - fn(I64, I64, I64) -> I64; - fn(I64, I64, I64, I64) -> I64; - fn(I64, I64, I64, I64, I64) -> I64; + fn(I64, I64, I64, I64) -> I8; + fn(I64, I64) -> I32; fn(I64, I8); + fn(I64) -> I64; + fn(I64); } }; } diff --git a/tests/disas/epoch-interruption-x86.wat b/tests/disas/epoch-interruption-x86.wat index 3423a754bb7c..dcb5e2307d9a 100644 --- a/tests/disas/epoch-interruption-x86.wat +++ b/tests/disas/epoch-interruption-x86.wat @@ -28,12 +28,12 @@ ;; jae 0x64 ;; jmp 0x46 ;; 57: movq %r13, %rdi -;; callq 0xec +;; callq 0x117 ;; jmp 0x46 ;; 64: movq 0x10(%r12), %rax ;; cmpq %rax, %rdi ;; jb 0x46 ;; 72: movq %r13, %rdi -;; callq 0xec +;; callq 0x117 ;; jmp 0x46 ;; 7f: ud2 diff --git a/tests/disas/winch/x64/call_indirect/call_indirect.wat b/tests/disas/winch/x64/call_indirect/call_indirect.wat index 1f44c50e6e96..7cf5513d5af1 100644 --- a/tests/disas/winch/x64/call_indirect/call_indirect.wat +++ b/tests/disas/winch/x64/call_indirect/call_indirect.wat @@ -76,7 +76,7 @@ ;; movq %r14, %rdi ;; movl $0, %esi ;; movl 8(%rsp), %edx -;; callq 0x2ed +;; callq 0x319 ;; addq $8, %rsp ;; addq $4, %rsp ;; movq 0x1c(%rsp), %r14 @@ -128,7 +128,7 @@ ;; movq %r14, %rdi ;; movl $0, %esi ;; movl 4(%rsp), %edx -;; callq 0x2ed +;; callq 0x319 ;; addq $4, %rsp ;; addq $4, %rsp ;; movq 0x20(%rsp), %r14 diff --git a/tests/disas/winch/x64/call_indirect/local_arg.wat b/tests/disas/winch/x64/call_indirect/local_arg.wat index 2ad9e7500a48..1b8140a87c22 100644 --- a/tests/disas/winch/x64/call_indirect/local_arg.wat +++ b/tests/disas/winch/x64/call_indirect/local_arg.wat @@ -72,7 +72,7 @@ ;; movq %r14, %rdi ;; movl $0, %esi ;; movl 8(%rsp), %edx -;; callq 0x307 +;; callq 0x35c ;; addq $8, %rsp ;; addq $4, %rsp ;; movq 0x1c(%rsp), %r14 diff --git a/tests/disas/winch/x64/load/grow_load.wat b/tests/disas/winch/x64/load/grow_load.wat index 9a6fb6c97833..4ef0e39424f0 100644 --- a/tests/disas/winch/x64/load/grow_load.wat +++ b/tests/disas/winch/x64/load/grow_load.wat @@ -65,7 +65,7 @@ ;; movq %r14, %rdi ;; movl 0xc(%rsp), %esi ;; movl $0, %edx -;; callq 0x2c6 +;; callq 0x2eb ;; addq $0xc, %rsp ;; addq $4, %rsp ;; movq 0x58(%rsp), %r14 diff --git a/tests/disas/winch/x64/table/fill.wat b/tests/disas/winch/x64/table/fill.wat index 45b7b3622f47..1d2f432b2ad0 100644 --- a/tests/disas/winch/x64/table/fill.wat +++ b/tests/disas/winch/x64/table/fill.wat @@ -113,7 +113,7 @@ ;; movq %r14, %rdi ;; movl $0, %esi ;; movl 0xc(%rsp), %edx -;; callq 0x4cf +;; callq 0x515 ;; addq $0xc, %rsp ;; addq $4, %rsp ;; movq 0x28(%rsp), %r14 @@ -133,7 +133,7 @@ ;; movl 0xc(%rsp), %edx ;; movq 4(%rsp), %rcx ;; movl (%rsp), %r8d -;; callq 0x510 +;; callq 0x556 ;; addq $0x10, %rsp ;; movq 0x28(%rsp), %r14 ;; addq $0x30, %rsp diff --git a/tests/disas/winch/x64/table/get.wat b/tests/disas/winch/x64/table/get.wat index df39fa13d49e..2927b19b7c78 100644 --- a/tests/disas/winch/x64/table/get.wat +++ b/tests/disas/winch/x64/table/get.wat @@ -65,7 +65,7 @@ ;; movq %r14, %rdi ;; movl $0, %esi ;; movl 0xc(%rsp), %edx -;; callq 0x2da +;; callq 0x32d ;; addq $0xc, %rsp ;; addq $4, %rsp ;; movq 0x18(%rsp), %r14 diff --git a/tests/disas/winch/x64/table/grow.wat b/tests/disas/winch/x64/table/grow.wat index 588c5b54784e..be7068a3aeff 100644 --- a/tests/disas/winch/x64/table/grow.wat +++ b/tests/disas/winch/x64/table/grow.wat @@ -30,7 +30,7 @@ ;; movl $0, %esi ;; movl $0xa, %edx ;; movq 8(%rsp), %rcx -;; callq 0x16b +;; callq 0x196 ;; addq $8, %rsp ;; addq $8, %rsp ;; movq 0x18(%rsp), %r14 diff --git a/tests/disas/winch/x64/table/init_copy_drop.wat b/tests/disas/winch/x64/table/init_copy_drop.wat index 3dfbe695c547..0cb0dffe13c1 100644 --- a/tests/disas/winch/x64/table/init_copy_drop.wat +++ b/tests/disas/winch/x64/table/init_copy_drop.wat @@ -142,11 +142,11 @@ ;; movl $7, %ecx ;; movl $0, %r8d ;; movl $4, %r9d -;; callq 0x8c4 +;; callq 0x974 ;; movq 8(%rsp), %r14 ;; movq %r14, %rdi ;; movl $1, %esi -;; callq 0x906 +;; callq 0x9b6 ;; movq 8(%rsp), %r14 ;; movq %r14, %rdi ;; movl $0, %esi @@ -154,11 +154,11 @@ ;; movl $0xf, %ecx ;; movl $1, %r8d ;; movl $3, %r9d -;; callq 0x8c4 +;; callq 0x974 ;; movq 8(%rsp), %r14 ;; movq %r14, %rdi ;; movl $3, %esi -;; callq 0x906 +;; callq 0x9b6 ;; movq 8(%rsp), %r14 ;; movq %r14, %rdi ;; movl $0, %esi @@ -166,7 +166,7 @@ ;; movl $0x14, %ecx ;; movl $0xf, %r8d ;; movl $5, %r9d -;; callq 0x945 +;; callq 0x9f5 ;; movq 8(%rsp), %r14 ;; movq %r14, %rdi ;; movl $0, %esi @@ -174,7 +174,7 @@ ;; movl $0x15, %ecx ;; movl $0x1d, %r8d ;; movl $1, %r9d -;; callq 0x945 +;; callq 0x9f5 ;; movq 8(%rsp), %r14 ;; movq %r14, %rdi ;; movl $0, %esi @@ -182,7 +182,7 @@ ;; movl $0x18, %ecx ;; movl $0xa, %r8d ;; movl $1, %r9d -;; callq 0x945 +;; callq 0x9f5 ;; movq 8(%rsp), %r14 ;; movq %r14, %rdi ;; movl $0, %esi @@ -190,7 +190,7 @@ ;; movl $0xd, %ecx ;; movl $0xb, %r8d ;; movl $4, %r9d -;; callq 0x945 +;; callq 0x9f5 ;; movq 8(%rsp), %r14 ;; movq %r14, %rdi ;; movl $0, %esi @@ -198,7 +198,7 @@ ;; movl $0x13, %ecx ;; movl $0x14, %r8d ;; movl $5, %r9d -;; callq 0x945 +;; callq 0x9f5 ;; movq 8(%rsp), %r14 ;; addq $0x10, %rsp ;; popq %rbp @@ -243,7 +243,7 @@ ;; movq %r14, %rdi ;; movl $0, %esi ;; movl 0xc(%rsp), %edx -;; callq 0x987 +;; callq 0xa37 ;; addq $0xc, %rsp ;; addq $4, %rsp ;; movq 0x18(%rsp), %r14 diff --git a/tests/disas/winch/x64/table/set.wat b/tests/disas/winch/x64/table/set.wat index 700ce802e753..cbfa924843bb 100644 --- a/tests/disas/winch/x64/table/set.wat +++ b/tests/disas/winch/x64/table/set.wat @@ -109,7 +109,7 @@ ;; movq %r14, %rdi ;; movl $0, %esi ;; movl 8(%rsp), %edx -;; callq 0x495 +;; callq 0x4ff ;; addq $8, %rsp ;; addq $4, %rsp ;; movq 0x1c(%rsp), %r14