From 45837e1a5aeabb3ea3c17391e70e69e4618fe825 Mon Sep 17 00:00:00 2001 From: Zoltan Varga Date: Thu, 30 Mar 2017 07:32:44 -0400 Subject: [PATCH 1/1] Add beginnings of mixed code execution support (#4613) * [interp] Fix a few problems exposed by the iltests.exe test suite. The ENDFINALLY opcode should empty the stack. The INITBLK opcode is two bytes. * [interp] Store the MonoMethod->RuntimeMethod mapping in a separate hash in MonoJitDomainInfo instead of using MonoDomain.jit_code_hash, the latter contains MonoMethod->MonoJitInfo mappings. Also avoid holding the domain lock while creating the RuntimeMethod structure. * [interp] Add a mono_jit_compile_method_jit_only () function which always compiles its argument using the JIT/AOT. * [interp] Print a warning when --interpreter is used with a runtime compiled without ENABLE_INTERPRETER. * [interp] Fix the size of the icall enter trampoline on amd64. Fix some formatting issues. * [interp] Add support for calling JITted code from the interpreter. This uses the gsharedvt_out wrappers used by bitcode to reduce the number of possible calling conventions since all arguments and the return value are passed/returned by value. Not enabled yet. * [interp] Add a --interp= argument which allows passing of options to the interpreter. Add a 'jit=' option which is used to control the set of types whose methods will be called by exiting the interpreter. * [interp] Add tests for mixed mode execution. * [interp] Add support for calling interpreter code from JITted code. * [interp] add mixedmode target to CI * Revert a debug change. * [runtime] Fix the locking in the gsharedvt/interp wrapper creation functions. * [interp] Implement support for interp->jit calls with more than 8 arguments. --- mono/cil/cil-opcodes.xml | 1 + mono/cil/opcode.def | 1 + mono/metadata/marshal.h | 7 + mono/mini/Makefile.am.in | 6 +- mono/mini/driver.c | 11 +- mono/mini/interp/interp-internals.h | 7 + mono/mini/interp/interp.c | 753 +++++++++++++++++++++++++--- mono/mini/interp/interp.h | 3 + mono/mini/interp/mintops.def | 3 +- mono/mini/interp/transform.c | 37 +- mono/mini/method-to-ir.c | 29 +- mono/mini/mini-generic-sharing.c | 168 ++++++- mono/mini/mini-runtime.c | 28 +- mono/mini/mini.h | 4 + mono/mini/mixed.cs | 182 +++++++ mono/mini/tramp-amd64.c | 17 +- scripts/ci/run-test-interpreter.sh | 1 + 17 files changed, 1153 insertions(+), 105 deletions(-) create mode 100644 mono/mini/mixed.cs diff --git a/mono/cil/cil-opcodes.xml b/mono/cil/cil-opcodes.xml index c8ef14eaeed..cafb5561891 100644 --- a/mono/cil/cil-opcodes.xml +++ b/mono/cil/cil-opcodes.xml @@ -320,4 +320,5 @@ + diff --git a/mono/cil/opcode.def b/mono/cil/opcode.def index fca2448151a..91e8924febc 100644 --- a/mono/cil/opcode.def +++ b/mono/cil/opcode.def @@ -320,6 +320,7 @@ OPDEF(CEE_MONO_CALLI_EXTRA_ARG, "mono_calli_extra_arg", VarPop, VarPush, InlineS OPDEF(CEE_MONO_LDDOMAIN, "mono_lddomain", Pop0, PushI, InlineNone, X, 2, 0xF0, 0x19, NEXT) OPDEF(CEE_MONO_ATOMIC_STORE_I4, "mono_atomic_store_i4", PopI+PopI, Push0, InlineI, X, 2, 0xF0, 0x1A, NEXT) OPDEF(CEE_MONO_GET_LAST_ERROR, "mono_get_last_error", Pop0, PushI, InlineNone, X, 2, 0xF0, 0x1B, NEXT) +OPDEF(CEE_MONO_GET_RGCTX_ARG, "mono_get_rgctx_arg", Pop0, PushI, InlineNone, X, 2, 0xF0, 0x1C, NEXT) #ifndef OPALIAS #define _MONO_CIL_OPALIAS_DEFINED_ #define OPALIAS(a,s,r) diff --git a/mono/metadata/marshal.h b/mono/metadata/marshal.h index 3f898dba5a0..9d24832dbee 100644 --- a/mono/metadata/marshal.h +++ b/mono/metadata/marshal.h @@ -121,6 +121,7 @@ typedef enum { /* Subtypes of MONO_WRAPPER_UNKNOWN */ WRAPPER_SUBTYPE_GSHAREDVT_IN_SIG, WRAPPER_SUBTYPE_GSHAREDVT_OUT_SIG, + WRAPPER_SUBTYPE_INTERP_IN } WrapperSubtype; typedef struct { @@ -195,6 +196,10 @@ typedef struct { MonoMethod *method; } DelegateInvokeWrapperInfo; +typedef struct { + MonoMethodSignature *sig; +} InterpInWrapperInfo; + /* * This structure contains additional information to uniquely identify a given wrapper * method. It can be retrieved by mono_marshal_get_wrapper_info () for certain types @@ -237,6 +242,8 @@ typedef struct { GsharedvtWrapperInfo gsharedvt; /* DELEGATE_INVOKE */ DelegateInvokeWrapperInfo delegate_invoke; + /* INTERP_IN */ + InterpInWrapperInfo interp_in; } d; } WrapperInfo; diff --git a/mono/mini/Makefile.am.in b/mono/mini/Makefile.am.in index 482398288f8..79041633ffa 100755 --- a/mono/mini/Makefile.am.in +++ b/mono/mini/Makefile.am.in @@ -506,7 +506,8 @@ test_sources = \ basic-vectors.cs \ aot-tests.cs \ gc-test.cs \ - gshared.cs + gshared.cs \ + mixed.cs if NACL_CODEGEN test_sources += nacl.cs @@ -751,6 +752,9 @@ rcheck: mono $(regtests) richeck: mono $(regtests) $(INTERP_RUNTIME) --regression $(regtests) +mixedcheck: mono mixed.exe + $(MINI_RUNTIME) --interp=jit=JitClass mixed.exe + if ARM check-seq-points: else diff --git a/mono/mini/driver.c b/mono/mini/driver.c index 9e09e7a944e..cd896155a12 100644 --- a/mono/mini/driver.c +++ b/mono/mini/driver.c @@ -1907,9 +1907,18 @@ mono_main (int argc, char* argv[]) #endif } else if (strcmp (argv [i], "--nollvm") == 0){ mono_use_llvm = FALSE; + } else if ((strcmp (argv [i], "--interpreter") == 0) || !strcmp (argv [i], "--interp")) { #ifdef ENABLE_INTERPRETER - } else if (strcmp (argv [i], "--interpreter") == 0) { mono_use_interpreter = TRUE; +#else + fprintf (stderr, "Mono Warning: --interpreter not enabled in this runtime.\n"); +#endif + } else if (strncmp (argv [i], "--interp=", 9) == 0) { +#ifdef ENABLE_INTERPRETER + mono_use_interpreter = TRUE; + mono_interp_parse_options (argv [i] + 9); +#else + fprintf (stderr, "Mono Warning: --interp= not enabled in this runtime.\n"); #endif #ifdef __native_client__ diff --git a/mono/mini/interp/interp-internals.h b/mono/mini/interp/interp-internals.h index f26e4ff57b3..bda0b457e85 100644 --- a/mono/mini/interp/interp-internals.h +++ b/mono/mini/interp/interp-internals.h @@ -82,6 +82,12 @@ typedef struct _RuntimeMethod guint32 *local_offsets; unsigned int param_count; unsigned int hasthis; + gpointer jit_wrapper; + gpointer jit_addr; + MonoMethodSignature *jit_sig; + gpointer jit_entry; + MonoType *rtype; + MonoType **param_types; } RuntimeMethod; struct _MonoInvocation { @@ -111,6 +117,7 @@ typedef struct { } ThreadContext; extern int mono_interp_traceopt; +extern GSList *jit_classes; MonoException * mono_interp_transform_method (RuntimeMethod *runtime_method, ThreadContext *context); diff --git a/mono/mini/interp/interp.c b/mono/mini/interp/interp.c index 5f44592287d..dcea13fe0d0 100644 --- a/mono/mini/interp/interp.c +++ b/mono/mini/interp/interp.c @@ -78,17 +78,29 @@ #endif #endif -#define INIT_FRAME(frame,parent_frame,method_args,method_retval,domain,mono_method,error) \ - do { \ - (frame)->parent = (parent_frame); \ - (frame)->stack_args = (method_args); \ - (frame)->retval = (method_retval); \ - (frame)->runtime_method = mono_interp_get_runtime_method ((domain), (mono_method), (error)); \ - (frame)->ex = NULL; \ - (frame)->ip = NULL; \ - (frame)->invoke_trap = 0; \ +static inline void +init_frame (MonoInvocation *frame, MonoInvocation *parent_frame, RuntimeMethod *rmethod, stackval *method_args, stackval *method_retval) +{ + frame->parent = parent_frame; + frame->stack_args = method_args; + frame->retval = method_retval; + frame->runtime_method = rmethod; + frame->ex = NULL; + frame->ip = NULL; + frame->invoke_trap = 0; +} + +#define INIT_FRAME(frame,parent_frame,method_args,method_retval,domain,mono_method,error) do { \ + RuntimeMethod *_rmethod = mono_interp_get_runtime_method ((domain), (mono_method), (error)); \ + init_frame ((frame), (parent_frame), _rmethod, (method_args), (method_retval)); \ } while (0) +/* + * List of classes whose methods will be executed by transitioning to JITted code. + * Used for testing. + */ +GSList *jit_classes; + void ves_exec_method (MonoInvocation *frame); static char* dump_stack (stackval *stack, stackval *sp); @@ -258,18 +270,33 @@ RuntimeMethod* mono_interp_get_runtime_method (MonoDomain *domain, MonoMethod *method, MonoError *error) { RuntimeMethod *rtm; + MonoJitDomainInfo *info; + MonoMethodSignature *sig; + int i; + error_init (error); + info = domain_jit_info (domain); mono_domain_jit_code_hash_lock (domain); - if ((rtm = mono_internal_hash_table_lookup (&domain->jit_code_hash, method))) { - mono_domain_jit_code_hash_unlock (domain); + rtm = mono_internal_hash_table_lookup (&info->interp_code_hash, method); + mono_domain_jit_code_hash_unlock (domain); + if (rtm) return rtm; - } + + sig = mono_method_signature (method); + rtm = mono_domain_alloc0 (domain, sizeof (RuntimeMethod)); rtm->method = method; - rtm->param_count = mono_method_signature (method)->param_count; - rtm->hasthis = mono_method_signature (method)->hasthis; - mono_internal_hash_table_insert (&domain->jit_code_hash, method, rtm); + rtm->param_count = sig->param_count; + rtm->hasthis = sig->hasthis; + rtm->rtype = mini_get_underlying_type (sig->ret); + rtm->param_types = mono_domain_alloc0 (domain, sizeof (MonoType*) * sig->param_count); + for (i = 0; i < sig->param_count; ++i) + rtm->param_types [i] = mini_get_underlying_type (sig->params [i]); + + mono_domain_jit_code_hash_lock (domain); + if (!mono_internal_hash_table_lookup (&info->interp_code_hash, method)) + mono_internal_hash_table_insert (&info->interp_code_hash, method, rtm); mono_domain_jit_code_hash_unlock (domain); return rtm; @@ -1415,6 +1442,209 @@ handle_enum: return retval; } +typedef struct { + RuntimeMethod *rmethod; + gpointer this_arg; + gpointer res; + gpointer args [16]; + gpointer *many_args; +} InterpEntryData; + +/* Main function for entering the interpreter from compiled code */ +static void +interp_entry (InterpEntryData *data) +{ + MonoInvocation frame; + RuntimeMethod *rmethod = data->rmethod; + ThreadContext *context = mono_native_tls_get_value (thread_context_id); + ThreadContext context_struct; + MonoInvocation *old_frame; + stackval result; + stackval *args; + MonoMethod *method; + MonoMethodSignature *sig; + MonoType *type; + int i; + + method = rmethod->method; + sig = mono_method_signature (method); + + // FIXME: Optimize this + + //printf ("%s\n", mono_method_full_name (method, 1)); + + frame.ex = NULL; + if (context == NULL) { + context = &context_struct; + memset (context, 0, sizeof (ThreadContext)); + context_struct.base_frame = &frame; + context_struct.env_frame = &frame; + mono_native_tls_set_value (thread_context_id, context); + } + else + old_frame = context->current_frame; + context->domain = mono_domain_get (); + + args = alloca (sizeof (stackval) * (sig->param_count + (sig->hasthis ? 1 : 0))); + if (sig->hasthis) + args [0].data.p = data->this_arg; + + gpointer *params; + if (data->many_args) + params = data->many_args; + else + params = data->args; + for (i = 0; i < sig->param_count; ++i) { + int a_index = i + (sig->hasthis ? 1 : 0); + if (sig->params [i]->byref) { + args [a_index].data.p = params [i]; + continue; + } + type = rmethod->param_types [i]; + switch (type->type) { + case MONO_TYPE_U1: + case MONO_TYPE_I1: + args [a_index].data.i = *(MonoBoolean*)params [i]; + break; + case MONO_TYPE_U2: + case MONO_TYPE_I2: + args [a_index].data.i = *(gint16*)params [i]; + break; + case MONO_TYPE_U: +#if SIZEOF_VOID_P == 4 + args [a_index].data.p = GINT_TO_POINTER (*(guint32*)params [i]); +#else + args [a_index].data.p = GINT_TO_POINTER (*(guint64*)params [i]); +#endif + break; + case MONO_TYPE_I: +#if SIZEOF_VOID_P == 4 + args [a_index].data.p = GINT_TO_POINTER (*(gint32*)params [i]); +#else + args [a_index].data.p = GINT_TO_POINTER (*(gint64*)params [i]); +#endif + break; + case MONO_TYPE_U4: + args [a_index].data.i = *(guint32*)params [i]; + break; + case MONO_TYPE_I4: + args [a_index].data.i = *(gint32*)params [i]; + break; + case MONO_TYPE_U8: + args [a_index].data.l = *(guint64*)params [i]; + break; + case MONO_TYPE_I8: + args [a_index].data.l = *(gint64*)params [i]; + break; + case MONO_TYPE_PTR: + case MONO_TYPE_OBJECT: + args [a_index].data.p = *(MonoObject**)params [i]; + break; + case MONO_TYPE_VALUETYPE: + args [a_index].data.p = params [i]; + break; + case MONO_TYPE_GENERICINST: + if (MONO_TYPE_IS_REFERENCE (type)) + args [a_index].data.p = params [i]; + else + args [a_index].data.vt = params [i]; + break; + default: + printf ("%s\n", mono_type_full_name (sig->params [i])); + NOT_IMPLEMENTED; + break; + } + } + + init_frame (&frame, context->current_frame, data->rmethod, args, &result); + context->managed_code = 1; + + type = rmethod->rtype; + switch (type->type) { + case MONO_TYPE_GENERICINST: + if (!MONO_TYPE_IS_REFERENCE (type)) + frame.retval->data.vt = data->res; + break; + case MONO_TYPE_VALUETYPE: + frame.retval->data.vt = data->res; + break; + default: + break; + } + + ves_exec_method_with_context (&frame, context); + context->managed_code = 0; + if (context == &context_struct) + mono_native_tls_set_value (thread_context_id, NULL); + else + context->current_frame = old_frame; + + // FIXME: + g_assert (frame.ex == NULL); + + type = rmethod->rtype; + switch (type->type) { + case MONO_TYPE_VOID: + break; + case MONO_TYPE_I1: + *(gint8*)data->res = frame.retval->data.i; + break; + case MONO_TYPE_U1: + *(guint8*)data->res = frame.retval->data.i; + break; + case MONO_TYPE_I2: + *(gint16*)data->res = frame.retval->data.i; + break; + case MONO_TYPE_U2: + *(guint16*)data->res = frame.retval->data.i; + break; + case MONO_TYPE_I4: + *(gint32*)data->res = frame.retval->data.i; + break; + case MONO_TYPE_U4: + *(guint64*)data->res = frame.retval->data.i; + break; + case MONO_TYPE_I8: + *(gint64*)data->res = frame.retval->data.i; + break; + case MONO_TYPE_U8: + *(guint64*)data->res = frame.retval->data.i; + break; + case MONO_TYPE_I: +#if SIZEOF_VOID_P == 8 + *(gint64*)data->res = (gint64)frame.retval->data.p; +#else + *(gint32*)data->res = (gint32)frame.retval->data.p; +#endif + break; + case MONO_TYPE_U: +#if SIZEOF_VOID_P == 8 + *(guint64*)data->res = (guint64)frame.retval->data.p; +#else + *(guint32*)data->res = (guint32)frame.retval->data.p; +#endif + break; + case MONO_TYPE_OBJECT: + /* No need for a write barrier */ + *(MonoObject**)data->res = (MonoObject*)frame.retval->data.p; + break; + case MONO_TYPE_GENERICINST: + if (MONO_TYPE_IS_REFERENCE (type)) { + *(MonoObject**)data->res = *(MonoObject**)frame.retval->data.p; + } else { + /* Already set before the call */ + } + break; + case MONO_TYPE_VALUETYPE: + /* Already set before the call */ + break; + default: + printf ("%s\n", mono_type_full_name (sig->ret)); + NOT_IMPLEMENTED; + break; + } +} + static stackval * do_icall (ThreadContext *context, int op, stackval *sp, gpointer ptr) { @@ -1504,84 +1734,220 @@ do_icall (ThreadContext *context, int op, stackval *sp, gpointer ptr) return sp; } -static mono_mutex_t create_method_pointer_mutex; - -static GHashTable *method_pointer_hash = NULL; - -#define TRAMPS_USED 8 - -static MonoMethod *method_pointers [TRAMPS_USED] = {0}; - -#define GEN_METHOD_PTR_TRAMP(num) \ - static MonoObject * mp_tramp_ ## num (MonoObject *this_obj, void **params, MonoObject **exc, void *compiled_method) { \ - MonoError error; \ - void *params_real[] = {this_obj, ¶ms, &exc, &compiled_method}; \ - MonoObject *ret = mono_interp_runtime_invoke (method_pointers [num], NULL, params_real, NULL, &error); \ - mono_error_cleanup (&error); \ - return ret; \ - } - +/* + * These functions are the entry points into the interpreter from compiled code. + * They are called by the interp_in wrappers. They have the following signature: + * void (, , , ..., , ) + * They pack up their arguments into an InterpEntryData structure and call interp_entry (). + * It would be possible for the wrappers to pack up the arguments etc, but that would make them bigger, and there are + * more wrappers then these functions. + * this/static * ret/void * 16 arguments -> 64 functions. + */ -GEN_METHOD_PTR_TRAMP (0); -GEN_METHOD_PTR_TRAMP (1); -GEN_METHOD_PTR_TRAMP (2); -GEN_METHOD_PTR_TRAMP (3); -GEN_METHOD_PTR_TRAMP (4); -GEN_METHOD_PTR_TRAMP (5); -GEN_METHOD_PTR_TRAMP (6); -GEN_METHOD_PTR_TRAMP (7); +#define MAX_INTERP_ENTRY_ARGS 8 -#undef GEN_METHOD_PTR_TRAMP +#define INTERP_ENTRY_BASE(_method, _this_arg, _res) \ + InterpEntryData data; \ + (data).rmethod = (_method); \ + (data).res = (_res); \ + (data).this_arg = (_this_arg); \ + (data).many_args = NULL; -gpointer *mp_tramps[TRAMPS_USED] = { - (gpointer) mp_tramp_0, (gpointer) mp_tramp_1, (gpointer) mp_tramp_2, (gpointer) mp_tramp_3, - (gpointer) mp_tramp_4, (gpointer) mp_tramp_5, (gpointer) mp_tramp_6, (gpointer) mp_tramp_7 -}; +#define INTERP_ENTRY0(_this_arg, _res, _method) { \ + INTERP_ENTRY_BASE (_method, _this_arg, _res); \ + interp_entry (&data); \ + } +#define INTERP_ENTRY1(_this_arg, _res, _method) { \ + INTERP_ENTRY_BASE (_method, _this_arg, _res); \ + (data).args [0] = arg1; \ + interp_entry (&data); \ + } +#define INTERP_ENTRY2(_this_arg, _res, _method) { \ + INTERP_ENTRY_BASE (_method, _this_arg, _res); \ + (data).args [0] = arg1; \ + (data).args [1] = arg2; \ + interp_entry (&data); \ + } +#define INTERP_ENTRY3(_this_arg, _res, _method) { \ + INTERP_ENTRY_BASE (_method, _this_arg, _res); \ + (data).args [0] = arg1; \ + (data).args [1] = arg2; \ + (data).args [2] = arg3; \ + interp_entry (&data); \ + } +#define INTERP_ENTRY4(_this_arg, _res, _method) { \ + INTERP_ENTRY_BASE (_method, _this_arg, _res); \ + (data).args [0] = arg1; \ + (data).args [1] = arg2; \ + (data).args [2] = arg3; \ + (data).args [3] = arg4; \ + interp_entry (&data); \ + } +#define INTERP_ENTRY5(_this_arg, _res, _method) { \ + INTERP_ENTRY_BASE (_method, _this_arg, _res); \ + (data).args [0] = arg1; \ + (data).args [1] = arg2; \ + (data).args [2] = arg3; \ + (data).args [3] = arg4; \ + (data).args [4] = arg5; \ + interp_entry (&data); \ + } +#define INTERP_ENTRY6(_this_arg, _res, _method) { \ + INTERP_ENTRY_BASE (_method, _this_arg, _res); \ + (data).args [0] = arg1; \ + (data).args [1] = arg2; \ + (data).args [2] = arg3; \ + (data).args [3] = arg4; \ + (data).args [4] = arg5; \ + (data).args [5] = arg6; \ + interp_entry (&data); \ + } +#define INTERP_ENTRY7(_this_arg, _res, _method) { \ + INTERP_ENTRY_BASE (_method, _this_arg, _res); \ + (data).args [0] = arg1; \ + (data).args [1] = arg2; \ + (data).args [2] = arg3; \ + (data).args [3] = arg4; \ + (data).args [4] = arg5; \ + (data).args [5] = arg6; \ + (data).args [6] = arg7; \ + interp_entry (&data); \ + } +#define INTERP_ENTRY8(_this_arg, _res, _method) { \ + INTERP_ENTRY_BASE (_method, _this_arg, _res); \ + (data).args [0] = arg1; \ + (data).args [1] = arg2; \ + (data).args [2] = arg3; \ + (data).args [3] = arg4; \ + (data).args [4] = arg5; \ + (data).args [5] = arg6; \ + (data).args [6] = arg7; \ + (data).args [7] = arg8; \ + interp_entry (&data); \ + } -static int tramps_used = 0; +#define ARGLIST0 RuntimeMethod *rmethod +#define ARGLIST1 gpointer arg1, RuntimeMethod *rmethod +#define ARGLIST2 gpointer arg1, gpointer arg2, RuntimeMethod *rmethod +#define ARGLIST3 gpointer arg1, gpointer arg2, gpointer arg3, RuntimeMethod *rmethod +#define ARGLIST4 gpointer arg1, gpointer arg2, gpointer arg3, gpointer arg4, RuntimeMethod *rmethod +#define ARGLIST5 gpointer arg1, gpointer arg2, gpointer arg3, gpointer arg4, gpointer arg5, RuntimeMethod *rmethod +#define ARGLIST6 gpointer arg1, gpointer arg2, gpointer arg3, gpointer arg4, gpointer arg5, gpointer arg6, RuntimeMethod *rmethod +#define ARGLIST7 gpointer arg1, gpointer arg2, gpointer arg3, gpointer arg4, gpointer arg5, gpointer arg6, gpointer arg7, RuntimeMethod *rmethod +#define ARGLIST8 gpointer arg1, gpointer arg2, gpointer arg3, gpointer arg4, gpointer arg5, gpointer arg6, gpointer arg7, gpointer arg8, RuntimeMethod *rmethod + +static void interp_entry_static_0 (ARGLIST0) INTERP_ENTRY0 (NULL, NULL, rmethod) +static void interp_entry_static_1 (ARGLIST1) INTERP_ENTRY1 (NULL, NULL, rmethod) +static void interp_entry_static_2 (ARGLIST2) INTERP_ENTRY2 (NULL, NULL, rmethod) +static void interp_entry_static_3 (ARGLIST3) INTERP_ENTRY3 (NULL, NULL, rmethod) +static void interp_entry_static_4 (ARGLIST4) INTERP_ENTRY4 (NULL, NULL, rmethod) +static void interp_entry_static_5 (ARGLIST5) INTERP_ENTRY5 (NULL, NULL, rmethod) +static void interp_entry_static_6 (ARGLIST6) INTERP_ENTRY6 (NULL, NULL, rmethod) +static void interp_entry_static_7 (ARGLIST7) INTERP_ENTRY7 (NULL, NULL, rmethod) +static void interp_entry_static_8 (ARGLIST8) INTERP_ENTRY8 (NULL, NULL, rmethod) +static void interp_entry_static_ret_0 (gpointer res, ARGLIST0) INTERP_ENTRY0 (NULL, res, rmethod) +static void interp_entry_static_ret_1 (gpointer res, ARGLIST1) INTERP_ENTRY1 (NULL, res, rmethod) +static void interp_entry_static_ret_2 (gpointer res, ARGLIST2) INTERP_ENTRY2 (NULL, res, rmethod) +static void interp_entry_static_ret_3 (gpointer res, ARGLIST3) INTERP_ENTRY3 (NULL, res, rmethod) +static void interp_entry_static_ret_4 (gpointer res, ARGLIST4) INTERP_ENTRY4 (NULL, res, rmethod) +static void interp_entry_static_ret_5 (gpointer res, ARGLIST5) INTERP_ENTRY5 (NULL, res, rmethod) +static void interp_entry_static_ret_6 (gpointer res, ARGLIST6) INTERP_ENTRY6 (NULL, res, rmethod) +static void interp_entry_static_ret_7 (gpointer res, ARGLIST7) INTERP_ENTRY7 (NULL, res, rmethod) +static void interp_entry_static_ret_8 (gpointer res, ARGLIST8) INTERP_ENTRY8 (NULL, res, rmethod) +static void interp_entry_instance_0 (gpointer this_arg, ARGLIST0) INTERP_ENTRY0 (this_arg, NULL, rmethod) +static void interp_entry_instance_1 (gpointer this_arg, ARGLIST1) INTERP_ENTRY1 (this_arg, NULL, rmethod) +static void interp_entry_instance_2 (gpointer this_arg, ARGLIST2) INTERP_ENTRY2 (this_arg, NULL, rmethod) +static void interp_entry_instance_3 (gpointer this_arg, ARGLIST3) INTERP_ENTRY3 (this_arg, NULL, rmethod) +static void interp_entry_instance_4 (gpointer this_arg, ARGLIST4) INTERP_ENTRY4 (this_arg, NULL, rmethod) +static void interp_entry_instance_5 (gpointer this_arg, ARGLIST5) INTERP_ENTRY5 (this_arg, NULL, rmethod) +static void interp_entry_instance_6 (gpointer this_arg, ARGLIST6) INTERP_ENTRY6 (this_arg, NULL, rmethod) +static void interp_entry_instance_7 (gpointer this_arg, ARGLIST7) INTERP_ENTRY7 (this_arg, NULL, rmethod) +static void interp_entry_instance_8 (gpointer this_arg, ARGLIST8) INTERP_ENTRY8 (this_arg, NULL, rmethod) +static void interp_entry_instance_ret_0 (gpointer this_arg, gpointer res, ARGLIST0) INTERP_ENTRY0 (this_arg, res, rmethod) +static void interp_entry_instance_ret_1 (gpointer this_arg, gpointer res, ARGLIST1) INTERP_ENTRY1 (this_arg, res, rmethod) +static void interp_entry_instance_ret_2 (gpointer this_arg, gpointer res, ARGLIST2) INTERP_ENTRY2 (this_arg, res, rmethod) +static void interp_entry_instance_ret_3 (gpointer this_arg, gpointer res, ARGLIST3) INTERP_ENTRY3 (this_arg, res, rmethod) +static void interp_entry_instance_ret_4 (gpointer this_arg, gpointer res, ARGLIST4) INTERP_ENTRY4 (this_arg, res, rmethod) +static void interp_entry_instance_ret_5 (gpointer this_arg, gpointer res, ARGLIST5) INTERP_ENTRY5 (this_arg, res, rmethod) +static void interp_entry_instance_ret_6 (gpointer this_arg, gpointer res, ARGLIST6) INTERP_ENTRY6 (this_arg, res, rmethod) +static void interp_entry_instance_ret_7 (gpointer this_arg, gpointer res, ARGLIST7) INTERP_ENTRY6 (this_arg, res, rmethod) +static void interp_entry_instance_ret_8 (gpointer this_arg, gpointer res, ARGLIST8) INTERP_ENTRY6 (this_arg, res, rmethod) + +#define INTERP_ENTRY_FUNCLIST(type) interp_entry_ ## type ## _0, interp_entry_ ## type ## _1, interp_entry_ ## type ## _2, interp_entry_ ## type ## _3, interp_entry_ ## type ## _4, interp_entry_ ## type ## _5, interp_entry_ ## type ## _6, interp_entry_ ## type ## _7, interp_entry_ ## type ## _8 + +gpointer entry_funcs_static [MAX_INTERP_ENTRY_ARGS + 1] = { INTERP_ENTRY_FUNCLIST (static) }; +gpointer entry_funcs_static_ret [MAX_INTERP_ENTRY_ARGS + 1] = { INTERP_ENTRY_FUNCLIST (static_ret) }; +gpointer entry_funcs_instance [MAX_INTERP_ENTRY_ARGS + 1] = { INTERP_ENTRY_FUNCLIST (instance) }; +gpointer entry_funcs_instance_ret [MAX_INTERP_ENTRY_ARGS + 1] = { INTERP_ENTRY_FUNCLIST (instance_ret) }; + +/* General version for methods with more than MAX_INTERP_ENTRY_ARGS arguments */ +static void +interp_entry_general (gpointer this_arg, gpointer res, gpointer *args, gpointer rmethod) +{ + INTERP_ENTRY_BASE (rmethod, this_arg, res); + data.many_args = args; + interp_entry (&data); +} +/* + * mono_interp_create_method_pointer: + * + * Return a function pointer which can be used to call METHOD using the + * interpreter. Return NULL for methods which are not supported. + */ gpointer mono_interp_create_method_pointer (MonoMethod *method, MonoError *error) { gpointer addr; - MonoJitInfo *ji; + MonoMethodSignature *sig = mono_method_signature (method); + MonoMethod *wrapper; + RuntimeMethod *rmethod; - mono_os_mutex_lock (&create_method_pointer_mutex); - if (!method_pointer_hash) { - // FIXME: is registering method table as GC root really necessary? - // MONO_GC_REGISTER_ROOT_FIXED (method_pointer_hash); - method_pointer_hash = g_hash_table_new (NULL, NULL); - } - addr = g_hash_table_lookup (method_pointer_hash, method); - if (addr) { - mono_os_mutex_unlock (&create_method_pointer_mutex); - return addr; + if (method->wrapper_type && method->wrapper_type != MONO_WRAPPER_RUNTIME_INVOKE) + return NULL; + + rmethod = mono_interp_get_runtime_method (mono_domain_get (), method, error); + if (rmethod->jit_entry) + return rmethod->jit_entry; + wrapper = mini_get_interp_in_wrapper (sig); + + gpointer jit_wrapper = mono_jit_compile_method_jit_only (wrapper, error); + mono_error_assert_ok (error); + + //printf ("%s %s\n", mono_method_full_name (method, 1), mono_method_full_name (wrapper, 1)); + gpointer entry_func; + if (sig->param_count > MAX_INTERP_ENTRY_ARGS) { + entry_func = interp_entry_general; + } else if (sig->hasthis) { + if (sig->ret->type == MONO_TYPE_VOID) + entry_func = entry_funcs_instance [sig->param_count]; + else + entry_func = entry_funcs_instance_ret [sig->param_count]; + } else { + if (sig->ret->type == MONO_TYPE_VOID) + entry_func = entry_funcs_static [sig->param_count]; + else + entry_func = entry_funcs_static_ret [sig->param_count]; } + g_assert (entry_func); + + /* 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; + mono_error_assert_ok (error); /* - * If it is a static P/Invoke method, we can just return the pointer - * to the method implementation. + * The wrapper is called by compiled code, which doesn't pass the extra argument, so we pass it in the + * rgctx register using a trampoline. */ - if (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL && ((MonoMethodPInvoke*) method)->addr) { - ji = g_new0 (MonoJitInfo, 1); - ji->d.method = method; - ji->code_size = 1; - ji->code_start = addr = ((MonoMethodPInvoke*) method)->addr; - - mono_jit_info_table_add (mono_get_root_domain (), ji); - } - else { - g_assert (method->wrapper_type == MONO_WRAPPER_RUNTIME_INVOKE); - g_assert (tramps_used < TRAMPS_USED); - - /* FIXME: needs locking */ - method_pointers [tramps_used] = method; - addr = mp_tramps [tramps_used]; - tramps_used++; - } - g_hash_table_insert (method_pointer_hash, method, addr); - mono_os_mutex_unlock (&create_method_pointer_mutex); + // FIXME: AOT + g_assert (!mono_aot_only); + addr = mono_arch_get_static_rgctx_trampoline (ftndesc, jit_wrapper); + + mono_memory_barrier (); + rmethod->jit_entry = addr; return addr; } @@ -1970,6 +2336,7 @@ ves_exec_method_with_context (MonoInvocation *frame, ThreadContext *context) child_frame.runtime_method = mono_interp_get_runtime_method (context->domain, mono_marshal_get_remoting_invoke (child_frame.runtime_method->method), &error); mono_error_cleanup (&error); /* FIXME: don't swallow the error */ } + ves_exec_method_with_context (&child_frame, context); context->current_frame = frame; @@ -2023,6 +2390,215 @@ ves_exec_method_with_context (MonoInvocation *frame, ThreadContext *context) } MINT_IN_BREAK; } + + MINT_IN_CASE(MINT_JIT_CALL) { + MonoMethodSignature *sig; + RuntimeMethod *rmethod = rtm->data_items [* (guint16 *)(ip + 1)]; + MonoFtnDesc ftndesc; + guint8 res_buf [256]; + MonoType *type; + + //printf ("%s\n", mono_method_full_name (rmethod->method, 1)); + + /* + * Call JITted code through a gsharedvt_out wrapper. These wrappers receive every argument + * by ref and return a return value using an explicit return value argument. + */ + if (!rmethod->jit_wrapper) { + MonoMethod *method = rmethod->method; + MonoError error; + + sig = mono_method_signature (method); + g_assert (sig); + + MonoMethod *wrapper = mini_get_gsharedvt_out_sig_wrapper (sig); + //printf ("J: %s %s\n", mono_method_full_name (method, 1), mono_method_full_name (wrapper, 1)); + + gpointer jit_wrapper = mono_jit_compile_method_jit_only (wrapper, &error); + mono_error_assert_ok (&error); + + gpointer addr = mono_jit_compile_method_jit_only (method, &error); + g_assert (addr); + mono_error_assert_ok (&error); + + rmethod->jit_addr = addr; + rmethod->jit_sig = sig; + mono_memory_barrier (); + rmethod->jit_wrapper = jit_wrapper; + + } else { + sig = rmethod->jit_sig; + } + + frame->ip = ip; + ip += 2; + sp -= sig->param_count; + if (sig->hasthis) + --sp; + + ftndesc.addr = rmethod->jit_addr; + ftndesc.arg = NULL; + + // FIXME: Optimize this + + gpointer args [32]; + int pindex = 0; + int stack_index = 0; + if (rmethod->hasthis) { + args [pindex ++] = sp [0].data.p; + stack_index ++; + } + type = rmethod->rtype; + if (type->type != MONO_TYPE_VOID) { + if (MONO_TYPE_ISSTRUCT (type)) + args [pindex ++] = vt_sp; + else + args [pindex ++] = res_buf; + } + for (int i = 0; i < rmethod->param_count; ++i) { + MonoType *t = rmethod->param_types [i]; + stackval *sval = &sp [stack_index + i]; + if (sig->params [i]->byref) { + args [pindex ++] = sval->data.p; + } else if (MONO_TYPE_ISSTRUCT (t)) { + args [pindex ++] = sval->data.p; + } else if (MONO_TYPE_IS_REFERENCE (t)) { + args [pindex ++] = &sval->data.p; + } else { + switch (t->type) { + case MONO_TYPE_I1: + case MONO_TYPE_U1: + case MONO_TYPE_I2: + case MONO_TYPE_U2: + case MONO_TYPE_I4: + case MONO_TYPE_U4: + case MONO_TYPE_VALUETYPE: + args [pindex ++] = &sval->data.i; + break; + case MONO_TYPE_PTR: + case MONO_TYPE_FNPTR: + case MONO_TYPE_I: + case MONO_TYPE_U: + case MONO_TYPE_OBJECT: + args [pindex ++] = &sval->data.p; + break; + case MONO_TYPE_I8: + case MONO_TYPE_U8: + args [pindex ++] = &sval->data.l; + break; + default: + printf ("%s\n", mono_type_full_name (t)); + g_assert_not_reached (); + } + } + } + + switch (pindex) { + case 0: { + void (*func)(gpointer) = rmethod->jit_wrapper; + + func (&ftndesc); + break; + } + case 1: { + void (*func)(gpointer, gpointer) = rmethod->jit_wrapper; + + func (args [0], &ftndesc); + break; + } + case 2: { + void (*func)(gpointer, gpointer, gpointer) = rmethod->jit_wrapper; + + func (args [0], args [1], &ftndesc); + break; + } + case 3: { + void (*func)(gpointer, gpointer, gpointer, gpointer) = rmethod->jit_wrapper; + + func (args [0], args [1], args [2], &ftndesc); + break; + } + case 4: { + void (*func)(gpointer, gpointer, gpointer, gpointer, gpointer) = rmethod->jit_wrapper; + + func (args [0], args [1], args [2], args [3], &ftndesc); + break; + } + case 5: { + void (*func)(gpointer, gpointer, gpointer, gpointer, gpointer, gpointer) = rmethod->jit_wrapper; + + func (args [0], args [1], args [2], args [3], args [4], &ftndesc); + break; + } + case 6: { + void (*func)(gpointer, gpointer, gpointer, gpointer, gpointer, gpointer, gpointer) = rmethod->jit_wrapper; + + func (args [0], args [1], args [2], args [3], args [4], args [5], &ftndesc); + break; + } + case 7: { + void (*func)(gpointer, gpointer, gpointer, gpointer, gpointer, gpointer, gpointer, gpointer) = rmethod->jit_wrapper; + + func (args [0], args [1], args [2], args [3], args [4], args [5], args [6], &ftndesc); + break; + } + default: + g_assert_not_reached (); + break; + } + + MonoType *rtype = rmethod->rtype; + switch (rtype->type) { + case MONO_TYPE_VOID: + case MONO_TYPE_OBJECT: + case MONO_TYPE_STRING: + case MONO_TYPE_CLASS: + case MONO_TYPE_ARRAY: + case MONO_TYPE_SZARRAY: + case MONO_TYPE_I: + case MONO_TYPE_U: + sp->data.p = *(gpointer*)res_buf; + break; + case MONO_TYPE_I1: + sp->data.i = *(gint8*)res_buf; + break; + case MONO_TYPE_U1: + sp->data.i = *(guint8*)res_buf; + break; + case MONO_TYPE_I2: + sp->data.i = *(gint16*)res_buf; + break; + case MONO_TYPE_U2: + sp->data.i = *(guint16*)res_buf; + break; + case MONO_TYPE_I4: + sp->data.i = *(gint32*)res_buf; + break; + case MONO_TYPE_U4: + sp->data.i = *(guint32*)res_buf; + break; + case MONO_TYPE_VALUETYPE: + /* The result was written to vt_sp */ + sp->data.p = vt_sp; + break; + case MONO_TYPE_GENERICINST: + if (MONO_TYPE_IS_REFERENCE (rtype)) { + sp->data.p = *(gpointer*)res_buf; + } else { + /* The result was written to vt_sp */ + sp->data.p = vt_sp; + } + break; + default: + printf ("%s\n", mono_type_full_name (rtype)); + g_assert_not_reached (); + break; + } + if (rtype->type != MONO_TYPE_VOID) + sp++; + MINT_IN_BREAK; + } + MINT_IN_CASE(MINT_CALLVIRT) { stackval *endsp = sp; MonoObject *this_arg; @@ -3668,6 +4244,9 @@ array_constructed: BINOP_CAST(l, -, guint64); MINT_IN_BREAK; MINT_IN_CASE(MINT_ENDFINALLY) + while (sp > frame->stack) { + --sp; + } if (finally_ips) { ip = finally_ips->data; finally_ips = g_slist_remove (finally_ips, ip); @@ -4425,12 +5004,25 @@ interp_ves_icall_get_trace (MonoException *exc, gint32 skip, MonoBoolean need_fi return res; } +void +mono_interp_parse_options (const char *options) +{ + char **args, **ptr; + + args = g_strsplit (options, ",", -1); + for (ptr = args; ptr && *ptr; ptr ++) { + char *arg = *ptr; + + if (strncmp (arg, "jit=", 4) == 0) + jit_classes = g_slist_prepend (jit_classes, arg + 4); + } +} + void mono_interp_init () { mono_native_tls_alloc (&thread_context_id, NULL); - mono_native_tls_set_value (thread_context_id, NULL); - mono_os_mutex_init_recursive (&create_method_pointer_mutex); + mono_native_tls_set_value (thread_context_id, NULL); mono_interp_transform_init (); } @@ -4547,6 +5139,7 @@ interp_regression_step (MonoImage *image, int verbose, int *total_run, int *tota *total += failed + cfailed; *total_run += run; } + static int interp_regression (MonoImage *image, int verbose, int *total_run) { diff --git a/mono/mini/interp/interp.h b/mono/mini/interp/interp.h index 6189b3d1be4..951c47c85e1 100644 --- a/mono/mini/interp/interp.h +++ b/mono/mini/interp/interp.h @@ -24,6 +24,9 @@ mono_interp_init_delegate (MonoDelegate *del); gpointer mono_interp_create_trampoline (MonoDomain *domain, MonoMethod *method, MonoError *error); +void +mono_interp_parse_options (const char *options); + void interp_walk_stack_with_ctx (MonoInternalStackWalk func, MonoContext *ctx, MonoUnwindOptions options, void *user_data); #endif /* __MONO_MINI_INTERPRETER_H__ */ diff --git a/mono/mini/interp/mintops.def b/mono/mini/interp/mintops.def index a6afa577ebd..43f4e6b124f 100644 --- a/mono/mini/interp/mintops.def +++ b/mono/mini/interp/mintops.def @@ -509,4 +509,5 @@ OPDEF(MINT_MONO_NEWOBJ, "mono_newobj", 2, MintOpClassToken) OPDEF(MINT_MONO_RETOBJ, "mono_retobj", 1, MintOpNoArgs) OPDEF(MINT_MONO_FREE, "mono_free", 1, MintOpNoArgs) - +// FIXME: MintOp +OPDEF(MINT_JIT_CALL, "mono_jit_call", 2, MintOpNoArgs) diff --git a/mono/mini/interp/transform.c b/mono/mini/interp/transform.c index 1b1776867fc..ee5f65ac116 100644 --- a/mono/mini/interp/transform.c +++ b/mono/mini/interp/transform.c @@ -636,6 +636,35 @@ get_data_item_index (TransformData *td, void *ptr) return index; } +static gboolean +jit_call_supported (MonoMethod *method, MonoMethodSignature *sig) +{ + GSList *l; + + if (sig->param_count > 6) + return FALSE; + if (sig->pinvoke) + return FALSE; + if (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) + return FALSE; + if (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) + return FALSE; + if (method->is_inflated) + return FALSE; + if (method->string_ctor) + return FALSE; + + for (l = jit_classes; l; l = l->next) { + char *class_name = l->data; + // FIXME: Namespaces + if (!strcmp (method->klass->name, class_name)) + return TRUE; + } + + //return TRUE; + return FALSE; +} + static void interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target_method, MonoDomain *domain, MonoGenericContext *generic_context, unsigned char *is_bb_start, int body_start_offset, MonoClass *constrained_class, gboolean readonly) { @@ -858,6 +887,10 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target ADD_CODE (td, get_data_item_index (td, target_method->klass)); ADD_CODE (td, 1 + target_method->klass->rank); } + } else if (!calli && !virtual && jit_call_supported (target_method, csignature)) { + ADD_CODE(td, MINT_JIT_CALL); + ADD_CODE(td, get_data_item_index (td, (void *)mono_interp_get_runtime_method (domain, target_method, &error))); + mono_error_assert_ok (&error); } else { if (calli) ADD_CODE(td, native ? MINT_CALLI_NAT : MINT_CALLI); @@ -865,7 +898,7 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target ADD_CODE(td, is_void ? MINT_VCALLVIRT : MINT_CALLVIRT); else ADD_CODE(td, is_void ? MINT_VCALL : MINT_CALL); - + if (calli) { ADD_CODE(td, get_data_item_index (td, (void *)csignature)); } else { @@ -2759,6 +2792,7 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo ++td.ip; break; case CEE_ENDFINALLY: + td.sp = td.stack; SIMPLE_OP (td, MINT_ENDFINALLY); generating_code = 0; break; @@ -3122,6 +3156,7 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo CHECK_STACK(&td, 3); ADD_CODE(&td, MINT_INITBLK); td.sp -= 3; + td.ip += 1; break; #if 0 case CEE_NO_: diff --git a/mono/mini/method-to-ir.c b/mono/mini/method-to-ir.c index 29e0b1081fe..2f96bebabc3 100644 --- a/mono/mini/method-to-ir.c +++ b/mono/mini/method-to-ir.c @@ -1261,16 +1261,22 @@ mono_get_got_var (MonoCompile *cfg) return cfg->got_var; } -static MonoInst * -mono_get_vtable_var (MonoCompile *cfg) +static void +mono_create_rgctx_var (MonoCompile *cfg) { - g_assert (cfg->gshared); - if (!cfg->rgctx_var) { cfg->rgctx_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL); /* force the var to be stack allocated */ cfg->rgctx_var->flags |= MONO_INST_VOLATILE; } +} + +static MonoInst * +mono_get_vtable_var (MonoCompile *cfg) +{ + g_assert (cfg->gshared); + + mono_create_rgctx_var (cfg); return cfg->rgctx_var; } @@ -12340,6 +12346,21 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b ins->type = STACK_I4; MONO_ADD_INS (cfg->cbb, ins); + ip += 2; + *sp++ = ins; + break; + case CEE_MONO_GET_RGCTX_ARG: + CHECK_OPSIZE (2); + CHECK_STACK_OVF (1); + + mono_create_rgctx_var (cfg); + + MONO_INST_NEW (cfg, ins, OP_MOVE); + ins->dreg = alloc_dreg (cfg, STACK_PTR); + ins->sreg1 = cfg->rgctx_var->dreg; + ins->type = STACK_PTR; + MONO_ADD_INS (cfg->cbb, ins); + ip += 2; *sp++ = ins; break; diff --git a/mono/mini/mini-generic-sharing.c b/mono/mini/mini-generic-sharing.c index deeee74524e..bc7d12aa678 100644 --- a/mono/mini/mini-generic-sharing.c +++ b/mono/mini/mini-generic-sharing.c @@ -1200,9 +1200,9 @@ mini_get_gsharedvt_in_sig_wrapper (MonoMethodSignature *sig) sig = mini_get_underlying_signature (sig); // FIXME: Normal cache + gshared_lock (); if (!cache) cache = g_hash_table_new_full ((GHashFunc)mono_signature_hash, (GEqualFunc)mono_metadata_signature_equal, NULL, NULL); - gshared_lock (); res = g_hash_table_lookup (cache, sig); gshared_unlock (); if (res) { @@ -1304,9 +1304,9 @@ mini_get_gsharedvt_out_sig_wrapper (MonoMethodSignature *sig) sig = mini_get_underlying_signature (sig); // FIXME: Normal cache + gshared_lock (); if (!cache) cache = g_hash_table_new_full ((GHashFunc)mono_signature_hash, (GEqualFunc)mono_metadata_signature_equal, NULL, NULL); - gshared_lock (); res = g_hash_table_lookup (cache, sig); gshared_unlock (); if (res) { @@ -1408,6 +1408,170 @@ mini_get_gsharedvt_out_sig_wrapper (MonoMethodSignature *sig) return res; } +/* + * mini_get_interp_in_wrapper: + * + * Return a wrapper which can be used to transition from compiled code to the interpreter. + * The wrapper has the same signature as SIG. It is very similar to a gsharedvt_in wrapper, + * except the 'extra_arg' is passed in the rgctx reg, so this wrapper needs to be + * called through a static rgctx trampoline. + * FIXME: Move this elsewhere. + */ +MonoMethod* +mini_get_interp_in_wrapper (MonoMethodSignature *sig) +{ + MonoMethodBuilder *mb; + MonoMethod *res, *cached; + WrapperInfo *info; + MonoMethodSignature *csig, *entry_sig; + int i, pindex, retval_var = 0; + static GHashTable *cache; + const char *name; + gboolean generic = FALSE; + + sig = mini_get_underlying_signature (sig); + + gshared_lock (); + if (!cache) + cache = g_hash_table_new_full ((GHashFunc)mono_signature_hash, (GEqualFunc)mono_metadata_signature_equal, NULL, NULL); + res = g_hash_table_lookup (cache, sig); + gshared_unlock (); + if (res) { + g_free (sig); + return res; + } + + if (sig->param_count > 8) + /* Call the generic interpreter entry point, the specialized ones only handle a limited number of arguments */ + generic = TRUE; + + /* Create the signature for the wrapper */ + csig = g_malloc0 (MONO_SIZEOF_METHOD_SIGNATURE + (sig->param_count * sizeof (MonoType*))); + memcpy (csig, sig, mono_metadata_signature_size (sig)); + + /* Create the signature for the callee callconv */ + if (generic) { + /* + * The called function has the following signature: + * interp_entry_general (gpointer this_arg, gpointer res, gpointer *args, gpointer rmethod) + */ + entry_sig = g_malloc0 (MONO_SIZEOF_METHOD_SIGNATURE + (4 * sizeof (MonoType*))); + entry_sig->ret = &mono_defaults.void_class->byval_arg; + entry_sig->param_count = 4; + entry_sig->params [0] = &mono_defaults.int_class->byval_arg; + entry_sig->params [1] = &mono_defaults.int_class->byval_arg; + entry_sig->params [2] = &mono_defaults.int_class->byval_arg; + entry_sig->params [3] = &mono_defaults.int_class->byval_arg; + name = "interp_in_generic"; + generic = TRUE; + } else { + /* + * The called function has the following signature: + * void entry(, , , ) + */ + entry_sig = g_malloc0 (MONO_SIZEOF_METHOD_SIGNATURE + ((sig->param_count + 2) * sizeof (MonoType*))); + memcpy (entry_sig, sig, mono_metadata_signature_size (sig)); + pindex = 0; + /* The return value is returned using an explicit vret argument */ + if (sig->ret->type != MONO_TYPE_VOID) { + entry_sig->params [pindex ++] = &mono_defaults.int_class->byval_arg; + entry_sig->ret = &mono_defaults.void_class->byval_arg; + } + for (i = 0; i < sig->param_count; i++) { + entry_sig->params [pindex] = sig->params [i]; + if (!sig->params [i]->byref) { + entry_sig->params [pindex] = mono_metadata_type_dup (NULL, entry_sig->params [pindex]); + entry_sig->params [pindex]->byref = 1; + } + pindex ++; + } + /* Extra arg */ + entry_sig->params [pindex ++] = &mono_defaults.int_class->byval_arg; + entry_sig->param_count = pindex; + name = sig->hasthis ? "interp_in" : "interp_in_static"; + } + + mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_UNKNOWN); + + // FIXME: save lmf + +#ifndef DISABLE_JIT + if (sig->ret->type != MONO_TYPE_VOID) + retval_var = mono_mb_add_local (mb, sig->ret); + + /* Make the call */ + if (generic) { + /* Collect arguments */ + int args_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg); + + mono_mb_emit_icon (mb, sizeof (gpointer) * sig->param_count); + mono_mb_emit_byte (mb, CEE_PREFIX1); + mono_mb_emit_byte (mb, CEE_LOCALLOC); + mono_mb_emit_stloc (mb, args_var); + + for (i = 0; i < sig->param_count; i++) { + mono_mb_emit_ldloc (mb, args_var); + mono_mb_emit_icon (mb, sizeof (gpointer) * i); + mono_mb_emit_byte (mb, CEE_ADD); + if (sig->params [i]->byref) + mono_mb_emit_ldarg (mb, i + (sig->hasthis == TRUE)); + else + mono_mb_emit_ldarg_addr (mb, i + (sig->hasthis == TRUE)); + mono_mb_emit_byte (mb, CEE_STIND_I); + } + + if (sig->hasthis) + mono_mb_emit_ldarg (mb, 0); + else + mono_mb_emit_byte (mb, CEE_LDNULL); + if (sig->ret->type != MONO_TYPE_VOID) + mono_mb_emit_ldloc_addr (mb, retval_var); + else + mono_mb_emit_byte (mb, CEE_LDNULL); + mono_mb_emit_ldloc (mb, args_var); + } else { + if (sig->hasthis) + mono_mb_emit_ldarg (mb, 0); + if (sig->ret->type != MONO_TYPE_VOID) + mono_mb_emit_ldloc_addr (mb, retval_var); + for (i = 0; i < sig->param_count; i++) { + if (sig->params [i]->byref) + mono_mb_emit_ldarg (mb, i + (sig->hasthis == TRUE)); + else + mono_mb_emit_ldarg_addr (mb, i + (sig->hasthis == TRUE)); + } + } + /* Extra arg */ + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_byte (mb, CEE_MONO_GET_RGCTX_ARG); + mono_mb_emit_icon (mb, sizeof (gpointer)); + mono_mb_emit_byte (mb, CEE_ADD); + mono_mb_emit_byte (mb, CEE_LDIND_I); + /* Method to call */ + mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); + mono_mb_emit_byte (mb, CEE_MONO_GET_RGCTX_ARG); + mono_mb_emit_byte (mb, CEE_LDIND_I); + mono_mb_emit_calli (mb, entry_sig); + if (sig->ret->type != MONO_TYPE_VOID) + mono_mb_emit_ldloc (mb, retval_var); + mono_mb_emit_byte (mb, CEE_RET); +#endif + + info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_INTERP_IN); + info->d.interp_in.sig = sig; + + res = mono_mb_create (mb, csig, sig->param_count + 16, info); + + gshared_lock (); + cached = g_hash_table_lookup (cache, sig); + if (cached) + res = cached; + else + g_hash_table_insert (cache, sig, res); + gshared_unlock (); + return res; +} + MonoMethodSignature* mini_get_gsharedvt_out_sig_wrapper_signature (gboolean has_this, gboolean has_ret, int param_count) { diff --git a/mono/mini/mini-runtime.c b/mono/mini/mini-runtime.c index d8f22a99a33..656499033cf 100644 --- a/mono/mini/mini-runtime.c +++ b/mono/mini/mini-runtime.c @@ -1751,7 +1751,7 @@ no_gsharedvt_in_wrapper (void) } static gpointer -mono_jit_compile_method_with_opt (MonoMethod *method, guint32 opt, MonoError *error) +mono_jit_compile_method_with_opt (MonoMethod *method, guint32 opt, gboolean jit_only, MonoError *error) { MonoDomain *target_domain, *domain = mono_domain_get (); MonoJitInfo *info; @@ -1763,8 +1763,11 @@ mono_jit_compile_method_with_opt (MonoMethod *method, guint32 opt, MonoError *er error_init (error); #ifdef ENABLE_INTERPRETER - if (mono_use_interpreter) - return mono_interp_create_method_pointer (method, error); + if (mono_use_interpreter && !jit_only) { + code = mono_interp_create_method_pointer (method, error); + if (code) + return code; + } #endif if (mono_llvm_only) @@ -1921,7 +1924,21 @@ mono_jit_compile_method (MonoMethod *method, MonoError *error) { gpointer code; - code = mono_jit_compile_method_with_opt (method, mono_get_optimizations_for_method (method, default_opt), error); + code = mono_jit_compile_method_with_opt (method, mono_get_optimizations_for_method (method, default_opt), FALSE, error); + return code; +} + +/* + * mono_jit_compile_method_jit_only: + * + * Compile METHOD using the JIT/AOT, even in interpreted mode. + */ +gpointer +mono_jit_compile_method_jit_only (MonoMethod *method, MonoError *error) +{ + gpointer code; + + code = mono_jit_compile_method_with_opt (method, mono_get_optimizations_for_method (method, default_opt), TRUE, error); return code; } @@ -2408,7 +2425,7 @@ mono_jit_runtime_invoke (MonoMethod *method, void *obj, void **params, MonoObjec } if (callee) { - compiled_method = mono_jit_compile_method_with_opt (callee, mono_get_optimizations_for_method (callee, default_opt), error); + compiled_method = mono_jit_compile_method (callee, error); if (!compiled_method) { g_assert (!mono_error_ok (error)); return NULL; @@ -3354,6 +3371,7 @@ mini_create_jit_domain_info (MonoDomain *domain) info->seq_points = g_hash_table_new_full (mono_aligned_addr_hash, NULL, NULL, mono_seq_point_info_free); info->arch_seq_points = g_hash_table_new (mono_aligned_addr_hash, NULL); info->jump_target_hash = g_hash_table_new (NULL, NULL); + mono_jit_code_hash_init (&info->interp_code_hash); domain->runtime_info = info; } diff --git a/mono/mini/mini.h b/mono/mini/mini.h index c84e5290d46..730c936de3c 100644 --- a/mono/mini/mini.h +++ b/mono/mini/mini.h @@ -366,6 +366,8 @@ typedef struct gpointer llvm_module; /* Maps MonoMethod -> GSlist of addresses */ GHashTable *llvm_jit_callees; + /* Maps MonoMethod -> RuntimeMethod */ + MonoInternalHashTable interp_code_hash; } MonoJitDomainInfo; typedef struct { @@ -2420,6 +2422,7 @@ gpointer mono_resolve_patch_target (MonoMethod *method, MonoDomain *dom gpointer mono_jit_find_compiled_method_with_jit_info (MonoDomain *domain, MonoMethod *method, MonoJitInfo **ji); gpointer mono_jit_find_compiled_method (MonoDomain *domain, MonoMethod *method); gpointer mono_jit_compile_method (MonoMethod *method, MonoError *error); +gpointer mono_jit_compile_method_jit_only (MonoMethod *method, MonoError *error); gpointer mono_jit_compile_method_inner (MonoMethod *method, MonoDomain *target_domain, int opt, MonoError *error); MonoLMF * mono_get_lmf (void); MonoLMF** mono_get_lmf_addr (void); @@ -3098,6 +3101,7 @@ MonoMethod* mini_get_gsharedvt_in_sig_wrapper (MonoMethodSignature *sig); MonoMethod* mini_get_gsharedvt_out_sig_wrapper (MonoMethodSignature *sig); MonoMethodSignature* mini_get_gsharedvt_out_sig_wrapper_signature (gboolean has_this, gboolean has_ret, int param_count); gboolean mini_gsharedvt_runtime_invoke_supported (MonoMethodSignature *sig); +MonoMethod* mini_get_interp_in_wrapper (MonoMethodSignature *sig); /* SIMD support */ diff --git a/mono/mini/mixed.cs b/mono/mini/mixed.cs new file mode 100644 index 00000000000..be88673cb18 --- /dev/null +++ b/mono/mini/mixed.cs @@ -0,0 +1,182 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; + +/* + * Regression tests for the mixed-mode execution. + * Run with --interp=jit=JitClass + */ + +struct AStruct { + public int i; +} + +struct GStruct { + public int i; +} + +class InterpClass +{ + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static void entry_void_0 () { + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static int entry_int_int (int i) { + return i + 1; + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public int entry_int_this_int (int i) { + return i + 1; + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static string entry_string_string (string s1, string s2) { + return s1 + s2; + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static AStruct entry_struct_struct (AStruct l) { + return l; + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static List entry_ginst_ginst (List l) { + return l; + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static GStruct entry_ginst_ginst_vtype (GStruct l) { + return l; + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static void entry_void_byref_int (ref int i) { + i = i + 1; + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static int entry_8_int_args (int i1, int i2, int i3, int i4, int i5, int i6, int i7, int i8) { + return i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8; + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static int entry_9_int_args (int i1, int i2, int i3, int i4, int i5, int i6, int i7, int i8, int i9) { + return i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9; + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static IntPtr entry_intptr_intptr (IntPtr i) { + return i; + } + +} + +/* The methods in this class will always be JITted */ +class JitClass +{ + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static int entry () { + InterpClass.entry_void_0 (); + InterpClass.entry_void_0 (); + int res = InterpClass.entry_int_int (1); + if (res != 2) + return 1; + var c = new InterpClass (); + res = c.entry_int_this_int (1); + if (res != 2) + return 2; + var s = InterpClass.entry_string_string ("A", "B"); + if (s != "AB") + return 3; + var astruct = new AStruct () { i = 1 }; + var astruct2 = InterpClass.entry_struct_struct (astruct); + if (astruct2.i != 1) + return 4; + var l = new List (); + var l2 = InterpClass.entry_ginst_ginst (l); + if (l != l2) + return 5; + var gstruct = new GStruct () { i = 1 }; + var gstruct2 = InterpClass.entry_ginst_ginst_vtype (gstruct); + if (gstruct2.i != 1) + return 6; + int val = 1; + InterpClass.entry_void_byref_int (ref val); + if (val != 2) + return 7; + res = InterpClass.entry_8_int_args (1, 2, 3, 4, 5, 6, 7, 8); + if (res != 36) + return 8; + res = InterpClass.entry_9_int_args (1, 2, 3, 4, 5, 6, 7, 8, 9); + if (res != 45) + return 9; + var ptr = new IntPtr (32); + var ptr2 = InterpClass.entry_intptr_intptr (ptr); + if (ptr != ptr2) + return 10; + return 0; + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static AStruct exit_vtype (AStruct s) { + return s; + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static List exit_ginst (List l) { + return l; + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static GStruct exit_ginst_vtype (GStruct l) { + return l; + } + + [MethodImplAttribute (MethodImplOptions.NoInlining)] + public static void exit_byref (ref int i) { + i += 1; + } +} + +#if __MOBILE__ +class MixedTests +#else +class Tests +#endif +{ + +#if !__MOBILE__ + public static int Main (string[] args) { + return TestDriver.RunTests (typeof (Tests), args); + } +#endif + + public static int test_0_entry () { + // This does an interp->jit transition + return JitClass.entry (); + } + + public static int test_0_exit () { + var astruct = new AStruct () { i = 1}; + var astruct2 = JitClass.exit_vtype (astruct); + if (astruct2.i != 1) + return 1; + var ginst = new List (); + var ginst2 = JitClass.exit_ginst (ginst); + if (ginst != ginst2) + return 2; + var gstruct = new GStruct () { i = 1 }; + var gstruct2 = JitClass.exit_ginst_vtype (gstruct); + if (gstruct2.i != 1) + return 3; + var anint = 1; + JitClass.exit_byref (ref anint); + if (anint != 2) + return 4; + return 0; + } +} \ No newline at end of file diff --git a/mono/mini/tramp-amd64.c b/mono/mini/tramp-amd64.c index db3c5b96090..aa61977cd8f 100644 --- a/mono/mini/tramp-amd64.c +++ b/mono/mini/tramp-amd64.c @@ -1015,9 +1015,10 @@ mono_arch_get_enter_icall_trampoline (MonoTrampInfo **info) MonoJumpInfo *ji = NULL; GSList *unwind_ops = NULL; static int farg_regs[] = {AMD64_XMM0, AMD64_XMM1, AMD64_XMM2}; - int i, framesize = 0, off_rbp, off_methodargs, off_targetaddr; + int buf_len, i, framesize = 0, off_rbp, off_methodargs, off_targetaddr; - start = code = (guint8 *) mono_global_codeman_reserve (256 + MONO_TRAMPOLINE_UNWINDINFO_SIZE(0)); + buf_len = 512 + MONO_TRAMPOLINE_UNWINDINFO_SIZE(0); + start = code = (guint8 *) mono_global_codeman_reserve (buf_len); off_rbp = -framesize; @@ -1056,9 +1057,8 @@ mono_arch_get_enter_icall_trampoline (MonoTrampInfo **info) amd64_dec_reg_size (code, AMD64_RAX, 1); } - for (i = 0; i < fregs_num; i++) { + for (i = 0; i < fregs_num; i++) x86_patch (label_fexits [i], code); - } /* load pointer to MethodArguments* into R11 */ amd64_mov_reg_reg (code, AMD64_R11, AMD64_ARG_REG2, sizeof (mgreg_t)); @@ -1086,9 +1086,8 @@ mono_arch_get_enter_icall_trampoline (MonoTrampInfo **info) amd64_dec_reg_size (code, AMD64_RAX, 1); } - for (i = 0; i < gregs_num; i++) { + for (i = 0; i < gregs_num; i++) x86_patch (label_gexits [i], code); - } /* load target addr */ amd64_mov_reg_membase (code, AMD64_R11, AMD64_RBP, off_targetaddr, sizeof (mgreg_t)); @@ -1108,8 +1107,6 @@ mono_arch_get_enter_icall_trampoline (MonoTrampInfo **info) label_is_float_ret = code; x86_branch8 (code, X86_CC_NZ, 0, FALSE); - - /* greg return */ /* load MethodArguments */ amd64_mov_reg_membase (code, AMD64_R11, AMD64_RBP, off_methodargs, sizeof (mgreg_t)); @@ -1125,8 +1122,6 @@ mono_arch_get_enter_icall_trampoline (MonoTrampInfo **info) label_leave_tramp [1] = code; x86_jump8 (code, 0); - - /* freg return */ x86_patch (label_is_float_ret, code); /* load MethodArguments */ @@ -1147,6 +1142,8 @@ mono_arch_get_enter_icall_trampoline (MonoTrampInfo **info) amd64_pop_reg (code, AMD64_RBP); amd64_ret (code); + g_assert (code - start < buf_len); + mono_arch_flush_icache (start, code - start); mono_profiler_code_buffer_new (start, code - start, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING, NULL); diff --git a/scripts/ci/run-test-interpreter.sh b/scripts/ci/run-test-interpreter.sh index 08a0c5d7da1..58fddbc742b 100755 --- a/scripts/ci/run-test-interpreter.sh +++ b/scripts/ci/run-test-interpreter.sh @@ -3,5 +3,6 @@ export TESTCMD=`dirname "${BASH_SOURCE[0]}"`/run-step.sh ${TESTCMD} --label=interpreter-regression --timeout=10m make -C mono/mini richeck +${TESTCMD} --label=mixedmode-regression --timeout=10m make -C mono/mini mixedcheck ${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 -- 2.25.1