#include <sys/time.h>
#endif
-#ifdef HAVE_VALGRIND_MEMCHECK_H
-#include <valgrind/memcheck.h>
-#endif
+#include <mono/utils/memcheck.h>
#include <mono/metadata/assembly.h>
#include <mono/metadata/loader.h>
#include <mono/utils/dtrace.h>
#include "mini.h"
+#include "tasklets.h"
#include <string.h>
#include <ctype.h>
#include "trace.h"
#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 */
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;
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;
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 {
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;
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);
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
return OP_STORER8_MEMBASE_REG;
case MONO_TYPE_VALUETYPE:
if (type->data.klass->enumtype) {
- type = type->data.klass->enum_basetype;
+ type = mono_class_enum_basetype (type->data.klass);
goto handle_enum;
}
if (MONO_CLASS_IS_SIMD (cfg, mono_class_from_mono_type (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:
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;
cfg->varinfo [num] = inst;
MONO_INIT_VARINFO (&cfg->vars [num], num);
+ MONO_VARINFO (cfg, num)->vreg = vreg;
if (vreg != -1)
set_vreg_to_inst (cfg, vreg, inst);
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);
res->call_convention = MONO_CALL_VARARG;
#endif
-#ifdef PLATFORM_WIN32
+#ifdef TARGET_WIN32
res->call_convention = MONO_CALL_C;
#endif
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;
}
}
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)
{
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
* We use the lock on the root domain instead of the JIT lock to protect
* callinfo->trampoline, since we do a lot of stuff inside the critical section.
*/
+ mono_loader_lock (); /*FIXME mono_compile_method requires the loader lock, by large.*/
mono_domain_lock (domain);
if (callinfo->trampoline) {
mono_domain_unlock (domain);
+ mono_loader_unlock ();
return callinfo->trampoline;
}
callinfo->trampoline = trampoline;
mono_domain_unlock (domain);
+ mono_loader_unlock ();
return callinfo->trampoline;
}
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)));
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:
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:
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];
}
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;
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);
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];
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:
#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];
}
*/
}
- if (cfg->disable_reuse_stack_slots)
+ if (!reuse_slot)
slot = 0xffffff;
if (slot == 0xffffff) {
* 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.
#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;
#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);
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;
}
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)
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);
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
*
* 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
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));
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:
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);
(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;
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:
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: {
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 {
- mono_domain_lock (domain);
- if (mono_aot_only)
+ if (mono_aot_only) {
jump_table = mono_domain_alloc (domain, sizeof (gpointer) * patch_info->data.table->table_size);
- else
- jump_table = mono_code_manager_reserve (domain->code_mp, sizeof (gpointer) * patch_info->data.table->table_size);
- mono_domain_unlock (domain);
+ } else {
+ jump_table = mono_domain_code_reserve (domain, sizeof (gpointer) * patch_info->data.table->table_size);
+ }
}
for (i = 0; i < patch_info->data.table->table_size; i++)
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:
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 ();
}
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)
{
mono_arch_create_vars (cfg);
}
+#endif /* #ifndef DISABLE_JIT */
+
void
mono_print_code (MonoCompile *cfg, const char* msg)
{
mono_print_bb (bb, msg);
}
-static MonoGenericInst*
-get_object_generic_inst (int type_argc)
+#ifndef DISABLE_JIT
+
+static void
+mono_postprocess_patches (MonoCompile *cfg)
{
- MonoType **type_argv;
+ MonoJumpInfo *patch_info;
int i;
- type_argv = alloca (sizeof (MonoType*) * type_argc);
+ for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
+ switch (patch_info->type) {
+ case MONO_PATCH_INFO_ABS: {
+ MonoJitICallInfo *info = mono_find_jit_icall_by_addr (patch_info->data.target);
- for (i = 0; i < type_argc; ++i)
- type_argv [i] = &mono_defaults.object_class->byval_arg;
+ /*
+ * Change patches of type MONO_PATCH_INFO_ABS into patches describing the
+ * absolute address.
+ */
+ if (info) {
+ //printf ("TEST %s %p\n", info->name, patch_info->data.target);
+ // FIXME: CLEAN UP THIS MESS.
+ if ((cfg->method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE) &&
+ strstr (cfg->method->name, info->name)) {
+ /*
+ * This is an icall wrapper, and this is a call to the
+ * wrapped function.
+ */
+ if (cfg->compile_aot) {
+ patch_info->type = MONO_PATCH_INFO_JIT_ICALL_ADDR;
+ patch_info->data.name = info->name;
+ }
+ } else {
+ /* for these array methods we currently register the same function pointer
+ * since it's a vararg function. But this means that mono_find_jit_icall_by_addr ()
+ * will return the incorrect one depending on the order they are registered.
+ * See tests/test-arr.cs
+ */
+ if (strstr (info->name, "ves_array_new_va_") == NULL && strstr (info->name, "ves_array_element_address_") == NULL) {
+ patch_info->type = MONO_PATCH_INFO_INTERNAL_METHOD;
+ patch_info->data.name = info->name;
+ }
+ }
+ }
- return mono_metadata_get_generic_inst (type_argc, type_argv);
-}
+ if (patch_info->type == MONO_PATCH_INFO_ABS) {
+ if (cfg->abs_patches) {
+ MonoJumpInfo *abs_ji = g_hash_table_lookup (cfg->abs_patches, patch_info->data.target);
+ if (abs_ji) {
+ patch_info->type = abs_ji->type;
+ patch_info->data.target = abs_ji->data.target;
+ }
+ }
+ }
-static MonoGenericContext
-construct_object_context_for_method (MonoMethod *method)
-{
- MonoGenericContext object_context;
+ break;
+ }
+ case MONO_PATCH_INFO_SWITCH: {
+ gpointer *table;
+ 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);
+ }
- g_assert (method->wrapper_type == MONO_WRAPPER_NONE);
- g_assert (!method->klass->generic_class);
- if (method->klass->generic_container) {
- int type_argc = method->klass->generic_container->type_argc;
+ for (i = 0; i < patch_info->data.table->table_size; i++) {
+ /* Might be NULL if the switch is eliminated */
+ if (patch_info->data.table->table [i]) {
+ g_assert (patch_info->data.table->table [i]->native_offset);
+ table [i] = GINT_TO_POINTER (patch_info->data.table->table [i]->native_offset);
+ } else {
+ table [i] = NULL;
+ }
+ }
+ patch_info->data.table->table = (MonoBasicBlock**)table;
+ break;
+ }
+ case MONO_PATCH_INFO_METHOD_JUMP: {
+ GSList *list;
+ MonoDomain *domain = cfg->domain;
+ unsigned char *ip = cfg->native_code + patch_info->ip.i;
- object_context.class_inst = get_object_generic_inst (type_argc);
- } else {
- object_context.class_inst = NULL;
+ mono_domain_lock (domain);
+ if (!domain_jit_info (domain)->jump_target_hash)
+ domain_jit_info (domain)->jump_target_hash = g_hash_table_new (NULL, NULL);
+ list = g_hash_table_lookup (domain_jit_info (domain)->jump_target_hash, patch_info->data.method);
+ list = g_slist_prepend (list, ip);
+ g_hash_table_insert (domain_jit_info (domain)->jump_target_hash, patch_info->data.method, list);
+ mono_domain_unlock (domain);
+ break;
+ }
+ default:
+ /* do nothing */
+ break;
+ }
}
+}
- if (mini_method_get_context (method)->method_inst) {
- int type_argc = mini_method_get_context (method)->method_inst->type_argc;
+static void
+collect_pred_seq_points (MonoBasicBlock *bb, MonoInst *ins, GSList **next, int depth)
+{
+ int i;
+ MonoBasicBlock *in_bb;
- object_context.method_inst = get_object_generic_inst (type_argc);
- } else {
- object_context.method_inst = NULL;
+ 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);
+ }
}
+}
- g_assert (object_context.class_inst || object_context.method_inst);
+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;
- return object_context;
-}
+ if (!cfg->seq_points)
+ return;
-#ifndef DISABLE_JIT
+ 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)
{
- MonoJumpInfo *patch_info;
MonoBasicBlock *bb;
- int i, max_epilog_size;
+ int max_epilog_size;
guint8 *code;
for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
#ifdef MONO_ARCH_HAVE_UNWIND_TABLE
unwindlen = mono_arch_unwindinfo_get_size (cfg->arch.unwindinfo);
#endif
- mono_domain_lock (cfg->domain);
- code = mono_code_manager_reserve (cfg->domain->code_mp, cfg->code_size + unwindlen);
- mono_domain_unlock (cfg->domain);
+ code = mono_domain_code_reserve (cfg->domain, cfg->code_size + unwindlen);
}
memcpy (code, cfg->native_code, cfg->code_len);
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: {
- MonoJitICallInfo *info = mono_find_jit_icall_by_addr (patch_info->data.target);
-
- /*
- * Change patches of type MONO_PATCH_INFO_ABS into patches describing the
- * absolute address.
- */
- if (info) {
- //printf ("TEST %s %p\n", info->name, patch_info->data.target);
- // FIXME: CLEAN UP THIS MESS.
- if ((cfg->method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE) &&
- strstr (cfg->method->name, info->name)) {
- /*
- * This is an icall wrapper, and this is a call to the
- * wrapped function.
- */
- if (cfg->compile_aot) {
- patch_info->type = MONO_PATCH_INFO_JIT_ICALL_ADDR;
- patch_info->data.name = info->name;
- }
- } else {
- /* for these array methods we currently register the same function pointer
- * since it's a vararg function. But this means that mono_find_jit_icall_by_addr ()
- * will return the incorrect one depending on the order they are registered.
- * See tests/test-arr.cs
- */
- if (strstr (info->name, "ves_array_new_va_") == NULL && strstr (info->name, "ves_array_element_address_") == NULL) {
- patch_info->type = MONO_PATCH_INFO_INTERNAL_METHOD;
- patch_info->data.name = info->name;
- }
- }
- }
-
- if (patch_info->type == MONO_PATCH_INFO_ABS) {
- if (cfg->abs_patches) {
- MonoJumpInfo *abs_ji = g_hash_table_lookup (cfg->abs_patches, patch_info->data.target);
- if (abs_ji) {
- patch_info->type = abs_ji->type;
- patch_info->data.target = abs_ji->data.target;
- }
- }
- }
-
- break;
- }
- case MONO_PATCH_INFO_SWITCH: {
- gpointer *table;
- if (cfg->method->dynamic) {
- table = mono_code_manager_reserve (cfg->dynamic_info->code_mp, sizeof (gpointer) * patch_info->data.table->table_size);
- } else {
- mono_domain_lock (cfg->domain);
- table = mono_code_manager_reserve (cfg->domain->code_mp, sizeof (gpointer) * patch_info->data.table->table_size);
- mono_domain_unlock (cfg->domain);
- }
-
- for (i = 0; i < patch_info->data.table->table_size; i++) {
- /* Might be NULL if the switch is eliminated */
- if (patch_info->data.table->table [i]) {
- g_assert (patch_info->data.table->table [i]->native_offset);
- table [i] = GINT_TO_POINTER (patch_info->data.table->table [i]->native_offset);
- } else {
- table [i] = NULL;
- }
- }
- patch_info->data.table->table = (MonoBasicBlock**)table;
- break;
- }
- case MONO_PATCH_INFO_METHOD_JUMP: {
- GSList *list;
- MonoDomain *domain = cfg->domain;
- unsigned char *ip = cfg->native_code + patch_info->ip.i;
-
- mono_domain_lock (domain);
- if (!domain_jit_info (domain)->jump_target_hash)
- domain_jit_info (domain)->jump_target_hash = g_hash_table_new (NULL, NULL);
- list = g_hash_table_lookup (domain_jit_info (domain)->jump_target_hash, patch_info->data.method);
- list = g_slist_prepend (list, ip);
- g_hash_table_insert (domain_jit_info (domain)->jump_target_hash, patch_info->data.method, list);
- mono_domain_unlock (domain);
- break;
- }
- default:
- /* do nothing */
- break;
- }
- }
+ mono_postprocess_patches (cfg);
#ifdef VALGRIND_JIT_REGISTER_MAP
if (valgrind_register){
if (cfg->method->dynamic) {
mono_code_manager_commit (cfg->dynamic_info->code_mp, cfg->native_code, cfg->code_size, cfg->code_len);
} else {
- mono_domain_lock (cfg->domain);
- mono_code_manager_commit (cfg->domain->code_mp, cfg->native_code, cfg->code_size, cfg->code_len);
- mono_domain_unlock (cfg->domain);
+ 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
}
}
+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
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)
mono_stats.generics_unsharable_methods++;
}
+ try_llvm = TRUE;
+
+#ifndef ENABLE_LLVM
+ try_llvm = FALSE;
+#endif
+
restart_compile:
if (try_generic_shared) {
MonoMethod *declaring_method;
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;
*/
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;
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));
*/
//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
*/
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;
}
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
+ * <something which throws>
+ * 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))
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);
/* 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) {
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)
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
}
#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
}
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
}
#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) {
}
#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 ())
}
#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);
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);
}
}
- //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) {
}
}
- 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)) {
+#ifdef ENABLE_LLVM
+ char *nm;
- if (cfg->generic_sharing_context)
- generic_info_size = sizeof (MonoGenericJitInfo);
- else
- generic_info_size = 0;
-
- if (cfg->method->dynamic) {
- jinfo = g_malloc0 (sizeof (MonoJitInfo) + (header->num_clauses * sizeof (MonoJitExceptionInfo)) +
- generic_info_size);
- } else {
- /* we access cfg->domain->mp */
- mono_domain_lock (cfg->domain);
- jinfo = mono_domain_alloc0 (cfg->domain, sizeof (MonoJitInfo) +
- (header->num_clauses * sizeof (MonoJitExceptionInfo)) +
- generic_info_size);
- mono_domain_unlock (cfg->domain);
- }
-
- if (cfg->generic_sharing_context) {
- MonoGenericContext object_context = construct_object_context_for_method (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);
+ /* 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->generic_sharing_context = cfg->generic_sharing_context;
+ if (cfg->flags & MONO_CFG_HAS_ARRAY_ACCESS)
+ mono_decompose_array_access_opts (cfg);
- /*
- * Non-generic static methods only get a "this" info
- * if they use the rgctx variable (which they are
- * forced to if they have any open catch clauses).
- */
- if (cfg->rgctx_var ||
- (!(method_to_compile->flags & METHOD_ATTRIBUTE_STATIC) &&
- !mini_method_get_context (method_to_compile)->method_inst &&
- !method_to_compile->klass->valuetype)) {
- 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 (!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;
-
- //g_print ("this in reg %d\n", 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);
-
- gi->this_in_reg = 0;
- gi->this_reg = inst->inst_basereg;
- gi->this_offset = inst->inst_offset;
+ InterlockedIncrement (&methods_with_llvm);
- //g_print ("this at offset %d from reg %d\n", gi->this_offset, gi->this_reg);
- }
- } else {
- gi->has_this = 0;
+ 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);
}
- 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;
}
-
- cfg->jit_info = jinfo;
-#if defined(__arm__)
- mono_arch_fixup_jinfo (cfg);
#endif
- mono_save_xdebug_info (method_to_register, jinfo->code_start, jinfo->code_size, cfg->args, cfg->locals, cfg->unwind_ops);
+ 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);
}
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;
}
#else
-MonoCompile*
-mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gboolean run_cctors, gboolean compile_aot, int parts)
-{
- g_assert_not_reached ();
- return NULL;
-}
-
-#endif /* DISABLE_JIT */
-
-MonoJitInfo*
-mono_domain_lookup_shared_generic (MonoDomain *domain, MonoMethod *open_method)
-{
- static gboolean inited = FALSE;
- static int lookups = 0;
- static int failed_lookups = 0;
-
- MonoGenericContext object_context;
- MonoMethod *object_method;
- MonoJitInfo *ji;
-
- object_context = construct_object_context_for_method (open_method);
- object_method = mono_class_inflate_generic_method (open_method, &object_context);
-
- ji = mono_internal_hash_table_lookup (&domain->jit_code_hash, object_method);
- if (ji && !ji->has_generic_jit_info)
- ji = NULL;
-
- if (!inited) {
- mono_counters_register ("Shared generic lookups", MONO_COUNTER_INT|MONO_COUNTER_GENERICS, &lookups);
- mono_counters_register ("Failed shared generic lookups", MONO_COUNTER_INT|MONO_COUNTER_GENERICS, &failed_lookups);
- inited = TRUE;
- }
-
- ++lookups;
- if (!ji)
- ++failed_lookups;
-
- return ji;
+MonoCompile*
+mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gboolean run_cctors, gboolean compile_aot, int parts)
+{
+ g_assert_not_reached ();
+ return NULL;
}
+#endif /* DISABLE_JIT */
+
static MonoJitInfo*
lookup_generic_method (MonoDomain *domain, MonoMethod *method)
{
{
MonoJitInfo *info;
+ mono_loader_lock (); /*FIXME lookup_method_inner acquired it*/
mono_domain_jit_code_hash_lock (domain);
info = lookup_method_inner (domain, method);
mono_domain_jit_code_hash_unlock (domain);
+ mono_loader_unlock ();
return info;
}
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);
vtable = mono_class_vtable (domain, method->klass);
g_assert (vtable);
mono_runtime_class_init (vtable);
+
return code;
}
}
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);
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);
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);
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 ();
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);
/* Check if some other thread already did the job. In this case, we can
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) {
}
mono_domain_unlock (target_domain);
+ mono_loader_unlock ();
vtable = mono_class_vtable (target_domain, method->klass);
if (!vtable) {
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;
/*
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 ();
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
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
}
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;
/* 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
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);
- invoke = mono_marshal_get_runtime_invoke (method);
- runtime_invoke = mono_jit_compile_method (invoke);
-
- /* We need this here becuase mono_marshal_get_runtime_invoke can be 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_domain_lock (domain);
+ info = g_hash_table_lookup (domain_info->runtime_invoke_hash, method);
+ mono_domain_unlock (domain);
- 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.
+ 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;
+ }
+ }
+
+ info = g_new0 (RuntimeInvokeInfo, 1);
+
+ invoke = mono_marshal_get_runtime_invoke (method, FALSE);
+ 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.
*/
- compiled_method = NULL;
- } else {
- compiled_method = mono_jit_compile_method (to_compile);
+#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);
+ }
+
+ 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.
+ */
+ 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);
+ }
+
+ /* 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;
+ 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))
exc = mono_get_exception_arithmetic ();
#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);
}
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_domain_get () || !jit_tls) {
+ if (mono_chain_signal (SIG_HANDLER_PARAMS))
+ return;
mono_handle_native_sigsegv (SIGSEGV, ctx);
+ }
ji = mono_jit_info_table_find (mono_domain_get (), mono_arch_ip_from_context (ctx));
fprintf (stderr, "At %s\n", method);
_exit (1);
} else {
+ /* The original handler might not like that it is executed on an altstack... */
+ if (!ji && mono_chain_signal (SIG_HANDLER_PARAMS))
+ return;
+
mono_arch_handle_altstack_exception (ctx, info->si_addr, FALSE);
}
#else
if (!ji) {
+ if (mono_chain_signal (SIG_HANDLER_PARAMS))
+ return;
+
mono_handle_native_sigsegv (SIGSEGV, ctx);
}
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);
}
}
#ifdef MONO_ARCH_HAVE_IMT
-static gpointer
+static G_GNUC_UNUSED gpointer
mini_get_imt_trampoline (void)
{
static gpointer tramp = NULL;
}
#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)
debug_options.break_on_unverified = TRUE;
else if (!strcmp (arg, "no-gdb-backtrace"))
debug_options.no_gdb_backtrace = TRUE;
+ else if (!strcmp (arg, "suspend-on-sigsegv"))
+ 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'\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);
}
}
{
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)
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;
}
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)
{
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;
mini_init (const char *filename, const char *runtime_version)
{
MonoDomain *domain;
+ MonoRuntimeCallbacks callbacks;
MONO_PROBE_VES_INIT_BEGIN ();
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 ();
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
#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);
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
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);
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 ();
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
#endif
mono_profiler_runtime_initialized ();
-
+
MONO_PROBE_VES_INIT_END ();
return domain;
g_print ("VTable data size: %ld\n", mono_stats.class_vtable_size);
g_print ("Mscorlib mempool size: %d\n", mono_mempool_get_allocated (mono_defaults.corlib->mempool));
- g_print ("\nGeneric instances: %ld\n", mono_stats.generic_instance_count);
- g_print ("Initialized classes: %ld\n", mono_stats.generic_class_count);
- g_print ("Inflated methods: %ld / %ld\n", mono_stats.inflated_method_count_2,
- mono_stats.inflated_method_count);
+ g_print ("\nInitialized classes: %ld\n", mono_stats.generic_class_count);
g_print ("Inflated types: %ld\n", mono_stats.inflated_type_count);
- g_print ("Generics metadata size: %ld\n", mono_stats.generics_metadata_size);
g_print ("Generics virtual invokes: %ld\n", mono_jit_stats.generic_virtual_invocations);
g_print ("Sharable generic methods: %ld\n", mono_stats.generics_sharable_methods);
#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
* 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 ();
mono_debugger_cleanup ();
+#ifdef ENABLE_LLVM
+ mono_llvm_cleanup ();
+#endif
+
mono_trampolines_cleanup ();
mono_unwind_cleanup ();
default_opt_set = TRUE;
}
+void
+mono_disable_optimizations (guint32 opts)
+{
+ default_opt &= ~opts;
+}
+
/*
* mono_get_runtime_build_info:
*
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
}
mono_compile_method (method);
if (strcmp (method->name, "Finalize") == 0) {
- invoke = mono_marshal_get_runtime_invoke (method);
+ invoke = mono_marshal_get_runtime_invoke (method, FALSE);
mono_compile_method (invoke);
}
if (method->klass->marshalbyref && mono_method_signature (method)->hasthis) {
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