[jit] test builtin types (#5208)
[mono.git] / mono / mini / mini-native-types.c
1 /**
2  * \file
3  * intrinsics for variable sized int/floats
4  *
5  * Author:
6  *   Rodrigo Kumpera (kumpera@gmail.com)
7  *
8  * (C) 2013 Xamarin
9  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
10  */
11
12 #include <config.h>
13 #include <stdio.h>
14
15 #include "mini.h"
16 #include "ir-emit.h"
17 #include "glib.h"
18
19
20 typedef struct {
21         const char *op_name;
22         short op_table[4];
23 } IntIntrisic;
24
25 typedef struct {
26         short op_index;
27         short big_stack_type;
28         short small_stack_type;
29         short stack_type;
30         short conv_4_to_8;
31         short conv_8_to_4;
32         short move;
33         short inc_op;
34         short dec_op;
35         short store_op;
36         short compare_op;
37 } MagicTypeInfo;
38
39
40 #if SIZEOF_VOID_P == 8
41 #define OP_PT_ADD OP_LADD
42 #define OP_PT_SUB OP_LSUB
43 #define OP_PT_MUL OP_LMUL
44 #define OP_PT_DIV OP_LDIV
45 #define OP_PT_REM OP_LREM
46 #define OP_PT_NEG OP_LNEG
47 #define OP_PT_AND OP_LAND
48 #define OP_PT_OR OP_LOR
49 #define OP_PT_XOR OP_LXOR
50 #define OP_PT_NOT OP_LNOT
51 #define OP_PT_SHL OP_LSHL
52 #define OP_PT_SHR OP_LSHR
53
54 #define OP_PT_DIV_UN OP_LDIV_UN
55 #define OP_PT_REM_UN OP_LREM_UN
56 #define OP_PT_SHR_UN OP_LSHR_UN
57
58 #define OP_PT_ADD_IMM OP_LADD_IMM
59 #define OP_PT_SUB_IMM OP_LSUB_IMM
60
61 #define OP_PT_STORE_FP_MEMBASE_REG OP_STORER8_MEMBASE_REG
62
63 #define OP_PCOMPARE OP_LCOMPARE
64
65 #else
66 #define OP_PT_ADD OP_IADD
67 #define OP_PT_SUB OP_ISUB
68 #define OP_PT_MUL OP_IMUL
69 #define OP_PT_DIV OP_IDIV
70 #define OP_PT_REM OP_IREM
71 #define OP_PT_NEG OP_INEG
72 #define OP_PT_AND OP_IAND
73 #define OP_PT_OR OP_IOR
74 #define OP_PT_XOR OP_IXOR
75 #define OP_PT_NOT OP_INOT
76 #define OP_PT_SHL OP_ISHL
77 #define OP_PT_SHR OP_ISHR
78
79 #define OP_PT_DIV_UN OP_IDIV_UN
80 #define OP_PT_REM_UN OP_IREM_UN
81 #define OP_PT_SHR_UN OP_ISHR_UN
82
83 #define OP_PT_ADD_IMM OP_IADD_IMM
84 #define OP_PT_SUB_IMM OP_ISUB_IMM
85
86 #define OP_PT_STORE_FP_MEMBASE_REG OP_STORER4_MEMBASE_REG
87
88 #define OP_PCOMPARE OP_ICOMPARE
89
90 #endif
91
92 static const IntIntrisic int_binop[] = {
93         { "op_Addition", { OP_PT_ADD, OP_PT_ADD, OP_FADD, OP_RADD } },
94         { "op_Subtraction", { OP_PT_SUB, OP_PT_SUB, OP_FSUB, OP_RSUB } },
95         { "op_Multiply", { OP_PT_MUL, OP_PT_MUL, OP_FMUL, OP_RMUL } },
96         { "op_Division", { OP_PT_DIV, OP_PT_DIV_UN, OP_FDIV, OP_RDIV } },
97         { "op_Modulus", { OP_PT_REM, OP_PT_REM_UN, OP_FREM, OP_RREM } },
98         { "op_BitwiseAnd", { OP_PT_AND, OP_PT_AND } },
99         { "op_BitwiseOr", { OP_PT_OR, OP_PT_OR } },
100         { "op_ExclusiveOr", { OP_PT_XOR, OP_PT_XOR } },
101         { "op_LeftShift", { OP_PT_SHL, OP_PT_SHL } },
102         { "op_RightShift", { OP_PT_SHR, OP_PT_SHR_UN } },
103 };
104
105 static const IntIntrisic int_unnop[] = {
106         { "op_UnaryPlus", { OP_MOVE, OP_MOVE, OP_FMOVE, OP_RMOVE } },
107         { "op_UnaryNegation", { OP_PT_NEG, OP_PT_NEG, OP_FNEG, OP_RNEG } },
108         { "op_OnesComplement", { OP_PT_NOT, OP_PT_NOT, OP_FNOT, OP_RNOT } },
109 };
110
111 static const IntIntrisic int_cmpop[] = {
112         { "op_Inequality", { OP_ICNEQ, OP_ICNEQ, OP_FCNEQ, OP_RCNEQ } },
113         { "op_Equality", { OP_ICEQ, OP_ICEQ, OP_FCEQ, OP_RCEQ } },
114         { "op_GreaterThan", { OP_ICGT, OP_ICGT_UN, OP_FCGT, OP_RCGT } },
115         { "op_GreaterThanOrEqual", { OP_ICGE, OP_ICGE_UN, OP_FCGE, OP_RCGE } },
116         { "op_LessThan", { OP_ICLT, OP_ICLT_UN, OP_FCLT, OP_RCLT } },
117         { "op_LessThanOrEqual", { OP_ICLE, OP_ICLE_UN, OP_FCLE, OP_RCLE } },
118 };
119
120 static const MagicTypeInfo type_info[] = {
121         //nint
122         { 0, STACK_I8, STACK_I4, STACK_PTR, OP_ICONV_TO_I8, OP_LCONV_TO_I4, OP_MOVE, OP_PT_ADD_IMM, OP_PT_SUB_IMM, OP_STORE_MEMBASE_REG, OP_PCOMPARE },
123         //nuint
124         { 1, STACK_I8, STACK_I4, STACK_PTR, OP_ICONV_TO_U8, OP_LCONV_TO_U4, OP_MOVE, OP_PT_ADD_IMM, OP_PT_SUB_IMM, OP_STORE_MEMBASE_REG, OP_PCOMPARE },
125         //nfloat
126         { 2, STACK_R8, STACK_R8, STACK_R8, OP_FCONV_TO_R8, OP_FCONV_TO_R4, OP_FMOVE, 0, 0, OP_PT_STORE_FP_MEMBASE_REG, 0 },
127 };
128
129 static inline gboolean mono_class_is_magic_int (MonoClass *klass);
130 static inline gboolean mono_class_is_magic_float (MonoClass *klass);
131
132
133 static inline gboolean
134 type_size (MonoCompile *cfg, MonoType *type)
135 {
136         if (type->type == MONO_TYPE_I4 || type->type == MONO_TYPE_U4)
137                 return 4;
138         else if (type->type == MONO_TYPE_I8 || type->type == MONO_TYPE_U8)
139                 return 8;
140         else if (type->type == MONO_TYPE_R4 && !type->byref && cfg->r4fp)
141                 return 4;
142         else if (type->type == MONO_TYPE_R8 && !type->byref)
143                 return 8;
144         return SIZEOF_VOID_P;
145 }
146
147 #ifndef DISABLE_JIT
148
149 static gboolean is_int_type (MonoType *t);
150 static gboolean is_float_type (MonoType *t);
151
152 static MonoInst*
153 emit_narrow (MonoCompile *cfg, const MagicTypeInfo *info, int sreg)
154 {
155         MonoInst *ins;
156
157         MONO_INST_NEW (cfg, ins, info->conv_8_to_4);
158         ins->sreg1 = sreg;
159         if (info->conv_8_to_4 == OP_FCONV_TO_R4)
160                 ins->type = cfg->r4_stack_type;
161         else
162                 ins->type = info->small_stack_type;
163         ins->dreg = alloc_dreg (cfg, ins->type);
164         MONO_ADD_INS (cfg->cbb, ins);
165         return mono_decompose_opcode (cfg, ins);
166 }
167
168 static MonoInst*
169 emit_widen (MonoCompile *cfg, const MagicTypeInfo *info, int sreg)
170 {
171         MonoInst *ins;
172
173         if (cfg->r4fp && info->conv_4_to_8 == OP_FCONV_TO_R8)
174                 MONO_INST_NEW (cfg, ins, OP_RCONV_TO_R8);
175         else
176                 MONO_INST_NEW (cfg, ins, info->conv_4_to_8);
177         ins->sreg1 = sreg;
178         ins->type = info->big_stack_type;
179         ins->dreg = alloc_dreg (cfg, info->big_stack_type); 
180         MONO_ADD_INS (cfg->cbb, ins);
181         return mono_decompose_opcode (cfg, ins);
182 }
183
184 static MonoInst*
185 emit_intrinsics (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args, const MagicTypeInfo *info)
186 {
187         int i = 0;
188         const char *name = cmethod->name;
189         MonoInst *ins;
190         int type_index, stack_type;
191
192         if (info->op_index == 2 && cfg->r4fp && SIZEOF_VOID_P == 4) {
193                 type_index = 3;
194                 stack_type = STACK_R4;
195         } else {
196                 type_index = info->op_index;
197                 stack_type = info->stack_type;
198         }
199
200         if (!strcmp ("op_Implicit", name) || !strcmp ("op_Explicit", name)) {
201                 int source_size = type_size (cfg, fsig->params [0]);
202                 int dest_size = type_size (cfg, fsig->ret);
203
204                 switch (info->big_stack_type) {
205                 case STACK_I8:
206                         if (!is_int_type (fsig->params [0]) || !is_int_type (fsig->ret))
207                                 return NULL;
208                         break;
209                 case STACK_R8:
210                         if (!is_float_type (fsig->params [0]) || !is_float_type (fsig->ret))
211                                 return NULL;
212                         break;
213                 default:
214                         g_assert_not_reached ();
215                 }
216
217                 //4 -> 4 or 8 -> 8
218                 if (source_size == dest_size)
219                         return args [0];
220
221                 //4 -> 8
222                 if (source_size < dest_size)
223                         return emit_widen (cfg, info, args [0]->dreg);
224
225                 //8 -> 4
226                 return emit_narrow (cfg, info, args [0]->dreg);
227         }
228
229         if (!strcmp (".ctor", name)) {
230                 gboolean is_ldaddr = args [0]->opcode == OP_LDADDR;
231                 int arg0 = args [1]->dreg;
232                 int arg_size = type_size (cfg, fsig->params [0]);
233
234                 if (arg_size > SIZEOF_VOID_P) //8 -> 4
235                         arg0 = emit_narrow (cfg, info, arg0)->dreg;
236                 else if (arg_size < SIZEOF_VOID_P) //4 -> 8
237                         arg0 = emit_widen (cfg, info, arg0)->dreg;
238
239                 if (is_ldaddr) { /*Eliminate LDADDR if it's initing a local var*/
240                         int dreg = ((MonoInst*)args [0]->inst_p0)->dreg;
241                         NULLIFY_INS (args [0]);
242                         EMIT_NEW_UNALU (cfg, ins, info->move, dreg, arg0);
243                         cfg->has_indirection = TRUE;
244                 } else {
245                         EMIT_NEW_STORE_MEMBASE (cfg, ins, info->store_op, args [0]->dreg, 0, arg0);
246                 }
247                 return ins;
248         }
249
250         if (!strcmp ("op_Increment", name) || !strcmp ("op_Decrement", name)) {
251                 gboolean inc = !strcmp ("op_Increment", name);
252                 /* FIXME float inc is too complex to bother with*/
253                 //this is broken with ints too
254                 // if (!info->inc_op)
255                         return NULL;
256
257                 /* We have IR for inc/dec */
258                 MONO_INST_NEW (cfg, ins, inc ? info->inc_op : info->dec_op);
259                 ins->dreg = alloc_dreg (cfg, info->stack_type);
260                 ins->sreg1 = args [0]->dreg;
261                 ins->inst_imm = 1;
262                 ins->type = info->stack_type;
263                 MONO_ADD_INS (cfg->cbb, ins);
264                 return ins;
265         }
266
267         for (i = 0; i < sizeof (int_binop) / sizeof  (IntIntrisic); ++i) {
268                 if (!strcmp (int_binop [i].op_name, name)) {
269                         if (!int_binop [i].op_table [info->op_index])
270                                 return NULL;
271                         g_assert (int_binop [i].op_table [type_index]);
272
273                         MONO_INST_NEW (cfg, ins, int_binop [i].op_table [type_index]);
274                         ins->dreg = alloc_dreg (cfg, stack_type);
275                         ins->sreg1 = args [0]->dreg;
276                 ins->sreg2 = args [1]->dreg;
277                         ins->type = stack_type;
278                         MONO_ADD_INS (cfg->cbb, ins);
279                         return mono_decompose_opcode (cfg, ins);
280                 }
281         }
282
283         for (i = 0; i < sizeof (int_unnop) / sizeof  (IntIntrisic); ++i) {
284                 if (!strcmp (int_unnop [i].op_name, name)) {
285                         g_assert (int_unnop [i].op_table [type_index]);
286
287                         MONO_INST_NEW (cfg, ins, int_unnop [i].op_table [type_index]);
288                         ins->dreg = alloc_dreg (cfg, stack_type);
289                         ins->sreg1 = args [0]->dreg;
290                         ins->type = stack_type;
291                         MONO_ADD_INS (cfg->cbb, ins);
292                         return ins;
293                 }
294         }
295
296         for (i = 0; i < sizeof (int_cmpop) / sizeof  (IntIntrisic); ++i) {
297                 if (!strcmp (int_cmpop [i].op_name, name)) {
298                         g_assert (int_cmpop [i].op_table [type_index]);
299
300                         if (info->compare_op) {
301                                 MONO_INST_NEW (cfg, ins, info->compare_op);
302                         ins->dreg = -1;
303                                 ins->sreg1 = args [0]->dreg;
304                         ins->sreg2 = args [1]->dreg;
305                                 MONO_ADD_INS (cfg->cbb, ins);
306
307                                 MONO_INST_NEW (cfg, ins, int_cmpop [i].op_table [type_index]);
308                         ins->dreg = alloc_preg (cfg);
309                                 ins->type = STACK_I4;
310                                 MONO_ADD_INS (cfg->cbb, ins);
311                         } else {
312                                 MONO_INST_NEW (cfg, ins, int_cmpop [i].op_table [type_index]);
313                                 ins->dreg = alloc_ireg (cfg);
314                                 ins->sreg1 = args [0]->dreg;
315                         ins->sreg2 = args [1]->dreg;
316                                 MONO_ADD_INS (cfg->cbb, ins);
317                         }
318
319                         return ins;
320                 }
321         }
322
323         return NULL;
324 }
325
326
327 MonoInst*
328 mono_emit_native_types_intrinsics (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
329 {
330         if (mono_class_is_magic_int (cmethod->klass)) {
331                 const char *class_name = cmethod->klass->name;
332                 if (!strcmp ("nint", class_name))
333                         return emit_intrinsics (cfg, cmethod, fsig, args, &type_info [0]);
334                 else
335                         return emit_intrinsics (cfg, cmethod, fsig, args, &type_info [1]);
336         } else if (mono_class_is_magic_float (cmethod->klass))
337                 return emit_intrinsics (cfg, cmethod, fsig, args, &type_info [2]);
338
339         return NULL;
340 }
341
342 #endif /* !DISABLE_JIT */
343
344 static inline gboolean
345 mono_class_is_magic_assembly (MonoClass *klass)
346 {
347         if (!klass->image->assembly_name)
348                 return FALSE;
349         if (!strcmp ("Xamarin.iOS", klass->image->assembly_name))
350                 return TRUE;
351         if (!strcmp ("Xamarin.Mac", klass->image->assembly_name))
352                 return TRUE;
353         if (!strcmp ("Xamarin.WatchOS", klass->image->assembly_name))
354                 return TRUE;
355         /* regression test suite */
356         if (!strcmp ("builtin-types", klass->image->assembly_name))
357                 return TRUE;
358         return FALSE;
359 }
360
361 static inline gboolean
362 mono_class_is_magic_int (MonoClass *klass)
363 {
364         static MonoClass *magic_nint_class;
365         static MonoClass *magic_nuint_class;
366
367         if (klass == magic_nint_class)
368                 return TRUE;
369
370         if (klass == magic_nuint_class)
371                 return TRUE;
372
373         if (magic_nint_class && magic_nuint_class)
374                 return FALSE;
375
376         if (!mono_class_is_magic_assembly (klass))
377                 return FALSE;
378
379         if (strcmp ("System", klass->name_space) != 0)
380                 return FALSE;
381
382         if (strcmp ("nint", klass->name) == 0) {
383                 magic_nint_class = klass;
384                 return TRUE;
385         }
386
387         if (strcmp ("nuint", klass->name) == 0){
388                 magic_nuint_class = klass;
389                 return TRUE;
390         }
391         return FALSE;
392 }
393
394 static inline gboolean
395 mono_class_is_magic_float (MonoClass *klass)
396 {
397         static MonoClass *magic_nfloat_class;
398
399         if (klass == magic_nfloat_class)
400                 return TRUE;
401
402         if (magic_nfloat_class)
403                 return FALSE;
404
405         if (!mono_class_is_magic_assembly (klass))
406                 return FALSE;
407
408         if (strcmp ("System", klass->name_space) != 0)
409                 return FALSE;
410
411         if (strcmp ("nfloat", klass->name) == 0) {
412                 magic_nfloat_class = klass;
413
414                 /* Assert that we are using the matching assembly */
415                 MonoClassField *value_field = mono_class_get_field_from_name (klass, "v");
416                 g_assert (value_field);
417                 MonoType *t = mono_field_get_type (value_field);
418                 MonoType *native = mini_native_type_replace_type (&klass->byval_arg);
419                 if (t->type != native->type)
420                         g_error ("Assembly used for native types '%s' doesn't match this runtime, %s is mapped to %s, expecting %s.\n", klass->image->name, klass->name, mono_type_full_name (t), mono_type_full_name (native));
421                 return TRUE;
422         }
423         return FALSE;
424 }
425
426 static gboolean
427 is_int_type (MonoType *t)
428 {
429         if (t->type != MONO_TYPE_I4 && t->type != MONO_TYPE_I8 && t->type != MONO_TYPE_U4 && t->type != MONO_TYPE_U8 && !mono_class_is_magic_int (mono_class_from_mono_type (t)))
430                 return FALSE;
431         return TRUE;
432 }
433
434 static gboolean
435 is_float_type (MonoType *t)
436 {
437         if (t->type != MONO_TYPE_R4 && t->type != MONO_TYPE_R8 && !mono_class_is_magic_float (mono_class_from_mono_type (t)))
438                 return FALSE;
439         return TRUE;
440 }
441
442 MonoType*
443 mini_native_type_replace_type (MonoType *type)
444 {
445         MonoClass *klass;
446
447         if (type->type != MONO_TYPE_VALUETYPE)
448                 return type;
449         klass = type->data.klass;
450
451         if (mono_class_is_magic_int (klass))
452                 return type->byref ? &mono_defaults.int_class->this_arg : &mono_defaults.int_class->byval_arg;
453         if (mono_class_is_magic_float (klass))
454 #if SIZEOF_VOID_P == 8
455                 return type->byref ? &mono_defaults.double_class->this_arg : &mono_defaults.double_class->byval_arg;
456 #else
457                 return type->byref ? &mono_defaults.single_class->this_arg : &mono_defaults.single_class->byval_arg;
458 #endif
459         return type;
460 }