6 #include <mono/utils/mono-compiler.h>
12 #include "mini-runtime.h"
14 #include "jit-icalls.h"
15 #include "debugger-agent.h"
17 #include <mono/metadata/abi-details.h>
18 #include <mono/metadata/gc-internals.h>
19 #include <mono/metadata/monitor.h>
20 #include <mono/utils/mono-memory-model.h>
22 static GENERATE_GET_CLASS_WITH_CACHE (runtime_helpers
, "System.Runtime.CompilerServices", "RuntimeHelpers")
23 static GENERATE_TRY_GET_CLASS_WITH_CACHE (math
, "System", "Math")
25 /* optimize the simple GetGenericValueImpl/SetGenericValueImpl generic icalls */
27 emit_array_generic_access (MonoCompile
*cfg
, MonoMethodSignature
*fsig
, MonoInst
**args
, int is_set
)
29 MonoInst
*addr
, *store
, *load
;
30 MonoClass
*eklass
= mono_class_from_mono_type_internal (fsig
->params
[2]);
32 /* the bounds check is already done by the callers */
33 addr
= mini_emit_ldelema_1_ins (cfg
, eklass
, args
[0], args
[1], FALSE
);
34 MonoType
*etype
= m_class_get_byval_arg (eklass
);
36 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg
, load
, etype
, args
[2]->dreg
, 0);
37 EMIT_NEW_STORE_MEMBASE_TYPE (cfg
, store
, etype
, addr
->dreg
, 0, load
->dreg
);
38 if (mini_type_is_reference (etype
))
39 mini_emit_write_barrier (cfg
, addr
, load
);
41 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg
, load
, etype
, addr
->dreg
, 0);
42 EMIT_NEW_STORE_MEMBASE_TYPE (cfg
, store
, etype
, args
[2]->dreg
, 0, load
->dreg
);
48 mono_type_is_native_blittable (MonoType
*t
)
50 if (MONO_TYPE_IS_REFERENCE (t
))
53 if (MONO_TYPE_IS_PRIMITIVE_SCALAR (t
))
56 MonoClass
*klass
= mono_class_from_mono_type_internal (t
);
58 //MonoClass::blitable depends on mono_class_setup_fields being done.
59 mono_class_setup_fields (klass
);
60 if (!m_class_is_blittable (klass
))
63 // If the native marshal size is different we can't convert PtrToStructure to a type load
64 if (mono_class_native_size (klass
, NULL
) != mono_class_value_size (klass
, NULL
))
71 mini_emit_inst_for_ctor (MonoCompile
*cfg
, MonoMethod
*cmethod
, MonoMethodSignature
*fsig
, MonoInst
**args
)
73 const char* cmethod_klass_name_space
= m_class_get_name_space (cmethod
->klass
);
74 const char* cmethod_klass_name
= m_class_get_name (cmethod
->klass
);
75 MonoImage
*cmethod_klass_image
= m_class_get_image (cmethod
->klass
);
76 gboolean in_corlib
= cmethod_klass_image
== mono_defaults
.corlib
;
79 /* Required intrinsics are always used even with -O=-intrins */
81 !strcmp (cmethod_klass_name_space
, "System") &&
82 !strcmp (cmethod_klass_name
, "ByReference`1")) {
83 /* public ByReference(ref T value) */
84 g_assert (fsig
->hasthis
&& fsig
->param_count
== 1);
85 EMIT_NEW_STORE_MEMBASE (cfg
, ins
, OP_STORE_MEMBASE_REG
, args
[0]->dreg
, 0, args
[1]->dreg
);
89 ins
= mono_emit_native_types_intrinsics (cfg
, cmethod
, fsig
, args
);
93 if (!(cfg
->opt
& MONO_OPT_INTRINS
))
96 #ifdef MONO_ARCH_SIMD_INTRINSICS
97 if (cfg
->opt
& MONO_OPT_SIMD
) {
98 ins
= mono_emit_simd_intrinsics (cfg
, cmethod
, fsig
, args
);
108 llvm_emit_inst_for_method (MonoCompile
*cfg
, MonoMethod
*cmethod
, MonoMethodSignature
*fsig
, MonoInst
**args
, gboolean in_corlib
)
110 MonoInst
*ins
= NULL
;
113 if (in_corlib
&& !strcmp (m_class_get_name (cmethod
->klass
), "MathF") && fsig
->param_count
&& fsig
->params
[0]->type
== MONO_TYPE_R4
&& cfg
->r4fp
) {
114 if (!strcmp (cmethod
->name
, "Sin"))
116 else if (!strcmp (cmethod
->name
, "Cos"))
118 else if (!strcmp (cmethod
->name
, "Abs"))
120 else if (!strcmp (cmethod
->name
, "Sqrt"))
122 else if (!strcmp (cmethod
->name
, "Floor"))
124 else if (!strcmp (cmethod
->name
, "Ceiling"))
126 else if (!strcmp (cmethod
->name
, "FusedMultiplyAdd"))
128 else if (!strcmp (cmethod
->name
, "Pow"))
130 if (opcode
&& fsig
->param_count
> 0) {
131 MONO_INST_NEW (cfg
, ins
, opcode
);
132 ins
->type
= STACK_R8
;
133 ins
->dreg
= mono_alloc_dreg (cfg
, (MonoStackType
)ins
->type
);
134 ins
->sreg1
= args
[0]->dreg
;
135 if (fsig
->param_count
> 1) { // POW
136 ins
->sreg2
= args
[1]->dreg
;
138 if (fsig
->param_count
> 2) { // FMA
139 ins
->sreg3
= args
[2]->dreg
;
141 g_assert (fsig
->param_count
<= 3);
142 MONO_ADD_INS (cfg
->cbb
, ins
);
145 /* The LLVM backend supports these intrinsics */
146 if (cmethod
->klass
== mono_class_try_get_math_class ()) {
147 if (strcmp (cmethod
->name
, "Sin") == 0) {
149 } else if (strcmp (cmethod
->name
, "Cos") == 0) {
151 } else if (strcmp (cmethod
->name
, "Sqrt") == 0) {
153 } else if (strcmp (cmethod
->name
, "Floor") == 0) {
155 } else if (strcmp (cmethod
->name
, "Ceiling") == 0) {
157 } else if (strcmp (cmethod
->name
, "FusedMultiplyAdd") == 0) {
159 } else if (strcmp (cmethod
->name
, "Abs") == 0 && fsig
->params
[0]->type
== MONO_TYPE_R8
) {
162 // Max and Min can only be optimized in fast math mode
163 else if (strcmp (cmethod
->name
, "Max") == 0 && mono_use_fast_math
&& fsig
->params
[0]->type
== MONO_TYPE_R8
) {
165 } else if (strcmp (cmethod
->name
, "Min") == 0 && mono_use_fast_math
&& fsig
->params
[0]->type
== MONO_TYPE_R8
) {
168 // Math.Max/Min also have float overloads (MathF.Max/Min just redirect to them)
169 else if (strcmp (cmethod
->name
, "Max") == 0 && mono_use_fast_math
&& fsig
->params
[0]->type
== MONO_TYPE_R4
) {
171 } else if (strcmp (cmethod
->name
, "Min") == 0 && mono_use_fast_math
&& fsig
->params
[0]->type
== MONO_TYPE_R4
) {
173 } else if (strcmp (cmethod
->name
, "Pow") == 0) {
177 if (opcode
&& fsig
->param_count
> 0) {
178 MONO_INST_NEW (cfg
, ins
, opcode
);
179 ins
->type
= STACK_R8
;
180 ins
->dreg
= mono_alloc_dreg (cfg
, (MonoStackType
)ins
->type
);
181 ins
->sreg1
= args
[0]->dreg
;
182 if (fsig
->param_count
> 1) { // POW, MIN, MAX
183 ins
->sreg2
= args
[1]->dreg
;
185 if (fsig
->param_count
> 2) { // FMA
186 ins
->sreg3
= args
[2]->dreg
;
188 g_assert (fsig
->param_count
<= 3);
189 MONO_ADD_INS (cfg
->cbb
, ins
);
193 if (cfg
->opt
& MONO_OPT_CMOV
) {
194 if (strcmp (cmethod
->name
, "Min") == 0) {
195 if (fsig
->params
[0]->type
== MONO_TYPE_I4
)
197 if (fsig
->params
[0]->type
== MONO_TYPE_U4
)
199 else if (fsig
->params
[0]->type
== MONO_TYPE_I8
)
201 else if (fsig
->params
[0]->type
== MONO_TYPE_U8
)
203 } else if (strcmp (cmethod
->name
, "Max") == 0) {
204 if (fsig
->params
[0]->type
== MONO_TYPE_I4
)
206 if (fsig
->params
[0]->type
== MONO_TYPE_U4
)
208 else if (fsig
->params
[0]->type
== MONO_TYPE_I8
)
210 else if (fsig
->params
[0]->type
== MONO_TYPE_U8
)
215 if (opcode
&& fsig
->param_count
== 2) {
216 MONO_INST_NEW (cfg
, ins
, opcode
);
217 ins
->type
= fsig
->params
[0]->type
== MONO_TYPE_I4
? STACK_I4
: STACK_I8
;
218 ins
->dreg
= mono_alloc_dreg (cfg
, (MonoStackType
)ins
->type
);
219 ins
->sreg1
= args
[0]->dreg
;
220 ins
->sreg2
= args
[1]->dreg
;
221 MONO_ADD_INS (cfg
->cbb
, ins
);
225 if (in_corlib
&& !strcmp (m_class_get_name (cmethod
->klass
), "Buffer")) {
226 if (!strcmp (cmethod
->name
, "Memmove") && fsig
->param_count
== 3 && fsig
->params
[0]->type
== MONO_TYPE_PTR
&& fsig
->params
[1]->type
== MONO_TYPE_PTR
) {
228 MONO_INST_NEW (cfg
, ins
, opcode
);
229 ins
->sreg1
= args
[0]->dreg
; // i1* dst
230 ins
->sreg2
= args
[1]->dreg
; // i1* src
231 ins
->sreg3
= args
[2]->dreg
; // i32/i64 len
232 MONO_ADD_INS (cfg
->cbb
, ins
);
240 emit_span_intrinsics (MonoCompile
*cfg
, MonoMethod
*cmethod
, MonoMethodSignature
*fsig
, MonoInst
**args
)
244 MonoClassField
*ptr_field
= mono_class_get_field_from_name_full (cmethod
->klass
, "_pointer", NULL
);
246 /* Portable Span<T> */
249 if (!strcmp (cmethod
->name
, "get_Item")) {
250 MonoClassField
*length_field
= mono_class_get_field_from_name_full (cmethod
->klass
, "_length", NULL
);
252 g_assert (length_field
);
254 MonoGenericClass
*gclass
= mono_class_get_generic_class (cmethod
->klass
);
255 MonoClass
*param_class
= mono_class_from_mono_type_internal (gclass
->context
.class_inst
->type_argv
[0]);
257 if (mini_is_gsharedvt_variable_klass (param_class
))
260 int span_reg
= args
[0]->dreg
;
261 /* Load _pointer.Value */
262 int base_reg
= alloc_preg (cfg
);
263 EMIT_NEW_LOAD_MEMBASE (cfg
, ins
, OP_LOAD_MEMBASE
, base_reg
, span_reg
, ptr_field
->offset
- MONO_ABI_SIZEOF (MonoObject
));
264 /* Similar to mini_emit_ldelema_1_ins () */
265 int size
= mono_class_array_element_size (param_class
);
267 int index_reg
= mini_emit_sext_index_reg (cfg
, args
[1]);
269 MONO_EMIT_BOUNDS_CHECK_OFFSET(cfg
, span_reg
, length_field
->offset
- MONO_ABI_SIZEOF (MonoObject
), index_reg
);
271 // FIXME: Sign extend index ?
273 int mult_reg
= alloc_preg (cfg
);
274 int add_reg
= alloc_preg (cfg
);
276 MONO_EMIT_NEW_BIALU_IMM (cfg
, OP_MUL_IMM
, mult_reg
, index_reg
, size
);
277 EMIT_NEW_BIALU (cfg
, ins
, OP_PADD
, add_reg
, base_reg
, mult_reg
);
278 ins
->klass
= param_class
;
279 ins
->type
= STACK_MP
;
282 } else if (!strcmp (cmethod
->name
, "get_Length")) {
283 MonoClassField
*length_field
= mono_class_get_field_from_name_full (cmethod
->klass
, "_length", NULL
);
284 g_assert (length_field
);
287 * FIXME: This doesn't work with abcrem, since the src is a unique LDADDR not
288 * the same array object.
290 MONO_INST_NEW (cfg
, ins
, OP_LDLEN
);
291 ins
->dreg
= alloc_preg (cfg
);
292 ins
->sreg1
= args
[0]->dreg
;
293 ins
->inst_imm
= length_field
->offset
- MONO_ABI_SIZEOF (MonoObject
);
294 ins
->type
= STACK_I4
;
295 MONO_ADD_INS (cfg
->cbb
, ins
);
297 cfg
->flags
|= MONO_CFG_NEEDS_DECOMPOSE
;
298 cfg
->cbb
->needs_decompose
= TRUE
;
307 emit_unsafe_intrinsics (MonoCompile
*cfg
, MonoMethod
*cmethod
, MonoMethodSignature
*fsig
, MonoInst
**args
)
311 MonoGenericContext
*ctx
= mono_method_get_context (cmethod
);
314 if (!strcmp (cmethod
->name
, "As")) {
316 g_assert (ctx
->method_inst
);
318 t
= ctx
->method_inst
->type_argv
[0];
319 if (mini_is_gsharedvt_variable_type (t
))
321 if (ctx
->method_inst
->type_argc
== 2) {
322 dreg
= alloc_preg (cfg
);
323 EMIT_NEW_UNALU (cfg
, ins
, OP_MOVE
, dreg
, args
[0]->dreg
);
324 ins
->type
= STACK_OBJ
;
325 ins
->klass
= mono_get_object_class ();
327 } else if (ctx
->method_inst
->type_argc
== 1) {
328 // Casts the given object to the specified type, performs no dynamic type checking.
329 g_assert (fsig
->param_count
== 1);
330 g_assert (fsig
->params
[0]->type
== MONO_TYPE_OBJECT
);
331 dreg
= alloc_preg (cfg
);
332 EMIT_NEW_UNALU (cfg
, ins
, OP_MOVE
, dreg
, args
[0]->dreg
);
333 ins
->type
= STACK_OBJ
;
334 ins
->klass
= mono_class_from_mono_type_internal (ctx
->method_inst
->type_argv
[0]);
337 } else if (!strcmp (cmethod
->name
, "AsPointer")) {
339 g_assert (ctx
->method_inst
);
340 g_assert (ctx
->method_inst
->type_argc
== 1);
341 g_assert (fsig
->param_count
== 1);
343 dreg
= alloc_preg (cfg
);
344 EMIT_NEW_UNALU (cfg
, ins
, OP_MOVE
, dreg
, args
[0]->dreg
);
345 ins
->type
= STACK_PTR
;
347 } else if (!strcmp (cmethod
->name
, "AsRef")) {
349 g_assert (ctx
->method_inst
);
350 g_assert (ctx
->method_inst
->type_argc
== 1);
351 g_assert (fsig
->param_count
== 1);
353 dreg
= alloc_preg (cfg
);
354 EMIT_NEW_UNALU (cfg
, ins
, OP_MOVE
, dreg
, args
[0]->dreg
);
355 ins
->type
= STACK_OBJ
;
356 ins
->klass
= mono_get_object_class ();
358 } else if (!strcmp (cmethod
->name
, "AreSame")) {
360 g_assert (ctx
->method_inst
);
361 g_assert (ctx
->method_inst
->type_argc
== 1);
362 g_assert (fsig
->param_count
== 2);
364 dreg
= alloc_ireg (cfg
);
365 EMIT_NEW_BIALU (cfg
, ins
, OP_COMPARE
, -1, args
[0]->dreg
, args
[1]->dreg
);
366 EMIT_NEW_UNALU (cfg
, ins
, OP_PCEQ
, dreg
, -1);
368 } else if (!strcmp (cmethod
->name
, "IsAddressLessThan")) {
370 g_assert (ctx
->method_inst
);
371 g_assert (ctx
->method_inst
->type_argc
== 1);
372 g_assert (fsig
->param_count
== 2);
374 dreg
= alloc_ireg (cfg
);
375 EMIT_NEW_BIALU (cfg
, ins
, OP_COMPARE
, -1, args
[0]->dreg
, args
[1]->dreg
);
376 EMIT_NEW_UNALU (cfg
, ins
, OP_PCLT_UN
, dreg
, -1);
378 } else if (!strcmp (cmethod
->name
, "IsAddressGreaterThan")) {
380 g_assert (ctx
->method_inst
);
381 g_assert (ctx
->method_inst
->type_argc
== 1);
382 g_assert (fsig
->param_count
== 2);
384 dreg
= alloc_ireg (cfg
);
385 EMIT_NEW_BIALU (cfg
, ins
, OP_COMPARE
, -1, args
[0]->dreg
, args
[1]->dreg
);
386 EMIT_NEW_UNALU (cfg
, ins
, OP_PCGT_UN
, dreg
, -1);
388 } else if (!strcmp (cmethod
->name
, "Add")) {
390 g_assert (ctx
->method_inst
);
391 g_assert (ctx
->method_inst
->type_argc
== 1);
392 g_assert (fsig
->param_count
== 2);
394 int mul_reg
= alloc_preg (cfg
);
396 t
= ctx
->method_inst
->type_argv
[0];
398 if (mini_is_gsharedvt_variable_type (t
)) {
399 esize_ins
= mini_emit_get_gsharedvt_info_klass (cfg
, mono_class_from_mono_type_internal (t
), MONO_RGCTX_INFO_CLASS_SIZEOF
);
400 if (SIZEOF_REGISTER
== 8)
401 MONO_EMIT_NEW_UNALU (cfg
, OP_SEXT_I4
, esize_ins
->dreg
, esize_ins
->dreg
);
403 t
= mini_type_get_underlying_type (t
);
404 int esize
= mono_class_array_element_size (mono_class_from_mono_type_internal (t
));
405 EMIT_NEW_ICONST (cfg
, esize_ins
, esize
);
407 esize_ins
->type
= STACK_I4
;
409 EMIT_NEW_BIALU (cfg
, ins
, OP_PMUL
, mul_reg
, args
[1]->dreg
, esize_ins
->dreg
);
410 ins
->type
= STACK_PTR
;
412 dreg
= alloc_preg (cfg
);
413 EMIT_NEW_BIALU (cfg
, ins
, OP_PADD
, dreg
, args
[0]->dreg
, mul_reg
);
414 ins
->type
= STACK_PTR
;
416 } else if (!strcmp (cmethod
->name
, "AddByteOffset")) {
418 g_assert (ctx
->method_inst
);
419 g_assert (ctx
->method_inst
->type_argc
== 1);
420 g_assert (fsig
->param_count
== 2);
422 if (fsig
->params
[1]->type
== MONO_TYPE_I
) {
423 int dreg
= alloc_preg (cfg
);
424 EMIT_NEW_BIALU (cfg
, ins
, OP_PADD
, dreg
, args
[0]->dreg
, args
[1]->dreg
);
425 ins
->type
= STACK_PTR
;
427 } else if (fsig
->params
[1]->type
== MONO_TYPE_U8
) {
428 int sreg
= args
[1]->dreg
;
429 if (SIZEOF_REGISTER
== 4) {
430 sreg
= alloc_ireg (cfg
);
431 EMIT_NEW_UNALU (cfg
, ins
, OP_LCONV_TO_U4
, sreg
, args
[1]->dreg
);
433 int dreg
= alloc_preg (cfg
);
434 EMIT_NEW_BIALU (cfg
, ins
, OP_PADD
, dreg
, args
[0]->dreg
, sreg
);
435 ins
->type
= STACK_PTR
;
438 } else if (!strcmp (cmethod
->name
, "SizeOf")) {
440 g_assert (ctx
->method_inst
);
441 g_assert (ctx
->method_inst
->type_argc
== 1);
442 g_assert (fsig
->param_count
== 0);
444 t
= ctx
->method_inst
->type_argv
[0];
445 if (mini_is_gsharedvt_variable_type (t
)) {
446 ins
= mini_emit_get_gsharedvt_info_klass (cfg
, mono_class_from_mono_type_internal (t
), MONO_RGCTX_INFO_CLASS_SIZEOF
);
448 int esize
= mono_type_size (t
, &align
);
449 EMIT_NEW_ICONST (cfg
, ins
, esize
);
451 ins
->type
= STACK_I4
;
453 } else if (!strcmp (cmethod
->name
, "ReadUnaligned")) {
455 g_assert (ctx
->method_inst
);
456 g_assert (ctx
->method_inst
->type_argc
== 1);
457 g_assert (fsig
->param_count
== 1);
459 t
= ctx
->method_inst
->type_argv
[0];
460 t
= mini_get_underlying_type (t
);
461 if (MONO_TYPE_IS_PRIMITIVE (t
) && t
->type
!= MONO_TYPE_R4
&& t
->type
!= MONO_TYPE_R8
) {
462 dreg
= alloc_ireg (cfg
);
463 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg
, ins
, t
, args
[0]->dreg
, 0);
464 ins
->type
= STACK_I4
;
465 ins
->flags
|= MONO_INST_UNALIGNED
;
468 } else if (!strcmp (cmethod
->name
, "WriteUnaligned")) {
470 g_assert (ctx
->method_inst
);
471 g_assert (ctx
->method_inst
->type_argc
== 1);
472 g_assert (fsig
->param_count
== 2);
474 t
= ctx
->method_inst
->type_argv
[0];
475 t
= mini_get_underlying_type (t
);
476 if (MONO_TYPE_IS_PRIMITIVE (t
) && t
->type
!= MONO_TYPE_R4
&& t
->type
!= MONO_TYPE_R8
) {
477 dreg
= alloc_ireg (cfg
);
478 EMIT_NEW_STORE_MEMBASE_TYPE (cfg
, ins
, t
, args
[0]->dreg
, 0, args
[1]->dreg
);
479 ins
->flags
|= MONO_INST_UNALIGNED
;
482 } else if (!strcmp (cmethod
->name
, "ByteOffset")) {
484 g_assert (ctx
->method_inst
);
485 g_assert (ctx
->method_inst
->type_argc
== 1);
486 g_assert (fsig
->param_count
== 2);
488 int dreg
= alloc_preg (cfg
);
489 EMIT_NEW_BIALU (cfg
, ins
, OP_PSUB
, dreg
, args
[1]->dreg
, args
[0]->dreg
);
490 ins
->type
= STACK_PTR
;
498 emit_jit_helpers_intrinsics (MonoCompile
*cfg
, MonoMethod
*cmethod
, MonoMethodSignature
*fsig
, MonoInst
**args
)
502 MonoGenericContext
*ctx
= mono_method_get_context (cmethod
);
505 if (!strcmp (cmethod
->name
, "EnumEquals") || !strcmp (cmethod
->name
, "EnumCompareTo")) {
507 g_assert (ctx
->method_inst
);
508 g_assert (ctx
->method_inst
->type_argc
== 1);
509 g_assert (fsig
->param_count
== 2);
511 t
= ctx
->method_inst
->type_argv
[0];
512 t
= mini_get_underlying_type (t
);
513 if (mini_is_gsharedvt_variable_type (t
))
516 gboolean is_i8
= (t
->type
== MONO_TYPE_I8
|| t
->type
== MONO_TYPE_U8
);
517 gboolean is_unsigned
= (t
->type
== MONO_TYPE_U1
|| t
->type
== MONO_TYPE_U2
|| t
->type
== MONO_TYPE_U4
|| t
->type
== MONO_TYPE_U8
|| t
->type
== MONO_TYPE_U
);
518 int cmp_op
, ceq_op
, cgt_op
, clt_op
;
521 cmp_op
= OP_LCOMPARE
;
523 cgt_op
= is_unsigned
? OP_LCGT_UN
: OP_LCGT
;
524 clt_op
= is_unsigned
? OP_LCLT_UN
: OP_LCLT
;
526 cmp_op
= OP_ICOMPARE
;
528 cgt_op
= is_unsigned
? OP_ICGT_UN
: OP_ICGT
;
529 clt_op
= is_unsigned
? OP_ICLT_UN
: OP_ICLT
;
532 if (!strcmp (cmethod
->name
, "EnumEquals")) {
533 dreg
= alloc_ireg (cfg
);
534 EMIT_NEW_BIALU (cfg
, ins
, cmp_op
, -1, args
[0]->dreg
, args
[1]->dreg
);
535 EMIT_NEW_UNALU (cfg
, ins
, ceq_op
, dreg
, -1);
537 // Use the branchless code (a > b) - (a < b)
540 reg1
= alloc_ireg (cfg
);
541 reg2
= alloc_ireg (cfg
);
542 dreg
= alloc_ireg (cfg
);
544 EMIT_NEW_BIALU (cfg
, ins
, cmp_op
, -1, args
[0]->dreg
, args
[1]->dreg
);
545 EMIT_NEW_UNALU (cfg
, ins
, cgt_op
, reg1
, -1);
546 EMIT_NEW_BIALU (cfg
, ins
, cmp_op
, -1, args
[0]->dreg
, args
[1]->dreg
);
547 EMIT_NEW_UNALU (cfg
, ins
, clt_op
, reg2
, -1);
548 EMIT_NEW_BIALU (cfg
, ins
, OP_ISUB
, dreg
, reg1
, reg2
);
557 mini_emit_inst_for_method (MonoCompile
*cfg
, MonoMethod
*cmethod
, MonoMethodSignature
*fsig
, MonoInst
**args
)
559 MonoInst
*ins
= NULL
;
560 MonoClass
*runtime_helpers_class
= mono_class_get_runtime_helpers_class ();
562 const char* cmethod_klass_name_space
= m_class_get_name_space (cmethod
->klass
);
563 const char* cmethod_klass_name
= m_class_get_name (cmethod
->klass
);
564 MonoImage
*cmethod_klass_image
= m_class_get_image (cmethod
->klass
);
565 gboolean in_corlib
= cmethod_klass_image
== mono_defaults
.corlib
;
567 /* Required intrinsics are always used even with -O=-intrins */
569 !strcmp (cmethod_klass_name_space
, "System") &&
570 !strcmp (cmethod_klass_name
, "ByReference`1") &&
571 !strcmp (cmethod
->name
, "get_Value")) {
572 g_assert (fsig
->hasthis
&& fsig
->param_count
== 0);
573 int dreg
= alloc_preg (cfg
);
574 EMIT_NEW_LOAD_MEMBASE (cfg
, ins
, OP_LOAD_MEMBASE
, dreg
, args
[0]->dreg
, 0);
576 } else if (in_corlib
&& cmethod
->klass
== mono_defaults
.object_class
) {
577 if (!strcmp (cmethod
->name
, "GetRawData")) {
578 int dreg
= alloc_preg (cfg
);
579 EMIT_NEW_BIALU_IMM (cfg
, ins
, OP_PADD_IMM
, dreg
, args
[0]->dreg
, MONO_ABI_SIZEOF (MonoObject
));
584 if (!(cfg
->opt
& MONO_OPT_INTRINS
))
587 if (cmethod
->klass
== mono_defaults
.string_class
) {
588 if (strcmp (cmethod
->name
, "get_Chars") == 0 && fsig
->param_count
+ fsig
->hasthis
== 2) {
589 int dreg
= alloc_ireg (cfg
);
590 int index_reg
= alloc_preg (cfg
);
591 int add_reg
= alloc_preg (cfg
);
593 #if SIZEOF_REGISTER == 8
594 if (COMPILE_LLVM (cfg
)) {
595 MONO_EMIT_NEW_UNALU (cfg
, OP_ZEXT_I4
, index_reg
, args
[1]->dreg
);
597 /* The array reg is 64 bits but the index reg is only 32 */
598 MONO_EMIT_NEW_UNALU (cfg
, OP_SEXT_I4
, index_reg
, args
[1]->dreg
);
601 index_reg
= args
[1]->dreg
;
603 MONO_EMIT_BOUNDS_CHECK (cfg
, args
[0]->dreg
, MonoString
, length
, index_reg
);
605 #if defined(TARGET_X86) || defined(TARGET_AMD64)
606 EMIT_NEW_X86_LEA (cfg
, ins
, args
[0]->dreg
, index_reg
, 1, MONO_STRUCT_OFFSET (MonoString
, chars
));
608 EMIT_NEW_LOAD_MEMBASE (cfg
, ins
, OP_LOADU2_MEMBASE
, dreg
,
611 int mult_reg
= alloc_preg (cfg
);
612 MONO_EMIT_NEW_BIALU_IMM (cfg
, OP_SHL_IMM
, mult_reg
, index_reg
, 1);
613 MONO_EMIT_NEW_BIALU (cfg
, OP_PADD
, add_reg
, mult_reg
, args
[0]->dreg
);
614 EMIT_NEW_LOAD_MEMBASE (cfg
, ins
, OP_LOADU2_MEMBASE
, dreg
,
615 add_reg
, MONO_STRUCT_OFFSET (MonoString
, chars
));
617 mini_type_from_op (cfg
, ins
, NULL
, NULL
);
619 } else if (strcmp (cmethod
->name
, "get_Length") == 0 && fsig
->param_count
+ fsig
->hasthis
== 1) {
620 int dreg
= alloc_ireg (cfg
);
621 /* Decompose later to allow more optimizations */
622 EMIT_NEW_UNALU (cfg
, ins
, OP_STRLEN
, dreg
, args
[0]->dreg
);
623 ins
->type
= STACK_I4
;
624 ins
->flags
|= MONO_INST_FAULT
;
625 cfg
->cbb
->needs_decompose
= TRUE
;
626 cfg
->flags
|= MONO_CFG_NEEDS_DECOMPOSE
;
631 } else if (cmethod
->klass
== mono_defaults
.object_class
) {
632 if (strcmp (cmethod
->name
, "GetType") == 0 && fsig
->param_count
+ fsig
->hasthis
== 1) {
633 int dreg
= alloc_ireg_ref (cfg
);
634 int vt_reg
= alloc_preg (cfg
);
635 MONO_EMIT_NEW_LOAD_MEMBASE_FAULT (cfg
, vt_reg
, args
[0]->dreg
, MONO_STRUCT_OFFSET (MonoObject
, vtable
));
636 EMIT_NEW_LOAD_MEMBASE (cfg
, ins
, OP_LOAD_MEMBASE
, dreg
, vt_reg
, MONO_STRUCT_OFFSET (MonoVTable
, type
));
637 mini_type_from_op (cfg
, ins
, NULL
, NULL
);
640 } else if (!cfg
->backend
->emulate_mul_div
&& strcmp (cmethod
->name
, "InternalGetHashCode") == 0 && fsig
->param_count
== 1 && !mono_gc_is_moving ()) {
641 int dreg
= alloc_ireg (cfg
);
642 int t1
= alloc_ireg (cfg
);
644 MONO_EMIT_NEW_BIALU_IMM (cfg
, OP_SHL_IMM
, t1
, args
[0]->dreg
, 3);
645 EMIT_NEW_BIALU_IMM (cfg
, ins
, OP_MUL_IMM
, dreg
, t1
, 2654435761u);
646 ins
->type
= STACK_I4
;
649 } else if (strcmp (cmethod
->name
, ".ctor") == 0 && fsig
->param_count
== 0) {
650 MONO_INST_NEW (cfg
, ins
, OP_NOP
);
651 MONO_ADD_INS (cfg
->cbb
, ins
);
655 } else if (cmethod
->klass
== mono_defaults
.array_class
) {
656 if (strcmp (cmethod
->name
, "GetGenericValueImpl") == 0 && fsig
->param_count
+ fsig
->hasthis
== 3 && !cfg
->gsharedvt
)
657 return emit_array_generic_access (cfg
, fsig
, args
, FALSE
);
658 else if (strcmp (cmethod
->name
, "SetGenericValueImpl") == 0 && fsig
->param_count
+ fsig
->hasthis
== 3 && !cfg
->gsharedvt
)
659 return emit_array_generic_access (cfg
, fsig
, args
, TRUE
);
660 else if (!strcmp (cmethod
->name
, "GetRawSzArrayData")) {
661 int dreg
= alloc_preg (cfg
);
662 EMIT_NEW_BIALU_IMM (cfg
, ins
, OP_PADD_IMM
, dreg
, args
[0]->dreg
, MONO_STRUCT_OFFSET (MonoArray
, vector
));
666 #ifndef MONO_BIG_ARRAYS
668 * This is an inline version of GetLength/GetLowerBound(0) used frequently in
671 else if (((strcmp (cmethod
->name
, "GetLength") == 0 && fsig
->param_count
+ fsig
->hasthis
== 2) ||
672 (strcmp (cmethod
->name
, "GetLowerBound") == 0 && fsig
->param_count
+ fsig
->hasthis
== 2)) &&
673 args
[1]->opcode
== OP_ICONST
&& args
[1]->inst_c0
== 0) {
674 int dreg
= alloc_ireg (cfg
);
675 int bounds_reg
= alloc_ireg_mp (cfg
);
676 MonoBasicBlock
*end_bb
, *szarray_bb
;
677 gboolean get_length
= strcmp (cmethod
->name
, "GetLength") == 0;
679 NEW_BBLOCK (cfg
, end_bb
);
680 NEW_BBLOCK (cfg
, szarray_bb
);
682 EMIT_NEW_LOAD_MEMBASE_FAULT (cfg
, ins
, OP_LOAD_MEMBASE
, bounds_reg
,
683 args
[0]->dreg
, MONO_STRUCT_OFFSET (MonoArray
, bounds
));
684 MONO_EMIT_NEW_BIALU_IMM (cfg
, OP_COMPARE_IMM
, -1, bounds_reg
, 0);
685 MONO_EMIT_NEW_BRANCH_BLOCK (cfg
, OP_IBEQ
, szarray_bb
);
686 /* Non-szarray case */
688 EMIT_NEW_LOAD_MEMBASE (cfg
, ins
, OP_LOADI4_MEMBASE
, dreg
,
689 bounds_reg
, MONO_STRUCT_OFFSET (MonoArrayBounds
, length
));
691 EMIT_NEW_LOAD_MEMBASE (cfg
, ins
, OP_LOADI4_MEMBASE
, dreg
,
692 bounds_reg
, MONO_STRUCT_OFFSET (MonoArrayBounds
, lower_bound
));
693 MONO_EMIT_NEW_BRANCH_BLOCK (cfg
, OP_BR
, end_bb
);
694 MONO_START_BB (cfg
, szarray_bb
);
697 EMIT_NEW_LOAD_MEMBASE (cfg
, ins
, OP_LOADI4_MEMBASE
, dreg
,
698 args
[0]->dreg
, MONO_STRUCT_OFFSET (MonoArray
, max_length
));
700 MONO_EMIT_NEW_ICONST (cfg
, dreg
, 0);
701 MONO_START_BB (cfg
, end_bb
);
703 EMIT_NEW_UNALU (cfg
, ins
, OP_MOVE
, dreg
, dreg
);
704 ins
->type
= STACK_I4
;
710 if (cmethod
->name
[0] != 'g')
713 if (strcmp (cmethod
->name
, "get_Rank") == 0 && fsig
->param_count
+ fsig
->hasthis
== 1) {
714 int dreg
= alloc_ireg (cfg
);
715 int vtable_reg
= alloc_preg (cfg
);
716 MONO_EMIT_NEW_LOAD_MEMBASE_OP_FAULT (cfg
, OP_LOAD_MEMBASE
, vtable_reg
,
717 args
[0]->dreg
, MONO_STRUCT_OFFSET (MonoObject
, vtable
));
718 EMIT_NEW_LOAD_MEMBASE (cfg
, ins
, OP_LOADU1_MEMBASE
, dreg
,
719 vtable_reg
, MONO_STRUCT_OFFSET (MonoVTable
, rank
));
720 mini_type_from_op (cfg
, ins
, NULL
, NULL
);
723 } else if (strcmp (cmethod
->name
, "get_Length") == 0 && fsig
->param_count
+ fsig
->hasthis
== 1) {
724 int dreg
= alloc_ireg (cfg
);
726 EMIT_NEW_LOAD_MEMBASE_FAULT (cfg
, ins
, OP_LOADI4_MEMBASE
, dreg
,
727 args
[0]->dreg
, MONO_STRUCT_OFFSET (MonoArray
, max_length
));
728 mini_type_from_op (cfg
, ins
, NULL
, NULL
);
733 } else if (cmethod
->klass
== runtime_helpers_class
) {
734 if (strcmp (cmethod
->name
, "get_OffsetToStringData") == 0 && fsig
->param_count
== 0) {
735 EMIT_NEW_ICONST (cfg
, ins
, MONO_STRUCT_OFFSET (MonoString
, chars
));
737 } else if (strcmp (cmethod
->name
, "IsReferenceOrContainsReferences") == 0 && fsig
->param_count
== 0) {
738 MonoGenericContext
*ctx
= mono_method_get_context (cmethod
);
740 g_assert (ctx
->method_inst
);
741 g_assert (ctx
->method_inst
->type_argc
== 1);
742 MonoType
*arg_type
= ctx
->method_inst
->type_argv
[0];
748 /* Resolve the argument class as possible so we can handle common cases fast */
749 t
= mini_get_underlying_type (arg_type
);
750 klass
= mono_class_from_mono_type_internal (t
);
751 mono_class_init_internal (klass
);
752 if (MONO_TYPE_IS_REFERENCE (t
))
753 EMIT_NEW_ICONST (cfg
, ins
, 1);
754 else if (MONO_TYPE_IS_PRIMITIVE (t
))
755 EMIT_NEW_ICONST (cfg
, ins
, 0);
756 else if (cfg
->gshared
&& (t
->type
== MONO_TYPE_VAR
|| t
->type
== MONO_TYPE_MVAR
) && !mini_type_var_is_vt (t
))
757 EMIT_NEW_ICONST (cfg
, ins
, 1);
758 else if (!cfg
->gshared
|| !mini_class_check_context_used (cfg
, klass
))
759 EMIT_NEW_ICONST (cfg
, ins
, m_class_has_references (klass
) ? 1 : 0);
761 g_assert (cfg
->gshared
);
763 /* Have to use the original argument class here */
764 MonoClass
*arg_class
= mono_class_from_mono_type_internal (arg_type
);
765 int context_used
= mini_class_check_context_used (cfg
, arg_class
);
767 /* This returns 1 or 2 */
768 MonoInst
*info
= mini_emit_get_rgctx_klass (cfg
, context_used
, arg_class
, MONO_RGCTX_INFO_CLASS_IS_REF_OR_CONTAINS_REFS
);
769 int dreg
= alloc_ireg (cfg
);
770 EMIT_NEW_BIALU_IMM (cfg
, ins
, OP_ISUB_IMM
, dreg
, info
->dreg
, 1);
774 } else if (strcmp (cmethod
->name
, "IsBitwiseEquatable") == 0 && fsig
->param_count
== 0) {
775 MonoGenericContext
*ctx
= mono_method_get_context (cmethod
);
777 g_assert (ctx
->method_inst
);
778 g_assert (ctx
->method_inst
->type_argc
== 1);
779 MonoType
*arg_type
= ctx
->method_inst
->type_argv
[0];
783 /* Resolve the argument class as possible so we can handle common cases fast */
784 t
= mini_get_underlying_type (arg_type
);
786 if (MONO_TYPE_IS_PRIMITIVE (t
) && t
->type
!= MONO_TYPE_R4
&& t
->type
!= MONO_TYPE_R8
)
787 EMIT_NEW_ICONST (cfg
, ins
, 1);
789 EMIT_NEW_ICONST (cfg
, ins
, 0);
791 } else if (!strcmp (cmethod
->name
, "ObjectHasComponentSize")) {
792 g_assert (fsig
->param_count
== 1);
793 g_assert (fsig
->params
[0]->type
== MONO_TYPE_OBJECT
);
794 // Return true for arrays and string
797 dreg
= alloc_ireg (cfg
);
799 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg
, OP_LOAD_MEMBASE
, dreg
, args
[0]->dreg
, MONO_STRUCT_OFFSET (MonoObject
, vtable
));
800 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg
, OP_LOADU1_MEMBASE
, dreg
, dreg
, MONO_STRUCT_OFFSET (MonoVTable
, flags
));
801 MONO_EMIT_NEW_BIALU_IMM (cfg
, OP_IAND_IMM
, dreg
, dreg
, MONO_VT_FLAG_ARRAY_OR_STRING
);
802 EMIT_NEW_BIALU_IMM (cfg
, ins
, OP_COMPARE_IMM
, -1, dreg
, 0);
803 EMIT_NEW_UNALU (cfg
, ins
, OP_ICGT
, dreg
, -1);
804 ins
->type
= STACK_I4
;
808 } else if (cmethod
->klass
== mono_defaults
.monitor_class
) {
809 gboolean is_enter
= FALSE
;
810 gboolean is_v4
= FALSE
;
812 if (!strcmp (cmethod
->name
, "Enter") && fsig
->param_count
== 2 && fsig
->params
[1]->byref
) {
816 if (!strcmp (cmethod
->name
, "Enter") && fsig
->param_count
== 1)
821 * To make async stack traces work, icalls which can block should have a wrapper.
822 * For Monitor.Enter, emit two calls: a fastpath which doesn't have a wrapper, and a slowpath, which does.
824 MonoBasicBlock
*end_bb
;
826 NEW_BBLOCK (cfg
, end_bb
);
829 ins
= mono_emit_jit_icall (cfg
, mono_monitor_enter_v4_fast
, args
);
831 ins
= mono_emit_jit_icall (cfg
, mono_monitor_enter_fast
, args
);
833 MONO_EMIT_NEW_BIALU_IMM (cfg
, OP_ICOMPARE_IMM
, -1, ins
->dreg
, 0);
834 MONO_EMIT_NEW_BRANCH_BLOCK (cfg
, OP_IBNE_UN
, end_bb
);
837 ins
= mono_emit_jit_icall (cfg
, mono_monitor_enter_v4_internal
, args
);
839 ins
= mono_emit_jit_icall (cfg
, mono_monitor_enter_internal
, args
);
841 MONO_START_BB (cfg
, end_bb
);
844 } else if (cmethod
->klass
== mono_defaults
.thread_class
) {
845 if (strcmp (cmethod
->name
, "SpinWait_nop") == 0 && fsig
->param_count
== 0) {
846 MONO_INST_NEW (cfg
, ins
, OP_RELAXED_NOP
);
847 MONO_ADD_INS (cfg
->cbb
, ins
);
849 } else if (strcmp (cmethod
->name
, "MemoryBarrier") == 0 && fsig
->param_count
== 0) {
850 return mini_emit_memory_barrier (cfg
, MONO_MEMORY_BARRIER_SEQ
);
851 } else if (!strcmp (cmethod
->name
, "VolatileRead") && fsig
->param_count
== 1) {
853 gboolean is_ref
= mini_type_is_reference (fsig
->params
[0]);
855 if (fsig
->params
[0]->type
== MONO_TYPE_I1
)
856 opcode
= OP_LOADI1_MEMBASE
;
857 else if (fsig
->params
[0]->type
== MONO_TYPE_U1
)
858 opcode
= OP_LOADU1_MEMBASE
;
859 else if (fsig
->params
[0]->type
== MONO_TYPE_I2
)
860 opcode
= OP_LOADI2_MEMBASE
;
861 else if (fsig
->params
[0]->type
== MONO_TYPE_U2
)
862 opcode
= OP_LOADU2_MEMBASE
;
863 else if (fsig
->params
[0]->type
== MONO_TYPE_I4
)
864 opcode
= OP_LOADI4_MEMBASE
;
865 else if (fsig
->params
[0]->type
== MONO_TYPE_U4
)
866 opcode
= OP_LOADU4_MEMBASE
;
867 else if (fsig
->params
[0]->type
== MONO_TYPE_I8
|| fsig
->params
[0]->type
== MONO_TYPE_U8
)
868 opcode
= OP_LOADI8_MEMBASE
;
869 else if (fsig
->params
[0]->type
== MONO_TYPE_R4
)
870 opcode
= OP_LOADR4_MEMBASE
;
871 else if (fsig
->params
[0]->type
== MONO_TYPE_R8
)
872 opcode
= OP_LOADR8_MEMBASE
;
873 else if (is_ref
|| fsig
->params
[0]->type
== MONO_TYPE_I
|| fsig
->params
[0]->type
== MONO_TYPE_U
)
874 opcode
= OP_LOAD_MEMBASE
;
877 MONO_INST_NEW (cfg
, ins
, opcode
);
878 ins
->inst_basereg
= args
[0]->dreg
;
879 ins
->inst_offset
= 0;
880 MONO_ADD_INS (cfg
->cbb
, ins
);
882 switch (fsig
->params
[0]->type
) {
889 ins
->dreg
= mono_alloc_ireg (cfg
);
890 ins
->type
= STACK_I4
;
894 ins
->dreg
= mono_alloc_lreg (cfg
);
895 ins
->type
= STACK_I8
;
899 ins
->dreg
= mono_alloc_ireg (cfg
);
900 #if SIZEOF_REGISTER == 8
901 ins
->type
= STACK_I8
;
903 ins
->type
= STACK_I4
;
908 ins
->dreg
= mono_alloc_freg (cfg
);
909 ins
->type
= STACK_R8
;
912 g_assert (mini_type_is_reference (fsig
->params
[0]));
913 ins
->dreg
= mono_alloc_ireg_ref (cfg
);
914 ins
->type
= STACK_OBJ
;
918 if (opcode
== OP_LOADI8_MEMBASE
)
919 ins
= mono_decompose_opcode (cfg
, ins
);
921 mini_emit_memory_barrier (cfg
, MONO_MEMORY_BARRIER_SEQ
);
925 } else if (!strcmp (cmethod
->name
, "VolatileWrite") && fsig
->param_count
== 2) {
927 gboolean is_ref
= mini_type_is_reference (fsig
->params
[0]);
929 if (fsig
->params
[0]->type
== MONO_TYPE_I1
|| fsig
->params
[0]->type
== MONO_TYPE_U1
)
930 opcode
= OP_STOREI1_MEMBASE_REG
;
931 else if (fsig
->params
[0]->type
== MONO_TYPE_I2
|| fsig
->params
[0]->type
== MONO_TYPE_U2
)
932 opcode
= OP_STOREI2_MEMBASE_REG
;
933 else if (fsig
->params
[0]->type
== MONO_TYPE_I4
|| fsig
->params
[0]->type
== MONO_TYPE_U4
)
934 opcode
= OP_STOREI4_MEMBASE_REG
;
935 else if (fsig
->params
[0]->type
== MONO_TYPE_I8
|| fsig
->params
[0]->type
== MONO_TYPE_U8
)
936 opcode
= OP_STOREI8_MEMBASE_REG
;
937 else if (fsig
->params
[0]->type
== MONO_TYPE_R4
)
938 opcode
= OP_STORER4_MEMBASE_REG
;
939 else if (fsig
->params
[0]->type
== MONO_TYPE_R8
)
940 opcode
= OP_STORER8_MEMBASE_REG
;
941 else if (is_ref
|| fsig
->params
[0]->type
== MONO_TYPE_I
|| fsig
->params
[0]->type
== MONO_TYPE_U
)
942 opcode
= OP_STORE_MEMBASE_REG
;
945 mini_emit_memory_barrier (cfg
, MONO_MEMORY_BARRIER_SEQ
);
947 MONO_INST_NEW (cfg
, ins
, opcode
);
948 ins
->sreg1
= args
[1]->dreg
;
949 ins
->inst_destbasereg
= args
[0]->dreg
;
950 ins
->inst_offset
= 0;
951 MONO_ADD_INS (cfg
->cbb
, ins
);
953 if (opcode
== OP_STOREI8_MEMBASE_REG
)
954 ins
= mono_decompose_opcode (cfg
, ins
);
959 } else if (in_corlib
&&
960 (strcmp (cmethod_klass_name_space
, "System.Threading") == 0) &&
961 (strcmp (cmethod_klass_name
, "Interlocked") == 0)) {
964 #if SIZEOF_REGISTER == 8
965 if (!cfg
->llvm_only
&& strcmp (cmethod
->name
, "Read") == 0 && fsig
->param_count
== 1 && (fsig
->params
[0]->type
== MONO_TYPE_I8
)) {
966 if (!cfg
->llvm_only
&& mono_arch_opcode_supported (OP_ATOMIC_LOAD_I8
)) {
967 MONO_INST_NEW (cfg
, ins
, OP_ATOMIC_LOAD_I8
);
968 ins
->dreg
= mono_alloc_preg (cfg
);
969 ins
->sreg1
= args
[0]->dreg
;
970 ins
->type
= STACK_I8
;
971 ins
->backend
.memory_barrier_kind
= MONO_MEMORY_BARRIER_SEQ
;
972 MONO_ADD_INS (cfg
->cbb
, ins
);
976 mini_emit_memory_barrier (cfg
, MONO_MEMORY_BARRIER_SEQ
);
978 /* 64 bit reads are already atomic */
979 MONO_INST_NEW (cfg
, load_ins
, OP_LOADI8_MEMBASE
);
980 load_ins
->dreg
= mono_alloc_preg (cfg
);
981 load_ins
->inst_basereg
= args
[0]->dreg
;
982 load_ins
->inst_offset
= 0;
983 load_ins
->type
= STACK_I8
;
984 MONO_ADD_INS (cfg
->cbb
, load_ins
);
986 mini_emit_memory_barrier (cfg
, MONO_MEMORY_BARRIER_SEQ
);
993 if (strcmp (cmethod
->name
, "Increment") == 0 && fsig
->param_count
== 1) {
994 MonoInst
*ins_iconst
;
997 if (fsig
->params
[0]->type
== MONO_TYPE_I4
) {
998 opcode
= OP_ATOMIC_ADD_I4
;
999 cfg
->has_atomic_add_i4
= TRUE
;
1001 #if SIZEOF_REGISTER == 8
1002 else if (fsig
->params
[0]->type
== MONO_TYPE_I8
)
1003 opcode
= OP_ATOMIC_ADD_I8
;
1006 if (!mono_arch_opcode_supported (opcode
))
1008 MONO_INST_NEW (cfg
, ins_iconst
, OP_ICONST
);
1009 ins_iconst
->inst_c0
= 1;
1010 ins_iconst
->dreg
= mono_alloc_ireg (cfg
);
1011 MONO_ADD_INS (cfg
->cbb
, ins_iconst
);
1013 MONO_INST_NEW (cfg
, ins
, opcode
);
1014 ins
->dreg
= mono_alloc_ireg (cfg
);
1015 ins
->inst_basereg
= args
[0]->dreg
;
1016 ins
->inst_offset
= 0;
1017 ins
->sreg2
= ins_iconst
->dreg
;
1018 ins
->type
= (opcode
== OP_ATOMIC_ADD_I4
) ? STACK_I4
: STACK_I8
;
1019 MONO_ADD_INS (cfg
->cbb
, ins
);
1021 } else if (strcmp (cmethod
->name
, "Decrement") == 0 && fsig
->param_count
== 1) {
1022 MonoInst
*ins_iconst
;
1025 if (fsig
->params
[0]->type
== MONO_TYPE_I4
) {
1026 opcode
= OP_ATOMIC_ADD_I4
;
1027 cfg
->has_atomic_add_i4
= TRUE
;
1029 #if SIZEOF_REGISTER == 8
1030 else if (fsig
->params
[0]->type
== MONO_TYPE_I8
)
1031 opcode
= OP_ATOMIC_ADD_I8
;
1034 if (!mono_arch_opcode_supported (opcode
))
1036 MONO_INST_NEW (cfg
, ins_iconst
, OP_ICONST
);
1037 ins_iconst
->inst_c0
= -1;
1038 ins_iconst
->dreg
= mono_alloc_ireg (cfg
);
1039 MONO_ADD_INS (cfg
->cbb
, ins_iconst
);
1041 MONO_INST_NEW (cfg
, ins
, opcode
);
1042 ins
->dreg
= mono_alloc_ireg (cfg
);
1043 ins
->inst_basereg
= args
[0]->dreg
;
1044 ins
->inst_offset
= 0;
1045 ins
->sreg2
= ins_iconst
->dreg
;
1046 ins
->type
= (opcode
== OP_ATOMIC_ADD_I4
) ? STACK_I4
: STACK_I8
;
1047 MONO_ADD_INS (cfg
->cbb
, ins
);
1049 } else if (strcmp (cmethod
->name
, "Add") == 0 && fsig
->param_count
== 2) {
1052 if (fsig
->params
[0]->type
== MONO_TYPE_I4
) {
1053 opcode
= OP_ATOMIC_ADD_I4
;
1054 cfg
->has_atomic_add_i4
= TRUE
;
1056 #if SIZEOF_REGISTER == 8
1057 else if (fsig
->params
[0]->type
== MONO_TYPE_I8
)
1058 opcode
= OP_ATOMIC_ADD_I8
;
1061 if (!mono_arch_opcode_supported (opcode
))
1063 MONO_INST_NEW (cfg
, ins
, opcode
);
1064 ins
->dreg
= mono_alloc_ireg (cfg
);
1065 ins
->inst_basereg
= args
[0]->dreg
;
1066 ins
->inst_offset
= 0;
1067 ins
->sreg2
= args
[1]->dreg
;
1068 ins
->type
= (opcode
== OP_ATOMIC_ADD_I4
) ? STACK_I4
: STACK_I8
;
1069 MONO_ADD_INS (cfg
->cbb
, ins
);
1072 else if (strcmp (cmethod
->name
, "Exchange") == 0 && fsig
->param_count
== 2) {
1073 MonoInst
*f2i
= NULL
, *i2f
;
1074 guint32 opcode
, f2i_opcode
, i2f_opcode
;
1075 gboolean is_ref
= mini_type_is_reference (fsig
->params
[0]);
1076 gboolean is_float
= fsig
->params
[0]->type
== MONO_TYPE_R4
|| fsig
->params
[0]->type
== MONO_TYPE_R8
;
1078 if (fsig
->params
[0]->type
== MONO_TYPE_I4
||
1079 fsig
->params
[0]->type
== MONO_TYPE_R4
) {
1080 opcode
= OP_ATOMIC_EXCHANGE_I4
;
1081 f2i_opcode
= OP_MOVE_F_TO_I4
;
1082 i2f_opcode
= OP_MOVE_I4_TO_F
;
1083 cfg
->has_atomic_exchange_i4
= TRUE
;
1085 #if SIZEOF_REGISTER == 8
1087 fsig
->params
[0]->type
== MONO_TYPE_I8
||
1088 fsig
->params
[0]->type
== MONO_TYPE_R8
||
1089 fsig
->params
[0]->type
== MONO_TYPE_I
) {
1090 opcode
= OP_ATOMIC_EXCHANGE_I8
;
1091 f2i_opcode
= OP_MOVE_F_TO_I8
;
1092 i2f_opcode
= OP_MOVE_I8_TO_F
;
1095 else if (is_ref
|| fsig
->params
[0]->type
== MONO_TYPE_I
) {
1096 opcode
= OP_ATOMIC_EXCHANGE_I4
;
1097 cfg
->has_atomic_exchange_i4
= TRUE
;
1103 if (!mono_arch_opcode_supported (opcode
))
1107 /* TODO: Decompose these opcodes instead of bailing here. */
1108 if (COMPILE_SOFT_FLOAT (cfg
))
1111 MONO_INST_NEW (cfg
, f2i
, f2i_opcode
);
1112 f2i
->dreg
= mono_alloc_ireg (cfg
);
1113 f2i
->sreg1
= args
[1]->dreg
;
1114 if (f2i_opcode
== OP_MOVE_F_TO_I4
)
1115 f2i
->backend
.spill_var
= mini_get_int_to_float_spill_area (cfg
);
1116 MONO_ADD_INS (cfg
->cbb
, f2i
);
1119 MONO_INST_NEW (cfg
, ins
, opcode
);
1120 ins
->dreg
= is_ref
? mono_alloc_ireg_ref (cfg
) : mono_alloc_ireg (cfg
);
1121 ins
->inst_basereg
= args
[0]->dreg
;
1122 ins
->inst_offset
= 0;
1123 ins
->sreg2
= is_float
? f2i
->dreg
: args
[1]->dreg
;
1124 MONO_ADD_INS (cfg
->cbb
, ins
);
1126 switch (fsig
->params
[0]->type
) {
1128 ins
->type
= STACK_I4
;
1131 ins
->type
= STACK_I8
;
1134 #if SIZEOF_REGISTER == 8
1135 ins
->type
= STACK_I8
;
1137 ins
->type
= STACK_I4
;
1142 ins
->type
= STACK_R8
;
1145 g_assert (mini_type_is_reference (fsig
->params
[0]));
1146 ins
->type
= STACK_OBJ
;
1151 MONO_INST_NEW (cfg
, i2f
, i2f_opcode
);
1152 i2f
->dreg
= mono_alloc_freg (cfg
);
1153 i2f
->sreg1
= ins
->dreg
;
1154 i2f
->type
= STACK_R8
;
1155 if (i2f_opcode
== OP_MOVE_I4_TO_F
)
1156 i2f
->backend
.spill_var
= mini_get_int_to_float_spill_area (cfg
);
1157 MONO_ADD_INS (cfg
->cbb
, i2f
);
1162 if (cfg
->gen_write_barriers
&& is_ref
)
1163 mini_emit_write_barrier (cfg
, args
[0], args
[1]);
1165 else if ((strcmp (cmethod
->name
, "CompareExchange") == 0) && fsig
->param_count
== 3) {
1166 MonoInst
*f2i_new
= NULL
, *f2i_cmp
= NULL
, *i2f
;
1167 guint32 opcode
, f2i_opcode
, i2f_opcode
;
1168 gboolean is_ref
= mini_type_is_reference (fsig
->params
[1]);
1169 gboolean is_float
= fsig
->params
[1]->type
== MONO_TYPE_R4
|| fsig
->params
[1]->type
== MONO_TYPE_R8
;
1171 if (fsig
->params
[1]->type
== MONO_TYPE_I4
||
1172 fsig
->params
[1]->type
== MONO_TYPE_R4
) {
1173 opcode
= OP_ATOMIC_CAS_I4
;
1174 f2i_opcode
= OP_MOVE_F_TO_I4
;
1175 i2f_opcode
= OP_MOVE_I4_TO_F
;
1176 cfg
->has_atomic_cas_i4
= TRUE
;
1178 #if SIZEOF_REGISTER == 8
1180 fsig
->params
[1]->type
== MONO_TYPE_I8
||
1181 fsig
->params
[1]->type
== MONO_TYPE_R8
||
1182 fsig
->params
[1]->type
== MONO_TYPE_I
) {
1183 opcode
= OP_ATOMIC_CAS_I8
;
1184 f2i_opcode
= OP_MOVE_F_TO_I8
;
1185 i2f_opcode
= OP_MOVE_I8_TO_F
;
1188 else if (is_ref
|| fsig
->params
[1]->type
== MONO_TYPE_I
) {
1189 opcode
= OP_ATOMIC_CAS_I4
;
1190 cfg
->has_atomic_cas_i4
= TRUE
;
1196 if (!mono_arch_opcode_supported (opcode
))
1200 /* TODO: Decompose these opcodes instead of bailing here. */
1201 if (COMPILE_SOFT_FLOAT (cfg
))
1204 MONO_INST_NEW (cfg
, f2i_new
, f2i_opcode
);
1205 f2i_new
->dreg
= mono_alloc_ireg (cfg
);
1206 f2i_new
->sreg1
= args
[1]->dreg
;
1207 if (f2i_opcode
== OP_MOVE_F_TO_I4
)
1208 f2i_new
->backend
.spill_var
= mini_get_int_to_float_spill_area (cfg
);
1209 MONO_ADD_INS (cfg
->cbb
, f2i_new
);
1211 MONO_INST_NEW (cfg
, f2i_cmp
, f2i_opcode
);
1212 f2i_cmp
->dreg
= mono_alloc_ireg (cfg
);
1213 f2i_cmp
->sreg1
= args
[2]->dreg
;
1214 if (f2i_opcode
== OP_MOVE_F_TO_I4
)
1215 f2i_cmp
->backend
.spill_var
= mini_get_int_to_float_spill_area (cfg
);
1216 MONO_ADD_INS (cfg
->cbb
, f2i_cmp
);
1219 MONO_INST_NEW (cfg
, ins
, opcode
);
1220 ins
->dreg
= is_ref
? alloc_ireg_ref (cfg
) : alloc_ireg (cfg
);
1221 ins
->sreg1
= args
[0]->dreg
;
1222 ins
->sreg2
= is_float
? f2i_new
->dreg
: args
[1]->dreg
;
1223 ins
->sreg3
= is_float
? f2i_cmp
->dreg
: args
[2]->dreg
;
1224 MONO_ADD_INS (cfg
->cbb
, ins
);
1226 switch (fsig
->params
[1]->type
) {
1228 ins
->type
= STACK_I4
;
1231 ins
->type
= STACK_I8
;
1234 #if SIZEOF_REGISTER == 8
1235 ins
->type
= STACK_I8
;
1237 ins
->type
= STACK_I4
;
1241 ins
->type
= cfg
->r4_stack_type
;
1244 ins
->type
= STACK_R8
;
1247 g_assert (mini_type_is_reference (fsig
->params
[1]));
1248 ins
->type
= STACK_OBJ
;
1253 MONO_INST_NEW (cfg
, i2f
, i2f_opcode
);
1254 i2f
->dreg
= mono_alloc_freg (cfg
);
1255 i2f
->sreg1
= ins
->dreg
;
1256 i2f
->type
= STACK_R8
;
1257 if (i2f_opcode
== OP_MOVE_I4_TO_F
)
1258 i2f
->backend
.spill_var
= mini_get_int_to_float_spill_area (cfg
);
1259 MONO_ADD_INS (cfg
->cbb
, i2f
);
1264 if (cfg
->gen_write_barriers
&& is_ref
)
1265 mini_emit_write_barrier (cfg
, args
[0], args
[1]);
1267 else if ((strcmp (cmethod
->name
, "CompareExchange") == 0) && fsig
->param_count
== 4 &&
1268 fsig
->params
[1]->type
== MONO_TYPE_I4
) {
1269 MonoInst
*cmp
, *ceq
;
1271 if (!mono_arch_opcode_supported (OP_ATOMIC_CAS_I4
))
1274 /* int32 r = CAS (location, value, comparand); */
1275 MONO_INST_NEW (cfg
, ins
, OP_ATOMIC_CAS_I4
);
1276 ins
->dreg
= alloc_ireg (cfg
);
1277 ins
->sreg1
= args
[0]->dreg
;
1278 ins
->sreg2
= args
[1]->dreg
;
1279 ins
->sreg3
= args
[2]->dreg
;
1280 ins
->type
= STACK_I4
;
1281 MONO_ADD_INS (cfg
->cbb
, ins
);
1283 /* bool result = r == comparand; */
1284 MONO_INST_NEW (cfg
, cmp
, OP_ICOMPARE
);
1285 cmp
->sreg1
= ins
->dreg
;
1286 cmp
->sreg2
= args
[2]->dreg
;
1287 cmp
->type
= STACK_I4
;
1288 MONO_ADD_INS (cfg
->cbb
, cmp
);
1290 MONO_INST_NEW (cfg
, ceq
, OP_ICEQ
);
1291 ceq
->dreg
= alloc_ireg (cfg
);
1292 ceq
->type
= STACK_I4
;
1293 MONO_ADD_INS (cfg
->cbb
, ceq
);
1295 /* *success = result; */
1296 MONO_EMIT_NEW_STORE_MEMBASE (cfg
, OP_STOREI1_MEMBASE_REG
, args
[3]->dreg
, 0, ceq
->dreg
);
1298 cfg
->has_atomic_cas_i4
= TRUE
;
1300 else if (strcmp (cmethod
->name
, "MemoryBarrier") == 0 && fsig
->param_count
== 0)
1301 ins
= mini_emit_memory_barrier (cfg
, MONO_MEMORY_BARRIER_SEQ
);
1305 } else if (in_corlib
&&
1306 (strcmp (cmethod_klass_name_space
, "System.Threading") == 0) &&
1307 (strcmp (cmethod_klass_name
, "Volatile") == 0)) {
1310 if (!cfg
->llvm_only
&& !strcmp (cmethod
->name
, "Read") && fsig
->param_count
== 1) {
1312 MonoType
*t
= fsig
->params
[0];
1314 gboolean is_float
= t
->type
== MONO_TYPE_R4
|| t
->type
== MONO_TYPE_R8
;
1316 g_assert (t
->byref
);
1317 /* t is a byref type, so the reference check is more complicated */
1318 is_ref
= mini_type_is_reference (m_class_get_byval_arg (mono_class_from_mono_type_internal (t
)));
1319 if (t
->type
== MONO_TYPE_I1
)
1320 opcode
= OP_ATOMIC_LOAD_I1
;
1321 else if (t
->type
== MONO_TYPE_U1
|| t
->type
== MONO_TYPE_BOOLEAN
)
1322 opcode
= OP_ATOMIC_LOAD_U1
;
1323 else if (t
->type
== MONO_TYPE_I2
)
1324 opcode
= OP_ATOMIC_LOAD_I2
;
1325 else if (t
->type
== MONO_TYPE_U2
)
1326 opcode
= OP_ATOMIC_LOAD_U2
;
1327 else if (t
->type
== MONO_TYPE_I4
)
1328 opcode
= OP_ATOMIC_LOAD_I4
;
1329 else if (t
->type
== MONO_TYPE_U4
)
1330 opcode
= OP_ATOMIC_LOAD_U4
;
1331 else if (t
->type
== MONO_TYPE_R4
)
1332 opcode
= OP_ATOMIC_LOAD_R4
;
1333 else if (t
->type
== MONO_TYPE_R8
)
1334 opcode
= OP_ATOMIC_LOAD_R8
;
1335 #if SIZEOF_REGISTER == 8
1336 else if (t
->type
== MONO_TYPE_I8
|| t
->type
== MONO_TYPE_I
)
1337 opcode
= OP_ATOMIC_LOAD_I8
;
1338 else if (is_ref
|| t
->type
== MONO_TYPE_U8
|| t
->type
== MONO_TYPE_U
)
1339 opcode
= OP_ATOMIC_LOAD_U8
;
1341 else if (t
->type
== MONO_TYPE_I
)
1342 opcode
= OP_ATOMIC_LOAD_I4
;
1343 else if (is_ref
|| t
->type
== MONO_TYPE_U
)
1344 opcode
= OP_ATOMIC_LOAD_U4
;
1348 if (!mono_arch_opcode_supported (opcode
))
1351 MONO_INST_NEW (cfg
, ins
, opcode
);
1352 ins
->dreg
= is_ref
? mono_alloc_ireg_ref (cfg
) : (is_float
? mono_alloc_freg (cfg
) : mono_alloc_ireg (cfg
));
1353 ins
->sreg1
= args
[0]->dreg
;
1354 ins
->backend
.memory_barrier_kind
= MONO_MEMORY_BARRIER_ACQ
;
1355 MONO_ADD_INS (cfg
->cbb
, ins
);
1358 case MONO_TYPE_BOOLEAN
:
1365 ins
->type
= STACK_I4
;
1369 ins
->type
= STACK_I8
;
1373 #if SIZEOF_REGISTER == 8
1374 ins
->type
= STACK_I8
;
1376 ins
->type
= STACK_I4
;
1380 ins
->type
= cfg
->r4_stack_type
;
1383 ins
->type
= STACK_R8
;
1387 ins
->type
= STACK_OBJ
;
1393 if (!cfg
->llvm_only
&& !strcmp (cmethod
->name
, "Write") && fsig
->param_count
== 2) {
1395 MonoType
*t
= fsig
->params
[0];
1398 g_assert (t
->byref
);
1399 is_ref
= mini_type_is_reference (m_class_get_byval_arg (mono_class_from_mono_type_internal (t
)));
1400 if (t
->type
== MONO_TYPE_I1
)
1401 opcode
= OP_ATOMIC_STORE_I1
;
1402 else if (t
->type
== MONO_TYPE_U1
|| t
->type
== MONO_TYPE_BOOLEAN
)
1403 opcode
= OP_ATOMIC_STORE_U1
;
1404 else if (t
->type
== MONO_TYPE_I2
)
1405 opcode
= OP_ATOMIC_STORE_I2
;
1406 else if (t
->type
== MONO_TYPE_U2
)
1407 opcode
= OP_ATOMIC_STORE_U2
;
1408 else if (t
->type
== MONO_TYPE_I4
)
1409 opcode
= OP_ATOMIC_STORE_I4
;
1410 else if (t
->type
== MONO_TYPE_U4
)
1411 opcode
= OP_ATOMIC_STORE_U4
;
1412 else if (t
->type
== MONO_TYPE_R4
)
1413 opcode
= OP_ATOMIC_STORE_R4
;
1414 else if (t
->type
== MONO_TYPE_R8
)
1415 opcode
= OP_ATOMIC_STORE_R8
;
1416 #if SIZEOF_REGISTER == 8
1417 else if (t
->type
== MONO_TYPE_I8
|| t
->type
== MONO_TYPE_I
)
1418 opcode
= OP_ATOMIC_STORE_I8
;
1419 else if (is_ref
|| t
->type
== MONO_TYPE_U8
|| t
->type
== MONO_TYPE_U
)
1420 opcode
= OP_ATOMIC_STORE_U8
;
1422 else if (t
->type
== MONO_TYPE_I
)
1423 opcode
= OP_ATOMIC_STORE_I4
;
1424 else if (is_ref
|| t
->type
== MONO_TYPE_U
)
1425 opcode
= OP_ATOMIC_STORE_U4
;
1429 if (!mono_arch_opcode_supported (opcode
))
1432 MONO_INST_NEW (cfg
, ins
, opcode
);
1433 ins
->dreg
= args
[0]->dreg
;
1434 ins
->sreg1
= args
[1]->dreg
;
1435 ins
->backend
.memory_barrier_kind
= MONO_MEMORY_BARRIER_REL
;
1436 MONO_ADD_INS (cfg
->cbb
, ins
);
1438 if (cfg
->gen_write_barriers
&& is_ref
)
1439 mini_emit_write_barrier (cfg
, args
[0], args
[1]);
1445 } else if (in_corlib
&&
1446 (strcmp (cmethod_klass_name_space
, "System.Diagnostics") == 0) &&
1447 (strcmp (cmethod_klass_name
, "Debugger") == 0)) {
1448 if (!strcmp (cmethod
->name
, "Break") && fsig
->param_count
== 0) {
1449 if (mini_should_insert_breakpoint (cfg
->method
)) {
1450 ins
= mono_emit_jit_icall (cfg
, mono_debugger_agent_user_break
, NULL
);
1452 MONO_INST_NEW (cfg
, ins
, OP_NOP
);
1453 MONO_ADD_INS (cfg
->cbb
, ins
);
1457 } else if (in_corlib
&&
1458 (strcmp (cmethod_klass_name_space
, "System") == 0) &&
1459 (strcmp (cmethod_klass_name
, "Environment") == 0)) {
1460 if (!strcmp (cmethod
->name
, "get_IsRunningOnWindows") && fsig
->param_count
== 0) {
1462 EMIT_NEW_ICONST (cfg
, ins
, 1);
1464 EMIT_NEW_ICONST (cfg
, ins
, 0);
1467 } else if (in_corlib
&&
1468 (strcmp (cmethod_klass_name_space
, "System.Reflection") == 0) &&
1469 (strcmp (cmethod_klass_name
, "Assembly") == 0)) {
1470 if (cfg
->llvm_only
&& !strcmp (cmethod
->name
, "GetExecutingAssembly")) {
1471 /* No stack walks are currently available, so implement this as an intrinsic */
1472 MonoInst
*assembly_ins
;
1474 EMIT_NEW_AOTCONST (cfg
, assembly_ins
, MONO_PATCH_INFO_IMAGE
, m_class_get_image (cfg
->method
->klass
));
1475 ins
= mono_emit_jit_icall (cfg
, mono_get_assembly_object
, &assembly_ins
);
1479 // While it is not required per
1480 // https://msdn.microsoft.com/en-us/library/system.reflection.assembly.getcallingassembly(v=vs.110).aspx.
1481 // have GetCallingAssembly be consistent independently of varying optimization.
1482 // This fixes mono/tests/test-inline-call-stack.cs under FullAOT+LLVM.
1483 cfg
->no_inline
|= COMPILE_LLVM (cfg
) && strcmp (cmethod
->name
, "GetCallingAssembly") == 0;
1485 } else if (in_corlib
&&
1486 (strcmp (cmethod_klass_name_space
, "System.Reflection") == 0) &&
1487 (strcmp (cmethod_klass_name
, "MethodBase") == 0)) {
1488 if (cfg
->llvm_only
&& !strcmp (cmethod
->name
, "GetCurrentMethod")) {
1489 /* No stack walks are currently available, so implement this as an intrinsic */
1490 MonoInst
*method_ins
;
1491 MonoMethod
*declaring
= cfg
->method
;
1493 /* This returns the declaring generic method */
1494 if (declaring
->is_inflated
)
1495 declaring
= ((MonoMethodInflated
*)cfg
->method
)->declaring
;
1496 EMIT_NEW_AOTCONST (cfg
, method_ins
, MONO_PATCH_INFO_METHODCONST
, declaring
);
1497 ins
= mono_emit_jit_icall (cfg
, mono_get_method_object
, &method_ins
);
1498 cfg
->no_inline
= TRUE
;
1499 if (cfg
->method
!= cfg
->current_method
)
1500 mini_set_inline_failure (cfg
, "MethodBase:GetCurrentMethod ()");
1503 } else if (cmethod
->klass
== mono_class_try_get_math_class ()) {
1505 * There is general branchless code for Min/Max, but it does not work for
1507 * http://everything2.com/?node_id=1051618
1511 * Constant folding for various Math methods.
1512 * we avoid folding constants that when computed would raise an error, in
1513 * case the user code was expecting to get that error raised
1515 if (fsig
->param_count
== 1 && args
[0]->opcode
== OP_R8CONST
){
1516 double source
= *(double *)args
[0]->inst_p0
;
1518 const char *mname
= cmethod
->name
;
1522 if (strcmp (mname
, "Abs") == 0 && fsig
->params
[0]->type
== MONO_TYPE_R8
) {
1524 } else if (strcmp (mname
, "Asin") == 0){
1525 if (fabs (source
) <= 1)
1527 } else if (strcmp (mname
, "Asinh") == 0){
1529 } else if (strcmp (mname
, "Acos") == 0){
1530 if (fabs (source
) <= 1)
1532 } else if (strcmp (mname
, "Acosh") == 0){
1535 } else if (strcmp (mname
, "Atan") == 0){
1537 } else if (strcmp (mname
, "Atanh") == 0){
1538 if (fabs (source
) < 1)
1541 } else if (c
== 'C'){
1542 if (strcmp (mname
, "Cos") == 0) {
1543 if (!isinf (source
))
1545 } else if (strcmp (mname
, "Cbrt") == 0){
1547 } else if (strcmp (mname
, "Cosh") == 0){
1550 } else if (c
== 'R'){
1551 if (strcmp (mname
, "Round") == 0)
1553 } else if (c
== 'S'){
1554 if (strcmp (mname
, "Sin") == 0) {
1555 if (!isinf (source
))
1557 } else if (strcmp (mname
, "Sqrt") == 0) {
1560 } else if (strcmp (mname
, "Sinh") == 0){
1563 } else if (c
== 'T'){
1564 if (strcmp (mname
, "Tan") == 0){
1565 if (!isinf (source
))
1567 } else if (strcmp (mname
, "Tanh") == 0){
1573 double *dest
= (double *) mono_domain_alloc (cfg
->domain
, sizeof (double));
1575 MONO_INST_NEW (cfg
, ins
, OP_R8CONST
);
1576 ins
->type
= STACK_R8
;
1577 ins
->dreg
= mono_alloc_dreg (cfg
, (MonoStackType
) ins
->type
);
1578 ins
->inst_p0
= dest
;
1582 result
= fabs (source
);
1585 result
= acos (source
);
1588 result
= acosh (source
);
1591 result
= asin (source
);
1594 result
= asinh (source
);
1597 result
= atan (source
);
1600 result
= atanh (source
);
1603 result
= cbrt (source
);
1606 result
= cos (source
);
1609 result
= cosh (source
);
1612 result
= round (source
);
1615 result
= sin (source
);
1618 result
= sinh (source
);
1621 result
= sqrt (source
);
1624 result
= tan (source
);
1627 result
= tanh (source
);
1630 g_error ("invalid opcode %d", (int)opcode
);
1633 MONO_ADD_INS (cfg
->cbb
, ins
);
1634 NULLIFY_INS (args
[0]);
1638 } else if (cmethod
->klass
== mono_defaults
.systemtype_class
&& !strcmp (cmethod
->name
, "op_Equality")) {
1639 EMIT_NEW_BIALU (cfg
, ins
, OP_COMPARE
, -1, args
[0]->dreg
, args
[1]->dreg
);
1640 MONO_INST_NEW (cfg
, ins
, OP_PCEQ
);
1641 ins
->dreg
= alloc_preg (cfg
);
1642 ins
->type
= STACK_I4
;
1643 MONO_ADD_INS (cfg
->cbb
, ins
);
1645 } else if (((!strcmp (cmethod_klass_image
->assembly
->aname
.name
, "MonoMac") ||
1646 !strcmp (cmethod_klass_image
->assembly
->aname
.name
, "monotouch")) &&
1647 !strcmp (cmethod_klass_name_space
, "XamCore.ObjCRuntime") &&
1648 !strcmp (cmethod_klass_name
, "Selector")) ||
1649 ((!strcmp (cmethod_klass_image
->assembly
->aname
.name
, "Xamarin.iOS") ||
1650 !strcmp (cmethod_klass_image
->assembly
->aname
.name
, "Xamarin.Mac")) &&
1651 !strcmp (cmethod_klass_name_space
, "ObjCRuntime") &&
1652 !strcmp (cmethod_klass_name
, "Selector"))
1654 if ((cfg
->backend
->have_objc_get_selector
|| cfg
->compile_llvm
) &&
1655 !strcmp (cmethod
->name
, "GetHandle") && fsig
->param_count
== 1 &&
1656 (args
[0]->opcode
== OP_GOT_ENTRY
|| args
[0]->opcode
== OP_AOTCONST
) &&
1659 MonoJumpInfoToken
*ji
;
1662 if (args
[0]->opcode
== OP_GOT_ENTRY
) {
1663 pi
= (MonoInst
*)args
[0]->inst_p1
;
1664 g_assert (pi
->opcode
== OP_PATCH_INFO
);
1665 g_assert (GPOINTER_TO_INT (pi
->inst_p1
) == MONO_PATCH_INFO_LDSTR
);
1666 ji
= (MonoJumpInfoToken
*)pi
->inst_p0
;
1668 g_assert (GPOINTER_TO_INT (args
[0]->inst_p1
) == MONO_PATCH_INFO_LDSTR
);
1669 ji
= (MonoJumpInfoToken
*)args
[0]->inst_p0
;
1672 NULLIFY_INS (args
[0]);
1674 s
= mono_ldstr_utf8 (ji
->image
, mono_metadata_token_index (ji
->token
), cfg
->error
);
1675 return_val_if_nok (cfg
->error
, NULL
);
1677 MONO_INST_NEW (cfg
, ins
, OP_OBJC_GET_SELECTOR
);
1678 ins
->dreg
= mono_alloc_ireg (cfg
);
1681 MONO_ADD_INS (cfg
->cbb
, ins
);
1684 } else if (in_corlib
&&
1685 (strcmp (cmethod_klass_name_space
, "System.Runtime.InteropServices") == 0) &&
1686 (strcmp (cmethod_klass_name
, "Marshal") == 0)) {
1687 //Convert Marshal.PtrToStructure<T> of blittable T to direct loads
1688 if (strcmp (cmethod
->name
, "PtrToStructure") == 0 &&
1689 cmethod
->is_inflated
&&
1690 fsig
->param_count
== 1 &&
1691 !mini_method_check_context_used (cfg
, cmethod
)) {
1693 MonoGenericContext
*method_context
= mono_method_get_context (cmethod
);
1694 MonoType
*arg0
= method_context
->method_inst
->type_argv
[0];
1695 if (mono_type_is_native_blittable (arg0
))
1696 return mini_emit_memory_load (cfg
, arg0
, args
[0], 0, 0);
1698 } else if (cmethod
->klass
== mono_defaults
.enum_class
&& !strcmp (cmethod
->name
, "HasFlag") &&
1699 args
[0]->opcode
== OP_BOX
&& args
[1]->opcode
== OP_BOX_ICONST
&& args
[0]->klass
== args
[1]->klass
) {
1700 args
[1]->opcode
= OP_ICONST
;
1701 ins
= mini_handle_enum_has_flag (cfg
, args
[0]->klass
, NULL
, args
[0]->sreg1
, args
[1]);
1702 NULLIFY_INS (args
[0]);
1704 } else if (in_corlib
&&
1705 !strcmp (cmethod_klass_name_space
, "System") &&
1706 (!strcmp (cmethod_klass_name
, "Span`1") || !strcmp (cmethod_klass_name
, "ReadOnlySpan`1"))) {
1707 return emit_span_intrinsics (cfg
, cmethod
, fsig
, args
);
1708 } else if (in_corlib
&&
1709 !strcmp (cmethod_klass_name_space
, "Internal.Runtime.CompilerServices") &&
1710 !strcmp (cmethod_klass_name
, "Unsafe")) {
1711 return emit_unsafe_intrinsics (cfg
, cmethod
, fsig
, args
);
1712 } else if (!strcmp (cmethod_klass_name_space
, "System.Runtime.CompilerServices") &&
1713 !strcmp (cmethod_klass_name
, "Unsafe") &&
1714 (in_corlib
|| !strcmp (cmethod_klass_image
->assembly
->aname
.name
, "System.Runtime.CompilerServices.Unsafe"))) {
1715 return emit_unsafe_intrinsics (cfg
, cmethod
, fsig
, args
);
1716 } else if (in_corlib
&&
1717 !strcmp (cmethod_klass_name_space
, "System.Runtime.CompilerServices") &&
1718 !strcmp (cmethod_klass_name
, "JitHelpers")) {
1719 return emit_jit_helpers_intrinsics (cfg
, cmethod
, fsig
, args
);
1722 #ifdef MONO_ARCH_SIMD_INTRINSICS
1723 if (cfg
->opt
& MONO_OPT_SIMD
) {
1724 ins
= mono_emit_simd_intrinsics (cfg
, cmethod
, fsig
, args
);
1730 /* Fallback if SIMD is disabled */
1731 if (in_corlib
&& !strcmp ("System.Numerics", cmethod_klass_name_space
) && !strcmp ("Vector", cmethod_klass_name
)) {
1732 if (!strcmp (cmethod
->name
, "get_IsHardwareAccelerated")) {
1733 EMIT_NEW_ICONST (cfg
, ins
, 0);
1734 ins
->type
= STACK_I4
;
1739 /* Workaround for the compiler server IsMemoryAvailable. */
1740 if (!strcmp ("Microsoft.CodeAnalysis.CompilerServer", cmethod_klass_name_space
) && !strcmp ("MemoryHelper", cmethod_klass_name
)) {
1741 if (!strcmp (cmethod
->name
, "IsMemoryAvailable")) {
1742 EMIT_NEW_ICONST (cfg
, ins
, 1);
1743 ins
->type
= STACK_I4
;
1748 #ifdef ENABLE_NETCORE
1749 // Return false for IsSupported for all types in System.Runtime.Intrinsics.X86
1750 // as we don't support them now
1752 !strcmp ("System.Runtime.Intrinsics.X86", cmethod_klass_name_space
) &&
1753 !strcmp (cmethod
->name
, "get_IsSupported")) {
1754 EMIT_NEW_ICONST (cfg
, ins
, 0);
1755 ins
->type
= STACK_I4
;
1760 ins
= mono_emit_native_types_intrinsics (cfg
, cmethod
, fsig
, args
);
1764 if (COMPILE_LLVM (cfg
)) {
1765 ins
= llvm_emit_inst_for_method (cfg
, cmethod
, fsig
, args
, in_corlib
);
1770 return mono_arch_emit_inst_for_method (cfg
, cmethod
, fsig
, args
);
1775 emit_array_unsafe_access (MonoCompile
*cfg
, MonoMethodSignature
*fsig
, MonoInst
**args
, int is_set
)
1780 eklass
= mono_class_from_mono_type_internal (fsig
->params
[2]);
1782 eklass
= mono_class_from_mono_type_internal (fsig
->ret
);
1785 return mini_emit_array_store (cfg
, eklass
, args
, FALSE
);
1787 MonoInst
*ins
, *addr
= mini_emit_ldelema_1_ins (cfg
, eklass
, args
[0], args
[1], FALSE
);
1788 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg
, ins
, m_class_get_byval_arg (eklass
), addr
->dreg
, 0);
1794 is_unsafe_mov_compatible (MonoCompile
*cfg
, MonoClass
*param_klass
, MonoClass
*return_klass
)
1797 int param_size
, return_size
;
1799 param_klass
= mono_class_from_mono_type_internal (mini_get_underlying_type (m_class_get_byval_arg (param_klass
)));
1800 return_klass
= mono_class_from_mono_type_internal (mini_get_underlying_type (m_class_get_byval_arg (return_klass
)));
1802 if (cfg
->verbose_level
> 3)
1803 printf ("[UNSAFE-MOV-INTRISIC] %s <- %s\n", m_class_get_name (return_klass
), m_class_get_name (param_klass
));
1805 //Don't allow mixing reference types with value types
1806 if (m_class_is_valuetype (param_klass
) != m_class_is_valuetype (return_klass
)) {
1807 if (cfg
->verbose_level
> 3)
1808 printf ("[UNSAFE-MOV-INTRISIC]\tone of the args is a valuetype and the other is not\n");
1812 if (!m_class_is_valuetype (param_klass
)) {
1813 if (cfg
->verbose_level
> 3)
1814 printf ("[UNSAFE-MOV-INTRISIC]\targs are reference types\n");
1819 if (m_class_has_references (param_klass
) || m_class_has_references (return_klass
))
1822 MonoType
*param_type
= m_class_get_byval_arg (param_klass
);
1823 MonoType
*return_type
= m_class_get_byval_arg (return_klass
);
1825 /* Avoid mixing structs and primitive types/enums, they need to be handled differently in the JIT */
1826 if ((MONO_TYPE_ISSTRUCT (param_type
) && !MONO_TYPE_ISSTRUCT (return_type
)) ||
1827 (!MONO_TYPE_ISSTRUCT (param_type
) && MONO_TYPE_ISSTRUCT (return_type
))) {
1828 if (cfg
->verbose_level
> 3)
1829 printf ("[UNSAFE-MOV-INTRISIC]\tmixing structs and scalars\n");
1833 if (param_type
->type
== MONO_TYPE_R4
|| param_type
->type
== MONO_TYPE_R8
||
1834 return_type
->type
== MONO_TYPE_R4
|| return_type
->type
== MONO_TYPE_R8
) {
1835 if (cfg
->verbose_level
> 3)
1836 printf ("[UNSAFE-MOV-INTRISIC]\tfloat or double are not supported\n");
1840 param_size
= mono_class_value_size (param_klass
, &align
);
1841 return_size
= mono_class_value_size (return_klass
, &align
);
1843 //We can do it if sizes match
1844 if (param_size
== return_size
) {
1845 if (cfg
->verbose_level
> 3)
1846 printf ("[UNSAFE-MOV-INTRISIC]\tsame size\n");
1850 //No simple way to handle struct if sizes don't match
1851 if (MONO_TYPE_ISSTRUCT (param_type
)) {
1852 if (cfg
->verbose_level
> 3)
1853 printf ("[UNSAFE-MOV-INTRISIC]\tsize mismatch and type is a struct\n");
1858 * Same reg size category.
1859 * A quick note on why we don't require widening here.
1860 * The intrinsic is "R Array.UnsafeMov<S,R> (S s)".
1862 * Since the source value comes from a function argument, the JIT will already have
1863 * the value in a VREG and performed any widening needed before (say, when loading from a field).
1865 if (param_size
<= 4 && return_size
<= 4) {
1866 if (cfg
->verbose_level
> 3)
1867 printf ("[UNSAFE-MOV-INTRISIC]\tsize mismatch but both are of the same reg class\n");
1875 emit_array_unsafe_mov (MonoCompile
*cfg
, MonoMethodSignature
*fsig
, MonoInst
**args
)
1877 MonoClass
*param_klass
= mono_class_from_mono_type_internal (fsig
->params
[0]);
1878 MonoClass
*return_klass
= mono_class_from_mono_type_internal (fsig
->ret
);
1880 if (mini_is_gsharedvt_variable_type (fsig
->ret
))
1883 //Valuetypes that are semantically equivalent or numbers than can be widened to
1884 if (is_unsafe_mov_compatible (cfg
, param_klass
, return_klass
))
1887 //Arrays of valuetypes that are semantically equivalent
1888 if (m_class_get_rank (param_klass
) == 1 && m_class_get_rank (return_klass
) == 1 && is_unsafe_mov_compatible (cfg
, m_class_get_element_class (param_klass
), m_class_get_element_class (return_klass
)))
1895 mini_emit_inst_for_sharable_method (MonoCompile
*cfg
, MonoMethod
*cmethod
, MonoMethodSignature
*fsig
, MonoInst
**args
)
1897 if (cmethod
->klass
== mono_defaults
.array_class
) {
1898 if (strcmp (cmethod
->name
, "UnsafeStore") == 0)
1899 return emit_array_unsafe_access (cfg
, fsig
, args
, TRUE
);
1900 else if (strcmp (cmethod
->name
, "UnsafeLoad") == 0)
1901 return emit_array_unsafe_access (cfg
, fsig
, args
, FALSE
);
1902 else if (strcmp (cmethod
->name
, "UnsafeMov") == 0)
1903 return emit_array_unsafe_mov (cfg
, fsig
, args
);
1910 mini_emit_inst_for_field_load (MonoCompile
*cfg
, MonoClassField
*field
)
1912 MonoClass
*klass
= field
->parent
;
1913 const char *klass_name_space
= m_class_get_name_space (klass
);
1914 const char *klass_name
= m_class_get_name (klass
);
1915 MonoImage
*klass_image
= m_class_get_image (klass
);
1916 gboolean in_corlib
= klass_image
== mono_defaults
.corlib
;
1920 if (in_corlib
&& !strcmp (klass_name_space
, "System") && !strcmp (klass_name
, "BitConverter") && !strcmp (field
->name
, "IsLittleEndian")) {
1921 is_le
= (TARGET_BYTE_ORDER
== G_LITTLE_ENDIAN
);
1922 EMIT_NEW_ICONST (cfg
, ins
, is_le
);
1925 #ifdef ENABLE_NETCORE
1926 else if ((klass
== mono_defaults
.int_class
|| klass
== mono_defaults
.uint_class
) && strcmp (field
->name
, "Zero") == 0) {
1927 EMIT_NEW_PCONST (cfg
, ins
, 0);
1934 MONO_EMPTY_SOURCE_FILE (intrinsics
);