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 MonoStackType big_stack_type
: 16;
29 MonoStackType small_stack_type
: 16;
30 MonoStackType stack_type
: 16;
33 short small_stack_type
;
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
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
99 mini_magic_type_size (MonoCompile
*cfg
, MonoType
*type
)
101 if (type
->type
== MONO_TYPE_I4
|| type
->type
== MONO_TYPE_U4
)
103 else if (type
->type
== MONO_TYPE_I8
|| type
->type
== MONO_TYPE_U8
)
105 else if (type
->type
== MONO_TYPE_R4
&& !type
->byref
&& (!cfg
|| cfg
->r4fp
))
107 else if (type
->type
== MONO_TYPE_R8
&& !type
->byref
)
109 return TARGET_SIZEOF_VOID_P
;
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
[] = {
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
},
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
},
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 },
152 emit_narrow (MonoCompile
*cfg
, const MagicTypeInfo
*info
, int sreg
)
156 MONO_INST_NEW (cfg
, ins
, info
->conv_8_to_4
);
158 if (info
->conv_8_to_4
== OP_FCONV_TO_R4
)
159 ins
->type
= cfg
->r4_stack_type
;
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
);
168 emit_widen (MonoCompile
*cfg
, const MagicTypeInfo
*info
, int sreg
)
172 if (cfg
->r4fp
&& info
->conv_4_to_8
== OP_FCONV_TO_R8
)
173 MONO_INST_NEW (cfg
, ins
, OP_RCONV_TO_R8
);
175 MONO_INST_NEW (cfg
, ins
, info
->conv_4_to_8
);
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
);
184 emit_intrinsics (MonoCompile
*cfg
, MonoMethod
*cmethod
, MonoMethodSignature
*fsig
, MonoInst
**args
, const MagicTypeInfo
*info
)
187 const char *name
= cmethod
->name
;
190 MonoStackType stack_type
;
192 if (info
->op_index
== 2 && cfg
->r4fp
&& TARGET_SIZEOF_VOID_P
== 4) {
194 stack_type
= STACK_R4
;
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
) {
206 if (!mini_magic_is_int_type (fsig
->params
[0]) || !mini_magic_is_int_type (fsig
->ret
))
210 if (!mini_magic_is_float_type (fsig
->params
[0]) || !mini_magic_is_float_type (fsig
->ret
))
214 g_assert_not_reached ();
218 if (source_size
== dest_size
)
222 if (source_size
< dest_size
)
223 return emit_widen (cfg
, info
, args
[0]->dreg
);
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
;
245 EMIT_NEW_STORE_MEMBASE (cfg
, ins
, info
->store_op
, args
[0]->dreg
, 0, arg0
);
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)
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
;
262 ins
->type
= info
->stack_type
;
263 MONO_ADD_INS (cfg
->cbb
, 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
])
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
);
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
];
302 if (info
->compare_op
) {
303 MONO_INST_NEW (cfg
, ins
, info
->compare_op
);
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
);
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
);
334 ins
->sreg1
= fcmp_dreg
;
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
);
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]);
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]);
368 #endif /* !DISABLE_JIT */
371 mono_class_is_magic_assembly (MonoClass
*klass
)
373 const char *aname
= m_class_get_image (klass
)->assembly_name
;
376 if (!strcmp ("Xamarin.iOS", aname
))
378 if (!strcmp ("Xamarin.Mac", aname
))
380 if (!strcmp ("Xamarin.WatchOS", aname
))
382 if (!strcmp ("Xamarin.MacCatalyst", aname
))
384 /* regression test suite */
385 if (!strcmp ("builtin-types", aname
))
387 if (!strcmp ("mini_tests", aname
))
393 mono_class_is_magic_int (MonoClass
*klass
)
395 static MonoClass
*magic_nint_class
;
396 static MonoClass
*magic_nuint_class
;
398 if (klass
== magic_nint_class
)
401 if (klass
== magic_nuint_class
)
404 if (magic_nint_class
&& magic_nuint_class
)
407 if (!mono_class_is_magic_assembly (klass
))
410 if (strcmp ("System", m_class_get_name_space (klass
)) != 0)
413 if (strcmp ("nint", m_class_get_name (klass
)) == 0) {
414 magic_nint_class
= klass
;
418 if (strcmp ("nuint", m_class_get_name (klass
)) == 0){
419 magic_nuint_class
= klass
;
426 mono_class_is_magic_float (MonoClass
*klass
)
428 static MonoClass
*magic_nfloat_class
;
430 if (klass
== magic_nfloat_class
)
433 if (magic_nfloat_class
)
436 if (!mono_class_is_magic_assembly (klass
))
439 if (strcmp ("System", m_class_get_name_space (klass
)) != 0 && strcmp ("ObjCRuntime", m_class_get_name_space (klass
)) != 0)
442 if (strcmp ("nfloat", m_class_get_name (klass
)) == 0) {
443 magic_nfloat_class
= klass
;
445 /* Assert that we are using the matching assembly */
446 MonoClassField
*value_field
= mono_class_get_field_from_name_full (klass
, "v", NULL
);
447 g_assert (value_field
);
448 MonoType
*t
= mono_field_get_type_internal (value_field
);
449 MonoType
*native
= mini_native_type_replace_type (m_class_get_byval_arg (klass
));
450 if (t
->type
!= native
->type
)
451 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
));
458 mini_magic_is_int_type (MonoType
*t
)
460 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
)))
466 mini_magic_is_float_type (MonoType
*t
)
468 if (t
->type
!= MONO_TYPE_R4
&& t
->type
!= MONO_TYPE_R8
&& !mono_class_is_magic_float (mono_class_from_mono_type_internal (t
)))
474 mini_native_type_replace_type (MonoType
*type
)
478 if (type
->type
!= MONO_TYPE_VALUETYPE
)
480 klass
= type
->data
.klass
;
482 if (mono_class_is_magic_int (klass
))
483 return type
->byref
? m_class_get_this_arg (mono_defaults
.int_class
) : mono_get_int_type ();
484 if (mono_class_is_magic_float (klass
))
485 #if TARGET_SIZEOF_VOID_P == 8
486 return type
->byref
? m_class_get_this_arg (mono_defaults
.double_class
) : m_class_get_byval_arg (mono_defaults
.double_class
);
488 return type
->byref
? m_class_get_this_arg (mono_defaults
.single_class
) : m_class_get_byval_arg (mono_defaults
.single_class
);