Quellcode durchsuchen

[runtime] Store the interpreter method invoked by the delegate in MonoDelegate.interp_method, make MonoDelegate.method_ptr always point to a JIT callable entry point. (#9019)

* [runtime] Add an 'interp_method' field to MonoDelegate.

* [runtime] Add a MONO_LD_DELEGATE_PTR jit opcode which is used to load the method ptr from a delegate, so the JIT and interpreter could have a different implementation.

* [runtime] Store the interpreter method invoked by the delegate in MonoDelegate.interp_method, make MonoDelegate.method_ptr always point to a JIT callable entry point.

* [interp] Avoid sharing pinvoke and non-pinvoke signatures in mini_get_interp_in_wrapper ().

* [interp] Fix interp_get_remoting_invoke (), it receives a interp entry trampoline as well.

* [interp] Store remoting invokes into interp_method in delegates.

* [interp] Handle compilation failures in do_jit_icall (). Handle calls to delegates created in JITted code.

* [jit] Add a 'aot-skip=' MONO_DEBUG option to skip loading the Nth aot loadable method, useful for forcing mixed mode transitions during testing.

* [jit] Add tests for fullaot+interp mode

The tests work by injecting interp-jit transitions in varying locations controlled by the MONO_DEBUG=aot-skip= option.

* [interp] Avoid races when caching method pointers. Fix wasm support.

* Revert "[interp] Avoid races when caching method pointers. Fix wasm support."

This reverts commit 0f78c230b5102d625c300a4e1c6eaf97ad055dd5.

* [interp] stub native-to-managed on platforms where it is not working

* [ci] add fullaotmixed-regression step
Zoltan Varga vor 7 Jahren
Ursprung
Commit
1a91bb9939

+ 1 - 1
configure.ac

@@ -40,7 +40,7 @@ MONO_VERSION_BUILD=`echo $VERSION | cut -d . -f 3`
 # This can be reset to 0 when Mono's version number is bumped
 # since it's part of the corlib version (the prefix '1' in the full
 # version number is to ensure the number isn't treated as octal in C)
-MONO_CORLIB_COUNTER=9
+MONO_CORLIB_COUNTER=10
 MONO_CORLIB_VERSION=`printf "1%02d%02d%02d%03d" $MONO_VERSION_MAJOR $MONO_VERSION_MINOR 0 $MONO_CORLIB_COUNTER`
 
 AC_DEFINE_UNQUOTED(MONO_CORLIB_VERSION,$MONO_CORLIB_VERSION,[Version of the corlib-runtime interface])

+ 1 - 0
mcs/class/corlib/System/Delegate.cs

@@ -62,6 +62,7 @@ namespace System
 		private IntPtr delegate_trampoline;
 		private IntPtr extra_arg;
 		private IntPtr method_code;
+		private IntPtr interp_method;
 		private MethodInfo method_info;
 
 		// Keep a ref of the MethodInfo passed to CreateDelegate.

+ 1 - 0
mono/cil/cil-opcodes.xml

@@ -322,4 +322,5 @@
 <opcode name="mono_get_last_error" input="Pop0" output="PushI" args="InlineNone" o1="0xF0" o2="0x1B" flow="next" />
 <opcode name="mono_get_rgctx_arg" input="Pop0" output="PushI" args="InlineNone" o1="0xF0" o2="0x1C" flow="next" />
 <opcode name="mono_ldptr_profiler_allocation_count" input="Pop0" output="PushI" args="InlineNone" o1="0xF0" o2="0x1D" flow="next" />
+<opcode name="mono_ld_delegate_method_ptr" input="Pop1" output="PushI" args="InlineNone" o1="0xF0" o2="0x1E" flow="next" />
 </opdesc>

+ 1 - 0
mono/cil/opcode.def

@@ -322,6 +322,7 @@ OPDEF(CEE_MONO_ATOMIC_STORE_I4, "mono_atomic_store_i4", PopI+PopI, Push0, Inline
 OPDEF(CEE_MONO_GET_LAST_ERROR, "mono_get_last_error", Pop0, PushI, InlineNone, 0, 2, 0xF0, 0x1B, NEXT)
 OPDEF(CEE_MONO_GET_RGCTX_ARG, "mono_get_rgctx_arg", Pop0, PushI, InlineNone, 0, 2, 0xF0, 0x1C, NEXT)
 OPDEF(CEE_MONO_LDPTR_PROFILER_ALLOCATION_COUNT, "mono_ldptr_profiler_allocation_count", Pop0, PushI, InlineNone, 0, 2, 0xF0, 0x1D, NEXT)
+OPDEF(CEE_MONO_LD_DELEGATE_METHOD_PTR, "mono_ld_delegate_method_ptr", Pop1, PushI, InlineNone, 0, 2, 0xF0, 0x1E, NEXT)
 #ifndef OPALIAS
 #define _MONO_CIL_OPALIAS_DEFINED_
 #define OPALIAS(a,s,r)

+ 4 - 4
mono/metadata/marshal-ilgen.c

@@ -3804,8 +3804,8 @@ emit_delegate_invoke_internal_ilgen (MonoMethodBuilder *mb, MonoMethodSignature
 			mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoDelegate, extra_arg));
 			mono_mb_emit_byte (mb, CEE_LDIND_I);
 			mono_mb_emit_ldarg (mb, 0);
-			mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoDelegate, method_ptr));
-			mono_mb_emit_byte (mb, CEE_LDIND_I);
+			mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
+			mono_mb_emit_byte (mb, CEE_MONO_LD_DELEGATE_METHOD_PTR);
 			mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
 			mono_mb_emit_op (mb, CEE_MONO_CALLI_EXTRA_ARG, sig);
 			mono_mb_emit_byte (mb, CEE_RET);
