[jit] Fix the saving of the 'cfg->ret_var_set' flag when inlining, it was set to...
[mono.git] / mono / mini / method-to-ir.c
index 2a5f8917d5e86dba99e1612918565f8dfd325040..ce326f3c2d3443d3461073d826215663523955e4 100644 (file)
@@ -1764,24 +1764,6 @@ emit_pop_lmf (MonoCompile *cfg)
        EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, lmf_addr_reg, 0, prev_lmf_reg);
 }
 
-static void
-emit_instrumentation_call (MonoCompile *cfg, void *func)
-{
-       MonoInst *iargs [1];
-
-       /*
-        * Avoid instrumenting inlined methods since it can
-        * distort profiling results.
-        */
-       if (cfg->method != cfg->current_method)
-               return;
-
-       if (cfg->prof_options & MONO_PROFILE_ENTER_LEAVE) {
-               EMIT_NEW_METHODCONST (cfg, iargs [0], cfg->method);
-               mono_emit_jit_icall (cfg, func, iargs);
-       }
-}
-
 static int
 ret_type_to_call_opcode (MonoCompile *cfg, MonoType *type, int calli, int virt)
 {
@@ -2235,7 +2217,7 @@ check_method_sharing (MonoCompile *cfg, MonoMethod *cmethod, gboolean *out_pass_
 
 inline static MonoCallInst *
 mono_emit_call_args (MonoCompile *cfg, MonoMethodSignature *sig, 
-                                        MonoInst **args, int calli, int virtual_, int tail, int rgctx, int unbox_trampoline)
+                                        MonoInst **args, int calli, int virtual_, int tail, int rgctx, int unbox_trampoline, MonoMethod *target)
 {
        MonoType *sig_ret;
        MonoCallInst *call;
@@ -2247,7 +2229,7 @@ mono_emit_call_args (MonoCompile *cfg, MonoMethodSignature *sig,
                tail = FALSE;
 
        if (tail) {
-               emit_instrumentation_call (cfg, mono_profiler_method_leave);
+               mini_profiler_emit_tail_call (cfg, target);
 
                MONO_INST_NEW_CALL (cfg, call, OP_TAILCALL);
        } else
@@ -2380,7 +2362,7 @@ mini_emit_calli (MonoCompile *cfg, MonoMethodSignature *sig, MonoInst **args, Mo
                MONO_ADD_INS (cfg->cbb, ins);
        }
 
-       call = mono_emit_call_args (cfg, sig, args, TRUE, FALSE, FALSE, rgctx_arg ? TRUE : FALSE, FALSE);
+       call = mono_emit_call_args (cfg, sig, args, TRUE, FALSE, FALSE, rgctx_arg ? TRUE : FALSE, FALSE, NULL);
 
        call->inst.sreg1 = addr->dreg;
 
@@ -2474,7 +2456,7 @@ mono_emit_method_call_full (MonoCompile *cfg, MonoMethod *method, MonoMethodSign
 
        need_unbox_trampoline = method->klass == mono_defaults.object_class || mono_class_is_interface (method->klass);
 
-       call = mono_emit_call_args (cfg, sig, args, FALSE, virtual_, tail, rgctx_arg ? TRUE : FALSE, need_unbox_trampoline);
+       call = mono_emit_call_args (cfg, sig, args, FALSE, virtual_, tail, rgctx_arg ? TRUE : FALSE, need_unbox_trampoline, method);
 
 #ifndef DISABLE_REMOTING
        if (might_be_remote)
@@ -2604,7 +2586,7 @@ mono_emit_native_call (MonoCompile *cfg, gconstpointer func, MonoMethodSignature
 
        g_assert (sig);
 
-       call = mono_emit_call_args (cfg, sig, args, FALSE, FALSE, FALSE, FALSE, FALSE);
+       call = mono_emit_call_args (cfg, sig, args, FALSE, FALSE, FALSE, FALSE, FALSE, NULL);
        call->fptr = func;
 
        MONO_ADD_INS (cfg->cbb, (MonoInst*)call);
@@ -3719,11 +3701,10 @@ handle_alloc (MonoCompile *cfg, MonoClass *klass, gboolean for_box, int context_
                /* This happens often in argument checking code, eg. throw new FooException... */
                /* Avoid relocations and save some space by calling a helper function specialized to mscorlib */
                EMIT_NEW_ICONST (cfg, iargs [0], mono_metadata_token_index (klass->type_token));
-               return mono_emit_jit_icall (cfg, mono_helper_newobj_mscorlib, iargs);
+               alloc_ftn = mono_helper_newobj_mscorlib;
        } else {
                MonoVTable *vtable = mono_class_vtable (cfg->domain, klass);
                MonoMethod *managed_alloc = NULL;
-               gboolean pass_lw;
 
                if (!vtable) {
                        mono_cfg_set_exception (cfg, MONO_EXCEPTION_TYPE_LOAD);
@@ -3742,16 +3723,8 @@ handle_alloc (MonoCompile *cfg, MonoClass *klass, gboolean for_box, int context_
                        EMIT_NEW_ICONST (cfg, iargs [1], size);
                        return mono_emit_method_call (cfg, managed_alloc, iargs, NULL);
                }
-               alloc_ftn = mono_class_get_allocation_ftn (vtable, for_box, &pass_lw);
-               if (pass_lw) {
-                       guint32 lw = vtable->klass->instance_size;
-                       lw = ((lw + (sizeof (gpointer) - 1)) & ~(sizeof (gpointer) - 1)) / sizeof (gpointer);
-                       EMIT_NEW_ICONST (cfg, iargs [0], lw);
-                       EMIT_NEW_VTABLECONST (cfg, iargs [1], vtable);
-               }
-               else {
-                       EMIT_NEW_VTABLECONST (cfg, iargs [0], vtable);
-               }
+               alloc_ftn = ves_icall_object_new_specific;
+               EMIT_NEW_VTABLECONST (cfg, iargs [0], vtable);
        }
 
        return mono_emit_jit_icall (cfg, alloc_ftn, iargs);
@@ -3999,6 +3972,8 @@ handle_delegate_ctor (MonoCompile *cfg, MonoClass *klass, MonoInst *target, Mono
        /* Set target field */
        /* Optimize away setting of NULL target */
        if (!MONO_INS_IS_PCONST_NULL (target)) {
+               MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, target->dreg, 0);
+               MONO_EMIT_NEW_COND_EXC (cfg, EQ, "NullReferenceException");
                MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, target), target->dreg);
                if (cfg->gen_write_barriers) {
                        dreg = alloc_preg (cfg);
@@ -4362,6 +4337,9 @@ mono_method_check_inlining (MonoCompile *cfg, MonoMethod *method)
        if (g_list_find (cfg->dont_inline, method))
                return FALSE;
 
+       if (mono_profiler_get_call_instrumentation_flags (method))
+               return FALSE;
+
        return TRUE;
 }
 
@@ -5867,7 +5845,7 @@ mini_redirect_call (MonoCompile *cfg, MonoMethod *method,
 {
        if (method->klass == mono_defaults.string_class) {
                /* managed string allocation support */
-               if (strcmp (method->name, "InternalAllocateStr") == 0 && !(mono_profiler_events & MONO_PROFILE_ALLOCATIONS) && !(cfg->opt & MONO_OPT_SHARED)) {
+               if (strcmp (method->name, "InternalAllocateStr") == 0 && !(cfg->opt & MONO_OPT_SHARED)) {
                        MonoInst *iargs [2];
                        MonoVTable *vtable = mono_class_vtable (cfg->domain, method->klass);
                        MonoMethod *managed_alloc = NULL;
@@ -6156,9 +6134,7 @@ inline_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig,
        prev_args = cfg->args;
        prev_arg_types = cfg->arg_types;
        prev_inlined_method = cfg->inlined_method;
-       cfg->inlined_method = cmethod;
-       cfg->ret_var_set = FALSE;
-       cfg->inline_depth ++;
+       prev_ret_var_set = cfg->ret_var_set;
        prev_real_offset = cfg->real_offset;
        prev_cbb_hash = cfg->cbb_hash;
        prev_cil_offset_to_bb = cfg->cil_offset_to_bb;
@@ -6168,9 +6144,12 @@ inline_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig,
        prev_cbb = cfg->cbb;
        prev_current_method = cfg->current_method;
        prev_generic_context = cfg->generic_context;
-       prev_ret_var_set = cfg->ret_var_set;
        prev_disable_inline = cfg->disable_inline;
 
+       cfg->inlined_method = cmethod;
+       cfg->ret_var_set = FALSE;
+       cfg->inline_depth ++;
+
        if (ip && *ip == CEE_CALLVIRT && !(cmethod->flags & METHOD_ATTRIBUTE_STATIC))
                virtual_ = TRUE;
 
@@ -6631,10 +6610,78 @@ set_exception_type_from_invalid_il (MonoCompile *cfg, MonoMethod *method, unsign
        cfg->headers_to_free = g_slist_prepend_mempool (cfg->mempool, cfg->headers_to_free, header);
 }
 
+static guint32
+mono_type_to_stloc_coerce (MonoType *type)
+{
+       if (type->byref)
+               return 0;
+
+       type = mini_get_underlying_type (type);
+handle_enum:
+       switch (type->type) {
+       case MONO_TYPE_I1:
+               return OP_ICONV_TO_I1;
+       case MONO_TYPE_U1:
+               return OP_ICONV_TO_U1;
+       case MONO_TYPE_I2:
+               return OP_ICONV_TO_I2;
+       case MONO_TYPE_U2:
+               return OP_ICONV_TO_U2;
+       case MONO_TYPE_I4:
+       case MONO_TYPE_U4:
+       case MONO_TYPE_I:
+       case MONO_TYPE_U:
+       case MONO_TYPE_PTR:
+       case MONO_TYPE_FNPTR:
+       case MONO_TYPE_CLASS:
+       case MONO_TYPE_STRING:
+       case MONO_TYPE_OBJECT:
+       case MONO_TYPE_SZARRAY:
+       case MONO_TYPE_ARRAY:
+       case MONO_TYPE_I8:
+       case MONO_TYPE_U8:
+       case MONO_TYPE_R4:
+       case MONO_TYPE_R8:
+       case MONO_TYPE_TYPEDBYREF:
+       case MONO_TYPE_GENERICINST:
+               return 0;
+       case MONO_TYPE_VALUETYPE:
+               if (type->data.klass->enumtype) {
+                       type = mono_class_enum_basetype (type->data.klass);
+                       goto handle_enum;
+               }
+               return 0;
+       case MONO_TYPE_VAR:
+       case MONO_TYPE_MVAR: //TODO I believe we don't need to handle gsharedvt as there won't be match and, for example, u1 is not covariant to u32
+               return 0;
+       default:
+               g_error ("unknown type 0x%02x in mono_type_to_stloc_coerce", type->type);
+       }
+       return -1;
+}
+
 static void
 emit_stloc_ir (MonoCompile *cfg, MonoInst **sp, MonoMethodHeader *header, int n)
 {
        MonoInst *ins;
+       guint32 coerce_op = mono_type_to_stloc_coerce (header->locals [n]);
+
+       if (coerce_op) {
+               if (cfg->cbb->last_ins == sp [0] && sp [0]->opcode == coerce_op) {
+                       if (cfg->verbose_level > 2)
+                               printf ("Found existing coercing is enough for stloc\n");
+               } else {
+                       MONO_INST_NEW (cfg, ins, coerce_op);
+                       ins->dreg = alloc_ireg (cfg);
+                       ins->sreg1 = sp [0]->dreg;
+                       ins->type = STACK_I4;
+                       ins->klass = mono_class_from_mono_type (header->locals [n]);
+                       MONO_ADD_INS (cfg->cbb, ins);
+                       *sp = mono_decompose_opcode (cfg, ins);
+               }
+       }
+
+
        guint32 opcode = mono_type_to_regmove (cfg, header->locals [n]);
        if ((opcode == OP_MOVE) && cfg->cbb->last_ins == sp [0]  &&
                        ((sp [0]->opcode == OP_ICONST) || (sp [0]->opcode == OP_I8CONST))) {
@@ -6649,6 +6696,30 @@ emit_stloc_ir (MonoCompile *cfg, MonoInst **sp, MonoMethodHeader *header, int n)
        }
 }
 
+static void
+emit_starg_ir (MonoCompile *cfg, MonoInst **sp, int n)
+{
+       MonoInst *ins;
+       guint32 coerce_op = mono_type_to_stloc_coerce (cfg->arg_types [n]);
+
+       if (coerce_op) {
+               if (cfg->cbb->last_ins == sp [0] && sp [0]->opcode == coerce_op) {
+                       if (cfg->verbose_level > 2)
+                               printf ("Found existing coercing is enough for starg\n");
+               } else {
+                       MONO_INST_NEW (cfg, ins, coerce_op);
+                       ins->dreg = alloc_ireg (cfg);
+                       ins->sreg1 = sp [0]->dreg;
+                       ins->type = STACK_I4;
+                       ins->klass = mono_class_from_mono_type (cfg->arg_types [n]);
+                       MONO_ADD_INS (cfg->cbb, ins);
+                       *sp = mono_decompose_opcode (cfg, ins);
+               }
+       }
+
+       EMIT_NEW_ARGSTORE (cfg, ins, n, *sp);
+}
+
 /*
  * ldloca inhibits many optimizations so try to get rid of it in common
  * cases.
@@ -7310,8 +7381,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
        cfg->dont_inline = g_list_prepend (cfg->dont_inline, method);
        if (cfg->method == method) {
 
-               if (cfg->prof_options & MONO_PROFILE_INS_COVERAGE)
-                       cfg->coverage_info = mono_profiler_coverage_alloc (cfg->method, header->code_size);
+               cfg->coverage_info = mono_profiler_coverage_alloc (cfg->method, header->code_size);
 
                /* ENTRY BLOCK */
                NEW_BBLOCK (cfg, start_bblock);
@@ -7346,6 +7416,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                        tblock->real_offset = clause->handler_offset;
                        tblock->flags |= BB_EXCEPTION_HANDLER;
 
+                       if (clause->flags == MONO_EXCEPTION_CLAUSE_FINALLY)
+                               mono_create_exvar_for_offset (cfg, clause->handler_offset);
                        /*
                         * Linking the try block with the EH block hinders inlining as we won't be able to 
                         * merge the bblocks from inlining and produce an artificial hole for no good reason.
@@ -7821,7 +7893,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                        CHECK_ARG (n);
                        if (!dont_verify_stloc && target_type_is_incompatible (cfg, param_types [ip [1]], *sp))
                                UNVERIFIED;
-                       EMIT_NEW_ARGSTORE (cfg, ins, n, *sp);
+                       emit_starg_ir (cfg, sp, n);
                        ip += 2;
                        break;
                case CEE_LDLOC_S:
@@ -8044,7 +8116,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                        if (cfg->gshared && mono_method_check_context_used (cmethod))
                                GENERIC_SHARING_FAILURE (CEE_JMP);
 
-                       emit_instrumentation_call (cfg, mono_profiler_method_leave);
+                       mini_profiler_emit_tail_call (cfg, cmethod);
 
                        fsig = mono_method_signature (cmethod);
                        n = fsig->param_count + fsig->hasthis;
@@ -8774,6 +8846,9 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                        }
                                        for (i = 0; i < n; ++i)
                                                EMIT_NEW_ARGSTORE (cfg, ins, i, sp [i]);
+
+                                       mini_profiler_emit_tail_call (cfg, cmethod);
+
                                        MONO_INST_NEW (cfg, ins, OP_BR);
                                        MONO_ADD_INS (cfg->cbb, ins);
                                        tblock = start_bblock->out_bb [0];
@@ -8986,7 +9061,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                        /* Handle tail calls similarly to normal calls */
                                        tail_call = TRUE;
                                } else {
-                                       emit_instrumentation_call (cfg, mono_profiler_method_leave);
+                                       mini_profiler_emit_tail_call (cfg, cmethod);
 
                                        MONO_INST_NEW_CALL (cfg, call, OP_JMP);
                                        call->tail_call = TRUE;
@@ -9097,6 +9172,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                        break;
                }
                case CEE_RET:
+                       mini_profiler_emit_leave (cfg, sig->ret->type != MONO_TYPE_VOID ? sp [-1] : NULL);
+
                        if (cfg->method != method) {
                                /* return from inlined method */
                                /* 
@@ -9120,8 +9197,6 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                        cfg->ret_var_set = TRUE;
                                } 
                        } else {
-                               emit_instrumentation_call (cfg, mono_profiler_method_leave);
-
                                if (cfg->lmf_var && cfg->cbb->in_count && !cfg->llvm_only)
                                        emit_pop_lmf (cfg);
 
@@ -11409,18 +11484,35 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
 
                        if ((handlers = mono_find_final_block (cfg, ip, target, MONO_EXCEPTION_CLAUSE_FINALLY))) {
                                GList *tmp;
-                               MonoExceptionClause *clause;
 
                                for (tmp = handlers; tmp; tmp = tmp->next) {
-                                       clause = (MonoExceptionClause *)tmp->data;
+                                       MonoExceptionClause *clause = (MonoExceptionClause *)tmp->data;
+                                       MonoInst *abort_exc = (MonoInst *)mono_find_exvar_for_offset (cfg, clause->handler_offset);
+                                       MonoBasicBlock *dont_throw;
+
                                        tblock = cfg->cil_offset_to_bb [clause->handler_offset];
                                        g_assert (tblock);
                                        link_bblock (cfg, cfg->cbb, tblock);
+
+                                       MONO_EMIT_NEW_PCONST (cfg, abort_exc->dreg, 0);
+
                                        MONO_INST_NEW (cfg, ins, OP_CALL_HANDLER);
                                        ins->inst_target_bb = tblock;
                                        ins->inst_eh_block = clause;
                                        MONO_ADD_INS (cfg->cbb, ins);
                                        cfg->cbb->has_call_handler = 1;
+
+                                       /* Throw exception if exvar is set */
+                                       /* FIXME Do we need this for calls from catch/filter ? */
+                                       NEW_BBLOCK (cfg, dont_throw);
+                                       MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, abort_exc->dreg, 0);
+                                       MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, dont_throw);
+                                       mono_emit_jit_icall (cfg, mono_thread_self_abort, NULL);
+                                       cfg->cbb->clause_hole = clause;
+
+                                       MONO_START_BB (cfg, dont_throw);
+                                       cfg->cbb->clause_hole = clause;
+
                                        if (COMPILE_LLVM (cfg)) {
                                                MonoBasicBlock *target_bb;
 
@@ -11500,7 +11592,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                        case CEE_MONO_LDPTR_CARD_TABLE:
                        case CEE_MONO_LDPTR_NURSERY_START:
                        case CEE_MONO_LDPTR_NURSERY_BITS:
-                       case CEE_MONO_LDPTR_INT_REQ_FLAG: {
+                       case CEE_MONO_LDPTR_INT_REQ_FLAG:
+                       case CEE_MONO_LDPTR_PROFILER_ALLOCATION_COUNT: {
                                CHECK_STACK_OVF (1);
 
                                switch (ip [1]) {
@@ -11516,6 +11609,9 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                case CEE_MONO_LDPTR_INT_REQ_FLAG:
                                        ins = mini_emit_runtime_constant (cfg, MONO_PATCH_INFO_INTERRUPTION_REQUEST_FLAG, NULL);
                                        break;
+                               case CEE_MONO_LDPTR_PROFILER_ALLOCATION_COUNT:
+                                       ins = mini_emit_runtime_constant (cfg, MONO_PATCH_INFO_PROFILER_ALLOCATION_COUNT, NULL);
+                                       break;
                                default:
                                        g_assert_not_reached ();
                                        break;
@@ -11687,6 +11783,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                if (sp != stack_start)
                                        UNVERIFIED;
                                
+                               mini_profiler_emit_leave (cfg, sp [0]);
+
                                MONO_INST_NEW (cfg, ins, OP_BR);
                                ins->inst_target_bb = end_bblock;
                                MONO_ADD_INS (cfg->cbb, ins);
@@ -11741,7 +11839,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                        cfg->dyn_call_var->flags |= MONO_INST_VOLATILE;
                                }
 
-                               /* Has to use a call inst since it local regalloc expects it */
+                               /* Has to use a call inst since local regalloc expects it */
                                MONO_INST_NEW_CALL (cfg, call, OP_DYN_CALL);
                                ins = (MonoInst*)call;
                                sp -= 2;
@@ -11750,6 +11848,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                MONO_ADD_INS (cfg->cbb, ins);
 
                                cfg->param_area = MAX (cfg->param_area, cfg->backend->dyn_call_param_area);
+                               /* OP_DYN_CALL might need to allocate a dynamically sized param area */
+                               cfg->flags |= MONO_CFG_HAS_ALLOCA;
 
                                ip += 2;
                                inline_costs += 10 * num_calls++;
@@ -12281,7 +12381,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                CHECK_ARG (n);
                                if (!dont_verify_stloc && target_type_is_incompatible (cfg, param_types [n], *sp))
                                        UNVERIFIED;
-                               EMIT_NEW_ARGSTORE (cfg, ins, n, *sp);
+                               emit_starg_ir (cfg, sp, n);
                                ip += 4;
                                break;
                        case CEE_LDLOC:
@@ -12628,7 +12728,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
        }
 
        cfg->cbb = init_localsbb;
-       emit_instrumentation_call (cfg, mono_profiler_method_enter);
+       mini_profiler_emit_enter (cfg);
 
        if (seq_points) {
                MonoBasicBlock *bb;