X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmini%2Fmini.c;h=fec74a8337a9c1b90b13616b8e8f52e4b103baf3;hb=4bcddd011f31598bcccdf52566a279840ef71b08;hp=bdb7332d91fea52e41a1c5e25d034504fd9eeef4;hpb=61bb56e984236cd1c1047cd3949d15bda1036888;p=mono.git diff --git a/mono/mini/mini.c b/mono/mini/mini.c index bdb7332d91f..fec74a8337a 100644 --- a/mono/mini/mini.c +++ b/mono/mini/mini.c @@ -33,30 +33,40 @@ #include #include #include +#include +#include #include "mini.h" #include #include #include "inssel.h" -#include "debug.h" #include "jit-icalls.c" +#define MONO_IS_COND_BRANCH(op) ((op >= CEE_BEQ && op <= CEE_BLT_UN) || (op >= OP_LBEQ && op <= OP_LBLT_UN) || (op >= OP_FBEQ && op <= OP_FBLT_UN)) + #define MONO_CHECK_THIS(ins) (cfg->method->signature->hasthis && (ins)->ssa_op == MONO_SSA_LOAD && (ins)->inst_left->inst_c0 == 0) +gboolean mono_arch_handle_exception (struct sigcontext *ctx, gpointer obj, gboolean test_only); static gpointer mono_jit_compile_method (MonoMethod *method); static void handle_stobj (MonoCompile *cfg, MonoBasicBlock *bblock, MonoInst *dest, MonoInst *src, const unsigned char *ip, MonoClass *klass, gboolean to_end, gboolean native); +static int mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_bblock, MonoBasicBlock *end_bblock, + int locals_offset, MonoInst *return_var, GList *dont_inline, MonoInst **inline_args, + guint inline_offset, gboolean is_virtual_call); + extern guint8 mono_burg_arity []; /* helper methods signature */ static MonoMethodSignature *helper_sig_long_long_long = NULL; static MonoMethodSignature *helper_sig_long_long_int = NULL; static MonoMethodSignature *helper_sig_newarr = NULL; +static MonoMethodSignature *helper_sig_newarr_specific = NULL; static MonoMethodSignature *helper_sig_ldstr = NULL; static MonoMethodSignature *helper_sig_domain_get = NULL; static MonoMethodSignature *helper_sig_object_new = NULL; +static MonoMethodSignature *helper_sig_object_new_specific = NULL; static MonoMethodSignature *helper_sig_compile = NULL; static MonoMethodSignature *helper_sig_compile_virt = NULL; static MonoMethodSignature *helper_sig_obj_ptr = NULL; @@ -67,6 +77,7 @@ 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_obj = NULL; +static MonoMethodSignature *helper_sig_ptr_int = NULL; static MonoMethodSignature *helper_sig_initobj = NULL; static MonoMethodSignature *helper_sig_memcpy = NULL; static MonoMethodSignature *helper_sig_memset = NULL; @@ -74,6 +85,7 @@ static MonoMethodSignature *helper_sig_ulong_double = NULL; static MonoMethodSignature *helper_sig_long_double = NULL; static MonoMethodSignature *helper_sig_uint_double = NULL; static MonoMethodSignature *helper_sig_int_double = NULL; +static MonoMethodSignature *helper_sig_stelem_ref = NULL; static guint32 default_opt = MONO_OPT_PEEPHOLE; @@ -81,11 +93,6 @@ guint32 mono_jit_tls_id = 0; gboolean mono_jit_trace_calls = FALSE; gboolean mono_break_on_exc = FALSE; gboolean mono_compile_aot = FALSE; -gboolean mono_trace_coverage = FALSE; -gboolean mono_jit_profile = FALSE; -MonoDebugFormat mono_debug_format = MONO_DEBUG_FORMAT_NONE; - -CRITICAL_SECTION *metadata_section = NULL; static int mini_verbose = 0; @@ -110,6 +117,7 @@ mono_type_blittable (MonoType *type) case MONO_TYPE_R8: case MONO_TYPE_I: case MONO_TYPE_U: + case MONO_TYPE_OBJECT: return TRUE; case MONO_TYPE_VALUETYPE: case MONO_TYPE_CLASS: @@ -151,25 +159,32 @@ mono_method_blittable (MonoMethod *method) } #endif -#if 0 /* debug function */ -static void +G_GNUC_UNUSED static void print_method_from_ip (void *ip) { MonoJitInfo *ji; char *method; + char *source; + MonoDomain *domain = mono_domain_get (); - ji = mono_jit_info_table_find (mono_domain_get (), ip); + ji = mono_jit_info_table_find (domain, ip); if (!ji) { g_print ("No method at %p\n", ip); return; } method = mono_method_full_name (ji->method, TRUE); - g_print ("IP at offset 0x%x of method %s (%p %p)\n", (char*)ip - (char*)ji->code_start, method, ji->code_start, (char*)ji->code_start + ji->code_size); + 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); + + if (source) + g_print ("%s\n", source); + + g_free (source); g_free (method); } -#endif #define MONO_INIT_VARINFO(vi,id) do { \ (vi)->range.first_use.pos.bid = 0xffff; \ @@ -255,7 +270,7 @@ print_method_from_ip (void *ip) } while (0) #define NEW_DOMAINCONST(cfg,dest) do { \ - if ((cfg->opt & MONO_OPT_SAHRED) || mono_compile_aot) { \ + if ((cfg->opt & MONO_OPT_SHARED) || mono_compile_aot) { \ NEW_TEMPLOAD (cfg, dest, mono_get_domainvar (cfg)->inst_c0); \ } else { \ NEW_PCONST (cfg, dest, (cfg)->domain); \ @@ -476,34 +491,13 @@ print_method_from_ip (void *ip) (dest)->klass = (k); \ } while (0) -static GHashTable *coverage_hash = NULL; - -MonoCoverageInfo * -mono_allocate_coverage_info (MonoMethod *method, int size) -{ - MonoCoverageInfo *res; - - if (!coverage_hash) - coverage_hash = g_hash_table_new (NULL, NULL); - - res = g_malloc0 (sizeof (MonoCoverageInfo) + sizeof (int) * size * 2); - - res->entries = size; - - g_hash_table_insert (coverage_hash, method, res); - - return res; -} - -MonoCoverageInfo * -mono_get_coverage_info (MonoMethod *method) -{ - if (!coverage_hash) - return NULL; +#define NEW_GROUP(cfg,dest,el1,el2) do { \ + (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \ + (dest)->opcode = OP_GROUP; \ + (dest)->inst_left = (el1); \ + (dest)->inst_right = (el2); \ + } while (0) - return g_hash_table_lookup (coverage_hash, method); -} - #if 0 static gint compare_bblock (gconstpointer a, gconstpointer b) @@ -593,10 +587,13 @@ mono_find_block_region (MonoCompile *cfg, int offset, int *filter_lengths) clause = &header->clauses [i]; if ((clause->flags & MONO_EXCEPTION_CLAUSE_FILTER) && (offset >= clause->token_or_filter) && (offset < (clause->token_or_filter + filter_lengths [i]))) - return (i << 8) | 128 | clause->flags; + return ((i + 1) << 8) | MONO_REGION_FILTER | clause->flags; if (MONO_OFFSET_IN_HANDLER (clause, offset)) { - return (i << 8) | 64 | clause->flags; + if (clause->flags & MONO_EXCEPTION_CLAUSE_FINALLY) + return ((i + 1) << 8) | MONO_REGION_FINALLY | clause->flags; + else + return ((i + 1) << 8) | MONO_REGION_CATCH | clause->flags; } } @@ -604,13 +601,13 @@ mono_find_block_region (MonoCompile *cfg, int offset, int *filter_lengths) for (i = 0; i < header->num_clauses; ++i) { clause = &header->clauses [i]; if (MONO_OFFSET_IN_CLAUSE (clause, offset)) - return (i << 8) | clause->flags; + return ((i + 1) << 8) | clause->flags; } return -1; } -static MonoBasicBlock * +static GList* mono_find_final_block (MonoCompile *cfg, unsigned char *ip, unsigned char *target, int type) { MonoMethod *method = cfg->method; @@ -618,19 +615,20 @@ mono_find_final_block (MonoCompile *cfg, unsigned char *ip, unsigned char *targe MonoExceptionClause *clause; MonoBasicBlock *handler; int i; + GList *res = NULL; for (i = 0; i < header->num_clauses; ++i) { clause = &header->clauses [i]; if (MONO_OFFSET_IN_CLAUSE (clause, (ip - header->code)) && (!MONO_OFFSET_IN_CLAUSE (clause, (target - header->code)))) { - if (clause->flags & type) { + if (clause->flags == type) { handler = g_hash_table_lookup (cfg->bb_hash, header->code + clause->handler_offset); g_assert (handler); - return handler; + res = g_list_append (res, handler); } } } - return NULL; + return res; } @@ -778,6 +776,12 @@ handle_enum: goto handle_enum; } return CEE_LDOBJ; + case MONO_TYPE_TYPEDBYREF: + return CEE_LDOBJ; + case MONO_TYPE_GENERICINST: + if (type->data.generic_inst->generic_type->type == MONO_TYPE_VALUETYPE) + return CEE_LDOBJ; + return CEE_LDIND_REF; default: g_error ("unknown type 0x%02x in type_to_ldind", type->type); } @@ -829,9 +833,14 @@ handle_enum: goto handle_enum; } return CEE_STOBJ; - /* fail right now */ + case MONO_TYPE_TYPEDBYREF: + return CEE_STOBJ; + case MONO_TYPE_GENERICINST: + if (type->data.generic_inst->generic_type->type == MONO_TYPE_VALUETYPE) + return CEE_STOBJ; + return CEE_STIND_REF; default: - g_error ("unknown type %02x in type_to_stind", type->type); + g_error ("unknown type 0x%02x in type_to_stind", type->type); } return -1; } @@ -891,6 +900,18 @@ handle_enum: inst->type = STACK_VTYPE; return; } + case MONO_TYPE_TYPEDBYREF: + inst->klass = mono_defaults.typed_reference_class; + inst->type = STACK_VTYPE; + return; + case MONO_TYPE_GENERICINST: + if (type->data.generic_inst->generic_type->type == MONO_TYPE_VALUETYPE) { + inst->klass = mono_class_from_mono_type (type); + inst->type = STACK_VTYPE; + } else { + inst->type = STACK_OBJ; + } + return; default: g_error ("unknown type 0x%02x in eval stack type", type->type); } @@ -934,10 +955,10 @@ bin_comp_table [STACK_MAX] [STACK_MAX] = { {0}, {0, 1, 0, 1, 0, 0, 4, 0}, {0, 0, 1, 0, 0, 0, 0, 0}, - {0, 1, 0, 1, 0, 2, 0, 0}, + {0, 1, 0, 1, 0, 2, 4, 0}, {0, 0, 0, 0, 1, 0, 0, 0}, {0, 0, 0, 2, 0, 1, 0, 0}, - {0, 4, 0, 0, 0, 0, 3, 0}, + {0, 4, 0, 4, 0, 0, 3, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, }; @@ -1268,36 +1289,19 @@ mono_get_domainvar (MonoCompile *cfg) return cfg->domainvar; } -static void -realloc_var_info (MonoCompile *cfg, int count) -{ - gpointer data; - int num = cfg->varinfo_count; - - g_assert (count > num); - - cfg->varinfo_count = count; - - data = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoInst*) * cfg->varinfo_count); - if (num) - memcpy (data, cfg->varinfo, sizeof (MonoInst*) * num); - cfg->varinfo = (MonoInst **)data; - - data = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoMethodVar*) * cfg->varinfo_count); - if (num) - memcpy (data, cfg->vars, sizeof (MonoMethodVar*) * num); - cfg->vars = (MonoMethodVar **)data; -} - MonoInst* mono_compile_create_var (MonoCompile *cfg, MonoType *type, int opcode) { MonoInst *inst; int num = cfg->num_varinfo; - if ((num + 1) >= cfg->varinfo_count) - realloc_var_info (cfg, cfg->varinfo_count + 16); + if ((num + 1) >= cfg->varinfo_count) { + cfg->varinfo_count = (cfg->varinfo_count + 2) * 2; + cfg->varinfo = (MonoInst **)g_realloc (cfg->varinfo, sizeof (MonoInst*) * cfg->varinfo_count); + cfg->vars = (MonoMethodVar **)g_realloc (cfg->vars, sizeof (MonoMethodVar*) * cfg->varinfo_count); + } + /*g_print ("created temp %d of type 0x%x\n", num, type->type);*/ mono_jit_stats.allocate_var++; MONO_INST_NEW (cfg, inst, opcode); @@ -1433,6 +1437,39 @@ mono_add_varcopy_to_end (MonoCompile *cfg, MonoBasicBlock *bb, int src, int dest } } +/* + * We try to share variables when possible + */ +static MonoInst * +mono_compile_get_interface_var (MonoCompile *cfg, int slot, MonoInst *ins) +{ + MonoInst *res; + int pos, vnum; + + /* inlining can result in deeper stacks */ + if (slot >= ((MonoMethodNormal *)cfg->method)->header->max_stack) + return mono_compile_create_var (cfg, type_from_stack_type (ins), OP_LOCAL); + + pos = ins->type - 1 + slot * STACK_MAX; + + switch (ins->type) { + case STACK_I4: + case STACK_I8: + case STACK_R8: + case STACK_PTR: + case STACK_MP: + case STACK_OBJ: + if ((vnum = cfg->intvars [pos])) + return cfg->varinfo [vnum]; + res = mono_compile_create_var (cfg, type_from_stack_type (ins), OP_LOCAL); + cfg->intvars [pos] = res->inst_c0; + break; + default: + res = mono_compile_create_var (cfg, type_from_stack_type (ins), OP_LOCAL); + } + return res; +} + /* * This function is called to handle items that are left on the evaluation stack * at basic block boundaries. What happens is that we save the values to local variables @@ -1468,12 +1505,13 @@ handle_stack_args (MonoCompile *cfg, MonoBasicBlock *bb, MonoInst **sp, int coun if (!found) { bb->out_stack = mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*) * count); for (i = 0; i < count; ++i) { - /* - * dietmar suggests that we can reuse temps already allocated - * for this purpouse, if they occupy the same stack slot and if - * they are of the same type. - */ +#if 1 + /* try to reuse temps already allocated for this purpouse, if they occupy the same + * stack slot and if they are of the same type. */ + bb->out_stack [i] = mono_compile_get_interface_var (cfg, i, sp [i]); +#else bb->out_stack [i] = mono_compile_create_var (cfg, type_from_stack_type (sp [i]), OP_LOCAL); +#endif } } } @@ -1545,8 +1583,10 @@ handle_enum: goto handle_enum; } else return calli? OP_VCALL_REG: virt? OP_VCALLVIRT: OP_VCALL; + case MONO_TYPE_TYPEDBYREF: + return calli? OP_VCALL_REG: virt? OP_VCALLVIRT: OP_VCALL; default: - g_error ("unknown type %02x in ret_type_to_call_opcode", type->type); + g_error ("unknown type 0x%02x in ret_type_to_call_opcode", type->type); } return -1; } @@ -1597,6 +1637,108 @@ handle_loaded_temps (MonoCompile *cfg, MonoBasicBlock *bblock, MonoInst **stack, } } +/* + * Prepare arguments for passing to a function call. + * Return a non-zero value if the arguments can't be passed to the given + * signature. + * The type checks are not yet complete and some conversions may need + * casts on 32 or 64 bit architectures. + */ +static int +check_call_signature (MonoCompile *cfg, MonoMethodSignature *sig, MonoInst **args) +{ + int i, simple_type; + + if (sig->hasthis) { + if (args [0]->type != STACK_OBJ && args [0]->type != STACK_MP && args [0]->type != STACK_PTR) + return 1; + args++; + } + 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; + } + simple_type = sig->params [i]->type; +handle_enum: + switch (simple_type) { + case MONO_TYPE_VOID: + return 1; + continue; + case MONO_TYPE_I1: + case MONO_TYPE_U1: + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_I2: + case MONO_TYPE_U2: + case MONO_TYPE_CHAR: + case MONO_TYPE_I4: + case MONO_TYPE_U4: + if (args [i]->type != STACK_I4 && args [i]->type != STACK_PTR) + return 1; + continue; + case MONO_TYPE_I: + case MONO_TYPE_U: + case MONO_TYPE_PTR: + if (args [i]->type != STACK_I4 && args [i]->type != STACK_PTR && args [i]->type != STACK_MP && args [i]->type != STACK_OBJ) + return 1; + continue; + case MONO_TYPE_CLASS: + case MONO_TYPE_STRING: + case MONO_TYPE_OBJECT: + case MONO_TYPE_SZARRAY: + case MONO_TYPE_ARRAY: + if (args [i]->type != STACK_OBJ) + return 1; + continue; + case MONO_TYPE_I8: + case MONO_TYPE_U8: + if (args [i]->type != STACK_I8) + return 1; + continue; + case MONO_TYPE_R4: + case MONO_TYPE_R8: + if (args [i]->type != STACK_R8) + return 1; + continue; + case MONO_TYPE_VALUETYPE: + if (sig->params [i]->data.klass->enumtype) { + simple_type = sig->params [i]->data.klass->enum_basetype->type; + goto handle_enum; + } + if (args [i]->type != STACK_VTYPE) + return 1; + continue; + case MONO_TYPE_TYPEDBYREF: + if (args [i]->type != STACK_VTYPE) + return 1; + continue; + default: + g_error ("unknown type 0x%02x in check_call_signature", simple_type); + } + } + return 0; +} + inline static int mono_spill_call (MonoCompile *cfg, MonoBasicBlock *bblock, MonoCallInst *call, MonoMethodSignature *sig, gboolean ret_object, const guint8 *ip, gboolean to_end) @@ -1653,6 +1795,7 @@ mono_emit_call_args (MonoCompile *cfg, MonoBasicBlock *bblock, MonoMethodSignatu MonoInst **args, int calli, int virtual, const guint8 *ip, gboolean to_end) { MonoCallInst *call; + MonoInst *arg; int i; MONO_INST_NEW_CALL (cfg, call, ret_type_to_call_opcode (sig->ret, calli, virtual)); @@ -1662,13 +1805,16 @@ mono_emit_call_args (MonoCompile *cfg, MonoBasicBlock *bblock, MonoMethodSignatu call->signature = sig; call = mono_arch_call_opcode (cfg, bblock, call, virtual); - for (i = 0; i < (sig->param_count + sig->hasthis); ++i) { - if (call->args [i]) { - if (to_end) - mono_add_ins_to_end (bblock, call->args [i]); - else - MONO_ADD_INS (bblock, call->args [i]); - } + for (arg = call->out_args; arg;) { + MonoInst *narg = arg->next; + arg->next = NULL; + if (!arg->cil_code) + arg->cil_code = ip; + if (to_end) + mono_add_ins_to_end (bblock, arg); + else + MONO_ADD_INS (bblock, arg); + arg = narg; } return call; } @@ -1708,11 +1854,11 @@ mono_emit_method_call (MonoCompile *cfg, MonoBasicBlock *bblock, MonoMethod *met inline static int mono_emit_method_call_spilled (MonoCompile *cfg, MonoBasicBlock *bblock, MonoMethod *method, - MonoInst **args, const guint8 *ip, MonoInst *this) + MonoMethodSignature *signature, MonoInst **args, const guint8 *ip, MonoInst *this) { - MonoCallInst *call = mono_emit_method_call (cfg, bblock, method, method->signature, args, ip, this); + MonoCallInst *call = mono_emit_method_call (cfg, bblock, method, signature, args, ip, this); - return mono_spill_call (cfg, bblock, call, method->signature, method->string_ctor, ip, FALSE); + return mono_spill_call (cfg, bblock, call, signature, method->string_ctor, ip, FALSE); } inline static int @@ -1745,6 +1891,7 @@ static void mono_emulate_opcode (MonoCompile *cfg, MonoInst *tree, MonoInst **iargs, MonoJitICallInfo *info) { MonoInst *ins, *temp = NULL, *store, *load; + MonoInst *last_arg = NULL; int i, nargs; MonoCallInst *call; @@ -1770,23 +1917,21 @@ mono_emulate_opcode (MonoCompile *cfg, MonoInst *tree, MonoInst **iargs, MonoJit nargs = info->sig->param_count + info->sig->hasthis; - for (i = 1; i < nargs; i++) { - call->args [i - 1]->next = call->args [i]; - } + for (last_arg = call->out_args; last_arg && last_arg->next; last_arg = last_arg->next) ; if (nargs) - call->args [nargs - 1]->next = store; + last_arg->next = store; if (cfg->prev_ins) { store->next = cfg->prev_ins->next; if (nargs) - cfg->prev_ins->next = call->args [0]; + cfg->prev_ins->next = call->out_args; else cfg->prev_ins->next = store; } else { store->next = cfg->cbb->code; if (nargs) - cfg->cbb->code = call->args [0]; + cfg->cbb->code = call->out_args; else cfg->cbb->code = store; } @@ -1832,6 +1977,7 @@ static void handle_stobj (MonoCompile *cfg, MonoBasicBlock *bblock, MonoInst *dest, MonoInst *src, const unsigned char *ip, MonoClass *klass, gboolean to_end, gboolean native) { MonoInst *iargs [3]; int n; + guint32 align = 0; g_assert (klass); /* @@ -1840,10 +1986,20 @@ handle_stobj (MonoCompile *cfg, MonoBasicBlock *bblock, MonoInst *dest, MonoInst */ if (native) - n = mono_class_native_size (klass, NULL); + n = mono_class_native_size (klass, &align); else - n = mono_class_value_size (klass, NULL); - + n = mono_class_value_size (klass, &align); + + if ((cfg->opt & MONO_OPT_INTRINS) && !to_end && n <= sizeof (gpointer) * 5) { + MonoInst *inst; + MONO_INST_NEW (cfg, inst, OP_MEMCPY); + inst->inst_left = dest; + inst->inst_right = src; + inst->cil_code = ip; + inst->unused = n; + MONO_ADD_INS (bblock, inst); + return; + } iargs [0] = dest; iargs [1] = src; NEW_ICONST (cfg, iargs [2], n); @@ -1880,6 +2036,13 @@ handle_initobj (MonoCompile *cfg, MonoBasicBlock *bblock, MonoInst *dest, const MONO_ADD_INS (bblock, ins); break; default: + if (n <= sizeof (gpointer) * 5) { + ins->opcode = OP_MEMSET; + ins->inst_imm = 0; + ins->unused = n; + MONO_ADD_INS (bblock, ins); + break; + } handle_loaded_temps (cfg, bblock, stack_start, sp); NEW_ICONST (cfg, ins, n); iargs [0] = dest; @@ -1891,20 +2054,36 @@ handle_initobj (MonoCompile *cfg, MonoBasicBlock *bblock, MonoInst *dest, const #define CODE_IS_STLOC(ip) (((ip) [0] >= CEE_STLOC_0 && (ip) [0] <= CEE_STLOC_3) || ((ip) [0] == CEE_STLOC_S)) +static gboolean +needs_cctor_run (MonoClass *klass, MonoMethod *caller) +{ + int i; + MonoMethod *method; + + for (i = 0; i < klass->method.count; ++i) { + method = klass->methods [i]; + if ((method->flags & METHOD_ATTRIBUTE_SPECIAL_NAME) && + (strcmp (".cctor", method->name) == 0)) { + if (caller == method) + return FALSE; + return TRUE; + } + } + return FALSE; +} + static gboolean -mono_method_check_inlining (MonoMethod *method) +mono_method_check_inlining (MonoCompile *cfg, MonoMethod *method) { MonoMethodHeader *header = ((MonoMethodNormal *)method)->header; MonoMethodSignature *signature = method->signature; + MonoVTable *vtable; int i; - /* fixme: we should inline wrappers */ - if (method->wrapper_type != MONO_WRAPPER_NONE) - return FALSE; - if ((method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) || (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) || (method->iflags & METHOD_IMPL_ATTRIBUTE_NOINLINING) || + (method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED) || (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) || (method->klass->marshalbyref) || !header || header->num_clauses || @@ -1919,6 +2098,27 @@ mono_method_check_inlining (MonoMethod *method) } } + /* + * if we can initialize the class of the method right away, we do, + * otherwise we don't allow inlining if the class needs initialization, + * since it would mean inserting a call to mono_runtime_class_init() + * inside the inlined code + */ + if (!(cfg->opt & MONO_OPT_SHARED)) { + vtable = mono_class_vtable (cfg->domain, method->klass); + if (method->klass->flags & TYPE_ATTRIBUTE_BEFORE_FIELD_INIT) + mono_runtime_class_init (vtable); + else if (!vtable->initialized && needs_cctor_run (method->klass, NULL)) + return FALSE; + } else { + /* + * If we're compiling for shared code + * the cctor will need to be run at aot method load time, for example, + * or at the end of the compilation of the inlining method. + */ + if (needs_cctor_run (method->klass, NULL) && !((method->klass->flags & TYPE_ATTRIBUTE_BEFORE_FIELD_INIT))) + return FALSE; + } //if (!MONO_TYPE_IS_VOID (signature->ret)) return FALSE; /* also consider num_locals? */ @@ -1928,6 +2128,87 @@ mono_method_check_inlining (MonoMethod *method) return FALSE; } +static MonoInst* +mini_get_ldelema_ins (MonoCompile *cfg, MonoBasicBlock *bblock, MonoMethod *cmethod, MonoInst **sp, unsigned char *ip, gboolean is_set) +{ + int temp, rank; + MonoInst *addr; + MonoMethodSignature *esig; + + rank = cmethod->signature->param_count - (is_set? 1: 0); + /* + * FIXME: handle TypeMismatch for set or use the slow path + * for that. + */ + if (rank == 2 && (cfg->opt & MONO_OPT_INTRINS)) { + MonoInst *indexes; + NEW_GROUP (cfg, indexes, sp [1], sp [2]); + MONO_INST_NEW (cfg, addr, OP_LDELEMA2D); + addr->inst_left = sp [0]; + addr->inst_right = indexes; + addr->cil_code = ip; + addr->type = STACK_MP; + addr->klass = cmethod->klass; + return addr; + } + esig = mono_get_element_address_signature (rank); + temp = mono_emit_native_call (cfg, bblock, ves_array_element_address, esig, sp, ip, FALSE); + NEW_TEMPLOAD (cfg, addr, temp); + return addr; +} + +static MonoInst* +mini_get_opcode_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args) +{ + int pc, op; + MonoInst *ins; + + if (cmethod->klass == mono_defaults.string_class) { + if (cmethod->name [0] != 'g' || strcmp (cmethod->name, "get_Chars")) + return NULL; + op = OP_GETCHR; + } else if (cmethod->klass == mono_defaults.math_class) { + if (strcmp (cmethod->name, "Sin") == 0) + op = OP_SIN; + else if (strcmp (cmethod->name, "Cos") == 0) + op = OP_COS; + else if (strcmp (cmethod->name, "Tan") == 0) + op = OP_TAN; + else if (strcmp (cmethod->name, "Atan") == 0) + op = OP_ATAN; + else if (strcmp (cmethod->name, "Sqrt") == 0) + op = OP_SQRT; + else if (strcmp (cmethod->name, "Abs") == 0 && fsig->params [0]->type == MONO_TYPE_R8) + op = OP_ABS; +#if 0 + /* OP_FREM is not IEEE compatible */ + else if (strcmp (cmethod->name, "IEEERemainder") == 0) + op = OP_FREM; +#endif + 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 + return NULL; + } else { + 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; +} + static void mono_save_args (MonoCompile *cfg, MonoBasicBlock *bblock, MonoMethodSignature *sig, MonoInst **sp, MonoInst **args) { @@ -1956,7 +2237,7 @@ mono_save_args (MonoCompile *cfg, MonoBasicBlock *bblock, MonoMethodSignature *s if (sp [0]->opcode == OP_ICONST) { *args++ = sp [0]; } else { - temp = mono_compile_create_var (cfg, type_from_stack_type (*sp), OP_LOCAL); + temp = mono_compile_create_var (cfg, sig->params [i], OP_LOCAL); *args++ = temp; NEW_TEMPSTORE (cfg, store, temp->inst_c0, *sp); store->cil_code = sp [0]->cil_code; @@ -1971,6 +2252,72 @@ mono_save_args (MonoCompile *cfg, MonoBasicBlock *bblock, MonoMethodSignature *s } } +static int +inline_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoBasicBlock *bblock, MonoInst **sp, + guchar *ip, guint real_offset, GList *dont_inline, MonoBasicBlock **last_b) +{ + MonoInst *ins, *rvar = NULL; + MonoMethodHeader *cheader; + MonoBasicBlock *ebblock, *sbblock; + int i, costs, new_locals_offset; + + if (cfg->verbose_level > 2) + g_print ("INLINE START %p %s\n", cmethod, mono_method_full_name (cmethod, TRUE)); + + cheader = ((MonoMethodNormal *)cmethod)->header; + + if (!cmethod->inline_info) { + mono_jit_stats.inlineable_methods++; + cmethod->inline_info = 1; + } + /* allocate space to store the return value */ + if (!MONO_TYPE_IS_VOID (fsig->ret)) { + rvar = mono_compile_create_var (cfg, fsig->ret, OP_LOCAL); + } + + /* allocate local variables */ + new_locals_offset = cfg->num_varinfo; + for (i = 0; i < cheader->num_locals; ++i) + mono_compile_create_var (cfg, cheader->locals [i], OP_LOCAL); + + /* allocate starte and end blocks */ + sbblock = NEW_BBLOCK (cfg); + sbblock->block_num = cfg->num_bblocks++; + sbblock->real_offset = real_offset; + + ebblock = NEW_BBLOCK (cfg); + ebblock->block_num = cfg->num_bblocks++; + ebblock->real_offset = real_offset; + + costs = mono_method_to_ir (cfg, cmethod, sbblock, ebblock, new_locals_offset, rvar, dont_inline, sp, real_offset, *ip == CEE_CALLVIRT); + + if (costs >= 0 && costs < 60) { + if (cfg->verbose_level > 2) + g_print ("INLINE END %s\n", mono_method_full_name (cmethod, TRUE)); + + mono_jit_stats.inlined_methods++; + + /* always add some code to avoid block split failures */ + MONO_INST_NEW (cfg, ins, CEE_NOP); + MONO_ADD_INS (bblock, ins); + ins->cil_code = ip; + + bblock->next_bb = sbblock; + link_bblock (cfg, bblock, sbblock); + + if (rvar) { + NEW_TEMPLOAD (cfg, ins, rvar->inst_c0); + *sp++ = ins; + } + *last_b = ebblock; + return costs + 1; + } else { + if (cfg->verbose_level > 2) + g_print ("INLINE ABORTED %s\n", mono_method_full_name (cmethod, TRUE)); + } + return 0; +} + /* * Some of these comments may well be out-of-date. * Design decisions: we do a single pass over the IL code (and we do bblock @@ -2001,16 +2348,105 @@ mono_save_args (MonoCompile *cfg, MonoBasicBlock *bblock, MonoMethodSignature *s #define CHECK_STACK(num) if ((sp - stack_start) < (num)) goto unverified #define CHECK_STACK_OVF(num) if (((sp - stack_start) + (num)) > header->max_stack) goto unverified +#define TYPE_PARAM_TO_TYPE(num) (method->klass->generic_inst->data.generic_inst->type_argv [(num)]) +#define TYPE_PARAM_TO_CLASS(num) (mono_class_from_mono_type (TYPE_PARAM_TO_TYPE ((num)))) + /* offset from br.s -> br like opcodes */ #define BIG_BRANCH_OFFSET 13 +static int +get_basic_blocks (MonoCompile *cfg, GHashTable *bbhash, MonoMethodHeader* header, guint real_offset, unsigned char *start, unsigned char *end) +{ + unsigned char *ip = start; + unsigned char *target; + int i; + guint cli_addr; + MonoBasicBlock *bblock; + const MonoOpcode *opcode; + + while (ip < end) { + cli_addr = ip - start; + i = mono_opcode_value ((const guint8 **)&ip); + opcode = &mono_opcodes [i]; + switch (opcode->argument) { + case MonoInlineNone: + ip++; + break; + case MonoInlineString: + case MonoInlineType: + case MonoInlineField: + case MonoInlineMethod: + case MonoInlineTok: + case MonoInlineSig: + case MonoShortInlineR: + case MonoInlineI: + ip += 5; + break; + case MonoInlineVar: + ip += 3; + break; + case MonoShortInlineVar: + case MonoShortInlineI: + ip += 2; + break; + case MonoShortInlineBrTarget: + target = start + cli_addr + 2 + (signed char)ip [1]; + GET_BBLOCK (cfg, bbhash, bblock, target); + ip += 2; + break; + case MonoInlineBrTarget: + target = start + cli_addr + 5 + (gint32)read32 (ip + 1); + GET_BBLOCK (cfg, bbhash, bblock, target); + ip += 5; + break; + case MonoInlineSwitch: { + guint32 n = read32 (ip + 1); + guint32 j; + ip += 5; + cli_addr += 5 + 4 * n; + target = start + cli_addr; + GET_BBLOCK (cfg, bbhash, bblock, target); + + for (j = 0; j < n; ++j) { + target = start + cli_addr + (gint32)read32 (ip); + GET_BBLOCK (cfg, bbhash, bblock, target); + ip += 4; + } + break; + } + case MonoInlineR: + case MonoInlineI8: + ip += 9; + break; + default: + g_assert_not_reached (); + } + } + return 0; +unverified: + return 1; +} + +static MonoClassField * +get_generic_field_inst (MonoClassField *field, MonoClass *klass, MonoClass **retclass) +{ + int i; + for (i = 0; i < field->parent->field.count; ++i) { + if (field == &field->parent->fields [i]) { + *retclass = klass; + return &klass->fields [i]; + } + } + return NULL; +} + /* * mono_method_to_ir: translates IL into basic blocks containing trees */ static int mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_bblock, MonoBasicBlock *end_bblock, int locals_offset, MonoInst *return_var, GList *dont_inline, MonoInst **inline_args, - guint inline_offset) + guint inline_offset, gboolean is_virtual_call) { MonoInst *zero_int32, *zero_int64, *zero_ptr, *zero_obj, *zero_r8; MonoInst *ins, **sp, **stack_start; @@ -2030,6 +2466,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b 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; image = method->klass->image; @@ -2047,6 +2484,13 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b bbhash = g_hash_table_new (g_direct_hash, NULL); } + if (cfg->verbose_level > 2) + g_print ("method to IR %s\n", mono_method_full_name (method, TRUE)); + + if (cfg->prof_options & MONO_PROFILE_INS_COVERAGE) + cfg->coverage_info = mono_profiler_coverage_alloc (cfg->method, header->code_size); + + dont_inline = g_list_prepend (dont_inline, method); if (cfg->method == method) { /* ENTRY BLOCK */ @@ -2067,12 +2511,16 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b arg_array [i] = cfg->varinfo [i]; if (mono_compile_aot) - cfg->opt |= MONO_OPT_SAHRED; + cfg->opt |= MONO_OPT_SHARED; if (header->num_clauses) { int size = sizeof (int) * header->num_clauses; filter_lengths = alloca (size); memset (filter_lengths, 0, size); + + cfg->spvar = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL); + /* prevent it from being register allocated */ + cfg->spvar->flags |= MONO_INST_INDIRECT; } /* handle exception clauses */ for (i = 0; i < header->num_clauses; ++i) { @@ -2082,6 +2530,13 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b tblock->real_offset = clause->try_offset; GET_BBLOCK (cfg, bbhash, tblock, ip + clause->handler_offset); tblock->real_offset = clause->handler_offset; + + if (clause->flags == MONO_EXCEPTION_CLAUSE_FINALLY || + clause->flags == MONO_EXCEPTION_CLAUSE_FILTER) { + MONO_INST_NEW (cfg, ins, OP_START_HANDLER); + MONO_ADD_INS (tblock, ins); + } + /*g_print ("clause try IL_%04x to IL_%04x handler %d at IL_%04x to IL_%04x\n", clause->try_offset, clause->try_offset + clause->try_len, clause->flags, clause->handler_offset, clause->handler_offset + clause->handler_len); while (p < end) { g_print ("%s", mono_disasm_code_one (NULL, method, p, &p)); @@ -2099,12 +2554,15 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b tblock->in_scount = 1; tblock->in_stack = mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*)); tblock->in_stack [0] = cfg->exvar; + if (clause->flags == MONO_EXCEPTION_CLAUSE_FILTER) { GET_BBLOCK (cfg, bbhash, tblock, ip + clause->token_or_filter); tblock->real_offset = clause->token_or_filter; tblock->in_scount = 1; tblock->in_stack = mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*)); tblock->in_stack [0] = cfg->exvar; + MONO_INST_NEW (cfg, ins, OP_START_HANDLER); + MONO_ADD_INS (tblock, ins); } } } @@ -2121,13 +2579,14 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b ADD_BBLOCK (cfg, bbhash, bblock); if (cfg->method == method) { - if (mono_method_has_breakpoint (method, FALSE)) { + breakpoint_id = mono_debugger_method_has_breakpoint (method); + if (breakpoint_id && (mono_debug_format != MONO_DEBUG_FORMAT_DEBUGGER)) { MONO_INST_NEW (cfg, ins, CEE_BREAK); MONO_ADD_INS (bblock, ins); } } - if ((header->init_locals || (cfg->method == method && (cfg->opt & MONO_OPT_SAHRED)))) { + if ((header->init_locals || (cfg->method == method && (cfg->opt & MONO_OPT_SHARED)))) { /* we use a separate basic block for the initialization code */ cfg->bb_init = init_localsbb = NEW_BBLOCK (cfg); init_localsbb->real_offset = real_offset; @@ -2141,7 +2600,10 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b link_bblock (cfg, start_bblock, bblock); } - mono_debug_init_method (cfg, bblock); + if (get_basic_blocks (cfg, bbhash, header, real_offset, ip, end)) + goto unverified; + + mono_debug_init_method (cfg, bblock, breakpoint_id); param_types = mono_mempool_alloc (cfg->mempool, sizeof (MonoType*) * (sig->hasthis + sig->param_count)); if (sig->hasthis) @@ -2162,7 +2624,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b zero_r8->inst_p0 = &r8_0; /* add a check for this != NULL to inlined methods */ - if (cfg->method != method && sig->hasthis) { + if (is_virtual_call) { MONO_INST_NEW (cfg, ins, OP_CHECK_THIS); NEW_ARGLOAD (cfg, ins->inst_left, 0); ins->cil_code = ip; @@ -2211,6 +2673,26 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b } } + if (cfg->coverage_info) { + MonoInst *store, *one; + guint32 cil_offset = ip - header->code; + cfg->coverage_info->data [cil_offset].cil_code = ip; + + /* TODO: Use an increment here */ + NEW_ICONST (cfg, one, 1); + one->cil_code = ip; + + NEW_PCONST (cfg, ins, &(cfg->coverage_info->data [cil_offset].count)); + ins->cil_code = ip; + + MONO_INST_NEW (cfg, store, CEE_STIND_I); + store->cil_code = ip; + store->inst_left = ins; + store->inst_right = one; + + MONO_ADD_INS (bblock, store); + } + 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)); @@ -2445,12 +2927,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b token = read32 (ip + 1); /* FIXME: check the signature matches */ cmethod = mono_get_method (image, token, NULL); - /* - * The current magic trampoline can't handle this - * apparently, so we compile the method right away. - * Later, we may need to fix the trampoline or use a different one. - */ - ins->inst_p0 = mono_compile_method (cmethod); + ins->inst_p0 = cmethod; MONO_ADD_INS (bblock, ins); ip += 5; start_new_bblock = 1; @@ -2460,15 +2937,13 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b case CEE_CALLVIRT: { MonoInst *addr = NULL; MonoMethodSignature *fsig = NULL; - MonoMethodHeader *cheader; - int mop, temp, array_rank = 0; + int temp, array_rank = 0; int virtual = *ip == CEE_CALLVIRT; token = read32 (ip + 1); if (*ip == CEE_CALLI) { cmethod = NULL; - cheader = NULL; CHECK_STACK (1); --sp; addr = *sp; @@ -2480,8 +2955,11 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b n = fsig->param_count + fsig->hasthis; } else { - cmethod = mono_get_method (image, token, NULL); - cheader = ((MonoMethodNormal *)cmethod)->header; + if (method->wrapper_type != MONO_WRAPPER_NONE) { + cmethod = (MonoMethod *)mono_method_get_wrapper_data (method, token); + } else { + cmethod = mono_get_method (image, token, NULL); + } if (!cmethod->klass->inited) mono_class_init (cmethod->klass); @@ -2498,7 +2976,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b } #endif } else { - fsig = cmethod->signature; + fsig = mono_method_get_signature (cmethod, image, token); } n = fsig->param_count + fsig->hasthis; @@ -2519,17 +2997,28 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b sp -= n; - if (cmethod && (mop = mono_find_method_opcode (cmethod))) { - - MONO_INST_NEW (cfg, ins, mop); - ins->cil_code = ip; - g_assert (n <= 2); + if (*ip != CEE_CALLI && check_call_signature (cfg, fsig, sp)) + goto unverified; - if (fsig->param_count > 0) { - ins->inst_i0 = sp [0]; - if (fsig->param_count > 1) - ins->inst_i1 = sp [1]; + if ((ins_flag & MONO_INST_TAILCALL) && cmethod && (*ip == CEE_CALL)) { + int i; + for (i = 0; i < n; ++i) { + NEW_ARGSTORE (cfg, ins, i, sp [i]); + ins->cil_code = ip; + MONO_ADD_INS (bblock, ins); } + MONO_INST_NEW (cfg, ins, CEE_JMP); + ins->cil_code = ip; + ins->inst_p0 = cmethod; + MONO_ADD_INS (bblock, ins); + 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))) { + ins->cil_code = ip; if (MONO_TYPE_IS_VOID (fsig->ret)) { MONO_ADD_INS (bblock, ins); @@ -2543,83 +3032,36 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b break; } - if ((cfg->opt & MONO_OPT_INLINE) && + handle_loaded_temps (cfg, bblock, stack_start, sp); + + if ((cfg->opt & MONO_OPT_INLINE) && cmethod && (!virtual || !(cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL) || (cmethod->flags & METHOD_ATTRIBUTE_FINAL)) && - cmethod && cheader && mono_method_check_inlining (cmethod) && - method != cmethod && !g_list_find (dont_inline, cmethod)) { - MonoInst *rvar = NULL; - MonoBasicBlock *ebblock, *sbblock; - int costs, new_locals_offset; + mono_method_check_inlining (cfg, cmethod) && + !g_list_find (dont_inline, cmethod)) { + int costs; + MonoBasicBlock *ebblock; - if (cfg->verbose_level > 2) - g_print ("INLINE START %p %s\n", cmethod, mono_method_full_name (cmethod, TRUE)); - - if (!cmethod->inline_info) { - mono_jit_stats.inlineable_methods++; - cmethod->inline_info = 1; - } - /* allocate space to store the return value */ - if (!MONO_TYPE_IS_VOID (fsig->ret)) - rvar = mono_compile_create_var (cfg, fsig->ret, OP_LOCAL); - - /* allocate local variables */ - new_locals_offset = cfg->num_varinfo; - for (i = 0; i < cheader->num_locals; ++i) - mono_compile_create_var (cfg, cheader->locals [i], OP_LOCAL); - - /* allocate starte and end blocks */ - sbblock = NEW_BBLOCK (cfg); - sbblock->block_num = cfg->num_bblocks++; - sbblock->real_offset = real_offset; - - ebblock = NEW_BBLOCK (cfg); - ebblock->block_num = cfg->num_bblocks++; - ebblock->real_offset = real_offset; - - dont_inline = g_list_prepend (dont_inline, method); - costs = mono_method_to_ir (cfg, cmethod, sbblock, ebblock, new_locals_offset, rvar, dont_inline, sp, real_offset); - dont_inline = g_list_remove (dont_inline, method); - - if (costs >= 0 && costs < 60) { - - mono_jit_stats.inlined_methods++; - - /* always add some code to avoid block split failures */ - MONO_INST_NEW (cfg, ins, CEE_NOP); - MONO_ADD_INS (bblock, ins); - ins->cil_code = ip; - - ip += 5; - real_offset += 5; - - bblock->next_bb = sbblock; - link_bblock (cfg, bblock, sbblock); + if ((costs = inline_method (cfg, cmethod, fsig, bblock, sp, ip, real_offset, dont_inline, &ebblock))) { + ip += 5; + real_offset += 5; GET_BBLOCK (cfg, bbhash, bblock, ip); ebblock->next_bb = bblock; link_bblock (cfg, ebblock, bblock); - if (rvar) { - NEW_TEMPLOAD (cfg, ins, rvar->inst_c0); - *sp++ = ins; - } - if (cfg->verbose_level > 2) - g_print ("INLINE END %s\n", mono_method_full_name (cmethod, TRUE)); - - // { static int c = 0; printf ("ICOUNT %d %d %s\n", c++, costs, mono_method_full_name (cmethod, TRUE)); } + if (!MONO_TYPE_IS_VOID (fsig->ret)) + sp++; + + /* indicates start of a new block, and triggers a load of all + stack arguments at bb boundarie */ + bblock = ebblock; inline_costs += costs; break; - } else { - - if (cfg->verbose_level > 2) - g_print ("INLINE ABORTED %s\n", mono_method_full_name (cmethod, TRUE)); - } } inline_costs += 10 * num_calls++; - handle_loaded_temps (cfg, bblock, stack_start, sp); /* tail recursion elimination */ if ((cfg->opt & MONO_OPT_TAILC) && *ip == CEE_CALL && cmethod == cfg->method && ip [5] == CEE_RET) { @@ -2666,14 +3108,10 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b } } else if (array_rank) { - MonoMethodSignature *esig; MonoInst *addr; if (strcmp (cmethod->name, "Set") == 0) { /* array Set */ - esig = mono_get_element_address_signature (fsig->param_count - 1); - - temp = mono_emit_native_call (cfg, bblock, ves_array_element_address, esig, sp, ip, FALSE); - NEW_TEMPLOAD (cfg, addr, temp); + addr = mini_get_ldelema_ins (cfg, bblock, cmethod, sp, ip, TRUE); NEW_INDSTORE (cfg, ins, addr, sp [fsig->param_count], fsig->params [fsig->param_count - 1]); ins->cil_code = ip; if (ins->opcode == CEE_STOBJ) { @@ -2683,21 +3121,14 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b } } else if (strcmp (cmethod->name, "Get") == 0) { /* array Get */ - esig = mono_get_element_address_signature (fsig->param_count); - - temp = mono_emit_native_call (cfg, bblock, ves_array_element_address, esig, sp, ip, FALSE); - NEW_TEMPLOAD (cfg, addr, temp); + addr = mini_get_ldelema_ins (cfg, bblock, cmethod, sp, ip, FALSE); NEW_INDLOAD (cfg, ins, addr, fsig->ret); ins->cil_code = ip; *sp++ = ins; } else if (strcmp (cmethod->name, "Address") == 0) { /* array Address */ - /* implement me */ - esig = mono_get_element_address_signature (fsig->param_count); - - temp = mono_emit_native_call (cfg, bblock, ves_array_element_address, esig, sp, ip, FALSE); - NEW_TEMPLOAD (cfg, *sp, temp); - sp++; + addr = mini_get_ldelema_ins (cfg, bblock, cmethod, sp, ip, FALSE); + *sp++ = addr; } else { g_assert_not_reached (); } @@ -2708,7 +3139,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b ins = (MonoInst*)mono_emit_method_call (cfg, bblock, cmethod, fsig, sp, ip, virtual ? sp [0] : NULL); *sp++ = ins; } else { - if ((temp = mono_emit_method_call_spilled (cfg, bblock, cmethod, sp, ip, virtual ? sp [0] : NULL)) != -1) { + if ((temp = mono_emit_method_call_spilled (cfg, bblock, cmethod, fsig, sp, ip, virtual ? sp [0] : NULL)) != -1) { NEW_TEMPLOAD (cfg, *sp, temp); sp++; } @@ -2958,17 +3389,17 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b case CEE_CONV_I1: case CEE_CONV_I2: case CEE_CONV_I4: - case CEE_CONV_I8: case CEE_CONV_R4: case CEE_CONV_R8: case CEE_CONV_U4: + case CEE_CONV_I8: case CEE_CONV_U8: case CEE_CONV_OVF_I8: case CEE_CONV_OVF_U8: case CEE_CONV_R_UN: CHECK_STACK (1); ADD_UNOP (*ip); - ip++; + ip++; break; case CEE_CONV_OVF_I4: case CEE_CONV_OVF_I1: @@ -3015,7 +3446,53 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b ip++; break; case CEE_CPOBJ: - g_error ("opcode 0x%02x not handled", *ip); + CHECK_STACK (2); + token = read32 (ip + 1); + if (method->wrapper_type != MONO_WRAPPER_NONE) + klass = mono_method_get_wrapper_data (method, token); + else + klass = mono_class_get (image, token); + + mono_class_init (klass); + if (klass->byval_arg.type == MONO_TYPE_VAR) + klass = TYPE_PARAM_TO_CLASS (klass->byval_arg.data.type_param); + sp -= 2; + if (MONO_TYPE_IS_REFERENCE (&klass->byval_arg)) { + MonoInst *store, *load; + 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->flags |= ins_flag; + MONO_INST_NEW (cfg, store, CEE_STIND_REF); + store->cil_code = ip; + handle_loaded_temps (cfg, bblock, stack_start, sp); + MONO_ADD_INS (bblock, store); + store->inst_i0 = sp [0]; + store->inst_i1 = load; + store->flags |= ins_flag; + } else { + n = mono_class_value_size (klass, NULL); + if ((cfg->opt & MONO_OPT_INTRINS) && n <= sizeof (gpointer) * 5) { + MonoInst *copy; + MONO_INST_NEW (cfg, copy, OP_MEMCPY); + copy->inst_left = sp [0]; + copy->inst_right = sp [1]; + copy->cil_code = ip; + copy->unused = n; + MONO_ADD_INS (bblock, copy); + } else { + MonoInst *iargs [3]; + iargs [0] = sp [0]; + iargs [1] = sp [1]; + NEW_ICONST (cfg, iargs [2], n); + iargs [2]->cil_code = ip; + + mono_emit_jit_icall (cfg, bblock, helper_memcpy, iargs, ip); + } + } + ins_flag = 0; + ip += 5; break; case CEE_LDOBJ: { MonoInst *iargs [3]; @@ -3026,16 +3503,43 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b klass = mono_method_get_wrapper_data (method, token); else klass = mono_class_get (image, token); + mono_class_init (klass); + if (klass->byval_arg.type == MONO_TYPE_VAR) + klass = TYPE_PARAM_TO_CLASS (klass->byval_arg.data.type_param); + if (MONO_TYPE_IS_REFERENCE (&klass->byval_arg)) { + 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->flags |= ins_flag; + ins_flag = 0; + *sp++ = ins; + ip += 5; + 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); - iargs [1] = *sp; - NEW_ICONST (cfg, iargs [2], n); - mono_emit_jit_icall (cfg, bblock, helper_memcpy, iargs, ip); + if ((cfg->opt & MONO_OPT_INTRINS) && n <= sizeof (gpointer) * 5) { + MonoInst *copy; + MONO_INST_NEW (cfg, copy, OP_MEMCPY); + copy->inst_left = iargs [0]; + copy->inst_right = *sp; + copy->cil_code = ip; + copy->unused = n; + MONO_ADD_INS (bblock, copy); + } else { + iargs [1] = *sp; + NEW_ICONST (cfg, iargs [2], n); + iargs [2]->cil_code = ip; + + mono_emit_jit_icall (cfg, bblock, helper_memcpy, iargs, ip); + } NEW_TEMPLOAD (cfg, *sp, ins->inst_c0); ++sp; ip += 5; + ins_flag = 0; inline_costs += 1; break; } @@ -3043,26 +3547,44 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b CHECK_STACK_OVF (1); n = read32 (ip + 1); - if ((cfg->opt & MONO_OPT_SAHRED) || mono_compile_aot) { + if (method->wrapper_type != MONO_WRAPPER_NONE) { int temp; - MonoInst *iargs [3]; - NEW_TEMPLOAD (cfg, iargs [0], mono_get_domainvar (cfg)->inst_c0); - NEW_IMAGECONST (cfg, iargs [1], image); - NEW_ICONST (cfg, iargs [2], mono_metadata_token_index (n)); - temp = mono_emit_jit_icall (cfg, bblock, mono_ldstr, iargs, ip); + MonoInst *iargs [1]; + + NEW_PCONST (cfg, iargs [0], mono_method_get_wrapper_data (method, n)); + temp = mono_emit_jit_icall (cfg, bblock, mono_string_new_wrapper, iargs, ip); NEW_TEMPLOAD (cfg, *sp, temp); + } else { - NEW_PCONST (cfg, ins, NULL); - ins->cil_code = ip; - ins->type = STACK_OBJ; - ins->inst_p0 = mono_ldstr (cfg->domain, image, mono_metadata_token_index (n)); - *sp = ins; + + if (mono_compile_aot) { + cfg->ldstr_list = g_list_prepend (cfg->ldstr_list, (gpointer)n); + } + + if ((cfg->opt & MONO_OPT_SHARED) || mono_compile_aot) { + int temp; + MonoInst *iargs [3]; + NEW_TEMPLOAD (cfg, iargs [0], mono_get_domainvar (cfg)->inst_c0); + NEW_IMAGECONST (cfg, iargs [1], image); + NEW_ICONST (cfg, iargs [2], mono_metadata_token_index (n)); + temp = mono_emit_jit_icall (cfg, bblock, mono_ldstr, iargs, ip); + NEW_TEMPLOAD (cfg, *sp, temp); + mono_ldstr (cfg->domain, image, mono_metadata_token_index (n)); + } else { + NEW_PCONST (cfg, ins, NULL); + ins->cil_code = ip; + ins->type = STACK_OBJ; + ins->inst_p0 = mono_ldstr (cfg->domain, image, mono_metadata_token_index (n)); + *sp = ins; + } } + sp++; ip += 5; break; case CEE_NEWOBJ: { MonoInst *iargs [2]; + MonoMethodSignature *fsig; int temp; token = read32 (ip + 1); @@ -3070,10 +3592,11 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b cmethod = mono_method_get_wrapper_data (method, token); } else cmethod = mono_get_method (image, token, NULL); + fsig = mono_method_get_signature (cmethod, image, token); mono_class_init (cmethod->klass); - n = cmethod->signature->param_count; + n = fsig->param_count; CHECK_STACK (n); /* move the args to allow room for 'this' in the first position */ @@ -3087,28 +3610,67 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b if (cmethod->klass->parent == mono_defaults.array_class) { NEW_METHODCONST (cfg, *sp, cmethod); - temp = mono_emit_native_call (cfg, bblock, mono_array_new_va, cmethod->signature, sp, ip, FALSE); + temp = mono_emit_native_call (cfg, bblock, mono_array_new_va, fsig, sp, ip, FALSE); } else if (cmethod->string_ctor) { /* we simply pass a null pointer */ NEW_PCONST (cfg, *sp, NULL); /* now call the string ctor */ - temp = mono_emit_method_call_spilled (cfg, bblock, cmethod, sp, ip, NULL); + temp = mono_emit_method_call_spilled (cfg, bblock, cmethod, fsig, sp, ip, NULL); } else { if (cmethod->klass->valuetype) { iargs [0] = mono_compile_create_var (cfg, &cmethod->klass->byval_arg, OP_LOCAL); temp = iargs [0]->inst_c0; NEW_TEMPLOADA (cfg, *sp, temp); } else { - NEW_DOMAINCONST (cfg, iargs [0]); - NEW_CLASSCONST (cfg, iargs [1], cmethod->klass); + if ((cfg->opt & MONO_OPT_SHARED) || mono_compile_aot) { + NEW_DOMAINCONST (cfg, iargs [0]); + NEW_CLASSCONST (cfg, iargs [1], cmethod->klass); - temp = mono_emit_jit_icall (cfg, bblock, mono_object_new, iargs, ip); + temp = mono_emit_jit_icall (cfg, bblock, mono_object_new, iargs, ip); + } else { + MonoVTable *vtable = mono_class_vtable (cfg->domain, cmethod->klass); + NEW_PCONST (cfg, iargs [0], vtable); + if (cmethod->klass->has_finalize || cmethod->klass->marshalbyref || (cfg->prof_options & MONO_PROFILE_ALLOCATIONS)) + temp = mono_emit_jit_icall (cfg, bblock, mono_object_new_specific, iargs, ip); + else + temp = mono_emit_jit_icall (cfg, bblock, mono_object_new_fast, iargs, ip); + } NEW_TEMPLOAD (cfg, *sp, temp); } - /* now call the actual ctor */ - mono_emit_method_call_spilled (cfg, bblock, cmethod, sp, ip, NULL); + if ((cfg->opt & MONO_OPT_INLINE) && cmethod && + mono_method_check_inlining (cfg, cmethod) && + !mono_class_is_subclass_of (cmethod->klass, mono_defaults.exception_class, FALSE) && + !g_list_find (dont_inline, cmethod)) { + int costs; + MonoBasicBlock *ebblock; + if ((costs = inline_method (cfg, cmethod, fsig, bblock, sp, ip, real_offset, dont_inline, &ebblock))) { + + ip += 5; + real_offset += 5; + + GET_BBLOCK (cfg, bbhash, bblock, ip); + ebblock->next_bb = bblock; + link_bblock (cfg, ebblock, bblock); + + NEW_TEMPLOAD (cfg, *sp, temp); + sp++; + + /* indicates start of a new block, and triggers a load + of all stack arguments at bb boundarie */ + bblock = ebblock; + + inline_costs += costs; + break; + + } else { + mono_emit_method_call_spilled (cfg, bblock, cmethod, fsig, sp, ip, sp[0]); + } + } else { + /* now call the actual ctor */ + mono_emit_method_call_spilled (cfg, bblock, cmethod, fsig, sp, ip, sp[0]); + } } NEW_TEMPLOAD (cfg, *sp, temp); @@ -3131,9 +3693,76 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b ip += 5; *sp++ = ins; break; + case CEE_UNBOX_ANY: { + MonoInst *add, *vtoffset; + MonoInst *iargs [3]; + + CHECK_STACK (1); + --sp; + token = read32 (ip + 1); + if (method->wrapper_type != MONO_WRAPPER_NONE) + klass = (MonoClass *)mono_method_get_wrapper_data (method, token); + else + klass = mono_class_get (image, token); + mono_class_init (klass); + + if (klass->byval_arg.type == MONO_TYPE_VAR) + klass = TYPE_PARAM_TO_CLASS (klass->byval_arg.data.type_param); + + if (MONO_TYPE_IS_REFERENCE (&klass->byval_arg)) { + /* CASTCLASS */ + MONO_INST_NEW (cfg, ins, CEE_CASTCLASS); + ins->type = STACK_OBJ; + ins->inst_left = *sp; + ins->klass = klass; + ins->inst_newa_class = klass; + ins->cil_code = ip; + *sp++ = ins; + ip += 5; + break; + } + + MONO_INST_NEW (cfg, ins, OP_UNBOXCAST); + ins->type = STACK_OBJ; + ins->inst_left = *sp; + ins->klass = klass; + ins->inst_newa_class = klass; + ins->cil_code = ip; + + MONO_INST_NEW (cfg, add, CEE_ADD); + NEW_ICONST (cfg, vtoffset, sizeof (MonoObject)); + add->inst_left = ins; + add->inst_right = vtoffset; + add->type = STACK_MP; + *sp = add; + ip += 5; + /* LDOBJ impl */ + 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); + if ((cfg->opt & MONO_OPT_INTRINS) && n <= sizeof (gpointer) * 5) { + MonoInst *copy; + MONO_INST_NEW (cfg, copy, OP_MEMCPY); + copy->inst_left = iargs [0]; + copy->inst_right = *sp; + copy->cil_code = ip; + copy->unused = n; + MONO_ADD_INS (bblock, copy); + } else { + iargs [1] = *sp; + NEW_ICONST (cfg, iargs [2], n); + iargs [2]->cil_code = ip; + + mono_emit_jit_icall (cfg, bblock, helper_memcpy, iargs, ip); + } + NEW_TEMPLOAD (cfg, *sp, ins->inst_c0); + ++sp; + inline_costs += 2; + break; + } case CEE_UNBOX: { MonoInst *add, *vtoffset; - /* FIXME: need to check class: move to inssel.brg? */ + CHECK_STACK (1); --sp; token = read32 (ip + 1); @@ -3142,14 +3771,25 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b else klass = mono_class_get (image, token); mono_class_init (klass); + + if (klass->byval_arg.type == MONO_TYPE_VAR) + klass = TYPE_PARAM_TO_CLASS (klass->byval_arg.data.type_param); + + MONO_INST_NEW (cfg, ins, OP_UNBOXCAST); + ins->type = STACK_OBJ; + ins->inst_left = *sp; + ins->klass = klass; + ins->inst_newa_class = klass; + ins->cil_code = ip; + MONO_INST_NEW (cfg, add, CEE_ADD); NEW_ICONST (cfg, vtoffset, sizeof (MonoObject)); - add->inst_left = *sp; + add->inst_left = ins; add->inst_right = vtoffset; add->type = STACK_MP; *sp++ = add; ip += 5; - inline_costs += 1; + inline_costs += 2; break; } case CEE_CASTCLASS: @@ -3158,6 +3798,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b --sp; klass = mono_class_get (image, read32 (ip + 1)); mono_class_init (klass); + if (klass->byval_arg.type == MONO_TYPE_VAR) + klass = TYPE_PARAM_TO_CLASS (klass->byval_arg.data.type_param); ins->type = STACK_OBJ; ins->inst_left = *sp; ins->klass = klass; @@ -3181,6 +3823,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b case CEE_STFLD: { MonoInst *offset_ins; MonoClassField *field; + MonoBasicBlock *ebblock; + int costs; guint foffset; if (*ip == CEE_STFLD) { @@ -3195,20 +3839,45 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b // goto unverified; token = read32 (ip + 1); field = mono_field_from_token (image, token, &klass); + if (field->parent->gen_params) + field = get_generic_field_inst (field, method->klass, &klass); mono_class_init (klass); + foffset = klass->valuetype? field->offset - sizeof (MonoObject): field->offset; /* FIXME: mark instructions for use in SSA */ if (*ip == CEE_STFLD) { if (klass->marshalbyref && !MONO_CHECK_THIS (sp [0])) { - /* fixme: we need to inline that call somehow */ MonoMethod *stfld_wrapper = mono_marshal_get_stfld_wrapper (field->type); MonoInst *iargs [5]; + iargs [0] = sp [0]; 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); + NEW_ICONST (cfg, iargs [3], klass->valuetype ? field->offset - sizeof (MonoObject) : + field->offset); iargs [4] = sp [1]; - mono_emit_method_call_spilled (cfg, bblock, stfld_wrapper, iargs, ip, NULL); + + if (cfg->opt & MONO_OPT_INLINE) { + costs = inline_method (cfg, stfld_wrapper, stfld_wrapper->signature, bblock, + iargs, ip, real_offset, dont_inline, &ebblock); + g_assert (costs > 0); + + ip += 5; + real_offset += 5; + + GET_BBLOCK (cfg, bbhash, bblock, ip); + ebblock->next_bb = bblock; + link_bblock (cfg, ebblock, bblock); + + /* indicates start of a new block, and triggers a load + of all stack arguments at bb boundarie */ + bblock = ebblock; + + inline_costs += costs; + break; + } else { + mono_emit_method_call_spilled (cfg, bblock, stfld_wrapper, stfld_wrapper->signature, iargs, ip, NULL); + } } else { MonoInst *store; NEW_ICONST (cfg, offset_ins, foffset); @@ -3237,18 +3906,49 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b MonoMethod *ldfld_wrapper = mono_marshal_get_ldfld_wrapper (field->type); MonoInst *iargs [4]; int temp; + iargs [0] = sp [0]; 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); - temp = mono_emit_method_call_spilled (cfg, bblock, ldfld_wrapper, iargs, ip, NULL); - if (*ip == CEE_LDFLDA) { - /* not sure howto handle this */ - NEW_TEMPLOADA (cfg, *sp, temp); + if (cfg->opt & MONO_OPT_INLINE) { + costs = inline_method (cfg, ldfld_wrapper, ldfld_wrapper->signature, bblock, + iargs, ip, real_offset, dont_inline, &ebblock); + g_assert (costs > 0); + + ip += 5; + real_offset += 5; + + GET_BBLOCK (cfg, bbhash, bblock, ip); + ebblock->next_bb = bblock; + link_bblock (cfg, ebblock, bblock); + + temp = iargs [0]->inst_i0->inst_c0; + + if (*ip == CEE_LDFLDA) { + /* not sure howto handle this */ + NEW_TEMPLOADA (cfg, *sp, temp); + } else { + NEW_TEMPLOAD (cfg, *sp, temp); + } + sp++; + + /* indicates start of a new block, and triggers a load of + all stack arguments at bb boundarie */ + bblock = ebblock; + + inline_costs += costs; + break; } else { - NEW_TEMPLOAD (cfg, *sp, temp); + temp = mono_emit_method_call_spilled (cfg, bblock, ldfld_wrapper, ldfld_wrapper->signature, iargs, ip, NULL); + if (*ip == CEE_LDFLDA) { + /* not sure howto handle this */ + NEW_TEMPLOADA (cfg, *sp, temp); + } else { + NEW_TEMPLOAD (cfg, *sp, temp); + } + sp++; } - sp++; } else { NEW_ICONST (cfg, offset_ins, foffset); MONO_INST_NEW (cfg, ins, CEE_ADD); @@ -3278,7 +3978,6 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b case CEE_LDSFLDA: case CEE_STSFLD: { MonoClassField *field; - MonoVTable *vtable; token = read32 (ip + 1); @@ -3287,7 +3986,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b handle_loaded_temps (cfg, bblock, stack_start, sp); - if (((cfg->opt & MONO_OPT_SAHRED) || mono_compile_aot)) { + if (((cfg->opt & MONO_OPT_SHARED) || mono_compile_aot)) { int temp; MonoInst *iargs [2]; g_assert (field->parent); @@ -3296,9 +3995,34 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b temp = mono_emit_jit_icall (cfg, bblock, mono_class_static_field_address, iargs, ip); NEW_TEMPLOAD (cfg, ins, temp); } else { + gpointer addr; + MonoVTable *vtable; vtable = mono_class_vtable (cfg->domain, klass); - NEW_PCONST (cfg, ins, (char*)vtable->data + field->offset); - ins->cil_code = ip; + if (!cfg->domain->thread_static_fields || !(addr = g_hash_table_lookup (cfg->domain->thread_static_fields, field))) { + if (!vtable->initialized && !(klass->flags & TYPE_ATTRIBUTE_BEFORE_FIELD_INIT) && needs_cctor_run (klass, method)) { + MonoInst *iargs [1]; + NEW_PCONST (cfg, iargs [0], vtable); + mono_emit_jit_icall (cfg, bblock, mono_runtime_class_init, iargs, ip); + if (cfg->verbose_level > 2) + g_print ("class %s.%s needs init call for %s\n", klass->name_space, klass->name, field->name); + } else { + mono_runtime_class_init (vtable); + } + addr = (char*)vtable->data + field->offset; + NEW_PCONST (cfg, ins, addr); + ins->cil_code = ip; + } else { + /* + * insert call to mono_threads_get_static_data (GPOINTER_TO_UINT (addr)) + * This could be later optimized to do just a couple of + * memory dereferences with constant offsets. + */ + int temp; + MonoInst *iargs [1]; + NEW_ICONST (cfg, iargs [0], GPOINTER_TO_UINT (addr)); + temp = mono_emit_jit_icall (cfg, bblock, mono_threads_get_static_data, iargs, ip); + NEW_TEMPLOAD (cfg, ins, temp); + } } /* FIXME: mark instructions for use in SSA */ @@ -3320,17 +4044,83 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b } else MONO_ADD_INS (bblock, store); } else { - MonoInst *load; - CHECK_STACK_OVF (1); - MONO_INST_NEW (cfg, load, mono_type_to_ldind (field->type)); - type_to_eval_stack_type (field->type, load); - load->cil_code = ip; - load->inst_left = ins; - *sp++ = load; - load->flags |= ins_flag; - ins_flag = 0; - /* fixme: dont see the problem why this does not work */ - //cfg->disable_aot = TRUE; + gboolean is_const = FALSE; + MonoVTable *vtable = mono_class_vtable (cfg->domain, klass); + if (!((cfg->opt & MONO_OPT_SHARED) || mono_compile_aot) && + vtable->initialized && (field->type->attrs & FIELD_ATTRIBUTE_INIT_ONLY)) { + gpointer addr = (char*)vtable->data + field->offset; + /* g_print ("RO-FIELD %s.%s:%s\n", klass->name_space, klass->name, field->name);*/ + is_const = TRUE; + switch (field->type->type) { + case MONO_TYPE_BOOLEAN: + case MONO_TYPE_U1: + NEW_ICONST (cfg, *sp, *((guint8 *)addr)); + sp++; + break; + case MONO_TYPE_I1: + NEW_ICONST (cfg, *sp, *((gint8 *)addr)); + sp++; + break; + case MONO_TYPE_CHAR: + case MONO_TYPE_U2: + NEW_ICONST (cfg, *sp, *((guint16 *)addr)); + sp++; + break; + case MONO_TYPE_I2: + NEW_ICONST (cfg, *sp, *((gint16 *)addr)); + sp++; + break; + break; + case MONO_TYPE_I4: + NEW_ICONST (cfg, *sp, *((gint32 *)addr)); + sp++; + break; + case MONO_TYPE_U4: + NEW_ICONST (cfg, *sp, *((guint32 *)addr)); + sp++; + break; + case MONO_TYPE_I: + case MONO_TYPE_U: + case MONO_TYPE_STRING: + case MONO_TYPE_OBJECT: + case MONO_TYPE_CLASS: + case MONO_TYPE_SZARRAY: + case MONO_TYPE_PTR: + case MONO_TYPE_FNPTR: + case MONO_TYPE_ARRAY: + NEW_PCONST (cfg, *sp, *((gpointer *)addr)); + type_to_eval_stack_type (field->type, *sp); + sp++; + break; + case MONO_TYPE_I8: + case MONO_TYPE_U8: + MONO_INST_NEW (cfg, *sp, OP_I8CONST); + sp [0]->type = STACK_I8; + sp [0]->inst_l = *((gint64 *)addr); + sp++; + break; + case MONO_TYPE_R4: + case MONO_TYPE_R8: + case MONO_TYPE_VALUETYPE: + default: + is_const = FALSE; + break; + } + } + + if (!is_const) { + MonoInst *load; + CHECK_STACK_OVF (1); + MONO_INST_NEW (cfg, load, mono_type_to_ldind (field->type)); + type_to_eval_stack_type (field->type, load); + load->cil_code = ip; + load->inst_left = ins; + *sp++ = load; + load->flags |= ins_flag; + ins_flag = 0; + /* fixme: dont see the problem why this does not work */ + //cfg->disable_aot = TRUE; + } } ip += 5; break; @@ -3344,7 +4134,22 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b else klass = mono_class_get (image, token); mono_class_init (klass); - handle_stobj (cfg, bblock, sp [0], sp [1], ip, klass, FALSE, FALSE); + if (klass->byval_arg.type == MONO_TYPE_VAR) + klass = TYPE_PARAM_TO_CLASS (klass->byval_arg.data.type_param); + n = mono_type_to_stind (&klass->byval_arg); + if (n == CEE_STOBJ) { + handle_stobj (cfg, bblock, sp [0], sp [1], ip, klass, FALSE, FALSE); + } else { + /* FIXME: should check item at sp [1] is compatible with the type of the store. */ + MonoInst *store; + MONO_INST_NEW (cfg, store, n); + store->cil_code = ip; + store->inst_left = sp [0]; + store->inst_right = sp [1]; + store->flags |= ins_flag; + MONO_ADD_INS (bblock, store); + } + ins_flag = 0; ip += 5; inline_costs += 1; break; @@ -3361,12 +4166,28 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b else klass = mono_class_get (image, token); mono_class_init (klass); + if (klass->byval_arg.type == MONO_TYPE_VAR) + klass = TYPE_PARAM_TO_CLASS (klass->byval_arg.data.type_param); + if (MONO_TYPE_IS_REFERENCE (&klass->byval_arg)) { + *sp = val; + ip += 5; + break; + } /* much like NEWOBJ */ - NEW_DOMAINCONST (cfg, iargs [0]); - NEW_CLASSCONST (cfg, iargs [1], klass); - - temp = mono_emit_jit_icall (cfg, bblock, mono_object_new, iargs, ip); + if ((cfg->opt & MONO_OPT_SHARED) || mono_compile_aot) { + NEW_DOMAINCONST (cfg, iargs [0]); + NEW_CLASSCONST (cfg, iargs [1], klass); + + temp = mono_emit_jit_icall (cfg, bblock, mono_object_new, iargs, ip); + } else { + MonoVTable *vtable = mono_class_vtable (cfg->domain, klass); + NEW_PCONST (cfg, iargs [0], vtable); + if (1 || klass->has_finalize || (cfg->prof_options & MONO_PROFILE_ALLOCATIONS)) + temp = mono_emit_jit_icall (cfg, bblock, mono_object_new_specific, iargs, ip); + else + temp = mono_emit_jit_icall (cfg, bblock, mono_object_new_fast, iargs, ip); + } NEW_TEMPLOAD (cfg, load, temp); NEW_ICONST (cfg, vtoffset, sizeof (MonoObject)); MONO_INST_NEW (cfg, add, CEE_ADD); @@ -3400,7 +4221,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b token = read32 (ip + 1); /* allocate the domainvar - becaus this is used in decompose_foreach */ - if ((cfg->opt & MONO_OPT_SAHRED) || mono_compile_aot) + if ((cfg->opt & MONO_OPT_SHARED) || mono_compile_aot) mono_get_domainvar (cfg); if (method->wrapper_type != MONO_WRAPPER_NONE) @@ -3409,6 +4230,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b klass = mono_class_get (image, token); mono_class_init (klass); + if (klass->byval_arg.type == MONO_TYPE_VAR) + klass = TYPE_PARAM_TO_CLASS (klass->byval_arg.data.type_param); ins->inst_newa_class = klass; ins->inst_newa_len = *sp; ins->type = STACK_OBJ; @@ -3430,11 +4253,32 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b sp -= 2; klass = mono_class_get (image, read32 (ip + 1)); mono_class_init (klass); + if (klass->byval_arg.type == MONO_TYPE_VAR) + klass = TYPE_PARAM_TO_CLASS (klass->byval_arg.data.type_param); NEW_LDELEMA (cfg, ins, sp, klass); ins->cil_code = ip; *sp++ = ins; ip += 5; break; + case CEE_LDELEM: { + MonoInst *load; + CHECK_STACK (2); + sp -= 2; + token = read32 (ip + 1); + klass = mono_class_get (image, token); + mono_class_init (klass); + if (klass->byval_arg.type == MONO_TYPE_VAR) + klass = TYPE_PARAM_TO_CLASS (klass->byval_arg.data.type_param); + NEW_LDELEMA (cfg, load, sp, klass); + load->cil_code = ip; + MONO_INST_NEW (cfg, ins, mono_type_to_ldind (&klass->byval_arg)); + ins->cil_code = ip; + ins->inst_left = load; + *sp++ = ins; + type_to_eval_stack_type (&klass->byval_arg, ins); + ip += 5; + break; + } case CEE_LDELEM_I1: case CEE_LDELEM_U1: case CEE_LDELEM_I2: @@ -3471,8 +4315,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b case CEE_STELEM_I4: case CEE_STELEM_I8: case CEE_STELEM_R4: - case CEE_STELEM_R8: - case CEE_STELEM_REF: { + case CEE_STELEM_R8: { MonoInst *load; /* * translate to: @@ -3491,7 +4334,73 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b ++ip; handle_loaded_temps (cfg, bblock, stack_start, sp); MONO_ADD_INS (bblock, ins); - /* FIXME: add the implicit STELEM_REF castclass */ + inline_costs += 1; + cfg->disable_ssa = TRUE; + break; + } + case CEE_STELEM: { + MonoInst *load; + /* + * translate to: + * stind.x (ldelema (array, index), val) + * ldelema does the bounds check + */ + CHECK_STACK (3); + sp -= 3; + token = read32 (ip + 1); + klass = mono_class_get (image, token); + mono_class_init (klass); + if (klass->byval_arg.type == MONO_TYPE_VAR) + klass = TYPE_PARAM_TO_CLASS (klass->byval_arg.data.type_param); + if (MONO_TYPE_IS_REFERENCE (&klass->byval_arg)) { + MonoInst *iargs [3]; + handle_loaded_temps (cfg, bblock, stack_start, sp); + + iargs [2] = sp [2]; + iargs [1] = sp [1]; + iargs [0] = sp [0]; + + mono_emit_jit_icall (cfg, bblock, helper_stelem_ref, iargs, ip); + } else { + NEW_LDELEMA (cfg, load, sp, klass); + load->cil_code = ip; + MONO_INST_NEW (cfg, ins, mono_type_to_stind (&klass->byval_arg)); + ins->cil_code = ip; + ins->inst_left = load; + ins->inst_right = sp [2]; + handle_loaded_temps (cfg, bblock, stack_start, sp); + MONO_ADD_INS (bblock, ins); + } + ip += 5; + inline_costs += 1; + cfg->disable_ssa = TRUE; + break; + } + case CEE_STELEM_REF: { + MonoInst *iargs [3]; + + CHECK_STACK (3); + sp -= 3; + + handle_loaded_temps (cfg, bblock, stack_start, sp); + + iargs [2] = sp [2]; + iargs [1] = sp [1]; + iargs [0] = sp [0]; + + mono_emit_jit_icall (cfg, bblock, helper_stelem_ref, iargs, ip); + + /* + MonoInst *group; + NEW_GROUP (cfg, group, sp [0], sp [1]); + MONO_INST_NEW (cfg, ins, CEE_STELEM_REF); + ins->cil_code = ip; + ins->inst_left = group; + ins->inst_right = sp [2]; + MONO_ADD_INS (bblock, ins); + */ + + ++ip; inline_costs += 1; cfg->disable_ssa = TRUE; break; @@ -3535,7 +4444,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b handle = mono_ldtoken (image, n, &handle_class); mono_class_init (handle_class); - if (((cfg->opt & MONO_OPT_SAHRED) || mono_compile_aot)) { + if (((cfg->opt & MONO_OPT_SHARED) || mono_compile_aot)) { int temp; MonoInst *res, *store, *addr, *vtvar, *iargs [2]; @@ -3595,7 +4504,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b start_new_bblock = 1; break; case CEE_LEAVE: - case CEE_LEAVE_S: + case CEE_LEAVE_S: { + GList *handlers; if (*ip == CEE_LEAVE) { target = ip + 5 + (gint32)read32(ip + 1); } else { @@ -3613,14 +4523,18 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b /* fixme: call fault handler ? */ - if ((tblock = mono_find_final_block (cfg, ip, target, MONO_EXCEPTION_CLAUSE_FINALLY))) { - link_bblock (cfg, bblock, tblock); - MONO_INST_NEW (cfg, ins, OP_HANDLER); - ins->cil_code = ip; - ins->inst_target_bb = tblock; - MONO_ADD_INS (bblock, ins); - } - + if ((handlers = mono_find_final_block (cfg, ip, target, MONO_EXCEPTION_CLAUSE_FINALLY))) { + GList *tmp; + for (tmp = handlers; tmp; tmp = tmp->next) { + tblock = tmp->data; + link_bblock (cfg, bblock, tblock); + MONO_INST_NEW (cfg, ins, OP_CALL_HANDLER); + ins->cil_code = ip; + ins->inst_target_bb = tblock; + MONO_ADD_INS (bblock, ins); + } + g_list_free (handlers); + } MONO_INST_NEW (cfg, ins, CEE_BR); ins->cil_code = ip; @@ -3636,8 +4550,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b else ip += 2; - break; + } case CEE_STIND_I: CHECK_STACK (2); MONO_INST_NEW (cfg, ins, *ip); @@ -3851,9 +4765,24 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b } case CEE_PREFIX1: { switch (ip [1]) { - case CEE_ARGLIST: - g_error ("opcode 0xfe 0x%02x not handled", ip [1]); + case CEE_ARGLIST: { + /* somewhat similar to LDTOKEN */ + MonoInst *addr, *vtvar; + CHECK_STACK_OVF (1); + vtvar = mono_compile_create_var (cfg, &mono_defaults.argumenthandle_class->byval_arg, OP_LOCAL); + + NEW_TEMPLOADA (cfg, addr, vtvar->inst_c0); + addr->cil_code = ip; + MONO_INST_NEW (cfg, ins, OP_ARGLIST); + ins->cil_code = ip; + ins->inst_left = addr; + MONO_ADD_INS (bblock, ins); + NEW_TEMPLOAD (cfg, ins, vtvar->inst_c0); + ins->cil_code = ip; + *sp++ = ins; + ip += 2; break; + } case CEE_CEQ: case CEE_CGT: case CEE_CGT_UN: @@ -3885,9 +4814,17 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b n = read32 (ip + 2); if (method->wrapper_type != MONO_WRAPPER_NONE) cmethod = mono_method_get_wrapper_data (method, n); - else + else { cmethod = mono_get_method (image, n, NULL); - + + /* + * We can't do this in mono_ldftn, since it is used in + * the synchronized wrapper, leading to an infinite loop. + */ + if (cmethod->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED) + cmethod = mono_marshal_get_synchronized_wrapper (cmethod); + } + mono_class_init (cmethod->klass); handle_loaded_temps (cfg, bblock, stack_start, sp); @@ -4047,10 +4984,31 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b klass = mono_method_get_wrapper_data (method, token); else klass = mono_class_get (image, token); - handle_initobj (cfg, bblock, *sp, NULL, klass, stack_start, sp); + if (klass->byval_arg.type == MONO_TYPE_VAR) + klass = TYPE_PARAM_TO_CLASS (klass->byval_arg.data.type_param); + if (MONO_TYPE_IS_REFERENCE (&klass->byval_arg)) { + MonoInst *store, *load; + NEW_PCONST (cfg, load, NULL); + load->cil_code = ip; + load->type = STACK_OBJ; + MONO_INST_NEW (cfg, store, CEE_STIND_REF); + store->cil_code = ip; + handle_loaded_temps (cfg, bblock, stack_start, sp); + MONO_ADD_INS (bblock, store); + store->inst_i0 = sp [0]; + store->inst_i1 = load; + break; + } else { + handle_initobj (cfg, bblock, *sp, NULL, klass, stack_start, sp); + } ip += 6; inline_costs += 1; break; + case CEE_CONSTRAINED_: + /* FIXME: implement */ + token = read32 (ip + 2); + ip += 6; + break; case CEE_CPBLK: case CEE_INITBLK: { MonoInst *iargs [3]; @@ -4069,6 +5027,16 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b inline_costs += 1; break; } + case CEE_NO_: + if (ip [2] & 0x1) + ins_flag |= MONO_INST_NOTYPECHECK; + if (ip [2] & 0x2) + ins_flag |= MONO_INST_NORANGECHECK; + /* we ignore the no-nullcheck for now since we + * really do it explicitly only when doing callvirt->call + */ + ip += 3; + break; case CEE_RETHROW: { MonoInst *load; /* FIXME: check we are in a catch handler */ @@ -4086,6 +5054,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b case CEE_SIZEOF: CHECK_STACK_OVF (1); token = read32 (ip + 2); + /* FIXXME: handle generics. */ if (mono_metadata_token_table (token) == MONO_TABLE_TYPESPEC) { MonoType *type = mono_type_create_from_typespec (image, token); token = mono_type_size (type, &align); @@ -4103,6 +5072,9 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b case CEE_REFANYTYPE: g_error ("opcode 0xfe 0x%02x not handled", ip [1]); break; + case CEE_READONLY_: + ip += 2; + break; default: g_error ("opcode 0xfe 0x%02x not handled", ip [1]); } @@ -4197,11 +5169,13 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b g_hash_table_destroy (bbhash); } + dont_inline = g_list_remove (dont_inline, method); return inline_costs; inline_failure: if (cfg->method != method) g_hash_table_destroy (bbhash); + dont_inline = g_list_remove (dont_inline, method); return -1; unverified: @@ -4209,6 +5183,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b g_hash_table_destroy (bbhash); g_error ("Invalid IL code at IL%04x in %s: %s\n", 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; } @@ -4333,12 +5308,25 @@ create_helper_signature (void) helper_sig_newarr->params [2] = &mono_defaults.int32_class->byval_arg; helper_sig_newarr->pinvoke = 1; + /* MonoArray * mono_array_new_specific (MonoVTable *vtable, guint32 len) */ + helper_sig_newarr_specific = mono_metadata_signature_alloc (mono_defaults.corlib, 2); + helper_sig_newarr_specific->params [0] = &mono_defaults.int_class->byval_arg; + helper_sig_newarr_specific->params [1] = &mono_defaults.int32_class->byval_arg; + helper_sig_newarr_specific->ret = &mono_defaults.object_class->byval_arg; + helper_sig_newarr_specific->pinvoke = 1; + /* MonoObject * mono_object_new (MonoDomain *domain, MonoClass *klass) */ helper_sig_object_new = mono_metadata_signature_alloc (mono_defaults.corlib, 2); helper_sig_object_new->params [0] = helper_sig_object_new->params [1] = &mono_defaults.int_class->byval_arg; helper_sig_object_new->ret = &mono_defaults.object_class->byval_arg; helper_sig_object_new->pinvoke = 1; + /* MonoObject * mono_object_new_specific (MonoVTable *vtable) */ + helper_sig_object_new_specific = mono_metadata_signature_alloc (mono_defaults.corlib, 1); + helper_sig_object_new_specific->params [0] = &mono_defaults.int_class->byval_arg; + helper_sig_object_new_specific->ret = &mono_defaults.object_class->byval_arg; + helper_sig_object_new_specific->pinvoke = 1; + /* void* mono_method_compile (MonoMethod*) */ helper_sig_compile = mono_metadata_signature_alloc (mono_defaults.corlib, 1); helper_sig_compile->params [0] = helper_sig_compile->ret = &mono_defaults.int_class->byval_arg; @@ -4362,6 +5350,14 @@ create_helper_signature (void) helper_sig_domain_get->ret = &mono_defaults.int_class->byval_arg; helper_sig_domain_get->pinvoke = 1; + /* void* stelem_ref (MonoArray *, int index, MonoObject *) */ + helper_sig_stelem_ref = mono_metadata_signature_alloc (mono_defaults.corlib, 3); + helper_sig_stelem_ref->params [0] = &mono_defaults.array_class->byval_arg; + helper_sig_stelem_ref->params [1] = &mono_defaults.int32_class->byval_arg; + helper_sig_stelem_ref->params [2] = &mono_defaults.object_class->byval_arg; + helper_sig_stelem_ref->ret = &mono_defaults.void_class->byval_arg; + helper_sig_stelem_ref->pinvoke = 1; + /* long amethod (long, long) */ helper_sig_long_long_long = mono_metadata_signature_alloc (mono_defaults.corlib, 2); helper_sig_long_long_long->params [0] = helper_sig_long_long_long->params [1] = @@ -4420,6 +5416,12 @@ create_helper_signature (void) helper_sig_ptr_obj->ret = &mono_defaults.int_class->byval_arg; helper_sig_ptr_obj->pinvoke = 1; + /* IntPtr amethod (int) */ + helper_sig_ptr_int = mono_metadata_signature_alloc (mono_defaults.corlib, 1); + helper_sig_ptr_int->params [0] = &mono_defaults.int32_class->byval_arg; + helper_sig_ptr_int->ret = &mono_defaults.int_class->byval_arg; + helper_sig_ptr_int->pinvoke = 1; + /* long amethod (long, guint32) */ helper_sig_long_long_int = mono_metadata_signature_alloc (mono_defaults.corlib, 2); helper_sig_long_long_int->params [0] = &mono_defaults.int64_class->byval_arg; @@ -4475,27 +5477,6 @@ create_helper_signature (void) helper_sig_memset->pinvoke = 1; } -static GHashTable *method_opcode_hash = NULL; - -static void -mono_register_method_opcode (MonoMethod *method, int opcode) -{ - - if (!method_opcode_hash) - method_opcode_hash = g_hash_table_new (NULL, NULL); - - g_hash_table_insert (method_opcode_hash, method, (gpointer)opcode); - -} - -int -mono_find_method_opcode (MonoMethod *method) -{ - g_assert (method_opcode_hash); - - return (int)g_hash_table_lookup (method_opcode_hash, method); -} - static GHashTable *jit_icall_hash_name = NULL; static GHashTable *jit_icall_hash_addr = NULL; @@ -4516,12 +5497,25 @@ mono_find_jit_icall_by_addr (gconstpointer addr) return g_hash_table_lookup (jit_icall_hash_addr, (gpointer)addr); } +gconstpointer +mono_icall_get_wrapper (MonoJitICallInfo* callinfo) +{ + char *name; + MonoMethod *wrapper; + + if (callinfo->wrapper) + return callinfo->wrapper; + name = g_strdup_printf ("__icall_wrapper_%s", callinfo->name); + wrapper = mono_marshal_get_icall_wrapper (callinfo->sig, name, callinfo->func); + callinfo->wrapper = mono_jit_compile_method (wrapper); + g_free (name); + return callinfo->wrapper; +} + MonoJitICallInfo * mono_register_jit_icall (gconstpointer func, const char *name, MonoMethodSignature *sig, gboolean is_save) { MonoJitICallInfo *info; - MonoMethod *wrapper; - char *n; g_assert (func); g_assert (name); @@ -4538,7 +5532,7 @@ mono_register_jit_icall (gconstpointer func, const char *name, MonoMethodSignatu info = g_new (MonoJitICallInfo, 1); - info->name = g_strdup (name); + info->name = name; info->func = func; info->sig = sig; @@ -4549,14 +5543,11 @@ mono_register_jit_icall (gconstpointer func, const char *name, MonoMethodSignatu ) { info->wrapper = func; } else { - g_assert (sig); - n = g_strdup_printf ("__icall_wrapper_%s", name); - wrapper = mono_marshal_get_icall_wrapper (sig, n, func); - info->wrapper = mono_jit_compile_method (wrapper); - g_free (n); + info->wrapper = NULL; + mono_icall_get_wrapper (info); } - g_hash_table_insert (jit_icall_hash_name, info->name, info); + g_hash_table_insert (jit_icall_hash_name, (gpointer)info->name, info); g_hash_table_insert (jit_icall_hash_addr, (gpointer)func, info); if (func != info->wrapper) g_hash_table_insert (jit_icall_hash_addr, (gpointer)info->wrapper, info); @@ -4576,10 +5567,9 @@ mono_find_jit_opcode_emulation (int opcode) } void -mono_register_opcode_emulation (int opcode, MonoMethodSignature *sig, gpointer func) +mono_register_opcode_emulation (int opcode, const char *name, MonoMethodSignature *sig, gpointer func, gboolean no_throw) { MonoJitICallInfo *info; - char *name; if (!emul_opcode_hash) emul_opcode_hash = g_hash_table_new (NULL, NULL); @@ -4587,11 +5577,7 @@ mono_register_opcode_emulation (int opcode, MonoMethodSignature *sig, gpointer f g_assert (!sig->hasthis); g_assert (sig->param_count < 3); - name = g_strdup_printf ("__emulate_%s", mono_inst_name (opcode)); - - info = mono_register_jit_icall (func, name, sig, FALSE); - - g_free (name); + info = mono_register_jit_icall (func, name, sig, no_throw); g_hash_table_insert (emul_opcode_hash, (gpointer)opcode, info); } @@ -4600,22 +5586,38 @@ static void decompose_foreach (MonoInst *tree, gpointer data) { static MonoJitICallInfo *newarr_info = NULL; + static MonoJitICallInfo *newarr_specific_info = NULL; + MonoJitICallInfo *info; switch (tree->opcode) { case CEE_NEWARR: { MonoCompile *cfg = data; MonoInst *iargs [3]; - NEW_DOMAINCONST (cfg, iargs [0]); - NEW_CLASSCONST (cfg, iargs [1], tree->inst_newa_class); - iargs [2] = tree->inst_newa_len; - if (!newarr_info) { - newarr_info = mono_find_jit_icall_by_addr (mono_array_new); + newarr_info = mono_find_jit_icall_by_addr (mono_array_new); g_assert (newarr_info); + newarr_specific_info = mono_find_jit_icall_by_addr (mono_array_new_specific); + g_assert (newarr_specific_info); + } + + if ((cfg->opt & MONO_OPT_SHARED) || mono_compile_aot) { + NEW_DOMAINCONST (cfg, iargs [0]); + NEW_CLASSCONST (cfg, iargs [1], tree->inst_newa_class); + iargs [2] = tree->inst_newa_len; + + info = newarr_info; + } + else { + MonoVTable *vtable = mono_class_vtable (cfg->domain, mono_array_class_get (tree->inst_newa_class, 1)); + + NEW_PCONST (cfg, iargs [0], vtable); + iargs [1] = tree->inst_newa_len; + + info = newarr_specific_info; } - mono_emulate_opcode (cfg, tree, iargs, newarr_info); + mono_emulate_opcode (cfg, tree, iargs, info); break; } @@ -4763,10 +5765,15 @@ mono_bblock_add_inst (MonoBasicBlock *bb, MonoInst *inst) void mono_destroy_compile (MonoCompile *cfg) { - //mono_mempool_stats (cfg->mempool); g_hash_table_destroy (cfg->bb_hash); + if (cfg->rs) + mono_regstate_free (cfg->rs); mono_mempool_destroy (cfg->mempool); + g_list_free (cfg->ldstr_list); + + g_free (cfg->varinfo); + g_free (cfg->vars); g_free (cfg); } @@ -4793,28 +5800,40 @@ mono_thread_abort (MonoObject *obj) { MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id); - g_free (jit_tls); + /* handle_remove should be eventually called for this thread, too + g_free (jit_tls);*/ ExitThread (-1); } static void -mono_thread_start_cb (guint32 tid, gpointer stack_start, gpointer func) +setup_jit_tls_data (gpointer stack_start, gpointer abort_func) { MonoJitTlsData *jit_tls; MonoLMF *lmf; + MonoThread *thread; jit_tls = g_new0 (MonoJitTlsData, 1); TlsSetValue (mono_jit_tls_id, jit_tls); - jit_tls->abort_func = mono_thread_abort; + jit_tls->abort_func = abort_func; jit_tls->end_of_stack = stack_start; lmf = g_new0 (MonoLMF, 1); lmf->ebp = -1; - jit_tls->lmf = lmf; + jit_tls->lmf = jit_tls->first_lmf = lmf; + + thread = mono_thread_current (); + if (thread) + thread->jit_data = jit_tls; +} + +static void +mono_thread_start_cb (guint32 tid, gpointer stack_start, gpointer func) +{ + setup_jit_tls_data (stack_start, mono_thread_abort); } void (*mono_thread_attach_aborted_cb ) (MonoObject *obj) = NULL; @@ -4831,20 +5850,19 @@ mono_thread_abort_dummy (MonoObject *obj) static void mono_thread_attach_cb (guint32 tid, gpointer stack_start) { - MonoJitTlsData *jit_tls; - MonoLMF *lmf; - - jit_tls = g_new0 (MonoJitTlsData, 1); - - TlsSetValue (mono_jit_tls_id, jit_tls); - - jit_tls->abort_func = mono_thread_abort_dummy; - jit_tls->end_of_stack = stack_start; + setup_jit_tls_data (stack_start, mono_thread_abort_dummy); +} - lmf = g_new0 (MonoLMF, 1); - lmf->ebp = -1; +static void +mini_thread_cleanup (MonoThread *thread) +{ + MonoJitTlsData *jit_tls = thread->jit_data; - jit_tls->lmf = lmf; + if (jit_tls) { + g_free (jit_tls->first_lmf); + g_free (jit_tls); + thread->jit_data = NULL; + } } void @@ -4892,8 +5910,20 @@ dec_foreach (MonoInst *tree, MonoCompile *cfg) { } break; - case 2: - if ((info = mono_find_jit_opcode_emulation (tree->opcode))) { + case 2: + if (tree->opcode == OP_LMUL + && (cfg->opt & MONO_OPT_INTRINS) + && (tree->inst_left->opcode == CEE_CONV_I8 + || tree->inst_left->opcode == CEE_CONV_U8) + && tree->inst_left->inst_left->type == STACK_I4 + && (tree->inst_right->opcode == CEE_CONV_I8 + || tree->inst_right->opcode == CEE_CONV_U8) + && tree->inst_right->inst_left->type == STACK_I4) { + tree->opcode = (tree->inst_left->opcode == CEE_CONV_I8 ? OP_BIGMUL: OP_BIGMUL_UN); + tree->inst_left = tree->inst_left->inst_left; + tree->inst_right = tree->inst_right->inst_left; + dec_foreach (tree, cfg); + } else if ((info = mono_find_jit_opcode_emulation (tree->opcode))) { MonoInst *iargs [2]; iargs [0] = tree->inst_i0; @@ -4939,6 +5969,47 @@ nullify_basic_block (MonoBasicBlock *bb) bb->out_bb = NULL; bb->next_bb = NULL; bb->code = bb->last_ins = NULL; + bb->cil_code = NULL; +} + +static void +replace_out_block (MonoBasicBlock *bb, MonoBasicBlock *orig, MonoBasicBlock *repl) +{ + int i; + + for (i = 0; i < bb->out_count; i++) { + MonoBasicBlock *ob = bb->out_bb [i]; + if (ob == orig) { + if (!repl) { + if (bb->out_count > 1) { + bb->out_bb [i] = bb->out_bb [bb->out_count - 1]; + } + bb->out_count--; + } else { + bb->out_bb [i] = repl; + } + } + } +} + +static void +replace_in_block (MonoBasicBlock *bb, MonoBasicBlock *orig, MonoBasicBlock *repl) +{ + 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; + } + } + } } static void @@ -4949,13 +6020,15 @@ replace_basic_block (MonoBasicBlock *bb, MonoBasicBlock *orig, MonoBasicBlock * for (i = 0; i < bb->out_count; i++) { MonoBasicBlock *ob = bb->out_bb [i]; for (j = 0; j < ob->in_count; j++) { - if (ob->in_bb [j] == orig) + if (ob->in_bb [j] == orig) { ob->in_bb [j] = repl; + } } } } + static void merge_basic_blocks (MonoBasicBlock *bb, MonoBasicBlock *bbn) { @@ -4979,7 +6052,7 @@ merge_basic_blocks (MonoBasicBlock *bb, MonoBasicBlock *bbn) static void optimize_branches (MonoCompile *cfg) { - int changed = FALSE; + int i, changed = FALSE; MonoBasicBlock *bb, *bbn; do { @@ -4988,27 +6061,46 @@ optimize_branches (MonoCompile *cfg) { /* we skip the entry block (exit is handled specially instead ) */ for (bb = cfg->bb_entry->next_bb; bb; bb = bb->next_bb) { + /* dont touch code inside exception clauses */ + if (bb->region != -1) + continue; + + if ((bbn = bb->next_bb) && bbn->in_count == 0 && bb->region == bbn->region) { + if (cfg->verbose_level > 2) + g_print ("nullify block triggered %d\n", bbn->block_num); + + bb->next_bb = bbn->next_bb; + + for (i = 0; i < bbn->out_count; i++) + replace_in_block (bbn->out_bb [i], bbn, NULL); + + nullify_basic_block (bbn); + changed = TRUE; + } + if (bb->out_count == 1) { bbn = bb->out_bb [0]; + /* 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 (bb->last_ins->opcode)) { + bb->last_ins->opcode = CEE_BR; + bb->last_ins->inst_target_bb = bb->last_ins->inst_true_bb; + changed = TRUE; + if (cfg->verbose_level > 2) + g_print ("cond branch removal triggered in %d %d\n", bb->block_num, bb->out_count); + } + if (bb->region == bbn->region && bb->next_bb == bbn) { - /* the block are in sequence anyway ... */ + /* the block are in sequence anyway ... */ - /* - * miguel: I do not understand what the test below does, could we - * use a macro, or a comment here? opcode > CEE_BEQ && <= BLT_UN - * - * It could also test for bb->last_in only once, and the value - * could be cached (last_ins->opcode) - */ - if (bb->last_ins && (bb->last_ins->opcode == CEE_BR || ( - (bb->last_ins && bb->last_ins->opcode >= CEE_BEQ && bb->last_ins->opcode <= CEE_BLT_UN)))) { + /* branches to the following block can be removed */ + if (bb->last_ins && bb->last_ins->opcode == CEE_BR) { bb->last_ins->opcode = CEE_NOP; changed = TRUE; if (cfg->verbose_level > 2) g_print ("br removal triggered %d -> %d\n", bb->block_num, bbn->block_num); } - /* fixme: this causes problems with inlining */ + if (bbn->in_count == 1) { if (bbn != cfg->bb_exit) { @@ -5020,39 +6112,102 @@ optimize_branches (MonoCompile *cfg) { //mono_print_bb_code (bb); } - } else { - if (bb->last_ins && bb->last_ins->opcode == CEE_BR) { - bbn = bb->last_ins->inst_target_bb; - if (bb->region == bbn->region && bbn->code && bbn->code->opcode == CEE_BR) { - /* - if (cfg->verbose_level > 2) - g_print ("in %s branch to branch triggered %d -> %d\n", cfg->method->name, bb->block_num, bbn->block_num); - bb->out_bb [0] = bb->last_ins->inst_target_bb = bbn->code->inst_target_bb; - changed = TRUE;*/ - } + } + } + } + } while (changed); + + do { + changed = FALSE; + + /* we skip the entry block (exit is handled specially instead ) */ + for (bb = cfg->bb_entry->next_bb; bb; bb = bb->next_bb) { + + /* dont touch code inside exception clauses */ + if (bb->region != -1) + continue; + + if ((bbn = bb->next_bb) && bbn->in_count == 0 && bb->region == bbn->region) { + if (cfg->verbose_level > 2) { + g_print ("nullify block triggered %d\n", bbn->block_num); + } + bb->next_bb = bbn->next_bb; + + for (i = 0; i < bbn->out_count; i++) + replace_in_block (bbn->out_bb [i], bbn, NULL); + + nullify_basic_block (bbn); + changed = TRUE; + break; + } + + + if (bb->out_count == 1) { + bbn = bb->out_bb [0]; + + if (bb->last_ins && bb->last_ins->opcode == CEE_BR) { + bbn = bb->last_ins->inst_target_bb; + if (bb->region == bbn->region && bbn->code && bbn->code->opcode == CEE_BR && + bbn->code->inst_target_bb->region == bb->region) { + + if (cfg->verbose_level > 2) + g_print ("in %s branch to branch triggered %d -> %d\n", cfg->method->name, + bb->block_num, bbn->block_num); + + replace_basic_block (bb, bb->out_bb [0], bbn->code->inst_target_bb); + bb->last_ins->inst_target_bb = bbn->code->inst_target_bb; + changed = TRUE; + break; } } } else if (bb->out_count == 2) { - /* fixme: this does not correctly unlink the blocks, so we get serious problems in idom code */ - if (0 && bb->last_ins && bb->last_ins->opcode >= CEE_BEQ && bb->last_ins->opcode <= CEE_BLT_UN) { + if (bb->last_ins && MONO_IS_COND_BRANCH (bb->last_ins->opcode)) { bbn = bb->last_ins->inst_true_bb; - if (bb->region == bbn->region && bbn->code && bbn->code->opcode == CEE_BR) { - if (cfg->verbose_level > 2) - g_print ("cbranch to branch triggered %d -> %d (0x%02x)\n", bb->block_num, - bbn->block_num, bbn->code->opcode); - - if (bb->out_bb [0] == bbn) { - bb->out_bb [0] = bbn->code->inst_target_bb; - } else if (bb->out_bb [1] == bbn) { - bb->out_bb [1] = bbn->code->inst_target_bb; - } + if (bb->region == bbn->region && bbn->code && bbn->code->opcode == CEE_BR && + bbn->code->inst_target_bb->region == bb->region) { + if (cfg->verbose_level > 2) + g_print ("cbranch1 to branch triggered %d -> (%d) %d (0x%02x)\n", + bb->block_num, bbn->block_num, bbn->code->inst_target_bb->block_num, + bbn->code->opcode); + bb->last_ins->inst_true_bb = bbn->code->inst_target_bb; + + replace_in_block (bbn, bb, NULL); + if (!bbn->in_count) + replace_in_block (bbn->code->inst_target_bb, bbn, bb); + replace_out_block (bb, bbn, bbn->code->inst_target_bb); + + link_bblock (cfg, bb, bbn->code->inst_target_bb); + + changed = TRUE; + break; + } + + bbn = bb->last_ins->inst_false_bb; + if (bb->region == bbn->region && bbn->code && bbn->code->opcode == CEE_BR && + bbn->code->inst_target_bb->region == bb->region) { + if (cfg->verbose_level > 2) + g_print ("cbranch2 to branch triggered %d -> (%d) %d (0x%02x)\n", + bb->block_num, bbn->block_num, bbn->code->inst_target_bb->block_num, + bbn->code->opcode); + + bb->last_ins->inst_false_bb = bbn->code->inst_target_bb; + + replace_in_block (bbn, bb, NULL); + if (!bbn->in_count) + replace_in_block (bbn->code->inst_target_bb, bbn, bb); + replace_out_block (bb, bbn, bbn->code->inst_target_bb); + + link_bblock (cfg, bb, bbn->code->inst_target_bb); + changed = TRUE; + break; } } } } } while (changed); + } static void @@ -5072,6 +6227,8 @@ mono_compile_create_vars (MonoCompile *cfg) cfg->ret->inst_vtype = sig->ret; cfg->ret->klass = mono_class_from_mono_type (sig->ret); } + if (cfg->verbose_level > 2) + g_print ("creating vars\n"); if (sig->hasthis) mono_compile_create_var (cfg, &cfg->method->klass->this_arg, OP_ARG); @@ -5081,8 +6238,12 @@ mono_compile_create_vars (MonoCompile *cfg) cfg->locals_start = cfg->num_varinfo; + if (cfg->verbose_level > 2) + g_print ("creating locals\n"); for (i = 0; i < header->num_locals; ++i) mono_compile_create_var (cfg, header->locals [i], OP_LOCAL); + if (cfg->verbose_level > 2) + g_print ("locals done\n"); } #if 0 @@ -5163,11 +6324,49 @@ emit_state (MonoCompile *cfg, MBState *state, int goal) static void mini_select_instructions (MonoCompile *cfg) { + static 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 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 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 + }; + MonoBasicBlock *bb; cfg->state_pool = mono_mempool_new (); cfg->rs = mono_regstate_new (); + for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { + if (bb->last_ins && MONO_IS_COND_BRANCH (bb->last_ins->opcode) && + bb->next_bb != bb->last_ins->inst_false_bb) { + + if (bb->next_bb == bb->last_ins->inst_true_bb) { + 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 { + MonoInst *inst = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoInst)); + inst->opcode = CEE_BR; + inst->inst_target_bb = bb->last_ins->inst_false_bb; + mono_bblock_add_inst (bb, inst); + } + } + } + #ifdef DEBUG_SELECTION if (cfg->verbose_level >= 4) { for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { @@ -5242,12 +6441,12 @@ mono_codegen (MonoCompile *cfg) mono_arch_local_regalloc (cfg, bb); } - if (mono_trace_coverage) - mono_allocate_coverage_info (cfg->method, cfg->num_bblocks); + if (cfg->prof_options & MONO_PROFILE_COVERAGE) + cfg->coverage_info = mono_profiler_coverage_alloc (cfg->method, cfg->num_bblocks); code = mono_arch_emit_prolog (cfg); - if (mono_jit_profile) + if (cfg->prof_options & MONO_PROFILE_ENTER_LEAVE) code = mono_arch_instrument_prolog (cfg, mono_profiler_method_enter, code, FALSE); cfg->code_len = code - cfg->native_code; @@ -5278,8 +6477,8 @@ mono_codegen (MonoCompile *cfg) /* g_assert (((int)cfg->native_code & (MONO_ARCH_CODE_ALIGNMENT - 1)) == 0); */ cfg->epilog_begin = cfg->code_len; - - if (mono_jit_profile) + + if (cfg->prof_options & MONO_PROFILE_ENTER_LEAVE) code = mono_arch_instrument_epilog (cfg, mono_profiler_method_leave, code, FALSE); cfg->code_len = code - cfg->native_code; @@ -5515,24 +6714,31 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, int p int dfn = 0, i, code_size_ratio; mono_jit_stats.methods_compiled++; + if (mono_profiler_get_events () & MONO_PROFILE_JIT_COMPILATION) + mono_profiler_method_jit (method); cfg = g_new0 (MonoCompile, 1); cfg->method = method; cfg->mempool = mono_mempool_new (); cfg->opt = opts; + cfg->prof_options = mono_profiler_get_events (); cfg->bb_hash = g_hash_table_new (g_direct_hash, NULL); cfg->domain = domain; cfg->verbose_level = mini_verbose; + cfg->intvars = mono_mempool_alloc0 (cfg->mempool, sizeof (guint16) * STACK_MAX * + ((MonoMethodNormal *)method)->header->max_stack); + + if (cfg->verbose_level > 2) + g_print ("converting method %s\n", mono_method_full_name (method, TRUE)); /* * create MonoInst* which represents arguments and local variables */ mono_compile_create_vars (cfg); - if (cfg->verbose_level > 2) - g_print ("converting method %s\n", mono_method_full_name (method, TRUE)); - - if ((i = mono_method_to_ir (cfg, method, NULL, NULL, cfg->locals_start, NULL, NULL, NULL, 0)) < 0) { + if ((i = mono_method_to_ir (cfg, method, NULL, NULL, cfg->locals_start, NULL, NULL, NULL, 0, FALSE)) < 0) { + if (cfg->prof_options & MONO_PROFILE_JIT_COMPILATION) + mono_profiler_method_end_jit (method, MONO_PROFILE_FAILED); mono_destroy_compile (cfg); return NULL; } @@ -5540,6 +6746,15 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, int p 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) { + /* + * we disable some optimizations if there are too many variables + * because JIT time may become too expensive. The actual number needs + * to be tweaked and eventually the non-linear algorithms should be fixed. + */ + 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 */ @@ -5550,9 +6765,26 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, int p df_visit (cfg->bb_entry, &dfn, cfg->bblocks); if (cfg->num_bblocks != dfn + 1) { - if (cfg->verbose_level > 1) - g_print ("unreachable code?\n"); + MonoBasicBlock *bb; + cfg->num_bblocks = dfn + 1; + + if (!header->clauses) { + /* remove unreachable code, because the code in them may be + * inconsistent (access to dead variables for example) */ + for (bb = cfg->bb_entry; bb;) { + MonoBasicBlock *bbn = bb->next_bb; + + if (bbn && bbn->region == -1 && !bbn->dfn) { + if (cfg->verbose_level > 1) + g_print ("found unreachabel code in BB%d\n", bbn->block_num); + bb->next_bb = bbn->next_bb; + nullify_basic_block (bbn); + } else { + bb = bb->next_bb; + } + } + } } if (cfg->opt & MONO_OPT_LOOP) { @@ -5618,14 +6850,15 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, int p decompose_pass (cfg); - if (cfg->opt & MONO_OPT_LINEARS) { + /* FIXME: disabled with exception clauses: bug #42136 */ + if ((!header->num_clauses) && (cfg->opt & MONO_OPT_LINEARS)) { GList *vars, *regs; /* fixme: maybe we can avoid to compute livenesss here if already computed ? */ cfg->comp_done &= ~MONO_COMP_LIVENESS; if (!(cfg->comp_done & MONO_COMP_LIVENESS)) mono_analyze_liveness (cfg); - + if ((vars = mono_arch_get_allocatable_int_vars (cfg))) { regs = mono_arch_get_global_int_regs (cfg); mono_linear_scan (cfg, vars, regs, &cfg->used_int_regs); @@ -5633,9 +6866,9 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, int p } //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); @@ -5685,13 +6918,14 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, int p tblock = g_hash_table_lookup (cfg->bb_hash, ip + ec->try_offset); g_assert (tblock); ei->try_start = cfg->native_code + tblock->native_offset; + g_assert (tblock->native_offset); tblock = g_hash_table_lookup (cfg->bb_hash, ip + ec->try_offset + ec->try_len); g_assert (tblock); ei->try_end = cfg->native_code + tblock->native_offset; + g_assert (tblock->native_offset); tblock = g_hash_table_lookup (cfg->bb_hash, ip + ec->handler_offset); g_assert (tblock); ei->handler_start = cfg->native_code + tblock->native_offset; - } } @@ -5711,6 +6945,9 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, int p } mono_jit_stats.native_code_size += cfg->code_len; + if (cfg->prof_options & MONO_PROFILE_JIT_COMPILATION) + mono_profiler_method_end_jit (method, MONO_PROFILE_OK); + return cfg; } @@ -5723,7 +6960,7 @@ mono_jit_compile_method (MonoMethod *method) GHashTable *jit_code_hash; gpointer code; - if (default_opt & MONO_OPT_SAHRED) + if (default_opt & MONO_OPT_SHARED) target_domain = mono_root_domain; else target_domain = domain; @@ -5795,8 +7032,23 @@ mono_jit_compile_method (MonoMethod *method) g_hash_table_insert (jit_code_hash, method, code); + if (target_domain->jump_target_hash) { + MonoJumpInfo patch_info; + GSList *list, *tmp; + list = g_hash_table_lookup (target_domain->jump_target_hash, method); + if (list) { + patch_info.next = NULL; + patch_info.ip.i = 0; + patch_info.type = MONO_PATCH_INFO_METHOD_JUMP; + patch_info.data.method = method; + g_hash_table_remove (target_domain->jump_target_hash, method); + } + for (tmp = list; tmp; tmp = tmp->next) + mono_arch_patch_code (NULL, target_domain, tmp->data, &patch_info); + g_slist_free (list); + } /* make sure runtime_init is called */ - mono_class_vtable (target_domain, method->klass); + mono_runtime_class_init (mono_class_vtable (target_domain, method->klass)); return code; } @@ -5872,6 +7124,18 @@ sigusr1_signal_handler (int _dummy) mono_arch_handle_exception (ctx, thread->abort_exc, FALSE); } +static void +sigquit_signal_handler (int _dummy) +{ + MonoException *exc; + GET_CONTEXT + + exc = mono_get_exception_execution_engine ("Interrupted (SIGQUIT)."); + + mono_arch_handle_exception (ctx, exc, FALSE); +} + + static void mono_runtime_install_handlers (void) { @@ -5898,6 +7162,12 @@ mono_runtime_install_handlers (void) //g_assert (syscall (SYS_sigaction, SIGFPE, &sa, NULL) != -1); g_assert (sigaction (SIGFPE, &sa, NULL) != -1); + /* catch SIGQUIT */ + sa.sa_handler = sigquit_signal_handler; + sigemptyset (&sa.sa_mask); + sa.sa_flags = 0; + g_assert (sigaction (SIGQUIT, &sa, NULL) != -1); + /* catch SIGILL */ sa.sa_handler = sigill_signal_handler; sigemptyset (&sa.sa_mask); @@ -5937,7 +7207,8 @@ mono_jit_create_remoting_trampoline (MonoMethod *method) MonoMethod *nm; guint8 *addr = NULL; - if (method->signature->hasthis && (method->klass->marshalbyref || method->klass == mono_defaults.object_class)) { + if ((method->flags & METHOD_ATTRIBUTE_ABSTRACT) || + (method->signature->hasthis && (method->klass->marshalbyref || method->klass == mono_defaults.object_class))) { nm = mono_marshal_get_remoting_invoke (method); addr = mono_compile_method (nm); } else { @@ -5946,36 +7217,15 @@ mono_jit_create_remoting_trampoline (MonoMethod *method) return addr; } -static MonoMethod * -mono_find_unique_method (MonoClass *klass, const char *name, int param_count) -{ - MonoMethod *rval = NULL; - int i; - - mono_class_init (klass); - - for (i = 0; i < klass->method.count; ++i) { - if (!strcmp (name, klass->methods [i]->name) && - klass->methods [i]->signature->param_count == param_count) { - g_assert (rval == NULL); - rval = klass->methods [i]; - g_assert (!(rval->flags & METHOD_ATTRIBUTE_VIRTUAL)); - } - } - - return rval; -} - - static CRITICAL_SECTION ms; MonoDomain * mini_init (const char *filename) { MonoDomain *domain; - MonoMethod *m; - MonoMethodDesc *desc; - + + mono_arch_cpu_init (); + metadata_section = &ms; InitializeCriticalSection (metadata_section); @@ -5985,6 +7235,7 @@ mini_init (const char *filename) mono_burg_init (); mono_runtime_install_handlers (); + mono_threads_install_cleanup (mini_thread_cleanup); mono_install_compile_method (mono_jit_compile_method); mono_install_trampoline (mono_arch_create_jit_trampoline); @@ -6022,46 +7273,30 @@ mini_init (const char *filename) mono_register_jit_icall (mono_arch_get_throw_exception_by_name (), "mono_arch_throw_exception_by_name", helper_sig_void_ptr, TRUE); - - g_assert ((m = mono_find_unique_method (mono_defaults.math_class, "Sin", 1))); - mono_register_method_opcode (m, OP_SIN); - g_assert ((m = mono_find_unique_method (mono_defaults.math_class, "Cos", 1))); - mono_register_method_opcode (m, OP_COS); - g_assert ((m = mono_find_unique_method (mono_defaults.math_class, "Tan", 1))); - mono_register_method_opcode (m, OP_TAN); - g_assert ((m = mono_find_unique_method (mono_defaults.math_class, "Atan", 1))); - mono_register_method_opcode (m, OP_ATAN); - g_assert ((m = mono_find_unique_method (mono_defaults.math_class, "Sqrt", 1))); - mono_register_method_opcode (m, OP_SQRT); - - g_assert ((desc = mono_method_desc_new ("System.Math:Abs(double)", 0))); - g_assert ((m = mono_method_desc_search_in_image (desc, mono_defaults.corlib))); - mono_register_method_opcode (m, OP_ABS); - /* * NOTE, NOTE, NOTE, NOTE: * when adding emulation for some opcodes, remember to also add a dummy * rule to the burg files, because we need the arity information to be correct. */ - mono_register_opcode_emulation (OP_LMUL, helper_sig_long_long_long, mono_llmult); - mono_register_opcode_emulation (OP_LMUL_OVF_UN, helper_sig_long_long_long, mono_llmult_ovf_un); - mono_register_opcode_emulation (OP_LMUL_OVF, helper_sig_long_long_long, mono_llmult_ovf); - mono_register_opcode_emulation (OP_LDIV, helper_sig_long_long_long, mono_lldiv); - mono_register_opcode_emulation (OP_LDIV_UN, helper_sig_long_long_long, mono_lldiv_un); - mono_register_opcode_emulation (OP_LREM, helper_sig_long_long_long, mono_llrem); - mono_register_opcode_emulation (OP_LREM_UN, helper_sig_long_long_long, mono_llrem_un); - - mono_register_opcode_emulation (OP_LSHL, helper_sig_long_long_int, mono_lshl); - mono_register_opcode_emulation (OP_LSHR, helper_sig_long_long_int, mono_lshr); - mono_register_opcode_emulation (OP_LSHR_UN, helper_sig_long_long_int, mono_lshr_un); - - mono_register_opcode_emulation (OP_FCONV_TO_U8, helper_sig_ulong_double, mono_fconv_u8); - mono_register_opcode_emulation (OP_FCONV_TO_U4, helper_sig_uint_double, mono_fconv_u4); - mono_register_opcode_emulation (OP_FCONV_TO_OVF_I8, helper_sig_long_double, mono_fconv_ovf_i8); - mono_register_opcode_emulation (OP_FCONV_TO_OVF_U8, helper_sig_ulong_double, mono_fconv_ovf_u8); + mono_register_opcode_emulation (OP_LMUL, "__emul_lmul", helper_sig_long_long_long, mono_llmult, TRUE); + mono_register_opcode_emulation (OP_LMUL_OVF_UN, "__emul_lmul_ovf_un", helper_sig_long_long_long, mono_llmult_ovf_un, FALSE); + mono_register_opcode_emulation (OP_LMUL_OVF, "__emul_lmul_ovf", helper_sig_long_long_long, mono_llmult_ovf, FALSE); + mono_register_opcode_emulation (OP_LDIV, "__emul_ldiv", helper_sig_long_long_long, mono_lldiv, FALSE); + mono_register_opcode_emulation (OP_LDIV_UN, "__emul_ldiv_un", helper_sig_long_long_long, mono_lldiv_un, FALSE); + mono_register_opcode_emulation (OP_LREM, "__emul_lrem", helper_sig_long_long_long, mono_llrem, FALSE); + mono_register_opcode_emulation (OP_LREM_UN, "__emul_lrem_un", helper_sig_long_long_long, mono_llrem_un, FALSE); + + mono_register_opcode_emulation (OP_LSHL, "__emul_lshl", helper_sig_long_long_int, mono_lshl, TRUE); + mono_register_opcode_emulation (OP_LSHR, "__emul_lshr", helper_sig_long_long_int, mono_lshr, TRUE); + mono_register_opcode_emulation (OP_LSHR_UN, "__emul_lshr_un", helper_sig_long_long_int, mono_lshr_un, TRUE); + + mono_register_opcode_emulation (OP_FCONV_TO_U8, "__emul_fconv_to_u8", helper_sig_ulong_double, mono_fconv_u8, FALSE); + mono_register_opcode_emulation (OP_FCONV_TO_U4, "__emul_fconv_to_u4", helper_sig_uint_double, mono_fconv_u4, FALSE); + mono_register_opcode_emulation (OP_FCONV_TO_OVF_I8, "__emul_fconv_to_ovf_i8", helper_sig_long_double, mono_fconv_ovf_i8, TRUE); + mono_register_opcode_emulation (OP_FCONV_TO_OVF_U8, "__emul_fconv_to_ovf_u8", helper_sig_ulong_double, mono_fconv_ovf_u8, TRUE); #if SIZEOF_VOID_P == 4 - mono_register_opcode_emulation (OP_FCONV_TO_U, helper_sig_uint_double, mono_fconv_u4); + mono_register_opcode_emulation (OP_FCONV_TO_U, "__emul_fconv_to_u", helper_sig_uint_double, mono_fconv_u4, TRUE); #else #warning "fixme: add opcode emulation" #endif @@ -6070,12 +7305,17 @@ mini_init (const char *filename) 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_threads_get_static_data, "mono_threads_get_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); mono_register_jit_icall (helper_memset, "helper_memset", helper_sig_memset, FALSE); mono_register_jit_icall (helper_initobj, "helper_initobj", helper_sig_initobj, FALSE); + mono_register_jit_icall (helper_stelem_ref, "helper_stelem_ref", helper_sig_stelem_ref, FALSE); mono_register_jit_icall (mono_object_new, "mono_object_new", helper_sig_object_new, FALSE); + mono_register_jit_icall (mono_object_new_specific, "mono_object_new_specific", helper_sig_object_new_specific, FALSE); + mono_register_jit_icall (mono_object_new_fast, "mono_object_new_fast", helper_sig_object_new_specific, FALSE); mono_register_jit_icall (mono_array_new, "mono_array_new", helper_sig_newarr, FALSE); + mono_register_jit_icall (mono_array_new_specific, "mono_array_new_specific", helper_sig_newarr_specific, FALSE); mono_register_jit_icall (mono_string_to_utf16, "mono_string_to_utf16", helper_sig_ptr_obj, FALSE); mono_register_jit_icall (mono_string_from_utf16, "mono_string_from_utf16", helper_sig_obj_ptr, FALSE); mono_register_jit_icall (mono_string_new_wrapper, "mono_string_new_wrapper", helper_sig_obj_ptr, FALSE); @@ -6092,11 +7332,12 @@ mini_init (const char *filename) mono_register_jit_icall (mono_string_to_byvalstr, "mono_string_to_byvalstr", helper_sig_void_ptr_ptr_ptr, FALSE); mono_register_jit_icall (mono_string_to_byvalwstr, "mono_string_to_byvalwstr", helper_sig_void_ptr_ptr_ptr, FALSE); mono_register_jit_icall (g_free, "g_free", helper_sig_void_ptr, FALSE); + mono_register_jit_icall (mono_runtime_class_init, "mono_runtime_class_init", helper_sig_void_ptr, FALSE); mono_register_jit_icall (mono_ldftn, "mono_ldftn", helper_sig_compile, FALSE); mono_register_jit_icall (mono_ldvirtfn, "mono_ldvirtfn", helper_sig_compile_virt, FALSE); - mono_runtime_init (domain, mono_thread_start_cb, - mono_thread_attach_cb); + mono_runtime_install_cleanup ((MonoDomainFunc)mini_cleanup); + mono_runtime_init (domain, mono_thread_start_cb, mono_thread_attach_cb); //mono_thread_attach (domain); return domain; @@ -6140,17 +7381,19 @@ void mini_cleanup (MonoDomain *domain) { /* - * mono_runtime_cleanup() needs to be called early since - * it needs the execution engine still fully working (it will - * wait for other threads to finish). + * mono_runtime_cleanup() and mono_domain_finalize () need to + * be called early since they need the execution engine still + * fully working (mono_domain_finalize may invoke managed finalizers + * and mono_runtime_cleanup will wait for other threads to finish). */ - mono_runtime_cleanup (domain); - mono_domain_finalize (domain); + mono_runtime_cleanup (domain); + mono_profiler_shutdown (); mono_debug_cleanup (); + #ifdef PLATFORM_WIN32 win32_seh_cleanup(); #endif @@ -6162,7 +7405,7 @@ mini_cleanup (MonoDomain *domain) } void -mini_set_defaults (int verbose_level, guint32 opts) +mono_set_defaults (int verbose_level, guint32 opts) { mini_verbose = verbose_level; default_opt = opts;