X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;ds=sidebyside;f=mono%2Fmini%2Fmini.c;h=6317fa546b806aaab691baa8becb8dd717c7fa70;hb=65b83e6fae44c72b19c695d61e7e66d214d46078;hp=364349f578b9f522fe114c73bca0d33875ee7fda;hpb=699e59742843044f6efa1726b7cb64f19d909e64;p=mono.git diff --git a/mono/mini/mini.c b/mono/mini/mini.c index 364349f578b..6317fa546b8 100644 --- a/mono/mini/mini.c +++ b/mono/mini/mini.c @@ -12,6 +12,7 @@ #include #include #include +#include #ifdef sun // Solaris x86 #include @@ -46,6 +47,8 @@ #include #include #include +#include +#include #include #include "mini.h" @@ -65,6 +68,7 @@ #define MONO_CHECK_THIS(ins) (cfg->method->signature->hasthis && (ins)->ssa_op == MONO_SSA_LOAD && (ins)->inst_left->inst_c0 == 0) +static void setup_stat_profiler (void); gboolean mono_arch_print_tree(MonoInst *tree, int arity); static gpointer mono_jit_compile_method_with_opt (MonoMethod *method, guint32 opt); static gpointer mono_jit_compile_method (MonoMethod *method); @@ -104,6 +108,7 @@ static MonoMethodSignature *helper_sig_void_obj_ptr_ptr_obj = NULL; static MonoMethodSignature *helper_sig_void_ptr_ptr = NULL; static MonoMethodSignature *helper_sig_void_ptr_ptr_ptr = NULL; static MonoMethodSignature *helper_sig_ptr_ptr_ptr = NULL; +static MonoMethodSignature *helper_sig_ptr_ptr_ptr_ptr = NULL; static MonoMethodSignature *helper_sig_ptr_obj = NULL; static MonoMethodSignature *helper_sig_ptr_obj_int = NULL; static MonoMethodSignature *helper_sig_ptr_int = NULL; @@ -121,13 +126,17 @@ static MonoMethodSignature *helper_sig_int_double = NULL; static MonoMethodSignature *helper_sig_stelem_ref = NULL; static MonoMethodSignature *helper_sig_stelem_ref_check = NULL; static MonoMethodSignature *helper_sig_class_init_trampoline = NULL; +static MonoMethodSignature *helper_sig_compile_generic_method = NULL; static guint32 default_opt = MONO_OPT_PEEPHOLE; guint32 mono_jit_tls_id = -1; MonoTraceSpec *mono_jit_trace_calls = NULL; gboolean mono_break_on_exc = FALSE; +#ifndef DISABLE_AOT gboolean mono_compile_aot = FALSE; +#endif +gboolean mono_use_security_manager = FALSE; static int mini_verbose = 0; @@ -135,6 +144,10 @@ static CRITICAL_SECTION jit_mutex; static GHashTable *class_init_hash_addr = NULL; +static MonoCodeManager *global_codeman = NULL; + +static GHashTable *jit_icall_name_hash = NULL; + gboolean mono_running_on_valgrind (void) { @@ -148,6 +161,31 @@ mono_running_on_valgrind (void) #endif } +/* debug function */ +G_GNUC_UNUSED static char* +get_method_from_ip (void *ip) +{ + MonoJitInfo *ji; + char *method; + char *source; + char *res; + MonoDomain *domain = mono_domain_get (); + + ji = mono_jit_info_table_find (domain, ip); + if (!ji) { + return NULL; + } + method = mono_method_full_name (ji->method, TRUE); + source = mono_debug_source_location_from_address (ji->method, (int) ip, NULL, 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); + g_free (method); + + return res; +} + /* debug function */ G_GNUC_UNUSED static void print_method_from_ip (void *ip) @@ -165,14 +203,13 @@ print_method_from_ip (void *ip) method = mono_method_full_name (ji->method, TRUE); source = mono_debug_source_location_from_address (ji->method, (int) ip, NULL, domain); - g_print ("IP %p at offset 0x%x of method %s (%p %p)\n", ip, (char*)ip - (char*)ji->code_start, method, ji->code_start, (char*)ji->code_start + ji->code_size); + 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_free (source); g_free (method); - } G_GNUC_UNUSED void @@ -208,6 +245,28 @@ gboolean mono_method_same_domain (MonoJitInfo *caller, MonoJitInfo *callee) return TRUE; } +/* + * mono_global_codeman_reserve: + * + * Allocate code memory from the global code manager. + */ +void *mono_global_codeman_reserve (int size) +{ + void *ptr; + + if (!global_codeman) { + /* This can happen during startup */ + global_codeman = mono_code_manager_new (); + return mono_code_manager_reserve (global_codeman, size); + } + else { + EnterCriticalSection (&jit_mutex); + ptr = mono_code_manager_reserve (global_codeman, size); + LeaveCriticalSection (&jit_mutex); + return ptr; + } +} + MonoJumpInfoToken * mono_jump_info_token_new (MonoMemPool *mp, MonoImage *image, guint32 token) { @@ -249,7 +308,7 @@ mono_jump_info_token_new (MonoMemPool *mp, MonoImage *image, guint32 token) #define CHECK_BBLOCK(target,ip,tblock) do { \ if ((target) < (ip) && !(tblock)->code) { \ bb_recheck = g_list_prepend (bb_recheck, (tblock)); \ - if (cfg->verbose_level > 2) g_print ("queued block %d for check at IL%04x from IL%04x\n", (tblock)->block_num, (target) - header->code, (ip) - header->code); \ + if (cfg->verbose_level > 2) g_print ("queued block %d for check at IL%04x from IL%04x\n", (tblock)->block_num, (int)((target) - header->code), (int)((ip) - header->code)); \ } \ } while (0) @@ -260,98 +319,95 @@ mono_jump_info_token_new (MonoMemPool *mp, MonoImage *image, guint32 token) (dest)->type = STACK_I4; \ } while (0) -#if SIZEOF_VOID_P == 8 -#define NEW_PCONST(cfg,dest,val) do { \ - (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \ - (dest)->opcode = OP_I8CONST; \ - (dest)->inst_p0 = (val); \ - (dest)->type = STACK_PTR; \ - } while (0) -#else #define NEW_PCONST(cfg,dest,val) do { \ (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \ - (dest)->opcode = OP_ICONST; \ + (dest)->opcode = OP_PCONST; \ (dest)->inst_p0 = (val); \ (dest)->type = STACK_PTR; \ } while (0) -#endif -#if SIZEOF_VOID_P == 8 -#define OP_PCONST OP_I8CONST -#else -#define OP_PCONST OP_ICONST -#endif -#define NEW_CLASSCONST(cfg,dest,val) do { \ - (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \ - (dest)->opcode = mono_compile_aot ? OP_AOTCONST : OP_PCONST; \ - (dest)->inst_p0 = (val); \ - (dest)->inst_i1 = (gpointer)MONO_PATCH_INFO_CLASS; \ - (dest)->type = STACK_PTR; \ - } while (0) +#ifdef MONO_ARCH_NEED_GOT_VAR -#define NEW_IMAGECONST(cfg,dest,val) do { \ +#define NEW_PATCH_INFO(cfg,dest,el1,el2) do { \ (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \ - (dest)->opcode = mono_compile_aot ? OP_AOTCONST : OP_PCONST; \ - (dest)->inst_p0 = (val); \ - (dest)->inst_i1 = (gpointer)MONO_PATCH_INFO_IMAGE; \ - (dest)->type = STACK_PTR; \ + (dest)->opcode = OP_PATCH_INFO; \ + (dest)->inst_left = (gpointer)(el1); \ + (dest)->inst_right = (gpointer)(el2); \ } while (0) -#define NEW_FIELDCONST(cfg,dest,field) do { \ +#define NEW_AOTCONST(cfg,dest,patch_type,cons) do { \ (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \ - (dest)->opcode = mono_compile_aot ? OP_AOTCONST : OP_PCONST; \ - (dest)->inst_p0 = (field); \ - (dest)->inst_i1 = (gpointer)MONO_PATCH_INFO_FIELD; \ + (dest)->opcode = cfg->compile_aot ? OP_GOT_ENTRY : OP_PCONST; \ + if (cfg->compile_aot) { \ + MonoInst *group, *got_var; \ + NEW_TEMPLOAD ((cfg), got_var, mono_get_got_var (cfg)->inst_c0); \ + NEW_PATCH_INFO ((cfg), group, cons, patch_type); \ + (dest)->inst_p0 = got_var; \ + (dest)->inst_p1 = group; \ + } else { \ + (dest)->inst_p0 = (cons); \ + (dest)->inst_i1 = (gpointer)(patch_type); \ + } \ (dest)->type = STACK_PTR; \ - } while (0) + } while (0) -#define NEW_METHODCONST(cfg,dest,val) do { \ +#define NEW_AOTCONST_TOKEN(cfg,dest,patch_type,image,token,stack_type) do { \ + MonoInst *group, *got_var; \ (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \ - (dest)->opcode = mono_compile_aot ? OP_AOTCONST : OP_PCONST; \ - (dest)->inst_p0 = (val); \ - (dest)->inst_i1 = (gpointer)MONO_PATCH_INFO_METHODCONST; \ - (dest)->type = STACK_PTR; \ - } while (0) + (dest)->opcode = OP_GOT_ENTRY; \ + NEW_TEMPLOAD ((cfg), got_var, mono_get_got_var (cfg)->inst_c0); \ + NEW_PATCH_INFO ((cfg), group, NULL, patch_type); \ + 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); \ + } while (0) -#define NEW_VTABLECONST(cfg,dest,vtable) do { \ - (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \ - (dest)->opcode = mono_compile_aot ? OP_AOTCONST : OP_PCONST; \ - (dest)->inst_p0 = mono_compile_aot ? (gpointer)((vtable)->klass) : (vtable); \ - (dest)->inst_i1 = (gpointer)MONO_PATCH_INFO_VTABLE; \ - (dest)->type = STACK_PTR; \ - } while (0) +#else -#define NEW_SFLDACONST(cfg,dest,field) do { \ +#define NEW_AOTCONST(cfg,dest,patch_type,cons) do { \ (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \ - (dest)->opcode = mono_compile_aot ? OP_AOTCONST : OP_PCONST; \ - (dest)->inst_p0 = (field); \ - (dest)->inst_i1 = (gpointer)MONO_PATCH_INFO_SFLDA; \ + (dest)->opcode = cfg->compile_aot ? OP_AOTCONST : OP_PCONST; \ + (dest)->inst_p0 = (cons); \ + (dest)->inst_i1 = (gpointer)(patch_type); \ (dest)->type = STACK_PTR; \ - } while (0) + } while (0) -#define NEW_LDSTRCONST(cfg,dest,image,token) do { \ +#define NEW_AOTCONST_TOKEN(cfg,dest,patch_type,image,token,stack_type) 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_i1 = (gpointer)MONO_PATCH_INFO_LDSTR; \ - (dest)->type = STACK_OBJ; \ - } while (0) + (dest)->inst_p1 = (gpointer)(patch_type); \ + (dest)->type = (stack_type); \ + } while (0) -#define NEW_TYPE_FROM_HANDLE_CONST(cfg,dest,image,token) 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_i1 = (gpointer)MONO_PATCH_INFO_TYPE_FROM_HANDLE; \ - (dest)->type = STACK_OBJ; \ - } while (0) +#endif -#define NEW_LDTOKENCONST(cfg,dest,image,token) 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_i1 = (gpointer)MONO_PATCH_INFO_LDTOKEN; \ - (dest)->type = STACK_PTR; \ +#define NEW_CLASSCONST(cfg,dest,val) NEW_AOTCONST ((cfg), (dest), MONO_PATCH_INFO_CLASS, (val)) + +#define NEW_IMAGECONST(cfg,dest,val) NEW_AOTCONST ((cfg), (dest), MONO_PATCH_INFO_IMAGE, (val)) + +#define NEW_FIELDCONST(cfg,dest,val) NEW_AOTCONST ((cfg), (dest), MONO_PATCH_INFO_FIELD, (val)) + +#define NEW_METHODCONST(cfg,dest,val) NEW_AOTCONST ((cfg), (dest), MONO_PATCH_INFO_METHODCONST, (val)) + +#define NEW_VTABLECONST(cfg,dest,vtable) NEW_AOTCONST ((cfg), (dest), MONO_PATCH_INFO_VTABLE, cfg->compile_aot ? (gpointer)((vtable)->klass) : (vtable)) + +#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_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_LDTOKENCONST(cfg,dest,image,token) NEW_AOTCONST_TOKEN ((cfg), (dest), MONO_PATCH_INFO_LDTOKEN, (image), (token), STACK_PTR) + +#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); \ + } else { \ + NEW_PCONST (cfg, args [0], (entry).blob); \ + } \ } while (0) #define NEW_DOMAINCONST(cfg,dest) do { \ @@ -484,6 +540,19 @@ mono_jump_info_token_new (MonoMemPool *mp, MonoImage *image, guint32 token) (dest)->klass = (dest)->inst_i0->klass; \ } while (0) +#define NEW_DUMMY_USE(cfg,dest,load) do { \ + (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \ + (dest)->opcode = OP_DUMMY_USE; \ + (dest)->inst_left = (load); \ + } while (0) + +#define NEW_DUMMY_STORE(cfg,dest,num) do { \ + (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \ + (dest)->inst_i0 = (cfg)->varinfo [(num)]; \ + (dest)->opcode = OP_DUMMY_STORE; \ + (dest)->klass = (dest)->inst_i0->klass; \ + } while (0) + #define ADD_BINOP(op) do { \ MONO_INST_NEW (cfg, ins, (op)); \ ins->cil_code = ip; \ @@ -673,7 +742,7 @@ link_bblock (MonoCompile *cfg, MonoBasicBlock *from, MonoBasicBlock* to) * that is in none of try/catch/filter. */ static int -mono_find_block_region (MonoCompile *cfg, int offset, int *filter_lengths) +mono_find_block_region (MonoCompile *cfg, int offset) { MonoMethod *method = cfg->method; MonoMethodHeader *header = mono_method_get_header (method); @@ -684,7 +753,7 @@ mono_find_block_region (MonoCompile *cfg, int offset, int *filter_lengths) for (i = 0; i < header->num_clauses; ++i) { clause = &header->clauses [i]; if ((clause->flags == MONO_EXCEPTION_CLAUSE_FILTER) && (offset >= clause->data.filter_offset) && - (offset < (clause->data.filter_offset + filter_lengths [i]))) + (offset < (clause->handler_offset))) return ((i + 1) << 8) | MONO_REGION_FILTER | clause->flags; if (MONO_OFFSET_IN_HANDLER (clause, offset)) { @@ -751,6 +820,30 @@ mono_create_spvar_for_region (MonoCompile *cfg, int region) g_hash_table_insert (cfg->spvars, GINT_TO_POINTER (region), var); } +static MonoInst * +mono_find_exvar_for_offset (MonoCompile *cfg, int offset) +{ + return g_hash_table_lookup (cfg->exvars, GINT_TO_POINTER (offset)); +} + +static MonoInst* +mono_create_exvar_for_offset (MonoCompile *cfg, int offset) +{ + MonoInst *var; + + var = g_hash_table_lookup (cfg->exvars, GINT_TO_POINTER (offset)); + if (var) + return var; + + var = mono_compile_create_var (cfg, &mono_defaults.object_class->byval_arg, OP_LOCAL); + /* prevent it from being register allocated */ + var->flags |= MONO_INST_INDIRECT; + + g_hash_table_insert (cfg->exvars, GINT_TO_POINTER (offset), var); + + return var; +} + static void df_visit (MonoBasicBlock *start, int *dfn, MonoBasicBlock **array) { @@ -847,6 +940,40 @@ split_bblock (MonoCompile *cfg, MonoBasicBlock *first, MonoBasicBlock *second) { } } +static guint32 +reverse_branch_op (guint32 opcode) +{ + static const int reverse_map [] = { + CEE_BNE_UN, CEE_BLT, CEE_BLE, CEE_BGT, CEE_BGE, + CEE_BEQ, CEE_BLT_UN, CEE_BLE_UN, CEE_BGT_UN, CEE_BGE_UN + }; + static const int reverse_fmap [] = { + OP_FBNE_UN, OP_FBLT, OP_FBLE, OP_FBGT, OP_FBGE, + OP_FBEQ, OP_FBLT_UN, OP_FBLE_UN, OP_FBGT_UN, OP_FBGE_UN + }; + static const int reverse_lmap [] = { + OP_LBNE_UN, OP_LBLT, OP_LBLE, OP_LBGT, OP_LBGE, + OP_LBEQ, OP_LBLT_UN, OP_LBLE_UN, OP_LBGT_UN, OP_LBGE_UN + }; + static const int reverse_imap [] = { + OP_IBNE_UN, OP_IBLT, OP_IBLE, OP_IBGT, OP_IBGE, + OP_IBEQ, OP_IBLT_UN, OP_IBLE_UN, OP_IBGT_UN, OP_IBGE_UN + }; + + if (opcode >= CEE_BEQ && opcode <= CEE_BLT_UN) { + opcode = reverse_map [opcode - CEE_BEQ]; + } else if (opcode >= OP_FBEQ && opcode <= OP_FBLT_UN) { + opcode = reverse_fmap [opcode - OP_FBEQ]; + } else if (opcode >= OP_LBEQ && opcode <= OP_LBLT_UN) { + opcode = reverse_lmap [opcode - OP_LBEQ]; + } else if (opcode >= OP_IBEQ && opcode <= OP_IBLT_UN) { + opcode = reverse_imap [opcode - OP_IBEQ]; + } else + g_assert_not_reached (); + + return opcode; +} + guint mono_type_to_ldind (MonoType *type) { @@ -896,7 +1023,7 @@ handle_enum: case MONO_TYPE_TYPEDBYREF: return CEE_LDOBJ; case MONO_TYPE_GENERICINST: - type = type->data.generic_inst->generic_type; + type = &type->data.generic_class->container_class->byval_arg; goto handle_enum; default: g_error ("unknown type 0x%02x in type_to_ldind", type->type); @@ -950,7 +1077,7 @@ handle_enum: case MONO_TYPE_TYPEDBYREF: return CEE_STOBJ; case MONO_TYPE_GENERICINST: - type = type->data.generic_inst->generic_type; + type = &type->data.generic_class->container_class->byval_arg; goto handle_enum; default: g_error ("unknown type 0x%02x in type_to_stind", type->type); @@ -963,14 +1090,22 @@ handle_enum: * FIXME: return a MonoType/MonoClass for the byref and VALUETYPE cases. */ static void -type_to_eval_stack_type (MonoType *type, MonoInst *inst) { +type_to_eval_stack_type (MonoType *type, MonoInst *inst) +{ + MonoClass *klass; + if (type->byref) { inst->type = STACK_MP; return; } + klass = mono_class_from_mono_type (type); + handle_enum: switch (type->type) { + case MONO_TYPE_VOID: + inst->type = STACK_INV; + return; case MONO_TYPE_I1: case MONO_TYPE_U1: case MONO_TYPE_BOOLEAN: @@ -1007,7 +1142,7 @@ handle_enum: type = type->data.klass->enum_basetype; goto handle_enum; } else { - inst->klass = type->data.klass; + inst->klass = klass; inst->type = STACK_VTYPE; return; } @@ -1016,7 +1151,7 @@ handle_enum: inst->type = STACK_VTYPE; return; case MONO_TYPE_GENERICINST: - type = type->data.generic_inst->generic_type; + type = &type->data.generic_class->container_class->byval_arg; goto handle_enum; default: g_error ("unknown type 0x%02x in eval stack type", type->type); @@ -1398,6 +1533,25 @@ mono_get_domainvar (MonoCompile *cfg) return cfg->domainvar; } +/* + * The got_var contains the address of the Global Offset Table when AOT + * compiling. + */ +inline static MonoInst * +mono_get_got_var (MonoCompile *cfg) +{ +#ifdef MONO_ARCH_NEED_GOT_VAR + if (!cfg->compile_aot) + return NULL; + if (!cfg->got_var) { + cfg->got_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL); + } + return cfg->got_var; +#else + return NULL; +#endif +} + MonoInst* mono_compile_create_var (MonoCompile *cfg, MonoType *type, int opcode) { @@ -1430,6 +1584,39 @@ mono_compile_create_var (MonoCompile *cfg, MonoType *type, int opcode) return inst; } +/* + * Transform a MonoInst into a load from the variable of index var_index. + */ +void +mono_compile_make_var_load (MonoCompile *cfg, MonoInst *dest, gssize var_index) { + memset (dest, 0, sizeof (MonoInst)); + dest->ssa_op = MONO_SSA_LOAD; + dest->inst_i0 = cfg->varinfo [var_index]; + dest->opcode = mono_type_to_ldind (dest->inst_i0->inst_vtype); + type_to_eval_stack_type (dest->inst_i0->inst_vtype, dest); + dest->klass = dest->inst_i0->klass; +} + +/* + * Create a MonoInst that is a load from the variable of index var_index. + */ +MonoInst* +mono_compile_create_var_load (MonoCompile *cfg, gssize var_index) { + MonoInst *dest; + NEW_TEMPLOAD (cfg,dest,var_index); + return dest; +} + +/* + * Create a MonoInst that is a store of the given value into the variable of index var_index. + */ +MonoInst* +mono_compile_create_var_store (MonoCompile *cfg, gssize var_index, MonoInst *value) { + MonoInst *dest; + NEW_TEMPSTORE (cfg, dest, var_index, value); + return dest; +} + static MonoType* type_from_stack_type (MonoInst *ins) { switch (ins->type) { @@ -1446,6 +1633,11 @@ type_from_stack_type (MonoInst *ins) { return NULL; } +MonoType* +mono_type_from_stack_type (MonoInst *ins) { + return type_from_stack_type (ins); +} + static MonoClass* array_access_to_klass (int opcode) { @@ -1486,7 +1678,7 @@ array_access_to_klass (int opcode) return NULL; } -static void +void mono_add_ins_to_end (MonoBasicBlock *bb, MonoInst *inst) { MonoInst *prev; @@ -1659,7 +1851,7 @@ handle_stack_args (MonoCompile *cfg, MonoBasicBlock *bb, MonoInst **sp, int coun mono_add_ins_to_end (bb, inst); } if (cfg->verbose_level > 3) - g_print ("storing %d to temp %d\n", i, locals [i]->inst_c0); + g_print ("storing %d to temp %d\n", i, (int)locals [i]->inst_c0); } /* @@ -1738,7 +1930,7 @@ handle_enum: case MONO_TYPE_TYPEDBYREF: return calli? OP_VCALL_REG: virt? OP_VCALLVIRT: OP_VCALL; case MONO_TYPE_GENERICINST: - type = type->data.generic_inst->generic_type; + type = &type->data.generic_class->container_class->byval_arg; goto handle_enum; default: g_error ("unknown type 0x%02x in ret_type_to_call_opcode", type->type); @@ -1750,12 +1942,16 @@ void mono_create_jump_table (MonoCompile *cfg, MonoInst *label, MonoBasicBlock **bbs, int num_blocks) { MonoJumpInfo *ji = mono_mempool_alloc (cfg->mempool, sizeof (MonoJumpInfo)); + MonoJumpInfoBBTable *table; + + table = mono_mempool_alloc (cfg->mempool, sizeof (MonoJumpInfoBBTable)); + table->table = bbs; + table->table_size = num_blocks; ji->ip.label = label; ji->type = MONO_PATCH_INFO_SWITCH; - ji->data.table = bbs; + ji->data.table = table; ji->next = cfg->patch_info; - ji->table_size = num_blocks; cfg->patch_info = ji; } @@ -1775,8 +1971,11 @@ handle_loaded_temps (MonoCompile *cfg, MonoBasicBlock *bblock, MonoInst **stack, while (stack < sp) { ins = *stack; /* handle also other constants */ - if (ins->opcode != OP_ICONST) { + if ((ins->opcode != OP_ICONST) && + /* temps never get written to again, so we can safely avoid duplicating them */ + !(ins->ssa_op == MONO_SSA_LOAD && ins->inst_i0->opcode == OP_LOCAL && ins->inst_i0->flags & MONO_INST_IS_TEMP)) { temp = mono_compile_create_var (cfg, type_from_stack_type (ins), OP_LOCAL); + temp->flags |= MONO_INST_IS_TEMP; NEW_TEMPSTORE (cfg, store, temp->inst_c0, ins); store->cil_code = ins->cil_code; if (store->opcode == CEE_STOBJ) { @@ -1812,25 +2011,6 @@ check_call_signature (MonoCompile *cfg, MonoMethodSignature *sig, MonoInst **arg } for (i = 0; i < sig->param_count; ++i) { if (sig->params [i]->byref) { - /* - * check the result of ldelema is only passed as an argument if the byref - * type matches exactly the array element type. - * FIXME: if the argument as been saved on the stack as part of the - * interface variable code (the value was on the stack at a basic block boundary) - * we need to add the check in that case, too. - */ - if (args [i]->opcode == CEE_LDELEMA) { - MonoInst *check; - MonoClass *exact_class = mono_class_from_mono_type (sig->params [i]); - if (!exact_class->valuetype) { - MONO_INST_NEW (cfg, check, OP_CHECK_ARRAY_TYPE); - check->cil_code = args [i]->cil_code; - check->klass = exact_class; - check->inst_left = args [i]->inst_left; - check->type = STACK_OBJ; - args [i]->inst_left = check; - } - } if (args [i]->type != STACK_MP && args [i]->type != STACK_PTR) return 1; continue; @@ -1889,7 +2069,7 @@ handle_enum: return 1; continue; case MONO_TYPE_GENERICINST: - simple_type = simple_type->data.generic_inst->generic_type; + simple_type = &simple_type->data.generic_class->container_class->byval_arg; goto handle_enum; default: @@ -1916,9 +2096,21 @@ mono_spill_call (MonoCompile *cfg, MonoBasicBlock *bblock, MonoCallInst *call, M type_to_eval_stack_type (ret, ins); temp = mono_compile_create_var (cfg, ret, OP_LOCAL); } + + temp->flags |= MONO_INST_IS_TEMP; if (MONO_TYPE_ISSTRUCT (ret)) { - MonoInst *loada; + MonoInst *loada, *dummy_store; + + /* + * Emit a dummy store to the local holding the result so the + * liveness info remains correct. + */ + NEW_DUMMY_STORE (cfg, dummy_store, temp->inst_c0); + if (to_end) + mono_add_ins_to_end (bblock, dummy_store); + else + MONO_ADD_INS (bblock, dummy_store); /* we use this to allocate native sized structs */ temp->unused = sig->pinvoke; @@ -1964,7 +2156,8 @@ mono_emit_call_args (MonoCompile *cfg, MonoBasicBlock *bblock, MonoMethodSignatu call->args = args; call->signature = sig; call = mono_arch_call_opcode (cfg, bblock, call, virtual); - + type_to_eval_stack_type (sig->ret, &call->inst); + for (arg = call->out_args; arg;) { MonoInst *narg = arg->next; arg->next = NULL; @@ -2009,6 +2202,12 @@ mono_emit_method_call (MonoCompile *cfg, MonoBasicBlock *bblock, MonoMethod *met call->inst.flags |= MONO_INST_HAS_METHOD; call->inst.inst_left = this; + if (!virtual) + mono_get_got_var (cfg); + else if (call->method->klass->flags & TYPE_ATTRIBUTE_INTERFACE) + /* Needed by the code generated in inssel.brg */ + mono_get_got_var (cfg); + return call; } @@ -2044,6 +2243,7 @@ mono_emit_jit_icall (MonoCompile *cfg, MonoBasicBlock *bblock, gconstpointer fun g_assert_not_reached (); } + mono_get_got_var (cfg); return mono_emit_native_call (cfg, bblock, mono_icall_get_wrapper (info), info->sig, args, ip, FALSE, FALSE); } @@ -2066,8 +2266,11 @@ mono_emulate_opcode (MonoCompile *cfg, MonoInst *tree, MonoInst **iargs, MonoJit call = mono_arch_call_opcode (cfg, cfg->cbb, call, FALSE); + mono_get_got_var (cfg); + if (!MONO_TYPE_IS_VOID (info->sig->ret)) { temp = mono_compile_create_var (cfg, info->sig->ret, OP_LOCAL); + temp->flags |= MONO_INST_IS_TEMP; NEW_TEMPSTORE (cfg, store, temp->inst_c0, ins); store->cil_code = tree->cil_code; } else { @@ -2118,7 +2321,7 @@ mono_get_element_address_signature (int arity) if (!sighash) { sighash = g_hash_table_new (NULL, NULL); } - else if ((res = g_hash_table_lookup (sighash, (gpointer)arity))) { + else if ((res = g_hash_table_lookup (sighash, GINT_TO_POINTER (arity)))) { LeaveCriticalSection (&jit_mutex); return res; } @@ -2137,7 +2340,7 @@ mono_get_element_address_signature (int arity) res->ret = &mono_defaults.int_class->byval_arg; - g_hash_table_insert (sighash, (gpointer)arity, res); + g_hash_table_insert (sighash, GINT_TO_POINTER (arity), res); LeaveCriticalSection (&jit_mutex); return res; @@ -2154,7 +2357,7 @@ mono_get_array_new_va_signature (int arity) if (!sighash) { sighash = g_hash_table_new (NULL, NULL); } - else if ((res = g_hash_table_lookup (sighash, (gpointer)arity))) { + else if ((res = g_hash_table_lookup (sighash, GINT_TO_POINTER (arity)))) { LeaveCriticalSection (&jit_mutex); return res; } @@ -2173,7 +2376,7 @@ mono_get_array_new_va_signature (int arity) res->ret = &mono_defaults.int_class->byval_arg; - g_hash_table_insert (sighash, (gpointer)arity, res); + g_hash_table_insert (sighash, GINT_TO_POINTER (arity), res); LeaveCriticalSection (&jit_mutex); return res; @@ -2198,6 +2401,11 @@ handle_stobj (MonoCompile *cfg, MonoBasicBlock *bblock, MonoInst *dest, MonoInst if ((cfg->opt & MONO_OPT_INTRINS) && !to_end && n <= sizeof (gpointer) * 5) { MonoInst *inst; + if (dest->opcode == OP_LDADDR) { + /* Keep liveness info correct */ + NEW_DUMMY_STORE (cfg, inst, dest->inst_i0->inst_c0); + MONO_ADD_INS (bblock, inst); + } MONO_INST_NEW (cfg, inst, OP_MEMCPY); inst->inst_left = dest; inst->inst_right = src; @@ -2321,6 +2529,7 @@ handle_array_new (MonoCompile *cfg, MonoBasicBlock *bblock, int rank, MonoInst * { MonoMethodSignature *esig; char icall_name [256]; + char *name; MonoJitICallInfo *info; /* Need to register the icall so it gets an icall wrapper */ @@ -2329,7 +2538,12 @@ handle_array_new (MonoCompile *cfg, MonoBasicBlock *bblock, int rank, MonoInst * info = mono_find_jit_icall_by_name (icall_name); if (info == NULL) { esig = mono_get_array_new_va_signature (rank); - info = mono_register_jit_icall (mono_array_new_va, g_strdup (icall_name), esig, FALSE); + name = g_strdup (icall_name); + info = mono_register_jit_icall (mono_array_new_va, name, esig, FALSE); + + EnterCriticalSection (&jit_mutex); + g_hash_table_insert (jit_icall_name_hash, name, name); + LeaveCriticalSection (&jit_mutex); } cfg->flags |= MONO_CFG_HAS_VARARGS; @@ -2337,6 +2551,39 @@ handle_array_new (MonoCompile *cfg, MonoBasicBlock *bblock, int rank, MonoInst * return mono_emit_native_call (cfg, bblock, mono_icall_get_wrapper (info), info->sig, sp, ip, TRUE, FALSE); } +static void +mono_emit_load_got_addr (MonoCompile *cfg) +{ + MonoInst *load, *store, *dummy_use; + MonoInst *get_got; + + if (!cfg->got_var || cfg->got_var_allocated) + return; + + MONO_INST_NEW (cfg, get_got, OP_LOAD_GOTADDR); + NEW_TEMPSTORE (cfg, store, cfg->got_var->inst_c0, get_got); + + /* Add it to the start of the first bblock */ + if (cfg->bb_entry->code) { + store->next = cfg->bb_entry->code; + cfg->bb_entry->code = store; + } + else + MONO_ADD_INS (cfg->bb_entry, store); + + cfg->got_var_allocated = TRUE; + + /* + * Add a dummy use to keep the got_var alive, since real uses might + * only be generated in the decompose or instruction selection phases. + * Add it to end_bblock, so the variable's lifetime covers the whole + * method. + */ + NEW_TEMPLOAD (cfg, load, cfg->got_var->inst_c0); + NEW_DUMMY_USE (cfg, dummy_use, load); + MONO_ADD_INS (cfg->bb_exit, dummy_use); +} + #define CODE_IS_STLOC(ip) (((ip) [0] >= CEE_STLOC_0 && (ip) [0] <= CEE_STLOC_3) || ((ip) [0] == CEE_STLOC_S)) static gboolean @@ -2422,6 +2669,7 @@ mini_get_ldelema_ins (MonoCompile *cfg, MonoBasicBlock *bblock, MonoMethod *cmet MonoInst *addr; MonoMethodSignature *esig; char icall_name [256]; + char *name; MonoJitICallInfo *info; rank = cmethod->signature->param_count - (is_set? 1: 0); @@ -2444,7 +2692,12 @@ mini_get_ldelema_ins (MonoCompile *cfg, MonoBasicBlock *bblock, MonoMethod *cmet info = mono_find_jit_icall_by_name (icall_name); if (info == NULL) { esig = mono_get_element_address_signature (rank); - info = mono_register_jit_icall (ves_array_element_address, g_strdup (icall_name), esig, FALSE); + name = g_strdup (icall_name); + info = mono_register_jit_icall (ves_array_element_address, name, esig, FALSE); + + EnterCriticalSection (&jit_mutex); + g_hash_table_insert (jit_icall_name_hash, name, name); + LeaveCriticalSection (&jit_mutex); } temp = mono_emit_native_call (cfg, bblock, mono_icall_get_wrapper (info), info->sig, sp, ip, FALSE, FALSE); @@ -2466,10 +2719,9 @@ mono_find_jit_opcode_emulation (int opcode) } static MonoInst* -mini_get_opcode_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args) +mini_get_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args) { - int pc, op; - MonoInst *ins; + MonoInst *ins = NULL; static MonoClass *runtime_helpers_class = NULL; if (! runtime_helpers_class) @@ -2480,22 +2732,41 @@ mini_get_opcode_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSig if (cmethod->name [0] != 'g') return NULL; - if (strcmp (cmethod->name, "get_Chars") == 0) - op = OP_GETCHR; - else if (strcmp (cmethod->name, "get_Length") == 0) - op = OP_STRLEN; - else return NULL; + if (strcmp (cmethod->name, "get_Chars") == 0) { + MONO_INST_NEW (cfg, ins, OP_GETCHR); + ins->inst_i0 = args [0]; + ins->inst_i1 = args [1]; + return ins; + } else if (strcmp (cmethod->name, "get_Length") == 0) { + MONO_INST_NEW (cfg, ins, OP_STRLEN); + ins->inst_i0 = args [0]; + return ins; + } else + return NULL; } else if (cmethod->klass == mono_defaults.object_class) { - if (strcmp (cmethod->name, "GetType") == 0) - op = OP_GETTYPE; - else + if (strcmp (cmethod->name, "GetType") == 0) { + MONO_INST_NEW (cfg, ins, OP_GETTYPE); + ins->inst_i0 = args [0]; + return ins; + } else if (strcmp (cmethod->name, "InternalGetHashCode") == 0) { + MONO_INST_NEW (cfg, ins, OP_GETHASHCODE); + ins->inst_i0 = args [0]; + return ins; + } else return NULL; } else if (cmethod->klass == mono_defaults.array_class) { - if (strcmp (cmethod->name, "get_Rank") == 0) - op = OP_ARRAY_RANK; - else if (strcmp (cmethod->name, "get_Length") == 0) - op = CEE_LDLEN; - else + if (cmethod->name [0] != 'g') + return NULL; + + if (strcmp (cmethod->name, "get_Rank") == 0) { + MONO_INST_NEW (cfg, ins, OP_ARRAY_RANK); + ins->inst_i0 = args [0]; + return ins; + } else if (strcmp (cmethod->name, "get_Length") == 0) { + MONO_INST_NEW (cfg, ins, CEE_LDLEN); + ins->inst_i0 = args [0]; + return ins; + } else return NULL; } else if (cmethod->klass == runtime_helpers_class) { if (strcmp (cmethod->name, "get_OffsetToStringData") == 0) { @@ -2507,21 +2778,9 @@ mini_get_opcode_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSig if (strcmp (cmethod->name, "get_CurrentThread") == 0 && (ins = mono_arch_get_thread_intrinsic (cfg))) return ins; return NULL; - } else { - op = mono_arch_get_opcode_for_method (cfg, cmethod, fsig, args); - if (op < 0) - return NULL; - } - pc = fsig->param_count + fsig->hasthis; - MONO_INST_NEW (cfg, ins, op); - - if (pc > 0) { - ins->inst_i0 = args [0]; - if (pc > 1) - ins->inst_i1 = args [1]; } - return ins; + return mono_arch_get_inst_for_method (cfg, cmethod, fsig, args); } static void @@ -2675,6 +2934,14 @@ inline_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, /* offset from br.s -> br like opcodes */ #define BIG_BRANCH_OFFSET 13 +static gboolean +ip_in_bb (MonoCompile *cfg, MonoBasicBlock *bb, const guint8* ip) +{ + MonoBasicBlock *b = g_hash_table_lookup (cfg->bb_hash, ip); + + return b == NULL || b == bb; +} + static int get_basic_blocks (MonoCompile *cfg, GHashTable *bbhash, MonoMethodHeader* header, guint real_offset, unsigned char *start, unsigned char *end, unsigned char **pos) { @@ -2752,10 +3019,17 @@ unverified: } static MonoInst* -emit_tree (MonoCompile *cfg, MonoBasicBlock *bblock, MonoInst *ins) +emit_tree (MonoCompile *cfg, MonoBasicBlock *bblock, MonoInst *ins, const guint8* ip_next) { MonoInst *store, *temp, *load; + + if (ip_in_bb (cfg, bblock, ip_next) && + (CODE_IS_STLOC (ip_next) || *ip_next == CEE_BRTRUE || *ip_next == CEE_BRFALSE || + *ip_next == CEE_BRTRUE_S || *ip_next == CEE_BRFALSE_S || *ip_next == CEE_RET)) + return ins; + temp = mono_compile_create_var (cfg, type_from_stack_type (ins), OP_LOCAL); + temp->flags |= MONO_INST_IS_TEMP; NEW_TEMPSTORE (cfg, store, temp->inst_c0, ins); store->cil_code = ins->cil_code; MONO_ADD_INS (bblock, store); @@ -2764,6 +3038,17 @@ emit_tree (MonoCompile *cfg, MonoBasicBlock *bblock, MonoInst *ins) return load; } +static inline MonoMethod * +mini_get_method (MonoImage *image, guint32 token, MonoClass *klass, MonoGenericContext *context) +{ + MonoMethod *method = mono_get_method_full (image, token, klass, context); + + if (method->is_inflated) + method = mono_get_inflated_method (method); + + return method; +} + /* * mono_method_to_ir: translates IL into basic blocks containing trees */ @@ -2792,9 +3077,10 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b GList *bb_recheck = NULL, *tmp; int i, n, start_new_bblock, align; int num_calls = 0, inline_costs = 0; - int *filter_lengths = NULL; int breakpoint_id = 0; guint real_offset, num_args; + MonoBoolean security; + MonoDeclSecurityActions actions; image = method->klass->image; header = mono_method_get_header (method); @@ -2808,7 +3094,9 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b if (method->signature->is_inflated) generic_context = ((MonoMethodInflated *) method)->context; else if (generic_container) - generic_context = generic_container->context; + generic_context = &generic_container->context; + + g_assert (!method->signature->has_type_parameters); if (cfg->method == method) { real_offset = 0; @@ -2824,6 +3112,10 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b dont_inline = g_list_prepend (dont_inline, method); if (cfg->method == method) { + if (cfg->method->save_lmf) + /* Needed by the prolog code */ + mono_get_got_var (cfg); + if (cfg->prof_options & MONO_PROFILE_INS_COVERAGE) cfg->coverage_info = mono_profiler_coverage_alloc (cfg->method, header->code_size); @@ -2845,11 +3137,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b arg_array [i] = cfg->varinfo [i]; if (header->num_clauses) { - int size = sizeof (int) * header->num_clauses; - filter_lengths = alloca (size); - memset (filter_lengths, 0, size); - cfg->spvars = g_hash_table_new (NULL, NULL); + cfg->exvars = g_hash_table_new (NULL, NULL); } /* handle exception clauses */ for (i = 0; i < header->num_clauses; ++i) { @@ -2873,29 +3162,33 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b /* catch and filter blocks get the exception object on the stack */ if (clause->flags == MONO_EXCEPTION_CLAUSE_NONE || clause->flags == MONO_EXCEPTION_CLAUSE_FILTER) { + MonoInst *load, *dummy_use; + /* mostly like handle_stack_args (), but just sets the input args */ /* g_print ("handling clause at IL_%04x\n", clause->handler_offset); */ - if (!cfg->exvar) { - cfg->exvar = mono_compile_create_var (cfg, &mono_defaults.object_class->byval_arg, OP_LOCAL); - /* prevent it from being register allocated */ - cfg->exvar->flags |= MONO_INST_INDIRECT; - } tblock->in_scount = 1; tblock->in_stack = mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*)); - tblock->in_stack [0] = cfg->exvar; + tblock->in_stack [0] = mono_create_exvar_for_offset (cfg, clause->handler_offset); + + /* + * Add a dummy use for the exvar so its liveness info will be + * correct. + */ + NEW_TEMPLOAD (cfg, load, tblock->in_stack [0]->inst_c0); + NEW_DUMMY_USE (cfg, dummy_use, load); + MONO_ADD_INS (tblock, dummy_use); if (clause->flags == MONO_EXCEPTION_CLAUSE_FILTER) { GET_BBLOCK (cfg, bbhash, tblock, ip + clause->data.filter_offset); tblock->real_offset = clause->data.filter_offset; tblock->in_scount = 1; tblock->in_stack = mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*)); - tblock->in_stack [0] = cfg->exvar; + tblock->in_stack [0] = mono_create_exvar_for_offset (cfg, clause->data.filter_offset); MONO_INST_NEW (cfg, ins, OP_START_HANDLER); MONO_ADD_INS (tblock, ins); } } } - } else { arg_array = alloca (sizeof (MonoInst *) * num_args); mono_save_args (cfg, start_bblock, sig, inline_args, arg_array); @@ -2914,8 +3207,17 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b MONO_ADD_INS (bblock, ins); } } + + security = mono_use_security_manager && mono_method_has_declsec (method); + /* at this point having security doesn't mean we have any code to generate */ + if (security && (cfg->method == method)) { + /* Only Demand, NonCasDemand and DemandChoice requires code generation. + * And we do not want to enter the next section (with allocation) if we + * have nothing to generate */ + security = mono_declsec_get_demands (method, &actions); + } - if ((header->init_locals || (cfg->method == method && (cfg->opt & MONO_OPT_SHARED))) || mono_compile_aot) { + if ((header->init_locals || (cfg->method == method && (cfg->opt & MONO_OPT_SHARED))) || mono_compile_aot || security) { /* we use a separate basic block for the initialization code */ cfg->bb_init = init_localsbb = NEW_BBLOCK (cfg); init_localsbb->real_offset = real_offset; @@ -2929,12 +3231,42 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b link_bblock (cfg, start_bblock, bblock); } + /* at this point we know, if security is TRUE, that some code needs to be generated */ + if (security && (cfg->method == method)) { + MonoInst *args [2]; + MonoSecurityManager* secman = mono_security_manager_get_methods (); + + if (actions.demand.blob) { + /* Add code for SecurityAction.Demand */ + NEW_DECLSECCONST (cfg, args[0], image, actions.demand); + NEW_ICONST (cfg, args [1], actions.demand.size); + /* Calls static void SecurityManager.InternalDemand (byte* permissions, int size); */ + mono_emit_method_call_spilled (cfg, init_localsbb, secman->demand, secman->demand->signature, args, ip, NULL); + } + if (actions.noncasdemand.blob) { + /* CLR 1.x uses a .noncasdemand (but 2.x doesn't) */ + /* For Mono we re-route non-CAS Demand to Demand (as the managed code must deal with it anyway) */ + NEW_DECLSECCONST (cfg, args[0], image, actions.noncasdemand); + NEW_ICONST (cfg, args [1], actions.noncasdemand.size); + /* Calls static void SecurityManager.InternalDemand (byte* permissions, int size); */ + mono_emit_method_call_spilled (cfg, init_localsbb, secman->demand, secman->demand->signature, args, ip, NULL); + } + if (actions.demandchoice.blob) { + /* New in 2.0, Demand must succeed for one of the permissions (i.e. not all) */ + NEW_DECLSECCONST (cfg, args[0], image, actions.demandchoice); + NEW_ICONST (cfg, args [1], actions.demandchoice.size); + /* Calls static void SecurityManager.InternalDemandChoice (byte* permissions, int size); */ + mono_emit_method_call_spilled (cfg, init_localsbb, secman->demandchoice, secman->demandchoice->signature, args, ip, NULL); + } + } + if (get_basic_blocks (cfg, bbhash, header, real_offset, ip, end, &err_pos)) { ip = err_pos; goto unverified; } - mono_debug_init_method (cfg, bblock, breakpoint_id); + if (cfg->method == method) + mono_debug_init_method (cfg, bblock, breakpoint_id); param_types = mono_mempool_alloc (cfg->mempool, sizeof (MonoType*) * num_args); if (sig->hasthis) @@ -2986,7 +3318,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b start_new_bblock = 0; for (i = 0; i < bblock->in_scount; ++i) { if (cfg->verbose_level > 3) - g_print ("loading %d from temp %d\n", i, bblock->in_stack [i]->inst_c0); + g_print ("loading %d from temp %d\n", i, (int)bblock->in_stack [i]->inst_c0); NEW_TEMPLOAD (cfg, ins, bblock->in_stack [i]->inst_c0); *sp++ = ins; } @@ -3001,7 +3333,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b bblock = tblock; for (i = 0; i < bblock->in_scount; ++i) { if (cfg->verbose_level > 3) - g_print ("loading %d from temp %d\n", i, bblock->in_stack [i]->inst_c0); + g_print ("loading %d from temp %d\n", i, (int)bblock->in_stack [i]->inst_c0); NEW_TEMPLOAD (cfg, ins, bblock->in_stack [i]->inst_c0); *sp++ = ins; } @@ -3031,7 +3363,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b } if (cfg->verbose_level > 3) - g_print ("converting (in B%d: stack: %d) %s", bblock->block_num, sp-stack_start, mono_disasm_code_one (NULL, method, ip, NULL)); + g_print ("converting (in B%d: stack: %d) %s", bblock->block_num, (int)(sp - stack_start), mono_disasm_code_one (NULL, method, ip, NULL)); switch (*ip) { case CEE_NOP: @@ -3217,6 +3549,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b ++ip; readr4 (ip, f); ins->inst_p0 = f; + ip += 4; *sp++ = ins; break; @@ -3230,6 +3563,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b ++ip; readr8 (ip, d); ins->inst_p0 = d; + ip += 8; *sp++ = ins; break; @@ -3253,17 +3587,60 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b temp->cil_code = ip; *sp++ = temp; } else { - temp = mono_compile_create_var (cfg, type_from_stack_type (ins), OP_LOCAL); - temp->cil_code = ip; - NEW_TEMPSTORE (cfg, store, temp->inst_c0, ins); - store->cil_code = ip; - MONO_ADD_INS (bblock, store); - NEW_TEMPLOAD (cfg, ins, temp->inst_c0); - *sp++ = ins; - ins->cil_code = ip; - NEW_TEMPLOAD (cfg, ins, temp->inst_c0); - *sp++ = ins; - ins->cil_code = ip; + /* Common idiom: ... / dup / stloc.x */ + int ins_len = 0; + if (ip_in_bb (cfg, bblock, ip + 1)) { + switch (ip [1]) { + case CEE_STLOC_0: + case CEE_STLOC_1: + case CEE_STLOC_2: + case CEE_STLOC_3: + n = (ip [1]) - CEE_STLOC_0; + ins_len = 1; + break; + + case CEE_STLOC_S: + n = ip [2]; + ins_len = 2; + break; + + case CEE_PREFIX1: + if (ip [2] == CEE_LDLOC) { + n = read16 (ip + 3); + ins_len = 4; + } + } + } + + if (ins_len) { + CHECK_LOCAL (n); + handle_loaded_temps (cfg, bblock, stack_start, sp - 1); + NEW_LOCSTORE (cfg, ins, n, *sp); + 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); + } else + MONO_ADD_INS (bblock, ins); + + NEW_LOCLOAD (cfg, *sp, n); + (*sp)->cil_code = ip; + ip += ins_len; + sp++; + } else { + temp = mono_compile_create_var (cfg, type_from_stack_type (ins), OP_LOCAL); + temp->flags |= MONO_INST_IS_TEMP; + temp->cil_code = ip; + NEW_TEMPSTORE (cfg, store, temp->inst_c0, ins); + store->cil_code = ip; + MONO_ADD_INS (bblock, store); + NEW_TEMPLOAD (cfg, ins, temp->inst_c0); + *sp++ = ins; + ins->cil_code = ip; + NEW_TEMPLOAD (cfg, ins, temp->inst_c0); + *sp++ = ins; + ins->cil_code = ip; + } } ++ip; inline_costs += 2; @@ -3284,7 +3661,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b MONO_INST_NEW (cfg, ins, CEE_JMP); token = read32 (ip + 1); /* FIXME: check the signature matches */ - cmethod = mono_get_method_full (image, token, NULL, generic_context); + cmethod = mini_get_method (image, token, NULL, generic_context); ins->inst_p0 = cmethod; MONO_ADD_INS (bblock, ins); ip += 5; @@ -3317,8 +3694,9 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b cmethod = (MonoMethod *)mono_method_get_wrapper_data (method, token); } else if (constrained_call) { cmethod = mono_get_method_constrained (image, token, constrained_call, generic_context); + cmethod = mono_get_inflated_method (cmethod); } else { - cmethod = mono_get_method_full (image, token, NULL, generic_context); + cmethod = mini_get_method (image, token, NULL, generic_context); } g_assert (cmethod); @@ -3327,10 +3705,12 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b mono_class_init (cmethod->klass); if (cmethod->signature->pinvoke) { - MonoMethod *wrapper = mono_marshal_get_native_wrapper (cmethod); - fsig = wrapper->signature; + MonoMethod *wrapper = mono_marshal_get_native_wrapper (cmethod); + fsig = wrapper->signature; + } else if (constrained_call) { + fsig = cmethod->signature; } else { - fsig = mono_method_get_signature (cmethod, image, token); + fsig = mono_method_get_signature_full (cmethod, image, token, generic_context); } n = fsig->param_count + fsig->hasthis; @@ -3345,6 +3725,21 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b } + if (!virtual) { + mono_get_got_var (cfg); + } else { + /* code in inssel.brg might transform a virtual call to a normal call */ + if (!(cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL) || + ((cmethod->flags & METHOD_ATTRIBUTE_FINAL) && + cmethod->wrapper_type != MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK)) + mono_get_got_var (cfg); + } + + if (cmethod && cmethod->klass->generic_container) { + G_BREAKPOINT (); + goto unverified; + } + CHECK_STACK (n); //g_assert (!virtual || fsig->hasthis); @@ -3388,8 +3783,40 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b constrained_call = NULL; } - if (*ip != CEE_CALLI && check_call_signature (cfg, fsig, sp)) + if (*ip != CEE_CALLI && check_call_signature (cfg, fsig, sp)) { + G_BREAKPOINT (); goto unverified; + } + + if (cmethod && virtual && cmethod->signature->generic_param_count) { + MonoInst *this_temp, *store; + MonoInst *iargs [3]; + + g_assert (cmethod->signature->is_inflated); + + this_temp = mono_compile_create_var (cfg, type_from_stack_type (sp [0]), OP_LOCAL); + this_temp->cil_code = ip; + NEW_TEMPSTORE (cfg, store, this_temp->inst_c0, sp [0]); + + store->cil_code = ip; + MONO_ADD_INS (bblock, store); + + NEW_TEMPLOAD (cfg, iargs [0], this_temp->inst_c0); + NEW_PCONST (cfg, iargs [1], cmethod); + NEW_PCONST (cfg, iargs [2], ((MonoMethodInflated *) cmethod)->context); + temp = mono_emit_jit_icall (cfg, bblock, helper_compile_generic_method, iargs, ip); + + NEW_TEMPLOAD (cfg, addr, temp); + NEW_TEMPLOAD (cfg, sp [0], this_temp->inst_c0); + + if ((temp = mono_emit_calli (cfg, bblock, fsig, sp, addr, ip)) != -1) { + NEW_TEMPLOAD (cfg, *sp, temp); + sp++; + } + + ip += 5; + break; + } if ((ins_flag & MONO_INST_TAILCALL) && cmethod && (*ip == CEE_CALL) && (mono_metadata_signature_equal (method->signature, cmethod->signature))) { int i; @@ -3416,13 +3843,14 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b ins->inst_p0 = cmethod; ins->inst_p1 = arg_array [0]; MONO_ADD_INS (bblock, ins); + link_bblock (cfg, bblock, end_bblock); start_new_bblock = 1; /* skip CEE_RET as well */ ip += 6; ins_flag = 0; break; } - if (cmethod && (cfg->opt & MONO_OPT_INTRINS) && (ins = mini_get_opcode_for_method (cfg, cmethod, fsig, sp))) { + if (cmethod && (cfg->opt & MONO_OPT_INTRINS) && (ins = mini_get_inst_for_method (cfg, cmethod, fsig, sp))) { ins->cil_code = ip; if (MONO_TYPE_IS_VOID (fsig->ret)) { @@ -3576,7 +4004,11 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b } } else { - if (0 && CODE_IS_STLOC (ip + 5) && (!MONO_TYPE_ISSTRUCT (fsig->ret)) && (!MONO_TYPE_IS_VOID (fsig->ret) || cmethod->string_ctor)) { + if (ip_in_bb (cfg, bblock, ip + 5) + && (!MONO_TYPE_ISSTRUCT (fsig->ret)) + && (!MONO_TYPE_IS_VOID (fsig->ret) || cmethod->string_ctor) + && (CODE_IS_STLOC (ip + 5) || ip [5] == CEE_POP || ip [5] == CEE_BRTRUE || ip [5] == CEE_BRFALSE || + ip [5] == CEE_BRTRUE_S || ip [5] == CEE_BRFALSE_S || ip [5] == CEE_RET)) { /* no need to spill */ ins = (MonoInst*)mono_emit_method_call (cfg, bblock, cmethod, fsig, sp, ip, virtual ? sp [0] : NULL); *sp++ = ins; @@ -3777,6 +4209,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b handle_stack_args (cfg, bblock, stack_start, sp - stack_start); sp = stack_start; } + /* Needed by the code generated in inssel.brg */ + mono_get_got_var (cfg); inline_costs += 20; break; case CEE_LDIND_I1: @@ -3848,7 +4282,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b } if (mono_find_jit_opcode_emulation (ins->opcode)) { --sp; - *sp++ = emit_tree (cfg, bblock, ins); + *sp++ = emit_tree (cfg, bblock, ins, ip + 1); } ip++; break; @@ -3869,7 +4303,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b ADD_UNOP (*ip); if (mono_find_jit_opcode_emulation (ins->opcode)) { --sp; - *sp++ = emit_tree (cfg, bblock, ins); + *sp++ = emit_tree (cfg, bblock, ins, ip + 1); } ip++; break; @@ -3933,7 +4367,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b MONO_INST_NEW (cfg, load, CEE_LDIND_REF); load->cil_code = ip; load->inst_i0 = sp [1]; - load->type = ldind_type [CEE_LDIND_REF]; + load->type = STACK_OBJ; load->flags |= ins_flag; MONO_INST_NEW (cfg, store, CEE_STIND_REF); store->cil_code = ip; @@ -3967,6 +4401,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b break; case CEE_LDOBJ: { MonoInst *iargs [3]; + int loc_index = -1; + int stloc_len = 0; CHECK_OPSIZE (5); CHECK_STACK (1); --sp; @@ -3981,13 +4417,47 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b MONO_INST_NEW (cfg, ins, CEE_LDIND_REF); ins->cil_code = ip; ins->inst_i0 = sp [0]; - ins->type = ldind_type [CEE_LDIND_REF]; + ins->type = STACK_OBJ; ins->flags |= ins_flag; ins_flag = 0; *sp++ = ins; ip += 5; break; } + + /* Optimize the common ldobj+stloc combination */ + switch (ip [5]) { + case CEE_STLOC_S: + loc_index = ip [6]; + stloc_len = 2; + break; + case CEE_STLOC_0: + case CEE_STLOC_1: + case CEE_STLOC_2: + case CEE_STLOC_3: + loc_index = ip [5] - CEE_STLOC_0; + stloc_len = 1; + break; + default: + break; + } + + if ((loc_index != -1) && ip_in_bb (cfg, bblock, ip + 5)) { + CHECK_LOCAL (loc_index); + NEW_LOCSTORE (cfg, ins, loc_index, *sp); + + if (ins->opcode == CEE_STOBJ) { + handle_loaded_temps (cfg, bblock, stack_start, sp); + 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); + ip += 5; + ip += stloc_len; + break; + } + } + n = mono_class_value_size (klass, NULL); ins = mono_compile_create_var (cfg, &klass->byval_arg, OP_LOCAL); NEW_TEMPLOADA (cfg, iargs [0], ins->inst_c0); @@ -4038,8 +4508,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b int temp; MonoInst *iargs [3]; - if (mono_compile_aot) { - cfg->ldstr_list = g_list_prepend (cfg->ldstr_list, (gpointer)n); + if (cfg->compile_aot) { + cfg->ldstr_list = g_list_prepend (cfg->ldstr_list, GINT_TO_POINTER (n)); } NEW_TEMPLOAD (cfg, iargs [0], mono_get_domainvar (cfg)->inst_c0); @@ -4049,7 +4519,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b NEW_TEMPLOAD (cfg, *sp, temp); mono_ldstr (cfg->domain, image, mono_metadata_token_index (n)); } else { - if (mono_compile_aot) + if (cfg->compile_aot) NEW_LDSTRCONST (cfg, ins, image, n); else { NEW_PCONST (cfg, ins, NULL); @@ -4074,7 +4544,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b if (method->wrapper_type != MONO_WRAPPER_NONE) { cmethod = mono_method_get_wrapper_data (method, token); } else - cmethod = mono_get_method_full (image, token, NULL, generic_context); + cmethod = mini_get_method (image, token, NULL, generic_context); fsig = mono_method_get_signature (cmethod, image, token); mono_class_init (cmethod->klass); @@ -4160,7 +4630,10 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b else klass = mono_class_get_full (image, token, generic_context); mono_class_init (klass); - + + /* Needed by the code generated in inssel.brg */ + mono_get_got_var (cfg); + if (klass->marshalbyref || klass->flags & TYPE_ATTRIBUTE_INTERFACE) { MonoMethod *mono_isinst; @@ -4198,7 +4671,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b ins->inst_left = *sp; ins->inst_newa_class = klass; ins->cil_code = ip; - *sp++ = emit_tree (cfg, bblock, ins); + *sp++ = emit_tree (cfg, bblock, ins, ip + 5); ip += 5; } break; @@ -4255,8 +4728,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b ins->inst_newa_class = klass; ins->cil_code = ip; *sp++ = ins; + ip += 5; } - ip += 5; break; } @@ -4311,6 +4784,9 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b klass = mono_class_get_full (image, token, generic_context); mono_class_init (klass); + /* Needed by the code generated in inssel.brg */ + mono_get_got_var (cfg); + MONO_INST_NEW (cfg, ins, OP_UNBOXCAST); ins->type = STACK_OBJ; ins->inst_left = *sp; @@ -4338,6 +4814,9 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b else klass = mono_class_get_full (image, token, generic_context); mono_class_init (klass); + + /* Needed by the code generated in inssel.brg */ + mono_get_got_var (cfg); if (klass->marshalbyref || klass->flags & TYPE_ATTRIBUTE_INTERFACE) { @@ -4376,7 +4855,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b ins->klass = klass; ins->inst_newa_class = klass; ins->cil_code = ip; - *sp++ = emit_tree (cfg, bblock, ins); + *sp++ = emit_tree (cfg, bblock, ins, ip + 5); ip += 5; } break; @@ -4386,8 +4865,10 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b --sp; ins->inst_left = *sp; ins->cil_code = ip++; + bblock->out_of_line = TRUE; MONO_ADD_INS (bblock, ins); sp = stack_start; + link_bblock (cfg, bblock, end_bblock); start_new_bblock = 1; break; case CEE_LDFLD: @@ -4482,7 +4963,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b 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) { + if ((cfg->opt & MONO_OPT_INLINE) && !MONO_TYPE_ISSTRUCT (ldfld_wrapper->signature->ret)) { costs = inline_method (cfg, ldfld_wrapper, ldfld_wrapper->signature, bblock, iargs, ip, real_offset, dont_inline, &ebblock, TRUE); g_assert (costs > 0); @@ -4560,10 +5041,16 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b if ((*ip) == CEE_STSFLD) handle_loaded_temps (cfg, bblock, stack_start, sp); + /* The special_static_fields field is init'd in mono_class_vtable, so it needs + * to be called here. + */ + if (!(cfg->opt & MONO_OPT_SHARED)) + mono_class_vtable (cfg->domain, klass); + if (cfg->domain->special_static_fields) addr = g_hash_table_lookup (cfg->domain->special_static_fields, field); - if ((cfg->opt & MONO_OPT_SHARED) || (mono_compile_aot && addr)) { + if ((cfg->opt & MONO_OPT_SHARED) || (cfg->compile_aot && addr)) { int temp; MonoInst *iargs [2]; g_assert (field->parent); @@ -4575,7 +5062,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b MonoVTable *vtable; vtable = mono_class_vtable (cfg->domain, klass); if (!addr) { - if ((!vtable->initialized || mono_compile_aot) && !(klass->flags & TYPE_ATTRIBUTE_BEFORE_FIELD_INIT) && mono_class_needs_cctor_run (klass, method)) { + if ((!vtable->initialized || cfg->compile_aot) && !(klass->flags & TYPE_ATTRIBUTE_BEFORE_FIELD_INIT) && mono_class_needs_cctor_run (klass, method)) { guint8 *tramp = mono_create_class_init_trampoline (vtable); mono_emit_native_call (cfg, bblock, tramp, helper_sig_class_init_trampoline, @@ -4588,7 +5075,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b } addr = (char*)vtable->data + field->offset; - if (mono_compile_aot) + if (cfg->compile_aot) NEW_SFLDACONST (cfg, ins, field); else NEW_PCONST (cfg, ins, addr); @@ -4628,12 +5115,16 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b } else { gboolean is_const = FALSE; MonoVTable *vtable = mono_class_vtable (cfg->domain, klass); - if (!((cfg->opt & MONO_OPT_SHARED) || mono_compile_aot) && + if (!((cfg->opt & MONO_OPT_SHARED) || cfg->compile_aot) && vtable->initialized && (field->type->attrs & FIELD_ATTRIBUTE_INIT_ONLY)) { gpointer addr = (char*)vtable->data + field->offset; + int ro_type = field->type->type; + if (ro_type == MONO_TYPE_VALUETYPE && field->type->data.klass->enumtype) { + ro_type = field->type->data.klass->enum_basetype->type; + } /* g_print ("RO-FIELD %s.%s:%s\n", klass->name_space, klass->name, field->name);*/ is_const = TRUE; - switch (field->type->type) { + switch (ro_type) { case MONO_TYPE_BOOLEAN: case MONO_TYPE_U1: NEW_ICONST (cfg, *sp, *((guint8 *)addr)); @@ -4772,7 +5263,10 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b /* LAME-IR: Mark it as used since otherwise it will be optimized away */ cfg->domainvar->flags |= MONO_INST_VOLATILE; } - + + /* Ditto */ + mono_get_got_var (cfg); + if (method->wrapper_type != MONO_WRAPPER_NONE) klass = (MonoClass *)mono_method_get_wrapper_data (method, token); else @@ -4819,6 +5313,21 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b klass = (MonoClass*)mono_method_get_wrapper_data (method, read32 (ip + 1)); else klass = mono_class_get_full (image, read32 (ip + 1), generic_context); + + /* we need to make sure that this array is exactly the type it needs + * to be for correctness. the wrappers are lax with their usage + * so we need to ignore them here + */ + if (!klass->valuetype && method->wrapper_type == MONO_WRAPPER_NONE) { + MonoInst* check; + MONO_INST_NEW (cfg, check, OP_CHECK_ARRAY_TYPE); + check->cil_code = ip; + check->klass = klass; + check->inst_left = sp [0]; + check->type = STACK_OBJ; + sp [0] = check; + } + mono_class_init (klass); NEW_LDELEMA (cfg, ins, sp, klass); ins->cil_code = ip; @@ -5050,12 +5559,13 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b if (cfg->opt & MONO_OPT_SHARED) { int temp; - MonoInst *res, *store, *addr, *vtvar, *iargs [2]; + MonoInst *res, *store, *addr, *vtvar, *iargs [3]; vtvar = mono_compile_create_var (cfg, &handle_class->byval_arg, OP_LOCAL); NEW_IMAGECONST (cfg, iargs [0], image); NEW_ICONST (cfg, iargs [1], n); + NEW_PCONST (cfg, iargs [2], generic_context); temp = mono_emit_jit_icall (cfg, bblock, mono_ldtoken_wrapper, iargs, ip); NEW_TEMPLOAD (cfg, res, temp); NEW_TEMPLOADA (cfg, addr, vtvar->inst_c0); @@ -5063,14 +5573,12 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b MONO_ADD_INS (bblock, store); NEW_TEMPLOAD (cfg, ins, vtvar->inst_c0); } else { - if ((ip [5] == CEE_CALL) && (cmethod = mono_get_method_full (image, read32 (ip + 6), NULL, generic_context)) && + if ((ip [5] == CEE_CALL) && (cmethod = mini_get_method (image, read32 (ip + 6), NULL, generic_context)) && (cmethod->klass == mono_defaults.monotype_class->parent) && - (strcmp (cmethod->name, "GetTypeFromHandle") == 0) && - ((g_hash_table_lookup (bbhash, ip + 5) == NULL) || - (g_hash_table_lookup (bbhash, ip + 5) == bblock))) { + (strcmp (cmethod->name, "GetTypeFromHandle") == 0) && ip_in_bb (cfg, bblock, ip + 5)) { MonoClass *tclass = mono_class_from_mono_type (handle); mono_class_init (tclass); - if (mono_compile_aot) + if (cfg->compile_aot) NEW_TYPE_FROM_HANDLE_CONST (cfg, ins, image, n); else NEW_PCONST (cfg, ins, mono_type_get_object (cfg->domain, handle)); @@ -5080,7 +5588,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b } else { MonoInst *store, *addr, *vtvar; - if (mono_compile_aot) + if (cfg->compile_aot) NEW_LDTOKENCONST (cfg, ins, image, n); else NEW_PCONST (cfg, ins, handle); @@ -5113,7 +5621,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b ADD_BINOP (*ip); if (mono_find_jit_opcode_emulation (ins->opcode)) { --sp; - *sp++ = emit_tree (cfg, bblock, ins); + *sp++ = emit_tree (cfg, bblock, ins, ip + 1); } ip++; break; @@ -5138,6 +5646,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b case CEE_LEAVE: case CEE_LEAVE_S: { GList *handlers; + if (*ip == CEE_LEAVE) { CHECK_OPSIZE (5); target = ip + 5 + (gint32)read32(ip + 1); @@ -5161,14 +5670,20 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b */ for (i = 0; i < header->num_clauses; ++i) { MonoExceptionClause *clause = &header->clauses [i]; - if (MONO_OFFSET_IN_HANDLER (clause, ip - header->code)) { + + 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; + + NEW_TEMPLOAD (cfg, load, mono_find_exvar_for_offset (cfg, clause->handler_offset)->inst_c0); + load->cil_code = ip; temp = mono_emit_jit_icall (cfg, bblock, mono_thread_get_pending_exception, NULL, ip); NEW_TEMPLOAD (cfg, *sp, temp); - + MONO_INST_NEW (cfg, ins, OP_THROW_OR_NULL); ins->inst_left = *sp; + ins->inst_right = load; ins->cil_code = ip; MONO_ADD_INS (bblock, ins); } @@ -5262,6 +5777,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b *sp++ = ins; ip += 6; inline_costs += 10 * num_calls++; + /* Can't embed random pointers into AOT code */ + cfg->disable_aot = 1; break; case CEE_MONO_VTADDR: CHECK_STACK (1); @@ -5348,7 +5865,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b ins->inst_left = *sp; ins->inst_newa_class = klass; ins->cil_code = ip; - *sp++ = emit_tree (cfg, bblock, ins); + *sp++ = emit_tree (cfg, bblock, ins, ip + 6); ip += 6; break; } @@ -5361,6 +5878,16 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b #endif ip += 2; break; + case CEE_MONO_CLASSCONST: + CHECK_STACK_OVF (1); + CHECK_OPSIZE (6); + token = read32 (ip + 2); + NEW_CLASSCONST (cfg, ins, mono_method_get_wrapper_data (method, token)); + ins->cil_code = ip; + *sp++ = ins; + ip += 6; + inline_costs += 10 * num_calls++; + break; default: g_error ("opcode 0x%02x 0x%02x not handled", MONO_CUSTOM_PREFIX, ip [1]); break; @@ -5425,7 +5952,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b */ if (cmp->inst_left->type == STACK_I8) { --sp; - *sp++ = emit_tree (cfg, bblock, ins); + *sp++ = emit_tree (cfg, bblock, ins, ip + 2); } ip += 2; break; @@ -5440,7 +5967,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b if (method->wrapper_type != MONO_WRAPPER_NONE) cmethod = mono_method_get_wrapper_data (method, n); else { - cmethod = mono_get_method_full (image, n, NULL, generic_context); + cmethod = mini_get_method (image, n, NULL, generic_context); } mono_class_init (cmethod->klass); @@ -5468,7 +5995,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b if (method->wrapper_type != MONO_WRAPPER_NONE) cmethod = mono_method_get_wrapper_data (method, n); else - cmethod = mono_get_method_full (image, n, NULL, generic_context); + cmethod = mini_get_method (image, n, NULL, generic_context); mono_class_init (cmethod->klass); handle_loaded_temps (cfg, bblock, stack_start, sp); @@ -5562,6 +6089,13 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b --sp; if (sp != stack_start) goto unverified; + if (cfg->method != method) + /* + * Inlining this into a loop in a parent could lead to + * stack overflows which is different behavior than the + * non-inlined case, thus disable inlining in this case. + */ + goto inline_failure; MONO_INST_NEW (cfg, ins, OP_LOCALLOC); ins->inst_left = *sp; ins->cil_code = ip; @@ -5601,7 +6135,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b } } g_assert (nearest); - filter_lengths [nearest_num] = (ip - header->code) - nearest->data.filter_offset; + if ((ip - header->code) != nearest->handler_offset) + goto unverified; break; } @@ -5697,14 +6232,24 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b break; case CEE_RETHROW: { MonoInst *load; - /* FIXME: check we are in a catch handler */ - NEW_TEMPLOAD (cfg, load, cfg->exvar->inst_c0); + int handler_offset = -1; + + 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_FINALLY)) + handler_offset = clause->handler_offset; + } + + g_assert (handler_offset != -1); + + NEW_TEMPLOAD (cfg, load, mono_find_exvar_for_offset (cfg, handler_offset)->inst_c0); load->cil_code = ip; MONO_INST_NEW (cfg, ins, OP_RETHROW); ins->inst_left = load; ins->cil_code = ip; MONO_ADD_INS (bblock, ins); sp = stack_start; + link_bblock (cfg, bblock, end_bblock); start_new_bblock = 1; ip += 2; break; @@ -5756,11 +6301,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b bblock->cil_length = ip - bblock->cil_code; bblock->next_bb = end_bblock; - link_bblock (cfg, bblock, end_bblock); if (cfg->method == method && cfg->domainvar) { - - MonoInst *store; MonoInst *get_domain; @@ -5778,6 +6320,9 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b MONO_ADD_INS (init_localsbb, store); } + if (cfg->method == method && cfg->got_var) + mono_emit_load_got_addr (cfg); + if (header->init_locals) { MonoInst *store; for (i = 0; i < header->num_locals; ++i) { @@ -5803,7 +6348,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b 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_inst_is_valuetype (ptype->data.generic_inst))) { + ((t == MONO_TYPE_GENERICINST) && mono_metadata_generic_class_is_valuetype (ptype->data.generic_class))) { NEW_LOCLOADA (cfg, ins, i); handle_initobj (cfg, init_localsbb, ins, NULL, mono_class_from_mono_type (ptype), NULL, NULL); break; @@ -5815,7 +6360,6 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b } } - /* resolve backward branches in the middle of an existing basic block */ for (tmp = bb_recheck; tmp; tmp = tmp->next) { bblock = tmp->data; @@ -5832,14 +6376,10 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b } } - /* - * we compute regions here, because the length of filter clauses is not known in advance. - * It is computed in the CEE_ENDFILTER case in the above switch statement - */ if (cfg->method == method) { MonoBasicBlock *bb; for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { - bb->region = mono_find_block_region (cfg, bb->real_offset, filter_lengths); + bb->region = mono_find_block_region (cfg, bb->real_offset); if (cfg->spvars) mono_create_spvar_for_region (cfg, bb->region); if (cfg->verbose_level > 2) @@ -5861,7 +6401,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b unverified: if (cfg->method != method) g_hash_table_destroy (bbhash); - g_error ("Invalid IL code at IL%04x in %s: %s\n", ip - header->code, + 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); return -1; @@ -5880,10 +6420,10 @@ mono_print_tree (MonoInst *tree) { switch (tree->opcode) { case OP_ICONST: - printf ("[%d]", tree->inst_c0); + printf ("[%d]", (int)tree->inst_c0); break; case OP_I8CONST: - printf ("[%lld]", tree->inst_l); + printf ("[%lld]", (long long)tree->inst_l); break; case OP_R8CONST: printf ("[%f]", *(double*)tree->inst_p0); @@ -5893,13 +6433,13 @@ mono_print_tree (MonoInst *tree) { break; case OP_ARG: case OP_LOCAL: - printf ("[%d]", tree->inst_c0); + printf ("[%d]", (int)tree->inst_c0); break; case OP_REGOFFSET: if (tree->inst_offset < 0) - printf ("[-0x%x(%s)]", -tree->inst_offset, mono_arch_regname (tree->inst_basereg)); + printf ("[-0x%x(%s)]", (int)(-tree->inst_offset), mono_arch_regname (tree->inst_basereg)); else - printf ("[0x%x(%s)]", tree->inst_offset, mono_arch_regname (tree->inst_basereg)); + printf ("[0x%x(%s)]", (int)(tree->inst_offset), mono_arch_regname (tree->inst_basereg)); break; case OP_REGVAR: printf ("[%s]", mono_arch_regname (tree->dreg)); @@ -5925,7 +6465,7 @@ mono_print_tree (MonoInst *tree) { } case OP_PHI: { int i; - printf ("[%d (", tree->inst_c0); + printf ("[%d (", (int)tree->inst_c0); for (i = 0; i < tree->inst_phi_args [0]; i++) { if (i) printf (", "); @@ -5947,9 +6487,10 @@ mono_print_tree (MonoInst *tree) { case OP_LOADI1_MEMBASE: case OP_LOADU2_MEMBASE: case OP_LOADI2_MEMBASE: - printf ("[%s] <- [%s + 0x%x]", mono_arch_regname (tree->dreg), mono_arch_regname (tree->inst_basereg), tree->inst_offset); + printf ("[%s] <- [%s + 0x%x]", mono_arch_regname (tree->dreg), mono_arch_regname (tree->inst_basereg), (int)tree->inst_offset); break; case CEE_BR: + case OP_CALL_HANDLER: printf ("[B%d]", tree->inst_target_bb->block_num); break; case CEE_SWITCH: @@ -6033,6 +6574,9 @@ create_helper_signature (void) /* void stelem_ref_check (MonoArray *, MonoObject *) */ helper_sig_stelem_ref_check = make_icall_sig ("void object object"); + /* void *helper_compile_generic_method (MonoObject *, MonoMethod *, MonoGenericContext *) */ + helper_sig_compile_generic_method = make_icall_sig ("ptr object ptr ptr"); + /* long amethod (long, long) */ helper_sig_long_long_long = make_icall_sig ("long long long"); @@ -6071,6 +6615,8 @@ create_helper_signature (void) /* intptr amethod (intptr, intptr) */ helper_sig_ptr_ptr_ptr = make_icall_sig ("ptr ptr ptr"); + helper_sig_ptr_ptr_ptr_ptr = make_icall_sig ("ptr ptr ptr ptr"); + /* IntPtr amethod (object) */ helper_sig_ptr_obj = make_icall_sig ("ptr object"); @@ -6152,7 +6698,7 @@ mono_create_class_init_trampoline (MonoVTable *vtable) /* previously created trampoline code */ mono_domain_lock (vtable->domain); code = - mono_g_hash_table_lookup (vtable->domain->class_init_trampoline_hash, + g_hash_table_lookup (vtable->domain->class_init_trampoline_hash, vtable); mono_domain_unlock (vtable->domain); if (code) @@ -6162,7 +6708,7 @@ mono_create_class_init_trampoline (MonoVTable *vtable) /* store trampoline address */ mono_domain_lock (vtable->domain); - mono_g_hash_table_insert (vtable->domain->class_init_trampoline_hash, + g_hash_table_insert (vtable->domain->class_init_trampoline_hash, vtable, code); mono_domain_unlock (vtable->domain); @@ -6190,7 +6736,7 @@ mono_create_jump_trampoline (MonoDomain *domain, MonoMethod *method, return code; mono_domain_lock (domain); - code = mono_g_hash_table_lookup (domain->jump_trampoline_hash, method); + code = g_hash_table_lookup (domain->jump_trampoline_hash, method); mono_domain_unlock (domain); if (code) return code; @@ -6205,7 +6751,7 @@ mono_create_jump_trampoline (MonoDomain *domain, MonoMethod *method, mono_jit_info_table_add (mono_get_root_domain (), ji); mono_domain_lock (domain); - mono_g_hash_table_insert (domain->jump_trampoline_hash, method, ji->code_start); + g_hash_table_insert (domain->jump_trampoline_hash, method, ji->code_start); mono_domain_unlock (domain); return ji->code_start; @@ -6228,6 +6774,8 @@ mono_create_jit_trampoline (MonoMethod *method) if (domain == mono_get_root_domain ()) method->info = tramp; + mono_jit_stats.method_trampolines++; + return tramp; } @@ -6265,6 +6813,162 @@ mono_dynamic_code_hash_lookup (MonoDomain *domain, MonoMethod *method) return res; } +typedef struct { + MonoClass *vtype; + GList *active; + GList *slots; +} StackSlotInfo; + +/* + * mono_allocate_stack_slots: + * + * Allocate stack slots for all non register allocated variables using a + * linear scan algorithm. + * Returns: an array of stack offsets which the caller should free. + * STACK_SIZE is set to the amount of stack space needed. + * STACK_ALIGN is set to the alignment needed by the locals area. + */ +gint32* +mono_allocate_stack_slots (MonoCompile *m, guint32 *stack_size, guint32 *stack_align) +{ + int i, slot, offset, size, align; + MonoMethodVar *vmv; + MonoInst *inst; + gint32 *offsets; + GList *vars = NULL, *l; + StackSlotInfo *scalar_stack_slots, *vtype_stack_slots, *slot_info; + MonoType *t; + int nvtypes; + + scalar_stack_slots = g_new0 (StackSlotInfo, MONO_TYPE_PINNED); + vtype_stack_slots = g_new0 (StackSlotInfo, 256); + nvtypes = 0; + + offsets = g_new (guint32, m->num_varinfo); + for (i = 0; i < m->num_varinfo; ++i) + offsets [i] = -1; + + for (i = m->locals_start; i < m->num_varinfo; i++) { + inst = m->varinfo [i]; + vmv = MONO_VARINFO (m, i); + + if ((inst->flags & MONO_INST_IS_DEAD) || inst->opcode == OP_REGVAR || inst->opcode == OP_REGOFFSET) + continue; + + vars = g_list_prepend (vars, vmv); + } + + vars = mono_varlist_sort (m, vars, 0); + offset = 0; + *stack_align = 0; + for (l = vars; l; l = l->next) { + vmv = l->data; + inst = m->varinfo [vmv->idx]; + + /* inst->unused indicates native sized value types, this is used by the + * 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); + + t = mono_type_get_underlying_type (inst->inst_vtype); + switch (t->type) { + case MONO_TYPE_VALUETYPE: + for (i = 0; i < nvtypes; ++i) + if (t->data.klass == vtype_stack_slots [i].vtype) + break; + if (i < nvtypes) + slot_info = &vtype_stack_slots [i]; + else { + g_assert (nvtypes < 256); + vtype_stack_slots [nvtypes].vtype = t->data.klass; + slot_info = &vtype_stack_slots [nvtypes]; + nvtypes ++; + } + break; + default: + slot_info = &scalar_stack_slots [t->type]; + } + + slot = 0xffffff; + if (m->comp_done & MONO_COMP_LIVENESS) { + //printf ("START %2d %08x %08x\n", vmv->idx, vmv->range.first_use.abs_pos, vmv->range.last_use.abs_pos); + + /* expire old intervals in active */ + while (slot_info->active) { + MonoMethodVar *amv = (MonoMethodVar *)slot_info->active->data; + + if (amv->range.last_use.abs_pos > vmv->range.first_use.abs_pos) + break; + + //printf ("EXPIR %2d %08x %08x C%d R%d\n", amv->idx, amv->range.first_use.abs_pos, amv->range.last_use.abs_pos, amv->spill_costs, amv->reg); + + slot_info->active = g_list_delete_link (slot_info->active, slot_info->active); + slot_info->slots = g_list_prepend (slot_info->slots, GINT_TO_POINTER (offsets [amv->idx])); + } + + /* + * This also handles the case when the variable is used in an + * exception region, as liveness info is not computed there. + */ + /* + * FIXME: All valuetypes are marked as INDIRECT because of LDADDR + * opcodes. + */ + if (! (inst->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT))) { + if (slot_info->slots) { + slot = (int)slot_info->slots->data; + + slot_info->slots = g_list_delete_link (slot_info->slots, slot_info->slots); + } + + slot_info->active = mono_varlist_insert_sorted (m, slot_info->active, vmv, TRUE); + } + } + + { + static int count = 0; + count ++; + + /* + if (count == atoi (getenv ("COUNT"))) + printf ("LAST: %s\n", mono_method_full_name (m->method, TRUE)); + if (count > atoi (getenv ("COUNT"))) + slot = 0xffffff; + else { + mono_print_tree_nl (inst); + } + */ + } + if (slot == 0xffffff) { + offset += size; + offset += align - 1; + offset &= ~(align - 1); + slot = offset; + + if (*stack_align == 0) + *stack_align = align; + } + + offsets [vmv->idx] = slot; + } + g_list_free (vars); + for (i = 0; i < MONO_TYPE_PINNED; ++i) { + g_list_free (scalar_stack_slots [i].active); + g_list_free (scalar_stack_slots [i].slots); + } + for (i = 0; i < nvtypes; ++i) { + g_list_free (vtype_stack_slots [i].active); + g_list_free (vtype_stack_slots [i].slots); + } + g_free (scalar_stack_slots); + g_free (vtype_stack_slots); + + *stack_size = offset; + return offsets; +} + void mono_register_opcode_emulation (int opcode, const char *name, MonoMethodSignature *sig, gpointer func, gboolean no_throw) { @@ -6443,6 +7147,8 @@ mono_destroy_compile (MonoCompile *cfg) mono_regstate_free (cfg->rs); if (cfg->spvars) g_hash_table_destroy (cfg->spvars); + if (cfg->exvars) + g_hash_table_destroy (cfg->exvars); mono_mempool_destroy (cfg->mempool); g_list_free (cfg->ldstr_list); @@ -6527,6 +7233,8 @@ mono_thread_start_cb (guint32 tid, gpointer stack_start, gpointer func) thread = mono_thread_current (); if (thread) thread->jit_data = jit_tls; + if (mono_profiler_get_events () & MONO_PROFILE_STATISTICAL) + setup_stat_profiler (); } void (*mono_thread_attach_aborted_cb ) (MonoObject *obj) = NULL; @@ -6644,15 +7352,15 @@ mono_resolve_patch_target (MonoMethod *method, MonoDomain *domain, guint8 *code, int i; if (method->dynamic) { - jump_table = mono_code_manager_reserve (mono_dynamic_code_hash_lookup (domain, method)->code_mp, sizeof (gpointer) * patch_info->table_size); + jump_table = mono_code_manager_reserve (mono_dynamic_code_hash_lookup (domain, method)->code_mp, sizeof (gpointer) * patch_info->data.table->table_size); } else { mono_domain_lock (domain); - jump_table = mono_code_manager_reserve (domain->code_mp, sizeof (gpointer) * patch_info->table_size); + jump_table = mono_code_manager_reserve (domain->code_mp, sizeof (gpointer) * patch_info->data.table->table_size); mono_domain_unlock (domain); } - for (i = 0; i < patch_info->table_size; i++) { - jump_table [i] = code + (int)patch_info->data.table [i]; + for (i = 0; i < patch_info->data.table->table_size; i++) { + jump_table [i] = code + GPOINTER_TO_INT (patch_info->data.table->table [i]); } target = jump_table; break; @@ -6665,7 +7373,7 @@ mono_resolve_patch_target (MonoMethod *method, MonoDomain *domain, guint8 *code, break; case MONO_PATCH_INFO_IID: mono_class_init (patch_info->data.klass); - target = (gpointer)patch_info->data.klass->interface_id; + target = GINT_TO_POINTER ((int)patch_info->data.klass->interface_id); break; case MONO_PATCH_INFO_VTABLE: target = mono_class_vtable (domain, patch_info->data.klass); @@ -6721,8 +7429,13 @@ mono_resolve_patch_target (MonoMethod *method, MonoDomain *domain, guint8 *code, target = handle; break; } + case MONO_PATCH_INFO_DECLSEC: + target = (mono_metadata_blob_heap (patch_info->data.token->image, patch_info->data.token->token) + 2); + break; case MONO_PATCH_INFO_BB_OVF: case MONO_PATCH_INFO_EXC_OVF: + case MONO_PATCH_INFO_GOT_OFFSET: + case MONO_PATCH_INFO_NONE: break; default: g_assert_not_reached (); @@ -6857,6 +7570,90 @@ replace_in_block (MonoBasicBlock *bb, MonoBasicBlock *orig, MonoBasicBlock *repl } } +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; + + for (inst = bb->code; inst != NULL; inst = inst->next) { + if (inst->opcode == OP_CALL_HANDLER) { + if (inst->inst_target_bb == orig) { + inst->inst_target_bb = repl; + } + } + } + if (bb->last_ins != NULL) { + switch (bb->last_ins->opcode) { + case CEE_BR: + if (bb->last_ins->inst_target_bb == orig) { + bb->last_ins->inst_target_bb = repl; + } + break; + case CEE_SWITCH: { + int i; + int n = GPOINTER_TO_INT (bb->last_ins->klass); + for (i = 0; i < n; i++ ) { + if (bb->last_ins->inst_many_bb [i] == orig) { + bb->last_ins->inst_many_bb [i] = repl; + } + } + break; + } + case CEE_BNE_UN: + case CEE_BEQ: + case CEE_BLT: + case CEE_BLT_UN: + case CEE_BGT: + case CEE_BGT_UN: + case CEE_BGE: + case CEE_BGE_UN: + case CEE_BLE: + case CEE_BLE_UN: + if (bb->last_ins->inst_true_bb == orig) { + bb->last_ins->inst_true_bb = repl; + } + if (bb->last_ins->inst_false_bb == orig) { + bb->last_ins->inst_false_bb = repl; + } + break; + default: + break; + } + } +} + static void replace_basic_block (MonoBasicBlock *bb, MonoBasicBlock *orig, MonoBasicBlock *repl) { @@ -6873,6 +7670,96 @@ replace_basic_block (MonoBasicBlock *bb, MonoBasicBlock *orig, MonoBasicBlock * } +/** + * Check if a bb is useless (is just made of NOPs and ends with an + * unconditional branch, or nothing). + * If it is so, unlink it from the CFG and nullify it, and return TRUE. + * Otherwise, return FALSE; + */ +static gboolean +remove_block_if_useless (MonoCompile *cfg, MonoBasicBlock *bb, MonoBasicBlock *previous_bb) { + MonoBasicBlock *target_bb = NULL; + MonoInst *inst; + + /* Do not touch handlers */ + if (bb->region != -1) return FALSE; + + for (inst = bb->code; inst != NULL; inst = inst->next) { + switch (inst->opcode) { + case CEE_NOP: + break; + case CEE_BR: + target_bb = inst->inst_target_bb; + break; + default: + return FALSE; + } + } + + if (target_bb == NULL) { + if ((bb->out_count == 1) && (bb->out_bb [0] == bb->next_bb)) { + target_bb = bb->next_bb; + } else { + /* Do not touch empty BBs that do not "fall through" to their next BB (like the exit BB) */ + return FALSE; + } + } + + /* Do not touch BBs following a switch (they are the "default" branch) */ + if ((previous_bb->last_ins != NULL) && (previous_bb->last_ins->opcode == CEE_SWITCH)) { + return FALSE; + } + + /* Do not touch BBs following the entry BB and jumping to something that is not */ + /* thiry "next" bb (the entry BB cannot contain the branch) */ + if ((previous_bb == cfg->bb_entry) && (bb->next_bb != target_bb)) { + return FALSE; + } + + if (target_bb != NULL) { + int i; + + if (cfg->verbose_level > 0) { + 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); + 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); + } + } + + if ((previous_bb != cfg->bb_entry) && + (previous_bb->region == bb->region) && + ((previous_bb->last_ins == NULL) || + ((previous_bb->last_ins->opcode != CEE_BR) && + (! (MONO_IS_COND_BRANCH_OP (previous_bb->last_ins))) && + (previous_bb->last_ins->opcode != CEE_SWITCH)))) { + for (i = 0; i < previous_bb->out_count; i++) { + if (previous_bb->out_bb [i] == target_bb) { + MonoInst *jump; + MONO_INST_NEW (cfg, jump, CEE_BR); + MONO_ADD_INS (previous_bb, jump); + jump->cil_code = previous_bb->cil_code; + jump->inst_target_bb = target_bb; + break; + } + } + } + + previous_bb->next_bb = bb->next_bb; + nullify_basic_block (bb); + + return TRUE; + } else { + return FALSE; + } +} static void merge_basic_blocks (MonoBasicBlock *bb, MonoBasicBlock *bbn) @@ -6895,6 +7782,25 @@ merge_basic_blocks (MonoBasicBlock *bb, MonoBasicBlock *bbn) nullify_basic_block (bbn); } +static void +move_basic_block_to_end (MonoCompile *cfg, MonoBasicBlock *bb) +{ + MonoBasicBlock *bbn; + + /* Find the previous */ + for (bbn = cfg->bb_entry; bbn->next_bb && bbn->next_bb != bb; bbn = bbn->next_bb) + ; + if (bbn->next_bb) { + bbn->next_bb = bb->next_bb; + } + + /* Find the last */ + for (bbn = cfg->bb_entry; bbn->next_bb; bbn = bbn->next_bb) + ; + bbn->next_bb = bb; + bb->next_bb = NULL; +} + /* * Optimizes the branches on the Control Flow Graph * @@ -6913,16 +7819,22 @@ optimize_branches (MonoCompile *cfg) */ niterations = 1000; do { + MonoBasicBlock *previous_bb; 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) { + for (previous_bb = cfg->bb_entry, bb = cfg->bb_entry->next_bb; bb; previous_bb = bb, bb = bb->next_bb) { /* dont touch code inside exception clauses */ if (bb->region != -1) continue; + if (remove_block_if_useless (cfg, bb, previous_bb)) { + changed = TRUE; + 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); @@ -6941,6 +7853,13 @@ optimize_branches (MonoCompile *cfg) /* conditional branches where true and false targets are the same can be also replaced with CEE_BR */ if (bb->last_ins && MONO_IS_COND_BRANCH_OP (bb->last_ins)) { + MonoInst *pop; + MONO_INST_NEW (cfg, pop, CEE_POP); + pop->inst_left = bb->last_ins->inst_left->inst_left; + mono_add_ins_to_end (bb, pop); + MONO_INST_NEW (cfg, pop, CEE_POP); + pop->inst_left = bb->last_ins->inst_left->inst_right; + mono_add_ins_to_end (bb, pop); bb->last_ins->opcode = CEE_BR; bb->last_ins->inst_target_bb = bb->last_ins->inst_true_bb; changed = TRUE; @@ -7024,6 +7943,26 @@ optimize_branches (MonoCompile *cfg) } } else if (bb->out_count == 2) { if (bb->last_ins && MONO_IS_COND_BRANCH_NOFP (bb->last_ins)) { + int branch_result = mono_eval_cond_branch (bb->last_ins); + MonoBasicBlock *taken_branch_target = NULL, *untaken_branch_target = NULL; + if (branch_result == BRANCH_TAKEN) { + taken_branch_target = bb->last_ins->inst_true_bb; + untaken_branch_target = bb->last_ins->inst_false_bb; + } else if (branch_result == BRANCH_NOT_TAKEN) { + taken_branch_target = bb->last_ins->inst_false_bb; + untaken_branch_target = bb->last_ins->inst_true_bb; + } + if (taken_branch_target) { + /* if mono_eval_cond_branch () is ever taken to handle + * non-constant values to compare, issue a pop here. + */ + 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); + changed = TRUE; + break; + } bbn = bb->last_ins->inst_true_bb; if (bb->region == bbn->region && bbn->code && bbn->code->opcode == CEE_BR && bbn->code->inst_target_bb->region == bb->region) { @@ -7066,6 +8005,23 @@ optimize_branches (MonoCompile *cfg) break; } } + +#ifdef MONO_ARCH_HAVE_OUT_OF_LINE_BBLOCKS + if (bb->last_ins && MONO_IS_COND_BRANCH_NOFP (bb->last_ins)) { + if (bb->last_ins->inst_false_bb->out_of_line) { + /* Reverse the branch */ + bb->last_ins->opcode = reverse_branch_op (bb->last_ins->opcode); + bbn = bb->last_ins->inst_false_bb; + bb->last_ins->inst_false_bb = bb->last_ins->inst_true_bb; + bb->last_ins->inst_true_bb = bbn; + + move_basic_block_to_end (cfg, bb->last_ins->inst_true_bb); + if (cfg->verbose_level > 2) + g_print ("cbranch to throw block triggered %d.\n", + bb->block_num); + } + } +#endif } } } while (changed && (niterations > 0)); @@ -7095,8 +8051,12 @@ mono_compile_create_vars (MonoCompile *cfg) if (sig->hasthis) mono_compile_create_var (cfg, &cfg->method->klass->this_arg, OP_ARG); - for (i = 0; i < sig->param_count; ++i) + for (i = 0; i < sig->param_count; ++i) { mono_compile_create_var (cfg, sig->params [i], OP_ARG); + if (sig->params [i]->byref) { + cfg->disable_ssa = TRUE; + } + } cfg->locals_start = cfg->num_varinfo; @@ -7200,23 +8160,6 @@ emit_state (MonoCompile *cfg, MBState *state, int goal) static void mini_select_instructions (MonoCompile *cfg) { - static const int reverse_map [] = { - CEE_BNE_UN, CEE_BLT, CEE_BLE, CEE_BGT, CEE_BGE, - CEE_BEQ, CEE_BLT_UN, CEE_BLE_UN, CEE_BGT_UN, CEE_BGE_UN - }; - static const int reverse_fmap [] = { - OP_FBNE_UN, OP_FBLT, OP_FBLE, OP_FBGT, OP_FBGE, - OP_FBEQ, OP_FBLT_UN, OP_FBLE_UN, OP_FBGT_UN, OP_FBGE_UN - }; - static const int reverse_lmap [] = { - OP_LBNE_UN, OP_LBLT, OP_LBLE, OP_LBGT, OP_LBGE, - OP_LBEQ, OP_LBLT_UN, OP_LBLE_UN, OP_LBGT_UN, OP_LBGE_UN - }; - static const int reverse_imap [] = { - OP_IBNE_UN, OP_IBLT, OP_IBLE, OP_IBGT, OP_IBGE, - OP_IBEQ, OP_IBLT_UN, OP_IBLE_UN, OP_IBGT_UN, OP_IBGE_UN - }; - MonoBasicBlock *bb; cfg->state_pool = mono_mempool_new (); @@ -7233,16 +8176,8 @@ mini_select_instructions (MonoCompile *cfg) MonoBasicBlock *tmp = bb->last_ins->inst_true_bb; bb->last_ins->inst_true_bb = bb->last_ins->inst_false_bb; bb->last_ins->inst_false_bb = tmp; - - if (bb->last_ins->opcode >= CEE_BEQ && bb->last_ins->opcode <= CEE_BLT_UN) { - bb->last_ins->opcode = reverse_map [bb->last_ins->opcode - CEE_BEQ]; - } else if (bb->last_ins->opcode >= OP_FBEQ && bb->last_ins->opcode <= OP_FBLT_UN) { - bb->last_ins->opcode = reverse_fmap [bb->last_ins->opcode - OP_FBEQ]; - } else if (bb->last_ins->opcode >= OP_LBEQ && bb->last_ins->opcode <= OP_LBLT_UN) { - bb->last_ins->opcode = reverse_lmap [bb->last_ins->opcode - OP_LBEQ]; - } else if (bb->last_ins->opcode >= OP_IBEQ && bb->last_ins->opcode <= OP_IBLT_UN) { - bb->last_ins->opcode = reverse_imap [bb->last_ins->opcode - OP_IBEQ]; - } + + bb->last_ins->opcode = reverse_branch_op (bb->last_ins->opcode); } else { MonoInst *inst = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoInst)); inst->opcode = CEE_BR; @@ -7343,12 +8278,32 @@ mono_codegen (MonoCompile *cfg) for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { bb->native_offset = cfg->code_len; mono_arch_output_basic_block (cfg, bb); + +#ifdef MONO_ARCH_HAVE_OUT_OF_LINE_BBLOCKS + if (bb == cfg->bb_exit) { + cfg->epilog_begin = cfg->code_len; + + if (cfg->prof_options & MONO_PROFILE_ENTER_LEAVE) { + code = cfg->native_code + cfg->code_len; + code = mono_arch_instrument_epilog (cfg, mono_profiler_method_leave, code, FALSE); + cfg->code_len = code - cfg->native_code; + } + + mono_arch_emit_epilog (cfg); + } +#endif } + +#ifndef MONO_ARCH_HAVE_OUT_OF_LINE_BBLOCKS cfg->bb_exit->native_offset = cfg->code_len; + max_epilog_size = mono_arch_max_epilog_size (cfg); +#else + mono_arch_emit_exceptions (cfg); - code = cfg->native_code + cfg->code_len; + max_epilog_size = 0; +#endif - max_epilog_size = mono_arch_max_epilog_size (cfg); + code = cfg->native_code + cfg->code_len; /* we always allocate code in cfg->domain->code_mp to increase locality */ cfg->code_size = cfg->code_len + max_epilog_size; @@ -7376,6 +8331,7 @@ mono_codegen (MonoCompile *cfg) /* g_assert (((int)cfg->native_code & (MONO_ARCH_CODE_ALIGNMENT - 1)) == 0); */ +#ifndef MONO_ARCH_HAVE_OUT_OF_LINE_BBLOCKS cfg->epilog_begin = cfg->code_len; if (cfg->prof_options & MONO_PROFILE_ENTER_LEAVE) @@ -7384,6 +8340,7 @@ mono_codegen (MonoCompile *cfg) cfg->code_len = code - cfg->native_code; mono_arch_emit_epilog (cfg); +#endif for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) { switch (patch_info->type) { @@ -7415,18 +8372,20 @@ mono_codegen (MonoCompile *cfg) case MONO_PATCH_INFO_SWITCH: { gpointer *table; if (cfg->method->dynamic) { - table = mono_code_manager_reserve (cfg->dynamic_info->code_mp, sizeof (gpointer) * patch_info->table_size); + table = mono_code_manager_reserve (cfg->dynamic_info->code_mp, sizeof (gpointer) * patch_info->data.table->table_size); } else { mono_domain_lock (cfg->domain); - table = mono_code_manager_reserve (cfg->domain->code_mp, sizeof (gpointer) * patch_info->table_size); + table = mono_code_manager_reserve (cfg->domain->code_mp, sizeof (gpointer) * patch_info->data.table->table_size); mono_domain_unlock (cfg->domain); } - - patch_info->ip.i = patch_info->ip.label->inst_c0; - for (i = 0; i < patch_info->table_size; i++) { - table [i] = (gpointer)patch_info->data.table [i]->native_offset; + + if (!cfg->compile_aot) + /* In the aot case, the patch already points to the correct location */ + patch_info->ip.i = patch_info->ip.label->inst_c0; + for (i = 0; i < patch_info->data.table->table_size; i++) { + table [i] = GINT_TO_POINTER (patch_info->data.table->table [i]->native_offset); } - patch_info->data.target = table; + patch_info->data.table->table = (MonoBasicBlock**)table; break; } default: @@ -7640,8 +8599,136 @@ mono_local_cprop (MonoCompile *cfg) } } +static void +remove_critical_edges (MonoCompile *cfg) { + MonoBasicBlock *bb; + MonoBasicBlock *previous_bb; + + if (cfg->verbose_level > 3) { + for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { + int i; + printf ("remove_critical_edges %s, BEFORE BB%d (in:", mono_method_full_name (cfg->method, TRUE), bb->block_num); + for (i = 0; i < bb->in_count; i++) { + printf (" %d", bb->in_bb [i]->block_num); + } + printf (") (out:"); + for (i = 0; i < bb->out_count; i++) { + printf (" %d", bb->out_bb [i]->block_num); + } + printf (")"); + if (bb->last_ins != NULL) { + printf (" "); + mono_print_tree (bb->last_ins); + } + printf ("\n"); + } + } + + for (previous_bb = cfg->bb_entry, bb = previous_bb->next_bb; bb != NULL; previous_bb = previous_bb->next_bb, bb = bb->next_bb) { + if (bb->in_count > 1) { + int in_bb_index; + for (in_bb_index = 0; in_bb_index < bb->in_count; in_bb_index++) { + MonoBasicBlock *in_bb = bb->in_bb [in_bb_index]; + if (in_bb->out_count > 1) { + MonoBasicBlock *new_bb = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoBasicBlock)); + new_bb->block_num = cfg->num_bblocks++; +// new_bb->real_offset = bb->real_offset; + new_bb->region = bb->region; + + /* Do not alter the CFG while altering the BB list */ + if (previous_bb->region == bb->region) { + if (previous_bb != cfg->bb_entry) { + /* If previous_bb "followed through" to bb, */ + /* keep it linked with a CEE_BR */ + if ((previous_bb->last_ins == NULL) || + ((previous_bb->last_ins->opcode != CEE_BR) && + (! (MONO_IS_COND_BRANCH_OP (previous_bb->last_ins))) && + (previous_bb->last_ins->opcode != CEE_SWITCH))) { + int i; + /* Make sure previous_bb really falls through bb */ + for (i = 0; i < previous_bb->out_count; i++) { + if (previous_bb->out_bb [i] == bb) { + MonoInst *jump; + MONO_INST_NEW (cfg, jump, CEE_BR); + MONO_ADD_INS (previous_bb, jump); + jump->cil_code = previous_bb->cil_code; + jump->inst_target_bb = bb; + break; + } + } + } + } else { + /* We cannot add any inst to the entry BB, so we must */ + /* put a new BB in the middle to hold the CEE_BR */ + MonoInst *jump; + MonoBasicBlock *new_bb_after_entry = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoBasicBlock)); + new_bb_after_entry->block_num = cfg->num_bblocks++; +// new_bb_after_entry->real_offset = bb->real_offset; + new_bb_after_entry->region = bb->region; + + MONO_INST_NEW (cfg, jump, CEE_BR); + MONO_ADD_INS (new_bb_after_entry, jump); + jump->cil_code = bb->cil_code; + jump->inst_target_bb = bb; + + previous_bb->next_bb = new_bb_after_entry; + previous_bb = new_bb_after_entry; + + if (cfg->verbose_level > 2) { + printf ("remove_critical_edges %s, added helper BB%d jumping to BB%d\n", mono_method_full_name (cfg->method, TRUE), new_bb_after_entry->block_num, bb->block_num); + } + } + } + + /* Insert new_bb in the BB list */ + previous_bb->next_bb = new_bb; + new_bb->next_bb = bb; + previous_bb = new_bb; + + /* Setup in_bb and out_bb */ + new_bb->in_bb = mono_mempool_alloc ((cfg)->mempool, sizeof (MonoBasicBlock*)); + new_bb->in_bb [0] = in_bb; + new_bb->in_count = 1; + new_bb->out_bb = mono_mempool_alloc ((cfg)->mempool, sizeof (MonoBasicBlock*)); + new_bb->out_bb [0] = bb; + new_bb->out_count = 1; + + /* Relink in_bb and bb to (from) new_bb */ + replace_out_block (in_bb, bb, new_bb); + replace_out_block_in_code (in_bb, bb, new_bb); + replace_in_block (bb, in_bb, new_bb); + + if (cfg->verbose_level > 2) { + printf ("remove_critical_edges %s, removed critical edge from BB%d to BB%d (added BB%d)\n", mono_method_full_name (cfg->method, TRUE), in_bb->block_num, bb->block_num, new_bb->block_num); + } + } + } + } + } + + if (cfg->verbose_level > 3) { + for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { + int i; + printf ("remove_critical_edges %s, AFTER BB%d (in:", mono_method_full_name (cfg->method, TRUE), bb->block_num); + for (i = 0; i < bb->in_count; i++) { + printf (" %d", bb->in_bb [i]->block_num); + } + printf (") (out:"); + for (i = 0; i < bb->out_count; i++) { + printf (" %d", bb->out_bb [i]->block_num); + } + printf (")"); + if (bb->last_ins != NULL) { + printf (" "); + mono_print_tree (bb->last_ins); + } + printf ("\n"); + } + } +} + MonoCompile* -mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gboolean run_cctors, int parts) +mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gboolean run_cctors, gboolean compile_aot, int parts) { MonoMethodHeader *header = mono_method_get_header (method); guint8 *ip = (guint8 *)header->code; @@ -7662,6 +8749,7 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool cfg->bb_hash = g_hash_table_new (NULL, NULL); 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); @@ -7683,7 +8771,7 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool mono_jit_stats.basic_blocks += cfg->num_bblocks; mono_jit_stats.max_basic_blocks = MAX (cfg->num_bblocks, mono_jit_stats.max_basic_blocks); - if (cfg->num_varinfo > 2000) { + if ((cfg->num_varinfo > 2000) && !mono_compile_aot) { /* * we disable some optimizations if there are too many variables * because JIT time may become too expensive. The actual number needs @@ -7692,14 +8780,19 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool cfg->opt &= ~ (MONO_OPT_LINEARS | MONO_OPT_COPYPROP | MONO_OPT_CONSPROP); cfg->disable_ssa = TRUE; } - /*g_print ("numblocks = %d\n", cfg->num_bblocks);*/ - /* Depth-first ordering on basic blocks */ - cfg->bblocks = mono_mempool_alloc (cfg->mempool, sizeof (MonoBasicBlock*) * (cfg->num_bblocks + 1)); + /*g_print ("numblocks = %d\n", cfg->num_bblocks);*/ if (cfg->opt & MONO_OPT_BRANCH) optimize_branches (cfg); + if (cfg->opt & MONO_OPT_SSAPRE) { + remove_critical_edges (cfg); + } + + /* Depth-first ordering on basic blocks */ + cfg->bblocks = mono_mempool_alloc (cfg->mempool, sizeof (MonoBasicBlock*) * (cfg->num_bblocks + 1)); + df_visit (cfg->bb_entry, &dfn, cfg->bblocks); if (cfg->num_bblocks != dfn + 1) { MonoBasicBlock *bb; @@ -7744,7 +8837,7 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool #else /* fixme: add all optimizations which requires SSA */ - if (cfg->opt & (MONO_OPT_DEADCE | MONO_OPT_ABCREM)) { + if (cfg->opt & (MONO_OPT_DEADCE | 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); @@ -7760,7 +8853,7 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool if (parts == 2) return cfg; - if ((cfg->opt & MONO_OPT_CONSPROP) || (cfg->opt & MONO_OPT_COPYPROP)) { + if ((cfg->opt & MONO_OPT_CONSPROP) || (cfg->opt & MONO_OPT_COPYPROP)) { if (cfg->comp_done & MONO_COMP_SSA) { mono_ssa_cprop (cfg); } else { @@ -7775,7 +8868,10 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool 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) @@ -7788,6 +8884,28 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool decompose_pass (cfg); + if (cfg->got_var) { + GList *regs; + + /* The decompose pass may create calls which need the got var */ + mono_emit_load_got_addr (cfg); + + /* + * Allways allocate the GOT var to a register, because keeping it + * in memory will increase the number of live temporaries in some + * code created by inssel.brg, leading to the well known spills+ + * branches problem. Testcase: mcs crash in + * System.MonoCustomAttrs:GetCustomAttributes. + */ + regs = mono_arch_get_global_int_regs (cfg); + g_assert (regs); + cfg->got_var->opcode = OP_REGVAR; + cfg->got_var->dreg = GPOINTER_TO_INT (regs->data); + cfg->used_int_regs |= 1LL << cfg->got_var->dreg; + + g_list_free (regs); + } + if (cfg->opt & MONO_OPT_LINEARS) { GList *vars, *regs; @@ -7798,13 +8916,15 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool 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); } } //mono_print_code (cfg); - //print_dfn (cfg); + //print_dfn (cfg); /* variables are allocated after decompose, since decompose could create temps */ mono_arch_allocate_vars (cfg); @@ -7831,11 +8951,11 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool jinfo->code_size = cfg->code_len; jinfo->used_regs = cfg->used_int_regs; jinfo->domain_neutral = (cfg->opt & MONO_OPT_SHARED) != 0; + jinfo->cas_inited = FALSE; /* initialization delayed at the first stalk walk using this method */ if (header->num_clauses) { int i; - jinfo->exvar_offset = cfg->exvar? cfg->exvar->inst_offset: 0; jinfo->num_clauses = header->num_clauses; jinfo->clauses = mono_mempool_alloc0 (cfg->domain->mp, sizeof (MonoJitExceptionInfo) * header->num_clauses); @@ -7844,9 +8964,13 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool MonoExceptionClause *ec = &header->clauses [i]; MonoJitExceptionInfo *ei = &jinfo->clauses [i]; MonoBasicBlock *tblock; + MonoInst *exvar; ei->flags = ec->flags; + exvar = mono_find_exvar_for_offset (cfg, ec->handler_offset); + ei->exvar_offset = exvar ? exvar->inst_offset : 0; + if (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER) { tblock = g_hash_table_lookup (cfg->bb_hash, ip + ec->data.filter_offset); g_assert (tblock); @@ -7967,7 +9091,7 @@ mono_jit_compile_method_inner (MonoMethod *method, MonoDomain *target_domain) return NULL; } - cfg = mini_method_compile (method, opt, target_domain, TRUE, 0); + cfg = mini_method_compile (method, opt, target_domain, TRUE, FALSE, 0); mono_domain_lock (target_domain); @@ -8189,10 +9313,13 @@ static void SIG_HANDLER_SIGNATURE (sigfpe_signal_handler) { MonoException *exc = NULL; +#ifndef MONO_ARCH_USE_SIGACTION + void *info = NULL; +#endif GET_CONTEXT; #if defined(MONO_ARCH_HAVE_IS_INT_OVERFLOW) - if (mono_arch_is_int_overflow (ctx)) + if (mono_arch_is_int_overflow (ctx, info)) exc = mono_get_exception_arithmetic (); else exc = mono_get_exception_divide_by_zero (); @@ -8257,10 +9384,18 @@ SIG_HANDLER_SIGNATURE (sigusr1_signal_handler) exc = mono_thread_request_interruption (running_managed); if (!exc) return; - + mono_arch_handle_exception (ctx, exc, FALSE); } +static void +SIG_HANDLER_SIGNATURE (sigprof_signal_handler) +{ + GET_CONTEXT; + + mono_profiler_stat_hit (mono_arch_ip_from_context (ctx), ctx); +} + static void SIG_HANDLER_SIGNATURE (sigquit_signal_handler) { @@ -8346,6 +9481,89 @@ mono_runtime_install_handlers (void) #endif /* PLATFORM_WIN32 */ } + +#ifdef HAVE_LINUX_RTC_H +#include +#include +#include +static int rtc_fd = -1; + +static int +enable_rtc_timer (gboolean enable) +{ + int flags; + flags = fcntl (rtc_fd, F_GETFL); + if (flags < 0) { + perror ("getflags"); + return 0; + } + if (enable) + flags |= FASYNC; + else + flags &= ~FASYNC; + if (fcntl (rtc_fd, F_SETFL, flags) == -1) { + perror ("setflags"); + return 0; + } + return 1; +} +#endif + +static void +setup_stat_profiler (void) +{ +#ifdef ITIMER_PROF + struct itimerval itval; + static int inited = 0; +#ifdef HAVE_LINUX_RTC_H + const char *rtc_freq; + if (!inited && (rtc_freq = g_getenv ("MONO_RTC"))) { + int freq = 0; + inited = 1; + if (*rtc_freq) + freq = atoi (rtc_freq); + if (!freq) + freq = 1024; + rtc_fd = open ("/dev/rtc", O_RDONLY); + if (rtc_fd == -1) { + perror ("open /dev/rtc"); + return; + } + add_signal_handler (SIGPROF, sigprof_signal_handler); + if (ioctl (rtc_fd, RTC_IRQP_SET, freq) == -1) { + perror ("set rtc freq"); + return; + } + if (ioctl (rtc_fd, RTC_PIE_ON, 0) == -1) { + perror ("start rtc"); + return; + } + if (fcntl (rtc_fd, F_SETSIG, SIGPROF) == -1) { + perror ("setsig"); + return; + } + if (fcntl (rtc_fd, F_SETOWN, getpid ()) == -1) { + perror ("setown"); + return; + } + enable_rtc_timer (TRUE); + return; + } + if (rtc_fd >= 0) + return; +#endif + + itval.it_interval.tv_usec = 999; + itval.it_interval.tv_sec = 0; + itval.it_value = itval.it_interval; + setitimer (ITIMER_PROF, &itval, NULL); + if (inited) + return; + inited = 1; + add_signal_handler (SIGPROF, sigprof_signal_handler); +#endif +} + /* mono_jit_create_remoting_trampoline: * @method: pointer to the method info * @@ -8375,6 +9593,11 @@ mini_init (const char *filename) { MonoDomain *domain; + InitializeCriticalSection (&jit_mutex); + + global_codeman = mono_code_manager_new (); + jit_icall_name_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + mono_arch_cpu_init (); if (!g_thread_supported ()) @@ -8385,8 +9608,6 @@ mini_init (const char *filename) mono_jit_tls_id = TlsAlloc (); setup_jit_tls_data ((gpointer)-1, mono_thread_abort); - InitializeCriticalSection (&jit_mutex); - mono_burg_init (); if (default_opt & MONO_OPT_AOT) @@ -8410,12 +9631,16 @@ mini_init (const char *filename) mono_install_stack_walk (mono_jit_walk_stack); domain = mono_init_from_assembly (filename, filename); - mono_init_icall (); + mono_icall_init (); mono_add_internal_call ("System.Diagnostics.StackFrame::get_frame_info", ves_icall_get_frame_info); mono_add_internal_call ("System.Diagnostics.StackTrace::get_trace", ves_icall_get_trace); + mono_add_internal_call ("System.Security.SecurityFrame::_GetSecurityFrame", + ves_icall_System_Security_SecurityFrame_GetSecurityFrame); + mono_add_internal_call ("System.Security.SecurityFrame::_GetSecurityStack", + ves_icall_System_Security_SecurityFrame_GetSecurityStack); mono_add_internal_call ("Mono.Runtime::mono_runtime_install_handlers", mono_runtime_install_handlers); @@ -8439,8 +9664,13 @@ mini_init (const char *filename) mono_register_jit_icall (mono_arch_get_rethrow_exception (), "mono_arch_rethrow_exception", helper_sig_void_obj, TRUE); mono_register_jit_icall (mono_arch_get_throw_exception_by_name (), "mono_arch_throw_exception_by_name", helper_sig_void_ptr, TRUE); +#if MONO_ARCH_HAVE_THROW_CORLIB_EXCEPTION + mono_register_jit_icall (mono_arch_get_throw_corlib_exception (), "mono_arch_throw_corlib_exception", + helper_sig_void_ptr, TRUE); +#endif mono_register_jit_icall (mono_thread_get_pending_exception, "mono_thread_get_pending_exception", helper_sig_obj_void, FALSE); mono_register_jit_icall (mono_thread_interruption_checkpoint, "mono_thread_interruption_checkpoint", helper_sig_void_void, FALSE); + mono_register_jit_icall (mono_thread_force_interruption_checkpoint, "mono_thread_force_interruption_checkpoint", helper_sig_void_void, FALSE); mono_register_jit_icall (mono_load_remote_field_new, "mono_load_remote_field_new", helper_sig_obj_obj_ptr_ptr, FALSE); mono_register_jit_icall (mono_store_remote_field_new, "mono_store_remote_field_new", helper_sig_void_obj_ptr_ptr_obj, FALSE); @@ -8498,7 +9728,7 @@ mini_init (const char *filename) /* other jit icalls */ mono_register_jit_icall (mono_class_static_field_address , "mono_class_static_field_address", helper_sig_ptr_ptr_ptr, FALSE); - mono_register_jit_icall (mono_ldtoken_wrapper, "mono_ldtoken_wrapper", helper_sig_ptr_ptr_ptr, FALSE); + mono_register_jit_icall (mono_ldtoken_wrapper, "mono_ldtoken_wrapper", helper_sig_ptr_ptr_ptr_ptr, FALSE); mono_register_jit_icall (mono_get_special_static_data, "mono_get_special_static_data", helper_sig_ptr_int, FALSE); mono_register_jit_icall (mono_ldstr, "mono_ldstr", helper_sig_ldstr, FALSE); mono_register_jit_icall (helper_memcpy, "helper_memcpy", helper_sig_memcpy, FALSE); @@ -8514,11 +9744,12 @@ mini_init (const char *filename) mono_register_jit_icall (mono_ldftn, "mono_ldftn", helper_sig_compile, FALSE); mono_register_jit_icall (mono_ldftn_nosync, "mono_ldftn_nosync", helper_sig_compile, FALSE); mono_register_jit_icall (mono_ldvirtfn, "mono_ldvirtfn", helper_sig_compile_virt, FALSE); + mono_register_jit_icall (helper_compile_generic_method, "compile_generic_method", helper_sig_compile_generic_method, FALSE); #endif #define JIT_RUNTIME_WORKS #ifdef JIT_RUNTIME_WORKS - mono_runtime_install_cleanup ((MonoDomainFunc)mini_cleanup); + mono_install_runtime_cleanup ((MonoDomainFunc)mini_cleanup); mono_runtime_init (domain, mono_thread_start_cb, mono_thread_attach_cb); #endif @@ -8559,7 +9790,9 @@ print_jit_stats (void) g_print ("VTable data size: %ld\n", mono_stats.class_vtable_size); g_print ("\nGeneric instances: %ld\n", mono_stats.generic_instance_count); - g_print ("Inflated methods: %ld\n", mono_stats.inflated_method_count); + g_print ("Initialized classes: %ld\n", mono_stats.generic_class_count); + g_print ("Inflated methods: %ld / %ld\n", mono_stats.inflated_method_count_2, + mono_stats.inflated_method_count); g_print ("Inflated types: %ld\n", mono_stats.inflated_type_count); g_print ("Generics metadata size: %ld\n", mono_stats.generics_metadata_size); } @@ -8568,6 +9801,11 @@ print_jit_stats (void) void mini_cleanup (MonoDomain *domain) { +#ifdef HAVE_LINUX_RTC_H + if (rtc_fd >= 0) + enable_rtc_timer (FALSE); +#endif + /* * mono_runtime_cleanup() and mono_domain_finalize () need to * be called early since they need the execution engine still @@ -8582,12 +9820,19 @@ mini_cleanup (MonoDomain *domain) mono_debug_cleanup (); + mono_icall_cleanup (); + #ifdef PLATFORM_WIN32 win32_seh_cleanup(); #endif mono_domain_free (domain, TRUE); + mono_code_manager_destroy (global_codeman); + g_hash_table_destroy (jit_icall_name_hash); + if (class_init_hash_addr) + g_hash_table_destroy (class_init_hash_addr); + print_jit_stats (); }