Merge pull request #1708 from alexanderkyte/always_use_imt
[mono.git] / mono / mini / method-to-ir.c
index 264cfbed25613d137fc4a0bebe9bb8330e2928c6..6bce61df376328e46939bc6bc8bbfbb46f274848 100755 (executable)
@@ -407,7 +407,7 @@ static MONO_NEVER_INLINE void
 gshared_failure (MonoCompile *cfg, int opcode, const char *file, int line)
 {
        if (cfg->verbose_level > 2)                                                                                     \
-               printf ("sharing failed for method %s.%s.%s/%d opcode %s line %d\n", cfg->current_method->klass->name_space, cfg->current_method->klass->name, cfg->current_method->name, cfg->current_method->signature->param_count, mono_opcode_name ((opcode)), __LINE__);
+               printf ("sharing failed for method %s.%s.%s/%d opcode %s line %d\n", cfg->current_method->klass->name_space, cfg->current_method->klass->name, cfg->current_method->name, cfg->current_method->signature->param_count, mono_opcode_name ((opcode)), line);
        mono_cfg_set_exception (cfg, MONO_EXCEPTION_GENERIC_SHARING_FAILED);
 }
 
@@ -660,6 +660,9 @@ mono_find_block_region (MonoCompile *cfg, int offset)
                        else
                                return ((i + 1) << 8) | MONO_REGION_CATCH | clause->flags;
                }
