Merge pull request #5714 from alexischr/update_bockbuild
[mono.git] / mono / mini / type-checking.c
1 /**
2  * \file
3  */
4
5 #include <config.h>
6 #include <mono/utils/mono-compiler.h>
7
8 #ifndef DISABLE_JIT
9
10 #include "mini.h"
11 #include "ir-emit.h"
12 #include <mono/metadata/abi-details.h>
13
14
15 #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)
16
17 static int
18 get_castclass_cache_idx (MonoCompile *cfg)
19 {
20         /* Each CASTCLASS_CACHE patch needs a unique index which identifies the call site */
21         cfg->castclass_cache_index ++;
22         return (cfg->method_index << 16) | cfg->castclass_cache_index;
23 }
24
25 static void
26 emit_cached_check_args (MonoCompile *cfg, MonoInst *obj, MonoClass *klass, int context_used, MonoInst *args[3])
27 {
28         args [0] = obj;
29
30         if (context_used) {
31                 MonoInst *cache_ins;
32
33                 cache_ins = mini_emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_CAST_CACHE);
34
35                 /* klass - it's the second element of the cache entry*/
36                 EMIT_NEW_LOAD_MEMBASE (cfg, args [1], OP_LOAD_MEMBASE, alloc_preg (cfg), cache_ins->dreg, sizeof (gpointer));
37
38                 args [2] = cache_ins; /* cache */
39         } else {
40                 int idx;
41
42                 EMIT_NEW_CLASSCONST (cfg, args [1], klass); /* klass */
43
44                 idx = get_castclass_cache_idx (cfg); /* inline cache*/
45                 args [2] = mini_emit_runtime_constant (cfg, MONO_PATCH_INFO_CASTCLASS_CACHE, GINT_TO_POINTER (idx));
46         }
47 }
48
49 static MonoInst*
50 emit_isinst_with_cache (MonoCompile *cfg, MonoInst *obj, MonoClass *klass, int context_used)
51 {
52         MonoInst *args [3];
53         MonoMethod *mono_isinst = mono_marshal_get_isinst_with_cache ();
54
55         emit_cached_check_args (cfg, obj, klass, context_used, args);
56         return mono_emit_method_call (cfg, mono_isinst, args, NULL);
57 }
58
59 static MonoInst*
60 emit_castclass_with_cache_no_details (MonoCompile *cfg, MonoInst *obj, MonoClass *klass, int context_used)
61 {
62         MonoInst *args [3];
63         MonoMethod *mono_castclass = mono_marshal_get_castclass_with_cache ();
64         MonoInst *res;
65
66         emit_cached_check_args (cfg, obj, klass, context_used, args);
67
68         res = mono_emit_method_call (cfg, mono_castclass, args, NULL);
69
70         return res;
71 }
72
73 static MonoInst*
74 emit_castclass_with_cache (MonoCompile *cfg, MonoInst *obj, MonoClass *klass, int context_used)
75 {
76         MonoInst *args [3];
77         MonoMethod *mono_castclass = mono_marshal_get_castclass_with_cache ();
78         MonoInst *res;
79
80         emit_cached_check_args (cfg, obj, klass, context_used, args);
81
82         mini_save_cast_details (cfg, klass, args [0]->dreg, TRUE);
83         res = mono_emit_method_call (cfg, mono_castclass, args, NULL);
84         mini_reset_cast_details (cfg);
85
86         return res;
87 }
88
89 static inline void
90 mini_emit_class_check_inst (MonoCompile *cfg, int klass_reg, MonoClass *klass, MonoInst *klass_inst)
91 {
92         if (klass_inst) {
93                 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, klass_reg, klass_inst->dreg);
94         } else {
95                 MonoInst *ins = mini_emit_runtime_constant (cfg, MONO_PATCH_INFO_CLASS, klass);
96                 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, klass_reg, ins->dreg);
97         }
98         MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException");
99 }
100
101
102 static void
103 mini_emit_isninst_cast_inst (MonoCompile *cfg, int klass_reg, MonoClass *klass, MonoInst *klass_ins, MonoBasicBlock *false_target, MonoBasicBlock *true_target)
104 {
105         int idepth_reg = alloc_preg (cfg);
106         int stypes_reg = alloc_preg (cfg);
107         int stype = alloc_preg (cfg);
108
109         mono_class_setup_supertypes (klass);
110
111         if (klass->idepth > MONO_DEFAULT_SUPERTABLE_SIZE) {
112                 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU2_MEMBASE, idepth_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, idepth));
113                 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, idepth_reg, klass->idepth);
114                 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBLT_UN, false_target);
115         }
116         MONO_EMIT_NEW_LOAD_MEMBASE (cfg, stypes_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, supertypes));
117         MONO_EMIT_NEW_LOAD_MEMBASE (cfg, stype, stypes_reg, ((klass->idepth - 1) * SIZEOF_VOID_P));
118         if (klass_ins) {
119                 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, stype, klass_ins->dreg);
120         } else if (cfg->compile_aot) {
121                 int const_reg = alloc_preg (cfg);
122                 MONO_EMIT_NEW_CLASSCONST (cfg, const_reg, klass);
123                 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, stype, const_reg);
124         } else {
125                 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, stype, klass);
126         }
127         MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, true_target);
128 }
129
130
131 static void
132 mini_emit_interface_bitmap_check (MonoCompile *cfg, int intf_bit_reg, int base_reg, int offset, MonoClass *klass)
133 {
134         int ibitmap_reg = alloc_preg (cfg);
135 #ifdef COMPRESSED_INTERFACE_BITMAP
136         MonoInst *args [2];
137         MonoInst *res, *ins;
138         NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, ibitmap_reg, base_reg, offset);
139         MONO_ADD_INS (cfg->cbb, ins);
140         args [0] = ins;
141         args [1] = mini_emit_runtime_constant (cfg, MONO_PATCH_INFO_IID, klass);
142         res = mono_emit_jit_icall (cfg, mono_class_interface_match, args);
143         MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, intf_bit_reg, res->dreg);
144 #else
145         int ibitmap_byte_reg = alloc_preg (cfg);
146
147         MONO_EMIT_NEW_LOAD_MEMBASE (cfg, ibitmap_reg, base_reg, offset);
148
149         if (cfg->compile_aot) {
150                 int iid_reg = alloc_preg (cfg);
151                 int shifted_iid_reg = alloc_preg (cfg);
152                 int ibitmap_byte_address_reg = alloc_preg (cfg);
153                 int masked_iid_reg = alloc_preg (cfg);
154                 int iid_one_bit_reg = alloc_preg (cfg);
155                 int iid_bit_reg = alloc_preg (cfg);
156                 MONO_EMIT_NEW_AOTCONST (cfg, iid_reg, klass, MONO_PATCH_INFO_IID);
157                 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SHR_IMM, shifted_iid_reg, iid_reg, 3);
158                 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, ibitmap_byte_address_reg, ibitmap_reg, shifted_iid_reg);
159                 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU1_MEMBASE, ibitmap_byte_reg, ibitmap_byte_address_reg, 0);
160                 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_IAND_IMM, masked_iid_reg, iid_reg, 7);
161                 MONO_EMIT_NEW_ICONST (cfg, iid_one_bit_reg, 1);
162                 MONO_EMIT_NEW_BIALU (cfg, OP_ISHL, iid_bit_reg, iid_one_bit_reg, masked_iid_reg);
163                 MONO_EMIT_NEW_BIALU (cfg, OP_IAND, intf_bit_reg, ibitmap_byte_reg, iid_bit_reg);
164         } else {
165                 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI1_MEMBASE, ibitmap_byte_reg, ibitmap_reg, klass->interface_id >> 3);
166                 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_AND_IMM, intf_bit_reg, ibitmap_byte_reg, 1 << (klass->interface_id & 7));
167         }
168 #endif
169 }
170
171 /* 
172  * Emit code which loads into "intf_bit_reg" a nonzero value if the MonoClass
173  * stored in "klass_reg" implements the interface "klass".
174  */
175 static void
176 mini_emit_load_intf_bit_reg_class (MonoCompile *cfg, int intf_bit_reg, int klass_reg, MonoClass *klass)
177 {
178         mini_emit_interface_bitmap_check (cfg, intf_bit_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, interface_bitmap), klass);
179 }
180
181 /* 
182  * Emit code which loads into "intf_bit_reg" a nonzero value if the MonoVTable
183  * stored in "vtable_reg" implements the interface "klass".
184  */
185 static void
186 mini_emit_load_intf_bit_reg_vtable (MonoCompile *cfg, int intf_bit_reg, int vtable_reg, MonoClass *klass)
187 {
188         mini_emit_interface_bitmap_check (cfg, intf_bit_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, interface_bitmap), klass);
189 }
190
191 /* 
192  * Emit code which checks whenever the interface id of @klass is smaller than
193  * than the value given by max_iid_reg.
194 */
195 static void
196 mini_emit_max_iid_check (MonoCompile *cfg, int max_iid_reg, MonoClass *klass,
197                                                  MonoBasicBlock *false_target)
198 {
199         if (cfg->compile_aot) {
200                 int iid_reg = alloc_preg (cfg);
201                 MONO_EMIT_NEW_AOTCONST (cfg, iid_reg, klass, MONO_PATCH_INFO_IID);
202                 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, max_iid_reg, iid_reg);
203         }
204         else
205                 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, max_iid_reg, klass->interface_id);
206         if (false_target)
207                 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBLT_UN, false_target);
208         else
209                 MONO_EMIT_NEW_COND_EXC (cfg, LT_UN, "InvalidCastException");
210 }
211
212 /* Same as above, but obtains max_iid from a vtable */
213 static void
214 mini_emit_max_iid_check_vtable (MonoCompile *cfg, int vtable_reg, MonoClass *klass,
215                                                                  MonoBasicBlock *false_target)
216 {
217         int max_iid_reg = alloc_preg (cfg);
218                 
219         MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU4_MEMBASE, max_iid_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, max_interface_id));
220         mini_emit_max_iid_check (cfg, max_iid_reg, klass, false_target);
221 }
222
223 /* Same as above, but obtains max_iid from a klass */
224 static void
225 mini_emit_max_iid_check_class (MonoCompile *cfg, int klass_reg, MonoClass *klass,
226                                                                  MonoBasicBlock *false_target)
227 {
228         int max_iid_reg = alloc_preg (cfg);
229
230         MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU4_MEMBASE, max_iid_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, max_interface_id));
231         mini_emit_max_iid_check (cfg, max_iid_reg, klass, false_target);
232 }
233
234 static inline void
235 mini_emit_class_check_branch (MonoCompile *cfg, int klass_reg, MonoClass *klass, int branch_op, MonoBasicBlock *target)
236 {
237         if (cfg->compile_aot) {
238                 int const_reg = alloc_preg (cfg);
239                 MONO_EMIT_NEW_CLASSCONST (cfg, const_reg, klass);
240                 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, klass_reg, const_reg);
241         } else {
242                 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, klass_reg, klass);
243         }
244         MONO_EMIT_NEW_BRANCH_BLOCK (cfg, branch_op, target);
245 }
246
247
248 static void
249 mini_emit_isninst_cast (MonoCompile *cfg, int klass_reg, MonoClass *klass, MonoBasicBlock *false_target, MonoBasicBlock *true_target)
250 {
251         mini_emit_isninst_cast_inst (cfg, klass_reg, klass, NULL, false_target, true_target);
252 }
253
254 static void
255 mini_emit_iface_cast (MonoCompile *cfg, int vtable_reg, MonoClass *klass, MonoBasicBlock *false_target, MonoBasicBlock *true_target)
256 {
257         int intf_reg = alloc_preg (cfg);
258
259         mini_emit_max_iid_check_vtable (cfg, vtable_reg, klass, false_target);
260         mini_emit_load_intf_bit_reg_vtable (cfg, intf_reg, vtable_reg, klass);
261         MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, intf_reg, 0);
262         if (true_target)
263                 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, true_target);
264         else
265                 MONO_EMIT_NEW_COND_EXC (cfg, EQ, "InvalidCastException");               
266 }
267
268 /*
269  * Variant of the above that takes a register to the class, not the vtable.
270  */
271 static void
272 mini_emit_iface_class_cast (MonoCompile *cfg, int klass_reg, MonoClass *klass, MonoBasicBlock *false_target, MonoBasicBlock *true_target)
273 {
274         int intf_bit_reg = alloc_preg (cfg);
275
276         mini_emit_max_iid_check_class (cfg, klass_reg, klass, false_target);
277         mini_emit_load_intf_bit_reg_class (cfg, intf_bit_reg, klass_reg, klass);
278         MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, intf_bit_reg, 0);
279         if (true_target)
280                 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, true_target);
281         else
282                 MONO_EMIT_NEW_COND_EXC (cfg, EQ, "InvalidCastException");
283 }
284
285
286 static void
287 mini_emit_castclass (MonoCompile *cfg, int obj_reg, int klass_reg, MonoClass *klass, MonoBasicBlock *object_is_null);
288         
289 static void
290 mini_emit_castclass_inst (MonoCompile *cfg, int obj_reg, int klass_reg, MonoClass *klass, MonoInst *klass_inst, MonoBasicBlock *object_is_null)
291 {
292         if (klass->rank) {
293                 int rank_reg = alloc_preg (cfg);
294                 int eclass_reg = alloc_preg (cfg);
295
296                 g_assert (!klass_inst);
297                 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU1_MEMBASE, rank_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, rank));
298                 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, rank_reg, klass->rank);
299                 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException");
300                 //              MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
301                 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, eclass_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, cast_class));
302                 if (klass->cast_class == mono_defaults.object_class) {
303                         int parent_reg = alloc_preg (cfg);
304                         MONO_EMIT_NEW_LOAD_MEMBASE (cfg, parent_reg, eclass_reg, MONO_STRUCT_OFFSET (MonoClass, parent));
305                         mini_emit_class_check_branch (cfg, parent_reg, mono_defaults.enum_class->parent, OP_PBNE_UN, object_is_null);
306                         mini_emit_class_check (cfg, eclass_reg, mono_defaults.enum_class);
307                 } else if (klass->cast_class == mono_defaults.enum_class->parent) {
308                         mini_emit_class_check_branch (cfg, eclass_reg, mono_defaults.enum_class->parent, OP_PBEQ, object_is_null);
309                         mini_emit_class_check (cfg, eclass_reg, mono_defaults.enum_class);
310                 } else if (klass->cast_class == mono_defaults.enum_class) {
311                         mini_emit_class_check (cfg, eclass_reg, mono_defaults.enum_class);
312                 } else if (mono_class_is_interface (klass->cast_class)) {
313                         mini_emit_iface_class_cast (cfg, eclass_reg, klass->cast_class, NULL, NULL);
314                 } else {
315                         // Pass -1 as obj_reg to skip the check below for arrays of arrays
316                         mini_emit_castclass (cfg, -1, eclass_reg, klass->cast_class, object_is_null);
317                 }
318
319                 if ((klass->rank == 1) && (klass->byval_arg.type == MONO_TYPE_SZARRAY) && (obj_reg != -1)) {
320                         /* Check that the object is a vector too */
321                         int bounds_reg = alloc_preg (cfg);
322                         MONO_EMIT_NEW_LOAD_MEMBASE (cfg, bounds_reg, obj_reg, MONO_STRUCT_OFFSET (MonoArray, bounds));
323                         MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, bounds_reg, 0);
324                         MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException");
325                 }
326         } else {
327                 int idepth_reg = alloc_preg (cfg);
328                 int stypes_reg = alloc_preg (cfg);
329                 int stype = alloc_preg (cfg);
330
331                 mono_class_setup_supertypes (klass);
332
333                 if (klass->idepth > MONO_DEFAULT_SUPERTABLE_SIZE) {
334                         MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU2_MEMBASE, idepth_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, idepth));
335                         MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, idepth_reg, klass->idepth);
336                         MONO_EMIT_NEW_COND_EXC (cfg, LT_UN, "InvalidCastException");
337                 }
338                 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, stypes_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, supertypes));
339                 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, stype, stypes_reg, ((klass->idepth - 1) * SIZEOF_VOID_P));
340                 mini_emit_class_check_inst (cfg, stype, klass, klass_inst);
341         }
342 }
343
344 static void
345 mini_emit_castclass (MonoCompile *cfg, int obj_reg, int klass_reg, MonoClass *klass, MonoBasicBlock *object_is_null)
346 {
347         mini_emit_castclass_inst (cfg, obj_reg, klass_reg, klass, NULL, object_is_null);
348 }
349
350 static void
351 emit_special_array_iface_check (MonoCompile *cfg, MonoInst *src, MonoClass* klass, int vtable_reg, MonoBasicBlock *true_bb, int context_used)
352 {
353         MonoBasicBlock *not_an_array;
354         int rank_reg;
355
356         if (!klass->is_array_special_interface)
357                 return;
358
359         rank_reg = alloc_ireg (cfg);
360
361         NEW_BBLOCK (cfg, not_an_array);
362         MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU1_MEMBASE, rank_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, rank));
363         MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, rank_reg, 1);
364         MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBNE_UN, not_an_array);
365
366         emit_castclass_with_cache_no_details (cfg, src, klass, context_used);
367         MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, true_bb);
368
369         MONO_START_BB (cfg, not_an_array);
370
371 }
372
373 /*
374  * Returns NULL and set the cfg exception on error.
375  */
376 static MonoInst*
377 handle_castclass (MonoCompile *cfg, MonoClass *klass, MonoInst *src, int context_used)
378 {
379         MonoBasicBlock *is_null_bb;
380         int obj_reg = src->dreg;
381         MonoInst *klass_inst = NULL;
382
383         if (MONO_INS_IS_PCONST_NULL (src))
384                 return src;
385
386         if (context_used) {
387
388                 if (is_complex_isinst (klass))
389                         return emit_castclass_with_cache (cfg, src, klass, context_used);
390
391                 klass_inst = mini_emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_KLASS);
392         }
393
394         NEW_BBLOCK (cfg, is_null_bb);
395
396         MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, obj_reg, 0);
397         MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, is_null_bb);
398
399         mini_save_cast_details (cfg, klass, obj_reg, FALSE);
400
401         if (mono_class_is_interface (klass)) {
402                 int tmp_reg = alloc_preg (cfg);
403 #ifndef DISABLE_REMOTING
404                 MonoBasicBlock *interface_fail_bb;
405                 int klass_reg = alloc_preg (cfg);
406
407                 NEW_BBLOCK (cfg, interface_fail_bb);
408
409                 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
410                 mini_emit_iface_cast (cfg, tmp_reg, klass, interface_fail_bb, is_null_bb);
411
412                 // iface bitmap check failed
413                 MONO_START_BB (cfg, interface_fail_bb);
414
415                 //Check if it's a rank zero array and emit fallback casting
416                 emit_special_array_iface_check (cfg, src, klass, tmp_reg, is_null_bb, context_used);
417
418                 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, tmp_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
419
420                 mini_emit_class_check (cfg, klass_reg, mono_defaults.transparent_proxy_class);
421
422                 tmp_reg = alloc_preg (cfg);
423                 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoTransparentProxy, custom_type_info));
424                 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, tmp_reg, 0);
425                 MONO_EMIT_NEW_COND_EXC (cfg, EQ, "InvalidCastException");
426
427                 MonoInst *args [1] = { src };
428                 MonoInst *proxy_test_inst = mono_emit_method_call (cfg, mono_marshal_get_proxy_cancast (klass), args, NULL);
429                 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, proxy_test_inst->dreg, 0);
430                 MONO_EMIT_NEW_COND_EXC (cfg, EQ, "InvalidCastException");
431
432                 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, is_null_bb);
433 #else
434                 MonoBasicBlock *interface_fail_bb = NULL;
435
436                 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
437
438                 if (klass->is_array_special_interface) {
439                         NEW_BBLOCK (cfg, interface_fail_bb);
440                         mini_emit_iface_cast (cfg, tmp_reg, klass, interface_fail_bb, is_null_bb);
441                         // iface bitmap check failed
442                         MONO_START_BB (cfg, interface_fail_bb);
443
444                         //Check if it's a rank zero array and emit fallback casting
445                         emit_special_array_iface_check (cfg, src, klass, tmp_reg, is_null_bb, context_used);
446                 } else {
447                         mini_emit_iface_cast (cfg, tmp_reg, klass, NULL, NULL);
448                         MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, is_null_bb);
449                 }
450 #endif
451         } else if (mono_class_is_marshalbyref (klass)) {
452 #ifndef DISABLE_REMOTING
453                 MonoBasicBlock *no_proxy_bb, *fail_1_bb;
454                 int tmp_reg = alloc_preg (cfg);
455                 int klass_reg = alloc_preg (cfg);
456
457                 NEW_BBLOCK (cfg, no_proxy_bb);
458
459                 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
460                 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, tmp_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
461                 mini_emit_class_check_branch (cfg, klass_reg, mono_defaults.transparent_proxy_class, OP_PBNE_UN, no_proxy_bb);
462
463                 tmp_reg = alloc_preg (cfg);
464                 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoTransparentProxy, remote_class));
465                 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, tmp_reg, MONO_STRUCT_OFFSET (MonoRemoteClass, proxy_class));
466
467                 tmp_reg = alloc_preg (cfg);
468                 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoTransparentProxy, custom_type_info));
469                 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, tmp_reg, 0);
470                 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, no_proxy_bb);
471
472                 NEW_BBLOCK (cfg, fail_1_bb);
473
474                 mini_emit_isninst_cast (cfg, klass_reg, klass, fail_1_bb, is_null_bb);
475
476                 MONO_START_BB (cfg, fail_1_bb);
477
478                 MonoInst *args [1] = { src };
479                 MonoInst *proxy_test_inst = mono_emit_method_call (cfg, mono_marshal_get_proxy_cancast (klass), args, NULL);
480                 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, proxy_test_inst->dreg, 0);
481                 MONO_EMIT_NEW_COND_EXC (cfg, EQ, "InvalidCastException");
482
483                 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, is_null_bb);
484
485                 MONO_START_BB (cfg, no_proxy_bb);
486
487                 mini_emit_castclass_inst (cfg, obj_reg, klass_reg, klass, klass_inst, is_null_bb);
488 #else
489                 g_error ("Transparent proxy support is disabled while trying to JIT code that uses it");
490 #endif
491         } else {
492                 int vtable_reg = alloc_preg (cfg);
493                 int klass_reg = alloc_preg (cfg);
494
495                 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, vtable_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
496
497                 if (!klass->rank && !cfg->compile_aot && !(cfg->opt & MONO_OPT_SHARED) && mono_class_is_sealed (klass)) {
498                         /* the remoting code is broken, access the class for now */
499                         if (0) { /*FIXME what exactly is broken? This change refers to r39380 from 2005 and mention some remoting fixes were due.*/
500                                 MonoVTable *vt = mono_class_vtable (cfg->domain, klass);
501                                 if (!vt) {
502                                         mono_cfg_set_exception (cfg, MONO_EXCEPTION_TYPE_LOAD);
503                                         cfg->exception_ptr = klass;
504                                         return NULL;
505                                 }
506                                 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, vtable_reg, vt);
507                         } else {
508                                 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
509                                 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, klass_reg, klass);
510                         }
511                         MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException");
512                 } else {
513                         MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
514                         mini_emit_castclass_inst (cfg, obj_reg, klass_reg, klass, klass_inst, is_null_bb);
515                 }
516         }
517
518         MONO_START_BB (cfg, is_null_bb);
519
520         mini_reset_cast_details (cfg);
521
522         return src;
523 }
524
525 /*
526  * Returns NULL and set the cfg exception on error.
527  */
528 static MonoInst*
529 handle_isinst (MonoCompile *cfg, MonoClass *klass, MonoInst *src, int context_used)
530 {
531         MonoInst *ins;
532         MonoBasicBlock *is_null_bb, *false_bb, *end_bb;
533         int obj_reg = src->dreg;
534         int vtable_reg = alloc_preg (cfg);
535         int res_reg = alloc_ireg_ref (cfg);
536         MonoInst *klass_inst = NULL;
537
538         if (context_used) {
539                 if(is_complex_isinst (klass))
540                         return emit_isinst_with_cache (cfg, src, klass, context_used);
541
542                 klass_inst = mini_emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_KLASS);
543         }
544
545         NEW_BBLOCK (cfg, is_null_bb);
546         NEW_BBLOCK (cfg, false_bb);
547         NEW_BBLOCK (cfg, end_bb);
548
549         /* Do the assignment at the beginning, so the other assignment can be if converted */
550         EMIT_NEW_UNALU (cfg, ins, OP_MOVE, res_reg, obj_reg);
551         ins->type = STACK_OBJ;
552         ins->klass = klass;
553
554         MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, obj_reg, 0);
555         MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, is_null_bb);
556
557         MONO_EMIT_NEW_LOAD_MEMBASE (cfg, vtable_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
558
559         if (mono_class_is_interface (klass)) {
560                 MonoBasicBlock *interface_fail_bb;
561
562                 NEW_BBLOCK (cfg, interface_fail_bb);
563
564                 mini_emit_iface_cast (cfg, vtable_reg, klass, interface_fail_bb, is_null_bb);
565                 MONO_START_BB (cfg, interface_fail_bb);
566
567                 if (klass->is_array_special_interface) {
568                         MonoBasicBlock *not_an_array;
569                         MonoInst *move;
570                         int rank_reg = alloc_ireg (cfg);
571
572                         NEW_BBLOCK (cfg, not_an_array);
573                         MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU1_MEMBASE, rank_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, rank));
574                         MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, rank_reg, 1);
575                         MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBNE_UN, not_an_array);
576
577                         MonoInst *res_inst = emit_isinst_with_cache (cfg, src, klass, context_used);
578                         EMIT_NEW_UNALU (cfg, move, OP_MOVE, res_reg, res_inst->dreg);
579                         MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
580
581                         MONO_START_BB (cfg, not_an_array);
582                 }
583
584 #ifndef DISABLE_REMOTING
585                 int tmp_reg, klass_reg;
586                 MonoBasicBlock *call_proxy_isinst;
587
588                 NEW_BBLOCK (cfg, call_proxy_isinst);
589
590                 klass_reg = alloc_preg (cfg);
591                 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
592
593                 mini_emit_class_check_branch (cfg, klass_reg, mono_defaults.transparent_proxy_class, OP_PBNE_UN, false_bb);
594
595                 tmp_reg = alloc_preg (cfg);
596                 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoTransparentProxy, custom_type_info));
597                 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, tmp_reg, 0);
598                 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, false_bb);
599
600                 MONO_START_BB (cfg, call_proxy_isinst);
601
602                 MonoInst *args [1] = { src };
603                 MonoInst *proxy_test_inst = mono_emit_method_call (cfg, mono_marshal_get_proxy_cancast (klass), args, NULL);
604                 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, proxy_test_inst->dreg, 0);
605                 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, is_null_bb);
606 #else
607                 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, false_bb);
608 #endif
609
610         } else if (mono_class_is_marshalbyref (klass)) {
611
612 #ifndef DISABLE_REMOTING
613                 int tmp_reg, klass_reg;
614                 MonoBasicBlock *no_proxy_bb, *call_proxy_isinst;
615
616                 NEW_BBLOCK (cfg, no_proxy_bb);
617                 NEW_BBLOCK (cfg, call_proxy_isinst);
618
619                 klass_reg = alloc_preg (cfg);
620                 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
621
622                 mini_emit_class_check_branch (cfg, klass_reg, mono_defaults.transparent_proxy_class, OP_PBNE_UN, no_proxy_bb);
623
624                 tmp_reg = alloc_preg (cfg);
625                 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoTransparentProxy, remote_class));
626                 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, tmp_reg, MONO_STRUCT_OFFSET (MonoRemoteClass, proxy_class));
627
628                 tmp_reg = alloc_preg (cfg);
629                 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoTransparentProxy, custom_type_info));
630                 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, tmp_reg, 0);
631                 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, false_bb);
632
633                 mini_emit_isninst_cast (cfg, klass_reg, klass, call_proxy_isinst, is_null_bb);
634
635                 MONO_START_BB (cfg, call_proxy_isinst);
636
637                 MonoInst *args [1] = { src };
638                 MonoInst *proxy_test_inst = mono_emit_method_call (cfg, mono_marshal_get_proxy_cancast (klass), args, NULL);
639                 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, proxy_test_inst->dreg, 0);
640                 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, is_null_bb);
641                 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, false_bb);
642
643                 MONO_START_BB (cfg, no_proxy_bb);
644
645                 mini_emit_isninst_cast (cfg, klass_reg, klass, false_bb, is_null_bb);
646 #else
647                 g_error ("transparent proxy support is disabled while trying to JIT code that uses it");
648 #endif
649         } else {
650                 int klass_reg = alloc_preg (cfg);
651
652                 if (klass->rank) {
653                         int rank_reg = alloc_preg (cfg);
654                         int eclass_reg = alloc_preg (cfg);
655
656                         if ((klass->rank == 1) && (klass->byval_arg.type == MONO_TYPE_SZARRAY)) {
657                                 /* Check that the object is a vector too */
658                                 int bounds_reg = alloc_preg (cfg);
659                                 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, bounds_reg, obj_reg, MONO_STRUCT_OFFSET (MonoArray, bounds));
660                                 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, bounds_reg, 0);
661                                 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, false_bb);
662                         }
663
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                                 /* the is_null_bb target simply copies the input register to the output */
687                                 mini_emit_isninst_cast (cfg, eclass_reg, klass->cast_class, false_bb, is_null_bb);
688                         }
689                 } else if (mono_class_is_nullable (klass)) {
690                         g_assert (!context_used);
691                         MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
692                         /* the is_null_bb target simply copies the input register to the output */
693                         mini_emit_isninst_cast (cfg, klass_reg, klass->cast_class, false_bb, is_null_bb);
694                 } else {
695                         if (!cfg->compile_aot && !(cfg->opt & MONO_OPT_SHARED) && mono_class_is_sealed (klass)) {
696                                 g_assert (!context_used);
697                                 /* the remoting code is broken, access the class for now */
698                                 if (0) {/*FIXME what exactly is broken? This change refers to r39380 from 2005 and mention some remoting fixes were due.*/
699                                         MonoVTable *vt = mono_class_vtable (cfg->domain, klass);
700                                         if (!vt) {
701                                                 mono_cfg_set_exception (cfg, MONO_EXCEPTION_TYPE_LOAD);
702                                                 cfg->exception_ptr = klass;
703                                                 return NULL;
704                                         }
705                                         MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, vtable_reg, vt);
706                                 } else {
707                                         MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
708                                         MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, klass_reg, klass);
709                                 }
710                                 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, false_bb);
711                                 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, is_null_bb);
712                         } else {
713                                 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
714                                 /* the is_null_bb target simply copies the input register to the output */
715                                 mini_emit_isninst_cast_inst (cfg, klass_reg, klass, klass_inst, false_bb, is_null_bb);
716                         }
717                 }
718         }
719
720         MONO_START_BB (cfg, false_bb);
721
722         MONO_EMIT_NEW_PCONST (cfg, res_reg, NULL);
723         MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
724
725         MONO_START_BB (cfg, is_null_bb);
726
727         MONO_START_BB (cfg, end_bb);
728
729         return ins;
730 }
731
732 static void
733 mono_decompose_typecheck (MonoCompile *cfg, MonoBasicBlock *bb, MonoInst *ins)
734 {
735         MonoInst *ret, *move, *source;
736         MonoClass *klass = ins->klass;
737         int context_used = mini_class_check_context_used (cfg, klass);
738         int is_isinst = ins->opcode == OP_ISINST;
739         g_assert (is_isinst || ins->opcode == OP_CASTCLASS);
740         source = get_vreg_to_inst (cfg, ins->sreg1);
741         if (!source || source == (MonoInst *) -1)
742                 source = mono_compile_create_var_for_vreg (cfg, &mono_defaults.object_class->byval_arg, OP_LOCAL, ins->sreg1);
743         g_assert (source && source != (MonoInst *) -1);
744
745         MonoBasicBlock *first_bb;
746         NEW_BBLOCK (cfg, first_bb);
747         cfg->cbb = first_bb;
748
749         if (mini_class_has_reference_variant_generic_argument (cfg, klass, context_used)) {
750                 if (is_isinst)
751                         ret = emit_isinst_with_cache (cfg, source, klass, context_used);
752                 else
753                         ret = emit_castclass_with_cache (cfg, source, klass, context_used);
754
755         } else {
756                 if (is_isinst)
757                         ret = handle_isinst (cfg, klass, source, context_used);
758                 else
759                         ret = handle_castclass (cfg, klass, source, context_used);
760         }
761         EMIT_NEW_UNALU (cfg, move, OP_MOVE, ins->dreg, ret->dreg);
762
763         g_assert (cfg->cbb->code || first_bb->code);
764         MonoInst *prev = ins->prev;
765         mono_replace_ins (cfg, bb, ins, &prev, first_bb, cfg->cbb);
766 }
767
768 void
769 mono_decompose_typechecks (MonoCompile *cfg)
770 {
771         gboolean found_typetest = FALSE;
772         for (MonoBasicBlock *bb = cfg->bb_entry; bb; bb = bb->next_bb) {
773                 MonoInst *ins;
774                 MONO_BB_FOR_EACH_INS (bb, ins) {
775                         switch (ins->opcode) {
776                         case OP_ISINST:
777                         case OP_CASTCLASS:
778                                 found_typetest = TRUE;
779                                 mono_decompose_typecheck (cfg, bb, ins);
780                                 break;
781                         }
782                 }
783         }
784         if ((cfg->verbose_level > 2) && found_typetest)
785                 mono_print_code (cfg, "AFTER DECOMPOSE TYPE_CHECKS");
786         
787 }
788
789
790 //API used by method-to-ir.c
791 void
792 mini_emit_class_check (MonoCompile *cfg, int klass_reg, MonoClass *klass)
793 {
794         mini_emit_class_check_inst (cfg, klass_reg, klass, NULL);
795 }
796
797 #else
798
799 MONO_EMPTY_SOURCE_FILE (type_checking);
800 #endif