X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmini%2Fmethod-to-ir.c;h=264cfbed25613d137fc4a0bebe9bb8330e2928c6;hb=438e3b5ac335f69a620d2db738626fca1d0c2980;hp=b61559e31eb707641f76a534e67a6f2d8d28ae94;hpb=1d6362cdbd39d35c2c5ba4f933ed96881f94f4a5;p=mono.git diff --git a/mono/mini/method-to-ir.c b/mono/mini/method-to-ir.c index b61559e31eb..264cfbed256 100755 --- a/mono/mini/method-to-ir.c +++ b/mono/mini/method-to-ir.c @@ -276,16 +276,14 @@ mono_type_to_regmove (MonoCompile *cfg, MonoType *type) if (type->byref) return OP_MOVE; - type = mini_replace_type (type); + type = mini_get_underlying_type (cfg, type); handle_enum: switch (type->type) { case MONO_TYPE_I1: case MONO_TYPE_U1: - case MONO_TYPE_BOOLEAN: return OP_MOVE; case MONO_TYPE_I2: case MONO_TYPE_U2: - case MONO_TYPE_CHAR: return OP_MOVE; case MONO_TYPE_I4: case MONO_TYPE_U4: @@ -331,7 +329,7 @@ handle_enum: if (mini_type_var_is_vt (cfg, type)) return OP_VMOVE; else - return OP_MOVE; + return mono_type_to_regmove (cfg, mini_get_underlying_type (cfg, type)); default: g_error ("unknown type 0x%02x in type_to_regstore", type->type); } @@ -459,19 +457,48 @@ gsharedvt_failure (MonoCompile *cfg, int opcode, const char *file, int line) } while (0) #endif +/* Emit conversions so both operands of a binary opcode are of the same type */ +static void +add_widen_op (MonoCompile *cfg, MonoInst *ins, MonoInst **arg1_ref, MonoInst **arg2_ref) +{ + MonoInst *arg1 = *arg1_ref; + MonoInst *arg2 = *arg2_ref; + + if (cfg->r4fp && + ((arg1->type == STACK_R4 && arg2->type == STACK_R8) || + (arg1->type == STACK_R8 && arg2->type == STACK_R4))) { + MonoInst *conv; + + /* Mixing r4/r8 is allowed by the spec */ + if (arg1->type == STACK_R4) { + int dreg = alloc_freg (cfg); + + EMIT_NEW_UNALU (cfg, conv, OP_RCONV_TO_R8, dreg, arg1->dreg); + conv->type = STACK_R8; + ins->sreg1 = dreg; + *arg1_ref = conv; + } + if (arg2->type == STACK_R4) { + int dreg = alloc_freg (cfg); + + EMIT_NEW_UNALU (cfg, conv, OP_RCONV_TO_R8, dreg, arg2->dreg); + conv->type = STACK_R8; + ins->sreg2 = dreg; + *arg2_ref = conv; + } + } + #if SIZEOF_REGISTER == 8 -#define ADD_WIDEN_OP(ins, arg1, arg2) do { \ - /* FIXME: Need to add many more cases */ \ - if ((arg1)->type == STACK_PTR && (arg2)->type == STACK_I4) { \ - MonoInst *widen; \ - int dr = alloc_preg (cfg); \ - EMIT_NEW_UNALU (cfg, widen, OP_SEXT_I4, dr, (arg2)->dreg); \ - (ins)->sreg2 = widen->dreg; \ - } \ - } while (0) -#else -#define ADD_WIDEN_OP(ins, arg1, arg2) + /* FIXME: Need to add many more cases */ + if ((arg1)->type == STACK_PTR && (arg2)->type == STACK_I4) { + MonoInst *widen; + + int dr = alloc_preg (cfg); + EMIT_NEW_UNALU (cfg, widen, OP_SEXT_I4, dr, (arg2)->dreg); + (ins)->sreg2 = widen->dreg; + } #endif +} #define ADD_BINOP(op) do { \ MONO_INST_NEW (cfg, ins, (op)); \ @@ -481,10 +508,10 @@ gsharedvt_failure (MonoCompile *cfg, int opcode, const char *file, int line) type_from_op (cfg, ins, sp [0], sp [1]); \ CHECK_TYPE (ins); \ /* Have to insert a widening op */ \ - ADD_WIDEN_OP (ins, sp [0], sp [1]); \ + add_widen_op (cfg, ins, &sp [0], &sp [1]); \ ins->dreg = alloc_dreg ((cfg), (ins)->type); \ MONO_ADD_INS ((cfg)->cbb, (ins)); \ - *sp++ = mono_decompose_opcode ((cfg), (ins)); \ + *sp++ = mono_decompose_opcode ((cfg), (ins), &bblock); \ } while (0) #define ADD_UNOP(op) do { \ @@ -495,7 +522,7 @@ gsharedvt_failure (MonoCompile *cfg, int opcode, const char *file, int line) CHECK_TYPE (ins); \ (ins)->dreg = alloc_dreg ((cfg), (ins)->type); \ MONO_ADD_INS ((cfg)->cbb, (ins)); \ - *sp++ = mono_decompose_opcode (cfg, ins); \ + *sp++ = mono_decompose_opcode (cfg, ins, &bblock); \ } while (0) #define ADD_BINCOND(next_block) do { \ @@ -506,6 +533,7 @@ gsharedvt_failure (MonoCompile *cfg, int opcode, const char *file, int line) cmp->sreg2 = sp [1]->dreg; \ type_from_op (cfg, cmp, sp [0], sp [1]); \ CHECK_TYPE (cmp); \ + add_widen_op (cfg, cmp, &sp [0], &sp [1]); \ type_from_op (cfg, ins, sp [0], sp [1]); \ ins->inst_many_bb = mono_mempool_alloc (cfg->mempool, sizeof(gpointer)*2); \ GET_BBLOCK (cfg, tblock, target); \ @@ -708,7 +736,7 @@ type_to_eval_stack_type (MonoCompile *cfg, MonoType *type, MonoInst *inst) { MonoClass *klass; - type = mini_replace_type (type); + type = mini_get_underlying_type (cfg, type); inst->klass = klass = mono_class_from_mono_type (type); if (type->byref) { inst->type = STACK_MP; @@ -722,10 +750,8 @@ handle_enum: return; case MONO_TYPE_I1: case MONO_TYPE_U1: - case MONO_TYPE_BOOLEAN: case MONO_TYPE_I2: case MONO_TYPE_U2: - case MONO_TYPE_CHAR: case MONO_TYPE_I4: case MONO_TYPE_U4: inst->type = STACK_I4; @@ -776,7 +802,7 @@ handle_enum: g_assert (cfg->gsharedvt); inst->type = STACK_VTYPE; } else { - inst->type = STACK_OBJ; + type_to_eval_stack_type (cfg, mini_get_underlying_type (cfg, type), inst); } return; default: @@ -793,11 +819,11 @@ bin_num_table [STACK_MAX] [STACK_MAX] = { {STACK_INV, STACK_I4, STACK_INV, STACK_PTR, STACK_INV, STACK_MP, STACK_INV, STACK_INV}, {STACK_INV, STACK_INV, STACK_I8, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV}, {STACK_INV, STACK_PTR, STACK_INV, STACK_PTR, STACK_INV, STACK_MP, STACK_INV, STACK_INV}, - {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_R8, STACK_INV, STACK_INV, STACK_INV}, + {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_R8, STACK_INV, STACK_INV, STACK_INV, STACK_R8}, {STACK_INV, STACK_MP, STACK_INV, STACK_MP, STACK_INV, STACK_PTR, STACK_INV, STACK_INV}, {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV}, {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV}, - {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_R4} + {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_R8, STACK_INV, STACK_INV, STACK_INV, STACK_R4} }; static const char @@ -825,11 +851,11 @@ bin_comp_table [STACK_MAX] [STACK_MAX] = { {0, 1, 0, 1, 0, 0, 0, 0}, /* i, int32 */ {0, 0, 1, 0, 0, 0, 0, 0}, /* L, int64 */ {0, 1, 0, 1, 0, 2, 4, 0}, /* p, ptr */ - {0, 0, 0, 0, 1, 0, 0, 0}, /* F, R8 */ + {0, 0, 0, 0, 1, 0, 0, 0, 1}, /* F, R8 */ {0, 0, 0, 2, 0, 1, 0, 0}, /* &, managed pointer */ {0, 0, 0, 4, 0, 0, 3, 0}, /* O, reference */ {0, 0, 0, 0, 0, 0, 0, 0}, /* vt value type */ - {0, 0, 0, 0, 0, 0, 0, 0, 1}, /* r, r4 */ + {0, 0, 0, 0, 1, 0, 0, 0, 1}, /* r, r4 */ }; /* reduce the size of this table */ @@ -1260,10 +1286,8 @@ type_to_stack_type (MonoCompile *cfg, MonoType *t) switch (t->type) { case MONO_TYPE_I1: case MONO_TYPE_U1: - case MONO_TYPE_BOOLEAN: case MONO_TYPE_I2: case MONO_TYPE_U2: - case MONO_TYPE_CHAR: case MONO_TYPE_I4: case MONO_TYPE_U4: return STACK_I4; @@ -2110,21 +2134,15 @@ emit_instrumentation_call (MonoCompile *cfg, void *func) static int ret_type_to_call_opcode (MonoCompile *cfg, MonoType *type, int calli, int virt, MonoGenericSharingContext *gsctx) { - if (type->byref) - return calli? OP_CALL_REG: virt? OP_CALL_MEMBASE: OP_CALL; - handle_enum: - type = mini_get_basic_type_from_generic (gsctx, type); - type = mini_replace_type (type); + type = mini_get_underlying_type (cfg, type); switch (type->type) { case MONO_TYPE_VOID: return calli? OP_VOIDCALL_REG: virt? OP_VOIDCALL_MEMBASE: OP_VOIDCALL; case MONO_TYPE_I1: case MONO_TYPE_U1: - case MONO_TYPE_BOOLEAN: case MONO_TYPE_I2: case MONO_TYPE_U2: - case MONO_TYPE_CHAR: case MONO_TYPE_I4: case MONO_TYPE_U4: return calli? OP_CALL_REG: virt? OP_CALL_MEMBASE: OP_CALL; @@ -2187,7 +2205,6 @@ target_type_is_incompatible (MonoCompile *cfg, MonoType *target, MonoInst *arg) MonoType *simple_type; MonoClass *klass; - target = mini_replace_type (target); if (target->byref) { /* FIXME: check that the pointed to types match */ if (arg->type == STACK_MP) @@ -2197,16 +2214,14 @@ target_type_is_incompatible (MonoCompile *cfg, MonoType *target, MonoInst *arg) return 1; } - simple_type = mono_type_get_underlying_type (target); + simple_type = mini_get_underlying_type (cfg, target); switch (simple_type->type) { case MONO_TYPE_VOID: return 1; case MONO_TYPE_I1: case MONO_TYPE_U1: - case MONO_TYPE_BOOLEAN: case MONO_TYPE_I2: case MONO_TYPE_U2: - case MONO_TYPE_CHAR: case MONO_TYPE_I4: case MONO_TYPE_U4: if (arg->type != STACK_I4 && arg->type != STACK_PTR) @@ -2320,8 +2335,7 @@ check_call_signature (MonoCompile *cfg, MonoMethodSignature *sig, MonoInst **arg return 1; continue; } - simple_type = sig->params [i]; - simple_type = mini_get_basic_type_from_generic (cfg->generic_sharing_context, simple_type); + simple_type = mini_get_underlying_type (cfg, sig->params [i]); handle_enum: switch (simple_type->type) { case MONO_TYPE_VOID: @@ -2329,10 +2343,8 @@ handle_enum: continue; case MONO_TYPE_I1: case MONO_TYPE_U1: - case MONO_TYPE_BOOLEAN: case MONO_TYPE_I2: case MONO_TYPE_U2: - case MONO_TYPE_CHAR: case MONO_TYPE_I4: case MONO_TYPE_U4: if (args [i]->type != STACK_I4 && args [i]->type != STACK_PTR) @@ -2582,7 +2594,7 @@ mono_emit_call_args (MonoCompile *cfg, MonoMethodSignature *sig, call->args = args; call->signature = sig; call->rgctx_reg = rgctx; - sig_ret = mini_replace_type (sig->ret); + sig_ret = mini_get_underlying_type (cfg, sig->ret); type_to_eval_stack_type ((cfg), sig_ret, &call->inst); @@ -2974,6 +2986,57 @@ mono_emit_abs_call (MonoCompile *cfg, MonoJumpInfoType patch_type, gconstpointer ((MonoCallInst*)ins)->fptr_is_patch = TRUE; 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_seq_points_debug_data || cfg->disable_direct_icalls) + return FALSE; + return TRUE; +} + +MonoInst* +mono_emit_jit_icall_by_info (MonoCompile *cfg, MonoJitICallInfo *info, MonoInst **args, MonoBasicBlock **out_cbb) +{ + /* + * Call the jit icall without a wrapper if possible. + * The wrapper is needed for the following reasons: + * - to handle exceptions thrown using mono_raise_exceptions () from the + * icall function. The EH code needs the lmf frame pushed by the + * wrapper to be able to unwind back to managed code. + * - to be able to do stack walks for asynchronously suspended + * threads when debugging. + */ + if (info->no_raise && direct_icalls_enabled (cfg)) { + char *name; + int costs; + + if (!info->wrapper_method) { + name = g_strdup_printf ("__icall_wrapper_%s", info->name); + info->wrapper_method = mono_marshal_get_icall_wrapper (info->sig, name, info->func, TRUE); + g_free (name); + mono_memory_barrier (); + } + + /* + * Inline the wrapper method, which is basically a call to the C icall, and + * an exception check. + */ + costs = inline_method (cfg, info->wrapper_method, NULL, + args, NULL, cfg->real_offset, TRUE, out_cbb); + g_assert (costs > 0); + g_assert (!MONO_TYPE_IS_VOID (info->sig->ret)); + + return args [0]; + } else { + return mono_emit_native_call (cfg, mono_icall_get_wrapper (info), info->sig, args); + } +} static MonoInst* mono_emit_widen_call_res (MonoCompile *cfg, MonoInst *ins, MonoMethodSignature *fsig) @@ -3081,7 +3144,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; @@ -3210,13 +3273,16 @@ void mini_emit_stobj (MonoCompile *cfg, MonoInst *dest, MonoInst *src, MonoClass *klass, gboolean native) { MonoInst *iargs [4]; - int context_used, n; + int n; guint32 align = 0; MonoMethod *memcpy_method; MonoInst *size_ins = NULL; MonoInst *memcpy_ins = NULL; g_assert (klass); + if (cfg->generic_sharing_context) + klass = mono_class_from_mono_type (mini_get_underlying_type (cfg, &klass->byval_arg)); + /* * This check breaks with spilled vars... need to handle it during verification anyway. * g_assert (klass && klass == src->klass && klass == dest->klass); @@ -3224,7 +3290,6 @@ mini_emit_stobj (MonoCompile *cfg, MonoInst *dest, MonoInst *src, MonoClass *kla if (mini_is_gsharedvt_klass (cfg, klass)) { g_assert (!native); - context_used = mini_class_check_context_used (cfg, klass); size_ins = emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_VALUE_SIZE); memcpy_ins = emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_MEMCPY); } @@ -3303,7 +3368,7 @@ void mini_emit_initobj (MonoCompile *cfg, MonoInst *dest, const guchar *ip, MonoClass *klass) { MonoInst *iargs [3]; - int n, context_used; + int n; guint32 align; MonoMethod *memset_method; MonoInst *size_ins = NULL; @@ -3311,10 +3376,8 @@ mini_emit_initobj (MonoCompile *cfg, MonoInst *dest, const guchar *ip, MonoClass static MonoMethod *bzero_method; /* FIXME: Optimize this for the case when dest is an LDADDR */ - mono_class_init (klass); if (mini_is_gsharedvt_klass (cfg, klass)) { - context_used = mini_class_check_context_used (cfg, klass); size_ins = emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_VALUE_SIZE); bzero_ins = emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_BZERO); if (!bzero_method) @@ -3798,7 +3861,7 @@ handle_unbox (MonoCompile *cfg, MonoClass *klass, MonoInst **sp, int context_use g_assert (klass->rank == 0); element_class = emit_get_rgctx_klass (cfg, context_used, - klass->element_class, MONO_RGCTX_INFO_KLASS); + klass, MONO_RGCTX_INFO_ELEMENT_KLASS); MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, eclass_reg, element_class->dreg); MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException"); @@ -3872,9 +3935,6 @@ handle_unbox_gsharedvt (MonoCompile *cfg, MonoClass *klass, MonoInst *obj, MonoB MonoInst *addr = emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_NULLABLE_CLASS_UNBOX); MonoInst *unbox_call; MonoMethodSignature *unbox_sig; - MonoInst *var; - - var = mono_compile_create_var (cfg, &klass->byval_arg, OP_LOCAL); unbox_sig = mono_mempool_alloc0 (cfg->mempool, MONO_SIZEOF_METHOD_SIGNATURE + (1 * sizeof (MonoType *))); unbox_sig->ret = &klass->byval_arg; @@ -3912,8 +3972,9 @@ handle_alloc (MonoCompile *cfg, MonoClass *klass, gboolean for_box, int context_ MonoInst *data; int rgctx_info; MonoInst *iargs [2]; + gboolean known_instance_size = !mini_is_gsharedvt_klass (cfg, klass); - MonoMethod *managed_alloc = mono_gc_get_managed_allocator (klass, for_box); + MonoMethod *managed_alloc = mono_gc_get_managed_allocator (klass, for_box, known_instance_size); if (cfg->opt & MONO_OPT_SHARED) rgctx_info = MONO_RGCTX_INFO_KLASS; @@ -3930,8 +3991,14 @@ handle_alloc (MonoCompile *cfg, MonoClass *klass, gboolean for_box, int context_ alloc_ftn = mono_object_new_specific; } - if (managed_alloc && !(cfg->opt & MONO_OPT_SHARED)) + if (managed_alloc && !(cfg->opt & MONO_OPT_SHARED)) { + if (known_instance_size) { + int size = mono_class_instance_size (klass); + + EMIT_NEW_ICONST (cfg, iargs [1], mono_gc_get_aligned_size_for_allocator (size)); + } return mono_emit_method_call (cfg, managed_alloc, iargs, NULL); + } return mono_emit_jit_icall (cfg, alloc_ftn, iargs); } @@ -3958,11 +4025,14 @@ handle_alloc (MonoCompile *cfg, MonoClass *klass, gboolean for_box, int context_ } #ifndef MONO_CROSS_COMPILE - managed_alloc = mono_gc_get_managed_allocator (klass, for_box); + managed_alloc = mono_gc_get_managed_allocator (klass, for_box, TRUE); #endif if (managed_alloc) { + int size = mono_class_instance_size (klass); + EMIT_NEW_VTABLECONST (cfg, iargs [0], vtable); + EMIT_NEW_ICONST (cfg, iargs [1], mono_gc_get_aligned_size_for_allocator (size)); return mono_emit_method_call (cfg, managed_alloc, iargs, NULL); } alloc_ftn = mono_class_get_allocation_ftn (vtable, for_box, &pass_lw); @@ -4022,7 +4092,7 @@ handle_box (MonoCompile *cfg, MonoInst *val, MonoClass *klass, int context_used, if (mini_is_gsharedvt_klass (cfg, klass)) { MonoBasicBlock *is_ref_bb, *is_nullable_bb, *end_bb; MonoInst *res, *is_ref, *src_var, *addr; - int addr_reg, dreg; + int dreg; dreg = alloc_ireg (cfg); @@ -4050,7 +4120,6 @@ handle_box (MonoCompile *cfg, MonoInst *val, MonoClass *klass, int context_used, /* Ref case */ MONO_START_BB (cfg, is_ref_bb); - addr_reg = alloc_ireg (cfg); /* val is a vtype, so has to load the value manually */ src_var = get_vreg_to_inst (cfg, val->dreg); @@ -4100,7 +4169,6 @@ handle_box (MonoCompile *cfg, MonoInst *val, MonoClass *klass, int context_used, } } - static gboolean mini_class_has_reference_variant_generic_argument (MonoCompile *cfg, MonoClass *klass, int context_used) { @@ -4129,6 +4197,37 @@ mini_class_has_reference_variant_generic_argument (MonoCompile *cfg, MonoClass * return FALSE; } +static GHashTable* direct_icall_type_hash; + +static gboolean +icall_is_direct_callable (MonoCompile *cfg, MonoMethod *cmethod) +{ + /* LLVM on amd64 can't handle calls to non-32 bit addresses */ + if (!direct_icalls_enabled (cfg)) + return FALSE; + + /* + * An icall is directly callable if it doesn't directly or indirectly call mono_raise_exception (). + * Whitelist a few icalls for now. + */ + if (!direct_icall_type_hash) { + GHashTable *h = g_hash_table_new (g_str_hash, g_str_equal); + + g_hash_table_insert (h, (char*)"Decimal", GUINT_TO_POINTER (1)); + g_hash_table_insert (h, (char*)"Number", GUINT_TO_POINTER (1)); + g_hash_table_insert (h, (char*)"Buffer", GUINT_TO_POINTER (1)); + mono_memory_barrier (); + direct_icall_type_hash = h; + } + + if (cmethod->klass == mono_defaults.math_class) + return TRUE; + /* No locking needed */ + if (cmethod->klass->image == mono_defaults.corlib && g_hash_table_lookup (direct_icall_type_hash, cmethod->klass->name)) + return TRUE; + return FALSE; +} + #define is_complex_isinst(klass) ((klass->flags & TYPE_ATTRIBUTE_INTERFACE) || klass->rank || mono_class_is_nullable (klass) || mono_class_is_marshalbyref (klass) || (klass->flags & TYPE_ATTRIBUTE_SEALED) || klass->byval_arg.type == MONO_TYPE_VAR || klass->byval_arg.type == MONO_TYPE_MVAR) static MonoInst* @@ -4147,6 +4246,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) { @@ -4161,9 +4268,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))); @@ -4642,7 +4747,7 @@ handle_enum_has_flag (MonoCompile *cfg, MonoClass *klass, MonoInst *enum_this, M { MonoType *enum_type = mono_type_get_underlying_type (&klass->byval_arg); guint32 load_opc = mono_type_to_load_membase (cfg, enum_type); - gboolean is_i4 = TRUE; + gboolean is_i4; switch (enum_type->type) { case MONO_TYPE_I8: @@ -4653,6 +4758,9 @@ handle_enum_has_flag (MonoCompile *cfg, MonoClass *klass, MonoInst *enum_this, M #endif is_i4 = FALSE; break; + default: + is_i4 = TRUE; + break; } { @@ -4669,10 +4777,10 @@ handle_enum_has_flag (MonoCompile *cfg, MonoClass *klass, MonoInst *enum_this, M ceq->type = STACK_I4; if (!is_i4) { - load = mono_decompose_opcode (cfg, load); - and = mono_decompose_opcode (cfg, and); - cmp = mono_decompose_opcode (cfg, cmp); - ceq = mono_decompose_opcode (cfg, ceq); + load = mono_decompose_opcode (cfg, load, NULL); + and = mono_decompose_opcode (cfg, and, NULL); + cmp = mono_decompose_opcode (cfg, cmp, NULL); + ceq = mono_decompose_opcode (cfg, ceq, NULL); } return ceq; @@ -4806,6 +4914,99 @@ handle_array_new (MonoCompile *cfg, int rank, MonoInst **sp, unsigned char *ip) return mono_emit_native_call (cfg, mono_icall_get_wrapper (info), info->sig, sp); } +/* + * handle_constrained_gsharedvt_call: + * + * Handle constrained calls where the receiver is a gsharedvt type. + * Return the instruction representing the call. Set the cfg exception on failure. + */ +static MonoInst* +handle_constrained_gsharedvt_call (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **sp, MonoClass *constrained_class, + gboolean *ref_emit_widen, MonoBasicBlock **ref_bblock) +{ + MonoInst *ins = NULL; + MonoBasicBlock *bblock = *ref_bblock; + gboolean emit_widen = *ref_emit_widen; + + /* + * Constrained calls need to behave differently at runtime dependending on whenever the receiver is instantiated as ref type or as a vtype. + * This is hard to do with the current call code, since we would have to emit a branch and two different calls. So instead, we + * pack the arguments into an array, and do the rest of the work in in an icall. + */ + if (((cmethod->klass == mono_defaults.object_class) || (cmethod->klass->flags & TYPE_ATTRIBUTE_INTERFACE) || (!cmethod->klass->valuetype && cmethod->klass->image != mono_defaults.corlib)) && + (MONO_TYPE_IS_VOID (fsig->ret) || MONO_TYPE_IS_PRIMITIVE (fsig->ret) || MONO_TYPE_IS_REFERENCE (fsig->ret) || MONO_TYPE_ISSTRUCT (fsig->ret) || mini_is_gsharedvt_type (cfg, fsig->ret)) && + (fsig->param_count == 0 || (!fsig->hasthis && fsig->param_count == 1) || (fsig->param_count == 1 && (MONO_TYPE_IS_REFERENCE (fsig->params [0]) || fsig->params [0]->byref || mini_is_gsharedvt_type (cfg, fsig->params [0]))))) { + MonoInst *args [16]; + + /* + * This case handles calls to + * - object:ToString()/Equals()/GetHashCode(), + * - System.IComparable:CompareTo() + * - System.IEquatable:Equals () + * plus some simple interface calls enough to support AsyncTaskMethodBuilder. + */ + + args [0] = sp [0]; + if (mono_method_check_context_used (cmethod)) + args [1] = emit_get_rgctx_method (cfg, mono_method_check_context_used (cmethod), cmethod, MONO_RGCTX_INFO_METHOD); + else + EMIT_NEW_METHODCONST (cfg, args [1], cmethod); + args [2] = emit_get_rgctx_klass (cfg, mono_class_check_context_used (constrained_class), constrained_class, MONO_RGCTX_INFO_KLASS); + + /* !fsig->hasthis is for the wrapper for the Object.GetType () icall */ + if (fsig->hasthis && fsig->param_count) { + /* Pass the arguments using a localloc-ed array using the format expected by runtime_invoke () */ + MONO_INST_NEW (cfg, ins, OP_LOCALLOC_IMM); + ins->dreg = alloc_preg (cfg); + ins->inst_imm = fsig->param_count * sizeof (mgreg_t); + MONO_ADD_INS (cfg->cbb, ins); + args [4] = ins; + + if (mini_is_gsharedvt_type (cfg, fsig->params [0])) { + int addr_reg; + + args [3] = emit_get_gsharedvt_info_klass (cfg, mono_class_from_mono_type (fsig->params [0]), MONO_RGCTX_INFO_CLASS_BOX_TYPE); + + EMIT_NEW_VARLOADA_VREG (cfg, ins, sp [1]->dreg, fsig->params [0]); + addr_reg = ins->dreg; + EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, args [4]->dreg, 0, addr_reg); + } else { + EMIT_NEW_ICONST (cfg, args [3], 0); + EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, args [4]->dreg, 0, sp [1]->dreg); + } + } else { + EMIT_NEW_ICONST (cfg, args [3], 0); + EMIT_NEW_ICONST (cfg, args [4], 0); + } + ins = mono_emit_jit_icall (cfg, mono_gsharedvt_constrained_call, args); + emit_widen = FALSE; + + if (mini_is_gsharedvt_type (cfg, fsig->ret)) { + ins = handle_unbox_gsharedvt (cfg, mono_class_from_mono_type (fsig->ret), ins, &bblock); + } else if (MONO_TYPE_IS_PRIMITIVE (fsig->ret) || MONO_TYPE_ISSTRUCT (fsig->ret)) { + MonoInst *add; + + /* Unbox */ + NEW_BIALU_IMM (cfg, add, OP_ADD_IMM, alloc_dreg (cfg, STACK_MP), ins->dreg, sizeof (MonoObject)); + MONO_ADD_INS (cfg->cbb, add); + /* Load value */ + NEW_LOAD_MEMBASE_TYPE (cfg, ins, fsig->ret, add->dreg, 0); + MONO_ADD_INS (cfg->cbb, ins); + /* ins represents the call result */ + } + } else { + GSHAREDVT_FAILURE (CEE_CALLVIRT); + } + + *ref_emit_widen = emit_widen; + *ref_bblock = bblock; + + return ins; + + exception_exit: + return NULL; +} + static void mono_emit_load_got_addr (MonoCompile *cfg) { @@ -5148,20 +5349,24 @@ mini_emit_ldelema_ins (MonoCompile *cfg, MonoMethod *cmethod, MonoInst **sp, uns MonoInst *addr; MonoMethod *addr_method; int element_size; + MonoClass *eclass = cmethod->klass->element_class; rank = mono_method_signature (cmethod)->param_count - (is_set? 1: 0); if (rank == 1) - return mini_emit_ldelema_1_ins (cfg, cmethod->klass->element_class, sp [0], sp [1], TRUE); + return mini_emit_ldelema_1_ins (cfg, eclass, sp [0], sp [1], TRUE); #ifndef MONO_ARCH_EMULATE_MUL_DIV /* emit_ldelema_2 depends on OP_LMUL */ - if (rank == 2 && (cfg->opt & MONO_OPT_INTRINS)) { - return mini_emit_ldelema_2_ins (cfg, cmethod->klass->element_class, sp [0], sp [1], sp [2]); + if (rank == 2 && (cfg->opt & MONO_OPT_INTRINS) && !mini_is_gsharedvt_variable_klass (cfg, eclass)) { + return mini_emit_ldelema_2_ins (cfg, eclass, sp [0], sp [1], sp [2]); } #endif - element_size = mono_class_array_element_size (cmethod->klass->element_class); + if (mini_is_gsharedvt_variable_klass (cfg, eclass)) + element_size = 0; + else + element_size = mono_class_array_element_size (eclass); addr_method = mono_marshal_get_array_address (rank, element_size); addr = mono_emit_method_call (cfg, addr_method, sp, NULL); @@ -5477,7 +5682,6 @@ mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign if (strcmp (cmethod->name, "get_Chars") == 0 && fsig->param_count == 2) { int dreg = alloc_ireg (cfg); int index_reg = alloc_preg (cfg); - int mult_reg = alloc_preg (cfg); int add_reg = alloc_preg (cfg); #if SIZEOF_REGISTER == 8 @@ -5491,11 +5695,10 @@ mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign #if defined(TARGET_X86) || defined(TARGET_AMD64) EMIT_NEW_X86_LEA (cfg, ins, args [0]->dreg, index_reg, 1, MONO_STRUCT_OFFSET (MonoString, chars)); add_reg = ins->dreg; - /* Avoid a warning */ - mult_reg = 0; EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADU2_MEMBASE, dreg, add_reg, 0); #else + int mult_reg = alloc_preg (cfg); MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SHL_IMM, mult_reg, index_reg, 1); MONO_EMIT_NEW_BIALU (cfg, OP_PADD, add_reg, mult_reg, args [0]->dreg); EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADU2_MEMBASE, dreg, @@ -5706,7 +5909,7 @@ mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign } if (opcode == OP_LOADI8_MEMBASE) - ins = mono_decompose_opcode (cfg, ins); + ins = mono_decompose_opcode (cfg, ins, NULL); emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_ACQ); @@ -5741,7 +5944,7 @@ mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign MONO_ADD_INS (cfg->cbb, ins); if (opcode == OP_STOREI8_MEMBASE_REG) - ins = mono_decompose_opcode (cfg, ins); + ins = mono_decompose_opcode (cfg, ins, NULL); return ins; } @@ -6066,7 +6269,7 @@ mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign ins->sreg3 = is_float ? f2i_cmp->dreg : args [2]->dreg; MONO_ADD_INS (cfg->cbb, ins); - switch (fsig->params [0]->type) { + switch (fsig->params [1]->type) { case MONO_TYPE_I4: ins->type = STACK_I4; break; @@ -6085,7 +6288,7 @@ mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign ins->type = STACK_R8; break; default: - g_assert (mini_type_is_reference (cfg, fsig->params [0])); + g_assert (mini_type_is_reference (cfg, fsig->params [1])); ins->type = STACK_OBJ; break; } @@ -6378,7 +6581,7 @@ mini_redirect_call (MonoCompile *cfg, MonoMethod *method, g_assert (vtable); /*Should not fail since it System.String*/ #ifndef MONO_CROSS_COMPILE - managed_alloc = mono_gc_get_managed_allocator (method->klass, FALSE); + managed_alloc = mono_gc_get_managed_allocator (method->klass, FALSE, FALSE); #endif if (!managed_alloc) return NULL; @@ -6487,7 +6690,7 @@ emit_init_rvar (MonoCompile *cfg, int dreg, MonoType *rtype) MonoInst *ins; int t; - rtype = mini_replace_type (rtype); + rtype = mini_get_underlying_type (cfg, rtype); t = rtype->type; if (rtype->byref) { @@ -6523,7 +6726,7 @@ emit_dummy_init_rvar (MonoCompile *cfg, int dreg, MonoType *rtype) { int t; - rtype = mini_replace_type (rtype); + rtype = mini_get_underlying_type (cfg, rtype); t = rtype->type; if (rtype->byref) { @@ -6601,6 +6804,9 @@ inline_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, return 0; #endif + if (!fsig) + fsig = mono_method_signature (cmethod); + if (cfg->verbose_level > 2) printf ("INLINE START %p %s -> %s\n", cmethod, mono_method_full_name (cfg->method, TRUE), mono_method_full_name (cmethod, TRUE)); @@ -6666,7 +6872,7 @@ inline_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, prev_ret_var_set = cfg->ret_var_set; prev_disable_inline = cfg->disable_inline; - if (*ip == CEE_CALLVIRT && !(cmethod->flags & METHOD_ATTRIBUTE_STATIC)) + if (ip && *ip == CEE_CALLVIRT && !(cmethod->flags & METHOD_ATTRIBUTE_STATIC)) virtual = TRUE; costs = mono_method_to_ir (cfg, cmethod, sbblock, ebblock, rvar, sp, real_offset, virtual); @@ -6740,7 +6946,8 @@ inline_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, cfg->cbb = ebblock; } - *out_cbb = cfg->cbb; + if (out_cbb) + *out_cbb = cfg->cbb; if (rvar) { /* @@ -7222,7 +7429,7 @@ emit_optimized_ldloca_ir (MonoCompile *cfg, unsigned char *ip, unsigned char *en token = read32 (ip + 2); klass = mini_get_class (cfg->current_method, token, cfg->generic_context); CHECK_TYPELOAD (klass); - type = mini_replace_type (&klass->byval_arg); + type = mini_get_underlying_type (cfg, &klass->byval_arg); emit_init_local (cfg, local, type, TRUE); return ip + 6; } @@ -7275,13 +7482,11 @@ is_jit_optimizer_disabled (MonoMethod *m) for (i = 0; i < attrs->num_attrs; ++i) { MonoCustomAttrEntry *attr = &attrs->attrs [i]; const gchar *p; - int len; MonoMethodSignature *sig; if (!attr->ctor || attr->ctor->klass != klass) continue; /* Decode the attribute. See reflection.c */ - len = attr->data_size; p = (const char*)attr->data; g_assert (read16 (p) == 0x0001); p += 2; @@ -7527,7 +7732,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b MonoImage *image; guint32 token, ins_flag; MonoClass *klass; - MonoClass *constrained_call = NULL; + MonoClass *constrained_class = NULL; unsigned char *ip, *end, *target, *err_pos; MonoMethodSignature *sig; MonoGenericContext *generic_context = NULL; @@ -8514,14 +8719,117 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b start_new_bblock = 1; break; } - case CEE_CALLI: + case CEE_CALLI: { + MonoInst *addr; + MonoMethodSignature *fsig; + + CHECK_OPSIZE (5); + token = read32 (ip + 1); + + ins = NULL; + + //GSHAREDVT_FAILURE (*ip); + cmethod = NULL; + CHECK_STACK (1); + --sp; + addr = *sp; + fsig = mini_get_signature (method, token, generic_context); + + if (method->dynamic && fsig->pinvoke) { + MonoInst *args [3]; + + /* + * This is a call through a function pointer using a pinvoke + * signature. Have to create a wrapper and call that instead. + * FIXME: This is very slow, need to create a wrapper at JIT time + * instead based on the signature. + */ + EMIT_NEW_IMAGECONST (cfg, args [0], method->klass->image); + EMIT_NEW_PCONST (cfg, args [1], fsig); + args [2] = addr; + addr = mono_emit_jit_icall (cfg, mono_get_native_calli_wrapper, args); + } + + n = fsig->param_count + fsig->hasthis; + + CHECK_STACK (n); + + //g_assert (!virtual || fsig->hasthis); + + sp -= n; + + inline_costs += 10 * num_calls++; + + /* + * Making generic calls out of gsharedvt methods. + * This needs to be used for all generic calls, not just ones with a gsharedvt signature, to avoid + * patching gshared method addresses into a gsharedvt method. + */ + if (cfg->gsharedvt && mini_is_gsharedvt_signature (cfg, fsig)) { + /* + * We pass the address to the gsharedvt trampoline in the rgctx reg + */ + MonoInst *callee = addr; + + if (method->wrapper_type != MONO_WRAPPER_DELEGATE_INVOKE) + /* Not tested */ + GSHAREDVT_FAILURE (*ip); + + addr = emit_get_rgctx_sig (cfg, context_used, + fsig, MONO_RGCTX_INFO_SIG_GSHAREDVT_OUT_TRAMPOLINE_CALLI); + ins = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr, NULL, callee); + goto calli_end; + } + + /* Prevent inlining of methods with indirect calls */ + INLINE_FAILURE ("indirect call"); + + if (addr->opcode == OP_PCONST || addr->opcode == OP_AOTCONST || addr->opcode == OP_GOT_ENTRY) { + int info_type; + gpointer info_data; + + /* + * Instead of emitting an indirect call, emit a direct call + * with the contents of the aotconst as the patch info. + */ + if (addr->opcode == OP_PCONST || addr->opcode == OP_AOTCONST) { + info_type = addr->inst_c1; + info_data = addr->inst_p0; + } else { + info_type = addr->inst_right->inst_c1; + info_data = addr->inst_right->inst_left; + } + + if (info_type == MONO_PATCH_INFO_ICALL_ADDR || info_type == MONO_PATCH_INFO_JIT_ICALL_ADDR) { + ins = (MonoInst*)mono_emit_abs_call (cfg, info_type, info_data, fsig, sp); + NULLIFY_INS (addr); + goto calli_end; + } + } + ins = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr, NULL, NULL); + + calli_end: + + /* End of call, INS should contain the result of the call, if any */ + + if (!MONO_TYPE_IS_VOID (fsig->ret)) { + g_assert (ins); + *sp++ = mono_emit_widen_call_res (cfg, ins, fsig); + } + + CHECK_CFG_EXCEPTION; + + ip += 5; + ins_flag = 0; + constrained_class = NULL; + break; + } case CEE_CALL: case CEE_CALLVIRT: { MonoInst *addr = NULL; MonoMethodSignature *fsig = NULL; int array_rank = 0; int virtual = *ip == CEE_CALLVIRT; - int calli = *ip == CEE_CALLI; gboolean pass_imt_from_rgctx = FALSE; MonoInst *imt_arg = NULL; MonoInst *keep_this_alive = NULL; @@ -8537,158 +8845,139 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b gboolean push_res = TRUE; gboolean skip_ret = FALSE; gboolean delegate_invoke = FALSE; + gboolean direct_icall = FALSE; + MonoMethod *cil_method; CHECK_OPSIZE (5); token = read32 (ip + 1); ins = NULL; - if (calli) { - //GSHAREDVT_FAILURE (*ip); - cmethod = NULL; - CHECK_STACK (1); - --sp; - addr = *sp; - fsig = mini_get_signature (method, token, generic_context); - n = fsig->param_count + fsig->hasthis; - - if (method->dynamic && fsig->pinvoke) { - MonoInst *args [3]; - - /* - * This is a call through a function pointer using a pinvoke - * signature. Have to create a wrapper and call that instead. - * FIXME: This is very slow, need to create a wrapper at JIT time - * instead based on the signature. - */ - EMIT_NEW_IMAGECONST (cfg, args [0], method->klass->image); - EMIT_NEW_PCONST (cfg, args [1], fsig); - args [2] = addr; - addr = mono_emit_jit_icall (cfg, mono_get_native_calli_wrapper, args); - } - } else { - MonoMethod *cil_method; - - cmethod = mini_get_method (cfg, method, token, NULL, generic_context); - cil_method = cmethod; + cmethod = mini_get_method (cfg, method, token, NULL, generic_context); + cil_method = cmethod; - if (constrained_call) { - if (method->wrapper_type != MONO_WRAPPER_NONE) { - if (cfg->verbose_level > 2) - printf ("DM Constrained call to %s\n", mono_type_get_full_name (constrained_call)); - if (!((constrained_call->byval_arg.type == MONO_TYPE_VAR || - constrained_call->byval_arg.type == MONO_TYPE_MVAR) && - cfg->generic_sharing_context)) { - cmethod = mono_get_method_constrained_with_method (image, cil_method, constrained_call, generic_context, &cfg->error); - CHECK_CFG_ERROR; - } - } else { - if (cfg->verbose_level > 2) - printf ("Constrained call to %s\n", mono_type_get_full_name (constrained_call)); - - 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. - */ - if (!mini_is_gsharedvt_klass (cfg, constrained_call)) - g_assert (!cmethod->klass->valuetype); - } else { - cmethod = mono_get_method_constrained_checked (image, token, constrained_call, generic_context, &cil_method, &cfg->error); - CHECK_CFG_ERROR; + if (constrained_class) { + 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 (!cmethod || mono_loader_get_last_error ()) - LOAD_ERROR; - if (!dont_verify && !cfg->skip_visibility) { - MonoMethod *target_method = cil_method; - if (method->is_inflated) { - target_method = mini_get_method_allow_open (method, token, NULL, &(mono_method_get_generic_container (method_definition)->context)); - } - if (!mono_method_can_access_method (method_definition, target_method) && - !mono_method_can_access_method (method, cil_method)) - METHOD_ACCESS_FAILURE (method, cil_method); - } - - if (mono_security_core_clr_enabled ()) - ensure_method_is_allowed_to_call_method (cfg, method, cil_method, bblock, ip); - if (!virtual && (cmethod->flags & METHOD_ATTRIBUTE_ABSTRACT)) - /* MS.NET seems to silently convert this to a callvirt */ - virtual = 1; - - { - /* - * MS.NET accepts non virtual calls to virtual final methods of transparent proxy classes and - * converts to a callvirt. - * - * tests/bug-515884.il is an example of this behavior - */ - const int test_flags = METHOD_ATTRIBUTE_VIRTUAL | METHOD_ATTRIBUTE_FINAL | METHOD_ATTRIBUTE_STATIC; - const int expected_flags = METHOD_ATTRIBUTE_VIRTUAL | METHOD_ATTRIBUTE_FINAL; - if (!virtual && mono_class_is_marshalbyref (cmethod->klass) && (cmethod->flags & test_flags) == expected_flags && cfg->method->wrapper_type == MONO_WRAPPER_NONE) - virtual = 1; - } - - if (!cmethod->klass->inited) - if (!mono_class_init (cmethod->klass)) - TYPE_LOAD_ERROR (cmethod->klass); - - if (cmethod->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL && - mini_class_is_system_array (cmethod->klass)) { - array_rank = cmethod->klass->rank; - fsig = mono_method_signature (cmethod); + if (method->wrapper_type != MONO_WRAPPER_NONE) { + if (cfg->verbose_level > 2) + printf ("DM Constrained call to %s\n", mono_type_get_full_name (constrained_class)); + if (!((constrained_class->byval_arg.type == MONO_TYPE_VAR || + constrained_class->byval_arg.type == MONO_TYPE_MVAR) && + cfg->generic_sharing_context)) { + cmethod = mono_get_method_constrained_with_method (image, cil_method, constrained_class, generic_context, &cfg->error); + CHECK_CFG_ERROR; + } } else { - fsig = mono_method_signature (cmethod); - - if (!fsig) - LOAD_ERROR; + if (cfg->verbose_level > 2) + printf ("Constrained call to %s\n", mono_type_get_full_name (constrained_class)); - if (fsig->pinvoke) { - MonoMethod *wrapper = mono_marshal_get_native_wrapper (cmethod, - check_for_pending_exc, cfg->compile_aot); - fsig = mono_method_signature (wrapper); - } else if (constrained_call) { - fsig = mono_method_signature (cmethod); + if ((constrained_class->byval_arg.type == MONO_TYPE_VAR || constrained_class->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. + */ + if (!mini_is_gsharedvt_klass (cfg, constrained_class)) + g_assert (!cmethod->klass->valuetype); } else { - fsig = mono_method_get_signature_checked (cmethod, image, token, generic_context, &cfg->error); + cmethod = mono_get_method_constrained_checked (image, token, constrained_class, generic_context, &cil_method, &cfg->error); CHECK_CFG_ERROR; } } + } + + if (!cmethod || mono_loader_get_last_error ()) + LOAD_ERROR; + if (!dont_verify && !cfg->skip_visibility) { + MonoMethod *target_method = cil_method; + if (method->is_inflated) { + target_method = mini_get_method_allow_open (method, token, NULL, &(mono_method_get_generic_container (method_definition)->context)); + } + if (!mono_method_can_access_method (method_definition, target_method) && + !mono_method_can_access_method (method, cil_method)) + METHOD_ACCESS_FAILURE (method, cil_method); + } - mono_save_token_info (cfg, image, token, cil_method); - - if (!(seq_point_locs && mono_bitset_test_fast (seq_point_locs, ip + 5 - header->code))) - need_seq_point = TRUE; + if (mono_security_core_clr_enabled ()) + ensure_method_is_allowed_to_call_method (cfg, method, cil_method, bblock, ip); - n = fsig->param_count + fsig->hasthis; + if (!virtual && (cmethod->flags & METHOD_ATTRIBUTE_ABSTRACT)) + /* MS.NET seems to silently convert this to a callvirt */ + virtual = 1; - /* Don't support calls made using type arguments for now */ + { /* - if (cfg->gsharedvt) { - if (mini_is_gsharedvt_signature (cfg, fsig)) - GSHAREDVT_FAILURE (*ip); - } - */ + * MS.NET accepts non virtual calls to virtual final methods of transparent proxy classes and + * converts to a callvirt. + * + * tests/bug-515884.il is an example of this behavior + */ + const int test_flags = METHOD_ATTRIBUTE_VIRTUAL | METHOD_ATTRIBUTE_FINAL | METHOD_ATTRIBUTE_STATIC; + const int expected_flags = METHOD_ATTRIBUTE_VIRTUAL | METHOD_ATTRIBUTE_FINAL; + if (!virtual && mono_class_is_marshalbyref (cmethod->klass) && (cmethod->flags & test_flags) == expected_flags && cfg->method->wrapper_type == MONO_WRAPPER_NONE) + virtual = 1; + } - if (mono_security_cas_enabled ()) { - if (check_linkdemand (cfg, method, cmethod)) - INLINE_FAILURE ("linkdemand"); - CHECK_CFG_EXCEPTION; - } + if (!cmethod->klass->inited) + if (!mono_class_init (cmethod->klass)) + TYPE_LOAD_ERROR (cmethod->klass); - if (cmethod->string_ctor && method->wrapper_type != MONO_WRAPPER_RUNTIME_INVOKE) - g_assert_not_reached (); + fsig = mono_method_signature (cmethod); + if (!fsig) + LOAD_ERROR; + if (cmethod->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL && + mini_class_is_system_array (cmethod->klass)) { + array_rank = cmethod->klass->rank; + } else if ((cmethod->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) && icall_is_direct_callable (cfg, cmethod)) { + direct_icall = TRUE; + } else if (fsig->pinvoke) { + MonoMethod *wrapper = mono_marshal_get_native_wrapper (cmethod, + check_for_pending_exc, cfg->compile_aot); + fsig = mono_method_signature (wrapper); + } else if (constrained_class) { + } else { + fsig = mono_method_get_signature_checked (cmethod, image, token, generic_context, &cfg->error); + CHECK_CFG_ERROR; } - if (!cfg->generic_sharing_context && cmethod && cmethod->klass->generic_container) + mono_save_token_info (cfg, image, token, cil_method); + + if (!(seq_point_locs && mono_bitset_test_fast (seq_point_locs, ip + 5 - header->code))) + need_seq_point = TRUE; + + /* Don't support calls made using type arguments for now */ + /* + if (cfg->gsharedvt) { + if (mini_is_gsharedvt_signature (cfg, fsig)) + GSHAREDVT_FAILURE (*ip); + } + */ + + if (mono_security_cas_enabled ()) { + if (check_linkdemand (cfg, method, cmethod)) + INLINE_FAILURE ("linkdemand"); + CHECK_CFG_EXCEPTION; + } + + if (cmethod->string_ctor && method->wrapper_type != MONO_WRAPPER_RUNTIME_INVOKE) + g_assert_not_reached (); + + n = fsig->param_count + fsig->hasthis; + + if (!cfg->generic_sharing_context && cmethod->klass->generic_container) UNVERIFIED; - if (!cfg->generic_sharing_context && cmethod) + if (!cfg->generic_sharing_context) g_assert (!mono_method_check_context_used (cmethod)); CHECK_STACK (n); @@ -8697,96 +8986,34 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b sp -= n; - if (constrained_call) { - if (mini_is_gsharedvt_klass (cfg, constrained_call)) { - /* - * Constrained calls need to behave differently at runtime dependending on whenever the receiver is instantiated as ref type or as a vtype. - */ - if ((cmethod->klass != mono_defaults.object_class) && constrained_call->valuetype && cmethod->klass->valuetype) { + if (constrained_class) { + if (mini_is_gsharedvt_klass (cfg, constrained_class)) { + if ((cmethod->klass != mono_defaults.object_class) && constrained_class->valuetype && cmethod->klass->valuetype) { /* The 'Own method' case below */ } else if (cmethod->klass->image != mono_defaults.corlib && !(cmethod->klass->flags & TYPE_ATTRIBUTE_INTERFACE) && !cmethod->klass->valuetype) { /* 'The type parameter is instantiated as a reference type' case below. */ - } else if (((cmethod->klass == mono_defaults.object_class) || (cmethod->klass->flags & TYPE_ATTRIBUTE_INTERFACE) || (!cmethod->klass->valuetype && cmethod->klass->image != mono_defaults.corlib)) && - (MONO_TYPE_IS_VOID (fsig->ret) || MONO_TYPE_IS_PRIMITIVE (fsig->ret) || MONO_TYPE_IS_REFERENCE (fsig->ret) || MONO_TYPE_ISSTRUCT (fsig->ret) || mini_is_gsharedvt_type (cfg, fsig->ret)) && - (fsig->param_count == 0 || (!fsig->hasthis && fsig->param_count == 1) || (fsig->param_count == 1 && (MONO_TYPE_IS_REFERENCE (fsig->params [0]) || mini_is_gsharedvt_type (cfg, fsig->params [0]))))) { - MonoInst *args [16]; - - /* - * This case handles calls to - * - object:ToString()/Equals()/GetHashCode(), - * - System.IComparable:CompareTo() - * - System.IEquatable:Equals () - * plus some simple interface calls enough to support AsyncTaskMethodBuilder. - */ - - args [0] = sp [0]; - if (mono_method_check_context_used (cmethod)) - args [1] = emit_get_rgctx_method (cfg, mono_method_check_context_used (cmethod), cmethod, MONO_RGCTX_INFO_METHOD); - else - EMIT_NEW_METHODCONST (cfg, args [1], cmethod); - args [2] = emit_get_rgctx_klass (cfg, mono_class_check_context_used (constrained_call), constrained_call, MONO_RGCTX_INFO_KLASS); - - /* !fsig->hasthis is for the wrapper for the Object.GetType () icall */ - if (fsig->hasthis && fsig->param_count) { - /* Pass the arguments using a localloc-ed array using the format expected by runtime_invoke () */ - MONO_INST_NEW (cfg, ins, OP_LOCALLOC_IMM); - ins->dreg = alloc_preg (cfg); - ins->inst_imm = fsig->param_count * sizeof (mgreg_t); - MONO_ADD_INS (cfg->cbb, ins); - args [4] = ins; - - if (mini_is_gsharedvt_type (cfg, fsig->params [0])) { - int addr_reg; - - args [3] = emit_get_gsharedvt_info_klass (cfg, mono_class_from_mono_type (fsig->params [0]), MONO_RGCTX_INFO_CLASS_BOX_TYPE); - - EMIT_NEW_VARLOADA_VREG (cfg, ins, sp [1]->dreg, fsig->params [0]); - addr_reg = ins->dreg; - EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, args [4]->dreg, 0, addr_reg); - } else { - EMIT_NEW_ICONST (cfg, args [3], 0); - EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, args [4]->dreg, 0, sp [1]->dreg); - } - } else { - EMIT_NEW_ICONST (cfg, args [3], 0); - EMIT_NEW_ICONST (cfg, args [4], 0); - } - ins = mono_emit_jit_icall (cfg, mono_gsharedvt_constrained_call, args); - emit_widen = FALSE; - - if (mini_is_gsharedvt_type (cfg, fsig->ret)) { - ins = handle_unbox_gsharedvt (cfg, mono_class_from_mono_type (fsig->ret), ins, &bblock); - } else if (MONO_TYPE_IS_PRIMITIVE (fsig->ret) || MONO_TYPE_ISSTRUCT (fsig->ret)) { - MonoInst *add; - - /* Unbox */ - NEW_BIALU_IMM (cfg, add, OP_ADD_IMM, alloc_dreg (cfg, STACK_MP), ins->dreg, sizeof (MonoObject)); - MONO_ADD_INS (cfg->cbb, add); - /* Load value */ - NEW_LOAD_MEMBASE_TYPE (cfg, ins, fsig->ret, add->dreg, 0); - MONO_ADD_INS (cfg->cbb, ins); - /* ins represents the call result */ - } - - goto call_end; } else { - GSHAREDVT_FAILURE (*ip); + ins = handle_constrained_gsharedvt_call (cfg, cmethod, fsig, sp, constrained_class, &emit_widen, &bblock); + CHECK_CFG_EXCEPTION; + g_assert (ins); + goto call_end; } } + /* * We have the `constrained.' prefix opcode. */ - if (constrained_call->valuetype && (cmethod->klass == mono_defaults.object_class || cmethod->klass == mono_defaults.enum_class->parent || cmethod->klass == mono_defaults.enum_class)) { + 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 * calling, so we need to box `this'. */ - EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &constrained_call->byval_arg, sp [0]->dreg, 0); - ins->klass = constrained_call; - sp [0] = handle_box (cfg, ins, constrained_call, mono_class_check_context_used (constrained_call), &bblock); + 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); CHECK_CFG_EXCEPTION; - } else if (!constrained_call->valuetype) { + } else if (!constrained_class->valuetype) { int dreg = alloc_ireg_ref (cfg); /* @@ -8804,38 +9031,38 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b /* Interface method */ int ioffset, slot; - mono_class_setup_vtable (constrained_call); - CHECK_TYPELOAD (constrained_call); - ioffset = mono_class_interface_offset (constrained_call, cmethod->klass); + mono_class_setup_vtable (constrained_class); + CHECK_TYPELOAD (constrained_class); + ioffset = mono_class_interface_offset (constrained_class, cmethod->klass); if (ioffset == -1) - TYPE_LOAD_ERROR (constrained_call); + TYPE_LOAD_ERROR (constrained_class); slot = mono_method_get_vtable_slot (cmethod); if (slot == -1) TYPE_LOAD_ERROR (cmethod->klass); - cmethod = constrained_call->vtable [ioffset + slot]; + cmethod = constrained_class->vtable [ioffset + slot]; if (cmethod->klass == mono_defaults.enum_class) { /* Enum implements some interfaces, so treat this as the first case */ - EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &constrained_call->byval_arg, sp [0]->dreg, 0); - ins->klass = constrained_call; - sp [0] = handle_box (cfg, ins, constrained_call, mono_class_check_context_used (constrained_call), &bblock); + 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); CHECK_CFG_EXCEPTION; } } virtual = 0; } - constrained_call = NULL; + constrained_class = NULL; } - if (!calli && check_call_signature (cfg, fsig, sp)) + if (check_call_signature (cfg, fsig, sp)) UNVERIFIED; #ifdef MONO_ARCH_HAVE_CREATE_DELEGATE_TRAMPOLINE - if (cmethod && (cmethod->klass->parent == mono_defaults.multicastdelegate_class) && !strcmp (cmethod->name, "Invoke")) + if ((cmethod->klass->parent == mono_defaults.multicastdelegate_class) && !strcmp (cmethod->name, "Invoke")) delegate_invoke = TRUE; #endif - if (cmethod && (cfg->opt & MONO_OPT_INTRINS) && (ins = mini_emit_inst_for_sharable_method (cfg, cmethod, fsig, sp))) { + if ((cfg->opt & MONO_OPT_INTRINS) && (ins = mini_emit_inst_for_sharable_method (cfg, cmethod, fsig, sp))) { bblock = cfg->cbb; if (!MONO_TYPE_IS_VOID (fsig->ret)) { type_to_eval_stack_type ((cfg), fsig->ret, ins); @@ -8849,15 +9076,14 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b * If the callee is a shared method, then its static cctor * might not get called after the call was patched. */ - if (cfg->generic_sharing_context && cmethod && cmethod->klass != method->klass && cmethod->klass->generic_class && mono_method_is_generic_sharable (cmethod, TRUE) && mono_class_needs_cctor_run (cmethod->klass, method)) { + if (cfg->generic_sharing_context && cmethod->klass != method->klass && cmethod->klass->generic_class && mono_method_is_generic_sharable (cmethod, TRUE) && mono_class_needs_cctor_run (cmethod->klass, method)) { emit_generic_class_init (cfg, cmethod->klass); CHECK_TYPELOAD (cmethod->klass); } - if (cmethod) - check_method_sharing (cfg, cmethod, &pass_vtable, &pass_mrgctx); + check_method_sharing (cfg, cmethod, &pass_vtable, &pass_mrgctx); - if (cfg->generic_sharing_context && cmethod) { + if (cfg->generic_sharing_context) { MonoGenericContext *cmethod_context = mono_method_get_context (cmethod); context_used = mini_method_check_context_used (cfg, cmethod); @@ -8924,7 +9150,6 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b if (pass_imt_from_rgctx) { g_assert (!pass_vtable); - g_assert (cmethod); imt_arg = emit_get_rgctx_method (cfg, context_used, cmethod, MONO_RGCTX_INFO_METHOD); @@ -8934,8 +9159,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b MONO_EMIT_NEW_CHECK_THIS (cfg, sp [0]->dreg); /* Calling virtual generic methods */ - if (cmethod && virtual && - (cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL) && + if (virtual && (cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL) && !(MONO_METHOD_IS_FINAL (cmethod) && cmethod->wrapper_type != MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK) && fsig->generic_param_count && @@ -9000,7 +9224,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b * To work around this, we extend such try blocks to include the last x bytes * of the Monitor.Enter () call. */ - if (cmethod && cmethod->klass == mono_defaults.monitor_class && !strcmp (cmethod->name, "Enter") && mono_method_signature (cmethod)->param_count == 1) { + if (cmethod->klass == mono_defaults.monitor_class && !strcmp (cmethod->name, "Enter") && mono_method_signature (cmethod)->param_count == 1) { MonoBasicBlock *tbb; GET_BBLOCK (cfg, tbb, ip + 5); @@ -9015,7 +9239,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b } /* Conversion to a JIT intrinsic */ - if (cmethod && (cfg->opt & MONO_OPT_INTRINS) && (ins = mini_emit_inst_for_method (cfg, cmethod, fsig, sp))) { + if ((cfg->opt & MONO_OPT_INTRINS) && (ins = mini_emit_inst_for_method (cfg, cmethod, fsig, sp))) { bblock = cfg->cbb; if (!MONO_TYPE_IS_VOID (fsig->ret)) { type_to_eval_stack_type ((cfg), fsig->ret, ins); @@ -9025,7 +9249,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b } /* Inlining */ - if (cmethod && (cfg->opt & MONO_OPT_INLINE) && + if ((cfg->opt & MONO_OPT_INLINE) && (!virtual || !(cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL) || MONO_METHOD_IS_FINAL (cmethod)) && mono_method_check_inlining (cfg, cmethod)) { int costs; @@ -9094,7 +9318,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b * This needs to be used for all generic calls, not just ones with a gsharedvt signature, to avoid * patching gshared method addresses into a gsharedvt method. */ - if (cmethod && cfg->gsharedvt && (mini_is_gsharedvt_signature (cfg, fsig) || cmethod->is_inflated || cmethod->klass->generic_class)) { + if (cfg->gsharedvt && (mini_is_gsharedvt_signature (cfg, fsig) || cmethod->is_inflated || cmethod->klass->generic_class) && + !(cmethod->klass->rank && cmethod->klass->byval_arg.type != MONO_TYPE_SZARRAY)) { MonoRgctxInfoType info_type; if (virtual) { @@ -9120,10 +9345,6 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b } } - if (cmethod->klass->rank && cmethod->klass->byval_arg.type != MONO_TYPE_SZARRAY) - /* test_0_multi_dim_arrays () in gshared.cs */ - GSHAREDVT_FAILURE (*ip); - if ((cmethod->klass->parent == mono_defaults.multicastdelegate_class) && (!strcmp (cmethod->name, "Invoke"))) keep_this_alive = sp [0]; @@ -9135,20 +9356,6 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b ins = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr, imt_arg, vtable_arg); goto call_end; - } else if (calli && cfg->gsharedvt && mini_is_gsharedvt_signature (cfg, fsig)) { - /* - * We pass the address to the gsharedvt trampoline in the rgctx reg - */ - MonoInst *callee = addr; - - if (method->wrapper_type != MONO_WRAPPER_DELEGATE_INVOKE) - /* Not tested */ - GSHAREDVT_FAILURE (*ip); - - addr = emit_get_rgctx_sig (cfg, context_used, - fsig, MONO_RGCTX_INFO_SIG_GSHAREDVT_OUT_TRAMPOLINE_CALLI); - ins = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr, NULL, callee); - goto call_end; } /* Generic sharing */ @@ -9183,43 +9390,26 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b goto call_end; } - /* Indirect calls */ - if (addr) { - if (call_opcode == CEE_CALL) - g_assert (context_used); - else if (call_opcode == CEE_CALLI) - g_assert (!vtable_arg); - else - /* FIXME: what the hell is this??? */ - g_assert (cmethod->flags & METHOD_ATTRIBUTE_FINAL || - !(cmethod->flags & METHOD_ATTRIBUTE_FINAL)); + /* Direct calls to icalls */ + if (direct_icall) { + MonoMethod *wrapper; + int costs; - /* Prevent inlining of methods with indirect calls */ - INLINE_FAILURE ("indirect call"); + /* Inline the wrapper */ + wrapper = mono_marshal_get_native_wrapper (cmethod, TRUE, cfg->compile_aot); - if (addr->opcode == OP_PCONST || addr->opcode == OP_AOTCONST || addr->opcode == OP_GOT_ENTRY) { - int info_type; - gpointer info_data; + costs = inline_method (cfg, wrapper, fsig, sp, ip, cfg->real_offset, TRUE, &bblock); + g_assert (costs > 0); + cfg->real_offset += 5; - /* - * Instead of emitting an indirect call, emit a direct call - * with the contents of the aotconst as the patch info. - */ - if (addr->opcode == OP_PCONST || addr->opcode == OP_AOTCONST) { - info_type = addr->inst_c1; - info_data = addr->inst_p0; - } else { - info_type = addr->inst_right->inst_c1; - info_data = addr->inst_right->inst_left; - } - - if (info_type == MONO_PATCH_INFO_ICALL_ADDR || info_type == MONO_PATCH_INFO_JIT_ICALL_ADDR) { - ins = (MonoInst*)mono_emit_abs_call (cfg, info_type, info_data, fsig, sp); - NULLIFY_INS (addr); - goto call_end; - } + if (!MONO_TYPE_IS_VOID (fsig->ret)) { + /* *sp is already set by inline_method */ + sp++; + push_res = FALSE; } - ins = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr, imt_arg, vtable_arg); + + inline_costs += costs; + goto call_end; } @@ -9243,6 +9433,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, fsig->params [fsig->param_count - 1], addr->dreg, 0, val->dreg); if (cfg->gen_write_barriers && val->type == STACK_OBJ && !(val->opcode == OP_PCONST && val->inst_c0 == 0)) emit_write_barrier (cfg, addr, val); + if (cfg->gen_write_barriers && mini_is_gsharedvt_klass (cfg, cmethod->klass)) + GSHAREDVT_FAILURE (*ip); } else if (strcmp (cmethod->name, "Get") == 0) { /* array Get */ addr = mini_emit_ldelema_ins (cfg, cmethod, sp, ip, FALSE); @@ -9272,7 +9464,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b /* FIXME: Enabling TAILC breaks some inlining/stack trace/etc tests */ /* FIXME: runtime generic context pointer for jumps? */ /* FIXME: handle this for generic sharing eventually */ - if (cmethod && (ins_flag & MONO_INST_TAILCALL) && + if ((ins_flag & MONO_INST_TAILCALL) && !vtable_arg && !cfg->generic_sharing_context && is_supported_tail_call (cfg, method, cmethod, fsig, call_opcode)) supported_tail_call = TRUE; @@ -9388,7 +9580,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b ip += 1; } ins_flag = 0; - constrained_call = NULL; + constrained_class = NULL; if (need_seq_point) emit_seq_point (cfg, method, ip, FALSE, TRUE); break; @@ -9423,7 +9615,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b emit_pop_lmf (cfg); if (cfg->ret) { - MonoType *ret_type = mini_replace_type (mono_method_signature (method)->ret); + MonoType *ret_type = mini_get_underlying_type (cfg, mono_method_signature (method)->ret); if (seq_points && !sym_seq_points) { /* @@ -9835,7 +10027,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b MONO_ADD_INS ((cfg)->cbb, (ins)); - *sp++ = mono_decompose_opcode (cfg, ins); + *sp++ = mono_decompose_opcode (cfg, ins, &bblock); ip++; break; case CEE_ADD: @@ -9858,7 +10050,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b ins->sreg2 = sp [1]->dreg; type_from_op (cfg, ins, sp [0], sp [1]); CHECK_TYPE (ins); - ADD_WIDEN_OP (ins, sp [0], sp [1]); + add_widen_op (cfg, ins, &sp [0], &sp [1]); ins->dreg = alloc_dreg ((cfg), (ins)->type); /* FIXME: Pass opcode to is_inst_imm */ @@ -9888,14 +10080,14 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b ins->inst_imm = (gssize)(sp [1]->inst_c0); ins->sreg2 = -1; - /* Might be followed by an instruction added by ADD_WIDEN_OP */ + /* Might be followed by an instruction added by add_widen_op */ if (sp [1]->next == NULL) NULLIFY_INS (sp [1]); } } MONO_ADD_INS ((cfg)->cbb, (ins)); - *sp++ = mono_decompose_opcode (cfg, ins); + *sp++ = mono_decompose_opcode (cfg, ins, &bblock); ip++; break; case CEE_NEG: @@ -10376,6 +10568,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; @@ -10384,10 +10577,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; @@ -10430,7 +10625,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b token = read32 (ip + 1); klass = mini_get_class (method, token, generic_context); CHECK_TYPELOAD (klass); - + mono_save_token_info (cfg, image, token, klass); context_used = mini_class_check_context_used (cfg, klass); @@ -11249,7 +11444,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b ins->type = STACK_I4; ins->dreg = alloc_ireg (cfg); MONO_ADD_INS (cfg->cbb, ins); - *sp = mono_decompose_opcode (cfg, ins); + *sp = mono_decompose_opcode (cfg, ins, &bblock); } if (context_used) { @@ -11474,7 +11669,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b ins->type = STACK_R8; MONO_ADD_INS (bblock, ins); - *sp++ = mono_decompose_opcode (cfg, ins); + *sp++ = mono_decompose_opcode (cfg, ins, &bblock); ++ip; break; @@ -11868,6 +12063,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; @@ -11876,13 +12114,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; @@ -12225,8 +12456,13 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b case CEE_CGT_UN: case CEE_CLT: case CEE_CLT_UN: { - MonoInst *cmp; + MonoInst *cmp, *arg1, *arg2; + CHECK_STACK (2); + sp -= 2; + arg1 = sp [0]; + arg2 = sp [1]; + /* * The following transforms: * CEE_CEQ into OP_CEQ @@ -12236,25 +12472,25 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b * CEE_CLT_UN into OP_CLT_UN */ MONO_INST_NEW (cfg, cmp, (OP_CEQ - CEE_CEQ) + ip [1]); - + MONO_INST_NEW (cfg, ins, cmp->opcode); - sp -= 2; - cmp->sreg1 = sp [0]->dreg; - cmp->sreg2 = sp [1]->dreg; - type_from_op (cfg, cmp, sp [0], sp [1]); + cmp->sreg1 = arg1->dreg; + cmp->sreg2 = arg2->dreg; + type_from_op (cfg, cmp, arg1, arg2); CHECK_TYPE (cmp); - if ((sp [0]->type == STACK_I8) || ((SIZEOF_VOID_P == 8) && ((sp [0]->type == STACK_PTR) || (sp [0]->type == STACK_OBJ) || (sp [0]->type == STACK_MP)))) + add_widen_op (cfg, cmp, &arg1, &arg2); + if ((arg1->type == STACK_I8) || ((SIZEOF_VOID_P == 8) && ((arg1->type == STACK_PTR) || (arg1->type == STACK_OBJ) || (arg1->type == STACK_MP)))) cmp->opcode = OP_LCOMPARE; - else if (sp [0]->type == STACK_R4) + else if (arg1->type == STACK_R4) cmp->opcode = OP_RCOMPARE; - else if (sp [0]->type == STACK_R8) + else if (arg1->type == STACK_R8) cmp->opcode = OP_FCOMPARE; else cmp->opcode = OP_ICOMPARE; MONO_ADD_INS (bblock, cmp); ins->type = STACK_I4; ins->dreg = alloc_dreg (cfg, ins->type); - type_from_op (cfg, ins, sp [0], sp [1]); + type_from_op (cfg, ins, arg1, arg2); if (cmp->opcode == OP_FCOMPARE || cmp->opcode == OP_RCOMPARE) { /* @@ -12529,7 +12765,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b break; case CEE_ENDFILTER: { MonoExceptionClause *clause, *nearest; - int cc, nearest_num; + int cc; CHECK_STACK (1); --sp; @@ -12542,15 +12778,12 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b ip += 2; nearest = NULL; - nearest_num = 0; for (cc = 0; cc < header->num_clauses; ++cc) { clause = &header->clauses [cc]; if ((clause->flags & MONO_EXCEPTION_CLAUSE_FILTER) && ((ip - header->code) > clause->data.filter_offset && (ip - header->code) <= clause->handler_offset) && - (!nearest || (clause->data.filter_offset < nearest->data.filter_offset))) { + (!nearest || (clause->data.filter_offset < nearest->data.filter_offset))) nearest = clause; - nearest_num = cc; - } } g_assert (nearest); if ((ip - header->code) != nearest->handler_offset) @@ -12592,8 +12825,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b case CEE_CONSTRAINED_: CHECK_OPSIZE (6); token = read32 (ip + 2); - constrained_call = mini_get_class (method, token, generic_context); - CHECK_TYPELOAD (constrained_call); + constrained_class = mini_get_class (method, token, generic_context); + CHECK_TYPELOAD (constrained_class); ip += 6; break; case CEE_CPBLK: