diff --git a/src/mono/CMakeLists.txt b/src/mono/CMakeLists.txt index 6909e62fcf0bd0..7ed11e8c699d70 100644 --- a/src/mono/CMakeLists.txt +++ b/src/mono/CMakeLists.txt @@ -655,7 +655,12 @@ if(LLVM_PREFIX) # llvm-config --cflags set(llvm_cflags "-I${LLVM_PREFIX}/include -D__STDC_CONSTANT_MACROS -D__STD_FORMAT_MACROS -D__STDC_LIMIT_MACROS") - set(llvm_cxxflags "-I${LLVM_PREFIX}/include ${MONO_cxx_include} ${MONO_cxx_std_version} ${MONO_stdlib} -fno-exceptions -fno-rtti -D__STDC_CONSTANT_MACROS -D__STD_FORMAT_MACROS -D__STDC_LIMIT_MACROS") + + if (HOST_BROWSER) + set(llvm_cxxflags "-I${LLVM_PREFIX}/include ${MONO_cxx_include} ${MONO_cxx_std_version} ${MONO_stdlib} -fno-rtti -D__STDC_CONSTANT_MACROS -D__STD_FORMAT_MACROS -D__STDC_LIMIT_MACROS") + else() + set(llvm_cxxflags "-I${LLVM_PREFIX}/include ${MONO_cxx_include} ${MONO_cxx_std_version} ${MONO_stdlib} -fno-exceptions -fno-rtti -D__STDC_CONSTANT_MACROS -D__STD_FORMAT_MACROS -D__STDC_LIMIT_MACROS") + endif() set(llvm_includedir "${LLVM_PREFIX}/include") if(HOST_LINUX) @@ -694,6 +699,10 @@ if(LLVM_PREFIX) string(REPLACE "/EHs-c-" "" llvm_cxxflags "${llvm_cxxflags}") # /GR- already enabled and inherited from LLVM flags. Corresponds to -fno-rtti. set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${llvm_cxxflags}") + elseif(HOST_BROWSER) + # emscripten's handling of the different exception modes is complex, so having multiple flags + # passed during a single compile is undesirable. we need to set them elsewhere. + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${llvm_cxxflags} -fno-rtti") else() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${llvm_cxxflags} -fexceptions -fno-rtti") endif() diff --git a/src/mono/mono.proj b/src/mono/mono.proj index 26c7c470b3447d..789544b8a4bdc7 100644 --- a/src/mono/mono.proj +++ b/src/mono/mono.proj @@ -207,7 +207,7 @@ %(_ActualVersionLines.Identity) %(_ExpectedVersionLines.Identity) - @@ -416,10 +416,8 @@ <_MonoCMakeArgs Include="-DENABLE_ICALL_EXPORT=1"/> <_MonoCMakeArgs Include="-DENABLE_LAZY_GC_THREAD_CREATION=1"/> <_MonoCMakeArgs Include="-DENABLE_WEBCIL=1"/> - <_MonoCFLAGS Include="-fexceptions"/> <_MonoCFLAGS Condition="'$(MonoWasmThreads)' == 'true'" Include="-pthread"/> <_MonoCFLAGS Condition="'$(MonoWasmThreads)' == 'true'" Include="-D_GNU_SOURCE=1" /> - <_MonoCXXFLAGS Include="-fexceptions"/> <_MonoCXXFLAGS Condition="'$(MonoWasmThreads)' == 'true'" Include="-pthread"/> <_MonoCXXFLAGS Condition="'$(MonoWasmThreads)' == 'true'" Include="-D_GNU_SOURCE=1" /> @@ -433,8 +431,8 @@ - <_MonoCFLAGS Include="$(EscapedQuoteW)-I$([MSBuild]::NormalizePath('$(MonoProjectRoot)', 'wasi', 'include').Replace('\','/'))$(EscapedQuoteW)"/> diff --git a/src/mono/mono/mini/CMakeLists.txt b/src/mono/mono/mini/CMakeLists.txt index cca4fea9ca493b..b7d7b6d029a594 100644 --- a/src/mono/mono/mini/CMakeLists.txt +++ b/src/mono/mono/mini/CMakeLists.txt @@ -503,10 +503,14 @@ if(HOST_BROWSER) # This is the only source file which contains a c++ throw or catch add_library(mono-wasm-eh-js STATIC llvm-runtime.cpp) target_link_libraries (mono-wasm-eh-js PRIVATE monoapi eglib_api) + set_target_properties(mono-wasm-eh-js PROPERTIES COMPILE_FLAGS "-fexceptions") + set_target_properties(mono-wasm-eh-js PROPERTIES LINK_FLAGS "-fexceptions -s EXPORT_EXCEPTION_HANDLING_HELPERS=1") install(TARGETS mono-wasm-eh-js LIBRARY) + add_library(mono-wasm-eh-wasm STATIC llvm-runtime.cpp) target_link_libraries (mono-wasm-eh-wasm PRIVATE monoapi eglib_api) set_target_properties(mono-wasm-eh-wasm PROPERTIES COMPILE_FLAGS "-fwasm-exceptions") + set_target_properties(mono-wasm-eh-wasm PROPERTIES LINK_FLAGS "-fwasm-exceptions -s EXPORT_EXCEPTION_HANDLING_HELPERS=1") install(TARGETS mono-wasm-eh-wasm LIBRARY) endif() diff --git a/src/mono/mono/mini/interp/interp.c b/src/mono/mono/mini/interp/interp.c index 463c25a7972bd9..35c3021dcee13c 100644 --- a/src/mono/mono/mini/interp/interp.c +++ b/src/mono/mono/mini/interp/interp.c @@ -2707,21 +2707,8 @@ do_jit_call (ThreadContext *context, stackval *ret_sp, stackval *sp, InterpFrame interp_push_lmf (&ext, frame); if (mono_aot_mode == MONO_AOT_MODE_LLVMONLY_INTERP) { -#if JITERPRETER_ENABLE_SPECIALIZED_JIT_CALL - /* - * invoke jit_call_cb via a single indirect function call that dispatches to - * either a specialized JS implementation or a specialized WASM EH version - * see jiterpreter-jit-call.ts and do-jit-call.wat - * NOTE: the first argument must ALWAYS be jit_call_cb for the specialization. - * the actual implementation cannot verify this at runtime, so get it right - * this is faster than mono_llvm_cpp_catch_exception by avoiding the use of - * emscripten invoke_vi to find and invoke jit_call_cb indirectly - */ - jiterpreter_do_jit_call (jit_call_cb, &cb_data, &thrown); -#else /* Catch the exception thrown by the native code using a try-catch */ mono_llvm_cpp_catch_exception (jit_call_cb, &cb_data, &thrown); -#endif } else { jit_call_cb (&cb_data); } diff --git a/src/mono/mono/mini/interp/jiterpreter.c b/src/mono/mono/mini/interp/jiterpreter.c index 0dd3175cfb74ab..ceb6a66679647e 100644 --- a/src/mono/mono/mini/interp/jiterpreter.c +++ b/src/mono/mono/mini/interp/jiterpreter.c @@ -60,10 +60,6 @@ void jiterp_preserve_module (void); static gint32 jiterpreter_abort_counts[MINT_LASTOP + 1] = { 0 }; static int64_t jiterp_trace_bailout_counts[256] = { 0 }; -// This function pointer is used by interp.c to invoke jit_call_cb for exception handling purposes -// See jiterpreter-jit-call.ts mono_jiterp_do_jit_call_indirect -WasmDoJitCall jiterpreter_do_jit_call = mono_jiterp_do_jit_call_indirect; - // We disable this diagnostic because EMSCRIPTEN_KEEPALIVE makes it a false alarm, the keepalive // functions are being used externally. Having a bunch of prototypes is pointless since these // functions are not consumed by C anywhere else @@ -1021,20 +1017,6 @@ mono_jiterp_get_options_as_json () return mono_options_get_as_json (); } -EMSCRIPTEN_KEEPALIVE void -mono_jiterp_update_jit_call_dispatcher (WasmDoJitCall dispatcher) -{ - // If we received a 0 dispatcher that means the TS side failed to compile - // any kind of dispatcher - this likely indicates that content security policy - // blocked the use of Module.addFunction - if (!dispatcher) - dispatcher = (WasmDoJitCall)mono_llvm_cpp_catch_exception; - else if (((int)(void*)dispatcher)==-1) - dispatcher = mono_jiterp_do_jit_call_indirect; - - jiterpreter_do_jit_call = dispatcher; -} - EMSCRIPTEN_KEEPALIVE int mono_jiterp_object_has_component_size (MonoObject ** ppObj) { diff --git a/src/mono/mono/mini/interp/jiterpreter.h b/src/mono/mono/mini/interp/jiterpreter.h index 643f2c05478289..26b05f64a0c85f 100644 --- a/src/mono/mono/mini/interp/jiterpreter.h +++ b/src/mono/mono/mini/interp/jiterpreter.h @@ -5,12 +5,8 @@ #ifdef DISABLE_THREADS #define JITERPRETER_ENABLE_JIT_CALL_TRAMPOLINES 1 -// enables specialized mono_llvm_cpp_catch_exception replacement (see jiterpreter-jit-call.ts) -// works even if the jiterpreter is otherwise disabled. -#define JITERPRETER_ENABLE_SPECIALIZED_JIT_CALL 1 #else #define JITERPRETER_ENABLE_JIT_CALL_TRAMPOLINES 0 -#define JITERPRETER_ENABLE_SPECIALIZED_JIT_CALL 0 #endif // DISABLE_THREADS // mono_interp_tier_prepare_jiterpreter will return these special values if it doesn't @@ -49,9 +45,8 @@ __attribute__ ((__packed__, __aligned__(2))) // Keep in sync with JiterpreterTable in jiterpreter-enums.ts enum { JITERPRETER_TABLE_TRACE = 0, - JITERPRETER_TABLE_DO_JIT_CALL = 1, - JITERPRETER_TABLE_JIT_CALL = 2, - JITERPRETER_TABLE_INTERP_ENTRY_STATIC_0 = 3, + JITERPRETER_TABLE_JIT_CALL = 1, + JITERPRETER_TABLE_INTERP_ENTRY_STATIC_0 = 2, JITERPRETER_TABLE_INTERP_ENTRY_STATIC_1, JITERPRETER_TABLE_INTERP_ENTRY_STATIC_2, JITERPRETER_TABLE_INTERP_ENTRY_STATIC_3, @@ -165,11 +160,6 @@ mono_interp_invoke_wasm_jit_call_trampoline ( void *ftndesc, gboolean *thrown ); -extern void -mono_jiterp_do_jit_call_indirect ( - gpointer cb, gpointer arg, gboolean *out_thrown -); - #ifdef __MONO_MINI_INTERPRETER_INTERNALS_H__ extern void @@ -247,8 +237,6 @@ mono_jiterp_tlqueue_purge_all (gpointer item); #endif // __MONO_MINI_INTERPRETER_INTERNALS_H__ -extern WasmDoJitCall jiterpreter_do_jit_call; - #endif // HOST_BROWSER #endif // __MONO_MINI_JITERPRETER_H__ diff --git a/src/mono/mono/mini/llvm-runtime.cpp b/src/mono/mono/mini/llvm-runtime.cpp index a14b26bd739cf4..8159020ea9fe88 100644 --- a/src/mono/mono/mini/llvm-runtime.cpp +++ b/src/mono/mono/mini/llvm-runtime.cpp @@ -47,4 +47,22 @@ mono_llvm_cpp_catch_exception (MonoLLVMInvokeCallback cb, gpointer arg, gboolean } } +#ifdef HOST_WASM + +// https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html +void *__cxa_begin_catch (void *exceptionObject); +void __cxa_end_catch (void); + +EMSCRIPTEN_KEEPALIVE void +mono_jiterp_begin_catch (void *exception_object) { + __cxa_begin_catch (exception_object); +} + +EMSCRIPTEN_KEEPALIVE void +mono_jiterp_end_catch (void) { + return __cxa_end_catch (); +} + +#endif + } diff --git a/src/mono/wasm/build/WasmApp.Native.targets b/src/mono/wasm/build/WasmApp.Native.targets index 557db242b43fa2..d9595b16c2eb42 100644 --- a/src/mono/wasm/build/WasmApp.Native.targets +++ b/src/mono/wasm/build/WasmApp.Native.targets @@ -26,6 +26,8 @@ _ReadEmccProps + <_EmccDefaultFlags Condition="'$(WasmEnableExceptionHandling)' == 'false'">-fexceptions + <_EmccDefaultFlags Condition="'$(WasmEnableExceptionHandling)' != 'false'">-fwasm-exceptions <_EmccDefaultFlags Condition="'$(WasmEnableSIMD)' == 'true'">-msimd128 <_ExeExt Condition="$([MSBuild]::IsOSPlatform('WINDOWS'))">.exe true @@ -254,6 +256,7 @@ <_EmccLDFlags Include="$(EmccLinkOptimizationFlag)" /> <_EmccLDFlags Include="@(_EmccCommonFlags)" /> <_EmccLDFlags Include="-s EXPORT_ES6=1" /> + <_EmccLDFlags Condition="'$(WasmEnableExceptionHandling)' != 'false'" Include="-s EXPORT_EXCEPTION_HANDLING_HELPERS=1" /> <_DriverCDependencies Include="$(_WasmPInvokeHPath);$(_WasmICallTablePath)" /> <_DriverCDependencies Include="$(_DriverGenCPath)" Condition="'$(_DriverGenCNeeded)' == 'true'" /> diff --git a/src/mono/wasm/runtime/cwraps.ts b/src/mono/wasm/runtime/cwraps.ts index f8c283eae52cf1..e2ddc4b41b3853 100644 --- a/src/mono/wasm/runtime/cwraps.ts +++ b/src/mono/wasm/runtime/cwraps.ts @@ -118,7 +118,6 @@ const fn_signatures: SigLine[] = [ [true, "mono_jiterp_adjust_abort_count", "number", ["number", "number"]], [true, "mono_jiterp_register_jit_call_thunk", "void", ["number", "number"]], [true, "mono_jiterp_type_get_raw_value_size", "number", ["number"]], - [true, "mono_jiterp_update_jit_call_dispatcher", "void", ["number"]], [true, "mono_jiterp_get_signature_has_this", "number", ["number"]], [true, "mono_jiterp_get_signature_return_type", "number", ["number"]], [true, "mono_jiterp_get_signature_param_count", "number", ["number"]], @@ -147,6 +146,8 @@ const fn_signatures: SigLine[] = [ [true, "mono_jiterp_tlqueue_next", "number", ["number"]], [true, "mono_jiterp_tlqueue_add", "number", ["number", "number"]], [true, "mono_jiterp_tlqueue_clear", "void", ["number"]], + [true, "mono_jiterp_begin_catch", "void", ["number"]], + [true, "mono_jiterp_end_catch", "void", []], ...diagnostics_cwraps, ...legacy_interop_cwraps @@ -259,7 +260,6 @@ export interface t_Cwraps { mono_jiterp_get_options_version(): number; mono_jiterp_adjust_abort_count(opcode: number, delta: number): number; mono_jiterp_register_jit_call_thunk(cinfo: number, func: number): void; - mono_jiterp_update_jit_call_dispatcher(fn: number): void; mono_jiterp_get_signature_has_this(sig: VoidPtr): number; mono_jiterp_get_signature_return_type(sig: VoidPtr): MonoType; mono_jiterp_get_signature_param_count(sig: VoidPtr): number; @@ -291,6 +291,8 @@ export interface t_Cwraps { // returns new size of queue after add mono_jiterp_tlqueue_add(queue: number, value: VoidPtr): number; mono_jiterp_tlqueue_clear(queue: number): void; + mono_jiterp_begin_catch(ptr: number): void; + mono_jiterp_end_catch(): void; } const wrapped_c_functions: t_Cwraps = {}; diff --git a/src/mono/wasm/runtime/exports-binding.ts b/src/mono/wasm/runtime/exports-binding.ts index e3143c04f973be..8e856c35fa4c53 100644 --- a/src/mono/wasm/runtime/exports-binding.ts +++ b/src/mono/wasm/runtime/exports-binding.ts @@ -10,7 +10,7 @@ import { mono_wasm_bind_cs_function } from "./invoke-cs"; import { mono_wasm_bind_js_function, mono_wasm_invoke_bound_function, mono_wasm_invoke_import } from "./invoke-js"; import { mono_interp_tier_prepare_jiterpreter, mono_jiterp_free_method_data_js } from "./jiterpreter"; import { mono_interp_jit_wasm_entry_trampoline, mono_interp_record_interp_entry } from "./jiterpreter-interp-entry"; -import { mono_interp_jit_wasm_jit_call_trampoline, mono_interp_invoke_wasm_jit_call_trampoline, mono_interp_flush_jitcall_queue, mono_jiterp_do_jit_call_indirect } from "./jiterpreter-jit-call"; +import { mono_interp_jit_wasm_jit_call_trampoline, mono_interp_invoke_wasm_jit_call_trampoline, mono_interp_flush_jitcall_queue } from "./jiterpreter-jit-call"; import { mono_wasm_marshal_promise } from "./marshal-to-js"; import { mono_wasm_eventloop_has_unsettled_interop_promises } from "./pthreads/shared/eventloop"; import { mono_wasm_pthread_on_pthread_attached, mono_wasm_pthread_on_pthread_detached } from "./pthreads/worker"; @@ -88,7 +88,6 @@ export const mono_wasm_imports = [ mono_interp_jit_wasm_jit_call_trampoline, mono_interp_invoke_wasm_jit_call_trampoline, mono_interp_flush_jitcall_queue, - mono_jiterp_do_jit_call_indirect, mono_jiterp_free_method_data_js, mono_wasm_profiler_enter, diff --git a/src/mono/wasm/runtime/jiterpreter-enums.ts b/src/mono/wasm/runtime/jiterpreter-enums.ts index 1c98615cc57d27..1e65f7b3cec39e 100644 --- a/src/mono/wasm/runtime/jiterpreter-enums.ts +++ b/src/mono/wasm/runtime/jiterpreter-enums.ts @@ -56,10 +56,9 @@ export const enum JiterpNumberMode { // keep in sync with jiterpreter.h export const enum JiterpreterTable { - Trace, - DoJitCall, - JitCall, - InterpEntryStatic0, + Trace = 0, + JitCall = 1, + InterpEntryStatic0 = 2, InterpEntryStatic1, InterpEntryStatic2, InterpEntryStatic3, diff --git a/src/mono/wasm/runtime/jiterpreter-feature-detect.ts b/src/mono/wasm/runtime/jiterpreter-feature-detect.ts deleted file mode 100644 index b17f78483da444..00000000000000 --- a/src/mono/wasm/runtime/jiterpreter-feature-detect.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { - WasmValtype, WasmOpcode, WasmSimdOpcode -} from "./jiterpreter-opcodes"; -import { - WasmBuilder, -} from "./jiterpreter-support"; - -export function compileDoJitCall () : WebAssembly.Module { - const builder = new WasmBuilder(0); - builder.defineType("jit_call_cb", { - "cb_data": WasmValtype.i32, - }, WasmValtype.void, true); - builder.defineType("do_jit_call", { - "unused": WasmValtype.i32, - "cb_data": WasmValtype.i32, - "thrown": WasmValtype.i32, - }, WasmValtype.void, true); - builder.defineImportedFunction("i", "jit_call_cb", "jit_call_cb", true); - builder.defineFunction({ - type: "do_jit_call", - name: "do_jit_call_indirect", - export: true, - locals: {} - }, () => { - builder.block(WasmValtype.void, WasmOpcode.try_); - builder.local("cb_data"); - builder.callImport("jit_call_cb"); - builder.appendU8(WasmOpcode.catch_all); - builder.local("thrown"); - builder.i32_const(1); - builder.appendU8(WasmOpcode.i32_store); - builder.appendMemarg(0, 0); - builder.endBlock(); - builder.appendU8(WasmOpcode.end); - }); - // Magic number and version - builder.appendU32(0x6d736100); - builder.appendU32(1); - builder.generateTypeSection(); - builder.emitImportsAndFunctions(false); - const buffer = builder.getArrayView(); - return new WebAssembly.Module(buffer); -} - -export function compileSimdFeatureDetect () : WebAssembly.Module { - const builder = new WasmBuilder(0); - builder.defineType("test", {}, WasmValtype.void, true); - builder.defineFunction({ - type: "test", - name: "test", - export: true, - locals: {} - }, () => { - builder.i32_const(0); - builder.appendSimd(WasmSimdOpcode.i32x4_splat); - builder.appendU8(WasmOpcode.drop); - builder.appendU8(WasmOpcode.end); - }); - // Magic number and version - builder.appendU32(0x6d736100); - builder.appendU32(1); - builder.generateTypeSection(); - builder.emitImportsAndFunctions(false); - const buffer = builder.getArrayView(); - return new WebAssembly.Module(buffer); -} diff --git a/src/mono/wasm/runtime/jiterpreter-jit-call.ts b/src/mono/wasm/runtime/jiterpreter-jit-call.ts index 1af011764e7d8e..1b6514fa64fae1 100644 --- a/src/mono/wasm/runtime/jiterpreter-jit-call.ts +++ b/src/mono/wasm/runtime/jiterpreter-jit-call.ts @@ -1,9 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -import MonoWasmThreads from "consts:monoWasmThreads"; import { MonoType, MonoMethod } from "./types/internal"; -import { NativePointer, Int32Ptr, VoidPtr } from "./types/emscripten"; +import { NativePointer, VoidPtr } from "./types/emscripten"; import { Module, mono_assert, runtimeHelpers } from "./globals"; import { getU8, getI32_unaligned, getU32_unaligned, setU32_unchecked, receiveWorkerHeapViews @@ -14,12 +13,9 @@ import { _now, getWasmFunctionTable, applyOptions, recordFailure, getOptions, getCounter, modifyCounter, - jiterpreter_allocate_tables + getRawCwrap } from "./jiterpreter-support"; import { JiterpreterTable, JiterpCounter, JitQueue } from "./jiterpreter-enums"; -import { - compileDoJitCall -} from "./jiterpreter-feature-detect"; import cwraps from "./cwraps"; import { mono_log_error, mono_log_info } from "./logging"; import { utf8ToString } from "./strings"; @@ -189,9 +185,33 @@ export function mono_interp_invoke_wasm_jit_call_trampoline( const thunk = getWasmTableEntry(thunkIndex); try { thunk(ret_sp, sp, ftndesc, thrown); - } catch (exc) { + } catch (exc: any) { receiveWorkerHeapViews(); - setU32_unchecked(thrown, 1); + const exceptionTag = (Module)["asm"]["__cpp_exception"]; + const haveTag = exceptionTag instanceof (WebAssembly).Tag; + if ( + !haveTag || ( + (exc instanceof (WebAssembly).Exception) && + exc.is(exceptionTag) + ) + ) { + setU32_unchecked(thrown, 1); + + // Call begin_catch and then end_catch to clean it up. + if (haveTag) { + // Wasm EH is enabled, so we know that the current exception is a C++ exception + const ptr = exc.getArg(exceptionTag, 0); + cwraps.mono_jiterp_begin_catch(ptr); + cwraps.mono_jiterp_end_catch(); + } else if (typeof (exc) === "number") { + // emscripten JS exception + cwraps.mono_jiterp_begin_catch(exc); + cwraps.mono_jiterp_end_catch(); + } else + throw exc; + } else { + throw exc; + } } } @@ -256,97 +276,18 @@ export function mono_interp_jit_wasm_jit_call_trampoline( mono_interp_flush_jitcall_queue(); } -let doJitCallModule: WebAssembly.Module | undefined = undefined; - function getIsWasmEhSupported(): boolean { if (wasmEhSupported !== undefined) return wasmEhSupported; // Probe whether the current environment can handle wasm exceptions - try { - doJitCallModule = compileDoJitCall(); - wasmEhSupported = true; - } catch (exc) { - mono_log_info("Disabling WASM EH support due to JIT failure", exc); - wasmEhSupported = false; - } + wasmEhSupported = runtimeHelpers.featureWasmEh === true; + if (!wasmEhSupported) + mono_log_info("Disabling Jiterpreter Exception Handling"); return wasmEhSupported; } -// this is the generic entry point for do_jit_call that is registered by default at runtime startup. -// its job is to do initialization for the optimized do_jit_call path, which will either use a jitted -// wasm trampoline or will use a specialized JS function. -export function mono_jiterp_do_jit_call_indirect( - jit_call_cb: number, cb_data: VoidPtr, thrown: Int32Ptr -): void { - mono_assert(!runtimeHelpers.storeMemorySnapshotPending, "Attempting to set function into table during creation of memory snapshot"); - const table = getWasmFunctionTable(); - const jitCallCb = table.get(jit_call_cb); - - // This should perform better than the regular mono_llvm_cpp_catch_exception because the call target - // is statically known, not being pulled out of a table. - const do_jit_call_indirect_js = function (unused: number, _cb_data: VoidPtr, _thrown: Int32Ptr) { - try { - jitCallCb(_cb_data); - } catch (exc) { - receiveWorkerHeapViews(); - setU32_unchecked(_thrown, 1); - } - }; - - let failed = !getIsWasmEhSupported(); - if (!failed) { - // Wasm EH is supported which means doJitCallModule was loaded and compiled. - // Now that we have jit_call_cb, we can instantiate it. - try { - const instance = new WebAssembly.Instance(doJitCallModule!, { - i: { - jit_call_cb: jitCallCb, - }, - m: { - h: (Module).getMemory() - }, - }); - const impl = instance.exports.do_jit_call_indirect; - if (typeof (impl) !== "function") - throw new Error("Did not find exported do_jit_call handler"); - - // Make sure we've allocated the jiterpreter tables first because we need to use them - jiterpreter_allocate_tables(Module); - // We successfully instantiated it so we can register it as the new do_jit_call handler - const result = addWasmFunctionPointer(JiterpreterTable.DoJitCall, impl); - cwraps.mono_jiterp_update_jit_call_dispatcher(result); - failed = false; - } catch (exc) { - mono_log_error("failed to compile do_jit_call handler", exc); - failed = true; - } - // If wasm EH support was detected, a native wasm implementation of the dispatcher was already registered. - } - - if (failed) { - // This means that either wasm EH is unavailable or we failed to JIT the handler somehow - try { - // HACK: It's not safe to use Module.addFunction in a multithreaded scenario - if (MonoWasmThreads) { - // Use mono_llvm_cpp_catch_exception instead - cwraps.mono_jiterp_update_jit_call_dispatcher(0); - } else { - // Use the JS helper function defined up above - const result = Module.addFunction(do_jit_call_indirect_js, "viii"); - cwraps.mono_jiterp_update_jit_call_dispatcher(result); - } - } catch { - // CSP policy or some other problem could break Module.addFunction, so in that case, pass 0 - // This will cause the runtime to use mono_llvm_cpp_catch_exception - cwraps.mono_jiterp_update_jit_call_dispatcher(0); - } - } - - do_jit_call_indirect_js(jit_call_cb, cb_data, thrown); -} - export function mono_interp_flush_jitcall_queue(): void { const jitQueue : TrampolineInfo[] = []; let methodPtr = 0; @@ -378,6 +319,14 @@ export function mono_interp_flush_jitcall_queue(): void { "thrown": WasmValtype.i32, }, WasmValtype.void, true ); + builder.defineType("begin_catch", { + "ptr": WasmValtype.i32, + }, WasmValtype.void, true); + builder.defineType("end_catch", { + }, WasmValtype.void, true); + + builder.defineImportedFunction("i", "begin_catch", "begin_catch", true, getRawCwrap("mono_jiterp_begin_catch")); + builder.defineImportedFunction("i", "end_catch", "end_catch", true, getRawCwrap("mono_jiterp_end_catch")); } else builder.clear(0); @@ -451,6 +400,9 @@ export function mono_interp_flush_jitcall_queue(): void { for (let i = 0; i < trampImports.length; i++) builder.markImportAsUsed(trampImports[i][0]); + builder.markImportAsUsed("begin_catch"); + builder.markImportAsUsed("end_catch"); + builder._generateImportSection(false); // Function section @@ -830,7 +782,10 @@ function generate_wasm_body( // If the call threw a JS or wasm exception, set the thrown flag if (builder.options.enableWasmEh) { - builder.appendU8(WasmOpcode.catch_all); + builder.appendU8(WasmOpcode.catch_); + builder.appendULeb(builder.getTypeIndex("__cpp_exception")); + builder.callImport("begin_catch"); + builder.callImport("end_catch"); builder.local("thrown"); builder.i32_const(1); builder.appendU8(WasmOpcode.i32_store); diff --git a/src/mono/wasm/runtime/jiterpreter-support.ts b/src/mono/wasm/runtime/jiterpreter-support.ts index 06e1e6569ffc8e..ab828d5dc3a54a 100644 --- a/src/mono/wasm/runtime/jiterpreter-support.ts +++ b/src/mono/wasm/runtime/jiterpreter-support.ts @@ -112,6 +112,7 @@ export class WasmBuilder { this.stack = [new BlobBuilder()]; this.clear(constantSlotCount); this.cfg = new Cfg(this); + this.defineType("__cpp_exception", { "ptr": WasmValtype.i32 }, WasmValtype.void, true); } clear(constantSlotCount: number) { @@ -176,14 +177,31 @@ export class WasmBuilder { return current.getArrayView(false).slice(0, current.size); } + setImportFunction(name: string, value: Function) { + const imp = this.importedFunctions[name]; + if (!imp) + throw new Error("No import named " + name); + imp.func = value; + } + + getExceptionTag(): any { + const exceptionTag = (Module)["asm"]["__cpp_exception"]; + if (typeof (exceptionTag) !== "undefined") + mono_assert(exceptionTag instanceof (WebAssembly).Tag, () => `expected __cpp_exception export from dotnet.wasm to be WebAssembly.Tag but was ${exceptionTag}`); + return exceptionTag; + } + getWasmImports(): WebAssembly.Imports { const memory = (Module).getMemory(); mono_assert(memory instanceof WebAssembly.Memory, () => `expected heap import to be WebAssembly.Memory but was ${memory}`); + const exceptionTag = this.getExceptionTag(); const result: any = { c: this.getConstants(), m: { h: memory }, }; + if (exceptionTag) + result.x = { e: exceptionTag }; const importsToEmit = this.getImportsToEmit(); @@ -463,10 +481,14 @@ export class WasmBuilder { if (includeFunctionTable !== false) throw new Error("function table imports are disabled"); + const enableWasmEh = this.getExceptionTag() !== undefined; + // Import section this.beginSection(2); this.appendULeb( - 1 + importsToEmit.length + this.constantSlots.length + + 1 + // memory + (enableWasmEh ? 1 : 0) + // c++ exception tag + importsToEmit.length + this.constantSlots.length + ((includeFunctionTable !== false) ? 1 : 0) ); @@ -488,6 +510,7 @@ export class WasmBuilder { this.appendU8(0x00); // constant } + // import the native heap this.appendName("m"); this.appendName("h"); if (MonoWasmThreads) { @@ -505,6 +528,18 @@ export class WasmBuilder { this.appendULeb(0x01); } + if (enableWasmEh) { + // import the c++ exception tag + this.appendName("x"); + this.appendName("e"); + // tagtype + this.appendU8(0x04); + // attribute (exception) + this.appendU8(0x0); + // signature + this.appendULeb(this.getTypeIndex("__cpp_exception")); + } + if (includeFunctionTable !== false) { this.appendName("f"); this.appendName("f"); @@ -555,6 +590,13 @@ export class WasmBuilder { func.index = this.importedFunctionCount++; } + getTypeIndex(name: string) { + const type = this.functionTypes[name]; + if (!type) + throw new Error("No type named " + name); + return type[0]; + } + defineFunction( options: { type: string, @@ -567,7 +609,7 @@ export class WasmBuilder { index: this.functions.length, name: options.name, typeName: options.type, - typeIndex: this.functionTypes[options.type][0], + typeIndex: this.getTypeIndex(options.type), export: options.export, locals: options.locals, generator, @@ -1990,8 +2032,6 @@ export function jiterpreter_allocate_tables(module: any) { if (options.enableStats) mono_log_info(`Allocated ${totalSize} function table entries for jiterpreter, bringing total table size to ${wasmTable.length}`); base = jiterpreter_allocate_table(JiterpreterTable.Trace, base, traceTableSize, getRawCwrap("mono_jiterp_placeholder_trace")); - // FIXME: Install mono_jiterp_do_jit_call_indirect somehow. - base = jiterpreter_allocate_table(JiterpreterTable.DoJitCall, base, 1, getRawCwrap("mono_llvm_cpp_catch_exception")); base = jiterpreter_allocate_table(JiterpreterTable.JitCall, base, jitCallTableSize, getRawCwrap("mono_jiterp_placeholder_jit_call")); for (let table = JiterpreterTable.InterpEntryStatic0; table <= JiterpreterTable.LAST; table++) base = jiterpreter_allocate_table(table, base, interpEntryTableSize, wasmTable.get(cwraps.mono_jiterp_get_interp_entry_func(table))); diff --git a/src/mono/wasm/runtime/jiterpreter-trace-generator.ts b/src/mono/wasm/runtime/jiterpreter-trace-generator.ts index 2c7bb17ef9e82c..616792ffec25ba 100644 --- a/src/mono/wasm/runtime/jiterpreter-trace-generator.ts +++ b/src/mono/wasm/runtime/jiterpreter-trace-generator.ts @@ -28,7 +28,6 @@ import { getMemberOffset, isZeroPageReserved, CfgBranchType, append_safepoint, modifyCounter, simdFallbackCounters, } from "./jiterpreter-support"; -import { compileSimdFeatureDetect } from "./jiterpreter-feature-detect"; import { sizeOfDataItem, sizeOfV128, sizeOfStackval, @@ -57,7 +56,7 @@ import { simdLoadTable, simdStoreTable, } from "./jiterpreter-tables"; import { mono_log_error, mono_log_info } from "./logging"; -import { mono_assert } from "./globals"; +import { mono_assert, runtimeHelpers } from "./globals"; // indexPlusOne so that ip[1] in the interpreter becomes getArgU16(ip, 1) function getArgU16(ip: MintOpcodePtr, indexPlusOne: number) { @@ -3268,15 +3267,9 @@ function getIsWasmSimdSupported(): boolean { if (wasmSimdSupported !== undefined) return wasmSimdSupported; - // Probe whether the current environment can handle wasm v128 opcodes. - try { - // Load and compile a test module that uses i32x4.splat. See wasm-simd-feature-detect.wat/wasm - const module = compileSimdFeatureDetect(); - wasmSimdSupported = !!module; - } catch (exc) { - mono_log_info("Disabling WASM SIMD support due to JIT failure", exc); - wasmSimdSupported = false; - } + wasmSimdSupported = runtimeHelpers.featureWasmSimd === true; + if (!wasmSimdSupported) + mono_log_info("Disabling Jiterpreter SIMD"); return wasmSimdSupported; } diff --git a/src/mono/wasm/runtime/startup.ts b/src/mono/wasm/runtime/startup.ts index 028df7aa069a22..d481f3b72d9f06 100644 --- a/src/mono/wasm/runtime/startup.ts +++ b/src/mono/wasm/runtime/startup.ts @@ -503,11 +503,13 @@ async function instantiate_wasm_module( } async function ensureUsedWasmFeatures() { + runtimeHelpers.featureWasmSimd = await loaderHelpers.simd(); + runtimeHelpers.featureWasmEh = await loaderHelpers.exceptions(); if (linkerWasmEnableSIMD) { - mono_assert(await loaderHelpers.simd(), "This browser/engine doesn't support WASM SIMD. Please use a modern version. See also https://aka.ms/dotnet-wasm-features"); + mono_assert(runtimeHelpers.featureWasmSimd, "This browser/engine doesn't support WASM SIMD. Please use a modern version. See also https://aka.ms/dotnet-wasm-features"); } if (linkerWasmEnableEH) { - mono_assert(await loaderHelpers.exceptions(), "This browser/engine doesn't support WASM exception handling. Please use a modern version. See also https://aka.ms/dotnet-wasm-features"); + mono_assert(runtimeHelpers.featureWasmEh, "This browser/engine doesn't support WASM exception handling. Please use a modern version. See also https://aka.ms/dotnet-wasm-features"); } } @@ -532,10 +534,6 @@ async function mono_wasm_before_memory_snapshot() { else throw new Error(`Expected environment variable '${k}' to be a string but it was ${typeof v}: '${v}'`); } - if (runtimeHelpers.config.startupMemoryCache) { - // disable the trampoline for now, we will re-enable it after we stored the snapshot - cwraps.mono_jiterp_update_jit_call_dispatcher(0); - } if (runtimeHelpers.config.runtimeOptions) mono_wasm_set_runtime_options(runtimeHelpers.config.runtimeOptions); @@ -560,8 +558,6 @@ async function mono_wasm_before_memory_snapshot() { // we didn't have snapshot yet and the feature is enabled. Take snapshot now. if (runtimeHelpers.config.startupMemoryCache) { - // this would install the mono_jiterp_do_jit_call_indirect - cwraps.mono_jiterp_update_jit_call_dispatcher(-1); await storeMemorySnapshot(localHeapViewU8().buffer); runtimeHelpers.storeMemorySnapshotPending = false; } diff --git a/src/mono/wasm/runtime/types/internal.ts b/src/mono/wasm/runtime/types/internal.ts index 751f2795e45452..cb980969a2bf02 100644 --- a/src/mono/wasm/runtime/types/internal.ts +++ b/src/mono/wasm/runtime/types/internal.ts @@ -203,6 +203,9 @@ export type RuntimeHelpers = { afterOnRuntimeInitialized: PromiseAndController, afterPostRun: PromiseAndController, + featureWasmEh: boolean, + featureWasmSimd: boolean, + //core stringify_as_error_with_stack?: (error: any) => string, instantiate_asset: (asset: AssetEntry, url: string, bytes: Uint8Array) => void, diff --git a/src/mono/wasm/wasm.proj b/src/mono/wasm/wasm.proj index 539def55f13130..818641337c2d27 100644 --- a/src/mono/wasm/wasm.proj +++ b/src/mono/wasm/wasm.proj @@ -365,6 +365,7 @@ $(ArtifactsObjDir)wasm/pinvoke-table.h $(ArtifactsObjDir)wasm/wasm_m2n_invoke.g.h + -g -Os -s -DDEBUG=1 -DENABLE_AOT_PROFILER=1 -DENABLE_BROWSER_PROFILER=1 -Oz -DENABLE_BROWSER_PROFILER=1