[2019-12] [jit] Avoid passing a vtable argument to DIM methods when making calls...
[mono-project.git] / mono / mini / type-checking.c
blob8e3211f139863cb79ef3ec9333ee398706de1a5f
1 /**
2 * \file
3 */
5 #include <config.h>
6 #include <mono/utils/mono-compiler.h>
8 #ifndef DISABLE_JIT
10 #include "mini.h"
11 #include "ir-emit.h"
12 #include <mono/metadata/abi-details.h>
13 #include <mono/metadata/class-abi-details.h>
16 #define is_complex_isinst(klass) (mono_class_is_interface (klass) || m_class_get_rank (klass) || mono_class_is_nullable (klass) || mono_class_is_marshalbyref (klass) || mono_class_is_sealed (klass) || m_class_get_byval_arg (klass)->type == MONO_TYPE_VAR || m_class_get_byval_arg (klass)->type == MONO_TYPE_MVAR)
18 static int
19 get_castclass_cache_idx (MonoCompile *cfg)
21 /* Each CASTCLASS_CACHE patch needs a unique index which identifies the call site */
22 cfg->castclass_cache_index ++;
23 return (cfg->method_index << 16) | cfg->castclass_cache_index;
26 static void
27 emit_cached_check_args (MonoCompile *cfg, MonoInst *obj, MonoClass *klass, int context_used, MonoInst *args[3])
29 args [0] = obj;
31 if (context_used) {
32 MonoInst *cache_ins;
34 cache_ins = mini_emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_CAST_CACHE);
36 /* klass - it's the second element of the cache entry*/
37 EMIT_NEW_LOAD_MEMBASE (cfg, args [1], OP_LOAD_MEMBASE, alloc_preg (cfg), cache_ins->dreg, TARGET_SIZEOF_VOID_P);
39 args [2] = cache_ins; /* cache */
40 } else {
41 int idx;
43 EMIT_NEW_CLASSCONST (cfg, args [1], klass); /* klass */
45 idx = get_castclass_cache_idx (cfg); /* inline cache*/
46 args [2] = mini_emit_runtime_constant (cfg, MONO_PATCH_INFO_CASTCLASS_CACHE, GINT_TO_POINTER (idx));
50 static MonoInst*
51 emit_isinst_with_cache (MonoCompile *cfg, MonoInst *obj, MonoClass *klass, int context_used)
53 MonoInst *args [3];
54 MonoMethod *mono_isinst = mono_marshal_get_isinst_with_cache ();
56 emit_cached_check_args (cfg, obj, klass, context_used, args);
57 return mono_emit_method_call (cfg, mono_isinst, args, NULL);
60 static MonoInst*
61 emit_castclass_with_cache_no_details (MonoCompile *cfg, MonoInst *obj, MonoClass *klass, int context_used)
63 MonoInst *args [3];
64 MonoMethod *mono_castclass = mono_marshal_get_castclass_with_cache ();
65 MonoInst *res;
67 emit_cached_check_args (cfg, obj, klass, context_used, args);
69 res = mono_emit_method_call (cfg, mono_castclass, args, NULL);
71 return res;
74 static MonoInst*
75 emit_castclass_with_cache (MonoCompile *cfg, MonoInst *obj, MonoClass *klass, int context_used)
77 MonoInst *args [3];
78 MonoMethod *mono_castclass = mono_marshal_get_castclass_with_cache ();
79 MonoInst *res;
81 emit_cached_check_args (cfg, obj, klass, context_used, args);
83 mini_save_cast_details (cfg, klass, args [0]->dreg, TRUE);
84 res = mono_emit_method_call (cfg, mono_castclass, args, NULL);
85 mini_reset_cast_details (cfg);
87 return res;
90 static void
91 mini_emit_class_check_inst (MonoCompile *cfg, int klass_reg, MonoClass *klass, MonoInst *klass_inst)
93 if (klass_inst) {
94 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, klass_reg, klass_inst->dreg);
95 } else {
96 MonoInst *ins = mini_emit_runtime_constant (cfg, MONO_PATCH_INFO_CLASS, klass);
97 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, klass_reg, ins->dreg);
99 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException");
103 static void
104 mini_emit_isninst_cast_inst (MonoCompile *cfg, int klass_reg, MonoClass *klass, MonoInst *klass_ins, MonoBasicBlock *false_target, MonoBasicBlock *true_target)
106 int idepth_reg = alloc_preg (cfg);
107 int stypes_reg = alloc_preg (cfg);
108 int stype = alloc_preg (cfg);
110 mono_class_setup_supertypes (klass);
112 if (m_class_get_idepth (klass) > MONO_DEFAULT_SUPERTABLE_SIZE) {
113 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU2_MEMBASE, idepth_reg, klass_reg, m_class_offsetof_idepth ());
114 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, idepth_reg, m_class_get_idepth (klass));
115 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBLT_UN, false_target);
117 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, stypes_reg, klass_reg, m_class_offsetof_supertypes ());
118 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, stype, stypes_reg, ((m_class_get_idepth (klass) - 1) * TARGET_SIZEOF_VOID_P));
119 if (klass_ins) {
120 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, stype, klass_ins->dreg);
121 } else if (cfg->compile_aot) {
122 int const_reg = alloc_preg (cfg);
123 MONO_EMIT_NEW_CLASSCONST (cfg, const_reg, klass);
124 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, stype, const_reg);
125 } else {
126 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, stype, (gsize)klass);
128 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, true_target);
132 static void
133 mini_emit_interface_bitmap_check (MonoCompile *cfg, int intf_bit_reg, int base_reg, int offset, MonoClass *klass)
135 int ibitmap_reg = alloc_preg (cfg);
136 #ifdef COMPRESSED_INTERFACE_BITMAP
137 MonoInst *args [2];
138 MonoInst *res, *ins;
139 NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, ibitmap_reg, base_reg, offset);
140 MONO_ADD_INS (cfg->cbb, ins);
141 args [0] = ins;
142 args [1] = mini_emit_runtime_constant (cfg, MONO_PATCH_INFO_IID, klass);
143 res = mono_emit_jit_icall (cfg, mono_class_interface_match, args);
144 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, intf_bit_reg, res->dreg);
145 #else
146 int ibitmap_byte_reg = alloc_preg (cfg);
148 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, ibitmap_reg, base_reg, offset);
150 if (cfg->compile_aot) {
151 int iid_reg = alloc_preg (cfg);
152 int shifted_iid_reg = alloc_preg (cfg);
153 int ibitmap_byte_address_reg = alloc_preg (cfg);
154 int masked_iid_reg = alloc_preg (cfg);
155 int iid_one_bit_reg = alloc_preg (cfg);
156 int iid_bit_reg = alloc_preg (cfg);
157 MONO_EMIT_NEW_AOTCONST (cfg, iid_reg, klass, MONO_PATCH_INFO_IID);
158 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SHR_IMM, shifted_iid_reg, iid_reg, 3);
159 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, ibitmap_byte_address_reg, ibitmap_reg, shifted_iid_reg);
160 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU1_MEMBASE, ibitmap_byte_reg, ibitmap_byte_address_reg, 0);
161 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_IAND_IMM, masked_iid_reg, iid_reg, 7);
162 MONO_EMIT_NEW_ICONST (cfg, iid_one_bit_reg, 1);
163 MONO_EMIT_NEW_BIALU (cfg, OP_ISHL, iid_bit_reg, iid_one_bit_reg, masked_iid_reg);
164 MONO_EMIT_NEW_BIALU (cfg, OP_IAND, intf_bit_reg, ibitmap_byte_reg, iid_bit_reg);
165 } else {
166 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI1_MEMBASE, ibitmap_byte_reg, ibitmap_reg, m_class_get_interface_id (klass) >> 3);
167 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_AND_IMM, intf_bit_reg, ibitmap_byte_reg, 1 << (m_class_get_interface_id (klass) & 7));
169 #endif
173 * Emit code which loads into "intf_bit_reg" a nonzero value if the MonoClass
174 * stored in "klass_reg" implements the interface "klass".
176 static void
177 mini_emit_load_intf_bit_reg_class (MonoCompile *cfg, int intf_bit_reg, int klass_reg, MonoClass *klass)
179 mini_emit_interface_bitmap_check (cfg, intf_bit_reg, klass_reg, m_class_offsetof_interface_bitmap (), klass);
183 * Emit code which loads into "intf_bit_reg" a nonzero value if the MonoVTable
184 * stored in "vtable_reg" implements the interface "klass".
186 static void
187 mini_emit_load_intf_bit_reg_vtable (MonoCompile *cfg, int intf_bit_reg, int vtable_reg, MonoClass *klass)
189 mini_emit_interface_bitmap_check (cfg, intf_bit_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, interface_bitmap), klass);
193 * Emit code which checks whenever the interface id of @klass is smaller than
194 * than the value given by max_iid_reg.
196 static void
197 mini_emit_max_iid_check (MonoCompile *cfg, int max_iid_reg, MonoClass *klass,
198 MonoBasicBlock *false_target)
200 if (cfg->compile_aot) {
201 int iid_reg = alloc_preg (cfg);
202 MONO_EMIT_NEW_AOTCONST (cfg, iid_reg, klass, MONO_PATCH_INFO_IID);
203 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, max_iid_reg, iid_reg);
205 else
206 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, max_iid_reg, m_class_get_interface_id (klass));
207 if (false_target)
208 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBLT_UN, false_target);
209 else
210 MONO_EMIT_NEW_COND_EXC (cfg, LT_UN, "InvalidCastException");
213 /* Same as above, but obtains max_iid from a vtable */
214 static void
215 mini_emit_max_iid_check_vtable (MonoCompile *cfg, int vtable_reg, MonoClass *klass,
216 MonoBasicBlock *false_target)
218 int max_iid_reg = alloc_preg (cfg);
220 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU4_MEMBASE, max_iid_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, max_interface_id));
221 mini_emit_max_iid_check (cfg, max_iid_reg, klass, false_target);
224 /* Same as above, but obtains max_iid from a klass */
225 static void
226 mini_emit_max_iid_check_class (MonoCompile *cfg, int klass_reg, MonoClass *klass,
227 MonoBasicBlock *false_target)
229 int max_iid_reg = alloc_preg (cfg);
231 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU4_MEMBASE, max_iid_reg, klass_reg, m_class_offsetof_max_interface_id ());
232 mini_emit_max_iid_check (cfg, max_iid_reg, klass, false_target);
235 static void
236 mini_emit_class_check_branch (MonoCompile *cfg, int klass_reg, MonoClass *klass, int branch_op, MonoBasicBlock *target)
238 if (cfg->compile_aot) {
239 int const_reg = alloc_preg (cfg);
240 MONO_EMIT_NEW_CLASSCONST (cfg, const_reg, klass);
241 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, klass_reg, const_reg);
242 } else {
243 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, klass_reg, (gsize)klass);
245 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, branch_op, target);
249 static void
250 mini_emit_isninst_cast (MonoCompile *cfg, int klass_reg, MonoClass *klass, MonoBasicBlock *false_target, MonoBasicBlock *true_target)
252 mini_emit_isninst_cast_inst (cfg, klass_reg, klass, NULL, false_target, true_target);
255 static void
256 mini_emit_iface_cast (MonoCompile *cfg, int vtable_reg, MonoClass *klass, MonoBasicBlock *false_target, MonoBasicBlock *true_target)
258 int intf_reg = alloc_preg (cfg);
260 mini_emit_max_iid_check_vtable (cfg, vtable_reg, klass, false_target);
261 mini_emit_load_intf_bit_reg_vtable (cfg, intf_reg, vtable_reg, klass);
262 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, intf_reg, 0);
263 if (true_target)
264 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, true_target);
265 else
266 MONO_EMIT_NEW_COND_EXC (cfg, EQ, "InvalidCastException");
270 * Variant of the above that takes a register to the class, not the vtable.
272 static void
273 mini_emit_iface_class_cast (MonoCompile *cfg, int klass_reg, MonoClass *klass, MonoBasicBlock *false_target, MonoBasicBlock *true_target)
275 int intf_bit_reg = alloc_preg (cfg);
277 mini_emit_max_iid_check_class (cfg, klass_reg, klass, false_target);
278 mini_emit_load_intf_bit_reg_class (cfg, intf_bit_reg, klass_reg, klass);
279 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, intf_bit_reg, 0);
280 if (true_target)
281 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, true_target);
282 else
283 MONO_EMIT_NEW_COND_EXC (cfg, EQ, "InvalidCastException");
287 static void
288 mini_emit_castclass (MonoCompile *cfg, int obj_reg, int klass_reg, MonoClass *klass, MonoBasicBlock *object_is_null);
290 static void
291 mini_emit_castclass_inst (MonoCompile *cfg, int obj_reg, int klass_reg, MonoClass *klass, MonoInst *klass_inst, MonoBasicBlock *object_is_null)
293 if (m_class_get_rank (klass)) {
294 int rank_reg = alloc_preg (cfg);
295 int eclass_reg = alloc_preg (cfg);
297 g_assert (!klass_inst);
299 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU1_MEMBASE, rank_reg, klass_reg, m_class_offsetof_rank ());
300 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, rank_reg, m_class_get_rank (klass));
301 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException");
303 // MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
304 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, eclass_reg, klass_reg, m_class_offsetof_cast_class ());
305 if (m_class_is_array_special_interface (m_class_get_cast_class (klass))) {
306 MonoInst *src;
308 MONO_INST_NEW (cfg, src, OP_LOCAL);
309 src->dreg = obj_reg;
310 emit_castclass_with_cache_no_details (cfg, src, klass, 0);
311 } else if (m_class_get_cast_class (klass) == mono_defaults.object_class) {
312 int parent_reg = alloc_preg (cfg);
313 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, parent_reg, eclass_reg, m_class_offsetof_parent ());
314 mini_emit_class_check_branch (cfg, parent_reg, m_class_get_parent (mono_defaults.enum_class), OP_PBNE_UN, object_is_null);
315 mini_emit_class_check (cfg, eclass_reg, mono_defaults.enum_class);
316 } else if (m_class_get_cast_class (klass) == m_class_get_parent (mono_defaults.enum_class)) {
317 mini_emit_class_check_branch (cfg, eclass_reg, m_class_get_parent (mono_defaults.enum_class), OP_PBEQ, object_is_null);
318 mini_emit_class_check (cfg, eclass_reg, mono_defaults.enum_class);
319 } else if (m_class_get_cast_class (klass) == mono_defaults.enum_class) {
320 mini_emit_class_check (cfg, eclass_reg, mono_defaults.enum_class);
321 } else if (mono_class_is_interface (m_class_get_cast_class (klass))) {
322 mini_emit_iface_class_cast (cfg, eclass_reg, m_class_get_cast_class (klass), NULL, NULL);
323 } else {
324 // Pass -1 as obj_reg to skip the check below for arrays of arrays
325 mini_emit_castclass (cfg, -1, eclass_reg, m_class_get_cast_class (klass), object_is_null);
328 if ((m_class_get_rank (klass) == 1) && (m_class_get_byval_arg (klass)->type == MONO_TYPE_SZARRAY) && (obj_reg != -1)) {
329 /* Check that the object is a vector too */
330 int bounds_reg = alloc_preg (cfg);
331 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, bounds_reg, obj_reg, MONO_STRUCT_OFFSET (MonoArray, bounds));
332 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, bounds_reg, 0);
333 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException");
335 } else {
336 int idepth_reg = alloc_preg (cfg);
337 int stypes_reg = alloc_preg (cfg);
338 int stype = alloc_preg (cfg);
340 mono_class_setup_supertypes (klass);
342 if (m_class_get_idepth (klass) > MONO_DEFAULT_SUPERTABLE_SIZE) {
343 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU2_MEMBASE, idepth_reg, klass_reg, m_class_offsetof_idepth ());
344 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, idepth_reg, m_class_get_idepth (klass));
345 MONO_EMIT_NEW_COND_EXC (cfg, LT_UN, "InvalidCastException");
347 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, stypes_reg, klass_reg, m_class_offsetof_supertypes ());
348 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, stype, stypes_reg, ((m_class_get_idepth (klass) - 1) * TARGET_SIZEOF_VOID_P));
349 mini_emit_class_check_inst (cfg, stype, klass, klass_inst);
353 static void
354 mini_emit_castclass (MonoCompile *cfg, int obj_reg, int klass_reg, MonoClass *klass, MonoBasicBlock *object_is_null)
356 mini_emit_castclass_inst (cfg, obj_reg, klass_reg, klass, NULL, object_is_null);
359 static void
360 emit_special_array_iface_check (MonoCompile *cfg, MonoInst *src, MonoClass* klass, int vtable_reg, MonoBasicBlock *not_an_array, MonoBasicBlock *true_bb, int context_used)
362 int rank_reg;
364 if (!m_class_is_array_special_interface (klass))
365 return;
367 rank_reg = alloc_ireg (cfg);
369 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU1_MEMBASE, rank_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, rank));
370 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, rank_reg, 1);
371 if (not_an_array)
372 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBNE_UN, not_an_array);
373 else
374 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException");
376 emit_castclass_with_cache_no_details (cfg, src, klass, context_used);
377 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, true_bb);
381 * Returns NULL and set the cfg exception on error.
383 static MonoInst*
384 handle_castclass (MonoCompile *cfg, MonoClass *klass, MonoInst *src, int context_used)
386 MonoBasicBlock *is_null_bb;
387 int obj_reg = src->dreg;
388 MonoInst *klass_inst = NULL;
390 if (MONO_INS_IS_PCONST_NULL (src))
391 return src;
393 if (context_used) {
395 if (is_complex_isinst (klass))
396 return emit_castclass_with_cache (cfg, src, klass, context_used);
398 klass_inst = mini_emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_KLASS);
401 NEW_BBLOCK (cfg, is_null_bb);
403 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, obj_reg, 0);
404 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, is_null_bb);
406 mini_save_cast_details (cfg, klass, obj_reg, FALSE);
408 if (mono_class_is_interface (klass)) {
409 int tmp_reg = alloc_preg (cfg);
410 #ifndef DISABLE_REMOTING
411 MonoBasicBlock *interface_fail_bb;
412 MonoBasicBlock *array_fail_bb;
413 int klass_reg = alloc_preg (cfg);
415 NEW_BBLOCK (cfg, interface_fail_bb);
416 NEW_BBLOCK (cfg, array_fail_bb);
418 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
419 mini_emit_iface_cast (cfg, tmp_reg, klass, interface_fail_bb, is_null_bb);
421 // iface bitmap check failed
422 MONO_START_BB (cfg, interface_fail_bb);
424 //Check if it's a rank zero array and emit fallback casting
425 emit_special_array_iface_check (cfg, src, klass, tmp_reg, array_fail_bb, is_null_bb, context_used);
427 // array check failed
428 MONO_START_BB (cfg, array_fail_bb);
430 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, tmp_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
432 mini_emit_class_check (cfg, klass_reg, mono_defaults.transparent_proxy_class);
434 tmp_reg = alloc_preg (cfg);
435 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoTransparentProxy, custom_type_info));
436 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, tmp_reg, 0);
437 MONO_EMIT_NEW_COND_EXC (cfg, EQ, "InvalidCastException");
439 MonoInst *args [1] = { src };
440 MonoInst *proxy_test_inst = mono_emit_method_call (cfg, mono_marshal_get_proxy_cancast (klass), args, NULL);
441 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, proxy_test_inst->dreg, 0);
442 MONO_EMIT_NEW_COND_EXC (cfg, EQ, "InvalidCastException");
444 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, is_null_bb);
445 #else
446 MonoBasicBlock *interface_fail_bb = NULL;
448 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
450 if (m_class_is_array_special_interface (klass)) {
451 NEW_BBLOCK (cfg, interface_fail_bb);
452 mini_emit_iface_cast (cfg, tmp_reg, klass, interface_fail_bb, is_null_bb);
453 // iface bitmap check failed
454 MONO_START_BB (cfg, interface_fail_bb);
456 //Check if it's a rank zero array and emit fallback casting
457 emit_special_array_iface_check (cfg, src, klass, tmp_reg, NULL, is_null_bb, context_used);
458 } else {
459 mini_emit_iface_cast (cfg, tmp_reg, klass, NULL, NULL);
460 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, is_null_bb);
462 #endif
463 } else if (mono_class_is_marshalbyref (klass)) {
464 #ifndef DISABLE_REMOTING
465 MonoBasicBlock *no_proxy_bb, *fail_1_bb;
466 int tmp_reg = alloc_preg (cfg);
467 int klass_reg = alloc_preg (cfg);
469 NEW_BBLOCK (cfg, no_proxy_bb);
471 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
472 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, tmp_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
473 mini_emit_class_check_branch (cfg, klass_reg, mono_defaults.transparent_proxy_class, OP_PBNE_UN, no_proxy_bb);
475 tmp_reg = alloc_preg (cfg);
476 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoTransparentProxy, remote_class));
477 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, tmp_reg, MONO_STRUCT_OFFSET (MonoRemoteClass, proxy_class));
479 tmp_reg = alloc_preg (cfg);
480 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoTransparentProxy, custom_type_info));
481 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, tmp_reg, 0);
482 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, no_proxy_bb);
484 NEW_BBLOCK (cfg, fail_1_bb);
486 mini_emit_isninst_cast (cfg, klass_reg, klass, fail_1_bb, is_null_bb);
488 MONO_START_BB (cfg, fail_1_bb);
490 MonoInst *args [1] = { src };
491 MonoInst *proxy_test_inst = mono_emit_method_call (cfg, mono_marshal_get_proxy_cancast (klass), args, NULL);
492 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, proxy_test_inst->dreg, 0);
493 MONO_EMIT_NEW_COND_EXC (cfg, EQ, "InvalidCastException");
495 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, is_null_bb);
497 MONO_START_BB (cfg, no_proxy_bb);
499 mini_emit_castclass_inst (cfg, obj_reg, klass_reg, klass, klass_inst, is_null_bb);
500 #else
501 g_error ("Transparent proxy support is disabled while trying to JIT code that uses it");
502 #endif
503 } else {
504 int vtable_reg = alloc_preg (cfg);
505 int klass_reg = alloc_preg (cfg);
507 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, vtable_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
509 if (!m_class_get_rank (klass) && !cfg->compile_aot && !(cfg->opt & MONO_OPT_SHARED) && mono_class_is_sealed (klass)) {
510 /* the remoting code is broken, access the class for now */
511 if (0) { /*FIXME what exactly is broken? This change refers to r39380 from 2005 and mention some remoting fixes were due.*/
512 MonoVTable *vt = mono_class_vtable_checked (cfg->domain, klass, cfg->error);
513 if (!is_ok (cfg->error)) {
514 mono_cfg_set_exception (cfg, MONO_EXCEPTION_MONO_ERROR);
515 return NULL;
517 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, vtable_reg, (gsize)vt);
518 } else {
519 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
520 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, klass_reg, (gsize)klass);
522 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException");
523 } else {
524 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
525 mini_emit_castclass_inst (cfg, obj_reg, klass_reg, klass, klass_inst, is_null_bb);
529 MONO_START_BB (cfg, is_null_bb);
531 mini_reset_cast_details (cfg);
533 return src;
537 * Returns NULL and set the cfg exception on error.
539 static MonoInst*
540 handle_isinst (MonoCompile *cfg, MonoClass *klass, MonoInst *src, int context_used)
542 MonoInst *ins;
543 MonoBasicBlock *is_null_bb, *false_bb, *end_bb;
544 int obj_reg = src->dreg;
545 int vtable_reg = alloc_preg (cfg);
546 int res_reg = alloc_ireg_ref (cfg);
547 MonoInst *klass_inst = NULL;
549 if (context_used) {
550 if(is_complex_isinst (klass))
551 return emit_isinst_with_cache (cfg, src, klass, context_used);
553 klass_inst = mini_emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_KLASS);
556 NEW_BBLOCK (cfg, is_null_bb);
557 NEW_BBLOCK (cfg, false_bb);
558 NEW_BBLOCK (cfg, end_bb);
560 /* Do the assignment at the beginning, so the other assignment can be if converted */
561 EMIT_NEW_UNALU (cfg, ins, OP_MOVE, res_reg, obj_reg);
562 ins->type = STACK_OBJ;
563 ins->klass = klass;
565 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, obj_reg, 0);
566 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, is_null_bb);
568 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, vtable_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
570 if (mono_class_is_interface (klass)) {
571 MonoBasicBlock *interface_fail_bb;
573 NEW_BBLOCK (cfg, interface_fail_bb);
575 mini_emit_iface_cast (cfg, vtable_reg, klass, interface_fail_bb, is_null_bb);
576 MONO_START_BB (cfg, interface_fail_bb);
578 if (m_class_is_array_special_interface (klass)) {
579 MonoBasicBlock *not_an_array;
580 MonoInst *move;
581 int rank_reg = alloc_ireg (cfg);
583 NEW_BBLOCK (cfg, not_an_array);
584 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU1_MEMBASE, rank_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, rank));
585 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, rank_reg, 1);
586 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBNE_UN, not_an_array);
588 MonoInst *res_inst = emit_isinst_with_cache (cfg, src, klass, context_used);
589 EMIT_NEW_UNALU (cfg, move, OP_MOVE, res_reg, res_inst->dreg);
590 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
592 MONO_START_BB (cfg, not_an_array);
595 #ifndef DISABLE_REMOTING
596 int tmp_reg, klass_reg;
597 MonoBasicBlock *call_proxy_isinst;
599 NEW_BBLOCK (cfg, call_proxy_isinst);
601 klass_reg = alloc_preg (cfg);
602 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
604 mini_emit_class_check_branch (cfg, klass_reg, mono_defaults.transparent_proxy_class, OP_PBNE_UN, false_bb);
606 tmp_reg = alloc_preg (cfg);
607 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoTransparentProxy, custom_type_info));
608 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, tmp_reg, 0);
609 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, false_bb);
611 MONO_START_BB (cfg, call_proxy_isinst);
613 MonoInst *args [1] = { src };
614 MonoInst *proxy_test_inst = mono_emit_method_call (cfg, mono_marshal_get_proxy_cancast (klass), args, NULL);
615 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, proxy_test_inst->dreg, 0);
616 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, is_null_bb);
617 #else
618 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, false_bb);
619 #endif
621 } else if (mono_class_is_marshalbyref (klass)) {
623 #ifndef DISABLE_REMOTING
624 int tmp_reg, klass_reg;
625 MonoBasicBlock *no_proxy_bb, *call_proxy_isinst;
627 NEW_BBLOCK (cfg, no_proxy_bb);
628 NEW_BBLOCK (cfg, call_proxy_isinst);
630 klass_reg = alloc_preg (cfg);
631 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
633 mini_emit_class_check_branch (cfg, klass_reg, mono_defaults.transparent_proxy_class, OP_PBNE_UN, no_proxy_bb);
635 tmp_reg = alloc_preg (cfg);
636 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoTransparentProxy, remote_class));
637 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, tmp_reg, MONO_STRUCT_OFFSET (MonoRemoteClass, proxy_class));
639 tmp_reg = alloc_preg (cfg);
640 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoTransparentProxy, custom_type_info));
641 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, tmp_reg, 0);
642 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, false_bb);
644 mini_emit_isninst_cast (cfg, klass_reg, klass, call_proxy_isinst, is_null_bb);
646 MONO_START_BB (cfg, call_proxy_isinst);
648 MonoInst *args [1] = { src };
649 MonoInst *proxy_test_inst = mono_emit_method_call (cfg, mono_marshal_get_proxy_cancast (klass), args, NULL);
650 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, proxy_test_inst->dreg, 0);
651 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, is_null_bb);
652 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, false_bb);
654 MONO_START_BB (cfg, no_proxy_bb);
656 mini_emit_isninst_cast (cfg, klass_reg, klass, false_bb, is_null_bb);
657 #else
658 g_error ("transparent proxy support is disabled while trying to JIT code that uses it");
659 #endif
660 } else {
661 int klass_reg = alloc_preg (cfg);
663 if (m_class_get_rank (klass)) {
664 int rank_reg = alloc_preg (cfg);
665 int eclass_reg = alloc_preg (cfg);
667 if ((m_class_get_rank (klass) == 1) && (m_class_get_byval_arg (klass)->type == MONO_TYPE_SZARRAY)) {
668 /* Check that the object is a vector too */
669 int bounds_reg = alloc_preg (cfg);
670 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, bounds_reg, obj_reg, MONO_STRUCT_OFFSET (MonoArray, bounds));
671 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, bounds_reg, 0);
672 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, false_bb);
675 g_assert (!context_used);
676 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU1_MEMBASE, rank_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, rank));
677 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, rank_reg, m_class_get_rank (klass));
678 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, false_bb);
679 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
680 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, eclass_reg, klass_reg, m_class_offsetof_cast_class ());
681 if (m_class_is_array_special_interface (m_class_get_cast_class (klass))) {
682 MonoInst *move, *res_inst;
684 res_inst = emit_isinst_with_cache (cfg, src, klass, context_used);
685 EMIT_NEW_UNALU (cfg, move, OP_MOVE, res_reg, res_inst->dreg);
686 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
687 } else if (m_class_get_cast_class (klass) == mono_defaults.object_class) {
688 int parent_reg, class_kind_reg;
689 MonoBasicBlock *pointer_check_bb;
691 NEW_BBLOCK (cfg, pointer_check_bb);
693 parent_reg = alloc_preg (cfg);
694 class_kind_reg = alloc_preg (cfg);
695 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, parent_reg, eclass_reg, m_class_offsetof_parent ());
696 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU1_MEMBASE, class_kind_reg, eclass_reg, m_class_offsetof_class_kind ());
698 // Check if the parent class of the element is not System.ValueType
699 mini_emit_class_check_branch (cfg, parent_reg, m_class_get_parent (mono_defaults.enum_class), OP_PBNE_UN, pointer_check_bb);
700 mini_emit_class_check_branch (cfg, eclass_reg, mono_defaults.enum_class, OP_PBEQ, is_null_bb);
701 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, false_bb);
703 MONO_START_BB (cfg, pointer_check_bb);
704 // Check if the parent class of the element is non-null, else manually check the type
705 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, parent_reg, NULL);
706 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, is_null_bb);
707 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, class_kind_reg, MONO_CLASS_POINTER);
708 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, false_bb);
709 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, is_null_bb);
710 } else if (m_class_get_cast_class (klass) == m_class_get_parent (mono_defaults.enum_class)) {
711 mini_emit_class_check_branch (cfg, eclass_reg, m_class_get_parent (mono_defaults.enum_class), OP_PBEQ, is_null_bb);
712 mini_emit_class_check_branch (cfg, eclass_reg, mono_defaults.enum_class, OP_PBEQ, is_null_bb);
713 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, false_bb);
714 } else if (m_class_get_cast_class (klass) == mono_defaults.enum_class) {
715 mini_emit_class_check_branch (cfg, eclass_reg, mono_defaults.enum_class, OP_PBEQ, is_null_bb);
716 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, false_bb);
717 } else if (mono_class_is_interface (m_class_get_cast_class (klass))) {
718 mini_emit_iface_class_cast (cfg, eclass_reg, m_class_get_cast_class (klass), false_bb, is_null_bb);
719 } else {
720 /* the is_null_bb target simply copies the input register to the output */
721 mini_emit_isninst_cast (cfg, eclass_reg, m_class_get_cast_class (klass), false_bb, is_null_bb);
723 } else if (mono_class_is_nullable (klass)) {
724 g_assert (!context_used);
725 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
726 /* the is_null_bb target simply copies the input register to the output */
727 mini_emit_isninst_cast (cfg, klass_reg, m_class_get_cast_class (klass), false_bb, is_null_bb);
728 } else {
729 if (!cfg->compile_aot && !(cfg->opt & MONO_OPT_SHARED) && mono_class_is_sealed (klass)) {
730 g_assert (!context_used);
731 /* the remoting code is broken, access the class for now */
732 if (0) {/*FIXME what exactly is broken? This change refers to r39380 from 2005 and mention some remoting fixes were due.*/
733 MonoVTable *vt = mono_class_vtable_checked (cfg->domain, klass, cfg->error);
734 if (!is_ok (cfg->error)) {
735 mono_cfg_set_exception (cfg, MONO_EXCEPTION_MONO_ERROR);
736 return NULL;
738 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, vtable_reg, (gsize)vt);
739 } else {
740 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
741 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, klass_reg, (gsize)klass);
743 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, false_bb);
744 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, is_null_bb);
745 } else {
746 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
747 /* the is_null_bb target simply copies the input register to the output */
748 mini_emit_isninst_cast_inst (cfg, klass_reg, klass, klass_inst, false_bb, is_null_bb);
753 MONO_START_BB (cfg, false_bb);
755 MONO_EMIT_NEW_PCONST (cfg, res_reg, NULL);
756 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
758 MONO_START_BB (cfg, is_null_bb);
760 MONO_START_BB (cfg, end_bb);
762 return ins;
765 static void
766 mono_decompose_typecheck (MonoCompile *cfg, MonoBasicBlock *bb, MonoInst *ins)
768 MonoInst *ret, *move, *source;
769 MonoClass *klass = ins->klass;
770 int context_used = mini_class_check_context_used (cfg, klass);
771 int is_isinst = ins->opcode == OP_ISINST;
772 g_assert (is_isinst || ins->opcode == OP_CASTCLASS);
773 source = get_vreg_to_inst (cfg, ins->sreg1);
774 if (!source || source == (MonoInst *) -1)
775 source = mono_compile_create_var_for_vreg (cfg, mono_get_object_type (), OP_LOCAL, ins->sreg1);
776 g_assert (source && source != (MonoInst *) -1);
778 MonoBasicBlock *first_bb;
779 NEW_BBLOCK (cfg, first_bb);
780 cfg->cbb = first_bb;
782 if (mini_class_has_reference_variant_generic_argument (cfg, klass, context_used)) {
783 if (is_isinst)
784 ret = emit_isinst_with_cache (cfg, source, klass, context_used);
785 else
786 ret = emit_castclass_with_cache (cfg, source, klass, context_used);
788 } else {
789 if (is_isinst)
790 ret = handle_isinst (cfg, klass, source, context_used);
791 else
792 ret = handle_castclass (cfg, klass, source, context_used);
794 EMIT_NEW_UNALU (cfg, move, OP_MOVE, ins->dreg, ret->dreg);
796 g_assert (cfg->cbb->code || first_bb->code);
797 MonoInst *prev = ins->prev;
798 mono_replace_ins (cfg, bb, ins, &prev, first_bb, cfg->cbb);
801 void
802 mono_decompose_typechecks (MonoCompile *cfg)
804 gboolean found_typetest = FALSE;
805 for (MonoBasicBlock *bb = cfg->bb_entry; bb; bb = bb->next_bb) {
806 MonoInst *ins;
807 MONO_BB_FOR_EACH_INS (bb, ins) {
808 switch (ins->opcode) {
809 case OP_ISINST:
810 case OP_CASTCLASS:
811 found_typetest = TRUE;
812 mono_decompose_typecheck (cfg, bb, ins);
813 break;
817 if ((cfg->verbose_level > 2) && found_typetest)
818 mono_print_code (cfg, "AFTER DECOMPOSE TYPE_CHECKS");
823 //API used by method-to-ir.c
824 void
825 mini_emit_class_check (MonoCompile *cfg, int klass_reg, MonoClass *klass)
827 mini_emit_class_check_inst (cfg, klass_reg, klass, NULL);
830 #else
832 MONO_EMPTY_SOURCE_FILE (type_checking);
833 #endif