[tests] Reenable enum equals test on interpreter (#18673)
[mono-project.git] / mono / mini / mini-native-types.c
blob7d4bf660966b7ed1f993564cbf4bed0dde76b006
1 /**
2 * \file
3 * intrinsics for variable sized int/floats
5 * Author:
6 * Rodrigo Kumpera (kumpera@gmail.com)
8 * (C) 2013 Xamarin
9 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
12 #include <config.h>
13 #include <stdio.h>
15 #include "mini.h"
16 #include "ir-emit.h"
17 #include "glib.h"
20 typedef struct {
21 const char *op_name;
22 short op_table[4];
23 } IntIntrisic;
25 typedef struct {
26 short op_index;
27 #ifdef __cplusplus
28 MonoStackType big_stack_type : 16;
29 MonoStackType small_stack_type : 16;
30 MonoStackType stack_type : 16;
31 #else
32 short big_stack_type;
33 short small_stack_type;
34 short stack_type;
35 #endif
36 short conv_4_to_8;
37 short conv_8_to_4;
38 short move;
39 short inc_op;
40 short dec_op;
41 short store_op;
42 short compare_op;
43 } MagicTypeInfo;
46 #if TARGET_SIZEOF_VOID_P == 8
47 #define OP_PT_ADD OP_LADD
48 #define OP_PT_SUB OP_LSUB
49 #define OP_PT_MUL OP_LMUL
50 #define OP_PT_DIV OP_LDIV
51 #define OP_PT_REM OP_LREM
52 #define OP_PT_NEG OP_LNEG
53 #define OP_PT_AND OP_LAND
54 #define OP_PT_OR OP_LOR
55 #define OP_PT_XOR OP_LXOR
56 #define OP_PT_NOT OP_LNOT
57 #define OP_PT_SHL OP_LSHL
58 #define OP_PT_SHR OP_LSHR
60 #define OP_PT_DIV_UN OP_LDIV_UN
61 #define OP_PT_REM_UN OP_LREM_UN
62 #define OP_PT_SHR_UN OP_LSHR_UN
64 #define OP_PT_ADD_IMM OP_LADD_IMM
65 #define OP_PT_SUB_IMM OP_LSUB_IMM
67 #define OP_PT_STORE_FP_MEMBASE_REG OP_STORER8_MEMBASE_REG
69 #define OP_PCOMPARE OP_LCOMPARE
71 #else
72 #define OP_PT_ADD OP_IADD
73 #define OP_PT_SUB OP_ISUB
74 #define OP_PT_MUL OP_IMUL
75 #define OP_PT_DIV OP_IDIV
76 #define OP_PT_REM OP_IREM
77 #define OP_PT_NEG OP_INEG
78 #define OP_PT_AND OP_IAND
79 #define OP_PT_OR OP_IOR
80 #define OP_PT_XOR OP_IXOR
81 #define OP_PT_NOT OP_INOT
82 #define OP_PT_SHL OP_ISHL
83 #define OP_PT_SHR OP_ISHR
85 #define OP_PT_DIV_UN OP_IDIV_UN
86 #define OP_PT_REM_UN OP_IREM_UN
87 #define OP_PT_SHR_UN OP_ISHR_UN
89 #define OP_PT_ADD_IMM OP_IADD_IMM
90 #define OP_PT_SUB_IMM OP_ISUB_IMM
92 #define OP_PT_STORE_FP_MEMBASE_REG OP_STORER4_MEMBASE_REG
94 #define OP_PCOMPARE OP_ICOMPARE
96 #endif
98 gsize
99 mini_magic_type_size (MonoCompile *cfg, MonoType *type)
101 if (type->type == MONO_TYPE_I4 || type->type == MONO_TYPE_U4)
102 return 4;
103 else if (type->type == MONO_TYPE_I8 || type->type == MONO_TYPE_U8)
104 return 8;
105 else if (type->type == MONO_TYPE_R4 && !type->byref && (!cfg || cfg->r4fp))
106 return 4;
107 else if (type->type == MONO_TYPE_R8 && !type->byref)
108 return 8;
109 return TARGET_SIZEOF_VOID_P;
112 #ifndef DISABLE_JIT
114 static const IntIntrisic int_binop[] = {
115 { "op_Addition", { OP_PT_ADD, OP_PT_ADD, OP_FADD, OP_RADD } },
116 { "op_Subtraction", { OP_PT_SUB, OP_PT_SUB, OP_FSUB, OP_RSUB } },
117 { "op_Multiply", { OP_PT_MUL, OP_PT_MUL, OP_FMUL, OP_RMUL } },
118 { "op_Division", { OP_PT_DIV, OP_PT_DIV_UN, OP_FDIV, OP_RDIV } },
119 { "op_Modulus", { OP_PT_REM, OP_PT_REM_UN, OP_FREM, OP_RREM } },
120 { "op_BitwiseAnd", { OP_PT_AND, OP_PT_AND } },
121 { "op_BitwiseOr", { OP_PT_OR, OP_PT_OR } },
122 { "op_ExclusiveOr", { OP_PT_XOR, OP_PT_XOR } },
123 { "op_LeftShift", { OP_PT_SHL, OP_PT_SHL } },
124 { "op_RightShift", { OP_PT_SHR, OP_PT_SHR_UN } },
127 static const IntIntrisic int_unnop[] = {
128 { "op_UnaryPlus", { OP_MOVE, OP_MOVE, OP_FMOVE, OP_RMOVE } },
129 { "op_UnaryNegation", { OP_PT_NEG, OP_PT_NEG, OP_FNEG, OP_RNEG } },
130 { "op_OnesComplement", { OP_PT_NOT, OP_PT_NOT, OP_FNOT, OP_RNOT } },
133 static const IntIntrisic int_cmpop[] = {
134 { "op_Inequality", { OP_ICNEQ, OP_ICNEQ, OP_FCNEQ, OP_RCNEQ } },
135 { "op_Equality", { OP_ICEQ, OP_ICEQ, OP_FCEQ, OP_RCEQ } },
136 { "op_GreaterThan", { OP_ICGT, OP_ICGT_UN, OP_FCGT, OP_RCGT } },
137 { "op_GreaterThanOrEqual", { OP_ICGE, OP_ICGE_UN, OP_FCLT_UN, OP_RCLT_UN } },
138 { "op_LessThan", { OP_ICLT, OP_ICLT_UN, OP_FCLT, OP_RCLT } },
139 { "op_LessThanOrEqual", { OP_ICLE, OP_ICLE_UN, OP_FCGT_UN, OP_RCGT_UN } },
142 static const MagicTypeInfo type_info[] = {
143 //nint
144 { 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 },
145 //nuint
146 { 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 },
147 //nfloat
148 { 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 },
151 static MonoInst*
152 emit_narrow (MonoCompile *cfg, const MagicTypeInfo *info, int sreg)
154 MonoInst *ins;
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, (MonoStackType)ins->type);
163 MONO_ADD_INS (cfg->cbb, ins);
164 return mono_decompose_opcode (cfg, ins);
167 static MonoInst*
168 emit_widen (MonoCompile *cfg, const MagicTypeInfo *info, int sreg)
170 MonoInst *ins;
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);
183 static MonoInst*
184 emit_intrinsics (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args, const MagicTypeInfo *info)
186 int i = 0;
187 const char *name = cmethod->name;
188 MonoInst *ins;
189 int type_index;
190 MonoStackType stack_type;
192 if (info->op_index == 2 && cfg->r4fp && TARGET_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;
200 if (!strcmp ("op_Implicit", name) || !strcmp ("op_Explicit", name)) {
201 int source_size = mini_magic_type_size (cfg, fsig->params [0]);
202 int dest_size = mini_magic_type_size (cfg, fsig->ret);
204 switch (info->big_stack_type) {
205 case STACK_I8:
206 if (!mini_magic_is_int_type (fsig->params [0]) || !mini_magic_is_int_type (fsig->ret))
207 return NULL;
208 break;
209 case STACK_R8:
210 if (!mini_magic_is_float_type (fsig->params [0]) || !mini_magic_is_float_type (fsig->ret))
211 return NULL;
212 break;
213 default:
214 g_assert_not_reached ();
217 //4 -> 4 or 8 -> 8
218 if (source_size == dest_size)
219 return args [0];
221 //4 -> 8
222 if (source_size < dest_size)
223 return emit_widen (cfg, info, args [0]->dreg);
225 //8 -> 4
226 return emit_narrow (cfg, info, args [0]->dreg);
229 if (!strcmp (".ctor", name)) {
230 gboolean is_ldaddr = args [0]->opcode == OP_LDADDR;
231 int arg0 = args [1]->dreg;
232 int arg_size = mini_magic_type_size (cfg, fsig->params [0]);
234 if (arg_size > TARGET_SIZEOF_VOID_P) //8 -> 4
235 arg0 = emit_narrow (cfg, info, arg0)->dreg;
236 else if (arg_size < TARGET_SIZEOF_VOID_P) //4 -> 8
237 arg0 = emit_widen (cfg, info, arg0)->dreg;
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);
247 return ins;
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;
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, (MonoStackType)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;
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]);
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);
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]);
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;
296 for (i = 0; i < sizeof (int_cmpop) / sizeof (IntIntrisic); ++i) {
297 if (!strcmp (int_cmpop [i].op_name, name)) {
298 short op_cmp = int_cmpop [i].op_table [type_index];
300 g_assert (op_cmp);
302 if (info->compare_op) {
303 MONO_INST_NEW (cfg, ins, info->compare_op);
304 ins->dreg = -1;
305 ins->sreg1 = args [0]->dreg;
306 ins->sreg2 = args [1]->dreg;
307 MONO_ADD_INS (cfg->cbb, ins);
309 MONO_INST_NEW (cfg, ins, op_cmp);
310 ins->dreg = alloc_preg (cfg);
311 ins->type = STACK_I4;
312 MONO_ADD_INS (cfg->cbb, ins);
313 } else {
314 MONO_INST_NEW (cfg, ins, op_cmp);
315 guint32 fcmp_dreg = ins->dreg = alloc_ireg (cfg);
316 ins->sreg1 = args [0]->dreg;
317 ins->sreg2 = args [1]->dreg;
318 MONO_ADD_INS (cfg->cbb, ins);
319 if (op_cmp == OP_FCLT_UN || op_cmp == OP_FCGT_UN || op_cmp == OP_RCLT_UN || op_cmp == OP_RCGT_UN) {
320 /* we have to negate the result of this comparison:
321 * - op_GreaterThanOrEqual maps to NOT x OP_FCLT_UN / OP_RCLT_UN
322 * - op_LessThanOrEqual maps to NOT x OP_FCGT_UN / OP_RCGT_UN
324 * this matches generated bytecode by C# when doing the
325 * same operations on float/double. the `_UN` suffix says
326 * that if an operand is NaN, the result is true. If
327 * OP_FCGE/OP_FCLE is used, it is mapped to instructions
328 * on some architectures that don't detect NaN. For
329 * example, on arm64 the condition `eq` doesn't respect
330 * NaN results of a `fcmp` instruction.
332 MONO_INST_NEW (cfg, ins, OP_ICOMPARE_IMM);
333 ins->dreg = -1;
334 ins->sreg1 = fcmp_dreg;
335 ins->inst_imm = 0;
336 MONO_ADD_INS (cfg->cbb, ins);
338 MONO_INST_NEW (cfg, ins, OP_CEQ);
339 ins->dreg = alloc_preg (cfg);
340 ins->type = STACK_I4;
341 MONO_ADD_INS (cfg->cbb, ins);
345 return ins;
349 return NULL;
353 MonoInst*
354 mono_emit_native_types_intrinsics (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
356 if (mono_class_is_magic_int (cmethod->klass)) {
357 const char *class_name = m_class_get_name (cmethod->klass);
358 if (!strcmp ("nint", class_name))
359 return emit_intrinsics (cfg, cmethod, fsig, args, &type_info [0]);
360 else
361 return emit_intrinsics (cfg, cmethod, fsig, args, &type_info [1]);
362 } else if (mono_class_is_magic_float (cmethod->klass))
363 return emit_intrinsics (cfg, cmethod, fsig, args, &type_info [2]);
365 return NULL;
368 #endif /* !DISABLE_JIT */
370 static gboolean
371 mono_class_is_magic_assembly (MonoClass *klass)
373 const char *aname = m_class_get_image (klass)->assembly_name;
374 if (!aname)
375 return FALSE;
376 if (!strcmp ("Xamarin.iOS", aname))
377 return TRUE;
378 if (!strcmp ("Xamarin.Mac", aname))
379 return TRUE;
380 if (!strcmp ("Xamarin.WatchOS", aname))
381 return TRUE;
382 /* regression test suite */
383 if (!strcmp ("builtin-types", aname))
384 return TRUE;
385 if (!strcmp ("mini_tests", aname))
386 return TRUE;
387 return FALSE;
390 gboolean
391 mono_class_is_magic_int (MonoClass *klass)
393 static MonoClass *magic_nint_class;
394 static MonoClass *magic_nuint_class;
396 if (klass == magic_nint_class)
397 return TRUE;
399 if (klass == magic_nuint_class)
400 return TRUE;
402 if (magic_nint_class && magic_nuint_class)
403 return FALSE;
405 if (!mono_class_is_magic_assembly (klass))
406 return FALSE;
408 if (strcmp ("System", m_class_get_name_space (klass)) != 0)
409 return FALSE;
411 if (strcmp ("nint", m_class_get_name (klass)) == 0) {
412 magic_nint_class = klass;
413 return TRUE;
416 if (strcmp ("nuint", m_class_get_name (klass)) == 0){
417 magic_nuint_class = klass;
418 return TRUE;
420 return FALSE;
423 gboolean
424 mono_class_is_magic_float (MonoClass *klass)
426 static MonoClass *magic_nfloat_class;
428 if (klass == magic_nfloat_class)
429 return TRUE;
431 if (magic_nfloat_class)
432 return FALSE;
434 if (!mono_class_is_magic_assembly (klass))
435 return FALSE;
437 if (strcmp ("System", m_class_get_name_space (klass)) != 0)
438 return FALSE;
440 if (strcmp ("nfloat", m_class_get_name (klass)) == 0) {
441 magic_nfloat_class = klass;
443 /* Assert that we are using the matching assembly */
444 MonoClassField *value_field = mono_class_get_field_from_name_full (klass, "v", NULL);
445 g_assert (value_field);
446 MonoType *t = mono_field_get_type_internal (value_field);
447 MonoType *native = mini_native_type_replace_type (m_class_get_byval_arg (klass));
448 if (t->type != native->type)
449 g_error ("Assembly used for native types '%s' doesn't match this runtime, %s is mapped to %s, expecting %s.\n", m_class_get_image (klass)->name, m_class_get_name (klass), mono_type_full_name (t), mono_type_full_name (native));
450 return TRUE;
452 return FALSE;
455 gboolean
456 mini_magic_is_int_type (MonoType *t)
458 if (t->type != MONO_TYPE_I && 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_internal (t)))
459 return FALSE;
460 return TRUE;
463 gboolean
464 mini_magic_is_float_type (MonoType *t)
466 if (t->type != MONO_TYPE_R4 && t->type != MONO_TYPE_R8 && !mono_class_is_magic_float (mono_class_from_mono_type_internal (t)))
467 return FALSE;
468 return TRUE;
471 MonoType*
472 mini_native_type_replace_type (MonoType *type)
474 MonoClass *klass;
476 if (type->type != MONO_TYPE_VALUETYPE)
477 return type;
478 klass = type->data.klass;
480 if (mono_class_is_magic_int (klass))
481 return type->byref ? m_class_get_this_arg (mono_defaults.int_class) : mono_get_int_type ();
482 if (mono_class_is_magic_float (klass))
483 #if TARGET_SIZEOF_VOID_P == 8
484 return type->byref ? m_class_get_this_arg (mono_defaults.double_class) : m_class_get_byval_arg (mono_defaults.double_class);
485 #else
486 return type->byref ? m_class_get_this_arg (mono_defaults.single_class) : m_class_get_byval_arg (mono_defaults.single_class);
487 #endif
488 return type;