[Facades] Use the Open.snk key for the System.ValueTuple facade (#4173)
[mono-project.git] / mono / mini / type-checking.c
blob8d9cf6fe47c3da9a13cdb780f5b64f12ba1c8851
1 #include <config.h>
2 #include <mono/utils/mono-compiler.h>
4 #ifndef DISABLE_JIT
6 #include "mini.h"
7 #include "ir-emit.h"
8 #include <mono/metadata/abi-details.h>
11 //XXX maybe move to mini.h / mini.c?
13 static int
14 mini_class_check_context_used (MonoCompile *cfg, MonoClass *klass)
16 if (cfg->gshared)
17 return mono_class_check_context_used (klass);
18 else
19 return 0;
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)
25 static int
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;
33 static void
34 emit_cached_check_args (MonoCompile *cfg, MonoInst *obj, MonoClass *klass, int context_used, MonoInst *args[3])
36 args [0] = obj;
38 if (context_used) {
39 MonoInst *cache_ins;
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 */
47 } else {
48 int idx;
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));
57 static MonoInst*
58 emit_isinst_with_cache (MonoCompile *cfg, MonoInst *obj, MonoClass *klass, int context_used)
60 MonoInst *args [3];
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);
67 static MonoInst*
68 emit_castclass_with_cache_no_details (MonoCompile *cfg, MonoInst *obj, MonoClass *klass, int context_used)
70 MonoInst *args [3];
71 MonoMethod *mono_castclass = mono_marshal_get_castclass_with_cache ();
72 MonoInst *res;
74 emit_cached_check_args (cfg, obj, klass, context_used, args);
76 res = mono_emit_method_call (cfg, mono_castclass, args, NULL);
78 return res;
81 static MonoInst*
82 emit_castclass_with_cache (MonoCompile *cfg, MonoInst *obj, MonoClass *klass, int context_used)
84 MonoInst *args [3];
85 MonoMethod *mono_castclass = mono_marshal_get_castclass_with_cache ();
86 MonoInst *res;
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);
94 return res;
97 static inline void
98 mini_emit_class_check_inst (MonoCompile *cfg, int klass_reg, MonoClass *klass, MonoInst *klass_inst)
100 if (klass_inst) {
101 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, klass_reg, klass_inst->dreg);
102 } else {
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");
110 static void
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));
126 if (klass_ins) {
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);
132 } else {
133 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, stype, klass);
135 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, true_target);
139 static void
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
144 MonoInst *args [2];
145 MonoInst *res, *ins;
146 NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, ibitmap_reg, base_reg, offset);
147 MONO_ADD_INS (cfg->cbb, ins);
148 args [0] = 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);
152 #else
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);
172 } else {
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));
176 #endif
180 * Emit code which loads into "intf_bit_reg" a nonzero value if the MonoClass
181 * stored in "klass_reg" implements the interface "klass".
183 static void
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".
193 static void
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.
203 static void
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);
212 else
213 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, max_iid_reg, klass->interface_id);
214 if (false_target)
215 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBLT_UN, false_target);
216 else
217 MONO_EMIT_NEW_COND_EXC (cfg, LT_UN, "InvalidCastException");
220 /* Same as above, but obtains max_iid from a vtable */
221 static void
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 */
232 static void
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);
242 static inline void
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);
249 } else {
250 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, klass_reg, klass);
252 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, branch_op, target);
256 static void
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);
262 static void
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);
270 if (true_target)
271 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, true_target);
272 else
273 MONO_EMIT_NEW_COND_EXC (cfg, EQ, "InvalidCastException");
277 * Variant of the above that takes a register to the class, not the vtable.
279 static void
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);
287 if (true_target)
288 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, true_target);
289 else
290 MONO_EMIT_NEW_COND_EXC (cfg, EQ, "InvalidCastException");
294 static void
295 mini_emit_castclass (MonoCompile *cfg, int obj_reg, int klass_reg, MonoClass *klass, MonoBasicBlock *object_is_null);
297 static void
298 mini_emit_castclass_inst (MonoCompile *cfg, int obj_reg, int klass_reg, MonoClass *klass, MonoInst *klass_inst, MonoBasicBlock *object_is_null)
300 if (klass->rank) {
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);
322 } else {
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");
334 } else {
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);
352 static void
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);
358 static void
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;
362 int rank_reg;
364 if (!klass->is_array_special_interface)
365 return;
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.
384 static MonoInst*
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))
392 return src;
394 if (context_used) {
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);
441 #else
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);
454 } else {
455 mini_emit_iface_cast (cfg, tmp_reg, klass, NULL, NULL);
456 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, is_null_bb);
458 #endif
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);
496 #else
497 g_error ("Transparent proxy support is disabled while trying to JIT code that uses it");
498 #endif
499 } else {
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);
509 if (!vt) {
510 mono_cfg_set_exception (cfg, MONO_EXCEPTION_TYPE_LOAD);
511 cfg->exception_ptr = klass;
512 return NULL;
514 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, vtable_reg, vt);
515 } else {
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");
520 } else {
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);
530 return src;
534 * Returns NULL and set the cfg exception on error.
536 static MonoInst*
537 handle_isinst (MonoCompile *cfg, MonoClass *klass, MonoInst *src, int context_used)
539 MonoInst *ins;
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;
546 if (context_used) {
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;
560 ins->klass = klass;
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;
577 MonoInst *move;
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);
614 #else
615 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, false_bb);
616 #endif
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);
654 #else
655 g_error ("transparent proxy support is disabled while trying to JIT code that uses it");
656 #endif
657 } else {
658 int klass_reg = alloc_preg (cfg);
660 if (klass->rank) {
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);
685 } else {
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);
702 } else {
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);
708 if (!vt) {
709 mono_cfg_set_exception (cfg, MONO_EXCEPTION_TYPE_LOAD);
710 cfg->exception_ptr = klass;
711 return NULL;
713 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, vtable_reg, vt);
714 } else {
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);
720 } else {
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);
737 return ins;
740 static void
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);
755 cfg->cbb = first_bb;
757 if (mini_class_has_reference_variant_generic_argument (cfg, klass, context_used)) {
758 if (is_isinst)
759 ret = emit_isinst_with_cache (cfg, source, klass, context_used);
760 else
761 ret = emit_castclass_with_cache (cfg, source, klass, context_used);
763 } else {
764 if (is_isinst)
765 ret = handle_isinst (cfg, klass, source, context_used);
766 else
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);
776 void
777 mono_decompose_typechecks (MonoCompile *cfg)
779 gboolean found_typetest = FALSE;
780 for (MonoBasicBlock *bb = cfg->bb_entry; bb; bb = bb->next_bb) {
781 MonoInst *ins;
782 MONO_BB_FOR_EACH_INS (bb, ins) {
783 switch (ins->opcode) {
784 case OP_ISINST:
785 case OP_CASTCLASS:
786 found_typetest = TRUE;
787 mono_decompose_typecheck (cfg, bb, ins);
788 break;
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
799 void
800 mini_emit_class_check (MonoCompile *cfg, int klass_reg, MonoClass *klass)
802 mini_emit_class_check_inst (cfg, klass_reg, klass, NULL);
805 #endif