X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmini%2Fmini.c;h=69e19cf74d148f54663305cd625120b96a4a601c;hb=5b558abeeb255a3179d4ca6a85617e051c6abd38;hp=fa0c6d2602825160fc39d1c6f67cfeccf5364914;hpb=ad808e5f2e7af9f6e27aceb8e1f1e2ac684a7d0b;p=mono.git diff --git a/mono/mini/mini.c b/mono/mini/mini.c index fa0c6d26028..69e19cf74d1 100644 --- a/mono/mini/mini.c +++ b/mono/mini/mini.c @@ -1,13 +1,15 @@ /* * mini.c: The new Mono code generator. * - * Author: + * Authors: * Paolo Molaro (lupus@ximian.com) * Dietmar Maurer (dietmar@ximian.com) * - * (C) 2002 Ximian, Inc. + * Copyright 2002-2003 Ximian, Inc. + * Coprygith 2003-2010 Novell, Inc. */ +#define MONO_LLVM_IN_MINI 1 #include #include #ifdef HAVE_ALLOCA_H @@ -45,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -53,6 +56,7 @@ #include #include "mini.h" +#include "mini-llvm.h" #include "tasklets.h" #include #include @@ -65,24 +69,38 @@ #include "mini-gc.h" #include "debugger-agent.h" +#if defined(HAVE_KW_THREAD) +#define MINI_FAST_TLS_SET(x,y) x = y +#define MINI_FAST_TLS_GET(x) x +#define MINI_FAST_TLS_INIT(x) +#define MINI_FAST_TLS_DECLARE(x) static __thread gpointer x MONO_TLS_FAST; +#define MINI_HAVE_FAST_TLS +#define MINI_THREAD_VAR_OFFSET(x,y) MONO_THREAD_VAR_OFFSET(x,y) +#elif (defined(__APPLE__) && defined(__i386__)) +#define MINI_FAST_TLS_SET(x,y) pthread_setspecific(x, y) +#define MINI_FAST_TLS_GET(x) pthread_getspecific(x) +#define MINI_FAST_TLS_INIT(x) pthread_key_create(&x, NULL) +#define MINI_FAST_TLS_DECLARE(x) static pthread_key_t x; +#define MINI_HAVE_FAST_TLS +#define MINI_THREAD_VAR_OFFSET(x,y) y = (gint32) x +#else +#define MINI_THREAD_VAR_OFFSET(x,y) MONO_THREAD_VAR_OFFSET(x,y) +#endif + static gpointer mono_jit_compile_method_with_opt (MonoMethod *method, guint32 opt, MonoException **ex); -/* helper methods signature */ -/* FIXME: Make these static again */ -MonoMethodSignature *helper_sig_class_init_trampoline = NULL; -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; guint32 mono_jit_tls_id = -1; -#ifdef HAVE_KW_THREAD -static __thread gpointer mono_jit_tls MONO_TLS_FAST; +#ifdef MINI_HAVE_FAST_TLS +MINI_FAST_TLS_DECLARE(mono_jit_tls); +#endif + +#ifndef MONO_ARCH_MONITOR_ENTER_ADJUSTMENT +#define MONO_ARCH_MONITOR_ENTER_ADJUSTMENT 1 #endif MonoTraceSpec *mono_jit_trace_calls = NULL; @@ -106,25 +124,11 @@ 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. - * 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. + * This flag controls whenever the runtime uses LLVM for JIT compilation, and whenever + * it can load AOT code compiled by LLVM. */ -#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) @@ -156,6 +160,114 @@ gboolean disable_vtypes_in_regs = FALSE; gboolean mono_dont_free_global_codeman; +gpointer +mono_realloc_native_code (MonoCompile *cfg) +{ +#if defined(__default_codegen__) + return g_realloc (cfg->native_code, cfg->code_size); +#elif defined(__native_client_codegen__) + guint old_padding; + gpointer native_code; + guint alignment_check; + + /* Save the old alignment offset so we can re-align after the realloc. */ + old_padding = (guint)(cfg->native_code - cfg->native_code_alloc); + + cfg->native_code_alloc = g_realloc ( cfg->native_code_alloc, + cfg->code_size + kNaClAlignment ); + + /* Align native_code to next nearest kNaClAlignment byte. */ + native_code = (guint)cfg->native_code_alloc + kNaClAlignment; + native_code = (guint)native_code & ~kNaClAlignmentMask; + + /* Shift the data to be 32-byte aligned again. */ + memmove (native_code, cfg->native_code_alloc + old_padding, cfg->code_size); + + alignment_check = (guint)native_code & kNaClAlignmentMask; + g_assert (alignment_check == 0); + return native_code; +#else + g_assert_not_reached (); + return cfg->native_code; +#endif +} + +#ifdef __native_client_codegen__ + +/* Prevent instructions from straddling a 32-byte alignment boundary. */ +/* Instructions longer than 32 bytes must be aligned internally. */ +/* IN: pcode, instlen */ +/* OUT: pcode */ +void mono_nacl_align_inst(guint8 **pcode, int instlen) { + int space_in_block; + + space_in_block = kNaClAlignment - ((uintptr_t)(*pcode) & kNaClAlignmentMask); + + if (G_UNLIKELY (instlen >= kNaClAlignment)) { + g_assert_not_reached(); + } else if (instlen > space_in_block) { + *pcode = mono_arch_nacl_pad(*pcode, space_in_block); + } +} + +/* Move emitted call sequence to the end of a kNaClAlignment-byte block. */ +/* IN: start pointer to start of call sequence */ +/* IN: pcode pointer to end of call sequence (current "IP") */ +/* OUT: start pointer to the start of the call sequence after padding */ +/* OUT: pcode pointer to the end of the call sequence after padding */ +void mono_nacl_align_call(guint8 **start, guint8 **pcode) { + const size_t MAX_NACL_CALL_LENGTH = kNaClAlignment; + guint8 copy_of_call[MAX_NACL_CALL_LENGTH]; + guint8 *temp; + + const size_t length = (size_t)((*pcode)-(*start)); + g_assert(length < MAX_NACL_CALL_LENGTH); + + memcpy(copy_of_call, *start, length); + temp = mono_nacl_pad_call(*start, (guint8)length); + memcpy(temp, copy_of_call, length); + (*start) = temp; + (*pcode) = temp + length; +} + +/* mono_nacl_pad_call(): Insert padding for Native Client call instructions */ +/* code pointer to buffer for emitting code */ +/* ilength length of call instruction */ +guint8 *mono_nacl_pad_call(guint8 *code, guint8 ilength) { + int freeSpaceInBlock = kNaClAlignment - ((uintptr_t)code & kNaClAlignmentMask); + int padding = freeSpaceInBlock - ilength; + + if (padding < 0) { + /* There isn't enough space in this block for the instruction. */ + /* Fill this block and start a new one. */ + code = mono_arch_nacl_pad(code, freeSpaceInBlock); + freeSpaceInBlock = kNaClAlignment; + padding = freeSpaceInBlock - ilength; + } + g_assert(ilength > 0); + g_assert(padding >= 0); + g_assert(padding < kNaClAlignment); + if (0 == padding) return code; + return mono_arch_nacl_pad(code, padding); +} + +guint8 *mono_nacl_align(guint8 *code) { + int padding = kNaClAlignment - ((uintptr_t)code & kNaClAlignmentMask); + if (padding != kNaClAlignment) code = mono_arch_nacl_pad(code, padding); + return code; +} + +void mono_nacl_fix_patches(const guint8 *code, MonoJumpInfo *ji) +{ + MonoJumpInfo *patch_info; + for (patch_info = ji; patch_info; patch_info = patch_info->next) { + unsigned char *ip = patch_info->ip.i + code; + ip = mono_arch_nacl_skip_nops(ip); + patch_info->ip.i = ip - code; + } +} +#endif /* __native_client_codegen__ */ + gboolean mono_running_on_valgrind (void) { @@ -346,6 +458,67 @@ void *mono_global_codeman_reserve (int size) } } +#if defined(__native_client_codegen__) && defined(__native_client__) +/* Given the temporary buffer (allocated by mono_global_codeman_reserve) into + * which we are generating code, return a pointer to the destination in the + * dynamic code segment into which the code will be copied when + * mono_global_codeman_commit is called. + * LOCKING: Acquires the jit lock. + */ +void* +nacl_global_codeman_get_dest (void *data) +{ + void *dest; + mono_jit_lock (); + dest = nacl_code_manager_get_code_dest (global_codeman, data); + mono_jit_unlock (); + return dest; +} + +void +mono_global_codeman_commit (void *data, int size, int newsize) +{ + mono_jit_lock (); + mono_code_manager_commit (global_codeman, data, size, newsize); + mono_jit_unlock (); +} + +/* + * Convenience function which calls mono_global_codeman_commit to validate and + * copy the code. The caller sets *buf_base and *buf_size to the start and size + * of the buffer (allocated by mono_global_codeman_reserve), and *code_end to + * the byte after the last instruction byte. On return, *buf_base will point to + * the start of the copied in the code segment, and *code_end will point after + * the end of the copied code. + */ +void +nacl_global_codeman_validate (guint8 **buf_base, int buf_size, guint8 **code_end) +{ + guint8 *tmp = nacl_global_codeman_get_dest (*buf_base); + mono_global_codeman_commit (*buf_base, buf_size, *code_end - *buf_base); + *code_end = tmp + (*code_end - *buf_base); + *buf_base = tmp; +} +#else +/* no-op versions of Native Client functions */ +void* +nacl_global_codeman_get_dest (void *data) +{ + return data; +} + +void +mono_global_codeman_commit (void *data, int size, int newsize) +{ +} + +void +nacl_global_codeman_validate (guint8 **buf_base, int buf_size, guint8 **code_end) +{ +} + +#endif /* __native_client__ */ + /** * mono_create_unwind_op: * @@ -425,9 +598,15 @@ mono_tramp_info_create (const char *name, guint8 *code, guint32 code_size, MonoJ void mono_tramp_info_free (MonoTrampInfo *info) { + GSList *l; + g_free (info->name); - // FIXME: ji + unwind_ops + // FIXME: ji + for (l = info->unwind_ops; l; l = l->next) + g_free (l->data); + g_slist_free (info->unwind_ops); + g_free (info); } #define MONO_INIT_VARINFO(vi,id) do { \ @@ -953,6 +1132,18 @@ mono_compile_create_var_for_vreg (MonoCompile *cfg, MonoType *type, int opcode, inst->backend.is_pinvoke = 0; inst->dreg = vreg; + if (cfg->compute_gc_maps) { + if (type->byref) { + mono_mark_vreg_as_mp (cfg, vreg); + } else { + MonoType *t = mini_type_get_underlying_type (NULL, type); + if ((MONO_TYPE_ISSTRUCT (t) && inst->klass->has_references) || MONO_TYPE_IS_REFERENCE (t)) { + inst->flags |= MONO_INST_GC_TRACK; + mono_mark_vreg_as_ref (cfg, vreg); + } + } + } + cfg->varinfo [num] = inst; MONO_INIT_VARINFO (&cfg->vars [num], num); @@ -1043,6 +1234,38 @@ mono_compile_create_var (MonoCompile *cfg, MonoType *type, int opcode) return mono_compile_create_var_for_vreg (cfg, type, opcode, dreg); } +void +mono_mark_vreg_as_ref (MonoCompile *cfg, int vreg) +{ + if (vreg >= cfg->vreg_is_ref_len) { + gboolean *tmp = cfg->vreg_is_ref; + int size = cfg->vreg_is_ref_len; + + while (vreg >= cfg->vreg_is_ref_len) + cfg->vreg_is_ref_len = cfg->vreg_is_ref_len ? cfg->vreg_is_ref_len * 2 : 32; + cfg->vreg_is_ref = mono_mempool_alloc0 (cfg->mempool, sizeof (gboolean) * cfg->vreg_is_ref_len); + if (size) + memcpy (cfg->vreg_is_ref, tmp, size * sizeof (gboolean)); + } + cfg->vreg_is_ref [vreg] = TRUE; +} + +void +mono_mark_vreg_as_mp (MonoCompile *cfg, int vreg) +{ + if (vreg >= cfg->vreg_is_mp_len) { + gboolean *tmp = cfg->vreg_is_mp; + int size = cfg->vreg_is_mp_len; + + while (vreg >= cfg->vreg_is_mp_len) + cfg->vreg_is_mp_len = cfg->vreg_is_mp_len ? cfg->vreg_is_mp_len * 2 : 32; + cfg->vreg_is_mp = mono_mempool_alloc0 (cfg->mempool, sizeof (gboolean) * cfg->vreg_is_mp_len); + if (size) + memcpy (cfg->vreg_is_mp, tmp, size * sizeof (gboolean)); + } + cfg->vreg_is_mp [vreg] = TRUE; +} + /* * Transform a MonoInst into a load from the variable of index var_index. */ @@ -1254,7 +1477,7 @@ gboolean mini_assembly_can_skip_verification (MonoDomain *domain, MonoMethod *method) { MonoAssembly *assembly = method->klass->image->assembly; - if (method->wrapper_type != MONO_WRAPPER_NONE) + if (method->wrapper_type != MONO_WRAPPER_NONE && method->wrapper_type != MONO_WRAPPER_DYNAMIC_METHOD) return FALSE; if (assembly->in_gac || assembly->image == mono_defaults.corlib) return FALSE; @@ -1270,8 +1493,8 @@ mini_assembly_can_skip_verification (MonoDomain *domain, MonoMethod *method) * * Returns true if the method is invalid. */ -gboolean -mini_method_verify (MonoCompile *cfg, MonoMethod *method) +static gboolean +mini_method_verify (MonoCompile *cfg, MonoMethod *method, gboolean fail_compile) { GSList *tmp, *res; gboolean is_fulltrust; @@ -1288,7 +1511,10 @@ mini_method_verify (MonoCompile *cfg, MonoMethod *method) res = mono_method_verify_with_current_settings (method, cfg->skip_visibility); if ((error = mono_loader_get_last_error ())) { - cfg->exception_type = error->exception_type; + if (fail_compile) + cfg->exception_type = error->exception_type; + else + mono_loader_clear_error (); if (res) mono_free_verify_list (res); return TRUE; @@ -1298,19 +1524,23 @@ 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) { + if (fail_compile) { char *method_name = mono_method_full_name (method, TRUE); - cfg->exception_type = info->exception_type; - cfg->exception_message = g_strdup_printf ("Error verifying %s: %s", method_name, info->info.message); + cfg->exception_type = info->exception_type; + cfg->exception_message = g_strdup_printf ("Error verifying %s: %s", method_name, info->info.message); + g_free (method_name); + } mono_free_verify_list (res); - g_free (method_name); return TRUE; } 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_printf ("Error verifying %s: %s", method_name, info->info.message); + if (fail_compile) { + char *method_name = mono_method_full_name (method, TRUE); + cfg->exception_type = info->exception_type; + cfg->exception_message = g_strdup_printf ("Error verifying %s: %s", method_name, info->info.message); + g_free (method_name); + } mono_free_verify_list (res); - g_free (method_name); return TRUE; } } @@ -1320,11 +1550,10 @@ mini_method_verify (MonoCompile *cfg, MonoMethod *method) return FALSE; } -/*Returns true is something went wrong*/ -static gboolean -mono_compile_is_broken (MonoCompile *cfg) +/*Returns true if something went wrong*/ +gboolean +mono_compile_is_broken (MonoCompile *cfg, MonoMethod *method, gboolean fail_compile) { - 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; @@ -1334,18 +1563,7 @@ mono_compile_is_broken (MonoCompile *cfg) method_definition = imethod->declaring; } - return !dont_verify && mini_method_verify (cfg, method_definition); -} - -static void -create_helper_signature (void) -{ - helper_sig_domain_get = mono_create_icall_signature ("ptr"); - helper_sig_class_init_trampoline = mono_create_icall_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"); + return !dont_verify && mini_method_verify (cfg, method_definition, fail_compile); } static gconstpointer @@ -1555,17 +1773,16 @@ mono_allocate_stack_slots_full2 (MonoCompile *cfg, gboolean backward, guint32 *s case MONO_TYPE_PTR: case MONO_TYPE_I: case MONO_TYPE_U: -#if SIZEOF_REGISTER == 4 +#if SIZEOF_VOID_P == 4 case MONO_TYPE_I4: #else case MONO_TYPE_I8: #endif -#ifdef HAVE_SGEN_GC - slot_info = &scalar_stack_slots [MONO_TYPE_I]; - break; -#else + if (cfg->disable_ref_noref_stack_slot_share) { + slot_info = &scalar_stack_slots [MONO_TYPE_I]; + break; + } /* Fall through */ -#endif case MONO_TYPE_CLASS: case MONO_TYPE_OBJECT: @@ -1790,7 +2007,7 @@ mono_allocate_stack_slots_full (MonoCompile *cfg, gboolean backward, guint32 *st vars = mono_varlist_sort (cfg, vars, 0); offset = 0; - *stack_align = sizeof (gpointer); + *stack_align = sizeof(mgreg_t); for (l = vars; l; l = l->next) { vmv = l->data; inst = cfg->varinfo [vmv->idx]; @@ -1845,17 +2062,16 @@ mono_allocate_stack_slots_full (MonoCompile *cfg, gboolean backward, guint32 *st case MONO_TYPE_PTR: case MONO_TYPE_I: case MONO_TYPE_U: -#if SIZEOF_REGISTER == 4 +#if SIZEOF_VOID_P == 4 case MONO_TYPE_I4: #else case MONO_TYPE_I8: #endif -#ifdef HAVE_SGEN_GC - slot_info = &scalar_stack_slots [MONO_TYPE_I]; - break; -#else + if (cfg->disable_ref_noref_stack_slot_share) { + slot_info = &scalar_stack_slots [MONO_TYPE_I]; + break; + } /* Fall through */ -#endif case MONO_TYPE_CLASS: case MONO_TYPE_OBJECT: @@ -2060,7 +2276,11 @@ print_dfn (MonoCompile *cfg) { MonoBasicBlock *bb; MonoInst *c; - g_print ("IR code for method %s\n", mono_method_full_name (cfg->method, TRUE)); + { + char *method_name = mono_method_full_name (cfg->method, TRUE); + g_print ("IR code for method %s\n", method_name); + g_free (method_name); + } for (i = 0; i < cfg->num_bblocks; ++i) { bb = cfg->bblocks [i]; @@ -2146,6 +2366,8 @@ mono_bblock_insert_before_ins (MonoBasicBlock *bb, MonoInst *ins, MonoInst *ins_ { if (ins == NULL) { ins = bb->code; + if (ins) + ins->prev = ins_to_insert; bb->code = ins_to_insert; ins_to_insert->next = ins; if (bb->last_ins == NULL) @@ -2221,20 +2443,22 @@ mono_destroy_compile (MonoCompile *cfg) g_hash_table_destroy (cfg->abs_patches); mono_mempool_destroy (cfg->mempool); + mono_debug_free_method (cfg); + g_free (cfg->varinfo); g_free (cfg->vars); g_free (cfg->exception_message); g_free (cfg); } -#ifdef HAVE_KW_THREAD -static __thread gpointer mono_lmf_addr MONO_TLS_FAST; +#ifdef MINI_HAVE_FAST_TLS +MINI_FAST_TLS_DECLARE(mono_lmf_addr); #ifdef MONO_ARCH_ENABLE_MONO_LMF_VAR /* * When this is defined, the current lmf is stored in this tls variable instead of in * jit_tls->lmf. */ -static __thread gpointer mono_lmf MONO_TLS_FAST; +MINI_FAST_TLS_DECLARE(mono_lmf); #endif #endif @@ -2247,9 +2471,9 @@ mono_get_jit_tls_key (void) gint32 mono_get_jit_tls_offset (void) { -#ifdef HAVE_KW_THREAD +#ifdef MINI_HAVE_FAST_TLS int offset; - MONO_THREAD_VAR_OFFSET (mono_jit_tls, offset); + MINI_THREAD_VAR_OFFSET (mono_jit_tls, offset); return offset; #else return -1; @@ -2259,9 +2483,9 @@ mono_get_jit_tls_offset (void) gint32 mono_get_lmf_tls_offset (void) { -#if defined(HAVE_KW_THREAD) && defined(MONO_ARCH_ENABLE_MONO_LMF_VAR) +#if defined(MINI_HAVE_FAST_TLS) && defined(MONO_ARCH_ENABLE_MONO_LMF_VAR) int offset; - MONO_THREAD_VAR_OFFSET(mono_lmf,offset); + MINI_THREAD_VAR_OFFSET(mono_lmf,offset); return offset; #else return -1; @@ -2272,22 +2496,25 @@ gint32 mono_get_lmf_addr_tls_offset (void) { int offset; - MONO_THREAD_VAR_OFFSET(mono_lmf_addr,offset); + MINI_THREAD_VAR_OFFSET(mono_lmf_addr,offset); return offset; } MonoLMF * mono_get_lmf (void) { -#if defined(HAVE_KW_THREAD) && defined(MONO_ARCH_ENABLE_MONO_LMF_VAR) - return mono_lmf; +#if defined(MINI_HAVE_FAST_TLS) && defined(MONO_ARCH_ENABLE_MONO_LMF_VAR) + return MINI_FAST_TLS_GET (mono_lmf); #else MonoJitTlsData *jit_tls; if ((jit_tls = TlsGetValue (mono_jit_tls_id))) return jit_tls->lmf; - - g_assert_not_reached (); + /* + * We do not assert here because this function can be called from + * mini-gc.c on a thread that has not executed any managed code, yet + * (the thread object allocation can trigger a collection). + */ return NULL; #endif } @@ -2295,8 +2522,8 @@ mono_get_lmf (void) MonoLMF ** mono_get_lmf_addr (void) { -#ifdef HAVE_KW_THREAD - return mono_lmf_addr; +#ifdef MINI_HAVE_FAST_TLS + return MINI_FAST_TLS_GET (mono_lmf_addr); #else MonoJitTlsData *jit_tls; @@ -2324,13 +2551,31 @@ mono_get_lmf_addr (void) void mono_set_lmf (MonoLMF *lmf) { -#if defined(HAVE_KW_THREAD) && defined(MONO_ARCH_ENABLE_MONO_LMF_VAR) - mono_lmf = lmf; +#if defined(MINI_HAVE_FAST_TLS) && defined(MONO_ARCH_ENABLE_MONO_LMF_VAR) + MINI_FAST_TLS_SET (mono_lmf, lmf); #endif (*mono_get_lmf_addr ()) = lmf; } +static void +mono_set_jit_tls (MonoJitTlsData *jit_tls) +{ + TlsSetValue (mono_jit_tls_id, jit_tls); + +#ifdef MINI_HAVE_FAST_TLS + MINI_FAST_TLS_SET (mono_jit_tls, jit_tls); +#endif +} + +static void +mono_set_lmf_addr (gpointer lmf_addr) +{ +#ifdef MINI_HAVE_FAST_TLS + MINI_FAST_TLS_SET (mono_lmf_addr, lmf_addr); +#endif +} + /* Called by native->managed wrappers */ void mono_jit_thread_attach (MonoDomain *domain) @@ -2342,8 +2587,8 @@ mono_jit_thread_attach (MonoDomain *domain) */ domain = mono_get_root_domain (); -#ifdef HAVE_KW_THREAD - if (!mono_lmf_addr) { +#ifdef MINI_HAVE_FAST_TLS + if (!MINI_FAST_TLS_GET (mono_lmf_addr)) { mono_thread_attach (domain); } #else @@ -2388,32 +2633,22 @@ setup_jit_tls_data (gpointer stack_start, gpointer abort_func) jit_tls = g_new0 (MonoJitTlsData, 1); - TlsSetValue (mono_jit_tls_id, jit_tls); - -#ifdef HAVE_KW_THREAD - mono_jit_tls = jit_tls; -#endif - jit_tls->abort_func = abort_func; jit_tls->end_of_stack = stack_start; + mono_set_jit_tls (jit_tls); + lmf = g_new0 (MonoLMF, 1); -#ifdef MONO_ARCH_INIT_TOP_LMF_ENTRY MONO_ARCH_INIT_TOP_LMF_ENTRY (lmf); -#else - lmf->ebp = -1; -#endif jit_tls->first_lmf = lmf; -#if defined(HAVE_KW_THREAD) && defined(MONO_ARCH_ENABLE_MONO_LMF_VAR) +#if defined(MINI_HAVE_FAST_TLS) && defined(MONO_ARCH_ENABLE_MONO_LMF_VAR) /* jit_tls->lmf is unused */ - mono_lmf = lmf; - mono_lmf_addr = &mono_lmf; + MINI_FAST_TLS_SET (mono_lmf, lmf); + mono_set_lmf_addr (&mono_lmf); #else -#if defined(HAVE_KW_THREAD) - mono_lmf_addr = &jit_tls->lmf; -#endif + mono_set_lmf_addr (&jit_tls->lmf); jit_tls->lmf = lmf; #endif @@ -2424,6 +2659,16 @@ setup_jit_tls_data (gpointer stack_start, gpointer abort_func) return jit_tls; } +static void +free_jit_tls_data (MonoJitTlsData *jit_tls) +{ + mono_arch_free_jit_tls_data (jit_tls); + mono_free_altstack (jit_tls); + + g_free (jit_tls->first_lmf); + g_free (jit_tls); +} + static void mono_thread_start_cb (intptr_t tid, gpointer stack_start, gpointer func) { @@ -2433,6 +2678,8 @@ mono_thread_start_cb (intptr_t tid, gpointer stack_start, gpointer func) mono_debugger_thread_created (tid, thread->root_domain_thread, jit_tls, func); if (thread) thread->jit_data = jit_tls; + + mono_arch_cpu_init (); } void (*mono_thread_attach_aborted_cb ) (MonoObject *obj) = NULL; @@ -2457,22 +2704,17 @@ mono_thread_attach_cb (intptr_t tid, gpointer stack_start) thread->jit_data = jit_tls; if (mono_profiler_get_events () & MONO_PROFILE_STATISTICAL) mono_runtime_setup_stat_profiler (); + + mono_arch_cpu_init (); } static void -mini_thread_cleanup (MonoThread *thread) +mini_thread_cleanup (MonoInternalThread *thread) { - MonoInternalThread *internal = thread->internal_thread; - MonoJitTlsData *jit_tls = internal->jit_data; + MonoJitTlsData *jit_tls = thread->jit_data; if (jit_tls) { mono_debugger_thread_cleanup (jit_tls); - mono_arch_free_jit_tls_data (jit_tls); - - mono_free_altstack (jit_tls); - g_free (jit_tls->first_lmf); - g_free (jit_tls); - 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 @@ -2480,17 +2722,15 @@ mini_thread_cleanup (MonoThread *thread) * * The current offender is mono_thread_manage which cleanup threads from the outside. */ - if (internal == mono_thread_internal_current ()) { - TlsSetValue (mono_jit_tls_id, NULL); - -#ifdef HAVE_KW_THREAD - mono_jit_tls = NULL; - mono_lmf_addr = NULL; -#if defined(MONO_ARCH_ENABLE_MONO_LMF_VAR) - mono_lmf = NULL; -#endif -#endif + if (thread == mono_thread_internal_current ()) { + mono_set_lmf (NULL); + mono_set_jit_tls (NULL); + mono_set_lmf_addr (NULL); } + + free_jit_tls_data (jit_tls); + + thread->jit_data = NULL; } } @@ -2710,7 +2950,13 @@ mono_resolve_patch_target (MonoMethod *method, MonoDomain *domain, guint8 *code, target = patch_info->data.inst->inst_c0 + code; break; case MONO_PATCH_INFO_IP: +#if defined(__native_client__) && defined(__native_client_codegen__) + /* Need to transform to the destination address, it's */ + /* emitted as an immediate in the code. */ + target = nacl_inverse_modify_patch_target(ip); +#else target = ip; +#endif break; case MONO_PATCH_INFO_METHOD_REL: target = code + patch_info->data.offset; @@ -2726,6 +2972,13 @@ mono_resolve_patch_target (MonoMethod *method, MonoDomain *domain, guint8 *code, } case MONO_PATCH_INFO_METHOD_JUMP: target = mono_create_jump_trampoline (domain, patch_info->data.method, FALSE); +#if defined(__native_client__) && defined(__native_client_codegen__) +#if defined(TARGET_AMD64) + /* This target is an absolute address, not relative to the */ + /* current code being emitted on AMD64. */ + target = nacl_inverse_modify_patch_target(target); +#endif +#endif break; case MONO_PATCH_INFO_METHOD: if (patch_info->data.method == method) { @@ -2739,6 +2992,11 @@ mono_resolve_patch_target (MonoMethod *method, MonoDomain *domain, guint8 *code, gpointer *jump_table; int i; +#if defined(__native_client__) && defined(__native_client_codegen__) + /* This memory will leak, but we don't care if we're */ + /* not deleting JIT'd methods anyway */ + jump_table = g_malloc0 (sizeof(gpointer) * patch_info->data.table->table_size); +#else if (method && method->dynamic) { jump_table = mono_code_manager_reserve (mono_dynamic_code_hash_lookup (domain, method)->code_mp, sizeof (gpointer) * patch_info->data.table->table_size); } else { @@ -2748,10 +3006,27 @@ mono_resolve_patch_target (MonoMethod *method, MonoDomain *domain, guint8 *code, jump_table = mono_domain_code_reserve (domain, sizeof (gpointer) * patch_info->data.table->table_size); } } +#endif - for (i = 0; i < patch_info->data.table->table_size; i++) + for (i = 0; i < patch_info->data.table->table_size; i++) { +#if defined(__native_client__) && defined(__native_client_codegen__) + /* 'code' is relative to the current code blob, we */ + /* need to do this transform on it to make the */ + /* pointers in this table absolute */ + jump_table [i] = nacl_inverse_modify_patch_target (code) + GPOINTER_TO_INT (patch_info->data.table->table [i]); +#else jump_table [i] = code + GPOINTER_TO_INT (patch_info->data.table->table [i]); +#endif + } + +#if defined(__native_client__) && defined(__native_client_codegen__) + /* jump_table is in the data section, we need to transform */ + /* it here so when it gets modified in amd64_patch it will */ + /* then point back to the absolute data address */ + target = nacl_inverse_modify_patch_target (jump_table); +#else target = jump_table; +#endif break; } case MONO_PATCH_INFO_METHODCONST: @@ -2944,6 +3219,13 @@ mono_resolve_patch_target (MonoMethod *method, MonoDomain *domain, guint8 *code, g_assert_not_reached (); #endif break; + case MONO_PATCH_INFO_GC_CARD_TABLE_ADDR: { + int card_table_shift_bits; + gpointer card_table_mask; + + target = mono_gc_get_card_table (&card_table_shift_bits, &card_table_mask); + break; + } default: g_assert_not_reached (); } @@ -3090,11 +3372,18 @@ mono_postprocess_patches (MonoCompile *cfg) } case MONO_PATCH_INFO_SWITCH: { gpointer *table; +#if defined(__native_client__) && defined(__native_client_codegen__) + /* This memory will leak. */ + /* TODO: can we free this when */ + /* making the final jump table? */ + table = g_malloc0 (sizeof(gpointer) * patch_info->data.table->table_size); +#else if (cfg->method->dynamic) { table = mono_code_manager_reserve (cfg->dynamic_info->code_mp, sizeof (gpointer) * patch_info->data.table->table_size); } else { table = mono_domain_code_reserve (cfg->domain, sizeof (gpointer) * patch_info->data.table->table_size); } +#endif for (i = 0; i < patch_info->data.table->table_size; i++) { /* Might be NULL if the switch is eliminated */ @@ -3112,6 +3401,12 @@ mono_postprocess_patches (MonoCompile *cfg) GSList *list; MonoDomain *domain = cfg->domain; unsigned char *ip = cfg->native_code + patch_info->ip.i; +#if defined(__native_client__) && defined(__native_client_codegen__) + /* When this jump target gets evaluated, the method */ + /* will be installed in the dynamic code section, */ + /* not at the location of cfg->native_code. */ + ip = nacl_inverse_modify_patch_target (cfg->native_code) + patch_info->ip.i; +#endif mono_domain_lock (domain); if (!domain_jit_info (domain)->jump_target_hash) @@ -3232,9 +3527,13 @@ mono_save_seq_point_info (MonoCompile *cfg) 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); + if (!cfg->compile_aot) { + mono_domain_lock (domain); + // FIXME: How can the lookup succeed ? + if (!g_hash_table_lookup (domain_jit_info (domain)->seq_points, cfg->method_to_register)) + 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; @@ -3247,6 +3546,15 @@ mono_codegen (MonoCompile *cfg) int max_epilog_size; guint8 *code; +#if defined(__native_client_codegen__) && defined(__native_client__) + void *code_dest; + + /* This keeps patch targets from being transformed during + * ordinary method compilation, for local branches and jumps. + */ + nacl_allow_target_modification (FALSE); +#endif + for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { cfg->spill_count = 0; /* we reuse dfn here */ @@ -3280,6 +3588,7 @@ mono_codegen (MonoCompile *cfg) /* emit code all basic blocks */ for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { bb->native_offset = cfg->code_len; + bb->real_native_offset = cfg->code_len; //if ((bb == cfg->bb_entry) || !(bb->region == -1 && !bb->dfn)) mono_arch_output_basic_block (cfg, bb); bb->native_length = cfg->code_len - bb->native_offset; @@ -3298,6 +3607,9 @@ mono_codegen (MonoCompile *cfg) } } +#ifdef __native_client_codegen__ + mono_nacl_fix_patches (cfg->native_code, cfg->patch_info); +#endif mono_arch_emit_exceptions (cfg); max_epilog_size = 0; @@ -3328,9 +3640,22 @@ mono_codegen (MonoCompile *cfg) #endif code = mono_domain_code_reserve (cfg->domain, cfg->code_size + unwindlen); } +#if defined(__native_client_codegen__) && defined(__native_client__) + nacl_allow_target_modification (TRUE); +#endif memcpy (code, cfg->native_code, cfg->code_len); +#if defined(__default_codegen__) g_free (cfg->native_code); +#elif defined(__native_client_codegen__) + if (cfg->native_code_alloc) { + g_free (cfg->native_code_alloc); + cfg->native_code_alloc = 0; + } + else if (cfg->native_code) { + g_free (cfg->native_code); + } +#endif /* __native_client_codegen__ */ cfg->native_code = code; code = cfg->native_code + cfg->code_len; @@ -3368,7 +3693,21 @@ if (valgrind_register){ #ifdef MONO_ARCH_HAVE_SAVE_UNWIND_INFO mono_arch_save_unwind_info (cfg); #endif - + +#if defined(__native_client_codegen__) && defined(__native_client__) + if (!cfg->compile_aot) { + if (cfg->method->dynamic) { + code_dest = nacl_code_manager_get_code_dest(cfg->dynamic_info->code_mp, cfg->native_code); + } else { + code_dest = nacl_domain_get_code_dest(cfg->domain, cfg->native_code); + } + } +#endif + +#if defined(__native_client_codegen__) + mono_nacl_fix_patches (cfg->native_code, cfg->patch_info); +#endif + mono_arch_patch_code (cfg->method, cfg->domain, cfg->native_code, cfg->patch_info, cfg->run_cctors); if (cfg->method->dynamic) { @@ -3376,6 +3715,9 @@ if (valgrind_register){ } else { mono_domain_code_commit (cfg->domain, cfg->native_code, cfg->code_size, cfg->code_len); } +#if defined(__native_client_codegen__) && defined(__native_client__) + cfg->native_code = code_dest; +#endif 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); @@ -3399,6 +3741,20 @@ compute_reachable (MonoBasicBlock *bb) } } +static void +mono_handle_out_of_line_bblock (MonoCompile *cfg) +{ + MonoBasicBlock *bb; + for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { + if (bb->next_bb && bb->next_bb->out_of_line && bb->last_ins && !MONO_IS_BRANCH_OP (bb->last_ins)) { + MonoInst *ins; + MONO_INST_NEW (cfg, ins, OP_BR); + MONO_ADD_INS (bb, ins); + ins->inst_target_bb = bb->next_bb; + } + } +} + static MonoJitInfo* create_jit_info (MonoCompile *cfg, MonoMethod *method_to_compile) { @@ -3482,12 +3838,18 @@ create_jit_info (MonoCompile *cfg, MonoMethod *method_to_compile) mini_method_get_context (method_to_compile)->method_inst || method_to_compile->klass->valuetype) { inst = cfg->rgctx_var; - g_assert (inst->opcode == OP_REGOFFSET); + if (!COMPILE_LLVM (cfg)) + g_assert (inst->opcode == OP_REGOFFSET); } else { inst = cfg->args [0]; } - if (inst->opcode == OP_REGVAR) { + if (COMPILE_LLVM (cfg)) { + g_assert (cfg->llvm_this_reg != -1); + gi->this_in_reg = 0; + gi->this_reg = cfg->llvm_this_reg; + gi->this_offset = cfg->llvm_this_offset; + } else if (inst->opcode == OP_REGVAR) { gi->this_in_reg = 1; gi->this_reg = inst->dreg; } else { @@ -3565,10 +3927,27 @@ create_jit_info (MonoCompile *cfg, MonoMethod *method_to_compile) 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); + ei->try_start = cfg->native_code + tblock->native_offset; + if (tblock->extend_try_block) { + /* + * Extend the try block backwards to include parts of the previous call + * instruction. + */ + ei->try_start = (guint8*)ei->try_start - MONO_ARCH_MONITOR_ENTER_ADJUSTMENT; + } tblock = cfg->cil_offset_to_bb [ec->try_offset + ec->try_len]; g_assert (tblock); + if (!tblock->native_offset) { + int j, end; + for (j = ec->try_offset + ec->try_len, end = ec->try_offset; j >= end; --j) { + MonoBasicBlock *bb = cfg->cil_offset_to_bb [j]; + if (bb && bb->native_offset) { + tblock = bb; + break; + } + } + } ei->try_end = cfg->native_code + tblock->native_offset; g_assert (tblock->native_offset); tblock = cfg->cil_offset_to_bb [ec->handler_offset]; @@ -3728,7 +4107,7 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool MonoCompile *cfg; int dfn, i, code_size_ratio; gboolean deadce_has_run = FALSE; - gboolean try_generic_shared, try_llvm; + gboolean try_generic_shared, try_llvm = FALSE; MonoMethod *method_to_compile, *method_to_register; mono_jit_stats.methods_compiled++; @@ -3736,7 +4115,7 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool mono_profiler_method_jit (method); if (MONO_PROBE_METHOD_COMPILE_BEGIN_ENABLED ()) MONO_PROBE_METHOD_COMPILE_BEGIN (method); - + if (compile_aot) /* * We might get passed the original generic method definition or @@ -3756,10 +4135,8 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool mono_stats.generics_unsharable_methods++; } - try_llvm = TRUE; - -#ifndef ENABLE_LLVM - try_llvm = FALSE; +#ifdef ENABLE_LLVM + try_llvm = mono_use_llvm; #endif restart_compile: @@ -3805,25 +4182,6 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool } 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; - } - } - mono_error_init (&err); sig = mono_method_signature_checked (cfg->method, &err); if (!sig) { @@ -3850,39 +4208,30 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool return cfg; } - if (header->clauses) { - /* - * FIXME: LLLVM 2.6/SVN no longer seems to generate correct exception info - * for JITted code. - */ - 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); + if (COMPILE_LLVM (cfg)) { + mono_llvm_check_method_supported (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); + } + mono_destroy_compile (cfg); + try_llvm = FALSE; + goto restart_compile; } - InterlockedIncrement (&methods_without_llvm); - mono_destroy_compile (cfg); - try_llvm = FALSE; - goto restart_compile; } } #endif @@ -3933,7 +4282,7 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool cfg->compute_precise_live_ranges = TRUE; } - mini_gc_init_gc_map (cfg); + mini_gc_init_cfg (cfg); if (COMPILE_LLVM (cfg)) { cfg->opt |= MONO_OPT_ABCREM; @@ -3961,12 +4310,14 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool cfg->intvars = mono_mempool_alloc0 (cfg->mempool, sizeof (guint16) * STACK_MAX * header->max_stack); if (cfg->verbose_level > 0) { + char *method_name; if (COMPILE_LLVM (cfg)) - g_print ("converting llvm method %s\n", mono_method_full_name (method, TRUE)); + g_print ("converting llvm method %s\n", method_name = mono_method_full_name (method, TRUE)); else if (cfg->generic_sharing_context) - g_print ("converting shared method %s\n", mono_method_full_name (method_to_compile, TRUE)); + g_print ("converting shared method %s\n", method_name = mono_method_full_name (method_to_compile, TRUE)); else - g_print ("converting method %s\n", mono_method_full_name (method, TRUE)); + g_print ("converting method %s\n", method_name = mono_method_full_name (method, TRUE)); + g_free (method_name); } if (cfg->opt & (MONO_OPT_ABCREM | MONO_OPT_SSAPRE)) @@ -4035,8 +4386,11 @@ 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)) + if (mono_compile_is_broken (cfg, cfg->method, TRUE)) { + if (mini_get_debug_options ()->break_on_unverified) + G_BREAKPOINT (); return cfg; + } /* * create MonoInst* which represents arguments and local variables @@ -4110,6 +4464,8 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool */ mono_liveness_handle_exception_clauses (cfg); + mono_handle_out_of_line_bblock (cfg); + /*g_print ("numblocks = %d\n", cfg->num_bblocks);*/ if (!COMPILE_LLVM (cfg)) @@ -4280,7 +4636,8 @@ 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)) + /* This removes MONO_INST_FAULT flags too so perform it unconditionally */ + if (cfg->opt & MONO_OPT_ABCREM) mono_perform_abc_removal (cfg); } @@ -4453,14 +4810,11 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool 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; } - InterlockedIncrement (&methods_with_llvm); - 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", @@ -4473,11 +4827,10 @@ 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 (COMPILE_LLVM (cfg)) + InterlockedIncrement (&mono_jit_stats.methods_with_llvm); + else + InterlockedIncrement (&mono_jit_stats.methods_without_llvm); cfg->jit_info = create_jit_info (cfg, method_to_compile); @@ -4489,12 +4842,19 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool } #endif - mono_save_xdebug_info (cfg); + if (!cfg->compile_aot) + mono_save_xdebug_info (cfg); mini_gc_create_gc_map (cfg); mono_save_seq_point_info (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->compile_aot) { mono_domain_lock (cfg->domain); mono_jit_info_table_add (cfg->domain, cfg->jit_info); @@ -4624,6 +4984,13 @@ mono_emit_jit_map (MonoJitInfo *jinfo) g_free (name); } } + +gboolean +mono_jit_map_is_enabled (void) +{ + return perf_map_file != NULL; +} + #endif static gpointer @@ -4635,6 +5002,8 @@ mono_jit_compile_method_inner (MonoMethod *method, MonoDomain *target_domain, in MonoVTable *vtable; MonoException *ex = NULL; guint32 prof_options; + GTimer *jit_timer; + MonoMethod *prof_method; #ifdef MONO_USE_AOT_COMPILER if (opt & MONO_OPT_AOT) { @@ -4670,12 +5039,19 @@ mono_jit_compile_method_inner (MonoMethod *method, MonoDomain *target_domain, in mono_lookup_pinvoke_call (method, NULL, NULL); } nm = mono_marshal_get_native_wrapper (method, check_for_pending_exc, FALSE); - return mono_get_addr_from_ftnptr (mono_compile_method (nm)); + code = mono_get_addr_from_ftnptr (mono_compile_method (nm)); + jinfo = mono_jit_info_table_find (target_domain, code); + if (!jinfo) + jinfo = mono_jit_info_table_find (mono_domain_get (), code); + if (jinfo) + mono_profiler_method_end_jit (method, jinfo, MONO_PROFILE_OK); + return code; //if (mono_debug_format != MONO_DEBUG_FORMAT_NONE) //mono_debug_add_wrapper (method, nm); } else if ((method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME)) { const char *name = method->name; + char *full_name, *msg; MonoMethod *nm; if (method->klass->parent == mono_defaults.multicastdelegate_class) { @@ -4708,6 +5084,12 @@ mono_jit_compile_method_inner (MonoMethod *method, MonoDomain *target_domain, in return mono_get_addr_from_ftnptr (mono_compile_method (nm)); } } + + full_name = mono_method_full_name (method, TRUE); + msg = g_strdup_printf ("Unrecognizable runtime implemented method '%s'", full_name); + *jit_ex = mono_exception_from_name_msg (mono_defaults.corlib, "System", "InvalidProgramException", msg); + g_free (full_name); + g_free (msg); return NULL; } @@ -4722,7 +5104,14 @@ mono_jit_compile_method_inner (MonoMethod *method, MonoDomain *target_domain, in return NULL; } + jit_timer = g_timer_new (); + cfg = mini_method_compile (method, opt, target_domain, TRUE, FALSE, 0); + prof_method = cfg->method; + + g_timer_stop (jit_timer); + mono_jit_stats.jit_time += g_timer_elapsed (jit_timer, NULL); + g_timer_destroy (jit_timer); switch (cfg->exception_type) { case MONO_EXCEPTION_NONE: @@ -4789,6 +5178,9 @@ mono_jit_compile_method_inner (MonoMethod *method, MonoDomain *target_domain, in ex = exp; break; } + case MONO_EXCEPTION_OUT_OF_MEMORY: + ex = mono_domain_get ()->out_of_memory_ex; + break; default: g_assert_not_reached (); } @@ -4874,8 +5266,10 @@ mono_jit_compile_method_inner (MonoMethod *method, MonoDomain *target_domain, in /* 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_profiler_method_end_jit (method, jinfo, MONO_PROFILE_OK); + if (prof_method != method) { + mono_profiler_method_end_jit (prof_method, jinfo, MONO_PROFILE_OK); } } @@ -4921,11 +5315,16 @@ mono_jit_compile_method_with_opt (MonoMethod *method, guint32 opt, MonoException /* We can't use a domain specific method in another domain */ if (! ((domain != target_domain) && !info->domain_neutral)) { MonoVTable *vtable; + MonoException *tmpEx; mono_jit_stats.methods_lookups++; vtable = mono_class_vtable (domain, method->klass); g_assert (vtable); - mono_runtime_class_init (vtable); + tmpEx = mono_runtime_class_init_full (vtable, ex == NULL); + if (tmpEx) { + *ex = tmpEx; + return NULL; + } return mono_create_ftnptr (target_domain, info->code_start); } } @@ -5243,6 +5642,10 @@ mono_jit_runtime_invoke (MonoMethod *method, void *obj, void **params, MonoObjec mono_runtime_class_init (info->vtable); } + /* The wrappers expect this to be initialized to NULL */ + if (exc) + *exc = NULL; + #ifdef MONO_ARCH_DYN_CALL_SUPPORTED if (info->dyn_call_info) { MonoMethodSignature *sig = mono_method_signature (method); @@ -5306,7 +5709,11 @@ SIG_HANDLER_SIGNATURE (mono_sigfpe_signal_handler) #if defined(MONO_ARCH_HAVE_IS_INT_OVERFLOW) if (mono_arch_is_int_overflow (ctx, info)) - exc = mono_get_exception_arithmetic (); + /* + * The spec says this throws ArithmeticException, but MS throws the derived + * OverflowException. + */ + exc = mono_get_exception_overflow (); else exc = mono_get_exception_divide_by_zero (); #else @@ -5343,6 +5750,7 @@ SIG_HANDLER_SIGNATURE (mono_sigsegv_signal_handler) { MonoJitInfo *ji; MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id); + gpointer fault_addr = NULL; GET_CONTEXT; @@ -5357,6 +5765,7 @@ SIG_HANDLER_SIGNATURE (mono_sigsegv_signal_handler) #endif #if !defined(HOST_WIN32) && defined(HAVE_SIG_INFO) + fault_addr = info->si_addr; if (mono_aot_is_pagefault (info->si_addr)) { mono_aot_handle_pagefault (info->si_addr); return; @@ -5376,11 +5785,23 @@ SIG_HANDLER_SIGNATURE (mono_sigsegv_signal_handler) if (mono_handle_soft_stack_ovf (jit_tls, ji, ctx, (guint8*)info->si_addr)) return; +#ifdef MONO_ARCH_HAVE_SIGCTX_TO_MONOCTX + /* info->si_addr seems to be NULL on some kernels when handling stack overflows */ + fault_addr = info->si_addr; + if (fault_addr == NULL) { + MonoContext mctx; + + mono_arch_sigctx_to_monoctx (ctx, &mctx); + + fault_addr = MONO_CONTEXT_GET_SP (&mctx); + } +#endif + /* The hard-guard page has been hit: there is not much we can do anymore * Print a hopefully clear message and abort. */ if (jit_tls->stack_size && - ABS ((guint8*)info->si_addr - ((guint8*)jit_tls->end_of_stack - jit_tls->stack_size)) < 32768) { + ABS ((guint8*)fault_addr - ((guint8*)jit_tls->end_of_stack - jit_tls->stack_size)) < 8192 * sizeof (gpointer)) { const char *method; /* we don't do much now, but we can warn the user with a useful message */ fprintf (stderr, "Stack overflow: IP: %p, fault addr: %p\n", mono_arch_ip_from_context (ctx), (gpointer)info->si_addr); @@ -5450,25 +5871,45 @@ mono_jit_create_remoting_trampoline (MonoDomain *domain, MonoMethod *method, Mon return mono_get_addr_from_ftnptr (addr); } -#ifdef MONO_ARCH_HAVE_IMT -static G_GNUC_UNUSED gpointer -mini_get_imt_trampoline (void) -{ - static gpointer tramp = NULL; - if (!tramp) - tramp = mono_create_specific_trampoline (MONO_FAKE_IMT_METHOD, MONO_TRAMPOLINE_JIT, mono_get_root_domain (), NULL); - return tramp; -} -#endif +static gpointer *vtable_trampolines; +static int vtable_trampolines_size; gpointer mini_get_vtable_trampoline (int slot_index) { - static gpointer tramp = NULL; + int index = slot_index + MONO_IMT_SIZE; - if (!tramp) - tramp = mono_create_specific_trampoline (MONO_FAKE_VTABLE_METHOD, MONO_TRAMPOLINE_JIT, mono_get_root_domain (), NULL); - return tramp; + g_assert (slot_index >= - MONO_IMT_SIZE); + if (!vtable_trampolines || slot_index + MONO_IMT_SIZE >= vtable_trampolines_size) { + mono_jit_lock (); + if (!vtable_trampolines || index >= vtable_trampolines_size) { + int new_size; + gpointer new_table; + + new_size = vtable_trampolines_size ? vtable_trampolines_size * 2 : 128; + while (new_size <= index) + new_size *= 2; + new_table = g_new0 (gpointer, new_size); + + if (vtable_trampolines) + memcpy (new_table, vtable_trampolines, vtable_trampolines_size * sizeof (gpointer)); + g_free (vtable_trampolines); + mono_memory_barrier (); + vtable_trampolines = new_table; + vtable_trampolines_size = new_size; + } + mono_jit_unlock (); + } + + if (!vtable_trampolines [index]) + vtable_trampolines [index] = mono_create_specific_trampoline (GUINT_TO_POINTER (slot_index), MONO_TRAMPOLINE_VCALL, mono_get_root_domain (), NULL); + return vtable_trampolines [index]; +} + +static gpointer +mini_get_imt_trampoline (int slot_index) +{ + return mini_get_vtable_trampoline (slot_index - MONO_IMT_SIZE); } static void @@ -5489,6 +5930,8 @@ mini_parse_debug_options (void) debug_options.handle_sigint = TRUE; else if (!strcmp (arg, "keep-delegates")) debug_options.keep_delegates = TRUE; + else if (!strcmp (arg, "reverse-pinvoke-exceptions")) + debug_options.reverse_pinvoke_exceptions = TRUE; else if (!strcmp (arg, "collect-pagefault-stats")) debug_options.collect_pagefault_stats = TRUE; else if (!strcmp (arg, "break-on-unverified")) @@ -5509,9 +5952,11 @@ mini_parse_debug_options (void) debug_options.gen_seq_points = TRUE; else if (!strcmp (arg, "init-stacks")) debug_options.init_stacks = TRUE; + else if (!strcmp (arg, "casts")) + debug_options.better_cast_details = 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', 'dyn-runtime-invoke', 'gdb', 'explicit-null-checks', 'init-stacks'\n"); + fprintf (stderr, "Available options: 'handle-sigint', 'keep-delegates', 'reverse-pinvoke-exceptions', '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); } } @@ -5562,6 +6007,16 @@ mini_get_addr_from_ftnptr (gpointer descr) #endif } +static void +register_jit_stats (void) +{ + mono_counters_register ("Compiled methods", MONO_COUNTER_JIT | MONO_COUNTER_WORD, &mono_jit_stats.methods_compiled); + mono_counters_register ("Methods from AOT", MONO_COUNTER_JIT | MONO_COUNTER_WORD, &mono_jit_stats.methods_aot); + mono_counters_register ("Methods JITted using LLVM", MONO_COUNTER_JIT | MONO_COUNTER_INT, &mono_jit_stats.methods_with_llvm); + mono_counters_register ("Methods JITted using mono JIT", MONO_COUNTER_JIT | MONO_COUNTER_INT, &mono_jit_stats.methods_without_llvm); + mono_counters_register ("Total time spent JITting (sec)", MONO_COUNTER_JIT | MONO_COUNTER_DOUBLE, &mono_jit_stats.jit_time); +} + static void runtime_invoke_info_free (gpointer value); static void @@ -5652,7 +6107,7 @@ mini_init (const char *filename, const char *runtime_version) MONO_PROBE_VES_INIT_BEGIN (); -#ifdef __linux__ +#if defined(__linux__) && !defined(__native_client__) if (access ("/proc/self/maps", F_OK) != 0) { g_print ("Mono requires /proc to be mounted.\n"); exit (1); @@ -5670,6 +6125,14 @@ mini_init (const char *filename, const char *runtime_version) mini_debugger_init (); #endif +#ifdef MINI_HAVE_FAST_TLS + MINI_FAST_TLS_INIT (mono_jit_tls); + MINI_FAST_TLS_INIT (mono_lmf_addr); +#ifdef MONO_ARCH_ENABLE_MONO_LMF_VAR + MINI_FAST_TLS_INIT (mono_lmf); +#endif +#endif + #ifdef MONO_ARCH_HAVE_TLS_GET mono_runtime_set_has_tls_get (MONO_ARCH_HAVE_TLS_GET); #else @@ -5687,14 +6150,15 @@ mini_init (const char *filename, const char *runtime_version) #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; - } + callbacks.get_vtable_trampoline = mini_get_vtable_trampoline; + callbacks.get_imt_trampoline = mini_get_imt_trampoline; } #endif mono_install_callbacks (&callbacks); + + if (getenv ("MONO_DEBUG") != NULL) + mini_parse_debug_options (); mono_arch_cpu_init (); @@ -5704,9 +6168,6 @@ mini_init (const char *filename, const char *runtime_version) mini_gc_init (); - if (getenv ("MONO_DEBUG") != NULL) - mini_parse_debug_options (); - if (getenv ("MONO_XDEBUG")) { char *xdebug_opts = getenv ("MONO_XDEBUG"); mono_xdebug_init (xdebug_opts); @@ -5720,7 +6181,14 @@ mini_init (const char *filename, const char *runtime_version) } #ifdef ENABLE_LLVM - mono_llvm_init (); + if (mono_use_llvm) { + if (!mono_llvm_load (NULL)) { + mono_use_llvm = FALSE; + fprintf (stderr, "Mono Warning: llvm support could not be loaded.\n"); + } + } + if (mono_use_llvm) + mono_llvm_init (); #endif mono_trampolines_init (); @@ -5759,15 +6227,7 @@ 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); @@ -5803,13 +6263,6 @@ mini_init (const char *filename, const char *runtime_version) 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 @@ -5819,6 +6272,9 @@ mini_init (const char *filename, const char *runtime_version) mono_icall_init (); + /* This should come after mono_init () too */ + mini_gc_init (); + mono_add_internal_call ("System.Diagnostics.StackFrame::get_frame_info", ves_icall_get_frame_info); mono_add_internal_call ("System.Diagnostics.StackTrace::get_trace", @@ -5832,8 +6288,9 @@ mini_init (const char *filename, const char *runtime_version) mono_add_internal_call ("Mono.Runtime::mono_runtime_install_handlers", mono_runtime_install_handlers); + mono_create_helper_signatures (); - create_helper_signature (); + register_jit_stats (); #define JIT_CALLS_WORK #ifdef JIT_CALLS_WORK @@ -5851,19 +6308,17 @@ 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); -#endif register_icall (mono_thread_get_undeniable_exception, "mono_thread_get_undeniable_exception", "object", FALSE); register_icall (mono_thread_interruption_checkpoint, "mono_thread_interruption_checkpoint", "void", FALSE); register_icall (mono_thread_force_interruption_checkpoint, "mono_thread_force_interruption_checkpoint", "void", FALSE); register_icall (mono_load_remote_field_new, "mono_load_remote_field_new", "object object ptr ptr", FALSE); register_icall (mono_store_remote_field_new, "mono_store_remote_field_new", "void object ptr ptr object", FALSE); +#if defined(__native_client__) || defined(__native_client_codegen__) + register_icall (mono_nacl_gc, "mono_nacl_gc", "void", TRUE); +#endif /* * NOTE, NOTE, NOTE, NOTE: * when adding emulation for some opcodes, remember to also add a dummy @@ -5934,7 +6389,11 @@ mini_init (const char *filename, const char *runtime_version) mono_register_opcode_emulation (OP_LCONV_TO_R_UN, "__emul_lconv_to_r8_un", "double long", mono_lconv_to_r8_un, FALSE); #endif #ifdef MONO_ARCH_EMULATE_FREM +#if defined(__default_codegen__) mono_register_opcode_emulation (OP_FREM, "__emul_frem", "double double double", fmod, FALSE); +#elif defined(__native_client_codegen__) + mono_register_opcode_emulation (OP_FREM, "__emul_frem", "double double double", mono_fmod, FALSE); +#endif #endif #ifdef MONO_ARCH_SOFT_FLOAT @@ -6019,6 +6478,13 @@ mini_init (const char *filename, const char *runtime_version) 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); + register_icall (mono_resume_unwind, "mono_resume_unwind", "void", TRUE); + + register_icall (mono_gc_wbarrier_value_copy_bitmap, "mono_gc_wbarrier_value_copy_bitmap", "void ptr ptr int int", FALSE); + + register_icall (mono_object_castclass_with_cache, "mono_object_castclass_with_cache", "object object ptr ptr", FALSE); + register_icall (mono_object_isinst_with_cache, "mono_object_isinst_with_cache", "object object ptr ptr", FALSE); + #endif mono_generic_sharing_init (); @@ -6059,10 +6525,7 @@ print_jit_stats (void) { if (mono_jit_stats.enabled) { g_print ("Mono Jit statistics\n"); - g_print ("Compiled methods: %ld\n", mono_jit_stats.methods_compiled); - g_print ("Methods from AOT: %ld\n", mono_jit_stats.methods_aot); g_print ("Methods cache lookup: %ld\n", mono_jit_stats.methods_lookups); - g_print ("Method trampolines: %ld\n", mono_jit_stats.method_trampolines); g_print ("Basic blocks: %ld\n", mono_jit_stats.basic_blocks); g_print ("Max basic blocks: %ld\n", mono_jit_stats.max_basic_blocks); g_print ("Allocated vars: %ld\n", mono_jit_stats.allocate_var); @@ -6115,13 +6578,9 @@ print_jit_stats (void) g_print ("JIT info table lookups: %ld\n", mono_stats.jit_info_table_lookup_count); g_print ("Hazardous pointers: %ld\n", mono_stats.hazardous_pointer_count); -#ifdef HAVE_SGEN_GC g_print ("Minor GC collections: %ld\n", mono_stats.minor_gc_count); -#endif g_print ("Major GC collections: %ld\n", mono_stats.major_gc_count); -#ifdef HAVE_SGEN_GC g_print ("Minor GC time in msecs: %lf\n", (double)mono_stats.minor_gc_time_usecs / 1000.0); -#endif g_print ("Major GC time in msecs: %lf\n", (double)mono_stats.major_gc_time_usecs / 1000.0); if (mono_security_get_mode () == MONO_SECURITY_MODE_CAS) { g_print ("\nDecl security check : %ld\n", mono_jit_stats.cas_declsec_check); @@ -6149,6 +6608,7 @@ mini_cleanup (MonoDomain *domain) #endif #ifndef MONO_CROSS_COMPILE + mono_runtime_shutdown (); /* * mono_runtime_cleanup() and mono_domain_finalize () need to * be called early since they need the execution engine still @@ -6167,6 +6627,8 @@ mini_cleanup (MonoDomain *domain) mono_runtime_cleanup (domain); #endif + free_jit_tls_data (TlsGetValue (mono_jit_tls_id)); + mono_icall_cleanup (); mono_runtime_cleanup_handlers (); @@ -6176,9 +6638,12 @@ mini_cleanup (MonoDomain *domain) mono_debugger_cleanup (); #ifdef ENABLE_LLVM - mono_llvm_cleanup (); + if (mono_use_llvm) + mono_llvm_cleanup (); #endif + mono_aot_cleanup (); + mono_trampolines_cleanup (); mono_unwind_cleanup (); @@ -6188,9 +6653,12 @@ mini_cleanup (MonoDomain *domain) g_hash_table_destroy (jit_icall_name_hash); g_free (emul_opcode_map); g_free (emul_opcode_opcodes); + g_free (vtable_trampolines); mono_arch_cleanup (); + mono_generic_sharing_cleanup (); + mono_cleanup (); mono_trace_cleanup (); @@ -6309,4 +6777,10 @@ mono_cfg_add_try_hole (MonoCompile *cfg, MonoExceptionClause *clause, guint8 *st cfg->try_block_holes = g_slist_append_mempool (cfg->mempool, cfg->try_block_holes, hole); } +void +mono_cfg_set_exception (MonoCompile *cfg, int type) +{ + cfg->exception_type = type; +} + #endif