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