#include "aliasing.h"
+#define BRANCH_COST 100
+#define INLINE_LENGTH_LIMIT 20
+#define INLINE_FAILURE do {\
+ if ((cfg->method != method) && (method->wrapper_type == MONO_WRAPPER_NONE))\
+ goto inline_failure;\
+ } while (0)
+
/*
* this is used to determine when some branch optimizations are possible: we exclude FP compares
* because they have weird semantics with NaNs.
#endif
}
+typedef struct {
+ void *ip;
+ MonoMethod *method;
+} FindTrampUserData;
+
+static void
+find_tramp (gpointer key, gpointer value, gpointer user_data)
+{
+ FindTrampUserData *ud = (FindTrampUserData*)user_data;
+
+ if (value == ud->ip)
+ ud->method = (MonoMethod*)key;
+}
+
/* debug function */
G_GNUC_UNUSED static char*
get_method_from_ip (void *ip)
char *source;
char *res;
MonoDomain *domain = mono_domain_get ();
+ FindTrampUserData user_data;
ji = mono_jit_info_table_find (domain, ip);
if (!ji) {
- return NULL;
+ user_data.ip = ip;
+ user_data.method = NULL;
+ mono_domain_lock (domain);
+ g_hash_table_foreach (domain->jit_trampoline_hash, find_tramp, &user_data);
+ mono_domain_unlock (domain);
+ if (user_data.method) {
+ char *mname = mono_method_full_name (user_data.method, TRUE);
+ res = g_strdup_printf ("<%p - JIT trampoline for %s>", ip, mname);
+ g_free (mname);
+ return res;
+ }
+ else
+ return NULL;
}
method = mono_method_full_name (ji->method, TRUE);
source = mono_debug_source_location_from_address (ji->method, (guint32)((guint8*)ip - (guint8*)ji->code_start), NULL, domain);
}
/* debug function */
-G_GNUC_UNUSED static void
-print_method_from_ip (void *ip)
+void
+mono_print_method_from_ip (void *ip)
{
MonoJitInfo *ji;
char *method;
char *source;
MonoDomain *domain = mono_domain_get ();
+ FindTrampUserData user_data;
ji = mono_jit_info_table_find (domain, ip);
if (!ji) {
- g_print ("No method at %p\n", ip);
+ user_data.ip = ip;
+ user_data.method = NULL;
+ mono_domain_lock (domain);
+ g_hash_table_foreach (domain->jit_trampoline_hash, find_tramp, &user_data);
+ mono_domain_unlock (domain);
+ if (user_data.method) {
+ char *mname = mono_method_full_name (user_data.method, TRUE);
+ printf ("IP %p is a JIT trampoline for %s\n", ip, mname);
+ g_free (mname);
+ }
+ else
+ g_print ("No method at %p\n", ip);
return;
}
method = mono_method_full_name (ji->method, TRUE);
g_free (source);
g_free (method);
}
-
-G_GNUC_UNUSED void
-mono_print_method_from_ip (void *ip)
-{
- print_method_from_ip (ip);
-}
/*
* mono_method_same_domain:
void
mono_unlink_bblock (MonoCompile *cfg, MonoBasicBlock *from, MonoBasicBlock* to)
{
- MonoBasicBlock **newa;
int i, pos;
gboolean found;
}
}
if (found) {
- newa = mono_mempool_alloc (cfg->mempool, sizeof (gpointer) * (from->out_count - 1));
pos = 0;
for (i = 0; i < from->out_count; ++i) {
if (from->out_bb [i] != to)
- newa [pos ++] = from->out_bb [i];
+ from->out_bb [pos ++] = from->out_bb [i];
}
+ g_assert (pos == from->out_count - 1);
from->out_count--;
- from->out_bb = newa;
}
found = FALSE;
}
}
if (found) {
- newa = mono_mempool_alloc (cfg->mempool, sizeof (gpointer) * (to->in_count - 1));
pos = 0;
for (i = 0; i < to->in_count; ++i) {
if (to->in_bb [i] != from)
- newa [pos ++] = to->in_bb [i];
+ to->in_bb [pos ++] = to->in_bb [i];
}
+ g_assert (pos == to->in_count - 1);
to->in_count--;
- to->in_bb = newa;
}
}
{
MonoClass *klass;
+ inst->klass = klass = mono_class_from_mono_type (type);
if (type->byref) {
inst->type = STACK_MP;
return;
}
- klass = mono_class_from_mono_type (type);
-
handle_enum:
switch (type->type) {
case MONO_TYPE_VOID:
if (header->code_size < atoi (getenv ("MONO_INLINELIMIT"))) {
return TRUE;
}
- } else if (header->code_size < 20)
+ } else if (header->code_size < INLINE_LENGTH_LIMIT)
return TRUE;
return FALSE;
return addr;
}
-static MonoJitICallInfo **emul_opcode_map = NULL;
+MonoJitICallInfo **emul_opcode_map = NULL;
-static inline MonoJitICallInfo *
+MonoJitICallInfo *
mono_find_jit_opcode_emulation (int opcode)
{
if (emul_opcode_map)
if (friend->public_key_token [0]) {
if (!accessing->aname.public_key_token [0])
continue;
- if (strcmp (friend->public_key_token, accessing->aname.public_key_token))
+ if (strcmp ((char*)friend->public_key_token, (char*)accessing->aname.public_key_token))
continue;
}
return TRUE;
dont_verify = method->klass->image->assembly->corlib_internal? TRUE: FALSE;
dont_verify |= method->wrapper_type == MONO_WRAPPER_XDOMAIN_INVOKE;
dont_verify |= method->wrapper_type == MONO_WRAPPER_XDOMAIN_DISPATCH;
+ dont_verify |= method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE; /* bug #77896 */
/* still some type unsefety issues in marshal wrappers... (unknown is PtrToStructure) */
dont_verify_stloc = method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE;
MonoInst *iargs [3];
g_assert (mono_method_signature (cmethod)->is_inflated);
+ /* Prevent inlining of methods that contain indirect calls */
+ INLINE_FAILURE;
this_temp = mono_compile_create_var (cfg, type_from_stack_type (sp [0]), OP_LOCAL);
this_temp->cil_code = ip;
if ((ins_flag & MONO_INST_TAILCALL) && cmethod && (*ip == CEE_CALL) &&
(mono_metadata_signature_equal (mono_method_signature (method), mono_method_signature (cmethod)))) {
int i;
+ /* Prevent inlining of methods with tail calls (the call stack would be altered) */
+ INLINE_FAILURE;
/* FIXME: This assumes the two methods has the same number and type of arguments */
for (i = 0; i < n; ++i) {
/* Check if argument is the same */
if ((cmethod->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
(cmethod->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)) {
+ /* Prevent inlining of methods that call wrappers */
+ INLINE_FAILURE;
cmethod = mono_marshal_get_native_wrapper (cmethod);
allways = TRUE;
}
gboolean has_vtargs = FALSE;
int i;
+ /* Prevent inlining of methods with tail calls (the call stack would be altered) */
+ INLINE_FAILURE;
/* keep it simple */
for (i = fsig->param_count - 1; i >= 0; i--) {
if (MONO_TYPE_ISSTRUCT (mono_method_signature (cmethod)->params [i]))
}
if (*ip == CEE_CALLI) {
-
+ /* Prevent inlining of methods with indirect calls */
+ INLINE_FAILURE;
if ((temp = mono_emit_calli (cfg, bblock, fsig, sp, addr, ip)) != -1) {
NEW_TEMPLOAD (cfg, *sp, temp);
sp++;
- }
-
+ }
} else if (array_rank) {
MonoInst *addr;
}
} else {
+ /* Prevent inlining of methods which call other methods */
+ INLINE_FAILURE;
if (ip_in_bb (cfg, bblock, ip + 5)
&& (!MONO_TYPE_ISSTRUCT (fsig->ret))
&& (!MONO_TYPE_IS_VOID (fsig->ret) || cmethod->string_ctor)
sp = stack_start;
}
start_new_bblock = 1;
- inline_costs += 10;
+ inline_costs += BRANCH_COST;
break;
case CEE_BRFALSE_S:
case CEE_BRTRUE_S:
handle_stack_args (cfg, bblock, stack_start, sp - stack_start);
sp = stack_start;
}
- inline_costs += 10;
+ inline_costs += BRANCH_COST;
break;
case CEE_BEQ_S:
case CEE_BGE_S:
handle_stack_args (cfg, bblock, stack_start, sp - stack_start);
sp = stack_start;
}
- inline_costs += 10;
+ inline_costs += BRANCH_COST;
break;
case CEE_BR:
CHECK_OPSIZE (5);
sp = stack_start;
}
start_new_bblock = 1;
- inline_costs += 10;
+ inline_costs += BRANCH_COST;
break;
case CEE_BRFALSE:
case CEE_BRTRUE:
handle_stack_args (cfg, bblock, stack_start, sp - stack_start);
sp = stack_start;
}
- inline_costs += 10;
+ inline_costs += BRANCH_COST;
break;
case CEE_BEQ:
case CEE_BGE:
handle_stack_args (cfg, bblock, stack_start, sp - stack_start);
sp = stack_start;
}
- inline_costs += 10;
+ inline_costs += BRANCH_COST;
break;
case CEE_SWITCH:
CHECK_OPSIZE (5);
}
/* Needed by the code generated in inssel.brg */
mono_get_got_var (cfg);
- inline_costs += 20;
+ inline_costs += (BRANCH_COST * 2);
break;
case CEE_LDIND_I1:
case CEE_LDIND_U1:
break;
} else {
+ /* Prevent inlining of methods which call other methods */
+ INLINE_FAILURE;
mono_emit_method_call_spilled (cfg, bblock, cmethod, fsig, sp, ip, callvirt_this_arg);
}
} else {
+ /* Prevent inlining of methods which call other methods */
+ INLINE_FAILURE;
/* now call the actual ctor */
mono_emit_method_call_spilled (cfg, bblock, cmethod, fsig, sp, ip, callvirt_this_arg);
}
ins->type = STACK_MP;
if (*ip == CEE_LDFLDA) {
+ ins->klass = mono_class_from_mono_type (field->type);
*sp++ = ins;
} else {
MonoInst *load;
/* FIXME: mark instructions for use in SSA */
if (*ip == CEE_LDSFLDA) {
+ ins->klass = mono_class_from_mono_type (field->type);
*sp++ = ins;
} else if (*ip == CEE_STSFLD) {
MonoInst *store;
for (i = 0; i < header->num_clauses; ++i) {
MonoExceptionClause *clause = &header->clauses [i];
- if (MONO_OFFSET_IN_HANDLER (clause, ip - header->code) && (clause->flags == MONO_EXCEPTION_CLAUSE_NONE) && (ip - header->code + ((*ip == CEE_LEAVE) ? 5 : 2)) == (clause->handler_offset + clause->handler_len)) {
+ /*
+ * Use <= in the final comparison to handle clauses with multiple
+ * leave statements, like in bug #78024.
+ * The ordering of the exception clauses guarantees that we find the
+ * innermost clause.
+ */
+ if (MONO_OFFSET_IN_HANDLER (clause, ip - header->code) && (clause->flags == MONO_EXCEPTION_CLAUSE_NONE) && (ip - header->code + ((*ip == CEE_LEAVE) ? 5 : 2)) <= (clause->handler_offset + clause->handler_len)) {
int temp;
MonoInst *load;
}
}
-static void
-replace_or_add_in_block (MonoCompile *cfg, MonoBasicBlock *bb, MonoBasicBlock *orig, MonoBasicBlock *repl)
-{
- gboolean found = FALSE;
- int i;
-
- for (i = 0; i < bb->in_count; i++) {
- MonoBasicBlock *ib = bb->in_bb [i];
- if (ib == orig) {
- if (!repl) {
- if (bb->in_count > 1) {
- bb->in_bb [i] = bb->in_bb [bb->in_count - 1];
- }
- bb->in_count--;
- } else {
- bb->in_bb [i] = repl;
- }
- found = TRUE;
- }
- }
-
- if (! found) {
- MonoBasicBlock **new_in_bb = mono_mempool_alloc (cfg->mempool, sizeof (MonoBasicBlock*) * (bb->in_count + 1));
- for (i = 0; i < bb->in_count; i++) {
- new_in_bb [i] = bb->in_bb [i];
- }
- new_in_bb [i] = repl;
- bb->in_count++;
- bb->in_bb = new_in_bb;
- }
-}
-
-
static void
replace_out_block_in_code (MonoBasicBlock *bb, MonoBasicBlock *orig, MonoBasicBlock *repl) {
MonoInst *inst;
printf ("remove_block_if_useless %s, removed BB%d\n", mono_method_full_name (cfg->method, TRUE), bb->block_num);
}
- for (i = 0; i < bb->in_count; i++) {
- MonoBasicBlock *in_bb = bb->in_bb [i];
- replace_out_block (in_bb, bb, target_bb);
+ /* unlink_bblock () modifies the bb->in_bb array so can't use a for loop here */
+ while (bb->in_count) {
+ MonoBasicBlock *in_bb = bb->in_bb [0];
+ mono_unlink_bblock (cfg, in_bb, bb);
+ link_bblock (cfg, in_bb, target_bb);
replace_out_block_in_code (in_bb, bb, target_bb);
- if (bb->in_count == 1) {
- replace_in_block (target_bb, bb, in_bb);
- } else {
- replace_or_add_in_block (cfg, target_bb, bb, in_bb);
- }
}
-
+
mono_unlink_bblock (cfg, bb, target_bb);
if ((previous_bb != cfg->bb_entry) &&
*/
bb->last_ins->opcode = CEE_BR;
bb->last_ins->inst_target_bb = taken_branch_target;
- replace_out_block (bb, untaken_branch_target, NULL);
- replace_in_block (untaken_branch_target, bb, NULL);
+ mono_unlink_bblock (cfg, bb, untaken_branch_target);
changed = TRUE;
continue;
}
bb->block_num, bbn->block_num, bbn->code->inst_target_bb->block_num,
bbn->code->opcode);
- bb->last_ins->inst_true_bb = bbn->code->inst_target_bb;
+ /*
+ * Unlink, then relink bblocks to avoid various
+ * tricky situations when the two targets of the branch
+ * are equal, or will become equal after the change.
+ */
+ mono_unlink_bblock (cfg, bb, bb->last_ins->inst_true_bb);
+ mono_unlink_bblock (cfg, bb, bb->last_ins->inst_false_bb);
- replace_in_block (bbn, bb, NULL);
- replace_out_block (bb, bbn, bbn->code->inst_target_bb);
+ bb->last_ins->inst_true_bb = bbn->code->inst_target_bb;
- link_bblock (cfg, bb, bbn->code->inst_target_bb);
+ link_bblock (cfg, bb, bb->last_ins->inst_true_bb);
+ link_bblock (cfg, bb, bb->last_ins->inst_false_bb);
changed = TRUE;
continue;
bb->block_num, bbn->block_num, bbn->code->inst_target_bb->block_num,
bbn->code->opcode);
- bb->last_ins->inst_false_bb = bbn->code->inst_target_bb;
+ mono_unlink_bblock (cfg, bb, bb->last_ins->inst_true_bb);
+ mono_unlink_bblock (cfg, bb, bb->last_ins->inst_false_bb);
- replace_in_block (bbn, bb, NULL);
- replace_out_block (bb, bbn, bbn->code->inst_target_bb);
+ bb->last_ins->inst_false_bb = bbn->code->inst_target_bb;
- link_bblock (cfg, bb, bbn->code->inst_target_bb);
+ link_bblock (cfg, bb, bb->last_ins->inst_true_bb);
+ link_bblock (cfg, bb, bb->last_ins->inst_false_bb);
changed = TRUE;
continue;
*/
;
else {
- patch_info->type = MONO_PATCH_INFO_INTERNAL_METHOD;
- patch_info->data.name = info->name;
+ /* 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;
+ }
}
}
else {
if (cfg->verbose_level > 0) {
char* nm = mono_method_full_name (cfg->method, TRUE);
- g_print ("Method %s emitted at %p to %p [%s]\n",
+ g_print ("Method %s emitted at %p to %p (code length %d) [%s]\n",
nm,
- cfg->native_code, cfg->native_code + cfg->code_len, cfg->domain->friendly_name);
+ cfg->native_code, cfg->native_code + cfg->code_len, cfg->code_len, cfg->domain->friendly_name);
g_free (nm);
}
mono_debug_close_method (cfg);
}
-static void
-mono_cprop_copy_values (MonoCompile *cfg, MonoInst *tree, MonoInst **acp)
-{
- MonoInst *cp;
- int arity;
-
- if (tree->ssa_op == MONO_SSA_LOAD && (tree->inst_i0->opcode == OP_LOCAL || tree->inst_i0->opcode == OP_ARG) &&
- (cp = acp [tree->inst_i0->inst_c0]) && !tree->inst_i0->flags) {
-
- if (cp->opcode == OP_ICONST) {
- if (cfg->opt & MONO_OPT_CONSPROP) {
- //{ static int c = 0; printf ("CCOPY %d %d %s\n", c++, cp->inst_c0, mono_method_full_name (cfg->method, TRUE)); }
- *tree = *cp;
- }
- } else {
- if (tree->inst_i0->inst_vtype->type == cp->inst_vtype->type) {
- if (cfg->opt & MONO_OPT_COPYPROP) {
- //{ static int c = 0; printf ("VCOPY %d\n", ++c); }
- tree->inst_i0 = cp;
- }
- }
- }
- } else {
- arity = mono_burg_arity [tree->opcode];
- if (arity) {
- mono_cprop_copy_values (cfg, tree->inst_i0, acp);
- if (cfg->opt & MONO_OPT_CFOLD)
- mono_constant_fold_inst (tree, NULL);
- /* The opcode may have changed */
- if (mono_burg_arity [tree->opcode] > 1) {
- mono_cprop_copy_values (cfg, tree->inst_i1, acp);
- if (cfg->opt & MONO_OPT_CFOLD)
- mono_constant_fold_inst (tree, NULL);
- }
- mono_constant_fold_inst (tree, NULL);
- }
- }
-}
-
-static void
-mono_cprop_invalidate_values (MonoInst *tree, MonoInst **acp, int acp_size)
-{
- int arity;
-
- switch (tree->opcode) {
- case CEE_STIND_I:
- case CEE_STIND_I1:
- case CEE_STIND_I2:
- case CEE_STIND_I4:
- case CEE_STIND_REF:
- case CEE_STIND_I8:
- case CEE_STIND_R4:
- case CEE_STIND_R8:
- case CEE_STOBJ:
- if ((tree->ssa_op == MONO_SSA_NOP) || (tree->ssa_op & MONO_SSA_ADDRESS_TAKEN)) {
- memset (acp, 0, sizeof (MonoInst *) * acp_size);
- return;
- }
-
- break;
- case CEE_CALL:
- case OP_CALL_REG:
- case CEE_CALLVIRT:
- case OP_LCALL_REG:
- case OP_LCALLVIRT:
- case OP_LCALL:
- case OP_FCALL_REG:
- case OP_FCALLVIRT:
- case OP_FCALL:
- case OP_VCALL_REG:
- case OP_VCALLVIRT:
- case OP_VCALL:
- case OP_VOIDCALL_REG:
- case OP_VOIDCALLVIRT:
- case OP_VOIDCALL: {
- MonoCallInst *call = (MonoCallInst *)tree;
- MonoMethodSignature *sig = call->signature;
- int i, byref = FALSE;
-
- for (i = 0; i < sig->param_count; i++) {
- if (sig->params [i]->byref) {
- byref = TRUE;
- break;
- }
- }
-
- if (byref)
- memset (acp, 0, sizeof (MonoInst *) * acp_size);
-
- return;
- }
- default:
- break;
- }
-
- arity = mono_burg_arity [tree->opcode];
-
- switch (arity) {
- case 0:
- break;
- case 1:
- mono_cprop_invalidate_values (tree->inst_i0, acp, acp_size);
- break;
- case 2:
- mono_cprop_invalidate_values (tree->inst_i0, acp, acp_size);
- mono_cprop_invalidate_values (tree->inst_i1, acp, acp_size);
- break;
- default:
- g_assert_not_reached ();
- }
-}
-
-static void
-mono_local_cprop_bb (MonoCompile *cfg, MonoBasicBlock *bb, MonoInst **acp, int acp_size)
-{
- MonoInst *tree = bb->code;
- int i;
-
- if (!tree)
- return;
-
- for (; tree; tree = tree->next) {
-
- mono_cprop_copy_values (cfg, tree, acp);
-
- mono_cprop_invalidate_values (tree, acp, acp_size);
-
- if (tree->ssa_op == MONO_SSA_STORE &&
- (tree->inst_i0->opcode == OP_LOCAL || tree->inst_i0->opcode == OP_ARG)) {
- MonoInst *i1 = tree->inst_i1;
-
- acp [tree->inst_i0->inst_c0] = NULL;
-
- for (i = 0; i < acp_size; i++) {
- if (acp [i] && acp [i]->opcode != OP_ICONST &&
- acp [i]->inst_c0 == tree->inst_i0->inst_c0) {
- acp [i] = NULL;
- }
- }
-
- if (i1->opcode == OP_ICONST) {
- acp [tree->inst_i0->inst_c0] = i1;
- //printf ("DEF1 BB%d %d\n", bb->block_num,tree->inst_i0->inst_c0);
- }
- if (i1->ssa_op == MONO_SSA_LOAD &&
- (i1->inst_i0->opcode == OP_LOCAL || i1->inst_i0->opcode == OP_ARG) &&
- (i1->inst_i0->inst_c0 != tree->inst_i0->inst_c0)) {
- acp [tree->inst_i0->inst_c0] = i1->inst_i0;
- //printf ("DEF2 BB%d %d %d\n", bb->block_num,tree->inst_i0->inst_c0,i1->inst_i0->inst_c0);
- }
- }
-
- /*
- if (tree->opcode == CEE_BEQ) {
- g_assert (tree->inst_i0->opcode == OP_COMPARE);
- if (tree->inst_i0->inst_i0->opcode == OP_ICONST &&
- tree->inst_i0->inst_i1->opcode == OP_ICONST) {
-
- tree->opcode = CEE_BR;
- if (tree->inst_i0->inst_i0->opcode == tree->inst_i0->inst_i1->opcode) {
- tree->inst_target_bb = tree->inst_true_bb;
- } else {
- tree->inst_target_bb = tree->inst_false_bb;
- }
- }
- }
- */
- }
-}
-
-static void
-mono_local_cprop (MonoCompile *cfg)
-{
- MonoBasicBlock *bb;
- MonoInst **acp;
-
- acp = alloca (sizeof (MonoInst *) * cfg->num_varinfo);
-
- for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
- memset (acp, 0, sizeof (MonoInst *) * cfg->num_varinfo);
- mono_local_cprop_bb (cfg, bb, acp, cfg->num_varinfo);
- }
-}
static void
remove_critical_edges (MonoCompile *cfg) {
ji = mono_jit_info_table_find (mono_domain_get (), mono_arch_ip_from_context(ctx));
if (!ji) {
- mono_handle_native_sigsegv (ctx);
+ mono_handle_native_sigsegv (SIGSEGV, ctx);
}
mono_arch_handle_exception (ctx, exc, FALSE);
}
+static void
+SIG_HANDLER_SIGNATURE (sigabrt_signal_handler)
+{
+ MonoJitInfo *ji;
+ GET_CONTEXT;
+
+ ji = mono_jit_info_table_find (mono_domain_get (), mono_arch_ip_from_context(ctx));
+ if (!ji) {
+ mono_handle_native_sigsegv (SIGABRT, ctx);
+ }
+}
+
static void
SIG_HANDLER_SIGNATURE (sigusr1_signal_handler)
{
add_signal_handler (mono_thread_get_abort_signal (), sigusr1_signal_handler);
signal (SIGPIPE, SIG_IGN);
+ add_signal_handler (SIGABRT, sigabrt_signal_handler);
+
/* catch SIGSEGV */
#ifdef MONO_ARCH_SIGSEGV_ON_ALTSTACK
sa.sa_sigaction = sigsegv_signal_handler;
#else
add_signal_handler (SIGSEGV, sigsegv_signal_handler);
#endif
-
#endif /* PLATFORM_WIN32 */
}
*/
mono_domain_finalize (domain, 2000);
+ /* This accesses metadata so needs to be called before runtime shutdown */
+ print_jit_stats ();
+
mono_runtime_cleanup (domain);
mono_profiler_shutdown ();
g_hash_table_destroy (jit_icall_name_hash);
if (class_init_hash_addr)
g_hash_table_destroy (class_init_hash_addr);
+ g_free (emul_opcode_map);
- print_jit_stats ();
mono_counters_dump (-1, stdout);
}