#include <mono/metadata/mono-config.h>
#include <mono/metadata/environment.h>
#include <mono/metadata/mono-debug.h>
-#include <mono/metadata/mono-debug-debugger.h>
#include <mono/metadata/monitor.h>
#include <mono/metadata/security-manager.h>
#include <mono/metadata/threads-types.h>
#include <mono/metadata/rawbuffer.h>
#include <mono/utils/mono-math.h>
#include <mono/utils/mono-compiler.h>
+#include <mono/utils/mono-counters.h>
#include <mono/os/gc_wrapper.h>
#include "mini.h"
#include "inssel.h"
#include "trace.h"
-#include "jit-icalls.c"
+#include "jit-icalls.h"
+
+#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
static gpointer mono_jit_compile_method_with_opt (MonoMethod *method, guint32 opt);
static gpointer mono_jit_compile_method (MonoMethod *method);
static gpointer mono_jit_find_compiled_method (MonoDomain *domain, MonoMethod *method);
+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);
int locals_offset, MonoInst *return_var, GList *dont_inline, MonoInst **inline_args,
guint inline_offset, gboolean is_virtual_call);
-extern guint8 mono_burg_arity [];
/* helper methods signature */
static MonoMethodSignature *helper_sig_class_init_trampoline = NULL;
static MonoMethodSignature *helper_sig_domain_get = NULL;
#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);
return res;
}
+G_GNUC_UNUSED char *
+mono_pmip (void *ip)
+{
+ return get_method_from_ip (ip);
+}
+
/* 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:
#define NEW_LOCLOADA(cfg,dest,num) do { \
(dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \
- (dest)->ssa_op = MONO_SSA_MAYBE_LOAD; \
+ (dest)->ssa_op = MONO_SSA_ADDRESS_TAKEN; \
(dest)->inst_i0 = (cfg)->varinfo [locals_offset + (num)]; \
(dest)->inst_i0->flags |= MONO_INST_INDIRECT; \
(dest)->opcode = OP_LDADDR; \
#define NEW_RETLOADA(cfg,dest) do { \
(dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \
- (dest)->ssa_op = MONO_SSA_MAYBE_LOAD; \
+ (dest)->ssa_op = MONO_SSA_ADDRESS_TAKEN; \
(dest)->inst_i0 = (cfg)->ret; \
(dest)->inst_i0->flags |= MONO_INST_INDIRECT; \
(dest)->opcode = cfg->ret_var_is_local ? OP_LDADDR : CEE_LDIND_I; \
#define NEW_ARGLOADA(cfg,dest,num) do { \
if (arg_array [(num)]->opcode == OP_ICONST) goto inline_failure; \
(dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \
- (dest)->ssa_op = MONO_SSA_MAYBE_LOAD; \
+ (dest)->ssa_op = MONO_SSA_ADDRESS_TAKEN; \
(dest)->inst_i0 = arg_array [(num)]; \
(dest)->inst_i0->flags |= MONO_INST_INDIRECT; \
(dest)->opcode = OP_LDADDR; \
#define NEW_TEMPLOADA(cfg,dest,num) do { \
(dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \
- (dest)->ssa_op = MONO_SSA_MAYBE_LOAD; \
+ (dest)->ssa_op = MONO_SSA_ADDRESS_TAKEN; \
(dest)->inst_i0 = (cfg)->varinfo [(num)]; \
(dest)->inst_i0->flags |= MONO_INST_INDIRECT; \
(dest)->opcode = OP_LDADDR; \
}
}
+/**
+ * mono_unlink_bblock:
+ *
+ * Unlink two basic blocks.
+ */
+void
+mono_unlink_bblock (MonoCompile *cfg, MonoBasicBlock *from, MonoBasicBlock* to)
+{
+ int i, pos;
+ gboolean found;
+
+ found = FALSE;
+ for (i = 0; i < from->out_count; ++i) {
+ if (to == from->out_bb [i]) {
+ found = TRUE;
+ break;
+ }
+ }
+ if (found) {
+ pos = 0;
+ for (i = 0; i < from->out_count; ++i) {
+ if (from->out_bb [i] != to)
+ from->out_bb [pos ++] = from->out_bb [i];
+ }
+ g_assert (pos == from->out_count - 1);
+ from->out_count--;
+ }
+
+ found = FALSE;
+ for (i = 0; i < to->in_count; ++i) {
+ if (from == to->in_bb [i]) {
+ found = TRUE;
+ break;
+ }
+ }
+ if (found) {
+ pos = 0;
+ for (i = 0; i < to->in_count; ++i) {
+ if (to->in_bb [i] != from)
+ to->in_bb [pos ++] = to->in_bb [i];
+ }
+ g_assert (pos == to->in_count - 1);
+ to->in_count--;
+ }
+}
+
/**
* mono_find_block_region:
*
int i;
array [*dfn] = start;
- /*g_print ("visit %d at %p\n", *dfn, start->cil_code);*/
+ /*g_print ("visit %d at %p (BB%ld)\n", *dfn, start->cil_code, start->block_num);*/
for (i = 0; i < start->out_count; ++i) {
if (start->out_bb [i]->dfn)
continue;
{
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:
static const char
bin_comp_table [STACK_MAX] [STACK_MAX] = {
+/* Inv i L p F & O vt */
{0},
- {0, 1, 0, 1, 0, 0, 4, 0},
- {0, 0, 1, 0, 0, 0, 0, 0},
- {0, 1, 0, 1, 0, 2, 4, 0},
- {0, 0, 0, 0, 1, 0, 0, 0},
- {0, 0, 0, 2, 0, 1, 0, 0},
- {0, 4, 0, 4, 0, 0, 3, 0},
- {0, 0, 0, 0, 0, 0, 0, 0},
+ {0, 1, 0, 1, 0, 0, 0, 0}, /* i, int32 */
+ {0, 0, 1, 0, 0, 0, 0, 0}, /* L, int64 */
+ {0, 1, 0, 1, 0, 2, 4, 0}, /* p, ptr */
+ {0, 0, 0, 0, 1, 0, 0, 0}, /* F, R8 */
+ {0, 0, 0, 2, 0, 1, 0, 0}, /* &, managed pointer */
+ {0, 0, 0, 4, 0, 0, 3, 0}, /* O, reference */
+ {0, 0, 0, 0, 0, 0, 0, 0}, /* vt value type */
};
/* reduce the size of this table */
static const char
ldind_type [] = {
- STACK_I4, STACK_I4, STACK_I4, STACK_I4, STACK_I4, STACK_I4, STACK_I8, STACK_MP, STACK_R8, STACK_R8, STACK_OBJ
+ STACK_I4, STACK_I4, STACK_I4, STACK_I4, STACK_I4, STACK_I4, STACK_I8, STACK_PTR, STACK_R8, STACK_R8, STACK_OBJ
};
/* map ldelem.x to the matching ldind.x opcode */
found = FALSE;
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;
//g_print (" %d", outb->block_num);
if (outb->in_stack) {
found = TRUE;
for (i = 0; i < bb->out_count; ++i) {
outb = bb->out_bb [i];
- if (outb->in_scount)
+ /* exception handlers are linked, but they should not be considered for stack args */
+ if (outb->flags & BB_EXCEPTION_HANDLER)
+ continue;
+ if (outb->in_scount) {
+ if (outb->in_scount != bb->out_scount)
+ G_BREAKPOINT ();
continue; /* check they are the same locals */
+ }
outb->in_scount = count;
outb->in_stack = bb->out_stack;
}
found = FALSE;
while (bindex < bb->out_count) {
outb = bb->out_bb [bindex];
+ /* exception handlers are linked, but they should not be considered for stack args */
+ if (outb->flags & BB_EXCEPTION_HANDLER) {
+ bindex++;
+ continue;
+ }
if (outb->in_stack != locals) {
/*
* Instead of storing sp [i] to locals [i], we need to store
}
}
+/*
+ * target_type_is_incompatible:
+ * @cfg: MonoCompile context
+ *
+ * Check that the item @arg on the evaluation stack can be stored
+ * in the target type (can be a local, or field, etc).
+ * The cfg arg can be used to check if we need verification or just
+ * validity checks.
+ *
+ * Returns: non-0 value if arg can't be stored on a target.
+ */
+static int
+target_type_is_incompatible (MonoCompile *cfg, MonoType *target, MonoInst *arg)
+{
+ MonoType *simple_type;
+ MonoClass *klass;
+
+ if (target->byref) {
+ /* FIXME: check that the pointed to types match */
+ if (arg->type == STACK_MP)
+ return arg->klass != mono_class_from_mono_type (target);
+ if (arg->type == STACK_PTR)
+ return 0;
+ return 1;
+ }
+ simple_type = mono_type_get_underlying_type (target);
+ switch (simple_type->type) {
+ case MONO_TYPE_VOID:
+ return 1;
+ case MONO_TYPE_I1:
+ case MONO_TYPE_U1:
+ case MONO_TYPE_BOOLEAN:
+ case MONO_TYPE_I2:
+ case MONO_TYPE_U2:
+ case MONO_TYPE_CHAR:
+ case MONO_TYPE_I4:
+ case MONO_TYPE_U4:
+ if (arg->type != STACK_I4 && arg->type != STACK_PTR)
+ return 1;
+ return 0;
+ case MONO_TYPE_PTR:
+ /* STACK_MP is needed when setting pinned locals */
+ if (arg->type != STACK_I4 && arg->type != STACK_PTR && arg->type != STACK_MP)
+ return 1;
+ return 0;
+ case MONO_TYPE_I:
+ case MONO_TYPE_U:
+ case MONO_TYPE_FNPTR:
+ if (arg->type != STACK_I4 && arg->type != STACK_PTR)
+ return 1;
+ return 0;
+ case MONO_TYPE_CLASS:
+ case MONO_TYPE_STRING:
+ case MONO_TYPE_OBJECT:
+ case MONO_TYPE_SZARRAY:
+ case MONO_TYPE_ARRAY:
+ if (arg->type != STACK_OBJ)
+ return 1;
+ /* FIXME: check type compatibility */
+ return 0;
+ case MONO_TYPE_I8:
+ case MONO_TYPE_U8:
+ if (arg->type != STACK_I8)
+ return 1;
+ return 0;
+ case MONO_TYPE_R4:
+ case MONO_TYPE_R8:
+ if (arg->type != STACK_R8)
+ return 1;
+ return 0;
+ case MONO_TYPE_VALUETYPE:
+ if (arg->type != STACK_VTYPE)
+ return 1;
+ klass = mono_class_from_mono_type (simple_type);
+ if (klass != arg->klass)
+ return 1;
+ return 0;
+ case MONO_TYPE_TYPEDBYREF:
+ if (arg->type != STACK_VTYPE)
+ return 1;
+ klass = mono_class_from_mono_type (simple_type);
+ if (klass != arg->klass)
+ return 1;
+ return 0;
+ case MONO_TYPE_GENERICINST:
+ if (mono_type_generic_inst_is_valuetype (simple_type)) {
+ if (arg->type != STACK_VTYPE)
+ return 1;
+ klass = mono_class_from_mono_type (simple_type);
+ if (klass != arg->klass)
+ return 1;
+ return 0;
+ } else {
+ if (arg->type != STACK_OBJ)
+ return 1;
+ /* FIXME: check type compatibility */
+ return 0;
+ }
+ default:
+ g_error ("unknown type 0x%02x in target_type_is_incompatible", simple_type->type);
+ }
+ return 1;
+}
+
/*
* Prepare arguments for passing to a function call.
* Return a non-zero value if the arguments can't be passed to the given
* signature.
* The type checks are not yet complete and some conversions may need
* casts on 32 or 64 bit architectures.
+ *
+ * FIXME: implement this using target_type_is_incompatible ()
*/
static int
check_call_signature (MonoCompile *cfg, MonoMethodSignature *sig, MonoInst **args)
handle_stobj (MonoCompile *cfg, MonoBasicBlock *bblock, MonoInst *dest, MonoInst *src, const unsigned char *ip, MonoClass *klass, gboolean to_end, gboolean native) {
MonoInst *iargs [3];
int n;
- int align = 0;
+ guint32 align = 0;
MonoMethod *memcpy_method;
g_assert (klass);
return mono_emit_jit_icall (cfg, bblock, alloc_ftn, iargs, ip);
}
+
+/**
+ * Handles unbox of a Nullable<T>, returning a temp variable
+ * where the result is stored
+ */
+static int
+handle_unbox_nullable (MonoCompile* cfg, MonoBasicBlock* bblock, MonoInst* val, const guchar *ip, MonoClass* klass)
+{
+ MonoMethod* method = mono_class_get_method_from_name (klass, "Unbox", 1);
+ return mono_emit_method_call_spilled (cfg, bblock, method, mono_method_signature (method), &val, ip, NULL);
+}
+
+
+
static MonoInst *
handle_box (MonoCompile *cfg, MonoBasicBlock *bblock, MonoInst *val, const guchar *ip, MonoClass *klass)
{
MonoInst *dest, *vtoffset, *add, *vstore;
int temp;
+ if (mono_class_is_nullable (klass)) {
+ MonoMethod* method = mono_class_get_method_from_name (klass, "Box", 1);
+ temp = mono_emit_method_call_spilled (cfg, bblock, method, mono_method_signature (method), &val, ip, NULL);
+ NEW_TEMPLOAD (cfg, dest, temp);
+ return dest;
+ }
+
+
temp = handle_alloc (cfg, bblock, klass, TRUE, ip);
NEW_TEMPLOAD (cfg, dest, temp);
NEW_ICONST (cfg, vtoffset, sizeof (MonoObject));
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;
rank = mono_method_signature (cmethod)->param_count - (is_set? 1: 0);
+ if (rank == 1) {
+ MONO_INST_NEW (cfg, addr, CEE_LDELEMA);
+ addr->inst_left = sp [0];
+ addr->inst_right = sp [1];
+ addr->cil_code = ip;
+ addr->type = STACK_MP;
+ addr->klass = cmethod->klass->element_class;
+ return addr;
+ }
+
if (rank == 2 && (cfg->opt & MONO_OPT_INTRINS)) {
#ifdef MONO_ARCH_EMULATE_MUL_DIV
/* OP_LDELEMA2D depends on OP_LMUL */
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)
"System.Runtime.CompilerServices", "RuntimeHelpers");
if (cmethod->klass == mono_defaults.string_class) {
- if (cmethod->name [0] != 'g')
- return NULL;
-
if (strcmp (cmethod->name, "get_Chars") == 0) {
MONO_INST_NEW (cfg, ins, OP_GETCHR);
ins->inst_i0 = args [0];
MONO_INST_NEW (cfg, ins, OP_STRLEN);
ins->inst_i0 = args [0];
return ins;
+ } else if (strcmp (cmethod->name, "InternalSetChar") == 0) {
+ MonoInst *get_addr;
+ MONO_INST_NEW (cfg, get_addr, OP_STR_CHAR_ADDR);
+ get_addr->inst_i0 = args [0];
+ get_addr->inst_i1 = args [1];
+ MONO_INST_NEW (cfg, ins, CEE_STIND_I2);
+ ins->inst_i0 = get_addr;
+ ins->inst_i1 = args [2];
+ return ins;
} else
return NULL;
} else if (cmethod->klass == mono_defaults.object_class) {
MONO_INST_NEW (cfg, ins, OP_GETTYPE);
ins->inst_i0 = args [0];
return ins;
- } else if (strcmp (cmethod->name, "InternalGetHashCode") == 0) {
-#ifdef MONO_ARCH_EMULATE_MUL_DIV
/* The OP_GETHASHCODE rule depends on OP_MUL */
-#else
+#if !defined(MONO_ARCH_EMULATE_MUL_DIV) && !defined(HAVE_MOVING_COLLECTOR)
+ } else if (strcmp (cmethod->name, "InternalGetHashCode") == 0) {
MONO_INST_NEW (cfg, ins, OP_GETHASHCODE);
ins->inst_i0 = args [0];
return ins;
return ins;
} else
return NULL;
- } else if (mini_class_is_system_array (cmethod->klass)) {
+ } else if (cmethod->klass == mono_defaults.array_class) {
if (cmethod->name [0] != 'g')
return NULL;
} else if (cmethod->klass == mono_defaults.thread_class) {
if (strcmp (cmethod->name, "get_CurrentThread") == 0 && (ins = mono_arch_get_thread_intrinsic (cfg)))
return ins;
+ } else if (mini_class_is_system_array (cmethod->klass) &&
+ strcmp (cmethod->name, "GetGenericValueImpl") == 0) {
+ MonoInst *sp [2];
+ MonoInst *ldelem, *store, *load;
+ MonoClass *eklass = mono_class_from_mono_type (fsig->params [1]);
+ int n;
+ n = mono_type_to_stind (&eklass->byval_arg);
+ if (n == CEE_STOBJ)
+ return NULL;
+ sp [0] = args [0];
+ sp [1] = args [1];
+ NEW_LDELEMA (cfg, ldelem, sp, eklass);
+ ldelem->flags |= MONO_INST_NORANGECHECK;
+ MONO_INST_NEW (cfg, store, n);
+ n = mono_type_to_ldind (&eklass->byval_arg);
+ MONO_INST_NEW (cfg, load, mono_type_to_ldind (&eklass->byval_arg));
+ type_to_eval_stack_type (&eklass->byval_arg, load);
+ load->inst_left = ldelem;
+ store->inst_left = args [2];
+ store->inst_right = load;
+ return store;
}
return mono_arch_get_inst_for_method (cfg, cmethod, fsig, args);
}
}
+static gboolean
+can_access_internals (MonoAssembly *accessing, MonoAssembly* accessed)
+{
+ GSList *tmp;
+ if (accessing == accessed)
+ return TRUE;
+ if (!accessed || !accessing)
+ return FALSE;
+ for (tmp = accessed->friend_assembly_names; tmp; tmp = tmp->next) {
+ MonoAssemblyName *friend = tmp->data;
+ /* Be conservative with checks */
+ if (!friend->name)
+ continue;
+ if (strcmp (accessing->aname.name, friend->name))
+ continue;
+ if (friend->public_key_token [0]) {
+ if (!accessing->aname.public_key_token [0])
+ continue;
+ if (strcmp ((char*)friend->public_key_token, (char*)accessing->aname.public_key_token))
+ continue;
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/* FIXME: check visibility of type, too */
+static gboolean
+can_access_member (MonoClass *access_klass, MonoClass *member_klass, int access_level)
+{
+ /* Partition I 8.5.3.2 */
+ /* the access level values are the same for fields and methods */
+ switch (access_level) {
+ case FIELD_ATTRIBUTE_COMPILER_CONTROLLED:
+ /* same compilation unit */
+ return access_klass->image == member_klass->image;
+ case FIELD_ATTRIBUTE_PRIVATE:
+ return access_klass == member_klass;
+ case FIELD_ATTRIBUTE_FAM_AND_ASSEM:
+ if (mono_class_has_parent (access_klass, member_klass) &&
+ can_access_internals (access_klass->image->assembly, member_klass->image->assembly))
+ return TRUE;
+ return FALSE;
+ case FIELD_ATTRIBUTE_ASSEMBLY:
+ return can_access_internals (access_klass->image->assembly, member_klass->image->assembly);
+ case FIELD_ATTRIBUTE_FAMILY:
+ if (mono_class_has_parent (access_klass, member_klass))
+ return TRUE;
+ return FALSE;
+ case FIELD_ATTRIBUTE_FAM_OR_ASSEM:
+ if (mono_class_has_parent (access_klass, member_klass))
+ return TRUE;
+ return can_access_internals (access_klass->image->assembly, member_klass->image->assembly);
+ case FIELD_ATTRIBUTE_PUBLIC:
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static gboolean
+can_access_field (MonoMethod *method, MonoClassField *field)
+{
+ /* FIXME: check all overlapping fields */
+ int can = can_access_member (method->klass, field->parent, field->type->attrs & FIELD_ATTRIBUTE_FIELD_ACCESS_MASK);
+ if (!can) {
+ MonoClass *nested = method->klass->nested_in;
+ while (nested) {
+ can = can_access_member (nested, field->parent, field->type->attrs & FIELD_ATTRIBUTE_FIELD_ACCESS_MASK);
+ if (can)
+ return TRUE;
+ nested = nested->nested_in;
+ }
+ }
+ return can;
+}
+
+static gboolean
+can_access_method (MonoMethod *method, MonoMethod *called)
+{
+ int can = can_access_member (method->klass, called->klass, called->flags & METHOD_ATTRIBUTE_MEMBER_ACCESS_MASK);
+ if (!can) {
+ MonoClass *nested = method->klass->nested_in;
+ while (nested) {
+ can = can_access_member (nested, called->klass, called->flags & METHOD_ATTRIBUTE_MEMBER_ACCESS_MASK);
+ if (can)
+ return TRUE;
+ nested = nested->nested_in;
+ }
+ }
+ /*
+ * FIXME:
+ * with generics calls to explicit interface implementations can be expressed
+ * directly: the method is private, but we must allow it. This may be opening
+ * a hole or the generics code should handle this differently.
+ * Maybe just ensure the interface type is public.
+ */
+ if ((called->flags & METHOD_ATTRIBUTE_VIRTUAL) && (called->flags & METHOD_ATTRIBUTE_FINAL))
+ return TRUE;
+ return can;
+}
/*
* mono_method_to_ir: translates IL into basic blocks containing trees
MonoGenericContainer *generic_container = NULL;
MonoType **param_types;
GList *bb_recheck = NULL, *tmp;
- int i, n, start_new_bblock, align;
+ int i, n, start_new_bblock, ialign;
int num_calls = 0, inline_costs = 0;
int breakpoint_id = 0;
+ guint32 align;
guint real_offset, num_args;
MonoBoolean security, pinvoke;
MonoSecurityManager* secman = NULL;
MonoDeclSecurityActions actions;
GSList *class_inits = NULL;
+ gboolean dont_verify, dont_verify_stloc;
+
+ /* serialization and xdomain stuff may need access to private fields and methods */
+ 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;
+ dont_verify_stloc |= method->wrapper_type == MONO_WRAPPER_UNKNOWN;
+ dont_verify_stloc |= method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED;
image = method->klass->image;
header = mono_method_get_header (method);
- generic_container = ((MonoMethodNormal *)method)->generic_container;
+ generic_container = method->generic_container;
sig = mono_method_signature (method);
num_args = sig->hasthis + sig->param_count;
ip = (unsigned char*)header->code;
}
/* handle exception clauses */
for (i = 0; i < header->num_clauses; ++i) {
- //unsigned char *p = ip;
+ MonoBasicBlock *try_bb;
MonoExceptionClause *clause = &header->clauses [i];
- GET_BBLOCK (cfg, bbhash, tblock, ip + clause->try_offset);
- tblock->real_offset = clause->try_offset;
+ GET_BBLOCK (cfg, bbhash, try_bb, ip + clause->try_offset);
+ try_bb->real_offset = clause->try_offset;
GET_BBLOCK (cfg, bbhash, tblock, ip + clause->handler_offset);
tblock->real_offset = clause->handler_offset;
+ tblock->flags |= BB_EXCEPTION_HANDLER;
+
+ link_bblock (cfg, try_bb, tblock);
+
+ if (*(ip + clause->handler_offset) == CEE_POP)
+ tblock->flags |= BB_EXCEPTION_DEAD_OBJ;
if (clause->flags == MONO_EXCEPTION_CLAUSE_FINALLY ||
clause->flags == MONO_EXCEPTION_CLAUSE_FILTER) {
MONO_INST_NEW (cfg, ins, OP_START_HANDLER);
MONO_ADD_INS (tblock, ins);
+
+ /* todo: is a fault block unsafe to optimize? */
+ if (clause->flags == MONO_EXCEPTION_CLAUSE_FAULT)
+ tblock->flags |= BB_EXCEPTION_UNSAFE;
}
+
/*g_print ("clause try IL_%04x to IL_%04x handler %d at IL_%04x to IL_%04x\n", clause->try_offset, clause->try_offset + clause->try_len, clause->flags, clause->handler_offset, clause->handler_offset + clause->handler_len);
while (p < end) {
g_print ("%s", mono_disasm_code_one (NULL, method, p, &p));
param_types [0] = method->klass->valuetype?&method->klass->this_arg:&method->klass->byval_arg;
for (n = 0; n < sig->param_count; ++n)
param_types [n + sig->hasthis] = sig->params [n];
+ for (n = 0; n < header->num_locals; ++n) {
+ if (header->locals [n]->type == MONO_TYPE_VOID && !header->locals [n]->byref)
+ goto unverified;
+ }
class_inits = NULL;
/* do this somewhere outside - not here */
handle_loaded_temps (cfg, bblock, stack_start, sp);
NEW_LOCSTORE (cfg, ins, n, *sp);
ins->cil_code = ip;
+ if (!dont_verify_stloc && target_type_is_incompatible (cfg, header->locals [n], *sp))
+ goto unverified;
if (ins->opcode == CEE_STOBJ) {
NEW_LOCLOADA (cfg, ins, n);
handle_stobj (cfg, bblock, ins, *sp, ip, ins->klass, FALSE, FALSE);
NEW_ARGSTORE (cfg, ins, ip [1], *sp);
handle_loaded_temps (cfg, bblock, stack_start, sp);
ins->cil_code = ip;
+ if (!dont_verify_stloc && target_type_is_incompatible (cfg, param_types [ip [1]], *sp))
+ goto unverified;
if (ins->opcode == CEE_STOBJ) {
NEW_ARGLOADA (cfg, ins, ip [1]);
handle_stobj (cfg, bblock, ins, *sp, ip, ins->klass, FALSE, FALSE);
CHECK_LOCAL (ip [1]);
NEW_LOCSTORE (cfg, ins, ip [1], *sp);
ins->cil_code = ip;
+ if (!dont_verify_stloc && target_type_is_incompatible (cfg, header->locals [ip [1]], *sp))
+ goto unverified;
if (ins->opcode == CEE_STOBJ) {
NEW_LOCLOADA (cfg, ins, ip [1]);
handle_stobj (cfg, bblock, ins, *sp, ip, ins->klass, FALSE, FALSE);
if (!cmethod)
goto load_error;
+ if (!dont_verify && !can_access_method (method, cmethod))
+ goto unverified;
if (!virtual && (cmethod->flags & METHOD_ATTRIBUTE_ABSTRACT))
/* MS.NET seems to silently convert this to a callvirt */
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:
CHECK_OPSIZE (2);
CHECK_STACK (1);
+ if (sp [-1]->type == STACK_VTYPE || sp [-1]->type == STACK_R8)
+ goto unverified;
MONO_INST_NEW (cfg, ins, *ip + BIG_BRANCH_OFFSET);
ins->cil_code = ip++;
target = ip + 1 + *(signed char*)ip;
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:
CHECK_OPSIZE (5);
CHECK_STACK (1);
+ if (sp [-1]->type == STACK_VTYPE || sp [-1]->type == STACK_R8)
+ goto unverified;
MONO_INST_NEW (cfg, ins, *ip);
ins->cil_code = ip++;
target = ip + 4 + (gint32)read32(ip);
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);
}
klass = mini_get_class (method, token, generic_context);
if (!klass)
goto load_error;
+ if (sp [0]->type != STACK_OBJ)
+ goto unverified;
/* Needed by the code generated in inssel.brg */
mono_get_got_var (cfg);
sp++;
bblock = ebblock;
inline_costs += costs;
-
- }
- else {
+ } else {
MONO_INST_NEW (cfg, ins, *ip);
ins->type = STACK_OBJ;
ins->inst_left = *sp;
ins->inst_newa_class = klass;
+ ins->klass = klass;
ins->cil_code = ip;
*sp++ = emit_tree (cfg, bblock, ins, ip + 5);
ip += 5;
sp++;
bblock = ebblock;
inline_costs += costs;
- }
- else {
+ } else {
MONO_INST_NEW (cfg, ins, CEE_CASTCLASS);
ins->type = STACK_OBJ;
ins->inst_left = *sp;
break;
}
+ if (mono_class_is_nullable (klass)) {
+ int v = handle_unbox_nullable (cfg, bblock, *sp, ip, klass);
+ NEW_TEMPLOAD (cfg, *sp, v);
+ sp ++;
+ ip += 5;
+ break;
+ }
+
MONO_INST_NEW (cfg, ins, OP_UNBOXCAST);
ins->type = STACK_OBJ;
ins->inst_left = *sp;
if (!klass)
goto load_error;
+ if (mono_class_is_nullable (klass)) {
+ int v = handle_unbox_nullable (cfg, bblock, *sp, ip, klass);
+ NEW_TEMPLOAD (cfg, *sp, v);
+ sp ++;
+ ip += 5;
+ break;
+ }
+
/* Needed by the code generated in inssel.brg */
mono_get_got_var (cfg);
add->inst_left = ins;
add->inst_right = vtoffset;
add->type = STACK_MP;
+ add->klass = klass;
*sp++ = add;
ip += 5;
inline_costs += 2;
klass = mini_get_class (method, token, generic_context);
if (!klass)
goto load_error;
+ if (sp [0]->type != STACK_OBJ)
+ goto unverified;
/* Needed by the code generated in inssel.brg */
mono_get_got_var (cfg);
sp++;
bblock = ebblock;
inline_costs += costs;
- }
- else {
+ } else {
MONO_INST_NEW (cfg, ins, *ip);
ins->type = STACK_OBJ;
ins->inst_left = *sp;
ins->cil_code = ip - 1;
MONO_ADD_INS (bblock, ins);
sp = stack_start;
+
link_bblock (cfg, bblock, end_bblock);
start_new_bblock = 1;
mono_get_got_var (cfg);
CHECK_STACK (1);
--sp;
}
- // FIXME: enable this test later.
- //if (sp [0]->type != STACK_OBJ && sp [0]->type != STACK_MP)
- // goto unverified;
+ if (sp [0]->type == STACK_I4 || sp [0]->type == STACK_I8 || sp [0]->type == STACK_R8)
+ goto unverified;
+ if (*ip != CEE_LDFLD && sp [0]->type == STACK_VTYPE)
+ goto unverified;
CHECK_OPSIZE (5);
token = read32 (ip + 1);
- field = mono_field_from_token (image, token, &klass, generic_context);
+ if (method->wrapper_type != MONO_WRAPPER_NONE) {
+ field = mono_method_get_wrapper_data (method, token);
+ klass = field->parent;
+ } else {
+ field = mono_field_from_token (image, token, &klass, generic_context);
+ }
if (!field)
goto load_error;
mono_class_init (klass);
+ if (!dont_verify && !can_access_field (method, field))
+ goto unverified;
foffset = klass->valuetype? field->offset - sizeof (MonoObject): field->offset;
/* FIXME: mark instructions for use in SSA */
if (*ip == CEE_STFLD) {
+ if (target_type_is_incompatible (cfg, field->type, sp [1]))
+ goto unverified;
if ((klass->marshalbyref && !MONO_CHECK_THIS (sp [0])) || klass->contextbound || klass == mono_defaults.marshalbyrefobject_class) {
MonoMethod *stfld_wrapper = mono_marshal_get_stfld_wrapper (field->type);
MonoInst *iargs [5];
}
} else {
if ((klass->marshalbyref && !MONO_CHECK_THIS (sp [0])) || klass->contextbound || klass == mono_defaults.marshalbyrefobject_class) {
- MonoMethod *ldfld_wrapper = mono_marshal_get_ldfld_wrapper (field->type);
+ MonoMethod *wrapper = (*ip == CEE_LDFLDA) ? mono_marshal_get_ldflda_wrapper (field->type) : mono_marshal_get_ldfld_wrapper (field->type);
MonoInst *iargs [4];
int temp;
NEW_CLASSCONST (cfg, iargs [1], klass);
NEW_FIELDCONST (cfg, iargs [2], field);
NEW_ICONST (cfg, iargs [3], klass->valuetype ? field->offset - sizeof (MonoObject) : field->offset);
- if ((cfg->opt & MONO_OPT_INLINE) && !MONO_TYPE_ISSTRUCT (mono_method_signature (ldfld_wrapper)->ret)) {
- costs = inline_method (cfg, ldfld_wrapper, mono_method_signature (ldfld_wrapper), bblock,
+ if ((cfg->opt & MONO_OPT_INLINE) && !MONO_TYPE_ISSTRUCT (mono_method_signature (wrapper)->ret)) {
+ costs = inline_method (cfg, wrapper, mono_method_signature (wrapper), bblock,
iargs, ip, real_offset, dont_inline, &ebblock, TRUE);
g_assert (costs > 0);
temp = iargs [0]->inst_i0->inst_c0;
- if (*ip == CEE_LDFLDA) {
- /* not sure howto handle this */
- NEW_TEMPLOADA (cfg, *sp, temp);
- } else {
- NEW_TEMPLOAD (cfg, *sp, temp);
- }
+ NEW_TEMPLOAD (cfg, *sp, temp);
sp++;
/* indicates start of a new block, and triggers a load of
inline_costs += costs;
break;
} else {
- temp = mono_emit_method_call_spilled (cfg, bblock, ldfld_wrapper, mono_method_signature (ldfld_wrapper), iargs, ip, NULL);
- if (*ip == CEE_LDFLDA) {
- /* not sure howto handle this */
- NEW_TEMPLOADA (cfg, *sp, temp);
- } else {
- NEW_TEMPLOAD (cfg, *sp, temp);
- }
+ temp = mono_emit_method_call_spilled (cfg, bblock, wrapper, mono_method_signature (wrapper), iargs, ip, NULL);
+ NEW_TEMPLOAD (cfg, *sp, temp);
sp++;
}
} else {
ins->type = STACK_MP;
if (*ip == CEE_LDFLDA) {
+ ins->klass = mono_class_from_mono_type (field->type);
*sp++ = ins;
} else {
MonoInst *load;
CHECK_OPSIZE (5);
token = read32 (ip + 1);
-
- field = mono_field_from_token (image, token, &klass, generic_context);
+ if (method->wrapper_type != MONO_WRAPPER_NONE) {
+ field = mono_method_get_wrapper_data (method, token);
+ klass = field->parent;
+ }
+ else
+ field = mono_field_from_token (image, token, &klass, generic_context);
if (!field)
goto load_error;
mono_class_init (klass);
/* 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;
ip += 5;
break;
}
- *sp++ = handle_box (cfg, bblock, val, ip, klass);
- ip += 5;
- inline_costs += 1;
+ if (klass == mono_defaults.void_class)
+ goto unverified;
+ if (target_type_is_incompatible (cfg, &klass->byval_arg, *sp))
+ goto 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)) {
+ /*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);
+ ins->cil_code = ip;
+ ins->inst_i0 = *sp;
+ ip += 5;
+ MONO_INST_NEW (cfg, ins, CEE_BR);
+ ins->cil_code = ip;
+ MONO_ADD_INS (bblock, ins);
+ if (*ip == CEE_BRTRUE_S) {
+ CHECK_OPSIZE (2);
+ ip++;
+ target = ip + 1 + (signed char)(*ip);
+ ip++;
+ } else {
+ CHECK_OPSIZE (5);
+ ip++;
+ target = ip + 4 + (gint)(read32 (ip));
+ ip += 4;
+ }
+ GET_BBLOCK (cfg, bbhash, tblock, target);
+ link_bblock (cfg, bblock, tblock);
+ CHECK_BBLOCK (target, ip, tblock);
+ ins->inst_target_bb = tblock;
+ if (sp != stack_start) {
+ handle_stack_args (cfg, bblock, stack_start, sp - stack_start);
+ sp = stack_start;
+ }
+ start_new_bblock = 1;
+ break;
+ }
+ *sp++ = handle_box (cfg, bblock, val, ip, klass);
+ ip += 5;
+ inline_costs += 1;
break;
}
case CEE_NEWARR:
break;
case CEE_LDLEN:
CHECK_STACK (1);
+ --sp;
+ if (sp [0]->type != STACK_OBJ)
+ goto unverified;
MONO_INST_NEW (cfg, ins, *ip);
ins->cil_code = ip++;
- --sp;
ins->inst_left = *sp;
ins->type = STACK_PTR;
*sp++ = ins;
CHECK_STACK (2);
sp -= 2;
CHECK_OPSIZE (5);
+ if (sp [0]->type != STACK_OBJ)
+ goto unverified;
klass = mini_get_class (method, read32 (ip + 1), generic_context);
if (!klass)
MonoInst *load;
CHECK_STACK (2);
sp -= 2;
+ if (sp [0]->type != STACK_OBJ)
+ goto unverified;
CHECK_OPSIZE (5);
token = read32 (ip + 1);
klass = mono_class_get_full (image, token, generic_context);
*/
CHECK_STACK (2);
sp -= 2;
+ if (sp [0]->type != STACK_OBJ)
+ goto unverified;
klass = array_access_to_klass (*ip);
NEW_LDELEMA (cfg, load, sp, klass);
load->cil_code = ip;
*/
CHECK_STACK (3);
sp -= 3;
+ if (sp [0]->type != STACK_OBJ)
+ goto unverified;
klass = array_access_to_klass (*ip);
NEW_LDELEMA (cfg, load, sp, klass);
load->cil_code = ip;
*/
CHECK_STACK (3);
sp -= 3;
+ if (sp [0]->type != STACK_OBJ)
+ goto unverified;
CHECK_OPSIZE (5);
token = read32 (ip + 1);
klass = mono_class_get_full (image, token, generic_context);
CHECK_STACK (3);
sp -= 3;
+ if (sp [0]->type != STACK_OBJ)
+ goto unverified;
+ if (sp [2]->type != STACK_OBJ)
+ goto unverified;
handle_loaded_temps (cfg, bblock, stack_start, sp);
if (method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD) {
handle = mono_method_get_wrapper_data (method, n);
handle_class = mono_method_get_wrapper_data (method, n + 1);
+ if (handle_class == mono_defaults.typehandle_class)
+ handle = &((MonoClass*)handle)->byval_arg;
}
else {
handle = mono_ldtoken (image, n, &handle_class, generic_context);
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;
CHECK_ARG (n);
NEW_ARGSTORE (cfg, ins, n, *sp);
ins->cil_code = ip;
+ if (!dont_verify_stloc && target_type_is_incompatible (cfg, param_types [n], *sp))
+ goto unverified;
if (ins->opcode == CEE_STOBJ) {
NEW_ARGLOADA (cfg, ins, n);
handle_stobj (cfg, bblock, ins, *sp, ip, ins->klass, FALSE, FALSE);
CHECK_LOCAL (n);
handle_loaded_temps (cfg, bblock, stack_start, sp);
NEW_LOCSTORE (cfg, ins, n, *sp);
+ if (!dont_verify_stloc && target_type_is_incompatible (cfg, header->locals [n], *sp))
+ goto unverified;
ins->cil_code = ip;
if (ins->opcode == CEE_STOBJ) {
NEW_LOCLOADA (cfg, ins, n);
MONO_INST_NEW (cfg, ins, OP_LOCALLOC);
ins->inst_left = *sp;
ins->cil_code = ip;
- ins->type = STACK_MP;
+ ins->type = STACK_PTR;
cfg->flags |= MONO_CFG_HAS_ALLOCA;
if (header->init_locals)
handler_offset = clause->handler_offset;
}
+ bblock->flags |= BB_EXCEPTION_UNSAFE;
+
g_assert (handler_offset != -1);
NEW_TEMPLOAD (cfg, load, mono_find_exvar_for_offset (cfg, handler_offset)->inst_c0);
/* FIXXME: handle generics. */
if (mono_metadata_token_table (token) == MONO_TABLE_TYPESPEC) {
MonoType *type = mono_type_create_from_typespec (image, token);
- token = mono_type_size (type, &align);
+ token = mono_type_size (type, &ialign);
} else {
MonoClass *klass = mono_class_get_full (image, token, generic_context);
if (!klass)
NEW_LOCSTORE (cfg, store, i, ins);
MONO_ADD_INS (init_localsbb, store);
} else if ((t == MONO_TYPE_VALUETYPE) || (t == MONO_TYPE_TYPEDBYREF) ||
- ((t == MONO_TYPE_GENERICINST) && mono_metadata_generic_class_is_valuetype (ptype->data.generic_class))) {
+ ((t == MONO_TYPE_GENERICINST) && mono_type_generic_inst_is_valuetype (ptype))) {
NEW_LOCLOADA (cfg, ins, i);
handle_initobj (cfg, init_localsbb, ins, NULL, mono_class_from_mono_type (ptype), NULL, NULL);
} else {
g_slist_free (class_inits);
dont_inline = g_list_remove (dont_inline, method);
+
+ if (inline_costs < 0) {
+ char *mname;
+
+ /* Method is too large */
+ mname = mono_method_full_name (method, TRUE);
+ cfg->exception_type = MONO_EXCEPTION_INVALID_PROGRAM;
+ cfg->exception_message = g_strdup_printf ("Method %s is too complex.", mname);
+ g_free (mname);
+ return -1;
+ }
+
return inline_costs;
inline_failure:
g_hash_table_destroy (bbhash);
g_slist_free (class_inits);
dont_inline = g_list_remove (dont_inline, method);
+ cfg->exception_type = MONO_EXCEPTION_TYPE_LOAD;
return -1;
unverified:
if (cfg->method != method)
g_hash_table_destroy (bbhash);
g_slist_free (class_inits);
- g_error ("Invalid IL code at IL%04x in %s: %s\n", (int)(ip - header->code),
- mono_method_full_name (method, TRUE), mono_disasm_code_one (NULL, method, ip, NULL));
dont_inline = g_list_remove (dont_inline, method);
+ cfg->exception_type = MONO_EXCEPTION_INVALID_PROGRAM;
+ cfg->exception_message = g_strdup_printf ("Invalid IL code in %s: %s\n",
+ mono_method_full_name (method, TRUE), mono_disasm_code_one (NULL, method, ip, NULL));
return -1;
}
{
char *name;
MonoMethod *wrapper;
- gconstpointer code;
- if (callinfo->wrapper)
+ if (callinfo->wrapper) {
return callinfo->wrapper;
-
+ }
+
+ if (callinfo->trampoline)
+ return callinfo->trampoline;
+
name = g_strdup_printf ("__icall_wrapper_%s", callinfo->name);
wrapper = mono_marshal_get_icall_wrapper (callinfo->sig, name, callinfo->func);
- /* Must be domain neutral since there is only one copy */
- code = mono_jit_compile_method_with_opt (wrapper, default_opt | MONO_OPT_SHARED);
+ g_free (name);
- if (!callinfo->wrapper) {
- callinfo->wrapper = code;
- mono_register_jit_icall_wrapper (callinfo, code);
- mono_debug_add_icall_wrapper (wrapper, callinfo);
- }
+ callinfo->trampoline = mono_create_ftnptr (mono_get_root_domain (), mono_create_jit_trampoline_in_domain (mono_get_root_domain (), wrapper));
+ mono_register_jit_icall_wrapper (callinfo, callinfo->trampoline);
- g_free (name);
- return callinfo->wrapper;
+ return callinfo->trampoline;
}
static void
return ji->code_start;
}
-gpointer
-mono_create_jit_trampoline (MonoMethod *method)
+static gpointer
+mono_create_jit_trampoline_in_domain (MonoDomain *domain, MonoMethod *method)
{
- MonoDomain *domain = mono_domain_get ();
gpointer tramp;
mono_domain_lock (domain);
return mono_create_jit_trampoline (mono_marshal_get_synchronized_wrapper (method));
#ifdef MONO_ARCH_HAVE_CREATE_SPECIFIC_TRAMPOLINE
- tramp = mono_arch_create_specific_trampoline (method, MONO_TRAMPOLINE_GENERIC, mono_domain_get (), NULL);
+ tramp = mono_arch_create_specific_trampoline (method, MONO_TRAMPOLINE_GENERIC, domain, NULL);
#else
tramp = mono_arch_create_jit_trampoline (method);
#endif
return tramp;
}
+gpointer
+mono_create_jit_trampoline (MonoMethod *method)
+{
+ return mono_create_jit_trampoline_in_domain (mono_domain_get (), method);
+}
+
#ifdef MONO_ARCH_HAVE_CREATE_TRAMPOLINE_FROM_TOKEN
gpointer
mono_create_jit_trampoline_from_token (MonoImage *image, guint32 token)
gint32*
mono_allocate_stack_slots_full (MonoCompile *m, gboolean backward, guint32 *stack_size, guint32 *stack_align)
{
- int i, slot, offset, size, align;
+ int i, slot, offset, size;
+ guint32 align;
MonoMethodVar *vmv;
MonoInst *inst;
gint32 *offsets;
* pinvoke wrappers when they call functions returning structures */
if (inst->unused && MONO_TYPE_ISSTRUCT (inst->inst_vtype) && inst->inst_vtype->type != MONO_TYPE_TYPEDBYREF)
size = mono_class_native_size (inst->inst_vtype->data.klass, &align);
- else
- size = mono_type_size (inst->inst_vtype, &align);
+ else {
+ int ialign;
+
+ size = mono_type_size (inst->inst_vtype, &ialign);
+ align = ialign;
+ }
t = mono_type_get_underlying_type (inst->inst_vtype);
switch (t->type) {
+ case MONO_TYPE_GENERICINST:
+ if (!mono_type_generic_inst_is_valuetype (t)) {
+ slot_info = &scalar_stack_slots [t->type];
+ break;
+ }
+ /* Fall through */
case MONO_TYPE_VALUETYPE:
for (i = 0; i < nvtypes; ++i)
if (t->data.klass == vtype_stack_slots [i].vtype)
* efficient copying (and to work around the fact that OP_MEMCPY
* and OP_MEMSET ignores alignment).
*/
- if (t->type == MONO_TYPE_VALUETYPE)
+ if (MONO_TYPE_ISSTRUCT (t))
align = sizeof (gpointer);
if (backward) {
g_free (cfg->varinfo);
g_free (cfg->vars);
+ g_free (cfg->exception_message);
g_free (cfg);
}
if (jit_tls) {
mono_arch_free_jit_tls_data (jit_tls);
- g_free (jit_tls->first_lmf);
- g_free (jit_tls);
- thread->jit_data = NULL;
#ifdef MONO_ARCH_SIGSEGV_ON_ALTSTACK
mono_free_altstack (jit_tls);
#endif
+ g_free (jit_tls->first_lmf);
+ g_free (jit_tls);
+ thread->jit_data = NULL;
}
}
}
}
-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;
MonoInst *inst;
/* Do not touch handlers */
- if (bb->region != -1) return FALSE;
+ if (bb->region != -1) {
+ bb->not_useless = TRUE;
+ return FALSE;
+ }
for (inst = bb->code; inst != NULL; inst = inst->next) {
switch (inst->opcode) {
target_bb = inst->inst_target_bb;
break;
default:
+ bb->not_useless = TRUE;
return FALSE;
}
}
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) &&
(previous_bb->region == bb->region) &&
((previous_bb->last_ins == NULL) ||
}
}
+/* checks that a and b represent the same instructions, conservatively,
+ * it can return FALSE also for two trees that are equal.
+ * FIXME: also make sure there are no side effects.
+ */
+static int
+same_trees (MonoInst *a, MonoInst *b)
+{
+ int arity;
+ if (a->opcode != b->opcode)
+ return FALSE;
+ arity = mono_burg_arity [a->opcode];
+ if (arity == 1) {
+ if (a->ssa_op == b->ssa_op && a->ssa_op == MONO_SSA_LOAD && a->inst_i0 == b->inst_i0)
+ return TRUE;
+ return same_trees (a->inst_left, b->inst_left);
+ } else if (arity == 2) {
+ return same_trees (a->inst_left, b->inst_left) && same_trees (a->inst_right, b->inst_right);
+ } else if (arity == 0) {
+ switch (a->opcode) {
+ case OP_ICONST:
+ return a->inst_c0 == b->inst_c0;
+ default:
+ return FALSE;
+ }
+ }
+ return FALSE;
+}
+
+static int
+get_unsigned_condbranch (int opcode)
+{
+ switch (opcode) {
+ case CEE_BLE: return CEE_BLE_UN;
+ case CEE_BLT: return CEE_BLT_UN;
+ case CEE_BGE: return CEE_BGE_UN;
+ case CEE_BGT: return CEE_BGT_UN;
+ }
+ g_assert_not_reached ();
+ return 0;
+}
+
+static int
+tree_is_unsigned (MonoInst* ins) {
+ switch (ins->opcode) {
+ case OP_ICONST:
+ return (int)ins->inst_c0 >= 0;
+ /* array lengths are positive as are string sizes */
+ case CEE_LDLEN:
+ case OP_STRLEN:
+ return TRUE;
+ case CEE_CONV_U1:
+ case CEE_CONV_U2:
+ case CEE_CONV_U4:
+ case CEE_CONV_OVF_U1:
+ case CEE_CONV_OVF_U2:
+ case CEE_CONV_OVF_U4:
+ return TRUE;
+ case CEE_LDIND_U1:
+ case CEE_LDIND_U2:
+ case CEE_LDIND_U4:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+/* check if an unsigned compare can be used instead of two signed compares
+ * for (val < 0 || val > limit) conditionals.
+ * Returns TRUE if the optimization has been applied.
+ * Note that this can't be applied if the second arg is not positive...
+ */
+static int
+try_unsigned_compare (MonoCompile *cfg, MonoBasicBlock *bb)
+{
+ MonoBasicBlock *truet, *falset;
+ MonoInst *cmp_inst = bb->last_ins->inst_left;
+ MonoInst *condb;
+ if (!cmp_inst->inst_right->inst_c0 == 0)
+ return FALSE;
+ truet = bb->last_ins->inst_true_bb;
+ falset = bb->last_ins->inst_false_bb;
+ if (falset->in_count != 1)
+ return FALSE;
+ condb = falset->last_ins;
+ /* target bb must have one instruction */
+ if (!condb || (condb != falset->code))
+ return FALSE;
+ if ((((condb->opcode == CEE_BLE || condb->opcode == CEE_BLT) && (condb->inst_false_bb == truet))
+ || ((condb->opcode == CEE_BGE || condb->opcode == CEE_BGT) && (condb->inst_true_bb == truet)))
+ && same_trees (cmp_inst->inst_left, condb->inst_left->inst_left)) {
+ if (!tree_is_unsigned (condb->inst_left->inst_right))
+ return FALSE;
+ condb->opcode = get_unsigned_condbranch (condb->opcode);
+ /* change the original condbranch to just point to the new unsigned check */
+ bb->last_ins->opcode = CEE_BR;
+ bb->last_ins->inst_target_bb = falset;
+ replace_out_block (bb, truet, NULL);
+ replace_in_block (truet, bb, NULL);
+ return TRUE;
+ }
+ return FALSE;
+}
+
/*
* Optimizes the branches on the Control Flow Graph
*
niterations = cfg->num_bblocks * 2;
else
niterations = 1000;
+
do {
MonoBasicBlock *previous_bb;
changed = FALSE;
if (bb->region != -1)
continue;
- if (remove_block_if_useless (cfg, bb, previous_bb)) {
+ if (!bb->not_useless && remove_block_if_useless (cfg, bb, previous_bb)) {
changed = TRUE;
continue;
}
g_print ("block merge triggered %d -> %d\n", bb->block_num, bbn->block_num);
merge_basic_blocks (bb, bbn);
changed = TRUE;
+ continue;
}
//mono_print_bb_code (bb);
}
- }
+ }
}
- }
- } while (changed && (niterations > 0));
-
- niterations = 1000;
- do {
- changed = FALSE;
- niterations --;
-
- /* we skip the entry block (exit is handled specially instead ) */
- for (bb = cfg->bb_entry->next_bb; bb; bb = bb->next_bb) {
-
- /* dont touch code inside exception clauses */
- if (bb->region != -1)
- continue;
-
if ((bbn = bb->next_bb) && bbn->in_count == 0 && bb->region == bbn->region) {
if (cfg->verbose_level > 2) {
g_print ("nullify block triggered %d\n", bbn->block_num);
nullify_basic_block (bbn);
changed = TRUE;
- break;
+ continue;
}
-
if (bb->out_count == 1) {
bbn = bb->out_bb [0];
link_bblock (cfg, bb, bbn->code->inst_target_bb);
bb->last_ins->inst_target_bb = bbn->code->inst_target_bb;
changed = TRUE;
- break;
+ continue;
}
}
} else if (bb->out_count == 2) {
*/
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;
- break;
+ continue;
}
bbn = bb->last_ins->inst_true_bb;
if (bb->region == bbn->region && bbn->code && bbn->code->opcode == CEE_BR &&
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);
- if (!bbn->in_count)
- replace_in_block (bbn->code->inst_target_bb, bbn, bb);
- 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;
- break;
+ continue;
}
bbn = bb->last_ins->inst_false_bb;
bb->block_num, bbn->block_num, bbn->code->inst_target_bb->block_num,
bbn->code->opcode);
+ mono_unlink_bblock (cfg, bb, bb->last_ins->inst_true_bb);
+ mono_unlink_bblock (cfg, bb, bb->last_ins->inst_false_bb);
+
bb->last_ins->inst_false_bb = bbn->code->inst_target_bb;
- replace_in_block (bbn, bb, NULL);
- if (!bbn->in_count)
- replace_in_block (bbn->code->inst_target_bb, bbn, bb);
- replace_out_block (bb, bbn, 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);
- link_bblock (cfg, bb, bbn->code->inst_target_bb);
+ changed = TRUE;
+ continue;
+ }
+ }
+ /* detect and optimize to unsigned compares checks like: if (v < 0 || v > limit */
+ if (bb->last_ins && bb->last_ins->opcode == CEE_BLT && bb->last_ins->inst_left->inst_right->opcode == OP_ICONST) {
+ if (try_unsigned_compare (cfg, bb)) {
+ /*g_print ("applied in bb %d (->%d) %s\n", bb->block_num, bb->last_ins->inst_target_bb->block_num, mono_method_full_name (cfg->method, TRUE));*/
changed = TRUE;
- break;
+ continue;
}
}
{
MBState *kids [10];
int ern = mono_burg_rule (state, goal);
- const guint16 *nts = mono_burg_nts [ern];
+ const guint16 *nts = mono_burg_nts_data + mono_burg_nts [ern];
MBEmitFunc emit;
//g_print ("rule: %s\n", mono_burg_rule_string [ern]);
*/
;
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) {
- 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) {
}
}
+/*
+ * mini_method_compile:
+ * @method: the method to compile
+ * @opts: the optimization flags to use
+ * @domain: the domain where the method will be compiled in
+ * @run_cctors: whether we should run type ctors if possible
+ * @compile_aot: whether this is an AOT compilation
+ * @parts: debug flag
+ *
+ * Returns: a MonoCompile* pointer. Caller must check the exception_type
+ * field in the returned struct to see if compilation succeded.
+ */
MonoCompile*
mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gboolean run_cctors, gboolean compile_aot, int parts)
{
MonoCompile *cfg;
MonoJitInfo *jinfo;
int dfn = 0, i, code_size_ratio;
-
- if (!header)
- return NULL;
-
- ip = (guint8 *)header->code;
+ gboolean deadce_has_run = FALSE;
mono_jit_stats.methods_compiled++;
if (mono_profiler_get_events () & MONO_PROFILE_JIT_COMPILATION)
cfg->domain = domain;
cfg->verbose_level = mini_verbose;
cfg->compile_aot = compile_aot;
- cfg->intvars = mono_mempool_alloc0 (cfg->mempool, sizeof (guint16) * STACK_MAX *
- mono_method_get_header (method)->max_stack);
+ 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 (cfg->prof_options & MONO_PROFILE_JIT_COMPILATION)
+ mono_profiler_method_end_jit (method, NULL, MONO_PROFILE_FAILED);
+ return cfg;
+ }
+
+ ip = (guint8 *)header->code;
+ cfg->intvars = mono_mempool_alloc0 (cfg->mempool, sizeof (guint16) * STACK_MAX * header->max_stack);
+ cfg->aliasing_info = NULL;
+
if (cfg->verbose_level > 2)
g_print ("converting method %s\n", mono_method_full_name (method, TRUE));
if ((i = mono_method_to_ir (cfg, method, NULL, NULL, cfg->locals_start, NULL, NULL, NULL, 0, FALSE)) < 0) {
if (cfg->prof_options & MONO_PROFILE_JIT_COMPILATION)
- mono_profiler_method_end_jit (method, MONO_PROFILE_FAILED);
- mono_destroy_compile (cfg);
- return NULL;
+ 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;
if (bbn && bbn->region == -1 && !bbn->dfn) {
if (cfg->verbose_level > 1)
- g_print ("found unreachabel code in BB%d\n", bbn->block_num);
+ g_print ("found unreachable code in BB%d\n", bbn->block_num);
bb->next_bb = bbn->next_bb;
nullify_basic_block (bbn);
} else {
#else
/* fixme: add all optimizations which requires SSA */
- if (cfg->opt & (MONO_OPT_DEADCE | MONO_OPT_ABCREM | MONO_OPT_SSAPRE)) {
+ if (cfg->opt & (MONO_OPT_SSA | MONO_OPT_ABCREM | MONO_OPT_SSAPRE)) {
if (!(cfg->comp_done & MONO_COMP_SSA) && !header->num_clauses && !cfg->disable_ssa) {
mono_local_cprop (cfg);
mono_ssa_compute (cfg);
}
if (cfg->comp_done & MONO_COMP_SSA) {
- mono_ssa_deadce (cfg);
+ //mono_ssa_deadce (cfg);
//mono_ssa_strength_reduction (cfg);
+ if (cfg->opt & MONO_OPT_SSAPRE) {
+ mono_perform_ssapre (cfg);
+ //mono_local_cprop (cfg);
+ }
+
+ if (cfg->opt & MONO_OPT_DEADCE) {
+ mono_ssa_deadce (cfg);
+ deadce_has_run = TRUE;
+ }
+
if ((cfg->flags & MONO_CFG_HAS_LDELEMA) && (cfg->opt & MONO_OPT_ABCREM))
mono_perform_abc_removal (cfg);
- if (cfg->opt & MONO_OPT_SSAPRE)
- mono_perform_ssapre (cfg);
-
mono_ssa_remove (cfg);
if (cfg->opt & MONO_OPT_BRANCH)
if (cfg->opt & MONO_OPT_LINEARS) {
GList *vars, *regs;
+
+ /* For now, compute aliasing info only if needed for deadce... */
+ if ((cfg->opt & MONO_OPT_DEADCE) && (! deadce_has_run) && (header->num_clauses == 0)) {
+ cfg->aliasing_info = mono_build_aliasing_information (cfg);
+ }
/* fixme: maybe we can avoid to compute livenesss here if already computed ? */
cfg->comp_done &= ~MONO_COMP_LIVENESS;
if (!(cfg->comp_done & MONO_COMP_LIVENESS))
mono_analyze_liveness (cfg);
+ if (cfg->aliasing_info != NULL) {
+ mono_aliasing_deadce (cfg->aliasing_info);
+ deadce_has_run = TRUE;
+ }
+
if ((vars = mono_arch_get_allocatable_int_vars (cfg))) {
regs = mono_arch_get_global_int_regs (cfg);
if (cfg->got_var)
regs = g_list_delete_link (regs, regs);
mono_linear_scan (cfg, vars, regs, &cfg->used_int_regs);
}
+
+ if (cfg->aliasing_info != NULL) {
+ mono_destroy_aliasing_information (cfg->aliasing_info);
+ cfg->aliasing_info = NULL;
+ }
}
//mono_print_code (cfg);
mono_jit_stats.native_code_size += cfg->code_len;
if (cfg->prof_options & MONO_PROFILE_JIT_COMPILATION)
- mono_profiler_method_end_jit (method, MONO_PROFILE_OK);
-
- /* this can only be set if the security manager is active */
- if (cfg->exception_type == MONO_EXCEPTION_SECURITY_LINKDEMAND) {
- MonoAssembly *assembly = mono_image_get_assembly (method->klass->image);
- MonoReflectionAssembly *refass = (MonoReflectionAssembly*) mono_assembly_get_object (domain, assembly);
- MonoReflectionMethod *refmet = mono_method_get_object (domain, method, NULL);
- MonoSecurityManager* secman = mono_security_manager_get_methods ();
- MonoObject *exc = NULL;
- gpointer args [3];
-
- args [0] = &cfg->exception_data;
- args [1] = refass;
- args [2] = refmet;
- mono_runtime_invoke (secman->linkdemandsecurityexception, NULL, args, &exc);
-
- mono_destroy_compile (cfg);
- cfg = NULL;
-
- mono_raise_exception ((MonoException*)exc);
- }
+ mono_profiler_method_end_jit (method, jinfo, MONO_PROFILE_OK);
return cfg;
}
static gpointer
-mono_jit_compile_method_inner (MonoMethod *method, MonoDomain *target_domain)
+mono_jit_compile_method_inner (MonoMethod *method, MonoDomain *target_domain, int opt)
{
MonoCompile *cfg;
GHashTable *jit_code_hash;
gpointer code = NULL;
- guint32 opt;
MonoJitInfo *info;
- opt = default_opt;
-
jit_code_hash = target_domain->jit_code_hash;
method = mono_get_inflated_method (method);
#ifdef MONO_USE_AOT_COMPILER
- if (!mono_compile_aot && (opt & MONO_OPT_AOT)) {
+ if (!mono_compile_aot && (opt & MONO_OPT_AOT) && !(mono_profiler_get_events () & MONO_PROFILE_JIT_COMPILATION)) {
MonoJitInfo *info;
MonoDomain *domain = mono_domain_get ();
cfg = mini_method_compile (method, opt, target_domain, TRUE, FALSE, 0);
- if (!cfg) {
+ switch (cfg->exception_type) {
+ case MONO_EXCEPTION_NONE: break;
+ case MONO_EXCEPTION_TYPE_LOAD:
+ case MONO_EXCEPTION_MISSING_FIELD:
+ case MONO_EXCEPTION_MISSING_METHOD: {
/* Throw a type load exception if needed */
MonoLoaderError *error = mono_loader_get_last_error ();
+ mono_destroy_compile (cfg);
if (error) {
MonoException *ex = mini_loader_error_to_exception (error);
mono_loader_clear_error ();
mono_raise_exception (ex);
- }
- else
+ } else {
g_assert_not_reached ();
+ }
+ }
+ 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);
+ 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);
+ break;
+ }
+ /* this can only be set if the security manager is active */
+ case MONO_EXCEPTION_SECURITY_LINKDEMAND: {
+ MonoAssembly *assembly = mono_image_get_assembly (method->klass->image);
+ MonoReflectionAssembly *refass = (MonoReflectionAssembly*) mono_assembly_get_object (target_domain, assembly);
+ MonoReflectionMethod *refmet = mono_method_get_object (target_domain, method, NULL);
+ MonoSecurityManager* secman = mono_security_manager_get_methods ();
+ MonoObject *exc = NULL;
+ gpointer args [3];
+
+ args [0] = &cfg->exception_data;
+ args [1] = refass;
+ args [2] = refmet;
+ mono_runtime_invoke (secman->linkdemandsecurityexception, NULL, args, &exc);
+
+ mono_destroy_compile (cfg);
+ cfg = NULL;
+
+ mono_raise_exception ((MonoException*)exc);
+ }
+ default:
+ g_assert_not_reached ();
}
mono_domain_lock (target_domain);
static gpointer
mono_jit_compile_method_with_opt (MonoMethod *method, guint32 opt)
{
- /* FIXME: later copy the code from mono */
MonoDomain *target_domain, *domain = mono_domain_get ();
MonoJitInfo *info;
gpointer p;
+ MonoJitICallInfo *callinfo = NULL;
+
+ /*
+ * ICALL wrappers are handled specially, since there is only one copy of them
+ * shared by all appdomains.
+ */
+ if ((method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE) && (strstr (method->name, "__icall_wrapper_") == method->name)) {
+ const char *icall_name;
+
+ icall_name = method->name + strlen ("__icall_wrapper_");
+ g_assert (icall_name);
+ callinfo = mono_find_jit_icall_by_name (icall_name);
+ g_assert (callinfo);
+
+ /* Must be domain neutral since there is only one copy */
+ opt |= MONO_OPT_SHARED;
+ }
if (opt & MONO_OPT_SHARED)
target_domain = mono_get_root_domain ();
mono_domain_unlock (target_domain);
mono_jit_stats.methods_lookups++;
mono_runtime_class_init (mono_class_vtable (domain, method->klass));
- return mono_create_ftnptr (domain, info->code_start);
+ return mono_create_ftnptr (target_domain, info->code_start);
}
}
mono_domain_unlock (target_domain);
- p = mono_jit_compile_method_inner (method, target_domain);
- return mono_create_ftnptr (domain, p);
+ p = mono_create_ftnptr (target_domain, mono_jit_compile_method_inner (method, target_domain, opt));
+
+ if (callinfo) {
+ mono_jit_lock ();
+ if (!callinfo->wrapper) {
+ callinfo->wrapper = p;
+ mono_register_jit_icall_wrapper (callinfo, p);
+ mono_debug_add_icall_wrapper (method, callinfo);
+ }
+ mono_jit_unlock ();
+ }
+
+ return p;
}
static gpointer
#define GET_CONTEXT \
ucontext_t *uctx = context; \
struct sigcontext *ctx = (struct sigcontext *)&(uctx->uc_mcontext);
-#elif defined(__ppc__) || defined (__powerpc__) || defined (__s390__) || defined (MONO_ARCH_USE_SIGACTION)
+#elif defined (MONO_ARCH_USE_SIGACTION)
#define GET_CONTEXT \
void *ctx = context;
#else
SIG_HANDLER_SIGNATURE (sigill_signal_handler)
{
MonoException *exc;
- GET_CONTEXT
+ GET_CONTEXT;
+
exc = mono_get_exception_execution_engine ("SIGILL");
mono_arch_handle_exception (ctx, exc, FALSE);
SIG_HANDLER_SIGNATURE (sigsegv_signal_handler)
{
MonoException *exc = NULL;
+ MonoJitInfo *ji;
+
#ifdef MONO_ARCH_SIGSEGV_ON_ALTSTACK
MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
#endif
- GET_CONTEXT
+ GET_CONTEXT;
#ifdef MONO_ARCH_USE_SIGACTION
if (debug_options.collect_pagefault_stats) {
exc = mono_domain_get ()->null_reference_ex;
#endif
- if (debug_options.abort_on_sigsegv) {
- MonoJitInfo *ji = mono_jit_info_table_find (mono_domain_get (), mono_arch_ip_from_context(ctx));
- if (!ji) {
- fprintf (stderr, "Got SIGSEGV while in unmanaged code, and the 'abort-on-sigsegv' MONO_DEBUG option is set. Aborting...\n");
- /* Segfault in unmanaged code */
- abort ();
- }
+ ji = mono_jit_info_table_find (mono_domain_get (), mono_arch_ip_from_context(ctx));
+ if (!ji) {
+ 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)
{
gboolean running_managed;
MonoException *exc;
+ MonoThread *thread = mono_thread_current ();
void *ji;
GET_CONTEXT;
+ if (thread->thread_dump_requested) {
+ thread->thread_dump_requested = FALSE;
+
+ mono_print_thread_dump (ctx);
+ }
+
/*
* FIXME:
* This is an async signal, so the code below must not call anything which
static void
SIG_HANDLER_SIGNATURE (sigquit_signal_handler)
{
- MonoException *exc;
- GET_CONTEXT
+ GET_CONTEXT;
- exc = mono_get_exception_execution_engine ("Interrupted (SIGQUIT).");
-
- mono_arch_handle_exception (ctx, exc, FALSE);
+ printf ("Full thread dump:\n");
+
+ mono_threads_request_thread_dump ();
+
+ /*
+ * print_thread_dump () skips the current thread, since sending a signal
+ * to it would invoke the signal handler below the sigquit signal handler,
+ * and signal handlers don't create an lmf, so the stack walk could not
+ * be performed.
+ */
+ mono_print_thread_dump (ctx);
}
static void
SIG_HANDLER_SIGNATURE (sigint_signal_handler)
{
MonoException *exc;
- GET_CONTEXT
+ GET_CONTEXT;
exc = mono_get_exception_execution_engine ("Interrupted (SIGINT).");
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 */
}
debug_options.handle_sigint = TRUE;
else if (!strcmp (arg, "keep-delegates"))
debug_options.keep_delegates = TRUE;
- else if (!strcmp (arg, "abort-on-sigsegv"))
- debug_options.abort_on_sigsegv = TRUE;
else if (!strcmp (arg, "collect-pagefault-stats"))
debug_options.collect_pagefault_stats = TRUE;
else {
fprintf (stderr, "Invalid option for the MONO_DEBUG env variable: %s\n", arg);
- fprintf (stderr, "Available options: 'handle-sigint', 'keep-delegates', 'abort-on-sigsegv', 'collect-pagefault-stats'\n");
+ fprintf (stderr, "Available options: 'handle-sigint', 'keep-delegates', 'collect-pagefault-stats'\n");
exit (1);
}
}
if (getenv ("MONO_DEBUG") != NULL)
mini_parse_debug_options ();
- if (mono_running_on_valgrind ()) {
+ /*
+ * Handle the case when we are called from a thread different from the main thread,
+ * confusing libgc.
+ * FIXME: Move this to libgc where it belongs.
+ *
+ * we used to do this only when running on valgrind,
+ * but it happens also in other setups.
+ */
+#if defined(HAVE_BOEHM_GC)
+#if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK)
+ {
+ size_t size;
+ void *sstart;
+ pthread_attr_t attr;
+ pthread_getattr_np (pthread_self (), &attr);
+ pthread_attr_getstack (&attr, &sstart, &size);
+ /*g_print ("stackbottom pth is: %p\n", (char*)sstart + size);*/
+#ifdef __ia64__
+ /*
+ * The calculation above doesn't seem to work on ia64, also we need to set
+ * GC_register_stackbottom as well, but don't know how.
+ */
+#else
+ GC_stackbottom = (char*)sstart + size;
+#endif
+ }
+#elif defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
+ GC_stackbottom = (char*)pthread_get_stackaddr_np (pthread_self ());
+#else
+ {
gsize stack_bottom = (gsize)&domain;
stack_bottom += 4095;
stack_bottom &= ~4095;
+ /*g_print ("stackbottom is: %p\n", (char*)stack_bottom);*/
GC_stackbottom = (char*)stack_bottom;
}
+#endif
+#endif
MONO_GC_PRE_INIT ();
mono_jit_tls_id = TlsAlloc ();
*/
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);
}
void