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