2009-02-23 Mark Probst <mark.probst@gmail.com>
[mono.git] / mono / mini / method-to-ir.c
index e27f645ab51a4ce22ca33aad69f95a25ed33ec2b..8a9bcd983419b8f235187823b23e36b92b45de81 100644 (file)
 #include <sys/time.h>
 #endif
 
+#ifdef HAVE_ALLOCA_H
+#include <alloca.h>
+#endif
+
 #ifdef HAVE_VALGRIND_MEMCHECK_H
 #include <valgrind/memcheck.h>
 #endif
@@ -210,7 +214,7 @@ handle_enum:
                return OP_FMOVE;
        case MONO_TYPE_VALUETYPE:
                if (type->data.klass->enumtype) {
-                       type = type->data.klass->enum_basetype;
+                       type = mono_class_enum_basetype (type->data.klass);
                        goto handle_enum;
                }
                if (MONO_CLASS_IS_SIMD (cfg, mono_class_from_mono_type (type)))
@@ -584,7 +588,7 @@ handle_enum:
                return;
        case MONO_TYPE_VALUETYPE:
                if (type->data.klass->enumtype) {
-                       type = type->data.klass->enum_basetype;
+                       type = mono_class_enum_basetype (type->data.klass);
                        goto handle_enum;
                } else {
                        inst->klass = klass;
@@ -1773,7 +1777,7 @@ handle_enum:
                return calli? OP_FCALL_REG: virt? OP_FCALLVIRT: OP_FCALL;
        case MONO_TYPE_VALUETYPE:
                if (type->data.klass->enumtype) {
-                       type = type->data.klass->enum_basetype;
+                       type = mono_class_enum_basetype (type->data.klass);
                        goto handle_enum;
                } else
                        return calli? OP_VCALL_REG: virt? OP_VCALLVIRT: OP_VCALL;
@@ -1974,7 +1978,7 @@ handle_enum:
                        continue;
                case MONO_TYPE_VALUETYPE:
                        if (simple_type->data.klass->enumtype) {
-                               simple_type = simple_type->data.klass->enum_basetype;
+                               simple_type = mono_class_enum_basetype (simple_type->data.klass);
                                goto handle_enum;
                        }
                        if (args [i]->type != STACK_VTYPE)
@@ -2205,7 +2209,7 @@ mono_emit_method_call_full (MonoCompile *cfg, MonoMethod *method, MonoMethodSign
        if (method->string_ctor) {
                /* Create the real signature */
                /* FIXME: Cache these */
-               MonoMethodSignature *ctor_sig = mono_metadata_signature_dup_full (cfg->mempool, sig);
+               MonoMethodSignature *ctor_sig = mono_metadata_signature_dup_mempool (cfg->mempool, sig);
                ctor_sig->ret = &mono_defaults.string_class->byval_arg;
 
                sig = ctor_sig;
@@ -2771,7 +2775,7 @@ handle_alloc (MonoCompile *cfg, MonoClass *klass, gboolean for_box)
                EMIT_NEW_CLASSCONST (cfg, iargs [1], klass);
 
                alloc_ftn = mono_object_new;
-       } else if (cfg->compile_aot && cfg->cbb->out_of_line && klass->type_token && klass->image == mono_defaults.corlib) {
+       } else if (cfg->compile_aot && cfg->cbb->out_of_line && klass->type_token && klass->image == mono_defaults.corlib && !klass->generic_class) {
                /* 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));
@@ -3301,7 +3305,7 @@ static gboolean inline_limit_inited;
 static gboolean
 mono_method_check_inlining (MonoCompile *cfg, MonoMethod *method)
 {
-       MonoMethodHeader *header = mono_method_get_header (method);
+       MonoMethodHeader *header;
        MonoVTable *vtable;
 #ifdef MONO_ARCH_SOFT_FLOAT
        MonoMethodSignature *sig = mono_method_signature (method);
@@ -3318,6 +3322,12 @@ mono_method_check_inlining (MonoCompile *cfg, MonoMethod *method)
                return TRUE;
 #endif
 
+       if (method->is_inflated)
+               /* Avoid inflating the header */
+               header = mono_method_get_header (((MonoMethodInflated*)method)->declaring);
+       else
+               header = mono_method_get_header (method);
+
        if ((method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) ||
            (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
            (method->iflags & METHOD_IMPL_ATTRIBUTE_NOINLINING) ||
@@ -3438,7 +3448,12 @@ mini_emit_ldelema_1_ins (MonoCompile *cfg, MonoClass *klass, MonoInst *arr, Mono
        index2_reg = alloc_preg (cfg);
        MONO_EMIT_NEW_UNALU (cfg, OP_SEXT_I4, index2_reg, index_reg);
 #else
-       index2_reg = index_reg;
+       if (index->type == STACK_I8) {
+               index2_reg = alloc_preg (cfg);
+               MONO_EMIT_NEW_UNALU (cfg, OP_LCONV_TO_I4, index2_reg, index_reg);
+       } else {
+               index2_reg = index_reg;
+       }
 #endif
 
        MONO_EMIT_BOUNDS_CHECK (cfg, array_reg, MonoArray, max_length, index2_reg);
@@ -4051,6 +4066,7 @@ inline_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig,
        guint32 prev_cil_offset_to_bb_len;
        MonoMethod *prev_current_method;
        MonoGenericContext *prev_generic_context;
+       gboolean ret_var_set, prev_ret_var_set;
 
        g_assert (cfg->exception_type == MONO_EXCEPTION_NONE);
 
@@ -4104,9 +4120,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;
 
        costs = mono_method_to_ir (cfg, cmethod, sbblock, ebblock, rvar, dont_inline, sp, real_offset, *ip == CEE_CALLVIRT);
 
+       ret_var_set = cfg->ret_var_set;
+
        cfg->inlined_method = prev_inlined_method;
        cfg->real_offset = prev_real_offset;
        cfg->cbb_hash = prev_cbb_hash;
@@ -4118,6 +4137,7 @@ inline_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig,
        cfg->arg_types = prev_arg_types;
        cfg->current_method = prev_current_method;
        cfg->generic_context = prev_generic_context;
+       cfg->ret_var_set = prev_ret_var_set;
 
        if ((costs >= 0 && costs < 60) || inline_allways) {
                if (cfg->verbose_level > 2)
@@ -4158,7 +4178,7 @@ inline_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig,
                         * If the inlined method contains only a throw, then the ret var is not 
                         * set, so set it to a dummy value.
                         */
-                       if (!cfg->ret_var_set) {
+                       if (!ret_var_set) {
                                static double r8_0 = 0.0;
 
                                switch (rvar->type) {
@@ -4943,6 +4963,31 @@ mono_decompose_soft_float (MonoCompile *cfg)
                                        restart = TRUE;
                                        break;
                                }
+                               case OP_CKFINITE: {
+                                       MonoInst *iargs [2];
+                                       MonoInst *call, *cmp;
+
+                                       /* Convert to icall+icompare+cond_exc+move */
+
+                                       /* Create dummy MonoInst's for the arguments */
+                                       MONO_INST_NEW (cfg, iargs [0], OP_ARG);
+                                       iargs [0]->dreg = ins->sreg1;
+
+                                       call = mono_emit_jit_icall (cfg, mono_isfinite, iargs);
+
+                                       MONO_INST_NEW (cfg, cmp, OP_ICOMPARE_IMM);
+                                       cmp->sreg1 = call->dreg;
+                                       cmp->inst_imm = 1;
+                                       MONO_ADD_INS (cfg->cbb, cmp);
+
+                                       MONO_EMIT_NEW_COND_EXC (cfg, INE_UN, "ArithmeticException");
+
+                                       /* Do the assignment if the value is finite */
+                                       MONO_EMIT_NEW_UNALU (cfg, OP_FMOVE, ins->dreg, ins->sreg1);
+
+                                       restart = TRUE;
+                                       break;
+                               }
                                default:
                                        if (spec [MONO_INST_SRC1] == 'f' || spec [MONO_INST_SRC2] == 'f' || spec [MONO_INST_DEST] == 'f') {
                                                mono_print_ins (ins);
@@ -4979,7 +5024,8 @@ emit_stloc_ir (MonoCompile *cfg, MonoInst **sp, MonoMethodHeader *header, int n)
 {
        MonoInst *ins;
        guint32 opcode = mono_type_to_regmove (cfg, header->locals [n]);
-       if ((opcode == OP_MOVE) && ((sp [0]->opcode == OP_ICONST) || (sp [0]->opcode == OP_I8CONST))) {
+       if ((opcode == OP_MOVE) && cfg->cbb->last_ins == sp [0]  &&
+                       ((sp [0]->opcode == OP_ICONST) || (sp [0]->opcode == OP_I8CONST))) {
                /* Optimize reg-reg moves away */
                /* 
                 * Can't optimize other opcodes, since sp[0] might point to
@@ -5033,6 +5079,17 @@ load_error:
        return NULL;
 }
 
+static gboolean
+is_exception_class (MonoClass *class)
+{
+       while (class) {
+               if (class == mono_defaults.exception_class)
+                       return TRUE;
+               class = class->parent;
+       }
+       return FALSE;
+}
+
 /*
  * mono_method_to_ir:
  *
@@ -5263,7 +5320,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                        }
                }
        } else {
-               arg_array = alloca (sizeof (MonoInst *) * num_args);
+               arg_array = (MonoInst **) alloca (sizeof (MonoInst *) * num_args);
                cfg->cbb = start_bblock;
                cfg->args = arg_array;
                mono_save_args (cfg, sig, inline_args);
@@ -5678,9 +5735,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                case CEE_LDC_R4: {
                        float *f;
                        /* FIXME: we should really allocate this only late in the compilation process */
-                       mono_domain_lock (cfg->domain);
                        f = mono_domain_alloc (cfg->domain, sizeof (float));
-                       mono_domain_unlock (cfg->domain);
                        CHECK_OPSIZE (5);
                        CHECK_STACK_OVF (1);
                        MONO_INST_NEW (cfg, ins, OP_R4CONST);
@@ -5698,9 +5753,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                case CEE_LDC_R8: {
                        double *d;
                        /* FIXME: we should really allocate this only late in the compilation process */
-                       mono_domain_lock (cfg->domain);
                        d = mono_domain_alloc (cfg->domain, sizeof (double));
-                       mono_domain_unlock (cfg->domain);
                        CHECK_OPSIZE (9);
                        CHECK_STACK_OVF (1);
                        MONO_INST_NEW (cfg, ins, OP_R8CONST);
@@ -5839,7 +5892,19 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                        cmethod =  (MonoMethod *)mono_method_get_wrapper_data (method, token);
                                        cil_method = cmethod;
                                } else if (constrained_call) {
-                                       cmethod = mono_get_method_constrained (image, token, constrained_call, generic_context, &cil_method);
+                                       if ((constrained_call->byval_arg.type == MONO_TYPE_VAR || constrained_call->byval_arg.type == MONO_TYPE_MVAR) && cfg->generic_sharing_context) {
+                                               /* 
+                                                * This is needed since get_method_constrained can't find 
+                                                * the method in klass representing a type var.
+                                                * The type var is guaranteed to be a reference type in this
+                                                * case.
+                                                */
+                                               cmethod = mini_get_method (cfg, method, token, NULL, generic_context);
+                                               cil_method = cmethod;
+                                               g_assert (!cmethod->klass->valuetype);
+                                       } else {
+                                               cmethod = mono_get_method_constrained (image, token, constrained_call, generic_context, &cil_method);
+                                       }
                                } else {
                                        cmethod = mini_get_method (cfg, method, token, NULL, generic_context);
                                        cil_method = cmethod;
@@ -5894,7 +5959,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                        CHECK_CFG_EXCEPTION;
                                }
 
-                               if (cmethod->string_ctor)
+                               if (cmethod->string_ctor && method->wrapper_type != MONO_WRAPPER_RUNTIME_INVOKE)
                                        g_assert_not_reached ();
                        }
 
@@ -7175,7 +7240,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                         * Generate smaller code for the common newobj <exception> instruction in
                         * argument checking code.
                         */
-                       if (bblock->out_of_line && cmethod->klass->image == mono_defaults.corlib && n <= 2 && 
+                       if (bblock->out_of_line && cmethod->klass->image == mono_defaults.corlib &&
+                               is_exception_class (cmethod->klass) && n <= 2 &&
                                ((n < 1) || (!fsig->params [0]->byref && fsig->params [0]->type == MONO_TYPE_STRING)) && 
                                ((n < 2) || (!fsig->params [1]->byref && fsig->params [1]->type == MONO_TYPE_STRING))) {
                                MonoInst *iargs [3];
@@ -7327,8 +7393,10 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                        mono_emit_rgctx_calli (cfg, fsig, sp, cmethod_addr, vtable_arg);
                                } else {
                                        INLINE_FAILURE;
-                                       mono_emit_rgctx_method_call_full (cfg, cmethod, fsig, sp,
-                                                       callvirt_this_arg, NULL, vtable_arg);
+                                       ins = mono_emit_rgctx_method_call_full (cfg, cmethod, fsig, sp,
+                                                                                                                       callvirt_this_arg, NULL, vtable_arg);
+                                       if (mono_method_is_generic_sharable_impl (cmethod, TRUE) && ((MonoCallInst*)ins)->method->wrapper_type == MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK)
+                                               GENERIC_SHARING_FAILURE (*ip);
                                }
                        }
 
@@ -7971,7 +8039,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                        gpointer addr = (char*)vtable->data + field->offset;
                                        int ro_type = field->type->type;
                                        if (ro_type == MONO_TYPE_VALUETYPE && field->type->data.klass->enumtype) {
-                                               ro_type = field->type->data.klass->enum_basetype->type;
+                                               ro_type = mono_class_enum_basetype (field->type->data.klass)->type;
                                        }
                                        /* printf ("RO-FIELD %s.%s:%s\n", klass->name_space, klass->name, mono_field_get_name (field));*/
                                        is_const = TRUE;
@@ -9401,7 +9469,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                        dreg = cfg->locals [i]->dreg;
 
                        if (t == MONO_TYPE_VALUETYPE && ptype->data.klass->enumtype)
-                               t = ptype->data.klass->enum_basetype->type;
+                               t = mono_class_enum_basetype (ptype->data.klass)->type;
                        if (ptype->byref) {
                                MONO_EMIT_NEW_PCONST (cfg, dreg, NULL);
                        } else if (t >= MONO_TYPE_BOOLEAN && t <= MONO_TYPE_U4) {
@@ -10187,6 +10255,8 @@ mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local_opts)
        guint32 i, lvregs_len;
        gboolean dest_has_lvreg = FALSE;
        guint32 stacktypes [128];
+       MonoInst **live_range_start, **live_range_end;
+       MonoBasicBlock **live_range_start_bb, **live_range_end_bb;
 
        *need_local_opts = FALSE;
 
@@ -10246,6 +10316,20 @@ mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local_opts)
        vreg_to_lvreg = mono_mempool_alloc0 (cfg->mempool, sizeof (guint32) * cfg->next_vreg);
        lvregs = mono_mempool_alloc (cfg->mempool, sizeof (guint32) * 1024);
        lvregs_len = 0;
+
+       /* 
+        * These arrays contain the first and last instructions accessing a given
+        * variable.
+        * Since we emit bblocks in the same order we process them here, and we
+        * don't split live ranges, these will precisely describe the live range of
+        * the variable, i.e. the instruction range where a valid value can be found
+        * in the variables location.
+        */
+       /* FIXME: Only do this if debugging info is requested */
+       live_range_start = g_new0 (MonoInst*, cfg->next_vreg);
+       live_range_end = g_new0 (MonoInst*, cfg->next_vreg);
+       live_range_start_bb = g_new (MonoBasicBlock*, cfg->next_vreg);
+       live_range_end_bb = g_new (MonoBasicBlock*, cfg->next_vreg);
        
        /* Add spill loads/stores */
        for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
@@ -10343,6 +10427,8 @@ mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local_opts)
                                MonoInst *var = get_vreg_to_inst (cfg, ins->dreg);
                                MonoInst *store_ins;
                                int store_opcode;
+                               MonoInst *def_ins = ins;
+                               int dreg = ins->dreg; /* The original vreg */
 
                                store_opcode = mono_type_to_store_membase (cfg, var->inst_vtype);
 
@@ -10355,6 +10441,7 @@ mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local_opts)
                                        g_assert (var->opcode == OP_REGOFFSET);
                                        if (ins->opcode == OP_MOVE) {
                                                NULLIFY_INS (ins);
+                                               def_ins = NULL;
                                        } else {
                                                ins->opcode = op_to_op_dest_membase (store_opcode, ins->opcode);
                                                ins->inst_basereg = var->inst_basereg;
@@ -10388,6 +10475,7 @@ mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local_opts)
                                                mono_bblock_insert_after_ins (bb, ins, store_ins);
                                                NEW_STORE_MEMBASE (cfg, store_ins, OP_STOREI4_MEMBASE_REG, var->inst_basereg, var->inst_offset + MINI_MS_WORD_OFFSET, ins->dreg + 2);
                                                mono_bblock_insert_after_ins (bb, ins, store_ins);
+                                               def_ins = store_ins;
                                        }
                                        else {
                                                g_assert (store_opcode != OP_STOREV_MEMBASE);
@@ -10430,6 +10518,8 @@ mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local_opts)
                                                        /* Insert it after the instruction */
                                                        mono_bblock_insert_after_ins (bb, ins, store_ins);
 
+                                                       def_ins = store_ins;
+
                                                        /* 
                                                         * We can't assign ins->dreg to var->dreg here, since the
                                                         * sregs could use it. So set a flag, and do it after
@@ -10440,6 +10530,11 @@ mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local_opts)
                                                }
                                        }
                                }
+
+                               if (def_ins && !live_range_start [dreg]) {
+                                       live_range_start [dreg] = def_ins;
+                                       live_range_start_bb [dreg] = bb;
+                               }
                        }
 
                        /************/
@@ -10452,6 +10547,7 @@ mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local_opts)
                                g_assert (((sreg == -1) && (regtype == ' ')) || ((sreg != -1) && (regtype != ' ')));
                                if ((sreg != -1) && get_vreg_to_inst (cfg, sreg)) {
                                        MonoInst *var = get_vreg_to_inst (cfg, sreg);
+                                       MonoInst *use_ins = ins;
                                        MonoInst *load_ins;
                                        guint32 load_opcode;
 
@@ -10460,6 +10556,8 @@ mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local_opts)
                                                        ins->sreg1 = var->dreg;
                                                else
                                                        ins->sreg2 = var->dreg;
+                                               live_range_end [var->dreg] = use_ins;
+                                               live_range_end_bb [var->dreg] = bb;
                                                continue;
                                        }
 
@@ -10523,6 +10621,7 @@ mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local_opts)
                                                        mono_bblock_insert_before_ins (bb, ins, load_ins);
                                                        NEW_LOAD_MEMBASE (cfg, load_ins, OP_LOADI4_MEMBASE, sreg + 1, var->inst_basereg, var->inst_offset + MINI_LS_WORD_OFFSET);
                                                        mono_bblock_insert_before_ins (bb, ins, load_ins);
+                                                       use_ins = load_ins;
                                                }
                                                else {
 #if SIZEOF_REGISTER == 4
@@ -10530,8 +10629,14 @@ mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local_opts)
 #endif
                                                        NEW_LOAD_MEMBASE (cfg, load_ins, load_opcode, sreg, var->inst_basereg, var->inst_offset);
                                                        mono_bblock_insert_before_ins (bb, ins, load_ins);
+                                                       use_ins = load_ins;
                                                }
                                        }
+
+                                       if (var->dreg < orig_next_vreg) {
+                                               live_range_end [var->dreg] = use_ins;
+                                               live_range_end_bb [var->dreg] = bb;
+                                       }
                                }
                        }
 
