[mono][wasm] Allow AOTing methods with catch/filter clauses. (#64867)
authorZoltan Varga <vargaz@gmail.com>
Tue, 1 Mar 2022 06:59:52 +0000 (01:59 -0500)
committerGitHub <noreply@github.com>
Tue, 1 Mar 2022 06:59:52 +0000 (01:59 -0500)
* [mono][wasm] Allow AOTing methods with catch clauses.

This works as follows:
* During EH, when a catch clause is found in an AOTed method, the
  EH state is saved into TLS and a c++ exception is thrown.
* The C++ exception is caught by the AOTed method, and the landing
  pad code calls mono_llvm_resume_exception_il_state ().
* That call will run the catch clause and the rest of the method
  code using the interpreter, storing the possible return value
  back into the AOTed method's stack frame.
* After the call, the method skips the rest of its code, and
  returns immediately to its caller.

* Fix console bench sample.

* Fix landing pads.

* Fix issues.

* Add support for filter clauses.

* Implement all wasm return conventions.

* Fix arg/local write back.

* Avoid throwing a c++ exception from do_jit_call () so the caller can clean up the interpreter stack.

* Disable AOTing some more assemblies on CI.

* Rename llvmonly EH functions to mini_llvmonly_ for clarity.

* Improve unwinding through interpreter frames.

Instead of throwing a c++ exception from mono_handle_exception ()
when an exception is caught in AOTed code, set context->has_resume_state,
so the intepreter will normally unwind until exiting interpreted code.
Then throw the c++ exception after the call to interp_exec_method ()
to unwind through the runtime code and the AOTed frames
which can't handle the exception.

20 files changed:
src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Roslyn3.11.Tests.csproj
src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Roslyn4.0.Tests.csproj
src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj
src/mono/mono/metadata/jit-icall-reg.h
src/mono/mono/mini/aot-compiler.c
src/mono/mono/mini/ee.h
src/mono/mono/mini/interp-stubs.c
src/mono/mono/mini/interp/interp-internals.h
src/mono/mono/mini/interp/interp.c
src/mono/mono/mini/llvmonly-runtime.c
src/mono/mono/mini/llvmonly-runtime.h
src/mono/mono/mini/method-to-ir.c
src/mono/mono/mini/mini-exceptions.c
src/mono/mono/mini/mini-llvm.c
src/mono/mono/mini/mini-runtime.c
src/mono/mono/mini/mini-runtime.h
src/mono/mono/mini/mini.c
src/mono/mono/mini/mini.h
src/mono/sample/wasm/browser-bench/Console/Makefile
src/mono/sample/wasm/browser-bench/Console/Wasm.Console.Bench.Sample.csproj

index 8ac0061..c9027ca 100644 (file)
@@ -2,6 +2,10 @@
 
   <Import Project="System.Text.Json.SourceGeneration.Tests.targets" />
 
+  <ItemGroup Condition="'$(ContinuousIntegrationBuild)' == 'true'">
+    <HighAotMemoryUsageAssembly Include="System.Text.Json.SourceGeneration.Roslyn3.11.Tests.dll"/>
+  </ItemGroup>
+
   <ItemGroup>
     <ProjectReference Include="..\..\gen\System.Text.Json.SourceGeneration.Roslyn3.11.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
     <ProjectReference Include="..\System.Text.Json.SourceGeneration.TestLibrary\System.Text.Json.TestLibrary.Roslyn3.11.csproj" />
index 6a306e0..588f786 100644 (file)
@@ -1,7 +1,8 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
-  <ItemGroup>
+  <ItemGroup Condition="'$(ContinuousIntegrationBuild)' == 'true'">
     <HighAotMemoryUsageAssembly Include="Microsoft.CodeAnalysis.CSharp.dll" />
+    <HighAotMemoryUsageAssembly Include="System.Text.Json.SourceGeneration.Roslyn4.0.Tests.dll"/>
   </ItemGroup>
 
   <Import Project="System.Text.Json.SourceGeneration.Tests.targets" />
index ebc4668..546bdf6 100644 (file)
   <PropertyGroup>
     <DefineConstants Condition="$([MSBuild]::GetTargetFrameworkIdentifier('$(TargetFramework)')) == '.NETCoreApp'">$(DefineConstants);BUILDING_INBOX_LIBRARY</DefineConstants>
   </PropertyGroup>
+
+  <ItemGroup Condition="'$(ContinuousIntegrationBuild)' == 'true'">
+    <HighAotMemoryUsageAssembly Include="System.Text.Json.Tests.dll"/>
+  </ItemGroup>
+
   <ItemGroup>
     <Compile Include="$(CommonTestPath)System\IO\WrappedMemoryStream.cs" Link="CommonTest\System\IO\WrappedMemoryStream.cs" />
     <Compile Include="..\Common\CollectionTests\CollectionTests.AsyncEnumerable.cs" Link="CommonTest\System\Text\Json\Tests\Serialization\CollectionTests\CollectionTests.AsyncEnumerable.cs" />
index 8786585..d4ca204 100644 (file)
@@ -142,6 +142,14 @@ MONO_JIT_ICALL (mini_llvmonly_throw_nullref_exception) \
 MONO_JIT_ICALL (mini_llvmonly_throw_aot_failed_exception) \
 MONO_JIT_ICALL (mini_llvmonly_pop_lmf) \
 MONO_JIT_ICALL (mini_llvmonly_interp_entry_gsharedvt) \
+MONO_JIT_ICALL (mini_llvmonly_throw_exception) \
+MONO_JIT_ICALL (mini_llvmonly_rethrow_exception) \
+MONO_JIT_ICALL (mini_llvmonly_throw_corlib_exception) \
+MONO_JIT_ICALL (mini_llvmonly_resume_exception) \
+MONO_JIT_ICALL (mini_llvmonly_resume_exception_il_state) \
+MONO_JIT_ICALL (mini_llvmonly_load_exception) \
+MONO_JIT_ICALL (mini_llvmonly_clear_exception) \
+MONO_JIT_ICALL (mini_llvmonly_match_exception) \
 MONO_JIT_ICALL (mono_amd64_resume_unwind)      \
 MONO_JIT_ICALL (mono_amd64_start_gsharedvt_call)       \
 MONO_JIT_ICALL (mono_amd64_throw_corlib_exception)     \
@@ -220,18 +228,11 @@ MONO_JIT_ICALL (mono_ldtoken_wrapper) \
 MONO_JIT_ICALL (mono_ldtoken_wrapper_generic_shared) \
 MONO_JIT_ICALL (mono_ldvirtfn) \
 MONO_JIT_ICALL (mono_ldvirtfn_gshared) \
-MONO_JIT_ICALL (mono_llvm_clear_exception) \
-MONO_JIT_ICALL (mono_llvm_load_exception) \
-MONO_JIT_ICALL (mono_llvm_match_exception) \
-MONO_JIT_ICALL (mono_llvm_resume_exception) \
 MONO_JIT_ICALL (mono_llvm_resume_unwind_trampoline) \
-MONO_JIT_ICALL (mono_llvm_rethrow_exception) \
 MONO_JIT_ICALL (mono_llvm_rethrow_exception_trampoline) \
 MONO_JIT_ICALL (mono_llvm_set_unhandled_exception_handler) \
-MONO_JIT_ICALL (mono_llvm_throw_corlib_exception) \
 MONO_JIT_ICALL (mono_llvm_throw_corlib_exception_abs_trampoline) \
 MONO_JIT_ICALL (mono_llvm_throw_corlib_exception_trampoline) \
-MONO_JIT_ICALL (mono_llvm_throw_exception) \
 MONO_JIT_ICALL (mono_llvm_throw_exception_trampoline) \
 MONO_JIT_ICALL (mono_llvmonly_init_delegate) \
 MONO_JIT_ICALL (mono_llvmonly_init_delegate_virtual) \
index f483724..cbe80ec 100644 (file)
@@ -13468,7 +13468,7 @@ static void aot_dump (MonoAotCompile *acfg)
 static const MonoJitICallId preinited_jit_icalls [] = {
        MONO_JIT_ICALL_mini_llvm_init_method,
        MONO_JIT_ICALL_mini_llvmonly_throw_nullref_exception,
-       MONO_JIT_ICALL_mono_llvm_throw_corlib_exception,
+       MONO_JIT_ICALL_mini_llvmonly_throw_corlib_exception,
        MONO_JIT_ICALL_mono_threads_state_poll,
        MONO_JIT_ICALL_mini_llvmonly_init_vtable_slot,
        MONO_JIT_ICALL_mono_helper_ldstr_mscorlib,
index 8eb73fd..3409d58 100644 (file)
@@ -14,7 +14,7 @@
 #ifndef __MONO_EE_H__
 #define __MONO_EE_H__
 
-#define MONO_EE_API_VERSION 0x15
+#define MONO_EE_API_VERSION 0x16
 
 typedef struct _MonoInterpStackIter MonoInterpStackIter;
 
@@ -38,7 +38,7 @@ typedef gpointer MonoInterpFrameHandle;
        MONO_EE_CALLBACK (void, get_resume_state, (const MonoJitTlsData *jit_tls, gboolean *has_resume_state, MonoInterpFrameHandle *interp_frame, gpointer *handler_ip)) \
        MONO_EE_CALLBACK (gboolean, run_finally, (StackFrameInfo *frame, int clause_index, gpointer handler_ip, gpointer handler_ip_end)) \
        MONO_EE_CALLBACK (gboolean, run_filter, (StackFrameInfo *frame, MonoException *ex, int clause_index, gpointer handler_ip, gpointer handler_ip_end)) \
-       MONO_EE_CALLBACK (gboolean, run_finally_with_il_state, (gpointer il_state, int clause_index, gpointer handler_ip, gpointer handler_ip_end)) \
+       MONO_EE_CALLBACK (gboolean, run_clause_with_il_state, (gpointer il_state, int clause_index, gpointer handler_ip, gpointer handler_ip_end, MonoObject *ex, gboolean *filtered, MonoExceptionEnum clause_type)) \
        MONO_EE_CALLBACK (void, frame_iter_init, (MonoInterpStackIter *iter, gpointer interp_exit_data)) \
        MONO_EE_CALLBACK (gboolean, frame_iter_next, (MonoInterpStackIter *iter, StackFrameInfo *frame)) \
        MONO_EE_CALLBACK (MonoJitInfo*, find_jit_info, (MonoMethod *method)) \
index 63ef7de..635c0a5 100644 (file)
@@ -115,7 +115,8 @@ stub_run_filter (StackFrameInfo *frame, MonoException *ex, int clause_index, gpo
 }
 
 static gboolean
-stub_run_finally_with_il_state (gpointer il_state, int clause_index, gpointer handler_ip, gpointer handler_ip_end)
+stub_run_clause_with_il_state (gpointer il_state, int clause_index, gpointer handler_ip, gpointer handler_ip_end, MonoObject *ex,
+                                                          gboolean *filtered, MonoExceptionEnum clause_type)
 {
        g_assert_not_reached ();
 }
index 49c04fb..99c0c37 100644 (file)
@@ -210,6 +210,7 @@ typedef struct {
        /* Lets interpreter know it has to resume execution after EH */
        gboolean has_resume_state;
        /* Frame to resume execution at */
+       /* Can be NULL if the exception is caught in an AOTed frame */
        InterpFrame *handler_frame;
        /* IP to resume execution at */
        const guint16 *handler_ip;
index a052897..44d9d8a 100644 (file)
@@ -92,8 +92,6 @@ struct FrameClauseArgs {
         * frame further down the stack.
         */
        const guint16 *end_at_ip;
-       /* When exiting this clause we also exit the frame */
-       int exit_clause;
        /* Frame that is executing this clause */
        InterpFrame *exec_frame;
 };
@@ -429,6 +427,14 @@ 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)
+{
+       if (context->has_resume_state && !context->handler_frame)
+               mono_llvm_cpp_throw_exception ();
+}
+
 void
 mono_interp_error_cleanup (MonoError* error)
 {
@@ -2118,6 +2124,8 @@ interp_runtime_invoke (MonoMethod *method, void *obj, void **params, MonoObject
 
        context->stack_pointer = (guchar*)sp;
 
+       check_pending_unwind (context);
+
        if (context->has_resume_state) {
                /*
                 * This can happen on wasm where native frames cannot be skipped during EH.
@@ -2220,10 +2228,12 @@ interp_entry (InterpEntryData *data)
        if (rmethod->needs_thread_attach)
                mono_threads_detach_coop (orig_domain, &attach_cookie);
 
+       check_pending_unwind (context);
+
        if (mono_llvm_only) {
                if (context->has_resume_state)
                        /* The exception will be handled in a frame above us */
-                       mono_llvm_reraise_exception ((MonoException*)mono_gchandle_get_target_internal (context->exc_gchandle));
+                       mono_llvm_cpp_throw_exception ();
        } else {
                g_assert (!context->has_resume_state);
        }
@@ -2641,9 +2651,20 @@ do_jit_call (ThreadContext *context, stackval *ret_sp, stackval *sp, InterpFrame
                         * This happens when interp_entry calls mono_llvm_reraise_exception ().
                         */
                        return;
-               MonoObject *obj = mono_llvm_load_exception ();
+               MonoJitTlsData *jit_tls = mono_get_jit_tls ();
+               if (jit_tls->resume_state.il_state) {
+                       /*
+                        * This c++ exception is going to be caught by an AOTed frame above us.
+                        * We can't rethrow here, since that will skip the cleanup of the
+                        * interpreter stack space etc. So instruct the interpreter to unwind.
+                        */
+                       context->has_resume_state = TRUE;
+                       context->handler_frame = NULL;
+                       return;
+               }
+               MonoObject *obj = mini_llvmonly_load_exception ();
                g_assert (obj);
-               mono_llvm_clear_exception ();
+               mini_llvmonly_clear_exception ();
                mono_error_set_exception_instance (error, (MonoException*)obj);
                return;
        }
@@ -2958,6 +2979,8 @@ 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);
+
        /* Write back the return value */
        /* 'frame' is still valid */
        mono_arch_set_native_call_context_ret (ccontext, &frame, sig, retp);
@@ -2996,8 +3019,10 @@ interp_compile_interp_method (MonoMethod *method, MonoError *error)
        InterpMethod *imethod = mono_interp_get_imethod (method, error);
        return_val_if_nok (error, NULL);
 
-       mono_interp_transform_method (imethod, get_context (), error);
-       return_val_if_nok (error, NULL);
+       if (!imethod->transformed) {
+               mono_interp_transform_method (imethod, get_context (), error);
+               return_val_if_nok (error, NULL);
+       }
 
        return imethod->jinfo;
 }
@@ -7269,6 +7294,8 @@ interp_parse_options (const char *options)
  * interp_set_resume_state:
  *
  *   Set the state the interpeter will continue to execute from after execution returns to the interpreter.
+ * If INTERP_FRAME is NULL, that means the exception is caught in an AOTed frame and the interpreter needs to
+ * unwind back to AOT code.
  */
 static void
 interp_set_resume_state (MonoJitTlsData *jit_tls, MonoObject *ex, MonoJitExceptionInfo *ei, MonoInterpFrameHandle interp_frame, gpointer handler_ip)
@@ -7286,8 +7313,10 @@ interp_set_resume_state (MonoJitTlsData *jit_tls, MonoObject *ex, MonoJitExcepti
                mono_gchandle_free_internal (context->exc_gchandle);
        context->exc_gchandle = mono_gchandle_new_internal ((MonoObject*)ex, FALSE);
        /* Ditto */
-       if (ei)
-               *(MonoObject**)(frame_locals (context->handler_frame) + ei->exvar_offset) = ex;
+       if (context->handler_frame) {
+               if (ei)
+                       *(MonoObject**)(frame_locals (context->handler_frame) + ei->exvar_offset) = ex;
+       }
        context->handler_ip = (const guint16*)handler_ip;
 }
 
@@ -7323,7 +7352,6 @@ interp_run_finally (StackFrameInfo *frame, int clause_index, gpointer handler_ip
        memset (&clause_args, 0, sizeof (FrameClauseArgs));
        clause_args.start_with_ip = (const guint16*)handler_ip;
        clause_args.end_at_ip = (const guint16*)handler_ip_end;
-       clause_args.exit_clause = clause_index;
        clause_args.exec_frame = iframe;
 
        state_ip = iframe->state.ip;
@@ -7339,6 +7367,9 @@ interp_run_finally (StackFrameInfo *frame, int clause_index, gpointer handler_ip
 
        iframe->next_free = next_free;
        iframe->state.ip = state_ip;
+
+       check_pending_unwind (context);
+
        if (context->has_resume_state) {
                return TRUE;
        } else {
@@ -7390,32 +7421,41 @@ interp_run_filter (StackFrameInfo *frame, MonoException *ex, int clause_index, g
 
        context->stack_pointer = (guchar*)child_frame.stack;
 
+       check_pending_unwind (context);
+
        /* ENDFILTER stores the result into child_frame->retval */
        return retval.data.i ? TRUE : FALSE;
 }
 
+/* Returns TRUE if there is a pending exception */
 static gboolean
-interp_run_finally_with_il_state (gpointer il_state_ptr, int clause_index, gpointer handler_ip, gpointer handler_ip_end)
+interp_run_clause_with_il_state (gpointer il_state_ptr, int clause_index, gpointer handler_ip, gpointer handler_ip_end,
+                                                                MonoObject *ex, gboolean *filtered, MonoExceptionEnum clause_type)
 {
        MonoMethodILState *il_state = (MonoMethodILState*)il_state_ptr;
        MonoMethodSignature *sig;
        ThreadContext *context = get_context ();
+       stackval *orig_sp;
        stackval *sp, *sp_args;
        InterpMethod *imethod;
        FrameClauseArgs clause_args;
        ERROR_DECL (error);
 
-       // FIXME: Optimize this ? Its only used during EH
-
        sig = mono_method_signature_internal (il_state->method);
        g_assert (sig);
 
        imethod = mono_interp_get_imethod (il_state->method, error);
        mono_error_assert_ok (error);
 
-       sp_args = sp = (stackval*)context->stack_pointer;
+       orig_sp = sp_args = sp = (stackval*)context->stack_pointer;
+
+       gpointer ret_addr = NULL;
 
        int findex = 0;
+       if (sig->ret->type != MONO_TYPE_VOID) {
+               ret_addr = il_state->data [findex];
+               findex ++;
+       }
        if (sig->hasthis) {
                if (il_state->data [findex])
                        sp_args->data.p = *(gpointer*)il_state->data [findex];
@@ -7451,7 +7491,7 @@ interp_run_finally_with_il_state (gpointer il_state_ptr, int clause_index, gpoin
        if (header->num_locals)
                memset (frame_locals (&frame) + imethod->local_offsets [0], 0, imethod->locals_size);
        /* Copy locals from il_state */
-       int locals_start = sig->hasthis + sig->param_count;
+       int locals_start = findex;
        for (int i = 0; i < header->num_locals; ++i) {
                if (il_state->data [locals_start + i])
                        stackval_from_data (header->locals [i], (stackval*)(frame_locals (&frame) + imethod->local_offsets [i]), il_state->data [locals_start + i], FALSE);
@@ -7459,24 +7499,33 @@ interp_run_finally_with_il_state (gpointer il_state_ptr, int clause_index, gpoin
 
        memset (&clause_args, 0, sizeof (FrameClauseArgs));
        clause_args.start_with_ip = (const guint16*)handler_ip;
-       clause_args.end_at_ip = (const guint16*)handler_ip_end;
-       clause_args.exit_clause = clause_index;
+       if (clause_type == MONO_EXCEPTION_CLAUSE_NONE || clause_type == MONO_EXCEPTION_CLAUSE_FILTER)
+               clause_args.end_at_ip = (const guint16*)clause_args.start_with_ip + 0xffffff;
+       else
+               clause_args.end_at_ip = (const guint16*)handler_ip_end;
        clause_args.exec_frame = &frame;
 
-       // this informs MINT_ENDFINALLY to return to EH
-       *(guint16**)(frame_locals (&frame) + imethod->clause_data_offsets [clause_index]) = NULL;
+       if (clause_type == MONO_EXCEPTION_CLAUSE_NONE || clause_type == MONO_EXCEPTION_CLAUSE_FILTER)
+               *(MonoObject**)(frame_locals (&frame) + imethod->jinfo->clauses [clause_index].exvar_offset) = ex;
+       else
+               // this informs MINT_ENDFINALLY to return to EH
+               *(guint16**)(frame_locals (&frame) + imethod->clause_data_offsets [clause_index]) = NULL;
+
+       /* Set in mono_handle_exception () */
+       context->has_resume_state = FALSE;
 
        interp_exec_method (&frame, context, &clause_args);
 
        /* Write back args */
        sp_args = sp;
        findex = 0;
+       if (sig->ret->type != MONO_TYPE_VOID)
+               findex ++;
        if (sig->hasthis) {
                // FIXME: This
                sp_args++;
                findex ++;
        }
-       findex = sig->hasthis ? 1 : 0;
        for (int i = 0; i < sig->param_count; ++i) {
                if (il_state->data [findex]) {
                        int size = stackval_to_data (sig->params [i], sp_args, il_state->data [findex], FALSE);
@@ -7494,11 +7543,19 @@ interp_run_finally_with_il_state (gpointer il_state_ptr, int clause_index, gpoin
        }
        mono_metadata_free_mh (header);
 
-       // FIXME: Restore stack ?
-       if (context->has_resume_state)
-               return TRUE;
-       else
-               return FALSE;
+       if (clause_type == MONO_EXCEPTION_CLAUSE_NONE && ret_addr) {
+               stackval_to_data (sig->ret, frame.retval, ret_addr, FALSE);
+       } else if (clause_type == MONO_EXCEPTION_CLAUSE_FILTER) {
+               g_assert (filtered);
+               *filtered = frame.retval->data.i;
+       }
+
+       memset (orig_sp, 0, (guint8*)context->stack_pointer - (guint8*)orig_sp);
+       context->stack_pointer = (guchar*)orig_sp;
+
+       check_pending_unwind (context);
+
+       return context->has_resume_state;
 }
 
 typedef struct {
index 2aeb93b..2840ec1 100644 (file)
@@ -572,7 +572,7 @@ mini_llvmonly_resolve_vcall_gsharedvt (MonoObject *this_obj, int slot, MonoMetho
        gpointer result = resolve_vcall (this_obj->vtable, slot, imt_method, out_arg, TRUE, error);
        if (!is_ok (error)) {
                MonoException *ex = mono_error_convert_to_exception (error);
-               mono_llvm_throw_exception ((MonoObject*)ex);
+               mini_llvmonly_throw_exception ((MonoObject*)ex);
        }
        return result;
 }
@@ -612,7 +612,7 @@ mini_llvmonly_resolve_vcall_gsharedvt_fast (MonoObject *this_obj, int slot)
        gpointer addr = resolve_vcall (vtable, slot, NULL, &arg, TRUE, error);
        if (!is_ok (error)) {
                MonoException *ex = mono_error_convert_to_exception (error);
-               mono_llvm_throw_exception ((MonoObject*)ex);
+               mini_llvmonly_throw_exception ((MonoObject*)ex);
        }
 
        ftndesc = m_class_alloc0 (vtable->klass, sizeof (MonoFtnDesc));
@@ -697,7 +697,7 @@ mini_llvmonly_resolve_generic_virtual_iface_call (MonoVTable *vt, int imt_slot,
        mini_resolve_imt_method (vt, imt + imt_slot, generic_virtual, &m, &aot_addr, &need_rgctx_tramp, &variant_iface, error);
        if (!is_ok (error)) {
                MonoException *ex = mono_error_convert_to_exception (error);
-               mono_llvm_throw_exception ((MonoObject*)ex);
+               mini_llvmonly_throw_exception ((MonoObject*)ex);
        }
 
        if (m_class_is_valuetype (vt->klass))
@@ -913,7 +913,7 @@ mini_llvmonly_resolve_iface_call_gsharedvt (MonoObject *this_obj, int imt_slot,
        gpointer addr = resolve_iface_call (this_obj, imt_slot, imt_method, out_arg, TRUE, error);
        if (!is_ok (error)) {
                MonoException *ex = mono_error_convert_to_exception (error);
-               mono_llvm_throw_exception ((MonoObject*)ex);
+               mini_llvmonly_throw_exception ((MonoObject*)ex);
        }
 
        if (!gsharedvt_imt [imt_slot]) {
@@ -946,7 +946,7 @@ mini_llvm_init_method (MonoAotFileInfo *info, gpointer aot_module, gpointer meth
                if (ex) {
                        /* Its okay to raise in llvmonly mode */
                        if (mono_llvm_only) {
-                               mono_llvm_throw_exception ((MonoObject*)ex);
+                               mini_llvmonly_throw_exception ((MonoObject*)ex);
                        } else {
                                mono_set_pending_exception (ex);
                        }
@@ -963,7 +963,7 @@ mini_llvmonly_throw_nullref_exception (void)
 
        guint32 ex_token_index = m_class_get_type_token (klass) - MONO_TOKEN_TYPE_DEF;
 
-       mono_llvm_throw_corlib_exception (ex_token_index);
+       mini_llvmonly_throw_corlib_exception (ex_token_index);
 }
 
 void
@@ -972,7 +972,7 @@ mini_llvmonly_throw_aot_failed_exception (const char *name)
        char *msg = g_strdup_printf ("AOT Compilation failed for method '%s'.", name);
        MonoException *ex = mono_get_exception_execution_engine (msg);
        g_free (msg);
-       mono_llvm_throw_exception ((MonoObject*)ex);
+       mini_llvmonly_throw_exception ((MonoObject*)ex);
 }
 
 /*
index 5b6c7ed..cb08671 100644 (file)
@@ -40,4 +40,14 @@ G_EXTERN_C void mini_llvmonly_pop_lmf (MonoLMF *lmf);
 
 G_EXTERN_C void mini_llvmonly_interp_entry_gsharedvt (gpointer imethod, gpointer res, gpointer *args);
 
+/* These are implemented in mini-exceptions.c */
+G_EXTERN_C void mini_llvmonly_throw_exception       (MonoObject *ex);
+G_EXTERN_C void mini_llvmonly_rethrow_exception     (MonoObject *ex);
+G_EXTERN_C void mini_llvmonly_throw_corlib_exception (guint32 ex_token_index);
+G_EXTERN_C void mini_llvmonly_resume_exception      (void);
+G_EXTERN_C void mini_llvmonly_resume_exception_il_state (MonoLMF *lmf, gpointer info);
+G_EXTERN_C MonoObject *mini_llvmonly_load_exception (void);
+G_EXTERN_C void mini_llvmonly_clear_exception       (void);
+G_EXTERN_C gint32 mini_llvmonly_match_exception     (MonoJitInfo *jinfo, guint32 region_start, guint32 region_end, gpointer rgctx, MonoObject *this_obj);
+
 #endif
index 33bc1f2..17d269b 100644 (file)
@@ -6493,6 +6493,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                        for (int i = 0; i < header->num_clauses; ++i) {
                                MonoExceptionClause *clause = &header->clauses [i];
                                /* Finally clauses are checked after the remove_finally pass */
+
                                if (clause->flags != MONO_EXCEPTION_CLAUSE_FINALLY)
                                        cfg->interp_entry_only = TRUE;
                        }
@@ -6675,6 +6676,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
        start_new_bblock = 0;
        MonoOpcodeEnum il_op; il_op = MonoOpcodeEnum_Invalid;
 
+       emit_set_deopt_il_offset (cfg, ip - cfg->cil_start);
+
        for (guchar *next_ip = ip; ip < end; ip = next_ip) {
                MonoOpcodeEnum previous_il_op = il_op;
                const guchar *tmp_ip = ip;
index 412c9af..e9466fe 100644 (file)
@@ -75,6 +75,7 @@
 #include "trace.h"
 #include "seq-points.h"
 #include "llvm-runtime.h"
+#include "llvmonly-runtime.h"
 #include "mini-llvm.h"
 #include "aot-runtime.h"
 #include "mini-runtime.h"
@@ -124,6 +125,8 @@ static gboolean mono_install_handler_block_guard (MonoThreadUnwindState *ctx);
 static void mono_uninstall_current_handler_block_guard (void);
 static gboolean mono_exception_walk_trace_internal (MonoException *ex, MonoExceptionFrameWalk func, gpointer user_data);
 static void throw_exception (MonoObject *ex, gboolean rethrow);
+static void llvmonly_raise_exception (MonoException *e);
+static void llvmonly_reraise_exception (MonoException *e);
 
 static gboolean
 first_managed (MonoStackFrameInfo *frame, MonoContext *ctx, gpointer addr)
@@ -233,8 +236,8 @@ mono_exceptions_init (void)
        cbs.mono_walk_stack_with_state = mono_walk_stack_with_state;
 
        if (mono_llvm_only) {
-               cbs.mono_raise_exception = mono_llvm_raise_exception;
-               cbs.mono_reraise_exception = mono_llvm_reraise_exception;
+               cbs.mono_raise_exception = llvmonly_raise_exception;
+               cbs.mono_reraise_exception = llvmonly_reraise_exception;
        } else {
                cbs.mono_raise_exception = (void (*)(MonoException *))mono_get_throw_exception ();
                cbs.mono_reraise_exception = (void (*)(MonoException *))mono_get_rethrow_exception ();
@@ -439,6 +442,7 @@ arch_unwind_frame (MonoJitTlsData *jit_tls,
                                frame->type = FRAME_TYPE_JIT_ENTRY;
                        } else if (ext->kind == MONO_LMFEXT_IL_STATE) {
                                frame->type = FRAME_TYPE_IL_STATE;
+                               frame->managed = TRUE;
                                frame->method = ext->il_state->method;
                                frame->actual_method = ext->il_state->method;
                                frame->il_state = ext->il_state;
@@ -1910,7 +1914,7 @@ handle_exception_first_pass (MonoContext *ctx, MonoObject *obj, gint32 *out_filt
 
                frame_count ++;
                method = jinfo_get_method (ji);
-               //printf ("M: %s %d.\n", mono_method_full_name (method, TRUE), frame_count);
+               //printf ("[%d]: %s.\n", frame_count, mono_method_full_name (method, TRUE));
 
                if (mini_debug_options.reverse_pinvoke_exceptions && method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED) {
                        g_error ("A native frame was found while unwinding the stack after an exception.\n"
@@ -2012,7 +2016,13 @@ handle_exception_first_pass (MonoContext *ctx, MonoObject *obj, gint32 *out_filt
                                                g_free (name);
                                        }
 
-                                       if (ji->is_interp) {
+                                       if (frame.type == FRAME_TYPE_IL_STATE) {
+                                               if (mono_trace_is_enabled () && mono_trace_eval (method))
+                                                       g_print ("EXCEPTION: filter clause found in AOTed code, running it with the interpreter.\n");
+                                               gboolean res = mini_get_interp_callbacks ()->run_clause_with_il_state (frame.il_state, i, ei->data.filter, NULL, ex_obj, &filtered, MONO_EXCEPTION_CLAUSE_FILTER);
+                                               // FIXME:
+                                               g_assert (!res);
+                                       } else if (ji->is_interp) {
                                                /* The filter ends where the exception handler starts */
                                                filtered = mini_get_interp_callbacks ()->run_filter (&frame, (MonoException*)ex_obj, i, ei->data.filter, ei->handler_start);
                                        } else {
@@ -2355,7 +2365,7 @@ mono_handle_exception_internal (MonoContext *ctx, MonoObject *obj, gboolean resu
 
                method = jinfo_get_method (ji);
                frame_count ++;
-               //printf ("M: %s %d.\n", mono_method_full_name (method, TRUE), frame_count);
+               //printf ("[%d] %s.\n", frame_count, mono_method_full_name (method, TRUE));
 
                if (stack_overflow) {
                        free_stack = (guint8*)(MONO_CONTEXT_GET_SP (ctx)) - (guint8*)(MONO_CONTEXT_GET_SP (&initial_ctx));
@@ -2479,6 +2489,24 @@ mono_handle_exception_internal (MonoContext *ctx, MonoObject *obj, gboolean resu
 
                                        mini_set_abort_threshold (&frame);
 
+                                       if (frame.type == FRAME_TYPE_IL_STATE) {
+                                               if (mono_trace_is_enabled () && mono_trace_eval (method))
+                                                       g_print ("EXCEPTION: catch clause found in AOTed code.\n");
+                                               /*
+                                                * Save the state needed to execute the catch clause in TLS, then
+                                                * throw a c++ exception which is going to be caught by AOTed
+                                                * methods, then execute the clause and the rest of the method
+                                                * using the interpreter.
+                                                */
+                                               jit_tls->resume_state.ex_obj = obj;
+                                               jit_tls->resume_state.ji = ji;
+                                               jit_tls->resume_state.clause_index = i;
+                                               jit_tls->resume_state.il_state = frame.il_state;
+
+                                               /* Instruct the intepreter to unwind back to AOTed code */
+                                               mini_get_interp_callbacks ()->set_resume_state (jit_tls, ex_obj, ei, NULL, NULL);
+                                       }
+
                                        if (in_interp) {
                                                /*
                                                 * ctx->pc points into the interpreter, after the call which transitioned to
@@ -2557,8 +2585,8 @@ mono_handle_exception_internal (MonoContext *ctx, MonoObject *obj, gboolean resu
                                                mini_set_abort_threshold (&frame);
                                                if (frame.type == FRAME_TYPE_IL_STATE) {
                                                        if (mono_trace_is_enabled () && mono_trace_eval (method))
-                                                               g_print ("EXCEPTION: clause found in AOTed code, running clause with interpreter.\n");
-                                                       mini_get_interp_callbacks ()->run_finally_with_il_state (frame.il_state, i, ei->handler_start, ei->data.handler_end);
+                                                               g_print ("EXCEPTION: finally/fault clause found in AOTed code, running it with the interpreter.\n");
+                                                       mini_get_interp_callbacks ()->run_clause_with_il_state (frame.il_state, i, ei->handler_start, ei->data.handler_end, NULL, NULL, MONO_EXCEPTION_CLAUSE_FINALLY);
                                                } else if (in_interp) {
                                                        gboolean has_ex = mini_get_interp_callbacks ()->run_finally (&frame, i, ei->handler_start, ei->data.handler_end);
                                                        if (has_ex) {
@@ -3439,11 +3467,18 @@ throw_exception (MonoObject *ex, gboolean rethrow)
 #endif
        }
 
-       mono_llvm_cpp_throw_exception ();
+       if (rethrow) {
+               MonoContext ctx;
+               MonoJitInfo *out_ji;
+               memset (&ctx, 0, sizeof (MonoContext));
+
+               mono_handle_exception_internal (&ctx, ex, FALSE, &out_ji);
+               mono_llvm_cpp_throw_exception ();
+       }
 }
 
 void
-mono_llvm_throw_exception (MonoObject *ex)
+mini_llvmonly_throw_exception (MonoObject *ex)
 {
        g_assert (mono_llvm_only);
 
@@ -3460,55 +3495,125 @@ mono_llvm_throw_exception (MonoObject *ex)
        mono_handle_exception (&ctx, (MonoObject*)ex);
 
        throw_exception (ex, FALSE);
+
+       /* Unwind back to either an AOTed frame or to the interpreter */
+       mono_llvm_cpp_throw_exception ();
 }
 
 void
-mono_llvm_rethrow_exception (MonoObject *ex)
+mini_llvmonly_rethrow_exception (MonoObject *ex)
 {
        throw_exception (ex, TRUE);
 }
 
 void
-mono_llvm_raise_exception (MonoException *e)
+mini_llvmonly_throw_corlib_exception (guint32 ex_token_index)
+{
+       guint32 ex_token = MONO_TOKEN_TYPE_DEF | ex_token_index;
+       MonoException *ex;
+
+       ex = mono_exception_from_token (m_class_get_image (mono_defaults.exception_class), ex_token);
+
+       mini_llvmonly_throw_exception ((MonoObject*)ex);
+}
+
+static void
+llvmonly_raise_exception (MonoException *e)
 {
-       mono_llvm_throw_exception ((MonoObject*)e);
+       mini_llvmonly_throw_exception ((MonoObject*)e);
 }
 
-void
-mono_llvm_reraise_exception (MonoException *e)
+static void
+llvmonly_reraise_exception (MonoException *e)
 {
-       mono_llvm_rethrow_exception ((MonoObject*)e);
+       mini_llvmonly_rethrow_exception ((MonoObject*)e);
 }
 
+/*
+ * mini_llvmonly_resume_exception:
+ *
+ *   Resume exception propagation.
+ */
 void
-mono_llvm_throw_corlib_exception (guint32 ex_token_index)
+mini_llvmonly_resume_exception (void)
 {
-       guint32 ex_token = MONO_TOKEN_TYPE_DEF | ex_token_index;
-       MonoException *ex;
+       mono_llvm_cpp_throw_exception ();
+}
 
-       ex = mono_exception_from_token (m_class_get_image (mono_defaults.exception_class), ex_token);
+static G_GNUC_UNUSED void
+print_lmf_chain (MonoLMF *lmf)
+{
+       MonoJitTlsData *jit_tls = mono_get_jit_tls ();
+
+       while (lmf && lmf != jit_tls->first_lmf) {
+               printf ("LMF: %p ", lmf);
+
+               if (((gsize)lmf->previous_lmf) & 2) {
+                       MonoLMFExt *ext = (MonoLMFExt*)lmf;
+
+                       switch (ext->kind) {
+                       case MONO_LMFEXT_DEBUGGER_INVOKE:
+                               printf ("dbg invoke");
+                               break;
+                       case MONO_LMFEXT_INTERP_EXIT:
+                       case MONO_LMFEXT_INTERP_EXIT_WITH_CTX:
+                               printf ("interp exit");
+                               break;
+                       case MONO_LMFEXT_JIT_ENTRY:
+                               printf ("jit entry");
+                               break;
+                       case MONO_LMFEXT_IL_STATE:
+                               printf ("il state ['%s']", mono_method_get_full_name (ext->il_state->method));
+                               break;
+                       default:
+                               g_assert_not_reached ();
+                               break;
+                       }
+                       printf ("\n");
 
-       mono_llvm_throw_exception ((MonoObject*)ex);
+                       lmf = (MonoLMF *)(((gsize)lmf->previous_lmf) & ~3);
+               }
+       }
 }
 
 /*
- * mono_llvm_resume_exception:
+ * mini_llvmonly_resume_exception_il_state:
  *
- *   Resume exception propagation.
+ *   Called from AOTed code to execute catch clauses.
  */
 void
-mono_llvm_resume_exception (void)
+mini_llvmonly_resume_exception_il_state (MonoLMF *lmf, gpointer info)
 {
-       mono_llvm_cpp_throw_exception ();
+       MonoMethodILState *il_state = (MonoMethodILState *)info;
+       MonoJitTlsData *jit_tls = mono_get_jit_tls ();
+
+       //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 ();
+       }
+       jit_tls->resume_state.il_state = NULL;
+
+       /* Pop the LMF frame so the caller doesn't have to do it */
+       mono_set_lmf ((MonoLMF *)(((gsize)lmf->previous_lmf) & ~3));
+
+       MonoJitInfo *ji = jit_tls->resume_state.ji;
+       int clause_index = jit_tls->resume_state.clause_index;
+       MonoJitExceptionInfo *ei = &ji->clauses [clause_index];
+       gboolean r = mini_get_interp_callbacks ()->run_clause_with_il_state (il_state, clause_index, ei->handler_start, NULL, jit_tls->resume_state.ex_obj, NULL, MONO_EXCEPTION_CLAUSE_NONE);
+       if (r)
+               /* Another exception thrown, continue unwinding */
+               mono_llvm_cpp_throw_exception ();
 }
 
 /*
- * mono_llvm_load_exception:
+ * mini_llvmonly_load_exception:
  *
  *   Return the currently thrown exception.
  */
 MonoObject *
-mono_llvm_load_exception (void)
+mini_llvmonly_load_exception (void)
 {
        HANDLE_FUNCTION_ENTER ();
        ERROR_DECL (error);
@@ -3557,12 +3662,12 @@ mono_llvm_load_exception (void)
 }
 
 /*
- * mono_llvm_clear_exception:
+ * mini_llvmonly_clear_exception:
  *
  *   Mark the currently thrown exception as handled.
  */
 void
-mono_llvm_clear_exception (void)
+mini_llvmonly_clear_exception (void)
 {
        MonoJitTlsData *jit_tls = mono_get_jit_tls ();
 
@@ -3576,13 +3681,13 @@ mono_llvm_clear_exception (void)
 }
 
 /*
- * mono_llvm_match_exception:
+ * mini_llvmonly_match_exception:
  *
  *   Return the innermost clause containing REGION_START-REGION_END which can handle
  * the current exception.
  */
 gint32
-mono_llvm_match_exception (MonoJitInfo *jinfo, guint32 region_start, guint32 region_end, gpointer rgctx, MonoObject *this_obj)
+mini_llvmonly_match_exception (MonoJitInfo *jinfo, guint32 region_start, guint32 region_end, gpointer rgctx, MonoObject *this_obj)
 {
        ERROR_DECL (error);
        MonoJitTlsData *jit_tls = mono_get_jit_tls ();
index 0a85c0c..5424541 100644 (file)
@@ -178,6 +178,7 @@ typedef struct {
        gboolean is_linkonce;
        gboolean emit_dummy_arg;
        gboolean has_safepoints;
+       gboolean has_catch;
        int this_arg_pindex, rgctx_arg_pindex;
        LLVMValueRef imt_rgctx_loc;
        GHashTable *llvm_types;
@@ -196,6 +197,7 @@ typedef struct {
        int *gc_var_indexes;
        LLVMValueRef gc_pin_area;
        LLVMValueRef il_state;
+       LLVMValueRef il_state_ret;
 } EmitContext;
 
 typedef struct {
@@ -2377,11 +2379,10 @@ emit_call (EmitContext *ctx, MonoBasicBlock *bb, LLVMBuilderRef *builder_ref, LL
        MonoExceptionClause *clause;
 
        if (ctx->llvm_only) {
-               clause = get_most_deep_clause (cfg, ctx, bb);
-
-               if (!ctx->cfg->deopt && clause) {
-                       g_assert (clause->flags == MONO_EXCEPTION_CLAUSE_NONE || clause->flags == MONO_EXCEPTION_CLAUSE_FINALLY || clause->flags == MONO_EXCEPTION_CLAUSE_FAULT);
+               clause = bb ? get_most_deep_clause (cfg, ctx, bb) : NULL;
 
+               // FIXME: Use an invoke only for calls inside try-catch blocks
+               if (clause && (!cfg->deopt || ctx->has_catch)) {
                        /*
                         * Have to use an invoke instead of a call, branching to the
                         * handler bblock of the clause containing this bblock.
@@ -2543,7 +2544,7 @@ emit_cond_system_exception (EmitContext *ctx, MonoBasicBlock *bb, const char *ex
 
                        if (!sig)
                                sig = LLVMFunctionType1 (LLVMVoidType (), LLVMInt32Type (), FALSE);
-                       callee = get_callee (ctx, sig, MONO_PATCH_INFO_JIT_ICALL_ADDR, GUINT_TO_POINTER (MONO_JIT_ICALL_mono_llvm_throw_corlib_exception));
+                       callee = get_callee (ctx, sig, MONO_PATCH_INFO_JIT_ICALL_ADDR, GUINT_TO_POINTER (MONO_JIT_ICALL_mini_llvmonly_throw_corlib_exception));
                        args [0] = LLVMConstInt (LLVMInt32Type (), m_class_get_type_token (exc_class) - MONO_TOKEN_TYPE_DEF, FALSE);
                        emit_call (ctx, bb, &builder, callee, args, 1);
                }
@@ -4044,7 +4045,7 @@ emit_entry_bb (EmitContext *ctx, LLVMBuilderRef builder)
        if (cfg->deopt) {
                LLVMValueRef addr, index [2];
                MonoMethodHeader *header = cfg->header;
-               int nfields = sig->hasthis + sig->param_count + header->num_locals + 2;
+               int nfields = (sig->ret->type != MONO_TYPE_VOID ? 1 : 0) + sig->hasthis + sig->param_count + header->num_locals + 2;
                LLVMTypeRef *types = g_alloca (nfields * sizeof (LLVMTypeRef));
                int findex = 0;
                /* method */
@@ -4053,6 +4054,8 @@ emit_entry_bb (EmitContext *ctx, LLVMBuilderRef builder)
                types [findex ++] = LLVMInt32Type ();
                int data_start = findex;
                /* data */
+               if (sig->ret->type != MONO_TYPE_VOID)
+                       types [findex ++] = IntPtrType ();
                if (sig->hasthis)
                        types [findex ++] = IntPtrType ();
                for (int i = 0; i < sig->param_count; ++i)
@@ -4081,6 +4084,16 @@ emit_entry_bb (EmitContext *ctx, LLVMBuilderRef builder)
                 * clauses are supposed to be volatile, so they have an address.
                 */
                findex = data_start;
+               if (sig->ret->type != MONO_TYPE_VOID) {
+                       LLVMTypeRef ret_type = type_to_llvm_type (ctx, sig->ret);
+                       ctx->il_state_ret = build_alloca_llvm_type_name (ctx, ret_type, 0, "il_state_ret");
+
+                       index [0] = LLVMConstInt (LLVMInt32Type (), 0, FALSE);
+                       index [1] = LLVMConstInt (LLVMInt32Type (), findex, FALSE);
+                       addr = LLVMBuildGEP (builder, ctx->il_state, index, 2, "");
+                       LLVMBuildStore (ctx->builder, ctx->il_state_ret, convert (ctx, addr, LLVMPointerType (LLVMTypeOf (ctx->il_state_ret), 0)));
+                       findex ++;
+               }
                for (int i = 0; i < sig->hasthis + sig->param_count; ++i) {
                        LLVMValueRef var_addr = ctx->addresses [cfg->args [i]->dreg];
 
@@ -4691,7 +4704,7 @@ process_call (EmitContext *ctx, MonoBasicBlock *bb, LLVMBuilderRef *builder_ref,
 static void
 emit_llvmonly_throw (EmitContext *ctx, MonoBasicBlock *bb, gboolean rethrow, LLVMValueRef exc)
 {
-       MonoJitICallId icall_id = rethrow ? MONO_JIT_ICALL_mono_llvm_rethrow_exception : MONO_JIT_ICALL_mono_llvm_throw_exception;
+       MonoJitICallId icall_id = rethrow ? MONO_JIT_ICALL_mini_llvmonly_rethrow_exception : MONO_JIT_ICALL_mini_llvmonly_throw_exception;
        LLVMValueRef callee = rethrow ? ctx->module->rethrow : ctx->module->throw_icall;
 
        LLVMTypeRef exc_type = type_to_llvm_type (ctx, m_class_get_byval_arg (mono_get_exception_class ()));
@@ -4753,7 +4766,7 @@ emit_throw (EmitContext *ctx, MonoBasicBlock *bb, gboolean rethrow, LLVMValueRef
 static void
 emit_resume_eh (EmitContext *ctx, MonoBasicBlock *bb)
 {
-       const MonoJitICallId icall_id = MONO_JIT_ICALL_mono_llvm_resume_exception;
+       const MonoJitICallId icall_id = MONO_JIT_ICALL_mini_llvmonly_resume_exception;
        LLVMValueRef callee;
 
        LLVMTypeRef fun_sig = LLVMFunctionType0 (LLVMVoidType (), FALSE);
@@ -4771,19 +4784,13 @@ emit_resume_eh (EmitContext *ctx, MonoBasicBlock *bb)
 static LLVMValueRef
 mono_llvm_emit_clear_exception_call (EmitContext *ctx, LLVMBuilderRef builder)
 {
-       const char *icall_name = "mono_llvm_clear_exception";
-       const MonoJitICallId icall_id = MONO_JIT_ICALL_mono_llvm_clear_exception;
+       const MonoJitICallId icall_id = MONO_JIT_ICALL_mini_llvmonly_clear_exception;
 
        LLVMTypeRef call_sig = LLVMFunctionType (LLVMVoidType (), NULL, 0, FALSE);
        LLVMValueRef callee = NULL;
 
        if (!callee) {
-               if (ctx->cfg->compile_aot) {
-                       callee = get_callee (ctx, call_sig, MONO_PATCH_INFO_JIT_ICALL_ID, GUINT_TO_POINTER (icall_id));
-               } else {
-                       // FIXME: This is broken.
-                       callee = LLVMAddFunction (ctx->lmodule, icall_name, call_sig);
-               }
+               callee = get_callee (ctx, call_sig, MONO_PATCH_INFO_JIT_ICALL_ID, GUINT_TO_POINTER (icall_id));
        }
 
        g_assert (builder && callee);
@@ -4794,32 +4801,28 @@ mono_llvm_emit_clear_exception_call (EmitContext *ctx, LLVMBuilderRef builder)
 static LLVMValueRef
 mono_llvm_emit_load_exception_call (EmitContext *ctx, LLVMBuilderRef builder)
 {
-       const char *icall_name = "mono_llvm_load_exception";
-       const MonoJitICallId icall_id = MONO_JIT_ICALL_mono_llvm_load_exception;
+       const MonoJitICallId icall_id = MONO_JIT_ICALL_mini_llvmonly_load_exception;
 
        LLVMTypeRef call_sig = LLVMFunctionType (ObjRefType (), NULL, 0, FALSE);
        LLVMValueRef callee = NULL;
 
+       g_assert (ctx->cfg->compile_aot);
+
        if (!callee) {
-               if (ctx->cfg->compile_aot) {
-                       callee = get_callee (ctx, call_sig, MONO_PATCH_INFO_JIT_ICALL_ID, GUINT_TO_POINTER (icall_id));
-               } else {
-                       // FIXME: This is broken.
-                       callee = LLVMAddFunction (ctx->lmodule, icall_name, call_sig);
-               }
+               callee = get_callee (ctx, call_sig, MONO_PATCH_INFO_JIT_ICALL_ID, GUINT_TO_POINTER (icall_id));
        }
 
        g_assert (builder && callee);
 
-       return LLVMBuildCall (builder, callee, NULL, 0, icall_name);
+       return LLVMBuildCall (builder, callee, NULL, 0, "load_exception");
 }
 
 
 static LLVMValueRef
 mono_llvm_emit_match_exception_call (EmitContext *ctx, LLVMBuilderRef builder, gint32 region_start, gint32 region_end)
 {
-       const char *icall_name = "mono_llvm_match_exception";
-       const MonoJitICallId icall_id = MONO_JIT_ICALL_mono_llvm_match_exception;
+       const char *icall_name = "mini_llvmonly_match_exception";
+       const MonoJitICallId icall_id = MONO_JIT_ICALL_mini_llvmonly_match_exception;
 
        ctx->builder = builder;
 
@@ -4930,6 +4933,90 @@ emit_landing_pad (EmitContext *ctx, int group_index, int group_size)
        LLVMValueRef cast = LLVMBuildBitCast (lpadBuilder, ctx->module->sentinel_exception, LLVMPointerType (LLVMInt8Type (), 0), "int8TypeInfo");
        LLVMAddClause (landing_pad, cast);
 
+       if (ctx->cfg->deopt) {
+               /*
+                * Call mini_llvmonly_resume_exception_il_state (lmf, il_state)
+                *
+                *   The call will execute the catch clause and the rest of the method and store the return
+                * value into ctx->il_state_ret.
+                */
+               if (!ctx->has_catch) {
+                       /* Unused */
+                       LLVMBuildUnreachable (lpadBuilder);
+                       return lpad_bb;
+               }
+
+               const MonoJitICallId icall_id = MONO_JIT_ICALL_mini_llvmonly_resume_exception_il_state;
+               LLVMValueRef callee;
+               LLVMValueRef args [2];
+
+               LLVMTypeRef fun_sig = LLVMFunctionType2 (LLVMVoidType (), IntPtrType (), IntPtrType (), FALSE);
+
+               callee = get_callee (ctx, fun_sig, MONO_PATCH_INFO_JIT_ICALL_ID, GUINT_TO_POINTER (icall_id));
+
+               g_assert (ctx->cfg->lmf_var);
+               g_assert (ctx->addresses [ctx->cfg->lmf_var->dreg]);
+               args [0] = LLVMBuildPtrToInt (ctx->builder, ctx->addresses [ctx->cfg->lmf_var->dreg], IntPtrType (), "");
+               args [1] = LLVMBuildPtrToInt (ctx->builder, ctx->il_state, IntPtrType (), "");
+               emit_call (ctx, NULL, &ctx->builder, callee, args, 2);
+
+               /* Return the value set in ctx->il_state_ret */
+
+               LLVMTypeRef ret_type = LLVMGetReturnType (LLVMGetElementType (LLVMTypeOf (ctx->lmethod)));
+               LLVMBuilderRef builder = ctx->builder;
+               LLVMValueRef addr, retval, gep, indexes [2];
+
+               switch (ctx->linfo->ret.storage) {
+               case LLVMArgNone:
+                       LLVMBuildRetVoid (builder);
+                       break;
+               case LLVMArgNormal:
+               case LLVMArgWasmVtypeAsScalar:
+               case LLVMArgVtypeInReg: {
+                       if (ctx->sig->ret->type == MONO_TYPE_VOID) {
+                               LLVMBuildRetVoid (builder);
+                               break;
+                       }
+
+                       addr = ctx->il_state_ret;
+                       g_assert (addr);
+
+                       addr = convert (ctx, ctx->il_state_ret, LLVMPointerType (ret_type, 0));
+                       indexes [0] = LLVMConstInt (LLVMInt32Type (), 0, FALSE);
+                       indexes [1] = LLVMConstInt (LLVMInt32Type (), 0, FALSE);
+                       gep = LLVMBuildGEP (builder, addr, indexes, 1, "");
+
+                       LLVMBuildRet (builder, LLVMBuildLoad (builder, gep, ""));
+                       break;
+               }
+               case LLVMArgVtypeRetAddr: {
+                       LLVMValueRef ret_addr;
+
+                       g_assert (cfg->vret_addr);
+                       ret_addr = ctx->values [cfg->vret_addr->dreg];
+
+                       addr = ctx->il_state_ret;
+                       g_assert (addr);
+
+                       /* The ret value is in il_state_ret, copy it to the memory pointed to by the vret arg */
+                       ret_type = type_to_llvm_type (ctx, ctx->sig->ret);
+                       indexes [0] = LLVMConstInt (LLVMInt32Type (), 0, FALSE);
+                       indexes [1] = LLVMConstInt (LLVMInt32Type (), 0, FALSE);
+                       gep = LLVMBuildGEP (builder, addr, indexes, 1, "");
+                       retval = convert (ctx, LLVMBuildLoad (builder, gep, ""), ret_type);
+
+                       LLVMBuildStore (builder, retval, convert (ctx, ret_addr, LLVMPointerType (ret_type, 0)));
+                       LLVMBuildRetVoid (builder);
+                       break;
+               }
+               default:
+                       g_assert_not_reached ();
+                       break;
+               }
+
+               return lpad_bb;
+       }
+
        LLVMBasicBlockRef resume_bb = gen_bb (ctx, "RESUME_BB");
        LLVMBuilderRef resume_builder = create_builder (ctx);
        ctx->builder = resume_builder;
@@ -5290,7 +5377,7 @@ emit_llvmonly_handler_start (EmitContext *ctx, MonoBasicBlock *bb, LLVMBasicBloc
        }
 
 #ifdef TARGET_WASM
-       if (ctx->cfg->lmf_var) {
+       if (ctx->cfg->lmf_var && !ctx->cfg->deopt) {
                LLVMValueRef callee;
                LLVMValueRef args [1];
                LLVMTypeRef sig = LLVMFunctionType1 (LLVMVoidType (), ctx->module->ptr_type, FALSE);
@@ -11192,6 +11279,12 @@ process_bb (EmitContext *ctx, MonoBasicBlock *bb)
                        has_terminator = TRUE;
                        break;
                }
+               case OP_ENDFILTER: {
+                       g_assert (cfg->llvm_only && cfg->deopt);
+                       LLVMBuildUnreachable (builder);
+                       has_terminator = TRUE;
+                       break;
+               }
                case OP_IL_SEQ_POINT:
                        break;
                default: {
@@ -11756,7 +11849,7 @@ emit_method_inner (EmitContext *ctx)
                clause = &header->clauses [i];
                if (clause->flags != MONO_EXCEPTION_CLAUSE_FINALLY && clause->flags != MONO_EXCEPTION_CLAUSE_FAULT && clause->flags != MONO_EXCEPTION_CLAUSE_NONE) {
                        if (cfg->llvm_only) {
-                               if (!cfg->interp_entry_only)
+                               if (!cfg->deopt && !cfg->interp_entry_only)
                                        llvmonly_fail = TRUE;
                        } else {
                                set_failure (ctx, "non-finally/catch/fault clause.");
@@ -11768,6 +11861,12 @@ emit_method_inner (EmitContext *ctx)
                /* We can't handle inlined methods with clauses */
                mono_llvm_add_func_attr (method, LLVM_ATTR_NO_INLINE);
 
+       for (int i = 0; i < cfg->header->num_clauses; i++) {
+               MonoExceptionClause *clause = &cfg->header->clauses [i];
+               if (clause->flags == MONO_EXCEPTION_CLAUSE_NONE || clause->flags == MONO_EXCEPTION_CLAUSE_FILTER)
+                       ctx->has_catch = TRUE;
+       }
+
        if (linfo->rgctx_arg) {
                ctx->rgctx_arg = LLVMGetParam (method, linfo->rgctx_arg_pindex);
                ctx->rgctx_arg_pindex = linfo->rgctx_arg_pindex;
@@ -11980,6 +12079,9 @@ emit_method_inner (EmitContext *ctx)
                if (ctx->cfg->interp_entry_only || !(bb->region != -1 && (bb->flags & BB_EXCEPTION_HANDLER)))
                        continue;
 
+               if (ctx->cfg->deopt && MONO_REGION_FLAGS (bb->region) == MONO_EXCEPTION_CLAUSE_FILTER)
+                       continue;
+
                clause_index = MONO_REGION_CLAUSE_INDEX (bb->region);
                g_hash_table_insert (ctx->region_to_handler, GUINT_TO_POINTER (mono_get_block_region_notry (cfg, bb->region)), bb);
                g_hash_table_insert (ctx->clause_to_handler, GINT_TO_POINTER (clause_index), bb);
index 697eacd..e9f4664 100644 (file)
@@ -4691,13 +4691,15 @@ register_icalls (void)
        g_assert (mono_get_lmf_addr == mono_tls_get_lmf_addr);
        register_icall (mono_domain_get, mono_icall_sig_ptr, TRUE);
 
-       register_icall (mono_llvm_throw_exception, mono_icall_sig_void_object, TRUE);
-       register_icall (mono_llvm_rethrow_exception, mono_icall_sig_void_object, TRUE);
-       register_icall (mono_llvm_resume_exception, mono_icall_sig_void, TRUE);
-       register_icall (mono_llvm_match_exception, mono_icall_sig_int_ptr_int_int_ptr_object, TRUE);
-       register_icall (mono_llvm_clear_exception, NULL, TRUE);
-       register_icall (mono_llvm_load_exception, mono_icall_sig_object, TRUE);
-       register_icall (mono_llvm_throw_corlib_exception, mono_icall_sig_void_int, TRUE);
+       register_icall (mini_llvmonly_throw_exception, mono_icall_sig_void_object, TRUE);
+       register_icall (mini_llvmonly_rethrow_exception, mono_icall_sig_void_object, TRUE);
+       register_icall (mini_llvmonly_throw_corlib_exception, mono_icall_sig_void_int, TRUE);
+       register_icall (mini_llvmonly_resume_exception, mono_icall_sig_void, TRUE);
+       register_icall (mini_llvmonly_resume_exception_il_state, mono_icall_sig_void_ptr_ptr, TRUE);
+       register_icall (mini_llvmonly_load_exception, mono_icall_sig_object, TRUE);
+       register_icall (mini_llvmonly_clear_exception, NULL, TRUE);
+       register_icall (mini_llvmonly_match_exception, mono_icall_sig_int_ptr_int_int_ptr_object, TRUE);
+
 #if defined(ENABLE_LLVM) && defined(HAVE_UNWIND_H)
        register_icall (mono_llvm_set_unhandled_exception_handler, NULL, TRUE);
 
index 4f34879..df19460 100644 (file)
@@ -129,6 +129,8 @@ typedef struct {
        gpointer        ex_obj;
        MonoLMF *lmf;
        int first_filter_idx, filter_idx;
+       /* MonoMethodILState */
+       gpointer il_state;
 } ResumeState;
 
 typedef void (*MonoAbortFunction)(MonoObject*);
index 3f68439..e2295a9 100644 (file)
@@ -3068,32 +3068,6 @@ mini_get_rgctx_access_for_method (MonoMethod *method)
        return MONO_RGCTX_ACCESS_THIS;
 }
 
-static gboolean
-can_deopt (MonoCompile *cfg)
-{
-       MonoMethodHeader *header = cfg->header;
-
-       if (!header->num_clauses)
-               return FALSE;
-
-       /*
-        * Currently, only finally/fault clauses are supported.
-        * Catch clauses are hard to support, since even if the
-        * rest of the method is executed by the interpreter, execution needs to
-        * return to the caller which is an AOTed method.
-        * Filter clauses could be supported, but every filter clause has an
-        * associated catch clause.
-        */
-       for (int i = 0; i < header->num_clauses; ++i) {
-               MonoExceptionClause *clause = &header->clauses [i];
-
-               if (clause->flags != MONO_EXCEPTION_CLAUSE_FINALLY && clause->flags != MONO_EXCEPTION_CLAUSE_FAULT)
-                       return FALSE;
-       }
-
-       return TRUE;
-}
-
 /*
  * mini_method_compile:
  * @method: the method to compile
@@ -3316,7 +3290,7 @@ mini_method_compile (MonoMethod *method, guint32 opts, JitFlags flags, int parts
                return cfg;
        }
 
-       if (cfg->llvm_only && cfg->interp && !cfg->interp_entry_only && header->num_clauses && can_deopt (cfg)) {
+       if (cfg->llvm_only && cfg->interp && !cfg->interp_entry_only && header->num_clauses) {
                cfg->deopt = TRUE;
                /* Can't reconstruct inlined state */
                cfg->disable_inline = TRUE;
index 1683ca4..a81aff3 100644 (file)
@@ -2579,16 +2579,6 @@ MONO_API gboolean mono_exception_walk_trace     (MonoException *ex, MonoExceptio
 MONO_COMPONENT_API void mono_restore_context                       (MonoContext *ctx);
 guint8* mono_jinfo_get_unwind_info              (MonoJitInfo *ji, guint32 *unwind_info_len);
 int  mono_jinfo_get_epilog_size                 (MonoJitInfo *ji);
-G_EXTERN_C void mono_llvm_rethrow_exception     (MonoObject *ex);
-G_EXTERN_C void mono_llvm_throw_exception       (MonoObject *ex);
-G_EXTERN_C void mono_llvm_throw_corlib_exception (guint32 ex_token_index);
-G_EXTERN_C void mono_llvm_resume_exception      (void);
-G_EXTERN_C void mono_llvm_clear_exception       (void);
-G_EXTERN_C MonoObject *mono_llvm_load_exception (void);
-void     mono_llvm_reset_exception              (void);
-void     mono_llvm_raise_exception              (MonoException *e);
-void     mono_llvm_reraise_exception            (MonoException *e);
-G_EXTERN_C gint32 mono_llvm_match_exception     (MonoJitInfo *jinfo, guint32 region_start, guint32 region_end, gpointer rgctx, MonoObject *this_obj);
 
 gboolean
 mono_find_jit_info_ext (MonoJitTlsData *jit_tls,
index bab3c8c..535cc53 100644 (file)
@@ -8,5 +8,6 @@ endif
 
 PROJECT_NAME=Wasm.Console.Bench.Sample.csproj
 CONSOLE_DLL=Wasm.Console.Bench.Sample.dll
+MAIN_JS=test-main.js
 
 run: run-console
index ab8a632..1bd4210 100644 (file)
@@ -4,6 +4,7 @@
     <WasmMainJSPath>$(MonoProjectRoot)\wasm\test-main.js</WasmMainJSPath>
     <WasmGenerateRunV8Script>true</WasmGenerateRunV8Script>
     <SuppressTrimAnalysisWarnings>true</SuppressTrimAnalysisWarnings>
+    <WasmNativeStrip>false</WasmNativeStrip>
   </PropertyGroup>
 
   <PropertyGroup>