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