3 * intrinsics for variable sized int/floats
6 * Rodrigo Kumpera (kumpera@gmail.com)
9 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
28 short small_stack_type
;
40 #if TARGET_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
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
58 #define OP_PT_ADD_IMM OP_LADD_IMM
59 #define OP_PT_SUB_IMM OP_LSUB_IMM
61 #define OP_PT_STORE_FP_MEMBASE_REG OP_STORER8_MEMBASE_REG
63 #define OP_PCOMPARE OP_LCOMPARE
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
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
83 #define OP_PT_ADD_IMM OP_IADD_IMM
84 #define OP_PT_SUB_IMM OP_ISUB_IMM
86 #define OP_PT_STORE_FP_MEMBASE_REG OP_STORER4_MEMBASE_REG
88 #define OP_PCOMPARE OP_ICOMPARE
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
} },
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
} },
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_FCLT_UN
, OP_RCLT_UN
} },
116 { "op_LessThan", { OP_ICLT
, OP_ICLT_UN
, OP_FCLT
, OP_RCLT
} },
117 { "op_LessThanOrEqual", { OP_ICLE
, OP_ICLE_UN
, OP_FCGT_UN
, OP_RCGT_UN
} },
120 static const MagicTypeInfo type_info
[] = {
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
},
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
},
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 },
130 static inline gboolean
131 type_size (MonoCompile
*cfg
, MonoType
*type
)
133 if (type
->type
== MONO_TYPE_I4
|| type
->type
== MONO_TYPE_U4
)
135 else if (type
->type
== MONO_TYPE_I8
|| type
->type
== MONO_TYPE_U8
)
137 else if (type
->type
== MONO_TYPE_R4
&& !type
->byref
&& cfg
->r4fp
)
139 else if (type
->type
== MONO_TYPE_R8
&& !type
->byref
)
141 return TARGET_SIZEOF_VOID_P
;
146 static gboolean
is_int_type (MonoType
*t
);
147 static gboolean
is_float_type (MonoType
*t
);
150 emit_narrow (MonoCompile
*cfg
, const MagicTypeInfo
*info
, int sreg
)
154 MONO_INST_NEW (cfg
, ins
, info
->conv_8_to_4
);
156 if (info
->conv_8_to_4
== OP_FCONV_TO_R4
)
157 ins
->type
= cfg
->r4_stack_type
;
159 ins
->type
= info
->small_stack_type
;
160 ins
->dreg
= alloc_dreg (cfg
, ins
->type
);
161 MONO_ADD_INS (cfg
->cbb
, ins
);
162 return mono_decompose_opcode (cfg
, ins
);
166 emit_widen (MonoCompile
*cfg
, const MagicTypeInfo
*info
, int sreg
)
170 if (cfg
->r4fp
&& info
->conv_4_to_8
== OP_FCONV_TO_R8
)
171 MONO_INST_NEW (cfg
, ins
, OP_RCONV_TO_R8
);
173 MONO_INST_NEW (cfg
, ins
, info
->conv_4_to_8
);
175 ins
->type
= info
->big_stack_type
;
176 ins
->dreg
= alloc_dreg (cfg
, info
->big_stack_type
);
177 MONO_ADD_INS (cfg
->cbb
, ins
);
178 return mono_decompose_opcode (cfg
, ins
);
182 emit_intrinsics (MonoCompile
*cfg
, MonoMethod
*cmethod
, MonoMethodSignature
*fsig
, MonoInst
**args
, const MagicTypeInfo
*info
)
185 const char *name
= cmethod
->name
;
187 int type_index
, stack_type
;
189 if (info
->op_index
== 2 && cfg
->r4fp
&& TARGET_SIZEOF_VOID_P
== 4) {
191 stack_type
= STACK_R4
;
193 type_index
= info
->op_index
;
194 stack_type
= info
->stack_type
;
197 if (!strcmp ("op_Implicit", name
) || !strcmp ("op_Explicit", name
)) {
198 int source_size
= type_size (cfg
, fsig
->params
[0]);
199 int dest_size
= type_size (cfg
, fsig
->ret
);
201 switch (info
->big_stack_type
) {
203 if (!is_int_type (fsig
->params
[0]) || !is_int_type (fsig
->ret
))
207 if (!is_float_type (fsig
->params
[0]) || !is_float_type (fsig
->ret
))
211 g_assert_not_reached ();
215 if (source_size
== dest_size
)
219 if (source_size
< dest_size
)
220 return emit_widen (cfg
, info
, args
[0]->dreg
);
223 return emit_narrow (cfg
, info
, args
[0]->dreg
);
226 if (!strcmp (".ctor", name
)) {
227 gboolean is_ldaddr
= args
[0]->opcode
== OP_LDADDR
;
228 int arg0
= args
[1]->dreg
;
229 int arg_size
= type_size (cfg
, fsig
->params
[0]);
231 if (arg_size
> TARGET_SIZEOF_VOID_P
) //8 -> 4
232 arg0
= emit_narrow (cfg
, info
, arg0
)->dreg
;
233 else if (arg_size
< TARGET_SIZEOF_VOID_P
) //4 -> 8
234 arg0
= emit_widen (cfg
, info
, arg0
)->dreg
;
236 if (is_ldaddr
) { /*Eliminate LDADDR if it's initing a local var*/
237 int dreg
= ((MonoInst
*)args
[0]->inst_p0
)->dreg
;
238 NULLIFY_INS (args
[0]);
239 EMIT_NEW_UNALU (cfg
, ins
, info
->move
, dreg
, arg0
);
240 cfg
->has_indirection
= TRUE
;
242 EMIT_NEW_STORE_MEMBASE (cfg
, ins
, info
->store_op
, args
[0]->dreg
, 0, arg0
);
247 if (!strcmp ("op_Increment", name
) || !strcmp ("op_Decrement", name
)) {
248 gboolean inc
= !strcmp ("op_Increment", name
);
249 /* FIXME float inc is too complex to bother with*/
250 //this is broken with ints too
251 // if (!info->inc_op)
254 /* We have IR for inc/dec */
255 MONO_INST_NEW (cfg
, ins
, inc
? info
->inc_op
: info
->dec_op
);
256 ins
->dreg
= alloc_dreg (cfg
, info
->stack_type
);
257 ins
->sreg1
= args
[0]->dreg
;
259 ins
->type
= info
->stack_type
;
260 MONO_ADD_INS (cfg
->cbb
, ins
);
264 for (i
= 0; i
< sizeof (int_binop
) / sizeof (IntIntrisic
); ++i
) {
265 if (!strcmp (int_binop
[i
].op_name
, name
)) {
266 if (!int_binop
[i
].op_table
[info
->op_index
])
268 g_assert (int_binop
[i
].op_table
[type_index
]);
270 MONO_INST_NEW (cfg
, ins
, int_binop
[i
].op_table
[type_index
]);
271 ins
->dreg
= alloc_dreg (cfg
, stack_type
);
272 ins
->sreg1
= args
[0]->dreg
;
273 ins
->sreg2
= args
[1]->dreg
;
274 ins
->type
= stack_type
;
275 MONO_ADD_INS (cfg
->cbb
, ins
);
276 return mono_decompose_opcode (cfg
, ins
);
280 for (i
= 0; i
< sizeof (int_unnop
) / sizeof (IntIntrisic
); ++i
) {
281 if (!strcmp (int_unnop
[i
].op_name
, name
)) {
282 g_assert (int_unnop
[i
].op_table
[type_index
]);
284 MONO_INST_NEW (cfg
, ins
, int_unnop
[i
].op_table
[type_index
]);
285 ins
->dreg
= alloc_dreg (cfg
, stack_type
);
286 ins
->sreg1
= args
[0]->dreg
;
287 ins
->type
= stack_type
;
288 MONO_ADD_INS (cfg
->cbb
, ins
);
293 for (i
= 0; i
< sizeof (int_cmpop
) / sizeof (IntIntrisic
); ++i
) {
294 if (!strcmp (int_cmpop
[i
].op_name
, name
)) {
295 short op_cmp
= int_cmpop
[i
].op_table
[type_index
];
299 if (info
->compare_op
) {
300 MONO_INST_NEW (cfg
, ins
, info
->compare_op
);
302 ins
->sreg1
= args
[0]->dreg
;
303 ins
->sreg2
= args
[1]->dreg
;
304 MONO_ADD_INS (cfg
->cbb
, ins
);
306 MONO_INST_NEW (cfg
, ins
, op_cmp
);
307 ins
->dreg
= alloc_preg (cfg
);
308 ins
->type
= STACK_I4
;
309 MONO_ADD_INS (cfg
->cbb
, ins
);
311 MONO_INST_NEW (cfg
, ins
, op_cmp
);
312 guint32 fcmp_dreg
= 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 if (op_cmp
== OP_FCLT_UN
|| op_cmp
== OP_FCGT_UN
|| op_cmp
== OP_RCLT_UN
|| op_cmp
== OP_RCGT_UN
) {
317 /* we have to negate the result of this comparison:
318 * - op_GreaterThanOrEqual maps to NOT x OP_FCLT_UN / OP_RCLT_UN
319 * - op_LessThanOrEqual maps to NOT x OP_FCGT_UN / OP_RCGT_UN
321 * this matches generated bytecode by C# when doing the
322 * same operations on float/double. the `_UN` suffix says
323 * that if an operand is NaN, the result is true. If
324 * OP_FCGE/OP_FCLE is used, it is mapped to instructions
325 * on some architectures that don't detect NaN. For
326 * example, on arm64 the condition `eq` doesn't respect
327 * NaN results of a `fcmp` instruction.
329 MONO_INST_NEW (cfg
, ins
, OP_ICOMPARE_IMM
);
331 ins
->sreg1
= fcmp_dreg
;
333 MONO_ADD_INS (cfg
->cbb
, ins
);
335 MONO_INST_NEW (cfg
, ins
, OP_CEQ
);
336 ins
->dreg
= alloc_preg (cfg
);
337 ins
->type
= STACK_I4
;
338 MONO_ADD_INS (cfg
->cbb
, ins
);
351 mono_emit_native_types_intrinsics (MonoCompile
*cfg
, MonoMethod
*cmethod
, MonoMethodSignature
*fsig
, MonoInst
**args
)
353 if (mono_class_is_magic_int (cmethod
->klass
)) {
354 const char *class_name
= m_class_get_name (cmethod
->klass
);
355 if (!strcmp ("nint", class_name
))
356 return emit_intrinsics (cfg
, cmethod
, fsig
, args
, &type_info
[0]);
358 return emit_intrinsics (cfg
, cmethod
, fsig
, args
, &type_info
[1]);
359 } else if (mono_class_is_magic_float (cmethod
->klass
))
360 return emit_intrinsics (cfg
, cmethod
, fsig
, args
, &type_info
[2]);
365 #endif /* !DISABLE_JIT */
367 static inline gboolean
368 mono_class_is_magic_assembly (MonoClass
*klass
)
370 const char *aname
= m_class_get_image (klass
)->assembly_name
;
373 if (!strcmp ("Xamarin.iOS", aname
))
375 if (!strcmp ("Xamarin.Mac", aname
))
377 if (!strcmp ("Xamarin.WatchOS", aname
))
379 /* regression test suite */
380 if (!strcmp ("builtin-types", aname
))
382 if (!strcmp ("mini_tests", aname
))
388 mono_class_is_magic_int (MonoClass
*klass
)
390 static MonoClass
*magic_nint_class
;
391 static MonoClass
*magic_nuint_class
;
393 if (klass
== magic_nint_class
)
396 if (klass
== magic_nuint_class
)
399 if (magic_nint_class
&& magic_nuint_class
)
402 if (!mono_class_is_magic_assembly (klass
))
405 if (strcmp ("System", m_class_get_name_space (klass
)) != 0)
408 if (strcmp ("nint", m_class_get_name (klass
)) == 0) {
409 magic_nint_class
= klass
;
413 if (strcmp ("nuint", m_class_get_name (klass
)) == 0){
414 magic_nuint_class
= klass
;
421 mono_class_is_magic_float (MonoClass
*klass
)
423 static MonoClass
*magic_nfloat_class
;
425 if (klass
== magic_nfloat_class
)
428 if (magic_nfloat_class
)
431 if (!mono_class_is_magic_assembly (klass
))
434 if (strcmp ("System", m_class_get_name_space (klass
)) != 0)
437 if (strcmp ("nfloat", m_class_get_name (klass
)) == 0) {
438 magic_nfloat_class
= klass
;
440 /* Assert that we are using the matching assembly */
441 MonoClassField
*value_field
= mono_class_get_field_from_name_full (klass
, "v", NULL
);
442 g_assert (value_field
);
443 MonoType
*t
= mono_field_get_type (value_field
);
444 MonoType
*native
= mini_native_type_replace_type (m_class_get_byval_arg (klass
));
445 if (t
->type
!= native
->type
)
446 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
));
453 is_int_type (MonoType
*t
)
455 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
)))
461 is_float_type (MonoType
*t
)
463 if (t
->type
!= MONO_TYPE_R4
&& t
->type
!= MONO_TYPE_R8
&& !mono_class_is_magic_float (mono_class_from_mono_type (t
)))
469 mini_native_type_replace_type (MonoType
*type
)
473 if (type
->type
!= MONO_TYPE_VALUETYPE
)
475 klass
= type
->data
.klass
;
477 if (mono_class_is_magic_int (klass
))
478 return type
->byref
? m_class_get_this_arg (mono_defaults
.int_class
) : mono_get_int_type ();
479 if (mono_class_is_magic_float (klass
))
480 #if TARGET_SIZEOF_VOID_P == 8
481 return type
->byref
? m_class_get_this_arg (mono_defaults
.double_class
) : m_class_get_byval_arg (mono_defaults
.double_class
);
483 return type
->byref
? m_class_get_this_arg (mono_defaults
.single_class
) : m_class_get_byval_arg (mono_defaults
.single_class
);