From: Rodrigo Kumpera Date: Thu, 24 Nov 2016 23:15:12 +0000 (-0800) Subject: [mini] Extract type checking code into type-checking.c X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=commitdiff_plain;h=1a645892a02ec5615845483c9501060b358ca3ba;p=mono.git [mini] Extract type checking code into type-checking.c --- diff --git a/mono/mini/method-to-ir.c b/mono/mini/method-to-ir.c index df104c2e4fc..6f1cebfdb57 100644 --- a/mono/mini/method-to-ir.c +++ b/mono/mini/method-to-ir.c @@ -1572,267 +1572,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 @@ -2773,8 +2516,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, @@ -3397,7 +3138,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) @@ -3677,8 +3418,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->current_method, context_used & MONO_GENERIC_CONTEXT_USED_METHOD, MONO_PATCH_INFO_CLASS, klass, rgctx_type); @@ -3866,7 +3607,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); @@ -3927,8 +3668,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); @@ -3960,7 +3701,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); @@ -3973,8 +3714,8 @@ 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) { @@ -3997,7 +3738,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)); @@ -4011,7 +3752,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) { @@ -4033,7 +3774,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); } /** @@ -4109,15 +3850,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)); @@ -4231,7 +3972,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]); @@ -4428,34 +4169,6 @@ handle_box (MonoCompile *cfg, MonoInst *val, MonoClass *klass, int context_used) } } -static gboolean -mini_class_has_reference_variant_generic_argument (MonoCompile *cfg, MonoClass *klass, int context_used) -{ - 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 @@ -4498,493 +4211,6 @@ method_needs_stack_walk (MonoCompile *cfg, MonoMethod *cmethod) 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); - - MONO_EMIT_NEW_ICONST (cfg, dreg, 2); - MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb); -#endif - - MONO_START_BB (cfg, true_bb); - - MONO_EMIT_NEW_ICONST (cfg, dreg, 0); - - MONO_START_BB (cfg, end_bb); - - /* FIXME: */ - MONO_INST_NEW (cfg, ins, OP_ICONST); - ins->dreg = dreg; - ins->type = STACK_I4; - - return ins; -} - -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); - -#ifndef DISABLE_REMOTING - int klass_reg = alloc_preg (cfg); - NEW_BBLOCK (cfg, end_bb); -#endif - - NEW_BBLOCK (cfg, ok_result_bb); - - MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, obj_reg, 0); - MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, ok_result_bb); - - save_cast_details (cfg, klass, obj_reg, FALSE); - - 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)); - - mini_emit_class_check (cfg, klass_reg, mono_defaults.transparent_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_COND_EXC (cfg, EQ, "InvalidCastException"); - - MONO_EMIT_NEW_ICONST (cfg, dreg, 1); - 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); - - 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); - - NEW_BBLOCK (cfg, fail_1_bb); - - mini_emit_isninst_cast (cfg, klass_reg, klass, fail_1_bb, ok_result_bb); - - MONO_START_BB (cfg, fail_1_bb); - - MONO_EMIT_NEW_ICONST (cfg, dreg, 1); - MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb); - - MONO_START_BB (cfg, no_proxy_bb); - - 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 - } - - MONO_START_BB (cfg, ok_result_bb); - - MONO_EMIT_NEW_ICONST (cfg, dreg, 0); - -#ifndef DISABLE_REMOTING - MONO_START_BB (cfg, end_bb); -#endif - - /* FIXME: */ - MONO_INST_NEW (cfg, ins, OP_ICONST); - ins->dreg = dreg; - ins->type = STACK_I4; - - return ins; -} - static G_GNUC_UNUSED MonoInst* handle_enum_has_flag (MonoCompile *cfg, MonoClass *klass, MonoInst *enum_this, MonoInst *enum_flag) { @@ -5207,7 +4433,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) { @@ -7083,6 +6309,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: * @@ -8052,7 +7284,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); @@ -9598,7 +8830,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b 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); @@ -11648,7 +10880,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)) { @@ -11899,7 +11131,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]; @@ -12155,7 +11387,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: @@ -12192,7 +11424,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); @@ -12289,7 +11521,7 @@ 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) { @@ -12321,7 +11553,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) { @@ -12733,9 +11965,9 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b 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]); + ins = mini_emit_cisinst (cfg, klass, sp [0]); else - ins = handle_ccastclass (cfg, klass, sp [0]); + ins = mini_emit_ccastclass (cfg, klass, sp [0]); *sp++ = ins; ip += 6; break; @@ -15135,73 +14367,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: diff --git a/mono/mini/mini.c b/mono/mini/mini.c index cfca91bf6e8..30609c8352c 100644 --- a/mono/mini/mini.c +++ b/mono/mini/mini.c @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -4033,6 +4034,34 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, JitFl return cfg; } +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; +} + void* mono_arch_instrument_epilog (MonoCompile *cfg, void *func, void *p, gboolean enable_arguments) { diff --git a/mono/mini/mini.h b/mono/mini/mini.h index 239cdd38ebd..844b25c51a2 100644 --- a/mono/mini/mini.h +++ b/mono/mini/mini.h @@ -2642,6 +2642,22 @@ int mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoB MonoInst *return_var, MonoInst **inline_args, guint inline_offset, gboolean is_virtual_call); +//the following methods could just be renamed/moved from method-to-ir.c +int mini_inline_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **sp, guchar *ip, + guint real_offset, gboolean inline_always); + +MonoInst* mini_emit_get_rgctx_klass (MonoCompile *cfg, int context_used, MonoClass *klass, MonoRgctxInfoType rgctx_type); +MonoInst* mini_emit_runtime_constant (MonoCompile *cfg, MonoJumpInfoType patch_type, gpointer data); +void mini_save_cast_details (MonoCompile *cfg, MonoClass *klass, int obj_reg, gboolean null_check); +void mini_reset_cast_details (MonoCompile *cfg); +void mini_emit_class_check (MonoCompile *cfg, int klass_reg, MonoClass *klass); + +MonoInst* mini_emit_ccastclass (MonoCompile *cfg, MonoClass *klass, MonoInst *src); +MonoInst* mini_emit_cisinst (MonoCompile *cfg, MonoClass *klass, MonoInst *src); + +gboolean mini_class_has_reference_variant_generic_argument (MonoCompile *cfg, MonoClass *klass, int context_used); + + MonoInst *mono_decompose_opcode (MonoCompile *cfg, MonoInst *ins); void mono_decompose_long_opts (MonoCompile *cfg); void mono_decompose_vtype_opts (MonoCompile *cfg); @@ -2653,6 +2669,8 @@ void mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local void mono_allocate_gsharedvt_vars (MonoCompile *cfg); void mono_if_conversion (MonoCompile *cfg); + + /* Delegates */ gpointer mini_get_delegate_arg (MonoMethod *method, gpointer method_ptr); void mini_init_delegate (MonoDelegate *del); diff --git a/mono/mini/type-checking.c b/mono/mini/type-checking.c new file mode 100644 index 00000000000..f9301ac1ed6 --- /dev/null +++ b/mono/mini/type-checking.c @@ -0,0 +1,847 @@ +#include +#include + +#ifndef DISABLE_JIT + +#include +#include +#include + + +//XXX maybe move to mini.h / mini.c? + +static int +mini_class_check_context_used (MonoCompile *cfg, MonoClass *klass) +{ + if (cfg->gshared) + return mono_class_check_context_used (klass); + else + return 0; +} + + +#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 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] = mini_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 (MonoCompile *cfg, MonoClass *klass, MonoInst **args) +{ + MonoMethod *mono_castclass = mono_marshal_get_castclass_with_cache (); + MonoInst *res; + + mini_save_cast_details (cfg, klass, args [0]->dreg, TRUE); + res = mono_emit_method_call (cfg, mono_castclass, args, NULL); + mini_reset_cast_details (cfg); + + return res; +} + +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 = mini_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 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_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] = mini_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 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_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 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) +{ + mini_emit_castclass_inst (cfg, obj_reg, klass_reg, klass, NULL, object_is_null); +} + + +/* + * 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 = mini_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 = mini_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); + + mini_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); + + mini_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 = mini_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 = mini_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* +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] = mini_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); +} + + +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 = mini_inline_method (cfg, wrapper, mono_method_signature (wrapper), iargs, 0, 0, TRUE); + } else { + MonoMethod *wrapper = mono_marshal_get_castclass (klass); + mini_save_cast_details (cfg, klass, source->dreg, TRUE); + costs = mini_inline_method (cfg, wrapper, mono_method_signature (wrapper), iargs, 0, 0, TRUE); + mini_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; + } + } + } +} + +//Those two functions will go away as we get rid of CEE_MONO_CISINST and CEE_MONO_CCASTCLASS. +MonoInst* +mini_emit_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); + + MONO_EMIT_NEW_ICONST (cfg, dreg, 2); + MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb); +#endif + + MONO_START_BB (cfg, true_bb); + + MONO_EMIT_NEW_ICONST (cfg, dreg, 0); + + MONO_START_BB (cfg, end_bb); + + /* FIXME: */ + MONO_INST_NEW (cfg, ins, OP_ICONST); + ins->dreg = dreg; + ins->type = STACK_I4; + + return ins; +} + +MonoInst* +mini_emit_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); + +#ifndef DISABLE_REMOTING + int klass_reg = alloc_preg (cfg); + NEW_BBLOCK (cfg, end_bb); +#endif + + NEW_BBLOCK (cfg, ok_result_bb); + + MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, obj_reg, 0); + MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, ok_result_bb); + + mini_save_cast_details (cfg, klass, obj_reg, FALSE); + + 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)); + + mini_emit_class_check (cfg, klass_reg, mono_defaults.transparent_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_COND_EXC (cfg, EQ, "InvalidCastException"); + + MONO_EMIT_NEW_ICONST (cfg, dreg, 1); + 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); + + 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); + + NEW_BBLOCK (cfg, fail_1_bb); + + mini_emit_isninst_cast (cfg, klass_reg, klass, fail_1_bb, ok_result_bb); + + MONO_START_BB (cfg, fail_1_bb); + + MONO_EMIT_NEW_ICONST (cfg, dreg, 1); + MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb); + + MONO_START_BB (cfg, no_proxy_bb); + + 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 + } + + MONO_START_BB (cfg, ok_result_bb); + + MONO_EMIT_NEW_ICONST (cfg, dreg, 0); + +#ifndef DISABLE_REMOTING + MONO_START_BB (cfg, end_bb); +#endif + + /* FIXME: */ + MONO_INST_NEW (cfg, ins, OP_ICONST); + ins->dreg = dreg; + ins->type = STACK_I4; + + return ins; +} + +//API used by method-to-ir.c +void +mini_emit_class_check (MonoCompile *cfg, int klass_reg, MonoClass *klass) +{ + mini_emit_class_check_inst (cfg, klass_reg, klass, NULL); +} + +#endif \ No newline at end of file