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