X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmini%2Fmini.c;h=f977b3a66c49caba9ae20bb4d015f0327c9b0121;hb=e4abb3281f6abfa1effcbfc174884a3bb63865a4;hp=749e482c9882e09f5e308b91261ccfe0060e2503;hpb=31ce743aa39f7334646fe9ab65fdc023a5cd55f0;p=mono.git diff --git a/mono/mini/mini.c b/mono/mini/mini.c index 749e482c988..f977b3a66c4 100644 --- a/mono/mini/mini.c +++ b/mono/mini/mini.c @@ -21,9 +21,7 @@ #include #endif -#ifdef HAVE_VALGRIND_MEMCHECK_H -#include -#endif +#include #include #include @@ -55,6 +53,7 @@ #include #include "mini.h" +#include "tasklets.h" #include #include #include "trace.h" @@ -63,9 +62,10 @@ #include "jit-icalls.h" #include "debug-mini.h" +#include "mini-gc.h" +#include "debugger-agent.h" -static gpointer mono_jit_compile_method_with_opt (MonoMethod *method, guint32 opt); -static gpointer mono_jit_compile_method (MonoMethod *method); +static gpointer mono_jit_compile_method_with_opt (MonoMethod *method, guint32 opt, MonoException **ex); /* helper methods signature */ /* FIXME: Make these static again */ @@ -74,6 +74,7 @@ MonoMethodSignature *helper_sig_domain_get = NULL; MonoMethodSignature *helper_sig_generic_class_init_trampoline = NULL; MonoMethodSignature *helper_sig_rgctx_lazy_fetch_trampoline = NULL; MonoMethodSignature *helper_sig_monitor_enter_exit_trampoline = NULL; +MonoMethodSignature *helper_sig_monitor_enter_exit_trampoline_llvm = NULL; static guint32 default_opt = 0; static gboolean default_opt_set = FALSE; @@ -102,17 +103,37 @@ int mono_break_at_bb_bb_num; gboolean mono_do_x86_stack_align = TRUE; const char *mono_build_date; gboolean mono_do_signal_chaining; - +static gboolean mono_using_xdebug; static int mini_verbose = 0; +/* Statistics */ +#ifdef ENABLE_LLVM +static int methods_with_llvm, methods_without_llvm; +#endif + +/* + * This flag controls whenever the runtime uses LLVM compiled code. + * Enabling this causes different/slower code paths to be used, which is why it + * defaults to FALSE if ENABLE_LLVM is not defined, i.e. the runtime is only capable of + * running AOT code compiled by LLVM. + * Changes when this flag is set include: + * - a per method vtable trampoline is used to handle virtual calls, instead of only + * one trampoline. + * - fast generic virtual calls are not supported. + */ +#ifdef ENABLE_LLVM +gboolean mono_use_llvm = TRUE; +#else +gboolean mono_use_llvm = FALSE; +#endif + #define mono_jit_lock() EnterCriticalSection (&jit_mutex) #define mono_jit_unlock() LeaveCriticalSection (&jit_mutex) static CRITICAL_SECTION jit_mutex; static MonoCodeManager *global_codeman = NULL; -/* FIXME: Make this static again */ -GHashTable *jit_icall_name_hash = NULL; +static GHashTable *jit_icall_name_hash = NULL; static MonoDebugOptions debug_options; @@ -139,17 +160,13 @@ gboolean mono_dont_free_global_codeman; gboolean mono_running_on_valgrind (void) { -#ifdef HAVE_VALGRIND_MEMCHECK_H - if (RUNNING_ON_VALGRIND){ + if (RUNNING_ON_VALGRIND){ #ifdef VALGRIND_JIT_REGISTER_MAP - valgrind_register = TRUE; + valgrind_register = TRUE; #endif - return TRUE; - } else - return FALSE; -#else + return TRUE; + } else return FALSE; -#endif } typedef struct { @@ -241,9 +258,10 @@ mono_print_method_from_ip (void *ip) char *method; MonoDebugSourceLocation *source; MonoDomain *domain = mono_domain_get (); + MonoDomain *target_domain = mono_domain_get (); FindTrampUserData user_data; - ji = mono_jit_info_table_find (domain, ip); + ji = mini_jit_info_table_find (domain, ip, &target_domain); if (!ji) { user_data.ip = ip; user_data.method = NULL; @@ -260,9 +278,9 @@ mono_print_method_from_ip (void *ip) return; } method = mono_method_full_name (ji->method, TRUE); - source = mono_debug_lookup_source_location (ji->method, (guint32)((guint8*)ip - (guint8*)ji->code_start), domain); + source = mono_debug_lookup_source_location (ji->method, (guint32)((guint8*)ip - (guint8*)ji->code_start), target_domain); - g_print ("IP %p at offset 0x%x of method %s (%p %p)[domain %p - %s]\n", ip, (int)((char*)ip - (char*)ji->code_start), method, ji->code_start, (char*)ji->code_start + ji->code_size, domain, domain->friendly_name); + g_print ("IP %p at offset 0x%x of method %s (%p %p)[domain %p - %s]\n", ip, (int)((char*)ip - (char*)ji->code_start), method, ji->code_start, (char*)ji->code_start + ji->code_size, target_domain, target_domain->friendly_name); if (source) g_print ("%s:%d\n", source->source_file, source->row); @@ -449,16 +467,63 @@ mono_bblocks_linked (MonoBasicBlock *bb1, MonoBasicBlock *bb2) return FALSE; } -MonoInst * -mono_find_spvar_for_region (MonoCompile *cfg, int region) +static int +mono_find_block_region_notry (MonoCompile *cfg, int offset) { - return g_hash_table_lookup (cfg->spvars, GINT_TO_POINTER (region)); + MonoMethod *method = cfg->method; + MonoMethodHeader *header = mono_method_get_header (method); + MonoExceptionClause *clause; + int i; + + for (i = 0; i < header->num_clauses; ++i) { + clause = &header->clauses [i]; + if ((clause->flags == MONO_EXCEPTION_CLAUSE_FILTER) && (offset >= clause->data.filter_offset) && + (offset < (clause->handler_offset))) + return ((i + 1) << 8) | MONO_REGION_FILTER | clause->flags; + + if (MONO_OFFSET_IN_HANDLER (clause, offset)) { + if (clause->flags == MONO_EXCEPTION_CLAUSE_FINALLY) + return ((i + 1) << 8) | MONO_REGION_FINALLY | clause->flags; + else if (clause->flags == MONO_EXCEPTION_CLAUSE_FAULT) + return ((i + 1) << 8) | MONO_REGION_FAULT | clause->flags; + else + return ((i + 1) << 8) | MONO_REGION_CATCH | clause->flags; + } + } + + return -1; +} + +/* + * mono_get_block_region_notry: + * + * Return the region corresponding to REGION, ignoring try clauses nested inside + * finally clauses. + */ +int +mono_get_block_region_notry (MonoCompile *cfg, int region) +{ + if ((region & (0xf << 4)) == MONO_REGION_TRY) { + MonoMethodHeader *header = mono_method_get_header (cfg->method); + + /* + * This can happen if a try clause is nested inside a finally clause. + */ + int clause_index = (region >> 8) - 1; + g_assert (clause_index >= 0 && clause_index < header->num_clauses); + + region = mono_find_block_region_notry (cfg, header->clauses [clause_index].try_offset); + } + + return region; } -static MonoInst * -mono_find_exvar_for_offset (MonoCompile *cfg, int offset) +MonoInst * +mono_find_spvar_for_region (MonoCompile *cfg, int region) { - return g_hash_table_lookup (cfg->exvars, GINT_TO_POINTER (offset)); + region = mono_get_block_region_notry (cfg, region); + + return g_hash_table_lookup (cfg->spvars, GINT_TO_POINTER (region)); } static void @@ -583,7 +648,9 @@ mono_type_to_load_membase (MonoCompile *cfg, MonoType *type) if (type->byref) return OP_LOAD_MEMBASE; - switch (mono_type_get_underlying_type (type)->type) { + type = mono_type_get_underlying_type (type); + + switch (type->type) { case MONO_TYPE_I1: return OP_LOADI1_MEMBASE; case MONO_TYPE_U1: @@ -694,6 +761,18 @@ mono_op_imm_to_op (int opcode) return OP_IAND; #else return OP_LAND; +#endif + case OP_OR_IMM: +#if SIZEOF_REGISTER == 4 + return OP_IOR; +#else + return OP_LOR; +#endif + case OP_XOR_IMM: +#if SIZEOF_REGISTER == 4 + return OP_IXOR; +#else + return OP_LXOR; #endif case OP_IAND_IMM: return OP_IAND; @@ -938,7 +1017,6 @@ mono_compile_create_var (MonoCompile *cfg, MonoType *type, int opcode) void mono_compile_make_var_load (MonoCompile *cfg, MonoInst *dest, gssize var_index) { memset (dest, 0, sizeof (MonoInst)); - dest->ssa_op = MONO_SSA_LOAD; dest->inst_i0 = cfg->varinfo [var_index]; dest->opcode = mini_type_to_ldind (cfg, dest->inst_i0->inst_vtype); type_to_eval_stack_type (cfg, dest->inst_i0->inst_vtype, dest); @@ -1090,7 +1168,7 @@ mono_get_array_new_va_signature (int arity) res->call_convention = MONO_CALL_VARARG; #endif -#ifdef PLATFORM_WIN32 +#ifdef TARGET_WIN32 res->call_convention = MONO_CALL_C; #endif @@ -1200,15 +1278,19 @@ mini_method_verify (MonoCompile *cfg, MonoMethod *method) for (tmp = res; tmp; tmp = tmp->next) { MonoVerifyInfoExtended *info = (MonoVerifyInfoExtended *)tmp->data; if (info->info.status == MONO_VERIFY_ERROR) { + char *method_name = mono_method_full_name (method, TRUE); cfg->exception_type = info->exception_type; - cfg->exception_message = g_strdup (info->info.message); + cfg->exception_message = g_strdup_printf ("Error verifying %s: %s", method_name, info->info.message); mono_free_verify_list (res); + g_free (method_name); return TRUE; } - if (info->info.status == MONO_VERIFY_NOT_VERIFIABLE && !is_fulltrust) { + if (info->info.status == MONO_VERIFY_NOT_VERIFIABLE && (!is_fulltrust || info->exception_type == MONO_EXCEPTION_METHOD_ACCESS || info->exception_type == MONO_EXCEPTION_FIELD_ACCESS)) { + char *method_name = mono_method_full_name (method, TRUE); cfg->exception_type = info->exception_type; - cfg->exception_message = g_strdup (info->info.message); + cfg->exception_message = g_strdup_printf ("Error verifying %s: %s", method_name, info->info.message); mono_free_verify_list (res); + g_free (method_name); return TRUE; } } @@ -1218,6 +1300,23 @@ mini_method_verify (MonoCompile *cfg, MonoMethod *method) return FALSE; } +/*Returns true is something went wrong*/ +static gboolean +mono_compile_is_broken (MonoCompile *cfg) +{ + MonoMethod *method = cfg->method; + MonoMethod *method_definition = method; + gboolean dont_verify = mini_assembly_can_skip_verification (cfg->domain, method); + dont_verify |= method->klass->image->assembly->corlib_internal; + + while (method_definition->is_inflated) { + MonoMethodInflated *imethod = (MonoMethodInflated *) method_definition; + method_definition = imethod->declaring; + } + + return !dont_verify && mini_method_verify (cfg, method_definition); +} + static void create_helper_signature (void) { @@ -1226,6 +1325,7 @@ create_helper_signature (void) helper_sig_generic_class_init_trampoline = mono_create_icall_signature ("void"); helper_sig_rgctx_lazy_fetch_trampoline = mono_create_icall_signature ("ptr ptr"); helper_sig_monitor_enter_exit_trampoline = mono_create_icall_signature ("void"); + helper_sig_monitor_enter_exit_trampoline_llvm = mono_create_icall_signature ("void object"); } static gconstpointer @@ -1342,6 +1442,7 @@ mono_allocate_stack_slots_full2 (MonoCompile *cfg, gboolean backward, guint32 *s StackSlotInfo *scalar_stack_slots, *vtype_stack_slots, *slot_info; MonoType *t; int nvtypes; + gboolean reuse_slot; LSCAN_DEBUG (printf ("Allocate Stack Slots 2 for %s:\n", mono_method_full_name (cfg->method, TRUE))); @@ -1396,8 +1497,15 @@ mono_allocate_stack_slots_full2 (MonoCompile *cfg, gboolean backward, guint32 *s size = mono_type_size (inst->inst_vtype, &ialign); align = ialign; + + if (MONO_CLASS_IS_SIMD (cfg, mono_class_from_mono_type (inst->inst_vtype))) + align = 16; } + reuse_slot = TRUE; + if (cfg->disable_reuse_stack_slots) + reuse_slot = FALSE; + t = mono_type_get_underlying_type (inst->inst_vtype); switch (t->type) { case MONO_TYPE_GENERICINST: @@ -1420,12 +1528,10 @@ mono_allocate_stack_slots_full2 (MonoCompile *cfg, gboolean backward, guint32 *s slot_info = &vtype_stack_slots [nvtypes]; nvtypes ++; } + if (cfg->disable_reuse_ref_stack_slots) + reuse_slot = FALSE; break; - case MONO_TYPE_CLASS: - case MONO_TYPE_OBJECT: - case MONO_TYPE_ARRAY: - case MONO_TYPE_SZARRAY: - case MONO_TYPE_STRING: + case MONO_TYPE_PTR: case MONO_TYPE_I: case MONO_TYPE_U: @@ -1433,10 +1539,25 @@ mono_allocate_stack_slots_full2 (MonoCompile *cfg, gboolean backward, guint32 *s case MONO_TYPE_I4: #else case MONO_TYPE_I8: +#endif +#ifdef HAVE_SGEN_GC + slot_info = &scalar_stack_slots [MONO_TYPE_I]; + break; +#else + /* Fall through */ +#endif + + case MONO_TYPE_CLASS: + case MONO_TYPE_OBJECT: + case MONO_TYPE_ARRAY: + case MONO_TYPE_SZARRAY: + case MONO_TYPE_STRING: /* Share non-float stack slots of the same size */ slot_info = &scalar_stack_slots [MONO_TYPE_CLASS]; + if (cfg->disable_reuse_ref_stack_slots) + reuse_slot = FALSE; break; -#endif + default: slot_info = &scalar_stack_slots [t->type]; } @@ -1554,14 +1675,19 @@ mono_allocate_stack_slots_full2 (MonoCompile *cfg, gboolean backward, guint32 *s LSCAN_DEBUG (printf ("R%d %s -> 0x%x\n", inst->dreg, mono_type_full_name (t), slot)); + if (!reuse_slot) + slot = 0xffffff; + if (slot == 0xffffff) { /* * Allways allocate valuetypes to sizeof (gpointer) to allow more * efficient copying (and to work around the fact that OP_MEMCPY * and OP_MEMSET ignores alignment). */ - if (MONO_TYPE_ISSTRUCT (t)) - align = MAX (sizeof (gpointer), mono_class_min_align (mono_class_from_mono_type (t))); + if (MONO_TYPE_ISSTRUCT (t)) { + align = MAX (align, sizeof (gpointer)); + align = MAX (align, mono_class_min_align (mono_class_from_mono_type (t))); + } if (backward) { offset += size; @@ -1619,6 +1745,7 @@ mono_allocate_stack_slots_full (MonoCompile *cfg, gboolean backward, guint32 *st StackSlotInfo *scalar_stack_slots, *vtype_stack_slots, *slot_info; MonoType *t; int nvtypes; + gboolean reuse_slot; if ((cfg->num_varinfo > 0) && MONO_VARINFO (cfg, 0)->interval) return mono_allocate_stack_slots_full2 (cfg, backward, stack_size, stack_align); @@ -1657,8 +1784,15 @@ mono_allocate_stack_slots_full (MonoCompile *cfg, gboolean backward, guint32 *st size = mono_type_size (inst->inst_vtype, &ialign); align = ialign; + + if (MONO_CLASS_IS_SIMD (cfg, mono_class_from_mono_type (inst->inst_vtype))) + align = 16; } + reuse_slot = TRUE; + if (cfg->disable_reuse_stack_slots) + reuse_slot = FALSE; + t = mono_type_get_underlying_type (inst->inst_vtype); if (t->byref) { slot_info = &scalar_stack_slots [MONO_TYPE_I]; @@ -1684,12 +1818,10 @@ mono_allocate_stack_slots_full (MonoCompile *cfg, gboolean backward, guint32 *st slot_info = &vtype_stack_slots [nvtypes]; nvtypes ++; } + if (cfg->disable_reuse_ref_stack_slots) + reuse_slot = FALSE; break; - case MONO_TYPE_CLASS: - case MONO_TYPE_OBJECT: - case MONO_TYPE_ARRAY: - case MONO_TYPE_SZARRAY: - case MONO_TYPE_STRING: + case MONO_TYPE_PTR: case MONO_TYPE_I: case MONO_TYPE_U: @@ -1698,9 +1830,24 @@ mono_allocate_stack_slots_full (MonoCompile *cfg, gboolean backward, guint32 *st #else case MONO_TYPE_I8: #endif +#ifdef HAVE_SGEN_GC + slot_info = &scalar_stack_slots [MONO_TYPE_I]; + break; +#else + /* Fall through */ +#endif + + case MONO_TYPE_CLASS: + case MONO_TYPE_OBJECT: + case MONO_TYPE_ARRAY: + case MONO_TYPE_SZARRAY: + case MONO_TYPE_STRING: /* Share non-float stack slots of the same size */ slot_info = &scalar_stack_slots [MONO_TYPE_CLASS]; + if (cfg->disable_reuse_ref_stack_slots) + reuse_slot = FALSE; break; + default: slot_info = &scalar_stack_slots [t->type]; } @@ -1757,7 +1904,7 @@ mono_allocate_stack_slots_full (MonoCompile *cfg, gboolean backward, guint32 *st */ } - if (cfg->disable_reuse_stack_slots) + if (!reuse_slot) slot = 0xffffff; if (slot == 0xffffff) { @@ -1767,7 +1914,8 @@ mono_allocate_stack_slots_full (MonoCompile *cfg, gboolean backward, guint32 *st * and OP_MEMSET ignores alignment). */ if (MONO_TYPE_ISSTRUCT (t)) { - align = MAX (sizeof (gpointer), mono_class_min_align (mono_class_from_mono_type (t))); + align = MAX (align, sizeof (gpointer)); + align = MAX (align, mono_class_min_align (mono_class_from_mono_type (t))); /* * Align the size too so the code generated for passing vtypes in * registers doesn't overwrite random locals. @@ -2097,6 +2245,19 @@ mono_get_lmf_addr (void) #else MonoJitTlsData *jit_tls; + if ((jit_tls = TlsGetValue (mono_jit_tls_id))) + return &jit_tls->lmf; + + /* + * When resolving the call to mono_jit_thread_attach full-aot will look + * in the plt, which causes a call into the generic trampoline, which in turn + * tries to resolve the lmf_addr creating a cyclic dependency. We cannot + * call mono_jit_thread_attach from the native-to-managed wrapper, without + * mono_get_lmf_addr, and mono_get_lmf_addr requires the thread to be attached. + */ + + mono_jit_thread_attach (NULL); + if ((jit_tls = TlsGetValue (mono_jit_tls_id))) return &jit_tls->lmf; @@ -2105,10 +2266,27 @@ mono_get_lmf_addr (void) #endif } +void +mono_set_lmf (MonoLMF *lmf) +{ +#if defined(HAVE_KW_THREAD) && defined(MONO_ARCH_ENABLE_MONO_LMF_VAR) + mono_lmf = lmf; +#endif + + (*mono_get_lmf_addr ()) = lmf; +} + /* Called by native->managed wrappers */ void mono_jit_thread_attach (MonoDomain *domain) { + if (!domain) + /* + * Happens when called from AOTed code which is only used in the root + * domain. + */ + domain = mono_get_root_domain (); + #ifdef HAVE_KW_THREAD if (!mono_lmf_addr) { mono_thread_attach (domain); @@ -2194,10 +2372,10 @@ setup_jit_tls_data (gpointer stack_start, gpointer abort_func) static void mono_thread_start_cb (gsize tid, gpointer stack_start, gpointer func) { - MonoThread *thread; + MonoInternalThread *thread; void *jit_tls = setup_jit_tls_data (stack_start, mono_thread_abort); - thread = mono_thread_current (); - mono_debugger_thread_created (tid, thread, jit_tls); + thread = mono_thread_internal_current (); + mono_debugger_thread_created (tid, thread->root_domain_thread, jit_tls, func); if (thread) thread->jit_data = jit_tls; } @@ -2216,10 +2394,10 @@ mono_thread_abort_dummy (MonoObject *obj) static void mono_thread_attach_cb (gsize tid, gpointer stack_start) { - MonoThread *thread; + MonoInternalThread *thread; void *jit_tls = setup_jit_tls_data (stack_start, mono_thread_abort_dummy); - thread = mono_thread_current (); - mono_debugger_thread_created (tid, thread, (MonoJitTlsData *) jit_tls); + thread = mono_thread_internal_current (); + mono_debugger_thread_created (tid, thread->root_domain_thread, (MonoJitTlsData *) jit_tls, NULL); if (thread) thread->jit_data = jit_tls; if (mono_profiler_get_events () & MONO_PROFILE_STATISTICAL) @@ -2229,7 +2407,8 @@ mono_thread_attach_cb (gsize tid, gpointer stack_start) static void mini_thread_cleanup (MonoThread *thread) { - MonoJitTlsData *jit_tls = thread->jit_data; + MonoInternalThread *internal = thread->internal_thread; + MonoJitTlsData *jit_tls = internal->jit_data; if (jit_tls) { mono_debugger_thread_cleanup (jit_tls); @@ -2238,7 +2417,7 @@ mini_thread_cleanup (MonoThread *thread) mono_free_altstack (jit_tls); g_free (jit_tls->first_lmf); g_free (jit_tls); - thread->jit_data = NULL; + internal->jit_data = NULL; /* We can't clean up tls information if we are on another thread, it will clean up the wrong stuff * It would be nice to issue a warning when this happens outside of the shutdown sequence. but it's @@ -2246,7 +2425,7 @@ mini_thread_cleanup (MonoThread *thread) * * The current offender is mono_thread_manage which cleanup threads from the outside. */ - if (thread == mono_thread_current ()) { + if (internal == mono_thread_internal_current ()) { TlsSetValue (mono_jit_tls_id, NULL); #ifdef HAVE_KW_THREAD @@ -2358,6 +2537,8 @@ mono_patch_info_dup_mp (MonoMemPool *mp, MonoJumpInfo *patch_info) case MONO_PATCH_INFO_SWITCH: res->data.table = mono_mempool_alloc (mp, sizeof (MonoJumpInfoBBTable)); memcpy (res->data.table, patch_info->data.table, sizeof (MonoJumpInfoBBTable)); + res->data.table->table = mono_mempool_alloc (mp, sizeof (MonoBasicBlock*) * patch_info->data.table->table_size); + memcpy (res->data.table->table, patch_info->data.table->table, sizeof (MonoBasicBlock*) * patch_info->data.table->table_size); break; case MONO_PATCH_INFO_RGCTX_FETCH: res->data.rgctx_entry = mono_mempool_alloc (mp, sizeof (MonoJumpInfoRgctxEntry)); @@ -2383,6 +2564,8 @@ mono_patch_info_hash (gconstpointer data) case MONO_PATCH_INFO_LDTOKEN: case MONO_PATCH_INFO_DECLSEC: return (ji->type << 8) | ji->data.token->token; + case MONO_PATCH_INFO_INTERNAL_METHOD: + return (ji->type << 8) | g_str_hash (ji->data.name); case MONO_PATCH_INFO_VTABLE: case MONO_PATCH_INFO_CLASS: case MONO_PATCH_INFO_IID: @@ -2392,10 +2575,10 @@ mono_patch_info_hash (gconstpointer data) case MONO_PATCH_INFO_METHOD: case MONO_PATCH_INFO_METHOD_JUMP: case MONO_PATCH_INFO_IMAGE: - case MONO_PATCH_INFO_INTERNAL_METHOD: case MONO_PATCH_INFO_JIT_ICALL_ADDR: case MONO_PATCH_INFO_FIELD: case MONO_PATCH_INFO_SFLDA: + case MONO_PATCH_INFO_SEQ_POINT_INFO: return (ji->type << 8) | (gssize)ji->data.target; default: return (ji->type << 8); @@ -2431,6 +2614,15 @@ mono_patch_info_equal (gconstpointer ka, gconstpointer kb) (ji1->data.token->context.method_inst != ji2->data.token->context.method_inst)) return 0; break; + case MONO_PATCH_INFO_INTERNAL_METHOD: + return g_str_equal (ji1->data.name, ji2->data.name); + + case MONO_PATCH_INFO_RGCTX_FETCH: { + MonoJumpInfoRgctxEntry *e1 = ji1->data.rgctx_entry; + MonoJumpInfoRgctxEntry *e2 = ji2->data.rgctx_entry; + + return e1->method == e2->method && e1->in_mrgctx == e2->in_mrgctx && e1->info_type == e2->info_type && mono_patch_info_equal (e1->data, e2->data); + } default: if (ji1->data.target != ji2->data.target) return 0; @@ -2448,7 +2640,11 @@ mono_resolve_patch_target (MonoMethod *method, MonoDomain *domain, guint8 *code, switch (patch_info->type) { case MONO_PATCH_INFO_BB: - g_assert (patch_info->data.bb->native_offset); + /* + * FIXME: This could be hit for methods without a prolog. Should use -1 + * but too much code depends on a 0 initial value. + */ + //g_assert (patch_info->data.bb->native_offset); target = patch_info->data.bb->native_offset + code; break; case MONO_PATCH_INFO_ABS: @@ -2480,12 +2676,7 @@ mono_resolve_patch_target (MonoMethod *method, MonoDomain *domain, guint8 *code, target = code; } else { /* get the trampoline to the method from the domain */ - if (method && method->wrapper_type == MONO_WRAPPER_STATIC_RGCTX_INVOKE) { - target = mono_create_jit_trampoline_in_domain (mono_domain_get (), - patch_info->data.method); - } else { - target = mono_create_jit_trampoline (patch_info->data.method); - } + target = mono_create_jit_trampoline (patch_info->data.method); } break; case MONO_PATCH_INFO_SWITCH: { @@ -2625,9 +2816,13 @@ mono_resolve_patch_target (MonoMethod *method, MonoDomain *domain, guint8 *code, case MONO_PATCH_INFO_INTERRUPTION_REQUEST_FLAG: target = mono_thread_interruption_request_flag (); break; - case MONO_PATCH_INFO_METHOD_RGCTX: - target = mono_method_lookup_rgctx (mono_class_vtable (domain, patch_info->data.method->klass), mini_method_get_context (patch_info->data.method)->method_inst); + case MONO_PATCH_INFO_METHOD_RGCTX: { + MonoVTable *vtable = mono_class_vtable (domain, patch_info->data.method->klass); + g_assert (vtable); + + target = mono_method_lookup_rgctx (vtable, mini_method_get_context (patch_info->data.method)->method_inst); break; + } case MONO_PATCH_INFO_BB_OVF: case MONO_PATCH_INFO_EXC_OVF: case MONO_PATCH_INFO_GOT_OFFSET: @@ -2665,6 +2860,23 @@ mono_resolve_patch_target (MonoMethod *method, MonoDomain *domain, guint8 *code, case MONO_PATCH_INFO_MONITOR_EXIT: target = mono_create_monitor_exit_trampoline (); break; +#ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED + case MONO_PATCH_INFO_SEQ_POINT_INFO: + if (!run_cctors) + /* AOT, not needed */ + target = NULL; + else + target = mono_arch_get_seq_point_info (domain, code); + break; +#endif + case MONO_PATCH_INFO_LLVM_IMT_TRAMPOLINE: +#ifdef MONO_ARCH_LLVM_SUPPORTED + g_assert (mono_use_llvm); + target = mono_create_llvm_imt_trampoline (domain, patch_info->data.imt_tramp->method, patch_info->data.imt_tramp->vt_offset); +#else + g_assert_not_reached (); +#endif + break; default: g_assert_not_reached (); } @@ -2672,6 +2884,17 @@ mono_resolve_patch_target (MonoMethod *method, MonoDomain *domain, guint8 *code, return (gpointer)target; } +void +mono_add_seq_point (MonoCompile *cfg, MonoBasicBlock *bb, MonoInst *ins, int native_offset) +{ + ins->inst_offset = native_offset; + g_ptr_array_add (cfg->seq_points, ins); + bb->seq_points = g_slist_prepend_mempool (cfg->mempool, bb->seq_points, ins); + bb->last_seq_point = ins; +} + +#ifndef DISABLE_JIT + static void mono_compile_create_vars (MonoCompile *cfg) { @@ -2732,6 +2955,8 @@ mono_compile_create_vars (MonoCompile *cfg) mono_arch_create_vars (cfg); } +#endif /* #ifndef DISABLE_JIT */ + void mono_print_code (MonoCompile *cfg, const char* msg) { @@ -2743,101 +2968,12 @@ mono_print_code (MonoCompile *cfg, const char* msg) #ifndef DISABLE_JIT -void -mono_codegen (MonoCompile *cfg) +static void +mono_postprocess_patches (MonoCompile *cfg) { MonoJumpInfo *patch_info; - MonoBasicBlock *bb; - int i, max_epilog_size; - guint8 *code; - - for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { - cfg->spill_count = 0; - /* we reuse dfn here */ - /* bb->dfn = bb_count++; */ - - mono_arch_lowering_pass (cfg, bb); - - if (cfg->opt & MONO_OPT_PEEPHOLE) - mono_arch_peephole_pass_1 (cfg, bb); - - if (!cfg->globalra) - mono_local_regalloc (cfg, bb); - - if (cfg->opt & MONO_OPT_PEEPHOLE) - mono_arch_peephole_pass_2 (cfg, bb); - } - - if (cfg->prof_options & MONO_PROFILE_COVERAGE) - cfg->coverage_info = mono_profiler_coverage_alloc (cfg->method, cfg->num_bblocks); - - code = mono_arch_emit_prolog (cfg); - - if (cfg->prof_options & MONO_PROFILE_ENTER_LEAVE) - code = mono_arch_instrument_prolog (cfg, mono_profiler_method_enter, code, FALSE); - - cfg->code_len = code - cfg->native_code; - cfg->prolog_end = cfg->code_len; - - mono_debug_open_method (cfg); - - /* emit code all basic blocks */ - for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { - bb->native_offset = cfg->code_len; - //if ((bb == cfg->bb_entry) || !(bb->region == -1 && !bb->dfn)) - mono_arch_output_basic_block (cfg, bb); - - if (bb == cfg->bb_exit) { - cfg->epilog_begin = cfg->code_len; - - if (cfg->prof_options & MONO_PROFILE_ENTER_LEAVE) { - code = cfg->native_code + cfg->code_len; - code = mono_arch_instrument_epilog (cfg, mono_profiler_method_leave, code, FALSE); - cfg->code_len = code - cfg->native_code; - g_assert (cfg->code_len < cfg->code_size); - } - - mono_arch_emit_epilog (cfg); - } - } - - mono_arch_emit_exceptions (cfg); - - max_epilog_size = 0; - - code = cfg->native_code + cfg->code_len; - - /* we always allocate code in cfg->domain->code_mp to increase locality */ - cfg->code_size = cfg->code_len + max_epilog_size; - /* fixme: align to MONO_ARCH_CODE_ALIGNMENT */ - - if (cfg->method->dynamic) { - guint unwindlen = 0; -#ifdef MONO_ARCH_HAVE_UNWIND_TABLE - unwindlen = mono_arch_unwindinfo_get_size (cfg->arch.unwindinfo); -#endif - /* Allocate the code into a separate memory pool so it can be freed */ - cfg->dynamic_info = g_new0 (MonoJitDynamicMethodInfo, 1); - cfg->dynamic_info->code_mp = mono_code_manager_new_dynamic (); - mono_domain_lock (cfg->domain); - mono_dynamic_code_hash_insert (cfg->domain, cfg->method, cfg->dynamic_info); - mono_domain_unlock (cfg->domain); - - code = mono_code_manager_reserve (cfg->dynamic_info->code_mp, cfg->code_size + unwindlen); - } else { - guint unwindlen = 0; -#ifdef MONO_ARCH_HAVE_UNWIND_TABLE - unwindlen = mono_arch_unwindinfo_get_size (cfg->arch.unwindinfo); -#endif - code = mono_domain_code_reserve (cfg->domain, cfg->code_size + unwindlen); - } + int i; - memcpy (code, cfg->native_code, cfg->code_len); - g_free (cfg->native_code); - cfg->native_code = code; - code = cfg->native_code + cfg->code_len; - - /* g_assert (((int)cfg->native_code & (MONO_ARCH_CODE_ALIGNMENT - 1)) == 0); */ for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) { switch (patch_info->type) { case MONO_PATCH_INFO_ABS: { @@ -2924,13 +3060,221 @@ mono_codegen (MonoCompile *cfg) break; } } +} -#ifdef VALGRIND_JIT_REGISTER_MAP -if (valgrind_register){ - char* nm = mono_method_full_name (cfg->method, TRUE); - VALGRIND_JIT_REGISTER_MAP (nm, cfg->native_code, cfg->native_code + cfg->code_len); - g_free (nm); - } +static void +collect_pred_seq_points (MonoBasicBlock *bb, MonoInst *ins, GSList **next, int depth) +{ + int i; + MonoBasicBlock *in_bb; + + for (i = 0; i < bb->in_count; ++i) { + in_bb = bb->in_bb [i]; + + if (in_bb->last_seq_point) { + next [in_bb->last_seq_point->backend.size] = g_slist_append (next [in_bb->last_seq_point->backend.size], GUINT_TO_POINTER (ins->backend.size)); + } else { + /* Have to look at its predecessors */ + if (depth < 5) + collect_pred_seq_points (in_bb, ins, next, depth + 1); + } + } +} + +static void +mono_save_seq_point_info (MonoCompile *cfg) +{ + MonoBasicBlock *bb; + GSList *bb_seq_points, *l; + MonoInst *last; + MonoDomain *domain = cfg->domain; + int i; + MonoSeqPointInfo *info; + GSList **next; + + if (!cfg->seq_points) + return; + + info = g_malloc0 (sizeof (MonoSeqPointInfo) + (cfg->seq_points->len - MONO_ZERO_LEN_ARRAY) * sizeof (SeqPoint)); + info->len = cfg->seq_points->len; + for (i = 0; i < cfg->seq_points->len; ++i) { + SeqPoint *sp = &info->seq_points [i]; + MonoInst *ins = g_ptr_array_index (cfg->seq_points, i); + + sp->il_offset = ins->inst_imm; + sp->native_offset = ins->inst_offset; + + /* Used below */ + ins->backend.size = i; + } + + /* + * For each sequence point, compute the list of sequence points immediately + * following it, this is needed to implement 'step over' in the debugger agent. + */ + next = g_new0 (GSList*, cfg->seq_points->len); + for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { + bb_seq_points = g_slist_reverse (bb->seq_points); + last = NULL; + for (l = bb_seq_points; l; l = l->next) { + MonoInst *ins = l->data; + + if (!(ins->flags & MONO_INST_SINGLE_STEP_LOC)) + continue; + + if (last != NULL) { + /* Link with the previous seq point in the same bb */ + next [last->backend.size] = g_slist_append (next [last->backend.size], GUINT_TO_POINTER (ins->backend.size)); + } else { + /* Link with the last bb in the previous bblocks */ + collect_pred_seq_points (bb, ins, next, 0); + } + + last = ins; + } + } + + if (cfg->verbose_level > 2) { + printf ("\nSEQ POINT MAP: \n"); + } + + for (i = 0; i < cfg->seq_points->len; ++i) { + SeqPoint *sp = &info->seq_points [i]; + GSList *l; + int j, next_index; + + sp->next_len = g_slist_length (next [i]); + sp->next = g_new (int, sp->next_len); + j = 0; + if (cfg->verbose_level > 2 && next [i]) { + printf ("\t0x%x ->", sp->il_offset); + for (l = next [i]; l; l = l->next) { + next_index = GPOINTER_TO_UINT (l->data); + printf (" 0x%x", info->seq_points [next_index].il_offset); + } + printf ("\n"); + } + for (l = next [i]; l; l = l->next) { + next_index = GPOINTER_TO_UINT (l->data); + sp->next [j ++] = next_index; + } + g_slist_free (next [i]); + } + g_free (next); + + cfg->seq_point_info = info; + + // FIXME: dynamic methods + mono_domain_lock (domain); + g_hash_table_insert (domain_jit_info (domain)->seq_points, cfg->method_to_register, info); + mono_domain_unlock (domain); + + g_ptr_array_free (cfg->seq_points, TRUE); + cfg->seq_points = NULL; +} + +void +mono_codegen (MonoCompile *cfg) +{ + MonoBasicBlock *bb; + int max_epilog_size; + guint8 *code; + + for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { + cfg->spill_count = 0; + /* we reuse dfn here */ + /* bb->dfn = bb_count++; */ + + mono_arch_lowering_pass (cfg, bb); + + if (cfg->opt & MONO_OPT_PEEPHOLE) + mono_arch_peephole_pass_1 (cfg, bb); + + if (!cfg->globalra) + mono_local_regalloc (cfg, bb); + + if (cfg->opt & MONO_OPT_PEEPHOLE) + mono_arch_peephole_pass_2 (cfg, bb); + } + + if (cfg->prof_options & MONO_PROFILE_COVERAGE) + cfg->coverage_info = mono_profiler_coverage_alloc (cfg->method, cfg->num_bblocks); + + code = mono_arch_emit_prolog (cfg); + + if (cfg->prof_options & MONO_PROFILE_ENTER_LEAVE) + code = mono_arch_instrument_prolog (cfg, mono_profiler_method_enter, code, FALSE); + + cfg->code_len = code - cfg->native_code; + cfg->prolog_end = cfg->code_len; + + mono_debug_open_method (cfg); + + /* emit code all basic blocks */ + for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { + bb->native_offset = cfg->code_len; + //if ((bb == cfg->bb_entry) || !(bb->region == -1 && !bb->dfn)) + mono_arch_output_basic_block (cfg, bb); + + if (bb == cfg->bb_exit) { + cfg->epilog_begin = cfg->code_len; + + if (cfg->prof_options & MONO_PROFILE_ENTER_LEAVE) { + code = cfg->native_code + cfg->code_len; + code = mono_arch_instrument_epilog (cfg, mono_profiler_method_leave, code, FALSE); + cfg->code_len = code - cfg->native_code; + g_assert (cfg->code_len < cfg->code_size); + } + + mono_arch_emit_epilog (cfg); + } + } + + mono_arch_emit_exceptions (cfg); + + max_epilog_size = 0; + + code = cfg->native_code + cfg->code_len; + + /* we always allocate code in cfg->domain->code_mp to increase locality */ + cfg->code_size = cfg->code_len + max_epilog_size; + /* fixme: align to MONO_ARCH_CODE_ALIGNMENT */ + + if (cfg->method->dynamic) { + guint unwindlen = 0; +#ifdef MONO_ARCH_HAVE_UNWIND_TABLE + unwindlen = mono_arch_unwindinfo_get_size (cfg->arch.unwindinfo); +#endif + /* Allocate the code into a separate memory pool so it can be freed */ + cfg->dynamic_info = g_new0 (MonoJitDynamicMethodInfo, 1); + cfg->dynamic_info->code_mp = mono_code_manager_new_dynamic (); + mono_domain_lock (cfg->domain); + mono_dynamic_code_hash_insert (cfg->domain, cfg->method, cfg->dynamic_info); + mono_domain_unlock (cfg->domain); + + code = mono_code_manager_reserve (cfg->dynamic_info->code_mp, cfg->code_size + unwindlen); + } else { + guint unwindlen = 0; +#ifdef MONO_ARCH_HAVE_UNWIND_TABLE + unwindlen = mono_arch_unwindinfo_get_size (cfg->arch.unwindinfo); +#endif + code = mono_domain_code_reserve (cfg->domain, cfg->code_size + unwindlen); + } + + memcpy (code, cfg->native_code, cfg->code_len); + g_free (cfg->native_code); + cfg->native_code = code; + code = cfg->native_code + cfg->code_len; + + /* g_assert (((int)cfg->native_code & (MONO_ARCH_CODE_ALIGNMENT - 1)) == 0); */ + mono_postprocess_patches (cfg); + +#ifdef VALGRIND_JIT_REGISTER_MAP +if (valgrind_register){ + char* nm = mono_method_full_name (cfg->method, TRUE); + VALGRIND_JIT_REGISTER_MAP (nm, cfg->native_code, cfg->native_code + cfg->code_len); + g_free (nm); + } #endif if (cfg->verbose_level > 0) { @@ -2964,10 +3308,12 @@ if (valgrind_register){ } else { mono_domain_code_commit (cfg->domain, cfg->native_code, cfg->code_size, cfg->code_len); } + mono_profiler_code_buffer_new (cfg->native_code, cfg->code_len, MONO_PROFILER_CODE_BUFFER_METHOD, cfg->method); mono_arch_flush_icache (cfg->native_code, cfg->code_len); mono_debug_close_method (cfg); + #ifdef MONO_ARCH_HAVE_UNWIND_TABLE mono_arch_unwindinfo_install_unwind_info (&cfg->arch.unwindinfo, cfg->native_code, cfg->code_len); #endif @@ -2985,6 +3331,150 @@ compute_reachable (MonoBasicBlock *bb) } } +static MonoJitInfo* +create_jit_info (MonoCompile *cfg, MonoMethod *method_to_compile) +{ + MonoMethodHeader *header; + MonoJitInfo *jinfo; + int num_clauses; + int generic_info_size; + + header = mono_method_get_header (method_to_compile); + + if (cfg->generic_sharing_context) + generic_info_size = sizeof (MonoGenericJitInfo); + else + generic_info_size = 0; + + if (COMPILE_LLVM (cfg)) + num_clauses = cfg->llvm_ex_info_len; + else + num_clauses = header->num_clauses; + + if (cfg->method->dynamic) { + jinfo = g_malloc0 (MONO_SIZEOF_JIT_INFO + (num_clauses * sizeof (MonoJitExceptionInfo)) + + generic_info_size); + } else { + jinfo = mono_domain_alloc0 (cfg->domain, MONO_SIZEOF_JIT_INFO + + (num_clauses * sizeof (MonoJitExceptionInfo)) + + generic_info_size); + } + + jinfo->method = cfg->method_to_register; + jinfo->code_start = cfg->native_code; + jinfo->code_size = cfg->code_len; + jinfo->used_regs = cfg->used_int_regs; + jinfo->domain_neutral = (cfg->opt & MONO_OPT_SHARED) != 0; + jinfo->cas_inited = FALSE; /* initialization delayed at the first stalk walk using this method */ + jinfo->num_clauses = num_clauses; + if (COMPILE_LLVM (cfg)) + jinfo->from_llvm = TRUE; + + if (cfg->generic_sharing_context) { + MonoInst *inst; + MonoGenericJitInfo *gi; + + jinfo->has_generic_jit_info = 1; + + gi = mono_jit_info_get_generic_jit_info (jinfo); + g_assert (gi); + + gi->generic_sharing_context = cfg->generic_sharing_context; + + if ((method_to_compile->flags & METHOD_ATTRIBUTE_STATIC) || + mini_method_get_context (method_to_compile)->method_inst || + method_to_compile->klass->valuetype) { + g_assert (cfg->rgctx_var); + } + + gi->has_this = 1; + + if ((method_to_compile->flags & METHOD_ATTRIBUTE_STATIC) || + mini_method_get_context (method_to_compile)->method_inst || + method_to_compile->klass->valuetype) { + inst = cfg->rgctx_var; + g_assert (inst->opcode == OP_REGOFFSET); + } else { + inst = cfg->args [0]; + } + + if (inst->opcode == OP_REGVAR) { + gi->this_in_reg = 1; + gi->this_reg = inst->dreg; + } else { + g_assert (inst->opcode == OP_REGOFFSET); +#ifdef TARGET_X86 + g_assert (inst->inst_basereg == X86_EBP); +#elif defined(TARGET_AMD64) + g_assert (inst->inst_basereg == X86_EBP || inst->inst_basereg == X86_ESP); +#endif + g_assert (inst->inst_offset >= G_MININT32 && inst->inst_offset <= G_MAXINT32); + + gi->this_in_reg = 0; + gi->this_reg = inst->inst_basereg; + gi->this_offset = inst->inst_offset; + } + } + + if (COMPILE_LLVM (cfg)) { + if (num_clauses) + memcpy (&jinfo->clauses [0], &cfg->llvm_ex_info [0], num_clauses * sizeof (MonoJitExceptionInfo)); + } else if (header->num_clauses) { + int i; + + for (i = 0; i < header->num_clauses; i++) { + MonoExceptionClause *ec = &header->clauses [i]; + MonoJitExceptionInfo *ei = &jinfo->clauses [i]; + MonoBasicBlock *tblock; + MonoInst *exvar; + + ei->flags = ec->flags; + + exvar = mono_find_exvar_for_offset (cfg, ec->handler_offset); + ei->exvar_offset = exvar ? exvar->inst_offset : 0; + + if (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER) { + tblock = cfg->cil_offset_to_bb [ec->data.filter_offset]; + g_assert (tblock); + ei->data.filter = cfg->native_code + tblock->native_offset; + } else { + ei->data.catch_class = ec->data.catch_class; + } + + tblock = cfg->cil_offset_to_bb [ec->try_offset]; + g_assert (tblock); + ei->try_start = cfg->native_code + tblock->native_offset; + g_assert (tblock->native_offset); + tblock = cfg->cil_offset_to_bb [ec->try_offset + ec->try_len]; + g_assert (tblock); + ei->try_end = cfg->native_code + tblock->native_offset; + g_assert (tblock->native_offset); + tblock = cfg->cil_offset_to_bb [ec->handler_offset]; + g_assert (tblock); + ei->handler_start = cfg->native_code + tblock->native_offset; + } + } + + /* + * Its possible to generate dwarf unwind info for xdebug etc, but not actually + * using it during runtime, hence the define. + */ +#ifdef MONO_ARCH_HAVE_XP_UNWIND + if (cfg->encoded_unwind_ops) { + jinfo->used_regs = mono_cache_unwind_info (cfg->encoded_unwind_ops, cfg->encoded_unwind_ops_len); + g_free (cfg->encoded_unwind_ops); + } else if (cfg->unwind_ops) { + guint32 info_len; + guint8 *unwind_info = mono_unwind_ops_encode (cfg->unwind_ops, &info_len); + + jinfo->used_regs = mono_cache_unwind_info (unwind_info, info_len); + g_free (unwind_info); + } +#endif + + return jinfo; +} + /* * mini_method_compile: * @method: the method to compile @@ -3003,12 +3493,10 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool MonoMethodHeader *header; guint8 *ip; MonoCompile *cfg; - MonoJitInfo *jinfo; int dfn, i, code_size_ratio; gboolean deadce_has_run = FALSE; - gboolean try_generic_shared; + gboolean try_generic_shared, try_llvm; MonoMethod *method_to_compile, *method_to_register; - int generic_info_size; mono_jit_stats.methods_compiled++; if (mono_profiler_get_events () & MONO_PROFILE_JIT_COMPILATION) @@ -3031,6 +3519,12 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool mono_stats.generics_unsharable_methods++; } + try_llvm = TRUE; + +#ifndef ENABLE_LLVM + try_llvm = FALSE; +#endif + restart_compile: if (try_generic_shared) { MonoMethod *declaring_method; @@ -3068,15 +3562,103 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool cfg->compile_aot = compile_aot; cfg->skip_visibility = method->skip_visibility; cfg->orig_method = method; + cfg->gen_seq_points = debug_options.gen_seq_points; + cfg->explicit_null_checks = debug_options.explicit_null_checks; if (try_generic_shared) cfg->generic_sharing_context = (MonoGenericSharingContext*)&cfg->generic_sharing_context; + cfg->compile_llvm = try_llvm; cfg->token_info_hash = g_hash_table_new (NULL, NULL); + if (cfg->gen_seq_points) + cfg->seq_points = g_ptr_array_new (); + if (cfg->compile_aot && !try_generic_shared && (method->is_generic || method->klass->generic_container)) { cfg->exception_type = MONO_EXCEPTION_GENERIC_SHARING_FAILED; return cfg; } + if (cfg->generic_sharing_context) { + MonoGenericContext object_context = mono_method_construct_object_context (method_to_compile); + + method_to_register = mono_class_inflate_generic_method (method_to_compile, &object_context); + } else { + g_assert (method == method_to_compile); + method_to_register = method; + } + cfg->method_to_register = method_to_register; + + if (cfg->compile_llvm) { + /* No way to obtain the location info for 'this' */ + if (try_generic_shared) { + cfg->exception_message = g_strdup ("gshared"); + cfg->disable_llvm = TRUE; + } + + if (cfg->method->save_lmf) { + cfg->exception_message = g_strdup ("lmf"); + cfg->disable_llvm = TRUE; + } + + /* FIXME: */ + if (cfg->method->dynamic) { + cfg->exception_message = g_strdup ("dynamic."); + cfg->disable_llvm = TRUE; + } + } + + header = mono_method_get_header (method_to_compile); + if (!header) { + MonoLoaderError *error; + + if ((error = mono_loader_get_last_error ())) { + cfg->exception_type = error->exception_type; + } else { + cfg->exception_type = MONO_EXCEPTION_INVALID_PROGRAM; + cfg->exception_message = g_strdup_printf ("Missing or incorrect header for method %s", cfg->method->name); + } + if (MONO_PROBE_METHOD_COMPILE_END_ENABLED ()) + MONO_PROBE_METHOD_COMPILE_END (method, FALSE); + return cfg; + } + + if (FALSE && header->clauses) { + /* + * Cannot be enabled until LLVM supports implicit exceptions, or we use + * explicit checks, or we disable this for methods which might throw implicit + * exceptions inside clauses. + */ + cfg->exception_message = g_strdup ("clauses"); + cfg->disable_llvm = TRUE; + } + +#ifdef ENABLE_LLVM + { + static gboolean inited; + + if (!inited) { + mono_counters_register ("Methods JITted using LLVM", MONO_COUNTER_JIT | MONO_COUNTER_INT, &methods_with_llvm); + mono_counters_register ("Methods JITted using mono JIT", MONO_COUNTER_JIT | MONO_COUNTER_INT, &methods_without_llvm); + inited = TRUE; + } + + /* + * Check for methods which cannot be compiled by LLVM early, to avoid + * the extra compilation pass. + */ + if (COMPILE_LLVM (cfg) && cfg->disable_llvm) { + if (cfg->verbose_level >= 1) { + //nm = mono_method_full_name (cfg->method, TRUE); + printf ("LLVM failed for '%s': %s\n", method->name, cfg->exception_message); + //g_free (nm); + } + InterlockedIncrement (&methods_without_llvm); + mono_destroy_compile (cfg); + try_llvm = FALSE; + goto restart_compile; + } + } +#endif + /* The debugger has no liveness information, so avoid sharing registers/stack slots */ if (mono_debug_using_mono_debugger () || debug_options.mdb_optimizations) { cfg->disable_reuse_registers = TRUE; @@ -3087,6 +3669,8 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool */ cfg->disable_initlocals_opt = TRUE; + cfg->extend_live_ranges = TRUE; + /* Temporarily disable this when running in the debugger until we have support * for this in the debugger. */ cfg->disable_omit_fp = TRUE; @@ -3104,30 +3688,54 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool cfg->opt &= ~MONO_OPT_COPYPROP; cfg->opt &= ~MONO_OPT_CONSPROP; cfg->opt &= ~MONO_OPT_GSHARED; + + /* This is needed for the soft debugger, which doesn't like code after the epilog */ + cfg->disable_out_of_line_bblocks = TRUE; } - header = mono_method_get_header (method_to_compile); - if (!header) { - cfg->exception_type = MONO_EXCEPTION_INVALID_PROGRAM; - cfg->exception_message = g_strdup_printf ("Missing or incorrect header for method %s", cfg->method->name); - if (MONO_PROBE_METHOD_COMPILE_END_ENABLED ()) - MONO_PROBE_METHOD_COMPILE_END (method, FALSE); - if (cfg->prof_options & MONO_PROFILE_JIT_COMPILATION) - mono_profiler_method_end_jit (method, NULL, MONO_PROFILE_FAILED); - return cfg; + if (mono_using_xdebug) { + /* + * Make each variable use its own register/stack slot and extend + * their liveness to cover the whole method, making them displayable + * in gdb even after they are dead. + */ + cfg->disable_reuse_registers = TRUE; + cfg->disable_reuse_stack_slots = TRUE; + cfg->extend_live_ranges = TRUE; + cfg->compute_precise_live_ranges = TRUE; + } + + mini_gc_init_gc_map (cfg); + + if (COMPILE_LLVM (cfg)) { + cfg->opt |= MONO_OPT_ABCREM; } if (getenv ("MONO_VERBOSE_METHOD")) { - if (strcmp (cfg->method->name, getenv ("MONO_VERBOSE_METHOD")) == 0) - cfg->verbose_level = 4; + char *name = getenv ("MONO_VERBOSE_METHOD"); + + if (strchr (name, '.') || strchr (name, ':')) { + MonoMethodDesc *desc; + + desc = mono_method_desc_new (name, TRUE); + if (mono_method_desc_full_match (desc, cfg->method)) { + cfg->verbose_level = 4; + } + mono_method_desc_free (desc); + } else { + if (strcmp (cfg->method->name, getenv ("MONO_VERBOSE_METHOD")) == 0) + cfg->verbose_level = 4; + } } ip = (guint8 *)header->code; cfg->intvars = mono_mempool_alloc0 (cfg->mempool, sizeof (guint16) * STACK_MAX * header->max_stack); - if (cfg->verbose_level > 2) { - if (cfg->generic_sharing_context) + if (cfg->verbose_level > 0) { + if (COMPILE_LLVM (cfg)) + g_print ("converting llvm method %s\n", mono_method_full_name (method, TRUE)); + else if (cfg->generic_sharing_context) g_print ("converting shared method %s\n", mono_method_full_name (method, TRUE)); else g_print ("converting method %s\n", mono_method_full_name (method, TRUE)); @@ -3198,6 +3806,10 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool */ //cfg->enable_extended_bblocks = TRUE; + /*We must verify the method before doing any IR generation as mono_compile_create_vars can assert.*/ + if (mono_compile_is_broken (cfg)) + return cfg; + /* * create MonoInst* which represents arguments and local variables */ @@ -3223,8 +3835,6 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool if (MONO_PROBE_METHOD_COMPILE_END_ENABLED ()) MONO_PROBE_METHOD_COMPILE_END (method, FALSE); - if (cfg->prof_options & MONO_PROFILE_JIT_COMPILATION) - mono_profiler_method_end_jit (method, NULL, MONO_PROFILE_FAILED); /* cfg contains the details of the failure, so let the caller cleanup */ return cfg; } @@ -3232,9 +3842,50 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool mono_jit_stats.basic_blocks += cfg->num_bblocks; mono_jit_stats.max_basic_blocks = MAX (cfg->num_bblocks, mono_jit_stats.max_basic_blocks); + if (COMPILE_LLVM (cfg)) { + MonoInst *ins; + + /* The IR has to be in SSA form for LLVM */ + cfg->opt |= MONO_OPT_SSA; + + // FIXME: + if (cfg->ret) { + // Allow SSA on the result value + cfg->ret->flags &= ~MONO_INST_VOLATILE; + + // Add an explicit return instruction referencing the return value + MONO_INST_NEW (cfg, ins, OP_SETRET); + ins->sreg1 = cfg->ret->dreg; + + MONO_ADD_INS (cfg->bb_exit, ins); + } + + cfg->opt &= ~MONO_OPT_LINEARS; + + /* FIXME: */ + cfg->opt &= ~MONO_OPT_BRANCH; + } + + /* todo: remove code when we have verified that the liveness for try/catch blocks + * works perfectly + */ + /* + * Currently, this can't be commented out since exception blocks are not + * processed during liveness analysis. + * It is also needed, because otherwise the local optimization passes would + * delete assignments in cases like this: + * r1 <- 1 + * + * r1 <- 2 + * This also allows SSA to be run on methods containing exception clauses, since + * SSA will ignore variables marked VOLATILE. + */ + mono_liveness_handle_exception_clauses (cfg); + /*g_print ("numblocks = %d\n", cfg->num_bblocks);*/ - mono_decompose_long_opts (cfg); + if (!COMPILE_LLVM (cfg)) + mono_decompose_long_opts (cfg); /* Should be done before branch opts */ if (cfg->opt & (MONO_OPT_CONSPROP | MONO_OPT_COPYPROP)) @@ -3247,7 +3898,9 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool mono_handle_global_vregs (cfg); if (cfg->opt & MONO_OPT_DEADCE) mono_local_deadce (cfg); - mono_if_conversion (cfg); + /* Disable this for LLVM to make the IR easier to handle */ + if (!COMPILE_LLVM (cfg)) + mono_if_conversion (cfg); if ((cfg->opt & MONO_OPT_SSAPRE) || cfg->globalra) mono_remove_critical_edges (cfg); @@ -3255,6 +3908,8 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool /* Depth-first ordering on basic blocks */ cfg->bblocks = mono_mempool_alloc (cfg->mempool, sizeof (MonoBasicBlock*) * (cfg->num_bblocks + 1)); + cfg->max_block_num = cfg->num_bblocks; + dfn = 0; df_visit (cfg->bb_entry, &dfn, cfg->bblocks); if (cfg->num_bblocks != dfn + 1) { @@ -3275,6 +3930,8 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool if (cfg->verbose_level > 1) g_print ("found unreachable code in BB%d\n", bb->block_num); bb->code = bb->last_ins = NULL; + while (bb->out_count) + mono_unlink_bblock (cfg, bb, bb->out_bb [0]); } } for (bb = cfg->bb_entry; bb; bb = bb->next_bb) @@ -3303,11 +3960,16 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool return cfg; } + /* + if (header->num_clauses) + cfg->disable_ssa = TRUE; + */ + //#define DEBUGSSA "logic_run" #define DEBUGSSA_CLASS "Tests" #ifdef DEBUGSSA - if (!header->num_clauses && !cfg->disable_ssa) { + if (!cfg->disable_ssa) { mono_local_cprop (cfg); #ifndef DISABLE_SSA @@ -3316,7 +3978,7 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool } #else if (cfg->opt & MONO_OPT_SSA) { - if (!(cfg->comp_done & MONO_COMP_SSA) && !header->num_clauses && !cfg->disable_ssa) { + if (!(cfg->comp_done & MONO_COMP_SSA) && !cfg->disable_ssa) { #ifndef DISABLE_SSA mono_ssa_compute (cfg); #endif @@ -3336,7 +3998,7 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool } if ((cfg->opt & MONO_OPT_CONSPROP) || (cfg->opt & MONO_OPT_COPYPROP)) { - if (cfg->comp_done & MONO_COMP_SSA) { + if (cfg->comp_done & MONO_COMP_SSA && !COMPILE_LLVM (cfg)) { #ifndef DISABLE_SSA mono_ssa_cprop (cfg); #endif @@ -3344,7 +4006,7 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool } #ifndef DISABLE_SSA - if (cfg->comp_done & MONO_COMP_SSA) { + if (cfg->comp_done & MONO_COMP_SSA && !COMPILE_LLVM (cfg)) { //mono_ssa_strength_reduction (cfg); if (cfg->opt & MONO_OPT_SSAPRE) { @@ -3389,6 +4051,11 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool } #endif + if (cfg->comp_done & MONO_COMP_SSA && COMPILE_LLVM (cfg)) { + if ((cfg->flags & (MONO_CFG_HAS_LDELEMA|MONO_CFG_HAS_CHECK_THIS)) && (cfg->opt & MONO_OPT_ABCREM)) + mono_perform_abc_removal (cfg); + } + /* after SSA removal */ if (parts == 3) { if (MONO_PROBE_METHOD_COMPILE_END_ENABLED ()) @@ -3397,9 +4064,11 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool } #ifdef MONO_ARCH_SOFT_FLOAT - mono_decompose_soft_float (cfg); + if (!COMPILE_LLVM (cfg)) + mono_decompose_soft_float (cfg); #endif - mono_decompose_vtype_opts (cfg); + if (!COMPILE_LLVM (cfg)) + mono_decompose_vtype_opts (cfg); if (cfg->flags & MONO_CFG_HAS_ARRAY_ACCESS) mono_decompose_array_access_opts (cfg); @@ -3424,12 +4093,8 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool g_list_free (regs); } - /* todo: remove code when we have verified that the liveness for try/catch blocks - * works perfectly - */ - /* - * Currently, this can't be commented out since exception blocks are not - * processed during liveness analysis. + /* + * Have to call this again to process variables added since the first call. */ mono_liveness_handle_exception_clauses (cfg); @@ -3459,19 +4124,22 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool } } - //mono_print_code (cfg); + //mono_print_code (cfg, ""); //print_dfn (cfg); /* variables are allocated after decompose, since decompose could create temps */ - if (!cfg->globalra) + if (!cfg->globalra && !COMPILE_LLVM (cfg)) { mono_arch_allocate_vars (cfg); + if (cfg->exception_type) + return cfg; + } { MonoBasicBlock *bb; gboolean need_local_opts; - if (!cfg->globalra) { + if (!cfg->globalra && !COMPILE_LLVM (cfg)) { mono_spill_global_vars (cfg, &need_local_opts); if (need_local_opts || cfg->compile_aot) { @@ -3522,153 +4190,75 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool } } - mono_codegen (cfg); - if (cfg->verbose_level >= 2) { - char *id = mono_method_full_name (cfg->method, FALSE); - mono_disassemble_code (cfg, cfg->native_code, cfg->code_len, id + 3); - g_free (id); - } - - if (cfg->generic_sharing_context) - generic_info_size = sizeof (MonoGenericJitInfo); - else - generic_info_size = 0; + if (COMPILE_LLVM (cfg)) { +#ifdef ENABLE_LLVM + char *nm; - if (cfg->method->dynamic) { - jinfo = g_malloc0 (sizeof (MonoJitInfo) + (header->num_clauses * sizeof (MonoJitExceptionInfo)) + - generic_info_size); - } else { - jinfo = mono_domain_alloc0 (cfg->domain, sizeof (MonoJitInfo) + - (header->num_clauses * sizeof (MonoJitExceptionInfo)) + - generic_info_size); - } - - if (cfg->generic_sharing_context) { - MonoGenericContext object_context = mono_method_construct_object_context (method_to_compile); - - method_to_register = mono_class_inflate_generic_method (method_to_compile, &object_context); - } else { - g_assert (method == method_to_compile); - method_to_register = method; - } - - jinfo->method = method_to_register; - jinfo->code_start = cfg->native_code; - jinfo->code_size = cfg->code_len; - jinfo->used_regs = cfg->used_int_regs; - jinfo->domain_neutral = (cfg->opt & MONO_OPT_SHARED) != 0; - jinfo->cas_inited = FALSE; /* initialization delayed at the first stalk walk using this method */ - jinfo->num_clauses = header->num_clauses; - - if (cfg->generic_sharing_context) { - MonoInst *inst; - MonoGenericJitInfo *gi; - - jinfo->has_generic_jit_info = 1; - - gi = mono_jit_info_get_generic_jit_info (jinfo); - g_assert (gi); - - gi->generic_sharing_context = cfg->generic_sharing_context; - - if ((method_to_compile->flags & METHOD_ATTRIBUTE_STATIC) || - mini_method_get_context (method_to_compile)->method_inst || - method_to_compile->klass->valuetype) { - g_assert (cfg->rgctx_var); + /* The IR has to be in SSA form for LLVM */ + if (!(cfg->comp_done & MONO_COMP_SSA)) { + cfg->exception_message = g_strdup ("SSA disabled."); + cfg->disable_llvm = TRUE; } - gi->has_this = 1; + if (cfg->flags & MONO_CFG_HAS_ARRAY_ACCESS) + mono_decompose_array_access_opts (cfg); - if ((method_to_compile->flags & METHOD_ATTRIBUTE_STATIC) || - mini_method_get_context (method_to_compile)->method_inst || - method_to_compile->klass->valuetype) { - inst = cfg->rgctx_var; - g_assert (inst->opcode == OP_REGOFFSET); - } else { - inst = cfg->args [0]; + if (!cfg->disable_llvm) + mono_llvm_emit_method (cfg); + if (cfg->disable_llvm) { + if (cfg->verbose_level >= 1) { + //nm = mono_method_full_name (cfg->method, TRUE); + printf ("LLVM failed for '%s': %s\n", method->name, cfg->exception_message); + //g_free (nm); + } + InterlockedIncrement (&methods_without_llvm); + mono_destroy_compile (cfg); + try_llvm = FALSE; + goto restart_compile; } - if (inst->opcode == OP_REGVAR) { - gi->this_in_reg = 1; - gi->this_reg = inst->dreg; - } else { - g_assert (inst->opcode == OP_REGOFFSET); -#ifdef __i386__ - g_assert (inst->inst_basereg == X86_EBP); -#elif defined(__x86_64__) - g_assert (inst->inst_basereg == X86_EBP || inst->inst_basereg == X86_ESP); -#endif - g_assert (inst->inst_offset >= G_MININT32 && inst->inst_offset <= G_MAXINT32); + InterlockedIncrement (&methods_with_llvm); - gi->this_in_reg = 0; - gi->this_reg = inst->inst_basereg; - gi->this_offset = inst->inst_offset; + if (cfg->verbose_level > 0 && !cfg->compile_aot) { + nm = mono_method_full_name (cfg->method, TRUE); + g_print ("LLVM Method %s emitted at %p to %p (code length %d) [%s]\n", + nm, + cfg->native_code, cfg->native_code + cfg->code_len, cfg->code_len, cfg->domain->friendly_name); + g_free (nm); } +#endif + } else { + mono_codegen (cfg); } - if (header->num_clauses) { - int i; - - for (i = 0; i < header->num_clauses; i++) { - MonoExceptionClause *ec = &header->clauses [i]; - MonoJitExceptionInfo *ei = &jinfo->clauses [i]; - MonoBasicBlock *tblock; - MonoInst *exvar; - - ei->flags = ec->flags; - - exvar = mono_find_exvar_for_offset (cfg, ec->handler_offset); - ei->exvar_offset = exvar ? exvar->inst_offset : 0; - - if (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER) { - tblock = cfg->cil_offset_to_bb [ec->data.filter_offset]; - g_assert (tblock); - ei->data.filter = cfg->native_code + tblock->native_offset; - } else { - ei->data.catch_class = ec->data.catch_class; - } - - tblock = cfg->cil_offset_to_bb [ec->try_offset]; - g_assert (tblock); - ei->try_start = cfg->native_code + tblock->native_offset; - g_assert (tblock->native_offset); - tblock = cfg->cil_offset_to_bb [ec->try_offset + ec->try_len]; - g_assert (tblock); - ei->try_end = cfg->native_code + tblock->native_offset; - g_assert (tblock->native_offset); - tblock = cfg->cil_offset_to_bb [ec->handler_offset]; - g_assert (tblock); - ei->handler_start = cfg->native_code + tblock->native_offset; - } + if (cfg->verbose_level >= 2) { + char *id = mono_method_full_name (cfg->method, FALSE); + mono_disassemble_code (cfg, cfg->native_code, cfg->code_len, id + 3); + g_free (id); } - /* - * Its possible to generate dwarf unwind info for xdebug etc, but not actually - * using it during runtime, hence the define. - */ -#ifdef MONO_ARCH_HAVE_XP_UNWIND - if (cfg->unwind_ops) { - guint32 info_len; - guint8 *unwind_info = mono_unwind_ops_encode (cfg->unwind_ops, &info_len); + cfg->jit_info = create_jit_info (cfg, method_to_compile); - jinfo->used_regs = mono_cache_unwind_info (unwind_info, info_len); - g_free (unwind_info); +#ifdef MONO_ARCH_HAVE_LIVERANGE_OPS + if (cfg->extend_live_ranges) { + /* Extend live ranges to cover the whole method */ + for (i = 0; i < cfg->num_varinfo; ++i) + MONO_VARINFO (cfg, i)->live_range_end = cfg->code_len; } #endif - cfg->jit_info = jinfo; -#if defined(__arm__) - mono_arch_fixup_jinfo (cfg); -#endif - mono_save_xdebug_info (cfg); + mini_gc_create_gc_map (cfg); + + mono_save_seq_point_info (cfg); + if (!cfg->compile_aot) { mono_domain_lock (cfg->domain); - mono_jit_info_table_add (cfg->domain, jinfo); + mono_jit_info_table_add (cfg->domain, cfg->jit_info); if (cfg->method->dynamic) - mono_dynamic_code_hash_lookup (cfg->domain, cfg->method)->ji = jinfo; + mono_dynamic_code_hash_lookup (cfg->domain, cfg->method)->ji = cfg->jit_info; mono_domain_unlock (cfg->domain); } @@ -3692,8 +4282,6 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool if (MONO_PROBE_METHOD_COMPILE_END_ENABLED ()) MONO_PROBE_METHOD_COMPILE_END (method, TRUE); - if (cfg->prof_options & MONO_PROFILE_JIT_COMPILATION) - mono_profiler_method_end_jit (method, jinfo, MONO_PROFILE_OK); return cfg; } @@ -3751,15 +4339,17 @@ lookup_method (MonoDomain *domain, MonoMethod *method) } static gpointer -mono_jit_compile_method_inner (MonoMethod *method, MonoDomain *target_domain, int opt) +mono_jit_compile_method_inner (MonoMethod *method, MonoDomain *target_domain, int opt, MonoException **jit_ex) { MonoCompile *cfg; gpointer code = NULL; - MonoJitInfo *info; + MonoJitInfo *jinfo, *info; MonoVTable *vtable; + MonoException *ex = NULL; + guint32 prof_options; #ifdef MONO_USE_AOT_COMPILER - if ((opt & MONO_OPT_AOT) && !(mono_profiler_get_events () & MONO_PROFILE_JIT_COMPILATION)) { + if (opt & MONO_OPT_AOT) { MonoDomain *domain = mono_domain_get (); mono_class_init (method->klass); @@ -3768,6 +4358,7 @@ mono_jit_compile_method_inner (MonoMethod *method, MonoDomain *target_domain, in vtable = mono_class_vtable (domain, method->klass); g_assert (vtable); mono_runtime_class_init (vtable); + return code; } } @@ -3782,7 +4373,7 @@ mono_jit_compile_method_inner (MonoMethod *method, MonoDomain *target_domain, in if (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) piinfo->addr = mono_lookup_internal_call (method); else if (method->iflags & METHOD_IMPL_ATTRIBUTE_NATIVE) -#ifdef PLATFORM_WIN32 +#ifdef HOST_WIN32 g_warning ("Method '%s' in assembly '%s' contains native code that cannot be executed by Mono in modules loaded from byte arrays. The assembly was probably created using C++/CLI.\n", mono_method_full_name (method, TRUE), method->klass->image->name); #else g_warning ("Method '%s' in assembly '%s' contains native code that cannot be executed by Mono on this platform. The assembly was probably created using C++/CLI.\n", mono_method_full_name (method, TRUE), method->klass->image->name); @@ -3832,8 +4423,16 @@ mono_jit_compile_method_inner (MonoMethod *method, MonoDomain *target_domain, in return NULL; } - if (mono_aot_only) - g_error ("Attempting to JIT compile method '%s' while running with --aot-only.\n", mono_method_full_name (method, TRUE)); + if (mono_aot_only) { + char *fullname = mono_method_full_name (method, TRUE); + char *msg = g_strdup_printf ("Attempting to JIT compile method '%s' while running with --aot-only.\n", fullname); + + *jit_ex = mono_get_exception_execution_engine (msg); + g_free (fullname); + g_free (msg); + + return NULL; + } cfg = mini_method_compile (method, opt, target_domain, TRUE, FALSE, 0); @@ -3847,7 +4446,6 @@ mono_jit_compile_method_inner (MonoMethod *method, MonoDomain *target_domain, in case MONO_EXCEPTION_BAD_IMAGE: { /* Throw a type load exception if needed */ MonoLoaderError *error = mono_loader_get_last_error (); - MonoException *ex; if (error) { ex = mono_loader_error_prepare_exception (error); @@ -3869,34 +4467,20 @@ mono_jit_compile_method_inner (MonoMethod *method, MonoDomain *target_domain, in g_assert_not_reached (); } } - mono_destroy_compile (cfg); - mono_raise_exception (ex); break; } - case MONO_EXCEPTION_INVALID_PROGRAM: { - MonoException *ex = mono_exception_from_name_msg (mono_defaults.corlib, "System", "InvalidProgramException", cfg->exception_message); - mono_destroy_compile (cfg); - mono_raise_exception (ex); + case MONO_EXCEPTION_INVALID_PROGRAM: + ex = mono_exception_from_name_msg (mono_defaults.corlib, "System", "InvalidProgramException", cfg->exception_message); break; - } - case MONO_EXCEPTION_UNVERIFIABLE_IL: { - MonoException *ex = mono_exception_from_name_msg (mono_defaults.corlib, "System.Security", "VerificationException", cfg->exception_message); - mono_destroy_compile (cfg); - mono_raise_exception (ex); + case MONO_EXCEPTION_UNVERIFIABLE_IL: + ex = mono_exception_from_name_msg (mono_defaults.corlib, "System.Security", "VerificationException", cfg->exception_message); break; - } - case MONO_EXCEPTION_METHOD_ACCESS: { - MonoException *ex = mono_exception_from_name_msg (mono_defaults.corlib, "System", "MethodAccessException", cfg->exception_message); - mono_destroy_compile (cfg); - mono_raise_exception (ex); + case MONO_EXCEPTION_METHOD_ACCESS: + ex = mono_exception_from_name_msg (mono_defaults.corlib, "System", "MethodAccessException", cfg->exception_message); break; - } - case MONO_EXCEPTION_FIELD_ACCESS: { - MonoException *ex = mono_exception_from_name_msg (mono_defaults.corlib, "System", "FieldAccessException", cfg->exception_message); - mono_destroy_compile (cfg); - mono_raise_exception (ex); + case MONO_EXCEPTION_FIELD_ACCESS: + ex = mono_exception_from_name_msg (mono_defaults.corlib, "System", "FieldAccessException", cfg->exception_message); break; - } /* this can only be set if the security manager is active */ case MONO_EXCEPTION_SECURITY_LINKDEMAND: { MonoSecurityManager* secman = mono_security_manager_get_methods (); @@ -3907,22 +4491,30 @@ mono_jit_compile_method_inner (MonoMethod *method, MonoDomain *target_domain, in args [1] = &method; mono_runtime_invoke (secman->linkdemandsecurityexception, NULL, args, &exc); - mono_destroy_compile (cfg); - cfg = NULL; - - mono_raise_exception ((MonoException*)exc); + ex = (MonoException*)exc; + break; } case MONO_EXCEPTION_OBJECT_SUPPLIED: { MonoException *exp = cfg->exception_ptr; MONO_GC_UNREGISTER_ROOT (cfg->exception_ptr); - mono_destroy_compile (cfg); - mono_raise_exception (exp); + + ex = exp; break; } default: g_assert_not_reached (); } + if (ex) { + if (cfg->prof_options & MONO_PROFILE_JIT_COMPILATION) + mono_profiler_method_end_jit (method, NULL, MONO_PROFILE_FAILED); + + mono_destroy_compile (cfg); + *jit_ex = ex; + + return NULL; + } + mono_loader_lock (); /*FIXME lookup_method_inner requires the loader lock*/ mono_domain_lock (target_domain); @@ -3951,6 +4543,10 @@ mono_jit_compile_method_inner (MonoMethod *method, MonoDomain *target_domain, in mono_domain_jit_code_hash_unlock (target_domain); } + jinfo = cfg->jit_info; + + prof_options = cfg->prof_options; + mono_destroy_compile (cfg); if (domain_jit_info (target_domain)->jump_target_hash) { @@ -3979,16 +4575,25 @@ mono_jit_compile_method_inner (MonoMethod *method, MonoDomain *target_domain, in g_assert (exc); mono_raise_exception (exc); } + + if (prof_options & MONO_PROFILE_JIT_COMPILATION) { + if (method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE) + /* The profiler doesn't know about wrappers, so pass the original icall method */ + mono_profiler_method_end_jit (mono_marshal_method_from_wrapper (method), jinfo, MONO_PROFILE_OK); + else + mono_profiler_method_end_jit (method, jinfo, MONO_PROFILE_OK); + } + mono_runtime_class_init (vtable); return code; } static gpointer -mono_jit_compile_method_with_opt (MonoMethod *method, guint32 opt) +mono_jit_compile_method_with_opt (MonoMethod *method, guint32 opt, MonoException **ex) { MonoDomain *target_domain, *domain = mono_domain_get (); MonoJitInfo *info; - gpointer p; + gpointer code, p; MonoJitICallInfo *callinfo = NULL; /* @@ -4020,12 +4625,17 @@ mono_jit_compile_method_with_opt (MonoMethod *method, guint32 opt) mono_jit_stats.methods_lookups++; vtable = mono_class_vtable (domain, method->klass); + g_assert (vtable); mono_runtime_class_init (vtable); return mono_create_ftnptr (target_domain, info->code_start); } } - p = mono_create_ftnptr (target_domain, mono_jit_compile_method_inner (method, target_domain, opt)); + code = mono_jit_compile_method_inner (method, target_domain, opt, ex); + if (!code) + return NULL; + + p = mono_create_ftnptr (target_domain, code); if (callinfo) { mono_jit_lock (); @@ -4040,10 +4650,19 @@ mono_jit_compile_method_with_opt (MonoMethod *method, guint32 opt) return p; } -static gpointer +gpointer mono_jit_compile_method (MonoMethod *method) { - return mono_jit_compile_method_with_opt (method, default_opt); + MonoException *ex = NULL; + gpointer code; + + code = mono_jit_compile_method_with_opt (method, default_opt, &ex); + if (!code) { + g_assert (ex); + mono_raise_exception (ex); + } + + return code; } #ifdef MONO_ARCH_HAVE_INVALIDATE_METHOD @@ -4079,6 +4698,7 @@ mono_jit_free_method (MonoDomain *domain, MonoMethod *method) g_hash_table_remove (domain_jit_info (domain)->dynamic_code_hash, method); mono_internal_hash_table_remove (&domain->jit_code_hash, method); g_hash_table_remove (domain_jit_info (domain)->jump_trampoline_hash, method); + g_hash_table_remove (domain_jit_info (domain)->runtime_invoke_hash, method); mono_domain_unlock (domain); #ifdef MONO_ARCH_HAVE_INVALIDATE_METHOD @@ -4109,7 +4729,7 @@ mono_jit_free_method (MonoDomain *domain, MonoMethod *method) } gpointer -mono_jit_find_compiled_method (MonoDomain *domain, MonoMethod *method) +mono_jit_find_compiled_method_with_jit_info (MonoDomain *domain, MonoMethod *method, MonoJitInfo **ji) { MonoDomain *target_domain; MonoJitInfo *info; @@ -4124,13 +4744,32 @@ mono_jit_find_compiled_method (MonoDomain *domain, MonoMethod *method) /* We can't use a domain specific method in another domain */ if (! ((domain != target_domain) && !info->domain_neutral)) { mono_jit_stats.methods_lookups++; + if (ji) + *ji = info; return info->code_start; } } + if (ji) + *ji = NULL; return NULL; } +gpointer +mono_jit_find_compiled_method (MonoDomain *domain, MonoMethod *method) +{ + return mono_jit_find_compiled_method_with_jit_info (domain, method, NULL); +} + +typedef struct { + MonoMethod *method; + gpointer compiled_method; + gpointer runtime_invoke; + MonoVTable *vtable; + MonoDynCallInfo *dyn_call_info; + MonoClass *ret_box_class; +} RuntimeInvokeInfo; + /** * mono_jit_runtime_invoke: * @method: the method to invoke @@ -4141,68 +4780,224 @@ mono_jit_find_compiled_method (MonoDomain *domain, MonoMethod *method) static MonoObject* mono_jit_runtime_invoke (MonoMethod *method, void *obj, void **params, MonoObject **exc) { - MonoMethod *to_compile; MonoMethod *invoke; MonoObject *(*runtime_invoke) (MonoObject *this, void **params, MonoObject **exc, void* compiled_method); - void* compiled_method; - MonoVTable *vtable; - + MonoDomain *domain = mono_domain_get (); + MonoJitDomainInfo *domain_info; + RuntimeInvokeInfo *info, *info2; + if (obj == NULL && !(method->flags & METHOD_ATTRIBUTE_STATIC) && !method->string_ctor && (method->wrapper_type == 0)) { g_warning ("Ignoring invocation of an instance method on a NULL instance.\n"); return NULL; } - if (mono_method_needs_static_rgctx_invoke (method, FALSE)) - to_compile = mono_marshal_get_static_rgctx_invoke (method); - else - to_compile = method; + domain_info = domain_jit_info (domain); - /* Special case parameterless ctors to speed up Activator.CreateInstance () */ - if (method->flags & (METHOD_ATTRIBUTE_SPECIAL_NAME | METHOD_ATTRIBUTE_RT_SPECIAL_NAME) && !strcmp (method->name, ".ctor") && mono_method_signature (method)->param_count == 0 && !method->klass->valuetype) { - MonoJitDomainInfo *domain_info = domain_jit_info (mono_domain_get ()); + mono_domain_lock (domain); + info = g_hash_table_lookup (domain_info->runtime_invoke_hash, method); + mono_domain_unlock (domain); - if (!domain_info->ctor_runtime_invoke) { - invoke = mono_marshal_get_runtime_invoke (method, FALSE); - domain_info->ctor_runtime_invoke = mono_jit_compile_method (invoke); + if (!info) { + if (mono_security_get_mode () == MONO_SECURITY_MODE_CORE_CLR) { + /* + * This might be redundant since mono_class_vtable () already does this, + * but keep it just in case for moonlight. + */ + mono_class_setup_vtable (method->klass); + if (method->klass->exception_type != MONO_EXCEPTION_NONE) { + if (exc) + *exc = (MonoObject*)mono_class_get_exception_for_failure (method->klass); + else + mono_raise_exception (mono_class_get_exception_for_failure (method->klass)); + return NULL; + } } - runtime_invoke = domain_info->ctor_runtime_invoke; - } else { + info = g_new0 (RuntimeInvokeInfo, 1); + invoke = mono_marshal_get_runtime_invoke (method, FALSE); - runtime_invoke = mono_jit_compile_method (invoke); + info->vtable = mono_class_vtable_full (domain, method->klass, TRUE); + g_assert (info->vtable); + + if (method->klass->rank && (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) && + (method->iflags & METHOD_IMPL_ATTRIBUTE_NATIVE)) { + /* + * Array Get/Set/Address methods. The JIT implements them using inline code + * inside the runtime invoke wrappers, so no need to compile them. + */ + info->compiled_method = NULL; + } else { + MonoException *jit_ex = NULL; + + info->compiled_method = mono_jit_compile_method_with_opt (method, default_opt, &jit_ex); + if (!info->compiled_method) { + g_free (info); + g_assert (jit_ex); + if (exc) { + *exc = (MonoObject*)jit_ex; + return NULL; + } else { + mono_raise_exception (jit_ex); + } + } + + if (mono_method_needs_static_rgctx_invoke (method, FALSE)) + info->compiled_method = mono_create_static_rgctx_trampoline (method, info->compiled_method); + } + + /* + * We want to avoid AOTing 1000s of runtime-invoke wrappers when running + * in full-aot mode, so we use a slower, but more generic wrapper if + * possible, built on top of the OP_DYN_CALL opcode provided by the JIT. + */ +#ifdef MONO_ARCH_DYN_CALL_SUPPORTED + if (mono_aot_only || debug_options.dyn_runtime_invoke) { + MonoMethodSignature *sig = mono_method_signature (method); + gboolean supported = TRUE; + int i; + + if (method->string_ctor) + sig = mono_marshal_get_string_ctor_signature (method); + + for (i = 0; i < sig->param_count; ++i) { + MonoType *t = sig->params [i]; + + if (t->type == MONO_TYPE_GENERICINST && mono_class_is_nullable (mono_class_from_mono_type (t))) + supported = FALSE; + } + + if (method->klass->contextbound || !info->compiled_method) + supported = FALSE; + + if (supported) + info->dyn_call_info = mono_arch_dyn_call_prepare (sig); + + if (info->dyn_call_info) { + switch (sig->ret->type) { + case MONO_TYPE_VOID: + break; + 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_I: + case MONO_TYPE_U: + case MONO_TYPE_I8: + case MONO_TYPE_U8: + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_CHAR: + case MONO_TYPE_R4: + case MONO_TYPE_R8: + info->ret_box_class = mono_class_from_mono_type (sig->ret); + break; + case MONO_TYPE_PTR: + info->ret_box_class = mono_defaults.int_class; + break; + case MONO_TYPE_STRING: + case MONO_TYPE_CLASS: + case MONO_TYPE_ARRAY: + case MONO_TYPE_SZARRAY: + case MONO_TYPE_OBJECT: + break; + case MONO_TYPE_GENERICINST: + if (!MONO_TYPE_IS_REFERENCE (sig->ret)) + info->ret_box_class = mono_class_from_mono_type (sig->ret); + break; + case MONO_TYPE_VALUETYPE: + info->ret_box_class = mono_class_from_mono_type (sig->ret); + break; + default: + g_assert_not_reached (); + break; + } + } + } +#endif + + if (!info->dyn_call_info) + info->runtime_invoke = mono_jit_compile_method (invoke); + + mono_domain_lock (domain); + info2 = g_hash_table_lookup (domain_info->runtime_invoke_hash, method); + if (info2) { + g_free (info); + info = info2; + } else { + g_hash_table_insert (domain_info->runtime_invoke_hash, method, info); + } + mono_domain_unlock (domain); } - /* We need this here becuase mono_marshal_get_runtime_invoke can be place - * the helper method in System.Object and not the target class + runtime_invoke = info->runtime_invoke; + + /* + * We need this here because mono_marshal_get_runtime_invoke can place + * the helper method in System.Object and not the target class. */ - vtable = mono_class_vtable (mono_domain_get (), method->klass); - g_assert (vtable); - mono_runtime_class_init (vtable); + mono_runtime_class_init (info->vtable); + +#ifdef MONO_ARCH_DYN_CALL_SUPPORTED + if (info->dyn_call_info) { + MonoMethodSignature *sig = mono_method_signature (method); + gpointer *args; + static RuntimeInvokeDynamicFunction dyn_runtime_invoke; + int i, pindex; + guint8 buf [128]; + guint8 retval [128]; + + if (!dyn_runtime_invoke) { + invoke = mono_marshal_get_runtime_invoke_dynamic (); + dyn_runtime_invoke = mono_jit_compile_method (invoke); + } - if (method->klass->rank && (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) && - (method->iflags & METHOD_IMPL_ATTRIBUTE_NATIVE)) { - /* - * Array Get/Set/Address methods. The JIT implements them using inline code - * inside the runtime invoke wrappers, so no need to compile them. - */ - compiled_method = NULL; - } else { - compiled_method = mono_jit_compile_method (to_compile); + /* Convert the arguments to the format expected by start_dyn_call () */ + args = g_alloca ((sig->param_count + sig->hasthis) * sizeof (gpointer)); + pindex = 0; + if (sig->hasthis) + args [pindex ++] = &obj; + for (i = 0; i < sig->param_count; ++i) { + MonoType *t = sig->params [i]; + + if (t->byref) { + args [pindex ++] = ¶ms [i]; + } else if (MONO_TYPE_IS_REFERENCE (t) || t->type == MONO_TYPE_PTR) { + args [pindex ++] = ¶ms [i]; + } else { + args [pindex ++] = params [i]; + } + } + + //printf ("M: %s\n", mono_method_full_name (method, TRUE)); + + mono_arch_start_dyn_call (info->dyn_call_info, (gpointer**)args, retval, buf, sizeof (buf)); + + dyn_runtime_invoke (buf, exc, info->compiled_method); + + mono_arch_finish_dyn_call (info->dyn_call_info, buf); + + if (info->ret_box_class) + return mono_value_box (domain, info->ret_box_class, retval); + else + return *(MonoObject**)retval; } - return runtime_invoke (obj, params, exc, compiled_method); +#endif + + return runtime_invoke (obj, params, exc, info->compiled_method); } void SIG_HANDLER_SIGNATURE (mono_sigfpe_signal_handler) { MonoException *exc = NULL; -#ifndef MONO_ARCH_USE_SIGACTION + MonoJitInfo *ji; +#if !(defined(MONO_ARCH_USE_SIGACTION) || defined(HOST_WIN32)) void *info = NULL; #endif GET_CONTEXT; - if (mono_chain_signal (SIG_HANDLER_PARAMS)) - return; + ji = mono_jit_info_table_find (mono_domain_get (), mono_arch_ip_from_context (ctx)); #if defined(MONO_ARCH_HAVE_IS_INT_OVERFLOW) if (mono_arch_is_int_overflow (ctx, info)) @@ -4212,6 +5007,13 @@ SIG_HANDLER_SIGNATURE (mono_sigfpe_signal_handler) #else exc = mono_get_exception_divide_by_zero (); #endif + + if (!ji) { + if (mono_chain_signal (SIG_HANDLER_PARAMS)) + return; + + mono_handle_native_sigsegv (SIGSEGV, ctx); + } mono_arch_handle_exception (ctx, exc, FALSE); } @@ -4238,6 +5040,23 @@ SIG_HANDLER_SIGNATURE (mono_sigsegv_signal_handler) GET_CONTEXT; +#ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED + if (mono_arch_is_single_step_event (info, ctx)) { + mono_debugger_agent_single_step_event (ctx); + return; + } else if (mono_arch_is_breakpoint_event (info, ctx)) { + mono_debugger_agent_breakpoint_hit (ctx); + return; + } +#endif + +#ifndef HOST_WIN32 + if (mono_aot_is_pagefault (info->si_addr)) { + mono_aot_handle_pagefault (info->si_addr); + return; + } +#endif + /* The thread might no be registered with the runtime */ if (!mono_domain_get () || !jit_tls) { if (mono_chain_signal (SIG_HANDLER_PARAMS)) @@ -4311,7 +5130,7 @@ mono_jit_create_remoting_trampoline (MonoDomain *domain, MonoMethod *method, Mon guint8 *addr = NULL; if ((method->flags & METHOD_ATTRIBUTE_VIRTUAL) && mono_method_signature (method)->generic_param_count) { - return mono_arch_create_specific_trampoline (method, MONO_TRAMPOLINE_GENERIC_VIRTUAL_REMOTING, + return mono_create_specific_trampoline (method, MONO_TRAMPOLINE_GENERIC_VIRTUAL_REMOTING, domain, NULL); } @@ -4326,7 +5145,7 @@ mono_jit_create_remoting_trampoline (MonoDomain *domain, MonoMethod *method, Mon } #ifdef MONO_ARCH_HAVE_IMT -static gpointer +static G_GNUC_UNUSED gpointer mini_get_imt_trampoline (void) { static gpointer tramp = NULL; @@ -4336,16 +5155,15 @@ mini_get_imt_trampoline (void) } #endif -#ifdef MONO_ARCH_COMMON_VTABLE_TRAMPOLINE gpointer -mini_get_vtable_trampoline (void) +mini_get_vtable_trampoline (int slot_index) { static gpointer tramp = NULL; + if (!tramp) tramp = mono_create_specific_trampoline (MONO_FAKE_VTABLE_METHOD, MONO_TRAMPOLINE_JIT, mono_get_root_domain (), NULL); return tramp; } -#endif static void mini_parse_debug_options (void) @@ -4375,9 +5193,19 @@ mini_parse_debug_options (void) debug_options.suspend_on_sigsegv = TRUE; else if (!strcmp (arg, "dont-free-domains")) mono_dont_free_domains = TRUE; + else if (!strcmp (arg, "dyn-runtime-invoke")) + debug_options.dyn_runtime_invoke = TRUE; + else if (!strcmp (arg, "gdb")) + debug_options.gdb = TRUE; + else if (!strcmp (arg, "explicit-null-checks")) + debug_options.explicit_null_checks = TRUE; + else if (!strcmp (arg, "gen-seq-points")) + debug_options.gen_seq_points = TRUE; + else if (!strcmp (arg, "init-stacks")) + debug_options.init_stacks = TRUE; else { fprintf (stderr, "Invalid option for the MONO_DEBUG env variable: %s\n", arg); - fprintf (stderr, "Available options: 'handle-sigint', 'keep-delegates', 'collect-pagefault-stats', 'break-on-unverified', 'no-gdb-backtrace', 'dont-free-domains', 'suspend-on-sigsegv'\n"); + fprintf (stderr, "Available options: 'handle-sigint', 'keep-delegates', 'collect-pagefault-stats', 'break-on-unverified', 'no-gdb-backtrace', 'dont-free-domains', 'suspend-on-sigsegv', 'dyn-runtime-invoke', 'gdb', 'explicit-null-checks', 'init-stacks'\n"); exit (1); } } @@ -4390,6 +5218,45 @@ mini_get_debug_options (void) { return &debug_options; } + +static gpointer +mini_create_ftnptr (MonoDomain *domain, gpointer addr) +{ +#ifdef __ia64__ + gpointer *desc; + + desc = mono_domain_code_reserve (domain, 2 * sizeof (gpointer)); + + desc [0] = addr; + desc [1] = NULL; + + return desc; +#elif defined(__ppc64__) || defined(__powerpc64__) + gpointer *desc; + + desc = mono_domain_alloc0 (domain, 3 * sizeof (gpointer)); + + desc [0] = addr; + desc [1] = NULL; + desc [2] = NULL; + + return desc; +#else + return addr; +#endif +} + +static gpointer +mini_get_addr_from_ftnptr (gpointer descr) +{ +#if defined(__ia64__) || defined(__ppc64__) || defined(__powerpc64__) + return *(gpointer*)descr; +#else + return descr; +#endif +} + +static void runtime_invoke_info_free (gpointer value); static void mini_create_jit_domain_info (MonoDomain *domain) @@ -4400,6 +5267,10 @@ mini_create_jit_domain_info (MonoDomain *domain) info->jump_trampoline_hash = g_hash_table_new (mono_aligned_addr_hash, NULL); info->jit_trampoline_hash = g_hash_table_new (mono_aligned_addr_hash, NULL); info->delegate_trampoline_hash = g_hash_table_new (mono_aligned_addr_hash, NULL); + info->llvm_vcall_trampoline_hash = g_hash_table_new (mono_aligned_addr_hash, NULL); + info->runtime_invoke_hash = g_hash_table_new_full (mono_aligned_addr_hash, NULL, NULL, runtime_invoke_info_free); + info->seq_points = g_hash_table_new_full (mono_aligned_addr_hash, NULL, NULL, g_free); + info->arch_seq_points = g_hash_table_new (mono_aligned_addr_hash, NULL); domain->runtime_info = info; } @@ -4418,6 +5289,18 @@ dynamic_method_info_free (gpointer key, gpointer value, gpointer user_data) g_free (di); } +static void +runtime_invoke_info_free (gpointer value) +{ + RuntimeInvokeInfo *info = (RuntimeInvokeInfo*)value; + +#ifdef MONO_ARCH_DYN_CALL_SUPPORTED + if (info->dyn_call_info) + mono_arch_dyn_call_free (info->dyn_call_info); +#endif + g_free (info); +} + static void mini_free_jit_domain_info (MonoDomain *domain) { @@ -4441,6 +5324,15 @@ mini_free_jit_domain_info (MonoDomain *domain) g_hash_table_destroy (info->jump_trampoline_hash); g_hash_table_destroy (info->jit_trampoline_hash); g_hash_table_destroy (info->delegate_trampoline_hash); + if (info->static_rgctx_trampoline_hash) + g_hash_table_destroy (info->static_rgctx_trampoline_hash); + g_hash_table_destroy (info->llvm_vcall_trampoline_hash); + g_hash_table_destroy (info->runtime_invoke_hash); + g_hash_table_destroy (info->seq_points); + g_hash_table_destroy (info->arch_seq_points); + + if (info->agent_info) + mono_debugger_agent_free_domain_info (domain); g_free (domain->runtime_info); domain->runtime_info = NULL; @@ -4450,6 +5342,7 @@ MonoDomain * mini_init (const char *filename, const char *runtime_version) { MonoDomain *domain; + MonoRuntimeCallbacks callbacks; MONO_PROBE_VES_INIT_BEGIN (); @@ -4466,30 +5359,69 @@ mini_init (const char *filename, const char *runtime_version) InitializeCriticalSection (&jit_mutex); +#ifdef MONO_DEBUGGER_SUPPORTED + if (mini_debug_running_inside_mdb ()) + mini_debugger_init (); +#endif + +#ifdef MONO_ARCH_HAVE_TLS_GET + mono_runtime_set_has_tls_get (TRUE); +#else + mono_runtime_set_has_tls_get (FALSE); +#endif + if (!global_codeman) global_codeman = mono_code_manager_new (); jit_icall_name_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + memset (&callbacks, 0, sizeof (callbacks)); + callbacks.create_ftnptr = mini_create_ftnptr; + callbacks.get_addr_from_ftnptr = mini_get_addr_from_ftnptr; + callbacks.get_runtime_build_info = mono_get_runtime_build_info; + +#ifdef MONO_ARCH_HAVE_IMT + if (mono_use_imt) { + if (!mono_use_llvm) { + /* LLVM needs a per-method vtable trampoline */ + callbacks.get_vtable_trampoline = mini_get_vtable_trampoline; + } + } +#endif + + mono_install_callbacks (&callbacks); + mono_arch_cpu_init (); mono_arch_init (); mono_unwind_init (); + mini_gc_init (); + + if (getenv ("MONO_DEBUG") != NULL) + mini_parse_debug_options (); + if (getenv ("MONO_XDEBUG")) { - mono_xdebug_init (); + char *xdebug_opts = getenv ("MONO_XDEBUG"); + mono_xdebug_init (xdebug_opts); /* So methods for multiple domains don't have the same address */ mono_dont_free_domains = TRUE; + mono_using_xdebug = TRUE; + } else if (mini_get_debug_options ()->gdb) { + mono_xdebug_init ((char*)"gdb"); + mono_dont_free_domains = TRUE; + mono_using_xdebug = TRUE; } +#ifdef ENABLE_LLVM + mono_llvm_init (); +#endif + mono_trampolines_init (); if (!g_thread_supported ()) g_thread_init (NULL); - if (getenv ("MONO_DEBUG") != NULL) - mini_parse_debug_options (); - mono_gc_base_init (); mono_jit_tls_id = TlsAlloc (); @@ -4498,7 +5430,15 @@ mini_init (const char *filename, const char *runtime_version) if (default_opt & MONO_OPT_AOT) mono_aot_init (); + mono_debugger_agent_init (); + +#ifdef MONO_ARCH_GSHARED_SUPPORTED + mono_set_generic_sharing_supported (TRUE); +#endif + +#ifndef MONO_CROSS_COMPILE mono_runtime_install_handlers (); +#endif mono_threads_install_cleanup (mini_thread_cleanup); #ifdef MONO_ARCH_HAVE_NOTIFY_PENDING_EXC @@ -4515,7 +5455,15 @@ mini_init (const char *filename, const char *runtime_version) #ifdef JIT_TRAMPOLINES_WORK mono_install_compile_method (mono_jit_compile_method); mono_install_free_method (mono_jit_free_method); +#ifdef MONO_ARCH_LLVM_SUPPORTED + if (mono_use_llvm) + /* The runtime currently only uses this for filling out vtables */ + mono_install_trampoline (mono_create_llvm_vcall_trampoline); + else + mono_install_trampoline (mono_create_jit_trampoline); +#else mono_install_trampoline (mono_create_jit_trampoline); +#endif mono_install_jump_trampoline (mono_create_jump_trampoline); mono_install_remoting_trampoline (mono_jit_create_remoting_trampoline); mono_install_delegate_trampoline (mono_create_delegate_trampoline); @@ -4531,25 +5479,33 @@ mini_init (const char *filename, const char *runtime_version) mono_install_get_class_from_name (mono_aot_get_class_from_name); mono_install_jit_info_find_in_aot (mono_aot_find_jit_info); + if (debug_options.collect_pagefault_stats) { + mono_aot_set_make_unreadable (TRUE); + } + if (runtime_version) domain = mono_init_version (filename, runtime_version); else domain = mono_init_from_assembly (filename, filename); if (mono_aot_only) { - /* The IMT tables are very dynamic thus they are hard to AOT */ - mono_use_imt = FALSE; /* This helps catch code allocation requests */ mono_code_manager_set_read_only (domain->code_mp); } #ifdef MONO_ARCH_HAVE_IMT if (mono_use_imt) { - mono_install_imt_thunk_builder (mono_arch_build_imt_thunk); - mono_install_imt_trampoline (mini_get_imt_trampoline ()); -#if MONO_ARCH_COMMON_VTABLE_TRAMPOLINE - mono_install_vtable_trampoline (mini_get_vtable_trampoline ()); -#endif + if (mono_aot_only) + mono_install_imt_thunk_builder (mono_aot_get_imt_thunk); + else + mono_install_imt_thunk_builder (mono_arch_build_imt_thunk); + if (!mono_use_llvm) { + /* + * The imt code in mono_magic_trampoline () can't handle LLVM code. By disabling + * this, we force iface calls to go through the llvm vcall trampoline. + */ + mono_install_imt_trampoline (mini_get_imt_trampoline ()); + } } #endif @@ -4591,7 +5547,9 @@ mini_init (const char *filename, const char *runtime_version) register_icall (mono_get_throw_exception (), "mono_arch_throw_exception", "void object", TRUE); register_icall (mono_get_rethrow_exception (), "mono_arch_rethrow_exception", "void object", TRUE); +#ifdef MONO_ARCH_HAVE_THROW_EXCEPTION_BY_NAME register_icall (mono_get_throw_exception_by_name (), "mono_arch_throw_exception_by_name", "void ptr", TRUE); +#endif #if MONO_ARCH_HAVE_THROW_CORLIB_EXCEPTION register_icall (mono_get_throw_corlib_exception (), "mono_arch_throw_corlib_exception", "void ptr", TRUE); @@ -4751,6 +5709,8 @@ mini_init (const char *filename, const char *runtime_version) register_icall (mono_create_corlib_exception_2, "mono_create_corlib_exception_2", "object int object object", TRUE); register_icall (mono_array_new_1, "mono_array_new_1", "object ptr int", FALSE); register_icall (mono_array_new_2, "mono_array_new_2", "object ptr int int", FALSE); + register_icall (mono_array_new_3, "mono_array_new_3", "object ptr int int int", FALSE); + register_icall (mono_get_native_calli_wrapper, "mono_get_native_calli_wrapper", "ptr ptr ptr ptr", FALSE); #endif mono_generic_sharing_init (); @@ -4759,6 +5719,10 @@ mini_init (const char *filename, const char *runtime_version) mono_simd_intrinsics_init (); #endif +#if MONO_SUPPORT_TASKLETS + mono_tasklets_init (); +#endif + if (mono_compile_aot) /* * Avoid running managed code when AOT compiling, since the platform @@ -4774,7 +5738,7 @@ mini_init (const char *filename, const char *runtime_version) #endif mono_profiler_runtime_initialized (); - + MONO_PROBE_VES_INIT_END (); return domain; @@ -4875,7 +5839,8 @@ mini_cleanup (MonoDomain *domain) #ifndef DISABLE_COM cominterop_release_all_rcws (); #endif - + +#ifndef MONO_CROSS_COMPILE /* * mono_runtime_cleanup() and mono_domain_finalize () need to * be called early since they need the execution engine still @@ -4883,14 +5848,17 @@ mini_cleanup (MonoDomain *domain) * and mono_runtime_cleanup will wait for other threads to finish). */ mono_domain_finalize (domain, 2000); +#endif /* This accesses metadata so needs to be called before runtime shutdown */ print_jit_stats (); - mono_runtime_cleanup (domain); - mono_profiler_shutdown (); +#ifndef MONO_CROSS_COMPILE + mono_runtime_cleanup (domain); +#endif + mono_icall_cleanup (); mono_runtime_cleanup_handlers (); @@ -4899,6 +5867,10 @@ mini_cleanup (MonoDomain *domain) mono_debugger_cleanup (); +#ifdef ENABLE_LLVM + mono_llvm_cleanup (); +#endif + mono_trampolines_cleanup (); mono_unwind_cleanup (); @@ -4934,6 +5906,12 @@ mono_set_defaults (int verbose_level, guint32 opts) default_opt_set = TRUE; } +void +mono_disable_optimizations (guint32 opts) +{ + default_opt &= ~opts; +} + /* * mono_get_runtime_build_info: * @@ -4944,9 +5922,9 @@ char* mono_get_runtime_build_info (void) { if (mono_build_date) - return g_strdup_printf ("%s %s", FULL_VERSION, mono_build_date); + return g_strdup_printf ("%s (%s %s)", VERSION, FULL_VERSION, mono_build_date); else - return g_strdup_printf ("%s", FULL_VERSION); + return g_strdup_printf ("%s (%s)", VERSION, FULL_VERSION); } static void @@ -5003,3 +5981,12 @@ void mono_precompile_assemblies () g_hash_table_destroy (assemblies); } + +#ifndef DISABLE_JIT + +void* +mono_arch_instrument_epilog (MonoCompile *cfg, void *func, void *p, gboolean enable_arguments) { + return mono_arch_instrument_epilog_full (cfg, func, p, enable_arguments, FALSE); +} + +#endif