[netcore] Implement missing Bmi1/Bmi2 intrinsics (#16919)
[mono-project.git] / mono / mini / intrinsics.c
blob57d621f44a9b895772292f9697daacd56fc25c6f
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;
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) {
114 // (float)
115 if (fsig->param_count == 1 && fsig->params [0]->type == MONO_TYPE_R4) {
116 if (!strcmp (cmethod->name, "Ceiling")) {
117 opcode = OP_CEILF;
118 } else if (!strcmp (cmethod->name, "Cos")) {
119 opcode = OP_COSF;
120 } else if (!strcmp (cmethod->name, "Exp")) {
121 opcode = OP_EXPF;
122 } else if (!strcmp (cmethod->name, "Floor")) {
123 opcode = OP_FLOORF;
124 } else if (!strcmp (cmethod->name, "Log2")) {
125 opcode = OP_LOG2F;
126 } else if (!strcmp (cmethod->name, "Log10")) {
127 opcode = OP_LOG10F;
128 } else if (!strcmp (cmethod->name, "Sin")) {
129 opcode = OP_SINF;
130 } else if (!strcmp (cmethod->name, "Sqrt")) {
131 opcode = OP_SQRTF;
132 } else if (!strcmp (cmethod->name, "Truncate")) {
133 opcode = OP_TRUNCF;
136 // (float, float)
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")) {
139 opcode = OP_RPOW;
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")) {
147 opcode = OP_FMAF;
151 if (opcode) {
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 ()) {
168 // (double)
169 if (fsig->param_count == 1 && fsig->params [0]->type == MONO_TYPE_R8) {
170 if (!strcmp (cmethod->name, "Abs")) {
171 opcode = OP_ABS;
172 } else if (!strcmp (cmethod->name, "Ceiling")) {
173 opcode = OP_CEIL;
174 } else if (!strcmp (cmethod->name, "Cos")) {
175 opcode = OP_COS;
176 } else if (!strcmp (cmethod->name, "Exp")) {
177 opcode = OP_EXP;
178 } else if (!strcmp (cmethod->name, "Floor")) {
179 opcode = OP_FLOOR;
180 } else if (!strcmp (cmethod->name, "Log")) {
181 opcode = OP_LOG;
182 } else if (!strcmp (cmethod->name, "Log2")) {
183 opcode = OP_LOG2;
184 } else if (!strcmp (cmethod->name, "Log10")) {
185 opcode = OP_LOG10;
186 } else if (!strcmp (cmethod->name, "Sin")) {
187 opcode = OP_SIN;
188 } else if (!strcmp (cmethod->name, "Sqrt")) {
189 opcode = OP_SQRT;
190 } else if (!strcmp (cmethod->name, "Truncate")) {
191 opcode = OP_TRUNC;
194 // (double, double)
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) {
198 opcode = OP_FMAX;
199 } else if (!strcmp (cmethod->name, "Min") && mono_use_fast_math) {
200 opcode = OP_FMIN;
201 } else if (!strcmp (cmethod->name, "Pow")) {
202 opcode = OP_FPOW;
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")) {
210 opcode = OP_FMA;
214 // Math also contains overloads for floats (MathF inlines them)
215 // (float)
216 if (fsig->param_count == 1 && fsig->params [0]->type == MONO_TYPE_R4) {
217 if (!strcmp (cmethod->name, "Abs")) {
218 opcode = OP_ABSF;
221 // (float, float)
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) {
224 opcode = OP_RMAX;
225 } else if (!strcmp (cmethod->name, "Min") && mono_use_fast_math) {
226 opcode = OP_RMIN;
227 } else if (!strcmp (cmethod->name, "Pow")) {
228 opcode = OP_RPOW;
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);
247 opcode = 0;
248 if (cfg->opt & MONO_OPT_CMOV) {
249 if (strcmp (cmethod->name, "Min") == 0) {
250 if (fsig->params [0]->type == MONO_TYPE_I4)
251 opcode = OP_IMIN;
252 if (fsig->params [0]->type == MONO_TYPE_U4)
253 opcode = OP_IMIN_UN;
254 else if (fsig->params [0]->type == MONO_TYPE_I8)
255 opcode = OP_LMIN;
256 else if (fsig->params [0]->type == MONO_TYPE_U8)
257 opcode = OP_LMIN_UN;
258 } else if (strcmp (cmethod->name, "Max") == 0) {
259 if (fsig->params [0]->type == MONO_TYPE_I4)
260 opcode = OP_IMAX;
261 if (fsig->params [0]->type == MONO_TYPE_U4)
262 opcode = OP_IMAX_UN;
263 else if (fsig->params [0]->type == MONO_TYPE_I8)
264 opcode = OP_LMAX;
265 else if (fsig->params [0]->type == MONO_TYPE_U8)
266 opcode = OP_LMAX_UN;
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);
296 return ins;
299 static MonoInst*
300 emit_span_intrinsics (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
302 MonoInst *ins;
304 MonoClassField *ptr_field = mono_class_get_field_from_name_full (cmethod->klass, "_pointer", NULL);
305 if (!ptr_field)
306 /* Portable Span<T> */
307 return NULL;
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))
318 return NULL;
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;
341 return ins;
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;
360 return ins;
363 return NULL;
366 static MonoInst*
367 emit_unsafe_intrinsics (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
369 MonoInst *ins;
370 int dreg, align;
371 MonoGenericContext *ctx = mono_method_get_context (cmethod);
372 MonoType *t;
374 if (!strcmp (cmethod->name, "As")) {
375 g_assert (ctx);
376 g_assert (ctx->method_inst);
378 t = ctx->method_inst->type_argv [0];
379 if (mini_is_gsharedvt_variable_type (t))
380 return NULL;
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 ();
386 return ins;
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]);
395 return ins;
397 } else if (!strcmp (cmethod->name, "AsPointer")) {
398 g_assert (ctx);
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;
406 return ins;
407 } else if (!strcmp (cmethod->name, "AsRef")) {
408 g_assert (ctx);
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 ();
417 return ins;
418 } else if (!strcmp (cmethod->name, "AreSame")) {
419 g_assert (ctx);
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);
427 return ins;
428 } else if (!strcmp (cmethod->name, "IsAddressLessThan")) {
429 g_assert (ctx);
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);
437 return ins;
438 } else if (!strcmp (cmethod->name, "IsAddressGreaterThan")) {
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 == 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);
447 return ins;
448 } else if (!strcmp (cmethod->name, "Add")) {
449 g_assert (ctx);
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];
457 MonoInst *esize_ins;
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);
462 } else {
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;
475 return ins;
476 } else if (!strcmp (cmethod->name, "AddByteOffset")) {
477 g_assert (ctx);
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;
486 return ins;
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;
496 return ins;
498 } else if (!strcmp (cmethod->name, "SizeOf")) {
499 g_assert (ctx);
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);
507 } else {
508 int esize = mono_type_size (t, &align);
509 EMIT_NEW_ICONST (cfg, ins, esize);
511 ins->type = STACK_I4;
512 return ins;
513 } else if (!strcmp (cmethod->name, "ReadUnaligned")) {
514 g_assert (ctx);
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;
526 return ins;
528 } else if (!strcmp (cmethod->name, "WriteUnaligned")) {
529 g_assert (ctx);
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;
540 return ins;
542 } else if (!strcmp (cmethod->name, "ByteOffset")) {
543 g_assert (ctx);
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;
551 return ins;
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);
560 return ins;
562 #endif
564 return NULL;
567 static MonoInst*
568 emit_jit_helpers_intrinsics (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
570 MonoInst *ins;
571 int dreg;
572 MonoGenericContext *ctx = mono_method_get_context (cmethod);
573 MonoType *t;
575 if (!strcmp (cmethod->name, "EnumEquals") || !strcmp (cmethod->name, "EnumCompareTo")) {
576 g_assert (ctx);
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))
584 return NULL;
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;
590 if (is_i8) {
591 cmp_op = OP_LCOMPARE;
592 ceq_op = OP_LCEQ;
593 cgt_op = is_unsigned ? OP_LCGT_UN : OP_LCGT;
594 clt_op = is_unsigned ? OP_LCLT_UN : OP_LCLT;
595 } else {
596 cmp_op = OP_ICOMPARE;
597 ceq_op = OP_ICEQ;
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);
606 } else {
607 // Use the branchless code (a > b) - (a < b)
608 int reg1, reg2;
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);
620 return ins;
623 return NULL;
626 MonoInst*
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 */
638 if (in_corlib &&
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);
645 return ins;
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));
650 return ins;
654 if (!(cfg->opt & MONO_OPT_INTRINS))
655 return NULL;
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);
666 } else {
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);
670 #else
671 index_reg = args [1]->dreg;
672 #endif
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));
677 add_reg = ins->dreg;
678 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADU2_MEMBASE, dreg,
679 add_reg, 0);
680 #else
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));
686 #endif
687 mini_type_from_op (cfg, ins, NULL, NULL);
688 return ins;
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;
698 return ins;
699 } else
700 return NULL;
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);
709 return ins;
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;
718 return ins;
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);
722 return ins;
723 } else
724 return NULL;
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));
733 return ins;
736 #ifndef MONO_BIG_ARRAYS
738 * This is an inline version of GetLength/GetLowerBound(0) used frequently in
739 * Array methods.
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 */
757 if (get_length)
758 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADI4_MEMBASE, dreg,
759 bounds_reg, MONO_STRUCT_OFFSET (MonoArrayBounds, length));
760 else
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);
765 /* Szarray case */
766 if (get_length)
767 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADI4_MEMBASE, dreg,
768 args [0]->dreg, MONO_STRUCT_OFFSET (MonoArray, max_length));
769 else
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;
776 return ins;
778 #endif
780 if (cmethod->name [0] != 'g')
781 return NULL;
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);
792 return ins;
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);
800 return ins;
801 } else
802 return 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));
806 return ins;
807 } else if (strcmp (cmethod->name, "IsReferenceOrContainsReferences") == 0 && fsig->param_count == 0) {
808 MonoGenericContext *ctx = mono_method_get_context (cmethod);
809 g_assert (ctx);
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];
813 MonoType *t;
814 MonoClass *klass;
816 ins = NULL;
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);
830 else {
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);
843 return ins;
844 } else if (strcmp (cmethod->name, "IsBitwiseEquatable") == 0 && fsig->param_count == 0) {
845 MonoGenericContext *ctx = mono_method_get_context (cmethod);
846 g_assert (ctx);
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];
850 MonoType *t;
851 ins = NULL;
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);
858 else
859 EMIT_NEW_ICONST (cfg, ins, 0);
860 return ins;
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
865 int dreg;
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;
875 return ins;
876 } else
877 return NULL;
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) {
883 is_enter = TRUE;
884 is_v4 = TRUE;
886 if (!strcmp (cmethod->name, "Enter") && fsig->param_count == 1)
887 is_enter = TRUE;
889 if (is_enter) {
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);
898 if (is_v4)
899 ins = mono_emit_jit_icall (cfg, mono_monitor_enter_v4_fast, args);
900 else
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);
906 if (is_v4)
907 ins = mono_emit_jit_icall (cfg, mono_monitor_enter_v4_internal, args);
908 else
909 ins = mono_emit_jit_icall (cfg, mono_monitor_enter_internal, args);
911 MONO_START_BB (cfg, end_bb);
912 return ins;
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);
918 return 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) {
922 guint32 opcode = 0;
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;
946 if (opcode) {
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) {
953 case MONO_TYPE_I1:
954 case MONO_TYPE_U1:
955 case MONO_TYPE_I2:
956 case MONO_TYPE_U2:
957 case MONO_TYPE_I4:
958 case MONO_TYPE_U4:
959 ins->dreg = mono_alloc_ireg (cfg);
960 ins->type = STACK_I4;
961 break;
962 case MONO_TYPE_I8:
963 case MONO_TYPE_U8:
964 ins->dreg = mono_alloc_lreg (cfg);
965 ins->type = STACK_I8;
966 break;
967 case MONO_TYPE_I:
968 case MONO_TYPE_U:
969 ins->dreg = mono_alloc_ireg (cfg);
970 #if SIZEOF_REGISTER == 8
971 ins->type = STACK_I8;
972 #else
973 ins->type = STACK_I4;
974 #endif
975 break;
976 case MONO_TYPE_R4:
977 case MONO_TYPE_R8:
978 ins->dreg = mono_alloc_freg (cfg);
979 ins->type = STACK_R8;
980 break;
981 default:
982 g_assert (mini_type_is_reference (fsig->params [0]));
983 ins->dreg = mono_alloc_ireg_ref (cfg);
984 ins->type = STACK_OBJ;
985 break;
988 if (opcode == OP_LOADI8_MEMBASE)
989 ins = mono_decompose_opcode (cfg, ins);
991 mini_emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_SEQ);
993 return ins;
995 } else if (!strcmp (cmethod->name, "VolatileWrite") && fsig->param_count == 2) {
996 guint32 opcode = 0;
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;
1014 if (opcode) {
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);
1026 return ins;
1029 } else if (in_corlib &&
1030 (strcmp (cmethod_klass_name_space, "System.Threading") == 0) &&
1031 (strcmp (cmethod_klass_name, "Interlocked") == 0)) {
1032 ins = NULL;
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);
1043 } else {
1044 MonoInst *load_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);
1058 ins = load_ins;
1061 #endif
1063 if (strcmp (cmethod->name, "Increment") == 0 && fsig->param_count == 1) {
1064 MonoInst *ins_iconst;
1065 guint32 opcode = 0;
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;
1074 #endif
1075 if (opcode) {
1076 if (!mono_arch_opcode_supported (opcode))
1077 return NULL;
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;
1093 guint32 opcode = 0;
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;
1102 #endif
1103 if (opcode) {
1104 if (!mono_arch_opcode_supported (opcode))
1105 return NULL;
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) {
1120 guint32 opcode = 0;
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;
1129 #endif
1130 if (opcode) {
1131 if (!mono_arch_opcode_supported (opcode))
1132 return NULL;
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
1156 else if (is_ref ||
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;
1164 #else
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;
1169 #endif
1170 else
1171 return NULL;
1173 if (!mono_arch_opcode_supported (opcode))
1174 return NULL;
1176 if (is_float) {
1177 /* TODO: Decompose these opcodes instead of bailing here. */
1178 if (COMPILE_SOFT_FLOAT (cfg))
1179 return NULL;
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) {
1197 case MONO_TYPE_I4:
1198 ins->type = STACK_I4;
1199 break;
1200 case MONO_TYPE_I8:
1201 ins->type = STACK_I8;
1202 break;
1203 case MONO_TYPE_I:
1204 #if SIZEOF_REGISTER == 8
1205 ins->type = STACK_I8;
1206 #else
1207 ins->type = STACK_I4;
1208 #endif
1209 break;
1210 case MONO_TYPE_R4:
1211 case MONO_TYPE_R8:
1212 ins->type = STACK_R8;
1213 break;
1214 default:
1215 g_assert (mini_type_is_reference (fsig->params [0]));
1216 ins->type = STACK_OBJ;
1217 break;
1220 if (is_float) {
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);
1229 ins = 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
1249 else if (is_ref ||
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;
1257 #else
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;
1262 #endif
1263 else
1264 return NULL;
1266 if (!mono_arch_opcode_supported (opcode))
1267 return NULL;
1269 if (is_float) {
1270 /* TODO: Decompose these opcodes instead of bailing here. */
1271 if (COMPILE_SOFT_FLOAT (cfg))
1272 return NULL;
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) {
1297 case MONO_TYPE_I4:
1298 ins->type = STACK_I4;
1299 break;
1300 case MONO_TYPE_I8:
1301 ins->type = STACK_I8;
1302 break;
1303 case MONO_TYPE_I:
1304 #if SIZEOF_REGISTER == 8
1305 ins->type = STACK_I8;
1306 #else
1307 ins->type = STACK_I4;
1308 #endif
1309 break;
1310 case MONO_TYPE_R4:
1311 ins->type = cfg->r4_stack_type;
1312 break;
1313 case MONO_TYPE_R8:
1314 ins->type = STACK_R8;
1315 break;
1316 default:
1317 g_assert (mini_type_is_reference (fsig->params [1]));
1318 ins->type = STACK_OBJ;
1319 break;
1322 if (is_float) {
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);
1331 ins = 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))
1342 return NULL;
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);
1373 if (ins)
1374 return ins;
1375 } else if (in_corlib &&
1376 (strcmp (cmethod_klass_name_space, "System.Threading") == 0) &&
1377 (strcmp (cmethod_klass_name, "Volatile") == 0)) {
1378 ins = NULL;
1380 if (!cfg->llvm_only && !strcmp (cmethod->name, "Read") && fsig->param_count == 1) {
1381 guint32 opcode = 0;
1382 MonoType *t = fsig->params [0];
1383 gboolean is_ref;
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;
1410 #else
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;
1415 #endif
1417 if (opcode) {
1418 if (!mono_arch_opcode_supported (opcode))
1419 return NULL;
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);
1427 switch (t->type) {
1428 case MONO_TYPE_BOOLEAN:
1429 case MONO_TYPE_I1:
1430 case MONO_TYPE_U1:
1431 case MONO_TYPE_I2:
1432 case MONO_TYPE_U2:
1433 case MONO_TYPE_I4:
1434 case MONO_TYPE_U4:
1435 ins->type = STACK_I4;
1436 break;
1437 case MONO_TYPE_I8:
1438 case MONO_TYPE_U8:
1439 ins->type = STACK_I8;
1440 break;
1441 case MONO_TYPE_I:
1442 case MONO_TYPE_U:
1443 #if SIZEOF_REGISTER == 8
1444 ins->type = STACK_I8;
1445 #else
1446 ins->type = STACK_I4;
1447 #endif
1448 break;
1449 case MONO_TYPE_R4:
1450 ins->type = cfg->r4_stack_type;
1451 break;
1452 case MONO_TYPE_R8:
1453 ins->type = STACK_R8;
1454 break;
1455 default:
1456 g_assert (is_ref);
1457 ins->type = STACK_OBJ;
1458 break;
1463 if (!cfg->llvm_only && !strcmp (cmethod->name, "Write") && fsig->param_count == 2) {
1464 guint32 opcode = 0;
1465 MonoType *t = fsig->params [0];
1466 gboolean is_ref;
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;
1491 #else
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;
1496 #endif
1498 if (opcode) {
1499 if (!mono_arch_opcode_supported (opcode))
1500 return NULL;
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]);
1513 if (ins)
1514 return ins;
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);
1521 } else {
1522 MONO_INST_NEW (cfg, ins, OP_NOP);
1523 MONO_ADD_INS (cfg->cbb, ins);
1525 return 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) {
1531 #ifdef TARGET_WIN32
1532 EMIT_NEW_ICONST (cfg, ins, 1);
1533 #else
1534 EMIT_NEW_ICONST (cfg, ins, 0);
1535 #endif
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);
1546 return 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 ()");
1571 return ins;
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
1576 * all inputs:
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;
1587 int opcode = 0;
1588 const char *mname = cmethod->name;
1589 char c = mname [0];
1591 if (c == 'A'){
1592 if (strcmp (mname, "Abs") == 0 && fsig->params [0]->type == MONO_TYPE_R8) {
1593 opcode = OP_ABS;
1594 } else if (strcmp (mname, "Asin") == 0){
1595 if (fabs (source) <= 1)
1596 opcode = OP_ASIN;
1597 } else if (strcmp (mname, "Asinh") == 0){
1598 opcode = OP_ASINH;
1599 } else if (strcmp (mname, "Acos") == 0){
1600 if (fabs (source) <= 1)
1601 opcode = OP_ACOS;
1602 } else if (strcmp (mname, "Acosh") == 0){
1603 if (source >= 1)
1604 opcode = OP_ACOSH;
1605 } else if (strcmp (mname, "Atan") == 0){
1606 opcode = OP_ATAN;
1607 } else if (strcmp (mname, "Atanh") == 0){
1608 if (fabs (source) < 1)
1609 opcode = OP_ATANH;
1611 } else if (c == 'C'){
1612 if (strcmp (mname, "Cos") == 0) {
1613 if (!isinf (source))
1614 opcode = OP_COS;
1615 } else if (strcmp (mname, "Cbrt") == 0){
1616 opcode = OP_CBRT;
1617 } else if (strcmp (mname, "Cosh") == 0){
1618 opcode = OP_COSH;
1620 } else if (c == 'R'){
1621 if (strcmp (mname, "Round") == 0)
1622 opcode = OP_ROUND;
1623 } else if (c == 'S'){
1624 if (strcmp (mname, "Sin") == 0) {
1625 if (!isinf (source))
1626 opcode = OP_SIN;
1627 } else if (strcmp (mname, "Sqrt") == 0) {
1628 if (source >= 0)
1629 opcode = OP_SQRT;
1630 } else if (strcmp (mname, "Sinh") == 0){
1631 opcode = OP_SINH;
1633 } else if (c == 'T'){
1634 if (strcmp (mname, "Tan") == 0){
1635 if (!isinf (source))
1636 opcode = OP_TAN;
1637 } else if (strcmp (mname, "Tanh") == 0){
1638 opcode = OP_TANH;
1642 if (opcode) {
1643 double *dest = (double *) mono_domain_alloc (cfg->domain, sizeof (double));
1644 double result = 0;
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;
1650 switch (opcode){
1651 case OP_ABS:
1652 result = fabs (source);
1653 break;
1654 case OP_ACOS:
1655 result = acos (source);
1656 break;
1657 case OP_ACOSH:
1658 result = acosh (source);
1659 break;
1660 case OP_ASIN:
1661 result = asin (source);
1662 break;
1663 case OP_ASINH:
1664 result= asinh (source);
1665 break;
1666 case OP_ATAN:
1667 result = atan (source);
1668 break;
1669 case OP_ATANH:
1670 result = atanh (source);
1671 break;
1672 case OP_CBRT:
1673 result = cbrt (source);
1674 break;
1675 case OP_COS:
1676 result = cos (source);
1677 break;
1678 case OP_COSH:
1679 result = cosh (source);
1680 break;
1681 case OP_ROUND:
1682 result = round (source);
1683 break;
1684 case OP_SIN:
1685 result = sin (source);
1686 break;
1687 case OP_SINH:
1688 result = sinh (source);
1689 break;
1690 case OP_SQRT:
1691 result = sqrt (source);
1692 break;
1693 case OP_TAN:
1694 result = tan (source);
1695 break;
1696 case OP_TANH:
1697 result = tanh (source);
1698 break;
1699 default:
1700 g_error ("invalid opcode %d", (int)opcode);
1702 *dest = result;
1703 MONO_ADD_INS (cfg->cbb, ins);
1704 NULLIFY_INS (args [0]);
1705 return ins;
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);
1714 return 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) &&
1727 cfg->compile_aot) {
1728 MonoInst *pi;
1729 MonoJumpInfoToken *ji;
1730 char *s;
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;
1737 } else {
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);
1749 // FIXME: Leaks
1750 ins->inst_p0 = s;
1751 MONO_ADD_INS (cfg->cbb, ins);
1752 return 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]);
1773 return ins;
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);
1795 if (ins)
1796 return ins;
1798 #endif
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;
1805 return ins;
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;
1814 return ins;
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
1821 if (in_corlib &&
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;
1826 return ins;
1828 #endif
1830 ins = mono_emit_native_types_intrinsics (cfg, cmethod, fsig, args);
1831 if (ins)
1832 return ins;
1834 if (COMPILE_LLVM (cfg)) {
1835 ins = llvm_emit_inst_for_method (cfg, cmethod, fsig, args, in_corlib);
1836 if (ins)
1837 return ins;
1840 return mono_arch_emit_inst_for_method (cfg, cmethod, fsig, args);
1844 static MonoInst*
1845 emit_array_unsafe_access (MonoCompile *cfg, MonoMethodSignature *fsig, MonoInst **args, int is_set)
1847 MonoClass *eklass;
1849 if (is_set)
1850 eklass = mono_class_from_mono_type_internal (fsig->params [2]);
1851 else
1852 eklass = mono_class_from_mono_type_internal (fsig->ret);
1854 if (is_set) {
1855 return mini_emit_array_store (cfg, eklass, args, FALSE);
1856 } else {
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);
1859 return ins;
1863 static gboolean
1864 is_unsafe_mov_compatible (MonoCompile *cfg, MonoClass *param_klass, MonoClass *return_klass)
1866 uint32_t align;
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");
1879 return FALSE;
1882 if (!m_class_is_valuetype (param_klass)) {
1883 if (cfg->verbose_level > 3)
1884 printf ("[UNSAFE-MOV-INTRISIC]\targs are reference types\n");
1885 return TRUE;
1888 //That are blitable
1889 if (m_class_has_references (param_klass) || m_class_has_references (return_klass))
1890 return FALSE;
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");
1900 return FALSE;
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");
1907 return FALSE;
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");
1917 return TRUE;
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");
1924 return FALSE;
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");
1938 return TRUE;
1941 return FALSE;
1944 static MonoInst*
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))
1951 return NULL;
1953 //Valuetypes that are semantically equivalent or numbers than can be widened to
1954 if (is_unsafe_mov_compatible (cfg, param_klass, return_klass))
1955 return args [0];
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)))
1959 return args [0];
1961 return NULL;
1964 MonoInst*
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);
1976 return NULL;
1979 MonoInst*
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;
1987 gboolean is_le;
1988 MonoInst *ins;
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);
1993 return ins;
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);
1998 return ins;
2000 #endif
2001 return NULL;
2003 #else
2004 MONO_EMPTY_SOURCE_FILE (intrinsics);
2005 #endif