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