2 #include <mono/utils/mono-compiler.h>
8 #include <mono/metadata/abi-details.h>
11 //XXX maybe move to mini.h / mini.c?
14 mini_class_check_context_used (MonoCompile
*cfg
, MonoClass
*klass
)
17 return mono_class_check_context_used (klass
);
23 #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)
26 get_castclass_cache_idx (MonoCompile
*cfg
)
28 /* Each CASTCLASS_CACHE patch needs a unique index which identifies the call site */
29 cfg
->castclass_cache_index
++;
30 return (cfg
->method_index
<< 16) | cfg
->castclass_cache_index
;
34 emit_cached_check_args (MonoCompile
*cfg
, MonoInst
*obj
, MonoClass
*klass
, int context_used
, MonoInst
*args
[3])
41 cache_ins
= mini_emit_get_rgctx_klass (cfg
, context_used
, klass
, MONO_RGCTX_INFO_CAST_CACHE
);
43 /* klass - it's the second element of the cache entry*/
44 EMIT_NEW_LOAD_MEMBASE (cfg
, args
[1], OP_LOAD_MEMBASE
, alloc_preg (cfg
), cache_ins
->dreg
, sizeof (gpointer
));
46 args
[2] = cache_ins
; /* cache */
50 EMIT_NEW_CLASSCONST (cfg
, args
[1], klass
); /* klass */
52 idx
= get_castclass_cache_idx (cfg
); /* inline cache*/
53 args
[2] = mini_emit_runtime_constant (cfg
, MONO_PATCH_INFO_CASTCLASS_CACHE
, GINT_TO_POINTER (idx
));
58 emit_isinst_with_cache (MonoCompile
*cfg
, MonoInst
*obj
, MonoClass
*klass
, int context_used
)
61 MonoMethod
*mono_isinst
= mono_marshal_get_isinst_with_cache ();
63 emit_cached_check_args (cfg
, obj
, klass
, context_used
, args
);
64 return mono_emit_method_call (cfg
, mono_isinst
, args
, NULL
);
68 emit_castclass_with_cache_no_details (MonoCompile
*cfg
, MonoInst
*obj
, MonoClass
*klass
, int context_used
)
71 MonoMethod
*mono_castclass
= mono_marshal_get_castclass_with_cache ();
74 emit_cached_check_args (cfg
, obj
, klass
, context_used
, args
);
76 res
= mono_emit_method_call (cfg
, mono_castclass
, args
, NULL
);
82 emit_castclass_with_cache (MonoCompile
*cfg
, MonoInst
*obj
, MonoClass
*klass
, int context_used
)
85 MonoMethod
*mono_castclass
= mono_marshal_get_castclass_with_cache ();
88 emit_cached_check_args (cfg
, obj
, klass
, context_used
, args
);
90 mini_save_cast_details (cfg
, klass
, args
[0]->dreg
, TRUE
);
91 res
= mono_emit_method_call (cfg
, mono_castclass
, args
, NULL
);
92 mini_reset_cast_details (cfg
);
98 mini_emit_class_check_inst (MonoCompile
*cfg
, int klass_reg
, MonoClass
*klass
, MonoInst
*klass_inst
)
101 MONO_EMIT_NEW_BIALU (cfg
, OP_COMPARE
, -1, klass_reg
, klass_inst
->dreg
);
103 MonoInst
*ins
= mini_emit_runtime_constant (cfg
, MONO_PATCH_INFO_CLASS
, klass
);
104 MONO_EMIT_NEW_BIALU (cfg
, OP_COMPARE
, -1, klass_reg
, ins
->dreg
);
106 MONO_EMIT_NEW_COND_EXC (cfg
, NE_UN
, "InvalidCastException");
111 mini_emit_isninst_cast_inst (MonoCompile
*cfg
, int klass_reg
, MonoClass
*klass
, MonoInst
*klass_ins
, MonoBasicBlock
*false_target
, MonoBasicBlock
*true_target
)
113 int idepth_reg
= alloc_preg (cfg
);
114 int stypes_reg
= alloc_preg (cfg
);
115 int stype
= alloc_preg (cfg
);
117 mono_class_setup_supertypes (klass
);
119 if (klass
->idepth
> MONO_DEFAULT_SUPERTABLE_SIZE
) {
120 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg
, OP_LOADU2_MEMBASE
, idepth_reg
, klass_reg
, MONO_STRUCT_OFFSET (MonoClass
, idepth
));
121 MONO_EMIT_NEW_BIALU_IMM (cfg
, OP_COMPARE_IMM
, -1, idepth_reg
, klass
->idepth
);
122 MONO_EMIT_NEW_BRANCH_BLOCK (cfg
, OP_PBLT_UN
, false_target
);
124 MONO_EMIT_NEW_LOAD_MEMBASE (cfg
, stypes_reg
, klass_reg
, MONO_STRUCT_OFFSET (MonoClass
, supertypes
));
125 MONO_EMIT_NEW_LOAD_MEMBASE (cfg
, stype
, stypes_reg
, ((klass
->idepth
- 1) * SIZEOF_VOID_P
));
127 MONO_EMIT_NEW_BIALU (cfg
, OP_COMPARE
, -1, stype
, klass_ins
->dreg
);
128 } else if (cfg
->compile_aot
) {
129 int const_reg
= alloc_preg (cfg
);
130 MONO_EMIT_NEW_CLASSCONST (cfg
, const_reg
, klass
);
131 MONO_EMIT_NEW_BIALU (cfg
, OP_COMPARE
, -1, stype
, const_reg
);
133 MONO_EMIT_NEW_BIALU_IMM (cfg
, OP_COMPARE_IMM
, -1, stype
, klass
);
135 MONO_EMIT_NEW_BRANCH_BLOCK (cfg
, OP_PBEQ
, true_target
);
140 mini_emit_interface_bitmap_check (MonoCompile
*cfg
, int intf_bit_reg
, int base_reg
, int offset
, MonoClass
*klass
)
142 int ibitmap_reg
= alloc_preg (cfg
);
143 #ifdef COMPRESSED_INTERFACE_BITMAP
146 NEW_LOAD_MEMBASE (cfg
, ins
, OP_LOAD_MEMBASE
, ibitmap_reg
, base_reg
, offset
);
147 MONO_ADD_INS (cfg
->cbb
, ins
);
149 args
[1] = mini_emit_runtime_constant (cfg
, MONO_PATCH_INFO_IID
, klass
);
150 res
= mono_emit_jit_icall (cfg
, mono_class_interface_match
, args
);
151 MONO_EMIT_NEW_UNALU (cfg
, OP_MOVE
, intf_bit_reg
, res
->dreg
);
153 int ibitmap_byte_reg
= alloc_preg (cfg
);
155 MONO_EMIT_NEW_LOAD_MEMBASE (cfg
, ibitmap_reg
, base_reg
, offset
);
157 if (cfg
->compile_aot
) {
158 int iid_reg
= alloc_preg (cfg
);
159 int shifted_iid_reg
= alloc_preg (cfg
);
160 int ibitmap_byte_address_reg
= alloc_preg (cfg
);
161 int masked_iid_reg
= alloc_preg (cfg
);
162 int iid_one_bit_reg
= alloc_preg (cfg
);
163 int iid_bit_reg
= alloc_preg (cfg
);
164 MONO_EMIT_NEW_AOTCONST (cfg
, iid_reg
, klass
, MONO_PATCH_INFO_IID
);
165 MONO_EMIT_NEW_BIALU_IMM (cfg
, OP_SHR_IMM
, shifted_iid_reg
, iid_reg
, 3);
166 MONO_EMIT_NEW_BIALU (cfg
, OP_PADD
, ibitmap_byte_address_reg
, ibitmap_reg
, shifted_iid_reg
);
167 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg
, OP_LOADU1_MEMBASE
, ibitmap_byte_reg
, ibitmap_byte_address_reg
, 0);
168 MONO_EMIT_NEW_BIALU_IMM (cfg
, OP_IAND_IMM
, masked_iid_reg
, iid_reg
, 7);
169 MONO_EMIT_NEW_ICONST (cfg
, iid_one_bit_reg
, 1);
170 MONO_EMIT_NEW_BIALU (cfg
, OP_ISHL
, iid_bit_reg
, iid_one_bit_reg
, masked_iid_reg
);
171 MONO_EMIT_NEW_BIALU (cfg
, OP_IAND
, intf_bit_reg
, ibitmap_byte_reg
, iid_bit_reg
);
173 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg
, OP_LOADI1_MEMBASE
, ibitmap_byte_reg
, ibitmap_reg
, klass
->interface_id
>> 3);
174 MONO_EMIT_NEW_BIALU_IMM (cfg
, OP_AND_IMM
, intf_bit_reg
, ibitmap_byte_reg
, 1 << (klass
->interface_id
& 7));
180 * Emit code which loads into "intf_bit_reg" a nonzero value if the MonoClass
181 * stored in "klass_reg" implements the interface "klass".
184 mini_emit_load_intf_bit_reg_class (MonoCompile
*cfg
, int intf_bit_reg
, int klass_reg
, MonoClass
*klass
)
186 mini_emit_interface_bitmap_check (cfg
, intf_bit_reg
, klass_reg
, MONO_STRUCT_OFFSET (MonoClass
, interface_bitmap
), klass
);
190 * Emit code which loads into "intf_bit_reg" a nonzero value if the MonoVTable
191 * stored in "vtable_reg" implements the interface "klass".
194 mini_emit_load_intf_bit_reg_vtable (MonoCompile
*cfg
, int intf_bit_reg
, int vtable_reg
, MonoClass
*klass
)
196 mini_emit_interface_bitmap_check (cfg
, intf_bit_reg
, vtable_reg
, MONO_STRUCT_OFFSET (MonoVTable
, interface_bitmap
), klass
);
200 * Emit code which checks whenever the interface id of @klass is smaller than
201 * than the value given by max_iid_reg.
204 mini_emit_max_iid_check (MonoCompile
*cfg
, int max_iid_reg
, MonoClass
*klass
,
205 MonoBasicBlock
*false_target
)
207 if (cfg
->compile_aot
) {
208 int iid_reg
= alloc_preg (cfg
);
209 MONO_EMIT_NEW_AOTCONST (cfg
, iid_reg
, klass
, MONO_PATCH_INFO_IID
);
210 MONO_EMIT_NEW_BIALU (cfg
, OP_COMPARE
, -1, max_iid_reg
, iid_reg
);
213 MONO_EMIT_NEW_BIALU_IMM (cfg
, OP_COMPARE_IMM
, -1, max_iid_reg
, klass
->interface_id
);
215 MONO_EMIT_NEW_BRANCH_BLOCK (cfg
, OP_PBLT_UN
, false_target
);
217 MONO_EMIT_NEW_COND_EXC (cfg
, LT_UN
, "InvalidCastException");
220 /* Same as above, but obtains max_iid from a vtable */
222 mini_emit_max_iid_check_vtable (MonoCompile
*cfg
, int vtable_reg
, MonoClass
*klass
,
223 MonoBasicBlock
*false_target
)
225 int max_iid_reg
= alloc_preg (cfg
);
227 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg
, OP_LOADU4_MEMBASE
, max_iid_reg
, vtable_reg
, MONO_STRUCT_OFFSET (MonoVTable
, max_interface_id
));
228 mini_emit_max_iid_check (cfg
, max_iid_reg
, klass
, false_target
);
231 /* Same as above, but obtains max_iid from a klass */
233 mini_emit_max_iid_check_class (MonoCompile
*cfg
, int klass_reg
, MonoClass
*klass
,
234 MonoBasicBlock
*false_target
)
236 int max_iid_reg
= alloc_preg (cfg
);
238 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg
, OP_LOADU4_MEMBASE
, max_iid_reg
, klass_reg
, MONO_STRUCT_OFFSET (MonoClass
, max_interface_id
));
239 mini_emit_max_iid_check (cfg
, max_iid_reg
, klass
, false_target
);
243 mini_emit_class_check_branch (MonoCompile
*cfg
, int klass_reg
, MonoClass
*klass
, int branch_op
, MonoBasicBlock
*target
)
245 if (cfg
->compile_aot
) {
246 int const_reg
= alloc_preg (cfg
);
247 MONO_EMIT_NEW_CLASSCONST (cfg
, const_reg
, klass
);
248 MONO_EMIT_NEW_BIALU (cfg
, OP_COMPARE
, -1, klass_reg
, const_reg
);
250 MONO_EMIT_NEW_BIALU_IMM (cfg
, OP_COMPARE_IMM
, -1, klass_reg
, klass
);
252 MONO_EMIT_NEW_BRANCH_BLOCK (cfg
, branch_op
, target
);
257 mini_emit_isninst_cast (MonoCompile
*cfg
, int klass_reg
, MonoClass
*klass
, MonoBasicBlock
*false_target
, MonoBasicBlock
*true_target
)
259 mini_emit_isninst_cast_inst (cfg
, klass_reg
, klass
, NULL
, false_target
, true_target
);
263 mini_emit_iface_cast (MonoCompile
*cfg
, int vtable_reg
, MonoClass
*klass
, MonoBasicBlock
*false_target
, MonoBasicBlock
*true_target
)
265 int intf_reg
= alloc_preg (cfg
);
267 mini_emit_max_iid_check_vtable (cfg
, vtable_reg
, klass
, false_target
);
268 mini_emit_load_intf_bit_reg_vtable (cfg
, intf_reg
, vtable_reg
, klass
);
269 MONO_EMIT_NEW_BIALU_IMM (cfg
, OP_COMPARE_IMM
, -1, intf_reg
, 0);
271 MONO_EMIT_NEW_BRANCH_BLOCK (cfg
, OP_PBNE_UN
, true_target
);
273 MONO_EMIT_NEW_COND_EXC (cfg
, EQ
, "InvalidCastException");
277 * Variant of the above that takes a register to the class, not the vtable.
280 mini_emit_iface_class_cast (MonoCompile
*cfg
, int klass_reg
, MonoClass
*klass
, MonoBasicBlock
*false_target
, MonoBasicBlock
*true_target
)
282 int intf_bit_reg
= alloc_preg (cfg
);
284 mini_emit_max_iid_check_class (cfg
, klass_reg
, klass
, false_target
);
285 mini_emit_load_intf_bit_reg_class (cfg
, intf_bit_reg
, klass_reg
, klass
);
286 MONO_EMIT_NEW_BIALU_IMM (cfg
, OP_COMPARE_IMM
, -1, intf_bit_reg
, 0);
288 MONO_EMIT_NEW_BRANCH_BLOCK (cfg
, OP_PBNE_UN
, true_target
);
290 MONO_EMIT_NEW_COND_EXC (cfg
, EQ
, "InvalidCastException");
295 mini_emit_castclass (MonoCompile
*cfg
, int obj_reg
, int klass_reg
, MonoClass
*klass
, MonoBasicBlock
*object_is_null
);
298 mini_emit_castclass_inst (MonoCompile
*cfg
, int obj_reg
, int klass_reg
, MonoClass
*klass
, MonoInst
*klass_inst
, MonoBasicBlock
*object_is_null
)
301 int rank_reg
= alloc_preg (cfg
);
302 int eclass_reg
= alloc_preg (cfg
);
304 g_assert (!klass_inst
);
305 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg
, OP_LOADU1_MEMBASE
, rank_reg
, klass_reg
, MONO_STRUCT_OFFSET (MonoClass
, rank
));
306 MONO_EMIT_NEW_BIALU_IMM (cfg
, OP_COMPARE_IMM
, -1, rank_reg
, klass
->rank
);
307 MONO_EMIT_NEW_COND_EXC (cfg
, NE_UN
, "InvalidCastException");
308 // MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
309 MONO_EMIT_NEW_LOAD_MEMBASE (cfg
, eclass_reg
, klass_reg
, MONO_STRUCT_OFFSET (MonoClass
, cast_class
));
310 if (klass
->cast_class
== mono_defaults
.object_class
) {
311 int parent_reg
= alloc_preg (cfg
);
312 MONO_EMIT_NEW_LOAD_MEMBASE (cfg
, parent_reg
, eclass_reg
, MONO_STRUCT_OFFSET (MonoClass
, parent
));
313 mini_emit_class_check_branch (cfg
, parent_reg
, mono_defaults
.enum_class
->parent
, OP_PBNE_UN
, object_is_null
);
314 mini_emit_class_check (cfg
, eclass_reg
, mono_defaults
.enum_class
);
315 } else if (klass
->cast_class
== mono_defaults
.enum_class
->parent
) {
316 mini_emit_class_check_branch (cfg
, eclass_reg
, mono_defaults
.enum_class
->parent
, OP_PBEQ
, object_is_null
);
317 mini_emit_class_check (cfg
, eclass_reg
, mono_defaults
.enum_class
);
318 } else if (klass
->cast_class
== mono_defaults
.enum_class
) {
319 mini_emit_class_check (cfg
, eclass_reg
, mono_defaults
.enum_class
);
320 } else if (mono_class_is_interface (klass
->cast_class
)) {
321 mini_emit_iface_class_cast (cfg
, eclass_reg
, klass
->cast_class
, NULL
, NULL
);
323 // Pass -1 as obj_reg to skip the check below for arrays of arrays
324 mini_emit_castclass (cfg
, -1, eclass_reg
, klass
->cast_class
, object_is_null
);
327 if ((klass
->rank
== 1) && (klass
->byval_arg
.type
== MONO_TYPE_SZARRAY
) && (obj_reg
!= -1)) {
328 /* Check that the object is a vector too */
329 int bounds_reg
= alloc_preg (cfg
);
330 MONO_EMIT_NEW_LOAD_MEMBASE (cfg
, bounds_reg
, obj_reg
, MONO_STRUCT_OFFSET (MonoArray
, bounds
));
331 MONO_EMIT_NEW_BIALU_IMM (cfg
, OP_COMPARE_IMM
, -1, bounds_reg
, 0);
332 MONO_EMIT_NEW_COND_EXC (cfg
, NE_UN
, "InvalidCastException");
335 int idepth_reg
= alloc_preg (cfg
);
336 int stypes_reg
= alloc_preg (cfg
);
337 int stype
= alloc_preg (cfg
);
339 mono_class_setup_supertypes (klass
);
341 if (klass
->idepth
> MONO_DEFAULT_SUPERTABLE_SIZE
) {
342 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg
, OP_LOADU2_MEMBASE
, idepth_reg
, klass_reg
, MONO_STRUCT_OFFSET (MonoClass
, idepth
));
343 MONO_EMIT_NEW_BIALU_IMM (cfg
, OP_COMPARE_IMM
, -1, idepth_reg
, klass
->idepth
);
344 MONO_EMIT_NEW_COND_EXC (cfg
, LT_UN
, "InvalidCastException");
346 MONO_EMIT_NEW_LOAD_MEMBASE (cfg
, stypes_reg
, klass_reg
, MONO_STRUCT_OFFSET (MonoClass
, supertypes
));
347 MONO_EMIT_NEW_LOAD_MEMBASE (cfg
, stype
, stypes_reg
, ((klass
->idepth
- 1) * SIZEOF_VOID_P
));
348 mini_emit_class_check_inst (cfg
, stype
, klass
, klass_inst
);
353 mini_emit_castclass (MonoCompile
*cfg
, int obj_reg
, int klass_reg
, MonoClass
*klass
, MonoBasicBlock
*object_is_null
)
355 mini_emit_castclass_inst (cfg
, obj_reg
, klass_reg
, klass
, NULL
, object_is_null
);
359 emit_special_array_iface_check (MonoCompile
*cfg
, MonoInst
*src
, MonoClass
* klass
, int vtable_reg
, MonoBasicBlock
*true_bb
, int context_used
)
361 MonoBasicBlock
*not_an_array
;
364 if (!klass
->is_array_special_interface
)
367 rank_reg
= alloc_ireg (cfg
);
369 NEW_BBLOCK (cfg
, not_an_array
);
370 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg
, OP_LOADU1_MEMBASE
, rank_reg
, vtable_reg
, MONO_STRUCT_OFFSET (MonoVTable
, rank
));
371 MONO_EMIT_NEW_BIALU_IMM (cfg
, OP_COMPARE_IMM
, -1, rank_reg
, 1);
372 MONO_EMIT_NEW_BRANCH_BLOCK (cfg
, OP_IBNE_UN
, not_an_array
);
374 emit_castclass_with_cache_no_details (cfg
, src
, klass
, context_used
);
375 MONO_EMIT_NEW_BRANCH_BLOCK (cfg
, OP_BR
, true_bb
);
377 MONO_START_BB (cfg
, not_an_array
);
382 * Returns NULL and set the cfg exception on error.
385 handle_castclass (MonoCompile
*cfg
, MonoClass
*klass
, MonoInst
*src
, int context_used
)
387 MonoBasicBlock
*is_null_bb
;
388 int obj_reg
= src
->dreg
;
389 MonoInst
*klass_inst
= NULL
;
391 if (MONO_INS_IS_PCONST_NULL (src
))
396 if (is_complex_isinst (klass
))
397 return emit_castclass_with_cache (cfg
, src
, klass
, context_used
);
399 klass_inst
= mini_emit_get_rgctx_klass (cfg
, context_used
, klass
, MONO_RGCTX_INFO_KLASS
);
402 NEW_BBLOCK (cfg
, is_null_bb
);
404 MONO_EMIT_NEW_BIALU_IMM (cfg
, OP_COMPARE_IMM
, -1, obj_reg
, 0);
405 MONO_EMIT_NEW_BRANCH_BLOCK (cfg
, OP_PBEQ
, is_null_bb
);
407 mini_save_cast_details (cfg
, klass
, obj_reg
, FALSE
);
409 if (mono_class_is_interface (klass
)) {
410 int tmp_reg
= alloc_preg (cfg
);
411 #ifndef DISABLE_REMOTING
412 MonoBasicBlock
*interface_fail_bb
;
413 int klass_reg
= alloc_preg (cfg
);
415 NEW_BBLOCK (cfg
, interface_fail_bb
);
417 MONO_EMIT_NEW_LOAD_MEMBASE (cfg
, tmp_reg
, obj_reg
, MONO_STRUCT_OFFSET (MonoObject
, vtable
));
418 mini_emit_iface_cast (cfg
, tmp_reg
, klass
, interface_fail_bb
, is_null_bb
);
420 // iface bitmap check failed
421 MONO_START_BB (cfg
, interface_fail_bb
);
423 //Check if it's a rank zero array and emit fallback casting
424 emit_special_array_iface_check (cfg
, src
, klass
, tmp_reg
, is_null_bb
, context_used
);
426 MONO_EMIT_NEW_LOAD_MEMBASE (cfg
, klass_reg
, tmp_reg
, MONO_STRUCT_OFFSET (MonoVTable
, klass
));
428 mini_emit_class_check (cfg
, klass_reg
, mono_defaults
.transparent_proxy_class
);
430 tmp_reg
= alloc_preg (cfg
);
431 MONO_EMIT_NEW_LOAD_MEMBASE (cfg
, tmp_reg
, obj_reg
, MONO_STRUCT_OFFSET (MonoTransparentProxy
, custom_type_info
));
432 MONO_EMIT_NEW_BIALU_IMM (cfg
, OP_COMPARE_IMM
, -1, tmp_reg
, 0);
433 MONO_EMIT_NEW_COND_EXC (cfg
, EQ
, "InvalidCastException");
435 MonoInst
*args
[1] = { src
};
436 MonoInst
*proxy_test_inst
= mono_emit_method_call (cfg
, mono_marshal_get_proxy_cancast (klass
), args
, NULL
);
437 MONO_EMIT_NEW_BIALU_IMM (cfg
, OP_COMPARE_IMM
, -1, proxy_test_inst
->dreg
, 0);
438 MONO_EMIT_NEW_COND_EXC (cfg
, EQ
, "InvalidCastException");
440 MONO_EMIT_NEW_BRANCH_BLOCK (cfg
, OP_BR
, is_null_bb
);
442 MonoBasicBlock
*interface_fail_bb
= NULL
;
444 MONO_EMIT_NEW_LOAD_MEMBASE (cfg
, tmp_reg
, obj_reg
, MONO_STRUCT_OFFSET (MonoObject
, vtable
));
446 if (klass
->is_array_special_interface
) {
447 NEW_BBLOCK (cfg
, interface_fail_bb
);
448 mini_emit_iface_cast (cfg
, tmp_reg
, klass
, interface_fail_bb
, is_null_bb
);
449 // iface bitmap check failed
450 MONO_START_BB (cfg
, interface_fail_bb
);
452 //Check if it's a rank zero array and emit fallback casting
453 emit_special_array_iface_check (cfg
, src
, klass
, tmp_reg
, is_null_bb
, context_used
);
455 mini_emit_iface_cast (cfg
, tmp_reg
, klass
, NULL
, NULL
);
456 MONO_EMIT_NEW_BRANCH_BLOCK (cfg
, OP_BR
, is_null_bb
);
459 } else if (mono_class_is_marshalbyref (klass
)) {
460 #ifndef DISABLE_REMOTING
461 MonoBasicBlock
*no_proxy_bb
, *fail_1_bb
;
462 int tmp_reg
= alloc_preg (cfg
);
463 int klass_reg
= alloc_preg (cfg
);
465 NEW_BBLOCK (cfg
, no_proxy_bb
);
467 MONO_EMIT_NEW_LOAD_MEMBASE (cfg
, tmp_reg
, obj_reg
, MONO_STRUCT_OFFSET (MonoObject
, vtable
));
468 MONO_EMIT_NEW_LOAD_MEMBASE (cfg
, klass_reg
, tmp_reg
, MONO_STRUCT_OFFSET (MonoVTable
, klass
));
469 mini_emit_class_check_branch (cfg
, klass_reg
, mono_defaults
.transparent_proxy_class
, OP_PBNE_UN
, no_proxy_bb
);
471 tmp_reg
= alloc_preg (cfg
);
472 MONO_EMIT_NEW_LOAD_MEMBASE (cfg
, tmp_reg
, obj_reg
, MONO_STRUCT_OFFSET (MonoTransparentProxy
, remote_class
));
473 MONO_EMIT_NEW_LOAD_MEMBASE (cfg
, klass_reg
, tmp_reg
, MONO_STRUCT_OFFSET (MonoRemoteClass
, proxy_class
));
475 tmp_reg
= alloc_preg (cfg
);
476 MONO_EMIT_NEW_LOAD_MEMBASE (cfg
, tmp_reg
, obj_reg
, MONO_STRUCT_OFFSET (MonoTransparentProxy
, custom_type_info
));
477 MONO_EMIT_NEW_BIALU_IMM (cfg
, OP_COMPARE_IMM
, -1, tmp_reg
, 0);
478 MONO_EMIT_NEW_BRANCH_BLOCK (cfg
, OP_PBEQ
, no_proxy_bb
);
480 NEW_BBLOCK (cfg
, fail_1_bb
);
482 mini_emit_isninst_cast (cfg
, klass_reg
, klass
, fail_1_bb
, is_null_bb
);
484 MONO_START_BB (cfg
, fail_1_bb
);
486 MonoInst
*args
[1] = { src
};
487 MonoInst
*proxy_test_inst
= mono_emit_method_call (cfg
, mono_marshal_get_proxy_cancast (klass
), args
, NULL
);
488 MONO_EMIT_NEW_BIALU_IMM (cfg
, OP_COMPARE_IMM
, -1, proxy_test_inst
->dreg
, 0);
489 MONO_EMIT_NEW_COND_EXC (cfg
, EQ
, "InvalidCastException");
491 MONO_EMIT_NEW_BRANCH_BLOCK (cfg
, OP_BR
, is_null_bb
);
493 MONO_START_BB (cfg
, no_proxy_bb
);
495 mini_emit_castclass_inst (cfg
, obj_reg
, klass_reg
, klass
, klass_inst
, is_null_bb
);
497 g_error ("Transparent proxy support is disabled while trying to JIT code that uses it");
500 int vtable_reg
= alloc_preg (cfg
);
501 int klass_reg
= alloc_preg (cfg
);
503 MONO_EMIT_NEW_LOAD_MEMBASE (cfg
, vtable_reg
, obj_reg
, MONO_STRUCT_OFFSET (MonoObject
, vtable
));
505 if (!klass
->rank
&& !cfg
->compile_aot
&& !(cfg
->opt
& MONO_OPT_SHARED
) && mono_class_is_sealed (klass
)) {
506 /* the remoting code is broken, access the class for now */
507 if (0) { /*FIXME what exactly is broken? This change refers to r39380 from 2005 and mention some remoting fixes were due.*/
508 MonoVTable
*vt
= mono_class_vtable (cfg
->domain
, klass
);
510 mono_cfg_set_exception (cfg
, MONO_EXCEPTION_TYPE_LOAD
);
511 cfg
->exception_ptr
= klass
;
514 MONO_EMIT_NEW_BIALU_IMM (cfg
, OP_COMPARE_IMM
, -1, vtable_reg
, vt
);
516 MONO_EMIT_NEW_LOAD_MEMBASE (cfg
, klass_reg
, vtable_reg
, MONO_STRUCT_OFFSET (MonoVTable
, klass
));
517 MONO_EMIT_NEW_BIALU_IMM (cfg
, OP_COMPARE_IMM
, -1, klass_reg
, klass
);
519 MONO_EMIT_NEW_COND_EXC (cfg
, NE_UN
, "InvalidCastException");
521 MONO_EMIT_NEW_LOAD_MEMBASE (cfg
, klass_reg
, vtable_reg
, MONO_STRUCT_OFFSET (MonoVTable
, klass
));
522 mini_emit_castclass_inst (cfg
, obj_reg
, klass_reg
, klass
, klass_inst
, is_null_bb
);
526 MONO_START_BB (cfg
, is_null_bb
);
528 mini_reset_cast_details (cfg
);
534 * Returns NULL and set the cfg exception on error.
537 handle_isinst (MonoCompile
*cfg
, MonoClass
*klass
, MonoInst
*src
, int context_used
)
540 MonoBasicBlock
*is_null_bb
, *false_bb
, *end_bb
;
541 int obj_reg
= src
->dreg
;
542 int vtable_reg
= alloc_preg (cfg
);
543 int res_reg
= alloc_ireg_ref (cfg
);
544 MonoInst
*klass_inst
= NULL
;
547 if(is_complex_isinst (klass
))
548 return emit_isinst_with_cache (cfg
, src
, klass
, context_used
);
550 klass_inst
= mini_emit_get_rgctx_klass (cfg
, context_used
, klass
, MONO_RGCTX_INFO_KLASS
);
553 NEW_BBLOCK (cfg
, is_null_bb
);
554 NEW_BBLOCK (cfg
, false_bb
);
555 NEW_BBLOCK (cfg
, end_bb
);
557 /* Do the assignment at the beginning, so the other assignment can be if converted */
558 EMIT_NEW_UNALU (cfg
, ins
, OP_MOVE
, res_reg
, obj_reg
);
559 ins
->type
= STACK_OBJ
;
562 MONO_EMIT_NEW_BIALU_IMM (cfg
, OP_COMPARE_IMM
, -1, obj_reg
, 0);
563 MONO_EMIT_NEW_BRANCH_BLOCK (cfg
, OP_IBEQ
, is_null_bb
);
565 MONO_EMIT_NEW_LOAD_MEMBASE (cfg
, vtable_reg
, obj_reg
, MONO_STRUCT_OFFSET (MonoObject
, vtable
));
567 if (mono_class_is_interface (klass
)) {
568 MonoBasicBlock
*interface_fail_bb
;
570 NEW_BBLOCK (cfg
, interface_fail_bb
);
572 mini_emit_iface_cast (cfg
, vtable_reg
, klass
, interface_fail_bb
, is_null_bb
);
573 MONO_START_BB (cfg
, interface_fail_bb
);
575 if (klass
->is_array_special_interface
) {
576 MonoBasicBlock
*not_an_array
;
578 int rank_reg
= alloc_ireg (cfg
);
580 NEW_BBLOCK (cfg
, not_an_array
);
581 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg
, OP_LOADU1_MEMBASE
, rank_reg
, vtable_reg
, MONO_STRUCT_OFFSET (MonoVTable
, rank
));
582 MONO_EMIT_NEW_BIALU_IMM (cfg
, OP_COMPARE_IMM
, -1, rank_reg
, 1);
583 MONO_EMIT_NEW_BRANCH_BLOCK (cfg
, OP_IBNE_UN
, not_an_array
);
585 MonoInst
*res_inst
= emit_isinst_with_cache (cfg
, src
, klass
, context_used
);
586 EMIT_NEW_UNALU (cfg
, move
, OP_MOVE
, res_reg
, res_inst
->dreg
);
587 MONO_EMIT_NEW_BRANCH_BLOCK (cfg
, OP_BR
, end_bb
);
589 MONO_START_BB (cfg
, not_an_array
);
592 #ifndef DISABLE_REMOTING
593 int tmp_reg
, klass_reg
;
594 MonoBasicBlock
*call_proxy_isinst
;
596 NEW_BBLOCK (cfg
, call_proxy_isinst
);
598 klass_reg
= alloc_preg (cfg
);
599 MONO_EMIT_NEW_LOAD_MEMBASE (cfg
, klass_reg
, vtable_reg
, MONO_STRUCT_OFFSET (MonoVTable
, klass
));
601 mini_emit_class_check_branch (cfg
, klass_reg
, mono_defaults
.transparent_proxy_class
, OP_PBNE_UN
, false_bb
);
603 tmp_reg
= alloc_preg (cfg
);
604 MONO_EMIT_NEW_LOAD_MEMBASE (cfg
, tmp_reg
, obj_reg
, MONO_STRUCT_OFFSET (MonoTransparentProxy
, custom_type_info
));
605 MONO_EMIT_NEW_BIALU_IMM (cfg
, OP_COMPARE_IMM
, -1, tmp_reg
, 0);
606 MONO_EMIT_NEW_BRANCH_BLOCK (cfg
, OP_PBEQ
, false_bb
);
608 MONO_START_BB (cfg
, call_proxy_isinst
);
610 MonoInst
*args
[1] = { src
};
611 MonoInst
*proxy_test_inst
= mono_emit_method_call (cfg
, mono_marshal_get_proxy_cancast (klass
), args
, NULL
);
612 MONO_EMIT_NEW_BIALU_IMM (cfg
, OP_COMPARE_IMM
, -1, proxy_test_inst
->dreg
, 0);
613 MONO_EMIT_NEW_BRANCH_BLOCK (cfg
, OP_PBNE_UN
, is_null_bb
);
615 MONO_EMIT_NEW_BRANCH_BLOCK (cfg
, OP_BR
, false_bb
);
618 } else if (mono_class_is_marshalbyref (klass
)) {
620 #ifndef DISABLE_REMOTING
621 int tmp_reg
, klass_reg
;
622 MonoBasicBlock
*no_proxy_bb
, *call_proxy_isinst
;
624 NEW_BBLOCK (cfg
, no_proxy_bb
);
625 NEW_BBLOCK (cfg
, call_proxy_isinst
);
627 klass_reg
= alloc_preg (cfg
);
628 MONO_EMIT_NEW_LOAD_MEMBASE (cfg
, klass_reg
, vtable_reg
, MONO_STRUCT_OFFSET (MonoVTable
, klass
));
630 mini_emit_class_check_branch (cfg
, klass_reg
, mono_defaults
.transparent_proxy_class
, OP_PBNE_UN
, no_proxy_bb
);
632 tmp_reg
= alloc_preg (cfg
);
633 MONO_EMIT_NEW_LOAD_MEMBASE (cfg
, tmp_reg
, obj_reg
, MONO_STRUCT_OFFSET (MonoTransparentProxy
, remote_class
));
634 MONO_EMIT_NEW_LOAD_MEMBASE (cfg
, klass_reg
, tmp_reg
, MONO_STRUCT_OFFSET (MonoRemoteClass
, proxy_class
));
636 tmp_reg
= alloc_preg (cfg
);
637 MONO_EMIT_NEW_LOAD_MEMBASE (cfg
, tmp_reg
, obj_reg
, MONO_STRUCT_OFFSET (MonoTransparentProxy
, custom_type_info
));
638 MONO_EMIT_NEW_BIALU_IMM (cfg
, OP_COMPARE_IMM
, -1, tmp_reg
, 0);
639 MONO_EMIT_NEW_BRANCH_BLOCK (cfg
, OP_PBEQ
, false_bb
);
641 mini_emit_isninst_cast (cfg
, klass_reg
, klass
, call_proxy_isinst
, is_null_bb
);
643 MONO_START_BB (cfg
, call_proxy_isinst
);
645 MonoInst
*args
[1] = { src
};
646 MonoInst
*proxy_test_inst
= mono_emit_method_call (cfg
, mono_marshal_get_proxy_cancast (klass
), args
, NULL
);
647 MONO_EMIT_NEW_BIALU_IMM (cfg
, OP_COMPARE_IMM
, -1, proxy_test_inst
->dreg
, 0);
648 MONO_EMIT_NEW_BRANCH_BLOCK (cfg
, OP_PBNE_UN
, is_null_bb
);
649 MONO_EMIT_NEW_BRANCH_BLOCK (cfg
, OP_BR
, false_bb
);
651 MONO_START_BB (cfg
, no_proxy_bb
);
653 mini_emit_isninst_cast (cfg
, klass_reg
, klass
, false_bb
, is_null_bb
);
655 g_error ("transparent proxy support is disabled while trying to JIT code that uses it");
658 int klass_reg
= alloc_preg (cfg
);
661 int rank_reg
= alloc_preg (cfg
);
662 int eclass_reg
= alloc_preg (cfg
);
664 g_assert (!context_used
);
665 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg
, OP_LOADU1_MEMBASE
, rank_reg
, vtable_reg
, MONO_STRUCT_OFFSET (MonoVTable
, rank
));
666 MONO_EMIT_NEW_BIALU_IMM (cfg
, OP_COMPARE_IMM
, -1, rank_reg
, klass
->rank
);
667 MONO_EMIT_NEW_BRANCH_BLOCK (cfg
, OP_PBNE_UN
, false_bb
);
668 MONO_EMIT_NEW_LOAD_MEMBASE (cfg
, klass_reg
, vtable_reg
, MONO_STRUCT_OFFSET (MonoVTable
, klass
));
669 MONO_EMIT_NEW_LOAD_MEMBASE (cfg
, eclass_reg
, klass_reg
, MONO_STRUCT_OFFSET (MonoClass
, cast_class
));
670 if (klass
->cast_class
== mono_defaults
.object_class
) {
671 int parent_reg
= alloc_preg (cfg
);
672 MONO_EMIT_NEW_LOAD_MEMBASE (cfg
, parent_reg
, eclass_reg
, MONO_STRUCT_OFFSET (MonoClass
, parent
));
673 mini_emit_class_check_branch (cfg
, parent_reg
, mono_defaults
.enum_class
->parent
, OP_PBNE_UN
, is_null_bb
);
674 mini_emit_class_check_branch (cfg
, eclass_reg
, mono_defaults
.enum_class
, OP_PBEQ
, is_null_bb
);
675 MONO_EMIT_NEW_BRANCH_BLOCK (cfg
, OP_BR
, false_bb
);
676 } else if (klass
->cast_class
== mono_defaults
.enum_class
->parent
) {
677 mini_emit_class_check_branch (cfg
, eclass_reg
, mono_defaults
.enum_class
->parent
, OP_PBEQ
, is_null_bb
);
678 mini_emit_class_check_branch (cfg
, eclass_reg
, mono_defaults
.enum_class
, OP_PBEQ
, is_null_bb
);
679 MONO_EMIT_NEW_BRANCH_BLOCK (cfg
, OP_BR
, false_bb
);
680 } else if (klass
->cast_class
== mono_defaults
.enum_class
) {
681 mini_emit_class_check_branch (cfg
, eclass_reg
, mono_defaults
.enum_class
, OP_PBEQ
, is_null_bb
);
682 MONO_EMIT_NEW_BRANCH_BLOCK (cfg
, OP_BR
, false_bb
);
683 } else if (mono_class_is_interface (klass
->cast_class
)) {
684 mini_emit_iface_class_cast (cfg
, eclass_reg
, klass
->cast_class
, false_bb
, is_null_bb
);
686 if ((klass
->rank
== 1) && (klass
->byval_arg
.type
== MONO_TYPE_SZARRAY
)) {
687 /* Check that the object is a vector too */
688 int bounds_reg
= alloc_preg (cfg
);
689 MONO_EMIT_NEW_LOAD_MEMBASE (cfg
, bounds_reg
, obj_reg
, MONO_STRUCT_OFFSET (MonoArray
, bounds
));
690 MONO_EMIT_NEW_BIALU_IMM (cfg
, OP_COMPARE_IMM
, -1, bounds_reg
, 0);
691 MONO_EMIT_NEW_BRANCH_BLOCK (cfg
, OP_PBNE_UN
, false_bb
);
694 /* the is_null_bb target simply copies the input register to the output */
695 mini_emit_isninst_cast (cfg
, eclass_reg
, klass
->cast_class
, false_bb
, is_null_bb
);
697 } else if (mono_class_is_nullable (klass
)) {
698 g_assert (!context_used
);
699 MONO_EMIT_NEW_LOAD_MEMBASE (cfg
, klass_reg
, vtable_reg
, MONO_STRUCT_OFFSET (MonoVTable
, klass
));
700 /* the is_null_bb target simply copies the input register to the output */
701 mini_emit_isninst_cast (cfg
, klass_reg
, klass
->cast_class
, false_bb
, is_null_bb
);
703 if (!cfg
->compile_aot
&& !(cfg
->opt
& MONO_OPT_SHARED
) && mono_class_is_sealed (klass
)) {
704 g_assert (!context_used
);
705 /* the remoting code is broken, access the class for now */
706 if (0) {/*FIXME what exactly is broken? This change refers to r39380 from 2005 and mention some remoting fixes were due.*/
707 MonoVTable
*vt
= mono_class_vtable (cfg
->domain
, klass
);
709 mono_cfg_set_exception (cfg
, MONO_EXCEPTION_TYPE_LOAD
);
710 cfg
->exception_ptr
= klass
;
713 MONO_EMIT_NEW_BIALU_IMM (cfg
, OP_COMPARE_IMM
, -1, vtable_reg
, vt
);
715 MONO_EMIT_NEW_LOAD_MEMBASE (cfg
, klass_reg
, vtable_reg
, MONO_STRUCT_OFFSET (MonoVTable
, klass
));
716 MONO_EMIT_NEW_BIALU_IMM (cfg
, OP_COMPARE_IMM
, -1, klass_reg
, klass
);
718 MONO_EMIT_NEW_BRANCH_BLOCK (cfg
, OP_PBNE_UN
, false_bb
);
719 MONO_EMIT_NEW_BRANCH_BLOCK (cfg
, OP_BR
, is_null_bb
);
721 MONO_EMIT_NEW_LOAD_MEMBASE (cfg
, klass_reg
, vtable_reg
, MONO_STRUCT_OFFSET (MonoVTable
, klass
));
722 /* the is_null_bb target simply copies the input register to the output */
723 mini_emit_isninst_cast_inst (cfg
, klass_reg
, klass
, klass_inst
, false_bb
, is_null_bb
);
728 MONO_START_BB (cfg
, false_bb
);
730 MONO_EMIT_NEW_PCONST (cfg
, res_reg
, NULL
);
731 MONO_EMIT_NEW_BRANCH_BLOCK (cfg
, OP_BR
, end_bb
);
733 MONO_START_BB (cfg
, is_null_bb
);
735 MONO_START_BB (cfg
, end_bb
);
741 mono_decompose_typecheck (MonoCompile
*cfg
, MonoBasicBlock
*bb
, MonoInst
*ins
)
743 MonoInst
*ret
, *move
, *source
;
744 MonoClass
*klass
= ins
->klass
;
745 int context_used
= mini_class_check_context_used (cfg
, klass
);
746 int is_isinst
= ins
->opcode
== OP_ISINST
;
747 g_assert (is_isinst
|| ins
->opcode
== OP_CASTCLASS
);
748 source
= get_vreg_to_inst (cfg
, ins
->sreg1
);
749 if (!source
|| source
== (MonoInst
*) -1)
750 source
= mono_compile_create_var_for_vreg (cfg
, &mono_defaults
.object_class
->byval_arg
, OP_LOCAL
, ins
->sreg1
);
751 g_assert (source
&& source
!= (MonoInst
*) -1);
753 MonoBasicBlock
*first_bb
;
754 NEW_BBLOCK (cfg
, first_bb
);
757 if (mini_class_has_reference_variant_generic_argument (cfg
, klass
, context_used
)) {
759 ret
= emit_isinst_with_cache (cfg
, source
, klass
, context_used
);
761 ret
= emit_castclass_with_cache (cfg
, source
, klass
, context_used
);
765 ret
= handle_isinst (cfg
, klass
, source
, context_used
);
767 ret
= handle_castclass (cfg
, klass
, source
, context_used
);
769 EMIT_NEW_UNALU (cfg
, move
, OP_MOVE
, ins
->dreg
, ret
->dreg
);
771 g_assert (cfg
->cbb
->code
|| first_bb
->code
);
772 MonoInst
*prev
= ins
->prev
;
773 mono_replace_ins (cfg
, bb
, ins
, &prev
, first_bb
, cfg
->cbb
);
777 mono_decompose_typechecks (MonoCompile
*cfg
)
779 gboolean found_typetest
= FALSE
;
780 for (MonoBasicBlock
*bb
= cfg
->bb_entry
; bb
; bb
= bb
->next_bb
) {
782 MONO_BB_FOR_EACH_INS (bb
, ins
) {
783 switch (ins
->opcode
) {
786 found_typetest
= TRUE
;
787 mono_decompose_typecheck (cfg
, bb
, ins
);
792 if ((cfg
->verbose_level
> 2) && found_typetest
)
793 mono_print_code (cfg
, "AFTER DECOMPOSE TYPE_CHECKS");
798 //API used by method-to-ir.c
800 mini_emit_class_check (MonoCompile
*cfg
, int klass_reg
, MonoClass
*klass
)
802 mini_emit_class_check_inst (cfg
, klass_reg
, klass
, NULL
);