#include <math.h>
#include <sys/time.h>
-#ifdef sun // Solaris x86
-#include <sys/types.h>
-#include <sys/ucontext.h>
-#endif
-
#ifdef HAVE_VALGRIND_MEMCHECK_H
#include <valgrind/memcheck.h>
#endif
#include <mono/utils/mono-math.h>
#include <mono/utils/mono-compiler.h>
#include <mono/utils/mono-counters.h>
+#include <mono/utils/mono-logger.h>
#include <mono/os/gc_wrapper.h>
#include "mini.h"
#define MONO_IS_COND_BRANCH_OP(ins) (((ins)->opcode >= CEE_BEQ && (ins)->opcode <= CEE_BLT_UN) || ((ins)->opcode >= OP_LBEQ && (ins)->opcode <= OP_LBLT_UN) || ((ins)->opcode >= OP_FBEQ && (ins)->opcode <= OP_FBLT_UN) || ((ins)->opcode >= OP_IBEQ && (ins)->opcode <= OP_IBLT_UN))
#define MONO_IS_COND_BRANCH_NOFP(ins) (MONO_IS_COND_BRANCH_OP(ins) && (ins)->inst_left->inst_left->type != STACK_R8)
+#define MONO_IS_BRANCH_OP(ins) (MONO_IS_COND_BRANCH_OP(ins) || ((ins)->opcode == CEE_BR) || ((ins)->opcode == OP_BR_REG) || ((ins)->opcode == CEE_SWITCH))
+
#define MONO_CHECK_THIS(ins) (mono_method_signature (cfg->method)->hasthis && (ins)->ssa_op == MONO_SSA_LOAD && (ins)->inst_left->inst_c0 == 0)
static void setup_stat_profiler (void);
static gpointer mono_create_jit_trampoline_in_domain (MonoDomain *domain, MonoMethod *method);
static void handle_stobj (MonoCompile *cfg, MonoBasicBlock *bblock, MonoInst *dest, MonoInst *src,
- const unsigned char *ip, MonoClass *klass, gboolean to_end, gboolean native);
+ const unsigned char *ip, MonoClass *klass, gboolean to_end, gboolean native, gboolean write_barrier);
static void dec_foreach (MonoInst *tree, MonoCompile *cfg);
static MonoMethodSignature *helper_sig_domain_get = NULL;
static guint32 default_opt = 0;
+static gboolean default_opt_set = FALSE;
guint32 mono_jit_tls_id = -1;
MonoTraceSpec *mono_jit_trace_calls = NULL;
{
MonoJitInfo *ji;
char *method;
- char *source;
char *res;
MonoDomain *domain = mono_domain_get ();
+ MonoDebugSourceLocation *location;
FindTrampUserData user_data;
ji = mono_jit_info_table_find (domain, ip);
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);
+ /* FIXME: unused ? */
+ location = mono_debug_lookup_source_location (ji->method, (guint32)((guint8*)ip - (guint8*)ji->code_start), domain);
res = g_strdup_printf (" %s + 0x%x (%p %p) [%p - %s]", method, (int)((char*)ip - (char*)ji->code_start), ji->code_start, (char*)ji->code_start + ji->code_size, domain, domain->friendly_name);
- g_free (source);
+ mono_debug_free_source_location (location);
g_free (method);
return res;
{
MonoJitInfo *ji;
char *method;
- char *source;
+ MonoDebugSourceLocation *source;
MonoDomain *domain = mono_domain_get ();
FindTrampUserData user_data;
return;
}
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);
+ source = mono_debug_lookup_source_location (ji->method, (guint32)((guint8*)ip - (guint8*)ji->code_start), 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);
if (source)
- g_print ("%s\n", source);
+ g_print ("%s:%d\n", source->source_file, source->row);
- g_free (source);
+ mono_debug_free_source_location (source);
g_free (method);
}
(dest)->type = STACK_PTR; \
} while (0)
-#define NEW_AOTCONST_TOKEN(cfg,dest,patch_type,image,token,stack_type) do { \
+#define NEW_AOTCONST_TOKEN(cfg,dest,patch_type,image,token,stack_type,stack_class) do { \
MonoInst *group, *got_var, *got_loc; \
(dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \
(dest)->opcode = OP_GOT_ENTRY; \
group->inst_p0 = mono_jump_info_token_new ((cfg)->mempool, (image), (token)); \
(dest)->inst_p0 = got_var; \
(dest)->inst_p1 = group; \
- (dest)->type = (stack_type); \
+ (dest)->type = (stack_type); \
+ (dest)->klass = (stack_class); \
} while (0)
#else
(dest)->type = STACK_PTR; \
} while (0)
-#define NEW_AOTCONST_TOKEN(cfg,dest,patch_type,image,token,stack_type) do { \
+#define NEW_AOTCONST_TOKEN(cfg,dest,patch_type,image,token,stack_type,stack_class) do { \
(dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \
(dest)->opcode = OP_AOTCONST; \
(dest)->inst_p0 = mono_jump_info_token_new ((cfg)->mempool, (image), (token)); \
(dest)->inst_p1 = (gpointer)(patch_type); \
(dest)->type = (stack_type); \
+ (dest)->klass = (stack_class); \
} while (0)
#endif
#define NEW_SFLDACONST(cfg,dest,val) NEW_AOTCONST ((cfg), (dest), MONO_PATCH_INFO_SFLDA, (val))
-#define NEW_LDSTRCONST(cfg,dest,image,token) NEW_AOTCONST_TOKEN ((cfg), (dest), MONO_PATCH_INFO_LDSTR, (image), (token), STACK_OBJ)
+#define NEW_LDSTRCONST(cfg,dest,image,token) NEW_AOTCONST_TOKEN ((cfg), (dest), MONO_PATCH_INFO_LDSTR, (image), (token), STACK_OBJ, mono_defaults.string_class)
-#define NEW_TYPE_FROM_HANDLE_CONST(cfg,dest,image,token) NEW_AOTCONST_TOKEN ((cfg), (dest), MONO_PATCH_INFO_TYPE_FROM_HANDLE, (image), (token), STACK_OBJ)
+#define NEW_TYPE_FROM_HANDLE_CONST(cfg,dest,image,token) NEW_AOTCONST_TOKEN ((cfg), (dest), MONO_PATCH_INFO_TYPE_FROM_HANDLE, (image), (token), STACK_OBJ, mono_defaults.monotype_class)
-#define NEW_LDTOKENCONST(cfg,dest,image,token) NEW_AOTCONST_TOKEN ((cfg), (dest), MONO_PATCH_INFO_LDTOKEN, (image), (token), STACK_PTR)
+#define NEW_LDTOKENCONST(cfg,dest,image,token) NEW_AOTCONST_TOKEN ((cfg), (dest), MONO_PATCH_INFO_LDTOKEN, (image), (token), STACK_PTR, NULL)
#define NEW_DECLSECCONST(cfg,dest,image,entry) do { \
if (cfg->compile_aot) { \
- NEW_AOTCONST_TOKEN (cfg, dest, MONO_PATCH_INFO_DECLSEC, image, (entry).index, STACK_OBJ); \
+ NEW_AOTCONST_TOKEN (cfg, dest, MONO_PATCH_INFO_DECLSEC, image, (entry).index, STACK_OBJ, NULL); \
} else { \
NEW_PCONST (cfg, args [0], (entry).blob); \
} \
case STACK_I8: return &mono_defaults.int64_class->byval_arg;
case STACK_PTR: return &mono_defaults.int_class->byval_arg;
case STACK_R8: return &mono_defaults.double_class->byval_arg;
- case STACK_MP: return &mono_defaults.int_class->byval_arg;
+ case STACK_MP:
+ /*
+ * FIXME: This doesn't work because mono_class_from_mono_type ()
+ * returns the original klass for a byref type, not a 'byref' class,
+ * causing the JIT to create variables with the wrong type, for
+ * example.
+ */
+ /*
+ if (ins->klass)
+ return &ins->klass->this_arg;
+ else
+ */
+ return &mono_defaults.object_class->this_arg;
case STACK_OBJ: return &mono_defaults.object_class->byval_arg;
case STACK_VTYPE: return &ins->klass->byval_arg;
default:
NEW_TEMPSTORE (cfg, inst, dest, load);
if (inst->opcode == CEE_STOBJ) {
NEW_TEMPLOADA (cfg, inst, dest);
- handle_stobj (cfg, bb, inst, load, NULL, inst->klass, TRUE, FALSE);
+ handle_stobj (cfg, bb, inst, load, NULL, inst->klass, TRUE, FALSE, FALSE);
} else {
inst->cil_code = NULL;
mono_add_ins_to_end (bb, inst);
return res;
}
+/*
+ * merge_stacks:
+ *
+ * Merge stack state between two basic blocks according to Ecma 335, Partition III,
+ * section 1.8.1.1. Store the resulting stack state into stack_2.
+ * Returns: TRUE, if verification succeeds, FALSE otherwise.
+ * FIXME: We should store the stack state in a dedicated structure instead of in
+ * MonoInst's.
+ */
+static gboolean
+merge_stacks (MonoCompile *cfg, MonoStackSlot *state_1, MonoStackSlot *state_2, guint32 size)
+{
+ int i;
+
+ if (cfg->dont_verify_stack_merge)
+ return TRUE;
+
+ /* FIXME: Implement all checks from the spec */
+
+ for (i = 0; i < size; ++i) {
+ MonoStackSlot *slot1 = &state_1 [i];
+ MonoStackSlot *slot2 = &state_2 [i];
+
+ if (slot1->type != slot2->type)
+ return FALSE;
+
+ switch (slot1->type) {
+ case STACK_PTR:
+ /* FIXME: Perform merge ? */
+ /* klass == NULL means a native int */
+ if (slot1->klass && slot2->klass) {
+ if (slot1->klass != slot2->klass)
+ return FALSE;
+ }
+ break;
+ case STACK_MP:
+ /* FIXME: Change this to an assert and fix the JIT to allways fill this */
+ if (slot1->klass && slot2->klass) {
+ if (slot1->klass != slot2->klass)
+ return FALSE;
+ }
+ break;
+ case STACK_OBJ: {
+ MonoClass *klass1 = slot1->klass;
+ MonoClass *klass2 = slot2->klass;
+
+ if (!klass1) {
+ /* slot1 is ldnull */
+ } else if (!klass2) {
+ /* slot2 is ldnull */
+ slot2->klass = slot1->klass;
+ }
+ break;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
/*
* This function is called to handle items that are left on the evaluation stack
* at basic block boundaries. What happens is that we save the values to local variables
* which case its old value should be used.
* A single joint point will use the same variables (stored in the array bb->out_stack or
* bb->in_stack, if the basic block is before or after the joint point).
+ * If the stack merge fails at a join point, cfg->unverifiable is set.
*/
static int
handle_stack_args (MonoCompile *cfg, MonoBasicBlock *bb, MonoInst **sp, int count) {
int i, bindex;
MonoBasicBlock *outb;
MonoInst *inst, **locals;
+ MonoStackSlot *stack_state;
gboolean found;
if (!count)
return 0;
if (cfg->verbose_level > 3)
g_print ("%d item(s) on exit from B%d\n", count, bb->block_num);
+
+ stack_state = mono_mempool_alloc (cfg->mempool, sizeof (MonoStackSlot) * count);
+ for (i = 0; i < count; ++i) {
+ stack_state [i].type = sp [i]->type;
+ stack_state [i].klass = sp [i]->klass;
+
+ /* Check that instructions other than ldnull have ins->klass set */
+ if (!cfg->dont_verify_stack_merge && (sp [i]->type == STACK_OBJ) && !((sp [i]->opcode == OP_PCONST) && sp [i]->inst_c0 == 0))
+ g_assert (sp [i]->klass);
+ }
+
+ /* Perform verification and stack state merge */
+ for (i = 0; i < bb->out_count; ++i) {
+ outb = bb->out_bb [i];
+
+ /* exception handlers are linked, but they should not be considered for stack args */
+ if (outb->flags & BB_EXCEPTION_HANDLER)
+ continue;
+ if (outb->stack_state) {
+ gboolean verified;
+
+ if (count != outb->in_scount) {
+ cfg->unverifiable = TRUE;
+ return 0;
+ }
+ verified = merge_stacks (cfg, stack_state, outb->stack_state, count);
+ if (!verified) {
+ cfg->unverifiable = TRUE;
+ return 0;
+ }
+
+ if (cfg->verbose_level > 3) {
+ int j;
+
+ for (j = 0; j < count; ++j)
+ printf ("\tStack state of BB%d, slot %d=%d\n", outb->block_num, j, outb->stack_state [j].type);
+ }
+ } else {
+ /* Make a copy of the stack state */
+ outb->stack_state = mono_mempool_alloc (cfg->mempool, sizeof (MonoStackSlot) * count);
+ memcpy (outb->stack_state, stack_state, sizeof (MonoStackSlot) * count);
+ }
+ }
+
if (!bb->out_scount) {
bb->out_scount = count;
//g_print ("bblock %d has out:", bb->block_num);
NEW_TEMPSTORE (cfg, inst, locals [i]->inst_c0, sp [i]);
if (inst->opcode == CEE_STOBJ) {
NEW_TEMPLOADA (cfg, inst, locals [i]->inst_c0);
- handle_stobj (cfg, bb, inst, sp [i], sp [i]->cil_code, inst->klass, TRUE, FALSE);
+ handle_stobj (cfg, bb, inst, sp [i], sp [i]->cil_code, inst->klass, TRUE, FALSE, FALSE);
} else {
inst->cil_code = sp [i]->cil_code;
mono_add_ins_to_end (bb, inst);
store->cil_code = ins->cil_code;
if (store->opcode == CEE_STOBJ) {
NEW_TEMPLOADA (cfg, store, temp->inst_c0);
- handle_stobj (cfg, bblock, store, ins, ins->cil_code, temp->klass, FALSE, FALSE);
+ handle_stobj (cfg, bblock, store, ins, ins->cil_code, temp->klass, FALSE, FALSE, FALSE);
} else
MONO_ADD_INS (bblock, store);
NEW_TEMPLOAD (cfg, load, temp->inst_c0);
}
static void
-handle_stobj (MonoCompile *cfg, MonoBasicBlock *bblock, MonoInst *dest, MonoInst *src, const unsigned char *ip, MonoClass *klass, gboolean to_end, gboolean native) {
+handle_stobj (MonoCompile *cfg, MonoBasicBlock *bblock, MonoInst *dest, MonoInst *src, const unsigned char *ip, MonoClass *klass, gboolean to_end, gboolean native, gboolean write_barrier) {
MonoInst *iargs [3];
int n;
guint32 align = 0;
else
n = mono_class_value_size (klass, &align);
+#if HAVE_WRITE_BARRIERS
+ /* if native is true there should be no references in the struct */
+ if (write_barrier && klass->has_references && !native) {
+ iargs [0] = dest;
+ iargs [1] = src;
+ NEW_PCONST (cfg, iargs [2], klass);
+
+ mono_emit_jit_icall (cfg, bblock, mono_value_copy, iargs, ip);
+ return;
+ }
+#endif
+
+ /* FIXME: add write barrier handling */
if ((cfg->opt & MONO_OPT_INTRINS) && !to_end && n <= sizeof (gpointer) * 5) {
MonoInst *inst;
if (dest->opcode == OP_LDADDR) {
vstore->inst_right = val;
if (vstore->opcode == CEE_STOBJ) {
- handle_stobj (cfg, bblock, add, val, ip, klass, FALSE, FALSE);
+ handle_stobj (cfg, bblock, add, val, ip, klass, FALSE, FALSE, TRUE);
} else
MONO_ADD_INS (bblock, vstore);
if (!(cfg->opt & MONO_OPT_SHARED)) {
vtable = mono_class_vtable (cfg->domain, method->klass);
if (method->klass->flags & TYPE_ATTRIBUTE_BEFORE_FIELD_INIT) {
- if (cfg->run_cctors)
+ if (cfg->run_cctors) {
+ /* This makes so that inline cannot trigger */
+ /* .cctors: too many apps depend on them */
+ /* running with a specific order... */
+ if (! vtable->initialized)
+ return FALSE;
mono_runtime_class_init (vtable);
+ }
}
else if (!vtable->initialized && mono_class_needs_cctor_run (method->klass, NULL))
return FALSE;
return NULL;
}
-static MonoException*
-mini_loader_error_to_exception (MonoLoaderError *error)
-{
- MonoException *ex = NULL;
-
- switch (error->kind) {
- case MONO_LOADER_ERROR_TYPE: {
- MonoString *class_name = mono_string_new (mono_domain_get (), error->class_name);
-
- ex = mono_get_exception_type_load (class_name, error->assembly_name);
- break;
- }
- case MONO_LOADER_ERROR_METHOD:
- case MONO_LOADER_ERROR_FIELD: {
- char *class_name;
-
- class_name = g_strdup_printf ("%s%s%s", error->klass->name_space, *error->klass->name_space ? "." : "", error->klass->name);
-
- if (error->kind == MONO_LOADER_ERROR_METHOD)
- ex = mono_get_exception_missing_method (class_name, error->member_name);
- else
- ex = mono_get_exception_missing_field (class_name, error->member_name);
- g_free (class_name);
- break;
- }
- default:
- g_assert_not_reached ();
- }
-
- return ex;
-}
-
static MonoInst*
mini_get_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
{
store->cil_code = sp [0]->cil_code;
if (store->opcode == CEE_STOBJ) {
NEW_TEMPLOADA (cfg, store, temp->inst_c0);
- handle_stobj (cfg, bblock, store, *sp, sp [0]->cil_code, temp->klass, FALSE, FALSE);
+ handle_stobj (cfg, bblock, store, *sp, sp [0]->cil_code, temp->klass, FALSE, FALSE, FALSE);
} else {
MONO_ADD_INS (bblock, store);
}
sp++;
}
}
+#define MONO_INLINE_CALLED_LIMITED_METHODS 0
+#define MONO_INLINE_CALLER_LIMITED_METHODS 0
+
+#if (MONO_INLINE_CALLED_LIMITED_METHODS)
+static char*
+mono_inline_called_method_name_limit = NULL;
+static gboolean check_inline_called_method_name_limit (MonoMethod *called_method) {
+ char *called_method_name = mono_method_full_name (called_method, TRUE);
+ int strncmp_result;
+
+ if (mono_inline_called_method_name_limit == NULL) {
+ char *limit_string = getenv ("MONO_INLINE_CALLED_METHOD_NAME_LIMIT");
+ if (limit_string != NULL) {
+ mono_inline_called_method_name_limit = limit_string;
+ } else {
+ mono_inline_called_method_name_limit = (char *) "";
+ }
+ }
+
+ strncmp_result = strncmp (called_method_name, mono_inline_called_method_name_limit, strlen (mono_inline_called_method_name_limit));
+ g_free (called_method_name);
+
+ //return (strncmp_result <= 0);
+ return (strncmp_result == 0);
+}
+#endif
+
+#if (MONO_INLINE_CALLER_LIMITED_METHODS)
+static char*
+mono_inline_caller_method_name_limit = NULL;
+static gboolean check_inline_caller_method_name_limit (MonoMethod *caller_method) {
+ char *caller_method_name = mono_method_full_name (caller_method, TRUE);
+ int strncmp_result;
+
+ if (mono_inline_caller_method_name_limit == NULL) {
+ char *limit_string = getenv ("MONO_INLINE_CALLER_METHOD_NAME_LIMIT");
+ if (limit_string != NULL) {
+ mono_inline_caller_method_name_limit = limit_string;
+ } else {
+ mono_inline_caller_method_name_limit = (char *) "";
+ }
+ }
+
+ strncmp_result = strncmp (caller_method_name, mono_inline_caller_method_name_limit, strlen (mono_inline_caller_method_name_limit));
+ g_free (caller_method_name);
+
+ //return (strncmp_result <= 0);
+ return (strncmp_result == 0);
+}
+#endif
static int
inline_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoBasicBlock *bblock, MonoInst **sp,
MonoBasicBlock *ebblock, *sbblock;
int i, costs, new_locals_offset;
MonoMethod *prev_inlined_method;
+#if (MONO_INLINE_CALLED_LIMITED_METHODS)
+ if ((! inline_allways) && ! check_inline_called_method_name_limit (cmethod))
+ return 0;
+#endif
+#if (MONO_INLINE_CALLER_LIMITED_METHODS)
+ if ((! inline_allways) && ! check_inline_caller_method_name_limit (cfg->method))
+ return 0;
+#endif
if (cfg->verbose_level > 2)
g_print ("INLINE START %p %s -> %s\n", cmethod, mono_method_full_name (cfg->method, TRUE), mono_method_full_name (cmethod, TRUE));
#define CHECK_ARG(num) if ((unsigned)(num) >= (unsigned)num_args) UNVERIFIED
#define CHECK_LOCAL(num) if ((unsigned)(num) >= (unsigned)header->num_locals) UNVERIFIED
#define CHECK_OPSIZE(size) if (ip + size > end) UNVERIFIED
-
+#define CHECK_UNVERIFIABLE(cfg) if (cfg->unverifiable) UNVERIFIED
/* offset from br.s -> br like opcodes */
#define BIG_BRANCH_OFFSET 13
return klass;
}
+/*
+ * Returns TRUE if the JIT should abort inlining because "callee"
+ * is influenced by security attributes.
+ */
static
-void check_linkdemand (MonoCompile *cfg, MonoMethod *caller, MonoMethod *callee, MonoBasicBlock *bblock, unsigned char *ip)
+gboolean check_linkdemand (MonoCompile *cfg, MonoMethod *caller, MonoMethod *callee, MonoBasicBlock *bblock, unsigned char *ip)
{
- guint32 result = mono_declsec_linkdemand (cfg->domain, caller, callee);
+ guint32 result;
+
+ if ((cfg->method != caller) && mono_method_has_declsec (callee)) {
+ return TRUE;
+ }
+
+ result = mono_declsec_linkdemand (cfg->domain, caller, callee);
if (result == MONO_JIT_SECURITY_OK)
- return;
+ return FALSE;
if (result == MONO_JIT_LINKDEMAND_ECMA) {
/* Generate code to throw a SecurityException before the actual call/link */
cfg->exception_type = MONO_EXCEPTION_SECURITY_LINKDEMAND;
cfg->exception_data = result;
}
+
+ return FALSE;
}
static gboolean
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 */
+ dont_verify |= method->wrapper_type == MONO_WRAPPER_COMINTEROP;
- /* still some type unsefety issues in marshal wrappers... (unknown is PtrToStructure) */
+ /* still some type unsafety issues in marshal wrappers... (unknown is PtrToStructure) */
dont_verify_stloc = method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE;
dont_verify_stloc |= method->wrapper_type == MONO_WRAPPER_UNKNOWN;
dont_verify_stloc |= method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED;
+ /* Not turned on yet */
+ cfg->dont_verify_stack_merge = TRUE;
+
image = method->klass->image;
header = mono_method_get_header (method);
generic_container = method->generic_container;
tblock->in_scount = 1;
tblock->in_stack = mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*));
tblock->in_stack [0] = mono_create_exvar_for_offset (cfg, clause->handler_offset);
+ tblock->stack_state = mono_mempool_alloc (cfg->mempool, sizeof (MonoStackSlot));
+ tblock->stack_state [0].type = STACK_OBJ;
+ /* FIXME? */
+ tblock->stack_state [0].klass = mono_defaults.object_class;
/*
* Add a dummy use for the exvar so its liveness info will be
tblock->real_offset = clause->data.filter_offset;
tblock->in_scount = 1;
tblock->in_stack = mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*));
+ tblock->stack_state = mono_mempool_alloc (cfg->mempool, sizeof (MonoStackSlot));
+ tblock->stack_state [0].type = STACK_OBJ;
+ /* FIXME? */
+ tblock->stack_state [0].klass = mono_defaults.object_class;
+
/* The filter block shares the exvar with the handler block */
tblock->in_stack [0] = mono_create_exvar_for_offset (cfg, clause->handler_offset);
MONO_INST_NEW (cfg, ins, OP_START_HANDLER);
if (sp != stack_start) {
handle_stack_args (cfg, bblock, stack_start, sp - stack_start);
sp = stack_start;
+ CHECK_UNVERIFIABLE (cfg);
}
bblock->next_bb = tblock;
bblock = tblock;
UNVERIFIED;
if (ins->opcode == CEE_STOBJ) {
NEW_LOCLOADA (cfg, ins, n);
- handle_stobj (cfg, bblock, ins, *sp, ip, ins->klass, FALSE, FALSE);
+ handle_stobj (cfg, bblock, ins, *sp, ip, ins->klass, FALSE, FALSE, FALSE);
} else
MONO_ADD_INS (bblock, ins);
++ip;
UNVERIFIED;
if (ins->opcode == CEE_STOBJ) {
NEW_ARGLOADA (cfg, ins, ip [1]);
- handle_stobj (cfg, bblock, ins, *sp, ip, ins->klass, FALSE, FALSE);
+ handle_stobj (cfg, bblock, ins, *sp, ip, ins->klass, FALSE, FALSE, FALSE);
} else
MONO_ADD_INS (bblock, ins);
ip += 2;
UNVERIFIED;
if (ins->opcode == CEE_STOBJ) {
NEW_LOCLOADA (cfg, ins, ip [1]);
- handle_stobj (cfg, bblock, ins, *sp, ip, ins->klass, FALSE, FALSE);
+ handle_stobj (cfg, bblock, ins, *sp, ip, ins->klass, FALSE, FALSE, FALSE);
} else
MONO_ADD_INS (bblock, ins);
ip += 2;
store->cil_code = ip;
if (store->opcode == CEE_STOBJ) {
NEW_TEMPLOADA (cfg, store, temp->inst_c0);
- handle_stobj (cfg, bblock, store, sp [0], sp [0]->cil_code, store->klass, TRUE, FALSE);
+ handle_stobj (cfg, bblock, store, sp [0], sp [0]->cil_code, store->klass, TRUE, FALSE, FALSE);
} else {
MONO_ADD_INS (bblock, store);
}
goto load_error;
if (mono_use_security_manager) {
- check_linkdemand (cfg, method, cmethod, bblock, ip);
+ if (check_linkdemand (cfg, method, cmethod, bblock, ip))
+ INLINE_FAILURE;
}
ins->inst_p0 = cmethod;
/* MS.NET seems to silently convert this to a callvirt */
virtual = 1;
- if (!cmethod->klass->inited)
- mono_class_init (cmethod->klass);
+ if (!cmethod->klass->inited){
+ if (!mono_class_init (cmethod->klass))
+ goto load_error;
+ }
if (mono_method_signature (cmethod)->pinvoke) {
MonoMethod *wrapper = mono_marshal_get_native_wrapper (cmethod);
n = fsig->param_count + fsig->hasthis;
if (mono_use_security_manager) {
- check_linkdemand (cfg, method, cmethod, bblock, ip);
+ if (check_linkdemand (cfg, method, cmethod, bblock, ip))
+ INLINE_FAILURE;
}
if (cmethod->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL &&
ins->cil_code = ip;
ins->inst_i0 = sp [0];
ins->type = STACK_OBJ;
+ ins->klass = mono_class_from_mono_type (&constrained_call->byval_arg);
sp [0] = ins;
} else if (cmethod->klass->valuetype)
virtual = 0;
}
ip += 5;
+ ins_flag = 0;
break;
}
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 */
ins->cil_code = ip;
if (ins->opcode == CEE_STOBJ) {
NEW_ARGLOADA (cfg, ins, i);
- handle_stobj (cfg, bblock, ins, sp [i], sp [i]->cil_code, ins->klass, FALSE, FALSE);
+ handle_stobj (cfg, bblock, ins, sp [i], sp [i]->cil_code, ins->klass, FALSE, FALSE, FALSE);
}
else
MONO_ADD_INS (bblock, ins);
}
ip += 5;
+ ins_flag = 0;
break;
}
bblock = ebblock;
inline_costs += costs;
+ ins_flag = 0;
break;
}
}
ip += 6;
else
ip += 5;
-
+ ins_flag = 0;
break;
}
}
NEW_INDSTORE (cfg, ins, addr, sp [fsig->param_count], fsig->params [fsig->param_count - 1]);
ins->cil_code = ip;
if (ins->opcode == CEE_STOBJ) {
- handle_stobj (cfg, bblock, addr, sp [fsig->param_count], ip, mono_class_from_mono_type (fsig->params [fsig->param_count-1]), FALSE, FALSE);
+ handle_stobj (cfg, bblock, addr, sp [fsig->param_count], ip, mono_class_from_mono_type (fsig->params [fsig->param_count-1]), FALSE, FALSE, TRUE);
} else {
MONO_ADD_INS (bblock, ins);
}
}
ip += 5;
+ ins_flag = 0;
break;
}
case CEE_RET:
if (store->opcode == CEE_STOBJ) {
g_assert_not_reached ();
NEW_TEMPLOADA (cfg, store, return_var->inst_c0);
- handle_stobj (cfg, bblock, store, *sp, sp [0]->cil_code, return_var->klass, FALSE, FALSE);
+ /* FIXME: it is possible some optimization will pass the a heap pointer for the struct address, so we'll need the write barrier */
+ handle_stobj (cfg, bblock, store, *sp, sp [0]->cil_code, return_var->klass, FALSE, FALSE, FALSE);
} else
MONO_ADD_INS (bblock, store);
}
ins->opcode = mono_type_to_stind (mono_method_signature (method)->ret);
if (ins->opcode == CEE_STOBJ) {
NEW_RETLOADA (cfg, ins);
- handle_stobj (cfg, bblock, ins, *sp, ip, ins->klass, FALSE, FALSE);
+ /* FIXME: it is possible some optimization will pass the a heap pointer for the struct address, so we'll need the write barrier */
+ handle_stobj (cfg, bblock, ins, *sp, ip, ins->klass, FALSE, FALSE, FALSE);
} else {
ins->opcode = OP_SETRET;
ins->cil_code = ip;
if (sp != stack_start) {
handle_stack_args (cfg, bblock, stack_start, sp - stack_start);
sp = stack_start;
+ CHECK_UNVERIFIABLE (cfg);
}
start_new_bblock = 1;
inline_costs += BRANCH_COST;
if (sp != stack_start) {
handle_stack_args (cfg, bblock, stack_start, sp - stack_start);
sp = stack_start;
+ CHECK_UNVERIFIABLE (cfg);
}
inline_costs += BRANCH_COST;
break;
if (sp != stack_start) {
handle_stack_args (cfg, bblock, stack_start, sp - stack_start);
sp = stack_start;
+ CHECK_UNVERIFIABLE (cfg);
}
inline_costs += BRANCH_COST;
break;
if (sp != stack_start) {
handle_stack_args (cfg, bblock, stack_start, sp - stack_start);
sp = stack_start;
+ CHECK_UNVERIFIABLE (cfg);
}
start_new_bblock = 1;
inline_costs += BRANCH_COST;
if (sp != stack_start) {
handle_stack_args (cfg, bblock, stack_start, sp - stack_start);
sp = stack_start;
+ CHECK_UNVERIFIABLE (cfg);
}
inline_costs += BRANCH_COST;
break;
if (sp != stack_start) {
handle_stack_args (cfg, bblock, stack_start, sp - stack_start);
sp = stack_start;
+ CHECK_UNVERIFIABLE (cfg);
}
inline_costs += BRANCH_COST;
break;
if (sp != stack_start) {
handle_stack_args (cfg, bblock, stack_start, sp - stack_start);
sp = stack_start;
+ CHECK_UNVERIFIABLE (cfg);
}
/* Needed by the code generated in inssel.brg */
mono_get_got_var (cfg);
ins->type = ldind_type [*ip - CEE_LDIND_I1];
ins->flags |= ins_flag;
ins_flag = 0;
+ if (ins->type == STACK_OBJ)
+ ins->klass = mono_defaults.object_class;
++ip;
break;
case CEE_STIND_REF:
case CEE_STIND_R4:
case CEE_STIND_R8:
CHECK_STACK (2);
+#if HAVE_WRITE_BARRIERS
+ if (*ip == CEE_STIND_REF && method->wrapper_type != MONO_WRAPPER_WRITE_BARRIER) {
+ /* insert call to write barrier */
+ MonoMethod *write_barrier = mono_marshal_get_write_barrier ();
+ sp -= 2;
+ mono_emit_method_call_spilled (cfg, bblock, write_barrier, mono_method_signature (write_barrier), sp, ip, NULL);
+ ip++;
+ break;
+ }
+#endif
MONO_INST_NEW (cfg, ins, *ip);
ins->cil_code = ip++;
sp -= 2;
load->cil_code = ip;
load->inst_i0 = sp [1];
load->type = STACK_OBJ;
+ load->klass = klass;
load->flags |= ins_flag;
MONO_INST_NEW (cfg, store, CEE_STIND_REF);
store->cil_code = ip;
ins->cil_code = ip;
ins->inst_i0 = sp [0];
ins->type = STACK_OBJ;
+ ins->klass = klass;
ins->flags |= ins_flag;
ins_flag = 0;
*sp++ = ins;
ins->cil_code = ip;
g_assert (ins->opcode == CEE_STOBJ);
NEW_LOCLOADA (cfg, ins, loc_index);
- handle_stobj (cfg, bblock, ins, *sp, ip, ins->klass, FALSE, FALSE);
+ handle_stobj (cfg, bblock, ins, *sp, ip, ins->klass, FALSE, FALSE, FALSE);
ip += 5;
ip += stloc_len;
break;
NEW_PCONST (cfg, ins, mono_method_get_wrapper_data (method, n));
ins->cil_code = ip;
ins->type = STACK_OBJ;
+ ins->klass = mono_defaults.string_class;
*sp = ins;
}
else if (method->wrapper_type != MONO_WRAPPER_NONE) {
ins->cil_code = ip;
ins->type = STACK_OBJ;
ins->inst_p0 = mono_ldstr (cfg->domain, image, mono_metadata_token_index (n));
+ ins->klass = mono_defaults.string_class;
*sp = ins;
}
}
goto load_error;
fsig = mono_method_get_signature (cmethod, image, token);
- mono_class_init (cmethod->klass);
+ if (!mono_class_init (cmethod->klass))
+ goto load_error;
if (mono_use_security_manager) {
- check_linkdemand (cfg, method, cmethod, bblock, ip);
+ if (check_linkdemand (cfg, method, cmethod, bblock, ip))
+ INLINE_FAILURE;
}
n = fsig->param_count;
add->inst_left = ins;
add->inst_right = vtoffset;
add->type = STACK_MP;
+ add->klass = mono_defaults.object_class;
*sp = add;
ip += 5;
/* LDOBJ impl */
} else {
mono_emit_method_call_spilled (cfg, bblock, stfld_wrapper, mono_method_signature (stfld_wrapper), iargs, ip, NULL);
}
+#if HAVE_WRITE_BARRIERS
+ } else if (mono_type_to_stind (field->type) == CEE_STIND_REF) {
+ /* insert call to write barrier */
+ MonoMethod *write_barrier = mono_marshal_get_write_barrier ();
+ MonoInst *iargs [2];
+ NEW_ICONST (cfg, offset_ins, foffset);
+ MONO_INST_NEW (cfg, ins, OP_PADD);
+ ins->cil_code = ip;
+ ins->inst_left = *sp;
+ ins->inst_right = offset_ins;
+ ins->type = STACK_MP;
+ ins->klass = mono_defaults.object_class;
+ iargs [0] = ins;
+ iargs [1] = sp [1];
+ mono_emit_method_call_spilled (cfg, bblock, write_barrier, mono_method_signature (write_barrier), iargs, ip, NULL);
+#endif
} else {
MonoInst *store;
NEW_ICONST (cfg, offset_ins, foffset);
ins_flag = 0;
if (store->opcode == CEE_STOBJ) {
handle_stobj (cfg, bblock, ins, sp [1], ip,
- mono_class_from_mono_type (field->type), FALSE, FALSE);
+ mono_class_from_mono_type (field->type), FALSE, FALSE, TRUE);
} else
MONO_ADD_INS (bblock, store);
}
g_print ("class %s.%s needs init call for %s\n", klass->name_space, klass->name, field->name);
class_inits = g_slist_prepend (class_inits, vtable);
} else {
- if (cfg->run_cctors)
+ if (cfg->run_cctors) {
+ /* This makes so that inline cannot trigger */
+ /* .cctors: too many apps depend on them */
+ /* running with a specific order... */
+ if (! vtable->initialized)
+ INLINE_FAILURE;
mono_runtime_class_init (vtable);
+ }
}
addr = (char*)vtable->data + field->offset;
ins_flag = 0;
if (store->opcode == CEE_STOBJ) {
- handle_stobj (cfg, bblock, ins, sp [0], ip, mono_class_from_mono_type (field->type), FALSE, FALSE);
+ handle_stobj (cfg, bblock, ins, sp [0], ip, mono_class_from_mono_type (field->type), FALSE, FALSE, FALSE);
} else
MONO_ADD_INS (bblock, store);
} else {
NEW_ICONST (cfg, *sp, *((guint32 *)addr));
sp++;
break;
+#ifndef HAVE_MOVING_COLLECTOR
case MONO_TYPE_I:
case MONO_TYPE_U:
case MONO_TYPE_STRING:
type_to_eval_stack_type (field->type, *sp);
sp++;
break;
+#endif
case MONO_TYPE_I8:
case MONO_TYPE_U8:
MONO_INST_NEW (cfg, *sp, OP_I8CONST);
goto load_error;
n = mono_type_to_stind (&klass->byval_arg);
if (n == CEE_STOBJ) {
- handle_stobj (cfg, bblock, sp [0], sp [1], ip, klass, FALSE, FALSE);
+ handle_stobj (cfg, bblock, sp [0], sp [1], ip, klass, FALSE, FALSE, TRUE);
} else {
/* FIXME: should check item at sp [1] is compatible with the type of the store. */
MonoInst *store;
if (target_type_is_incompatible (cfg, &klass->byval_arg, *sp))
UNVERIFIED;
/* frequent check in generic code: box (struct), brtrue */
- if (ip + 5 < end && ip_in_bb (cfg, bblock, ip + 5) && (ip [5] == CEE_BRTRUE || ip [5] == CEE_BRTRUE_S)) {
+ if (!mono_class_is_nullable (klass) &&
+ ip + 5 < end && ip_in_bb (cfg, bblock, ip + 5) && (ip [5] == CEE_BRTRUE || ip [5] == CEE_BRTRUE_S)) {
/*g_print ("box-brtrue opt at 0x%04x in %s\n", real_offset, method->name);*/
MONO_INST_NEW (cfg, ins, CEE_POP);
MONO_ADD_INS (bblock, ins);
if (sp != stack_start) {
handle_stack_args (cfg, bblock, stack_start, sp - stack_start);
sp = stack_start;
+ CHECK_UNVERIFIABLE (cfg);
}
start_new_bblock = 1;
break;
ins->inst_newa_class = klass;
ins->inst_newa_len = *sp;
ins->type = STACK_OBJ;
+ ins->klass = klass;
ip += 5;
*sp++ = ins;
/*
check->klass = klass;
check->inst_left = sp [0];
check->type = STACK_OBJ;
+ check->klass = klass;
sp [0] = check;
}
ins->inst_left = load;
*sp++ = ins;
ins->type = ldind_type [ins->opcode - CEE_LDIND_I1];
+ ins->klass = klass;
++ip;
break;
}
n = mono_type_to_stind (&klass->byval_arg);
if (n == CEE_STOBJ)
- handle_stobj (cfg, bblock, load, sp [2], ip, klass, FALSE, FALSE);
+ handle_stobj (cfg, bblock, load, sp [2], ip, klass, FALSE, FALSE, TRUE);
else {
MONO_INST_NEW (cfg, ins, n);
ins->cil_code = ip;
klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
NEW_RETLOADA (cfg, ins);
- handle_stobj (cfg, bblock, ins, *sp, ip, klass, FALSE, TRUE);
+ handle_stobj (cfg, bblock, ins, *sp, ip, klass, FALSE, TRUE, FALSE);
if (sp != stack_start)
UNVERIFIED;
mono_class_init (cmethod->klass);
if (mono_use_security_manager) {
- check_linkdemand (cfg, method, cmethod, bblock, ip);
+ if (check_linkdemand (cfg, method, cmethod, bblock, ip))
+ INLINE_FAILURE;
}
handle_loaded_temps (cfg, bblock, stack_start, sp);
mono_class_init (cmethod->klass);
if (mono_use_security_manager) {
- check_linkdemand (cfg, method, cmethod, bblock, ip);
+ if (check_linkdemand (cfg, method, cmethod, bblock, ip))
+ INLINE_FAILURE;
}
handle_loaded_temps (cfg, bblock, stack_start, sp);
UNVERIFIED;
if (ins->opcode == CEE_STOBJ) {
NEW_ARGLOADA (cfg, ins, n);
- handle_stobj (cfg, bblock, ins, *sp, ip, ins->klass, FALSE, FALSE);
+ handle_stobj (cfg, bblock, ins, *sp, ip, ins->klass, FALSE, FALSE, FALSE);
} else
MONO_ADD_INS (bblock, ins);
ip += 4;
ins->cil_code = ip;
if (ins->opcode == CEE_STOBJ) {
NEW_LOCLOADA (cfg, ins, n);
- handle_stobj (cfg, bblock, ins, *sp, ip, ins->klass, FALSE, FALSE);
+ handle_stobj (cfg, bblock, ins, *sp, ip, ins->klass, FALSE, FALSE, FALSE);
} else
MONO_ADD_INS (bblock, ins);
ip += 4;
NEW_PCONST (cfg, load, NULL);
load->cil_code = ip;
load->type = STACK_OBJ;
+ load->klass = klass;
MONO_INST_NEW (cfg, store, CEE_STIND_REF);
store->cil_code = ip;
handle_loaded_temps (cfg, bblock, stack_start, sp);
#ifdef MONO_ARCH_HAVE_CREATE_SPECIFIC_TRAMPOLINE
code = mono_arch_create_specific_trampoline (method, MONO_TRAMPOLINE_JUMP, mono_domain_get (), &code_size);
- ji = g_new0 (MonoJitInfo, 1);
+ mono_domain_lock (domain);
+ ji = mono_mempool_alloc0 (domain->mp, sizeof (MonoJitInfo));
+ mono_domain_unlock (domain);
ji->code_start = code;
ji->code_size = code_size;
ji->method = method;
}
}
+/**
+ * mono_patch_info_dup_mp:
+ *
+ * Make a copy of PATCH_INFO, allocating memory from the mempool MP.
+ */
+MonoJumpInfo*
+mono_patch_info_dup_mp (MonoMemPool *mp, MonoJumpInfo *patch_info)
+{
+ MonoJumpInfo *res = mono_mempool_alloc (mp, sizeof (MonoJumpInfo));
+ memcpy (res, patch_info, sizeof (MonoJumpInfo));
+
+ switch (patch_info->type) {
+ case MONO_PATCH_INFO_LDSTR:
+ case MONO_PATCH_INFO_TYPE_FROM_HANDLE:
+ case MONO_PATCH_INFO_LDTOKEN:
+ case MONO_PATCH_INFO_DECLSEC:
+ res->data.token = mono_mempool_alloc (mp, sizeof (MonoJumpInfoToken));
+ memcpy (res->data.token, patch_info->data.token, sizeof (MonoJumpInfoToken));
+ break;
+ case MONO_PATCH_INFO_SWITCH:
+ res->data.table = mono_mempool_alloc (mp, sizeof (MonoJumpInfoBBTable));
+ memcpy (res->data.table, patch_info->data.table, sizeof (MonoJumpInfoBBTable));
+ break;
+ default:
+ break;
+ }
+
+ return res;
+}
+
gpointer
mono_resolve_patch_target (MonoMethod *method, MonoDomain *domain, guint8 *code, MonoJumpInfo *patch_info, gboolean run_cctors)
{
if ((previous_bb == cfg->bb_entry) && (bb->next_bb != target_bb)) {
return FALSE;
}
+
+ /*
+ * Do not touch BBs following a try block as the code in
+ * mini_method_compile needs them to compute the length of the try block.
+ */
+ if (MONO_BBLOCK_IS_IN_REGION (previous_bb, MONO_REGION_TRY))
+ return FALSE;
/* Check that there is a target BB, and that bb is not an empty loop (Bug 75061) */
if ((target_bb != NULL) && (target_bb != bb)) {
replace_basic_block (bb, bbn, bb);
+ /* Nullify branch at the end of bb */
+ if (bb->last_ins && MONO_IS_BRANCH_OP (bb->last_ins)) {
+ bb->last_ins->opcode = CEE_NOP;
+ }
+
if (bb->last_ins) {
if (bbn->code) {
bb->last_ins->next = bbn->code;
if (parts == 3)
return cfg;
+ if (cfg->verbose_level > 4) {
+ printf ("BEFORE DECOMPSE START\n");
+ mono_print_code (cfg);
+ printf ("BEFORE DECOMPSE END\n");
+ }
+
decompose_pass (cfg);
if (cfg->got_var) {
MonoMethod *nm;
MonoMethodPInvoke* piinfo = (MonoMethodPInvoke *) method;
- if (method->iflags & METHOD_IMPL_ATTRIBUTE_NATIVE)
+ if (method->iflags & METHOD_IMPL_ATTRIBUTE_NATIVE && !MONO_CLASS_IS_IMPORT(method->klass))
g_error ("Method '%s' in assembly '%s' contains native code and mono can't run it. The assembly was probably created by Managed C++.\n", mono_method_full_name (method, TRUE), method->klass->image->name);
if (!piinfo->addr) {
mono_destroy_compile (cfg);
if (error) {
- MonoException *ex = mini_loader_error_to_exception (error);
- mono_loader_clear_error ();
+ MonoException *ex = mono_loader_error_prepare_exception (error);
mono_raise_exception (ex);
} else {
g_assert_not_reached ();
mono_domain_lock (domain);
g_hash_table_remove (domain->dynamic_code_hash, method);
g_hash_table_remove (domain->jit_code_hash, method);
+ g_hash_table_remove (domain->jump_trampoline_hash, method);
mono_domain_unlock (domain);
#ifdef MONO_ARCH_HAVE_INVALIDATE_METHOD
#ifdef __sparc
#define GET_CONTEXT \
void *ctx = context;
-#elif defined(sun) // Solaris x86
-#define GET_CONTEXT \
- ucontext_t *uctx = context; \
- struct sigcontext *ctx = (struct sigcontext *)&(uctx->uc_mcontext);
#elif defined (MONO_ARCH_USE_SIGACTION)
#define GET_CONTEXT \
void *ctx = context;
MonoDomain *domain;
/* Happens when using the embedding interface */
- if (default_opt == 0)
+ if (!default_opt_set)
default_opt = mono_parse_default_optimizations (NULL);
InitializeCriticalSection (&jit_mutex);
pthread_attr_t attr;
pthread_getattr_np (pthread_self (), &attr);
pthread_attr_getstack (&attr, &sstart, &size);
+ pthread_attr_destroy (&attr);
/*g_print ("stackbottom pth is: %p\n", (char*)sstart + size);*/
#ifdef __ia64__
/*
register_icall (mono_helper_ldstr, "helper_ldstr", "object ptr int", FALSE);
register_icall (mono_helper_ldstr_mscorlib, "helper_ldstr_mscorlib", "object int", FALSE);
register_icall (mono_helper_newobj_mscorlib, "helper_newobj_mscorlib", "object int", FALSE);
+ register_icall (mono_value_copy, "mono_value_copy", "void ptr ptr ptr", FALSE);
#endif
#define JIT_RUNTIME_WORKS
mono_cleanup ();
+ mono_trace_cleanup ();
+
mono_counters_dump (-1, stdout);
+
+ TlsFree(mono_jit_tls_id);
+
+ DeleteCriticalSection (&jit_mutex);
+
+ DeleteCriticalSection (&mono_delegate_section);
}
void
{
mini_verbose = verbose_level;
default_opt = opts;
+ default_opt_set = TRUE;
}
static void