Merge pull request #1708 from alexanderkyte/always_use_imt
[mono.git] / mono / mini / method-to-ir.c
index a35f564967d169f6573cd6b3f908192f71f4c94b..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) +
@@ -2987,11 +2982,22 @@ mono_emit_abs_call (MonoCompile *cfg, MonoJumpInfoType patch_type, gconstpointer
        return ins;
 }
 
+static gboolean
+direct_icalls_enabled (MonoCompile *cfg)
+{
+       /* LLVM on amd64 can't handle calls to non-32 bit addresses */
+#ifdef TARGET_AMD64
+       if (cfg->compile_llvm)
+               return FALSE;
+#endif
+       if (cfg->gen_sdb_seq_points || cfg->disable_direct_icalls)
+               return FALSE;
+       return TRUE;
+}
+
 MonoInst*
 mono_emit_jit_icall_by_info (MonoCompile *cfg, MonoJitICallInfo *info, MonoInst **args, MonoBasicBlock **out_cbb)
 {
-       gboolean no_wrapper = FALSE;
-
        /*
         * Call the jit icall without a wrapper if possible.
         * The wrapper is needed for the following reasons:
@@ -3001,14 +3007,7 @@ mono_emit_jit_icall_by_info (MonoCompile *cfg, MonoJitICallInfo *info, MonoInst
         * - to be able to do stack walks for asynchronously suspended
         *   threads when debugging.
         */
-       if (info->no_raise) {
-               no_wrapper = TRUE;
-               /* LLVM on amd64 can't handle calls to non-32 bit addresses */
-               if ((cfg->compile_llvm && SIZEOF_VOID_P == 8) || cfg->gen_seq_points_debug_data || cfg->disable_direct_icalls)
-                       no_wrapper = FALSE;
-       }
-
-       if (no_wrapper) {
+       if (info->no_raise && direct_icalls_enabled (cfg)) {
                char *name;
                int costs;
 
@@ -3140,7 +3139,7 @@ emit_write_barrier (MonoCompile *cfg, MonoInst *ptr, MonoInst *value)
                wbarrier->sreg1 = ptr->dreg;
                wbarrier->sreg2 = value->dreg;
                MONO_ADD_INS (cfg->cbb, wbarrier);
-       } else if (card_table) {
+       } else if (card_table && !cfg->compile_aot && !mono_gc_card_table_nursery_check ()) {
                int offset_reg = alloc_preg (cfg);
                int card_reg  = alloc_preg (cfg);
                MonoInst *ins;
@@ -3507,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,
@@ -4199,7 +4220,7 @@ static gboolean
 icall_is_direct_callable (MonoCompile *cfg, MonoMethod *cmethod)
 {
        /* LLVM on amd64 can't handle calls to non-32 bit addresses */
-       if ((cfg->compile_llvm && SIZEOF_VOID_P == 8) || cfg->gen_seq_points_debug_data || cfg->disable_direct_icalls)
+       if (!direct_icalls_enabled (cfg))
                return FALSE;
 
        /*
@@ -4242,6 +4263,14 @@ emit_castclass_with_cache (MonoCompile *cfg, MonoClass *klass, MonoInst **args,
        return res;
 }
 
+static int
+get_castclass_cache_idx (MonoCompile *cfg)
+{
+       /* Each CASTCLASS_CACHE patch needs a unique index which identifies the call site */
+       cfg->castclass_cache_index ++;
+       return (cfg->method_index << 16) | cfg->castclass_cache_index;
+}
+
 static MonoInst*
 emit_castclass_with_cache_nonshared (MonoCompile *cfg, MonoInst *obj, MonoClass *klass, MonoBasicBlock **out_bblock)
 {
@@ -4256,9 +4285,7 @@ emit_castclass_with_cache_nonshared (MonoCompile *cfg, MonoInst *obj, MonoClass
 
        /* inline cache*/
        if (cfg->compile_aot) {
-               /* Each CASTCLASS_CACHE patch needs a unique index which identifies the call site */
-               cfg->castclass_cache_index ++;
-               idx = (cfg->method_index << 16) | cfg->castclass_cache_index;
+               idx = get_castclass_cache_idx (cfg);
                EMIT_NEW_AOTCONST (cfg, args [2], MONO_PATCH_INFO_CASTCLASS_CACHE, GINT_TO_POINTER (idx));
        } else {
                EMIT_NEW_PCONST (cfg, args [2], mono_domain_alloc0 (cfg->domain, sizeof (gpointer)));
@@ -7785,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;
@@ -8836,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);
@@ -8850,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;
                                        }
                                }
 
@@ -8993,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
@@ -9167,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
 
@@ -9320,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,
@@ -10558,6 +10642,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                        if (!context_used && mini_class_has_reference_variant_generic_argument (cfg, klass, context_used)) {
                                MonoMethod *mono_isinst = mono_marshal_get_isinst_with_cache ();
                                MonoInst *args [3];
+                               int idx;
 
                                /* obj */
                                args [0] = *sp;
@@ -10566,10 +10651,12 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                EMIT_NEW_CLASSCONST (cfg, args [1], klass);
 
                                /* inline cache*/
-                               if (cfg->compile_aot)
-                                       EMIT_NEW_AOTCONST (cfg, args [2], MONO_PATCH_INFO_CASTCLASS_CACHE, NULL);
-                               else
+                               if (cfg->compile_aot) {
+                                       idx = get_castclass_cache_idx (cfg);
+                                       EMIT_NEW_AOTCONST (cfg, args [2], MONO_PATCH_INFO_CASTCLASS_CACHE, GINT_TO_POINTER (idx));
+                               } else {
                                        EMIT_NEW_PCONST (cfg, args [2], mono_domain_alloc0 (cfg->domain, sizeof (gpointer)));
+                               }
 
                                *sp++ = mono_emit_method_call (cfg, mono_isinst, args, NULL);
                                ip += 5;
@@ -12050,6 +12137,49 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
 
                                break;
                        }
+                       case CEE_MONO_LDPTR_CARD_TABLE: {
+                               int shift_bits;
+                               gpointer card_mask;
+                               CHECK_STACK_OVF (1);
+
+                               if (cfg->compile_aot)
+                                       EMIT_NEW_AOTCONST (cfg, ins, MONO_PATCH_INFO_GC_CARD_TABLE_ADDR, NULL);
+                               else
+                                       EMIT_NEW_PCONST (cfg, ins, mono_gc_get_card_table (&shift_bits, &card_mask));
+
+                               *sp++ = ins;
+                               ip += 2;
+                               inline_costs += 10 * num_calls++;
+                               break;
+                       }
+                       case CEE_MONO_LDPTR_NURSERY_START: {
+                               int shift_bits;
+                               size_t size;
+                               CHECK_STACK_OVF (1);
+
+                               if (cfg->compile_aot)
+                                       EMIT_NEW_AOTCONST (cfg, ins, MONO_PATCH_INFO_GC_NURSERY_START, NULL);
+                               else
+                                       EMIT_NEW_PCONST (cfg, ins, mono_gc_get_nursery (&shift_bits, &size));
+
+                               *sp++ = ins;
+                               ip += 2;
+                               inline_costs += 10 * num_calls++;
+                               break;
+                       }
+                       case CEE_MONO_LDPTR_INT_REQ_FLAG: {
+                               CHECK_STACK_OVF (1);
+
+                               if (cfg->compile_aot)
+                                       EMIT_NEW_AOTCONST (cfg, ins, MONO_PATCH_INFO_INTERRUPTION_REQUEST_FLAG, NULL);
+                               else
+                                       EMIT_NEW_PCONST (cfg, ins, mono_thread_interruption_request_flag ());
+
+                               *sp++ = ins;
+                               ip += 2;
+                               inline_costs += 10 * num_calls++;
+                               break;
+                       }
                        case CEE_MONO_LDPTR: {
                                gpointer ptr;
 
@@ -12058,13 +12188,6 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                token = read32 (ip + 2);
 
                                ptr = mono_method_get_wrapper_data (method, token);
-                               /* FIXME: Generalize this */
-                               if (cfg->compile_aot && ptr == mono_thread_interruption_request_flag ()) {
-                                       EMIT_NEW_AOTCONST (cfg, ins, MONO_PATCH_INFO_INTERRUPTION_REQUEST_FLAG, NULL);
-                                       *sp++ = ins;
-                                       ip += 6;
-                                       break;
-                               }
                                EMIT_NEW_PCONST (cfg, ins, ptr);
                                *sp++ = ins;
                                ip += 6;
@@ -13022,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);