From 5c5d0c7059c618615c75e54d914b45478bc4223f Mon Sep 17 00:00:00 2001 From: Zoltan Varga Date: Tue, 21 Nov 2023 12:28:08 -0500 Subject: [PATCH] Add a optional mode on wasm which doesn't depend on c++ unwinding so it works on wasi. * Add an constant option to turn on this functionality. * Add global flag variable to signal whenever manual unwinding is in progress. * Modify the EH code to check this flag. * Add new helper functions to either throw/catch a c++ exception or set/check the flag. * Modify AOTed code to check the flag and either return, or branch to a landing pad. --- src/mono/mono/mini/aot-compiler.c | 5 + src/mono/mono/mini/aot-runtime.c | 1 + src/mono/mono/mini/interp/interp.c | 59 +++++--- src/mono/mono/mini/llvm-runtime.cpp | 2 + src/mono/mono/mini/mini-exceptions.c | 89 +++++++++++- src/mono/mono/mini/mini-llvm.c | 196 +++++++++++++++++++-------- src/mono/mono/mini/mini-runtime.c | 10 +- src/mono/mono/mini/mini.h | 5 + src/mono/mono/mini/patch-info.h | 1 + src/mono/mono/utils/options-def.h | 6 + 10 files changed, 296 insertions(+), 78 deletions(-) diff --git a/src/mono/mono/mini/aot-compiler.c b/src/mono/mono/mini/aot-compiler.c index bc9e690f05fe0a..87e2889eb06339 100644 --- a/src/mono/mono/mini/aot-compiler.c +++ b/src/mono/mono/mini/aot-compiler.c @@ -7301,6 +7301,7 @@ encode_patch (MonoAotCompile *acfg, MonoJumpInfo *patch_info, guint8 *buf, guint encode_field_info (acfg, patch_info->data.field, p, &p); break; case MONO_PATCH_INFO_INTERRUPTION_REQUEST_FLAG: + case MONO_PATCH_INFO_LLVMONLY_DO_UNWIND_FLAG: break; case MONO_PATCH_INFO_PROFILER_ALLOCATION_COUNT: case MONO_PATCH_INFO_PROFILER_CLAUSE_COUNT: @@ -14586,6 +14587,10 @@ add_preinit_got_slots (MonoAotCompile *acfg) ji->type = MONO_PATCH_INFO_GC_SAFE_POINT_FLAG; add_preinit_slot (acfg, ji); + ji = (MonoJumpInfo *)mono_mempool_alloc0 (acfg->mempool, sizeof (MonoJumpInfo)); + ji->type = MONO_PATCH_INFO_LLVMONLY_DO_UNWIND_FLAG; + add_preinit_slot (acfg, ji); + if (!acfg->aot_opts.llvm_only) { for (i = 0; i < TLS_KEY_NUM; i++) { ji = (MonoJumpInfo *)mono_mempool_alloc0 (acfg->mempool, sizeof (MonoJumpInfo)); diff --git a/src/mono/mono/mini/aot-runtime.c b/src/mono/mono/mini/aot-runtime.c index 0faba078d6dacc..48f6e44868971f 100644 --- a/src/mono/mono/mini/aot-runtime.c +++ b/src/mono/mono/mini/aot-runtime.c @@ -3969,6 +3969,7 @@ decode_patch (MonoAotModule *aot_module, MonoMemPool *mp, MonoJumpInfo *ji, guin case MONO_PATCH_INFO_GC_NURSERY_BITS: case MONO_PATCH_INFO_PROFILER_ALLOCATION_COUNT: case MONO_PATCH_INFO_PROFILER_CLAUSE_COUNT: + case MONO_PATCH_INFO_LLVMONLY_DO_UNWIND_FLAG: break; case MONO_PATCH_INFO_SPECIFIC_TRAMPOLINE_LAZY_FETCH_ADDR: ji->data.uindex = decode_value (p, &p); diff --git a/src/mono/mono/mini/interp/interp.c b/src/mono/mono/mini/interp/interp.c index c35a61ded1167f..294570483dd54a 100644 --- a/src/mono/mono/mini/interp/interp.c +++ b/src/mono/mono/mini/interp/interp.c @@ -446,12 +446,10 @@ interp_free_context (gpointer ctx) g_free (context); } -/* Continue unwinding if there is an exception that needs to be handled in an AOTed frame above us */ -static void -check_pending_unwind (ThreadContext *context) +static gboolean +need_native_unwind (ThreadContext *context) { - if (context->has_resume_state && !context->handler_frame) - mono_llvm_cpp_throw_exception (); + return context->has_resume_state && !context->handler_frame; } void @@ -2101,7 +2099,7 @@ interp_runtime_invoke (MonoMethod *method, void *obj, void **params, MonoObject * EH processing will continue when control returns to the interpreter. */ if (mono_aot_mode == MONO_AOT_MODE_LLVMONLY_INTERP) - mono_llvm_cpp_throw_exception (); + mono_llvm_start_native_unwind (); return NULL; } // The return value is at the bottom of the stack @@ -2199,12 +2197,18 @@ interp_entry (InterpEntryData *data) if (rmethod->needs_thread_attach) mono_threads_detach_coop (orig_domain, &attach_cookie); - check_pending_unwind (context); + if (need_native_unwind (context)) { + mono_llvm_start_native_unwind (); + return; + } if (mono_llvm_only) { - if (context->has_resume_state) + if (context->has_resume_state) { /* The exception will be handled in a frame above us */ - mono_llvm_cpp_throw_exception (); + mono_llvm_start_native_unwind (); + // FIXME: Set dummy return value ? + return; + } } else { g_assert (!context->has_resume_state); } @@ -2357,7 +2361,7 @@ typedef struct { MonoFtnDesc ftndesc; } JitCallCbData; -/* Callback called by mono_llvm_cpp_catch_exception () */ +/* Callback called by mono_llvm_catch_exception () */ static void jit_call_cb (gpointer arg) { @@ -2713,7 +2717,7 @@ do_jit_call (ThreadContext *context, stackval *ret_sp, stackval *sp, InterpFrame if (mono_aot_mode == MONO_AOT_MODE_LLVMONLY_INTERP) { /* Catch the exception thrown by the native code using a try-catch */ - mono_llvm_cpp_catch_exception (jit_call_cb, &cb_data, &thrown); + mono_llvm_catch_exception (jit_call_cb, &cb_data, &thrown); } else { jit_call_cb (&cb_data); } @@ -3080,7 +3084,10 @@ interp_entry_from_trampoline (gpointer ccontext_untyped, gpointer rmethod_untype if (rmethod->needs_thread_attach) mono_threads_detach_coop (orig_domain, &attach_cookie); - check_pending_unwind (context); + if (need_native_unwind (context)) { + mono_llvm_start_native_unwind (); + return; + } /* Write back the return value */ /* 'frame' is still valid */ @@ -7962,7 +7969,10 @@ interp_run_finally (StackFrameInfo *frame, int clause_index) iframe->next_free = next_free; iframe->state.ip = state_ip; - check_pending_unwind (context); + if (need_native_unwind (context)) { + mono_llvm_start_native_unwind (); + return TRUE; + } if (context->has_resume_state) { return TRUE; @@ -8015,7 +8025,10 @@ interp_run_filter (StackFrameInfo *frame, MonoException *ex, int clause_index, g context->stack_pointer = (guchar*)child_frame.stack; - check_pending_unwind (context); + if (need_native_unwind (context)) { + mono_llvm_start_native_unwind (); + return TRUE; + } /* ENDFILTER stores the result into child_frame->retval */ return retval.data.i ? TRUE : FALSE; @@ -8159,7 +8172,10 @@ interp_run_clause_with_il_state (gpointer il_state_ptr, int clause_index, MonoOb memset (sp, 0, (guint8*)context->stack_pointer - (guint8*)sp); context->stack_pointer = (guchar*)sp; - check_pending_unwind (context); + if (need_native_unwind (context)) { + mono_llvm_start_native_unwind (); + return FALSE; + } return context->has_resume_state; } @@ -8739,7 +8755,12 @@ mono_jiterp_ld_delegate_method_ptr (gpointer *destination, MonoDelegate **source MONO_ALWAYS_INLINE void mono_jiterp_check_pending_unwind (ThreadContext *context) { - return check_pending_unwind (context); + if (need_native_unwind (context)) { + // FIXME: Caller needs to check this + if (mono_opt_llvm_emulate_unwind) + g_assert_not_reached (); + mono_llvm_start_native_unwind (); + } } MONO_ALWAYS_INLINE void * @@ -8804,9 +8825,11 @@ mono_jiterp_interp_entry (JiterpEntryData *_data, void *res) mono_jiterp_check_pending_unwind (header.context); if (mono_llvm_only) { - if (header.context->has_resume_state) + if (header.context->has_resume_state) { /* The exception will be handled in a frame above us */ - mono_llvm_cpp_throw_exception (); + mono_llvm_start_native_unwind (); + return; + } } else { g_assert (!header.context->has_resume_state); } diff --git a/src/mono/mono/mini/llvm-runtime.cpp b/src/mono/mono/mini/llvm-runtime.cpp index 8159020ea9fe88..b57a442578e2e6 100644 --- a/src/mono/mono/mini/llvm-runtime.cpp +++ b/src/mono/mono/mini/llvm-runtime.cpp @@ -14,6 +14,8 @@ extern "C" { + extern void mono_wasm_print_stack_trace (void); + // Only called by C functions or LLVM IR generated by Mono cross compiler. // 'mono_llvm_cpp_throw_exception': function assumed not to throw an exception but does. The function is extern "C" and /EHc was specified. MONO_DISABLE_WARNING(4297) diff --git a/src/mono/mono/mini/mini-exceptions.c b/src/mono/mono/mini/mini-exceptions.c index 1b6f17555dbd59..a3d88a1fc0e5f5 100644 --- a/src/mono/mono/mini/mini-exceptions.c +++ b/src/mono/mono/mini/mini-exceptions.c @@ -79,6 +79,7 @@ #include "mini-llvm.h" #include "aot-runtime.h" #include "mini-runtime.h" +#include "llvm-runtime.h" #include "interp/interp.h" #ifdef ENABLE_LLVM @@ -117,6 +118,14 @@ static gpointer throw_corlib_exception_func; static MonoFtnPtrEHCallback ftnptr_eh_callback; +/* + * Global flag signaling whenever native unwinding is in progress. + * Accessed directly from AOTed code. + * When set, native code should return to their caller until the unwinding + * is finished. + */ +int mono_llvmonly_do_unwind_flag; + static void mono_walk_stack_full (MonoJitStackWalk func, MonoContext *start_ctx, MonoJitTlsData *jit_tls, MonoLMF *lmf, MonoUnwindOptions unwind_options, gpointer user_data, gboolean crash_context); static void mono_raise_exception_with_ctx (MonoException *exc, MonoContext *ctx); static void mono_runtime_walk_stack_with_ctx (MonoJitStackWalk func, MonoContext *start_ctx, MonoUnwindOptions unwind_options, void *user_data); @@ -3431,7 +3440,7 @@ mini_llvmonly_throw_exception (MonoObject *ex) llvmonly_setup_exception (ex, FALSE); /* Unwind back to either an AOTed frame or to the interpreter */ - mono_llvm_cpp_throw_exception (); + mono_llvm_start_native_unwind (); } void @@ -3445,7 +3454,8 @@ mini_llvmonly_rethrow_exception (MonoObject *ex) mono_handle_exception_internal (&ctx, ex, FALSE, &out_ji); - mono_llvm_cpp_throw_exception (); + /* Unwind back to either an AOTed frame or to the interpreter */ + mono_llvm_start_native_unwind (); } void @@ -3520,11 +3530,16 @@ mini_llvmonly_resume_exception_il_state (MonoLMF *lmf, gpointer info) MonoMethodILState *il_state = (MonoMethodILState *)info; MonoJitTlsData *jit_tls = mono_get_jit_tls (); +#ifdef HOST_WASM + //mono_wasm_print_stack_trace (); +#endif + //print_lmf_chain (lmf); if (jit_tls->resume_state.il_state != il_state) { /* Call from an AOT method which doesn't catch this exception, continue unwinding */ - mono_llvm_cpp_throw_exception (); + mono_llvm_start_native_unwind (); + return; } jit_tls->resume_state.il_state = NULL; @@ -3537,9 +3552,11 @@ mini_llvmonly_resume_exception_il_state (MonoLMF *lmf, gpointer info) int clause_index = jit_tls->resume_state.clause_index; gboolean r = mini_get_interp_callbacks ()->run_clause_with_il_state (il_state, clause_index, ex_obj, NULL); - if (r) + if (r) { /* Another exception thrown, continue unwinding */ - mono_llvm_cpp_throw_exception (); + mono_llvm_start_native_unwind (); + return; + } } /* @@ -3630,3 +3647,65 @@ mono_debug_personality (void) g_assert_not_reached (); } #endif + +/* + * mono_llvm_catch_exception: + * + * Call CB(ARG), catching native exceptions. + * Set OUT_THROW to true if a native exceptions was thrown. + */ +void +mono_llvm_catch_exception (MonoLLVMInvokeCallback cb, gpointer arg, gboolean *out_thrown) +{ + *out_thrown = FALSE; + + if (mono_opt_llvm_emulate_unwind) { +#ifdef DISABLE_THREADS + // FIXME: The flag needs to be thread local + g_assert_not_reached (); +#endif + // FIXME: + //g_assert (!mono_llvmonly_do_unwind_flag); + mono_llvmonly_do_unwind_flag = FALSE; + cb (arg); + if (mono_llvmonly_do_unwind_flag) { + mono_llvmonly_do_unwind_flag = FALSE; + *out_thrown = TRUE; + } + } else { + mono_llvm_cpp_catch_exception (cb, arg, out_thrown); + } +} + +/* + * mono_llvm_start_native_unwind: + * + * Start native unwinding. + * This will either throw a c++ exception or set a flag. + * If this returns, the caller should manually unwind by + * returning to its caller. + */ +void +mono_llvm_start_native_unwind (void) +{ + if (mono_opt_llvm_emulate_unwind) { + g_assert (!mono_llvmonly_do_unwind_flag); + mono_llvmonly_do_unwind_flag = TRUE; + } else { + mono_llvm_cpp_throw_exception (); + } +} + +/* + * mono_llvm_stop_native_unwind: + * + * Stop native unwinding. + */ +void +mono_llvm_stop_native_unwind (void) +{ + if (mono_opt_llvm_emulate_unwind) { + g_assert (mono_llvmonly_do_unwind_flag); + mono_llvmonly_do_unwind_flag = FALSE; + } +} diff --git a/src/mono/mono/mini/mini-llvm.c b/src/mono/mono/mini/mini-llvm.c index 6ba6a106d34a2a..07cc8892cf3112 100644 --- a/src/mono/mono/mini/mini-llvm.c +++ b/src/mono/mono/mini/mini-llvm.c @@ -129,6 +129,7 @@ typedef struct { LLVMValueRef sentinel_exception; LLVMValueRef gc_safe_point_flag_var; LLVMValueRef interrupt_flag_var; + LLVMValueRef do_unwind_flag_var; void *di_builder, *cu; GHashTable *objc_selector_to_var; GPtrArray *cfgs; @@ -2018,21 +2019,34 @@ get_aotconst_module (MonoLLVMModule *module, LLVMBuilderRef builder, MonoJumpInf if (out_got_offset) *out_got_offset = got_offset; - if (module->static_link && type == MONO_PATCH_INFO_GC_SAFE_POINT_FLAG) { - if (!module->gc_safe_point_flag_var) { - const char *symbol = "mono_polling_required"; - module->gc_safe_point_flag_var = LLVMAddGlobal (module->lmodule, llvm_type, symbol); - LLVMSetLinkage (module->gc_safe_point_flag_var, LLVMExternalLinkage); - } - return LLVMBuildBitCast (builder, module->gc_safe_point_flag_var, llvm_type, ""); - } - if (module->static_link && type == MONO_PATCH_INFO_INTERRUPTION_REQUEST_FLAG) { - if (!module->interrupt_flag_var) { - const char *symbol = "mono_thread_interruption_request_flag"; - module->interrupt_flag_var = LLVMAddGlobal (module->lmodule, llvm_type, symbol); - LLVMSetLinkage (module->interrupt_flag_var, LLVMExternalLinkage); + if (module->static_link) { + /* Access some C globals directly */ + switch (type) { + case MONO_PATCH_INFO_GC_SAFE_POINT_FLAG: + if (!module->gc_safe_point_flag_var) { + const char *symbol = "mono_polling_required"; + module->gc_safe_point_flag_var = LLVMAddGlobal (module->lmodule, llvm_type, symbol); + LLVMSetLinkage (module->gc_safe_point_flag_var, LLVMExternalLinkage); + } + return LLVMBuildBitCast (builder, module->gc_safe_point_flag_var, llvm_type, ""); + case MONO_PATCH_INFO_INTERRUPTION_REQUEST_FLAG: + if (!module->interrupt_flag_var) { + const char *symbol = "mono_thread_interruption_request_flag"; + module->interrupt_flag_var = LLVMAddGlobal (module->lmodule, llvm_type, symbol); + LLVMSetLinkage (module->interrupt_flag_var, LLVMExternalLinkage); + } + return LLVMBuildBitCast (builder, module->interrupt_flag_var, llvm_type, ""); + case MONO_PATCH_INFO_LLVMONLY_DO_UNWIND_FLAG: + // FIXME: When threads are enabled, the variable needs to be TLS + if (!module->do_unwind_flag_var) { + const char *symbol = "mono_llvmonly_do_unwind_flag"; + module->do_unwind_flag_var = LLVMAddGlobal (module->lmodule, llvm_type, symbol); + LLVMSetLinkage (module->do_unwind_flag_var, LLVMExternalLinkage); + } + return LLVMBuildBitCast (builder, module->do_unwind_flag_var, llvm_type, ""); + default: + break; } - return LLVMBuildBitCast (builder, module->interrupt_flag_var, llvm_type, ""); } Address *addr = (Address*)g_hash_table_lookup (module->aotconst_vars, GINT_TO_POINTER (got_offset)); @@ -2457,6 +2471,43 @@ set_invariant_load_flag (LLVMValueRef v) LLVMSetMetadata (v, md_kind, LLVMMDNode (&md_arg, 1)); } +static void +emit_check_unwind_flag (EmitContext *ctx, LLVMBuilderRef builder, LLVMBasicBlockRef ex_bb, LLVMBasicBlockRef noex_bb) +{ + LLVMValueRef flag_var = get_aotconst (ctx, MONO_PATCH_INFO_LLVMONLY_DO_UNWIND_FLAG, NULL, pointer_type (i4_t)); + LLVMValueRef flag = LLVMBuildLoad2 (builder, i4_t, flag_var, ""); + + LLVMValueRef cmp = LLVMBuildICmp (builder, LLVMIntNE, flag, const_int32 (0), ""); + LLVMBuildCondBr (builder, cmp, ex_bb, noex_bb); +} + +static void +emit_check_unwind_flag_ret (EmitContext *ctx, LLVMBuilderRef *builder_ref, LLVMBasicBlockRef *noex_bb_ref) +{ + LLVMBasicBlockRef ex_bb = gen_bb (ctx, "CALL_EX_BB"); + LLVMBasicBlockRef noex_bb = gen_bb (ctx, "CALL_NOEX_BB"); + LLVMBuilderRef builder; + + emit_check_unwind_flag (ctx, *builder_ref, ex_bb, noex_bb); + + builder = ctx->builder = create_builder (ctx); + LLVMPositionBuilderAtEnd (ctx->builder, ex_bb); + + LLVMTypeRef ret_type = LLVMGetReturnType (ctx->lmethod_type); + + if (ret_type == LLVMVoidType ()) + LLVMBuildRetVoid (builder); + else + LLVMBuildRet (builder, LLVMGetUndef (ret_type)); + + builder = ctx->builder = create_builder (ctx); + LLVMPositionBuilderAtEnd (ctx->builder, noex_bb); + + *builder_ref = builder; + if (noex_bb_ref) + *noex_bb_ref = noex_bb; +} + /* * emit_call: * @@ -2492,51 +2543,78 @@ emit_call (EmitContext *ctx, MonoBasicBlock *bb, LLVMBuilderRef *builder_ref, LL if (lpad_bb) { LLVMBasicBlockRef noex_bb = gen_bb (ctx, "CALL_NOEX_BB"); - /* Use an invoke */ - lcall = LLVMBuildInvoke2 (builder, sig, callee, args, pindex, noex_bb, lpad_bb, ""); + if (mono_opt_llvm_emulate_unwind) { + lcall = LLVMBuildCall2 (builder, sig, callee, args, pindex, ""); - builder = ctx->builder = create_builder (ctx); - LLVMPositionBuilderAtEnd (ctx->builder, noex_bb); + emit_check_unwind_flag (ctx, builder, lpad_bb, noex_bb); + + builder = ctx->builder = create_builder (ctx); + LLVMPositionBuilderAtEnd (ctx->builder, noex_bb); + + ctx->bblocks [bb->block_num].end_bblock = noex_bb; + } else { + /* Use an invoke */ + lcall = LLVMBuildInvoke2 (builder, sig, callee, args, pindex, noex_bb, lpad_bb, ""); + builder = ctx->builder = create_builder (ctx); + LLVMPositionBuilderAtEnd (ctx->builder, noex_bb); - ctx->bblocks [bb->block_num].end_bblock = noex_bb; + ctx->bblocks [bb->block_num].end_bblock = noex_bb; + } } } - } else { - int clause_index = get_handler_clause (cfg, bb); - if (clause_index != -1) { - MonoMethodHeader *header = cfg->header; - MonoExceptionClause *ec = &header->clauses [clause_index]; - MonoBasicBlock *tblock; - LLVMBasicBlockRef ex_bb, noex_bb; + if (!lcall) { + lcall = LLVMBuildCall2 (builder, sig, callee, args, pindex, ""); + ctx->builder = builder; - /* - * Have to use an invoke instead of a call, branching to the - * handler bblock of the clause containing this bblock. - */ + if (mono_opt_llvm_emulate_unwind) { + LLVMBasicBlockRef noex_bb; - g_assert (ec->flags == MONO_EXCEPTION_CLAUSE_NONE || ec->flags == MONO_EXCEPTION_CLAUSE_FINALLY || ec->flags == MONO_EXCEPTION_CLAUSE_FAULT); + emit_check_unwind_flag_ret (ctx, &builder, &noex_bb); - tblock = cfg->cil_offset_to_bb [ec->handler_offset]; - g_assert (tblock); + if (bb) + ctx->bblocks [bb->block_num].end_bblock = noex_bb; + } + } - ctx->bblocks [tblock->block_num].invoke_target = TRUE; + if (builder_ref) + *builder_ref = ctx->builder; - ex_bb = get_bb (ctx, tblock); + return lcall; + } - noex_bb = gen_bb (ctx, "NOEX_BB"); + int clause_index = get_handler_clause (cfg, bb); - /* Use an invoke */ - lcall = LLVMBuildInvoke2 (builder, sig, callee, args, pindex, noex_bb, ex_bb, ""); + if (clause_index != -1) { + MonoMethodHeader *header = cfg->header; + MonoExceptionClause *ec = &header->clauses [clause_index]; + MonoBasicBlock *tblock; + LLVMBasicBlockRef ex_bb, noex_bb; - builder = ctx->builder = create_builder (ctx); - LLVMPositionBuilderAtEnd (ctx->builder, noex_bb); + /* + * Have to use an invoke instead of a call, branching to the + * handler bblock of the clause containing this bblock. + */ - ctx->bblocks [bb->block_num].end_bblock = noex_bb; - } - } + g_assert (ec->flags == MONO_EXCEPTION_CLAUSE_NONE || ec->flags == MONO_EXCEPTION_CLAUSE_FINALLY || ec->flags == MONO_EXCEPTION_CLAUSE_FAULT); + + tblock = cfg->cil_offset_to_bb [ec->handler_offset]; + g_assert (tblock); + + ctx->bblocks [tblock->block_num].invoke_target = TRUE; + + ex_bb = get_bb (ctx, tblock); + + noex_bb = gen_bb (ctx, "NOEX_BB"); - if (!lcall) { + /* Use an invoke */ + lcall = LLVMBuildInvoke2 (builder, sig, callee, args, pindex, noex_bb, ex_bb, ""); + + builder = ctx->builder = create_builder (ctx); + LLVMPositionBuilderAtEnd (ctx->builder, noex_bb); + + ctx->bblocks [bb->block_num].end_bblock = noex_bb; + } else { lcall = LLVMBuildCall2 (builder, sig, callee, args, pindex, ""); ctx->builder = builder; } @@ -3445,13 +3523,17 @@ emit_init_func (MonoLLVMModule *module, MonoAotInitSubtype subtype) callee = get_aotconst_module (module, builder, MONO_PATCH_INFO_JIT_ICALL_ID, GINT_TO_POINTER (MONO_JIT_ICALL_mini_llvm_init_method), pointer_type (icall_sig), NULL, NULL); LLVMBuildCall2 (builder, icall_sig, callee, args, LLVMCountParamTypes (icall_sig), ""); - /* - * Set the inited flag - * This is already done by the LLVM methods themselves, but its needed by JITted methods. - */ - indexes [0] = const_int32 (0); - indexes [1] = index_var; - LLVMBuildStore (builder, LLVMConstInt (LLVMInt8Type (), 1, FALSE), LLVMBuildGEP2 (builder, module->inited_var->type, module->inited_var->value, indexes, 2, "")); + if (mono_opt_llvm_emulate_unwind) { + /* Let the caller check the unwind flag before setting the inited flag */ + } else { + /* + * Set the inited flag + * This is already done by the LLVM methods themselves, but its needed by JITted methods. + */ + indexes [0] = const_int32 (0); + indexes [1] = index_var; + LLVMBuildStore (builder, LLVMConstInt (LLVMInt8Type (), 1, FALSE), LLVMBuildGEP2 (builder, module->inited_var->type, module->inited_var->value, indexes, 2, "")); + } LLVMBuildBr (builder, inited_bb); @@ -3764,6 +3846,9 @@ emit_method_init (EmitContext *ctx) */ set_call_cold_cconv (call); + if (mono_opt_llvm_emulate_unwind) + emit_check_unwind_flag_ret (ctx, &builder, NULL); + // Set the inited flag indexes [0] = const_int32 (0); indexes [1] = const_int32 (cfg->method_index); @@ -4990,7 +5075,8 @@ emit_llvmonly_landing_pad (EmitContext *ctx, int group_index, int group_size) g_free (bb_name); LLVMPositionBuilderAtEnd (builder, lpad_bb); - if (mono_opt_wasm_exceptions) { + if (mono_opt_llvm_emulate_unwind) { + } else if (mono_opt_wasm_exceptions) { /* WASM EH uses catchpad instructions */ LLVMValueRef lpad = LLVMBuildCatchSwitch (builder, NULL, NULL, 1, ""); @@ -5071,7 +5157,11 @@ emit_llvmonly_landing_pad (EmitContext *ctx, int group_index, int group_size) return lpad_bb; } - if (mono_opt_wasm_exceptions) { + if (mono_opt_llvm_emulate_unwind) { + /* Clear the flag */ + LLVMValueRef flag_var = get_aotconst (ctx, MONO_PATCH_INFO_LLVMONLY_DO_UNWIND_FLAG, NULL, pointer_type (i4_t)); + LLVMBuildStore (builder, const_int32 (0), flag_var); + } else if (mono_opt_wasm_exceptions) { bb_name = g_strdup_printf ("CATCH_CONT%d_BB", group_index); LLVMBasicBlockRef catch_cont_bb = gen_bb (ctx, bb_name); g_free (bb_name); diff --git a/src/mono/mono/mini/mini-runtime.c b/src/mono/mono/mini/mini-runtime.c index 257ca8fb0d4c3f..d2a4e0b1aa1ab7 100644 --- a/src/mono/mono/mini/mini-runtime.c +++ b/src/mono/mono/mini/mini-runtime.c @@ -1263,6 +1263,7 @@ mono_patch_info_hash (gconstpointer data) case MONO_PATCH_INFO_PROFILER_CLAUSE_COUNT: case MONO_PATCH_INFO_SPECIFIC_TRAMPOLINES: case MONO_PATCH_INFO_SPECIFIC_TRAMPOLINES_GOT_SLOTS_BASE: + case MONO_PATCH_INFO_LLVMONLY_DO_UNWIND_FLAG: return hash; case MONO_PATCH_INFO_SPECIFIC_TRAMPOLINE_LAZY_FETCH_ADDR: return (guint)(hash | ji->data.uindex); @@ -1747,6 +1748,10 @@ mono_resolve_patch_target_ext (MonoMemoryManager *mem_manager, MonoMethod *metho target = (gpointer) &mono_profiler_state.exception_clause_count; break; } + case MONO_PATCH_INFO_LLVMONLY_DO_UNWIND_FLAG: { + target = (gpointer) &mono_llvmonly_do_unwind_flag; + break; + } case MONO_PATCH_INFO_SPECIFIC_TRAMPOLINES: case MONO_PATCH_INFO_SPECIFIC_TRAMPOLINES_GOT_SLOTS_BASE: { /* Resolved in aot-runtime.c */ @@ -2877,7 +2882,7 @@ jit_compile_method_with_opt (JitCompileMethodWithOptCallbackData *params) gboolean thrown = FALSE; #if defined(ENABLE_LLVM_RUNTIME) || defined(ENABLE_LLVM) - mono_llvm_cpp_catch_exception (jit_compile_method_with_opt_cb, params, &thrown); + mono_llvm_catch_exception (jit_compile_method_with_opt_cb, params, &thrown); #else jit_compile_method_with_opt_cb (params); #endif @@ -3511,7 +3516,8 @@ mono_jit_runtime_invoke (MonoMethod *method, void *obj, void **params, MonoObjec if (callee) { compiled_method = mono_jit_compile_method_jit_only (callee, error); if (!compiled_method) { - g_assert (!is_ok (error)); + if (!mono_opt_llvm_emulate_unwind) + g_assert (!is_ok (error)); if (mono_use_interpreter) use_interp = TRUE; diff --git a/src/mono/mono/mini/mini.h b/src/mono/mono/mini/mini.h index 4924a0be9d4899..6ab2e82c0a6fd5 100644 --- a/src/mono/mono/mini/mini.h +++ b/src/mono/mono/mini/mini.h @@ -56,6 +56,7 @@ typedef struct SeqPointInfo SeqPointInfo; #include #include "cfgdump.h" #include "tiered.h" +#include "llvm-runtime.h" #include "mono/metadata/tabledefs.h" #include "mono/metadata/marshal.h" @@ -341,6 +342,7 @@ extern int mono_break_at_bb_bb_num; extern gboolean mono_do_x86_stack_align; extern int mini_verbose; extern int valgrind_register; +extern int mono_llvmonly_do_unwind_flag; #define INS_INFO(opcode) (&mini_ins_info [((opcode) - OP_START - 1) * 4]) @@ -2625,6 +2627,9 @@ MonoBoolean mono_get_frame_info (gint32 skip, MonoMethod **out_method MonoDebugSourceLocation **out_location, gint32 *iloffset, gint32 *native_offset); void mono_set_cast_details (MonoClass *from, MonoClass *to); +void mono_llvm_catch_exception (MonoLLVMInvokeCallback cb, gpointer arg, gboolean *out_thrown); +void mono_llvm_start_native_unwind (void); +void mono_llvm_stop_native_unwind (void); void mono_decompose_typechecks (MonoCompile *cfg); /* Dominator/SSA methods */ diff --git a/src/mono/mono/mini/patch-info.h b/src/mono/mono/mini/patch-info.h index eaec7450c24c93..b1934cce517355 100644 --- a/src/mono/mono/mini/patch-info.h +++ b/src/mono/mono/mini/patch-info.h @@ -48,6 +48,7 @@ PATCH_INFO(LDSTR_LIT, "ldstr_lit") PATCH_INFO(GC_NURSERY_START, "gc_nursery_start") PATCH_INFO(VIRT_METHOD, "virt_method") PATCH_INFO(GC_SAFE_POINT_FLAG, "gc_safe_point_flag") +PATCH_INFO(LLVMONLY_DO_UNWIND_FLAG, "llvmonly_do_unwind_flag") PATCH_INFO(NONE, "none") PATCH_INFO(AOT_MODULE, "aot_module") PATCH_INFO(AOT_JIT_INFO, "aot_jit_info") diff --git a/src/mono/mono/utils/options-def.h b/src/mono/mono/utils/options-def.h index c3aa7babbad908..8f3ec6d47c4e9f 100644 --- a/src/mono/mono/utils/options-def.h +++ b/src/mono/mono/utils/options-def.h @@ -167,6 +167,12 @@ DEFINE_BOOL_READONLY(experimental_gshared_mrgctx, "experimental-gshared-mrgctx", DEFINE_BOOL(experimental_gshared_mrgctx, "experimental-gshared-mrgctx", FALSE, "Use a mrgctx for all gshared methods") #endif +#if defined(TARGET_WASI) +DEFINE_BOOL_READONLY(llvm_emulate_unwind, "emulate-unwind", TRUE, "") +#else +DEFINE_BOOL_READONLY(llvm_emulate_unwind, "emulate-unwind", FALSE, "") +#endif + /* Cleanup */ #undef DEFINE_OPTION_FULL #undef DEFINE_OPTION_READONLY