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);
}
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;
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) +
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:
* - 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;
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;
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,
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;
/*
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)
{
/* 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)));
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;
gboolean skip_ret = FALSE;
gboolean delegate_invoke = FALSE;
gboolean direct_icall = FALSE;
+ gboolean constrained_partial_call = FALSE;
MonoMethod *cil_method;
CHECK_OPSIZE (5);
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;
}
}
/*
* 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
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
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,
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;
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;
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;
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;
}
/* 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);