@@ -10559,6 +10664,33 @@ mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local_opts)
                                mono_print_ins_index (1, ins);
                }
        }
+       
+#ifdef MONO_ARCH_HAVE_LIVERANGE_OPS
+       /*
+        * Emit LIVERANGE_START/LIVERANGE_END opcodes, the backend will implement them
+        * by storing the current native offset into MonoMethodVar->live_range_start/end.
+        */
+       for (i = 0; i < cfg->num_varinfo; ++i) {
+               int vreg = MONO_VARINFO (cfg, i)->vreg;
+               MonoInst *ins;
+
+               if (live_range_start [vreg]) {
+                       MONO_INST_NEW (cfg, ins, OP_LIVERANGE_START);
+                       ins->inst_c0 = i;
+                       mono_bblock_insert_after_ins (live_range_start_bb [vreg], live_range_start [vreg], ins);
+               }
+               if (live_range_end [vreg]) {
+                       MONO_INST_NEW (cfg, ins, OP_LIVERANGE_END);
+                       ins->inst_c0 = i;
+                       mono_bblock_insert_after_ins (live_range_end_bb [vreg], live_range_end [vreg], ins);
+               }
+       }
+#endif
+
+       g_free (live_range_start);
+       g_free (live_range_end);
+       g_free (live_range_start_bb);
+       g_free (live_range_end_bb);
 }
 
 /**