X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmini%2Fmethod-to-ir.c;h=62bc0f5fc52ad32eaa28fe5b38d88c038492544f;hb=f704d56c378b28e252572db4730a6e13edc14aa0;hp=87a0974d0d62ea8ecff10fe43232a56e980cf247;hpb=0e1a4d8a68024e4f77f9d415de5734140d77b824;p=mono.git diff --git a/mono/mini/method-to-ir.c b/mono/mini/method-to-ir.c index 87a0974d0d6..62bc0f5fc52 100644 --- a/mono/mini/method-to-ir.c +++ b/mono/mini/method-to-ir.c @@ -1,5 +1,6 @@ -/* - * method-to-ir.c: Convert CIL to the JIT internal representation +/** + * \file + * Convert CIL to the JIT internal representation * * Author: * Paolo Molaro (lupus@ximian.com) @@ -13,6 +14,7 @@ #include #include +#include "mini.h" #ifndef DISABLE_JIT @@ -35,7 +37,6 @@ #endif #include -#include "mini.h" #include #include #include @@ -50,8 +51,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -59,8 +59,6 @@ #include #include #include -#include -#include #include #include #include @@ -145,22 +143,25 @@ static int stind_to_store_membase (int opcode); int mono_op_to_op_imm (int opcode); int mono_op_to_op_imm_noemul (int opcode); -MONO_API MonoInst* mono_emit_native_call (MonoCompile *cfg, gconstpointer func, MonoMethodSignature *sig, MonoInst **args); - static int inline_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **sp, guchar *ip, guint real_offset, gboolean inline_always); static MonoInst* emit_llvmonly_virtual_call (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, int context_used, MonoInst **sp); +inline static MonoInst* +mono_emit_calli (MonoCompile *cfg, MonoMethodSignature *sig, MonoInst **args, MonoInst *addr, MonoInst *imt_arg, MonoInst *rgctx_arg); + /* helper methods signatures */ static MonoMethodSignature *helper_sig_domain_get; static MonoMethodSignature *helper_sig_rgctx_lazy_fetch_trampoline; static MonoMethodSignature *helper_sig_llvmonly_imt_trampoline; static MonoMethodSignature *helper_sig_jit_thread_attach; +static MonoMethodSignature *helper_sig_get_tls_tramp; +static MonoMethodSignature *helper_sig_set_tls_tramp; /* type loading helpers */ -static GENERATE_GET_CLASS_WITH_CACHE (runtime_helpers, System.Runtime.CompilerServices, RuntimeHelpers) -static GENERATE_TRY_GET_CLASS_WITH_CACHE (debuggable_attribute, System.Diagnostics, DebuggableAttribute) +static GENERATE_GET_CLASS_WITH_CACHE (runtime_helpers, "System.Runtime.CompilerServices", "RuntimeHelpers") +static GENERATE_TRY_GET_CLASS_WITH_CACHE (debuggable_attribute, "System.Diagnostics", "DebuggableAttribute") /* * Instruction metadata @@ -203,12 +204,6 @@ const gint8 ins_sreg_counts[] = { #undef MINI_OP #undef MINI_OP3 -#define MONO_INIT_VARINFO(vi,id) do { \ - (vi)->range.first_use.pos.bid = 0xffff; \ - (vi)->reg = -1; \ - (vi)->idx = (id); \ -} while (0) - guint32 mono_alloc_ireg (MonoCompile *cfg) { @@ -350,14 +345,19 @@ mono_print_bb (MonoBasicBlock *bb, const char *msg) { int i; MonoInst *tree; + GString *str = g_string_new (""); - printf ("\n%s %d: [IN: ", msg, bb->block_num); + g_string_append_printf (str, "%s %d: [IN: ", msg, bb->block_num); for (i = 0; i < bb->in_count; ++i) - printf (" BB%d(%d)", bb->in_bb [i]->block_num, bb->in_bb [i]->dfn); - printf (", OUT: "); + g_string_append_printf (str, " BB%d(%d)", bb->in_bb [i]->block_num, bb->in_bb [i]->dfn); + g_string_append_printf (str, ", OUT: "); for (i = 0; i < bb->out_count; ++i) - printf (" BB%d(%d)", bb->out_bb [i]->block_num, bb->out_bb [i]->dfn); - printf (" ]\n"); + g_string_append_printf (str, " BB%d(%d)", bb->out_bb [i]->block_num, bb->out_bb [i]->dfn); + g_string_append_printf (str, " ]\n"); + + g_print ("%s", str->str); + g_string_free (str, TRUE); + for (tree = bb->code; tree; tree = tree->next) mono_print_ins_index (-1, tree); } @@ -369,6 +369,8 @@ mono_create_helper_signatures (void) helper_sig_rgctx_lazy_fetch_trampoline = mono_create_icall_signature ("ptr ptr"); helper_sig_llvmonly_imt_trampoline = mono_create_icall_signature ("ptr ptr ptr"); helper_sig_jit_thread_attach = mono_create_icall_signature ("ptr ptr"); + helper_sig_get_tls_tramp = mono_create_icall_signature ("ptr"); + helper_sig_set_tls_tramp = mono_create_icall_signature ("void ptr"); } static MONO_NEVER_INLINE void @@ -1259,16 +1261,22 @@ mono_get_got_var (MonoCompile *cfg) return cfg->got_var; } -static MonoInst * -mono_get_vtable_var (MonoCompile *cfg) +static void +mono_create_rgctx_var (MonoCompile *cfg) { - g_assert (cfg->gshared); - if (!cfg->rgctx_var) { cfg->rgctx_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL); /* force the var to be stack allocated */ cfg->rgctx_var->flags |= MONO_INST_VOLATILE; } +} + +static MonoInst * +mono_get_vtable_var (MonoCompile *cfg) +{ + g_assert (cfg->gshared); + + mono_create_rgctx_var (cfg); return cfg->rgctx_var; } @@ -1574,267 +1582,10 @@ emit_runtime_constant (MonoCompile *cfg, MonoJumpInfoType patch_type, gpointer d return ins; } -static void -mini_emit_interface_bitmap_check (MonoCompile *cfg, int intf_bit_reg, int base_reg, int offset, MonoClass *klass) -{ - int ibitmap_reg = alloc_preg (cfg); -#ifdef COMPRESSED_INTERFACE_BITMAP - MonoInst *args [2]; - MonoInst *res, *ins; - NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, ibitmap_reg, base_reg, offset); - MONO_ADD_INS (cfg->cbb, ins); - args [0] = ins; - args [1] = emit_runtime_constant (cfg, MONO_PATCH_INFO_IID, klass); - res = mono_emit_jit_icall (cfg, mono_class_interface_match, args); - MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, intf_bit_reg, res->dreg); -#else - int ibitmap_byte_reg = alloc_preg (cfg); - - MONO_EMIT_NEW_LOAD_MEMBASE (cfg, ibitmap_reg, base_reg, offset); - - if (cfg->compile_aot) { - int iid_reg = alloc_preg (cfg); - int shifted_iid_reg = alloc_preg (cfg); - int ibitmap_byte_address_reg = alloc_preg (cfg); - int masked_iid_reg = alloc_preg (cfg); - int iid_one_bit_reg = alloc_preg (cfg); - int iid_bit_reg = alloc_preg (cfg); - MONO_EMIT_NEW_AOTCONST (cfg, iid_reg, klass, MONO_PATCH_INFO_IID); - MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SHR_IMM, shifted_iid_reg, iid_reg, 3); - MONO_EMIT_NEW_BIALU (cfg, OP_PADD, ibitmap_byte_address_reg, ibitmap_reg, shifted_iid_reg); - MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU1_MEMBASE, ibitmap_byte_reg, ibitmap_byte_address_reg, 0); - MONO_EMIT_NEW_BIALU_IMM (cfg, OP_IAND_IMM, masked_iid_reg, iid_reg, 7); - MONO_EMIT_NEW_ICONST (cfg, iid_one_bit_reg, 1); - MONO_EMIT_NEW_BIALU (cfg, OP_ISHL, iid_bit_reg, iid_one_bit_reg, masked_iid_reg); - MONO_EMIT_NEW_BIALU (cfg, OP_IAND, intf_bit_reg, ibitmap_byte_reg, iid_bit_reg); - } else { - MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI1_MEMBASE, ibitmap_byte_reg, ibitmap_reg, klass->interface_id >> 3); - MONO_EMIT_NEW_BIALU_IMM (cfg, OP_AND_IMM, intf_bit_reg, ibitmap_byte_reg, 1 << (klass->interface_id & 7)); - } -#endif -} - -/* - * Emit code which loads into "intf_bit_reg" a nonzero value if the MonoClass - * stored in "klass_reg" implements the interface "klass". - */ -static void -mini_emit_load_intf_bit_reg_class (MonoCompile *cfg, int intf_bit_reg, int klass_reg, MonoClass *klass) -{ - mini_emit_interface_bitmap_check (cfg, intf_bit_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, interface_bitmap), klass); -} - -/* - * Emit code which loads into "intf_bit_reg" a nonzero value if the MonoVTable - * stored in "vtable_reg" implements the interface "klass". - */ -static void -mini_emit_load_intf_bit_reg_vtable (MonoCompile *cfg, int intf_bit_reg, int vtable_reg, MonoClass *klass) -{ - mini_emit_interface_bitmap_check (cfg, intf_bit_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, interface_bitmap), klass); -} - -/* - * Emit code which checks whenever the interface id of @klass is smaller than - * than the value given by max_iid_reg. -*/ -static void -mini_emit_max_iid_check (MonoCompile *cfg, int max_iid_reg, MonoClass *klass, - MonoBasicBlock *false_target) -{ - if (cfg->compile_aot) { - int iid_reg = alloc_preg (cfg); - MONO_EMIT_NEW_AOTCONST (cfg, iid_reg, klass, MONO_PATCH_INFO_IID); - MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, max_iid_reg, iid_reg); - } - else - MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, max_iid_reg, klass->interface_id); - if (false_target) - MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBLT_UN, false_target); - else - MONO_EMIT_NEW_COND_EXC (cfg, LT_UN, "InvalidCastException"); -} - -/* Same as above, but obtains max_iid from a vtable */ -static void -mini_emit_max_iid_check_vtable (MonoCompile *cfg, int vtable_reg, MonoClass *klass, - MonoBasicBlock *false_target) -{ - int max_iid_reg = alloc_preg (cfg); - - MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU4_MEMBASE, max_iid_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, max_interface_id)); - mini_emit_max_iid_check (cfg, max_iid_reg, klass, false_target); -} - -/* Same as above, but obtains max_iid from a klass */ -static void -mini_emit_max_iid_check_class (MonoCompile *cfg, int klass_reg, MonoClass *klass, - MonoBasicBlock *false_target) -{ - int max_iid_reg = alloc_preg (cfg); - - MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU4_MEMBASE, max_iid_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, max_interface_id)); - mini_emit_max_iid_check (cfg, max_iid_reg, klass, false_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); - int stype = alloc_preg (cfg); - - mono_class_setup_supertypes (klass); - - if (klass->idepth > MONO_DEFAULT_SUPERTABLE_SIZE) { - MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU2_MEMBASE, idepth_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, idepth)); - MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, idepth_reg, klass->idepth); - MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBLT_UN, false_target); - } - MONO_EMIT_NEW_LOAD_MEMBASE (cfg, stypes_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, supertypes)); - MONO_EMIT_NEW_LOAD_MEMBASE (cfg, stype, stypes_reg, ((klass->idepth - 1) * SIZEOF_VOID_P)); - 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); - } else { - MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, stype, klass); - } - MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, true_target); -} - -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); - - mini_emit_max_iid_check_vtable (cfg, vtable_reg, klass, false_target); - mini_emit_load_intf_bit_reg_vtable (cfg, intf_reg, vtable_reg, klass); - MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, intf_reg, 0); - if (true_target) - MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, true_target); - else - MONO_EMIT_NEW_COND_EXC (cfg, EQ, "InvalidCastException"); -} - -/* - * Variant of the above that takes a register to the class, not the vtable. - */ -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); - - mini_emit_max_iid_check_class (cfg, klass_reg, klass, false_target); - mini_emit_load_intf_bit_reg_class (cfg, intf_bit_reg, klass_reg, klass); - MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, intf_bit_reg, 0); - if (true_target) - MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, true_target); - else - MONO_EMIT_NEW_COND_EXC (cfg, EQ, "InvalidCastException"); -} - -static inline void -mini_emit_class_check_inst (MonoCompile *cfg, int klass_reg, MonoClass *klass, MonoInst *klass_inst) -{ - if (klass_inst) { - MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, klass_reg, klass_inst->dreg); - } else { - MonoInst *ins = emit_runtime_constant (cfg, MONO_PATCH_INFO_CLASS, klass); - MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, klass_reg, ins->dreg); - } - MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException"); -} - -static inline void -mini_emit_class_check (MonoCompile *cfg, int klass_reg, MonoClass *klass) -{ - 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) -{ - 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); - } else { - MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, klass_reg, 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_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, MONO_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"); - // MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass)); - MONO_EMIT_NEW_LOAD_MEMBASE (cfg, eclass_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, cast_class)); - if (klass->cast_class == mono_defaults.object_class) { - int parent_reg = alloc_preg (cfg); - MONO_EMIT_NEW_LOAD_MEMBASE (cfg, parent_reg, eclass_reg, MONO_STRUCT_OFFSET (MonoClass, parent)); - mini_emit_class_check_branch (cfg, parent_reg, mono_defaults.enum_class->parent, OP_PBNE_UN, object_is_null); - mini_emit_class_check (cfg, eclass_reg, mono_defaults.enum_class); - } else if (klass->cast_class == mono_defaults.enum_class->parent) { - mini_emit_class_check_branch (cfg, eclass_reg, mono_defaults.enum_class->parent, OP_PBEQ, object_is_null); - mini_emit_class_check (cfg, eclass_reg, mono_defaults.enum_class); - } else if (klass->cast_class == mono_defaults.enum_class) { - mini_emit_class_check (cfg, eclass_reg, mono_defaults.enum_class); - } else if (mono_class_is_interface (klass->cast_class)) { - mini_emit_iface_class_cast (cfg, eclass_reg, klass->cast_class, NULL, NULL); - } else { - // Pass -1 as obj_reg to skip the check below for arrays of arrays - mini_emit_castclass (cfg, -1, eclass_reg, klass->cast_class, object_is_null); - } - - if ((klass->rank == 1) && (klass->byval_arg.type == MONO_TYPE_SZARRAY) && (obj_reg != -1)) { - /* Check that the object is a vector too */ - int bounds_reg = alloc_preg (cfg); - MONO_EMIT_NEW_LOAD_MEMBASE (cfg, bounds_reg, obj_reg, MONO_STRUCT_OFFSET (MonoArray, bounds)); - MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, bounds_reg, 0); - MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException"); - } - } else { - int idepth_reg = alloc_preg (cfg); - int stypes_reg = alloc_preg (cfg); - int stype = alloc_preg (cfg); - - mono_class_setup_supertypes (klass); - - if (klass->idepth > MONO_DEFAULT_SUPERTABLE_SIZE) { - MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU2_MEMBASE, idepth_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, idepth)); - MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, idepth_reg, klass->idepth); - MONO_EMIT_NEW_COND_EXC (cfg, LT_UN, "InvalidCastException"); - } - MONO_EMIT_NEW_LOAD_MEMBASE (cfg, stypes_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, supertypes)); - MONO_EMIT_NEW_LOAD_MEMBASE (cfg, stype, stypes_reg, ((klass->idepth - 1) * SIZEOF_VOID_P)); - 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) +MonoInst* +mini_emit_runtime_constant (MonoCompile *cfg, MonoJumpInfoType patch_type, gpointer data) { - mini_emit_castclass_inst (cfg, obj_reg, klass_reg, klass, NULL, object_is_null); + return emit_runtime_constant (cfg, patch_type, data); } static void @@ -1973,22 +1724,91 @@ mini_emit_memcpy (MonoCompile *cfg, int destreg, int doffset, int srcreg, int so } } -static void -emit_tls_set (MonoCompile *cfg, int sreg1, MonoTlsKey tls_key) +static MonoInst* +mono_create_fast_tls_getter (MonoCompile *cfg, MonoTlsKey key) { - MonoInst *ins, *c; + int tls_offset = mono_tls_get_tls_offset (key); + + if (cfg->compile_aot) + return NULL; + + if (tls_offset != -1 && mono_arch_have_fast_tls ()) { + MonoInst *ins; + MONO_INST_NEW (cfg, ins, OP_TLS_GET); + ins->dreg = mono_alloc_preg (cfg); + ins->inst_offset = tls_offset; + return ins; + } + return NULL; +} + +static MonoInst* +mono_create_fast_tls_setter (MonoCompile *cfg, MonoInst* value, MonoTlsKey key) +{ + int tls_offset = mono_tls_get_tls_offset (key); + + if (cfg->compile_aot) + return NULL; + + if (tls_offset != -1 && mono_arch_have_fast_tls ()) { + MonoInst *ins; + MONO_INST_NEW (cfg, ins, OP_TLS_SET); + ins->sreg1 = value->dreg; + ins->inst_offset = tls_offset; + return ins; + } + return NULL; +} + + +MonoInst* +mono_create_tls_get (MonoCompile *cfg, MonoTlsKey key) +{ + MonoInst *fast_tls = NULL; + + if (!mini_get_debug_options ()->use_fallback_tls) + fast_tls = mono_create_fast_tls_getter (cfg, key); + + if (fast_tls) { + MONO_ADD_INS (cfg->cbb, fast_tls); + return fast_tls; + } if (cfg->compile_aot) { - EMIT_NEW_TLS_OFFSETCONST (cfg, c, tls_key); - MONO_INST_NEW (cfg, ins, OP_TLS_SET_REG); - ins->sreg1 = sreg1; - ins->sreg2 = c->dreg; - MONO_ADD_INS (cfg->cbb, ins); + MonoInst *addr; + /* + * tls getters are critical pieces of code and we don't want to resolve them + * through the standard plt/tramp mechanism since we might expose ourselves + * to crashes and infinite recursions. + */ + EMIT_NEW_AOTCONST (cfg, addr, MONO_PATCH_INFO_GET_TLS_TRAMP, (void*)key); + return mono_emit_calli (cfg, helper_sig_get_tls_tramp, NULL, addr, NULL, NULL); } else { - MONO_INST_NEW (cfg, ins, OP_TLS_SET); - ins->sreg1 = sreg1; - ins->inst_offset = mini_get_tls_offset (tls_key); - MONO_ADD_INS (cfg->cbb, ins); + gpointer getter = mono_tls_get_tls_getter (key, FALSE); + return mono_emit_jit_icall (cfg, getter, NULL); + } +} + +static MonoInst* +mono_create_tls_set (MonoCompile *cfg, MonoInst *value, MonoTlsKey key) +{ + MonoInst *fast_tls = NULL; + + if (!mini_get_debug_options ()->use_fallback_tls) + fast_tls = mono_create_fast_tls_setter (cfg, value, key); + + if (fast_tls) { + MONO_ADD_INS (cfg->cbb, fast_tls); + return fast_tls; + } + + if (cfg->compile_aot) { + MonoInst *addr; + EMIT_NEW_AOTCONST (cfg, addr, MONO_PATCH_INFO_SET_TLS_TRAMP, (void*)key); + return mono_emit_calli (cfg, helper_sig_set_tls_tramp, &value, addr, NULL, NULL); + } else { + gpointer setter = mono_tls_get_tls_setter (key, FALSE); + return mono_emit_jit_icall (cfg, setter, &value); } } @@ -2007,79 +1827,40 @@ emit_push_lmf (MonoCompile *cfg) * lmf->prev_lmf = *lmf_addr * *lmf_addr = lmf */ - int lmf_reg, prev_lmf_reg; MonoInst *ins, *lmf_ins; if (!cfg->lmf_ir) return; - if (cfg->lmf_ir_mono_lmf && mini_tls_get_supported (cfg, TLS_KEY_LMF)) { - /* Load current lmf */ - lmf_ins = mono_get_lmf_intrinsic (cfg); - g_assert (lmf_ins); - MONO_ADD_INS (cfg->cbb, lmf_ins); - EMIT_NEW_VARLOADA (cfg, ins, cfg->lmf_var, NULL); - lmf_reg = ins->dreg; - /* Save previous_lmf */ - EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, lmf_reg, MONO_STRUCT_OFFSET (MonoLMF, previous_lmf), lmf_ins->dreg); - /* Set new LMF */ - emit_tls_set (cfg, lmf_reg, TLS_KEY_LMF); - } else { - /* - * Store lmf_addr in a variable, so it can be allocated to a global register. - */ - if (!cfg->lmf_addr_var) - cfg->lmf_addr_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL); + int lmf_reg, prev_lmf_reg; + /* + * Store lmf_addr in a variable, so it can be allocated to a global register. + */ + if (!cfg->lmf_addr_var) + cfg->lmf_addr_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL); #ifdef HOST_WIN32 - ins = mono_get_jit_tls_intrinsic (cfg); - if (ins) { - int jit_tls_dreg = ins->dreg; - - MONO_ADD_INS (cfg->cbb, ins); - lmf_reg = alloc_preg (cfg); - EMIT_NEW_BIALU_IMM (cfg, lmf_ins, OP_PADD_IMM, lmf_reg, jit_tls_dreg, MONO_STRUCT_OFFSET (MonoJitTlsData, lmf)); - } else { - lmf_ins = mono_emit_jit_icall (cfg, mono_get_lmf_addr, NULL); - } -#else - lmf_ins = mono_get_lmf_addr_intrinsic (cfg); - if (lmf_ins) { - MONO_ADD_INS (cfg->cbb, lmf_ins); - } else { -#ifdef TARGET_IOS - MonoInst *args [16], *jit_tls_ins, *ins; + ins = mono_create_tls_get (cfg, TLS_KEY_JIT_TLS); + g_assert (ins); + int jit_tls_dreg = ins->dreg; - /* Inline mono_get_lmf_addr () */ - /* jit_tls = pthread_getspecific (mono_jit_tls_id); lmf_addr = &jit_tls->lmf; */ - - /* Load mono_jit_tls_id */ - if (cfg->compile_aot) - EMIT_NEW_AOTCONST (cfg, args [0], MONO_PATCH_INFO_JIT_TLS_ID, NULL); - else - EMIT_NEW_ICONST (cfg, args [0], mono_jit_tls_id); - /* call pthread_getspecific () */ - jit_tls_ins = mono_emit_jit_icall (cfg, pthread_getspecific, args); - /* lmf_addr = &jit_tls->lmf */ - EMIT_NEW_BIALU_IMM (cfg, ins, OP_PADD_IMM, cfg->lmf_addr_var->dreg, jit_tls_ins->dreg, MONO_STRUCT_OFFSET (MonoJitTlsData, lmf)); - lmf_ins = ins; + lmf_reg = alloc_preg (cfg); + EMIT_NEW_BIALU_IMM (cfg, lmf_ins, OP_PADD_IMM, lmf_reg, jit_tls_dreg, MONO_STRUCT_OFFSET (MonoJitTlsData, lmf)); #else - lmf_ins = mono_emit_jit_icall (cfg, mono_get_lmf_addr, NULL); -#endif - } + lmf_ins = mono_create_tls_get (cfg, TLS_KEY_LMF_ADDR); + g_assert (lmf_ins); #endif - lmf_ins->dreg = cfg->lmf_addr_var->dreg; + lmf_ins->dreg = cfg->lmf_addr_var->dreg; - EMIT_NEW_VARLOADA (cfg, ins, cfg->lmf_var, NULL); - lmf_reg = ins->dreg; + EMIT_NEW_VARLOADA (cfg, ins, cfg->lmf_var, NULL); + lmf_reg = ins->dreg; - prev_lmf_reg = alloc_preg (cfg); - /* Save previous_lmf */ - EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, prev_lmf_reg, cfg->lmf_addr_var->dreg, 0); - EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, lmf_reg, MONO_STRUCT_OFFSET (MonoLMF, previous_lmf), prev_lmf_reg); - /* Set new lmf */ - EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, cfg->lmf_addr_var->dreg, 0, lmf_reg); - } + prev_lmf_reg = alloc_preg (cfg); + /* Save previous_lmf */ + EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, prev_lmf_reg, cfg->lmf_addr_var->dreg, 0); + EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, lmf_reg, MONO_STRUCT_OFFSET (MonoLMF, previous_lmf), prev_lmf_reg); + /* Set new lmf */ + EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, cfg->lmf_addr_var->dreg, 0, lmf_reg); } /* @@ -2090,7 +1871,7 @@ emit_push_lmf (MonoCompile *cfg) static void emit_pop_lmf (MonoCompile *cfg) { - int lmf_reg, lmf_addr_reg, prev_lmf_reg; + int lmf_reg, lmf_addr_reg; MonoInst *ins; if (!cfg->lmf_ir) @@ -2099,26 +1880,19 @@ emit_pop_lmf (MonoCompile *cfg) EMIT_NEW_VARLOADA (cfg, ins, cfg->lmf_var, NULL); lmf_reg = ins->dreg; - if (cfg->lmf_ir_mono_lmf && mini_tls_get_supported (cfg, TLS_KEY_LMF)) { - /* Load previous_lmf */ - prev_lmf_reg = alloc_preg (cfg); - EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, prev_lmf_reg, lmf_reg, MONO_STRUCT_OFFSET (MonoLMF, previous_lmf)); - /* Set new LMF */ - emit_tls_set (cfg, prev_lmf_reg, TLS_KEY_LMF); - } else { - /* - * Emit IR to pop the LMF: - * *(lmf->lmf_addr) = lmf->prev_lmf - */ - /* This could be called before emit_push_lmf () */ - if (!cfg->lmf_addr_var) - cfg->lmf_addr_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL); - lmf_addr_reg = cfg->lmf_addr_var->dreg; + int prev_lmf_reg; + /* + * Emit IR to pop the LMF: + * *(lmf->lmf_addr) = lmf->prev_lmf + */ + /* This could be called before emit_push_lmf () */ + if (!cfg->lmf_addr_var) + cfg->lmf_addr_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL); + lmf_addr_reg = cfg->lmf_addr_var->dreg; - prev_lmf_reg = alloc_preg (cfg); - EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, prev_lmf_reg, lmf_reg, MONO_STRUCT_OFFSET (MonoLMF, previous_lmf)); - EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, lmf_addr_reg, 0, prev_lmf_reg); - } + prev_lmf_reg = alloc_preg (cfg); + EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, prev_lmf_reg, lmf_reg, MONO_STRUCT_OFFSET (MonoLMF, previous_lmf)); + EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, lmf_addr_reg, 0, prev_lmf_reg); } static void @@ -2775,8 +2549,6 @@ emit_get_gsharedvt_info_klass (MonoCompile *cfg, MonoClass *klass, MonoRgctxInfo static MonoInst* emit_get_rgctx_method (MonoCompile *cfg, int context_used, MonoMethod *cmethod, MonoRgctxInfoType rgctx_type); -static MonoInst* -emit_get_rgctx_klass (MonoCompile *cfg, int context_used, MonoClass *klass, MonoRgctxInfoType rgctx_type); static MonoInst* mono_emit_method_call_full (MonoCompile *cfg, MonoMethod *method, MonoMethodSignature *sig, gboolean tail, @@ -3073,6 +2845,8 @@ emit_llvmonly_calli (MonoCompile *cfg, MonoMethodSignature *fsig, MonoInst **arg static gboolean direct_icalls_enabled (MonoCompile *cfg) { + return FALSE; + /* LLVM on amd64 can't handle calls to non-32 bit addresses */ #ifdef TARGET_AMD64 if (cfg->compile_llvm && !cfg->llvm_only) @@ -3165,12 +2939,12 @@ mono_emit_widen_call_res (MonoCompile *cfg, MonoInst *ins, MonoMethodSignature * static void -emit_method_access_failure (MonoCompile *cfg, MonoMethod *method, MonoMethod *cil_method) +emit_method_access_failure (MonoCompile *cfg, MonoMethod *caller, MonoMethod *callee) { MonoInst *args [16]; - args [0] = emit_get_rgctx_method (cfg, mono_method_check_context_used (method), method, MONO_RGCTX_INFO_METHOD); - args [1] = emit_get_rgctx_method (cfg, mono_method_check_context_used (cil_method), cil_method, MONO_RGCTX_INFO_METHOD); + args [0] = emit_get_rgctx_method (cfg, mono_method_check_context_used (caller), caller, MONO_RGCTX_INFO_METHOD); + args [1] = emit_get_rgctx_method (cfg, mono_method_check_context_used (callee), callee, MONO_RGCTX_INFO_METHOD); mono_emit_jit_icall (cfg, mono_throw_method_access, args); } @@ -3399,7 +3173,7 @@ mini_emit_stobj (MonoCompile *cfg, MonoInst *dest, MonoInst *src, MonoClass *kla if (!size_ins && (cfg->opt & MONO_OPT_INTRINS) && mono_emit_wb_aware_memcpy (cfg, klass, iargs, n, align)) { return; } else if (context_used) { - iargs [2] = emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_KLASS); + iargs [2] = mini_emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_KLASS); } else { iargs [2] = emit_runtime_constant (cfg, MONO_PATCH_INFO_CLASS, klass); if (!cfg->compile_aot) @@ -3679,8 +3453,8 @@ emit_rgctx_fetch (MonoCompile *cfg, MonoInst *rgctx, MonoJumpInfoRgctxEntry *ent return mono_emit_abs_call (cfg, MONO_PATCH_INFO_RGCTX_FETCH, entry, helper_sig_rgctx_lazy_fetch_trampoline, &rgctx); } -static MonoInst* -emit_get_rgctx_klass (MonoCompile *cfg, int context_used, +MonoInst* +mini_emit_get_rgctx_klass (MonoCompile *cfg, int context_used, MonoClass *klass, MonoRgctxInfoType rgctx_type) { MonoJumpInfoRgctxEntry *entry = mono_patch_info_rgctx_entry_new (cfg->mempool, cfg->method, context_used & MONO_GENERIC_CONTEXT_USED_METHOD, MONO_PATCH_INFO_CLASS, klass, rgctx_type); @@ -3868,7 +3642,7 @@ emit_class_init (MonoCompile *cfg, MonoClass *klass) context_used = mini_class_check_context_used (cfg, klass); if (context_used) { - vtable_arg = emit_get_rgctx_klass (cfg, context_used, + vtable_arg = mini_emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_VTABLE); } else { MonoVTable *vtable = mono_class_vtable (cfg->domain, klass); @@ -3889,20 +3663,13 @@ emit_class_init (MonoCompile *cfg, MonoClass *klass) ins->sreg1 = vtable_arg->dreg; MONO_ADD_INS (cfg->cbb, ins); } else { - static int byte_offset = -1; - static guint8 bitmask; - int bits_reg, inited_reg; + int inited_reg; MonoBasicBlock *inited_bb; MonoInst *args [16]; - if (byte_offset < 0) - mono_marshal_find_bitfield_offset (MonoVTable, initialized, &byte_offset, &bitmask); - - bits_reg = alloc_ireg (cfg); inited_reg = alloc_ireg (cfg); - MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU1_MEMBASE, bits_reg, vtable_arg->dreg, byte_offset); - MONO_EMIT_NEW_BIALU_IMM (cfg, OP_IAND_IMM, inited_reg, bits_reg, bitmask); + MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU1_MEMBASE, inited_reg, vtable_arg->dreg, MONO_STRUCT_OFFSET (MonoVTable, initialized)); NEW_BBLOCK (cfg, inited_bb); @@ -3929,8 +3696,8 @@ emit_seq_point (MonoCompile *cfg, MonoMethod *method, guint8* ip, gboolean intr_ } } -static void -save_cast_details (MonoCompile *cfg, MonoClass *klass, int obj_reg, gboolean null_check) +void +mini_save_cast_details (MonoCompile *cfg, MonoClass *klass, int obj_reg, gboolean null_check) { if (mini_get_debug_options ()->better_cast_details) { int vtable_reg = alloc_preg (cfg); @@ -3946,13 +3713,12 @@ save_cast_details (MonoCompile *cfg, MonoClass *klass, int obj_reg, gboolean nul MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, is_null_bb); } - tls_get = mono_get_jit_tls_intrinsic (cfg); + tls_get = mono_create_tls_get (cfg, TLS_KEY_JIT_TLS); if (!tls_get) { fprintf (stderr, "error: --debug=casts not supported on this platform.\n."); exit (1); } - MONO_ADD_INS (cfg->cbb, tls_get); MONO_EMIT_NEW_LOAD_MEMBASE (cfg, vtable_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable)); MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass)); @@ -3962,7 +3728,7 @@ save_cast_details (MonoCompile *cfg, MonoClass *klass, int obj_reg, gboolean nul if (context_used) { MonoInst *class_ins; - class_ins = emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_KLASS); + class_ins = mini_emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_KLASS); to_klass_reg = class_ins->dreg; } else { to_klass_reg = alloc_preg (cfg); @@ -3975,14 +3741,12 @@ save_cast_details (MonoCompile *cfg, MonoClass *klass, int obj_reg, gboolean nul } } -static void -reset_cast_details (MonoCompile *cfg) +void +mini_reset_cast_details (MonoCompile *cfg) { /* Reset the variables holding the cast details */ if (mini_get_debug_options ()->better_cast_details) { - MonoInst *tls_get = mono_get_jit_tls_intrinsic (cfg); - - MONO_ADD_INS (cfg->cbb, tls_get); + MonoInst *tls_get = mono_create_tls_get (cfg, TLS_KEY_JIT_TLS); /* It is enough to reset the from field */ MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STORE_MEMBASE_IMM, tls_get->dreg, MONO_STRUCT_OFFSET (MonoJitTlsData, class_cast_from), 0); } @@ -3999,7 +3763,7 @@ mini_emit_check_array_type (MonoCompile *cfg, MonoInst *obj, MonoClass *array_cl context_used = mini_class_check_context_used (cfg, array_class); - save_cast_details (cfg, array_class, obj->dreg, FALSE); + mini_save_cast_details (cfg, array_class, obj->dreg, FALSE); MONO_EMIT_NEW_LOAD_MEMBASE_FAULT (cfg, vtable_reg, obj->dreg, MONO_STRUCT_OFFSET (MonoObject, vtable)); @@ -4013,7 +3777,7 @@ mini_emit_check_array_type (MonoCompile *cfg, MonoInst *obj, MonoClass *array_cl } else if (context_used) { MonoInst *vtable_ins; - vtable_ins = emit_get_rgctx_klass (cfg, context_used, array_class, MONO_RGCTX_INFO_VTABLE); + vtable_ins = mini_emit_get_rgctx_klass (cfg, context_used, array_class, MONO_RGCTX_INFO_VTABLE); MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, vtable_reg, vtable_ins->dreg); } else { if (cfg->compile_aot) { @@ -4035,7 +3799,7 @@ mini_emit_check_array_type (MonoCompile *cfg, MonoInst *obj, MonoClass *array_cl MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "ArrayTypeMismatchException"); - reset_cast_details (cfg); + mini_reset_cast_details (cfg); } /** @@ -4111,15 +3875,15 @@ handle_unbox (MonoCompile *cfg, MonoClass *klass, MonoInst **sp, int context_use /* This assertion is from the unboxcast insn */ g_assert (klass->rank == 0); - element_class = emit_get_rgctx_klass (cfg, context_used, + element_class = mini_emit_get_rgctx_klass (cfg, context_used, 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"); } else { - save_cast_details (cfg, klass->element_class, obj_reg, FALSE); + mini_save_cast_details (cfg, klass->element_class, obj_reg, FALSE); mini_emit_class_check (cfg, eclass_reg, klass->element_class); - reset_cast_details (cfg); + mini_reset_cast_details (cfg); } NEW_BIALU_IMM (cfg, add, OP_ADD_IMM, alloc_dreg (cfg, STACK_MP), obj_reg, sizeof (MonoObject)); @@ -4233,7 +3997,7 @@ handle_alloc (MonoCompile *cfg, MonoClass *klass, gboolean for_box, int context_ rgctx_info = MONO_RGCTX_INFO_KLASS; else rgctx_info = MONO_RGCTX_INFO_VTABLE; - data = emit_get_rgctx_klass (cfg, context_used, klass, rgctx_info); + data = mini_emit_get_rgctx_klass (cfg, context_used, klass, rgctx_info); if (cfg->opt & MONO_OPT_SHARED) { EMIT_NEW_DOMAINCONST (cfg, iargs [0]); @@ -4298,693 +4062,178 @@ handle_alloc (MonoCompile *cfg, MonoClass *klass, gboolean for_box, int context_ EMIT_NEW_VTABLECONST (cfg, iargs [1], vtable); } else { - EMIT_NEW_VTABLECONST (cfg, iargs [0], vtable); - } - } - - return mono_emit_jit_icall (cfg, alloc_ftn, iargs); -} - -/* - * Returns NULL and set the cfg exception on error. - */ -static MonoInst* -handle_box (MonoCompile *cfg, MonoInst *val, MonoClass *klass, int context_used) -{ - MonoInst *alloc, *ins; - - if (mono_class_is_nullable (klass)) { - MonoMethod* method = mono_class_get_method_from_name (klass, "Box", 1); - - if (context_used) { - if (cfg->llvm_only && cfg->gsharedvt) { - MonoInst *addr = emit_get_rgctx_method (cfg, context_used, method, - MONO_RGCTX_INFO_GENERIC_METHOD_CODE); - return emit_llvmonly_calli (cfg, mono_method_signature (method), &val, addr); - } else { - /* FIXME: What if the class is shared? We might not - have to get the method address from the RGCTX. */ - MonoInst *addr = emit_get_rgctx_method (cfg, context_used, method, - MONO_RGCTX_INFO_GENERIC_METHOD_CODE); - MonoInst *rgctx = emit_get_rgctx (cfg, cfg->method, context_used); - - return mono_emit_calli (cfg, mono_method_signature (method), &val, addr, NULL, rgctx); - } - } else { - gboolean pass_vtable, pass_mrgctx; - MonoInst *rgctx_arg = NULL; - - check_method_sharing (cfg, method, &pass_vtable, &pass_mrgctx); - g_assert (!pass_mrgctx); - - if (pass_vtable) { - MonoVTable *vtable = mono_class_vtable (cfg->domain, method->klass); - - g_assert (vtable); - EMIT_NEW_VTABLECONST (cfg, rgctx_arg, vtable); - } - - return mono_emit_method_call_full (cfg, method, NULL, FALSE, &val, NULL, NULL, rgctx_arg); - } - } - - if (mini_is_gsharedvt_klass (klass)) { - MonoBasicBlock *is_ref_bb, *is_nullable_bb, *end_bb; - MonoInst *res, *is_ref, *src_var, *addr; - int dreg; - - dreg = alloc_ireg (cfg); - - NEW_BBLOCK (cfg, is_ref_bb); - NEW_BBLOCK (cfg, is_nullable_bb); - NEW_BBLOCK (cfg, end_bb); - is_ref = emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_CLASS_BOX_TYPE); - MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, is_ref->dreg, MONO_GSHAREDVT_BOX_TYPE_REF); - MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, is_ref_bb); - - MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, is_ref->dreg, MONO_GSHAREDVT_BOX_TYPE_NULLABLE); - MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, is_nullable_bb); - - /* Non-ref case */ - alloc = handle_alloc (cfg, klass, TRUE, context_used); - if (!alloc) - return NULL; - EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, alloc->dreg, sizeof (MonoObject), val->dreg); - ins->opcode = OP_STOREV_MEMBASE; - - EMIT_NEW_UNALU (cfg, res, OP_MOVE, dreg, alloc->dreg); - res->type = STACK_OBJ; - res->klass = klass; - MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb); - - /* Ref case */ - MONO_START_BB (cfg, is_ref_bb); - - /* val is a vtype, so has to load the value manually */ - src_var = get_vreg_to_inst (cfg, val->dreg); - if (!src_var) - src_var = mono_compile_create_var_for_vreg (cfg, &klass->byval_arg, OP_LOCAL, val->dreg); - EMIT_NEW_VARLOADA (cfg, addr, src_var, src_var->inst_vtype); - MONO_EMIT_NEW_LOAD_MEMBASE (cfg, dreg, addr->dreg, 0); - MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb); - - /* Nullable case */ - MONO_START_BB (cfg, is_nullable_bb); - - { - MonoInst *addr = emit_get_gsharedvt_info_klass (cfg, klass, - MONO_RGCTX_INFO_NULLABLE_CLASS_BOX); - MonoInst *box_call; - MonoMethodSignature *box_sig; - - /* - * klass is Nullable, need to call Nullable.Box () using a gsharedvt signature, but we cannot - * construct that method at JIT time, so have to do things by hand. - */ - box_sig = (MonoMethodSignature *)mono_mempool_alloc0 (cfg->mempool, MONO_SIZEOF_METHOD_SIGNATURE + (1 * sizeof (MonoType *))); - box_sig->ret = &mono_defaults.object_class->byval_arg; - box_sig->param_count = 1; - box_sig->params [0] = &klass->byval_arg; - - if (cfg->llvm_only) - box_call = emit_llvmonly_calli (cfg, box_sig, &val, addr); - else - box_call = mono_emit_calli (cfg, box_sig, &val, addr, NULL, NULL); - EMIT_NEW_UNALU (cfg, res, OP_MOVE, dreg, box_call->dreg); - res->type = STACK_OBJ; - res->klass = klass; - } - - MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb); - - MONO_START_BB (cfg, end_bb); - - return res; - } else { - alloc = handle_alloc (cfg, klass, TRUE, context_used); - if (!alloc) - return NULL; - - EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, alloc->dreg, sizeof (MonoObject), val->dreg); - return alloc; - } -} - -static gboolean -mini_class_has_reference_variant_generic_argument (MonoCompile *cfg, MonoClass *klass, int context_used) -{ - int i; - MonoGenericContainer *container; - MonoGenericInst *ginst; - - if (mono_class_is_ginst (klass)) { - container = mono_class_get_generic_container (mono_class_get_generic_class (klass)->container_class); - ginst = mono_class_get_generic_class (klass)->context.class_inst; - } else if (mono_class_is_gtd (klass) && context_used) { - container = mono_class_get_generic_container (klass); - ginst = container->context.class_inst; - } else { - return FALSE; - } - - for (i = 0; i < container->type_argc; ++i) { - MonoType *type; - if (!(mono_generic_container_get_param_info (container, i)->flags & (MONO_GEN_PARAM_VARIANT|MONO_GEN_PARAM_COVARIANT))) - continue; - type = ginst->type_argv [i]; - if (mini_type_is_reference (type)) - return TRUE; - } - 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)); - g_hash_table_insert (h, (char*)"Monitor", 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; -} - -static gboolean -method_needs_stack_walk (MonoCompile *cfg, MonoMethod *cmethod) -{ - if (cmethod->klass == mono_defaults.systemtype_class) { - if (!strcmp (cmethod->name, "GetType")) - return TRUE; - } - return FALSE; -} - -#define is_complex_isinst(klass) (mono_class_is_interface (klass) || klass->rank || mono_class_is_nullable (klass) || mono_class_is_marshalbyref (klass) || mono_class_is_sealed (klass) || klass->byval_arg.type == MONO_TYPE_VAR || klass->byval_arg.type == MONO_TYPE_MVAR) - -static MonoInst* -emit_isinst_with_cache (MonoCompile *cfg, MonoClass *klass, MonoInst **args) -{ - MonoMethod *mono_isinst = mono_marshal_get_isinst_with_cache (); - return mono_emit_method_call (cfg, mono_isinst, args, NULL); -} - -static MonoInst* -emit_castclass_with_cache (MonoCompile *cfg, MonoClass *klass, MonoInst **args) -{ - MonoMethod *mono_castclass = mono_marshal_get_castclass_with_cache (); - MonoInst *res; - - save_cast_details (cfg, klass, args [0]->dreg, TRUE); - res = mono_emit_method_call (cfg, mono_castclass, args, NULL); - reset_cast_details (cfg); - - 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_isinst_with_cache_nonshared (MonoCompile *cfg, MonoInst *obj, MonoClass *klass) -{ - MonoInst *args [3]; - int idx; - - args [0] = obj; /* obj */ - EMIT_NEW_CLASSCONST (cfg, args [1], klass); /* klass */ - - idx = get_castclass_cache_idx (cfg); /* inline cache*/ - args [2] = emit_runtime_constant (cfg, MONO_PATCH_INFO_CASTCLASS_CACHE, GINT_TO_POINTER (idx)); - - return emit_isinst_with_cache (cfg, klass, args); -} - -static MonoInst* -emit_castclass_with_cache_nonshared (MonoCompile *cfg, MonoInst *obj, MonoClass *klass) -{ - MonoInst *args [3]; - int idx; - - /* obj */ - args [0] = obj; - - /* klass */ - EMIT_NEW_CLASSCONST (cfg, args [1], klass); - - /* inline cache*/ - idx = get_castclass_cache_idx (cfg); - args [2] = emit_runtime_constant (cfg, MONO_PATCH_INFO_CASTCLASS_CACHE, GINT_TO_POINTER (idx)); - - /*The wrapper doesn't inline well so the bloat of inlining doesn't pay off.*/ - return emit_castclass_with_cache (cfg, klass, args); -} - -/* - * Returns NULL and set the cfg exception on error. - */ -static MonoInst* -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 (MONO_INS_IS_PCONST_NULL (src)) - return src; - - if (context_used) { - MonoInst *args [3]; - - if (mini_class_has_reference_variant_generic_argument (cfg, klass, context_used) || is_complex_isinst (klass)) { - MonoInst *cache_ins; - - cache_ins = emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_CAST_CACHE); - - /* obj */ - args [0] = src; - - /* klass - it's the second element of the cache entry*/ - EMIT_NEW_LOAD_MEMBASE (cfg, args [1], OP_LOAD_MEMBASE, alloc_preg (cfg), cache_ins->dreg, sizeof (gpointer)); - - /* cache */ - args [2] = cache_ins; - - return emit_castclass_with_cache (cfg, klass, args); - } - - klass_inst = emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_KLASS); - } - - NEW_BBLOCK (cfg, is_null_bb); - - MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, obj_reg, 0); - MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, is_null_bb); - - save_cast_details (cfg, klass, obj_reg, FALSE); - - if (mono_class_is_interface (klass)) { - MONO_EMIT_NEW_LOAD_MEMBASE (cfg, vtable_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable)); - mini_emit_iface_cast (cfg, vtable_reg, klass, NULL, NULL); - } else { - int klass_reg = alloc_preg (cfg); - - MONO_EMIT_NEW_LOAD_MEMBASE (cfg, vtable_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable)); - - if (!klass->rank && !cfg->compile_aot && !(cfg->opt & MONO_OPT_SHARED) && mono_class_is_sealed (klass)) { - /* 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); - if (!vt) { - mono_cfg_set_exception (cfg, MONO_EXCEPTION_TYPE_LOAD); - cfg->exception_ptr = klass; - return NULL; - } - MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, vtable_reg, vt); - } else { - MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass)); - MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, klass_reg, klass); - } - MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException"); - } else { - MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass)); - mini_emit_castclass_inst (cfg, obj_reg, klass_reg, klass, klass_inst, is_null_bb); - } - } - - MONO_START_BB (cfg, is_null_bb); - - reset_cast_details (cfg); - - return src; -} - -/* - * Returns NULL and set the cfg exception on error. - */ -static MonoInst* -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_ireg_ref (cfg); - MonoInst *klass_inst = NULL; - - if (context_used) { - MonoInst *args [3]; - - if(mini_class_has_reference_variant_generic_argument (cfg, klass, context_used) || is_complex_isinst (klass)) { - MonoInst *cache_ins = emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_CAST_CACHE); - - args [0] = src; /* obj */ - - /* klass - it's the second element of the cache entry*/ - EMIT_NEW_LOAD_MEMBASE (cfg, args [1], OP_LOAD_MEMBASE, alloc_preg (cfg), cache_ins->dreg, sizeof (gpointer)); - - args [2] = cache_ins; /* cache */ - return emit_isinst_with_cache (cfg, klass, args); - } - - klass_inst = emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_KLASS); - } - - NEW_BBLOCK (cfg, is_null_bb); - NEW_BBLOCK (cfg, false_bb); - NEW_BBLOCK (cfg, end_bb); - - /* Do the assignment at the beginning, so the other assignment can be if converted */ - EMIT_NEW_UNALU (cfg, ins, OP_MOVE, res_reg, obj_reg); - ins->type = STACK_OBJ; - ins->klass = klass; - - 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, MONO_STRUCT_OFFSET (MonoObject, vtable)); - - if (mono_class_is_interface (klass)) { - 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); - - 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, MONO_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); - MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass)); - MONO_EMIT_NEW_LOAD_MEMBASE (cfg, eclass_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, cast_class)); - if (klass->cast_class == mono_defaults.object_class) { - int parent_reg = alloc_preg (cfg); - MONO_EMIT_NEW_LOAD_MEMBASE (cfg, parent_reg, eclass_reg, MONO_STRUCT_OFFSET (MonoClass, parent)); - mini_emit_class_check_branch (cfg, parent_reg, mono_defaults.enum_class->parent, OP_PBNE_UN, is_null_bb); - mini_emit_class_check_branch (cfg, eclass_reg, mono_defaults.enum_class, OP_PBEQ, is_null_bb); - MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, false_bb); - } else if (klass->cast_class == mono_defaults.enum_class->parent) { - mini_emit_class_check_branch (cfg, eclass_reg, mono_defaults.enum_class->parent, OP_PBEQ, is_null_bb); - mini_emit_class_check_branch (cfg, eclass_reg, mono_defaults.enum_class, OP_PBEQ, is_null_bb); - MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, false_bb); - } else if (klass->cast_class == mono_defaults.enum_class) { - mini_emit_class_check_branch (cfg, eclass_reg, mono_defaults.enum_class, OP_PBEQ, is_null_bb); - MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, false_bb); - } else if (mono_class_is_interface (klass->cast_class)) { - mini_emit_iface_class_cast (cfg, eclass_reg, klass->cast_class, false_bb, is_null_bb); - } else { - if ((klass->rank == 1) && (klass->byval_arg.type == MONO_TYPE_SZARRAY)) { - /* Check that the object is a vector too */ - int bounds_reg = alloc_preg (cfg); - MONO_EMIT_NEW_LOAD_MEMBASE (cfg, bounds_reg, obj_reg, MONO_STRUCT_OFFSET (MonoArray, bounds)); - MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, bounds_reg, 0); - MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, false_bb); - } - - /* the is_null_bb target simply copies the input register to the output */ - 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, MONO_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) && mono_class_is_sealed (klass)) { - 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); - if (!vt) { - mono_cfg_set_exception (cfg, MONO_EXCEPTION_TYPE_LOAD); - cfg->exception_ptr = klass; - return NULL; - } - MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, vtable_reg, vt); - } else { - MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass)); - MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, klass_reg, klass); - } - MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, false_bb); - MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, is_null_bb); - } else { - MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass)); - /* the is_null_bb target simply copies the input register to the output */ - mini_emit_isninst_cast_inst (cfg, klass_reg, klass, klass_inst, false_bb, is_null_bb); - } - } - } - - MONO_START_BB (cfg, false_bb); - - MONO_EMIT_NEW_PCONST (cfg, res_reg, 0); - MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb); - - MONO_START_BB (cfg, is_null_bb); - - MONO_START_BB (cfg, end_bb); - - return ins; -} - -static MonoInst* -handle_cisinst (MonoCompile *cfg, MonoClass *klass, MonoInst *src) -{ - /* This opcode takes as input an object reference and a class, and returns: - 0) if the object is an instance of the class, - 1) if the object is not instance of the class, - 2) if the object is a proxy whose type cannot be determined */ - - MonoInst *ins; -#ifndef DISABLE_REMOTING - MonoBasicBlock *true_bb, *false_bb, *false2_bb, *end_bb, *no_proxy_bb, *interface_fail_bb; -#else - MonoBasicBlock *true_bb, *false_bb, *end_bb; -#endif - int obj_reg = src->dreg; - int dreg = alloc_ireg (cfg); - int tmp_reg; -#ifndef DISABLE_REMOTING - int klass_reg = alloc_preg (cfg); -#endif - - NEW_BBLOCK (cfg, true_bb); - NEW_BBLOCK (cfg, false_bb); - NEW_BBLOCK (cfg, end_bb); -#ifndef DISABLE_REMOTING - NEW_BBLOCK (cfg, false2_bb); - NEW_BBLOCK (cfg, no_proxy_bb); -#endif - - MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, obj_reg, 0); - MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, false_bb); - - if (mono_class_is_interface (klass)) { -#ifndef DISABLE_REMOTING - NEW_BBLOCK (cfg, interface_fail_bb); -#endif - - tmp_reg = alloc_preg (cfg); - MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable)); -#ifndef DISABLE_REMOTING - mini_emit_iface_cast (cfg, tmp_reg, klass, interface_fail_bb, true_bb); - MONO_START_BB (cfg, interface_fail_bb); - MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, tmp_reg, MONO_STRUCT_OFFSET (MonoVTable, klass)); - - mini_emit_class_check_branch (cfg, klass_reg, mono_defaults.transparent_proxy_class, OP_PBNE_UN, false_bb); - - tmp_reg = alloc_preg (cfg); - MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoTransparentProxy, custom_type_info)); - MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, tmp_reg, 0); - MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, false2_bb); -#else - mini_emit_iface_cast (cfg, tmp_reg, klass, false_bb, true_bb); -#endif - } else { -#ifndef DISABLE_REMOTING - tmp_reg = alloc_preg (cfg); - MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable)); - MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, tmp_reg, MONO_STRUCT_OFFSET (MonoVTable, klass)); - - mini_emit_class_check_branch (cfg, klass_reg, mono_defaults.transparent_proxy_class, OP_PBNE_UN, no_proxy_bb); - tmp_reg = alloc_preg (cfg); - MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoTransparentProxy, remote_class)); - MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, tmp_reg, MONO_STRUCT_OFFSET (MonoRemoteClass, proxy_class)); - - tmp_reg = alloc_preg (cfg); - MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoTransparentProxy, custom_type_info)); - MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, tmp_reg, 0); - MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, no_proxy_bb); - - mini_emit_isninst_cast (cfg, klass_reg, klass, false2_bb, true_bb); - MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, false2_bb); - - MONO_START_BB (cfg, no_proxy_bb); - - mini_emit_isninst_cast (cfg, klass_reg, klass, false_bb, true_bb); -#else - g_error ("transparent proxy support is disabled while trying to JIT code that uses it"); -#endif - } - - MONO_START_BB (cfg, false_bb); - - MONO_EMIT_NEW_ICONST (cfg, dreg, 1); - MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb); - -#ifndef DISABLE_REMOTING - MONO_START_BB (cfg, false2_bb); + EMIT_NEW_VTABLECONST (cfg, iargs [0], vtable); + } + } - MONO_EMIT_NEW_ICONST (cfg, dreg, 2); - MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb); -#endif + return mono_emit_jit_icall (cfg, alloc_ftn, iargs); +} + +/* + * Returns NULL and set the cfg exception on error. + */ +static MonoInst* +handle_box (MonoCompile *cfg, MonoInst *val, MonoClass *klass, int context_used) +{ + MonoInst *alloc, *ins; - MONO_START_BB (cfg, true_bb); + if (mono_class_is_nullable (klass)) { + MonoMethod* method = mono_class_get_method_from_name (klass, "Box", 1); - MONO_EMIT_NEW_ICONST (cfg, dreg, 0); + if (context_used) { + if (cfg->llvm_only && cfg->gsharedvt) { + MonoInst *addr = emit_get_rgctx_method (cfg, context_used, method, + MONO_RGCTX_INFO_GENERIC_METHOD_CODE); + return emit_llvmonly_calli (cfg, mono_method_signature (method), &val, addr); + } else { + /* FIXME: What if the class is shared? We might not + have to get the method address from the RGCTX. */ + MonoInst *addr = emit_get_rgctx_method (cfg, context_used, method, + MONO_RGCTX_INFO_GENERIC_METHOD_CODE); + MonoInst *rgctx = emit_get_rgctx (cfg, cfg->method, context_used); - MONO_START_BB (cfg, end_bb); + return mono_emit_calli (cfg, mono_method_signature (method), &val, addr, NULL, rgctx); + } + } else { + gboolean pass_vtable, pass_mrgctx; + MonoInst *rgctx_arg = NULL; - /* FIXME: */ - MONO_INST_NEW (cfg, ins, OP_ICONST); - ins->dreg = dreg; - ins->type = STACK_I4; + check_method_sharing (cfg, method, &pass_vtable, &pass_mrgctx); + g_assert (!pass_mrgctx); - return ins; -} + if (pass_vtable) { + MonoVTable *vtable = mono_class_vtable (cfg->domain, method->klass); -static MonoInst* -handle_ccastclass (MonoCompile *cfg, MonoClass *klass, MonoInst *src) -{ - /* This opcode takes as input an object reference and a class, and returns: - 0) if the object is an instance of the class, - 1) if the object is a proxy whose type cannot be determined - an InvalidCastException exception is thrown otherwhise*/ - - MonoInst *ins; -#ifndef DISABLE_REMOTING - MonoBasicBlock *end_bb, *ok_result_bb, *no_proxy_bb, *interface_fail_bb, *fail_1_bb; -#else - MonoBasicBlock *ok_result_bb; -#endif - int obj_reg = src->dreg; - int dreg = alloc_ireg (cfg); - int tmp_reg = alloc_preg (cfg); + g_assert (vtable); + EMIT_NEW_VTABLECONST (cfg, rgctx_arg, vtable); + } -#ifndef DISABLE_REMOTING - int klass_reg = alloc_preg (cfg); - NEW_BBLOCK (cfg, end_bb); -#endif + return mono_emit_method_call_full (cfg, method, NULL, FALSE, &val, NULL, NULL, rgctx_arg); + } + } - NEW_BBLOCK (cfg, ok_result_bb); + if (mini_is_gsharedvt_klass (klass)) { + MonoBasicBlock *is_ref_bb, *is_nullable_bb, *end_bb; + MonoInst *res, *is_ref, *src_var, *addr; + int dreg; - MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, obj_reg, 0); - MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, ok_result_bb); + dreg = alloc_ireg (cfg); - save_cast_details (cfg, klass, obj_reg, FALSE); + NEW_BBLOCK (cfg, is_ref_bb); + NEW_BBLOCK (cfg, is_nullable_bb); + NEW_BBLOCK (cfg, end_bb); + is_ref = emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_CLASS_BOX_TYPE); + MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, is_ref->dreg, MONO_GSHAREDVT_BOX_TYPE_REF); + MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, is_ref_bb); - if (mono_class_is_interface (klass)) { -#ifndef DISABLE_REMOTING - NEW_BBLOCK (cfg, interface_fail_bb); - - MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable)); - mini_emit_iface_cast (cfg, tmp_reg, klass, interface_fail_bb, ok_result_bb); - MONO_START_BB (cfg, interface_fail_bb); - MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, tmp_reg, MONO_STRUCT_OFFSET (MonoVTable, klass)); + MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, is_ref->dreg, MONO_GSHAREDVT_BOX_TYPE_NULLABLE); + MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, is_nullable_bb); - mini_emit_class_check (cfg, klass_reg, mono_defaults.transparent_proxy_class); + /* Non-ref case */ + alloc = handle_alloc (cfg, klass, TRUE, context_used); + if (!alloc) + return NULL; + EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, alloc->dreg, sizeof (MonoObject), val->dreg); + ins->opcode = OP_STOREV_MEMBASE; - tmp_reg = alloc_preg (cfg); - MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoTransparentProxy, custom_type_info)); - MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, tmp_reg, 0); - MONO_EMIT_NEW_COND_EXC (cfg, EQ, "InvalidCastException"); - - MONO_EMIT_NEW_ICONST (cfg, dreg, 1); + EMIT_NEW_UNALU (cfg, res, OP_MOVE, dreg, alloc->dreg); + res->type = STACK_OBJ; + res->klass = klass; MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb); -#else - MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable)); - mini_emit_iface_cast (cfg, tmp_reg, klass, NULL, NULL); - MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, ok_result_bb); -#endif - } else { -#ifndef DISABLE_REMOTING - NEW_BBLOCK (cfg, no_proxy_bb); + + /* Ref case */ + MONO_START_BB (cfg, is_ref_bb); - MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable)); - MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, tmp_reg, MONO_STRUCT_OFFSET (MonoVTable, klass)); - mini_emit_class_check_branch (cfg, klass_reg, mono_defaults.transparent_proxy_class, OP_PBNE_UN, no_proxy_bb); + /* val is a vtype, so has to load the value manually */ + src_var = get_vreg_to_inst (cfg, val->dreg); + if (!src_var) + src_var = mono_compile_create_var_for_vreg (cfg, &klass->byval_arg, OP_LOCAL, val->dreg); + EMIT_NEW_VARLOADA (cfg, addr, src_var, src_var->inst_vtype); + MONO_EMIT_NEW_LOAD_MEMBASE (cfg, dreg, addr->dreg, 0); + MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb); - tmp_reg = alloc_preg (cfg); - MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoTransparentProxy, remote_class)); - MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, tmp_reg, MONO_STRUCT_OFFSET (MonoRemoteClass, proxy_class)); + /* Nullable case */ + MONO_START_BB (cfg, is_nullable_bb); - tmp_reg = alloc_preg (cfg); - MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoTransparentProxy, custom_type_info)); - MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, tmp_reg, 0); - MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, no_proxy_bb); + { + MonoInst *addr = emit_get_gsharedvt_info_klass (cfg, klass, + MONO_RGCTX_INFO_NULLABLE_CLASS_BOX); + MonoInst *box_call; + MonoMethodSignature *box_sig; - NEW_BBLOCK (cfg, fail_1_bb); - - mini_emit_isninst_cast (cfg, klass_reg, klass, fail_1_bb, ok_result_bb); + /* + * klass is Nullable, need to call Nullable.Box () using a gsharedvt signature, but we cannot + * construct that method at JIT time, so have to do things by hand. + */ + box_sig = (MonoMethodSignature *)mono_mempool_alloc0 (cfg->mempool, MONO_SIZEOF_METHOD_SIGNATURE + (1 * sizeof (MonoType *))); + box_sig->ret = &mono_defaults.object_class->byval_arg; + box_sig->param_count = 1; + box_sig->params [0] = &klass->byval_arg; - MONO_START_BB (cfg, fail_1_bb); + if (cfg->llvm_only) + box_call = emit_llvmonly_calli (cfg, box_sig, &val, addr); + else + box_call = mono_emit_calli (cfg, box_sig, &val, addr, NULL, NULL); + EMIT_NEW_UNALU (cfg, res, OP_MOVE, dreg, box_call->dreg); + res->type = STACK_OBJ; + res->klass = klass; + } - MONO_EMIT_NEW_ICONST (cfg, dreg, 1); MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb); - MONO_START_BB (cfg, no_proxy_bb); + MONO_START_BB (cfg, end_bb); + + return res; + } else { + alloc = handle_alloc (cfg, klass, TRUE, context_used); + if (!alloc) + return NULL; - mini_emit_castclass (cfg, obj_reg, klass_reg, klass, ok_result_bb); -#else - g_error ("Transparent proxy support is disabled while trying to JIT code that uses it"); -#endif + EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, alloc->dreg, sizeof (MonoObject), val->dreg); + return alloc; } +} - MONO_START_BB (cfg, ok_result_bb); +static GHashTable* direct_icall_type_hash; - MONO_EMIT_NEW_ICONST (cfg, dreg, 0); +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; -#ifndef DISABLE_REMOTING - MONO_START_BB (cfg, end_bb); -#endif + /* + * 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); - /* FIXME: */ - MONO_INST_NEW (cfg, ins, OP_ICONST); - ins->dreg = dreg; - ins->type = STACK_I4; + 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)); + g_hash_table_insert (h, (char*)"Monitor", GUINT_TO_POINTER (1)); + mono_memory_barrier (); + direct_icall_type_hash = h; + } - return ins; + 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; +} + +static gboolean +method_needs_stack_walk (MonoCompile *cfg, MonoMethod *cmethod) +{ + if (cmethod->klass == mono_defaults.systemtype_class) { + if (!strcmp (cmethod->name, "GetType")) + return TRUE; + } + return FALSE; } static G_GNUC_UNUSED MonoInst* @@ -5192,7 +4441,7 @@ handle_constrained_gsharedvt_call (MonoCompile *cfg, MonoMethod *cmethod, MonoMe * pack the arguments into an array, and do the rest of the work in in an icall. */ if (((cmethod->klass == mono_defaults.object_class) || mono_class_is_interface (cmethod->klass) || (!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 (fsig->ret)) && + (MONO_TYPE_IS_VOID (fsig->ret) || MONO_TYPE_IS_PRIMITIVE (fsig->ret) || MONO_TYPE_IS_REFERENCE (fsig->ret) || MONO_TYPE_ISSTRUCT (fsig->ret) || mono_class_is_enum (mono_class_from_mono_type (fsig->ret)) || mini_is_gsharedvt_type (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 (fsig->params [0]))))) { MonoInst *args [16]; @@ -5209,7 +4458,7 @@ handle_constrained_gsharedvt_call (MonoCompile *cfg, MonoMethod *cmethod, MonoMe 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); + args [2] = mini_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) { @@ -5244,7 +4493,7 @@ handle_constrained_gsharedvt_call (MonoCompile *cfg, MonoMethod *cmethod, MonoMe if (mini_is_gsharedvt_type (fsig->ret)) { ins = handle_unbox_gsharedvt (cfg, mono_class_from_mono_type (fsig->ret), ins); - } else if (MONO_TYPE_IS_PRIMITIVE (fsig->ret) || MONO_TYPE_ISSTRUCT (fsig->ret)) { + } else if (MONO_TYPE_IS_PRIMITIVE (fsig->ret) || MONO_TYPE_ISSTRUCT (fsig->ret) || mono_class_is_enum (mono_class_from_mono_type (fsig->ret))) { MonoInst *add; /* Unbox */ @@ -5336,9 +4585,11 @@ mono_method_check_inlining (MonoCompile *cfg, MonoMethod *method) /* also consider num_locals? */ /* Do the size check early to avoid creating vtables */ if (!inline_limit_inited) { - if (g_getenv ("MONO_INLINELIMIT")) - inline_limit = atoi (g_getenv ("MONO_INLINELIMIT")); - else + char *inlinelimit; + if ((inlinelimit = g_getenv ("MONO_INLINELIMIT"))) { + inline_limit = atoi (inlinelimit); + g_free (inlinelimit); + } else inline_limit = INLINE_LENGTH_LIMIT; inline_limit_inited = TRUE; } @@ -5638,18 +4889,18 @@ static MonoBreakPolicyFunc break_policy_func = always_insert_breakpoint; /** * mono_set_break_policy: - * policy_callback: the new callback function + * \param 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 + * (both break IL instructions and \c 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 + * \p policy_callback will be called every time a break point instruction needs to + * be inserted with the method argument being the method that calls \c Debugger.Break + * or has the IL \c break instruction. The callback should return \c MONO_BREAK_POLICY_NEVER * if it wants the breakpoint to not be effective in the given method. - * #MONO_BREAK_POLICY_ALWAYS is the default. + * \c MONO_BREAK_POLICY_ALWAYS is the default. */ void mono_set_break_policy (MonoBreakPolicyFunc policy_callback) @@ -5983,8 +5234,7 @@ static MonoInst* mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args) { MonoInst *ins = NULL; - - MonoClass *runtime_helpers_class = mono_class_get_runtime_helpers_class (); + MonoClass *runtime_helpers_class = mono_class_get_runtime_helpers_class (); if (cmethod->klass == mono_defaults.string_class) { if (strcmp (cmethod->name, "get_Chars") == 0 && fsig->param_count + fsig->hasthis == 2) { @@ -6130,6 +5380,37 @@ mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign } else if (cmethod->klass == runtime_helpers_class) { if (strcmp (cmethod->name, "get_OffsetToStringData") == 0 && fsig->param_count == 0) { EMIT_NEW_ICONST (cfg, ins, MONO_STRUCT_OFFSET (MonoString, chars)); + return ins; + } else if (strcmp (cmethod->name, "IsReferenceOrContainsReferences") == 0 && fsig->param_count == 0) { + MonoGenericContext *ctx = mono_method_get_context (cmethod); + g_assert (ctx); + g_assert (ctx->method_inst); + g_assert (ctx->method_inst->type_argc == 1); + MonoType *t = mini_get_underlying_type (ctx->method_inst->type_argv [0]); + MonoClass *klass = mono_class_from_mono_type (t); + + ins = NULL; + + mono_class_init (klass); + if (MONO_TYPE_IS_REFERENCE (t)) + EMIT_NEW_ICONST (cfg, ins, 1); + else if (MONO_TYPE_IS_PRIMITIVE (t)) + EMIT_NEW_ICONST (cfg, ins, 0); + else if (cfg->gshared && (t->type == MONO_TYPE_VAR || t->type == MONO_TYPE_MVAR) && !mini_type_var_is_vt (t)) + EMIT_NEW_ICONST (cfg, ins, 1); + else if (!cfg->gshared || !mini_class_check_context_used (cfg, klass)) + EMIT_NEW_ICONST (cfg, ins, klass->has_references ? 1 : 0); + else { + g_assert (cfg->gshared); + + int context_used = mini_class_check_context_used (cfg, klass); + + /* This returns 1 or 2 */ + MonoInst *info = mini_emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_CLASS_IS_REF_OR_CONTAINS_REFS); + int dreg = alloc_ireg (cfg); + EMIT_NEW_BIALU_IMM (cfg, ins, OP_ISUB_IMM, dreg, info->dreg, 1); + } + return ins; } else return NULL; @@ -6156,7 +5437,7 @@ mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign ins = mono_emit_jit_icall (cfg, is_v4 ? (gpointer)mono_monitor_enter_v4_fast : (gpointer)mono_monitor_enter_fast, args); MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ICOMPARE_IMM, -1, ins->dreg, 0); MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBNE_UN, end_bb); - ins = mono_emit_jit_icall (cfg, is_v4 ? (gpointer)mono_monitor_enter_v4 : (gpointer)mono_monitor_enter, args); + ins = mono_emit_jit_icall (cfg, is_v4 ? (gpointer)mono_monitor_enter_v4_internal : (gpointer)mono_monitor_enter_internal, args); MONO_START_BB (cfg, end_bb); return ins; } @@ -7090,6 +6371,12 @@ emit_init_local (MonoCompile *cfg, int local, MonoType *type, gboolean init) } } +int +mini_inline_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **sp, guchar *ip, guint real_offset, gboolean inline_always) +{ + return inline_method (cfg, cmethod, fsig, sp, ip, real_offset, inline_always); +} + /* * inline_method: * @@ -7235,7 +6522,8 @@ inline_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, * Get rid of the begin and end bblocks if possible to aid local * optimizations. */ - mono_merge_basic_blocks (cfg, prev_cbb, sbblock); + if (prev_cbb->out_count == 1) + mono_merge_basic_blocks (cfg, prev_cbb, sbblock); if ((prev_cbb->out_count == 1) && (prev_cbb->out_bb [0]->in_count == 1) && (prev_cbb->out_bb [0] != ebblock)) mono_merge_basic_blocks (cfg, prev_cbb, prev_cbb->out_bb [0]); @@ -7443,7 +6731,7 @@ mini_get_method_allow_open (MonoMethod *m, guint32 token, MonoClass *klass, Mono { MonoMethod *method; - mono_error_init (error); + error_init (error); if (m->wrapper_type != MONO_WRAPPER_NONE) { method = (MonoMethod *)mono_method_get_wrapper_data (m, token); @@ -7500,7 +6788,7 @@ mini_get_signature (MonoMethod *method, guint32 token, MonoGenericContext *conte { MonoMethodSignature *fsig; - mono_error_init (error); + error_init (error); if (method->wrapper_type != MONO_WRAPPER_NONE) { fsig = (MonoMethodSignature *)mono_method_get_wrapper_data (method, token); } else { @@ -7610,14 +6898,12 @@ initialize_array_data (MonoMethod *method, gboolean aot, unsigned char *ip, Mono return NULL; if (strcmp (cmethod->name, "InitializeArray") || strcmp (cmethod->klass->name, "RuntimeHelpers") || cmethod->klass->image != mono_defaults.corlib) return NULL; - switch (mono_type_get_underlying_type (&klass->byval_arg)->type) { - case MONO_TYPE_BOOLEAN: + switch (mini_get_underlying_type (&klass->byval_arg)->type) { case MONO_TYPE_I1: case MONO_TYPE_U1: size = 1; break; /* we need to swap on big endian, so punt. Should we handle R4 and R8 as well? */ #if TARGET_BYTE_ORDER == G_LITTLE_ENDIAN - case MONO_TYPE_CHAR: case MONO_TYPE_I2: case MONO_TYPE_U2: size = 2; break; @@ -7738,6 +7024,7 @@ emit_llvmonly_virtual_call (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSig gboolean variant_iface = FALSE; guint32 slot; int offset; + gboolean special_array_interface = cmethod->klass->is_array_special_interface; /* * In llvm-only mode, vtables contain function descriptors instead of @@ -7796,7 +7083,7 @@ emit_llvmonly_virtual_call (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSig return emit_extra_arg_calli (cfg, fsig, sp, arg_reg, call_target); } - if (!fsig->generic_param_count && is_iface && !variant_iface && !is_gsharedvt) { + if (!fsig->generic_param_count && is_iface && !variant_iface && !is_gsharedvt && !special_array_interface) { /* * A simple interface call * @@ -7835,7 +7122,7 @@ emit_llvmonly_virtual_call (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSig return emit_llvmonly_calli (cfg, fsig, sp, ftndesc_ins); } - if ((fsig->generic_param_count || variant_iface) && !is_gsharedvt) { + if ((fsig->generic_param_count || variant_iface || special_array_interface) && !is_gsharedvt) { /* * This is similar to the interface case, the vtable slot points to an imt thunk which is * dynamically extended as more instantiations are discovered. @@ -8059,7 +7346,7 @@ handle_ctor_call (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fs cmethod, MONO_RGCTX_INFO_METHOD_RGCTX); } else { if (context_used) { - vtable_arg = emit_get_rgctx_klass (cfg, context_used, + vtable_arg = mini_emit_get_rgctx_klass (cfg, context_used, cmethod->klass, MONO_RGCTX_INFO_VTABLE); } else { MonoVTable *vtable = mono_class_vtable (cfg->domain, cmethod->klass); @@ -8274,6 +7561,16 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b mono_bitset_set_fast (seq_point_locs, sps [i].il_offset); } g_free (sps); + + MonoDebugMethodAsyncInfo* asyncMethod = mono_debug_lookup_method_async_debug_info (method); + if (asyncMethod) { + for (i = 0; asyncMethod != NULL && i < asyncMethod->num_awaits; i++) + { + mono_bitset_set_fast (seq_point_locs, asyncMethod->resume_offsets[i]); + mono_bitset_set_fast (seq_point_locs, asyncMethod->yield_offsets[i]); + } + mono_debug_free_method_async_debug_info (asyncMethod); + } } else if (!method->wrapper_type && !method->dynamic && mono_debug_image_has_debug_info (method->klass->image)) { /* Methods without line number info like auto-generated property accessors */ seq_point_locs = mono_bitset_mem_new (mono_mempool_alloc0 (cfg->mempool, mono_bitset_alloc_size (header->code_size, 0)), header->code_size, 0); @@ -9309,6 +8606,15 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b CHECK_CFG_ERROR; } } + + if (constrained_class->enumtype && !strcmp (cmethod->name, "GetHashCode")) { + /* Use the corresponding method from the base type to avoid boxing */ + MonoType *base_type = mono_class_enum_basetype (constrained_class); + g_assert (base_type); + constrained_class = mono_class_from_mono_type (base_type); + cmethod = mono_class_get_method_from_name (constrained_class, cmethod->name, 0); + g_assert (cmethod); + } } if (!dont_verify && !cfg->skip_visibility) { @@ -9599,13 +8905,13 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b * request a generic sharing context. */ if (context_used && - ((method->flags & METHOD_ATTRIBUTE_STATIC) || method->klass->valuetype)) + ((cfg->method->flags & METHOD_ATTRIBUTE_STATIC) || cfg->method->klass->valuetype)) mono_get_vtable_var (cfg); } if (pass_vtable) { if (context_used) { - vtable_arg = emit_get_rgctx_klass (cfg, context_used, cmethod->klass, MONO_RGCTX_INFO_VTABLE); + vtable_arg = mini_emit_get_rgctx_klass (cfg, context_used, cmethod->klass, MONO_RGCTX_INFO_VTABLE); } else { MonoVTable *vtable = mono_class_vtable (cfg->domain, cmethod->klass); @@ -11577,7 +10883,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b is_special_static = mono_class_field_is_special_static (field); if (is_special_static && ((gsize)addr & 0x80000000) == 0) - thread_ins = mono_get_thread_intrinsic (cfg); + thread_ins = mono_create_tls_get (cfg, TLS_KEY_THREAD); else thread_ins = NULL; @@ -11591,9 +10897,9 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b guint32 offset; int idx, static_data_reg, array_reg, dreg; - GSHAREDVT_FAILURE (op); + if (context_used && cfg->gsharedvt && mini_is_gsharedvt_klass (klass)) + GSHAREDVT_FAILURE (op); - MONO_ADD_INS (cfg->cbb, thread_ins); static_data_reg = alloc_ireg (cfg); MONO_EMIT_NEW_LOAD_MEMBASE (cfg, static_data_reg, thread_ins->dreg, MONO_STRUCT_OFFSET (MonoInternalThread, static_data)); @@ -11655,7 +10961,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b * * super_info.static_data + field->offset */ - static_data = emit_get_rgctx_klass (cfg, context_used, + static_data = mini_emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_STATIC_DATA); if (mini_is_gsharedvt_klass (klass)) { @@ -11906,7 +11212,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b /* FIXME: Use OP_NEWARR and decompose later to help abcrem */ /* vtable */ - args [0] = emit_get_rgctx_klass (cfg, context_used, + args [0] = mini_emit_get_rgctx_klass (cfg, context_used, array_class, MONO_RGCTX_INFO_VTABLE); /* array len */ args [1] = sp [0]; @@ -12162,7 +11468,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b if (context_used) { MonoInst *klass_ins; - klass_ins = emit_get_rgctx_klass (cfg, context_used, + klass_ins = mini_emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_KLASS); // FIXME: @@ -12199,7 +11505,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b MonoInst *const_ins; int type_reg = alloc_preg (cfg); - const_ins = emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_KLASS); + const_ins = mini_emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_KLASS); MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREP_MEMBASE_REG, addr->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, klass), const_ins->dreg); MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ADD_IMM, type_reg, const_ins->dreg, MONO_STRUCT_OFFSET (MonoClass, byval_arg)); MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREP_MEMBASE_REG, addr->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, type), type_reg); @@ -12296,11 +11602,11 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b mono_class_init (tclass); if (context_used) { - ins = emit_get_rgctx_klass (cfg, context_used, + ins = mini_emit_get_rgctx_klass (cfg, context_used, tclass, MONO_RGCTX_INFO_REFLECTION_TYPE); } else if (cfg->compile_aot) { if (method->wrapper_type) { - mono_error_init (&error); //got to do it since there are multiple conditionals below + error_init (&error); //got to do it since there are multiple conditionals below if (mono_class_get_checked (tclass->image, tclass->type_token, &error) == tclass && !generic_context) { /* Special case for static synchronized wrappers */ EMIT_NEW_TYPE_FROM_HANDLE_CONST (cfg, ins, tclass->image, tclass->type_token, generic_context); @@ -12328,7 +11634,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b if (context_used) { if (handle_class == mono_defaults.typehandle_class) { - ins = emit_get_rgctx_klass (cfg, context_used, + ins = mini_emit_get_rgctx_klass (cfg, context_used, mono_class_from_mono_type ((MonoType *)handle), MONO_RGCTX_INFO_TYPE); } else if (handle_class == mono_defaults.methodhandle_class) { @@ -12731,22 +12037,6 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b ip += 6; break; } - case CEE_MONO_CISINST: - case CEE_MONO_CCASTCLASS: { - int token; - CHECK_STACK (1); - --sp; - CHECK_OPSIZE (6); - token = read32 (ip + 2); - klass = (MonoClass *)mono_method_get_wrapper_data (method, token); - if (ip [1] == CEE_MONO_CISINST) - ins = handle_cisinst (cfg, klass, sp [0]); - else - ins = handle_ccastclass (cfg, klass, sp [0]); - *sp++ = ins; - ip += 6; - break; - } case CEE_MONO_SAVE_LMF: case CEE_MONO_RESTORE_LMF: ip += 2; @@ -12773,18 +12063,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b g_assert (key < TLS_KEY_NUM); ins = mono_create_tls_get (cfg, key); - if (!ins) { - if (cfg->compile_aot) { - DISABLE_AOT (cfg); - MONO_INST_NEW (cfg, ins, OP_TLS_GET); - ins->dreg = alloc_preg (cfg); - ins->type = STACK_PTR; - } else { - g_assert_not_reached (); - } - } + g_assert (ins); ins->type = STACK_PTR; - MONO_ADD_INS (cfg->cbb, ins); *sp++ = ins; ip += 6; break; @@ -12852,10 +12132,10 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b EMIT_NEW_PCONST (cfg, ins, NULL); MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, cfg->orig_domain_var->dreg, ins->dreg); - ad_ins = mono_get_domain_intrinsic (cfg); - jit_tls_ins = mono_get_jit_tls_intrinsic (cfg); + ad_ins = mono_create_tls_get (cfg, TLS_KEY_DOMAIN); + jit_tls_ins = mono_create_tls_get (cfg, TLS_KEY_JIT_TLS); - if (cfg->backend->have_tls_get && ad_ins && jit_tls_ins) { + if (ad_ins && jit_tls_ins) { NEW_BBLOCK (cfg, next_bb); NEW_BBLOCK (cfg, call_bb); @@ -12865,11 +12145,9 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b } else { EMIT_NEW_PCONST (cfg, domain_ins, cfg->domain); } - MONO_ADD_INS (cfg->cbb, ad_ins); MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, ad_ins->dreg, domain_ins->dreg); MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, call_bb); - MONO_ADD_INS (cfg->cbb, jit_tls_ins); MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, jit_tls_ins->dreg, 0); MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, call_bb); @@ -13070,6 +12348,21 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b ins->type = STACK_I4; MONO_ADD_INS (cfg->cbb, ins); + ip += 2; + *sp++ = ins; + break; + case CEE_MONO_GET_RGCTX_ARG: + CHECK_OPSIZE (2); + CHECK_STACK_OVF (1); + + mono_create_rgctx_var (cfg); + + MONO_INST_NEW (cfg, ins, OP_MOVE); + ins->dreg = alloc_dreg (cfg, STACK_PTR); + ins->sreg1 = cfg->rgctx_var->dreg; + ins->type = STACK_PTR; + MONO_ADD_INS (cfg->cbb, ins); + ip += 2; *sp++ = ins; break; @@ -13668,11 +12961,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b cfg->cbb = init_localsbb; - if ((get_domain = mono_get_domain_intrinsic (cfg))) { - MONO_ADD_INS (cfg->cbb, get_domain); - } else { - get_domain = mono_emit_jit_icall (cfg, mono_domain_get, NULL); - } + get_domain = mono_create_tls_get (cfg, TLS_KEY_DOMAIN); NEW_TEMPSTORE (cfg, store, cfg->domainvar->inst_c0, get_domain); MONO_ADD_INS (cfg->cbb, store); } @@ -14562,7 +13851,7 @@ mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local_opts) int orig_next_vreg; guint32 *vreg_to_lvreg; guint32 *lvregs; - guint32 i, lvregs_len; + guint32 i, lvregs_len, lvregs_size; gboolean dest_has_lvreg = FALSE; MonoStackType stacktypes [128]; MonoInst **live_range_start, **live_range_end; @@ -14635,7 +13924,8 @@ mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local_opts) */ orig_next_vreg = cfg->next_vreg; vreg_to_lvreg = (guint32 *)mono_mempool_alloc0 (cfg->mempool, sizeof (guint32) * cfg->next_vreg); - lvregs = (guint32 *)mono_mempool_alloc (cfg->mempool, sizeof (guint32) * 1024); + lvregs_size = 1024; + lvregs = (guint32 *)mono_mempool_alloc (cfg->mempool, sizeof (guint32) * lvregs_size); lvregs_len = 0; /* @@ -15018,7 +14308,12 @@ mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local_opts) } g_assert (sreg != -1); vreg_to_lvreg [var->dreg] = sreg; - g_assert (lvregs_len < 1024); + if (lvregs_len >= lvregs_size) { + guint32 *new_lvregs = mono_mempool_alloc0 (cfg->mempool, sizeof (guint32) * lvregs_size * 2); + memcpy (new_lvregs, lvregs, sizeof (guint32) * lvregs_size); + lvregs = new_lvregs; + lvregs_size *= 2; + } lvregs [lvregs_len ++] = var->dreg; } } @@ -15065,7 +14360,12 @@ mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local_opts) if (dest_has_lvreg) { g_assert (ins->dreg != -1); vreg_to_lvreg [prev_dreg] = ins->dreg; - g_assert (lvregs_len < 1024); + if (lvregs_len >= lvregs_size) { + guint32 *new_lvregs = mono_mempool_alloc0 (cfg->mempool, sizeof (guint32) * lvregs_size * 2); + memcpy (new_lvregs, lvregs, sizeof (guint32) * lvregs_size); + lvregs = new_lvregs; + lvregs_size *= 2; + } lvregs [lvregs_len ++] = prev_dreg; dest_has_lvreg = FALSE; } @@ -15153,73 +14453,6 @@ mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local_opts) g_free (live_range_end_bb); } -static void -mono_decompose_typecheck (MonoCompile *cfg, MonoBasicBlock *bb, MonoInst *ins) -{ - MonoInst *ret, *move, *source; - MonoClass *klass = ins->klass; - int context_used = mini_class_check_context_used (cfg, klass); - int is_isinst = ins->opcode == OP_ISINST; - g_assert (is_isinst || ins->opcode == OP_CASTCLASS); - source = get_vreg_to_inst (cfg, ins->sreg1); - if (!source || source == (MonoInst *) -1) - source = mono_compile_create_var_for_vreg (cfg, &mono_defaults.object_class->byval_arg, OP_LOCAL, ins->sreg1); - g_assert (source && source != (MonoInst *) -1); - - MonoBasicBlock *first_bb; - NEW_BBLOCK (cfg, first_bb); - cfg->cbb = first_bb; - - if (!context_used && (mini_class_has_reference_variant_generic_argument (cfg, klass, context_used) || klass->is_array_special_interface)) { - if (is_isinst) - ret = emit_isinst_with_cache_nonshared (cfg, source, klass); - else - ret = emit_castclass_with_cache_nonshared (cfg, source, klass); - } else if (!context_used && (mono_class_is_marshalbyref (klass) || mono_class_is_interface (klass))) { - MonoInst *iargs [1]; - int costs; - - iargs [0] = source; - if (is_isinst) { - MonoMethod *wrapper = mono_marshal_get_isinst (klass); - costs = inline_method (cfg, wrapper, mono_method_signature (wrapper), iargs, 0, 0, TRUE); - } else { - MonoMethod *wrapper = mono_marshal_get_castclass (klass); - save_cast_details (cfg, klass, source->dreg, TRUE); - costs = inline_method (cfg, wrapper, mono_method_signature (wrapper), iargs, 0, 0, TRUE); - reset_cast_details (cfg); - } - g_assert (costs > 0); - ret = iargs [0]; - } else { - if (is_isinst) - ret = handle_isinst (cfg, klass, source, context_used); - else - ret = handle_castclass (cfg, klass, source, context_used); - } - EMIT_NEW_UNALU (cfg, move, OP_MOVE, ins->dreg, ret->dreg); - - g_assert (cfg->cbb->code || first_bb->code); - MonoInst *prev = ins->prev; - mono_replace_ins (cfg, bb, ins, &prev, first_bb, cfg->cbb); -} - -void -mono_decompose_typechecks (MonoCompile *cfg) -{ - for (MonoBasicBlock *bb = cfg->bb_entry; bb; bb = bb->next_bb) { - MonoInst *ins; - MONO_BB_FOR_EACH_INS (bb, ins) { - switch (ins->opcode) { - case OP_ISINST: - case OP_CASTCLASS: - mono_decompose_typecheck (cfg, bb, ins); - break; - } - } - } -} - /** * FIXME: @@ -15347,6 +14580,9 @@ NOTES #else /* !DISABLE_JIT */ -MONO_EMPTY_SOURCE_FILE (method_to_ir); +void +mono_set_break_policy (MonoBreakPolicyFunc policy_callback) +{ +} #endif /* !DISABLE_JIT */