X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmini%2Fmethod-to-ir.c;h=75ced4cf5b1f23a91b1435fae6eddf9092c276c4;hb=b43d924d7569d58f8d97f6ba1f4ce22ad48a492b;hp=3f18e21fd550817d951c18f533dbd1fd950e69a9;hpb=aa133b141858c50620328eb8cac742d4ee6eb876;p=mono.git diff --git a/mono/mini/method-to-ir.c b/mono/mini/method-to-ir.c index 3f18e21fd55..75ced4cf5b1 100644 --- a/mono/mini/method-to-ir.c +++ b/mono/mini/method-to-ir.c @@ -27,9 +27,7 @@ #include #endif -#ifdef HAVE_VALGRIND_MEMCHECK_H -#include -#endif +#include #include #include @@ -49,7 +47,10 @@ #include #include #include +#include +#include #include +#include #include "mini.h" #include "trace.h" @@ -57,6 +58,8 @@ #include "ir-emit.h" #include "jit-icalls.h" +#include "jit.h" +#include "debugger-agent.h" #define BRANCH_COST 100 #define INLINE_LENGTH_LIMIT 20 @@ -114,6 +117,7 @@ extern MonoMethodSignature *helper_sig_domain_get; extern MonoMethodSignature *helper_sig_generic_class_init_trampoline; extern MonoMethodSignature *helper_sig_rgctx_lazy_fetch_trampoline; extern MonoMethodSignature *helper_sig_monitor_enter_exit_trampoline; +extern MonoMethodSignature *helper_sig_monitor_enter_exit_trampoline_llvm; /* * Instruction metadata @@ -156,8 +160,6 @@ const gint8 ins_sreg_counts[] = { #undef MINI_OP #undef MINI_OP3 -extern GHashTable *jit_icall_name_hash; - #define MONO_INIT_VARINFO(vi,id) do { \ (vi)->range.first_use.pos.bid = 0xffff; \ (vi)->reg = -1; \ @@ -529,7 +531,7 @@ mono_create_spvar_for_region (MonoCompile *cfg, int region) g_hash_table_insert (cfg->spvars, GINT_TO_POINTER (region), var); } -static MonoInst * +MonoInst * mono_find_exvar_for_offset (MonoCompile *cfg, int offset) { return g_hash_table_lookup (cfg->exvars, GINT_TO_POINTER (offset)); @@ -1096,7 +1098,8 @@ type_from_stack_type (MonoInst *ins) { static G_GNUC_UNUSED int type_to_stack_type (MonoType *t) { - switch (mono_type_get_underlying_type (t)->type) { + t = mono_type_get_underlying_type (t); + switch (t->type) { case MONO_TYPE_I1: case MONO_TYPE_U1: case MONO_TYPE_BOOLEAN: @@ -1482,8 +1485,8 @@ mini_emit_max_iid_check_class (MonoCompile *cfg, int klass_reg, MonoClass *klass mini_emit_max_iid_check (cfg, max_iid_reg, klass, false_target); } -static void -mini_emit_isninst_cast (MonoCompile *cfg, int klass_reg, MonoClass *klass, MonoBasicBlock *false_target, MonoBasicBlock *true_target) +static void +mini_emit_isninst_cast_inst (MonoCompile *cfg, int klass_reg, MonoClass *klass, MonoInst *klass_ins, MonoBasicBlock *false_target, MonoBasicBlock *true_target) { int idepth_reg = alloc_preg (cfg); int stypes_reg = alloc_preg (cfg); @@ -1496,7 +1499,9 @@ mini_emit_isninst_cast (MonoCompile *cfg, int klass_reg, MonoClass *klass, MonoB } MONO_EMIT_NEW_LOAD_MEMBASE (cfg, stypes_reg, klass_reg, G_STRUCT_OFFSET (MonoClass, supertypes)); MONO_EMIT_NEW_LOAD_MEMBASE (cfg, stype, stypes_reg, ((klass->idepth - 1) * SIZEOF_VOID_P)); - if (cfg->compile_aot) { + if (klass_ins) { + MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, stype, klass_ins->dreg); + } else if (cfg->compile_aot) { int const_reg = alloc_preg (cfg); MONO_EMIT_NEW_CLASSCONST (cfg, const_reg, klass); MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, stype, const_reg); @@ -1506,7 +1511,13 @@ mini_emit_isninst_cast (MonoCompile *cfg, int klass_reg, MonoClass *klass, MonoB MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, true_target); } -static void +static void +mini_emit_isninst_cast (MonoCompile *cfg, int klass_reg, MonoClass *klass, MonoBasicBlock *false_target, MonoBasicBlock *true_target) +{ + mini_emit_isninst_cast_inst (cfg, klass_reg, klass, NULL, false_target, true_target); +} + +static void mini_emit_iface_cast (MonoCompile *cfg, int vtable_reg, MonoClass *klass, MonoBasicBlock *false_target, MonoBasicBlock *true_target) { int intf_reg = alloc_preg (cfg); @@ -1523,7 +1534,7 @@ mini_emit_iface_cast (MonoCompile *cfg, int vtable_reg, MonoClass *klass, MonoBa /* * Variant of the above that takes a register to the class, not the vtable. */ -static void +static void mini_emit_iface_class_cast (MonoCompile *cfg, int klass_reg, MonoClass *klass, MonoBasicBlock *false_target, MonoBasicBlock *true_target) { int intf_bit_reg = alloc_preg (cfg); @@ -1538,9 +1549,11 @@ mini_emit_iface_class_cast (MonoCompile *cfg, int klass_reg, MonoClass *klass, M } static inline void -mini_emit_class_check (MonoCompile *cfg, int klass_reg, MonoClass *klass) +mini_emit_class_check_inst (MonoCompile *cfg, int klass_reg, MonoClass *klass, MonoInst *klass_inst) { - if (cfg->compile_aot) { + if (klass_inst) { + MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, klass_reg, klass_inst->dreg); + } else if (cfg->compile_aot) { int const_reg = alloc_preg (cfg); MONO_EMIT_NEW_CLASSCONST (cfg, const_reg, klass); MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, klass_reg, const_reg); @@ -1550,6 +1563,12 @@ mini_emit_class_check (MonoCompile *cfg, int klass_reg, MonoClass *klass) MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException"); } +static inline void +mini_emit_class_check (MonoCompile *cfg, int klass_reg, MonoClass *klass) +{ + return mini_emit_class_check_inst (cfg, klass_reg, klass, NULL); +} + static inline void mini_emit_class_check_branch (MonoCompile *cfg, int klass_reg, MonoClass *klass, int branch_op, MonoBasicBlock *target) { @@ -1562,14 +1581,18 @@ mini_emit_class_check_branch (MonoCompile *cfg, int klass_reg, MonoClass *klass, } MONO_EMIT_NEW_BRANCH_BLOCK (cfg, branch_op, target); } + +static void +mini_emit_castclass (MonoCompile *cfg, int obj_reg, int klass_reg, MonoClass *klass, MonoBasicBlock *object_is_null); -static void -mini_emit_castclass (MonoCompile *cfg, int obj_reg, int klass_reg, MonoClass *klass, MonoBasicBlock *object_is_null) +static void +mini_emit_castclass_inst (MonoCompile *cfg, int obj_reg, int klass_reg, MonoClass *klass, MonoInst *klass_inst, MonoBasicBlock *object_is_null) { if (klass->rank) { int rank_reg = alloc_preg (cfg); int eclass_reg = alloc_preg (cfg); + g_assert (!klass_inst); MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU1_MEMBASE, rank_reg, klass_reg, G_STRUCT_OFFSET (MonoClass, rank)); MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, rank_reg, klass->rank); MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException"); @@ -1611,10 +1634,16 @@ mini_emit_castclass (MonoCompile *cfg, int obj_reg, int klass_reg, MonoClass *kl } MONO_EMIT_NEW_LOAD_MEMBASE (cfg, stypes_reg, klass_reg, G_STRUCT_OFFSET (MonoClass, supertypes)); MONO_EMIT_NEW_LOAD_MEMBASE (cfg, stype, stypes_reg, ((klass->idepth - 1) * SIZEOF_VOID_P)); - mini_emit_class_check (cfg, stype, klass); + mini_emit_class_check_inst (cfg, stype, klass, klass_inst); } } +static void +mini_emit_castclass (MonoCompile *cfg, int obj_reg, int klass_reg, MonoClass *klass, MonoBasicBlock *object_is_null) +{ + return mini_emit_castclass_inst (cfg, obj_reg, klass_reg, klass, NULL, object_is_null); +} + static void mini_emit_memset (MonoCompile *cfg, int destreg, int offset, int size, int val, int align) { @@ -1703,6 +1732,9 @@ mini_emit_memcpy (MonoCompile *cfg, int destreg, int doffset, int srcreg, int so if (align == 0) align = 4; + /*FIXME arbitrary hack to avoid unbound code expansion.*/ + g_assert (size < 10000); + if (align < 4) { /* This could be optimized further if neccesary */ while (size >= 1) { @@ -2098,9 +2130,6 @@ mono_patch_info_new (MonoMemPool *mp, int ip, MonoJumpInfoType type, gconstpoint return ji; } -inline static MonoInst* -mono_emit_jit_icall (MonoCompile *cfg, gconstpointer func, MonoInst **args); - inline static MonoCallInst * mono_emit_call_args (MonoCompile *cfg, MonoMethodSignature *sig, MonoInst **args, int calli, int virtual, int tail) @@ -2153,30 +2182,32 @@ mono_emit_call_args (MonoCompile *cfg, MonoMethodSignature *sig, call->inst.dreg = alloc_dreg (cfg, call->inst.type); #ifdef MONO_ARCH_SOFT_FLOAT - /* - * If the call has a float argument, we would need to do an r8->r4 conversion using - * an icall, but that cannot be done during the call sequence since it would clobber - * the call registers + the stack. So we do it before emitting the call. - */ - for (i = 0; i < sig->param_count + sig->hasthis; ++i) { - MonoType *t; - MonoInst *in = call->args [i]; + if (COMPILE_SOFT_FLOAT (cfg)) { + /* + * If the call has a float argument, we would need to do an r8->r4 conversion using + * an icall, but that cannot be done during the call sequence since it would clobber + * the call registers + the stack. So we do it before emitting the call. + */ + for (i = 0; i < sig->param_count + sig->hasthis; ++i) { + MonoType *t; + MonoInst *in = call->args [i]; - if (i >= sig->hasthis) - t = sig->params [i - sig->hasthis]; - else - t = &mono_defaults.int_class->byval_arg; - t = mono_type_get_underlying_type (t); + if (i >= sig->hasthis) + t = sig->params [i - sig->hasthis]; + else + t = &mono_defaults.int_class->byval_arg; + t = mono_type_get_underlying_type (t); - if (!t->byref && t->type == MONO_TYPE_R4) { - MonoInst *iargs [1]; - MonoInst *conv; + if (!t->byref && t->type == MONO_TYPE_R4) { + MonoInst *iargs [1]; + MonoInst *conv; - iargs [0] = in; - conv = mono_emit_jit_icall (cfg, mono_fload_r4_arg, iargs); + iargs [0] = in; + conv = mono_emit_jit_icall (cfg, mono_fload_r4_arg, iargs); - /* The result will be in an int vreg */ - call->args [i] = conv; + /* The result will be in an int vreg */ + call->args [i] = conv; + } } } #endif @@ -2287,6 +2318,8 @@ mono_emit_method_call_full (MonoCompile *cfg, MonoMethod *method, MonoMethodSign #ifdef MONO_ARCH_HAVE_CREATE_DELEGATE_TRAMPOLINE if ((method->klass->parent == mono_defaults.multicastdelegate_class) && (!strcmp (method->name, "Invoke"))) { + MONO_EMIT_NULL_CHECK (cfg, this_reg); + /* Make a call to delegate->invoke_impl */ call->inst.opcode = callvirt_to_call_membase (call->inst.opcode); call->inst.inst_basereg = this_reg; @@ -2300,20 +2333,22 @@ mono_emit_method_call_full (MonoCompile *cfg, MonoMethod *method, MonoMethodSign if ((!cfg->compile_aot || enable_for_aot) && (!(method->flags & METHOD_ATTRIBUTE_VIRTUAL) || (MONO_METHOD_IS_FINAL (method) && - method->wrapper_type != MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK))) { + method->wrapper_type != MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK)) && + !(method->klass->marshalbyref && context_used)) { /* * the method is not virtual, we just need to ensure this is not null * and then we can call the method directly. */ if (method->klass->marshalbyref || method->klass == mono_defaults.object_class) { + /* + * The check above ensures method is not gshared, this is needed since + * gshared methods can't have wrappers. + */ method = call->method = mono_marshal_get_remoting_invoke_with_check (method); } - if (!method->string_ctor) { - cfg->flags |= MONO_CFG_HAS_CHECK_THIS; - MONO_EMIT_NEW_UNALU (cfg, OP_CHECK_THIS, -1, this_reg); - MONO_EMIT_NEW_UNALU (cfg, OP_NOT_NULL, -1, this_reg); - } + if (!method->string_ctor) + MONO_EMIT_NEW_CHECK_THIS (cfg, this_reg); call->inst.opcode = callvirt_to_call (call->inst.opcode); @@ -2328,9 +2363,7 @@ mono_emit_method_call_full (MonoCompile *cfg, MonoMethod *method, MonoMethodSign * it's class or the method itself are sealed. * But first we need to ensure it's not a null reference. */ - cfg->flags |= MONO_CFG_HAS_CHECK_THIS; - MONO_EMIT_NEW_UNALU (cfg, OP_CHECK_THIS, -1, this_reg); - MONO_EMIT_NEW_UNALU (cfg, OP_NOT_NULL, -1, this_reg); + MONO_EMIT_NEW_CHECK_THIS (cfg, this_reg); call->inst.opcode = callvirt_to_call (call->inst.opcode); MONO_ADD_INS (cfg->cbb, (MonoInst*)call); @@ -2341,7 +2374,7 @@ mono_emit_method_call_full (MonoCompile *cfg, MonoMethod *method, MonoMethodSign call->inst.opcode = callvirt_to_call_membase (call->inst.opcode); vtable_reg = alloc_preg (cfg); - MONO_EMIT_NEW_LOAD_MEMBASE (cfg, vtable_reg, this_reg, G_STRUCT_OFFSET (MonoObject, vtable)); + MONO_EMIT_NEW_LOAD_MEMBASE_FAULT (cfg, vtable_reg, this_reg, G_STRUCT_OFFSET (MonoObject, vtable)); if (method->klass->flags & TYPE_ATTRIBUTE_INTERFACE) { slot_reg = -1; #ifdef MONO_ARCH_HAVE_IMT @@ -2360,7 +2393,7 @@ mono_emit_method_call_full (MonoCompile *cfg, MonoMethod *method, MonoMethodSign } else { slot_reg = vtable_reg; call->inst.inst_offset = G_STRUCT_OFFSET (MonoVTable, vtable) + - (mono_method_get_vtable_index (method) * SIZEOF_VOID_P); + ((mono_method_get_vtable_index (method)) * (SIZEOF_VOID_P)); #ifdef MONO_ARCH_HAVE_IMT if (imt_arg) { g_assert (mono_method_signature (method)->generic_param_count); @@ -2382,7 +2415,9 @@ static MonoInst* mono_emit_rgctx_method_call_full (MonoCompile *cfg, MonoMethod *method, MonoMethodSignature *sig, MonoInst **args, MonoInst *this, MonoInst *imt_arg, MonoInst *vtable_arg) { +#ifdef MONO_ARCH_RGCTX_REG int rgctx_reg = 0; +#endif MonoInst *ins; MonoCallInst *call; @@ -2410,7 +2445,7 @@ mono_emit_rgctx_method_call_full (MonoCompile *cfg, MonoMethod *method, MonoMeth return ins; } -static inline MonoInst* +MonoInst* mono_emit_method_call (MonoCompile *cfg, MonoMethod *method, MonoInst **args, MonoInst *this) { return mono_emit_method_call_full (cfg, method, mono_method_signature (method), args, this, NULL); @@ -2432,7 +2467,7 @@ mono_emit_native_call (MonoCompile *cfg, gconstpointer func, MonoMethodSignature return (MonoInst*)call; } -inline static MonoInst* +MonoInst* mono_emit_jit_icall (MonoCompile *cfg, gconstpointer func, MonoInst **args) { MonoJitICallInfo *info = mono_find_jit_icall_by_addr (func); @@ -2559,8 +2594,12 @@ mini_emit_stobj (MonoCompile *cfg, MonoInst *dest, MonoInst *src, MonoClass *kla if (context_used) { iargs [2] = emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_KLASS); } else { - EMIT_NEW_PCONST (cfg, iargs [2], klass); - mono_class_compute_gc_descriptor (klass); + if (cfg->compile_aot) { + EMIT_NEW_CLASSCONST (cfg, iargs [2], klass); + } else { + EMIT_NEW_PCONST (cfg, iargs [2], klass); + mono_class_compute_gc_descriptor (klass); + } } mono_emit_jit_icall (cfg, mono_value_copy, iargs); @@ -2699,14 +2738,35 @@ emit_get_rgctx_klass (MonoCompile *cfg, int context_used, return emit_rgctx_fetch (cfg, rgctx, entry); } +/* + * emit_get_rgctx_method: + * + * Emit IR to load the property RGCTX_TYPE of CMETHOD. If context_used is 0, emit + * normal constants, else emit a load from the rgctx. + */ static MonoInst* emit_get_rgctx_method (MonoCompile *cfg, int context_used, MonoMethod *cmethod, int rgctx_type) { - MonoJumpInfoRgctxEntry *entry = mono_patch_info_rgctx_entry_new (cfg->mempool, cfg->current_method, context_used & MONO_GENERIC_CONTEXT_USED_METHOD, MONO_PATCH_INFO_METHODCONST, cmethod, rgctx_type); - MonoInst *rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used); + if (!context_used) { + MonoInst *ins; - return emit_rgctx_fetch (cfg, rgctx, entry); + switch (rgctx_type) { + case MONO_RGCTX_INFO_METHOD: + EMIT_NEW_METHODCONST (cfg, ins, cmethod); + return ins; + case MONO_RGCTX_INFO_METHOD_RGCTX: + EMIT_NEW_METHOD_RGCTX_CONST (cfg, ins, cmethod); + return ins; + default: + g_assert_not_reached (); + } + } else { + MonoJumpInfoRgctxEntry *entry = mono_patch_info_rgctx_entry_new (cfg->mempool, cfg->current_method, context_used & MONO_GENERIC_CONTEXT_USED_METHOD, MONO_PATCH_INFO_METHODCONST, cmethod, rgctx_type); + MonoInst *rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used); + + return emit_rgctx_fetch (cfg, rgctx, entry); + } } static MonoInst* @@ -2876,7 +2936,7 @@ handle_unbox (MonoCompile *cfg, MonoClass *klass, MonoInst **sp, int context_use int rank_reg = alloc_dreg (cfg ,STACK_I4); obj_reg = sp [0]->dreg; - MONO_EMIT_NEW_LOAD_MEMBASE (cfg, vtable_reg, obj_reg, G_STRUCT_OFFSET (MonoObject, vtable)); + MONO_EMIT_NEW_LOAD_MEMBASE_FAULT (cfg, vtable_reg, obj_reg, G_STRUCT_OFFSET (MonoObject, vtable)); MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU1_MEMBASE, rank_reg, vtable_reg, G_STRUCT_OFFSET (MonoVTable, rank)); /* FIXME: generics */ @@ -3049,11 +3109,34 @@ handle_box_from_inst (MonoCompile *cfg, MonoInst *val, MonoClass *klass, int con * Returns NULL and set the cfg exception on error. */ static MonoInst* -handle_castclass (MonoCompile *cfg, MonoClass *klass, MonoInst *src) +handle_castclass (MonoCompile *cfg, MonoClass *klass, MonoInst *src, int context_used) { MonoBasicBlock *is_null_bb; int obj_reg = src->dreg; int vtable_reg = alloc_preg (cfg); + MonoInst *klass_inst = NULL; + + if (context_used) { + MonoInst *args [2]; + + klass_inst = emit_get_rgctx_klass (cfg, context_used, + klass, MONO_RGCTX_INFO_KLASS); + + // FIXME: This doesn't work yet (mcs/tests/gtest-304.cs fails) + if (TRUE || (klass->flags & TYPE_ATTRIBUTE_INTERFACE) || klass->rank || mono_class_is_nullable (klass) || klass->marshalbyref || (klass->flags & TYPE_ATTRIBUTE_SEALED) || mono_class_has_variant_generic_params (klass)) { + /* Complex case, handle by an icall */ + + /* obj */ + args [0] = src; + + /* klass */ + args [1] = klass_inst; + + return mono_emit_jit_icall (cfg, mono_object_castclass, args); + } else { + /* Simple case, handled by the code below */ + } + } NEW_BBLOCK (cfg, is_null_bb); @@ -3087,7 +3170,7 @@ handle_castclass (MonoCompile *cfg, MonoClass *klass, MonoInst *src) MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException"); } else { MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, G_STRUCT_OFFSET (MonoVTable, klass)); - mini_emit_castclass (cfg, obj_reg, klass_reg, klass, is_null_bb); + mini_emit_castclass_inst (cfg, obj_reg, klass_reg, klass, klass_inst, is_null_bb); } } @@ -3102,13 +3185,35 @@ handle_castclass (MonoCompile *cfg, MonoClass *klass, MonoInst *src) * Returns NULL and set the cfg exception on error. */ static MonoInst* -handle_isinst (MonoCompile *cfg, MonoClass *klass, MonoInst *src) +handle_isinst (MonoCompile *cfg, MonoClass *klass, MonoInst *src, int context_used) { MonoInst *ins; MonoBasicBlock *is_null_bb, *false_bb, *end_bb; int obj_reg = src->dreg; int vtable_reg = alloc_preg (cfg); int res_reg = alloc_preg (cfg); + MonoInst *klass_inst = NULL; + + if (context_used) { + klass_inst = emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_KLASS); + + // FIXME: This doesn't work yet (mcs/tests/gtest-304.cs fails) + if (TRUE || (klass->flags & TYPE_ATTRIBUTE_INTERFACE) || klass->rank || mono_class_is_nullable (klass) || klass->marshalbyref || (klass->flags & TYPE_ATTRIBUTE_SEALED) || mono_class_has_variant_generic_params (klass)) { + MonoInst *args [2]; + + /* Complex case, handle by an icall */ + + /* obj */ + args [0] = src; + + /* klass */ + args [1] = klass_inst; + + return mono_emit_jit_icall (cfg, mono_object_isinst, args); + } else { + /* Simple case, the code below can handle it */ + } + } NEW_BBLOCK (cfg, is_null_bb); NEW_BBLOCK (cfg, false_bb); @@ -3122,19 +3227,20 @@ handle_isinst (MonoCompile *cfg, MonoClass *klass, MonoInst *src) MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, obj_reg, 0); MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, is_null_bb); + MONO_EMIT_NEW_LOAD_MEMBASE (cfg, vtable_reg, obj_reg, G_STRUCT_OFFSET (MonoObject, vtable)); + if (klass->flags & TYPE_ATTRIBUTE_INTERFACE) { - MONO_EMIT_NEW_LOAD_MEMBASE (cfg, vtable_reg, obj_reg, G_STRUCT_OFFSET (MonoObject, vtable)); + g_assert (!context_used); /* the is_null_bb target simply copies the input register to the output */ mini_emit_iface_cast (cfg, vtable_reg, klass, false_bb, is_null_bb); } else { int klass_reg = alloc_preg (cfg); - MONO_EMIT_NEW_LOAD_MEMBASE (cfg, vtable_reg, obj_reg, G_STRUCT_OFFSET (MonoObject, vtable)); - if (klass->rank) { int rank_reg = alloc_preg (cfg); int eclass_reg = alloc_preg (cfg); + g_assert (!context_used); MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU1_MEMBASE, rank_reg, vtable_reg, G_STRUCT_OFFSET (MonoVTable, rank)); MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, rank_reg, klass->rank); MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, false_bb); @@ -3168,11 +3274,13 @@ handle_isinst (MonoCompile *cfg, MonoClass *klass, MonoInst *src) mini_emit_isninst_cast (cfg, eclass_reg, klass->cast_class, false_bb, is_null_bb); } } else if (mono_class_is_nullable (klass)) { + g_assert (!context_used); MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, G_STRUCT_OFFSET (MonoVTable, klass)); /* the is_null_bb target simply copies the input register to the output */ mini_emit_isninst_cast (cfg, klass_reg, klass->cast_class, false_bb, is_null_bb); } else { if (!cfg->compile_aot && !(cfg->opt & MONO_OPT_SHARED) && (klass->flags & TYPE_ATTRIBUTE_SEALED)) { + g_assert (!context_used); /* the remoting code is broken, access the class for now */ if (0) {/*FIXME what exactly is broken? This change refers to r39380 from 2005 and mention some remoting fixes were due.*/ MonoVTable *vt = mono_class_vtable (cfg->domain, klass); @@ -3191,7 +3299,7 @@ handle_isinst (MonoCompile *cfg, MonoClass *klass, MonoInst *src) } else { MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, G_STRUCT_OFFSET (MonoVTable, klass)); /* the is_null_bb target simply copies the input register to the output */ - mini_emit_isninst_cast (cfg, klass_reg, klass, false_bb, is_null_bb); + mini_emit_isninst_cast_inst (cfg, klass_reg, klass, klass_inst, false_bb, is_null_bb); } } } @@ -3381,7 +3489,7 @@ handle_ccastclass (MonoCompile *cfg, MonoClass *klass, MonoInst *src) * Returns NULL and set the cfg exception on error. */ static G_GNUC_UNUSED MonoInst* -handle_delegate_ctor (MonoCompile *cfg, MonoClass *klass, MonoInst *target, MonoMethod *method) +handle_delegate_ctor (MonoCompile *cfg, MonoClass *klass, MonoInst *target, MonoMethod *method, int context_used) { gpointer *trampoline; MonoInst *obj, *method_ins, *tramp_ins; @@ -3400,7 +3508,7 @@ handle_delegate_ctor (MonoCompile *cfg, MonoClass *klass, MonoInst *target, Mono MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, G_STRUCT_OFFSET (MonoDelegate, target), target->dreg); /* Set method field */ - EMIT_NEW_METHODCONST (cfg, method_ins, method); + method_ins = emit_get_rgctx_method (cfg, context_used, method, MONO_RGCTX_INFO_METHOD); MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, G_STRUCT_OFFSET (MonoDelegate, method), method_ins->dreg); /* @@ -3411,18 +3519,22 @@ handle_delegate_ctor (MonoCompile *cfg, MonoClass *klass, MonoInst *target, Mono if (!cfg->compile_aot && !method->dynamic) { MonoInst *code_slot_ins; - domain = mono_domain_get (); - mono_domain_lock (domain); - if (!domain_jit_info (domain)->method_code_hash) - domain_jit_info (domain)->method_code_hash = g_hash_table_new (NULL, NULL); - code_slot = g_hash_table_lookup (domain_jit_info (domain)->method_code_hash, method); - if (!code_slot) { - code_slot = mono_domain_alloc0 (domain, sizeof (gpointer)); - g_hash_table_insert (domain_jit_info (domain)->method_code_hash, method, code_slot); + if (context_used) { + code_slot_ins = emit_get_rgctx_method (cfg, context_used, method, MONO_RGCTX_INFO_METHOD_DELEGATE_CODE); + } else { + domain = mono_domain_get (); + mono_domain_lock (domain); + if (!domain_jit_info (domain)->method_code_hash) + domain_jit_info (domain)->method_code_hash = g_hash_table_new (NULL, NULL); + code_slot = g_hash_table_lookup (domain_jit_info (domain)->method_code_hash, method); + if (!code_slot) { + code_slot = mono_domain_alloc0 (domain, sizeof (gpointer)); + g_hash_table_insert (domain_jit_info (domain)->method_code_hash, method, code_slot); + } + mono_domain_unlock (domain); + + EMIT_NEW_PCONST (cfg, code_slot_ins, code_slot); } - mono_domain_unlock (domain); - - EMIT_NEW_PCONST (cfg, code_slot_ins, code_slot); MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, G_STRUCT_OFFSET (MonoDelegate, method_code), code_slot_ins->dreg); } @@ -3507,6 +3619,9 @@ mono_method_check_inlining (MonoCompile *cfg, MonoMethod *method) if (cfg->generic_sharing_context) return FALSE; + if (cfg->inline_depth > 10) + return FALSE; + #ifdef MONO_ARCH_HAVE_LMF_OPS if (((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) || (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)) && @@ -3638,8 +3753,13 @@ mini_emit_ldelema_1_ins (MonoCompile *cfg, MonoClass *klass, MonoInst *arr, Mono #if SIZEOF_REGISTER == 8 /* The array reg is 64 bits but the index reg is only 32 */ - index2_reg = alloc_preg (cfg); - MONO_EMIT_NEW_UNALU (cfg, OP_SEXT_I4, index2_reg, index_reg); + if (COMPILE_LLVM (cfg)) { + /* Not needed */ + index2_reg = index_reg; + } else { + index2_reg = alloc_preg (cfg); + MONO_EMIT_NEW_UNALU (cfg, OP_SEXT_I4, index2_reg, index_reg); + } #else if (index->type == STACK_I8) { index2_reg = alloc_preg (cfg); @@ -3759,6 +3879,53 @@ mini_emit_ldelema_ins (MonoCompile *cfg, MonoMethod *cmethod, MonoInst **sp, uns return addr; } +static MonoBreakPolicy +always_insert_breakpoint (MonoMethod *method) +{ + return MONO_BREAK_POLICY_ALWAYS; +} + +static MonoBreakPolicyFunc break_policy_func = always_insert_breakpoint; + +/** + * mono_set_break_policy: + * policy_callback: the new callback function + * + * Allow embedders to decide wherther to actually obey breakpoint instructions + * (both break IL instructions and Debugger.Break () method calls), for example + * to not allow an app to be aborted by a perfectly valid IL opcode when executing + * untrusted or semi-trusted code. + * + * @policy_callback will be called every time a break point instruction needs to + * be inserted with the method argument being the method that calls Debugger.Break() + * or has the IL break instruction. The callback should return #MONO_BREAK_POLICY_NEVER + * if it wants the breakpoint to not be effective in the given method. + * #MONO_BREAK_POLICY_ALWAYS is the default. + */ +void +mono_set_break_policy (MonoBreakPolicyFunc policy_callback) +{ + if (policy_callback) + break_policy_func = policy_callback; + else + break_policy_func = always_insert_breakpoint; +} + +static gboolean +should_insert_brekpoint (MonoMethod *method) { + switch (break_policy_func (method)) { + case MONO_BREAK_POLICY_ALWAYS: + return TRUE; + case MONO_BREAK_POLICY_NEVER: + return FALSE; + case MONO_BREAK_POLICY_ON_DBG: + return mono_debug_using_mono_debugger (); + default: + g_warning ("Incorrect value returned from break policy callback"); + return FALSE; + } +} + static MonoInst* mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args) { @@ -3823,7 +3990,7 @@ mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign if (strcmp (cmethod->name, "GetType") == 0) { int dreg = alloc_preg (cfg); int vt_reg = alloc_preg (cfg); - MONO_EMIT_NEW_LOAD_MEMBASE (cfg, vt_reg, args [0]->dreg, G_STRUCT_OFFSET (MonoObject, vtable)); + MONO_EMIT_NEW_LOAD_MEMBASE_FAULT (cfg, vt_reg, args [0]->dreg, G_STRUCT_OFFSET (MonoObject, vtable)); EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, dreg, vt_reg, G_STRUCT_OFFSET (MonoVTable, type)); type_from_op (ins, NULL, NULL); @@ -3852,8 +4019,8 @@ mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign if (strcmp (cmethod->name, "get_Rank") == 0) { int dreg = alloc_ireg (cfg); int vtable_reg = alloc_preg (cfg); - MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOAD_MEMBASE, vtable_reg, - args [0]->dreg, G_STRUCT_OFFSET (MonoObject, vtable)); + MONO_EMIT_NEW_LOAD_MEMBASE_OP_FAULT (cfg, OP_LOAD_MEMBASE, vtable_reg, + args [0]->dreg, G_STRUCT_OFFSET (MonoObject, vtable)); EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADU1_MEMBASE, dreg, vtable_reg, G_STRUCT_OFFSET (MonoVTable, rank)); type_from_op (ins, NULL, NULL); @@ -3862,8 +4029,8 @@ mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign } else if (strcmp (cmethod->name, "get_Length") == 0) { int dreg = alloc_ireg (cfg); - EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADI4_MEMBASE, dreg, - args [0]->dreg, G_STRUCT_OFFSET (MonoArray, max_length)); + EMIT_NEW_LOAD_MEMBASE_FAULT (cfg, ins, OP_LOADI4_MEMBASE, dreg, + args [0]->dreg, G_STRUCT_OFFSET (MonoArray, max_length)); type_from_op (ins, NULL, NULL); return ins; @@ -3877,12 +4044,7 @@ mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign } else return NULL; } else if (cmethod->klass == mono_defaults.thread_class) { - if (strcmp (cmethod->name, "get_CurrentThread") == 0 && (ins = mono_arch_get_thread_intrinsic (cfg))) { - ins->dreg = alloc_preg (cfg); - ins->type = STACK_OBJ; - MONO_ADD_INS (cfg->cbb, ins); - return ins; - } else if (strcmp (cmethod->name, "SpinWait_nop") == 0) { + if (strcmp (cmethod->name, "SpinWait_nop") == 0) { MONO_INST_NEW (cfg, ins, OP_RELAXED_NOP); MONO_ADD_INS (cfg->cbb, ins); return ins; @@ -3896,19 +4058,31 @@ mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign if (strcmp (cmethod->name, "Enter") == 0) { MonoCallInst *call; - call = (MonoCallInst*)mono_emit_abs_call (cfg, MONO_PATCH_INFO_MONITOR_ENTER, - NULL, helper_sig_monitor_enter_exit_trampoline, NULL); - mono_call_inst_add_outarg_reg (cfg, call, args [0]->dreg, - MONO_ARCH_MONITOR_OBJECT_REG, FALSE); + if (COMPILE_LLVM (cfg)) { + /* + * Pass the argument normally, the LLVM backend will handle the + * calling convention problems. + */ + call = (MonoCallInst*)mono_emit_abs_call (cfg, MONO_PATCH_INFO_MONITOR_ENTER, NULL, helper_sig_monitor_enter_exit_trampoline_llvm, args); + } else { + call = (MonoCallInst*)mono_emit_abs_call (cfg, MONO_PATCH_INFO_MONITOR_ENTER, + NULL, helper_sig_monitor_enter_exit_trampoline, NULL); + mono_call_inst_add_outarg_reg (cfg, call, args [0]->dreg, + MONO_ARCH_MONITOR_OBJECT_REG, FALSE); + } return (MonoInst*)call; } else if (strcmp (cmethod->name, "Exit") == 0) { MonoCallInst *call; - call = (MonoCallInst*)mono_emit_abs_call (cfg, MONO_PATCH_INFO_MONITOR_EXIT, - NULL, helper_sig_monitor_enter_exit_trampoline, NULL); - mono_call_inst_add_outarg_reg (cfg, call, args [0]->dreg, - MONO_ARCH_MONITOR_OBJECT_REG, FALSE); + if (COMPILE_LLVM (cfg)) { + call = (MonoCallInst*)mono_emit_abs_call (cfg, MONO_PATCH_INFO_MONITOR_EXIT, NULL, helper_sig_monitor_enter_exit_trampoline_llvm, args); + } else { + call = (MonoCallInst*)mono_emit_abs_call (cfg, MONO_PATCH_INFO_MONITOR_EXIT, + NULL, helper_sig_monitor_enter_exit_trampoline, NULL); + mono_call_inst_add_outarg_reg (cfg, call, args [0]->dreg, + MONO_ARCH_MONITOR_OBJECT_REG, FALSE); + } return (MonoInst*)call; } @@ -4117,13 +4291,16 @@ mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign } else if (cmethod->klass->image == mono_defaults.corlib) { if (cmethod->name [0] == 'B' && strcmp (cmethod->name, "Break") == 0 && strcmp (cmethod->klass->name, "Debugger") == 0) { - MONO_INST_NEW (cfg, ins, OP_BREAK); + if (should_insert_brekpoint (cfg->method)) + MONO_INST_NEW (cfg, ins, OP_BREAK); + else + MONO_INST_NEW (cfg, ins, OP_NOP); MONO_ADD_INS (cfg->cbb, ins); return ins; } if (cmethod->name [0] == 'g' && strcmp (cmethod->name, "get_IsRunningOnWindows") == 0 && strcmp (cmethod->klass->name, "Environment") == 0) { -#ifdef PLATFORM_WIN32 +#ifdef TARGET_WIN32 EMIT_NEW_ICONST (cfg, ins, 1); #else EMIT_NEW_ICONST (cfg, ins, 0); @@ -4159,7 +4336,7 @@ mini_redirect_call (MonoCompile *cfg, MonoMethod *method, { if (method->klass == mono_defaults.string_class) { /* managed string allocation support */ - if (strcmp (method->name, "InternalAllocateStr") == 0) { + if (strcmp (method->name, "InternalAllocateStr") == 0 && !(mono_profiler_events & MONO_PROFILE_STRING_ALLOC)) { MonoInst *iargs [2]; MonoVTable *vtable = mono_class_vtable (cfg->domain, method->klass); MonoMethod *managed_alloc = NULL; @@ -4332,6 +4509,7 @@ inline_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, prev_inlined_method = cfg->inlined_method; cfg->inlined_method = cmethod; cfg->ret_var_set = FALSE; + cfg->inline_depth ++; prev_real_offset = cfg->real_offset; prev_cbb_hash = cfg->cbb_hash; prev_cil_offset_to_bb = cfg->cil_offset_to_bb; @@ -4358,6 +4536,7 @@ inline_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, cfg->current_method = prev_current_method; cfg->generic_context = prev_generic_context; cfg->ret_var_set = prev_ret_var_set; + cfg->inline_depth --; if ((costs >= 0 && costs < 60) || inline_allways) { if (cfg->verbose_level > 2) @@ -4850,396 +5029,6 @@ generic_class_is_reference_type (MonoCompile *cfg, MonoClass *klass) return MONO_TYPE_IS_REFERENCE (type); } -/** - * mono_decompose_array_access_opts: - * - * Decompose array access opcodes. - * This should be in decompose.c, but it emits calls so it has to stay here until - * the old JIT is gone. - */ -void -mono_decompose_array_access_opts (MonoCompile *cfg) -{ - MonoBasicBlock *bb, *first_bb; - - /* - * Unlike decompose_long_opts, this pass does not alter the CFG of the method so it - * can be executed anytime. It should be run before decompose_long - */ - - /** - * Create a dummy bblock and emit code into it so we can use the normal - * code generation macros. - */ - cfg->cbb = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoBasicBlock)); - first_bb = cfg->cbb; - - for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { - MonoInst *ins; - MonoInst *prev = NULL; - MonoInst *dest; - MonoInst *iargs [3]; - gboolean restart; - - if (!bb->has_array_access) - continue; - - if (cfg->verbose_level > 3) mono_print_bb (bb, "BEFORE DECOMPOSE-ARRAY-ACCESS-OPTS "); - - cfg->cbb->code = cfg->cbb->last_ins = NULL; - restart = TRUE; - - while (restart) { - restart = FALSE; - - for (ins = bb->code; ins; ins = ins->next) { - switch (ins->opcode) { - case OP_LDLEN: - NEW_LOAD_MEMBASE (cfg, dest, OP_LOADI4_MEMBASE, ins->dreg, ins->sreg1, - G_STRUCT_OFFSET (MonoArray, max_length)); - MONO_ADD_INS (cfg->cbb, dest); - break; - case OP_BOUNDS_CHECK: - MONO_ARCH_EMIT_BOUNDS_CHECK (cfg, ins->sreg1, ins->inst_imm, ins->sreg2); - break; - case OP_NEWARR: - if (cfg->opt & MONO_OPT_SHARED) { - EMIT_NEW_DOMAINCONST (cfg, iargs [0]); - EMIT_NEW_CLASSCONST (cfg, iargs [1], ins->inst_newa_class); - MONO_INST_NEW (cfg, iargs [2], OP_MOVE); - iargs [2]->dreg = ins->sreg1; - - dest = mono_emit_jit_icall (cfg, mono_array_new, iargs); - dest->dreg = ins->dreg; - } else { - MonoVTable *vtable = mono_class_vtable (cfg->domain, mono_array_class_get (ins->inst_newa_class, 1)); - - g_assert (vtable); /*This shall not fail since we check for this condition on OP_NEWARR creation*/ - NEW_VTABLECONST (cfg, iargs [0], vtable); - MONO_ADD_INS (cfg->cbb, iargs [0]); - MONO_INST_NEW (cfg, iargs [1], OP_MOVE); - iargs [1]->dreg = ins->sreg1; - - dest = mono_emit_jit_icall (cfg, mono_array_new_specific, iargs); - dest->dreg = ins->dreg; - } - break; - case OP_STRLEN: - NEW_LOAD_MEMBASE (cfg, dest, OP_LOADI4_MEMBASE, ins->dreg, - ins->sreg1, G_STRUCT_OFFSET (MonoString, length)); - MONO_ADD_INS (cfg->cbb, dest); - break; - default: - break; - } - - g_assert (cfg->cbb == first_bb); - - if (cfg->cbb->code || (cfg->cbb != first_bb)) { - /* Replace the original instruction with the new code sequence */ - - mono_replace_ins (cfg, bb, ins, &prev, first_bb, cfg->cbb); - first_bb->code = first_bb->last_ins = NULL; - first_bb->in_count = first_bb->out_count = 0; - cfg->cbb = first_bb; - } - else - prev = ins; - } - } - - if (cfg->verbose_level > 3) mono_print_bb (bb, "AFTER DECOMPOSE-ARRAY-ACCESS-OPTS "); - } -} - -typedef union { - guint32 vali [2]; - gint64 vall; - double vald; -} DVal; - -#ifdef MONO_ARCH_SOFT_FLOAT - -/** - * mono_decompose_soft_float: - * - * Soft float support on ARM. We store each double value in a pair of integer vregs, - * similar to long support on 32 bit platforms. 32 bit float values require special - * handling when used as locals, arguments, and in calls. - * One big problem with soft-float is that there are few r4 test cases in our test suite. - */ -void -mono_decompose_soft_float (MonoCompile *cfg) -{ - MonoBasicBlock *bb, *first_bb; - - /* - * This pass creates long opcodes, so it should be run before decompose_long_opts (). - */ - - /** - * Create a dummy bblock and emit code into it so we can use the normal - * code generation macros. - */ - cfg->cbb = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoBasicBlock)); - first_bb = cfg->cbb; - - for (bb = cfg->bb_entry; bb; bb = bb->next_bb) { - MonoInst *ins; - MonoInst *prev = NULL; - gboolean restart; - - if (cfg->verbose_level > 3) mono_print_bb (bb, "BEFORE HANDLE-SOFT-FLOAT "); - - cfg->cbb->code = cfg->cbb->last_ins = NULL; - restart = TRUE; - - while (restart) { - restart = FALSE; - - for (ins = bb->code; ins; ins = ins->next) { - const char *spec = INS_INFO (ins->opcode); - - /* Most fp operations are handled automatically by opcode emulation */ - - switch (ins->opcode) { - case OP_R8CONST: { - DVal d; - d.vald = *(double*)ins->inst_p0; - MONO_EMIT_NEW_I8CONST (cfg, ins->dreg, d.vall); - break; - } - case OP_R4CONST: { - DVal d; - /* We load the r8 value */ - d.vald = *(float*)ins->inst_p0; - MONO_EMIT_NEW_I8CONST (cfg, ins->dreg, d.vall); - break; - } - case OP_FMOVE: - ins->opcode = OP_LMOVE; - break; - case OP_FGETLOW32: - ins->opcode = OP_MOVE; - ins->sreg1 = ins->sreg1 + 1; - break; - case OP_FGETHIGH32: - ins->opcode = OP_MOVE; - ins->sreg1 = ins->sreg1 + 2; - break; - case OP_SETFRET: { - int reg = ins->sreg1; - - ins->opcode = OP_SETLRET; - ins->dreg = -1; - ins->sreg1 = reg + 1; - ins->sreg2 = reg + 2; - break; - } - case OP_LOADR8_MEMBASE: - ins->opcode = OP_LOADI8_MEMBASE; - break; - case OP_STORER8_MEMBASE_REG: - ins->opcode = OP_STOREI8_MEMBASE_REG; - break; - case OP_STORER4_MEMBASE_REG: { - MonoInst *iargs [2]; - int addr_reg; - - /* Arg 1 is the double value */ - MONO_INST_NEW (cfg, iargs [0], OP_ARG); - iargs [0]->dreg = ins->sreg1; - - /* Arg 2 is the address to store to */ - addr_reg = mono_alloc_preg (cfg); - EMIT_NEW_BIALU_IMM (cfg, iargs [1], OP_PADD_IMM, addr_reg, ins->inst_destbasereg, ins->inst_offset); - mono_emit_jit_icall (cfg, mono_fstore_r4, iargs); - restart = TRUE; - break; - } - case OP_LOADR4_MEMBASE: { - MonoInst *iargs [1]; - MonoInst *conv; - int addr_reg; - - addr_reg = mono_alloc_preg (cfg); - EMIT_NEW_BIALU_IMM (cfg, iargs [0], OP_PADD_IMM, addr_reg, ins->inst_basereg, ins->inst_offset); - conv = mono_emit_jit_icall (cfg, mono_fload_r4, iargs); - conv->dreg = ins->dreg; - break; - } - case OP_FCALL: - case OP_FCALL_REG: - case OP_FCALL_MEMBASE: { - MonoCallInst *call = (MonoCallInst*)ins; - if (call->signature->ret->type == MONO_TYPE_R4) { - MonoCallInst *call2; - MonoInst *iargs [1]; - MonoInst *conv; - - /* Convert the call into a call returning an int */ - MONO_INST_NEW_CALL (cfg, call2, OP_CALL); - memcpy (call2, call, sizeof (MonoCallInst)); - switch (ins->opcode) { - case OP_FCALL: - call2->inst.opcode = OP_CALL; - break; - case OP_FCALL_REG: - call2->inst.opcode = OP_CALL_REG; - break; - case OP_FCALL_MEMBASE: - call2->inst.opcode = OP_CALL_MEMBASE; - break; - default: - g_assert_not_reached (); - } - call2->inst.dreg = mono_alloc_ireg (cfg); - MONO_ADD_INS (cfg->cbb, (MonoInst*)call2); - - /* FIXME: Optimize this */ - - /* Emit an r4->r8 conversion */ - EMIT_NEW_VARLOADA_VREG (cfg, iargs [0], call2->inst.dreg, &mono_defaults.int32_class->byval_arg); - conv = mono_emit_jit_icall (cfg, mono_fload_r4, iargs); - conv->dreg = ins->dreg; - } else { - switch (ins->opcode) { - case OP_FCALL: - ins->opcode = OP_LCALL; - break; - case OP_FCALL_REG: - ins->opcode = OP_LCALL_REG; - break; - case OP_FCALL_MEMBASE: - ins->opcode = OP_LCALL_MEMBASE; - break; - default: - g_assert_not_reached (); - } - } - break; - } - case OP_FCOMPARE: { - MonoJitICallInfo *info; - MonoInst *iargs [2]; - MonoInst *call, *cmp, *br; - - /* Convert fcompare+fbcc to icall+icompare+beq */ - - info = mono_find_jit_opcode_emulation (ins->next->opcode); - g_assert (info); - - /* Create dummy MonoInst's for the arguments */ - MONO_INST_NEW (cfg, iargs [0], OP_ARG); - iargs [0]->dreg = ins->sreg1; - MONO_INST_NEW (cfg, iargs [1], OP_ARG); - iargs [1]->dreg = ins->sreg2; - - call = mono_emit_native_call (cfg, mono_icall_get_wrapper (info), info->sig, iargs); - - MONO_INST_NEW (cfg, cmp, OP_ICOMPARE_IMM); - cmp->sreg1 = call->dreg; - cmp->inst_imm = 0; - MONO_ADD_INS (cfg->cbb, cmp); - - MONO_INST_NEW (cfg, br, OP_IBNE_UN); - br->inst_many_bb = mono_mempool_alloc (cfg->mempool, sizeof (gpointer) * 2); - br->inst_true_bb = ins->next->inst_true_bb; - br->inst_false_bb = ins->next->inst_false_bb; - MONO_ADD_INS (cfg->cbb, br); - - /* The call sequence might include fp ins */ - restart = TRUE; - - /* Skip fbcc or fccc */ - NULLIFY_INS (ins->next); - break; - } - case OP_FCEQ: - case OP_FCGT: - case OP_FCGT_UN: - case OP_FCLT: - case OP_FCLT_UN: { - MonoJitICallInfo *info; - MonoInst *iargs [2]; - MonoInst *call; - - /* Convert fccc to icall+icompare+iceq */ - - info = mono_find_jit_opcode_emulation (ins->opcode); - g_assert (info); - - /* Create dummy MonoInst's for the arguments */ - MONO_INST_NEW (cfg, iargs [0], OP_ARG); - iargs [0]->dreg = ins->sreg1; - MONO_INST_NEW (cfg, iargs [1], OP_ARG); - iargs [1]->dreg = ins->sreg2; - - call = mono_emit_native_call (cfg, mono_icall_get_wrapper (info), info->sig, iargs); - - MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ICOMPARE_IMM, -1, call->dreg, 1); - MONO_EMIT_NEW_UNALU (cfg, OP_ICEQ, ins->dreg, -1); - - /* The call sequence might include fp ins */ - restart = TRUE; - break; - } - case OP_CKFINITE: { - MonoInst *iargs [2]; - MonoInst *call, *cmp; - - /* Convert to icall+icompare+cond_exc+move */ - - /* Create dummy MonoInst's for the arguments */ - MONO_INST_NEW (cfg, iargs [0], OP_ARG); - iargs [0]->dreg = ins->sreg1; - - call = mono_emit_jit_icall (cfg, mono_isfinite, iargs); - - MONO_INST_NEW (cfg, cmp, OP_ICOMPARE_IMM); - cmp->sreg1 = call->dreg; - cmp->inst_imm = 1; - MONO_ADD_INS (cfg->cbb, cmp); - - MONO_EMIT_NEW_COND_EXC (cfg, INE_UN, "ArithmeticException"); - - /* Do the assignment if the value is finite */ - MONO_EMIT_NEW_UNALU (cfg, OP_FMOVE, ins->dreg, ins->sreg1); - - restart = TRUE; - break; - } - default: - if (spec [MONO_INST_SRC1] == 'f' || spec [MONO_INST_SRC2] == 'f' || spec [MONO_INST_DEST] == 'f') { - mono_print_ins (ins); - g_assert_not_reached (); - } - break; - } - - g_assert (cfg->cbb == first_bb); - - if (cfg->cbb->code || (cfg->cbb != first_bb)) { - /* Replace the original instruction with the new code sequence */ - - mono_replace_ins (cfg, bb, ins, &prev, first_bb, cfg->cbb); - first_bb->code = first_bb->last_ins = NULL; - first_bb->in_count = first_bb->out_count = 0; - cfg->cbb = first_bb; - } - else - prev = ins; - } - } - - if (cfg->verbose_level > 3) mono_print_bb (bb, "AFTER HANDLE-SOFT-FLOAT "); - } - - mono_decompose_long_opts (cfg); -} - -#endif - static void emit_stloc_ir (MonoCompile *cfg, MonoInst **sp, MonoMethodHeader *header, int n) { @@ -5321,8 +5110,10 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b MonoInst *return_var, GList *dont_inline, MonoInst **inline_args, guint inline_offset, gboolean is_virtual_call) { + MonoError error; MonoInst *ins, **sp, **stack_start; MonoBasicBlock *bblock, *tblock = NULL, *init_localsbb = NULL; + MonoSimpleBasicBlock *bb = NULL; MonoMethod *cmethod, *method_definition; MonoInst **arg_array; MonoMethodHeader *header; @@ -5346,7 +5137,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b GSList *class_inits = NULL; gboolean dont_verify, dont_verify_stloc, readonly = FALSE; int context_used; - gboolean init_locals; + gboolean init_locals, seq_points, skip_dead_blocks; /* serialization and xdomain stuff may need access to private fields and methods */ dont_verify = method->klass->image->assembly->corlib_internal? TRUE: FALSE; @@ -5374,6 +5165,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b mono_jit_stats.cil_code_size += header->code_size; init_locals = header->init_locals; + seq_points = cfg->gen_seq_points && cfg->method == method; + /* * Methods without init_locals set could cause asserts in various passes * (#497220). @@ -5721,9 +5514,17 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b NEW_ARGLOAD (cfg, arg_ins, 0); MONO_ADD_INS (cfg->cbb, arg_ins); - cfg->flags |= MONO_CFG_HAS_CHECK_THIS; - MONO_EMIT_NEW_UNALU (cfg, OP_CHECK_THIS, -1, arg_ins->dreg); - MONO_EMIT_NEW_UNALU (cfg, OP_NOT_NULL, -1, arg_ins->dreg); + MONO_EMIT_NEW_CHECK_THIS (cfg, arg_ins->dreg); + } + + skip_dead_blocks = !dont_verify; + if (skip_dead_blocks) { + bb = mono_basic_block_split (method, &error); + if (!mono_error_ok (&error)) { + mono_error_cleanup (&error); + UNVERIFIED; + } + g_assert (bb); } /* we use a spare stack slot in SWITCH and NEWOBJ and others */ @@ -5733,7 +5534,6 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b start_new_bblock = 0; cfg->cbb = bblock; while (ip < end) { - if (cfg->method == method) cfg->real_offset = ip - header->code; else @@ -5784,6 +5584,38 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b } } + if (skip_dead_blocks) { + int ip_offset = ip - header->code; + + if (ip_offset == bb->end) + bb = bb->next; + + if (bb->dead) { + int op_size = mono_opcode_size (ip, end); + g_assert (op_size > 0); /*The BB formation pass must catch all bad ops*/ + + if (cfg->verbose_level > 3) printf ("SKIPPING DEAD OP at %x\n", ip_offset); + + if (ip_offset + op_size == bb->end) { + MONO_INST_NEW (cfg, ins, OP_NOP); + MONO_ADD_INS (bblock, ins); + start_new_bblock = 1; + } + + ip += op_size; + continue; + } + } + /* + * Sequence points are points where the debugger can place a breakpoint. + * Currently, we generate these automatically at points where the IL + * stack is empty. + */ + if (seq_points && sp == stack_start) { + NEW_SEQ_POINT (cfg, ins, ip - header->code, TRUE); + MONO_ADD_INS (cfg->cbb, ins); + } + bblock->real_offset = cfg->real_offset; if ((cfg->method == method) && cfg->coverage_info) { @@ -5815,7 +5647,10 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b MONO_ADD_INS (bblock, ins); break; case CEE_BREAK: - MONO_INST_NEW (cfg, ins, OP_BREAK); + if (should_insert_brekpoint (cfg->method)) + MONO_INST_NEW (cfg, ins, OP_BREAK); + else + MONO_INST_NEW (cfg, ins, OP_NOP); ip++; MONO_ADD_INS (bblock, ins); break; @@ -6168,6 +6003,21 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b fsig = mono_metadata_parse_signature (image, token); 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; @@ -6263,15 +6113,12 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b * We have the `constrained.' prefix opcode. */ if (constrained_call->valuetype && !cmethod->klass->valuetype) { - int dreg; - /* * 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'. */ - dreg = alloc_dreg (cfg, STACK_VTYPE); - EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADV_MEMBASE, dreg, sp [0]->dreg, 0); + 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); CHECK_CFG_EXCEPTION; @@ -6377,12 +6224,17 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b if (pass_mrgctx) { g_assert (!vtable_arg); - if (context_used) { - vtable_arg = emit_get_rgctx_method (cfg, context_used, cmethod, MONO_RGCTX_INFO_METHOD_RGCTX); - } else { - EMIT_NEW_METHOD_RGCTX_CONST (cfg, vtable_arg, cmethod); + if (!cfg->compile_aot) { + /* + * emit_get_rgctx_method () calls mono_class_vtable () so check + * for type load errors before. + */ + mono_class_vtable (cfg->domain, cmethod->klass); + CHECK_TYPELOAD (cmethod->klass); } + vtable_arg = emit_get_rgctx_method (cfg, context_used, cmethod, MONO_RGCTX_INFO_METHOD_RGCTX); + if (!(cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL) || MONO_METHOD_IS_FINAL (cmethod)) { if (virtual) @@ -6399,13 +6251,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b cmethod, MONO_RGCTX_INFO_METHOD); } - if (check_this) { - MonoInst *check; - - MONO_INST_NEW (cfg, check, OP_CHECK_THIS); - check->sreg1 = sp [0]->dreg; - MONO_ADD_INS (cfg->cbb, check); - } + if (check_this) + MONO_EMIT_NEW_CHECK_THIS (cfg, sp [0]->dreg); /* Calling virtual generic methods */ if (cmethod && virtual && @@ -6421,17 +6268,14 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b /* Prevent inlining of methods that contain indirect calls */ INLINE_FAILURE; -#if MONO_ARCH_HAVE_GENERALIZED_IMT_THUNK && !defined(ENABLE_LLVM) - if (cmethod->wrapper_type == MONO_WRAPPER_NONE && mono_use_imt) { +#if MONO_ARCH_HAVE_GENERALIZED_IMT_THUNK + /* The llvm vcall trampolines doesn't support generic virtual calls yet */ + if (cmethod->wrapper_type == MONO_WRAPPER_NONE && mono_use_imt && !mono_use_llvm) { g_assert (!imt_arg); - if (context_used) { - imt_arg = emit_get_rgctx_method (cfg, context_used, - cmethod, MONO_RGCTX_INFO_METHOD); - - } else { + if (!context_used) g_assert (cmethod->is_inflated); - EMIT_NEW_METHODCONST (cfg, imt_arg, cmethod); - } + imt_arg = emit_get_rgctx_method (cfg, context_used, + cmethod, MONO_RGCTX_INFO_METHOD); ins = mono_emit_method_call_full (cfg, cmethod, fsig, sp, sp [0], imt_arg); } else #endif @@ -6444,17 +6288,11 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b this_arg_temp = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL); EMIT_NEW_TEMPLOAD (cfg, iargs [0], this_temp->inst_c0); - if (context_used) { - iargs [1] = emit_get_rgctx_method (cfg, context_used, - cmethod, MONO_RGCTX_INFO_METHOD); - EMIT_NEW_TEMPLOADA (cfg, iargs [2], this_arg_temp->inst_c0); - addr = mono_emit_jit_icall (cfg, - mono_helper_compile_generic_method, iargs); - } else { - EMIT_NEW_METHODCONST (cfg, iargs [1], cmethod); - EMIT_NEW_TEMPLOADA (cfg, iargs [2], this_arg_temp->inst_c0); - addr = mono_emit_jit_icall (cfg, mono_helper_compile_generic_method, iargs); - } + iargs [1] = emit_get_rgctx_method (cfg, context_used, + cmethod, MONO_RGCTX_INFO_METHOD); + EMIT_NEW_TEMPLOADA (cfg, iargs [2], this_arg_temp->inst_c0); + addr = mono_emit_jit_icall (cfg, + mono_helper_compile_generic_method, iargs); EMIT_NEW_TEMPLOAD (cfg, sp [0], this_arg_temp->inst_c0); @@ -6761,6 +6599,18 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b if (cfg->ret) { MonoType *ret_type = mono_method_signature (method)->ret; + if (seq_points) { + /* + * Place a seq point here too even through the IL stack is not + * empty, so a step over on + * call + * ret + * will work correctly. + */ + NEW_SEQ_POINT (cfg, ins, ip - header->code, TRUE); + MONO_ADD_INS (cfg->cbb, ins); + } + g_assert (!return_var); CHECK_STACK (1); --sp; @@ -6779,7 +6629,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b } } else { #ifdef MONO_ARCH_SOFT_FLOAT - if (!ret_type->byref && ret_type->type == MONO_TYPE_R4) { + if (COMPILE_SOFT_FLOAT (cfg) && !ret_type->byref && ret_type->type == MONO_TYPE_R4) { MonoInst *iargs [1]; MonoInst *conv; @@ -7187,7 +7037,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b #endif } else - ins->inst_p1 = (gpointer)(gssize)(sp [1]->inst_c0); + ins->inst_imm = (gssize)(sp [1]->inst_c0); ins->sreg2 = -1; /* Might be followed by an instruction added by ADD_WIDEN_OP */ @@ -7456,6 +7306,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b if (!cmethod) goto load_error; fsig = mono_method_get_signature (cmethod, image, token); + if (!fsig) + goto load_error; mono_save_token_info (cfg, image, token, cmethod); @@ -7476,12 +7328,11 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b if (cmethod->klass->valuetype && mono_class_generic_sharing_enabled (cmethod->klass) && mono_method_is_generic_sharable_impl (cmethod, TRUE)) { if (cmethod->is_inflated && mono_method_get_context (cmethod)->method_inst) { - if (context_used) { - vtable_arg = emit_get_rgctx_method (cfg, context_used, - cmethod, MONO_RGCTX_INFO_METHOD_RGCTX); - } else { - EMIT_NEW_METHOD_RGCTX_CONST (cfg, vtable_arg, cmethod); - } + mono_class_vtable (cfg->domain, cmethod->klass); + CHECK_TYPELOAD (cmethod->klass); + + vtable_arg = emit_get_rgctx_method (cfg, context_used, + cmethod, MONO_RGCTX_INFO_METHOD_RGCTX); } else { if (context_used) { vtable_arg = emit_get_rgctx_klass (cfg, context_used, @@ -7552,18 +7403,16 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b if (mini_class_is_system_array (cmethod->klass)) { g_assert (!vtable_arg); - if (context_used) { - *sp = emit_get_rgctx_method (cfg, context_used, - cmethod, MONO_RGCTX_INFO_METHOD); - } else { - EMIT_NEW_METHODCONST (cfg, *sp, cmethod); - } + *sp = emit_get_rgctx_method (cfg, context_used, + cmethod, MONO_RGCTX_INFO_METHOD); /* Avoid varargs in the common case */ if (fsig->param_count == 1) alloc = mono_emit_jit_icall (cfg, mono_array_new_1, sp); else if (fsig->param_count == 2) alloc = mono_emit_jit_icall (cfg, mono_array_new_2, sp); + else if (fsig->param_count == 3) + alloc = mono_emit_jit_icall (cfg, mono_array_new_3, sp); else alloc = handle_array_new (cfg, fsig->param_count, sp, ip); } else if (cmethod->string_ctor) { @@ -7687,21 +7536,20 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b if (cfg->generic_sharing_context) context_used = mono_class_check_context_used (klass); - if (context_used) { + if (!context_used && mono_class_has_variant_generic_params (klass)) { MonoInst *args [2]; /* obj */ args [0] = *sp; /* klass */ - args [1] = emit_get_rgctx_klass (cfg, context_used, - klass, MONO_RGCTX_INFO_KLASS); + EMIT_NEW_CLASSCONST (cfg, args [1], klass); ins = mono_emit_jit_icall (cfg, mono_object_castclass, args); *sp ++ = ins; ip += 5; inline_costs += 2; - } else if (klass->marshalbyref || klass->flags & TYPE_ATTRIBUTE_INTERFACE) { + } else if (!context_used && (klass->marshalbyref || klass->flags & TYPE_ATTRIBUTE_INTERFACE)) { MonoMethod *mono_castclass; MonoInst *iargs [1]; int costs; @@ -7722,7 +7570,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b inline_costs += costs; } else { - ins = handle_castclass (cfg, klass, *sp); + ins = handle_castclass (cfg, klass, *sp, context_used); CHECK_CFG_EXCEPTION; bblock = cfg->cbb; *sp ++ = ins; @@ -7742,20 +7590,20 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b if (cfg->generic_sharing_context) context_used = mono_class_check_context_used (klass); - if (context_used) { + if (!context_used && mono_class_has_variant_generic_params (klass)) { MonoInst *args [2]; /* obj */ args [0] = *sp; /* klass */ - args [1] = emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_KLASS); + EMIT_NEW_CLASSCONST (cfg, args [1], klass); *sp = mono_emit_jit_icall (cfg, mono_object_isinst, args); sp++; ip += 5; inline_costs += 2; - } else if (klass->marshalbyref || klass->flags & TYPE_ATTRIBUTE_INTERFACE) { + } else if (!context_used && (klass->marshalbyref || klass->flags & TYPE_ATTRIBUTE_INTERFACE)) { MonoMethod *mono_isinst; MonoInst *iargs [1]; int costs; @@ -7776,7 +7624,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b inline_costs += costs; } else { - ins = handle_isinst (cfg, klass, *sp); + ins = handle_isinst (cfg, klass, *sp, context_used); CHECK_CFG_EXCEPTION; bblock = cfg->cbb; *sp ++ = ins; @@ -7830,7 +7678,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b *sp++ = iargs [0]; inline_costs += costs; } else { - ins = handle_castclass (cfg, klass, *sp); + ins = handle_castclass (cfg, klass, *sp, 0); CHECK_CFG_EXCEPTION; bblock = cfg->cbb; *sp ++ = ins; @@ -8025,7 +7873,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b field->offset); iargs [4] = sp [1]; - if (cfg->opt & MONO_OPT_INLINE) { + if (cfg->opt & MONO_OPT_INLINE || cfg->compile_aot) { costs = inline_method (cfg, stfld_wrapper, mono_method_signature (stfld_wrapper), iargs, ip, cfg->real_offset, dont_inline, TRUE); g_assert (costs > 0); @@ -8040,6 +7888,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b } else { MonoInst *store; + MONO_EMIT_NULL_CHECK (cfg, sp [0]->dreg); + EMIT_NEW_STORE_MEMBASE_TYPE (cfg, store, field->type, sp [0]->dreg, foffset, sp [1]->dreg); #if HAVE_WRITE_BARRIERS @@ -8102,6 +7952,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b sp [0] = ins; } + MONO_EMIT_NULL_CHECK (cfg, sp [0]->dreg); + if (*ip == CEE_LDFLDA) { dreg = alloc_preg (cfg); @@ -8114,6 +7966,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, load, field->type, sp [0]->dreg, foffset); load->flags |= ins_flag; + load->flags |= MONO_INST_FAULT; *sp++ = load; } } @@ -8431,22 +8284,38 @@ 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 = ins; + *sp = mono_decompose_opcode (cfg, ins); } if (context_used) { - MonoInst *args [2]; + MonoInst *args [3]; + MonoClass *array_class = mono_array_class_get (klass, 1); + /* FIXME: we cannot get a managed + allocator because we can't get the + open generic class's vtable. We + have the same problem in + handle_alloc_from_inst(). This + needs to be solved so that we can + have managed allocs of shared + generic classes. */ + /* + MonoVTable *array_class_vtable = mono_class_vtable (cfg->domain, array_class); + MonoMethod *managed_alloc = mono_gc_get_managed_array_allocator (array_class_vtable, 1); + */ + MonoMethod *managed_alloc = NULL; /* FIXME: Decompose later to help abcrem */ /* vtable */ args [0] = emit_get_rgctx_klass (cfg, context_used, - mono_array_class_get (klass, 1), MONO_RGCTX_INFO_VTABLE); - + array_class, MONO_RGCTX_INFO_VTABLE); /* array len */ args [1] = sp [0]; - ins = mono_emit_jit_icall (cfg, mono_array_new_specific, args); + if (managed_alloc) + ins = mono_emit_method_call (cfg, managed_alloc, args, NULL); + else + ins = mono_emit_jit_icall (cfg, mono_array_new_specific, args); } else { if (cfg->opt & MONO_OPT_SHARED) { /* Decompose now to avoid problems with references to the domainvar */ @@ -8957,6 +8826,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b /* * If this leave statement is in a catch block, check for a * pending exception, and rethrow it if necessary. + * We avoid doing this in runtime invoke wrappers, since those are called + * by native code which excepts the wrapper to catch all exceptions. */ for (i = 0; i < header->num_clauses; ++i) { MonoExceptionClause *clause = &header->clauses [i]; @@ -8967,7 +8838,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b * The ordering of the exception clauses guarantees that we find the * innermost clause. */ - if (MONO_OFFSET_IN_HANDLER (clause, ip - header->code) && (clause->flags == MONO_EXCEPTION_CLAUSE_NONE) && (ip - header->code + ((*ip == CEE_LEAVE) ? 5 : 2)) <= (clause->handler_offset + clause->handler_len)) { + if (MONO_OFFSET_IN_HANDLER (clause, ip - header->code) && (clause->flags == MONO_EXCEPTION_CLAUSE_NONE) && (ip - header->code + ((*ip == CEE_LEAVE) ? 5 : 2)) <= (clause->handler_offset + clause->handler_len) && method->wrapper_type != MONO_WRAPPER_RUNTIME_INVOKE) { MonoInst *exc_ins; MonoBasicBlock *dont_throw; @@ -9005,6 +8876,17 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b ins->inst_target_bb = tblock; MONO_ADD_INS (bblock, ins); bblock->has_call_handler = 1; + if (COMPILE_LLVM (cfg)) { + MonoBasicBlock *target_bb; + + /* + * Link the finally bblock with the target, since it will + * conceptually branch there. + * FIXME: Have to link the bblock containing the endfinally. + */ + GET_BBLOCK (cfg, target_bb, target); + link_bblock (cfg, tblock, target_bb); + } } g_list_free (handlers); } @@ -9270,6 +9152,37 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b *sp++ = ins; ip += 6; break; + case CEE_MONO_DYN_CALL: { + MonoCallInst *call; + + /* It would be easier to call a trampoline, but that would put an + * extra frame on the stack, confusing exception handling. So + * implement it inline using an opcode for now. + */ + + if (!cfg->dyn_call_var) { + cfg->dyn_call_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL); + /* prevent it from being register allocated */ + cfg->dyn_call_var->flags |= MONO_INST_INDIRECT; + } + + /* Has to use a call inst since it local regalloc expects it */ + MONO_INST_NEW_CALL (cfg, call, OP_DYN_CALL); + ins = (MonoInst*)call; + sp -= 2; + ins->sreg1 = sp [0]->dreg; + ins->sreg2 = sp [1]->dreg; + MONO_ADD_INS (bblock, ins); + +#ifdef MONO_ARCH_DYN_CALL_PARAM_AREA + cfg->param_area = MAX (cfg->param_area, MONO_ARCH_DYN_CALL_PARAM_AREA); +#endif + + ip += 2; + inline_costs += 10 * num_calls++; + + break; + } default: g_error ("opcode 0x%02x 0x%02x not handled", MONO_CUSTOM_PREFIX, ip [1]); break; @@ -9381,37 +9294,37 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b */ #if defined(MONO_ARCH_HAVE_CREATE_DELEGATE_TRAMPOLINE) && !defined(HAVE_WRITE_BARRIERS) /* FIXME: SGEN support */ - /* FIXME: handle shared static generic methods */ - /* FIXME: handle this in shared code */ - if (!needs_static_rgctx_invoke && !context_used && (sp > stack_start) && (ip + 6 + 5 < end) && ip_in_bb (cfg, bblock, ip + 6) && (ip [6] == CEE_NEWOBJ)) { + if ((sp > stack_start) && (ip + 6 + 5 < end) && ip_in_bb (cfg, bblock, ip + 6) && (ip [6] == CEE_NEWOBJ)) { MonoMethod *ctor_method = mini_get_method (cfg, method, read32 (ip + 7), NULL, generic_context); if (ctor_method && (ctor_method->klass->parent == mono_defaults.multicastdelegate_class)) { MonoInst *target_ins; MonoMethod *invoke; + int invoke_context_used = 0; invoke = mono_get_delegate_invoke (ctor_method->klass); if (!invoke || !mono_method_signature (invoke)) goto load_error; - ip += 6; - if (cfg->verbose_level > 3) - g_print ("converting (in B%d: stack: %d) %s", bblock->block_num, (int)(sp - stack_start), mono_disasm_code_one (NULL, method, ip, NULL)); - target_ins = sp [-1]; - sp --; - *sp = handle_delegate_ctor (cfg, ctor_method->klass, target_ins, cmethod); - CHECK_CFG_EXCEPTION; - ip += 5; - sp ++; - break; + if (cfg->generic_sharing_context) + invoke_context_used = mono_method_check_context_used (invoke); + + if (invoke_context_used == 0) { + ip += 6; + if (cfg->verbose_level > 3) + g_print ("converting (in B%d: stack: %d) %s", bblock->block_num, (int)(sp - stack_start), mono_disasm_code_one (NULL, method, ip, NULL)); + target_ins = sp [-1]; + sp --; + *sp = handle_delegate_ctor (cfg, ctor_method->klass, target_ins, cmethod, context_used); + CHECK_CFG_EXCEPTION; + ip += 5; + sp ++; + break; + } } } #endif - if (context_used) { - argconst = emit_get_rgctx_method (cfg, context_used, cmethod, MONO_RGCTX_INFO_METHOD); - } else { - EMIT_NEW_METHODCONST (cfg, argconst, cmethod); - } + argconst = emit_get_rgctx_method (cfg, context_used, cmethod, MONO_RGCTX_INFO_METHOD); ins = mono_emit_jit_icall (cfg, mono_ldftn, &argconst); *sp++ = ins; @@ -9444,14 +9357,13 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b --sp; args [0] = *sp; - if (context_used) { - args [1] = emit_get_rgctx_method (cfg, context_used, - cmethod, MONO_RGCTX_INFO_METHOD); + args [1] = emit_get_rgctx_method (cfg, context_used, + cmethod, MONO_RGCTX_INFO_METHOD); + + if (context_used) *sp++ = mono_emit_jit_icall (cfg, mono_ldvirtfn_gshared, args); - } else { - EMIT_NEW_METHODCONST (cfg, args [1], cmethod); + else *sp++ = mono_emit_jit_icall (cfg, mono_ldvirtfn, args); - } ip += 6; inline_costs += 10 * num_calls++; @@ -9729,13 +9641,27 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b readonly = TRUE; ip += 2; break; + + case CEE_UNUSED56: + case CEE_UNUSED57: + case CEE_UNUSED70: + case CEE_UNUSED: + case CEE_UNUSED99: + UNVERIFIED; + default: - g_error ("opcode 0xfe 0x%02x not handled", ip [1]); + g_warning ("opcode 0xfe 0x%02x not handled", ip [1]); + UNVERIFIED; } break; } + case CEE_UNUSED58: + case CEE_UNUSED1: + UNVERIFIED; + default: - g_error ("opcode 0x%02x not handled", *ip); + g_warning ("opcode 0x%02x not handled", *ip); + UNVERIFIED; } } if (start_new_bblock != 1) @@ -9804,6 +9730,25 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b } } + if (cfg->init_ref_vars && cfg->method == method) { + /* Emit initialization for ref vars */ + // FIXME: Avoid duplication initialization for IL locals. + for (i = 0; i < cfg->num_varinfo; ++i) { + MonoInst *ins = cfg->varinfo [i]; + + if (ins->opcode == OP_LOCAL && ins->type == STACK_OBJ) + MONO_EMIT_NEW_PCONST (cfg, ins->dreg, NULL); + } + } + + /* Add a sequence point for method entry/exit events */ + if (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); + MONO_ADD_INS (cfg->bb_exit, ins); + } + cfg->ip = NULL; if (cfg->method == method) { @@ -9839,22 +9784,26 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b exception_exit: g_assert (cfg->exception_type != MONO_EXCEPTION_NONE); g_slist_free (class_inits); + mono_basic_block_free (bb); dont_inline = g_list_remove (dont_inline, method); return -1; inline_failure: g_slist_free (class_inits); + mono_basic_block_free (bb); dont_inline = g_list_remove (dont_inline, method); return -1; load_error: g_slist_free (class_inits); + mono_basic_block_free (bb); dont_inline = g_list_remove (dont_inline, method); cfg->exception_type = MONO_EXCEPTION_TYPE_LOAD; return -1; unverified: g_slist_free (class_inits); + mono_basic_block_free (bb); dont_inline = g_list_remove (dont_inline, method); set_exception_type_from_invalid_il (cfg, method, ip); return -1; @@ -10432,6 +10381,9 @@ mono_handle_global_vregs (MonoCompile *cfg) case 'i': mono_compile_create_var_for_vreg (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL, vreg); break; + case 'l': + mono_compile_create_var_for_vreg (cfg, &mono_defaults.int64_class->byval_arg, OP_LOCAL, vreg); + break; case 'f': mono_compile_create_var_for_vreg (cfg, &mono_defaults.double_class->byval_arg, OP_LOCAL, vreg); break; @@ -10595,12 +10547,13 @@ mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local_opts) if ((ins->opcode != OP_REGVAR) && !(ins->flags & MONO_INST_IS_DEAD)) { switch (ins->type) { -#ifdef MONO_ARCH_SOFT_FLOAT case STACK_R8: -#endif case STACK_I8: { MonoInst *tree; + if (ins->type == STACK_R8 && !COMPILE_SOFT_FLOAT (cfg)) + break; + g_assert (ins->opcode == OP_REGOFFSET); tree = get_vreg_to_inst (cfg, ins->dreg + 1); @@ -10642,6 +10595,10 @@ mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local_opts) * don't split live ranges, these will precisely describe the live range of * the variable, i.e. the instruction range where a valid value can be found * in the variables location. + * The live range is computed using the liveness info computed by the liveness pass. + * We can't use vmv->range, since that is an abstract live range, and we need + * one which is instruction precise. + * FIXME: Variables used in out-of-line bblocks have a hole in their live range. */ /* FIXME: Only do this if debugging info is requested */ live_range_start = g_new0 (MonoInst*, cfg->next_vreg); @@ -10786,12 +10743,10 @@ mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local_opts) lvreg = 0; -#ifdef MONO_ARCH_SOFT_FLOAT - if (store_opcode == OP_STORER8_MEMBASE_REG) { + if (COMPILE_SOFT_FLOAT (cfg) && store_opcode == OP_STORER8_MEMBASE_REG) { regtype = 'l'; store_opcode = OP_STOREI8_MEMBASE_REG; } -#endif ins->dreg = alloc_dreg (cfg, stacktypes [regtype]); @@ -10995,6 +10950,29 @@ mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local_opts) if (cfg->verbose_level > 2) mono_print_ins_index (1, ins); } + + /* Extend the live range based on the liveness info */ + if (cfg->compute_precise_live_ranges && bb->live_out_set && bb->code) { + for (i = 0; i < cfg->num_varinfo; i ++) { + MonoMethodVar *vi = MONO_VARINFO (cfg, i); + + if (vreg_is_volatile (cfg, vi->vreg)) + /* The liveness info is incomplete */ + continue; + + if (mono_bitset_test_fast (bb->live_in_set, i) && !live_range_start [vi->vreg]) { + /* Live from at least the first ins of this bb */ + live_range_start [vi->vreg] = bb->code; + live_range_start_bb [vi->vreg] = bb; + } + + if (mono_bitset_test_fast (bb->live_out_set, i)) { + /* Live at least until the last ins of this bb */ + live_range_end [vi->vreg] = bb->last_ins; + live_range_end_bb [vi->vreg] = bb; + } + } + } } #ifdef MONO_ARCH_HAVE_LIVERANGE_OPS @@ -11002,21 +10980,26 @@ mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local_opts) * Emit LIVERANGE_START/LIVERANGE_END opcodes, the backend will implement them * by storing the current native offset into MonoMethodVar->live_range_start/end. */ - for (i = 0; i < cfg->num_varinfo; ++i) { - int vreg = MONO_VARINFO (cfg, i)->vreg; - MonoInst *ins; - - if (live_range_start [vreg]) { - MONO_INST_NEW (cfg, ins, OP_LIVERANGE_START); - ins->inst_c0 = i; - ins->inst_c1 = vreg; - mono_bblock_insert_after_ins (live_range_start_bb [vreg], live_range_start [vreg], ins); - } - if (live_range_end [vreg]) { - MONO_INST_NEW (cfg, ins, OP_LIVERANGE_END); - ins->inst_c0 = i; - ins->inst_c1 = vreg; - mono_bblock_insert_after_ins (live_range_end_bb [vreg], live_range_end [vreg], ins); + if (cfg->compute_precise_live_ranges && cfg->comp_done & MONO_COMP_LIVENESS) { + for (i = 0; i < cfg->num_varinfo; ++i) { + int vreg = MONO_VARINFO (cfg, i)->vreg; + MonoInst *ins; + + if (live_range_start [vreg]) { + MONO_INST_NEW (cfg, ins, OP_LIVERANGE_START); + ins->inst_c0 = i; + ins->inst_c1 = vreg; + mono_bblock_insert_after_ins (live_range_start_bb [vreg], live_range_start [vreg], ins); + } + if (live_range_end [vreg]) { + MONO_INST_NEW (cfg, ins, OP_LIVERANGE_END); + ins->inst_c0 = i; + ins->inst_c1 = vreg; + if (live_range_end [vreg] == live_range_end_bb [vreg]->last_ins) + mono_add_ins_to_end (live_range_end_bb [vreg], ins); + else + mono_bblock_insert_after_ins (live_range_end_bb [vreg], live_range_end [vreg], ins); + } } } #endif