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
;
112 // Convert Math and MathF methods into LLVM intrinsics, e.g. MathF.Sin -> @llvm.sin.f32
113 if (in_corlib
&& !strcmp (m_class_get_name (cmethod
->klass
), "MathF") && cfg
->r4fp
) {
115 if (fsig
->param_count
== 1 && fsig
->params
[0]->type
== MONO_TYPE_R4
) {
116 if (!strcmp (cmethod
->name
, "Ceiling")) {
118 } else if (!strcmp (cmethod
->name
, "Cos")) {
120 } else if (!strcmp (cmethod
->name
, "Exp")) {
122 } else if (!strcmp (cmethod
->name
, "Floor")) {
124 } else if (!strcmp (cmethod
->name
, "Log2")) {
126 } else if (!strcmp (cmethod
->name
, "Log10")) {
128 } else if (!strcmp (cmethod
->name
, "Sin")) {
130 } else if (!strcmp (cmethod
->name
, "Sqrt")) {
132 } else if (!strcmp (cmethod
->name
, "Truncate")) {
137 if (fsig
->param_count
== 2 && fsig
->params
[0]->type
== MONO_TYPE_R4
&& fsig
->params
[1]->type
== MONO_TYPE_R4
) {
138 if (!strcmp (cmethod
->name
, "Pow")) {
140 } else if (!strcmp (cmethod
->name
, "CopySign")) {
141 opcode
= OP_RCOPYSIGN
;
144 // (float, float, float)
145 if (fsig
->param_count
== 3 && fsig
->params
[0]->type
== MONO_TYPE_R4
&& fsig
->params
[1]->type
== MONO_TYPE_R4
&& fsig
->params
[2]->type
== MONO_TYPE_R4
) {
146 if (!strcmp (cmethod
->name
, "FusedMultiplyAdd")) {
152 MONO_INST_NEW (cfg
, ins
, opcode
);
153 ins
->type
= STACK_R8
;
154 ins
->dreg
= mono_alloc_dreg (cfg
, (MonoStackType
)ins
->type
);
155 ins
->sreg1
= args
[0]->dreg
;
156 if (fsig
->param_count
> 1) {
157 ins
->sreg2
= args
[1]->dreg
;
159 if (fsig
->param_count
> 2) {
160 ins
->sreg3
= args
[2]->dreg
;
162 g_assert (fsig
->param_count
<= 3);
163 MONO_ADD_INS (cfg
->cbb
, ins
);
167 if (cmethod
->klass
== mono_class_try_get_math_class ()) {
169 if (fsig
->param_count
== 1 && fsig
->params
[0]->type
== MONO_TYPE_R8
) {
170 if (!strcmp (cmethod
->name
, "Abs")) {
172 } else if (!strcmp (cmethod
->name
, "Ceiling")) {
174 } else if (!strcmp (cmethod
->name
, "Cos")) {
176 } else if (!strcmp (cmethod
->name
, "Exp")) {
178 } else if (!strcmp (cmethod
->name
, "Floor")) {
180 } else if (!strcmp (cmethod
->name
, "Log")) {
182 } else if (!strcmp (cmethod
->name
, "Log2")) {
184 } else if (!strcmp (cmethod
->name
, "Log10")) {
186 } else if (!strcmp (cmethod
->name
, "Sin")) {
188 } else if (!strcmp (cmethod
->name
, "Sqrt")) {
190 } else if (!strcmp (cmethod
->name
, "Truncate")) {
195 if (fsig
->param_count
== 2 && fsig
->params
[0]->type
== MONO_TYPE_R8
&& fsig
->params
[1]->type
== MONO_TYPE_R8
) {
196 // Max and Min can only be optimized in fast math mode
197 if (!strcmp (cmethod
->name
, "Max") && mono_use_fast_math
) {
199 } else if (!strcmp (cmethod
->name
, "Min") && mono_use_fast_math
) {
201 } else if (!strcmp (cmethod
->name
, "Pow")) {
203 } else if (!strcmp (cmethod
->name
, "CopySign")) {
204 opcode
= OP_FCOPYSIGN
;
207 // (double, double, double)
208 if (fsig
->param_count
== 3 && fsig
->params
[0]->type
== MONO_TYPE_R8
&& fsig
->params
[1]->type
== MONO_TYPE_R8
&& fsig
->params
[2]->type
== MONO_TYPE_R8
) {
209 if (!strcmp (cmethod
->name
, "FusedMultiplyAdd")) {
214 // Math also contains overloads for floats (MathF inlines them)
216 if (fsig
->param_count
== 1 && fsig
->params
[0]->type
== MONO_TYPE_R4
) {
217 if (!strcmp (cmethod
->name
, "Abs")) {
222 if (fsig
->param_count
== 2 && fsig
->params
[0]->type
== MONO_TYPE_R4
&& fsig
->params
[1]->type
== MONO_TYPE_R4
) {
223 if (!strcmp (cmethod
->name
, "Max") && mono_use_fast_math
) {
225 } else if (!strcmp (cmethod
->name
, "Min") && mono_use_fast_math
) {
227 } else if (!strcmp (cmethod
->name
, "Pow")) {
232 if (opcode
&& fsig
->param_count
> 0) {
233 MONO_INST_NEW (cfg
, ins
, opcode
);
234 ins
->type
= STACK_R8
;
235 ins
->dreg
= mono_alloc_dreg (cfg
, (MonoStackType
)ins
->type
);
236 ins
->sreg1
= args
[0]->dreg
;
237 if (fsig
->param_count
> 1) {
238 ins
->sreg2
= args
[1]->dreg
;
240 if (fsig
->param_count
> 2) {
241 ins
->sreg3
= args
[2]->dreg
;
243 g_assert (fsig
->param_count
<= 3);
244 MONO_ADD_INS (cfg
->cbb
, ins
);
248 if (cfg
->opt
& MONO_OPT_CMOV
) {
249 if (strcmp (cmethod
->name
, "Min") == 0) {
250 if (fsig
->params
[0]->type
== MONO_TYPE_I4
)
252 if (fsig
->params
[0]->type
== MONO_TYPE_U4
)
254 else if (fsig
->params
[0]->type
== MONO_TYPE_I8
)
256 else if (fsig
->params
[0]->type
== MONO_TYPE_U8
)
258 } else if (strcmp (cmethod
->name
, "Max") == 0) {
259 if (fsig
->params
[0]->type
== MONO_TYPE_I4
)
261 if (fsig
->params
[0]->type
== MONO_TYPE_U4
)
263 else if (fsig
->params
[0]->type
== MONO_TYPE_I8
)
265 else if (fsig
->params
[0]->type
== MONO_TYPE_U8
)
270 if (opcode
&& fsig
->param_count
== 2) {
271 MONO_INST_NEW (cfg
, ins
, opcode
);
272 ins
->type
= fsig
->params
[0]->type
== MONO_TYPE_I4
? STACK_I4
: STACK_I8
;
273 ins
->dreg
= mono_alloc_dreg (cfg
, (MonoStackType
)ins
->type
);
274 ins
->sreg1
= args
[0]->dreg
;
275 ins
->sreg2
= args
[1]->dreg
;
276 MONO_ADD_INS (cfg
->cbb
, ins
);
280 if (in_corlib
&& !strcmp (m_class_get_name (cmethod
->klass
), "Buffer")) {
281 if (!strcmp (cmethod
->name
, "Memmove") && fsig
->param_count
== 3 && fsig
->params
[0]->type
== MONO_TYPE_PTR
&& fsig
->params
[1]->type
== MONO_TYPE_PTR
) {
282 // throw NRE if src or dst are nulls
283 MONO_EMIT_NEW_BIALU_IMM (cfg
, OP_COMPARE_IMM
, -1, args
[0]->dreg
, 0);
284 MONO_EMIT_NEW_COND_EXC (cfg
, EQ
, "NullReferenceException");
285 MONO_EMIT_NEW_BIALU_IMM (cfg
, OP_COMPARE_IMM
, -1, args
[1]->dreg
, 0);
286 MONO_EMIT_NEW_COND_EXC (cfg
, EQ
, "NullReferenceException");
288 MONO_INST_NEW (cfg
, ins
, OP_MEMMOVE
);
289 ins
->sreg1
= args
[0]->dreg
; // i1* dst
290 ins
->sreg2
= args
[1]->dreg
; // i1* src
291 ins
->sreg3
= args
[2]->dreg
; // i32/i64 len
292 MONO_ADD_INS (cfg
->cbb
, ins
);
300 emit_span_intrinsics (MonoCompile
*cfg
, MonoMethod
*cmethod
, MonoMethodSignature
*fsig
, MonoInst
**args
)
304 MonoClassField
*ptr_field
= mono_class_get_field_from_name_full (cmethod
->klass
, "_pointer", NULL
);
306 /* Portable Span<T> */
309 if (!strcmp (cmethod
->name
, "get_Item")) {
310 MonoClassField
*length_field
= mono_class_get_field_from_name_full (cmethod
->klass
, "_length", NULL
);
312 g_assert (length_field
);
314 MonoGenericClass
*gclass
= mono_class_get_generic_class (cmethod
->klass
);
315 MonoClass
*param_class
= mono_class_from_mono_type_internal (gclass
->context
.class_inst
->type_argv
[0]);
317 if (mini_is_gsharedvt_variable_klass (param_class
))
320 int span_reg
= args
[0]->dreg
;
321 /* Load _pointer.Value */
322 int base_reg
= alloc_preg (cfg
);
323 EMIT_NEW_LOAD_MEMBASE (cfg
, ins
, OP_LOAD_MEMBASE
, base_reg
, span_reg
, ptr_field
->offset
- MONO_ABI_SIZEOF (MonoObject
));
324 /* Similar to mini_emit_ldelema_1_ins () */
325 int size
= mono_class_array_element_size (param_class
);
327 int index_reg
= mini_emit_sext_index_reg (cfg
, args
[1]);
329 mini_emit_bounds_check_offset (cfg
, span_reg
, length_field
->offset
- MONO_ABI_SIZEOF (MonoObject
), index_reg
, NULL
);
331 // FIXME: Sign extend index ?
333 int mult_reg
= alloc_preg (cfg
);
334 int add_reg
= alloc_preg (cfg
);
336 MONO_EMIT_NEW_BIALU_IMM (cfg
, OP_MUL_IMM
, mult_reg
, index_reg
, size
);
337 EMIT_NEW_BIALU (cfg
, ins
, OP_PADD
, add_reg
, base_reg
, mult_reg
);
338 ins
->klass
= param_class
;
339 ins
->type
= STACK_MP
;
342 } else if (!strcmp (cmethod
->name
, "get_Length")) {
343 MonoClassField
*length_field
= mono_class_get_field_from_name_full (cmethod
->klass
, "_length", NULL
);
344 g_assert (length_field
);
347 * FIXME: This doesn't work with abcrem, since the src is a unique LDADDR not
348 * the same array object.
350 MONO_INST_NEW (cfg
, ins
, OP_LDLEN
);
351 ins
->dreg
= alloc_preg (cfg
);
352 ins
->sreg1
= args
[0]->dreg
;
353 ins
->inst_imm
= length_field
->offset
- MONO_ABI_SIZEOF (MonoObject
);
354 ins
->type
= STACK_I4
;
355 MONO_ADD_INS (cfg
->cbb
, ins
);
357 cfg
->flags
|= MONO_CFG_NEEDS_DECOMPOSE
;
358 cfg
->cbb
->needs_decompose
= TRUE
;
367 emit_unsafe_intrinsics (MonoCompile
*cfg
, MonoMethod
*cmethod
, MonoMethodSignature
*fsig
, MonoInst
**args
)
371 MonoGenericContext
*ctx
= mono_method_get_context (cmethod
);
374 if (!strcmp (cmethod
->name
, "As")) {
376 g_assert (ctx
->method_inst
);
378 t
= ctx
->method_inst
->type_argv
[0];
379 if (mini_is_gsharedvt_variable_type (t
))
381 if (ctx
->method_inst
->type_argc
== 2) {
382 dreg
= alloc_preg (cfg
);
383 EMIT_NEW_UNALU (cfg
, ins
, OP_MOVE
, dreg
, args
[0]->dreg
);
384 ins
->type
= STACK_OBJ
;
385 ins
->klass
= mono_get_object_class ();
387 } else if (ctx
->method_inst
->type_argc
== 1) {
388 // Casts the given object to the specified type, performs no dynamic type checking.
389 g_assert (fsig
->param_count
== 1);
390 g_assert (fsig
->params
[0]->type
== MONO_TYPE_OBJECT
);
391 dreg
= alloc_preg (cfg
);
392 EMIT_NEW_UNALU (cfg
, ins
, OP_MOVE
, dreg
, args
[0]->dreg
);
393 ins
->type
= STACK_OBJ
;
394 ins
->klass
= mono_class_from_mono_type_internal (ctx
->method_inst
->type_argv
[0]);
397 } else if (!strcmp (cmethod
->name
, "AsPointer")) {
399 g_assert (ctx
->method_inst
);
400 g_assert (ctx
->method_inst
->type_argc
== 1);
401 g_assert (fsig
->param_count
== 1);
403 dreg
= alloc_preg (cfg
);
404 EMIT_NEW_UNALU (cfg
, ins
, OP_MOVE
, dreg
, args
[0]->dreg
);
405 ins
->type
= STACK_PTR
;
407 } else if (!strcmp (cmethod
->name
, "AsRef")) {
409 g_assert (ctx
->method_inst
);
410 g_assert (ctx
->method_inst
->type_argc
== 1);
411 g_assert (fsig
->param_count
== 1);
413 dreg
= alloc_preg (cfg
);
414 EMIT_NEW_UNALU (cfg
, ins
, OP_MOVE
, dreg
, args
[0]->dreg
);
415 ins
->type
= STACK_OBJ
;
416 ins
->klass
= mono_get_object_class ();
418 } else if (!strcmp (cmethod
->name
, "AreSame")) {
420 g_assert (ctx
->method_inst
);
421 g_assert (ctx
->method_inst
->type_argc
== 1);
422 g_assert (fsig
->param_count
== 2);
424 dreg
= alloc_ireg (cfg
);
425 EMIT_NEW_BIALU (cfg
, ins
, OP_COMPARE
, -1, args
[0]->dreg
, args
[1]->dreg
);
426 EMIT_NEW_UNALU (cfg
, ins
, OP_PCEQ
, dreg
, -1);
428 } else if (!strcmp (cmethod
->name
, "IsAddressLessThan")) {
430 g_assert (ctx
->method_inst
);
431 g_assert (ctx
->method_inst
->type_argc
== 1);
432 g_assert (fsig
->param_count
== 2);
434 dreg
= alloc_ireg (cfg
);
435 EMIT_NEW_BIALU (cfg
, ins
, OP_COMPARE
, -1, args
[0]->dreg
, args
[1]->dreg
);
436 EMIT_NEW_UNALU (cfg
, ins
, OP_PCLT_UN
, dreg
, -1);
438 } else if (!strcmp (cmethod
->name
, "IsAddressGreaterThan")) {
440 g_assert (ctx
->method_inst
);
441 g_assert (ctx
->method_inst
->type_argc
== 1);
442 g_assert (fsig
->param_count
== 2);
444 dreg
= alloc_ireg (cfg
);
445 EMIT_NEW_BIALU (cfg
, ins
, OP_COMPARE
, -1, args
[0]->dreg
, args
[1]->dreg
);
446 EMIT_NEW_UNALU (cfg
, ins
, OP_PCGT_UN
, dreg
, -1);
448 } else if (!strcmp (cmethod
->name
, "Add")) {
450 g_assert (ctx
->method_inst
);
451 g_assert (ctx
->method_inst
->type_argc
== 1);
452 g_assert (fsig
->param_count
== 2);
454 int mul_reg
= alloc_preg (cfg
);
456 t
= ctx
->method_inst
->type_argv
[0];
458 if (mini_is_gsharedvt_variable_type (t
)) {
459 esize_ins
= mini_emit_get_gsharedvt_info_klass (cfg
, mono_class_from_mono_type_internal (t
), MONO_RGCTX_INFO_CLASS_SIZEOF
);
460 if (SIZEOF_REGISTER
== 8)
461 MONO_EMIT_NEW_UNALU (cfg
, OP_SEXT_I4
, esize_ins
->dreg
, esize_ins
->dreg
);
463 t
= mini_type_get_underlying_type (t
);
464 int esize
= mono_class_array_element_size (mono_class_from_mono_type_internal (t
));
465 EMIT_NEW_ICONST (cfg
, esize_ins
, esize
);
467 esize_ins
->type
= STACK_I4
;
469 EMIT_NEW_BIALU (cfg
, ins
, OP_PMUL
, mul_reg
, args
[1]->dreg
, esize_ins
->dreg
);
470 ins
->type
= STACK_PTR
;
472 dreg
= alloc_preg (cfg
);
473 EMIT_NEW_BIALU (cfg
, ins
, OP_PADD
, dreg
, args
[0]->dreg
, mul_reg
);
474 ins
->type
= STACK_PTR
;
476 } else if (!strcmp (cmethod
->name
, "AddByteOffset")) {
478 g_assert (ctx
->method_inst
);
479 g_assert (ctx
->method_inst
->type_argc
== 1);
480 g_assert (fsig
->param_count
== 2);
482 if (fsig
->params
[1]->type
== MONO_TYPE_I
) {
483 int dreg
= alloc_preg (cfg
);
484 EMIT_NEW_BIALU (cfg
, ins
, OP_PADD
, dreg
, args
[0]->dreg
, args
[1]->dreg
);
485 ins
->type
= STACK_PTR
;
487 } else if (fsig
->params
[1]->type
== MONO_TYPE_U8
) {
488 int sreg
= args
[1]->dreg
;
489 if (SIZEOF_REGISTER
== 4) {
490 sreg
= alloc_ireg (cfg
);
491 EMIT_NEW_UNALU (cfg
, ins
, OP_LCONV_TO_U4
, sreg
, args
[1]->dreg
);
493 int dreg
= alloc_preg (cfg
);
494 EMIT_NEW_BIALU (cfg
, ins
, OP_PADD
, dreg
, args
[0]->dreg
, sreg
);
495 ins
->type
= STACK_PTR
;
498 } else if (!strcmp (cmethod
->name
, "SizeOf")) {
500 g_assert (ctx
->method_inst
);
501 g_assert (ctx
->method_inst
->type_argc
== 1);
502 g_assert (fsig
->param_count
== 0);
504 t
= ctx
->method_inst
->type_argv
[0];
505 if (mini_is_gsharedvt_variable_type (t
)) {
506 ins
= mini_emit_get_gsharedvt_info_klass (cfg
, mono_class_from_mono_type_internal (t
), MONO_RGCTX_INFO_CLASS_SIZEOF
);
508 int esize
= mono_type_size (t
, &align
);
509 EMIT_NEW_ICONST (cfg
, ins
, esize
);
511 ins
->type
= STACK_I4
;
513 } else if (!strcmp (cmethod
->name
, "ReadUnaligned")) {
515 g_assert (ctx
->method_inst
);
516 g_assert (ctx
->method_inst
->type_argc
== 1);
517 g_assert (fsig
->param_count
== 1);
519 t
= ctx
->method_inst
->type_argv
[0];
520 t
= mini_get_underlying_type (t
);
521 if (MONO_TYPE_IS_PRIMITIVE (t
) && t
->type
!= MONO_TYPE_R4
&& t
->type
!= MONO_TYPE_R8
) {
522 dreg
= alloc_ireg (cfg
);
523 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg
, ins
, t
, args
[0]->dreg
, 0);
524 ins
->type
= STACK_I4
;
525 ins
->flags
|= MONO_INST_UNALIGNED
;
528 } else if (!strcmp (cmethod
->name
, "WriteUnaligned")) {
530 g_assert (ctx
->method_inst
);
531 g_assert (ctx
->method_inst
->type_argc
== 1);
532 g_assert (fsig
->param_count
== 2);
534 t
= ctx
->method_inst
->type_argv
[0];
535 t
= mini_get_underlying_type (t
);
536 if (MONO_TYPE_IS_PRIMITIVE (t
) && t
->type
!= MONO_TYPE_R4
&& t
->type
!= MONO_TYPE_R8
) {
537 dreg
= alloc_ireg (cfg
);
538 EMIT_NEW_STORE_MEMBASE_TYPE (cfg
, ins
, t
, args
[0]->dreg
, 0, args
[1]->dreg
);
539 ins
->flags
|= MONO_INST_UNALIGNED
;
542 } else if (!strcmp (cmethod
->name
, "ByteOffset")) {
544 g_assert (ctx
->method_inst
);
545 g_assert (ctx
->method_inst
->type_argc
== 1);
546 g_assert (fsig
->param_count
== 2);
548 int dreg
= alloc_preg (cfg
);
549 EMIT_NEW_BIALU (cfg
, ins
, OP_PSUB
, dreg
, args
[1]->dreg
, args
[0]->dreg
);
550 ins
->type
= STACK_PTR
;
553 #ifdef ENABLE_NETCORE
554 else if (!strcmp (cmethod
->name
, "InitBlockUnaligned")) {
555 g_assert (fsig
->param_count
== 3);
557 mini_emit_memory_init_bytes (cfg
, args
[0], args
[1], args
[2], MONO_INST_UNALIGNED
);
558 MONO_INST_NEW (cfg
, ins
, OP_NOP
);
559 MONO_ADD_INS (cfg
->cbb
, ins
);
568 emit_jit_helpers_intrinsics (MonoCompile
*cfg
, MonoMethod
*cmethod
, MonoMethodSignature
*fsig
, MonoInst
**args
)
572 MonoGenericContext
*ctx
= mono_method_get_context (cmethod
);
575 if (!strcmp (cmethod
->name
, "EnumEquals") || !strcmp (cmethod
->name
, "EnumCompareTo")) {
577 g_assert (ctx
->method_inst
);
578 g_assert (ctx
->method_inst
->type_argc
== 1);
579 g_assert (fsig
->param_count
== 2);
581 t
= ctx
->method_inst
->type_argv
[0];
582 t
= mini_get_underlying_type (t
);
583 if (mini_is_gsharedvt_variable_type (t
))
586 gboolean is_i8
= (t
->type
== MONO_TYPE_I8
|| t
->type
== MONO_TYPE_U8
);
587 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
);
588 int cmp_op
, ceq_op
, cgt_op
, clt_op
;
591 cmp_op
= OP_LCOMPARE
;
593 cgt_op
= is_unsigned
? OP_LCGT_UN
: OP_LCGT
;
594 clt_op
= is_unsigned
? OP_LCLT_UN
: OP_LCLT
;
596 cmp_op
= OP_ICOMPARE
;
598 cgt_op
= is_unsigned
? OP_ICGT_UN
: OP_ICGT
;
599 clt_op
= is_unsigned
? OP_ICLT_UN
: OP_ICLT
;
602 if (!strcmp (cmethod
->name
, "EnumEquals")) {
603 dreg
= alloc_ireg (cfg
);
604 EMIT_NEW_BIALU (cfg
, ins
, cmp_op
, -1, args
[0]->dreg
, args
[1]->dreg
);
605 EMIT_NEW_UNALU (cfg
, ins
, ceq_op
, dreg
, -1);
607 // Use the branchless code (a > b) - (a < b)
610 reg1
= alloc_ireg (cfg
);
611 reg2
= alloc_ireg (cfg
);
612 dreg
= alloc_ireg (cfg
);
614 EMIT_NEW_BIALU (cfg
, ins
, cmp_op
, -1, args
[0]->dreg
, args
[1]->dreg
);
615 EMIT_NEW_UNALU (cfg
, ins
, cgt_op
, reg1
, -1);
616 EMIT_NEW_BIALU (cfg
, ins
, cmp_op
, -1, args
[0]->dreg
, args
[1]->dreg
);
617 EMIT_NEW_UNALU (cfg
, ins
, clt_op
, reg2
, -1);
618 EMIT_NEW_BIALU (cfg
, ins
, OP_ISUB
, dreg
, reg1
, reg2
);
627 mini_emit_inst_for_method (MonoCompile
*cfg
, MonoMethod
*cmethod
, MonoMethodSignature
*fsig
, MonoInst
**args
)
629 MonoInst
*ins
= NULL
;
630 MonoClass
*runtime_helpers_class
= mono_class_get_runtime_helpers_class ();
632 const char* cmethod_klass_name_space
= m_class_get_name_space (cmethod
->klass
);
633 const char* cmethod_klass_name
= m_class_get_name (cmethod
->klass
);
634 MonoImage
*cmethod_klass_image
= m_class_get_image (cmethod
->klass
);
635 gboolean in_corlib
= cmethod_klass_image
== mono_defaults
.corlib
;
637 /* Required intrinsics are always used even with -O=-intrins */
639 !strcmp (cmethod_klass_name_space
, "System") &&
640 !strcmp (cmethod_klass_name
, "ByReference`1") &&
641 !strcmp (cmethod
->name
, "get_Value")) {
642 g_assert (fsig
->hasthis
&& fsig
->param_count
== 0);
643 int dreg
= alloc_preg (cfg
);
644 EMIT_NEW_LOAD_MEMBASE (cfg
, ins
, OP_LOAD_MEMBASE
, dreg
, args
[0]->dreg
, 0);
646 } else if (in_corlib
&& cmethod
->klass
== mono_defaults
.object_class
) {
647 if (!strcmp (cmethod
->name
, "GetRawData")) {
648 int dreg
= alloc_preg (cfg
);
649 EMIT_NEW_BIALU_IMM (cfg
, ins
, OP_PADD_IMM
, dreg
, args
[0]->dreg
, MONO_ABI_SIZEOF (MonoObject
));
654 if (!(cfg
->opt
& MONO_OPT_INTRINS
))
657 if (cmethod
->klass
== mono_defaults
.string_class
) {
658 if (strcmp (cmethod
->name
, "get_Chars") == 0 && fsig
->param_count
+ fsig
->hasthis
== 2) {
659 int dreg
= alloc_ireg (cfg
);
660 int index_reg
= alloc_preg (cfg
);
661 int add_reg
= alloc_preg (cfg
);
663 #if SIZEOF_REGISTER == 8
664 if (COMPILE_LLVM (cfg
)) {
665 MONO_EMIT_NEW_UNALU (cfg
, OP_ZEXT_I4
, index_reg
, args
[1]->dreg
);
667 /* The array reg is 64 bits but the index reg is only 32 */
668 MONO_EMIT_NEW_UNALU (cfg
, OP_SEXT_I4
, index_reg
, args
[1]->dreg
);
671 index_reg
= args
[1]->dreg
;
673 MONO_EMIT_BOUNDS_CHECK (cfg
, args
[0]->dreg
, MonoString
, length
, index_reg
);
675 #if defined(TARGET_X86) || defined(TARGET_AMD64)
676 EMIT_NEW_X86_LEA (cfg
, ins
, args
[0]->dreg
, index_reg
, 1, MONO_STRUCT_OFFSET (MonoString
, chars
));
678 EMIT_NEW_LOAD_MEMBASE (cfg
, ins
, OP_LOADU2_MEMBASE
, dreg
,
681 int mult_reg
= alloc_preg (cfg
);
682 MONO_EMIT_NEW_BIALU_IMM (cfg
, OP_SHL_IMM
, mult_reg
, index_reg
, 1);
683 MONO_EMIT_NEW_BIALU (cfg
, OP_PADD
, add_reg
, mult_reg
, args
[0]->dreg
);
684 EMIT_NEW_LOAD_MEMBASE (cfg
, ins
, OP_LOADU2_MEMBASE
, dreg
,
685 add_reg
, MONO_STRUCT_OFFSET (MonoString
, chars
));
687 mini_type_from_op (cfg
, ins
, NULL
, NULL
);
689 } else if (strcmp (cmethod
->name
, "get_Length") == 0 && fsig
->param_count
+ fsig
->hasthis
== 1) {
690 int dreg
= alloc_ireg (cfg
);
691 /* Decompose later to allow more optimizations */
692 EMIT_NEW_UNALU (cfg
, ins
, OP_STRLEN
, dreg
, args
[0]->dreg
);
693 ins
->type
= STACK_I4
;
694 ins
->flags
|= MONO_INST_FAULT
;
695 cfg
->cbb
->needs_decompose
= TRUE
;
696 cfg
->flags
|= MONO_CFG_NEEDS_DECOMPOSE
;
701 } else if (cmethod
->klass
== mono_defaults
.object_class
) {
702 if (strcmp (cmethod
->name
, "GetType") == 0 && fsig
->param_count
+ fsig
->hasthis
== 1) {
703 int dreg
= alloc_ireg_ref (cfg
);
704 int vt_reg
= alloc_preg (cfg
);
705 MONO_EMIT_NEW_LOAD_MEMBASE_FAULT (cfg
, vt_reg
, args
[0]->dreg
, MONO_STRUCT_OFFSET (MonoObject
, vtable
));
706 EMIT_NEW_LOAD_MEMBASE (cfg
, ins
, OP_LOAD_MEMBASE
, dreg
, vt_reg
, MONO_STRUCT_OFFSET (MonoVTable
, type
));
707 mini_type_from_op (cfg
, ins
, NULL
, NULL
);
710 } else if (!cfg
->backend
->emulate_mul_div
&& strcmp (cmethod
->name
, "InternalGetHashCode") == 0 && fsig
->param_count
== 1 && !mono_gc_is_moving ()) {
711 int dreg
= alloc_ireg (cfg
);
712 int t1
= alloc_ireg (cfg
);
714 MONO_EMIT_NEW_BIALU_IMM (cfg
, OP_SHL_IMM
, t1
, args
[0]->dreg
, 3);
715 EMIT_NEW_BIALU_IMM (cfg
, ins
, OP_MUL_IMM
, dreg
, t1
, 2654435761u);
716 ins
->type
= STACK_I4
;
719 } else if (strcmp (cmethod
->name
, ".ctor") == 0 && fsig
->param_count
== 0) {
720 MONO_INST_NEW (cfg
, ins
, OP_NOP
);
721 MONO_ADD_INS (cfg
->cbb
, ins
);
725 } else if (cmethod
->klass
== mono_defaults
.array_class
) {
726 if (strcmp (cmethod
->name
, "GetGenericValueImpl") == 0 && fsig
->param_count
+ fsig
->hasthis
== 3 && !cfg
->gsharedvt
)
727 return emit_array_generic_access (cfg
, fsig
, args
, FALSE
);
728 else if (strcmp (cmethod
->name
, "SetGenericValueImpl") == 0 && fsig
->param_count
+ fsig
->hasthis
== 3 && !cfg
->gsharedvt
)
729 return emit_array_generic_access (cfg
, fsig
, args
, TRUE
);
730 else if (!strcmp (cmethod
->name
, "GetRawSzArrayData")) {
731 int dreg
= alloc_preg (cfg
);
732 EMIT_NEW_BIALU_IMM (cfg
, ins
, OP_PADD_IMM
, dreg
, args
[0]->dreg
, MONO_STRUCT_OFFSET (MonoArray
, vector
));
736 #ifndef MONO_BIG_ARRAYS
738 * This is an inline version of GetLength/GetLowerBound(0) used frequently in
741 else if (((strcmp (cmethod
->name
, "GetLength") == 0 && fsig
->param_count
+ fsig
->hasthis
== 2) ||
742 (strcmp (cmethod
->name
, "GetLowerBound") == 0 && fsig
->param_count
+ fsig
->hasthis
== 2)) &&
743 args
[1]->opcode
== OP_ICONST
&& args
[1]->inst_c0
== 0) {
744 int dreg
= alloc_ireg (cfg
);
745 int bounds_reg
= alloc_ireg_mp (cfg
);
746 MonoBasicBlock
*end_bb
, *szarray_bb
;
747 gboolean get_length
= strcmp (cmethod
->name
, "GetLength") == 0;
749 NEW_BBLOCK (cfg
, end_bb
);
750 NEW_BBLOCK (cfg
, szarray_bb
);
752 EMIT_NEW_LOAD_MEMBASE_FAULT (cfg
, ins
, OP_LOAD_MEMBASE
, bounds_reg
,
753 args
[0]->dreg
, MONO_STRUCT_OFFSET (MonoArray
, bounds
));
754 MONO_EMIT_NEW_BIALU_IMM (cfg
, OP_COMPARE_IMM
, -1, bounds_reg
, 0);
755 MONO_EMIT_NEW_BRANCH_BLOCK (cfg
, OP_IBEQ
, szarray_bb
);
756 /* Non-szarray case */
758 EMIT_NEW_LOAD_MEMBASE (cfg
, ins
, OP_LOADI4_MEMBASE
, dreg
,
759 bounds_reg
, MONO_STRUCT_OFFSET (MonoArrayBounds
, length
));
761 EMIT_NEW_LOAD_MEMBASE (cfg
, ins
, OP_LOADI4_MEMBASE
, dreg
,
762 bounds_reg
, MONO_STRUCT_OFFSET (MonoArrayBounds
, lower_bound
));
763 MONO_EMIT_NEW_BRANCH_BLOCK (cfg
, OP_BR
, end_bb
);
764 MONO_START_BB (cfg
, szarray_bb
);
767 EMIT_NEW_LOAD_MEMBASE (cfg
, ins
, OP_LOADI4_MEMBASE
, dreg
,
768 args
[0]->dreg
, MONO_STRUCT_OFFSET (MonoArray
, max_length
));
770 MONO_EMIT_NEW_ICONST (cfg
, dreg
, 0);
771 MONO_START_BB (cfg
, end_bb
);
773 EMIT_NEW_UNALU (cfg
, ins
, OP_MOVE
, dreg
, dreg
);
774 ins
->type
= STACK_I4
;
780 if (cmethod
->name
[0] != 'g')
783 if (strcmp (cmethod
->name
, "get_Rank") == 0 && fsig
->param_count
+ fsig
->hasthis
== 1) {
784 int dreg
= alloc_ireg (cfg
);
785 int vtable_reg
= alloc_preg (cfg
);
786 MONO_EMIT_NEW_LOAD_MEMBASE_OP_FAULT (cfg
, OP_LOAD_MEMBASE
, vtable_reg
,
787 args
[0]->dreg
, MONO_STRUCT_OFFSET (MonoObject
, vtable
));
788 EMIT_NEW_LOAD_MEMBASE (cfg
, ins
, OP_LOADU1_MEMBASE
, dreg
,
789 vtable_reg
, MONO_STRUCT_OFFSET (MonoVTable
, rank
));
790 mini_type_from_op (cfg
, ins
, NULL
, NULL
);
793 } else if (strcmp (cmethod
->name
, "get_Length") == 0 && fsig
->param_count
+ fsig
->hasthis
== 1) {
794 int dreg
= alloc_ireg (cfg
);
796 EMIT_NEW_LOAD_MEMBASE_FAULT (cfg
, ins
, OP_LOADI4_MEMBASE
, dreg
,
797 args
[0]->dreg
, MONO_STRUCT_OFFSET (MonoArray
, max_length
));
798 mini_type_from_op (cfg
, ins
, NULL
, NULL
);
803 } else if (cmethod
->klass
== runtime_helpers_class
) {
804 if (strcmp (cmethod
->name
, "get_OffsetToStringData") == 0 && fsig
->param_count
== 0) {
805 EMIT_NEW_ICONST (cfg
, ins
, MONO_STRUCT_OFFSET (MonoString
, chars
));
807 } else if (strcmp (cmethod
->name
, "IsReferenceOrContainsReferences") == 0 && fsig
->param_count
== 0) {
808 MonoGenericContext
*ctx
= mono_method_get_context (cmethod
);
810 g_assert (ctx
->method_inst
);
811 g_assert (ctx
->method_inst
->type_argc
== 1);
812 MonoType
*arg_type
= ctx
->method_inst
->type_argv
[0];
818 /* Resolve the argument class as possible so we can handle common cases fast */
819 t
= mini_get_underlying_type (arg_type
);
820 klass
= mono_class_from_mono_type_internal (t
);
821 mono_class_init_internal (klass
);
822 if (MONO_TYPE_IS_REFERENCE (t
))
823 EMIT_NEW_ICONST (cfg
, ins
, 1);
824 else if (MONO_TYPE_IS_PRIMITIVE (t
))
825 EMIT_NEW_ICONST (cfg
, ins
, 0);
826 else if (cfg
->gshared
&& (t
->type
== MONO_TYPE_VAR
|| t
->type
== MONO_TYPE_MVAR
) && !mini_type_var_is_vt (t
))
827 EMIT_NEW_ICONST (cfg
, ins
, 1);
828 else if (!cfg
->gshared
|| !mini_class_check_context_used (cfg
, klass
))
829 EMIT_NEW_ICONST (cfg
, ins
, m_class_has_references (klass
) ? 1 : 0);
831 g_assert (cfg
->gshared
);
833 /* Have to use the original argument class here */
834 MonoClass
*arg_class
= mono_class_from_mono_type_internal (arg_type
);
835 int context_used
= mini_class_check_context_used (cfg
, arg_class
);
837 /* This returns 1 or 2 */
838 MonoInst
*info
= mini_emit_get_rgctx_klass (cfg
, context_used
, arg_class
, MONO_RGCTX_INFO_CLASS_IS_REF_OR_CONTAINS_REFS
);
839 int dreg
= alloc_ireg (cfg
);
840 EMIT_NEW_BIALU_IMM (cfg
, ins
, OP_ISUB_IMM
, dreg
, info
->dreg
, 1);
844 } else if (strcmp (cmethod
->name
, "IsBitwiseEquatable") == 0 && fsig
->param_count
== 0) {
845 MonoGenericContext
*ctx
= mono_method_get_context (cmethod
);
847 g_assert (ctx
->method_inst
);
848 g_assert (ctx
->method_inst
->type_argc
== 1);
849 MonoType
*arg_type
= ctx
->method_inst
->type_argv
[0];
853 /* Resolve the argument class as possible so we can handle common cases fast */
854 t
= mini_get_underlying_type (arg_type
);
856 if (MONO_TYPE_IS_PRIMITIVE (t
) && t
->type
!= MONO_TYPE_R4
&& t
->type
!= MONO_TYPE_R8
)
857 EMIT_NEW_ICONST (cfg
, ins
, 1);
859 EMIT_NEW_ICONST (cfg
, ins
, 0);
861 } else if (!strcmp (cmethod
->name
, "ObjectHasComponentSize")) {
862 g_assert (fsig
->param_count
== 1);
863 g_assert (fsig
->params
[0]->type
== MONO_TYPE_OBJECT
);
864 // Return true for arrays and string
867 dreg
= alloc_ireg (cfg
);
869 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg
, OP_LOAD_MEMBASE
, dreg
, args
[0]->dreg
, MONO_STRUCT_OFFSET (MonoObject
, vtable
));
870 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg
, OP_LOADU1_MEMBASE
, dreg
, dreg
, MONO_STRUCT_OFFSET (MonoVTable
, flags
));
871 MONO_EMIT_NEW_BIALU_IMM (cfg
, OP_IAND_IMM
, dreg
, dreg
, MONO_VT_FLAG_ARRAY_OR_STRING
);
872 EMIT_NEW_BIALU_IMM (cfg
, ins
, OP_COMPARE_IMM
, -1, dreg
, 0);
873 EMIT_NEW_UNALU (cfg
, ins
, OP_ICGT
, dreg
, -1);
874 ins
->type
= STACK_I4
;
878 } else if (cmethod
->klass
== mono_defaults
.monitor_class
) {
879 gboolean is_enter
= FALSE
;
880 gboolean is_v4
= FALSE
;
882 if (!strcmp (cmethod
->name
, "Enter") && fsig
->param_count
== 2 && fsig
->params
[1]->byref
) {
886 if (!strcmp (cmethod
->name
, "Enter") && fsig
->param_count
== 1)
891 * To make async stack traces work, icalls which can block should have a wrapper.
892 * For Monitor.Enter, emit two calls: a fastpath which doesn't have a wrapper, and a slowpath, which does.
894 MonoBasicBlock
*end_bb
;
896 NEW_BBLOCK (cfg
, end_bb
);
899 ins
= mono_emit_jit_icall (cfg
, mono_monitor_enter_v4_fast
, args
);
901 ins
= mono_emit_jit_icall (cfg
, mono_monitor_enter_fast
, args
);
903 MONO_EMIT_NEW_BIALU_IMM (cfg
, OP_ICOMPARE_IMM
, -1, ins
->dreg
, 0);
904 MONO_EMIT_NEW_BRANCH_BLOCK (cfg
, OP_IBNE_UN
, end_bb
);
907 ins
= mono_emit_jit_icall (cfg
, mono_monitor_enter_v4_internal
, args
);
909 ins
= mono_emit_jit_icall (cfg
, mono_monitor_enter_internal
, args
);
911 MONO_START_BB (cfg
, end_bb
);
914 } else if (cmethod
->klass
== mono_defaults
.thread_class
) {
915 if (strcmp (cmethod
->name
, "SpinWait_nop") == 0 && fsig
->param_count
== 0) {
916 MONO_INST_NEW (cfg
, ins
, OP_RELAXED_NOP
);
917 MONO_ADD_INS (cfg
->cbb
, ins
);
919 } else if (strcmp (cmethod
->name
, "MemoryBarrier") == 0 && fsig
->param_count
== 0) {
920 return mini_emit_memory_barrier (cfg
, MONO_MEMORY_BARRIER_SEQ
);
921 } else if (!strcmp (cmethod
->name
, "VolatileRead") && fsig
->param_count
== 1) {
923 gboolean is_ref
= mini_type_is_reference (fsig
->params
[0]);
925 if (fsig
->params
[0]->type
== MONO_TYPE_I1
)
926 opcode
= OP_LOADI1_MEMBASE
;
927 else if (fsig
->params
[0]->type
== MONO_TYPE_U1
)
928 opcode
= OP_LOADU1_MEMBASE
;
929 else if (fsig
->params
[0]->type
== MONO_TYPE_I2
)
930 opcode
= OP_LOADI2_MEMBASE
;
931 else if (fsig
->params
[0]->type
== MONO_TYPE_U2
)
932 opcode
= OP_LOADU2_MEMBASE
;
933 else if (fsig
->params
[0]->type
== MONO_TYPE_I4
)
934 opcode
= OP_LOADI4_MEMBASE
;
935 else if (fsig
->params
[0]->type
== MONO_TYPE_U4
)
936 opcode
= OP_LOADU4_MEMBASE
;
937 else if (fsig
->params
[0]->type
== MONO_TYPE_I8
|| fsig
->params
[0]->type
== MONO_TYPE_U8
)
938 opcode
= OP_LOADI8_MEMBASE
;
939 else if (fsig
->params
[0]->type
== MONO_TYPE_R4
)
940 opcode
= OP_LOADR4_MEMBASE
;
941 else if (fsig
->params
[0]->type
== MONO_TYPE_R8
)
942 opcode
= OP_LOADR8_MEMBASE
;
943 else if (is_ref
|| fsig
->params
[0]->type
== MONO_TYPE_I
|| fsig
->params
[0]->type
== MONO_TYPE_U
)
944 opcode
= OP_LOAD_MEMBASE
;
947 MONO_INST_NEW (cfg
, ins
, opcode
);
948 ins
->inst_basereg
= args
[0]->dreg
;
949 ins
->inst_offset
= 0;
950 MONO_ADD_INS (cfg
->cbb
, ins
);
952 switch (fsig
->params
[0]->type
) {
959 ins
->dreg
= mono_alloc_ireg (cfg
);
960 ins
->type
= STACK_I4
;
964 ins
->dreg
= mono_alloc_lreg (cfg
);
965 ins
->type
= STACK_I8
;
969 ins
->dreg
= mono_alloc_ireg (cfg
);
970 #if SIZEOF_REGISTER == 8
971 ins
->type
= STACK_I8
;
973 ins
->type
= STACK_I4
;
978 ins
->dreg
= mono_alloc_freg (cfg
);
979 ins
->type
= STACK_R8
;
982 g_assert (mini_type_is_reference (fsig
->params
[0]));
983 ins
->dreg
= mono_alloc_ireg_ref (cfg
);
984 ins
->type
= STACK_OBJ
;
988 if (opcode
== OP_LOADI8_MEMBASE
)
989 ins
= mono_decompose_opcode (cfg
, ins
);
991 mini_emit_memory_barrier (cfg
, MONO_MEMORY_BARRIER_SEQ
);
995 } else if (!strcmp (cmethod
->name
, "VolatileWrite") && fsig
->param_count
== 2) {
997 gboolean is_ref
= mini_type_is_reference (fsig
->params
[0]);
999 if (fsig
->params
[0]->type
== MONO_TYPE_I1
|| fsig
->params
[0]->type
== MONO_TYPE_U1
)
1000 opcode
= OP_STOREI1_MEMBASE_REG
;
1001 else if (fsig
->params
[0]->type
== MONO_TYPE_I2
|| fsig
->params
[0]->type
== MONO_TYPE_U2
)
1002 opcode
= OP_STOREI2_MEMBASE_REG
;
1003 else if (fsig
->params
[0]->type
== MONO_TYPE_I4
|| fsig
->params
[0]->type
== MONO_TYPE_U4
)
1004 opcode
= OP_STOREI4_MEMBASE_REG
;
1005 else if (fsig
->params
[0]->type
== MONO_TYPE_I8
|| fsig
->params
[0]->type
== MONO_TYPE_U8
)
1006 opcode
= OP_STOREI8_MEMBASE_REG
;
1007 else if (fsig
->params
[0]->type
== MONO_TYPE_R4
)
1008 opcode
= OP_STORER4_MEMBASE_REG
;
1009 else if (fsig
->params
[0]->type
== MONO_TYPE_R8
)
1010 opcode
= OP_STORER8_MEMBASE_REG
;
1011 else if (is_ref
|| fsig
->params
[0]->type
== MONO_TYPE_I
|| fsig
->params
[0]->type
== MONO_TYPE_U
)
1012 opcode
= OP_STORE_MEMBASE_REG
;
1015 mini_emit_memory_barrier (cfg
, MONO_MEMORY_BARRIER_SEQ
);
1017 MONO_INST_NEW (cfg
, ins
, opcode
);
1018 ins
->sreg1
= args
[1]->dreg
;
1019 ins
->inst_destbasereg
= args
[0]->dreg
;
1020 ins
->inst_offset
= 0;
1021 MONO_ADD_INS (cfg
->cbb
, ins
);
1023 if (opcode
== OP_STOREI8_MEMBASE_REG
)
1024 ins
= mono_decompose_opcode (cfg
, ins
);
1029 } else if (in_corlib
&&
1030 (strcmp (cmethod_klass_name_space
, "System.Threading") == 0) &&
1031 (strcmp (cmethod_klass_name
, "Interlocked") == 0)) {
1034 #if SIZEOF_REGISTER == 8
1035 if (!cfg
->llvm_only
&& strcmp (cmethod
->name
, "Read") == 0 && fsig
->param_count
== 1 && (fsig
->params
[0]->type
== MONO_TYPE_I8
)) {
1036 if (!cfg
->llvm_only
&& mono_arch_opcode_supported (OP_ATOMIC_LOAD_I8
)) {
1037 MONO_INST_NEW (cfg
, ins
, OP_ATOMIC_LOAD_I8
);
1038 ins
->dreg
= mono_alloc_preg (cfg
);
1039 ins
->sreg1
= args
[0]->dreg
;
1040 ins
->type
= STACK_I8
;
1041 ins
->backend
.memory_barrier_kind
= MONO_MEMORY_BARRIER_SEQ
;
1042 MONO_ADD_INS (cfg
->cbb
, ins
);
1046 mini_emit_memory_barrier (cfg
, MONO_MEMORY_BARRIER_SEQ
);
1048 /* 64 bit reads are already atomic */
1049 MONO_INST_NEW (cfg
, load_ins
, OP_LOADI8_MEMBASE
);
1050 load_ins
->dreg
= mono_alloc_preg (cfg
);
1051 load_ins
->inst_basereg
= args
[0]->dreg
;
1052 load_ins
->inst_offset
= 0;
1053 load_ins
->type
= STACK_I8
;
1054 MONO_ADD_INS (cfg
->cbb
, load_ins
);
1056 mini_emit_memory_barrier (cfg
, MONO_MEMORY_BARRIER_SEQ
);
1063 if (strcmp (cmethod
->name
, "Increment") == 0 && fsig
->param_count
== 1) {
1064 MonoInst
*ins_iconst
;
1067 if (fsig
->params
[0]->type
== MONO_TYPE_I4
) {
1068 opcode
= OP_ATOMIC_ADD_I4
;
1069 cfg
->has_atomic_add_i4
= TRUE
;
1071 #if SIZEOF_REGISTER == 8
1072 else if (fsig
->params
[0]->type
== MONO_TYPE_I8
)
1073 opcode
= OP_ATOMIC_ADD_I8
;
1076 if (!mono_arch_opcode_supported (opcode
))
1078 MONO_INST_NEW (cfg
, ins_iconst
, OP_ICONST
);
1079 ins_iconst
->inst_c0
= 1;
1080 ins_iconst
->dreg
= mono_alloc_ireg (cfg
);
1081 MONO_ADD_INS (cfg
->cbb
, ins_iconst
);
1083 MONO_INST_NEW (cfg
, ins
, opcode
);
1084 ins
->dreg
= mono_alloc_ireg (cfg
);
1085 ins
->inst_basereg
= args
[0]->dreg
;
1086 ins
->inst_offset
= 0;
1087 ins
->sreg2
= ins_iconst
->dreg
;
1088 ins
->type
= (opcode
== OP_ATOMIC_ADD_I4
) ? STACK_I4
: STACK_I8
;
1089 MONO_ADD_INS (cfg
->cbb
, ins
);
1091 } else if (strcmp (cmethod
->name
, "Decrement") == 0 && fsig
->param_count
== 1) {
1092 MonoInst
*ins_iconst
;
1095 if (fsig
->params
[0]->type
== MONO_TYPE_I4
) {
1096 opcode
= OP_ATOMIC_ADD_I4
;
1097 cfg
->has_atomic_add_i4
= TRUE
;
1099 #if SIZEOF_REGISTER == 8
1100 else if (fsig
->params
[0]->type
== MONO_TYPE_I8
)
1101 opcode
= OP_ATOMIC_ADD_I8
;
1104 if (!mono_arch_opcode_supported (opcode
))
1106 MONO_INST_NEW (cfg
, ins_iconst
, OP_ICONST
);
1107 ins_iconst
->inst_c0
= -1;
1108 ins_iconst
->dreg
= mono_alloc_ireg (cfg
);
1109 MONO_ADD_INS (cfg
->cbb
, ins_iconst
);
1111 MONO_INST_NEW (cfg
, ins
, opcode
);
1112 ins
->dreg
= mono_alloc_ireg (cfg
);
1113 ins
->inst_basereg
= args
[0]->dreg
;
1114 ins
->inst_offset
= 0;
1115 ins
->sreg2
= ins_iconst
->dreg
;
1116 ins
->type
= (opcode
== OP_ATOMIC_ADD_I4
) ? STACK_I4
: STACK_I8
;
1117 MONO_ADD_INS (cfg
->cbb
, ins
);
1119 } else if (strcmp (cmethod
->name
, "Add") == 0 && fsig
->param_count
== 2) {
1122 if (fsig
->params
[0]->type
== MONO_TYPE_I4
) {
1123 opcode
= OP_ATOMIC_ADD_I4
;
1124 cfg
->has_atomic_add_i4
= TRUE
;
1126 #if SIZEOF_REGISTER == 8
1127 else if (fsig
->params
[0]->type
== MONO_TYPE_I8
)
1128 opcode
= OP_ATOMIC_ADD_I8
;
1131 if (!mono_arch_opcode_supported (opcode
))
1133 MONO_INST_NEW (cfg
, ins
, opcode
);
1134 ins
->dreg
= mono_alloc_ireg (cfg
);
1135 ins
->inst_basereg
= args
[0]->dreg
;
1136 ins
->inst_offset
= 0;
1137 ins
->sreg2
= args
[1]->dreg
;
1138 ins
->type
= (opcode
== OP_ATOMIC_ADD_I4
) ? STACK_I4
: STACK_I8
;
1139 MONO_ADD_INS (cfg
->cbb
, ins
);
1142 else if (strcmp (cmethod
->name
, "Exchange") == 0 && fsig
->param_count
== 2) {
1143 MonoInst
*f2i
= NULL
, *i2f
;
1144 guint32 opcode
, f2i_opcode
, i2f_opcode
;
1145 gboolean is_ref
= mini_type_is_reference (fsig
->params
[0]);
1146 gboolean is_float
= fsig
->params
[0]->type
== MONO_TYPE_R4
|| fsig
->params
[0]->type
== MONO_TYPE_R8
;
1148 if (fsig
->params
[0]->type
== MONO_TYPE_I4
||
1149 fsig
->params
[0]->type
== MONO_TYPE_R4
) {
1150 opcode
= OP_ATOMIC_EXCHANGE_I4
;
1151 f2i_opcode
= OP_MOVE_F_TO_I4
;
1152 i2f_opcode
= OP_MOVE_I4_TO_F
;
1153 cfg
->has_atomic_exchange_i4
= TRUE
;
1155 #if SIZEOF_REGISTER == 8
1157 fsig
->params
[0]->type
== MONO_TYPE_I8
||
1158 fsig
->params
[0]->type
== MONO_TYPE_R8
||
1159 fsig
->params
[0]->type
== MONO_TYPE_I
) {
1160 opcode
= OP_ATOMIC_EXCHANGE_I8
;
1161 f2i_opcode
= OP_MOVE_F_TO_I8
;
1162 i2f_opcode
= OP_MOVE_I8_TO_F
;
1165 else if (is_ref
|| fsig
->params
[0]->type
== MONO_TYPE_I
) {
1166 opcode
= OP_ATOMIC_EXCHANGE_I4
;
1167 cfg
->has_atomic_exchange_i4
= TRUE
;
1173 if (!mono_arch_opcode_supported (opcode
))
1177 /* TODO: Decompose these opcodes instead of bailing here. */
1178 if (COMPILE_SOFT_FLOAT (cfg
))
1181 MONO_INST_NEW (cfg
, f2i
, f2i_opcode
);
1182 f2i
->dreg
= mono_alloc_ireg (cfg
);
1183 f2i
->sreg1
= args
[1]->dreg
;
1184 if (f2i_opcode
== OP_MOVE_F_TO_I4
)
1185 f2i
->backend
.spill_var
= mini_get_int_to_float_spill_area (cfg
);
1186 MONO_ADD_INS (cfg
->cbb
, f2i
);
1189 MONO_INST_NEW (cfg
, ins
, opcode
);
1190 ins
->dreg
= is_ref
? mono_alloc_ireg_ref (cfg
) : mono_alloc_ireg (cfg
);
1191 ins
->inst_basereg
= args
[0]->dreg
;
1192 ins
->inst_offset
= 0;
1193 ins
->sreg2
= is_float
? f2i
->dreg
: args
[1]->dreg
;
1194 MONO_ADD_INS (cfg
->cbb
, ins
);
1196 switch (fsig
->params
[0]->type
) {
1198 ins
->type
= STACK_I4
;
1201 ins
->type
= STACK_I8
;
1204 #if SIZEOF_REGISTER == 8
1205 ins
->type
= STACK_I8
;
1207 ins
->type
= STACK_I4
;
1212 ins
->type
= STACK_R8
;
1215 g_assert (mini_type_is_reference (fsig
->params
[0]));
1216 ins
->type
= STACK_OBJ
;
1221 MONO_INST_NEW (cfg
, i2f
, i2f_opcode
);
1222 i2f
->dreg
= mono_alloc_freg (cfg
);
1223 i2f
->sreg1
= ins
->dreg
;
1224 i2f
->type
= STACK_R8
;
1225 if (i2f_opcode
== OP_MOVE_I4_TO_F
)
1226 i2f
->backend
.spill_var
= mini_get_int_to_float_spill_area (cfg
);
1227 MONO_ADD_INS (cfg
->cbb
, i2f
);
1232 if (cfg
->gen_write_barriers
&& is_ref
)
1233 mini_emit_write_barrier (cfg
, args
[0], args
[1]);
1235 else if ((strcmp (cmethod
->name
, "CompareExchange") == 0) && fsig
->param_count
== 3) {
1236 MonoInst
*f2i_new
= NULL
, *f2i_cmp
= NULL
, *i2f
;
1237 guint32 opcode
, f2i_opcode
, i2f_opcode
;
1238 gboolean is_ref
= mini_type_is_reference (fsig
->params
[1]);
1239 gboolean is_float
= fsig
->params
[1]->type
== MONO_TYPE_R4
|| fsig
->params
[1]->type
== MONO_TYPE_R8
;
1241 if (fsig
->params
[1]->type
== MONO_TYPE_I4
||
1242 fsig
->params
[1]->type
== MONO_TYPE_R4
) {
1243 opcode
= OP_ATOMIC_CAS_I4
;
1244 f2i_opcode
= OP_MOVE_F_TO_I4
;
1245 i2f_opcode
= OP_MOVE_I4_TO_F
;
1246 cfg
->has_atomic_cas_i4
= TRUE
;
1248 #if SIZEOF_REGISTER == 8
1250 fsig
->params
[1]->type
== MONO_TYPE_I8
||
1251 fsig
->params
[1]->type
== MONO_TYPE_R8
||
1252 fsig
->params
[1]->type
== MONO_TYPE_I
) {
1253 opcode
= OP_ATOMIC_CAS_I8
;
1254 f2i_opcode
= OP_MOVE_F_TO_I8
;
1255 i2f_opcode
= OP_MOVE_I8_TO_F
;
1258 else if (is_ref
|| fsig
->params
[1]->type
== MONO_TYPE_I
) {
1259 opcode
= OP_ATOMIC_CAS_I4
;
1260 cfg
->has_atomic_cas_i4
= TRUE
;
1266 if (!mono_arch_opcode_supported (opcode
))
1270 /* TODO: Decompose these opcodes instead of bailing here. */
1271 if (COMPILE_SOFT_FLOAT (cfg
))
1274 MONO_INST_NEW (cfg
, f2i_new
, f2i_opcode
);
1275 f2i_new
->dreg
= mono_alloc_ireg (cfg
);
1276 f2i_new
->sreg1
= args
[1]->dreg
;
1277 if (f2i_opcode
== OP_MOVE_F_TO_I4
)
1278 f2i_new
->backend
.spill_var
= mini_get_int_to_float_spill_area (cfg
);
1279 MONO_ADD_INS (cfg
->cbb
, f2i_new
);
1281 MONO_INST_NEW (cfg
, f2i_cmp
, f2i_opcode
);
1282 f2i_cmp
->dreg
= mono_alloc_ireg (cfg
);
1283 f2i_cmp
->sreg1
= args
[2]->dreg
;
1284 if (f2i_opcode
== OP_MOVE_F_TO_I4
)
1285 f2i_cmp
->backend
.spill_var
= mini_get_int_to_float_spill_area (cfg
);
1286 MONO_ADD_INS (cfg
->cbb
, f2i_cmp
);
1289 MONO_INST_NEW (cfg
, ins
, opcode
);
1290 ins
->dreg
= is_ref
? alloc_ireg_ref (cfg
) : alloc_ireg (cfg
);
1291 ins
->sreg1
= args
[0]->dreg
;
1292 ins
->sreg2
= is_float
? f2i_new
->dreg
: args
[1]->dreg
;
1293 ins
->sreg3
= is_float
? f2i_cmp
->dreg
: args
[2]->dreg
;
1294 MONO_ADD_INS (cfg
->cbb
, ins
);
1296 switch (fsig
->params
[1]->type
) {
1298 ins
->type
= STACK_I4
;
1301 ins
->type
= STACK_I8
;
1304 #if SIZEOF_REGISTER == 8
1305 ins
->type
= STACK_I8
;
1307 ins
->type
= STACK_I4
;
1311 ins
->type
= cfg
->r4_stack_type
;
1314 ins
->type
= STACK_R8
;
1317 g_assert (mini_type_is_reference (fsig
->params
[1]));
1318 ins
->type
= STACK_OBJ
;
1323 MONO_INST_NEW (cfg
, i2f
, i2f_opcode
);
1324 i2f
->dreg
= mono_alloc_freg (cfg
);
1325 i2f
->sreg1
= ins
->dreg
;
1326 i2f
->type
= STACK_R8
;
1327 if (i2f_opcode
== OP_MOVE_I4_TO_F
)
1328 i2f
->backend
.spill_var
= mini_get_int_to_float_spill_area (cfg
);
1329 MONO_ADD_INS (cfg
->cbb
, i2f
);
1334 if (cfg
->gen_write_barriers
&& is_ref
)
1335 mini_emit_write_barrier (cfg
, args
[0], args
[1]);
1337 else if ((strcmp (cmethod
->name
, "CompareExchange") == 0) && fsig
->param_count
== 4 &&
1338 fsig
->params
[1]->type
== MONO_TYPE_I4
) {
1339 MonoInst
*cmp
, *ceq
;
1341 if (!mono_arch_opcode_supported (OP_ATOMIC_CAS_I4
))
1344 /* int32 r = CAS (location, value, comparand); */
1345 MONO_INST_NEW (cfg
, ins
, OP_ATOMIC_CAS_I4
);
1346 ins
->dreg
= alloc_ireg (cfg
);
1347 ins
->sreg1
= args
[0]->dreg
;
1348 ins
->sreg2
= args
[1]->dreg
;
1349 ins
->sreg3
= args
[2]->dreg
;
1350 ins
->type
= STACK_I4
;
1351 MONO_ADD_INS (cfg
->cbb
, ins
);
1353 /* bool result = r == comparand; */
1354 MONO_INST_NEW (cfg
, cmp
, OP_ICOMPARE
);
1355 cmp
->sreg1
= ins
->dreg
;
1356 cmp
->sreg2
= args
[2]->dreg
;
1357 cmp
->type
= STACK_I4
;
1358 MONO_ADD_INS (cfg
->cbb
, cmp
);
1360 MONO_INST_NEW (cfg
, ceq
, OP_ICEQ
);
1361 ceq
->dreg
= alloc_ireg (cfg
);
1362 ceq
->type
= STACK_I4
;
1363 MONO_ADD_INS (cfg
->cbb
, ceq
);
1365 /* *success = result; */
1366 MONO_EMIT_NEW_STORE_MEMBASE (cfg
, OP_STOREI1_MEMBASE_REG
, args
[3]->dreg
, 0, ceq
->dreg
);
1368 cfg
->has_atomic_cas_i4
= TRUE
;
1370 else if (strcmp (cmethod
->name
, "MemoryBarrier") == 0 && fsig
->param_count
== 0)
1371 ins
= mini_emit_memory_barrier (cfg
, MONO_MEMORY_BARRIER_SEQ
);
1375 } else if (in_corlib
&&
1376 (strcmp (cmethod_klass_name_space
, "System.Threading") == 0) &&
1377 (strcmp (cmethod_klass_name
, "Volatile") == 0)) {
1380 if (!cfg
->llvm_only
&& !strcmp (cmethod
->name
, "Read") && fsig
->param_count
== 1) {
1382 MonoType
*t
= fsig
->params
[0];
1384 gboolean is_float
= t
->type
== MONO_TYPE_R4
|| t
->type
== MONO_TYPE_R8
;
1386 g_assert (t
->byref
);
1387 /* t is a byref type, so the reference check is more complicated */
1388 is_ref
= mini_type_is_reference (m_class_get_byval_arg (mono_class_from_mono_type_internal (t
)));
1389 if (t
->type
== MONO_TYPE_I1
)
1390 opcode
= OP_ATOMIC_LOAD_I1
;
1391 else if (t
->type
== MONO_TYPE_U1
|| t
->type
== MONO_TYPE_BOOLEAN
)
1392 opcode
= OP_ATOMIC_LOAD_U1
;
1393 else if (t
->type
== MONO_TYPE_I2
)
1394 opcode
= OP_ATOMIC_LOAD_I2
;
1395 else if (t
->type
== MONO_TYPE_U2
)
1396 opcode
= OP_ATOMIC_LOAD_U2
;
1397 else if (t
->type
== MONO_TYPE_I4
)
1398 opcode
= OP_ATOMIC_LOAD_I4
;
1399 else if (t
->type
== MONO_TYPE_U4
)
1400 opcode
= OP_ATOMIC_LOAD_U4
;
1401 else if (t
->type
== MONO_TYPE_R4
)
1402 opcode
= OP_ATOMIC_LOAD_R4
;
1403 else if (t
->type
== MONO_TYPE_R8
)
1404 opcode
= OP_ATOMIC_LOAD_R8
;
1405 #if SIZEOF_REGISTER == 8
1406 else if (t
->type
== MONO_TYPE_I8
|| t
->type
== MONO_TYPE_I
)
1407 opcode
= OP_ATOMIC_LOAD_I8
;
1408 else if (is_ref
|| t
->type
== MONO_TYPE_U8
|| t
->type
== MONO_TYPE_U
)
1409 opcode
= OP_ATOMIC_LOAD_U8
;
1411 else if (t
->type
== MONO_TYPE_I
)
1412 opcode
= OP_ATOMIC_LOAD_I4
;
1413 else if (is_ref
|| t
->type
== MONO_TYPE_U
)
1414 opcode
= OP_ATOMIC_LOAD_U4
;
1418 if (!mono_arch_opcode_supported (opcode
))
1421 MONO_INST_NEW (cfg
, ins
, opcode
);
1422 ins
->dreg
= is_ref
? mono_alloc_ireg_ref (cfg
) : (is_float
? mono_alloc_freg (cfg
) : mono_alloc_ireg (cfg
));
1423 ins
->sreg1
= args
[0]->dreg
;
1424 ins
->backend
.memory_barrier_kind
= MONO_MEMORY_BARRIER_ACQ
;
1425 MONO_ADD_INS (cfg
->cbb
, ins
);
1428 case MONO_TYPE_BOOLEAN
:
1435 ins
->type
= STACK_I4
;
1439 ins
->type
= STACK_I8
;
1443 #if SIZEOF_REGISTER == 8
1444 ins
->type
= STACK_I8
;
1446 ins
->type
= STACK_I4
;
1450 ins
->type
= cfg
->r4_stack_type
;
1453 ins
->type
= STACK_R8
;
1457 ins
->type
= STACK_OBJ
;
1463 if (!cfg
->llvm_only
&& !strcmp (cmethod
->name
, "Write") && fsig
->param_count
== 2) {
1465 MonoType
*t
= fsig
->params
[0];
1468 g_assert (t
->byref
);
1469 is_ref
= mini_type_is_reference (m_class_get_byval_arg (mono_class_from_mono_type_internal (t
)));
1470 if (t
->type
== MONO_TYPE_I1
)
1471 opcode
= OP_ATOMIC_STORE_I1
;
1472 else if (t
->type
== MONO_TYPE_U1
|| t
->type
== MONO_TYPE_BOOLEAN
)
1473 opcode
= OP_ATOMIC_STORE_U1
;
1474 else if (t
->type
== MONO_TYPE_I2
)
1475 opcode
= OP_ATOMIC_STORE_I2
;
1476 else if (t
->type
== MONO_TYPE_U2
)
1477 opcode
= OP_ATOMIC_STORE_U2
;
1478 else if (t
->type
== MONO_TYPE_I4
)
1479 opcode
= OP_ATOMIC_STORE_I4
;
1480 else if (t
->type
== MONO_TYPE_U4
)
1481 opcode
= OP_ATOMIC_STORE_U4
;
1482 else if (t
->type
== MONO_TYPE_R4
)
1483 opcode
= OP_ATOMIC_STORE_R4
;
1484 else if (t
->type
== MONO_TYPE_R8
)
1485 opcode
= OP_ATOMIC_STORE_R8
;
1486 #if SIZEOF_REGISTER == 8
1487 else if (t
->type
== MONO_TYPE_I8
|| t
->type
== MONO_TYPE_I
)
1488 opcode
= OP_ATOMIC_STORE_I8
;
1489 else if (is_ref
|| t
->type
== MONO_TYPE_U8
|| t
->type
== MONO_TYPE_U
)
1490 opcode
= OP_ATOMIC_STORE_U8
;
1492 else if (t
->type
== MONO_TYPE_I
)
1493 opcode
= OP_ATOMIC_STORE_I4
;
1494 else if (is_ref
|| t
->type
== MONO_TYPE_U
)
1495 opcode
= OP_ATOMIC_STORE_U4
;
1499 if (!mono_arch_opcode_supported (opcode
))
1502 MONO_INST_NEW (cfg
, ins
, opcode
);
1503 ins
->dreg
= args
[0]->dreg
;
1504 ins
->sreg1
= args
[1]->dreg
;
1505 ins
->backend
.memory_barrier_kind
= MONO_MEMORY_BARRIER_REL
;
1506 MONO_ADD_INS (cfg
->cbb
, ins
);
1508 if (cfg
->gen_write_barriers
&& is_ref
)
1509 mini_emit_write_barrier (cfg
, args
[0], args
[1]);
1515 } else if (in_corlib
&&
1516 (strcmp (cmethod_klass_name_space
, "System.Diagnostics") == 0) &&
1517 (strcmp (cmethod_klass_name
, "Debugger") == 0)) {
1518 if (!strcmp (cmethod
->name
, "Break") && fsig
->param_count
== 0) {
1519 if (mini_should_insert_breakpoint (cfg
->method
)) {
1520 ins
= mono_emit_jit_icall (cfg
, mono_debugger_agent_user_break
, NULL
);
1522 MONO_INST_NEW (cfg
, ins
, OP_NOP
);
1523 MONO_ADD_INS (cfg
->cbb
, ins
);
1527 } else if (in_corlib
&&
1528 (strcmp (cmethod_klass_name_space
, "System") == 0) &&
1529 (strcmp (cmethod_klass_name
, "Environment") == 0)) {
1530 if (!strcmp (cmethod
->name
, "get_IsRunningOnWindows") && fsig
->param_count
== 0) {
1532 EMIT_NEW_ICONST (cfg
, ins
, 1);
1534 EMIT_NEW_ICONST (cfg
, ins
, 0);
1537 } else if (in_corlib
&&
1538 (strcmp (cmethod_klass_name_space
, "System.Reflection") == 0) &&
1539 (strcmp (cmethod_klass_name
, "Assembly") == 0)) {
1540 if (cfg
->llvm_only
&& !strcmp (cmethod
->name
, "GetExecutingAssembly")) {
1541 /* No stack walks are currently available, so implement this as an intrinsic */
1542 MonoInst
*assembly_ins
;
1544 EMIT_NEW_AOTCONST (cfg
, assembly_ins
, MONO_PATCH_INFO_IMAGE
, m_class_get_image (cfg
->method
->klass
));
1545 ins
= mono_emit_jit_icall (cfg
, mono_get_assembly_object
, &assembly_ins
);
1549 // While it is not required per
1550 // https://msdn.microsoft.com/en-us/library/system.reflection.assembly.getcallingassembly(v=vs.110).aspx.
1551 // have GetCallingAssembly be consistent independently of varying optimization.
1552 // This fixes mono/tests/test-inline-call-stack.cs under FullAOT+LLVM.
1553 cfg
->no_inline
|= COMPILE_LLVM (cfg
) && strcmp (cmethod
->name
, "GetCallingAssembly") == 0;
1555 } else if (in_corlib
&&
1556 (strcmp (cmethod_klass_name_space
, "System.Reflection") == 0) &&
1557 (strcmp (cmethod_klass_name
, "MethodBase") == 0)) {
1558 if (cfg
->llvm_only
&& !strcmp (cmethod
->name
, "GetCurrentMethod")) {
1559 /* No stack walks are currently available, so implement this as an intrinsic */
1560 MonoInst
*method_ins
;
1561 MonoMethod
*declaring
= cfg
->method
;
1563 /* This returns the declaring generic method */
1564 if (declaring
->is_inflated
)
1565 declaring
= ((MonoMethodInflated
*)cfg
->method
)->declaring
;
1566 EMIT_NEW_AOTCONST (cfg
, method_ins
, MONO_PATCH_INFO_METHODCONST
, declaring
);
1567 ins
= mono_emit_jit_icall (cfg
, mono_get_method_object
, &method_ins
);
1568 cfg
->no_inline
= TRUE
;
1569 if (cfg
->method
!= cfg
->current_method
)
1570 mini_set_inline_failure (cfg
, "MethodBase:GetCurrentMethod ()");
1573 } else if (cmethod
->klass
== mono_class_try_get_math_class ()) {
1575 * There is general branchless code for Min/Max, but it does not work for
1577 * http://everything2.com/?node_id=1051618
1581 * Constant folding for various Math methods.
1582 * we avoid folding constants that when computed would raise an error, in
1583 * case the user code was expecting to get that error raised
1585 if (fsig
->param_count
== 1 && args
[0]->opcode
== OP_R8CONST
){
1586 double source
= *(double *)args
[0]->inst_p0
;
1588 const char *mname
= cmethod
->name
;
1592 if (strcmp (mname
, "Abs") == 0 && fsig
->params
[0]->type
== MONO_TYPE_R8
) {
1594 } else if (strcmp (mname
, "Asin") == 0){
1595 if (fabs (source
) <= 1)
1597 } else if (strcmp (mname
, "Asinh") == 0){
1599 } else if (strcmp (mname
, "Acos") == 0){
1600 if (fabs (source
) <= 1)
1602 } else if (strcmp (mname
, "Acosh") == 0){
1605 } else if (strcmp (mname
, "Atan") == 0){
1607 } else if (strcmp (mname
, "Atanh") == 0){
1608 if (fabs (source
) < 1)
1611 } else if (c
== 'C'){
1612 if (strcmp (mname
, "Cos") == 0) {
1613 if (!isinf (source
))
1615 } else if (strcmp (mname
, "Cbrt") == 0){
1617 } else if (strcmp (mname
, "Cosh") == 0){
1620 } else if (c
== 'R'){
1621 if (strcmp (mname
, "Round") == 0)
1623 } else if (c
== 'S'){
1624 if (strcmp (mname
, "Sin") == 0) {
1625 if (!isinf (source
))
1627 } else if (strcmp (mname
, "Sqrt") == 0) {
1630 } else if (strcmp (mname
, "Sinh") == 0){
1633 } else if (c
== 'T'){
1634 if (strcmp (mname
, "Tan") == 0){
1635 if (!isinf (source
))
1637 } else if (strcmp (mname
, "Tanh") == 0){
1643 double *dest
= (double *) mono_domain_alloc (cfg
->domain
, sizeof (double));
1645 MONO_INST_NEW (cfg
, ins
, OP_R8CONST
);
1646 ins
->type
= STACK_R8
;
1647 ins
->dreg
= mono_alloc_dreg (cfg
, (MonoStackType
) ins
->type
);
1648 ins
->inst_p0
= dest
;
1652 result
= fabs (source
);
1655 result
= acos (source
);
1658 result
= acosh (source
);
1661 result
= asin (source
);
1664 result
= asinh (source
);
1667 result
= atan (source
);
1670 result
= atanh (source
);
1673 result
= cbrt (source
);
1676 result
= cos (source
);
1679 result
= cosh (source
);
1682 result
= round (source
);
1685 result
= sin (source
);
1688 result
= sinh (source
);
1691 result
= sqrt (source
);
1694 result
= tan (source
);
1697 result
= tanh (source
);
1700 g_error ("invalid opcode %d", (int)opcode
);
1703 MONO_ADD_INS (cfg
->cbb
, ins
);
1704 NULLIFY_INS (args
[0]);
1708 } else if (cmethod
->klass
== mono_defaults
.systemtype_class
&& !strcmp (cmethod
->name
, "op_Equality")) {
1709 EMIT_NEW_BIALU (cfg
, ins
, OP_COMPARE
, -1, args
[0]->dreg
, args
[1]->dreg
);
1710 MONO_INST_NEW (cfg
, ins
, OP_PCEQ
);
1711 ins
->dreg
= alloc_preg (cfg
);
1712 ins
->type
= STACK_I4
;
1713 MONO_ADD_INS (cfg
->cbb
, ins
);
1715 } else if (((!strcmp (cmethod_klass_image
->assembly
->aname
.name
, "MonoMac") ||
1716 !strcmp (cmethod_klass_image
->assembly
->aname
.name
, "monotouch")) &&
1717 !strcmp (cmethod_klass_name_space
, "XamCore.ObjCRuntime") &&
1718 !strcmp (cmethod_klass_name
, "Selector")) ||
1719 ((!strcmp (cmethod_klass_image
->assembly
->aname
.name
, "Xamarin.iOS") ||
1720 !strcmp (cmethod_klass_image
->assembly
->aname
.name
, "Xamarin.Mac")) &&
1721 !strcmp (cmethod_klass_name_space
, "ObjCRuntime") &&
1722 !strcmp (cmethod_klass_name
, "Selector"))
1724 if ((cfg
->backend
->have_objc_get_selector
|| cfg
->compile_llvm
) &&
1725 !strcmp (cmethod
->name
, "GetHandle") && fsig
->param_count
== 1 &&
1726 (args
[0]->opcode
== OP_GOT_ENTRY
|| args
[0]->opcode
== OP_AOTCONST
) &&
1729 MonoJumpInfoToken
*ji
;
1732 if (args
[0]->opcode
== OP_GOT_ENTRY
) {
1733 pi
= (MonoInst
*)args
[0]->inst_p1
;
1734 g_assert (pi
->opcode
== OP_PATCH_INFO
);
1735 g_assert (GPOINTER_TO_INT (pi
->inst_p1
) == MONO_PATCH_INFO_LDSTR
);
1736 ji
= (MonoJumpInfoToken
*)pi
->inst_p0
;
1738 g_assert (GPOINTER_TO_INT (args
[0]->inst_p1
) == MONO_PATCH_INFO_LDSTR
);
1739 ji
= (MonoJumpInfoToken
*)args
[0]->inst_p0
;
1742 NULLIFY_INS (args
[0]);
1744 s
= mono_ldstr_utf8 (ji
->image
, mono_metadata_token_index (ji
->token
), cfg
->error
);
1745 return_val_if_nok (cfg
->error
, NULL
);
1747 MONO_INST_NEW (cfg
, ins
, OP_OBJC_GET_SELECTOR
);
1748 ins
->dreg
= mono_alloc_ireg (cfg
);
1751 MONO_ADD_INS (cfg
->cbb
, ins
);
1754 } else if (in_corlib
&&
1755 (strcmp (cmethod_klass_name_space
, "System.Runtime.InteropServices") == 0) &&
1756 (strcmp (cmethod_klass_name
, "Marshal") == 0)) {
1757 //Convert Marshal.PtrToStructure<T> of blittable T to direct loads
1758 if (strcmp (cmethod
->name
, "PtrToStructure") == 0 &&
1759 cmethod
->is_inflated
&&
1760 fsig
->param_count
== 1 &&
1761 !mini_method_check_context_used (cfg
, cmethod
)) {
1763 MonoGenericContext
*method_context
= mono_method_get_context (cmethod
);
1764 MonoType
*arg0
= method_context
->method_inst
->type_argv
[0];
1765 if (mono_type_is_native_blittable (arg0
))
1766 return mini_emit_memory_load (cfg
, arg0
, args
[0], 0, 0);
1768 } else if (cmethod
->klass
== mono_defaults
.enum_class
&& !strcmp (cmethod
->name
, "HasFlag") &&
1769 args
[0]->opcode
== OP_BOX
&& args
[1]->opcode
== OP_BOX_ICONST
&& args
[0]->klass
== args
[1]->klass
) {
1770 args
[1]->opcode
= OP_ICONST
;
1771 ins
= mini_handle_enum_has_flag (cfg
, args
[0]->klass
, NULL
, args
[0]->sreg1
, args
[1]);
1772 NULLIFY_INS (args
[0]);
1774 } else if (in_corlib
&&
1775 !strcmp (cmethod_klass_name_space
, "System") &&
1776 (!strcmp (cmethod_klass_name
, "Span`1") || !strcmp (cmethod_klass_name
, "ReadOnlySpan`1"))) {
1777 return emit_span_intrinsics (cfg
, cmethod
, fsig
, args
);
1778 } else if (in_corlib
&&
1779 !strcmp (cmethod_klass_name_space
, "Internal.Runtime.CompilerServices") &&
1780 !strcmp (cmethod_klass_name
, "Unsafe")) {
1781 return emit_unsafe_intrinsics (cfg
, cmethod
, fsig
, args
);
1782 } else if (!strcmp (cmethod_klass_name_space
, "System.Runtime.CompilerServices") &&
1783 !strcmp (cmethod_klass_name
, "Unsafe") &&
1784 (in_corlib
|| !strcmp (cmethod_klass_image
->assembly
->aname
.name
, "System.Runtime.CompilerServices.Unsafe"))) {
1785 return emit_unsafe_intrinsics (cfg
, cmethod
, fsig
, args
);
1786 } else if (in_corlib
&&
1787 !strcmp (cmethod_klass_name_space
, "System.Runtime.CompilerServices") &&
1788 !strcmp (cmethod_klass_name
, "JitHelpers")) {
1789 return emit_jit_helpers_intrinsics (cfg
, cmethod
, fsig
, args
);
1792 #ifdef MONO_ARCH_SIMD_INTRINSICS
1793 if (cfg
->opt
& MONO_OPT_SIMD
) {
1794 ins
= mono_emit_simd_intrinsics (cfg
, cmethod
, fsig
, args
);
1800 /* Fallback if SIMD is disabled */
1801 if (in_corlib
&& !strcmp ("System.Numerics", cmethod_klass_name_space
) && !strcmp ("Vector", cmethod_klass_name
)) {
1802 if (!strcmp (cmethod
->name
, "get_IsHardwareAccelerated")) {
1803 EMIT_NEW_ICONST (cfg
, ins
, 0);
1804 ins
->type
= STACK_I4
;
1809 /* Workaround for the compiler server IsMemoryAvailable. */
1810 if (!strcmp ("Microsoft.CodeAnalysis.CompilerServer", cmethod_klass_name_space
) && !strcmp ("MemoryHelper", cmethod_klass_name
)) {
1811 if (!strcmp (cmethod
->name
, "IsMemoryAvailable")) {
1812 EMIT_NEW_ICONST (cfg
, ins
, 1);
1813 ins
->type
= STACK_I4
;
1818 #ifdef ENABLE_NETCORE
1819 // Return false for IsSupported for all types in System.Runtime.Intrinsics.X86
1820 // as we don't support them now
1822 !strcmp ("System.Runtime.Intrinsics.X86", cmethod_klass_name_space
) &&
1823 !strcmp (cmethod
->name
, "get_IsSupported")) {
1824 EMIT_NEW_ICONST (cfg
, ins
, 0);
1825 ins
->type
= STACK_I4
;
1830 ins
= mono_emit_native_types_intrinsics (cfg
, cmethod
, fsig
, args
);
1834 if (COMPILE_LLVM (cfg
)) {
1835 ins
= llvm_emit_inst_for_method (cfg
, cmethod
, fsig
, args
, in_corlib
);
1840 return mono_arch_emit_inst_for_method (cfg
, cmethod
, fsig
, args
);
1845 emit_array_unsafe_access (MonoCompile
*cfg
, MonoMethodSignature
*fsig
, MonoInst
**args
, int is_set
)
1850 eklass
= mono_class_from_mono_type_internal (fsig
->params
[2]);
1852 eklass
= mono_class_from_mono_type_internal (fsig
->ret
);
1855 return mini_emit_array_store (cfg
, eklass
, args
, FALSE
);
1857 MonoInst
*ins
, *addr
= mini_emit_ldelema_1_ins (cfg
, eklass
, args
[0], args
[1], FALSE
);
1858 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg
, ins
, m_class_get_byval_arg (eklass
), addr
->dreg
, 0);
1864 is_unsafe_mov_compatible (MonoCompile
*cfg
, MonoClass
*param_klass
, MonoClass
*return_klass
)
1867 int param_size
, return_size
;
1869 param_klass
= mono_class_from_mono_type_internal (mini_get_underlying_type (m_class_get_byval_arg (param_klass
)));
1870 return_klass
= mono_class_from_mono_type_internal (mini_get_underlying_type (m_class_get_byval_arg (return_klass
)));
1872 if (cfg
->verbose_level
> 3)
1873 printf ("[UNSAFE-MOV-INTRISIC] %s <- %s\n", m_class_get_name (return_klass
), m_class_get_name (param_klass
));
1875 //Don't allow mixing reference types with value types
1876 if (m_class_is_valuetype (param_klass
) != m_class_is_valuetype (return_klass
)) {
1877 if (cfg
->verbose_level
> 3)
1878 printf ("[UNSAFE-MOV-INTRISIC]\tone of the args is a valuetype and the other is not\n");
1882 if (!m_class_is_valuetype (param_klass
)) {
1883 if (cfg
->verbose_level
> 3)
1884 printf ("[UNSAFE-MOV-INTRISIC]\targs are reference types\n");
1889 if (m_class_has_references (param_klass
) || m_class_has_references (return_klass
))
1892 MonoType
*param_type
= m_class_get_byval_arg (param_klass
);
1893 MonoType
*return_type
= m_class_get_byval_arg (return_klass
);
1895 /* Avoid mixing structs and primitive types/enums, they need to be handled differently in the JIT */
1896 if ((MONO_TYPE_ISSTRUCT (param_type
) && !MONO_TYPE_ISSTRUCT (return_type
)) ||
1897 (!MONO_TYPE_ISSTRUCT (param_type
) && MONO_TYPE_ISSTRUCT (return_type
))) {
1898 if (cfg
->verbose_level
> 3)
1899 printf ("[UNSAFE-MOV-INTRISIC]\tmixing structs and scalars\n");
1903 if (param_type
->type
== MONO_TYPE_R4
|| param_type
->type
== MONO_TYPE_R8
||
1904 return_type
->type
== MONO_TYPE_R4
|| return_type
->type
== MONO_TYPE_R8
) {
1905 if (cfg
->verbose_level
> 3)
1906 printf ("[UNSAFE-MOV-INTRISIC]\tfloat or double are not supported\n");
1910 param_size
= mono_class_value_size (param_klass
, &align
);
1911 return_size
= mono_class_value_size (return_klass
, &align
);
1913 //We can do it if sizes match
1914 if (param_size
== return_size
) {
1915 if (cfg
->verbose_level
> 3)
1916 printf ("[UNSAFE-MOV-INTRISIC]\tsame size\n");
1920 //No simple way to handle struct if sizes don't match
1921 if (MONO_TYPE_ISSTRUCT (param_type
)) {
1922 if (cfg
->verbose_level
> 3)
1923 printf ("[UNSAFE-MOV-INTRISIC]\tsize mismatch and type is a struct\n");
1928 * Same reg size category.
1929 * A quick note on why we don't require widening here.
1930 * The intrinsic is "R Array.UnsafeMov<S,R> (S s)".
1932 * Since the source value comes from a function argument, the JIT will already have
1933 * the value in a VREG and performed any widening needed before (say, when loading from a field).
1935 if (param_size
<= 4 && return_size
<= 4) {
1936 if (cfg
->verbose_level
> 3)
1937 printf ("[UNSAFE-MOV-INTRISIC]\tsize mismatch but both are of the same reg class\n");
1945 emit_array_unsafe_mov (MonoCompile
*cfg
, MonoMethodSignature
*fsig
, MonoInst
**args
)
1947 MonoClass
*param_klass
= mono_class_from_mono_type_internal (fsig
->params
[0]);
1948 MonoClass
*return_klass
= mono_class_from_mono_type_internal (fsig
->ret
);
1950 if (mini_is_gsharedvt_variable_type (fsig
->ret
))
1953 //Valuetypes that are semantically equivalent or numbers than can be widened to
1954 if (is_unsafe_mov_compatible (cfg
, param_klass
, return_klass
))
1957 //Arrays of valuetypes that are semantically equivalent
1958 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
)))
1965 mini_emit_inst_for_sharable_method (MonoCompile
*cfg
, MonoMethod
*cmethod
, MonoMethodSignature
*fsig
, MonoInst
**args
)
1967 if (cmethod
->klass
== mono_defaults
.array_class
) {
1968 if (strcmp (cmethod
->name
, "UnsafeStore") == 0)
1969 return emit_array_unsafe_access (cfg
, fsig
, args
, TRUE
);
1970 else if (strcmp (cmethod
->name
, "UnsafeLoad") == 0)
1971 return emit_array_unsafe_access (cfg
, fsig
, args
, FALSE
);
1972 else if (strcmp (cmethod
->name
, "UnsafeMov") == 0)
1973 return emit_array_unsafe_mov (cfg
, fsig
, args
);
1980 mini_emit_inst_for_field_load (MonoCompile
*cfg
, MonoClassField
*field
)
1982 MonoClass
*klass
= field
->parent
;
1983 const char *klass_name_space
= m_class_get_name_space (klass
);
1984 const char *klass_name
= m_class_get_name (klass
);
1985 MonoImage
*klass_image
= m_class_get_image (klass
);
1986 gboolean in_corlib
= klass_image
== mono_defaults
.corlib
;
1990 if (in_corlib
&& !strcmp (klass_name_space
, "System") && !strcmp (klass_name
, "BitConverter") && !strcmp (field
->name
, "IsLittleEndian")) {
1991 is_le
= (TARGET_BYTE_ORDER
== G_LITTLE_ENDIAN
);
1992 EMIT_NEW_ICONST (cfg
, ins
, is_le
);
1995 #ifdef ENABLE_NETCORE
1996 else if ((klass
== mono_defaults
.int_class
|| klass
== mono_defaults
.uint_class
) && strcmp (field
->name
, "Zero") == 0) {
1997 EMIT_NEW_PCONST (cfg
, ins
, 0);
2004 MONO_EMPTY_SOURCE_FILE (intrinsics
);