[interp] Remove varargs from InterpFrame and recompute it instead (#16598)
[mono-project.git] / mono / mini / intrinsics.c
blob8eeb5513a641d7b8d252153bbae359df5e74e70f
1 /**
2 * Intrinsics support
3 */
5 #include <config.h>
6 #include <mono/utils/mono-compiler.h>
7 #include <math.h>
9 #ifndef DISABLE_JIT
11 #include "mini.h"
12 #include "mini-runtime.h"
13 #include "ir-emit.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 */
26 static MonoInst*
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);
35 if (is_set) {
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);
40 } else {
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);
44 return store;
47 static gboolean
48 mono_type_is_native_blittable (MonoType *t)
50 if (MONO_TYPE_IS_REFERENCE (t))
51 return FALSE;
53 if (MONO_TYPE_IS_PRIMITIVE_SCALAR (t))
54 return TRUE;
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))
61 return FALSE;
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))
65 return FALSE;
67 return TRUE;
70 MonoInst*
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;
77 MonoInst *ins = NULL;
79 /* Required intrinsics are always used even with -O=-intrins */
80 if (in_corlib &&
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);
86 return ins;
89 ins = mono_emit_native_types_intrinsics (cfg, cmethod, fsig, args);
90 if (ins)
91 return ins;
93 if (!(cfg->opt & MONO_OPT_INTRINS))
94 return NULL;
96 #ifdef MONO_ARCH_SIMD_INTRINSICS
97 if (cfg->opt & MONO_OPT_SIMD) {
98 ins = mono_emit_simd_intrinsics (cfg, cmethod, fsig, args);
99 if (ins)
100 return ins;
102 #endif
104 return NULL;
107 static MonoInst*
108 llvm_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args, gboolean in_corlib)
110 MonoInst *ins = NULL;
111 int opcode = 0;
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"))
115 opcode = OP_SINF;
116 else if (!strcmp (cmethod->name, "Cos"))
117 opcode = OP_COSF;
118 else if (!strcmp (cmethod->name, "Abs"))
119 opcode = OP_ABSF;
120 else if (!strcmp (cmethod->name, "Sqrt"))
121 opcode = OP_SQRTF;
122 else if (!strcmp (cmethod->name, "Floor"))
123 opcode = OP_FLOORF;
124 else if (!strcmp (cmethod->name, "Ceiling"))
125 opcode = OP_CEILF;
126 else if (!strcmp (cmethod->name, "FusedMultiplyAdd"))
127 opcode = OP_FMAF;
128 else if (!strcmp (cmethod->name, "Pow"))
129 opcode = OP_RPOW;
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) {
148 opcode = OP_SIN;
149 } else if (strcmp (cmethod->name, "Cos") == 0) {
150 opcode = OP_COS;
151 } else if (strcmp (cmethod->name, "Sqrt") == 0) {
152 opcode = OP_SQRT;
153 } else if (strcmp (cmethod->name, "Floor") == 0) {
154 opcode = OP_FLOOR;
155 } else if (strcmp (cmethod->name, "Ceiling") == 0) {
156 opcode = OP_CEIL;
157 } else if (strcmp (cmethod->name, "FusedMultiplyAdd") == 0) {
158 opcode = OP_FMA;
159 } else if (strcmp (cmethod->name, "Abs") == 0 && fsig->params [0]->type == MONO_TYPE_R8) {
160 opcode = OP_ABS;
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) {
164 opcode = OP_FMAX;
165 } else if (strcmp (cmethod->name, "Min") == 0 && mono_use_fast_math && fsig->params [0]->type == MONO_TYPE_R8) {
166 opcode = OP_FMIN;
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) {
170 opcode = OP_RMAX;
171 } else if (strcmp (cmethod->name, "Min") == 0 && mono_use_fast_math && fsig->params [0]->type == MONO_TYPE_R4) {
172 opcode = OP_RMIN;
173 } else if (strcmp (cmethod->name, "Pow") == 0) {
174 opcode = OP_FPOW;
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);
192 opcode = 0;
193 if (cfg->opt & MONO_OPT_CMOV) {
194 if (strcmp (cmethod->name, "Min") == 0) {
195 if (fsig->params [0]->type == MONO_TYPE_I4)
196 opcode = OP_IMIN;
197 if (fsig->params [0]->type == MONO_TYPE_U4)
198 opcode = OP_IMIN_UN;
199 else if (fsig->params [0]->type == MONO_TYPE_I8)
200 opcode = OP_LMIN;
201 else if (fsig->params [0]->type == MONO_TYPE_U8)
202 opcode = OP_LMIN_UN;
203 } else if (strcmp (cmethod->name, "Max") == 0) {
204 if (fsig->params [0]->type == MONO_TYPE_I4)
205 opcode = OP_IMAX;
206 if (fsig->params [0]->type == MONO_TYPE_U4)
207 opcode = OP_IMAX_UN;
208 else if (fsig->params [0]->type == MONO_TYPE_I8)
209 opcode = OP_LMAX;
210 else if (fsig->params [0]->type == MONO_TYPE_U8)
211 opcode = OP_LMAX_UN;
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) {
227 opcode = OP_MEMMOVE;
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);
236 return ins;
239 static MonoInst*
240 emit_span_intrinsics (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
242 MonoInst *ins;
244 MonoClassField *ptr_field = mono_class_get_field_from_name_full (cmethod->klass, "_pointer", NULL);
245 if (!ptr_field)
246 /* Portable Span<T> */
247 return NULL;
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))
258 return NULL;
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;
281 return ins;
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;
300 return ins;
303 return NULL;
306 static MonoInst*
307 emit_unsafe_intrinsics (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
309 MonoInst *ins;
310 int dreg, align;
311 MonoGenericContext *ctx = mono_method_get_context (cmethod);
312 MonoType *t;
314 if (!strcmp (cmethod->name, "As")) {
315 g_assert (ctx);
316 g_assert (ctx->method_inst);
318 t = ctx->method_inst->type_argv [0];
319 if (mini_is_gsharedvt_variable_type (t))
320 return NULL;
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 ();
326 return ins;
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]);
335 return ins;
337 } else if (!strcmp (cmethod->name, "AsPointer")) {
338 g_assert (ctx);
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;
346 return ins;
347 } else if (!strcmp (cmethod->name, "AsRef")) {
348 g_assert (ctx);
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 ();
357 return ins;
358 } else if (!strcmp (cmethod->name, "AreSame")) {
359 g_assert (ctx);
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);
367 return ins;
368 } else if (!strcmp (cmethod->name, "IsAddressLessThan")) {
369 g_assert (ctx);
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);
377 return ins;
378 } else if (!strcmp (cmethod->name, "IsAddressGreaterThan")) {
379 g_assert (ctx);
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);
387 return ins;
388 } else if (!strcmp (cmethod->name, "Add")) {
389 g_assert (ctx);
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];
397 MonoInst *esize_ins;
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);
402 } else {
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;
415 return ins;
416 } else if (!strcmp (cmethod->name, "AddByteOffset")) {
417 g_assert (ctx);
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;
426 return ins;
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;
436 return ins;
438 } else if (!strcmp (cmethod->name, "SizeOf")) {
439 g_assert (ctx);
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);
447 } else {
448 int esize = mono_type_size (t, &align);
449 EMIT_NEW_ICONST (cfg, ins, esize);
451 ins->type = STACK_I4;
452 return ins;
453 } else if (!strcmp (cmethod->name, "ReadUnaligned")) {
454 g_assert (ctx);
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;
466 return ins;
468 } else if (!strcmp (cmethod->name, "WriteUnaligned")) {
469 g_assert (ctx);
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;
480 return ins;
482 } else if (!strcmp (cmethod->name, "ByteOffset")) {
483 g_assert (ctx);
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;
491 return ins;
494 return NULL;
497 static MonoInst*
498 emit_jit_helpers_intrinsics (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
500 MonoInst *ins;
501 int dreg;
502 MonoGenericContext *ctx = mono_method_get_context (cmethod);
503 MonoType *t;
505 if (!strcmp (cmethod->name, "EnumEquals") || !strcmp (cmethod->name, "EnumCompareTo")) {
506 g_assert (ctx);
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))
514 return NULL;
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;
520 if (is_i8) {
521 cmp_op = OP_LCOMPARE;
522 ceq_op = OP_LCEQ;
523 cgt_op = is_unsigned ? OP_LCGT_UN : OP_LCGT;
524 clt_op = is_unsigned ? OP_LCLT_UN : OP_LCLT;
525 } else {
526 cmp_op = OP_ICOMPARE;
527 ceq_op = OP_ICEQ;
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);
536 } else {
537 // Use the branchless code (a > b) - (a < b)
538 int reg1, reg2;
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);
550 return ins;
553 return NULL;
556 MonoInst*
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 */
568 if (in_corlib &&
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);
575 return ins;
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));
580 return ins;
584 if (!(cfg->opt & MONO_OPT_INTRINS))
585 return NULL;
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);
596 } else {
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);
600 #else
601 index_reg = args [1]->dreg;
602 #endif
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));
607 add_reg = ins->dreg;
608 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADU2_MEMBASE, dreg,
609 add_reg, 0);
610 #else
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));
616 #endif
617 mini_type_from_op (cfg, ins, NULL, NULL);
618 return ins;
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;
628 return ins;
629 } else
630 return NULL;
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);
639 return ins;
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;
648 return ins;
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);
652 return ins;
653 } else
654 return NULL;
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));
663 return ins;
666 #ifndef MONO_BIG_ARRAYS
668 * This is an inline version of GetLength/GetLowerBound(0) used frequently in
669 * Array methods.
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 */
687 if (get_length)
688 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADI4_MEMBASE, dreg,
689 bounds_reg, MONO_STRUCT_OFFSET (MonoArrayBounds, length));
690 else
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);
695 /* Szarray case */
696 if (get_length)
697 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADI4_MEMBASE, dreg,
698 args [0]->dreg, MONO_STRUCT_OFFSET (MonoArray, max_length));
699 else
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;
706 return ins;
708 #endif
710 if (cmethod->name [0] != 'g')
711 return NULL;
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);
722 return ins;
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);
730 return ins;
731 } else
732 return 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));
736 return ins;
737 } else if (strcmp (cmethod->name, "IsReferenceOrContainsReferences") == 0 && fsig->param_count == 0) {
738 MonoGenericContext *ctx = mono_method_get_context (cmethod);
739 g_assert (ctx);
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];
743 MonoType *t;
744 MonoClass *klass;
746 ins = NULL;
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);
760 else {
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);
773 return ins;
774 } else if (strcmp (cmethod->name, "IsBitwiseEquatable") == 0 && fsig->param_count == 0) {
775 MonoGenericContext *ctx = mono_method_get_context (cmethod);
776 g_assert (ctx);
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];
780 MonoType *t;
781 ins = NULL;
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);
788 else
789 EMIT_NEW_ICONST (cfg, ins, 0);
790 return ins;
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
795 int dreg;
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;
805 return ins;
806 } else
807 return NULL;
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) {
813 is_enter = TRUE;
814 is_v4 = TRUE;
816 if (!strcmp (cmethod->name, "Enter") && fsig->param_count == 1)
817 is_enter = TRUE;
819 if (is_enter) {
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);
828 if (is_v4)
829 ins = mono_emit_jit_icall (cfg, mono_monitor_enter_v4_fast, args);
830 else
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);
836 if (is_v4)
837 ins = mono_emit_jit_icall (cfg, mono_monitor_enter_v4_internal, args);
838 else
839 ins = mono_emit_jit_icall (cfg, mono_monitor_enter_internal, args);
841 MONO_START_BB (cfg, end_bb);
842 return ins;
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);
848 return 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) {
852 guint32 opcode = 0;
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;
876 if (opcode) {
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) {
883 case MONO_TYPE_I1:
884 case MONO_TYPE_U1:
885 case MONO_TYPE_I2:
886 case MONO_TYPE_U2:
887 case MONO_TYPE_I4:
888 case MONO_TYPE_U4:
889 ins->dreg = mono_alloc_ireg (cfg);
890 ins->type = STACK_I4;
891 break;
892 case MONO_TYPE_I8:
893 case MONO_TYPE_U8:
894 ins->dreg = mono_alloc_lreg (cfg);
895 ins->type = STACK_I8;
896 break;
897 case MONO_TYPE_I:
898 case MONO_TYPE_U:
899 ins->dreg = mono_alloc_ireg (cfg);
900 #if SIZEOF_REGISTER == 8
901 ins->type = STACK_I8;
902 #else
903 ins->type = STACK_I4;
904 #endif
905 break;
906 case MONO_TYPE_R4:
907 case MONO_TYPE_R8:
908 ins->dreg = mono_alloc_freg (cfg);
909 ins->type = STACK_R8;
910 break;
911 default:
912 g_assert (mini_type_is_reference (fsig->params [0]));
913 ins->dreg = mono_alloc_ireg_ref (cfg);
914 ins->type = STACK_OBJ;
915 break;
918 if (opcode == OP_LOADI8_MEMBASE)
919 ins = mono_decompose_opcode (cfg, ins);
921 mini_emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_SEQ);
923 return ins;
925 } else if (!strcmp (cmethod->name, "VolatileWrite") && fsig->param_count == 2) {
926 guint32 opcode = 0;
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;
944 if (opcode) {
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);
956 return ins;
959 } else if (in_corlib &&
960 (strcmp (cmethod_klass_name_space, "System.Threading") == 0) &&
961 (strcmp (cmethod_klass_name, "Interlocked") == 0)) {
962 ins = NULL;
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);
973 } else {
974 MonoInst *load_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);
988 ins = load_ins;
991 #endif
993 if (strcmp (cmethod->name, "Increment") == 0 && fsig->param_count == 1) {
994 MonoInst *ins_iconst;
995 guint32 opcode = 0;
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;
1004 #endif
1005 if (opcode) {
1006 if (!mono_arch_opcode_supported (opcode))
1007 return NULL;
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;
1023 guint32 opcode = 0;
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;
1032 #endif
1033 if (opcode) {
1034 if (!mono_arch_opcode_supported (opcode))
1035 return NULL;
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) {
1050 guint32 opcode = 0;
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;
1059 #endif
1060 if (opcode) {
1061 if (!mono_arch_opcode_supported (opcode))
1062 return NULL;
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
1086 else if (is_ref ||
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;
1094 #else
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;
1099 #endif
1100 else
1101 return NULL;
1103 if (!mono_arch_opcode_supported (opcode))
1104 return NULL;
1106 if (is_float) {
1107 /* TODO: Decompose these opcodes instead of bailing here. */
1108 if (COMPILE_SOFT_FLOAT (cfg))
1109 return NULL;
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) {
1127 case MONO_TYPE_I4:
1128 ins->type = STACK_I4;
1129 break;
1130 case MONO_TYPE_I8:
1131 ins->type = STACK_I8;
1132 break;
1133 case MONO_TYPE_I:
1134 #if SIZEOF_REGISTER == 8
1135 ins->type = STACK_I8;
1136 #else
1137 ins->type = STACK_I4;
1138 #endif
1139 break;
1140 case MONO_TYPE_R4:
1141 case MONO_TYPE_R8:
1142 ins->type = STACK_R8;
1143 break;
1144 default:
1145 g_assert (mini_type_is_reference (fsig->params [0]));
1146 ins->type = STACK_OBJ;
1147 break;
1150 if (is_float) {
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);
1159 ins = 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
1179 else if (is_ref ||
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;
1187 #else
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;
1192 #endif
1193 else
1194 return NULL;
1196 if (!mono_arch_opcode_supported (opcode))
1197 return NULL;
1199 if (is_float) {
1200 /* TODO: Decompose these opcodes instead of bailing here. */
1201 if (COMPILE_SOFT_FLOAT (cfg))
1202 return NULL;
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) {
1227 case MONO_TYPE_I4:
1228 ins->type = STACK_I4;
1229 break;
1230 case MONO_TYPE_I8:
1231 ins->type = STACK_I8;
1232 break;
1233 case MONO_TYPE_I:
1234 #if SIZEOF_REGISTER == 8
1235 ins->type = STACK_I8;
1236 #else
1237 ins->type = STACK_I4;
1238 #endif
1239 break;
1240 case MONO_TYPE_R4:
1241 ins->type = cfg->r4_stack_type;
1242 break;
1243 case MONO_TYPE_R8:
1244 ins->type = STACK_R8;
1245 break;
1246 default:
1247 g_assert (mini_type_is_reference (fsig->params [1]));
1248 ins->type = STACK_OBJ;
1249 break;
1252 if (is_float) {
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);
1261 ins = 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))
1272 return NULL;
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);
1303 if (ins)
1304 return ins;
1305 } else if (in_corlib &&
1306 (strcmp (cmethod_klass_name_space, "System.Threading") == 0) &&
1307 (strcmp (cmethod_klass_name, "Volatile") == 0)) {
1308 ins = NULL;
1310 if (!cfg->llvm_only && !strcmp (cmethod->name, "Read") && fsig->param_count == 1) {
1311 guint32 opcode = 0;
1312 MonoType *t = fsig->params [0];
1313 gboolean is_ref;
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;
1340 #else
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;
1345 #endif
1347 if (opcode) {
1348 if (!mono_arch_opcode_supported (opcode))
1349 return NULL;
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);
1357 switch (t->type) {
1358 case MONO_TYPE_BOOLEAN:
1359 case MONO_TYPE_I1:
1360 case MONO_TYPE_U1:
1361 case MONO_TYPE_I2:
1362 case MONO_TYPE_U2:
1363 case MONO_TYPE_I4:
1364 case MONO_TYPE_U4:
1365 ins->type = STACK_I4;
1366 break;
1367 case MONO_TYPE_I8:
1368 case MONO_TYPE_U8:
1369 ins->type = STACK_I8;
1370 break;
1371 case MONO_TYPE_I:
1372 case MONO_TYPE_U:
1373 #if SIZEOF_REGISTER == 8
1374 ins->type = STACK_I8;
1375 #else
1376 ins->type = STACK_I4;
1377 #endif
1378 break;
1379 case MONO_TYPE_R4:
1380 ins->type = cfg->r4_stack_type;
1381 break;
1382 case MONO_TYPE_R8:
1383 ins->type = STACK_R8;
1384 break;
1385 default:
1386 g_assert (is_ref);
1387 ins->type = STACK_OBJ;
1388 break;
1393 if (!cfg->llvm_only && !strcmp (cmethod->name, "Write") && fsig->param_count == 2) {
1394 guint32 opcode = 0;
1395 MonoType *t = fsig->params [0];
1396 gboolean is_ref;
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;
1421 #else
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;
1426 #endif
1428 if (opcode) {
1429 if (!mono_arch_opcode_supported (opcode))
1430 return NULL;
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]);
1443 if (ins)
1444 return ins;
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);
1451 } else {
1452 MONO_INST_NEW (cfg, ins, OP_NOP);
1453 MONO_ADD_INS (cfg->cbb, ins);
1455 return 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) {
1461 #ifdef TARGET_WIN32
1462 EMIT_NEW_ICONST (cfg, ins, 1);
1463 #else
1464 EMIT_NEW_ICONST (cfg, ins, 0);
1465 #endif
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);
1476 return 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 ()");
1501 return ins;
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
1506 * all inputs:
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;
1517 int opcode = 0;
1518 const char *mname = cmethod->name;
1519 char c = mname [0];
1521 if (c == 'A'){
1522 if (strcmp (mname, "Abs") == 0 && fsig->params [0]->type == MONO_TYPE_R8) {
1523 opcode = OP_ABS;
1524 } else if (strcmp (mname, "Asin") == 0){
1525 if (fabs (source) <= 1)
1526 opcode = OP_ASIN;
1527 } else if (strcmp (mname, "Asinh") == 0){
1528 opcode = OP_ASINH;
1529 } else if (strcmp (mname, "Acos") == 0){
1530 if (fabs (source) <= 1)
1531 opcode = OP_ACOS;
1532 } else if (strcmp (mname, "Acosh") == 0){
1533 if (source >= 1)
1534 opcode = OP_ACOSH;
1535 } else if (strcmp (mname, "Atan") == 0){
1536 opcode = OP_ATAN;
1537 } else if (strcmp (mname, "Atanh") == 0){
1538 if (fabs (source) < 1)
1539 opcode = OP_ATANH;
1541 } else if (c == 'C'){
1542 if (strcmp (mname, "Cos") == 0) {
1543 if (!isinf (source))
1544 opcode = OP_COS;
1545 } else if (strcmp (mname, "Cbrt") == 0){
1546 opcode = OP_CBRT;
1547 } else if (strcmp (mname, "Cosh") == 0){
1548 opcode = OP_COSH;
1550 } else if (c == 'R'){
1551 if (strcmp (mname, "Round") == 0)
1552 opcode = OP_ROUND;
1553 } else if (c == 'S'){
1554 if (strcmp (mname, "Sin") == 0) {
1555 if (!isinf (source))
1556 opcode = OP_SIN;
1557 } else if (strcmp (mname, "Sqrt") == 0) {
1558 if (source >= 0)
1559 opcode = OP_SQRT;
1560 } else if (strcmp (mname, "Sinh") == 0){
1561 opcode = OP_SINH;
1563 } else if (c == 'T'){
1564 if (strcmp (mname, "Tan") == 0){
1565 if (!isinf (source))
1566 opcode = OP_TAN;
1567 } else if (strcmp (mname, "Tanh") == 0){
1568 opcode = OP_TANH;
1572 if (opcode) {
1573 double *dest = (double *) mono_domain_alloc (cfg->domain, sizeof (double));
1574 double result = 0;
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;
1580 switch (opcode){
1581 case OP_ABS:
1582 result = fabs (source);
1583 break;
1584 case OP_ACOS:
1585 result = acos (source);
1586 break;
1587 case OP_ACOSH:
1588 result = acosh (source);
1589 break;
1590 case OP_ASIN:
1591 result = asin (source);
1592 break;
1593 case OP_ASINH:
1594 result= asinh (source);
1595 break;
1596 case OP_ATAN:
1597 result = atan (source);
1598 break;
1599 case OP_ATANH:
1600 result = atanh (source);
1601 break;
1602 case OP_CBRT:
1603 result = cbrt (source);
1604 break;
1605 case OP_COS:
1606 result = cos (source);
1607 break;
1608 case OP_COSH:
1609 result = cosh (source);
1610 break;
1611 case OP_ROUND:
1612 result = round (source);
1613 break;
1614 case OP_SIN:
1615 result = sin (source);
1616 break;
1617 case OP_SINH:
1618 result = sinh (source);
1619 break;
1620 case OP_SQRT:
1621 result = sqrt (source);
1622 break;
1623 case OP_TAN:
1624 result = tan (source);
1625 break;
1626 case OP_TANH:
1627 result = tanh (source);
1628 break;
1629 default:
1630 g_error ("invalid opcode %d", (int)opcode);
1632 *dest = result;
1633 MONO_ADD_INS (cfg->cbb, ins);
1634 NULLIFY_INS (args [0]);
1635 return ins;
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);
1644 return 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) &&
1657 cfg->compile_aot) {
1658 MonoInst *pi;
1659 MonoJumpInfoToken *ji;
1660 char *s;
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;
1667 } else {
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);
1679 // FIXME: Leaks
1680 ins->inst_p0 = s;
1681 MONO_ADD_INS (cfg->cbb, ins);
1682 return 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]);
1703 return ins;
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);
1725 if (ins)
1726 return ins;
1728 #endif
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;
1735 return ins;
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;
1744 return ins;
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
1751 if (in_corlib &&
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;
1756 return ins;
1758 #endif
1760 ins = mono_emit_native_types_intrinsics (cfg, cmethod, fsig, args);
1761 if (ins)
1762 return ins;
1764 if (COMPILE_LLVM (cfg)) {
1765 ins = llvm_emit_inst_for_method (cfg, cmethod, fsig, args, in_corlib);
1766 if (ins)
1767 return ins;
1770 return mono_arch_emit_inst_for_method (cfg, cmethod, fsig, args);
1774 static MonoInst*
1775 emit_array_unsafe_access (MonoCompile *cfg, MonoMethodSignature *fsig, MonoInst **args, int is_set)
1777 MonoClass *eklass;
1779 if (is_set)
1780 eklass = mono_class_from_mono_type_internal (fsig->params [2]);
1781 else
1782 eklass = mono_class_from_mono_type_internal (fsig->ret);
1784 if (is_set) {
1785 return mini_emit_array_store (cfg, eklass, args, FALSE);
1786 } else {
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);
1789 return ins;
1793 static gboolean
1794 is_unsafe_mov_compatible (MonoCompile *cfg, MonoClass *param_klass, MonoClass *return_klass)
1796 uint32_t align;
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");
1809 return FALSE;
1812 if (!m_class_is_valuetype (param_klass)) {
1813 if (cfg->verbose_level > 3)
1814 printf ("[UNSAFE-MOV-INTRISIC]\targs are reference types\n");
1815 return TRUE;
1818 //That are blitable
1819 if (m_class_has_references (param_klass) || m_class_has_references (return_klass))
1820 return FALSE;
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");
1830 return FALSE;
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");
1837 return FALSE;
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");
1847 return TRUE;
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");
1854 return FALSE;
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");
1868 return TRUE;
1871 return FALSE;
1874 static MonoInst*
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))
1881 return NULL;
1883 //Valuetypes that are semantically equivalent or numbers than can be widened to
1884 if (is_unsafe_mov_compatible (cfg, param_klass, return_klass))
1885 return args [0];
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)))
1889 return args [0];
1891 return NULL;
1894 MonoInst*
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);
1906 return NULL;
1909 MonoInst*
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;
1917 gboolean is_le;
1918 MonoInst *ins;
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);
1923 return ins;
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);
1928 return ins;
1930 #endif
1931 return NULL;
1933 #else
1934 MONO_EMPTY_SOURCE_FILE (intrinsics);
1935 #endif