@@ -3847,8 +3847,8 @@ emit_delegate_invoke_internal_ilgen (MonoMethodBuilder *mb, MonoMethodSignature
 		mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoDelegate, extra_arg));
 		mono_mb_emit_byte (mb, CEE_LDIND_I);
 		mono_mb_emit_ldarg (mb, 0);
-		mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoDelegate, method_ptr));
-		mono_mb_emit_byte (mb, CEE_LDIND_I);
+		mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
+		mono_mb_emit_byte (mb, CEE_MONO_LD_DELEGATE_METHOD_PTR);
 		mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
 		mono_mb_emit_op (mb, CEE_MONO_CALLI_EXTRA_ARG, invoke_sig);
 	}

+ 1 - 0
mono/metadata/object-internals.h

@@ -801,6 +801,7 @@ struct _MonoDelegate {
 	 * the compiled code of the method, or NULL if it is not yet compiled.
 	 */
 	guint8 **method_code;
+	gpointer interp_method;
 	MonoReflectionMethod *method_info;
 	MonoReflectionMethod *original_method_info;
 	MonoObject *data;

+ 1 - 1
mono/metadata/object.c

@@ -8194,7 +8194,7 @@ mono_delegate_ctor_with_method (MonoObjectHandle this_obj, MonoObjectHandle targ
 #ifndef DISABLE_REMOTING
 	if (!MONO_HANDLE_IS_NULL (target) && mono_class_is_transparent_proxy (mono_handle_class (target))) {
 		if (callbacks.interp_get_remoting_invoke) {
-			MONO_HANDLE_SETVAL (delegate, method_ptr, gpointer, callbacks.interp_get_remoting_invoke (addr, error));
+			MONO_HANDLE_SETVAL (delegate, interp_method, gpointer, callbacks.interp_get_remoting_invoke (addr, error));
 		} else {
 			g_assert (method);
 			method = mono_marshal_get_remoting_invoke (method, error);

+ 5 - 1
mono/mini/Makefile.am.in

@@ -809,6 +809,9 @@ gsharedvtcheck:
 hybridcheck:
 	$(MAKE) fullaotcheck HYBRID=1
 
+fullaotmixedcheck:
+	$(MAKE) fullaotcheck MIXED=1
+
 fullaot_regtests = $(regtests)
 fullaot_testing_deps = generics-variant-types.dll TestDriver.dll MemoryIntrinsics.dll
 fullaot_testing_deps_commas = generics-variant-types.dll,TestDriver.dll,MemoryIntrinsics.dll
@@ -842,7 +845,7 @@ FULLAOT_LIBS = $(filter-out $(FULLAOT_LIBS_DISABLED),$(FULLAOT_LIBS_UNIVERSAL))
 
 FULLAOT_TMP_DIR=$(top_builddir)/mono/mini/fullaot-tmp
 
-FULLAOT_AOT_ARGS=$(if $(HYBRID),hybrid,full)
+FULLAOT_AOT_ARGS=$(if $(HYBRID),hybrid,full,interp)
 FULLAOT_ARGS=$(if $(HYBRID),--hybrid-aot,--full-aot)
 
 # This currently only works on amd64/arm
@@ -854,6 +857,7 @@ fullaotcheck: $(mono) $(fullaot_regtests) $(fullaot_testing_deps)
 	MONO_PATH=$(FULLAOT_TMP_DIR) $(top_builddir)/runtime/mono-wrapper $(MOBILE_RUNTIME_ARG) $(LLVM_AOT_RUNTIME_OPTS) $(GSHAREDVT_RUNTIME_OPTS) --aot="$(FULLAOT_AOT_ARGS),$(MONO_FULLAOT_ADDITIONAL_ARGS)$(INVARIANT_AOT_OPTIONS)" $(FULLAOT_TMP_DIR)/{$(fullaot_testing_deps_commas),*.exe} || exit 1
 	ln -s $(if $(MONO_EXECUTABLE),$(MONO_EXECUTABLE),$$PWD/mono) $(FULLAOT_TMP_DIR)/
 	for i in $(fullaot_regtests); do echo $$i; MONO_PATH=$(FULLAOT_TMP_DIR) $(top_builddir)/runtime/mono-wrapper $(MOBILE_RUNTIME_ARG) $(FULLAOT_ARGS) $(FULLAOT_TMP_DIR)/$$i --exclude '!FULLAOT' $(ARCH_FULLAOT_EXCLUDE) || exit 1; done
+	if test x$(MIXED) == x1; then failed=0;i=0; while test $$i -lt 900; do i=`expr $$i + 1`; bash -c "echo -n '.'"; MONO_PATH=$(FULLAOT_TMP_DIR) MONO_DEBUG=aot-skip=$$i ./mono --full-aot-interp $(FULLAOT_TMP_DIR)/basic.exe > $(FULLAOT_TMP_DIR)/mixed.log || failed=1; if test $$failed -eq 1; then echo "Failed at $$i"; break; fi; done; fi
 
 # This can run in parallel
 fullaot-libs: $(patsubst %,fullaot-tmp/%.dylib,$(FULLAOT_LIBS))

+ 19 - 0
mono/mini/aot-runtime.c

@@ -4063,6 +4063,25 @@ load_method (MonoDomain *domain, MonoAotModule *amodule, MonoImage *image, MonoM
 	if ((amodule->methods_loaded [method_index / 32] >> (method_index % 32)) & 0x1)
 		return code;
 
+	if (mini_debug_options.aot_skip_set && !(method && method->wrapper_type)) {
+		gint32 methods_aot = mono_atomic_load_i32 (&mono_jit_stats.methods_aot);
+		if (methods_aot == mini_debug_options.aot_skip) {
+			if (!method) {
+				method = mono_get_method_checked (image, token, NULL, NULL, error);
+				if (!method)
+					return NULL;
+			}
+			if (method) {
+				char *name = mono_method_full_name (method, TRUE);
+				g_print ("NON AOT METHOD: %s.\n", name);
+				g_free (name);
+			} else {
+				g_print ("NON AOT METHOD: %p %d\n", code, method_index);
+			}
+			return NULL;
+		}
+	}
+
 	if (mono_last_aot_method != -1) {
 		gint32 methods_aot = mono_atomic_load_i32 (&mono_jit_stats.methods_aot);
 		if (methods_aot >= mono_last_aot_method)

+ 2 - 2
mono/mini/ee.h

@@ -15,7 +15,7 @@
 #ifndef __MONO_EE_H__
 #define __MONO_EE_H__
 
-#define MONO_EE_API_VERSION 0x3
+#define MONO_EE_API_VERSION 0x4
 
 typedef struct _MonoInterpStackIter MonoInterpStackIter;
 
@@ -31,8 +31,8 @@ struct _MonoEECallbacks {
 	gpointer (*create_method_pointer) (MonoMethod *method, MonoError *error);
 	MonoObject* (*runtime_invoke) (MonoMethod *method, void *obj, void **params, MonoObject **exc, MonoError *error);
 	void (*init_delegate) (MonoDelegate *del);
+	void (*delegate_ctor) (MonoObjectHandle this_obj, MonoObjectHandle target, gpointer addr, MonoError *error);
 	gpointer (*get_remoting_invoke) (gpointer imethod, MonoError *error);
-	gpointer (*create_trampoline) (MonoDomain *domain, MonoMethod *method, MonoError *error);
 	void (*set_resume_state) (MonoJitTlsData *jit_tls, MonoException *ex, MonoJitExceptionInfo *ei, MonoInterpFrameHandle interp_frame, gpointer handler_ip);
 	gboolean (*run_finally) (StackFrameInfo *frame, int clause_index, gpointer handler_ip);
 	gboolean (*run_filter) (StackFrameInfo *frame, MonoException *ex, int clause_index, gpointer handler_ip);

+ 4 - 5
mono/mini/interp-stubs.c

@@ -133,15 +133,14 @@ stub_get_remoting_invoke (gpointer imethod, MonoError *error)
 	return NULL;
 }
 
-static gpointer
-stub_create_trampoline (MonoDomain *domain, MonoMethod *method, MonoError *error)
+static void
+stub_walk_stack_with_ctx (MonoInternalStackWalk func, MonoContext *ctx, MonoUnwindOptions options, void *user_data)
 {
 	g_assert_not_reached ();
-	return NULL;
 }
 
 static void
-stub_walk_stack_with_ctx (MonoInternalStackWalk func, MonoContext *ctx, MonoUnwindOptions options, void *user_data)
+stub_delegate_ctor (MonoObjectHandle this_obj, MonoObjectHandle target, gpointer addr, MonoError *error)
 {
 	g_assert_not_reached ();
 }
@@ -158,7 +157,6 @@ mono_interp_stub_init (void)
 	c.runtime_invoke = stub_runtime_invoke;
 	c.init_delegate = stub_init_delegate;
 	c.get_remoting_invoke = stub_get_remoting_invoke;
-	c.create_trampoline = stub_create_trampoline;
 	c.set_resume_state = stub_set_resume_state;
 	c.run_finally = stub_run_finally;
 	c.run_filter = stub_run_filter;
@@ -175,5 +173,6 @@ mono_interp_stub_init (void)
 	c.frame_get_parent = stub_frame_get_parent;
 	c.start_single_stepping = stub_start_single_stepping;
 	c.stop_single_stepping = stub_stop_single_stepping;
+	c.delegate_ctor = stub_delegate_ctor;
 	mini_install_interp_callbacks (&c);
 }

+ 110 - 26
mono/mini/interp/interp.c

@@ -117,6 +117,7 @@ static gboolean interp_init_done = FALSE;
 static char* dump_frame (InterpFrame *inv);
 static MonoArray *get_trace_ips (MonoDomain *domain, InterpFrame *top);
 static void interp_exec_method_full (InterpFrame *frame, ThreadContext *context, guint16 *start_with_ip, MonoException *filter_exception, int exit_at_finally, InterpFrame *base_frame);
+static InterpMethod* lookup_method_pointer (gpointer addr);
 
 typedef void (*ICallMethod) (InterpFrame *frame);
 
@@ -276,14 +277,15 @@ lookup_imethod (MonoDomain *domain, MonoMethod *method)
 }
 
 static gpointer
-interp_get_remoting_invoke (gpointer imethod, MonoError *error)
+interp_get_remoting_invoke (gpointer addr, MonoError *error)
 {
 #ifndef DISABLE_REMOTING
-	InterpMethod *imethod_cast = (InterpMethod*) imethod;
+	InterpMethod *imethod = lookup_method_pointer (addr);
 
+	g_assert (imethod);
 	g_assert (mono_use_interpreter);
 
-	MonoMethod *remoting_invoke_method = mono_marshal_get_remoting_invoke (imethod_cast->method, error);
+	MonoMethod *remoting_invoke_method = mono_marshal_get_remoting_invoke (imethod->method, error);
 	return_val_if_nok (error, NULL);
 	return mono_interp_get_imethod (mono_domain_get (), remoting_invoke_method, error);
 #else
@@ -332,14 +334,6 @@ mono_interp_get_imethod (MonoDomain *domain, MonoMethod *method, MonoError *erro
 	return rtm;
 }
 
-static gpointer
-interp_create_trampoline (MonoDomain *domain, MonoMethod *method, MonoError *error)
-{
-	if (method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)
-		method = mono_marshal_get_synchronized_wrapper (method);
-	return mono_interp_get_imethod (domain, method, error);
-}
-
 /*
  * interp_push_lmf:
  *
@@ -1220,13 +1214,45 @@ ves_pinvoke_method (InterpFrame *frame, MonoMethodSignature *sig, MonoFuncV addr
 #endif
 }
 
+/*
+ * interp_init_delegate:
+ *
+ *   Initialize del->interp_method.
+ */
 static void
 interp_init_delegate (MonoDelegate *del)
 {
-	if (del->method)
-		return;
-	/* shouldn't need a write barrier because we don't write a MonoObject into the field */
-	del->method = ((InterpMethod *) del->method_ptr)->method;
+	if (del->interp_method) {
+		/* Delegate created by a call to ves_icall_mono_delegate_ctor_interp () */
+		del->method = ((InterpMethod *)del->interp_method)->method;
+	} else if (del->method) {
+		/* Delegate created dynamically */
+		ERROR_DECL (error);
+		del->interp_method = mono_interp_get_imethod (del->object.vtable->domain, del->method, error);
+		mono_error_assert_ok (error);
+	} else {
+		/* Created from JITted code */
+		g_assert (del->method_ptr);
+		del->interp_method = lookup_method_pointer (del->method_ptr);
+		g_assert (del->interp_method);
+	}
+}
+
+static void
+interp_delegate_ctor (MonoObjectHandle this_obj, MonoObjectHandle target, gpointer addr, MonoError *error)
+{
+	/*
+	 * addr is the result of an LDFTN opcode, i.e. an InterpMethod
+	 */
+	InterpMethod *imethod = (InterpMethod*)addr;
+
+	g_assert (imethod->method);
+	gpointer entry = mini_get_interp_callbacks ()->create_method_pointer (imethod->method, error);
+	return_if_nok (error);
+
+	MONO_HANDLE_SETVAL (MONO_HANDLE_CAST (MonoDelegate, this_obj), interp_method, gpointer, imethod);
+
+	mono_delegate_ctor (this_obj, target, entry, error);
 }
 
 /*
@@ -1800,7 +1826,7 @@ do_icall (ThreadContext *context, int op, stackval *sp, gpointer ptr)
 }
 
 static MONO_NEVER_INLINE stackval *
-do_jit_call (stackval *sp, unsigned char *vt_sp, ThreadContext *context, InterpFrame *frame, InterpMethod *rmethod)
+do_jit_call (stackval *sp, unsigned char *vt_sp, ThreadContext *context, InterpFrame *frame, InterpMethod *rmethod, MonoError *error)
 {
 	MonoMethodSignature *sig;
 	MonoFtnDesc ftndesc;
@@ -1816,7 +1842,6 @@ do_jit_call (stackval *sp, unsigned char *vt_sp, ThreadContext *context, InterpF
 	 */
 	if (!rmethod->jit_wrapper) {
 		MonoMethod *method = rmethod->method;
-		ERROR_DECL (error);
 
 		sig = mono_method_signature (method);
 		g_assert (sig);
@@ -1828,8 +1853,8 @@ do_jit_call (stackval *sp, unsigned char *vt_sp, ThreadContext *context, InterpF
 		mono_error_assert_ok (error);
 
 		gpointer addr = mono_jit_compile_method_jit_only (method, error);
+		return_val_if_nok (error, NULL);
 		g_assert (addr);
-		mono_error_assert_ok (error);
 
 		rmethod->jit_addr = addr;
 		rmethod->jit_sig = sig;
@@ -2314,6 +2339,29 @@ interp_entry_from_trampoline (gpointer ccontext_untyped, gpointer rmethod_untype
 }
 #endif
 
+static InterpMethod*
+lookup_method_pointer (gpointer addr)
+{
+	MonoDomain *domain = mono_domain_get ();
+	MonoJitDomainInfo *info = domain_jit_info (domain);
+	InterpMethod *res = NULL;
+
+	mono_domain_lock (domain);
+	if (info->interp_method_pointer_hash)
+		res = g_hash_table_lookup (info->interp_method_pointer_hash, addr);
+	mono_domain_unlock (domain);
+
+	return res;
+}
+
+#ifndef MONO_ARCH_HAVE_INTERP_NATIVE_TO_MANAGED
+static void
+interp_no_native_to_managed (void)
+{
+	g_error ("interpreter: native-to-managed transition not available on this platform");
+}
+#endif
+
 /*
  * interp_create_method_pointer:
  *
@@ -2323,16 +2371,21 @@ interp_entry_from_trampoline (gpointer ccontext_untyped, gpointer rmethod_untype
 static gpointer
 interp_create_method_pointer (MonoMethod *method, MonoError *error)
 {
+#ifndef MONO_ARCH_HAVE_INTERP_NATIVE_TO_MANAGED
+	return interp_no_native_to_managed;
+#else
 	gpointer addr, entry_func, entry_wrapper;
-	InterpMethod *rmethod = mono_interp_get_imethod (mono_domain_get (), method, error);
+	MonoDomain *domain = mono_domain_get ();
+	MonoJitDomainInfo *info;
+	InterpMethod *imethod = mono_interp_get_imethod (domain, method, error);
 
 	/* HACK: method_ptr of delegate should point to a runtime method*/
 	if (method->wrapper_type && (method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD ||
 				(method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE)))
-		return rmethod;
+		return imethod;
 
-	if (rmethod->jit_entry)
-		return rmethod->jit_entry;
+	if (imethod->jit_entry)
+		return imethod->jit_entry;
 
 	MonoMethodSignature *sig = mono_method_signature (method);
 #ifndef MONO_ARCH_HAVE_INTERP_ENTRY_TRAMPOLINE
@@ -2377,7 +2430,7 @@ interp_create_method_pointer (MonoMethod *method, MonoError *error)
 	/* This is the argument passed to the interp_in wrapper by the static rgctx trampoline */
 	MonoFtnDesc *ftndesc = g_new0 (MonoFtnDesc, 1);
 	ftndesc->addr = entry_func;
-	ftndesc->arg = rmethod;
+	ftndesc->arg = imethod;
 	mono_error_assert_ok (error);
 
 	/*
@@ -2387,10 +2440,18 @@ interp_create_method_pointer (MonoMethod *method, MonoError *error)
 
 	addr = mono_create_ftnptr_arg_trampoline (ftndesc, entry_wrapper);
 
+	info = domain_jit_info (domain);
+	mono_domain_lock (domain);
+	if (!info->interp_method_pointer_hash)
+		info->interp_method_pointer_hash = g_hash_table_new (NULL, NULL);
+	g_hash_table_insert (info->interp_method_pointer_hash, addr, imethod);
+	mono_domain_unlock (domain);
+
 	mono_memory_barrier ();
-	rmethod->jit_entry = addr;
+	imethod->jit_entry = addr;
 
 	return addr;
+#endif
 }
 
 #if COUNT_OPS
@@ -2872,9 +2933,14 @@ interp_exec_method_full (InterpFrame *frame, ThreadContext *context, guint16 *st
 		}
 		MINT_IN_CASE(MINT_JIT_CALL) {
 			InterpMethod *rmethod = rtm->data_items [* (guint16 *)(ip + 1)];
+			ERROR_DECL (error);
 			frame->ip = ip;
 			ip += 2;
-			sp = do_jit_call (sp, vt_sp, context, frame, rmethod);
+			sp = do_jit_call (sp, vt_sp, context, frame, rmethod, error);
+			if (!is_ok (error)) {
+				MonoException *ex = mono_error_convert_to_exception (error);
+				THROW_EX (ex, ip);
+			}
 
 			if (context->has_resume_state) {
 				/*
@@ -5183,6 +5249,24 @@ array_constructed:
 			frame->ex_handler = NULL;
 			THROW_EX_GENERAL (*(MonoException**)(frame->locals + exvar_offset), ip - 1, TRUE);
 			MINT_IN_BREAK;
+	   }
+	   MINT_IN_CASE(MINT_LD_DELEGATE_METHOD_PTR) {
+		   MonoDelegate *del;
+
+		   --sp;
+		   del = (MonoDelegate*)sp->data.p;
+		   if (!del->interp_method) {
+			   /* Not created from interpreted code */
+			   ERROR_DECL (error);
+			   g_assert (del->method);
+			   del->interp_method = mono_interp_get_imethod (del->object.vtable->domain, del->method, error);
+			   mono_error_assert_ok (error);
+		   }
+		   g_assert (del->interp_method);
+		   sp->data.p = del->interp_method;
+		   ++sp;
+		   ip += 1;
+		   MINT_IN_BREAK;
 	   }
 		MINT_IN_DEFAULT
 			g_print ("Unimplemented opcode: %04x %s at 0x%x\n", *ip, mono_interp_opname[*ip], ip-rtm->code);
@@ -5553,8 +5637,8 @@ mono_ee_interp_init (const char *opts)
 	c.create_method_pointer = interp_create_method_pointer;
 	c.runtime_invoke = interp_runtime_invoke;
 	c.init_delegate = interp_init_delegate;
+	c.delegate_ctor = interp_delegate_ctor;
 	c.get_remoting_invoke = interp_get_remoting_invoke;
-	c.create_trampoline = interp_create_trampoline;
 	c.set_resume_state = interp_set_resume_state;
 	c.run_finally = interp_run_finally;
 	c.run_filter = interp_run_filter;

+ 1 - 0
mono/mini/interp/mintops.def

@@ -558,6 +558,7 @@ OPDEF(MINT_JIT_CALL, "mono_jit_call", 2, MintOpNoArgs)
 OPDEF(MINT_SDB_INTR_LOC, "sdb_intr_loc", 1, MintOpNoArgs)
 OPDEF(MINT_SDB_SEQ_POINT, "sdb_seq_point", 1, MintOpNoArgs)
 OPDEF(MINT_SDB_BREAKPOINT, "sdb_breakpoint", 1, MintOpNoArgs)
+OPDEF(MINT_LD_DELEGATE_METHOD_PTR, "ld_delegate_method_ptr", 1, MintOpNoArgs)
 
 OPDEF(MINT_START_ABORT_PROT, "start_abort_protected", 1, MintOpNoArgs)
 OPDEF(MINT_END_ABORT_PROT, "end_abort_protected", 1, MintOpNoArgs)

+ 7 - 1
mono/mini/interp/transform.c

@@ -4017,6 +4017,12 @@ generate (MonoMethod *method, MonoMethodHeader *header, InterpMethod *rtm, unsig
 		case MONO_CUSTOM_PREFIX:
 			++td->ip;
 		        switch (*td->ip) {
+				case CEE_MONO_LD_DELEGATE_METHOD_PTR:
+					--td->sp;
+					td->ip += 1;
+					ADD_CODE (td, MINT_LD_DELEGATE_METHOD_PTR);
+					PUSH_SIMPLE_TYPE (td, STACK_TYPE_I);
+					break;
 				case CEE_MONO_CALLI_EXTRA_ARG:
 					/* Same as CEE_CALLI, except that we drop the extra arg required for llvm specific behaviour */
 					ADD_CODE (td, MINT_POP);
@@ -4677,7 +4683,7 @@ mono_interp_transform_method (InterpMethod *imethod, ThreadContext *context, Int
 			const char *name = method->name;
 			if (m_class_get_parent (method->klass) == mono_defaults.multicastdelegate_class) {
 				if (*name == '.' && (strcmp (name, ".ctor") == 0)) {
-					MonoJitICallInfo *mi = mono_find_jit_icall_by_name ("ves_icall_mono_delegate_ctor");
+					MonoJitICallInfo *mi = mono_find_jit_icall_by_name ("ves_icall_mono_delegate_ctor_interp");
 					g_assert (mi);
 					char *wrapper_name = g_strdup_printf ("__icall_wrapper_%s", mi->name);
 					nm = mono_marshal_get_icall_wrapper (mi->sig, wrapper_name, mi->func, TRUE);

+ 13 - 0
mono/mini/jit-icalls.c

@@ -1514,6 +1514,19 @@ ves_icall_mono_delegate_ctor (MonoObject *this_obj_raw, MonoObject *target_raw,
 	HANDLE_FUNCTION_RETURN ();
 }
 
+void
+ves_icall_mono_delegate_ctor_interp (MonoObject *this_obj_raw, MonoObject *target_raw, gpointer addr)
+{
+	HANDLE_FUNCTION_ENTER ();
+	ERROR_DECL (error);
+	MONO_HANDLE_DCL (MonoObject, this_obj);
+	MONO_HANDLE_DCL (MonoObject, target);
+
+	mini_get_interp_callbacks ()->delegate_ctor (this_obj, target, addr, error);
+	mono_error_set_pending_exception (error);
+	HANDLE_FUNCTION_RETURN ();
+}
+
 gpointer
 mono_fill_class_rgctx (MonoVTable *vtable, int index)
 {

+ 3 - 0
mono/mini/jit-icalls.h

@@ -195,6 +195,9 @@ mono_generic_class_init (MonoVTable *vtable);
 void
 ves_icall_mono_delegate_ctor (MonoObject *this_obj, MonoObject *target, gpointer addr);
 
+void
+ves_icall_mono_delegate_ctor_interp (MonoObject *this_obj, MonoObject *target, gpointer addr);
+
 MonoObject*
 mono_gsharedvt_constrained_call (gpointer mp, MonoMethod *cmethod, MonoClass *klass, gboolean deref_arg, gpointer *args);
 

+ 12 - 0
mono/mini/method-to-ir.c

@@ -11177,6 +11177,18 @@ mono_ldptr:
 			mono_emit_jit_icall (cfg, mono_jit_set_domain, args);
 			break;
 		}
+		case MONO_CEE_MONO_LD_DELEGATE_METHOD_PTR: {
+			MonoInst *addr;
+
+			CHECK_STACK (1);
+			--sp;
+			addr = *sp;
+
+			dreg = alloc_preg (cfg);
+			EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, dreg, sp [0]->dreg, MONO_STRUCT_OFFSET (MonoDelegate, method_ptr));
+			*sp++ = ins;
+			break;
+		}
 		case MONO_CEE_MONO_CALLI_EXTRA_ARG: {
 			MonoInst *addr;
 			MonoMethodSignature *fsig;

+ 1 - 0
mono/mini/mini-amd64.h

@@ -451,6 +451,7 @@ typedef struct {
 #define MONO_ARCH_FLOAT32_SUPPORTED 1
 
 #define MONO_ARCH_HAVE_INTERP_PINVOKE_TRAMP
+#define MONO_ARCH_HAVE_INTERP_NATIVE_TO_MANAGED 1
 
 #if defined(TARGET_OSX) || defined(__linux__)
 #define MONO_ARCH_HAVE_UNWIND_BACKTRACE 1

+ 1 - 0
mono/mini/mini-arm.h

@@ -366,6 +366,7 @@ typedef struct MonoCompileArch {
 #define MONO_ARCH_HAVE_INTERP_ENTRY_TRAMPOLINE 1
 #define MONO_ARCH_HAVE_FTNPTR_ARG_TRAMPOLINE 1
 #define MONO_ARCH_HAVE_INTERP_PINVOKE_TRAMP 1
+#define MONO_ARCH_HAVE_INTERP_NATIVE_TO_MANAGED 1
 
 #if defined(TARGET_WATCHOS) || (defined(__linux__) && !defined(TARGET_ANDROID))
 #define MONO_ARCH_DISABLE_HW_TRAPS 1

+ 1 - 0
mono/mini/mini-arm64.h

@@ -131,6 +131,7 @@ typedef struct {
 #define MONO_ARCH_GSHARED_SUPPORTED 1
 #define MONO_ARCH_INTERPRETER_SUPPORTED 1
 #define MONO_ARCH_HAVE_INTERP_ENTRY_TRAMPOLINE 1
+#define MONO_ARCH_HAVE_INTERP_NATIVE_TO_MANAGED 1
 #define MONO_ARCH_AOT_SUPPORTED 1
 #define MONO_ARCH_LLVM_SUPPORTED 1
 #define MONO_ARCH_HAVE_FULL_AOT_TRAMPOLINES 1

+ 10 - 1
mono/mini/mini-generic-sharing.c

@@ -1426,6 +1426,15 @@ mini_get_gsharedvt_out_sig_wrapper (MonoMethodSignature *sig)
 	return res;
 }
 
+static gboolean
+signature_equal_pinvoke (MonoMethodSignature *sig1, MonoMethodSignature *sig2)
+{
+	/* mono_metadata_signature_equal () doesn't do this check */
+	if (sig1->pinvoke != sig2->pinvoke)
+		return FALSE;
+	return mono_metadata_signature_equal (sig1, sig2);
+}
+
 /*
  * mini_get_interp_in_wrapper:
  *
@@ -1452,7 +1461,7 @@ mini_get_interp_in_wrapper (MonoMethodSignature *sig)
 
 	gshared_lock ();
 	if (!cache)
-		cache = g_hash_table_new_full ((GHashFunc)mono_signature_hash, (GEqualFunc)mono_metadata_signature_equal, NULL, NULL);
+		cache = g_hash_table_new_full ((GHashFunc)mono_signature_hash, (GEqualFunc)signature_equal_pinvoke, NULL, NULL);
 	res = g_hash_table_lookup (cache, sig);
 	gshared_unlock ();
 	if (res) {

+ 6 - 1
mono/mini/mini-runtime.c

@@ -3740,7 +3740,10 @@ mini_parse_debug_option (const char *option)
 		mini_debug_options.verbose_gdb = TRUE;
 	else if (!strncmp (option, "thread-dump-dir=", 16))
 		mono_set_thread_dump_dir(g_strdup(option + 16));
-	else
+	else if (!strncmp (option, "aot-skip=", 9)) {
+		mini_debug_options.aot_skip_set = TRUE;
+		mini_debug_options.aot_skip = atoi (option + 9);
+	} else
 		return FALSE;
 
 	return TRUE;
@@ -3978,6 +3981,7 @@ mini_free_jit_domain_info (MonoDomain *domain)
 	g_hash_table_destroy (info->static_rgctx_trampoline_hash);
 	g_hash_table_destroy (info->mrgctx_hash);
 	g_hash_table_destroy (info->method_rgctx_hash);
+	g_hash_table_destroy (info->interp_method_pointer_hash);
 	g_hash_table_destroy (info->llvm_vcall_trampoline_hash);
 	mono_conc_hashtable_destroy (info->runtime_invoke_hash);
 	g_hash_table_destroy (info->seq_points);
@@ -4582,6 +4586,7 @@ register_icalls (void)
 
 	/* other jit icalls */
 	register_icall (ves_icall_mono_delegate_ctor, "ves_icall_mono_delegate_ctor", "void object object ptr", FALSE);
+	register_icall (ves_icall_mono_delegate_ctor_interp, "ves_icall_mono_delegate_ctor_interp", "void object object ptr", FALSE);
 	register_icall (mono_class_static_field_address , "mono_class_static_field_address",
 				 "ptr ptr ptr", FALSE);
 	register_icall (mono_ldtoken_wrapper, "mono_ldtoken_wrapper", "ptr ptr ptr ptr", FALSE);

+ 9 - 0
mono/mini/mini-runtime.h

@@ -54,6 +54,8 @@ typedef struct
 	/* Maps MonoMethod -> 	MonoMethodRuntimeGenericContext */
 	GHashTable *mrgctx_hash;
 	GHashTable *method_rgctx_hash;
+	/* Maps gpointer -> InterpMethod */
+	GHashTable *interp_method_pointer_hash;
 } MonoJitDomainInfo;
 
 #define domain_jit_info(domain) ((MonoJitDomainInfo*)((domain)->runtime_info))
@@ -227,6 +229,13 @@ typedef struct MonoDebugOptions {
 
 	// Internal testing feature.
 	gboolean test_tailcall_require;
+
+	/*
+	 * Internal testing feature
+	 * Testing feature, skip loading the Nth aot loadable method.
+	 */
+	gboolean aot_skip_set;
+	int aot_skip;
 } MonoDebugOptions;
 
 

+ 1 - 1
mono/mini/mini-trampolines.c

@@ -1456,7 +1456,7 @@ mono_create_jump_trampoline (MonoDomain *domain, MonoMethod *method, gboolean ad
 	error_init (error);
 
 	if (mono_use_interpreter) {
-		gpointer ret = mini_get_interp_callbacks ()->create_trampoline (domain, method, error);
+		gpointer ret = mini_get_interp_callbacks ()->create_method_pointer (method, error);
 		if (!mono_error_ok (error))
 			return NULL;
 		return ret;

+ 1 - 0
scripts/ci/run-test-interpreter.sh

@@ -5,6 +5,7 @@ export TEST_WITH_INTERPRETER=1
 
 ${TESTCMD} --label=interpreter-regression --timeout=10m make -C mono/mini richeck
 ${TESTCMD} --label=mixedmode-regression --timeout=10m make -C mono/mini mixedcheck
+${TESTCMD} --label=fullaotmixed-regression --timeout=10m make -C mono/mini fullaotmixedcheck
 ${TESTCMD} --label=compile-runtime-tests --timeout=40m make -w -C mono/tests -j4 tests
 ${TESTCMD} --label=runtime-interp --timeout=160m make -w -C mono/tests -k testinterp V=1 CI=1 CI_PR=$([[ ${CI_TAGS} == *'pull-request'* ]] && echo 1 || true)
 ${TESTCMD} --label=corlib --timeout=160m make -w -C mcs/class/corlib run-test V=1