+       }
+       for (i = 0; i < header->num_clauses; ++i) {
+               clause = &header->clauses [i];
 
                if (MONO_OFFSET_IN_CLAUSE (clause, offset))
                        return ((i + 1) << 8) | clause->flags;
@@ -2895,18 +2898,10 @@ mono_emit_method_call_full (MonoCompile *cfg, MonoMethod *method, MonoMethodSign
                        vtable_reg = alloc_preg (cfg);
                        MONO_EMIT_NEW_LOAD_MEMBASE_FAULT (cfg, vtable_reg, this_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
                        if (method->klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
-                               slot_reg = -1;
-                               if (mono_use_imt) {
-                                       guint32 imt_slot = mono_method_get_imt_slot (method);
-                                       emit_imt_argument (cfg, call, call->method, imt_arg);
-                                       slot_reg = vtable_reg;
-                                       offset = ((gint32)imt_slot - MONO_IMT_SIZE) * SIZEOF_VOID_P;
-                               }
-                               if (slot_reg == -1) {
-                                       slot_reg = alloc_preg (cfg);
-                                       mini_emit_load_intf_reg_vtable (cfg, slot_reg, vtable_reg, method->klass);
-                                       offset = mono_method_get_vtable_index (method) * SIZEOF_VOID_P;
-                               }
+                               guint32 imt_slot = mono_method_get_imt_slot (method);
+                               emit_imt_argument (cfg, call, call->method, imt_arg);
+                               slot_reg = vtable_reg;
+                               offset = ((gint32)imt_slot - MONO_IMT_SIZE) * SIZEOF_VOID_P;
                        } else {
                                slot_reg = vtable_reg;
                                offset = MONO_STRUCT_OFFSET (MonoVTable, vtable) +
@@ -2995,7 +2990,7 @@ direct_icalls_enabled (MonoCompile *cfg)
        if (cfg->compile_llvm)
                return FALSE;
 #endif
-       if (cfg->gen_seq_points_debug_data || cfg->disable_direct_icalls)
+       if (cfg->gen_sdb_seq_points || cfg->disable_direct_icalls)
                return FALSE;
        return TRUE;
 }
@@ -3511,6 +3506,28 @@ emit_get_rgctx_gsharedvt_call (MonoCompile *cfg, int context_used,
        return emit_rgctx_fetch (cfg, rgctx, entry);
 }
 
+/*
+ * emit_get_rgctx_virt_method:
+ *
+ *   Return data for method VIRT_METHOD for a receiver of type KLASS.
+ */
+static MonoInst*
+emit_get_rgctx_virt_method (MonoCompile *cfg, int context_used,
+                                                       MonoClass *klass, MonoMethod *virt_method, MonoRgctxInfoType rgctx_type)
+{
+       MonoJumpInfoVirtMethod *info;
+       MonoJumpInfoRgctxEntry *entry;
+       MonoInst *rgctx;
+
+       info = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoJumpInfoVirtMethod));
+       info->klass = klass;
+       info->method = virt_method;
+
+       entry = mono_patch_info_rgctx_entry_new (cfg->mempool, cfg->current_method, context_used & MONO_GENERIC_CONTEXT_USED_METHOD, MONO_PATCH_INFO_VIRT_METHOD, info, rgctx_type);
+       rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
+
+       return emit_rgctx_fetch (cfg, rgctx, entry);
+}
 
 static MonoInst*
 emit_get_rgctx_gsharedvt_method (MonoCompile *cfg, int context_used,
@@ -7795,16 +7812,13 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
        cfg->stat_cil_code_size += header->code_size;
 
        seq_points = cfg->gen_seq_points && cfg->method == method;
-#ifdef PLATFORM_ANDROID
-       seq_points &= cfg->method->wrapper_type == MONO_WRAPPER_NONE;
-#endif
 
        if (method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED) {
                /* We could hit a seq point before attaching to the JIT (#8338) */
                seq_points = FALSE;
        }
 
-       if (cfg->gen_seq_points_debug_data && cfg->method == method) {
+       if (cfg->gen_sdb_seq_points && cfg->method == method) {
                minfo = mono_debug_lookup_method (method);
                if (minfo) {
                        int i, n_il_offsets;
@@ -8846,6 +8860,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                        gboolean skip_ret = FALSE;
                        gboolean delegate_invoke = FALSE;
                        gboolean direct_icall = FALSE;
+                       gboolean constrained_partial_call = FALSE;
                        MonoMethod *cil_method;
 
                        CHECK_OPSIZE (5);
@@ -8860,10 +8875,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                if ((constrained_class->byval_arg.type == MONO_TYPE_VAR || constrained_class->byval_arg.type == MONO_TYPE_MVAR) && cfg->generic_sharing_context) {
                                        if (!mini_is_gsharedvt_klass (cfg, constrained_class)) {
                                                g_assert (!cmethod->klass->valuetype);
-                                               if (!mini_type_is_reference (cfg, &constrained_class->byval_arg)) {
-                                                       /* FIXME: gshared type constrained to a primitive type */
-                                                       GENERIC_SHARING_FAILURE (CEE_CALL);
-                                               }
+                                               if (!mini_type_is_reference (cfg, &constrained_class->byval_arg))
+                                                       constrained_partial_call = TRUE;
                                        }
                                }
 
@@ -9003,7 +9016,69 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                /*
                                 * We have the `constrained.' prefix opcode.
                                 */
-                               if (constrained_class->valuetype && (cmethod->klass == mono_defaults.object_class || cmethod->klass == mono_defaults.enum_class->parent || cmethod->klass == mono_defaults.enum_class)) {
+                               if (constrained_partial_call) {
+                                       gboolean need_box = TRUE;
+
+                                       /*
+                                        * The receiver is a valuetype, but the exact type is not known at compile time. This means the
+                                        * called method is not known at compile time either. The called method could end up being
+                                        * one of the methods on the parent classes (object/valuetype/enum), in which case we need
+                                        * to box the receiver.
+                                        * A simple solution would be to box always and make a normal virtual call, but that would
+                                        * be bad performance wise.
+                                        */
+                                       if (cmethod->klass->flags & TYPE_ATTRIBUTE_INTERFACE && cmethod->klass->generic_class) {
+                                               /*
+                                                * The parent classes implement no generic interfaces, so the called method will be a vtype method, so no boxing neccessary.
+                                                */
+                                               need_box = FALSE;
+                                       }
+
+                                       if (need_box) {
+                                               MonoInst *box_type;
+                                               MonoBasicBlock *is_ref_bb, *end_bb;
+                                               MonoInst *nonbox_call;
+
+                                               /*
+                                                * Determine at runtime whenever the called method is defined on object/valuetype/enum, and emit a boxing call
+                                                * if needed.
+                                                * FIXME: It is possible to inline the called method in a lot of cases, i.e. for T_INT,
+                                                * the no-box case goes to a method in Int32, while the box case goes to a method in Enum.
+                                                */
+                                               addr = emit_get_rgctx_virt_method (cfg, mono_class_check_context_used (constrained_class), constrained_class, cmethod, MONO_RGCTX_INFO_VIRT_METHOD_CODE);
+
+                                               NEW_BBLOCK (cfg, is_ref_bb);
+                                               NEW_BBLOCK (cfg, end_bb);
+
+                                               box_type = emit_get_rgctx_virt_method (cfg, mono_class_check_context_used (constrained_class), constrained_class, cmethod, MONO_RGCTX_INFO_VIRT_METHOD_BOX_TYPE);
+                                               MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, box_type->dreg, 1);
+                                               MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, is_ref_bb);
+
+                                               /* Non-ref case */
+                                               nonbox_call = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr, NULL, NULL);
+
+                                               MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
+
+                                               /* Ref case */
+                                               MONO_START_BB (cfg, is_ref_bb);
+                                               EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &constrained_class->byval_arg, sp [0]->dreg, 0);
+                                               ins->klass = constrained_class;
+                                               sp [0] = handle_box (cfg, ins, constrained_class, mono_class_check_context_used (constrained_class), &bblock);
+                                               ins = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr, NULL, NULL);
+
+                                               MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
+
+                                               MONO_START_BB (cfg, end_bb);
+                                               bblock = end_bb;
+
+                                               nonbox_call->dreg = ins->dreg;
+                                       } else {
+                                               g_assert (cmethod->klass->flags & TYPE_ATTRIBUTE_INTERFACE);
+                                               addr = emit_get_rgctx_virt_method (cfg, mono_class_check_context_used (constrained_class), constrained_class, cmethod, MONO_RGCTX_INFO_VIRT_METHOD_CODE);
+                                               ins = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr, NULL, NULL);
+                                       }
+                                       goto call_end;
+                               } else if (constrained_class->valuetype && (cmethod->klass == mono_defaults.object_class || cmethod->klass == mono_defaults.enum_class->parent || cmethod->klass == mono_defaults.enum_class)) {
                                        /*
                                         * The type parameter is instantiated as a valuetype,
                                         * but that type doesn't override the method we're
@@ -9177,7 +9252,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                        GSHAREDVT_FAILURE (*ip);
 
 #if MONO_ARCH_HAVE_GENERALIZED_IMT_THUNK && defined(MONO_ARCH_GSHARED_SUPPORTED)
-                               if (cmethod->wrapper_type == MONO_WRAPPER_NONE && mono_use_imt)
+                               if (cmethod->wrapper_type == MONO_WRAPPER_NONE)
                                        use_imt = TRUE;
 #endif
 
@@ -9330,7 +9405,6 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                                GSHAREDVT_FAILURE (*ip);
                                        if (fsig->generic_param_count) {
                                                /* virtual generic call */
-                                               g_assert (mono_use_imt);
                                                g_assert (!imt_arg);
                                                /* Same as the virtual generic case above */
                                                imt_arg = emit_get_rgctx_method (cfg, context_used,
@@ -13071,7 +13145,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
        }
 
        /* Add a sequence point for method entry/exit events */
-       if (cfg->gen_seq_points_debug_data) {
+       if (seq_points && cfg->gen_sdb_seq_points) {
                NEW_SEQ_POINT (cfg, ins, METHOD_ENTRY_IL_OFFSET, FALSE);
                MONO_ADD_INS (init_localsbb, ins);
                NEW_SEQ_POINT (cfg, ins, METHOD_EXIT_IL_OFFSET, FALSE);