2 * method-to-ir.c: Convert CIL to the JIT internal representation
5 * Paolo Molaro (lupus@ximian.com)
6 * Dietmar Maurer (dietmar@ximian.com)
8 * (C) 2002 Ximian, Inc.
9 * Copyright 2003-2010 Novell, Inc (http://www.novell.com)
10 * Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
27 #ifdef HAVE_SYS_TIME_H
35 #include <mono/utils/memcheck.h>
37 #include <mono/metadata/abi-details.h>
38 #include <mono/metadata/assembly.h>
39 #include <mono/metadata/attrdefs.h>
40 #include <mono/metadata/loader.h>
41 #include <mono/metadata/tabledefs.h>
42 #include <mono/metadata/class.h>
43 #include <mono/metadata/object.h>
44 #include <mono/metadata/exception.h>
45 #include <mono/metadata/opcodes.h>
46 #include <mono/metadata/mono-endian.h>
47 #include <mono/metadata/tokentype.h>
48 #include <mono/metadata/tabledefs.h>
49 #include <mono/metadata/marshal.h>
50 #include <mono/metadata/debug-helpers.h>
51 #include <mono/metadata/mono-debug.h>
52 #include <mono/metadata/mono-debug-debugger.h>
53 #include <mono/metadata/gc-internals.h>
54 #include <mono/metadata/security-manager.h>
55 #include <mono/metadata/threads-types.h>
56 #include <mono/metadata/security-core-clr.h>
57 #include <mono/metadata/profiler-private.h>
58 #include <mono/metadata/profiler.h>
59 #include <mono/metadata/monitor.h>
60 #include <mono/metadata/debug-mono-symfile.h>
61 #include <mono/utils/mono-compiler.h>
62 #include <mono/utils/mono-memory-model.h>
63 #include <mono/utils/mono-error-internals.h>
64 #include <mono/metadata/mono-basic-block.h>
65 #include <mono/metadata/reflection-internals.h>
71 #include "jit-icalls.h"
73 #include "debugger-agent.h"
74 #include "seq-points.h"
75 #include "aot-compiler.h"
76 #include "mini-llvm.h"
78 #define BRANCH_COST 10
79 #define INLINE_LENGTH_LIMIT 20
81 /* These have 'cfg' as an implicit argument */
82 #define INLINE_FAILURE(msg) do { \
83 if ((cfg->method != cfg->current_method) && (cfg->current_method->wrapper_type == MONO_WRAPPER_NONE)) { \
84 inline_failure (cfg, msg); \
85 goto exception_exit; \
88 #define CHECK_CFG_EXCEPTION do {\
89 if (cfg->exception_type != MONO_EXCEPTION_NONE) \
90 goto exception_exit; \
92 #define METHOD_ACCESS_FAILURE(method, cmethod) do { \
93 method_access_failure ((cfg), (method), (cmethod)); \
94 goto exception_exit; \
96 #define FIELD_ACCESS_FAILURE(method, field) do { \
97 field_access_failure ((cfg), (method), (field)); \
98 goto exception_exit; \
100 #define GENERIC_SHARING_FAILURE(opcode) do { \
101 if (cfg->gshared) { \
102 gshared_failure (cfg, opcode, __FILE__, __LINE__); \
103 goto exception_exit; \
106 #define GSHAREDVT_FAILURE(opcode) do { \
107 if (cfg->gsharedvt) { \
108 gsharedvt_failure (cfg, opcode, __FILE__, __LINE__); \
109 goto exception_exit; \
112 #define OUT_OF_MEMORY_FAILURE do { \
113 mono_cfg_set_exception (cfg, MONO_EXCEPTION_MONO_ERROR); \
114 mono_error_set_out_of_memory (&cfg->error, ""); \
115 goto exception_exit; \
117 #define DISABLE_AOT(cfg) do { \
118 if ((cfg)->verbose_level >= 2) \
119 printf ("AOT disabled: %s:%d\n", __FILE__, __LINE__); \
120 (cfg)->disable_aot = TRUE; \
122 #define LOAD_ERROR do { \
123 break_on_unverified (); \
124 mono_cfg_set_exception (cfg, MONO_EXCEPTION_TYPE_LOAD); \
125 goto exception_exit; \
128 #define TYPE_LOAD_ERROR(klass) do { \
129 cfg->exception_ptr = klass; \
133 #define CHECK_CFG_ERROR do {\
134 if (!mono_error_ok (&cfg->error)) { \
135 mono_cfg_set_exception (cfg, MONO_EXCEPTION_MONO_ERROR); \
136 goto mono_error_exit; \
140 /* Determine whenever 'ins' represents a load of the 'this' argument */
141 #define MONO_CHECK_THIS(ins) (mono_method_signature (cfg->method)->hasthis && ((ins)->opcode == OP_MOVE) && ((ins)->sreg1 == cfg->args [0]->dreg))
143 static int ldind_to_load_membase (int opcode);
144 static int stind_to_store_membase (int opcode);
146 int mono_op_to_op_imm (int opcode);
147 int mono_op_to_op_imm_noemul (int opcode);
149 MONO_API MonoInst* mono_emit_native_call (MonoCompile *cfg, gconstpointer func, MonoMethodSignature *sig, MonoInst **args);
151 static int inline_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **sp,
152 guchar *ip, guint real_offset, gboolean inline_always);
154 emit_llvmonly_virtual_call (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, int context_used, MonoInst **sp);
156 /* helper methods signatures */
157 static MonoMethodSignature *helper_sig_domain_get;
158 static MonoMethodSignature *helper_sig_rgctx_lazy_fetch_trampoline;
159 static MonoMethodSignature *helper_sig_llvmonly_imt_thunk;
162 /* type loading helpers */
163 static GENERATE_GET_CLASS_WITH_CACHE (runtime_helpers, System.Runtime.CompilerServices, RuntimeHelpers)
164 static GENERATE_TRY_GET_CLASS_WITH_CACHE (debuggable_attribute, System.Diagnostics, DebuggableAttribute)
167 * Instruction metadata
175 #define MINI_OP(a,b,dest,src1,src2) dest, src1, src2, ' ',
176 #define MINI_OP3(a,b,dest,src1,src2,src3) dest, src1, src2, src3,
182 #if SIZEOF_REGISTER == 8 && SIZEOF_REGISTER == SIZEOF_VOID_P
187 /* keep in sync with the enum in mini.h */
190 #include "mini-ops.h"
195 #define MINI_OP(a,b,dest,src1,src2) ((src2) != NONE ? 2 : ((src1) != NONE ? 1 : 0)),
196 #define MINI_OP3(a,b,dest,src1,src2,src3) ((src3) != NONE ? 3 : ((src2) != NONE ? 2 : ((src1) != NONE ? 1 : 0))),
198 * This should contain the index of the last sreg + 1. This is not the same
199 * as the number of sregs for opcodes like IA64_CMP_EQ_IMM.
201 const gint8 ins_sreg_counts[] = {
202 #include "mini-ops.h"
207 #define MONO_INIT_VARINFO(vi,id) do { \
208 (vi)->range.first_use.pos.bid = 0xffff; \
214 mono_alloc_ireg (MonoCompile *cfg)
216 return alloc_ireg (cfg);
220 mono_alloc_lreg (MonoCompile *cfg)
222 return alloc_lreg (cfg);
226 mono_alloc_freg (MonoCompile *cfg)
228 return alloc_freg (cfg);
232 mono_alloc_preg (MonoCompile *cfg)
234 return alloc_preg (cfg);
238 mono_alloc_dreg (MonoCompile *cfg, MonoStackType stack_type)
240 return alloc_dreg (cfg, stack_type);
244 * mono_alloc_ireg_ref:
246 * Allocate an IREG, and mark it as holding a GC ref.
249 mono_alloc_ireg_ref (MonoCompile *cfg)
251 return alloc_ireg_ref (cfg);
255 * mono_alloc_ireg_mp:
257 * Allocate an IREG, and mark it as holding a managed pointer.
260 mono_alloc_ireg_mp (MonoCompile *cfg)
262 return alloc_ireg_mp (cfg);
266 * mono_alloc_ireg_copy:
268 * Allocate an IREG with the same GC type as VREG.
271 mono_alloc_ireg_copy (MonoCompile *cfg, guint32 vreg)
273 if (vreg_is_ref (cfg, vreg))
274 return alloc_ireg_ref (cfg);
275 else if (vreg_is_mp (cfg, vreg))
276 return alloc_ireg_mp (cfg);
278 return alloc_ireg (cfg);
282 mono_type_to_regmove (MonoCompile *cfg, MonoType *type)
287 type = mini_get_underlying_type (type);
289 switch (type->type) {
302 case MONO_TYPE_FNPTR:
304 case MONO_TYPE_CLASS:
305 case MONO_TYPE_STRING:
306 case MONO_TYPE_OBJECT:
307 case MONO_TYPE_SZARRAY:
308 case MONO_TYPE_ARRAY:
312 #if SIZEOF_REGISTER == 8
318 return cfg->r4fp ? OP_RMOVE : OP_FMOVE;
321 case MONO_TYPE_VALUETYPE:
322 if (type->data.klass->enumtype) {
323 type = mono_class_enum_basetype (type->data.klass);
326 if (MONO_CLASS_IS_SIMD (cfg, mono_class_from_mono_type (type)))
329 case MONO_TYPE_TYPEDBYREF:
331 case MONO_TYPE_GENERICINST:
332 type = &type->data.generic_class->container_class->byval_arg;
336 g_assert (cfg->gshared);
337 if (mini_type_var_is_vt (type))
340 return mono_type_to_regmove (cfg, mini_get_underlying_type (type));
342 g_error ("unknown type 0x%02x in type_to_regstore", type->type);
348 mono_print_bb (MonoBasicBlock *bb, const char *msg)
353 printf ("\n%s %d: [IN: ", msg, bb->block_num);
354 for (i = 0; i < bb->in_count; ++i)
355 printf (" BB%d(%d)", bb->in_bb [i]->block_num, bb->in_bb [i]->dfn);
357 for (i = 0; i < bb->out_count; ++i)
358 printf (" BB%d(%d)", bb->out_bb [i]->block_num, bb->out_bb [i]->dfn);
360 for (tree = bb->code; tree; tree = tree->next)
361 mono_print_ins_index (-1, tree);
365 mono_create_helper_signatures (void)
367 helper_sig_domain_get = mono_create_icall_signature ("ptr");
368 helper_sig_rgctx_lazy_fetch_trampoline = mono_create_icall_signature ("ptr ptr");
369 helper_sig_llvmonly_imt_thunk = mono_create_icall_signature ("ptr ptr ptr");
372 static MONO_NEVER_INLINE void
373 break_on_unverified (void)
375 if (mini_get_debug_options ()->break_on_unverified)
379 static MONO_NEVER_INLINE void
380 method_access_failure (MonoCompile *cfg, MonoMethod *method, MonoMethod *cil_method)
382 char *method_fname = mono_method_full_name (method, TRUE);
383 char *cil_method_fname = mono_method_full_name (cil_method, TRUE);
384 mono_cfg_set_exception (cfg, MONO_EXCEPTION_MONO_ERROR);
385 mono_error_set_generic_error (&cfg->error, "System", "MethodAccessException", "Method `%s' is inaccessible from method `%s'\n", cil_method_fname, method_fname);
386 g_free (method_fname);
387 g_free (cil_method_fname);
390 static MONO_NEVER_INLINE void
391 field_access_failure (MonoCompile *cfg, MonoMethod *method, MonoClassField *field)
393 char *method_fname = mono_method_full_name (method, TRUE);
394 char *field_fname = mono_field_full_name (field);
395 mono_cfg_set_exception (cfg, MONO_EXCEPTION_MONO_ERROR);
396 mono_error_set_generic_error (&cfg->error, "System", "FieldAccessException", "Field `%s' is inaccessible from method `%s'\n", field_fname, method_fname);
397 g_free (method_fname);
398 g_free (field_fname);
401 static MONO_NEVER_INLINE void
402 inline_failure (MonoCompile *cfg, const char *msg)
404 if (cfg->verbose_level >= 2)
405 printf ("inline failed: %s\n", msg);
406 mono_cfg_set_exception (cfg, MONO_EXCEPTION_INLINE_FAILED);
409 static MONO_NEVER_INLINE void
410 gshared_failure (MonoCompile *cfg, int opcode, const char *file, int line)
412 if (cfg->verbose_level > 2) \
413 printf ("sharing failed for method %s.%s.%s/%d opcode %s line %d\n", cfg->current_method->klass->name_space, cfg->current_method->klass->name, cfg->current_method->name, cfg->current_method->signature->param_count, mono_opcode_name ((opcode)), line);
414 mono_cfg_set_exception (cfg, MONO_EXCEPTION_GENERIC_SHARING_FAILED);
417 static MONO_NEVER_INLINE void
418 gsharedvt_failure (MonoCompile *cfg, int opcode, const char *file, int line)
420 cfg->exception_message = g_strdup_printf ("gsharedvt failed for method %s.%s.%s/%d opcode %s %s:%d", cfg->current_method->klass->name_space, cfg->current_method->klass->name, cfg->current_method->name, cfg->current_method->signature->param_count, mono_opcode_name ((opcode)), file, line);
421 if (cfg->verbose_level >= 2)
422 printf ("%s\n", cfg->exception_message);
423 mono_cfg_set_exception (cfg, MONO_EXCEPTION_GENERIC_SHARING_FAILED);
427 * When using gsharedvt, some instatiations might be verifiable, and some might be not. i.e.
428 * foo<T> (int i) { ldarg.0; box T; }
430 #define UNVERIFIED do { \
431 if (cfg->gsharedvt) { \
432 if (cfg->verbose_level > 2) \
433 printf ("gsharedvt method failed to verify, falling back to instantiation.\n"); \
434 mono_cfg_set_exception (cfg, MONO_EXCEPTION_GENERIC_SHARING_FAILED); \
435 goto exception_exit; \
437 break_on_unverified (); \
441 #define GET_BBLOCK(cfg,tblock,ip) do { \
442 (tblock) = cfg->cil_offset_to_bb [(ip) - cfg->cil_start]; \
444 if ((ip) >= end || (ip) < header->code) UNVERIFIED; \
445 NEW_BBLOCK (cfg, (tblock)); \
446 (tblock)->cil_code = (ip); \
447 ADD_BBLOCK (cfg, (tblock)); \
451 #if defined(TARGET_X86) || defined(TARGET_AMD64)
452 #define EMIT_NEW_X86_LEA(cfg,dest,sr1,sr2,shift,imm) do { \
453 MONO_INST_NEW (cfg, dest, OP_X86_LEA); \
454 (dest)->dreg = alloc_ireg_mp ((cfg)); \
455 (dest)->sreg1 = (sr1); \
456 (dest)->sreg2 = (sr2); \
457 (dest)->inst_imm = (imm); \
458 (dest)->backend.shift_amount = (shift); \
459 MONO_ADD_INS ((cfg)->cbb, (dest)); \
463 /* Emit conversions so both operands of a binary opcode are of the same type */
465 add_widen_op (MonoCompile *cfg, MonoInst *ins, MonoInst **arg1_ref, MonoInst **arg2_ref)
467 MonoInst *arg1 = *arg1_ref;
468 MonoInst *arg2 = *arg2_ref;
471 ((arg1->type == STACK_R4 && arg2->type == STACK_R8) ||
472 (arg1->type == STACK_R8 && arg2->type == STACK_R4))) {
475 /* Mixing r4/r8 is allowed by the spec */
476 if (arg1->type == STACK_R4) {
477 int dreg = alloc_freg (cfg);
479 EMIT_NEW_UNALU (cfg, conv, OP_RCONV_TO_R8, dreg, arg1->dreg);
480 conv->type = STACK_R8;
484 if (arg2->type == STACK_R4) {
485 int dreg = alloc_freg (cfg);
487 EMIT_NEW_UNALU (cfg, conv, OP_RCONV_TO_R8, dreg, arg2->dreg);
488 conv->type = STACK_R8;
494 #if SIZEOF_REGISTER == 8
495 /* FIXME: Need to add many more cases */
496 if ((arg1)->type == STACK_PTR && (arg2)->type == STACK_I4) {
499 int dr = alloc_preg (cfg);
500 EMIT_NEW_UNALU (cfg, widen, OP_SEXT_I4, dr, (arg2)->dreg);
501 (ins)->sreg2 = widen->dreg;
506 #define ADD_BINOP(op) do { \
507 MONO_INST_NEW (cfg, ins, (op)); \
509 ins->sreg1 = sp [0]->dreg; \
510 ins->sreg2 = sp [1]->dreg; \
511 type_from_op (cfg, ins, sp [0], sp [1]); \
513 /* Have to insert a widening op */ \
514 add_widen_op (cfg, ins, &sp [0], &sp [1]); \
515 ins->dreg = alloc_dreg ((cfg), (MonoStackType)(ins)->type); \
516 MONO_ADD_INS ((cfg)->cbb, (ins)); \
517 *sp++ = mono_decompose_opcode ((cfg), (ins)); \
520 #define ADD_UNOP(op) do { \
521 MONO_INST_NEW (cfg, ins, (op)); \
523 ins->sreg1 = sp [0]->dreg; \
524 type_from_op (cfg, ins, sp [0], NULL); \
526 (ins)->dreg = alloc_dreg ((cfg), (MonoStackType)(ins)->type); \
527 MONO_ADD_INS ((cfg)->cbb, (ins)); \
528 *sp++ = mono_decompose_opcode (cfg, ins); \
531 #define ADD_BINCOND(next_block) do { \
534 MONO_INST_NEW(cfg, cmp, OP_COMPARE); \
535 cmp->sreg1 = sp [0]->dreg; \
536 cmp->sreg2 = sp [1]->dreg; \
537 type_from_op (cfg, cmp, sp [0], sp [1]); \
539 add_widen_op (cfg, cmp, &sp [0], &sp [1]); \
540 type_from_op (cfg, ins, sp [0], sp [1]); \
541 ins->inst_many_bb = (MonoBasicBlock **)mono_mempool_alloc (cfg->mempool, sizeof(gpointer)*2); \
542 GET_BBLOCK (cfg, tblock, target); \
543 link_bblock (cfg, cfg->cbb, tblock); \
544 ins->inst_true_bb = tblock; \
545 if ((next_block)) { \
546 link_bblock (cfg, cfg->cbb, (next_block)); \
547 ins->inst_false_bb = (next_block); \
548 start_new_bblock = 1; \
550 GET_BBLOCK (cfg, tblock, ip); \
551 link_bblock (cfg, cfg->cbb, tblock); \
552 ins->inst_false_bb = tblock; \
553 start_new_bblock = 2; \
555 if (sp != stack_start) { \
556 handle_stack_args (cfg, stack_start, sp - stack_start); \
557 CHECK_UNVERIFIABLE (cfg); \
559 MONO_ADD_INS (cfg->cbb, cmp); \
560 MONO_ADD_INS (cfg->cbb, ins); \
564 * link_bblock: Links two basic blocks
566 * links two basic blocks in the control flow graph, the 'from'
567 * argument is the starting block and the 'to' argument is the block
568 * the control flow ends to after 'from'.
571 link_bblock (MonoCompile *cfg, MonoBasicBlock *from, MonoBasicBlock* to)
573 MonoBasicBlock **newa;
577 if (from->cil_code) {
579 printf ("edge from IL%04x to IL_%04x\n", from->cil_code - cfg->cil_code, to->cil_code - cfg->cil_code);
581 printf ("edge from IL%04x to exit\n", from->cil_code - cfg->cil_code);
584 printf ("edge from entry to IL_%04x\n", to->cil_code - cfg->cil_code);
586 printf ("edge from entry to exit\n");
591 for (i = 0; i < from->out_count; ++i) {
592 if (to == from->out_bb [i]) {
598 newa = (MonoBasicBlock **)mono_mempool_alloc (cfg->mempool, sizeof (gpointer) * (from->out_count + 1));
599 for (i = 0; i < from->out_count; ++i) {
600 newa [i] = from->out_bb [i];
608 for (i = 0; i < to->in_count; ++i) {
609 if (from == to->in_bb [i]) {
615 newa = (MonoBasicBlock **)mono_mempool_alloc (cfg->mempool, sizeof (gpointer) * (to->in_count + 1));
616 for (i = 0; i < to->in_count; ++i) {
617 newa [i] = to->in_bb [i];
626 mono_link_bblock (MonoCompile *cfg, MonoBasicBlock *from, MonoBasicBlock* to)
628 link_bblock (cfg, from, to);
632 * mono_find_block_region:
634 * We mark each basic block with a region ID. We use that to avoid BB
635 * optimizations when blocks are in different regions.
638 * A region token that encodes where this region is, and information
639 * about the clause owner for this block.
641 * The region encodes the try/catch/filter clause that owns this block
642 * as well as the type. -1 is a special value that represents a block
643 * that is in none of try/catch/filter.
646 mono_find_block_region (MonoCompile *cfg, int offset)
648 MonoMethodHeader *header = cfg->header;
649 MonoExceptionClause *clause;
652 for (i = 0; i < header->num_clauses; ++i) {
653 clause = &header->clauses [i];
654 if ((clause->flags == MONO_EXCEPTION_CLAUSE_FILTER) && (offset >= clause->data.filter_offset) &&
655 (offset < (clause->handler_offset)))
656 return ((i + 1) << 8) | MONO_REGION_FILTER | clause->flags;
658 if (MONO_OFFSET_IN_HANDLER (clause, offset)) {
659 if (clause->flags == MONO_EXCEPTION_CLAUSE_FINALLY)
660 return ((i + 1) << 8) | MONO_REGION_FINALLY | clause->flags;
661 else if (clause->flags == MONO_EXCEPTION_CLAUSE_FAULT)
662 return ((i + 1) << 8) | MONO_REGION_FAULT | clause->flags;
664 return ((i + 1) << 8) | MONO_REGION_CATCH | clause->flags;
667 for (i = 0; i < header->num_clauses; ++i) {
668 clause = &header->clauses [i];
670 if (MONO_OFFSET_IN_CLAUSE (clause, offset))
671 return ((i + 1) << 8) | clause->flags;
678 mono_find_final_block (MonoCompile *cfg, unsigned char *ip, unsigned char *target, int type)
680 MonoMethodHeader *header = cfg->header;
681 MonoExceptionClause *clause;
685 for (i = 0; i < header->num_clauses; ++i) {
686 clause = &header->clauses [i];
687 if (MONO_OFFSET_IN_CLAUSE (clause, (ip - header->code)) &&
688 (!MONO_OFFSET_IN_CLAUSE (clause, (target - header->code)))) {
689 if (clause->flags == type)
690 res = g_list_append (res, clause);
697 mono_create_spvar_for_region (MonoCompile *cfg, int region)
701 var = (MonoInst *)g_hash_table_lookup (cfg->spvars, GINT_TO_POINTER (region));
705 var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
706 /* prevent it from being register allocated */
707 var->flags |= MONO_INST_VOLATILE;
709 g_hash_table_insert (cfg->spvars, GINT_TO_POINTER (region), var);
713 mono_find_exvar_for_offset (MonoCompile *cfg, int offset)
715 return (MonoInst *)g_hash_table_lookup (cfg->exvars, GINT_TO_POINTER (offset));
719 mono_create_exvar_for_offset (MonoCompile *cfg, int offset)
723 var = (MonoInst *)g_hash_table_lookup (cfg->exvars, GINT_TO_POINTER (offset));
727 var = mono_compile_create_var (cfg, &mono_defaults.object_class->byval_arg, OP_LOCAL);
728 /* prevent it from being register allocated */
729 var->flags |= MONO_INST_VOLATILE;
731 g_hash_table_insert (cfg->exvars, GINT_TO_POINTER (offset), var);
737 * Returns the type used in the eval stack when @type is loaded.
738 * FIXME: return a MonoType/MonoClass for the byref and VALUETYPE cases.
741 type_to_eval_stack_type (MonoCompile *cfg, MonoType *type, MonoInst *inst)
745 type = mini_get_underlying_type (type);
746 inst->klass = klass = mono_class_from_mono_type (type);
748 inst->type = STACK_MP;
753 switch (type->type) {
755 inst->type = STACK_INV;
763 inst->type = STACK_I4;
768 case MONO_TYPE_FNPTR:
769 inst->type = STACK_PTR;
771 case MONO_TYPE_CLASS:
772 case MONO_TYPE_STRING:
773 case MONO_TYPE_OBJECT:
774 case MONO_TYPE_SZARRAY:
775 case MONO_TYPE_ARRAY:
776 inst->type = STACK_OBJ;
780 inst->type = STACK_I8;
783 inst->type = cfg->r4_stack_type;
786 inst->type = STACK_R8;
788 case MONO_TYPE_VALUETYPE:
789 if (type->data.klass->enumtype) {
790 type = mono_class_enum_basetype (type->data.klass);
794 inst->type = STACK_VTYPE;
797 case MONO_TYPE_TYPEDBYREF:
798 inst->klass = mono_defaults.typed_reference_class;
799 inst->type = STACK_VTYPE;
801 case MONO_TYPE_GENERICINST:
802 type = &type->data.generic_class->container_class->byval_arg;
806 g_assert (cfg->gshared);
807 if (mini_is_gsharedvt_type (type)) {
808 g_assert (cfg->gsharedvt);
809 inst->type = STACK_VTYPE;
811 type_to_eval_stack_type (cfg, mini_get_underlying_type (type), inst);
815 g_error ("unknown type 0x%02x in eval stack type", type->type);
820 * The following tables are used to quickly validate the IL code in type_from_op ().
823 bin_num_table [STACK_MAX] [STACK_MAX] = {
824 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
825 {STACK_INV, STACK_I4, STACK_INV, STACK_PTR, STACK_INV, STACK_MP, STACK_INV, STACK_INV},
826 {STACK_INV, STACK_INV, STACK_I8, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
827 {STACK_INV, STACK_PTR, STACK_INV, STACK_PTR, STACK_INV, STACK_MP, STACK_INV, STACK_INV},
828 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_R8, STACK_INV, STACK_INV, STACK_INV, STACK_R8},
829 {STACK_INV, STACK_MP, STACK_INV, STACK_MP, STACK_INV, STACK_PTR, STACK_INV, STACK_INV},
830 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
831 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
832 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_R8, STACK_INV, STACK_INV, STACK_INV, STACK_R4}
837 STACK_INV, STACK_I4, STACK_I8, STACK_PTR, STACK_R8, STACK_INV, STACK_INV, STACK_INV, STACK_R4
840 /* reduce the size of this table */
842 bin_int_table [STACK_MAX] [STACK_MAX] = {
843 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
844 {STACK_INV, STACK_I4, STACK_INV, STACK_PTR, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
845 {STACK_INV, STACK_INV, STACK_I8, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
846 {STACK_INV, STACK_PTR, STACK_INV, STACK_PTR, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
847 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
848 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
849 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
850 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV}
854 bin_comp_table [STACK_MAX] [STACK_MAX] = {
855 /* Inv i L p F & O vt r4 */
857 {0, 1, 0, 1, 0, 0, 0, 0}, /* i, int32 */
858 {0, 0, 1, 0, 0, 0, 0, 0}, /* L, int64 */
859 {0, 1, 0, 1, 0, 2, 4, 0}, /* p, ptr */
860 {0, 0, 0, 0, 1, 0, 0, 0, 1}, /* F, R8 */
861 {0, 0, 0, 2, 0, 1, 0, 0}, /* &, managed pointer */
862 {0, 0, 0, 4, 0, 0, 3, 0}, /* O, reference */
863 {0, 0, 0, 0, 0, 0, 0, 0}, /* vt value type */
864 {0, 0, 0, 0, 1, 0, 0, 0, 1}, /* r, r4 */
867 /* reduce the size of this table */
869 shift_table [STACK_MAX] [STACK_MAX] = {
870 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
871 {STACK_INV, STACK_I4, STACK_INV, STACK_I4, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
872 {STACK_INV, STACK_I8, STACK_INV, STACK_I8, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
873 {STACK_INV, STACK_PTR, STACK_INV, STACK_PTR, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
874 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
875 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
876 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
877 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV}
881 * Tables to map from the non-specific opcode to the matching
882 * type-specific opcode.
884 /* handles from CEE_ADD to CEE_SHR_UN (CEE_REM_UN for floats) */
886 binops_op_map [STACK_MAX] = {
887 0, OP_IADD-CEE_ADD, OP_LADD-CEE_ADD, OP_PADD-CEE_ADD, OP_FADD-CEE_ADD, OP_PADD-CEE_ADD, 0, 0, OP_RADD-CEE_ADD
890 /* handles from CEE_NEG to CEE_CONV_U8 */
892 unops_op_map [STACK_MAX] = {
893 0, OP_INEG-CEE_NEG, OP_LNEG-CEE_NEG, OP_PNEG-CEE_NEG, OP_FNEG-CEE_NEG, OP_PNEG-CEE_NEG, 0, 0, OP_RNEG-CEE_NEG
896 /* handles from CEE_CONV_U2 to CEE_SUB_OVF_UN */
898 ovfops_op_map [STACK_MAX] = {
899 0, OP_ICONV_TO_U2-CEE_CONV_U2, OP_LCONV_TO_U2-CEE_CONV_U2, OP_PCONV_TO_U2-CEE_CONV_U2, OP_FCONV_TO_U2-CEE_CONV_U2, OP_PCONV_TO_U2-CEE_CONV_U2, OP_PCONV_TO_U2-CEE_CONV_U2, 0, OP_RCONV_TO_U2-CEE_CONV_U2
902 /* handles from CEE_CONV_OVF_I1_UN to CEE_CONV_OVF_U_UN */
904 ovf2ops_op_map [STACK_MAX] = {
905 0, OP_ICONV_TO_OVF_I1_UN-CEE_CONV_OVF_I1_UN, OP_LCONV_TO_OVF_I1_UN-CEE_CONV_OVF_I1_UN, OP_PCONV_TO_OVF_I1_UN-CEE_CONV_OVF_I1_UN, OP_FCONV_TO_OVF_I1_UN-CEE_CONV_OVF_I1_UN, OP_PCONV_TO_OVF_I1_UN-CEE_CONV_OVF_I1_UN, 0, 0, OP_RCONV_TO_OVF_I1_UN-CEE_CONV_OVF_I1_UN
908 /* handles from CEE_CONV_OVF_I1 to CEE_CONV_OVF_U8 */
910 ovf3ops_op_map [STACK_MAX] = {
911 0, OP_ICONV_TO_OVF_I1-CEE_CONV_OVF_I1, OP_LCONV_TO_OVF_I1-CEE_CONV_OVF_I1, OP_PCONV_TO_OVF_I1-CEE_CONV_OVF_I1, OP_FCONV_TO_OVF_I1-CEE_CONV_OVF_I1, OP_PCONV_TO_OVF_I1-CEE_CONV_OVF_I1, 0, 0, OP_RCONV_TO_OVF_I1-CEE_CONV_OVF_I1
914 /* handles from CEE_BEQ to CEE_BLT_UN */
916 beqops_op_map [STACK_MAX] = {
917 0, OP_IBEQ-CEE_BEQ, OP_LBEQ-CEE_BEQ, OP_PBEQ-CEE_BEQ, OP_FBEQ-CEE_BEQ, OP_PBEQ-CEE_BEQ, OP_PBEQ-CEE_BEQ, 0, OP_FBEQ-CEE_BEQ
920 /* handles from CEE_CEQ to CEE_CLT_UN */
922 ceqops_op_map [STACK_MAX] = {
923 0, OP_ICEQ-OP_CEQ, OP_LCEQ-OP_CEQ, OP_PCEQ-OP_CEQ, OP_FCEQ-OP_CEQ, OP_PCEQ-OP_CEQ, OP_PCEQ-OP_CEQ, 0, OP_RCEQ-OP_CEQ
927 * Sets ins->type (the type on the eval stack) according to the
928 * type of the opcode and the arguments to it.
929 * Invalid IL code is marked by setting ins->type to the invalid value STACK_INV.
931 * FIXME: this function sets ins->type unconditionally in some cases, but
932 * it should set it to invalid for some types (a conv.x on an object)
935 type_from_op (MonoCompile *cfg, MonoInst *ins, MonoInst *src1, MonoInst *src2)
937 switch (ins->opcode) {
944 /* FIXME: check unverifiable args for STACK_MP */
945 ins->type = bin_num_table [src1->type] [src2->type];
946 ins->opcode += binops_op_map [ins->type];
953 ins->type = bin_int_table [src1->type] [src2->type];
954 ins->opcode += binops_op_map [ins->type];
959 ins->type = shift_table [src1->type] [src2->type];
960 ins->opcode += binops_op_map [ins->type];
965 ins->type = bin_comp_table [src1->type] [src2->type] ? STACK_I4: STACK_INV;
966 if ((src1->type == STACK_I8) || ((SIZEOF_VOID_P == 8) && ((src1->type == STACK_PTR) || (src1->type == STACK_OBJ) || (src1->type == STACK_MP))))
967 ins->opcode = OP_LCOMPARE;
968 else if (src1->type == STACK_R4)
969 ins->opcode = OP_RCOMPARE;
970 else if (src1->type == STACK_R8)
971 ins->opcode = OP_FCOMPARE;
973 ins->opcode = OP_ICOMPARE;
975 case OP_ICOMPARE_IMM:
976 ins->type = bin_comp_table [src1->type] [src1->type] ? STACK_I4 : STACK_INV;
977 if ((src1->type == STACK_I8) || ((SIZEOF_VOID_P == 8) && ((src1->type == STACK_PTR) || (src1->type == STACK_OBJ) || (src1->type == STACK_MP))))
978 ins->opcode = OP_LCOMPARE_IMM;
990 ins->opcode += beqops_op_map [src1->type];
993 ins->type = bin_comp_table [src1->type] [src2->type] ? STACK_I4: STACK_INV;
994 ins->opcode += ceqops_op_map [src1->type];
1000 ins->type = (bin_comp_table [src1->type] [src2->type] & 1) ? STACK_I4: STACK_INV;
1001 ins->opcode += ceqops_op_map [src1->type];
1005 ins->type = neg_table [src1->type];
1006 ins->opcode += unops_op_map [ins->type];
1009 if (src1->type >= STACK_I4 && src1->type <= STACK_PTR)
1010 ins->type = src1->type;
1012 ins->type = STACK_INV;
1013 ins->opcode += unops_op_map [ins->type];
1019 ins->type = STACK_I4;
1020 ins->opcode += unops_op_map [src1->type];
1023 ins->type = STACK_R8;
1024 switch (src1->type) {
1027 ins->opcode = OP_ICONV_TO_R_UN;
1030 ins->opcode = OP_LCONV_TO_R_UN;
1034 case CEE_CONV_OVF_I1:
1035 case CEE_CONV_OVF_U1:
1036 case CEE_CONV_OVF_I2:
1037 case CEE_CONV_OVF_U2:
1038 case CEE_CONV_OVF_I4:
1039 case CEE_CONV_OVF_U4:
1040 ins->type = STACK_I4;
1041 ins->opcode += ovf3ops_op_map [src1->type];
1043 case CEE_CONV_OVF_I_UN:
1044 case CEE_CONV_OVF_U_UN:
1045 ins->type = STACK_PTR;
1046 ins->opcode += ovf2ops_op_map [src1->type];
1048 case CEE_CONV_OVF_I1_UN:
1049 case CEE_CONV_OVF_I2_UN:
1050 case CEE_CONV_OVF_I4_UN:
1051 case CEE_CONV_OVF_U1_UN:
1052 case CEE_CONV_OVF_U2_UN:
1053 case CEE_CONV_OVF_U4_UN:
1054 ins->type = STACK_I4;
1055 ins->opcode += ovf2ops_op_map [src1->type];
1058 ins->type = STACK_PTR;
1059 switch (src1->type) {
1061 ins->opcode = OP_ICONV_TO_U;
1065 #if SIZEOF_VOID_P == 8
1066 ins->opcode = OP_LCONV_TO_U;
1068 ins->opcode = OP_MOVE;
1072 ins->opcode = OP_LCONV_TO_U;
1075 ins->opcode = OP_FCONV_TO_U;
1081 ins->type = STACK_I8;
1082 ins->opcode += unops_op_map [src1->type];
1084 case CEE_CONV_OVF_I8:
1085 case CEE_CONV_OVF_U8:
1086 ins->type = STACK_I8;
1087 ins->opcode += ovf3ops_op_map [src1->type];
1089 case CEE_CONV_OVF_U8_UN:
1090 case CEE_CONV_OVF_I8_UN:
1091 ins->type = STACK_I8;
1092 ins->opcode += ovf2ops_op_map [src1->type];
1095 ins->type = cfg->r4_stack_type;
1096 ins->opcode += unops_op_map [src1->type];
1099 ins->type = STACK_R8;
1100 ins->opcode += unops_op_map [src1->type];
1103 ins->type = STACK_R8;
1107 ins->type = STACK_I4;
1108 ins->opcode += ovfops_op_map [src1->type];
1111 case CEE_CONV_OVF_I:
1112 case CEE_CONV_OVF_U:
1113 ins->type = STACK_PTR;
1114 ins->opcode += ovfops_op_map [src1->type];
1117 case CEE_ADD_OVF_UN:
1119 case CEE_MUL_OVF_UN:
1121 case CEE_SUB_OVF_UN:
1122 ins->type = bin_num_table [src1->type] [src2->type];
1123 ins->opcode += ovfops_op_map [src1->type];
1124 if (ins->type == STACK_R8)
1125 ins->type = STACK_INV;
1127 case OP_LOAD_MEMBASE:
1128 ins->type = STACK_PTR;
1130 case OP_LOADI1_MEMBASE:
1131 case OP_LOADU1_MEMBASE:
1132 case OP_LOADI2_MEMBASE:
1133 case OP_LOADU2_MEMBASE:
1134 case OP_LOADI4_MEMBASE:
1135 case OP_LOADU4_MEMBASE:
1136 ins->type = STACK_PTR;
1138 case OP_LOADI8_MEMBASE:
1139 ins->type = STACK_I8;
1141 case OP_LOADR4_MEMBASE:
1142 ins->type = cfg->r4_stack_type;
1144 case OP_LOADR8_MEMBASE:
1145 ins->type = STACK_R8;
1148 g_error ("opcode 0x%04x not handled in type from op", ins->opcode);
1152 if (ins->type == STACK_MP)
1153 ins->klass = mono_defaults.object_class;
1158 STACK_I4, STACK_I4, STACK_I4, STACK_I4, STACK_I4, STACK_I4, STACK_I8, STACK_PTR, STACK_R8, STACK_R8, STACK_OBJ
1164 param_table [STACK_MAX] [STACK_MAX] = {
1169 check_values_to_signature (MonoInst *args, MonoType *this_ins, MonoMethodSignature *sig)
1174 switch (args->type) {
1184 for (i = 0; i < sig->param_count; ++i) {
1185 switch (args [i].type) {
1189 if (!sig->params [i]->byref)
1193 if (sig->params [i]->byref)
1195 switch (sig->params [i]->type) {
1196 case MONO_TYPE_CLASS:
1197 case MONO_TYPE_STRING:
1198 case MONO_TYPE_OBJECT:
1199 case MONO_TYPE_SZARRAY:
1200 case MONO_TYPE_ARRAY:
1207 if (sig->params [i]->byref)
1209 if (sig->params [i]->type != MONO_TYPE_R4 && sig->params [i]->type != MONO_TYPE_R8)
1218 /*if (!param_table [args [i].type] [sig->params [i]->type])
1226 * When we need a pointer to the current domain many times in a method, we
1227 * call mono_domain_get() once and we store the result in a local variable.
1228 * This function returns the variable that represents the MonoDomain*.
1230 inline static MonoInst *
1231 mono_get_domainvar (MonoCompile *cfg)
1233 if (!cfg->domainvar)
1234 cfg->domainvar = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
1235 return cfg->domainvar;
1239 * The got_var contains the address of the Global Offset Table when AOT
1243 mono_get_got_var (MonoCompile *cfg)
1245 if (!cfg->compile_aot || !cfg->backend->need_got_var)
1247 if (!cfg->got_var) {
1248 cfg->got_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
1250 return cfg->got_var;
1254 mono_get_vtable_var (MonoCompile *cfg)
1256 g_assert (cfg->gshared);
1258 if (!cfg->rgctx_var) {
1259 cfg->rgctx_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
1260 /* force the var to be stack allocated */
1261 cfg->rgctx_var->flags |= MONO_INST_VOLATILE;
1264 return cfg->rgctx_var;
1268 type_from_stack_type (MonoInst *ins) {
1269 switch (ins->type) {
1270 case STACK_I4: return &mono_defaults.int32_class->byval_arg;
1271 case STACK_I8: return &mono_defaults.int64_class->byval_arg;
1272 case STACK_PTR: return &mono_defaults.int_class->byval_arg;
1273 case STACK_R4: return &mono_defaults.single_class->byval_arg;
1274 case STACK_R8: return &mono_defaults.double_class->byval_arg;
1276 return &ins->klass->this_arg;
1277 case STACK_OBJ: return &mono_defaults.object_class->byval_arg;
1278 case STACK_VTYPE: return &ins->klass->byval_arg;
1280 g_error ("stack type %d to monotype not handled\n", ins->type);
1285 static G_GNUC_UNUSED int
1286 type_to_stack_type (MonoCompile *cfg, MonoType *t)
1288 t = mono_type_get_underlying_type (t);
1300 case MONO_TYPE_FNPTR:
1302 case MONO_TYPE_CLASS:
1303 case MONO_TYPE_STRING:
1304 case MONO_TYPE_OBJECT:
1305 case MONO_TYPE_SZARRAY:
1306 case MONO_TYPE_ARRAY:
1312 return cfg->r4_stack_type;
1315 case MONO_TYPE_VALUETYPE:
1316 case MONO_TYPE_TYPEDBYREF:
1318 case MONO_TYPE_GENERICINST:
1319 if (mono_type_generic_inst_is_valuetype (t))
1325 g_assert_not_reached ();
1332 array_access_to_klass (int opcode)
1336 return mono_defaults.byte_class;
1338 return mono_defaults.uint16_class;
1341 return mono_defaults.int_class;
1344 return mono_defaults.sbyte_class;
1347 return mono_defaults.int16_class;
1350 return mono_defaults.int32_class;
1352 return mono_defaults.uint32_class;
1355 return mono_defaults.int64_class;
1358 return mono_defaults.single_class;
1361 return mono_defaults.double_class;
1362 case CEE_LDELEM_REF:
1363 case CEE_STELEM_REF:
1364 return mono_defaults.object_class;
1366 g_assert_not_reached ();
1372 * We try to share variables when possible
1375 mono_compile_get_interface_var (MonoCompile *cfg, int slot, MonoInst *ins)
1380 /* inlining can result in deeper stacks */
1381 if (slot >= cfg->header->max_stack)
1382 return mono_compile_create_var (cfg, type_from_stack_type (ins), OP_LOCAL);
1384 pos = ins->type - 1 + slot * STACK_MAX;
1386 switch (ins->type) {
1393 if ((vnum = cfg->intvars [pos]))
1394 return cfg->varinfo [vnum];
1395 res = mono_compile_create_var (cfg, type_from_stack_type (ins), OP_LOCAL);
1396 cfg->intvars [pos] = res->inst_c0;
1399 res = mono_compile_create_var (cfg, type_from_stack_type (ins), OP_LOCAL);
1405 mono_save_token_info (MonoCompile *cfg, MonoImage *image, guint32 token, gpointer key)
1408 * Don't use this if a generic_context is set, since that means AOT can't
1409 * look up the method using just the image+token.
1410 * table == 0 means this is a reference made from a wrapper.
1412 if (cfg->compile_aot && !cfg->generic_context && (mono_metadata_token_table (token) > 0)) {
1413 MonoJumpInfoToken *jump_info_token = (MonoJumpInfoToken *)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoJumpInfoToken));
1414 jump_info_token->image = image;
1415 jump_info_token->token = token;
1416 g_hash_table_insert (cfg->token_info_hash, key, jump_info_token);
1421 * This function is called to handle items that are left on the evaluation stack
1422 * at basic block boundaries. What happens is that we save the values to local variables
1423 * and we reload them later when first entering the target basic block (with the
1424 * handle_loaded_temps () function).
1425 * A single joint point will use the same variables (stored in the array bb->out_stack or
1426 * bb->in_stack, if the basic block is before or after the joint point).
1428 * This function needs to be called _before_ emitting the last instruction of
1429 * the bb (i.e. before emitting a branch).
1430 * If the stack merge fails at a join point, cfg->unverifiable is set.
1433 handle_stack_args (MonoCompile *cfg, MonoInst **sp, int count)
1436 MonoBasicBlock *bb = cfg->cbb;
1437 MonoBasicBlock *outb;
1438 MonoInst *inst, **locals;
1443 if (cfg->verbose_level > 3)
1444 printf ("%d item(s) on exit from B%d\n", count, bb->block_num);
1445 if (!bb->out_scount) {
1446 bb->out_scount = count;
1447 //printf ("bblock %d has out:", bb->block_num);
1449 for (i = 0; i < bb->out_count; ++i) {
1450 outb = bb->out_bb [i];
1451 /* exception handlers are linked, but they should not be considered for stack args */
1452 if (outb->flags & BB_EXCEPTION_HANDLER)
1454 //printf (" %d", outb->block_num);
1455 if (outb->in_stack) {
1457 bb->out_stack = outb->in_stack;
1463 bb->out_stack = (MonoInst **)mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*) * count);
1464 for (i = 0; i < count; ++i) {
1466 * try to reuse temps already allocated for this purpouse, if they occupy the same
1467 * stack slot and if they are of the same type.
1468 * This won't cause conflicts since if 'local' is used to
1469 * store one of the values in the in_stack of a bblock, then
1470 * the same variable will be used for the same outgoing stack
1472 * This doesn't work when inlining methods, since the bblocks
1473 * in the inlined methods do not inherit their in_stack from
1474 * the bblock they are inlined to. See bug #58863 for an
1477 if (cfg->inlined_method)
1478 bb->out_stack [i] = mono_compile_create_var (cfg, type_from_stack_type (sp [i]), OP_LOCAL);
1480 bb->out_stack [i] = mono_compile_get_interface_var (cfg, i, sp [i]);
1485 for (i = 0; i < bb->out_count; ++i) {
1486 outb = bb->out_bb [i];
1487 /* exception handlers are linked, but they should not be considered for stack args */
1488 if (outb->flags & BB_EXCEPTION_HANDLER)
1490 if (outb->in_scount) {
1491 if (outb->in_scount != bb->out_scount) {
1492 cfg->unverifiable = TRUE;
1495 continue; /* check they are the same locals */
1497 outb->in_scount = count;
1498 outb->in_stack = bb->out_stack;
1501 locals = bb->out_stack;
1503 for (i = 0; i < count; ++i) {
1504 EMIT_NEW_TEMPSTORE (cfg, inst, locals [i]->inst_c0, sp [i]);
1505 inst->cil_code = sp [i]->cil_code;
1506 sp [i] = locals [i];
1507 if (cfg->verbose_level > 3)
1508 printf ("storing %d to temp %d\n", i, (int)locals [i]->inst_c0);
1512 * It is possible that the out bblocks already have in_stack assigned, and
1513 * the in_stacks differ. In this case, we will store to all the different
1520 /* Find a bblock which has a different in_stack */
1522 while (bindex < bb->out_count) {
1523 outb = bb->out_bb [bindex];
1524 /* exception handlers are linked, but they should not be considered for stack args */
1525 if (outb->flags & BB_EXCEPTION_HANDLER) {
1529 if (outb->in_stack != locals) {
1530 for (i = 0; i < count; ++i) {
1531 EMIT_NEW_TEMPSTORE (cfg, inst, outb->in_stack [i]->inst_c0, sp [i]);
1532 inst->cil_code = sp [i]->cil_code;
1533 sp [i] = locals [i];
1534 if (cfg->verbose_level > 3)
1535 printf ("storing %d to temp %d\n", i, (int)outb->in_stack [i]->inst_c0);
1537 locals = outb->in_stack;
1547 emit_runtime_constant (MonoCompile *cfg, MonoJumpInfoType patch_type, gpointer data)
1551 if (cfg->compile_aot) {
1552 EMIT_NEW_AOTCONST (cfg, ins, patch_type, data);
1558 ji.type = patch_type;
1559 ji.data.target = data;
1560 target = mono_resolve_patch_target (NULL, cfg->domain, NULL, &ji, FALSE, &error);
1561 mono_error_assert_ok (&error);
1563 EMIT_NEW_PCONST (cfg, ins, target);
1569 mini_emit_interface_bitmap_check (MonoCompile *cfg, int intf_bit_reg, int base_reg, int offset, MonoClass *klass)
1571 int ibitmap_reg = alloc_preg (cfg);
1572 #ifdef COMPRESSED_INTERFACE_BITMAP
1574 MonoInst *res, *ins;
1575 NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, ibitmap_reg, base_reg, offset);
1576 MONO_ADD_INS (cfg->cbb, ins);
1578 args [1] = emit_runtime_constant (cfg, MONO_PATCH_INFO_IID, klass);
1579 res = mono_emit_jit_icall (cfg, mono_class_interface_match, args);
1580 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, intf_bit_reg, res->dreg);
1582 int ibitmap_byte_reg = alloc_preg (cfg);
1584 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, ibitmap_reg, base_reg, offset);
1586 if (cfg->compile_aot) {
1587 int iid_reg = alloc_preg (cfg);
1588 int shifted_iid_reg = alloc_preg (cfg);
1589 int ibitmap_byte_address_reg = alloc_preg (cfg);
1590 int masked_iid_reg = alloc_preg (cfg);
1591 int iid_one_bit_reg = alloc_preg (cfg);
1592 int iid_bit_reg = alloc_preg (cfg);
1593 MONO_EMIT_NEW_AOTCONST (cfg, iid_reg, klass, MONO_PATCH_INFO_IID);
1594 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SHR_IMM, shifted_iid_reg, iid_reg, 3);
1595 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, ibitmap_byte_address_reg, ibitmap_reg, shifted_iid_reg);
1596 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU1_MEMBASE, ibitmap_byte_reg, ibitmap_byte_address_reg, 0);
1597 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_IAND_IMM, masked_iid_reg, iid_reg, 7);
1598 MONO_EMIT_NEW_ICONST (cfg, iid_one_bit_reg, 1);
1599 MONO_EMIT_NEW_BIALU (cfg, OP_ISHL, iid_bit_reg, iid_one_bit_reg, masked_iid_reg);
1600 MONO_EMIT_NEW_BIALU (cfg, OP_IAND, intf_bit_reg, ibitmap_byte_reg, iid_bit_reg);
1602 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI1_MEMBASE, ibitmap_byte_reg, ibitmap_reg, klass->interface_id >> 3);
1603 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_AND_IMM, intf_bit_reg, ibitmap_byte_reg, 1 << (klass->interface_id & 7));
1609 * Emit code which loads into "intf_bit_reg" a nonzero value if the MonoClass
1610 * stored in "klass_reg" implements the interface "klass".
1613 mini_emit_load_intf_bit_reg_class (MonoCompile *cfg, int intf_bit_reg, int klass_reg, MonoClass *klass)
1615 mini_emit_interface_bitmap_check (cfg, intf_bit_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, interface_bitmap), klass);
1619 * Emit code which loads into "intf_bit_reg" a nonzero value if the MonoVTable
1620 * stored in "vtable_reg" implements the interface "klass".
1623 mini_emit_load_intf_bit_reg_vtable (MonoCompile *cfg, int intf_bit_reg, int vtable_reg, MonoClass *klass)
1625 mini_emit_interface_bitmap_check (cfg, intf_bit_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, interface_bitmap), klass);
1629 * Emit code which checks whenever the interface id of @klass is smaller than
1630 * than the value given by max_iid_reg.
1633 mini_emit_max_iid_check (MonoCompile *cfg, int max_iid_reg, MonoClass *klass,
1634 MonoBasicBlock *false_target)
1636 if (cfg->compile_aot) {
1637 int iid_reg = alloc_preg (cfg);
1638 MONO_EMIT_NEW_AOTCONST (cfg, iid_reg, klass, MONO_PATCH_INFO_IID);
1639 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, max_iid_reg, iid_reg);
1642 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, max_iid_reg, klass->interface_id);
1644 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBLT_UN, false_target);
1646 MONO_EMIT_NEW_COND_EXC (cfg, LT_UN, "InvalidCastException");
1649 /* Same as above, but obtains max_iid from a vtable */
1651 mini_emit_max_iid_check_vtable (MonoCompile *cfg, int vtable_reg, MonoClass *klass,
1652 MonoBasicBlock *false_target)
1654 int max_iid_reg = alloc_preg (cfg);
1656 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU2_MEMBASE, max_iid_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, max_interface_id));
1657 mini_emit_max_iid_check (cfg, max_iid_reg, klass, false_target);
1660 /* Same as above, but obtains max_iid from a klass */
1662 mini_emit_max_iid_check_class (MonoCompile *cfg, int klass_reg, MonoClass *klass,
1663 MonoBasicBlock *false_target)
1665 int max_iid_reg = alloc_preg (cfg);
1667 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU2_MEMBASE, max_iid_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, max_interface_id));
1668 mini_emit_max_iid_check (cfg, max_iid_reg, klass, false_target);
1672 mini_emit_isninst_cast_inst (MonoCompile *cfg, int klass_reg, MonoClass *klass, MonoInst *klass_ins, MonoBasicBlock *false_target, MonoBasicBlock *true_target)
1674 int idepth_reg = alloc_preg (cfg);
1675 int stypes_reg = alloc_preg (cfg);
1676 int stype = alloc_preg (cfg);
1678 mono_class_setup_supertypes (klass);
1680 if (klass->idepth > MONO_DEFAULT_SUPERTABLE_SIZE) {
1681 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU2_MEMBASE, idepth_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, idepth));
1682 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, idepth_reg, klass->idepth);
1683 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBLT_UN, false_target);
1685 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, stypes_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, supertypes));
1686 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, stype, stypes_reg, ((klass->idepth - 1) * SIZEOF_VOID_P));
1688 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, stype, klass_ins->dreg);
1689 } else if (cfg->compile_aot) {
1690 int const_reg = alloc_preg (cfg);
1691 MONO_EMIT_NEW_CLASSCONST (cfg, const_reg, klass);
1692 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, stype, const_reg);
1694 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, stype, klass);
1696 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, true_target);
1700 mini_emit_isninst_cast (MonoCompile *cfg, int klass_reg, MonoClass *klass, MonoBasicBlock *false_target, MonoBasicBlock *true_target)
1702 mini_emit_isninst_cast_inst (cfg, klass_reg, klass, NULL, false_target, true_target);
1706 mini_emit_iface_cast (MonoCompile *cfg, int vtable_reg, MonoClass *klass, MonoBasicBlock *false_target, MonoBasicBlock *true_target)
1708 int intf_reg = alloc_preg (cfg);
1710 mini_emit_max_iid_check_vtable (cfg, vtable_reg, klass, false_target);
1711 mini_emit_load_intf_bit_reg_vtable (cfg, intf_reg, vtable_reg, klass);
1712 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, intf_reg, 0);
1714 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, true_target);
1716 MONO_EMIT_NEW_COND_EXC (cfg, EQ, "InvalidCastException");
1720 * Variant of the above that takes a register to the class, not the vtable.
1723 mini_emit_iface_class_cast (MonoCompile *cfg, int klass_reg, MonoClass *klass, MonoBasicBlock *false_target, MonoBasicBlock *true_target)
1725 int intf_bit_reg = alloc_preg (cfg);
1727 mini_emit_max_iid_check_class (cfg, klass_reg, klass, false_target);
1728 mini_emit_load_intf_bit_reg_class (cfg, intf_bit_reg, klass_reg, klass);
1729 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, intf_bit_reg, 0);
1731 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, true_target);
1733 MONO_EMIT_NEW_COND_EXC (cfg, EQ, "InvalidCastException");
1737 mini_emit_class_check_inst (MonoCompile *cfg, int klass_reg, MonoClass *klass, MonoInst *klass_inst)
1740 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, klass_reg, klass_inst->dreg);
1742 MonoInst *ins = emit_runtime_constant (cfg, MONO_PATCH_INFO_CLASS, klass);
1743 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, klass_reg, ins->dreg);
1745 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException");
1749 mini_emit_class_check (MonoCompile *cfg, int klass_reg, MonoClass *klass)
1751 mini_emit_class_check_inst (cfg, klass_reg, klass, NULL);
1755 mini_emit_class_check_branch (MonoCompile *cfg, int klass_reg, MonoClass *klass, int branch_op, MonoBasicBlock *target)
1757 if (cfg->compile_aot) {
1758 int const_reg = alloc_preg (cfg);
1759 MONO_EMIT_NEW_CLASSCONST (cfg, const_reg, klass);
1760 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, klass_reg, const_reg);
1762 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, klass_reg, klass);
1764 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, branch_op, target);
1768 mini_emit_castclass (MonoCompile *cfg, int obj_reg, int klass_reg, MonoClass *klass, MonoBasicBlock *object_is_null);
1771 mini_emit_castclass_inst (MonoCompile *cfg, int obj_reg, int klass_reg, MonoClass *klass, MonoInst *klass_inst, MonoBasicBlock *object_is_null)
1774 int rank_reg = alloc_preg (cfg);
1775 int eclass_reg = alloc_preg (cfg);
1777 g_assert (!klass_inst);
1778 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU1_MEMBASE, rank_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, rank));
1779 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, rank_reg, klass->rank);
1780 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException");
1781 // MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
1782 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, eclass_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, cast_class));
1783 if (klass->cast_class == mono_defaults.object_class) {
1784 int parent_reg = alloc_preg (cfg);
1785 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, parent_reg, eclass_reg, MONO_STRUCT_OFFSET (MonoClass, parent));
1786 mini_emit_class_check_branch (cfg, parent_reg, mono_defaults.enum_class->parent, OP_PBNE_UN, object_is_null);
1787 mini_emit_class_check (cfg, eclass_reg, mono_defaults.enum_class);
1788 } else if (klass->cast_class == mono_defaults.enum_class->parent) {
1789 mini_emit_class_check_branch (cfg, eclass_reg, mono_defaults.enum_class->parent, OP_PBEQ, object_is_null);
1790 mini_emit_class_check (cfg, eclass_reg, mono_defaults.enum_class);
1791 } else if (klass->cast_class == mono_defaults.enum_class) {
1792 mini_emit_class_check (cfg, eclass_reg, mono_defaults.enum_class);
1793 } else if (klass->cast_class->flags & TYPE_ATTRIBUTE_INTERFACE) {
1794 mini_emit_iface_class_cast (cfg, eclass_reg, klass->cast_class, NULL, NULL);
1796 // Pass -1 as obj_reg to skip the check below for arrays of arrays
1797 mini_emit_castclass (cfg, -1, eclass_reg, klass->cast_class, object_is_null);
1800 if ((klass->rank == 1) && (klass->byval_arg.type == MONO_TYPE_SZARRAY) && (obj_reg != -1)) {
1801 /* Check that the object is a vector too */
1802 int bounds_reg = alloc_preg (cfg);
1803 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, bounds_reg, obj_reg, MONO_STRUCT_OFFSET (MonoArray, bounds));
1804 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, bounds_reg, 0);
1805 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException");
1808 int idepth_reg = alloc_preg (cfg);
1809 int stypes_reg = alloc_preg (cfg);
1810 int stype = alloc_preg (cfg);
1812 mono_class_setup_supertypes (klass);
1814 if (klass->idepth > MONO_DEFAULT_SUPERTABLE_SIZE) {
1815 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU2_MEMBASE, idepth_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, idepth));
1816 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, idepth_reg, klass->idepth);
1817 MONO_EMIT_NEW_COND_EXC (cfg, LT_UN, "InvalidCastException");
1819 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, stypes_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, supertypes));
1820 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, stype, stypes_reg, ((klass->idepth - 1) * SIZEOF_VOID_P));
1821 mini_emit_class_check_inst (cfg, stype, klass, klass_inst);
1826 mini_emit_castclass (MonoCompile *cfg, int obj_reg, int klass_reg, MonoClass *klass, MonoBasicBlock *object_is_null)
1828 mini_emit_castclass_inst (cfg, obj_reg, klass_reg, klass, NULL, object_is_null);
1832 mini_emit_memset (MonoCompile *cfg, int destreg, int offset, int size, int val, int align)
1836 g_assert (val == 0);
1841 if ((size <= SIZEOF_REGISTER) && (size <= align)) {
1844 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREI1_MEMBASE_IMM, destreg, offset, val);
1847 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREI2_MEMBASE_IMM, destreg, offset, val);
1850 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREI4_MEMBASE_IMM, destreg, offset, val);
1852 #if SIZEOF_REGISTER == 8
1854 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREI8_MEMBASE_IMM, destreg, offset, val);
1860 val_reg = alloc_preg (cfg);
1862 if (SIZEOF_REGISTER == 8)
1863 MONO_EMIT_NEW_I8CONST (cfg, val_reg, val);
1865 MONO_EMIT_NEW_ICONST (cfg, val_reg, val);
1868 /* This could be optimized further if neccesary */
1870 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI1_MEMBASE_REG, destreg, offset, val_reg);
1877 if (!cfg->backend->no_unaligned_access && SIZEOF_REGISTER == 8) {
1879 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI4_MEMBASE_REG, destreg, offset, val_reg);
1884 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI8_MEMBASE_REG, destreg, offset, val_reg);
1891 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI4_MEMBASE_REG, destreg, offset, val_reg);
1896 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI2_MEMBASE_REG, destreg, offset, val_reg);
1901 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI1_MEMBASE_REG, destreg, offset, val_reg);
1908 mini_emit_memcpy (MonoCompile *cfg, int destreg, int doffset, int srcreg, int soffset, int size, int align)
1915 /*FIXME arbitrary hack to avoid unbound code expansion.*/
1916 g_assert (size < 10000);
1919 /* This could be optimized further if neccesary */
1921 cur_reg = alloc_preg (cfg);
1922 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI1_MEMBASE, cur_reg, srcreg, soffset);
1923 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI1_MEMBASE_REG, destreg, doffset, cur_reg);
1930 if (!cfg->backend->no_unaligned_access && SIZEOF_REGISTER == 8) {
1932 cur_reg = alloc_preg (cfg);
1933 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI8_MEMBASE, cur_reg, srcreg, soffset);
1934 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI8_MEMBASE_REG, destreg, doffset, cur_reg);
1942 cur_reg = alloc_preg (cfg);
1943 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI4_MEMBASE, cur_reg, srcreg, soffset);
1944 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI4_MEMBASE_REG, destreg, doffset, cur_reg);
1950 cur_reg = alloc_preg (cfg);
1951 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI2_MEMBASE, cur_reg, srcreg, soffset);
1952 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI2_MEMBASE_REG, destreg, doffset, cur_reg);
1958 cur_reg = alloc_preg (cfg);
1959 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI1_MEMBASE, cur_reg, srcreg, soffset);
1960 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI1_MEMBASE_REG, destreg, doffset, cur_reg);
1968 emit_tls_set (MonoCompile *cfg, int sreg1, MonoTlsKey tls_key)
1972 if (cfg->compile_aot) {
1973 EMIT_NEW_TLS_OFFSETCONST (cfg, c, tls_key);
1974 MONO_INST_NEW (cfg, ins, OP_TLS_SET_REG);
1976 ins->sreg2 = c->dreg;
1977 MONO_ADD_INS (cfg->cbb, ins);
1979 MONO_INST_NEW (cfg, ins, OP_TLS_SET);
1981 ins->inst_offset = mini_get_tls_offset (tls_key);
1982 MONO_ADD_INS (cfg->cbb, ins);
1989 * Emit IR to push the current LMF onto the LMF stack.
1992 emit_push_lmf (MonoCompile *cfg)
1995 * Emit IR to push the LMF:
1996 * lmf_addr = <lmf_addr from tls>
1997 * lmf->lmf_addr = lmf_addr
1998 * lmf->prev_lmf = *lmf_addr
2001 int lmf_reg, prev_lmf_reg;
2002 MonoInst *ins, *lmf_ins;
2007 if (cfg->lmf_ir_mono_lmf && mini_tls_get_supported (cfg, TLS_KEY_LMF)) {
2008 /* Load current lmf */
2009 lmf_ins = mono_get_lmf_intrinsic (cfg);
2011 MONO_ADD_INS (cfg->cbb, lmf_ins);
2012 EMIT_NEW_VARLOADA (cfg, ins, cfg->lmf_var, NULL);
2013 lmf_reg = ins->dreg;
2014 /* Save previous_lmf */
2015 EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, lmf_reg, MONO_STRUCT_OFFSET (MonoLMF, previous_lmf), lmf_ins->dreg);
2017 emit_tls_set (cfg, lmf_reg, TLS_KEY_LMF);
2020 * Store lmf_addr in a variable, so it can be allocated to a global register.
2022 if (!cfg->lmf_addr_var)
2023 cfg->lmf_addr_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
2026 ins = mono_get_jit_tls_intrinsic (cfg);
2028 int jit_tls_dreg = ins->dreg;
2030 MONO_ADD_INS (cfg->cbb, ins);
2031 lmf_reg = alloc_preg (cfg);
2032 EMIT_NEW_BIALU_IMM (cfg, lmf_ins, OP_PADD_IMM, lmf_reg, jit_tls_dreg, MONO_STRUCT_OFFSET (MonoJitTlsData, lmf));
2034 lmf_ins = mono_emit_jit_icall (cfg, mono_get_lmf_addr, NULL);
2037 lmf_ins = mono_get_lmf_addr_intrinsic (cfg);
2039 MONO_ADD_INS (cfg->cbb, lmf_ins);
2042 MonoInst *args [16], *jit_tls_ins, *ins;
2044 /* Inline mono_get_lmf_addr () */
2045 /* jit_tls = pthread_getspecific (mono_jit_tls_id); lmf_addr = &jit_tls->lmf; */
2047 /* Load mono_jit_tls_id */
2048 if (cfg->compile_aot)
2049 EMIT_NEW_AOTCONST (cfg, args [0], MONO_PATCH_INFO_JIT_TLS_ID, NULL);
2051 EMIT_NEW_ICONST (cfg, args [0], mono_jit_tls_id);
2052 /* call pthread_getspecific () */
2053 jit_tls_ins = mono_emit_jit_icall (cfg, pthread_getspecific, args);
2054 /* lmf_addr = &jit_tls->lmf */
2055 EMIT_NEW_BIALU_IMM (cfg, ins, OP_PADD_IMM, cfg->lmf_addr_var->dreg, jit_tls_ins->dreg, MONO_STRUCT_OFFSET (MonoJitTlsData, lmf));
2058 lmf_ins = mono_emit_jit_icall (cfg, mono_get_lmf_addr, NULL);
2062 lmf_ins->dreg = cfg->lmf_addr_var->dreg;
2064 EMIT_NEW_VARLOADA (cfg, ins, cfg->lmf_var, NULL);
2065 lmf_reg = ins->dreg;
2067 prev_lmf_reg = alloc_preg (cfg);
2068 /* Save previous_lmf */
2069 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, prev_lmf_reg, cfg->lmf_addr_var->dreg, 0);
2070 EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, lmf_reg, MONO_STRUCT_OFFSET (MonoLMF, previous_lmf), prev_lmf_reg);
2072 EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, cfg->lmf_addr_var->dreg, 0, lmf_reg);
2079 * Emit IR to pop the current LMF from the LMF stack.
2082 emit_pop_lmf (MonoCompile *cfg)
2084 int lmf_reg, lmf_addr_reg, prev_lmf_reg;
2090 EMIT_NEW_VARLOADA (cfg, ins, cfg->lmf_var, NULL);
2091 lmf_reg = ins->dreg;
2093 if (cfg->lmf_ir_mono_lmf && mini_tls_get_supported (cfg, TLS_KEY_LMF)) {
2094 /* Load previous_lmf */
2095 prev_lmf_reg = alloc_preg (cfg);
2096 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, prev_lmf_reg, lmf_reg, MONO_STRUCT_OFFSET (MonoLMF, previous_lmf));
2098 emit_tls_set (cfg, prev_lmf_reg, TLS_KEY_LMF);
2101 * Emit IR to pop the LMF:
2102 * *(lmf->lmf_addr) = lmf->prev_lmf
2104 /* This could be called before emit_push_lmf () */
2105 if (!cfg->lmf_addr_var)
2106 cfg->lmf_addr_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
2107 lmf_addr_reg = cfg->lmf_addr_var->dreg;
2109 prev_lmf_reg = alloc_preg (cfg);
2110 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, prev_lmf_reg, lmf_reg, MONO_STRUCT_OFFSET (MonoLMF, previous_lmf));
2111 EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, lmf_addr_reg, 0, prev_lmf_reg);
2116 emit_instrumentation_call (MonoCompile *cfg, void *func)
2118 MonoInst *iargs [1];
2121 * Avoid instrumenting inlined methods since it can
2122 * distort profiling results.
2124 if (cfg->method != cfg->current_method)
2127 if (cfg->prof_options & MONO_PROFILE_ENTER_LEAVE) {
2128 EMIT_NEW_METHODCONST (cfg, iargs [0], cfg->method);
2129 mono_emit_jit_icall (cfg, func, iargs);
2134 ret_type_to_call_opcode (MonoCompile *cfg, MonoType *type, int calli, int virt)
2137 type = mini_get_underlying_type (type);
2138 switch (type->type) {
2139 case MONO_TYPE_VOID:
2140 return calli? OP_VOIDCALL_REG: virt? OP_VOIDCALL_MEMBASE: OP_VOIDCALL;
2147 return calli? OP_CALL_REG: virt? OP_CALL_MEMBASE: OP_CALL;
2151 case MONO_TYPE_FNPTR:
2152 return calli? OP_CALL_REG: virt? OP_CALL_MEMBASE: OP_CALL;
2153 case MONO_TYPE_CLASS:
2154 case MONO_TYPE_STRING:
2155 case MONO_TYPE_OBJECT:
2156 case MONO_TYPE_SZARRAY:
2157 case MONO_TYPE_ARRAY:
2158 return calli? OP_CALL_REG: virt? OP_CALL_MEMBASE: OP_CALL;
2161 return calli? OP_LCALL_REG: virt? OP_LCALL_MEMBASE: OP_LCALL;
2164 return calli? OP_RCALL_REG: virt? OP_RCALL_MEMBASE: OP_RCALL;
2166 return calli? OP_FCALL_REG: virt? OP_FCALL_MEMBASE: OP_FCALL;
2168 return calli? OP_FCALL_REG: virt? OP_FCALL_MEMBASE: OP_FCALL;
2169 case MONO_TYPE_VALUETYPE:
2170 if (type->data.klass->enumtype) {
2171 type = mono_class_enum_basetype (type->data.klass);
2174 return calli? OP_VCALL_REG: virt? OP_VCALL_MEMBASE: OP_VCALL;
2175 case MONO_TYPE_TYPEDBYREF:
2176 return calli? OP_VCALL_REG: virt? OP_VCALL_MEMBASE: OP_VCALL;
2177 case MONO_TYPE_GENERICINST:
2178 type = &type->data.generic_class->container_class->byval_arg;
2181 case MONO_TYPE_MVAR:
2183 return calli? OP_VCALL_REG: virt? OP_VCALL_MEMBASE: OP_VCALL;
2185 g_error ("unknown type 0x%02x in ret_type_to_call_opcode", type->type);
2190 //XXX this ignores if t is byref
2191 #define MONO_TYPE_IS_PRIMITIVE_SCALAR(t) ((((((t)->type >= MONO_TYPE_BOOLEAN && (t)->type <= MONO_TYPE_U8) || ((t)->type >= MONO_TYPE_I && (t)->type <= MONO_TYPE_U)))))
2194 * target_type_is_incompatible:
2195 * @cfg: MonoCompile context
2197 * Check that the item @arg on the evaluation stack can be stored
2198 * in the target type (can be a local, or field, etc).
2199 * The cfg arg can be used to check if we need verification or just
2202 * Returns: non-0 value if arg can't be stored on a target.
2205 target_type_is_incompatible (MonoCompile *cfg, MonoType *target, MonoInst *arg)
2207 MonoType *simple_type;
2210 if (target->byref) {
2211 /* FIXME: check that the pointed to types match */
2212 if (arg->type == STACK_MP) {
2213 MonoClass *base_class = mono_class_from_mono_type (target);
2214 /* This is needed to handle gshared types + ldaddr */
2215 simple_type = mini_get_underlying_type (&base_class->byval_arg);
2217 /* if the target is native int& or same type */
2218 if (target->type == MONO_TYPE_I || arg->klass == mono_class_from_mono_type (simple_type))
2221 /* Both are primitive type byrefs and the source points to a larger type that the destination */
2222 if (MONO_TYPE_IS_PRIMITIVE_SCALAR (target) && MONO_TYPE_IS_PRIMITIVE_SCALAR (&arg->klass->byval_arg) &&
2223 mono_class_instance_size (mono_class_from_mono_type (target)) <= mono_class_instance_size (arg->klass))
2227 if (arg->type == STACK_PTR)
2232 simple_type = mini_get_underlying_type (target);
2233 switch (simple_type->type) {
2234 case MONO_TYPE_VOID:
2242 if (arg->type != STACK_I4 && arg->type != STACK_PTR)
2246 /* STACK_MP is needed when setting pinned locals */
2247 if (arg->type != STACK_I4 && arg->type != STACK_PTR && arg->type != STACK_MP)
2252 case MONO_TYPE_FNPTR:
2254 * Some opcodes like ldloca returns 'transient pointers' which can be stored in
2255 * in native int. (#688008).
2257 if (arg->type != STACK_I4 && arg->type != STACK_PTR && arg->type != STACK_MP)
2260 case MONO_TYPE_CLASS:
2261 case MONO_TYPE_STRING:
2262 case MONO_TYPE_OBJECT:
2263 case MONO_TYPE_SZARRAY:
2264 case MONO_TYPE_ARRAY:
2265 if (arg->type != STACK_OBJ)
2267 /* FIXME: check type compatibility */
2271 if (arg->type != STACK_I8)
2275 if (arg->type != cfg->r4_stack_type)
2279 if (arg->type != STACK_R8)
2282 case MONO_TYPE_VALUETYPE:
2283 if (arg->type != STACK_VTYPE)
2285 klass = mono_class_from_mono_type (simple_type);
2286 if (klass != arg->klass)
2289 case MONO_TYPE_TYPEDBYREF:
2290 if (arg->type != STACK_VTYPE)
2292 klass = mono_class_from_mono_type (simple_type);
2293 if (klass != arg->klass)
2296 case MONO_TYPE_GENERICINST:
2297 if (mono_type_generic_inst_is_valuetype (simple_type)) {
2298 MonoClass *target_class;
2299 if (arg->type != STACK_VTYPE)
2301 klass = mono_class_from_mono_type (simple_type);
2302 target_class = mono_class_from_mono_type (target);
2303 /* The second cases is needed when doing partial sharing */
2304 if (klass != arg->klass && target_class != arg->klass && target_class != mono_class_from_mono_type (mini_get_underlying_type (&arg->klass->byval_arg)))
2308 if (arg->type != STACK_OBJ)
2310 /* FIXME: check type compatibility */
2314 case MONO_TYPE_MVAR:
2315 g_assert (cfg->gshared);
2316 if (mini_type_var_is_vt (simple_type)) {
2317 if (arg->type != STACK_VTYPE)
2320 if (arg->type != STACK_OBJ)
2325 g_error ("unknown type 0x%02x in target_type_is_incompatible", simple_type->type);
2331 * Prepare arguments for passing to a function call.
2332 * Return a non-zero value if the arguments can't be passed to the given
2334 * The type checks are not yet complete and some conversions may need
2335 * casts on 32 or 64 bit architectures.
2337 * FIXME: implement this using target_type_is_incompatible ()
2340 check_call_signature (MonoCompile *cfg, MonoMethodSignature *sig, MonoInst **args)
2342 MonoType *simple_type;
2346 if (args [0]->type != STACK_OBJ && args [0]->type != STACK_MP && args [0]->type != STACK_PTR)
2350 for (i = 0; i < sig->param_count; ++i) {
2351 if (sig->params [i]->byref) {
2352 if (args [i]->type != STACK_MP && args [i]->type != STACK_PTR)
2356 simple_type = mini_get_underlying_type (sig->params [i]);
2358 switch (simple_type->type) {
2359 case MONO_TYPE_VOID:
2368 if (args [i]->type != STACK_I4 && args [i]->type != STACK_PTR)
2374 case MONO_TYPE_FNPTR:
2375 if (args [i]->type != STACK_I4 && args [i]->type != STACK_PTR && args [i]->type != STACK_MP && args [i]->type != STACK_OBJ)
2378 case MONO_TYPE_CLASS:
2379 case MONO_TYPE_STRING:
2380 case MONO_TYPE_OBJECT:
2381 case MONO_TYPE_SZARRAY:
2382 case MONO_TYPE_ARRAY:
2383 if (args [i]->type != STACK_OBJ)
2388 if (args [i]->type != STACK_I8)
2392 if (args [i]->type != cfg->r4_stack_type)
2396 if (args [i]->type != STACK_R8)
2399 case MONO_TYPE_VALUETYPE:
2400 if (simple_type->data.klass->enumtype) {
2401 simple_type = mono_class_enum_basetype (simple_type->data.klass);
2404 if (args [i]->type != STACK_VTYPE)
2407 case MONO_TYPE_TYPEDBYREF:
2408 if (args [i]->type != STACK_VTYPE)
2411 case MONO_TYPE_GENERICINST:
2412 simple_type = &simple_type->data.generic_class->container_class->byval_arg;
2415 case MONO_TYPE_MVAR:
2417 if (args [i]->type != STACK_VTYPE)
2421 g_error ("unknown type 0x%02x in check_call_signature",
2429 callvirt_to_call (int opcode)
2432 case OP_CALL_MEMBASE:
2434 case OP_VOIDCALL_MEMBASE:
2436 case OP_FCALL_MEMBASE:
2438 case OP_RCALL_MEMBASE:
2440 case OP_VCALL_MEMBASE:
2442 case OP_LCALL_MEMBASE:
2445 g_assert_not_reached ();
2452 callvirt_to_call_reg (int opcode)
2455 case OP_CALL_MEMBASE:
2457 case OP_VOIDCALL_MEMBASE:
2458 return OP_VOIDCALL_REG;
2459 case OP_FCALL_MEMBASE:
2460 return OP_FCALL_REG;
2461 case OP_RCALL_MEMBASE:
2462 return OP_RCALL_REG;
2463 case OP_VCALL_MEMBASE:
2464 return OP_VCALL_REG;
2465 case OP_LCALL_MEMBASE:
2466 return OP_LCALL_REG;
2468 g_assert_not_reached ();
2474 /* Either METHOD or IMT_ARG needs to be set */
2476 emit_imt_argument (MonoCompile *cfg, MonoCallInst *call, MonoMethod *method, MonoInst *imt_arg)
2480 if (COMPILE_LLVM (cfg)) {
2482 method_reg = alloc_preg (cfg);
2483 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, method_reg, imt_arg->dreg);
2485 MonoInst *ins = emit_runtime_constant (cfg, MONO_PATCH_INFO_METHODCONST, method);
2486 method_reg = ins->dreg;
2490 call->imt_arg_reg = method_reg;
2492 mono_call_inst_add_outarg_reg (cfg, call, method_reg, MONO_ARCH_IMT_REG, FALSE);
2497 method_reg = alloc_preg (cfg);
2498 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, method_reg, imt_arg->dreg);
2500 MonoInst *ins = emit_runtime_constant (cfg, MONO_PATCH_INFO_METHODCONST, method);
2501 method_reg = ins->dreg;
2504 mono_call_inst_add_outarg_reg (cfg, call, method_reg, MONO_ARCH_IMT_REG, FALSE);
2507 static MonoJumpInfo *
2508 mono_patch_info_new (MonoMemPool *mp, int ip, MonoJumpInfoType type, gconstpointer target)
2510 MonoJumpInfo *ji = (MonoJumpInfo *)mono_mempool_alloc (mp, sizeof (MonoJumpInfo));
2514 ji->data.target = target;
2520 mini_class_check_context_used (MonoCompile *cfg, MonoClass *klass)
2523 return mono_class_check_context_used (klass);
2529 mini_method_check_context_used (MonoCompile *cfg, MonoMethod *method)
2532 return mono_method_check_context_used (method);
2538 * check_method_sharing:
2540 * Check whenever the vtable or an mrgctx needs to be passed when calling CMETHOD.
2543 check_method_sharing (MonoCompile *cfg, MonoMethod *cmethod, gboolean *out_pass_vtable, gboolean *out_pass_mrgctx)
2545 gboolean pass_vtable = FALSE;
2546 gboolean pass_mrgctx = FALSE;
2548 if (((cmethod->flags & METHOD_ATTRIBUTE_STATIC) || cmethod->klass->valuetype) &&
2549 (cmethod->klass->generic_class || cmethod->klass->generic_container)) {
2550 gboolean sharable = FALSE;
2552 if (mono_method_is_generic_sharable_full (cmethod, TRUE, TRUE, TRUE))
2556 * Pass vtable iff target method might
2557 * be shared, which means that sharing
2558 * is enabled for its class and its
2559 * context is sharable (and it's not a
2562 if (sharable && !(mini_method_get_context (cmethod) && mini_method_get_context (cmethod)->method_inst))
2566 if (mini_method_get_context (cmethod) &&
2567 mini_method_get_context (cmethod)->method_inst) {
2568 g_assert (!pass_vtable);
2570 if (mono_method_is_generic_sharable_full (cmethod, TRUE, TRUE, TRUE)) {
2573 if (cfg->gsharedvt && mini_is_gsharedvt_signature (mono_method_signature (cmethod)))
2578 if (out_pass_vtable)
2579 *out_pass_vtable = pass_vtable;
2580 if (out_pass_mrgctx)
2581 *out_pass_mrgctx = pass_mrgctx;
2584 inline static MonoCallInst *
2585 mono_emit_call_args (MonoCompile *cfg, MonoMethodSignature *sig,
2586 MonoInst **args, int calli, int virtual_, int tail, int rgctx, int unbox_trampoline)
2590 #ifdef MONO_ARCH_SOFT_FLOAT_FALLBACK
2598 emit_instrumentation_call (cfg, mono_profiler_method_leave);
2600 MONO_INST_NEW_CALL (cfg, call, OP_TAILCALL);
2602 MONO_INST_NEW_CALL (cfg, call, ret_type_to_call_opcode (cfg, sig->ret, calli, virtual_));
2605 call->signature = sig;
2606 call->rgctx_reg = rgctx;
2607 sig_ret = mini_get_underlying_type (sig->ret);
2609 type_to_eval_stack_type ((cfg), sig_ret, &call->inst);
2612 if (mini_type_is_vtype (sig_ret)) {
2613 call->vret_var = cfg->vret_addr;
2614 //g_assert_not_reached ();
2616 } else if (mini_type_is_vtype (sig_ret)) {
2617 MonoInst *temp = mono_compile_create_var (cfg, sig_ret, OP_LOCAL);
2620 temp->backend.is_pinvoke = sig->pinvoke;
2623 * We use a new opcode OP_OUTARG_VTRETADDR instead of LDADDR for emitting the
2624 * address of return value to increase optimization opportunities.
2625 * Before vtype decomposition, the dreg of the call ins itself represents the
2626 * fact the call modifies the return value. After decomposition, the call will
2627 * be transformed into one of the OP_VOIDCALL opcodes, and the VTRETADDR opcode
2628 * will be transformed into an LDADDR.
2630 MONO_INST_NEW (cfg, loada, OP_OUTARG_VTRETADDR);
2631 loada->dreg = alloc_preg (cfg);
2632 loada->inst_p0 = temp;
2633 /* We reference the call too since call->dreg could change during optimization */
2634 loada->inst_p1 = call;
2635 MONO_ADD_INS (cfg->cbb, loada);
2637 call->inst.dreg = temp->dreg;
2639 call->vret_var = loada;
2640 } else if (!MONO_TYPE_IS_VOID (sig_ret))
2641 call->inst.dreg = alloc_dreg (cfg, (MonoStackType)call->inst.type);
2643 #ifdef MONO_ARCH_SOFT_FLOAT_FALLBACK
2644 if (COMPILE_SOFT_FLOAT (cfg)) {
2646 * If the call has a float argument, we would need to do an r8->r4 conversion using
2647 * an icall, but that cannot be done during the call sequence since it would clobber
2648 * the call registers + the stack. So we do it before emitting the call.
2650 for (i = 0; i < sig->param_count + sig->hasthis; ++i) {
2652 MonoInst *in = call->args [i];
2654 if (i >= sig->hasthis)
2655 t = sig->params [i - sig->hasthis];
2657 t = &mono_defaults.int_class->byval_arg;
2658 t = mono_type_get_underlying_type (t);
2660 if (!t->byref && t->type == MONO_TYPE_R4) {
2661 MonoInst *iargs [1];
2665 conv = mono_emit_jit_icall (cfg, mono_fload_r4_arg, iargs);
2667 /* The result will be in an int vreg */
2668 call->args [i] = conv;
2674 call->need_unbox_trampoline = unbox_trampoline;
2677 if (COMPILE_LLVM (cfg))
2678 mono_llvm_emit_call (cfg, call);
2680 mono_arch_emit_call (cfg, call);
2682 mono_arch_emit_call (cfg, call);
2685 cfg->param_area = MAX (cfg->param_area, call->stack_usage);
2686 cfg->flags |= MONO_CFG_HAS_CALLS;
2692 set_rgctx_arg (MonoCompile *cfg, MonoCallInst *call, int rgctx_reg, MonoInst *rgctx_arg)
2694 mono_call_inst_add_outarg_reg (cfg, call, rgctx_reg, MONO_ARCH_RGCTX_REG, FALSE);
2695 cfg->uses_rgctx_reg = TRUE;
2696 call->rgctx_reg = TRUE;
2698 call->rgctx_arg_reg = rgctx_reg;
2702 inline static MonoInst*
2703 mono_emit_calli (MonoCompile *cfg, MonoMethodSignature *sig, MonoInst **args, MonoInst *addr, MonoInst *imt_arg, MonoInst *rgctx_arg)
2708 gboolean check_sp = FALSE;
2710 if (cfg->check_pinvoke_callconv && cfg->method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE) {
2711 WrapperInfo *info = mono_marshal_get_wrapper_info (cfg->method);
2713 if (info && info->subtype == WRAPPER_SUBTYPE_PINVOKE)
2718 rgctx_reg = mono_alloc_preg (cfg);
2719 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, rgctx_reg, rgctx_arg->dreg);
2723 if (!cfg->stack_inbalance_var)
2724 cfg->stack_inbalance_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
2726 MONO_INST_NEW (cfg, ins, OP_GET_SP);
2727 ins->dreg = cfg->stack_inbalance_var->dreg;
2728 MONO_ADD_INS (cfg->cbb, ins);
2731 call = mono_emit_call_args (cfg, sig, args, TRUE, FALSE, FALSE, rgctx_arg ? TRUE : FALSE, FALSE);
2733 call->inst.sreg1 = addr->dreg;
2736 emit_imt_argument (cfg, call, NULL, imt_arg);
2738 MONO_ADD_INS (cfg->cbb, (MonoInst*)call);
2743 sp_reg = mono_alloc_preg (cfg);
2745 MONO_INST_NEW (cfg, ins, OP_GET_SP);
2747 MONO_ADD_INS (cfg->cbb, ins);
2749 /* Restore the stack so we don't crash when throwing the exception */
2750 MONO_INST_NEW (cfg, ins, OP_SET_SP);
2751 ins->sreg1 = cfg->stack_inbalance_var->dreg;
2752 MONO_ADD_INS (cfg->cbb, ins);
2754 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, cfg->stack_inbalance_var->dreg, sp_reg);
2755 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "ExecutionEngineException");
2759 set_rgctx_arg (cfg, call, rgctx_reg, rgctx_arg);
2761 return (MonoInst*)call;
2765 emit_get_gsharedvt_info_klass (MonoCompile *cfg, MonoClass *klass, MonoRgctxInfoType rgctx_type);
2768 emit_get_rgctx_method (MonoCompile *cfg, int context_used, MonoMethod *cmethod, MonoRgctxInfoType rgctx_type);
2770 emit_get_rgctx_klass (MonoCompile *cfg, int context_used, MonoClass *klass, MonoRgctxInfoType rgctx_type);
2773 mono_emit_method_call_full (MonoCompile *cfg, MonoMethod *method, MonoMethodSignature *sig, gboolean tail,
2774 MonoInst **args, MonoInst *this_ins, MonoInst *imt_arg, MonoInst *rgctx_arg)
2776 #ifndef DISABLE_REMOTING
2777 gboolean might_be_remote = FALSE;
2779 gboolean virtual_ = this_ins != NULL;
2780 gboolean enable_for_aot = TRUE;
2783 MonoInst *call_target = NULL;
2785 gboolean need_unbox_trampoline;
2788 sig = mono_method_signature (method);
2790 if (cfg->llvm_only && (method->klass->flags & TYPE_ATTRIBUTE_INTERFACE))
2791 g_assert_not_reached ();
2794 rgctx_reg = mono_alloc_preg (cfg);
2795 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, rgctx_reg, rgctx_arg->dreg);
2798 if (method->string_ctor) {
2799 /* Create the real signature */
2800 /* FIXME: Cache these */
2801 MonoMethodSignature *ctor_sig = mono_metadata_signature_dup_mempool (cfg->mempool, sig);
2802 ctor_sig->ret = &mono_defaults.string_class->byval_arg;
2807 context_used = mini_method_check_context_used (cfg, method);
2809 #ifndef DISABLE_REMOTING
2810 might_be_remote = this_ins && sig->hasthis &&
2811 (mono_class_is_marshalbyref (method->klass) || method->klass == mono_defaults.object_class) &&
2812 !(method->flags & METHOD_ATTRIBUTE_VIRTUAL) && (!MONO_CHECK_THIS (this_ins) || context_used);
2814 if (might_be_remote && context_used) {
2817 g_assert (cfg->gshared);
2819 addr = emit_get_rgctx_method (cfg, context_used, method, MONO_RGCTX_INFO_REMOTING_INVOKE_WITH_CHECK);
2821 return mono_emit_calli (cfg, sig, args, addr, NULL, NULL);
2825 if (cfg->llvm_only && !call_target && virtual_ && (method->flags & METHOD_ATTRIBUTE_VIRTUAL))
2826 return emit_llvmonly_virtual_call (cfg, method, sig, 0, args);
2828 need_unbox_trampoline = method->klass == mono_defaults.object_class || (method->klass->flags & TYPE_ATTRIBUTE_INTERFACE);
2830 call = mono_emit_call_args (cfg, sig, args, FALSE, virtual_, tail, rgctx_arg ? TRUE : FALSE, need_unbox_trampoline);
2832 #ifndef DISABLE_REMOTING
2833 if (might_be_remote)
2834 call->method = mono_marshal_get_remoting_invoke_with_check (method);
2837 call->method = method;
2838 call->inst.flags |= MONO_INST_HAS_METHOD;
2839 call->inst.inst_left = this_ins;
2840 call->tail_call = tail;
2843 int vtable_reg, slot_reg, this_reg;
2846 this_reg = this_ins->dreg;
2848 if (!cfg->llvm_only && (method->klass->parent == mono_defaults.multicastdelegate_class) && !strcmp (method->name, "Invoke")) {
2849 MonoInst *dummy_use;
2851 MONO_EMIT_NULL_CHECK (cfg, this_reg);
2853 /* Make a call to delegate->invoke_impl */
2854 call->inst.inst_basereg = this_reg;
2855 call->inst.inst_offset = MONO_STRUCT_OFFSET (MonoDelegate, invoke_impl);
2856 MONO_ADD_INS (cfg->cbb, (MonoInst*)call);
2858 /* We must emit a dummy use here because the delegate trampoline will
2859 replace the 'this' argument with the delegate target making this activation
2860 no longer a root for the delegate.
2861 This is an issue for delegates that target collectible code such as dynamic
2862 methods of GC'able assemblies.
2864 For a test case look into #667921.
2866 FIXME: a dummy use is not the best way to do it as the local register allocator
2867 will put it on a caller save register and spil it around the call.
2868 Ideally, we would either put it on a callee save register or only do the store part.
2870 EMIT_NEW_DUMMY_USE (cfg, dummy_use, args [0]);
2872 return (MonoInst*)call;
2875 if ((!cfg->compile_aot || enable_for_aot) &&
2876 (!(method->flags & METHOD_ATTRIBUTE_VIRTUAL) ||
2877 (MONO_METHOD_IS_FINAL (method) &&
2878 method->wrapper_type != MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK)) &&
2879 !(mono_class_is_marshalbyref (method->klass) && context_used)) {
2881 * the method is not virtual, we just need to ensure this is not null
2882 * and then we can call the method directly.
2884 #ifndef DISABLE_REMOTING
2885 if (mono_class_is_marshalbyref (method->klass) || method->klass == mono_defaults.object_class) {
2887 * The check above ensures method is not gshared, this is needed since
2888 * gshared methods can't have wrappers.
2890 method = call->method = mono_marshal_get_remoting_invoke_with_check (method);
2894 if (!method->string_ctor)
2895 MONO_EMIT_NEW_CHECK_THIS (cfg, this_reg);
2897 call->inst.opcode = callvirt_to_call (call->inst.opcode);
2898 } else if ((method->flags & METHOD_ATTRIBUTE_VIRTUAL) && MONO_METHOD_IS_FINAL (method)) {
2900 * the method is virtual, but we can statically dispatch since either
2901 * it's class or the method itself are sealed.
2902 * But first we need to ensure it's not a null reference.
2904 MONO_EMIT_NEW_CHECK_THIS (cfg, this_reg);
2906 call->inst.opcode = callvirt_to_call (call->inst.opcode);
2907 } else if (call_target) {
2908 vtable_reg = alloc_preg (cfg);
2909 MONO_EMIT_NEW_LOAD_MEMBASE_FAULT (cfg, vtable_reg, this_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
2911 call->inst.opcode = callvirt_to_call_reg (call->inst.opcode);
2912 call->inst.sreg1 = call_target->dreg;
2913 call->inst.flags &= !MONO_INST_HAS_METHOD;
2915 vtable_reg = alloc_preg (cfg);
2916 MONO_EMIT_NEW_LOAD_MEMBASE_FAULT (cfg, vtable_reg, this_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
2917 if (method->klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
2918 guint32 imt_slot = mono_method_get_imt_slot (method);
2919 emit_imt_argument (cfg, call, call->method, imt_arg);
2920 slot_reg = vtable_reg;
2921 offset = ((gint32)imt_slot - MONO_IMT_SIZE) * SIZEOF_VOID_P;
2923 slot_reg = vtable_reg;
2924 offset = MONO_STRUCT_OFFSET (MonoVTable, vtable) +
2925 ((mono_method_get_vtable_index (method)) * (SIZEOF_VOID_P));
2927 g_assert (mono_method_signature (method)->generic_param_count);
2928 emit_imt_argument (cfg, call, call->method, imt_arg);
2932 call->inst.sreg1 = slot_reg;
2933 call->inst.inst_offset = offset;
2934 call->is_virtual = TRUE;
2938 MONO_ADD_INS (cfg->cbb, (MonoInst*)call);
2941 set_rgctx_arg (cfg, call, rgctx_reg, rgctx_arg);
2943 return (MonoInst*)call;
2947 mono_emit_method_call (MonoCompile *cfg, MonoMethod *method, MonoInst **args, MonoInst *this_ins)
2949 return mono_emit_method_call_full (cfg, method, mono_method_signature (method), FALSE, args, this_ins, NULL, NULL);
2953 mono_emit_native_call (MonoCompile *cfg, gconstpointer func, MonoMethodSignature *sig,
2960 call = mono_emit_call_args (cfg, sig, args, FALSE, FALSE, FALSE, FALSE, FALSE);
2963 MONO_ADD_INS (cfg->cbb, (MonoInst*)call);
2965 return (MonoInst*)call;
2969 mono_emit_jit_icall (MonoCompile *cfg, gconstpointer func, MonoInst **args)
2971 MonoJitICallInfo *info = mono_find_jit_icall_by_addr (func);
2975 return mono_emit_native_call (cfg, mono_icall_get_wrapper (info), info->sig, args);
2979 * mono_emit_abs_call:
2981 * Emit a call to the runtime function described by PATCH_TYPE and DATA.
2983 inline static MonoInst*
2984 mono_emit_abs_call (MonoCompile *cfg, MonoJumpInfoType patch_type, gconstpointer data,
2985 MonoMethodSignature *sig, MonoInst **args)
2987 MonoJumpInfo *ji = mono_patch_info_new (cfg->mempool, 0, patch_type, data);
2991 * We pass ji as the call address, the PATCH_INFO_ABS resolving code will
2994 if (cfg->abs_patches == NULL)
2995 cfg->abs_patches = g_hash_table_new (NULL, NULL);
2996 g_hash_table_insert (cfg->abs_patches, ji, ji);
2997 ins = mono_emit_native_call (cfg, ji, sig, args);
2998 ((MonoCallInst*)ins)->fptr_is_patch = TRUE;
3002 static MonoMethodSignature*
3003 sig_to_rgctx_sig (MonoMethodSignature *sig)
3005 // FIXME: memory allocation
3006 MonoMethodSignature *res;
3009 res = (MonoMethodSignature *)g_malloc (MONO_SIZEOF_METHOD_SIGNATURE + (sig->param_count + 1) * sizeof (MonoType*));
3010 memcpy (res, sig, MONO_SIZEOF_METHOD_SIGNATURE);
3011 res->param_count = sig->param_count + 1;
3012 for (i = 0; i < sig->param_count; ++i)
3013 res->params [i] = sig->params [i];
3014 res->params [sig->param_count] = &mono_defaults.int_class->this_arg;
3018 /* Make an indirect call to FSIG passing an additional argument */
3020 emit_extra_arg_calli (MonoCompile *cfg, MonoMethodSignature *fsig, MonoInst **orig_args, int arg_reg, MonoInst *call_target)
3022 MonoMethodSignature *csig;
3023 MonoInst *args_buf [16];
3025 int i, pindex, tmp_reg;
3027 /* Make a call with an rgctx/extra arg */
3028 if (fsig->param_count + 2 < 16)
3031 args = (MonoInst **)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoInst*) * (fsig->param_count + 2));
3034 args [pindex ++] = orig_args [0];
3035 for (i = 0; i < fsig->param_count; ++i)
3036 args [pindex ++] = orig_args [fsig->hasthis + i];
3037 tmp_reg = alloc_preg (cfg);
3038 EMIT_NEW_UNALU (cfg, args [pindex], OP_MOVE, tmp_reg, arg_reg);
3039 csig = sig_to_rgctx_sig (fsig);
3040 return mono_emit_calli (cfg, csig, args, call_target, NULL, NULL);
3043 /* Emit an indirect call to the function descriptor ADDR */
3045 emit_llvmonly_calli (MonoCompile *cfg, MonoMethodSignature *fsig, MonoInst **args, MonoInst *addr)
3047 int addr_reg, arg_reg;
3048 MonoInst *call_target;
3050 g_assert (cfg->llvm_only);
3053 * addr points to a <addr, arg> pair, load both of them, and
3054 * make a call to addr, passing arg as an extra arg.
3056 addr_reg = alloc_preg (cfg);
3057 EMIT_NEW_LOAD_MEMBASE (cfg, call_target, OP_LOAD_MEMBASE, addr_reg, addr->dreg, 0);
3058 arg_reg = alloc_preg (cfg);
3059 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, arg_reg, addr->dreg, sizeof (gpointer));
3061 return emit_extra_arg_calli (cfg, fsig, args, arg_reg, call_target);
3065 direct_icalls_enabled (MonoCompile *cfg)
3067 /* LLVM on amd64 can't handle calls to non-32 bit addresses */
3069 if (cfg->compile_llvm)
3072 if (cfg->gen_sdb_seq_points || cfg->disable_direct_icalls)
3078 mono_emit_jit_icall_by_info (MonoCompile *cfg, MonoJitICallInfo *info, MonoInst **args)
3081 * Call the jit icall without a wrapper if possible.
3082 * The wrapper is needed for the following reasons:
3083 * - to handle exceptions thrown using mono_raise_exceptions () from the
3084 * icall function. The EH code needs the lmf frame pushed by the
3085 * wrapper to be able to unwind back to managed code.
3086 * - to be able to do stack walks for asynchronously suspended
3087 * threads when debugging.
3089 if (info->no_raise && direct_icalls_enabled (cfg)) {
3093 if (!info->wrapper_method) {
3094 name = g_strdup_printf ("__icall_wrapper_%s", info->name);
3095 info->wrapper_method = mono_marshal_get_icall_wrapper (info->sig, name, info->func, TRUE);
3097 mono_memory_barrier ();
3101 * Inline the wrapper method, which is basically a call to the C icall, and
3102 * an exception check.
3104 costs = inline_method (cfg, info->wrapper_method, NULL,
3105 args, NULL, cfg->real_offset, TRUE);
3106 g_assert (costs > 0);
3107 g_assert (!MONO_TYPE_IS_VOID (info->sig->ret));
3111 return mono_emit_native_call (cfg, mono_icall_get_wrapper (info), info->sig, args);
3116 mono_emit_widen_call_res (MonoCompile *cfg, MonoInst *ins, MonoMethodSignature *fsig)
3118 if (!MONO_TYPE_IS_VOID (fsig->ret)) {
3119 if ((fsig->pinvoke || LLVM_ENABLED) && !fsig->ret->byref) {
3123 * Native code might return non register sized integers
3124 * without initializing the upper bits.
3126 switch (mono_type_to_load_membase (cfg, fsig->ret)) {
3127 case OP_LOADI1_MEMBASE:
3128 widen_op = OP_ICONV_TO_I1;
3130 case OP_LOADU1_MEMBASE:
3131 widen_op = OP_ICONV_TO_U1;
3133 case OP_LOADI2_MEMBASE:
3134 widen_op = OP_ICONV_TO_I2;
3136 case OP_LOADU2_MEMBASE:
3137 widen_op = OP_ICONV_TO_U2;
3143 if (widen_op != -1) {
3144 int dreg = alloc_preg (cfg);
3147 EMIT_NEW_UNALU (cfg, widen, widen_op, dreg, ins->dreg);
3148 widen->type = ins->type;
3158 get_memcpy_method (void)
3160 static MonoMethod *memcpy_method = NULL;
3161 if (!memcpy_method) {
3162 memcpy_method = mono_class_get_method_from_name (mono_defaults.string_class, "memcpy", 3);
3164 g_error ("Old corlib found. Install a new one");
3166 return memcpy_method;
3170 create_write_barrier_bitmap (MonoCompile *cfg, MonoClass *klass, unsigned *wb_bitmap, int offset)
3172 MonoClassField *field;
3173 gpointer iter = NULL;
3175 while ((field = mono_class_get_fields (klass, &iter))) {
3178 if (field->type->attrs & FIELD_ATTRIBUTE_STATIC)
3180 foffset = klass->valuetype ? field->offset - sizeof (MonoObject): field->offset;
3181 if (mini_type_is_reference (mono_field_get_type (field))) {
3182 g_assert ((foffset % SIZEOF_VOID_P) == 0);
3183 *wb_bitmap |= 1 << ((offset + foffset) / SIZEOF_VOID_P);
3185 MonoClass *field_class = mono_class_from_mono_type (field->type);
3186 if (field_class->has_references)
3187 create_write_barrier_bitmap (cfg, field_class, wb_bitmap, offset + foffset);
3193 emit_write_barrier (MonoCompile *cfg, MonoInst *ptr, MonoInst *value)
3195 int card_table_shift_bits;
3196 gpointer card_table_mask;
3198 MonoInst *dummy_use;
3199 int nursery_shift_bits;
3200 size_t nursery_size;
3202 if (!cfg->gen_write_barriers)
3205 card_table = mono_gc_get_card_table (&card_table_shift_bits, &card_table_mask);
3207 mono_gc_get_nursery (&nursery_shift_bits, &nursery_size);
3209 if (cfg->backend->have_card_table_wb && !cfg->compile_aot && card_table && nursery_shift_bits > 0 && !COMPILE_LLVM (cfg)) {
3212 MONO_INST_NEW (cfg, wbarrier, OP_CARD_TABLE_WBARRIER);
3213 wbarrier->sreg1 = ptr->dreg;
3214 wbarrier->sreg2 = value->dreg;
3215 MONO_ADD_INS (cfg->cbb, wbarrier);
3216 } else if (card_table && !cfg->compile_aot && !mono_gc_card_table_nursery_check ()) {
3217 int offset_reg = alloc_preg (cfg);
3221 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SHR_UN_IMM, offset_reg, ptr->dreg, card_table_shift_bits);
3222 if (card_table_mask)
3223 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_PAND_IMM, offset_reg, offset_reg, card_table_mask);
3225 /*We can't use PADD_IMM since the cardtable might end up in high addresses and amd64 doesn't support
3226 * IMM's larger than 32bits.
3228 ins = emit_runtime_constant (cfg, MONO_PATCH_INFO_GC_CARD_TABLE_ADDR, NULL);
3229 card_reg = ins->dreg;
3231 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, offset_reg, offset_reg, card_reg);
3232 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREI1_MEMBASE_IMM, offset_reg, 0, 1);
3234 MonoMethod *write_barrier = mono_gc_get_write_barrier ();
3235 mono_emit_method_call (cfg, write_barrier, &ptr, NULL);
3238 EMIT_NEW_DUMMY_USE (cfg, dummy_use, value);
3242 mono_emit_wb_aware_memcpy (MonoCompile *cfg, MonoClass *klass, MonoInst *iargs[4], int size, int align)
3244 int dest_ptr_reg, tmp_reg, destreg, srcreg, offset;
3245 unsigned need_wb = 0;
3250 /*types with references can't have alignment smaller than sizeof(void*) */
3251 if (align < SIZEOF_VOID_P)
3254 /*This value cannot be bigger than 32 due to the way we calculate the required wb bitmap.*/
3255 if (size > 32 * SIZEOF_VOID_P)
3258 create_write_barrier_bitmap (cfg, klass, &need_wb, 0);
3260 /* We don't unroll more than 5 stores to avoid code bloat. */
3261 if (size > 5 * SIZEOF_VOID_P) {
3262 /*This is harmless and simplify mono_gc_wbarrier_value_copy_bitmap */
3263 size += (SIZEOF_VOID_P - 1);
3264 size &= ~(SIZEOF_VOID_P - 1);
3266 EMIT_NEW_ICONST (cfg, iargs [2], size);
3267 EMIT_NEW_ICONST (cfg, iargs [3], need_wb);
3268 mono_emit_jit_icall (cfg, mono_gc_wbarrier_value_copy_bitmap, iargs);
3272 destreg = iargs [0]->dreg;
3273 srcreg = iargs [1]->dreg;
3276 dest_ptr_reg = alloc_preg (cfg);
3277 tmp_reg = alloc_preg (cfg);
3280 EMIT_NEW_UNALU (cfg, iargs [0], OP_MOVE, dest_ptr_reg, destreg);
3282 while (size >= SIZEOF_VOID_P) {
3283 MonoInst *load_inst;
3284 MONO_INST_NEW (cfg, load_inst, OP_LOAD_MEMBASE);
3285 load_inst->dreg = tmp_reg;
3286 load_inst->inst_basereg = srcreg;
3287 load_inst->inst_offset = offset;
3288 MONO_ADD_INS (cfg->cbb, load_inst);
3290 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREP_MEMBASE_REG, dest_ptr_reg, 0, tmp_reg);
3293 emit_write_barrier (cfg, iargs [0], load_inst);
3295 offset += SIZEOF_VOID_P;
3296 size -= SIZEOF_VOID_P;
3299 /*tmp += sizeof (void*)*/
3300 if (size >= SIZEOF_VOID_P) {
3301 NEW_BIALU_IMM (cfg, iargs [0], OP_PADD_IMM, dest_ptr_reg, dest_ptr_reg, SIZEOF_VOID_P);
3302 MONO_ADD_INS (cfg->cbb, iargs [0]);
3306 /* Those cannot be references since size < sizeof (void*) */
3308 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI4_MEMBASE, tmp_reg, srcreg, offset);
3309 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI4_MEMBASE_REG, destreg, offset, tmp_reg);
3315 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI2_MEMBASE, tmp_reg, srcreg, offset);
3316 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI2_MEMBASE_REG, destreg, offset, tmp_reg);
3322 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI1_MEMBASE, tmp_reg, srcreg, offset);
3323 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI1_MEMBASE_REG, destreg, offset, tmp_reg);
3332 * Emit code to copy a valuetype of type @klass whose address is stored in
3333 * @src->dreg to memory whose address is stored at @dest->dreg.
3336 mini_emit_stobj (MonoCompile *cfg, MonoInst *dest, MonoInst *src, MonoClass *klass, gboolean native)
3338 MonoInst *iargs [4];
3341 MonoMethod *memcpy_method;
3342 MonoInst *size_ins = NULL;
3343 MonoInst *memcpy_ins = NULL;
3347 klass = mono_class_from_mono_type (mini_get_underlying_type (&klass->byval_arg));
3350 * This check breaks with spilled vars... need to handle it during verification anyway.
3351 * g_assert (klass && klass == src->klass && klass == dest->klass);
3354 if (mini_is_gsharedvt_klass (klass)) {
3356 size_ins = emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_VALUE_SIZE);
3357 memcpy_ins = emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_MEMCPY);
3361 n = mono_class_native_size (klass, &align);
3363 n = mono_class_value_size (klass, &align);
3365 /* if native is true there should be no references in the struct */
3366 if (cfg->gen_write_barriers && (klass->has_references || size_ins) && !native) {
3367 /* Avoid barriers when storing to the stack */
3368 if (!((dest->opcode == OP_ADD_IMM && dest->sreg1 == cfg->frame_reg) ||
3369 (dest->opcode == OP_LDADDR))) {
3375 context_used = mini_class_check_context_used (cfg, klass);
3377 /* It's ok to intrinsify under gsharing since shared code types are layout stable. */
3378 if (!size_ins && (cfg->opt & MONO_OPT_INTRINS) && mono_emit_wb_aware_memcpy (cfg, klass, iargs, n, align)) {
3380 } else if (context_used) {
3381 iargs [2] = emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_KLASS);
3383 iargs [2] = emit_runtime_constant (cfg, MONO_PATCH_INFO_CLASS, klass);
3384 if (!cfg->compile_aot)
3385 mono_class_compute_gc_descriptor (klass);
3389 mono_emit_jit_icall (cfg, mono_gsharedvt_value_copy, iargs);
3391 mono_emit_jit_icall (cfg, mono_value_copy, iargs);
3396 if (!size_ins && (cfg->opt & MONO_OPT_INTRINS) && n <= sizeof (gpointer) * 8) {
3397 /* FIXME: Optimize the case when src/dest is OP_LDADDR */
3398 mini_emit_memcpy (cfg, dest->dreg, 0, src->dreg, 0, n, align);
3403 iargs [2] = size_ins;
3405 EMIT_NEW_ICONST (cfg, iargs [2], n);
3407 memcpy_method = get_memcpy_method ();
3409 mono_emit_calli (cfg, mono_method_signature (memcpy_method), iargs, memcpy_ins, NULL, NULL);
3411 mono_emit_method_call (cfg, memcpy_method, iargs, NULL);
3416 get_memset_method (void)
3418 static MonoMethod *memset_method = NULL;
3419 if (!memset_method) {
3420 memset_method = mono_class_get_method_from_name (mono_defaults.string_class, "memset", 3);
3422 g_error ("Old corlib found. Install a new one");
3424 return memset_method;
3428 mini_emit_initobj (MonoCompile *cfg, MonoInst *dest, const guchar *ip, MonoClass *klass)
3430 MonoInst *iargs [3];
3433 MonoMethod *memset_method;
3434 MonoInst *size_ins = NULL;
3435 MonoInst *bzero_ins = NULL;
3436 static MonoMethod *bzero_method;
3438 /* FIXME: Optimize this for the case when dest is an LDADDR */
3439 mono_class_init (klass);
3440 if (mini_is_gsharedvt_klass (klass)) {
3441 size_ins = emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_VALUE_SIZE);
3442 bzero_ins = emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_BZERO);
3444 bzero_method = mono_class_get_method_from_name (mono_defaults.string_class, "bzero_aligned_1", 2);
3445 g_assert (bzero_method);
3447 iargs [1] = size_ins;
3448 mono_emit_calli (cfg, mono_method_signature (bzero_method), iargs, bzero_ins, NULL, NULL);
3452 klass = mono_class_from_mono_type (mini_get_underlying_type (&klass->byval_arg));
3454 n = mono_class_value_size (klass, &align);
3456 if (n <= sizeof (gpointer) * 8) {
3457 mini_emit_memset (cfg, dest->dreg, 0, n, 0, align);
3460 memset_method = get_memset_method ();
3462 EMIT_NEW_ICONST (cfg, iargs [1], 0);
3463 EMIT_NEW_ICONST (cfg, iargs [2], n);
3464 mono_emit_method_call (cfg, memset_method, iargs, NULL);
3471 * Emit IR to return either the this pointer for instance method,
3472 * or the mrgctx for static methods.
3475 emit_get_rgctx (MonoCompile *cfg, MonoMethod *method, int context_used)
3477 MonoInst *this_ins = NULL;
3479 g_assert (cfg->gshared);
3481 if (!(method->flags & METHOD_ATTRIBUTE_STATIC) &&
3482 !(context_used & MONO_GENERIC_CONTEXT_USED_METHOD) &&
3483 !method->klass->valuetype)
3484 EMIT_NEW_ARGLOAD (cfg, this_ins, 0);
3486 if (context_used & MONO_GENERIC_CONTEXT_USED_METHOD) {
3487 MonoInst *mrgctx_loc, *mrgctx_var;
3489 g_assert (!this_ins);
3490 g_assert (method->is_inflated && mono_method_get_context (method)->method_inst);
3492 mrgctx_loc = mono_get_vtable_var (cfg);
3493 EMIT_NEW_TEMPLOAD (cfg, mrgctx_var, mrgctx_loc->inst_c0);
3496 } else if (method->flags & METHOD_ATTRIBUTE_STATIC || method->klass->valuetype) {
3497 MonoInst *vtable_loc, *vtable_var;
3499 g_assert (!this_ins);
3501 vtable_loc = mono_get_vtable_var (cfg);
3502 EMIT_NEW_TEMPLOAD (cfg, vtable_var, vtable_loc->inst_c0);
3504 if (method->is_inflated && mono_method_get_context (method)->method_inst) {
3505 MonoInst *mrgctx_var = vtable_var;
3508 vtable_reg = alloc_preg (cfg);
3509 EMIT_NEW_LOAD_MEMBASE (cfg, vtable_var, OP_LOAD_MEMBASE, vtable_reg, mrgctx_var->dreg, MONO_STRUCT_OFFSET (MonoMethodRuntimeGenericContext, class_vtable));
3510 vtable_var->type = STACK_PTR;
3518 vtable_reg = alloc_preg (cfg);
3519 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, vtable_reg, this_ins->dreg, MONO_STRUCT_OFFSET (MonoObject, vtable));
3524 static MonoJumpInfoRgctxEntry *
3525 mono_patch_info_rgctx_entry_new (MonoMemPool *mp, MonoMethod *method, gboolean in_mrgctx, MonoJumpInfoType patch_type, gconstpointer patch_data, MonoRgctxInfoType info_type)
3527 MonoJumpInfoRgctxEntry *res = (MonoJumpInfoRgctxEntry *)mono_mempool_alloc0 (mp, sizeof (MonoJumpInfoRgctxEntry));
3528 res->method = method;
3529 res->in_mrgctx = in_mrgctx;
3530 res->data = (MonoJumpInfo *)mono_mempool_alloc0 (mp, sizeof (MonoJumpInfo));
3531 res->data->type = patch_type;
3532 res->data->data.target = patch_data;
3533 res->info_type = info_type;
3538 static inline MonoInst*
3539 emit_rgctx_fetch_inline (MonoCompile *cfg, MonoInst *rgctx, MonoJumpInfoRgctxEntry *entry)
3541 MonoInst *args [16];
3544 // FIXME: No fastpath since the slot is not a compile time constant
3546 EMIT_NEW_AOTCONST (cfg, args [1], MONO_PATCH_INFO_RGCTX_SLOT_INDEX, entry);
3547 if (entry->in_mrgctx)
3548 call = mono_emit_jit_icall (cfg, mono_fill_method_rgctx, args);
3550 call = mono_emit_jit_icall (cfg, mono_fill_class_rgctx, args);
3554 * FIXME: This can be called during decompose, which is a problem since it creates
3556 * Also, the fastpath doesn't work since the slot number is dynamically allocated.
3558 int i, slot, depth, index, rgctx_reg, val_reg, res_reg;
3560 MonoBasicBlock *is_null_bb, *end_bb;
3561 MonoInst *res, *ins, *call;
3564 slot = mini_get_rgctx_entry_slot (entry);
3566 mrgctx = MONO_RGCTX_SLOT_IS_MRGCTX (slot);
3567 index = MONO_RGCTX_SLOT_INDEX (slot);
3569 index += MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT / sizeof (gpointer);
3570 for (depth = 0; ; ++depth) {
3571 int size = mono_class_rgctx_get_array_size (depth, mrgctx);
3573 if (index < size - 1)
3578 NEW_BBLOCK (cfg, end_bb);
3579 NEW_BBLOCK (cfg, is_null_bb);
3582 rgctx_reg = rgctx->dreg;
3584 rgctx_reg = alloc_preg (cfg);
3586 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, rgctx_reg, rgctx->dreg, MONO_STRUCT_OFFSET (MonoVTable, runtime_generic_context));
3587 // FIXME: Avoid this check by allocating the table when the vtable is created etc.
3588 NEW_BBLOCK (cfg, is_null_bb);
3590 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, rgctx_reg, 0);
3591 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, is_null_bb);
3594 for (i = 0; i < depth; ++i) {
3595 int array_reg = alloc_preg (cfg);
3597 /* load ptr to next array */
3598 if (mrgctx && i == 0)
3599 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, array_reg, rgctx_reg, MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT);
3601 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, array_reg, rgctx_reg, 0);
3602 rgctx_reg = array_reg;
3603 /* is the ptr null? */
3604 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, rgctx_reg, 0);
3605 /* if yes, jump to actual trampoline */
3606 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, is_null_bb);
3610 val_reg = alloc_preg (cfg);
3611 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, val_reg, rgctx_reg, (index + 1) * sizeof (gpointer));
3612 /* is the slot null? */
3613 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, val_reg, 0);
3614 /* if yes, jump to actual trampoline */
3615 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, is_null_bb);
3618 res_reg = alloc_preg (cfg);
3619 MONO_INST_NEW (cfg, ins, OP_MOVE);
3620 ins->dreg = res_reg;
3621 ins->sreg1 = val_reg;
3622 MONO_ADD_INS (cfg->cbb, ins);
3624 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
3627 MONO_START_BB (cfg, is_null_bb);
3629 EMIT_NEW_ICONST (cfg, args [1], index);
3631 call = mono_emit_jit_icall (cfg, mono_fill_method_rgctx, args);
3633 call = mono_emit_jit_icall (cfg, mono_fill_class_rgctx, args);
3634 MONO_INST_NEW (cfg, ins, OP_MOVE);
3635 ins->dreg = res_reg;
3636 ins->sreg1 = call->dreg;
3637 MONO_ADD_INS (cfg->cbb, ins);
3638 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
3640 MONO_START_BB (cfg, end_bb);
3649 * Emit IR to load the value of the rgctx entry ENTRY from the rgctx
3652 static inline MonoInst*
3653 emit_rgctx_fetch (MonoCompile *cfg, MonoInst *rgctx, MonoJumpInfoRgctxEntry *entry)
3656 return emit_rgctx_fetch_inline (cfg, rgctx, entry);
3658 return mono_emit_abs_call (cfg, MONO_PATCH_INFO_RGCTX_FETCH, entry, helper_sig_rgctx_lazy_fetch_trampoline, &rgctx);
3662 emit_get_rgctx_klass (MonoCompile *cfg, int context_used,
3663 MonoClass *klass, MonoRgctxInfoType rgctx_type)
3665 MonoJumpInfoRgctxEntry *entry = mono_patch_info_rgctx_entry_new (cfg->mempool, cfg->current_method, context_used & MONO_GENERIC_CONTEXT_USED_METHOD, MONO_PATCH_INFO_CLASS, klass, rgctx_type);
3666 MonoInst *rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
3668 return emit_rgctx_fetch (cfg, rgctx, entry);
3672 emit_get_rgctx_sig (MonoCompile *cfg, int context_used,
3673 MonoMethodSignature *sig, MonoRgctxInfoType rgctx_type)
3675 MonoJumpInfoRgctxEntry *entry = mono_patch_info_rgctx_entry_new (cfg->mempool, cfg->current_method, context_used & MONO_GENERIC_CONTEXT_USED_METHOD, MONO_PATCH_INFO_SIGNATURE, sig, rgctx_type);
3676 MonoInst *rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
3678 return emit_rgctx_fetch (cfg, rgctx, entry);
3682 emit_get_rgctx_gsharedvt_call (MonoCompile *cfg, int context_used,
3683 MonoMethodSignature *sig, MonoMethod *cmethod, MonoRgctxInfoType rgctx_type)
3685 MonoJumpInfoGSharedVtCall *call_info;
3686 MonoJumpInfoRgctxEntry *entry;
3689 call_info = (MonoJumpInfoGSharedVtCall *)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoJumpInfoGSharedVtCall));
3690 call_info->sig = sig;
3691 call_info->method = cmethod;
3693 entry = mono_patch_info_rgctx_entry_new (cfg->mempool, cfg->current_method, context_used & MONO_GENERIC_CONTEXT_USED_METHOD, MONO_PATCH_INFO_GSHAREDVT_CALL, call_info, rgctx_type);
3694 rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
3696 return emit_rgctx_fetch (cfg, rgctx, entry);
3700 * emit_get_rgctx_virt_method:
3702 * Return data for method VIRT_METHOD for a receiver of type KLASS.
3705 emit_get_rgctx_virt_method (MonoCompile *cfg, int context_used,
3706 MonoClass *klass, MonoMethod *virt_method, MonoRgctxInfoType rgctx_type)
3708 MonoJumpInfoVirtMethod *info;
3709 MonoJumpInfoRgctxEntry *entry;
3712 info = (MonoJumpInfoVirtMethod *)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoJumpInfoVirtMethod));
3713 info->klass = klass;
3714 info->method = virt_method;
3716 entry = mono_patch_info_rgctx_entry_new (cfg->mempool, cfg->current_method, context_used & MONO_GENERIC_CONTEXT_USED_METHOD, MONO_PATCH_INFO_VIRT_METHOD, info, rgctx_type);
3717 rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
3719 return emit_rgctx_fetch (cfg, rgctx, entry);
3723 emit_get_rgctx_gsharedvt_method (MonoCompile *cfg, int context_used,
3724 MonoMethod *cmethod, MonoGSharedVtMethodInfo *info)
3726 MonoJumpInfoRgctxEntry *entry;
3729 entry = mono_patch_info_rgctx_entry_new (cfg->mempool, cfg->current_method, context_used & MONO_GENERIC_CONTEXT_USED_METHOD, MONO_PATCH_INFO_GSHAREDVT_METHOD, info, MONO_RGCTX_INFO_METHOD_GSHAREDVT_INFO);
3730 rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
3732 return emit_rgctx_fetch (cfg, rgctx, entry);
3736 * emit_get_rgctx_method:
3738 * Emit IR to load the property RGCTX_TYPE of CMETHOD. If context_used is 0, emit
3739 * normal constants, else emit a load from the rgctx.
3742 emit_get_rgctx_method (MonoCompile *cfg, int context_used,
3743 MonoMethod *cmethod, MonoRgctxInfoType rgctx_type)
3745 if (!context_used) {
3748 switch (rgctx_type) {
3749 case MONO_RGCTX_INFO_METHOD:
3750 EMIT_NEW_METHODCONST (cfg, ins, cmethod);
3752 case MONO_RGCTX_INFO_METHOD_RGCTX:
3753 EMIT_NEW_METHOD_RGCTX_CONST (cfg, ins, cmethod);
3756 g_assert_not_reached ();
3759 MonoJumpInfoRgctxEntry *entry = mono_patch_info_rgctx_entry_new (cfg->mempool, cfg->current_method, context_used & MONO_GENERIC_CONTEXT_USED_METHOD, MONO_PATCH_INFO_METHODCONST, cmethod, rgctx_type);
3760 MonoInst *rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
3762 return emit_rgctx_fetch (cfg, rgctx, entry);
3767 emit_get_rgctx_field (MonoCompile *cfg, int context_used,
3768 MonoClassField *field, MonoRgctxInfoType rgctx_type)
3770 MonoJumpInfoRgctxEntry *entry = mono_patch_info_rgctx_entry_new (cfg->mempool, cfg->current_method, context_used & MONO_GENERIC_CONTEXT_USED_METHOD, MONO_PATCH_INFO_FIELD, field, rgctx_type);
3771 MonoInst *rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
3773 return emit_rgctx_fetch (cfg, rgctx, entry);
3777 get_gsharedvt_info_slot (MonoCompile *cfg, gpointer data, MonoRgctxInfoType rgctx_type)
3779 MonoGSharedVtMethodInfo *info = cfg->gsharedvt_info;
3780 MonoRuntimeGenericContextInfoTemplate *template_;
3785 for (i = 0; i < info->num_entries; ++i) {
3786 MonoRuntimeGenericContextInfoTemplate *otemplate = &info->entries [i];
3788 if (otemplate->info_type == rgctx_type && otemplate->data == data && rgctx_type != MONO_RGCTX_INFO_LOCAL_OFFSET)
3792 if (info->num_entries == info->count_entries) {
3793 MonoRuntimeGenericContextInfoTemplate *new_entries;
3794 int new_count_entries = info->count_entries ? info->count_entries * 2 : 16;
3796 new_entries = (MonoRuntimeGenericContextInfoTemplate *)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoRuntimeGenericContextInfoTemplate) * new_count_entries);
3798 memcpy (new_entries, info->entries, sizeof (MonoRuntimeGenericContextInfoTemplate) * info->count_entries);
3799 info->entries = new_entries;
3800 info->count_entries = new_count_entries;
3803 idx = info->num_entries;
3804 template_ = &info->entries [idx];
3805 template_->info_type = rgctx_type;
3806 template_->data = data;
3808 info->num_entries ++;
3814 * emit_get_gsharedvt_info:
3816 * This is similar to emit_get_rgctx_.., but loads the data from the gsharedvt info var instead of calling an rgctx fetch trampoline.
3819 emit_get_gsharedvt_info (MonoCompile *cfg, gpointer data, MonoRgctxInfoType rgctx_type)
3824 idx = get_gsharedvt_info_slot (cfg, data, rgctx_type);
3825 /* Load info->entries [idx] */
3826 dreg = alloc_preg (cfg);
3827 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, dreg, cfg->gsharedvt_info_var->dreg, MONO_STRUCT_OFFSET (MonoGSharedVtMethodRuntimeInfo, entries) + (idx * sizeof (gpointer)));
3833 emit_get_gsharedvt_info_klass (MonoCompile *cfg, MonoClass *klass, MonoRgctxInfoType rgctx_type)
3835 return emit_get_gsharedvt_info (cfg, &klass->byval_arg, rgctx_type);
3839 * On return the caller must check @klass for load errors.
3842 emit_class_init (MonoCompile *cfg, MonoClass *klass)
3844 MonoInst *vtable_arg;
3847 context_used = mini_class_check_context_used (cfg, klass);
3850 vtable_arg = emit_get_rgctx_klass (cfg, context_used,
3851 klass, MONO_RGCTX_INFO_VTABLE);
3853 MonoVTable *vtable = mono_class_vtable (cfg->domain, klass);
3857 EMIT_NEW_VTABLECONST (cfg, vtable_arg, vtable);
3860 if (!COMPILE_LLVM (cfg) && cfg->backend->have_op_generic_class_init) {
3864 * Using an opcode instead of emitting IR here allows the hiding of the call inside the opcode,
3865 * so this doesn't have to clobber any regs and it doesn't break basic blocks.
3867 MONO_INST_NEW (cfg, ins, OP_GENERIC_CLASS_INIT);
3868 ins->sreg1 = vtable_arg->dreg;
3869 MONO_ADD_INS (cfg->cbb, ins);
3871 static int byte_offset = -1;
3872 static guint8 bitmask;
3873 int bits_reg, inited_reg;
3874 MonoBasicBlock *inited_bb;
3875 MonoInst *args [16];
3877 if (byte_offset < 0)
3878 mono_marshal_find_bitfield_offset (MonoVTable, initialized, &byte_offset, &bitmask);
3880 bits_reg = alloc_ireg (cfg);
3881 inited_reg = alloc_ireg (cfg);
3883 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU1_MEMBASE, bits_reg, vtable_arg->dreg, byte_offset);
3884 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_IAND_IMM, inited_reg, bits_reg, bitmask);
3886 NEW_BBLOCK (cfg, inited_bb);
3888 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, inited_reg, 0);
3889 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBNE_UN, inited_bb);
3891 args [0] = vtable_arg;
3892 mono_emit_jit_icall (cfg, mono_generic_class_init, args);
3894 MONO_START_BB (cfg, inited_bb);
3899 emit_seq_point (MonoCompile *cfg, MonoMethod *method, guint8* ip, gboolean intr_loc, gboolean nonempty_stack)
3903 if (cfg->gen_seq_points && cfg->method == method) {
3904 NEW_SEQ_POINT (cfg, ins, ip - cfg->header->code, intr_loc);
3906 ins->flags |= MONO_INST_NONEMPTY_STACK;
3907 MONO_ADD_INS (cfg->cbb, ins);
3912 save_cast_details (MonoCompile *cfg, MonoClass *klass, int obj_reg, gboolean null_check)
3914 if (mini_get_debug_options ()->better_cast_details) {
3915 int vtable_reg = alloc_preg (cfg);
3916 int klass_reg = alloc_preg (cfg);
3917 MonoBasicBlock *is_null_bb = NULL;
3919 int to_klass_reg, context_used;
3922 NEW_BBLOCK (cfg, is_null_bb);
3924 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, obj_reg, 0);
3925 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, is_null_bb);
3928 tls_get = mono_get_jit_tls_intrinsic (cfg);
3930 fprintf (stderr, "error: --debug=casts not supported on this platform.\n.");
3934 MONO_ADD_INS (cfg->cbb, tls_get);
3935 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, vtable_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
3936 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
3938 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, tls_get->dreg, MONO_STRUCT_OFFSET (MonoJitTlsData, class_cast_from), klass_reg);
3940 context_used = mini_class_check_context_used (cfg, klass);
3942 MonoInst *class_ins;
3944 class_ins = emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_KLASS);
3945 to_klass_reg = class_ins->dreg;
3947 to_klass_reg = alloc_preg (cfg);
3948 MONO_EMIT_NEW_CLASSCONST (cfg, to_klass_reg, klass);
3950 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, tls_get->dreg, MONO_STRUCT_OFFSET (MonoJitTlsData, class_cast_to), to_klass_reg);
3953 MONO_START_BB (cfg, is_null_bb);
3958 reset_cast_details (MonoCompile *cfg)
3960 /* Reset the variables holding the cast details */
3961 if (mini_get_debug_options ()->better_cast_details) {
3962 MonoInst *tls_get = mono_get_jit_tls_intrinsic (cfg);
3964 MONO_ADD_INS (cfg->cbb, tls_get);
3965 /* It is enough to reset the from field */
3966 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STORE_MEMBASE_IMM, tls_get->dreg, MONO_STRUCT_OFFSET (MonoJitTlsData, class_cast_from), 0);
3971 * On return the caller must check @array_class for load errors
3974 mini_emit_check_array_type (MonoCompile *cfg, MonoInst *obj, MonoClass *array_class)
3976 int vtable_reg = alloc_preg (cfg);
3979 context_used = mini_class_check_context_used (cfg, array_class);
3981 save_cast_details (cfg, array_class, obj->dreg, FALSE);
3983 MONO_EMIT_NEW_LOAD_MEMBASE_FAULT (cfg, vtable_reg, obj->dreg, MONO_STRUCT_OFFSET (MonoObject, vtable));
3985 if (cfg->opt & MONO_OPT_SHARED) {
3986 int class_reg = alloc_preg (cfg);
3989 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, class_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
3990 ins = emit_runtime_constant (cfg, MONO_PATCH_INFO_CLASS, array_class);
3991 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, class_reg, ins->dreg);
3992 } else if (context_used) {
3993 MonoInst *vtable_ins;
3995 vtable_ins = emit_get_rgctx_klass (cfg, context_used, array_class, MONO_RGCTX_INFO_VTABLE);
3996 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, vtable_reg, vtable_ins->dreg);
3998 if (cfg->compile_aot) {
4002 if (!(vtable = mono_class_vtable (cfg->domain, array_class)))
4004 vt_reg = alloc_preg (cfg);
4005 MONO_EMIT_NEW_VTABLECONST (cfg, vt_reg, vtable);
4006 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, vtable_reg, vt_reg);
4009 if (!(vtable = mono_class_vtable (cfg->domain, array_class)))
4011 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, vtable_reg, vtable);
4015 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "ArrayTypeMismatchException");
4017 reset_cast_details (cfg);
4021 * Handles unbox of a Nullable<T>. If context_used is non zero, then shared
4022 * generic code is generated.
4025 handle_unbox_nullable (MonoCompile* cfg, MonoInst* val, MonoClass* klass, int context_used)
4027 MonoMethod* method = mono_class_get_method_from_name (klass, "Unbox", 1);
4030 MonoInst *rgctx, *addr;
4032 /* FIXME: What if the class is shared? We might not
4033 have to get the address of the method from the
4035 addr = emit_get_rgctx_method (cfg, context_used, method,
4036 MONO_RGCTX_INFO_GENERIC_METHOD_CODE);
4037 if (cfg->llvm_only && cfg->gsharedvt) {
4038 return emit_llvmonly_calli (cfg, mono_method_signature (method), &val, addr);
4040 rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
4042 return mono_emit_calli (cfg, mono_method_signature (method), &val, addr, NULL, rgctx);
4045 gboolean pass_vtable, pass_mrgctx;
4046 MonoInst *rgctx_arg = NULL;
4048 check_method_sharing (cfg, method, &pass_vtable, &pass_mrgctx);
4049 g_assert (!pass_mrgctx);
4052 MonoVTable *vtable = mono_class_vtable (cfg->domain, method->klass);
4055 EMIT_NEW_VTABLECONST (cfg, rgctx_arg, vtable);
4058 return mono_emit_method_call_full (cfg, method, NULL, FALSE, &val, NULL, NULL, rgctx_arg);
4063 handle_unbox (MonoCompile *cfg, MonoClass *klass, MonoInst **sp, int context_used)
4067 int vtable_reg = alloc_dreg (cfg ,STACK_PTR);
4068 int klass_reg = alloc_dreg (cfg ,STACK_PTR);
4069 int eclass_reg = alloc_dreg (cfg ,STACK_PTR);
4070 int rank_reg = alloc_dreg (cfg ,STACK_I4);
4072 obj_reg = sp [0]->dreg;
4073 MONO_EMIT_NEW_LOAD_MEMBASE_FAULT (cfg, vtable_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
4074 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU1_MEMBASE, rank_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, rank));
4076 /* FIXME: generics */
4077 g_assert (klass->rank == 0);
4080 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, rank_reg, 0);
4081 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException");
4083 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4084 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, eclass_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, element_class));
4087 MonoInst *element_class;
4089 /* This assertion is from the unboxcast insn */
4090 g_assert (klass->rank == 0);
4092 element_class = emit_get_rgctx_klass (cfg, context_used,
4093 klass, MONO_RGCTX_INFO_ELEMENT_KLASS);
4095 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, eclass_reg, element_class->dreg);
4096 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException");
4098 save_cast_details (cfg, klass->element_class, obj_reg, FALSE);
4099 mini_emit_class_check (cfg, eclass_reg, klass->element_class);
4100 reset_cast_details (cfg);
4103 NEW_BIALU_IMM (cfg, add, OP_ADD_IMM, alloc_dreg (cfg, STACK_MP), obj_reg, sizeof (MonoObject));
4104 MONO_ADD_INS (cfg->cbb, add);
4105 add->type = STACK_MP;
4112 handle_unbox_gsharedvt (MonoCompile *cfg, MonoClass *klass, MonoInst *obj)
4114 MonoInst *addr, *klass_inst, *is_ref, *args[16];
4115 MonoBasicBlock *is_ref_bb, *is_nullable_bb, *end_bb;
4119 klass_inst = emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_KLASS);
4125 args [1] = klass_inst;
4128 obj = mono_emit_jit_icall (cfg, mono_object_castclass_unbox, args);
4130 NEW_BBLOCK (cfg, is_ref_bb);
4131 NEW_BBLOCK (cfg, is_nullable_bb);
4132 NEW_BBLOCK (cfg, end_bb);
4133 is_ref = emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_CLASS_BOX_TYPE);
4134 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, is_ref->dreg, MONO_GSHAREDVT_BOX_TYPE_REF);
4135 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, is_ref_bb);
4137 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, is_ref->dreg, MONO_GSHAREDVT_BOX_TYPE_NULLABLE);
4138 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, is_nullable_bb);
4140 /* This will contain either the address of the unboxed vtype, or an address of the temporary where the ref is stored */
4141 addr_reg = alloc_dreg (cfg, STACK_MP);
4145 NEW_BIALU_IMM (cfg, addr, OP_ADD_IMM, addr_reg, obj->dreg, sizeof (MonoObject));
4146 MONO_ADD_INS (cfg->cbb, addr);
4148 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4151 MONO_START_BB (cfg, is_ref_bb);
4153 /* Save the ref to a temporary */
4154 dreg = alloc_ireg (cfg);
4155 EMIT_NEW_VARLOADA_VREG (cfg, addr, dreg, &klass->byval_arg);
4156 addr->dreg = addr_reg;
4157 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, addr->dreg, 0, obj->dreg);
4158 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4161 MONO_START_BB (cfg, is_nullable_bb);
4164 MonoInst *addr = emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_NULLABLE_CLASS_UNBOX);
4165 MonoInst *unbox_call;
4166 MonoMethodSignature *unbox_sig;
4168 unbox_sig = (MonoMethodSignature *)mono_mempool_alloc0 (cfg->mempool, MONO_SIZEOF_METHOD_SIGNATURE + (1 * sizeof (MonoType *)));
4169 unbox_sig->ret = &klass->byval_arg;
4170 unbox_sig->param_count = 1;
4171 unbox_sig->params [0] = &mono_defaults.object_class->byval_arg;
4174 unbox_call = emit_llvmonly_calli (cfg, unbox_sig, &obj, addr);
4176 unbox_call = mono_emit_calli (cfg, unbox_sig, &obj, addr, NULL, NULL);
4178 EMIT_NEW_VARLOADA_VREG (cfg, addr, unbox_call->dreg, &klass->byval_arg);
4179 addr->dreg = addr_reg;
4182 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4185 MONO_START_BB (cfg, end_bb);
4188 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, addr_reg, 0);
4194 * Returns NULL and set the cfg exception on error.
4197 handle_alloc (MonoCompile *cfg, MonoClass *klass, gboolean for_box, int context_used)
4199 MonoInst *iargs [2];
4204 MonoRgctxInfoType rgctx_info;
4205 MonoInst *iargs [2];
4206 gboolean known_instance_size = !mini_is_gsharedvt_klass (klass);
4208 MonoMethod *managed_alloc = mono_gc_get_managed_allocator (klass, for_box, known_instance_size);
4210 if (cfg->opt & MONO_OPT_SHARED)
4211 rgctx_info = MONO_RGCTX_INFO_KLASS;
4213 rgctx_info = MONO_RGCTX_INFO_VTABLE;
4214 data = emit_get_rgctx_klass (cfg, context_used, klass, rgctx_info);
4216 if (cfg->opt & MONO_OPT_SHARED) {
4217 EMIT_NEW_DOMAINCONST (cfg, iargs [0]);
4219 alloc_ftn = ves_icall_object_new;
4222 alloc_ftn = ves_icall_object_new_specific;
4225 if (managed_alloc && !(cfg->opt & MONO_OPT_SHARED)) {
4226 if (known_instance_size) {
4227 int size = mono_class_instance_size (klass);
4228 if (size < sizeof (MonoObject))
4229 g_error ("Invalid size %d for class %s", size, mono_type_get_full_name (klass));
4231 EMIT_NEW_ICONST (cfg, iargs [1], mono_gc_get_aligned_size_for_allocator (size));
4233 return mono_emit_method_call (cfg, managed_alloc, iargs, NULL);
4236 return mono_emit_jit_icall (cfg, alloc_ftn, iargs);
4239 if (cfg->opt & MONO_OPT_SHARED) {
4240 EMIT_NEW_DOMAINCONST (cfg, iargs [0]);
4241 EMIT_NEW_CLASSCONST (cfg, iargs [1], klass);
4243 alloc_ftn = ves_icall_object_new;
4244 } else if (cfg->compile_aot && cfg->cbb->out_of_line && klass->type_token && klass->image == mono_defaults.corlib && !klass->generic_class) {
4245 /* This happens often in argument checking code, eg. throw new FooException... */
4246 /* Avoid relocations and save some space by calling a helper function specialized to mscorlib */
4247 EMIT_NEW_ICONST (cfg, iargs [0], mono_metadata_token_index (klass->type_token));
4248 return mono_emit_jit_icall (cfg, mono_helper_newobj_mscorlib, iargs);
4250 MonoVTable *vtable = mono_class_vtable (cfg->domain, klass);
4251 MonoMethod *managed_alloc = NULL;
4255 mono_cfg_set_exception (cfg, MONO_EXCEPTION_TYPE_LOAD);
4256 cfg->exception_ptr = klass;
4260 managed_alloc = mono_gc_get_managed_allocator (klass, for_box, TRUE);
4262 if (managed_alloc) {
4263 int size = mono_class_instance_size (klass);
4264 if (size < sizeof (MonoObject))
4265 g_error ("Invalid size %d for class %s", size, mono_type_get_full_name (klass));
4267 EMIT_NEW_VTABLECONST (cfg, iargs [0], vtable);
4268 EMIT_NEW_ICONST (cfg, iargs [1], mono_gc_get_aligned_size_for_allocator (size));
4269 return mono_emit_method_call (cfg, managed_alloc, iargs, NULL);
4271 alloc_ftn = mono_class_get_allocation_ftn (vtable, for_box, &pass_lw);
4273 guint32 lw = vtable->klass->instance_size;
4274 lw = ((lw + (sizeof (gpointer) - 1)) & ~(sizeof (gpointer) - 1)) / sizeof (gpointer);
4275 EMIT_NEW_ICONST (cfg, iargs [0], lw);
4276 EMIT_NEW_VTABLECONST (cfg, iargs [1], vtable);
4279 EMIT_NEW_VTABLECONST (cfg, iargs [0], vtable);
4283 return mono_emit_jit_icall (cfg, alloc_ftn, iargs);
4287 * Returns NULL and set the cfg exception on error.
4290 handle_box (MonoCompile *cfg, MonoInst *val, MonoClass *klass, int context_used)
4292 MonoInst *alloc, *ins;
4294 if (mono_class_is_nullable (klass)) {
4295 MonoMethod* method = mono_class_get_method_from_name (klass, "Box", 1);
4298 if (cfg->llvm_only && cfg->gsharedvt) {
4299 MonoInst *addr = emit_get_rgctx_method (cfg, context_used, method,
4300 MONO_RGCTX_INFO_GENERIC_METHOD_CODE);
4301 return emit_llvmonly_calli (cfg, mono_method_signature (method), &val, addr);
4303 /* FIXME: What if the class is shared? We might not
4304 have to get the method address from the RGCTX. */
4305 MonoInst *addr = emit_get_rgctx_method (cfg, context_used, method,
4306 MONO_RGCTX_INFO_GENERIC_METHOD_CODE);
4307 MonoInst *rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
4309 return mono_emit_calli (cfg, mono_method_signature (method), &val, addr, NULL, rgctx);
4312 gboolean pass_vtable, pass_mrgctx;
4313 MonoInst *rgctx_arg = NULL;
4315 check_method_sharing (cfg, method, &pass_vtable, &pass_mrgctx);
4316 g_assert (!pass_mrgctx);
4319 MonoVTable *vtable = mono_class_vtable (cfg->domain, method->klass);
4322 EMIT_NEW_VTABLECONST (cfg, rgctx_arg, vtable);
4325 return mono_emit_method_call_full (cfg, method, NULL, FALSE, &val, NULL, NULL, rgctx_arg);
4329 if (mini_is_gsharedvt_klass (klass)) {
4330 MonoBasicBlock *is_ref_bb, *is_nullable_bb, *end_bb;
4331 MonoInst *res, *is_ref, *src_var, *addr;
4334 dreg = alloc_ireg (cfg);
4336 NEW_BBLOCK (cfg, is_ref_bb);
4337 NEW_BBLOCK (cfg, is_nullable_bb);
4338 NEW_BBLOCK (cfg, end_bb);
4339 is_ref = emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_CLASS_BOX_TYPE);
4340 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, is_ref->dreg, MONO_GSHAREDVT_BOX_TYPE_REF);
4341 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, is_ref_bb);
4343 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, is_ref->dreg, MONO_GSHAREDVT_BOX_TYPE_NULLABLE);
4344 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, is_nullable_bb);
4347 alloc = handle_alloc (cfg, klass, TRUE, context_used);
4350 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, alloc->dreg, sizeof (MonoObject), val->dreg);
4351 ins->opcode = OP_STOREV_MEMBASE;
4353 EMIT_NEW_UNALU (cfg, res, OP_MOVE, dreg, alloc->dreg);
4354 res->type = STACK_OBJ;
4356 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4359 MONO_START_BB (cfg, is_ref_bb);
4361 /* val is a vtype, so has to load the value manually */
4362 src_var = get_vreg_to_inst (cfg, val->dreg);
4364 src_var = mono_compile_create_var_for_vreg (cfg, &klass->byval_arg, OP_LOCAL, val->dreg);
4365 EMIT_NEW_VARLOADA (cfg, addr, src_var, src_var->inst_vtype);
4366 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, dreg, addr->dreg, 0);
4367 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4370 MONO_START_BB (cfg, is_nullable_bb);
4373 MonoInst *addr = emit_get_gsharedvt_info_klass (cfg, klass,
4374 MONO_RGCTX_INFO_NULLABLE_CLASS_BOX);
4376 MonoMethodSignature *box_sig;
4379 * klass is Nullable<T>, need to call Nullable<T>.Box () using a gsharedvt signature, but we cannot
4380 * construct that method at JIT time, so have to do things by hand.
4382 box_sig = (MonoMethodSignature *)mono_mempool_alloc0 (cfg->mempool, MONO_SIZEOF_METHOD_SIGNATURE + (1 * sizeof (MonoType *)));
4383 box_sig->ret = &mono_defaults.object_class->byval_arg;
4384 box_sig->param_count = 1;
4385 box_sig->params [0] = &klass->byval_arg;
4388 box_call = emit_llvmonly_calli (cfg, box_sig, &val, addr);
4390 box_call = mono_emit_calli (cfg, box_sig, &val, addr, NULL, NULL);
4391 EMIT_NEW_UNALU (cfg, res, OP_MOVE, dreg, box_call->dreg);
4392 res->type = STACK_OBJ;
4396 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4398 MONO_START_BB (cfg, end_bb);
4402 alloc = handle_alloc (cfg, klass, TRUE, context_used);
4406 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, alloc->dreg, sizeof (MonoObject), val->dreg);
4412 mini_class_has_reference_variant_generic_argument (MonoCompile *cfg, MonoClass *klass, int context_used)
4415 MonoGenericContainer *container;
4416 MonoGenericInst *ginst;
4418 if (klass->generic_class) {
4419 container = klass->generic_class->container_class->generic_container;
4420 ginst = klass->generic_class->context.class_inst;
4421 } else if (klass->generic_container && context_used) {
4422 container = klass->generic_container;
4423 ginst = container->context.class_inst;
4428 for (i = 0; i < container->type_argc; ++i) {
4430 if (!(mono_generic_container_get_param_info (container, i)->flags & (MONO_GEN_PARAM_VARIANT|MONO_GEN_PARAM_COVARIANT)))
4432 type = ginst->type_argv [i];
4433 if (mini_type_is_reference (type))
4439 static GHashTable* direct_icall_type_hash;
4442 icall_is_direct_callable (MonoCompile *cfg, MonoMethod *cmethod)
4444 /* LLVM on amd64 can't handle calls to non-32 bit addresses */
4445 if (!direct_icalls_enabled (cfg))
4449 * An icall is directly callable if it doesn't directly or indirectly call mono_raise_exception ().
4450 * Whitelist a few icalls for now.
4452 if (!direct_icall_type_hash) {
4453 GHashTable *h = g_hash_table_new (g_str_hash, g_str_equal);
4455 g_hash_table_insert (h, (char*)"Decimal", GUINT_TO_POINTER (1));
4456 g_hash_table_insert (h, (char*)"Number", GUINT_TO_POINTER (1));
4457 g_hash_table_insert (h, (char*)"Buffer", GUINT_TO_POINTER (1));
4458 g_hash_table_insert (h, (char*)"Monitor", GUINT_TO_POINTER (1));
4459 mono_memory_barrier ();
4460 direct_icall_type_hash = h;
4463 if (cmethod->klass == mono_defaults.math_class)
4465 /* No locking needed */
4466 if (cmethod->klass->image == mono_defaults.corlib && g_hash_table_lookup (direct_icall_type_hash, cmethod->klass->name))
4471 #define is_complex_isinst(klass) ((klass->flags & TYPE_ATTRIBUTE_INTERFACE) || klass->rank || mono_class_is_nullable (klass) || mono_class_is_marshalbyref (klass) || (klass->flags & TYPE_ATTRIBUTE_SEALED) || klass->byval_arg.type == MONO_TYPE_VAR || klass->byval_arg.type == MONO_TYPE_MVAR)
4474 emit_castclass_with_cache (MonoCompile *cfg, MonoClass *klass, MonoInst **args)
4476 MonoMethod *mono_castclass;
4479 mono_castclass = mono_marshal_get_castclass_with_cache ();
4481 save_cast_details (cfg, klass, args [0]->dreg, TRUE);
4482 res = mono_emit_method_call (cfg, mono_castclass, args, NULL);
4483 reset_cast_details (cfg);
4489 get_castclass_cache_idx (MonoCompile *cfg)
4491 /* Each CASTCLASS_CACHE patch needs a unique index which identifies the call site */
4492 cfg->castclass_cache_index ++;
4493 return (cfg->method_index << 16) | cfg->castclass_cache_index;
4497 emit_castclass_with_cache_nonshared (MonoCompile *cfg, MonoInst *obj, MonoClass *klass)
4506 EMIT_NEW_CLASSCONST (cfg, args [1], klass);
4509 idx = get_castclass_cache_idx (cfg);
4510 args [2] = emit_runtime_constant (cfg, MONO_PATCH_INFO_CASTCLASS_CACHE, GINT_TO_POINTER (idx));
4512 /*The wrapper doesn't inline well so the bloat of inlining doesn't pay off.*/
4513 return emit_castclass_with_cache (cfg, klass, args);
4517 * Returns NULL and set the cfg exception on error.
4520 handle_castclass (MonoCompile *cfg, MonoClass *klass, MonoInst *src, guint8 *ip, int *inline_costs)
4522 MonoBasicBlock *is_null_bb;
4523 int obj_reg = src->dreg;
4524 int vtable_reg = alloc_preg (cfg);
4526 MonoInst *klass_inst = NULL, *res;
4528 context_used = mini_class_check_context_used (cfg, klass);
4530 if (!context_used && mini_class_has_reference_variant_generic_argument (cfg, klass, context_used)) {
4531 res = emit_castclass_with_cache_nonshared (cfg, src, klass);
4532 (*inline_costs) += 2;
4534 } else if (!context_used && (mono_class_is_marshalbyref (klass) || klass->flags & TYPE_ATTRIBUTE_INTERFACE)) {
4535 MonoMethod *mono_castclass;
4536 MonoInst *iargs [1];
4539 mono_castclass = mono_marshal_get_castclass (klass);
4542 save_cast_details (cfg, klass, src->dreg, TRUE);
4543 costs = inline_method (cfg, mono_castclass, mono_method_signature (mono_castclass),
4544 iargs, ip, cfg->real_offset, TRUE);
4545 reset_cast_details (cfg);
4546 CHECK_CFG_EXCEPTION;
4547 g_assert (costs > 0);
4549 cfg->real_offset += 5;
4551 (*inline_costs) += costs;
4559 if(mini_class_has_reference_variant_generic_argument (cfg, klass, context_used) || is_complex_isinst (klass)) {
4560 MonoInst *cache_ins;
4562 cache_ins = emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_CAST_CACHE);
4567 /* klass - it's the second element of the cache entry*/
4568 EMIT_NEW_LOAD_MEMBASE (cfg, args [1], OP_LOAD_MEMBASE, alloc_preg (cfg), cache_ins->dreg, sizeof (gpointer));
4571 args [2] = cache_ins;
4573 return emit_castclass_with_cache (cfg, klass, args);
4576 klass_inst = emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_KLASS);
4579 NEW_BBLOCK (cfg, is_null_bb);
4581 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, obj_reg, 0);
4582 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, is_null_bb);
4584 save_cast_details (cfg, klass, obj_reg, FALSE);
4586 if (klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
4587 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, vtable_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
4588 mini_emit_iface_cast (cfg, vtable_reg, klass, NULL, NULL);
4590 int klass_reg = alloc_preg (cfg);
4592 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, vtable_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
4594 if (!klass->rank && !cfg->compile_aot && !(cfg->opt & MONO_OPT_SHARED) && (klass->flags & TYPE_ATTRIBUTE_SEALED)) {
4595 /* the remoting code is broken, access the class for now */
4596 if (0) { /*FIXME what exactly is broken? This change refers to r39380 from 2005 and mention some remoting fixes were due.*/
4597 MonoVTable *vt = mono_class_vtable (cfg->domain, klass);
4599 mono_cfg_set_exception (cfg, MONO_EXCEPTION_TYPE_LOAD);
4600 cfg->exception_ptr = klass;
4603 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, vtable_reg, vt);
4605 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4606 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, klass_reg, klass);
4608 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException");
4610 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4611 mini_emit_castclass_inst (cfg, obj_reg, klass_reg, klass, klass_inst, is_null_bb);
4615 MONO_START_BB (cfg, is_null_bb);
4617 reset_cast_details (cfg);
4626 * Returns NULL and set the cfg exception on error.
4629 handle_isinst (MonoCompile *cfg, MonoClass *klass, MonoInst *src, int context_used)
4632 MonoBasicBlock *is_null_bb, *false_bb, *end_bb;
4633 int obj_reg = src->dreg;
4634 int vtable_reg = alloc_preg (cfg);
4635 int res_reg = alloc_ireg_ref (cfg);
4636 MonoInst *klass_inst = NULL;
4641 if(mini_class_has_reference_variant_generic_argument (cfg, klass, context_used) || is_complex_isinst (klass)) {
4642 MonoMethod *mono_isinst = mono_marshal_get_isinst_with_cache ();
4643 MonoInst *cache_ins;
4645 cache_ins = emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_CAST_CACHE);
4650 /* klass - it's the second element of the cache entry*/
4651 EMIT_NEW_LOAD_MEMBASE (cfg, args [1], OP_LOAD_MEMBASE, alloc_preg (cfg), cache_ins->dreg, sizeof (gpointer));
4654 args [2] = cache_ins;
4656 return mono_emit_method_call (cfg, mono_isinst, args, NULL);
4659 klass_inst = emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_KLASS);
4662 NEW_BBLOCK (cfg, is_null_bb);
4663 NEW_BBLOCK (cfg, false_bb);
4664 NEW_BBLOCK (cfg, end_bb);
4666 /* Do the assignment at the beginning, so the other assignment can be if converted */
4667 EMIT_NEW_UNALU (cfg, ins, OP_MOVE, res_reg, obj_reg);
4668 ins->type = STACK_OBJ;
4671 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, obj_reg, 0);
4672 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, is_null_bb);
4674 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, vtable_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
4676 if (klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
4677 g_assert (!context_used);
4678 /* the is_null_bb target simply copies the input register to the output */
4679 mini_emit_iface_cast (cfg, vtable_reg, klass, false_bb, is_null_bb);
4681 int klass_reg = alloc_preg (cfg);
4684 int rank_reg = alloc_preg (cfg);
4685 int eclass_reg = alloc_preg (cfg);
4687 g_assert (!context_used);
4688 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU1_MEMBASE, rank_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, rank));
4689 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, rank_reg, klass->rank);
4690 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, false_bb);
4691 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4692 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, eclass_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, cast_class));
4693 if (klass->cast_class == mono_defaults.object_class) {
4694 int parent_reg = alloc_preg (cfg);
4695 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, parent_reg, eclass_reg, MONO_STRUCT_OFFSET (MonoClass, parent));
4696 mini_emit_class_check_branch (cfg, parent_reg, mono_defaults.enum_class->parent, OP_PBNE_UN, is_null_bb);
4697 mini_emit_class_check_branch (cfg, eclass_reg, mono_defaults.enum_class, OP_PBEQ, is_null_bb);
4698 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, false_bb);
4699 } else if (klass->cast_class == mono_defaults.enum_class->parent) {
4700 mini_emit_class_check_branch (cfg, eclass_reg, mono_defaults.enum_class->parent, OP_PBEQ, is_null_bb);
4701 mini_emit_class_check_branch (cfg, eclass_reg, mono_defaults.enum_class, OP_PBEQ, is_null_bb);
4702 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, false_bb);
4703 } else if (klass->cast_class == mono_defaults.enum_class) {
4704 mini_emit_class_check_branch (cfg, eclass_reg, mono_defaults.enum_class, OP_PBEQ, is_null_bb);
4705 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, false_bb);
4706 } else if (klass->cast_class->flags & TYPE_ATTRIBUTE_INTERFACE) {
4707 mini_emit_iface_class_cast (cfg, eclass_reg, klass->cast_class, false_bb, is_null_bb);
4709 if ((klass->rank == 1) && (klass->byval_arg.type == MONO_TYPE_SZARRAY)) {
4710 /* Check that the object is a vector too */
4711 int bounds_reg = alloc_preg (cfg);
4712 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, bounds_reg, obj_reg, MONO_STRUCT_OFFSET (MonoArray, bounds));
4713 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, bounds_reg, 0);
4714 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, false_bb);
4717 /* the is_null_bb target simply copies the input register to the output */
4718 mini_emit_isninst_cast (cfg, eclass_reg, klass->cast_class, false_bb, is_null_bb);
4720 } else if (mono_class_is_nullable (klass)) {
4721 g_assert (!context_used);
4722 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4723 /* the is_null_bb target simply copies the input register to the output */
4724 mini_emit_isninst_cast (cfg, klass_reg, klass->cast_class, false_bb, is_null_bb);
4726 if (!cfg->compile_aot && !(cfg->opt & MONO_OPT_SHARED) && (klass->flags & TYPE_ATTRIBUTE_SEALED)) {
4727 g_assert (!context_used);
4728 /* the remoting code is broken, access the class for now */
4729 if (0) {/*FIXME what exactly is broken? This change refers to r39380 from 2005 and mention some remoting fixes were due.*/
4730 MonoVTable *vt = mono_class_vtable (cfg->domain, klass);
4732 mono_cfg_set_exception (cfg, MONO_EXCEPTION_TYPE_LOAD);
4733 cfg->exception_ptr = klass;
4736 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, vtable_reg, vt);
4738 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4739 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, klass_reg, klass);
4741 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, false_bb);
4742 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, is_null_bb);
4744 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4745 /* the is_null_bb target simply copies the input register to the output */
4746 mini_emit_isninst_cast_inst (cfg, klass_reg, klass, klass_inst, false_bb, is_null_bb);
4751 MONO_START_BB (cfg, false_bb);
4753 MONO_EMIT_NEW_PCONST (cfg, res_reg, 0);
4754 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4756 MONO_START_BB (cfg, is_null_bb);
4758 MONO_START_BB (cfg, end_bb);
4764 handle_cisinst (MonoCompile *cfg, MonoClass *klass, MonoInst *src)
4766 /* This opcode takes as input an object reference and a class, and returns:
4767 0) if the object is an instance of the class,
4768 1) if the object is not instance of the class,
4769 2) if the object is a proxy whose type cannot be determined */
4772 #ifndef DISABLE_REMOTING
4773 MonoBasicBlock *true_bb, *false_bb, *false2_bb, *end_bb, *no_proxy_bb, *interface_fail_bb;
4775 MonoBasicBlock *true_bb, *false_bb, *end_bb;
4777 int obj_reg = src->dreg;
4778 int dreg = alloc_ireg (cfg);
4780 #ifndef DISABLE_REMOTING
4781 int klass_reg = alloc_preg (cfg);
4784 NEW_BBLOCK (cfg, true_bb);
4785 NEW_BBLOCK (cfg, false_bb);
4786 NEW_BBLOCK (cfg, end_bb);
4787 #ifndef DISABLE_REMOTING
4788 NEW_BBLOCK (cfg, false2_bb);
4789 NEW_BBLOCK (cfg, no_proxy_bb);
4792 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, obj_reg, 0);
4793 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, false_bb);
4795 if (klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
4796 #ifndef DISABLE_REMOTING
4797 NEW_BBLOCK (cfg, interface_fail_bb);
4800 tmp_reg = alloc_preg (cfg);
4801 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
4802 #ifndef DISABLE_REMOTING
4803 mini_emit_iface_cast (cfg, tmp_reg, klass, interface_fail_bb, true_bb);
4804 MONO_START_BB (cfg, interface_fail_bb);
4805 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, tmp_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4807 mini_emit_class_check_branch (cfg, klass_reg, mono_defaults.transparent_proxy_class, OP_PBNE_UN, false_bb);
4809 tmp_reg = alloc_preg (cfg);
4810 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoTransparentProxy, custom_type_info));
4811 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, tmp_reg, 0);
4812 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, false2_bb);
4814 mini_emit_iface_cast (cfg, tmp_reg, klass, false_bb, true_bb);
4817 #ifndef DISABLE_REMOTING
4818 tmp_reg = alloc_preg (cfg);
4819 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
4820 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, tmp_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4822 mini_emit_class_check_branch (cfg, klass_reg, mono_defaults.transparent_proxy_class, OP_PBNE_UN, no_proxy_bb);
4823 tmp_reg = alloc_preg (cfg);
4824 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoTransparentProxy, remote_class));
4825 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, tmp_reg, MONO_STRUCT_OFFSET (MonoRemoteClass, proxy_class));
4827 tmp_reg = alloc_preg (cfg);
4828 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoTransparentProxy, custom_type_info));
4829 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, tmp_reg, 0);
4830 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, no_proxy_bb);
4832 mini_emit_isninst_cast (cfg, klass_reg, klass, false2_bb, true_bb);
4833 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, false2_bb);
4835 MONO_START_BB (cfg, no_proxy_bb);
4837 mini_emit_isninst_cast (cfg, klass_reg, klass, false_bb, true_bb);
4839 g_error ("transparent proxy support is disabled while trying to JIT code that uses it");
4843 MONO_START_BB (cfg, false_bb);
4845 MONO_EMIT_NEW_ICONST (cfg, dreg, 1);
4846 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4848 #ifndef DISABLE_REMOTING
4849 MONO_START_BB (cfg, false2_bb);
4851 MONO_EMIT_NEW_ICONST (cfg, dreg, 2);
4852 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4855 MONO_START_BB (cfg, true_bb);
4857 MONO_EMIT_NEW_ICONST (cfg, dreg, 0);
4859 MONO_START_BB (cfg, end_bb);
4862 MONO_INST_NEW (cfg, ins, OP_ICONST);
4864 ins->type = STACK_I4;
4870 handle_ccastclass (MonoCompile *cfg, MonoClass *klass, MonoInst *src)
4872 /* This opcode takes as input an object reference and a class, and returns:
4873 0) if the object is an instance of the class,
4874 1) if the object is a proxy whose type cannot be determined
4875 an InvalidCastException exception is thrown otherwhise*/
4878 #ifndef DISABLE_REMOTING
4879 MonoBasicBlock *end_bb, *ok_result_bb, *no_proxy_bb, *interface_fail_bb, *fail_1_bb;
4881 MonoBasicBlock *ok_result_bb;
4883 int obj_reg = src->dreg;
4884 int dreg = alloc_ireg (cfg);
4885 int tmp_reg = alloc_preg (cfg);
4887 #ifndef DISABLE_REMOTING
4888 int klass_reg = alloc_preg (cfg);
4889 NEW_BBLOCK (cfg, end_bb);
4892 NEW_BBLOCK (cfg, ok_result_bb);
4894 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, obj_reg, 0);
4895 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, ok_result_bb);
4897 save_cast_details (cfg, klass, obj_reg, FALSE);
4899 if (klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
4900 #ifndef DISABLE_REMOTING
4901 NEW_BBLOCK (cfg, interface_fail_bb);
4903 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
4904 mini_emit_iface_cast (cfg, tmp_reg, klass, interface_fail_bb, ok_result_bb);
4905 MONO_START_BB (cfg, interface_fail_bb);
4906 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, tmp_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4908 mini_emit_class_check (cfg, klass_reg, mono_defaults.transparent_proxy_class);
4910 tmp_reg = alloc_preg (cfg);
4911 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoTransparentProxy, custom_type_info));
4912 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, tmp_reg, 0);
4913 MONO_EMIT_NEW_COND_EXC (cfg, EQ, "InvalidCastException");
4915 MONO_EMIT_NEW_ICONST (cfg, dreg, 1);
4916 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4918 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
4919 mini_emit_iface_cast (cfg, tmp_reg, klass, NULL, NULL);
4920 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, ok_result_bb);
4923 #ifndef DISABLE_REMOTING
4924 NEW_BBLOCK (cfg, no_proxy_bb);
4926 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
4927 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, tmp_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4928 mini_emit_class_check_branch (cfg, klass_reg, mono_defaults.transparent_proxy_class, OP_PBNE_UN, no_proxy_bb);
4930 tmp_reg = alloc_preg (cfg);
4931 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoTransparentProxy, remote_class));
4932 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, tmp_reg, MONO_STRUCT_OFFSET (MonoRemoteClass, proxy_class));
4934 tmp_reg = alloc_preg (cfg);
4935 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoTransparentProxy, custom_type_info));
4936 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, tmp_reg, 0);
4937 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, no_proxy_bb);
4939 NEW_BBLOCK (cfg, fail_1_bb);
4941 mini_emit_isninst_cast (cfg, klass_reg, klass, fail_1_bb, ok_result_bb);
4943 MONO_START_BB (cfg, fail_1_bb);
4945 MONO_EMIT_NEW_ICONST (cfg, dreg, 1);
4946 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4948 MONO_START_BB (cfg, no_proxy_bb);
4950 mini_emit_castclass (cfg, obj_reg, klass_reg, klass, ok_result_bb);
4952 g_error ("Transparent proxy support is disabled while trying to JIT code that uses it");
4956 MONO_START_BB (cfg, ok_result_bb);
4958 MONO_EMIT_NEW_ICONST (cfg, dreg, 0);
4960 #ifndef DISABLE_REMOTING
4961 MONO_START_BB (cfg, end_bb);
4965 MONO_INST_NEW (cfg, ins, OP_ICONST);
4967 ins->type = STACK_I4;
4972 static G_GNUC_UNUSED MonoInst*
4973 handle_enum_has_flag (MonoCompile *cfg, MonoClass *klass, MonoInst *enum_this, MonoInst *enum_flag)
4975 MonoType *enum_type = mono_type_get_underlying_type (&klass->byval_arg);
4976 guint32 load_opc = mono_type_to_load_membase (cfg, enum_type);
4979 switch (enum_type->type) {
4982 #if SIZEOF_REGISTER == 8
4994 MonoInst *load, *and_, *cmp, *ceq;
4995 int enum_reg = is_i4 ? alloc_ireg (cfg) : alloc_lreg (cfg);
4996 int and_reg = is_i4 ? alloc_ireg (cfg) : alloc_lreg (cfg);
4997 int dest_reg = alloc_ireg (cfg);
4999 EMIT_NEW_LOAD_MEMBASE (cfg, load, load_opc, enum_reg, enum_this->dreg, 0);
5000 EMIT_NEW_BIALU (cfg, and_, is_i4 ? OP_IAND : OP_LAND, and_reg, enum_reg, enum_flag->dreg);
5001 EMIT_NEW_BIALU (cfg, cmp, is_i4 ? OP_ICOMPARE : OP_LCOMPARE, -1, and_reg, enum_flag->dreg);
5002 EMIT_NEW_UNALU (cfg, ceq, is_i4 ? OP_ICEQ : OP_LCEQ, dest_reg, -1);
5004 ceq->type = STACK_I4;
5007 load = mono_decompose_opcode (cfg, load);
5008 and_ = mono_decompose_opcode (cfg, and_);
5009 cmp = mono_decompose_opcode (cfg, cmp);
5010 ceq = mono_decompose_opcode (cfg, ceq);
5018 * Returns NULL and set the cfg exception on error.
5020 static G_GNUC_UNUSED MonoInst*
5021 handle_delegate_ctor (MonoCompile *cfg, MonoClass *klass, MonoInst *target, MonoMethod *method, int context_used, gboolean virtual_)
5025 gpointer trampoline;
5026 MonoInst *obj, *method_ins, *tramp_ins;
5030 if (virtual_ && !cfg->llvm_only) {
5031 MonoMethod *invoke = mono_get_delegate_invoke (klass);
5034 if (!mono_get_delegate_virtual_invoke_impl (mono_method_signature (invoke), context_used ? NULL : method))
5038 obj = handle_alloc (cfg, klass, FALSE, mono_class_check_context_used (klass));
5042 /* Inline the contents of mono_delegate_ctor */
5044 /* Set target field */
5045 /* Optimize away setting of NULL target */
5046 if (!(target->opcode == OP_PCONST && target->inst_p0 == 0)) {
5047 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, target), target->dreg);
5048 if (cfg->gen_write_barriers) {
5049 dreg = alloc_preg (cfg);
5050 EMIT_NEW_BIALU_IMM (cfg, ptr, OP_PADD_IMM, dreg, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, target));
5051 emit_write_barrier (cfg, ptr, target);
5055 /* Set method field */
5056 method_ins = emit_get_rgctx_method (cfg, context_used, method, MONO_RGCTX_INFO_METHOD);
5057 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, method), method_ins->dreg);
5060 * To avoid looking up the compiled code belonging to the target method
5061 * in mono_delegate_trampoline (), we allocate a per-domain memory slot to
5062 * store it, and we fill it after the method has been compiled.
5064 if (!method->dynamic && !(cfg->opt & MONO_OPT_SHARED)) {
5065 MonoInst *code_slot_ins;
5068 code_slot_ins = emit_get_rgctx_method (cfg, context_used, method, MONO_RGCTX_INFO_METHOD_DELEGATE_CODE);
5070 domain = mono_domain_get ();
5071 mono_domain_lock (domain);
5072 if (!domain_jit_info (domain)->method_code_hash)
5073 domain_jit_info (domain)->method_code_hash = g_hash_table_new (NULL, NULL);
5074 code_slot = (guint8 **)g_hash_table_lookup (domain_jit_info (domain)->method_code_hash, method);
5076 code_slot = (guint8 **)mono_domain_alloc0 (domain, sizeof (gpointer));
5077 g_hash_table_insert (domain_jit_info (domain)->method_code_hash, method, code_slot);
5079 mono_domain_unlock (domain);
5081 code_slot_ins = emit_runtime_constant (cfg, MONO_PATCH_INFO_METHOD_CODE_SLOT, method);
5083 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, method_code), code_slot_ins->dreg);
5086 if (cfg->llvm_only) {
5087 MonoInst *args [16];
5092 args [2] = emit_get_rgctx_method (cfg, context_used, method, MONO_RGCTX_INFO_METHOD);
5093 mono_emit_jit_icall (cfg, mono_llvmonly_init_delegate_virtual, args);
5096 mono_emit_jit_icall (cfg, mono_llvmonly_init_delegate, args);
5102 if (cfg->compile_aot) {
5103 MonoDelegateClassMethodPair *del_tramp;
5105 del_tramp = (MonoDelegateClassMethodPair *)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoDelegateClassMethodPair));
5106 del_tramp->klass = klass;
5107 del_tramp->method = context_used ? NULL : method;
5108 del_tramp->is_virtual = virtual_;
5109 EMIT_NEW_AOTCONST (cfg, tramp_ins, MONO_PATCH_INFO_DELEGATE_TRAMPOLINE, del_tramp);
5112 trampoline = mono_create_delegate_virtual_trampoline (cfg->domain, klass, context_used ? NULL : method);
5114 trampoline = mono_create_delegate_trampoline_info (cfg->domain, klass, context_used ? NULL : method);
5115 EMIT_NEW_PCONST (cfg, tramp_ins, trampoline);
5118 /* Set invoke_impl field */
5120 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, invoke_impl), tramp_ins->dreg);
5122 dreg = alloc_preg (cfg);
5123 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, dreg, tramp_ins->dreg, MONO_STRUCT_OFFSET (MonoDelegateTrampInfo, invoke_impl));
5124 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, invoke_impl), dreg);
5126 dreg = alloc_preg (cfg);
5127 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, dreg, tramp_ins->dreg, MONO_STRUCT_OFFSET (MonoDelegateTrampInfo, method_ptr));
5128 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, method_ptr), dreg);
5131 dreg = alloc_preg (cfg);
5132 MONO_EMIT_NEW_ICONST (cfg, dreg, virtual_ ? 1 : 0);
5133 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI1_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, method_is_virtual), dreg);
5135 /* All the checks which are in mono_delegate_ctor () are done by the delegate trampoline */
5141 handle_array_new (MonoCompile *cfg, int rank, MonoInst **sp, unsigned char *ip)
5143 MonoJitICallInfo *info;
5145 /* Need to register the icall so it gets an icall wrapper */
5146 info = mono_get_array_new_va_icall (rank);
5148 cfg->flags |= MONO_CFG_HAS_VARARGS;
5150 /* mono_array_new_va () needs a vararg calling convention */
5151 cfg->exception_message = g_strdup ("array-new");
5152 cfg->disable_llvm = TRUE;
5154 /* FIXME: This uses info->sig, but it should use the signature of the wrapper */
5155 return mono_emit_native_call (cfg, mono_icall_get_wrapper (info), info->sig, sp);
5159 * handle_constrained_gsharedvt_call:
5161 * Handle constrained calls where the receiver is a gsharedvt type.
5162 * Return the instruction representing the call. Set the cfg exception on failure.
5165 handle_constrained_gsharedvt_call (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **sp, MonoClass *constrained_class,
5166 gboolean *ref_emit_widen)
5168 MonoInst *ins = NULL;
5169 gboolean emit_widen = *ref_emit_widen;
5172 * Constrained calls need to behave differently at runtime dependending on whenever the receiver is instantiated as ref type or as a vtype.
5173 * This is hard to do with the current call code, since we would have to emit a branch and two different calls. So instead, we
5174 * pack the arguments into an array, and do the rest of the work in in an icall.
5176 if (((cmethod->klass == mono_defaults.object_class) || (cmethod->klass->flags & TYPE_ATTRIBUTE_INTERFACE) || (!cmethod->klass->valuetype && cmethod->klass->image != mono_defaults.corlib)) &&
5177 (MONO_TYPE_IS_VOID (fsig->ret) || MONO_TYPE_IS_PRIMITIVE (fsig->ret) || MONO_TYPE_IS_REFERENCE (fsig->ret) || MONO_TYPE_ISSTRUCT (fsig->ret) || mini_is_gsharedvt_type (fsig->ret)) &&
5178 (fsig->param_count == 0 || (!fsig->hasthis && fsig->param_count == 1) || (fsig->param_count == 1 && (MONO_TYPE_IS_REFERENCE (fsig->params [0]) || fsig->params [0]->byref || mini_is_gsharedvt_type (fsig->params [0]))))) {
5179 MonoInst *args [16];
5182 * This case handles calls to
5183 * - object:ToString()/Equals()/GetHashCode(),
5184 * - System.IComparable<T>:CompareTo()
5185 * - System.IEquatable<T>:Equals ()
5186 * plus some simple interface calls enough to support AsyncTaskMethodBuilder.
5190 if (mono_method_check_context_used (cmethod))
5191 args [1] = emit_get_rgctx_method (cfg, mono_method_check_context_used (cmethod), cmethod, MONO_RGCTX_INFO_METHOD);
5193 EMIT_NEW_METHODCONST (cfg, args [1], cmethod);
5194 args [2] = emit_get_rgctx_klass (cfg, mono_class_check_context_used (constrained_class), constrained_class, MONO_RGCTX_INFO_KLASS);
5196 /* !fsig->hasthis is for the wrapper for the Object.GetType () icall */
5197 if (fsig->hasthis && fsig->param_count) {
5198 /* Pass the arguments using a localloc-ed array using the format expected by runtime_invoke () */
5199 MONO_INST_NEW (cfg, ins, OP_LOCALLOC_IMM);
5200 ins->dreg = alloc_preg (cfg);
5201 ins->inst_imm = fsig->param_count * sizeof (mgreg_t);
5202 MONO_ADD_INS (cfg->cbb, ins);
5205 if (mini_is_gsharedvt_type (fsig->params [0])) {
5206 int addr_reg, deref_arg_reg;
5208 ins = emit_get_gsharedvt_info_klass (cfg, mono_class_from_mono_type (fsig->params [0]), MONO_RGCTX_INFO_CLASS_BOX_TYPE);
5209 deref_arg_reg = alloc_preg (cfg);
5210 /* deref_arg = BOX_TYPE != MONO_GSHAREDVT_BOX_TYPE_VTYPE */
5211 EMIT_NEW_BIALU_IMM (cfg, args [3], OP_ISUB_IMM, deref_arg_reg, ins->dreg, 1);
5213 EMIT_NEW_VARLOADA_VREG (cfg, ins, sp [1]->dreg, fsig->params [0]);
5214 addr_reg = ins->dreg;
5215 EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, args [4]->dreg, 0, addr_reg);
5217 EMIT_NEW_ICONST (cfg, args [3], 0);
5218 EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, args [4]->dreg, 0, sp [1]->dreg);
5221 EMIT_NEW_ICONST (cfg, args [3], 0);
5222 EMIT_NEW_ICONST (cfg, args [4], 0);
5224 ins = mono_emit_jit_icall (cfg, mono_gsharedvt_constrained_call, args);
5227 if (mini_is_gsharedvt_type (fsig->ret)) {
5228 ins = handle_unbox_gsharedvt (cfg, mono_class_from_mono_type (fsig->ret), ins);
5229 } else if (MONO_TYPE_IS_PRIMITIVE (fsig->ret) || MONO_TYPE_ISSTRUCT (fsig->ret)) {
5233 NEW_BIALU_IMM (cfg, add, OP_ADD_IMM, alloc_dreg (cfg, STACK_MP), ins->dreg, sizeof (MonoObject));
5234 MONO_ADD_INS (cfg->cbb, add);
5236 NEW_LOAD_MEMBASE_TYPE (cfg, ins, fsig->ret, add->dreg, 0);
5237 MONO_ADD_INS (cfg->cbb, ins);
5238 /* ins represents the call result */
5241 GSHAREDVT_FAILURE (CEE_CALLVIRT);
5244 *ref_emit_widen = emit_widen;
5253 mono_emit_load_got_addr (MonoCompile *cfg)
5255 MonoInst *getaddr, *dummy_use;
5257 if (!cfg->got_var || cfg->got_var_allocated)
5260 MONO_INST_NEW (cfg, getaddr, OP_LOAD_GOTADDR);
5261 getaddr->cil_code = cfg->header->code;
5262 getaddr->dreg = cfg->got_var->dreg;
5264 /* Add it to the start of the first bblock */
5265 if (cfg->bb_entry->code) {
5266 getaddr->next = cfg->bb_entry->code;
5267 cfg->bb_entry->code = getaddr;
5270 MONO_ADD_INS (cfg->bb_entry, getaddr);
5272 cfg->got_var_allocated = TRUE;
5275 * Add a dummy use to keep the got_var alive, since real uses might
5276 * only be generated by the back ends.
5277 * Add it to end_bblock, so the variable's lifetime covers the whole
5279 * It would be better to make the usage of the got var explicit in all
5280 * cases when the backend needs it (i.e. calls, throw etc.), so this
5281 * wouldn't be needed.
5283 NEW_DUMMY_USE (cfg, dummy_use, cfg->got_var);
5284 MONO_ADD_INS (cfg->bb_exit, dummy_use);
5287 static int inline_limit;
5288 static gboolean inline_limit_inited;
5291 mono_method_check_inlining (MonoCompile *cfg, MonoMethod *method)
5293 MonoMethodHeaderSummary header;
5295 #ifdef MONO_ARCH_SOFT_FLOAT_FALLBACK
5296 MonoMethodSignature *sig = mono_method_signature (method);
5300 if (cfg->disable_inline)
5305 if (cfg->inline_depth > 10)
5308 if (!mono_method_get_header_summary (method, &header))
5311 /*runtime, icall and pinvoke are checked by summary call*/
5312 if ((method->iflags & METHOD_IMPL_ATTRIBUTE_NOINLINING) ||
5313 (method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED) ||
5314 (mono_class_is_marshalbyref (method->klass)) ||
5318 /* also consider num_locals? */
5319 /* Do the size check early to avoid creating vtables */
5320 if (!inline_limit_inited) {
5321 if (g_getenv ("MONO_INLINELIMIT"))
5322 inline_limit = atoi (g_getenv ("MONO_INLINELIMIT"));
5324 inline_limit = INLINE_LENGTH_LIMIT;
5325 inline_limit_inited = TRUE;
5327 if (header.code_size >= inline_limit && !(method->iflags & METHOD_IMPL_ATTRIBUTE_AGGRESSIVE_INLINING))
5331 * if we can initialize the class of the method right away, we do,
5332 * otherwise we don't allow inlining if the class needs initialization,
5333 * since it would mean inserting a call to mono_runtime_class_init()
5334 * inside the inlined code
5336 if (!(cfg->opt & MONO_OPT_SHARED)) {
5337 /* The AggressiveInlining hint is a good excuse to force that cctor to run. */
5338 if (method->iflags & METHOD_IMPL_ATTRIBUTE_AGGRESSIVE_INLINING) {
5339 vtable = mono_class_vtable (cfg->domain, method->klass);
5342 if (!cfg->compile_aot) {
5344 if (!mono_runtime_class_init_full (vtable, &error))
5345 mono_error_raise_exception (&error); /* FIXME don't raise here */
5347 } else if (method->klass->flags & TYPE_ATTRIBUTE_BEFORE_FIELD_INIT) {
5348 if (cfg->run_cctors && method->klass->has_cctor) {
5349 /*FIXME it would easier and lazier to just use mono_class_try_get_vtable */
5350 if (!method->klass->runtime_info)
5351 /* No vtable created yet */
5353 vtable = mono_class_vtable (cfg->domain, method->klass);
5356 /* This makes so that inline cannot trigger */
5357 /* .cctors: too many apps depend on them */
5358 /* running with a specific order... */
5359 if (! vtable->initialized)
5362 if (!mono_runtime_class_init_full (vtable, &error))
5363 mono_error_raise_exception (&error); /* FIXME don't raise here */
5365 } else if (mono_class_needs_cctor_run (method->klass, NULL)) {
5366 if (!method->klass->runtime_info)
5367 /* No vtable created yet */
5369 vtable = mono_class_vtable (cfg->domain, method->klass);
5372 if (!vtable->initialized)
5377 * If we're compiling for shared code
5378 * the cctor will need to be run at aot method load time, for example,
5379 * or at the end of the compilation of the inlining method.
5381 if (mono_class_needs_cctor_run (method->klass, NULL) && !((method->klass->flags & TYPE_ATTRIBUTE_BEFORE_FIELD_INIT)))
5385 #ifdef MONO_ARCH_SOFT_FLOAT_FALLBACK
5386 if (mono_arch_is_soft_float ()) {
5388 if (sig->ret && sig->ret->type == MONO_TYPE_R4)
5390 for (i = 0; i < sig->param_count; ++i)
5391 if (!sig->params [i]->byref && sig->params [i]->type == MONO_TYPE_R4)
5396 if (g_list_find (cfg->dont_inline, method))
5403 mini_field_access_needs_cctor_run (MonoCompile *cfg, MonoMethod *method, MonoClass *klass, MonoVTable *vtable)
5405 if (!cfg->compile_aot) {
5407 if (vtable->initialized)
5411 if (klass->flags & TYPE_ATTRIBUTE_BEFORE_FIELD_INIT) {
5412 if (cfg->method == method)
5416 if (!mono_class_needs_cctor_run (klass, method))
5419 if (! (method->flags & METHOD_ATTRIBUTE_STATIC) && (klass == method->klass))
5420 /* The initialization is already done before the method is called */
5427 mini_emit_ldelema_1_ins (MonoCompile *cfg, MonoClass *klass, MonoInst *arr, MonoInst *index, gboolean bcheck)
5431 int mult_reg, add_reg, array_reg, index_reg, index2_reg;
5434 if (mini_is_gsharedvt_variable_klass (klass)) {
5437 mono_class_init (klass);
5438 size = mono_class_array_element_size (klass);
5441 mult_reg = alloc_preg (cfg);
5442 array_reg = arr->dreg;
5443 index_reg = index->dreg;
5445 #if SIZEOF_REGISTER == 8
5446 /* The array reg is 64 bits but the index reg is only 32 */
5447 if (COMPILE_LLVM (cfg)) {
5449 index2_reg = index_reg;
5451 index2_reg = alloc_preg (cfg);
5452 MONO_EMIT_NEW_UNALU (cfg, OP_SEXT_I4, index2_reg, index_reg);
5455 if (index->type == STACK_I8) {
5456 index2_reg = alloc_preg (cfg);
5457 MONO_EMIT_NEW_UNALU (cfg, OP_LCONV_TO_I4, index2_reg, index_reg);
5459 index2_reg = index_reg;
5464 MONO_EMIT_BOUNDS_CHECK (cfg, array_reg, MonoArray, max_length, index2_reg);
5466 #if defined(TARGET_X86) || defined(TARGET_AMD64)
5467 if (size == 1 || size == 2 || size == 4 || size == 8) {
5468 static const int fast_log2 [] = { 1, 0, 1, -1, 2, -1, -1, -1, 3 };
5470 EMIT_NEW_X86_LEA (cfg, ins, array_reg, index2_reg, fast_log2 [size], MONO_STRUCT_OFFSET (MonoArray, vector));
5471 ins->klass = mono_class_get_element_class (klass);
5472 ins->type = STACK_MP;
5478 add_reg = alloc_ireg_mp (cfg);
5481 MonoInst *rgctx_ins;
5484 g_assert (cfg->gshared);
5485 context_used = mini_class_check_context_used (cfg, klass);
5486 g_assert (context_used);
5487 rgctx_ins = emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_ARRAY_ELEMENT_SIZE);
5488 MONO_EMIT_NEW_BIALU (cfg, OP_IMUL, mult_reg, index2_reg, rgctx_ins->dreg);
5490 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_MUL_IMM, mult_reg, index2_reg, size);
5492 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, add_reg, array_reg, mult_reg);
5493 NEW_BIALU_IMM (cfg, ins, OP_PADD_IMM, add_reg, add_reg, MONO_STRUCT_OFFSET (MonoArray, vector));
5494 ins->klass = mono_class_get_element_class (klass);
5495 ins->type = STACK_MP;
5496 MONO_ADD_INS (cfg->cbb, ins);
5502 mini_emit_ldelema_2_ins (MonoCompile *cfg, MonoClass *klass, MonoInst *arr, MonoInst *index_ins1, MonoInst *index_ins2)
5504 int bounds_reg = alloc_preg (cfg);
5505 int add_reg = alloc_ireg_mp (cfg);
5506 int mult_reg = alloc_preg (cfg);
5507 int mult2_reg = alloc_preg (cfg);
5508 int low1_reg = alloc_preg (cfg);
5509 int low2_reg = alloc_preg (cfg);
5510 int high1_reg = alloc_preg (cfg);
5511 int high2_reg = alloc_preg (cfg);
5512 int realidx1_reg = alloc_preg (cfg);
5513 int realidx2_reg = alloc_preg (cfg);
5514 int sum_reg = alloc_preg (cfg);
5515 int index1, index2, tmpreg;
5519 mono_class_init (klass);
5520 size = mono_class_array_element_size (klass);
5522 index1 = index_ins1->dreg;
5523 index2 = index_ins2->dreg;
5525 #if SIZEOF_REGISTER == 8
5526 /* The array reg is 64 bits but the index reg is only 32 */
5527 if (COMPILE_LLVM (cfg)) {
5530 tmpreg = alloc_preg (cfg);
5531 MONO_EMIT_NEW_UNALU (cfg, OP_SEXT_I4, tmpreg, index1);
5533 tmpreg = alloc_preg (cfg);
5534 MONO_EMIT_NEW_UNALU (cfg, OP_SEXT_I4, tmpreg, index2);
5538 // FIXME: Do we need to do something here for i8 indexes, like in ldelema_1_ins ?
5542 /* range checking */
5543 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, bounds_reg,
5544 arr->dreg, MONO_STRUCT_OFFSET (MonoArray, bounds));
5546 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI4_MEMBASE, low1_reg,
5547 bounds_reg, MONO_STRUCT_OFFSET (MonoArrayBounds, lower_bound));
5548 MONO_EMIT_NEW_BIALU (cfg, OP_PSUB, realidx1_reg, index1, low1_reg);
5549 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI4_MEMBASE, high1_reg,
5550 bounds_reg, MONO_STRUCT_OFFSET (MonoArrayBounds, length));
5551 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, high1_reg, realidx1_reg);
5552 MONO_EMIT_NEW_COND_EXC (cfg, LE_UN, "IndexOutOfRangeException");
5554 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI4_MEMBASE, low2_reg,
5555 bounds_reg, sizeof (MonoArrayBounds) + MONO_STRUCT_OFFSET (MonoArrayBounds, lower_bound));
5556 MONO_EMIT_NEW_BIALU (cfg, OP_PSUB, realidx2_reg, index2, low2_reg);
5557 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI4_MEMBASE, high2_reg,
5558 bounds_reg, sizeof (MonoArrayBounds) + MONO_STRUCT_OFFSET (MonoArrayBounds, length));
5559 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, high2_reg, realidx2_reg);
5560 MONO_EMIT_NEW_COND_EXC (cfg, LE_UN, "IndexOutOfRangeException");
5562 MONO_EMIT_NEW_BIALU (cfg, OP_PMUL, mult_reg, high2_reg, realidx1_reg);
5563 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, sum_reg, mult_reg, realidx2_reg);
5564 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_PMUL_IMM, mult2_reg, sum_reg, size);
5565 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, add_reg, mult2_reg, arr->dreg);
5566 NEW_BIALU_IMM (cfg, ins, OP_PADD_IMM, add_reg, add_reg, MONO_STRUCT_OFFSET (MonoArray, vector));
5568 ins->type = STACK_MP;
5570 MONO_ADD_INS (cfg->cbb, ins);
5576 mini_emit_ldelema_ins (MonoCompile *cfg, MonoMethod *cmethod, MonoInst **sp, unsigned char *ip, gboolean is_set)
5580 MonoMethod *addr_method;
5582 MonoClass *eclass = cmethod->klass->element_class;
5584 rank = mono_method_signature (cmethod)->param_count - (is_set? 1: 0);
5587 return mini_emit_ldelema_1_ins (cfg, eclass, sp [0], sp [1], TRUE);
5589 /* emit_ldelema_2 depends on OP_LMUL */
5590 if (!cfg->backend->emulate_mul_div && rank == 2 && (cfg->opt & MONO_OPT_INTRINS) && !mini_is_gsharedvt_variable_klass (eclass)) {
5591 return mini_emit_ldelema_2_ins (cfg, eclass, sp [0], sp [1], sp [2]);
5594 if (mini_is_gsharedvt_variable_klass (eclass))
5597 element_size = mono_class_array_element_size (eclass);
5598 addr_method = mono_marshal_get_array_address (rank, element_size);
5599 addr = mono_emit_method_call (cfg, addr_method, sp, NULL);
5604 static MonoBreakPolicy
5605 always_insert_breakpoint (MonoMethod *method)
5607 return MONO_BREAK_POLICY_ALWAYS;
5610 static MonoBreakPolicyFunc break_policy_func = always_insert_breakpoint;
5613 * mono_set_break_policy:
5614 * policy_callback: the new callback function
5616 * Allow embedders to decide wherther to actually obey breakpoint instructions
5617 * (both break IL instructions and Debugger.Break () method calls), for example
5618 * to not allow an app to be aborted by a perfectly valid IL opcode when executing
5619 * untrusted or semi-trusted code.
5621 * @policy_callback will be called every time a break point instruction needs to
5622 * be inserted with the method argument being the method that calls Debugger.Break()
5623 * or has the IL break instruction. The callback should return #MONO_BREAK_POLICY_NEVER
5624 * if it wants the breakpoint to not be effective in the given method.
5625 * #MONO_BREAK_POLICY_ALWAYS is the default.
5628 mono_set_break_policy (MonoBreakPolicyFunc policy_callback)
5630 if (policy_callback)
5631 break_policy_func = policy_callback;
5633 break_policy_func = always_insert_breakpoint;
5637 should_insert_brekpoint (MonoMethod *method) {
5638 switch (break_policy_func (method)) {
5639 case MONO_BREAK_POLICY_ALWAYS:
5641 case MONO_BREAK_POLICY_NEVER:
5643 case MONO_BREAK_POLICY_ON_DBG:
5644 g_warning ("mdb no longer supported");
5647 g_warning ("Incorrect value returned from break policy callback");
5652 /* optimize the simple GetGenericValueImpl/SetGenericValueImpl generic icalls */
5654 emit_array_generic_access (MonoCompile *cfg, MonoMethodSignature *fsig, MonoInst **args, int is_set)
5656 MonoInst *addr, *store, *load;
5657 MonoClass *eklass = mono_class_from_mono_type (fsig->params [2]);
5659 /* the bounds check is already done by the callers */
5660 addr = mini_emit_ldelema_1_ins (cfg, eklass, args [0], args [1], FALSE);
5662 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, load, &eklass->byval_arg, args [2]->dreg, 0);
5663 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, store, &eklass->byval_arg, addr->dreg, 0, load->dreg);
5664 if (mini_type_is_reference (fsig->params [2]))
5665 emit_write_barrier (cfg, addr, load);
5667 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, load, &eklass->byval_arg, addr->dreg, 0);
5668 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, store, &eklass->byval_arg, args [2]->dreg, 0, load->dreg);
5675 generic_class_is_reference_type (MonoCompile *cfg, MonoClass *klass)
5677 return mini_type_is_reference (&klass->byval_arg);
5681 emit_array_store (MonoCompile *cfg, MonoClass *klass, MonoInst **sp, gboolean safety_checks)
5683 if (safety_checks && generic_class_is_reference_type (cfg, klass) &&
5684 !(sp [2]->opcode == OP_PCONST && sp [2]->inst_p0 == NULL)) {
5685 MonoClass *obj_array = mono_array_class_get_cached (mono_defaults.object_class, 1);
5686 MonoMethod *helper = mono_marshal_get_virtual_stelemref (obj_array);
5687 MonoInst *iargs [3];
5690 mono_class_setup_vtable (obj_array);
5691 g_assert (helper->slot);
5693 if (sp [0]->type != STACK_OBJ)
5695 if (sp [2]->type != STACK_OBJ)
5702 return mono_emit_method_call (cfg, helper, iargs, sp [0]);
5706 if (mini_is_gsharedvt_variable_klass (klass)) {
5709 // FIXME-VT: OP_ICONST optimization
5710 addr = mini_emit_ldelema_1_ins (cfg, klass, sp [0], sp [1], TRUE);
5711 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, addr->dreg, 0, sp [2]->dreg);
5712 ins->opcode = OP_STOREV_MEMBASE;
5713 } else if (sp [1]->opcode == OP_ICONST) {
5714 int array_reg = sp [0]->dreg;
5715 int index_reg = sp [1]->dreg;
5716 int offset = (mono_class_array_element_size (klass) * sp [1]->inst_c0) + MONO_STRUCT_OFFSET (MonoArray, vector);
5718 if (SIZEOF_REGISTER == 8 && COMPILE_LLVM (cfg))
5719 MONO_EMIT_NEW_UNALU (cfg, OP_ZEXT_I4, index_reg, index_reg);
5722 MONO_EMIT_BOUNDS_CHECK (cfg, array_reg, MonoArray, max_length, index_reg);
5723 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, array_reg, offset, sp [2]->dreg);
5725 MonoInst *addr = mini_emit_ldelema_1_ins (cfg, klass, sp [0], sp [1], safety_checks);
5726 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, addr->dreg, 0, sp [2]->dreg);
5727 if (generic_class_is_reference_type (cfg, klass))
5728 emit_write_barrier (cfg, addr, sp [2]);
5735 emit_array_unsafe_access (MonoCompile *cfg, MonoMethodSignature *fsig, MonoInst **args, int is_set)
5740 eklass = mono_class_from_mono_type (fsig->params [2]);
5742 eklass = mono_class_from_mono_type (fsig->ret);
5745 return emit_array_store (cfg, eklass, args, FALSE);
5747 MonoInst *ins, *addr = mini_emit_ldelema_1_ins (cfg, eklass, args [0], args [1], FALSE);
5748 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &eklass->byval_arg, addr->dreg, 0);
5754 is_unsafe_mov_compatible (MonoCompile *cfg, MonoClass *param_klass, MonoClass *return_klass)
5757 int param_size, return_size;
5759 param_klass = mono_class_from_mono_type (mini_get_underlying_type (¶m_klass->byval_arg));
5760 return_klass = mono_class_from_mono_type (mini_get_underlying_type (&return_klass->byval_arg));
5762 if (cfg->verbose_level > 3)
5763 printf ("[UNSAFE-MOV-INTRISIC] %s <- %s\n", return_klass->name, param_klass->name);
5765 //Don't allow mixing reference types with value types
5766 if (param_klass->valuetype != return_klass->valuetype) {
5767 if (cfg->verbose_level > 3)
5768 printf ("[UNSAFE-MOV-INTRISIC]\tone of the args is a valuetype and the other is not\n");
5772 if (!param_klass->valuetype) {
5773 if (cfg->verbose_level > 3)
5774 printf ("[UNSAFE-MOV-INTRISIC]\targs are reference types\n");
5779 if (param_klass->has_references || return_klass->has_references)
5782 /* Avoid mixing structs and primitive types/enums, they need to be handled differently in the JIT */
5783 if ((MONO_TYPE_ISSTRUCT (¶m_klass->byval_arg) && !MONO_TYPE_ISSTRUCT (&return_klass->byval_arg)) ||
5784 (!MONO_TYPE_ISSTRUCT (¶m_klass->byval_arg) && MONO_TYPE_ISSTRUCT (&return_klass->byval_arg))) {
5785 if (cfg->verbose_level > 3)
5786 printf ("[UNSAFE-MOV-INTRISIC]\tmixing structs and scalars\n");
5790 if (param_klass->byval_arg.type == MONO_TYPE_R4 || param_klass->byval_arg.type == MONO_TYPE_R8 ||
5791 return_klass->byval_arg.type == MONO_TYPE_R4 || return_klass->byval_arg.type == MONO_TYPE_R8) {
5792 if (cfg->verbose_level > 3)
5793 printf ("[UNSAFE-MOV-INTRISIC]\tfloat or double are not supported\n");
5797 param_size = mono_class_value_size (param_klass, &align);
5798 return_size = mono_class_value_size (return_klass, &align);
5800 //We can do it if sizes match
5801 if (param_size == return_size) {
5802 if (cfg->verbose_level > 3)
5803 printf ("[UNSAFE-MOV-INTRISIC]\tsame size\n");
5807 //No simple way to handle struct if sizes don't match
5808 if (MONO_TYPE_ISSTRUCT (¶m_klass->byval_arg)) {
5809 if (cfg->verbose_level > 3)
5810 printf ("[UNSAFE-MOV-INTRISIC]\tsize mismatch and type is a struct\n");
5815 * Same reg size category.
5816 * A quick note on why we don't require widening here.
5817 * The intrinsic is "R Array.UnsafeMov<S,R> (S s)".
5819 * Since the source value comes from a function argument, the JIT will already have
5820 * the value in a VREG and performed any widening needed before (say, when loading from a field).
5822 if (param_size <= 4 && return_size <= 4) {
5823 if (cfg->verbose_level > 3)
5824 printf ("[UNSAFE-MOV-INTRISIC]\tsize mismatch but both are of the same reg class\n");
5832 emit_array_unsafe_mov (MonoCompile *cfg, MonoMethodSignature *fsig, MonoInst **args)
5834 MonoClass *param_klass = mono_class_from_mono_type (fsig->params [0]);
5835 MonoClass *return_klass = mono_class_from_mono_type (fsig->ret);
5837 if (mini_is_gsharedvt_variable_type (fsig->ret))
5840 //Valuetypes that are semantically equivalent or numbers than can be widened to
5841 if (is_unsafe_mov_compatible (cfg, param_klass, return_klass))
5844 //Arrays of valuetypes that are semantically equivalent
5845 if (param_klass->rank == 1 && return_klass->rank == 1 && is_unsafe_mov_compatible (cfg, param_klass->element_class, return_klass->element_class))
5852 mini_emit_inst_for_ctor (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
5854 #ifdef MONO_ARCH_SIMD_INTRINSICS
5855 MonoInst *ins = NULL;
5857 if (cfg->opt & MONO_OPT_SIMD) {
5858 ins = mono_emit_simd_intrinsics (cfg, cmethod, fsig, args);
5864 return mono_emit_native_types_intrinsics (cfg, cmethod, fsig, args);
5868 emit_memory_barrier (MonoCompile *cfg, int kind)
5870 MonoInst *ins = NULL;
5871 MONO_INST_NEW (cfg, ins, OP_MEMORY_BARRIER);
5872 MONO_ADD_INS (cfg->cbb, ins);
5873 ins->backend.memory_barrier_kind = kind;
5879 llvm_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
5881 MonoInst *ins = NULL;
5884 /* The LLVM backend supports these intrinsics */
5885 if (cmethod->klass == mono_defaults.math_class) {
5886 if (strcmp (cmethod->name, "Sin") == 0) {
5888 } else if (strcmp (cmethod->name, "Cos") == 0) {
5890 } else if (strcmp (cmethod->name, "Sqrt") == 0) {
5892 } else if (strcmp (cmethod->name, "Abs") == 0 && fsig->params [0]->type == MONO_TYPE_R8) {
5896 if (opcode && fsig->param_count == 1) {
5897 MONO_INST_NEW (cfg, ins, opcode);
5898 ins->type = STACK_R8;
5899 ins->dreg = mono_alloc_freg (cfg);
5900 ins->sreg1 = args [0]->dreg;
5901 MONO_ADD_INS (cfg->cbb, ins);
5905 if (cfg->opt & MONO_OPT_CMOV) {
5906 if (strcmp (cmethod->name, "Min") == 0) {
5907 if (fsig->params [0]->type == MONO_TYPE_I4)
5909 if (fsig->params [0]->type == MONO_TYPE_U4)
5910 opcode = OP_IMIN_UN;
5911 else if (fsig->params [0]->type == MONO_TYPE_I8)
5913 else if (fsig->params [0]->type == MONO_TYPE_U8)
5914 opcode = OP_LMIN_UN;
5915 } else if (strcmp (cmethod->name, "Max") == 0) {
5916 if (fsig->params [0]->type == MONO_TYPE_I4)
5918 if (fsig->params [0]->type == MONO_TYPE_U4)
5919 opcode = OP_IMAX_UN;
5920 else if (fsig->params [0]->type == MONO_TYPE_I8)
5922 else if (fsig->params [0]->type == MONO_TYPE_U8)
5923 opcode = OP_LMAX_UN;
5927 if (opcode && fsig->param_count == 2) {
5928 MONO_INST_NEW (cfg, ins, opcode);
5929 ins->type = fsig->params [0]->type == MONO_TYPE_I4 ? STACK_I4 : STACK_I8;
5930 ins->dreg = mono_alloc_ireg (cfg);
5931 ins->sreg1 = args [0]->dreg;
5932 ins->sreg2 = args [1]->dreg;
5933 MONO_ADD_INS (cfg->cbb, ins);
5941 mini_emit_inst_for_sharable_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
5943 if (cmethod->klass == mono_defaults.array_class) {
5944 if (strcmp (cmethod->name, "UnsafeStore") == 0)
5945 return emit_array_unsafe_access (cfg, fsig, args, TRUE);
5946 else if (strcmp (cmethod->name, "UnsafeLoad") == 0)
5947 return emit_array_unsafe_access (cfg, fsig, args, FALSE);
5948 else if (strcmp (cmethod->name, "UnsafeMov") == 0)
5949 return emit_array_unsafe_mov (cfg, fsig, args);
5956 mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
5958 MonoInst *ins = NULL;
5960 MonoClass *runtime_helpers_class = mono_class_get_runtime_helpers_class ();
5962 if (cmethod->klass == mono_defaults.string_class) {
5963 if (strcmp (cmethod->name, "get_Chars") == 0 && fsig->param_count + fsig->hasthis == 2) {
5964 int dreg = alloc_ireg (cfg);
5965 int index_reg = alloc_preg (cfg);
5966 int add_reg = alloc_preg (cfg);
5968 #if SIZEOF_REGISTER == 8
5969 if (COMPILE_LLVM (cfg)) {
5970 MONO_EMIT_NEW_UNALU (cfg, OP_ZEXT_I4, index_reg, args [1]->dreg);
5972 /* The array reg is 64 bits but the index reg is only 32 */
5973 MONO_EMIT_NEW_UNALU (cfg, OP_SEXT_I4, index_reg, args [1]->dreg);
5976 index_reg = args [1]->dreg;
5978 MONO_EMIT_BOUNDS_CHECK (cfg, args [0]->dreg, MonoString, length, index_reg);
5980 #if defined(TARGET_X86) || defined(TARGET_AMD64)
5981 EMIT_NEW_X86_LEA (cfg, ins, args [0]->dreg, index_reg, 1, MONO_STRUCT_OFFSET (MonoString, chars));
5982 add_reg = ins->dreg;
5983 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADU2_MEMBASE, dreg,
5986 int mult_reg = alloc_preg (cfg);
5987 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SHL_IMM, mult_reg, index_reg, 1);
5988 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, add_reg, mult_reg, args [0]->dreg);
5989 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADU2_MEMBASE, dreg,
5990 add_reg, MONO_STRUCT_OFFSET (MonoString, chars));
5992 type_from_op (cfg, ins, NULL, NULL);
5994 } else if (strcmp (cmethod->name, "get_Length") == 0 && fsig->param_count + fsig->hasthis == 1) {
5995 int dreg = alloc_ireg (cfg);
5996 /* Decompose later to allow more optimizations */
5997 EMIT_NEW_UNALU (cfg, ins, OP_STRLEN, dreg, args [0]->dreg);
5998 ins->type = STACK_I4;
5999 ins->flags |= MONO_INST_FAULT;
6000 cfg->cbb->has_array_access = TRUE;
6001 cfg->flags |= MONO_CFG_HAS_ARRAY_ACCESS;
6006 } else if (cmethod->klass == mono_defaults.object_class) {
6007 if (strcmp (cmethod->name, "GetType") == 0 && fsig->param_count + fsig->hasthis == 1) {
6008 int dreg = alloc_ireg_ref (cfg);
6009 int vt_reg = alloc_preg (cfg);
6010 MONO_EMIT_NEW_LOAD_MEMBASE_FAULT (cfg, vt_reg, args [0]->dreg, MONO_STRUCT_OFFSET (MonoObject, vtable));
6011 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, dreg, vt_reg, MONO_STRUCT_OFFSET (MonoVTable, type));
6012 type_from_op (cfg, ins, NULL, NULL);
6015 } else if (!cfg->backend->emulate_mul_div && strcmp (cmethod->name, "InternalGetHashCode") == 0 && fsig->param_count == 1 && !mono_gc_is_moving ()) {
6016 int dreg = alloc_ireg (cfg);
6017 int t1 = alloc_ireg (cfg);
6019 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SHL_IMM, t1, args [0]->dreg, 3);
6020 EMIT_NEW_BIALU_IMM (cfg, ins, OP_MUL_IMM, dreg, t1, 2654435761u);
6021 ins->type = STACK_I4;
6024 } else if (strcmp (cmethod->name, ".ctor") == 0 && fsig->param_count == 0) {
6025 MONO_INST_NEW (cfg, ins, OP_NOP);
6026 MONO_ADD_INS (cfg->cbb, ins);
6030 } else if (cmethod->klass == mono_defaults.array_class) {
6031 if (strcmp (cmethod->name, "GetGenericValueImpl") == 0 && fsig->param_count + fsig->hasthis == 3 && !cfg->gsharedvt)
6032 return emit_array_generic_access (cfg, fsig, args, FALSE);
6033 else if (strcmp (cmethod->name, "SetGenericValueImpl") == 0 && fsig->param_count + fsig->hasthis == 3 && !cfg->gsharedvt)
6034 return emit_array_generic_access (cfg, fsig, args, TRUE);
6036 #ifndef MONO_BIG_ARRAYS
6038 * This is an inline version of GetLength/GetLowerBound(0) used frequently in
6041 else if (((strcmp (cmethod->name, "GetLength") == 0 && fsig->param_count + fsig->hasthis == 2) ||
6042 (strcmp (cmethod->name, "GetLowerBound") == 0 && fsig->param_count + fsig->hasthis == 2)) &&
6043 args [1]->opcode == OP_ICONST && args [1]->inst_c0 == 0) {
6044 int dreg = alloc_ireg (cfg);
6045 int bounds_reg = alloc_ireg_mp (cfg);
6046 MonoBasicBlock *end_bb, *szarray_bb;
6047 gboolean get_length = strcmp (cmethod->name, "GetLength") == 0;
6049 NEW_BBLOCK (cfg, end_bb);
6050 NEW_BBLOCK (cfg, szarray_bb);
6052 EMIT_NEW_LOAD_MEMBASE_FAULT (cfg, ins, OP_LOAD_MEMBASE, bounds_reg,
6053 args [0]->dreg, MONO_STRUCT_OFFSET (MonoArray, bounds));
6054 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, bounds_reg, 0);
6055 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, szarray_bb);
6056 /* Non-szarray case */
6058 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADI4_MEMBASE, dreg,
6059 bounds_reg, MONO_STRUCT_OFFSET (MonoArrayBounds, length));
6061 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADI4_MEMBASE, dreg,
6062 bounds_reg, MONO_STRUCT_OFFSET (MonoArrayBounds, lower_bound));
6063 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
6064 MONO_START_BB (cfg, szarray_bb);
6067 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADI4_MEMBASE, dreg,
6068 args [0]->dreg, MONO_STRUCT_OFFSET (MonoArray, max_length));
6070 MONO_EMIT_NEW_ICONST (cfg, dreg, 0);
6071 MONO_START_BB (cfg, end_bb);
6073 EMIT_NEW_UNALU (cfg, ins, OP_MOVE, dreg, dreg);
6074 ins->type = STACK_I4;
6080 if (cmethod->name [0] != 'g')
6083 if (strcmp (cmethod->name, "get_Rank") == 0 && fsig->param_count + fsig->hasthis == 1) {
6084 int dreg = alloc_ireg (cfg);
6085 int vtable_reg = alloc_preg (cfg);
6086 MONO_EMIT_NEW_LOAD_MEMBASE_OP_FAULT (cfg, OP_LOAD_MEMBASE, vtable_reg,
6087 args [0]->dreg, MONO_STRUCT_OFFSET (MonoObject, vtable));
6088 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADU1_MEMBASE, dreg,
6089 vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, rank));
6090 type_from_op (cfg, ins, NULL, NULL);
6093 } else if (strcmp (cmethod->name, "get_Length") == 0 && fsig->param_count + fsig->hasthis == 1) {
6094 int dreg = alloc_ireg (cfg);
6096 EMIT_NEW_LOAD_MEMBASE_FAULT (cfg, ins, OP_LOADI4_MEMBASE, dreg,
6097 args [0]->dreg, MONO_STRUCT_OFFSET (MonoArray, max_length));
6098 type_from_op (cfg, ins, NULL, NULL);
6103 } else if (cmethod->klass == runtime_helpers_class) {
6104 if (strcmp (cmethod->name, "get_OffsetToStringData") == 0 && fsig->param_count == 0) {
6105 EMIT_NEW_ICONST (cfg, ins, MONO_STRUCT_OFFSET (MonoString, chars));
6109 } else if (cmethod->klass == mono_defaults.monitor_class) {
6110 gboolean is_enter = FALSE;
6111 gboolean is_v4 = FALSE;
6113 if (!strcmp (cmethod->name, "enter_with_atomic_var") && mono_method_signature (cmethod)->param_count == 2) {
6117 if (!strcmp (cmethod->name, "Enter") && mono_method_signature (cmethod)->param_count == 1)
6122 * To make async stack traces work, icalls which can block should have a wrapper.
6123 * For Monitor.Enter, emit two calls: a fastpath which doesn't have a wrapper, and a slowpath, which does.
6125 MonoBasicBlock *end_bb;
6127 NEW_BBLOCK (cfg, end_bb);
6129 ins = mono_emit_jit_icall (cfg, is_v4 ? (gpointer)mono_monitor_enter_v4_fast : (gpointer)mono_monitor_enter_fast, args);
6130 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ICOMPARE_IMM, -1, ins->dreg, 0);
6131 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBNE_UN, end_bb);
6132 ins = mono_emit_jit_icall (cfg, is_v4 ? (gpointer)mono_monitor_enter_v4 : (gpointer)mono_monitor_enter, args);
6133 MONO_START_BB (cfg, end_bb);
6136 } else if (cmethod->klass == mono_defaults.thread_class) {
6137 if (strcmp (cmethod->name, "SpinWait_nop") == 0 && fsig->param_count == 0) {
6138 MONO_INST_NEW (cfg, ins, OP_RELAXED_NOP);
6139 MONO_ADD_INS (cfg->cbb, ins);
6141 } else if (strcmp (cmethod->name, "MemoryBarrier") == 0 && fsig->param_count == 0) {
6142 return emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_SEQ);
6143 } else if (!strcmp (cmethod->name, "VolatileRead") && fsig->param_count == 1) {
6145 gboolean is_ref = mini_type_is_reference (fsig->params [0]);
6147 if (fsig->params [0]->type == MONO_TYPE_I1)
6148 opcode = OP_LOADI1_MEMBASE;
6149 else if (fsig->params [0]->type == MONO_TYPE_U1)
6150 opcode = OP_LOADU1_MEMBASE;
6151 else if (fsig->params [0]->type == MONO_TYPE_I2)
6152 opcode = OP_LOADI2_MEMBASE;
6153 else if (fsig->params [0]->type == MONO_TYPE_U2)
6154 opcode = OP_LOADU2_MEMBASE;
6155 else if (fsig->params [0]->type == MONO_TYPE_I4)
6156 opcode = OP_LOADI4_MEMBASE;
6157 else if (fsig->params [0]->type == MONO_TYPE_U4)
6158 opcode = OP_LOADU4_MEMBASE;
6159 else if (fsig->params [0]->type == MONO_TYPE_I8 || fsig->params [0]->type == MONO_TYPE_U8)
6160 opcode = OP_LOADI8_MEMBASE;
6161 else if (fsig->params [0]->type == MONO_TYPE_R4)
6162 opcode = OP_LOADR4_MEMBASE;
6163 else if (fsig->params [0]->type == MONO_TYPE_R8)
6164 opcode = OP_LOADR8_MEMBASE;
6165 else if (is_ref || fsig->params [0]->type == MONO_TYPE_I || fsig->params [0]->type == MONO_TYPE_U)
6166 opcode = OP_LOAD_MEMBASE;
6169 MONO_INST_NEW (cfg, ins, opcode);
6170 ins->inst_basereg = args [0]->dreg;
6171 ins->inst_offset = 0;
6172 MONO_ADD_INS (cfg->cbb, ins);
6174 switch (fsig->params [0]->type) {
6181 ins->dreg = mono_alloc_ireg (cfg);
6182 ins->type = STACK_I4;
6186 ins->dreg = mono_alloc_lreg (cfg);
6187 ins->type = STACK_I8;
6191 ins->dreg = mono_alloc_ireg (cfg);
6192 #if SIZEOF_REGISTER == 8
6193 ins->type = STACK_I8;
6195 ins->type = STACK_I4;
6200 ins->dreg = mono_alloc_freg (cfg);
6201 ins->type = STACK_R8;
6204 g_assert (mini_type_is_reference (fsig->params [0]));
6205 ins->dreg = mono_alloc_ireg_ref (cfg);
6206 ins->type = STACK_OBJ;
6210 if (opcode == OP_LOADI8_MEMBASE)
6211 ins = mono_decompose_opcode (cfg, ins);
6213 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_SEQ);
6217 } else if (!strcmp (cmethod->name, "VolatileWrite") && fsig->param_count == 2) {
6219 gboolean is_ref = mini_type_is_reference (fsig->params [0]);
6221 if (fsig->params [0]->type == MONO_TYPE_I1 || fsig->params [0]->type == MONO_TYPE_U1)
6222 opcode = OP_STOREI1_MEMBASE_REG;
6223 else if (fsig->params [0]->type == MONO_TYPE_I2 || fsig->params [0]->type == MONO_TYPE_U2)
6224 opcode = OP_STOREI2_MEMBASE_REG;
6225 else if (fsig->params [0]->type == MONO_TYPE_I4 || fsig->params [0]->type == MONO_TYPE_U4)
6226 opcode = OP_STOREI4_MEMBASE_REG;
6227 else if (fsig->params [0]->type == MONO_TYPE_I8 || fsig->params [0]->type == MONO_TYPE_U8)
6228 opcode = OP_STOREI8_MEMBASE_REG;
6229 else if (fsig->params [0]->type == MONO_TYPE_R4)
6230 opcode = OP_STORER4_MEMBASE_REG;
6231 else if (fsig->params [0]->type == MONO_TYPE_R8)
6232 opcode = OP_STORER8_MEMBASE_REG;
6233 else if (is_ref || fsig->params [0]->type == MONO_TYPE_I || fsig->params [0]->type == MONO_TYPE_U)
6234 opcode = OP_STORE_MEMBASE_REG;
6237 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_SEQ);
6239 MONO_INST_NEW (cfg, ins, opcode);
6240 ins->sreg1 = args [1]->dreg;
6241 ins->inst_destbasereg = args [0]->dreg;
6242 ins->inst_offset = 0;
6243 MONO_ADD_INS (cfg->cbb, ins);
6245 if (opcode == OP_STOREI8_MEMBASE_REG)
6246 ins = mono_decompose_opcode (cfg, ins);
6251 } else if (cmethod->klass->image == mono_defaults.corlib &&
6252 (strcmp (cmethod->klass->name_space, "System.Threading") == 0) &&
6253 (strcmp (cmethod->klass->name, "Interlocked") == 0)) {
6256 #if SIZEOF_REGISTER == 8
6257 if (!cfg->llvm_only && strcmp (cmethod->name, "Read") == 0 && fsig->param_count == 1 && (fsig->params [0]->type == MONO_TYPE_I8)) {
6258 if (!cfg->llvm_only && mono_arch_opcode_supported (OP_ATOMIC_LOAD_I8)) {
6259 MONO_INST_NEW (cfg, ins, OP_ATOMIC_LOAD_I8);
6260 ins->dreg = mono_alloc_preg (cfg);
6261 ins->sreg1 = args [0]->dreg;
6262 ins->type = STACK_I8;
6263 ins->backend.memory_barrier_kind = MONO_MEMORY_BARRIER_SEQ;
6264 MONO_ADD_INS (cfg->cbb, ins);
6268 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_SEQ);
6270 /* 64 bit reads are already atomic */
6271 MONO_INST_NEW (cfg, load_ins, OP_LOADI8_MEMBASE);
6272 load_ins->dreg = mono_alloc_preg (cfg);
6273 load_ins->inst_basereg = args [0]->dreg;
6274 load_ins->inst_offset = 0;
6275 load_ins->type = STACK_I8;
6276 MONO_ADD_INS (cfg->cbb, load_ins);
6278 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_SEQ);
6285 if (strcmp (cmethod->name, "Increment") == 0 && fsig->param_count == 1) {
6286 MonoInst *ins_iconst;
6289 if (fsig->params [0]->type == MONO_TYPE_I4) {
6290 opcode = OP_ATOMIC_ADD_I4;
6291 cfg->has_atomic_add_i4 = TRUE;
6293 #if SIZEOF_REGISTER == 8
6294 else if (fsig->params [0]->type == MONO_TYPE_I8)
6295 opcode = OP_ATOMIC_ADD_I8;
6298 if (!mono_arch_opcode_supported (opcode))
6300 MONO_INST_NEW (cfg, ins_iconst, OP_ICONST);
6301 ins_iconst->inst_c0 = 1;
6302 ins_iconst->dreg = mono_alloc_ireg (cfg);
6303 MONO_ADD_INS (cfg->cbb, ins_iconst);
6305 MONO_INST_NEW (cfg, ins, opcode);
6306 ins->dreg = mono_alloc_ireg (cfg);
6307 ins->inst_basereg = args [0]->dreg;
6308 ins->inst_offset = 0;
6309 ins->sreg2 = ins_iconst->dreg;
6310 ins->type = (opcode == OP_ATOMIC_ADD_I4) ? STACK_I4 : STACK_I8;
6311 MONO_ADD_INS (cfg->cbb, ins);
6313 } else if (strcmp (cmethod->name, "Decrement") == 0 && fsig->param_count == 1) {
6314 MonoInst *ins_iconst;
6317 if (fsig->params [0]->type == MONO_TYPE_I4) {
6318 opcode = OP_ATOMIC_ADD_I4;
6319 cfg->has_atomic_add_i4 = TRUE;
6321 #if SIZEOF_REGISTER == 8
6322 else if (fsig->params [0]->type == MONO_TYPE_I8)
6323 opcode = OP_ATOMIC_ADD_I8;
6326 if (!mono_arch_opcode_supported (opcode))
6328 MONO_INST_NEW (cfg, ins_iconst, OP_ICONST);
6329 ins_iconst->inst_c0 = -1;
6330 ins_iconst->dreg = mono_alloc_ireg (cfg);
6331 MONO_ADD_INS (cfg->cbb, ins_iconst);
6333 MONO_INST_NEW (cfg, ins, opcode);
6334 ins->dreg = mono_alloc_ireg (cfg);
6335 ins->inst_basereg = args [0]->dreg;
6336 ins->inst_offset = 0;
6337 ins->sreg2 = ins_iconst->dreg;
6338 ins->type = (opcode == OP_ATOMIC_ADD_I4) ? STACK_I4 : STACK_I8;
6339 MONO_ADD_INS (cfg->cbb, ins);
6341 } else if (strcmp (cmethod->name, "Add") == 0 && fsig->param_count == 2) {
6344 if (fsig->params [0]->type == MONO_TYPE_I4) {
6345 opcode = OP_ATOMIC_ADD_I4;
6346 cfg->has_atomic_add_i4 = TRUE;
6348 #if SIZEOF_REGISTER == 8
6349 else if (fsig->params [0]->type == MONO_TYPE_I8)
6350 opcode = OP_ATOMIC_ADD_I8;
6353 if (!mono_arch_opcode_supported (opcode))
6355 MONO_INST_NEW (cfg, ins, opcode);
6356 ins->dreg = mono_alloc_ireg (cfg);
6357 ins->inst_basereg = args [0]->dreg;
6358 ins->inst_offset = 0;
6359 ins->sreg2 = args [1]->dreg;
6360 ins->type = (opcode == OP_ATOMIC_ADD_I4) ? STACK_I4 : STACK_I8;
6361 MONO_ADD_INS (cfg->cbb, ins);
6364 else if (strcmp (cmethod->name, "Exchange") == 0 && fsig->param_count == 2) {
6365 MonoInst *f2i = NULL, *i2f;
6366 guint32 opcode, f2i_opcode, i2f_opcode;
6367 gboolean is_ref = mini_type_is_reference (fsig->params [0]);
6368 gboolean is_float = fsig->params [0]->type == MONO_TYPE_R4 || fsig->params [0]->type == MONO_TYPE_R8;
6370 if (fsig->params [0]->type == MONO_TYPE_I4 ||
6371 fsig->params [0]->type == MONO_TYPE_R4) {
6372 opcode = OP_ATOMIC_EXCHANGE_I4;
6373 f2i_opcode = OP_MOVE_F_TO_I4;
6374 i2f_opcode = OP_MOVE_I4_TO_F;
6375 cfg->has_atomic_exchange_i4 = TRUE;
6377 #if SIZEOF_REGISTER == 8
6379 fsig->params [0]->type == MONO_TYPE_I8 ||
6380 fsig->params [0]->type == MONO_TYPE_R8 ||
6381 fsig->params [0]->type == MONO_TYPE_I) {
6382 opcode = OP_ATOMIC_EXCHANGE_I8;
6383 f2i_opcode = OP_MOVE_F_TO_I8;
6384 i2f_opcode = OP_MOVE_I8_TO_F;
6387 else if (is_ref || fsig->params [0]->type == MONO_TYPE_I) {
6388 opcode = OP_ATOMIC_EXCHANGE_I4;
6389 cfg->has_atomic_exchange_i4 = TRUE;
6395 if (!mono_arch_opcode_supported (opcode))
6399 /* TODO: Decompose these opcodes instead of bailing here. */
6400 if (COMPILE_SOFT_FLOAT (cfg))
6403 MONO_INST_NEW (cfg, f2i, f2i_opcode);
6404 f2i->dreg = mono_alloc_ireg (cfg);
6405 f2i->sreg1 = args [1]->dreg;
6406 if (f2i_opcode == OP_MOVE_F_TO_I4)
6407 f2i->backend.spill_var = mini_get_int_to_float_spill_area (cfg);
6408 MONO_ADD_INS (cfg->cbb, f2i);
6411 MONO_INST_NEW (cfg, ins, opcode);
6412 ins->dreg = is_ref ? mono_alloc_ireg_ref (cfg) : mono_alloc_ireg (cfg);
6413 ins->inst_basereg = args [0]->dreg;
6414 ins->inst_offset = 0;
6415 ins->sreg2 = is_float ? f2i->dreg : args [1]->dreg;
6416 MONO_ADD_INS (cfg->cbb, ins);
6418 switch (fsig->params [0]->type) {
6420 ins->type = STACK_I4;
6423 ins->type = STACK_I8;
6426 #if SIZEOF_REGISTER == 8
6427 ins->type = STACK_I8;
6429 ins->type = STACK_I4;
6434 ins->type = STACK_R8;
6437 g_assert (mini_type_is_reference (fsig->params [0]));
6438 ins->type = STACK_OBJ;
6443 MONO_INST_NEW (cfg, i2f, i2f_opcode);
6444 i2f->dreg = mono_alloc_freg (cfg);
6445 i2f->sreg1 = ins->dreg;
6446 i2f->type = STACK_R8;
6447 if (i2f_opcode == OP_MOVE_I4_TO_F)
6448 i2f->backend.spill_var = mini_get_int_to_float_spill_area (cfg);
6449 MONO_ADD_INS (cfg->cbb, i2f);
6454 if (cfg->gen_write_barriers && is_ref)
6455 emit_write_barrier (cfg, args [0], args [1]);
6457 else if ((strcmp (cmethod->name, "CompareExchange") == 0) && fsig->param_count == 3) {
6458 MonoInst *f2i_new = NULL, *f2i_cmp = NULL, *i2f;
6459 guint32 opcode, f2i_opcode, i2f_opcode;
6460 gboolean is_ref = mini_type_is_reference (fsig->params [1]);
6461 gboolean is_float = fsig->params [1]->type == MONO_TYPE_R4 || fsig->params [1]->type == MONO_TYPE_R8;
6463 if (fsig->params [1]->type == MONO_TYPE_I4 ||
6464 fsig->params [1]->type == MONO_TYPE_R4) {
6465 opcode = OP_ATOMIC_CAS_I4;
6466 f2i_opcode = OP_MOVE_F_TO_I4;
6467 i2f_opcode = OP_MOVE_I4_TO_F;
6468 cfg->has_atomic_cas_i4 = TRUE;
6470 #if SIZEOF_REGISTER == 8
6472 fsig->params [1]->type == MONO_TYPE_I8 ||
6473 fsig->params [1]->type == MONO_TYPE_R8 ||
6474 fsig->params [1]->type == MONO_TYPE_I) {
6475 opcode = OP_ATOMIC_CAS_I8;
6476 f2i_opcode = OP_MOVE_F_TO_I8;
6477 i2f_opcode = OP_MOVE_I8_TO_F;
6480 else if (is_ref || fsig->params [1]->type == MONO_TYPE_I) {
6481 opcode = OP_ATOMIC_CAS_I4;
6482 cfg->has_atomic_cas_i4 = TRUE;
6488 if (!mono_arch_opcode_supported (opcode))
6492 /* TODO: Decompose these opcodes instead of bailing here. */
6493 if (COMPILE_SOFT_FLOAT (cfg))
6496 MONO_INST_NEW (cfg, f2i_new, f2i_opcode);
6497 f2i_new->dreg = mono_alloc_ireg (cfg);
6498 f2i_new->sreg1 = args [1]->dreg;
6499 if (f2i_opcode == OP_MOVE_F_TO_I4)
6500 f2i_new->backend.spill_var = mini_get_int_to_float_spill_area (cfg);
6501 MONO_ADD_INS (cfg->cbb, f2i_new);
6503 MONO_INST_NEW (cfg, f2i_cmp, f2i_opcode);
6504 f2i_cmp->dreg = mono_alloc_ireg (cfg);
6505 f2i_cmp->sreg1 = args [2]->dreg;
6506 if (f2i_opcode == OP_MOVE_F_TO_I4)
6507 f2i_cmp->backend.spill_var = mini_get_int_to_float_spill_area (cfg);
6508 MONO_ADD_INS (cfg->cbb, f2i_cmp);
6511 MONO_INST_NEW (cfg, ins, opcode);
6512 ins->dreg = is_ref ? alloc_ireg_ref (cfg) : alloc_ireg (cfg);
6513 ins->sreg1 = args [0]->dreg;
6514 ins->sreg2 = is_float ? f2i_new->dreg : args [1]->dreg;
6515 ins->sreg3 = is_float ? f2i_cmp->dreg : args [2]->dreg;
6516 MONO_ADD_INS (cfg->cbb, ins);
6518 switch (fsig->params [1]->type) {
6520 ins->type = STACK_I4;
6523 ins->type = STACK_I8;
6526 #if SIZEOF_REGISTER == 8
6527 ins->type = STACK_I8;
6529 ins->type = STACK_I4;
6533 ins->type = cfg->r4_stack_type;
6536 ins->type = STACK_R8;
6539 g_assert (mini_type_is_reference (fsig->params [1]));
6540 ins->type = STACK_OBJ;
6545 MONO_INST_NEW (cfg, i2f, i2f_opcode);
6546 i2f->dreg = mono_alloc_freg (cfg);
6547 i2f->sreg1 = ins->dreg;
6548 i2f->type = STACK_R8;
6549 if (i2f_opcode == OP_MOVE_I4_TO_F)
6550 i2f->backend.spill_var = mini_get_int_to_float_spill_area (cfg);
6551 MONO_ADD_INS (cfg->cbb, i2f);
6556 if (cfg->gen_write_barriers && is_ref)
6557 emit_write_barrier (cfg, args [0], args [1]);
6559 else if ((strcmp (cmethod->name, "CompareExchange") == 0) && fsig->param_count == 4 &&
6560 fsig->params [1]->type == MONO_TYPE_I4) {
6561 MonoInst *cmp, *ceq;
6563 if (!mono_arch_opcode_supported (OP_ATOMIC_CAS_I4))
6566 /* int32 r = CAS (location, value, comparand); */
6567 MONO_INST_NEW (cfg, ins, OP_ATOMIC_CAS_I4);
6568 ins->dreg = alloc_ireg (cfg);
6569 ins->sreg1 = args [0]->dreg;
6570 ins->sreg2 = args [1]->dreg;
6571 ins->sreg3 = args [2]->dreg;
6572 ins->type = STACK_I4;
6573 MONO_ADD_INS (cfg->cbb, ins);
6575 /* bool result = r == comparand; */
6576 MONO_INST_NEW (cfg, cmp, OP_ICOMPARE);
6577 cmp->sreg1 = ins->dreg;
6578 cmp->sreg2 = args [2]->dreg;
6579 cmp->type = STACK_I4;
6580 MONO_ADD_INS (cfg->cbb, cmp);
6582 MONO_INST_NEW (cfg, ceq, OP_ICEQ);
6583 ceq->dreg = alloc_ireg (cfg);
6584 ceq->type = STACK_I4;
6585 MONO_ADD_INS (cfg->cbb, ceq);
6587 /* *success = result; */
6588 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI1_MEMBASE_REG, args [3]->dreg, 0, ceq->dreg);
6590 cfg->has_atomic_cas_i4 = TRUE;
6592 else if (strcmp (cmethod->name, "MemoryBarrier") == 0 && fsig->param_count == 0)
6593 ins = emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_SEQ);
6597 } else if (cmethod->klass->image == mono_defaults.corlib &&
6598 (strcmp (cmethod->klass->name_space, "System.Threading") == 0) &&
6599 (strcmp (cmethod->klass->name, "Volatile") == 0)) {
6602 if (!cfg->llvm_only && !strcmp (cmethod->name, "Read") && fsig->param_count == 1) {
6604 MonoType *t = fsig->params [0];
6606 gboolean is_float = t->type == MONO_TYPE_R4 || t->type == MONO_TYPE_R8;
6608 g_assert (t->byref);
6609 /* t is a byref type, so the reference check is more complicated */
6610 is_ref = mini_type_is_reference (&mono_class_from_mono_type (t)->byval_arg);
6611 if (t->type == MONO_TYPE_I1)
6612 opcode = OP_ATOMIC_LOAD_I1;
6613 else if (t->type == MONO_TYPE_U1 || t->type == MONO_TYPE_BOOLEAN)
6614 opcode = OP_ATOMIC_LOAD_U1;
6615 else if (t->type == MONO_TYPE_I2)
6616 opcode = OP_ATOMIC_LOAD_I2;
6617 else if (t->type == MONO_TYPE_U2)
6618 opcode = OP_ATOMIC_LOAD_U2;
6619 else if (t->type == MONO_TYPE_I4)
6620 opcode = OP_ATOMIC_LOAD_I4;
6621 else if (t->type == MONO_TYPE_U4)
6622 opcode = OP_ATOMIC_LOAD_U4;
6623 else if (t->type == MONO_TYPE_R4)
6624 opcode = OP_ATOMIC_LOAD_R4;
6625 else if (t->type == MONO_TYPE_R8)
6626 opcode = OP_ATOMIC_LOAD_R8;
6627 #if SIZEOF_REGISTER == 8
6628 else if (t->type == MONO_TYPE_I8 || t->type == MONO_TYPE_I)
6629 opcode = OP_ATOMIC_LOAD_I8;
6630 else if (is_ref || t->type == MONO_TYPE_U8 || t->type == MONO_TYPE_U)
6631 opcode = OP_ATOMIC_LOAD_U8;
6633 else if (t->type == MONO_TYPE_I)
6634 opcode = OP_ATOMIC_LOAD_I4;
6635 else if (is_ref || t->type == MONO_TYPE_U)
6636 opcode = OP_ATOMIC_LOAD_U4;
6640 if (!mono_arch_opcode_supported (opcode))
6643 MONO_INST_NEW (cfg, ins, opcode);
6644 ins->dreg = is_ref ? mono_alloc_ireg_ref (cfg) : (is_float ? mono_alloc_freg (cfg) : mono_alloc_ireg (cfg));
6645 ins->sreg1 = args [0]->dreg;
6646 ins->backend.memory_barrier_kind = MONO_MEMORY_BARRIER_ACQ;
6647 MONO_ADD_INS (cfg->cbb, ins);
6650 case MONO_TYPE_BOOLEAN:
6657 ins->type = STACK_I4;
6661 ins->type = STACK_I8;
6665 #if SIZEOF_REGISTER == 8
6666 ins->type = STACK_I8;
6668 ins->type = STACK_I4;
6672 ins->type = cfg->r4_stack_type;
6675 ins->type = STACK_R8;
6679 ins->type = STACK_OBJ;
6685 if (!cfg->llvm_only && !strcmp (cmethod->name, "Write") && fsig->param_count == 2) {
6687 MonoType *t = fsig->params [0];
6690 g_assert (t->byref);
6691 is_ref = mini_type_is_reference (&mono_class_from_mono_type (t)->byval_arg);
6692 if (t->type == MONO_TYPE_I1)
6693 opcode = OP_ATOMIC_STORE_I1;
6694 else if (t->type == MONO_TYPE_U1 || t->type == MONO_TYPE_BOOLEAN)
6695 opcode = OP_ATOMIC_STORE_U1;
6696 else if (t->type == MONO_TYPE_I2)
6697 opcode = OP_ATOMIC_STORE_I2;
6698 else if (t->type == MONO_TYPE_U2)
6699 opcode = OP_ATOMIC_STORE_U2;
6700 else if (t->type == MONO_TYPE_I4)
6701 opcode = OP_ATOMIC_STORE_I4;
6702 else if (t->type == MONO_TYPE_U4)
6703 opcode = OP_ATOMIC_STORE_U4;
6704 else if (t->type == MONO_TYPE_R4)
6705 opcode = OP_ATOMIC_STORE_R4;
6706 else if (t->type == MONO_TYPE_R8)
6707 opcode = OP_ATOMIC_STORE_R8;
6708 #if SIZEOF_REGISTER == 8
6709 else if (t->type == MONO_TYPE_I8 || t->type == MONO_TYPE_I)
6710 opcode = OP_ATOMIC_STORE_I8;
6711 else if (is_ref || t->type == MONO_TYPE_U8 || t->type == MONO_TYPE_U)
6712 opcode = OP_ATOMIC_STORE_U8;
6714 else if (t->type == MONO_TYPE_I)
6715 opcode = OP_ATOMIC_STORE_I4;
6716 else if (is_ref || t->type == MONO_TYPE_U)
6717 opcode = OP_ATOMIC_STORE_U4;
6721 if (!mono_arch_opcode_supported (opcode))
6724 MONO_INST_NEW (cfg, ins, opcode);
6725 ins->dreg = args [0]->dreg;
6726 ins->sreg1 = args [1]->dreg;
6727 ins->backend.memory_barrier_kind = MONO_MEMORY_BARRIER_REL;
6728 MONO_ADD_INS (cfg->cbb, ins);
6730 if (cfg->gen_write_barriers && is_ref)
6731 emit_write_barrier (cfg, args [0], args [1]);
6737 } else if (cmethod->klass->image == mono_defaults.corlib &&
6738 (strcmp (cmethod->klass->name_space, "System.Diagnostics") == 0) &&
6739 (strcmp (cmethod->klass->name, "Debugger") == 0)) {
6740 if (!strcmp (cmethod->name, "Break") && fsig->param_count == 0) {
6741 if (should_insert_brekpoint (cfg->method)) {
6742 ins = mono_emit_jit_icall (cfg, mono_debugger_agent_user_break, NULL);
6744 MONO_INST_NEW (cfg, ins, OP_NOP);
6745 MONO_ADD_INS (cfg->cbb, ins);
6749 } else if (cmethod->klass->image == mono_defaults.corlib &&
6750 (strcmp (cmethod->klass->name_space, "System") == 0) &&
6751 (strcmp (cmethod->klass->name, "Environment") == 0)) {
6752 if (!strcmp (cmethod->name, "get_IsRunningOnWindows") && fsig->param_count == 0) {
6754 EMIT_NEW_ICONST (cfg, ins, 1);
6756 EMIT_NEW_ICONST (cfg, ins, 0);
6759 } else if (cmethod->klass->image == mono_defaults.corlib &&
6760 (strcmp (cmethod->klass->name_space, "System.Reflection") == 0) &&
6761 (strcmp (cmethod->klass->name, "Assembly") == 0)) {
6762 if (cfg->llvm_only && !strcmp (cmethod->name, "GetExecutingAssembly")) {
6763 /* No stack walks are currently available, so implement this as an intrinsic */
6764 MonoInst *assembly_ins;
6766 EMIT_NEW_AOTCONST (cfg, assembly_ins, MONO_PATCH_INFO_IMAGE, cfg->method->klass->image);
6767 ins = mono_emit_jit_icall (cfg, mono_get_assembly_object, &assembly_ins);
6770 } else if (cmethod->klass->image == mono_defaults.corlib &&
6771 (strcmp (cmethod->klass->name_space, "System.Reflection") == 0) &&
6772 (strcmp (cmethod->klass->name, "MethodBase") == 0)) {
6773 if (cfg->llvm_only && !strcmp (cmethod->name, "GetCurrentMethod")) {
6774 /* No stack walks are currently available, so implement this as an intrinsic */
6775 MonoInst *method_ins;
6776 MonoMethod *declaring = cfg->method;
6778 /* This returns the declaring generic method */
6779 if (declaring->is_inflated)
6780 declaring = ((MonoMethodInflated*)cfg->method)->declaring;
6781 EMIT_NEW_AOTCONST (cfg, method_ins, MONO_PATCH_INFO_METHODCONST, declaring);
6782 ins = mono_emit_jit_icall (cfg, mono_get_method_object, &method_ins);
6783 cfg->no_inline = TRUE;
6784 if (cfg->method != cfg->current_method)
6785 inline_failure (cfg, "MethodBase:GetCurrentMethod ()");
6788 } else if (cmethod->klass == mono_defaults.math_class) {
6790 * There is general branchless code for Min/Max, but it does not work for
6792 * http://everything2.com/?node_id=1051618
6794 } else if (((!strcmp (cmethod->klass->image->assembly->aname.name, "MonoMac") ||
6795 !strcmp (cmethod->klass->image->assembly->aname.name, "monotouch")) &&
6796 !strcmp (cmethod->klass->name_space, "XamCore.ObjCRuntime") &&
6797 !strcmp (cmethod->klass->name, "Selector")) ||
6798 (!strcmp (cmethod->klass->image->assembly->aname.name, "Xamarin.iOS") &&
6799 !strcmp (cmethod->klass->name_space, "ObjCRuntime") &&
6800 !strcmp (cmethod->klass->name, "Selector"))
6802 if (cfg->backend->have_objc_get_selector &&
6803 !strcmp (cmethod->name, "GetHandle") && fsig->param_count == 1 &&
6804 (args [0]->opcode == OP_GOT_ENTRY || args [0]->opcode == OP_AOTCONST) &&
6805 cfg->compile_aot && !cfg->llvm_only) {
6807 MonoJumpInfoToken *ji;
6812 cfg->exception_message = g_strdup ("GetHandle");
6813 cfg->disable_llvm = TRUE;
6815 if (args [0]->opcode == OP_GOT_ENTRY) {
6816 pi = (MonoInst *)args [0]->inst_p1;
6817 g_assert (pi->opcode == OP_PATCH_INFO);
6818 g_assert (GPOINTER_TO_INT (pi->inst_p1) == MONO_PATCH_INFO_LDSTR);
6819 ji = (MonoJumpInfoToken *)pi->inst_p0;
6821 g_assert (GPOINTER_TO_INT (args [0]->inst_p1) == MONO_PATCH_INFO_LDSTR);
6822 ji = (MonoJumpInfoToken *)args [0]->inst_p0;
6825 NULLIFY_INS (args [0]);
6828 s = mono_ldstr (cfg->domain, ji->image, mono_metadata_token_index (ji->token));
6829 MONO_INST_NEW (cfg, ins, OP_OBJC_GET_SELECTOR);
6830 ins->dreg = mono_alloc_ireg (cfg);
6832 ins->inst_p0 = mono_string_to_utf8 (s);
6833 MONO_ADD_INS (cfg->cbb, ins);
6838 #ifdef MONO_ARCH_SIMD_INTRINSICS
6839 if (cfg->opt & MONO_OPT_SIMD) {
6840 ins = mono_emit_simd_intrinsics (cfg, cmethod, fsig, args);
6846 ins = mono_emit_native_types_intrinsics (cfg, cmethod, fsig, args);
6850 if (COMPILE_LLVM (cfg)) {
6851 ins = llvm_emit_inst_for_method (cfg, cmethod, fsig, args);
6856 return mono_arch_emit_inst_for_method (cfg, cmethod, fsig, args);
6860 * This entry point could be used later for arbitrary method
6863 inline static MonoInst*
6864 mini_redirect_call (MonoCompile *cfg, MonoMethod *method,
6865 MonoMethodSignature *signature, MonoInst **args, MonoInst *this_ins)
6867 if (method->klass == mono_defaults.string_class) {
6868 /* managed string allocation support */
6869 if (strcmp (method->name, "InternalAllocateStr") == 0 && !(mono_profiler_events & MONO_PROFILE_ALLOCATIONS) && !(cfg->opt & MONO_OPT_SHARED)) {
6870 MonoInst *iargs [2];
6871 MonoVTable *vtable = mono_class_vtable (cfg->domain, method->klass);
6872 MonoMethod *managed_alloc = NULL;
6874 g_assert (vtable); /*Should not fail since it System.String*/
6875 #ifndef MONO_CROSS_COMPILE
6876 managed_alloc = mono_gc_get_managed_allocator (method->klass, FALSE, FALSE);
6880 EMIT_NEW_VTABLECONST (cfg, iargs [0], vtable);
6881 iargs [1] = args [0];
6882 return mono_emit_method_call (cfg, managed_alloc, iargs, this_ins);
6889 mono_save_args (MonoCompile *cfg, MonoMethodSignature *sig, MonoInst **sp)
6891 MonoInst *store, *temp;
6894 for (i = 0; i < sig->param_count + sig->hasthis; ++i) {
6895 MonoType *argtype = (sig->hasthis && (i == 0)) ? type_from_stack_type (*sp) : sig->params [i - sig->hasthis];
6898 * FIXME: We should use *args++ = sp [0], but that would mean the arg
6899 * would be different than the MonoInst's used to represent arguments, and
6900 * the ldelema implementation can't deal with that.
6901 * Solution: When ldelema is used on an inline argument, create a var for
6902 * it, emit ldelema on that var, and emit the saving code below in
6903 * inline_method () if needed.
6905 temp = mono_compile_create_var (cfg, argtype, OP_LOCAL);
6906 cfg->args [i] = temp;
6907 /* This uses cfg->args [i] which is set by the preceeding line */
6908 EMIT_NEW_ARGSTORE (cfg, store, i, *sp);
6909 store->cil_code = sp [0]->cil_code;
6914 #define MONO_INLINE_CALLED_LIMITED_METHODS 1
6915 #define MONO_INLINE_CALLER_LIMITED_METHODS 1
6917 #if (MONO_INLINE_CALLED_LIMITED_METHODS)
6919 check_inline_called_method_name_limit (MonoMethod *called_method)
6922 static const char *limit = NULL;
6924 if (limit == NULL) {
6925 const char *limit_string = g_getenv ("MONO_INLINE_CALLED_METHOD_NAME_LIMIT");
6927 if (limit_string != NULL)
6928 limit = limit_string;
6933 if (limit [0] != '\0') {
6934 char *called_method_name = mono_method_full_name (called_method, TRUE);
6936 strncmp_result = strncmp (called_method_name, limit, strlen (limit));
6937 g_free (called_method_name);
6939 //return (strncmp_result <= 0);
6940 return (strncmp_result == 0);
6947 #if (MONO_INLINE_CALLER_LIMITED_METHODS)
6949 check_inline_caller_method_name_limit (MonoMethod *caller_method)
6952 static const char *limit = NULL;
6954 if (limit == NULL) {
6955 const char *limit_string = g_getenv ("MONO_INLINE_CALLER_METHOD_NAME_LIMIT");
6956 if (limit_string != NULL) {
6957 limit = limit_string;
6963 if (limit [0] != '\0') {
6964 char *caller_method_name = mono_method_full_name (caller_method, TRUE);
6966 strncmp_result = strncmp (caller_method_name, limit, strlen (limit));
6967 g_free (caller_method_name);
6969 //return (strncmp_result <= 0);
6970 return (strncmp_result == 0);
6978 emit_init_rvar (MonoCompile *cfg, int dreg, MonoType *rtype)
6980 static double r8_0 = 0.0;
6981 static float r4_0 = 0.0;
6985 rtype = mini_get_underlying_type (rtype);
6989 MONO_EMIT_NEW_PCONST (cfg, dreg, NULL);
6990 } else if (t >= MONO_TYPE_BOOLEAN && t <= MONO_TYPE_U4) {
6991 MONO_EMIT_NEW_ICONST (cfg, dreg, 0);
6992 } else if (t == MONO_TYPE_I8 || t == MONO_TYPE_U8) {
6993 MONO_EMIT_NEW_I8CONST (cfg, dreg, 0);
6994 } else if (cfg->r4fp && t == MONO_TYPE_R4) {
6995 MONO_INST_NEW (cfg, ins, OP_R4CONST);
6996 ins->type = STACK_R4;
6997 ins->inst_p0 = (void*)&r4_0;
6999 MONO_ADD_INS (cfg->cbb, ins);
7000 } else if (t == MONO_TYPE_R4 || t == MONO_TYPE_R8) {
7001 MONO_INST_NEW (cfg, ins, OP_R8CONST);
7002 ins->type = STACK_R8;
7003 ins->inst_p0 = (void*)&r8_0;
7005 MONO_ADD_INS (cfg->cbb, ins);
7006 } else if ((t == MONO_TYPE_VALUETYPE) || (t == MONO_TYPE_TYPEDBYREF) ||
7007 ((t == MONO_TYPE_GENERICINST) && mono_type_generic_inst_is_valuetype (rtype))) {
7008 MONO_EMIT_NEW_VZERO (cfg, dreg, mono_class_from_mono_type (rtype));
7009 } else if (((t == MONO_TYPE_VAR) || (t == MONO_TYPE_MVAR)) && mini_type_var_is_vt (rtype)) {
7010 MONO_EMIT_NEW_VZERO (cfg, dreg, mono_class_from_mono_type (rtype));
7012 MONO_EMIT_NEW_PCONST (cfg, dreg, NULL);
7017 emit_dummy_init_rvar (MonoCompile *cfg, int dreg, MonoType *rtype)
7021 rtype = mini_get_underlying_type (rtype);
7025 MONO_EMIT_NEW_DUMMY_INIT (cfg, dreg, OP_DUMMY_PCONST);
7026 } else if (t >= MONO_TYPE_BOOLEAN && t <= MONO_TYPE_U4) {
7027 MONO_EMIT_NEW_DUMMY_INIT (cfg, dreg, OP_DUMMY_ICONST);
7028 } else if (t == MONO_TYPE_I8 || t == MONO_TYPE_U8) {
7029 MONO_EMIT_NEW_DUMMY_INIT (cfg, dreg, OP_DUMMY_I8CONST);
7030 } else if (cfg->r4fp && t == MONO_TYPE_R4) {
7031 MONO_EMIT_NEW_DUMMY_INIT (cfg, dreg, OP_DUMMY_R4CONST);
7032 } else if (t == MONO_TYPE_R4 || t == MONO_TYPE_R8) {
7033 MONO_EMIT_NEW_DUMMY_INIT (cfg, dreg, OP_DUMMY_R8CONST);
7034 } else if ((t == MONO_TYPE_VALUETYPE) || (t == MONO_TYPE_TYPEDBYREF) ||
7035 ((t == MONO_TYPE_GENERICINST) && mono_type_generic_inst_is_valuetype (rtype))) {
7036 MONO_EMIT_NEW_DUMMY_INIT (cfg, dreg, OP_DUMMY_VZERO);
7037 } else if (((t == MONO_TYPE_VAR) || (t == MONO_TYPE_MVAR)) && mini_type_var_is_vt (rtype)) {
7038 MONO_EMIT_NEW_DUMMY_INIT (cfg, dreg, OP_DUMMY_VZERO);
7040 emit_init_rvar (cfg, dreg, rtype);
7044 /* If INIT is FALSE, emit dummy initialization statements to keep the IR valid */
7046 emit_init_local (MonoCompile *cfg, int local, MonoType *type, gboolean init)
7048 MonoInst *var = cfg->locals [local];
7049 if (COMPILE_SOFT_FLOAT (cfg)) {
7051 int reg = alloc_dreg (cfg, (MonoStackType)var->type);
7052 emit_init_rvar (cfg, reg, type);
7053 EMIT_NEW_LOCSTORE (cfg, store, local, cfg->cbb->last_ins);
7056 emit_init_rvar (cfg, var->dreg, type);
7058 emit_dummy_init_rvar (cfg, var->dreg, type);
7065 * Return the cost of inlining CMETHOD.
7068 inline_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **sp,
7069 guchar *ip, guint real_offset, gboolean inline_always)
7072 MonoInst *ins, *rvar = NULL;
7073 MonoMethodHeader *cheader;
7074 MonoBasicBlock *ebblock, *sbblock;
7076 MonoMethod *prev_inlined_method;
7077 MonoInst **prev_locals, **prev_args;
7078 MonoType **prev_arg_types;
7079 guint prev_real_offset;
7080 GHashTable *prev_cbb_hash;
7081 MonoBasicBlock **prev_cil_offset_to_bb;
7082 MonoBasicBlock *prev_cbb;
7083 unsigned char* prev_cil_start;
7084 guint32 prev_cil_offset_to_bb_len;
7085 MonoMethod *prev_current_method;
7086 MonoGenericContext *prev_generic_context;
7087 gboolean ret_var_set, prev_ret_var_set, prev_disable_inline, virtual_ = FALSE;
7089 g_assert (cfg->exception_type == MONO_EXCEPTION_NONE);
7091 #if (MONO_INLINE_CALLED_LIMITED_METHODS)
7092 if ((! inline_always) && ! check_inline_called_method_name_limit (cmethod))
7095 #if (MONO_INLINE_CALLER_LIMITED_METHODS)
7096 if ((! inline_always) && ! check_inline_caller_method_name_limit (cfg->method))
7101 fsig = mono_method_signature (cmethod);
7103 if (cfg->verbose_level > 2)
7104 printf ("INLINE START %p %s -> %s\n", cmethod, mono_method_full_name (cfg->method, TRUE), mono_method_full_name (cmethod, TRUE));
7106 if (!cmethod->inline_info) {
7107 cfg->stat_inlineable_methods++;
7108 cmethod->inline_info = 1;
7111 /* allocate local variables */
7112 cheader = mono_method_get_header_checked (cmethod, &error);
7114 if (inline_always) {
7115 mono_cfg_set_exception (cfg, MONO_EXCEPTION_MONO_ERROR);
7116 mono_error_move (&cfg->error, &error);
7118 mono_error_cleanup (&error);
7123 /*Must verify before creating locals as it can cause the JIT to assert.*/
7124 if (mono_compile_is_broken (cfg, cmethod, FALSE)) {
7125 mono_metadata_free_mh (cheader);
7129 /* allocate space to store the return value */
7130 if (!MONO_TYPE_IS_VOID (fsig->ret)) {
7131 rvar = mono_compile_create_var (cfg, fsig->ret, OP_LOCAL);
7134 prev_locals = cfg->locals;
7135 cfg->locals = (MonoInst **)mono_mempool_alloc0 (cfg->mempool, cheader->num_locals * sizeof (MonoInst*));
7136 for (i = 0; i < cheader->num_locals; ++i)
7137 cfg->locals [i] = mono_compile_create_var (cfg, cheader->locals [i], OP_LOCAL);
7139 /* allocate start and end blocks */
7140 /* This is needed so if the inline is aborted, we can clean up */
7141 NEW_BBLOCK (cfg, sbblock);
7142 sbblock->real_offset = real_offset;
7144 NEW_BBLOCK (cfg, ebblock);
7145 ebblock->block_num = cfg->num_bblocks++;
7146 ebblock->real_offset = real_offset;
7148 prev_args = cfg->args;
7149 prev_arg_types = cfg->arg_types;
7150 prev_inlined_method = cfg->inlined_method;
7151 cfg->inlined_method = cmethod;
7152 cfg->ret_var_set = FALSE;
7153 cfg->inline_depth ++;
7154 prev_real_offset = cfg->real_offset;
7155 prev_cbb_hash = cfg->cbb_hash;
7156 prev_cil_offset_to_bb = cfg->cil_offset_to_bb;
7157 prev_cil_offset_to_bb_len = cfg->cil_offset_to_bb_len;
7158 prev_cil_start = cfg->cil_start;
7159 prev_cbb = cfg->cbb;
7160 prev_current_method = cfg->current_method;
7161 prev_generic_context = cfg->generic_context;
7162 prev_ret_var_set = cfg->ret_var_set;
7163 prev_disable_inline = cfg->disable_inline;
7165 if (ip && *ip == CEE_CALLVIRT && !(cmethod->flags & METHOD_ATTRIBUTE_STATIC))
7168 costs = mono_method_to_ir (cfg, cmethod, sbblock, ebblock, rvar, sp, real_offset, virtual_);
7170 ret_var_set = cfg->ret_var_set;
7172 cfg->inlined_method = prev_inlined_method;
7173 cfg->real_offset = prev_real_offset;
7174 cfg->cbb_hash = prev_cbb_hash;
7175 cfg->cil_offset_to_bb = prev_cil_offset_to_bb;
7176 cfg->cil_offset_to_bb_len = prev_cil_offset_to_bb_len;
7177 cfg->cil_start = prev_cil_start;
7178 cfg->locals = prev_locals;
7179 cfg->args = prev_args;
7180 cfg->arg_types = prev_arg_types;
7181 cfg->current_method = prev_current_method;
7182 cfg->generic_context = prev_generic_context;
7183 cfg->ret_var_set = prev_ret_var_set;
7184 cfg->disable_inline = prev_disable_inline;
7185 cfg->inline_depth --;
7187 if ((costs >= 0 && costs < 60) || inline_always || (costs >= 0 && (cmethod->iflags & METHOD_IMPL_ATTRIBUTE_AGGRESSIVE_INLINING))) {
7188 if (cfg->verbose_level > 2)
7189 printf ("INLINE END %s -> %s\n", mono_method_full_name (cfg->method, TRUE), mono_method_full_name (cmethod, TRUE));
7191 cfg->stat_inlined_methods++;
7193 /* always add some code to avoid block split failures */
7194 MONO_INST_NEW (cfg, ins, OP_NOP);
7195 MONO_ADD_INS (prev_cbb, ins);
7197 prev_cbb->next_bb = sbblock;
7198 link_bblock (cfg, prev_cbb, sbblock);
7201 * Get rid of the begin and end bblocks if possible to aid local
7204 mono_merge_basic_blocks (cfg, prev_cbb, sbblock);
7206 if ((prev_cbb->out_count == 1) && (prev_cbb->out_bb [0]->in_count == 1) && (prev_cbb->out_bb [0] != ebblock))
7207 mono_merge_basic_blocks (cfg, prev_cbb, prev_cbb->out_bb [0]);
7209 if ((ebblock->in_count == 1) && ebblock->in_bb [0]->out_count == 1) {
7210 MonoBasicBlock *prev = ebblock->in_bb [0];
7212 if (prev->next_bb == ebblock) {
7213 mono_merge_basic_blocks (cfg, prev, ebblock);
7215 if ((prev_cbb->out_count == 1) && (prev_cbb->out_bb [0]->in_count == 1) && (prev_cbb->out_bb [0] == prev)) {
7216 mono_merge_basic_blocks (cfg, prev_cbb, prev);
7217 cfg->cbb = prev_cbb;
7220 /* There could be a bblock after 'prev', and making 'prev' the current bb could cause problems */
7225 * Its possible that the rvar is set in some prev bblock, but not in others.
7231 for (i = 0; i < ebblock->in_count; ++i) {
7232 bb = ebblock->in_bb [i];
7234 if (bb->last_ins && bb->last_ins->opcode == OP_NOT_REACHED) {
7237 emit_init_rvar (cfg, rvar->dreg, fsig->ret);
7247 * If the inlined method contains only a throw, then the ret var is not
7248 * set, so set it to a dummy value.
7251 emit_init_rvar (cfg, rvar->dreg, fsig->ret);
7253 EMIT_NEW_TEMPLOAD (cfg, ins, rvar->inst_c0);
7256 cfg->headers_to_free = g_slist_prepend_mempool (cfg->mempool, cfg->headers_to_free, cheader);
7259 if (cfg->verbose_level > 2)
7260 printf ("INLINE ABORTED %s (cost %d)\n", mono_method_full_name (cmethod, TRUE), costs);
7261 cfg->exception_type = MONO_EXCEPTION_NONE;
7262 mono_loader_clear_error ();
7264 /* This gets rid of the newly added bblocks */
7265 cfg->cbb = prev_cbb;
7267 cfg->headers_to_free = g_slist_prepend_mempool (cfg->mempool, cfg->headers_to_free, cheader);
7272 * Some of these comments may well be out-of-date.
7273 * Design decisions: we do a single pass over the IL code (and we do bblock
7274 * splitting/merging in the few cases when it's required: a back jump to an IL
7275 * address that was not already seen as bblock starting point).
7276 * Code is validated as we go (full verification is still better left to metadata/verify.c).
7277 * Complex operations are decomposed in simpler ones right away. We need to let the
7278 * arch-specific code peek and poke inside this process somehow (except when the
7279 * optimizations can take advantage of the full semantic info of coarse opcodes).
7280 * All the opcodes of the form opcode.s are 'normalized' to opcode.
7281 * MonoInst->opcode initially is the IL opcode or some simplification of that
7282 * (OP_LOAD, OP_STORE). The arch-specific code may rearrange it to an arch-specific
7283 * opcode with value bigger than OP_LAST.
7284 * At this point the IR can be handed over to an interpreter, a dumb code generator
7285 * or to the optimizing code generator that will translate it to SSA form.
7287 * Profiling directed optimizations.
7288 * We may compile by default with few or no optimizations and instrument the code
7289 * or the user may indicate what methods to optimize the most either in a config file
7290 * or through repeated runs where the compiler applies offline the optimizations to
7291 * each method and then decides if it was worth it.
7294 #define CHECK_TYPE(ins) if (!(ins)->type) UNVERIFIED
7295 #define CHECK_STACK(num) if ((sp - stack_start) < (num)) UNVERIFIED
7296 #define CHECK_STACK_OVF(num) if (((sp - stack_start) + (num)) > header->max_stack) UNVERIFIED
7297 #define CHECK_ARG(num) if ((unsigned)(num) >= (unsigned)num_args) UNVERIFIED
7298 #define CHECK_LOCAL(num) if ((unsigned)(num) >= (unsigned)header->num_locals) UNVERIFIED
7299 #define CHECK_OPSIZE(size) if (ip + size > end) UNVERIFIED
7300 #define CHECK_UNVERIFIABLE(cfg) if (cfg->unverifiable) UNVERIFIED
7301 #define CHECK_TYPELOAD(klass) if (!(klass) || mono_class_has_failure (klass)) TYPE_LOAD_ERROR ((klass))
7303 /* offset from br.s -> br like opcodes */
7304 #define BIG_BRANCH_OFFSET 13
7307 ip_in_bb (MonoCompile *cfg, MonoBasicBlock *bb, const guint8* ip)
7309 MonoBasicBlock *b = cfg->cil_offset_to_bb [ip - cfg->cil_start];
7311 return b == NULL || b == bb;
7315 get_basic_blocks (MonoCompile *cfg, MonoMethodHeader* header, guint real_offset, unsigned char *start, unsigned char *end, unsigned char **pos)
7317 unsigned char *ip = start;
7318 unsigned char *target;
7321 MonoBasicBlock *bblock;
7322 const MonoOpcode *opcode;
7325 cli_addr = ip - start;
7326 i = mono_opcode_value ((const guint8 **)&ip, end);
7329 opcode = &mono_opcodes [i];
7330 switch (opcode->argument) {
7331 case MonoInlineNone:
7334 case MonoInlineString:
7335 case MonoInlineType:
7336 case MonoInlineField:
7337 case MonoInlineMethod:
7340 case MonoShortInlineR:
7347 case MonoShortInlineVar:
7348 case MonoShortInlineI:
7351 case MonoShortInlineBrTarget:
7352 target = start + cli_addr + 2 + (signed char)ip [1];
7353 GET_BBLOCK (cfg, bblock, target);
7356 GET_BBLOCK (cfg, bblock, ip);
7358 case MonoInlineBrTarget:
7359 target = start + cli_addr + 5 + (gint32)read32 (ip + 1);
7360 GET_BBLOCK (cfg, bblock, target);
7363 GET_BBLOCK (cfg, bblock, ip);
7365 case MonoInlineSwitch: {
7366 guint32 n = read32 (ip + 1);
7369 cli_addr += 5 + 4 * n;
7370 target = start + cli_addr;
7371 GET_BBLOCK (cfg, bblock, target);
7373 for (j = 0; j < n; ++j) {
7374 target = start + cli_addr + (gint32)read32 (ip);
7375 GET_BBLOCK (cfg, bblock, target);
7385 g_assert_not_reached ();
7388 if (i == CEE_THROW) {
7389 unsigned char *bb_start = ip - 1;
7391 /* Find the start of the bblock containing the throw */
7393 while ((bb_start >= start) && !bblock) {
7394 bblock = cfg->cil_offset_to_bb [(bb_start) - start];
7398 bblock->out_of_line = 1;
7408 static inline MonoMethod *
7409 mini_get_method_allow_open (MonoMethod *m, guint32 token, MonoClass *klass, MonoGenericContext *context, MonoError *error)
7413 mono_error_init (error);
7415 if (m->wrapper_type != MONO_WRAPPER_NONE) {
7416 method = (MonoMethod *)mono_method_get_wrapper_data (m, token);
7418 method = mono_class_inflate_generic_method_checked (method, context, error);
7421 method = mono_get_method_checked (m->klass->image, token, klass, context, error);
7427 static inline MonoMethod *
7428 mini_get_method (MonoCompile *cfg, MonoMethod *m, guint32 token, MonoClass *klass, MonoGenericContext *context)
7431 MonoMethod *method = mini_get_method_allow_open (m, token, klass, context, cfg ? &cfg->error : &error);
7433 if (method && cfg && !cfg->gshared && mono_class_is_open_constructed_type (&method->klass->byval_arg)) {
7434 mono_error_set_bad_image (&cfg->error, cfg->method->klass->image, "Method with open type while not compiling gshared");
7438 if (!method && !cfg)
7439 mono_error_cleanup (&error); /* FIXME don't swallow the error */
7444 static inline MonoClass*
7445 mini_get_class (MonoMethod *method, guint32 token, MonoGenericContext *context)
7450 if (method->wrapper_type != MONO_WRAPPER_NONE) {
7451 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
7453 klass = mono_class_inflate_generic_class_checked (klass, context, &error);
7454 mono_error_cleanup (&error); /* FIXME don't swallow the error */
7457 klass = mono_class_get_and_inflate_typespec_checked (method->klass->image, token, context, &error);
7458 mono_error_cleanup (&error); /* FIXME don't swallow the error */
7461 mono_class_init (klass);
7465 static inline MonoMethodSignature*
7466 mini_get_signature (MonoMethod *method, guint32 token, MonoGenericContext *context)
7468 MonoMethodSignature *fsig;
7470 if (method->wrapper_type != MONO_WRAPPER_NONE) {
7471 fsig = (MonoMethodSignature *)mono_method_get_wrapper_data (method, token);
7473 fsig = mono_metadata_parse_signature (method->klass->image, token);
7477 fsig = mono_inflate_generic_signature(fsig, context, &error);
7479 g_assert(mono_error_ok(&error));
7485 throw_exception (void)
7487 static MonoMethod *method = NULL;
7490 MonoSecurityManager *secman = mono_security_manager_get_methods ();
7491 method = mono_class_get_method_from_name (secman->securitymanager, "ThrowException", 1);
7498 emit_throw_exception (MonoCompile *cfg, MonoException *ex)
7500 MonoMethod *thrower = throw_exception ();
7503 EMIT_NEW_PCONST (cfg, args [0], ex);
7504 mono_emit_method_call (cfg, thrower, args, NULL);
7508 * Return the original method is a wrapper is specified. We can only access
7509 * the custom attributes from the original method.
7512 get_original_method (MonoMethod *method)
7514 if (method->wrapper_type == MONO_WRAPPER_NONE)
7517 /* native code (which is like Critical) can call any managed method XXX FIXME XXX to validate all usages */
7518 if (method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED)
7521 /* in other cases we need to find the original method */
7522 return mono_marshal_method_from_wrapper (method);
7526 ensure_method_is_allowed_to_access_field (MonoCompile *cfg, MonoMethod *caller, MonoClassField *field)
7528 /* we can't get the coreclr security level on wrappers since they don't have the attributes */
7529 MonoException *ex = mono_security_core_clr_is_field_access_allowed (get_original_method (caller), field);
7531 emit_throw_exception (cfg, ex);
7535 ensure_method_is_allowed_to_call_method (MonoCompile *cfg, MonoMethod *caller, MonoMethod *callee)
7537 /* we can't get the coreclr security level on wrappers since they don't have the attributes */
7538 MonoException *ex = mono_security_core_clr_is_call_allowed (get_original_method (caller), callee);
7540 emit_throw_exception (cfg, ex);
7544 * Check that the IL instructions at ip are the array initialization
7545 * sequence and return the pointer to the data and the size.
7548 initialize_array_data (MonoMethod *method, gboolean aot, unsigned char *ip, MonoClass *klass, guint32 len, int *out_size, guint32 *out_field_token)
7551 * newarr[System.Int32]
7553 * ldtoken field valuetype ...
7554 * call void class [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(class [mscorlib]System.Array, valuetype [mscorlib]System.RuntimeFieldHandle)
7556 if (ip [0] == CEE_DUP && ip [1] == CEE_LDTOKEN && ip [5] == 0x4 && ip [6] == CEE_CALL) {
7558 guint32 token = read32 (ip + 7);
7559 guint32 field_token = read32 (ip + 2);
7560 guint32 field_index = field_token & 0xffffff;
7562 const char *data_ptr;
7564 MonoMethod *cmethod;
7565 MonoClass *dummy_class;
7566 MonoClassField *field = mono_field_from_token_checked (method->klass->image, field_token, &dummy_class, NULL, &error);
7570 mono_error_cleanup (&error); /* FIXME don't swallow the error */
7574 *out_field_token = field_token;
7576 cmethod = mini_get_method (NULL, method, token, NULL, NULL);
7579 if (strcmp (cmethod->name, "InitializeArray") || strcmp (cmethod->klass->name, "RuntimeHelpers") || cmethod->klass->image != mono_defaults.corlib)
7581 switch (mono_type_get_underlying_type (&klass->byval_arg)->type) {
7582 case MONO_TYPE_BOOLEAN:
7586 /* we need to swap on big endian, so punt. Should we handle R4 and R8 as well? */
7587 #if TARGET_BYTE_ORDER == G_LITTLE_ENDIAN
7588 case MONO_TYPE_CHAR:
7605 if (size > mono_type_size (field->type, &dummy_align))
7608 /*g_print ("optimized in %s: size: %d, numelems: %d\n", method->name, size, newarr->inst_newa_len->inst_c0);*/
7609 if (!image_is_dynamic (method->klass->image)) {
7610 field_index = read32 (ip + 2) & 0xffffff;
7611 mono_metadata_field_info (method->klass->image, field_index - 1, NULL, &rva, NULL);
7612 data_ptr = mono_image_rva_map (method->klass->image, rva);
7613 /*g_print ("field: 0x%08x, rva: %d, rva_ptr: %p\n", read32 (ip + 2), rva, data_ptr);*/
7614 /* for aot code we do the lookup on load */
7615 if (aot && data_ptr)
7616 return (const char *)GUINT_TO_POINTER (rva);
7618 /*FIXME is it possible to AOT a SRE assembly not meant to be saved? */
7620 data_ptr = mono_field_get_data (field);
7628 set_exception_type_from_invalid_il (MonoCompile *cfg, MonoMethod *method, unsigned char *ip)
7631 char *method_fname = mono_method_full_name (method, TRUE);
7633 MonoMethodHeader *header = mono_method_get_header_checked (method, &error);
7636 method_code = g_strdup_printf ("could not parse method body due to %s", mono_error_get_message (&error));
7637 mono_error_cleanup (&error);
7638 } else if (header->code_size == 0)
7639 method_code = g_strdup ("method body is empty.");
7641 method_code = mono_disasm_code_one (NULL, method, ip, NULL);
7642 mono_cfg_set_exception_invalid_program (cfg, g_strdup_printf ("Invalid IL code in %s: %s\n", method_fname, method_code));
7643 g_free (method_fname);
7644 g_free (method_code);
7645 cfg->headers_to_free = g_slist_prepend_mempool (cfg->mempool, cfg->headers_to_free, header);
7649 emit_stloc_ir (MonoCompile *cfg, MonoInst **sp, MonoMethodHeader *header, int n)
7652 guint32 opcode = mono_type_to_regmove (cfg, header->locals [n]);
7653 if ((opcode == OP_MOVE) && cfg->cbb->last_ins == sp [0] &&
7654 ((sp [0]->opcode == OP_ICONST) || (sp [0]->opcode == OP_I8CONST))) {
7655 /* Optimize reg-reg moves away */
7657 * Can't optimize other opcodes, since sp[0] might point to
7658 * the last ins of a decomposed opcode.
7660 sp [0]->dreg = (cfg)->locals [n]->dreg;
7662 EMIT_NEW_LOCSTORE (cfg, ins, n, *sp);
7667 * ldloca inhibits many optimizations so try to get rid of it in common
7670 static inline unsigned char *
7671 emit_optimized_ldloca_ir (MonoCompile *cfg, unsigned char *ip, unsigned char *end, int size)
7681 local = read16 (ip + 2);
7685 if (ip + 6 < end && (ip [0] == CEE_PREFIX1) && (ip [1] == CEE_INITOBJ) && ip_in_bb (cfg, cfg->cbb, ip + 1)) {
7686 /* From the INITOBJ case */
7687 token = read32 (ip + 2);
7688 klass = mini_get_class (cfg->current_method, token, cfg->generic_context);
7689 CHECK_TYPELOAD (klass);
7690 type = mini_get_underlying_type (&klass->byval_arg);
7691 emit_init_local (cfg, local, type, TRUE);
7699 emit_llvmonly_virtual_call (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, int context_used, MonoInst **sp)
7701 MonoInst *icall_args [16];
7702 MonoInst *call_target, *ins, *vtable_ins;
7703 int arg_reg, this_reg, vtable_reg;
7704 gboolean is_iface = cmethod->klass->flags & TYPE_ATTRIBUTE_INTERFACE;
7705 gboolean is_gsharedvt = cfg->gsharedvt && mini_is_gsharedvt_variable_signature (fsig);
7706 gboolean variant_iface = FALSE;
7711 * In llvm-only mode, vtables contain function descriptors instead of
7712 * method addresses/trampolines.
7714 MONO_EMIT_NULL_CHECK (cfg, sp [0]->dreg);
7717 slot = mono_method_get_imt_slot (cmethod);
7719 slot = mono_method_get_vtable_index (cmethod);
7721 this_reg = sp [0]->dreg;
7723 if (is_iface && mono_class_has_variant_generic_params (cmethod->klass))
7724 variant_iface = TRUE;
7726 if (!fsig->generic_param_count && !is_iface && !is_gsharedvt) {
7728 * The simplest case, a normal virtual call.
7730 int slot_reg = alloc_preg (cfg);
7731 int addr_reg = alloc_preg (cfg);
7732 int arg_reg = alloc_preg (cfg);
7733 MonoBasicBlock *non_null_bb;
7735 vtable_reg = alloc_preg (cfg);
7736 EMIT_NEW_LOAD_MEMBASE (cfg, vtable_ins, OP_LOAD_MEMBASE, vtable_reg, this_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
7737 offset = MONO_STRUCT_OFFSET (MonoVTable, vtable) + (slot * SIZEOF_VOID_P);
7739 /* Load the vtable slot, which contains a function descriptor. */
7740 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, slot_reg, vtable_reg, offset);
7742 NEW_BBLOCK (cfg, non_null_bb);
7744 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, slot_reg, 0);
7745 cfg->cbb->last_ins->flags |= MONO_INST_LIKELY;
7746 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, non_null_bb);
7749 // FIXME: Make the wrapper use the preserveall cconv
7750 // FIXME: Use one icall per slot for small slot numbers ?
7751 icall_args [0] = vtable_ins;
7752 EMIT_NEW_ICONST (cfg, icall_args [1], slot);
7753 /* Make the icall return the vtable slot value to save some code space */
7754 ins = mono_emit_jit_icall (cfg, mono_init_vtable_slot, icall_args);
7755 ins->dreg = slot_reg;
7756 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, non_null_bb);
7759 MONO_START_BB (cfg, non_null_bb);
7760 /* Load the address + arg from the vtable slot */
7761 EMIT_NEW_LOAD_MEMBASE (cfg, call_target, OP_LOAD_MEMBASE, addr_reg, slot_reg, 0);
7762 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, arg_reg, slot_reg, SIZEOF_VOID_P);
7764 return emit_extra_arg_calli (cfg, fsig, sp, arg_reg, call_target);
7767 if (!fsig->generic_param_count && is_iface && !variant_iface && !is_gsharedvt) {
7769 * A simple interface call
7771 * We make a call through an imt slot to obtain the function descriptor we need to call.
7772 * The imt slot contains a function descriptor for a runtime function + arg.
7774 int slot_reg = alloc_preg (cfg);
7775 int addr_reg = alloc_preg (cfg);
7776 int arg_reg = alloc_preg (cfg);
7777 MonoInst *thunk_addr_ins, *thunk_arg_ins, *ftndesc_ins;
7779 vtable_reg = alloc_preg (cfg);
7780 EMIT_NEW_LOAD_MEMBASE (cfg, vtable_ins, OP_LOAD_MEMBASE, vtable_reg, this_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
7781 offset = ((gint32)slot - MONO_IMT_SIZE) * SIZEOF_VOID_P;
7784 * The slot is already initialized when the vtable is created so there is no need
7788 /* Load the imt slot, which contains a function descriptor. */
7789 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, slot_reg, vtable_reg, offset);
7791 /* Load the address + arg of the imt thunk from the imt slot */
7792 EMIT_NEW_LOAD_MEMBASE (cfg, thunk_addr_ins, OP_LOAD_MEMBASE, addr_reg, slot_reg, 0);
7793 EMIT_NEW_LOAD_MEMBASE (cfg, thunk_arg_ins, OP_LOAD_MEMBASE, arg_reg, slot_reg, SIZEOF_VOID_P);
7795 * IMT thunks in llvm-only mode are C functions which take an info argument
7796 * plus the imt method and return the ftndesc to call.
7798 icall_args [0] = thunk_arg_ins;
7799 icall_args [1] = emit_get_rgctx_method (cfg, context_used,
7800 cmethod, MONO_RGCTX_INFO_METHOD);
7801 ftndesc_ins = mono_emit_calli (cfg, helper_sig_llvmonly_imt_thunk, icall_args, thunk_addr_ins, NULL, NULL);
7803 return emit_llvmonly_calli (cfg, fsig, sp, ftndesc_ins);
7806 if ((fsig->generic_param_count || variant_iface) && !is_gsharedvt) {
7808 * This is similar to the interface case, the vtable slot points to an imt thunk which is
7809 * dynamically extended as more instantiations are discovered.
7810 * This handles generic virtual methods both on classes and interfaces.
7812 int slot_reg = alloc_preg (cfg);
7813 int addr_reg = alloc_preg (cfg);
7814 int arg_reg = alloc_preg (cfg);
7815 int ftndesc_reg = alloc_preg (cfg);
7816 MonoInst *thunk_addr_ins, *thunk_arg_ins, *ftndesc_ins;
7817 MonoBasicBlock *slowpath_bb, *end_bb;
7819 NEW_BBLOCK (cfg, slowpath_bb);
7820 NEW_BBLOCK (cfg, end_bb);
7822 vtable_reg = alloc_preg (cfg);
7823 EMIT_NEW_LOAD_MEMBASE (cfg, vtable_ins, OP_LOAD_MEMBASE, vtable_reg, this_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
7825 offset = ((gint32)slot - MONO_IMT_SIZE) * SIZEOF_VOID_P;
7827 offset = MONO_STRUCT_OFFSET (MonoVTable, vtable) + (slot * SIZEOF_VOID_P);
7829 /* Load the slot, which contains a function descriptor. */
7830 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, slot_reg, vtable_reg, offset);
7832 /* These slots are not initialized, so fall back to the slow path until they are initialized */
7833 /* That happens when mono_method_add_generic_virtual_invocation () creates an IMT thunk */
7834 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, slot_reg, 0);
7835 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, slowpath_bb);
7838 /* Same as with iface calls */
7839 EMIT_NEW_LOAD_MEMBASE (cfg, thunk_addr_ins, OP_LOAD_MEMBASE, addr_reg, slot_reg, 0);
7840 EMIT_NEW_LOAD_MEMBASE (cfg, thunk_arg_ins, OP_LOAD_MEMBASE, arg_reg, slot_reg, SIZEOF_VOID_P);
7841 icall_args [0] = thunk_arg_ins;
7842 icall_args [1] = emit_get_rgctx_method (cfg, context_used,
7843 cmethod, MONO_RGCTX_INFO_METHOD);
7844 ftndesc_ins = mono_emit_calli (cfg, helper_sig_llvmonly_imt_thunk, icall_args, thunk_addr_ins, NULL, NULL);
7845 ftndesc_ins->dreg = ftndesc_reg;
7847 * Unlike normal iface calls, these imt thunks can return NULL, i.e. when they are passed an instantiation
7848 * they don't know about yet. Fall back to the slowpath in that case.
7850 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, ftndesc_reg, 0);
7851 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, slowpath_bb);
7853 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
7856 MONO_START_BB (cfg, slowpath_bb);
7857 icall_args [0] = vtable_ins;
7858 EMIT_NEW_ICONST (cfg, icall_args [1], slot);
7859 icall_args [2] = emit_get_rgctx_method (cfg, context_used,
7860 cmethod, MONO_RGCTX_INFO_METHOD);
7862 ftndesc_ins = mono_emit_jit_icall (cfg, mono_resolve_generic_virtual_iface_call, icall_args);
7864 ftndesc_ins = mono_emit_jit_icall (cfg, mono_resolve_generic_virtual_call, icall_args);
7865 ftndesc_ins->dreg = ftndesc_reg;
7866 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
7869 MONO_START_BB (cfg, end_bb);
7870 return emit_llvmonly_calli (cfg, fsig, sp, ftndesc_ins);
7874 * Non-optimized cases
7876 icall_args [0] = sp [0];
7877 EMIT_NEW_ICONST (cfg, icall_args [1], slot);
7879 icall_args [2] = emit_get_rgctx_method (cfg, context_used,
7880 cmethod, MONO_RGCTX_INFO_METHOD);
7882 arg_reg = alloc_preg (cfg);
7883 MONO_EMIT_NEW_PCONST (cfg, arg_reg, NULL);
7884 EMIT_NEW_VARLOADA_VREG (cfg, icall_args [3], arg_reg, &mono_defaults.int_class->byval_arg);
7886 g_assert (is_gsharedvt);
7888 call_target = mono_emit_jit_icall (cfg, mono_resolve_iface_call_gsharedvt, icall_args);
7890 call_target = mono_emit_jit_icall (cfg, mono_resolve_vcall_gsharedvt, icall_args);
7893 * Pass the extra argument even if the callee doesn't receive it, most
7894 * calling conventions allow this.
7896 return emit_extra_arg_calli (cfg, fsig, sp, arg_reg, call_target);
7900 is_exception_class (MonoClass *klass)
7903 if (klass == mono_defaults.exception_class)
7905 klass = klass->parent;
7911 * is_jit_optimizer_disabled:
7913 * Determine whenever M's assembly has a DebuggableAttribute with the
7914 * IsJITOptimizerDisabled flag set.
7917 is_jit_optimizer_disabled (MonoMethod *m)
7920 MonoAssembly *ass = m->klass->image->assembly;
7921 MonoCustomAttrInfo* attrs;
7924 gboolean val = FALSE;
7927 if (ass->jit_optimizer_disabled_inited)
7928 return ass->jit_optimizer_disabled;
7930 klass = mono_class_try_get_debuggable_attribute_class ();
7934 ass->jit_optimizer_disabled = FALSE;
7935 mono_memory_barrier ();
7936 ass->jit_optimizer_disabled_inited = TRUE;
7940 attrs = mono_custom_attrs_from_assembly_checked (ass, &error);
7941 mono_error_cleanup (&error); /* FIXME don't swallow the error */
7943 for (i = 0; i < attrs->num_attrs; ++i) {
7944 MonoCustomAttrEntry *attr = &attrs->attrs [i];
7946 MonoMethodSignature *sig;
7948 if (!attr->ctor || attr->ctor->klass != klass)
7950 /* Decode the attribute. See reflection.c */
7951 p = (const char*)attr->data;
7952 g_assert (read16 (p) == 0x0001);
7955 // FIXME: Support named parameters
7956 sig = mono_method_signature (attr->ctor);
7957 if (sig->param_count != 2 || sig->params [0]->type != MONO_TYPE_BOOLEAN || sig->params [1]->type != MONO_TYPE_BOOLEAN)
7959 /* Two boolean arguments */
7963 mono_custom_attrs_free (attrs);
7966 ass->jit_optimizer_disabled = val;
7967 mono_memory_barrier ();
7968 ass->jit_optimizer_disabled_inited = TRUE;
7974 is_supported_tail_call (MonoCompile *cfg, MonoMethod *method, MonoMethod *cmethod, MonoMethodSignature *fsig, int call_opcode)
7976 gboolean supported_tail_call;
7979 supported_tail_call = mono_arch_tail_call_supported (cfg, mono_method_signature (method), mono_method_signature (cmethod));
7981 for (i = 0; i < fsig->param_count; ++i) {
7982 if (fsig->params [i]->byref || fsig->params [i]->type == MONO_TYPE_PTR || fsig->params [i]->type == MONO_TYPE_FNPTR)
7983 /* These can point to the current method's stack */
7984 supported_tail_call = FALSE;
7986 if (fsig->hasthis && cmethod->klass->valuetype)
7987 /* this might point to the current method's stack */
7988 supported_tail_call = FALSE;
7989 if (cmethod->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)
7990 supported_tail_call = FALSE;
7991 if (cfg->method->save_lmf)
7992 supported_tail_call = FALSE;
7993 if (cmethod->wrapper_type && cmethod->wrapper_type != MONO_WRAPPER_DYNAMIC_METHOD)
7994 supported_tail_call = FALSE;
7995 if (call_opcode != CEE_CALL)
7996 supported_tail_call = FALSE;
7998 /* Debugging support */
8000 if (supported_tail_call) {
8001 if (!mono_debug_count ())
8002 supported_tail_call = FALSE;
8006 return supported_tail_call;
8012 * Handle calls made to ctors from NEWOBJ opcodes.
8015 handle_ctor_call (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, int context_used,
8016 MonoInst **sp, guint8 *ip, int *inline_costs)
8018 MonoInst *vtable_arg = NULL, *callvirt_this_arg = NULL, *ins;
8020 if (cmethod->klass->valuetype && mono_class_generic_sharing_enabled (cmethod->klass) &&
8021 mono_method_is_generic_sharable (cmethod, TRUE)) {
8022 if (cmethod->is_inflated && mono_method_get_context (cmethod)->method_inst) {
8023 mono_class_vtable (cfg->domain, cmethod->klass);
8024 CHECK_TYPELOAD (cmethod->klass);
8026 vtable_arg = emit_get_rgctx_method (cfg, context_used,
8027 cmethod, MONO_RGCTX_INFO_METHOD_RGCTX);
8030 vtable_arg = emit_get_rgctx_klass (cfg, context_used,
8031 cmethod->klass, MONO_RGCTX_INFO_VTABLE);
8033 MonoVTable *vtable = mono_class_vtable (cfg->domain, cmethod->klass);
8035 CHECK_TYPELOAD (cmethod->klass);
8036 EMIT_NEW_VTABLECONST (cfg, vtable_arg, vtable);
8041 /* Avoid virtual calls to ctors if possible */
8042 if (mono_class_is_marshalbyref (cmethod->klass))
8043 callvirt_this_arg = sp [0];
8045 if (cmethod && (cfg->opt & MONO_OPT_INTRINS) && (ins = mini_emit_inst_for_ctor (cfg, cmethod, fsig, sp))) {
8046 g_assert (MONO_TYPE_IS_VOID (fsig->ret));
8047 CHECK_CFG_EXCEPTION;
8048 } else if ((cfg->opt & MONO_OPT_INLINE) && cmethod && !context_used && !vtable_arg &&
8049 mono_method_check_inlining (cfg, cmethod) &&
8050 !mono_class_is_subclass_of (cmethod->klass, mono_defaults.exception_class, FALSE)) {
8053 if ((costs = inline_method (cfg, cmethod, fsig, sp, ip, cfg->real_offset, FALSE))) {
8054 cfg->real_offset += 5;
8056 *inline_costs += costs - 5;
8058 INLINE_FAILURE ("inline failure");
8059 // FIXME-VT: Clean this up
8060 if (cfg->gsharedvt && mini_is_gsharedvt_signature (fsig))
8061 GSHAREDVT_FAILURE(*ip);
8062 mono_emit_method_call_full (cfg, cmethod, fsig, FALSE, sp, callvirt_this_arg, NULL, NULL);
8064 } else if (cfg->gsharedvt && mini_is_gsharedvt_signature (fsig)) {
8067 addr = emit_get_rgctx_gsharedvt_call (cfg, context_used, fsig, cmethod, MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE);
8069 if (cfg->llvm_only) {
8070 // FIXME: Avoid initializing vtable_arg
8071 emit_llvmonly_calli (cfg, fsig, sp, addr);
8073 mono_emit_calli (cfg, fsig, sp, addr, NULL, vtable_arg);
8075 } else if (context_used &&
8076 ((!mono_method_is_generic_sharable_full (cmethod, TRUE, FALSE, FALSE) ||
8077 !mono_class_generic_sharing_enabled (cmethod->klass)) || cfg->gsharedvt)) {
8078 MonoInst *cmethod_addr;
8080 /* Generic calls made out of gsharedvt methods cannot be patched, so use an indirect call */
8082 if (cfg->llvm_only) {
8083 MonoInst *addr = emit_get_rgctx_method (cfg, context_used, cmethod,
8084 MONO_RGCTX_INFO_GENERIC_METHOD_CODE);
8085 emit_llvmonly_calli (cfg, fsig, sp, addr);
8087 cmethod_addr = emit_get_rgctx_method (cfg, context_used,
8088 cmethod, MONO_RGCTX_INFO_GENERIC_METHOD_CODE);
8090 mono_emit_calli (cfg, fsig, sp, cmethod_addr, NULL, vtable_arg);
8093 INLINE_FAILURE ("ctor call");
8094 ins = mono_emit_method_call_full (cfg, cmethod, fsig, FALSE, sp,
8095 callvirt_this_arg, NULL, vtable_arg);
8102 emit_setret (MonoCompile *cfg, MonoInst *val)
8104 MonoType *ret_type = mini_get_underlying_type (mono_method_signature (cfg->method)->ret);
8107 if (mini_type_to_stind (cfg, ret_type) == CEE_STOBJ) {
8110 if (!cfg->vret_addr) {
8111 EMIT_NEW_VARSTORE (cfg, ins, cfg->ret, ret_type, val);
8113 EMIT_NEW_RETLOADA (cfg, ret_addr);
8115 EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STOREV_MEMBASE, ret_addr->dreg, 0, val->dreg);
8116 ins->klass = mono_class_from_mono_type (ret_type);
8119 #ifdef MONO_ARCH_SOFT_FLOAT_FALLBACK
8120 if (COMPILE_SOFT_FLOAT (cfg) && !ret_type->byref && ret_type->type == MONO_TYPE_R4) {
8121 MonoInst *iargs [1];
8125 conv = mono_emit_jit_icall (cfg, mono_fload_r4_arg, iargs);
8126 mono_arch_emit_setret (cfg, cfg->method, conv);
8128 mono_arch_emit_setret (cfg, cfg->method, val);
8131 mono_arch_emit_setret (cfg, cfg->method, val);
8137 * mono_method_to_ir:
8139 * Translate the .net IL into linear IR.
8142 mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_bblock, MonoBasicBlock *end_bblock,
8143 MonoInst *return_var, MonoInst **inline_args,
8144 guint inline_offset, gboolean is_virtual_call)
8147 MonoInst *ins, **sp, **stack_start;
8148 MonoBasicBlock *tblock = NULL, *init_localsbb = NULL;
8149 MonoSimpleBasicBlock *bb = NULL, *original_bb = NULL;
8150 MonoMethod *cmethod, *method_definition;
8151 MonoInst **arg_array;
8152 MonoMethodHeader *header;
8154 guint32 token, ins_flag;
8156 MonoClass *constrained_class = NULL;
8157 unsigned char *ip, *end, *target, *err_pos;
8158 MonoMethodSignature *sig;
8159 MonoGenericContext *generic_context = NULL;
8160 MonoGenericContainer *generic_container = NULL;
8161 MonoType **param_types;
8162 int i, n, start_new_bblock, dreg;
8163 int num_calls = 0, inline_costs = 0;
8164 int breakpoint_id = 0;
8166 GSList *class_inits = NULL;
8167 gboolean dont_verify, dont_verify_stloc, readonly = FALSE;
8169 gboolean init_locals, seq_points, skip_dead_blocks;
8170 gboolean sym_seq_points = FALSE;
8171 MonoDebugMethodInfo *minfo;
8172 MonoBitSet *seq_point_locs = NULL;
8173 MonoBitSet *seq_point_set_locs = NULL;
8175 cfg->disable_inline = is_jit_optimizer_disabled (method);
8177 /* serialization and xdomain stuff may need access to private fields and methods */
8178 dont_verify = method->klass->image->assembly->corlib_internal? TRUE: FALSE;
8179 dont_verify |= method->wrapper_type == MONO_WRAPPER_XDOMAIN_INVOKE;
8180 dont_verify |= method->wrapper_type == MONO_WRAPPER_XDOMAIN_DISPATCH;
8181 dont_verify |= method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE; /* bug #77896 */
8182 dont_verify |= method->wrapper_type == MONO_WRAPPER_COMINTEROP;
8183 dont_verify |= method->wrapper_type == MONO_WRAPPER_COMINTEROP_INVOKE;
8185 /* still some type unsafety issues in marshal wrappers... (unknown is PtrToStructure) */
8186 dont_verify_stloc = method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE;
8187 dont_verify_stloc |= method->wrapper_type == MONO_WRAPPER_UNKNOWN;
8188 dont_verify_stloc |= method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED;
8189 dont_verify_stloc |= method->wrapper_type == MONO_WRAPPER_STELEMREF;
8191 image = method->klass->image;
8192 header = mono_method_get_header_checked (method, &cfg->error);
8194 mono_cfg_set_exception (cfg, MONO_EXCEPTION_MONO_ERROR);
8195 goto exception_exit;
8197 generic_container = mono_method_get_generic_container (method);
8198 sig = mono_method_signature (method);
8199 num_args = sig->hasthis + sig->param_count;
8200 ip = (unsigned char*)header->code;
8201 cfg->cil_start = ip;
8202 end = ip + header->code_size;
8203 cfg->stat_cil_code_size += header->code_size;
8205 seq_points = cfg->gen_seq_points && cfg->method == method;
8207 if (method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED) {
8208 /* We could hit a seq point before attaching to the JIT (#8338) */
8212 if (cfg->gen_sdb_seq_points && cfg->method == method) {
8213 minfo = mono_debug_lookup_method (method);
8215 MonoSymSeqPoint *sps;
8216 int i, n_il_offsets;
8218 mono_debug_get_seq_points (minfo, NULL, NULL, NULL, &sps, &n_il_offsets);
8219 seq_point_locs = mono_bitset_mem_new (mono_mempool_alloc0 (cfg->mempool, mono_bitset_alloc_size (header->code_size, 0)), header->code_size, 0);
8220 seq_point_set_locs = mono_bitset_mem_new (mono_mempool_alloc0 (cfg->mempool, mono_bitset_alloc_size (header->code_size, 0)), header->code_size, 0);
8221 sym_seq_points = TRUE;
8222 for (i = 0; i < n_il_offsets; ++i) {
8223 if (sps [i].il_offset < header->code_size)
8224 mono_bitset_set_fast (seq_point_locs, sps [i].il_offset);
8227 } else if (!method->wrapper_type && !method->dynamic && mono_debug_image_has_debug_info (method->klass->image)) {
8228 /* Methods without line number info like auto-generated property accessors */
8229 seq_point_locs = mono_bitset_mem_new (mono_mempool_alloc0 (cfg->mempool, mono_bitset_alloc_size (header->code_size, 0)), header->code_size, 0);
8230 seq_point_set_locs = mono_bitset_mem_new (mono_mempool_alloc0 (cfg->mempool, mono_bitset_alloc_size (header->code_size, 0)), header->code_size, 0);
8231 sym_seq_points = TRUE;
8236 * Methods without init_locals set could cause asserts in various passes
8237 * (#497220). To work around this, we emit dummy initialization opcodes
8238 * (OP_DUMMY_ICONST etc.) which generate no code. These are only supported
8239 * on some platforms.
8241 if ((cfg->opt & MONO_OPT_UNSAFE) && cfg->backend->have_dummy_init)
8242 init_locals = header->init_locals;
8246 method_definition = method;
8247 while (method_definition->is_inflated) {
8248 MonoMethodInflated *imethod = (MonoMethodInflated *) method_definition;
8249 method_definition = imethod->declaring;
8252 /* SkipVerification is not allowed if core-clr is enabled */
8253 if (!dont_verify && mini_assembly_can_skip_verification (cfg->domain, method)) {
8255 dont_verify_stloc = TRUE;
8258 if (sig->is_inflated)
8259 generic_context = mono_method_get_context (method);
8260 else if (generic_container)
8261 generic_context = &generic_container->context;
8262 cfg->generic_context = generic_context;
8265 g_assert (!sig->has_type_parameters);
8267 if (sig->generic_param_count && method->wrapper_type == MONO_WRAPPER_NONE) {
8268 g_assert (method->is_inflated);
8269 g_assert (mono_method_get_context (method)->method_inst);
8271 if (method->is_inflated && mono_method_get_context (method)->method_inst)
8272 g_assert (sig->generic_param_count);
8274 if (cfg->method == method) {
8275 cfg->real_offset = 0;
8277 cfg->real_offset = inline_offset;
8280 cfg->cil_offset_to_bb = (MonoBasicBlock **)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoBasicBlock*) * header->code_size);
8281 cfg->cil_offset_to_bb_len = header->code_size;
8283 cfg->current_method = method;
8285 if (cfg->verbose_level > 2)
8286 printf ("method to IR %s\n", mono_method_full_name (method, TRUE));
8288 param_types = (MonoType **)mono_mempool_alloc (cfg->mempool, sizeof (MonoType*) * num_args);
8290 param_types [0] = method->klass->valuetype?&method->klass->this_arg:&method->klass->byval_arg;
8291 for (n = 0; n < sig->param_count; ++n)
8292 param_types [n + sig->hasthis] = sig->params [n];
8293 cfg->arg_types = param_types;
8295 cfg->dont_inline = g_list_prepend (cfg->dont_inline, method);
8296 if (cfg->method == method) {
8298 if (cfg->prof_options & MONO_PROFILE_INS_COVERAGE)
8299 cfg->coverage_info = mono_profiler_coverage_alloc (cfg->method, header->code_size);
8302 NEW_BBLOCK (cfg, start_bblock);
8303 cfg->bb_entry = start_bblock;
8304 start_bblock->cil_code = NULL;
8305 start_bblock->cil_length = 0;
8308 NEW_BBLOCK (cfg, end_bblock);
8309 cfg->bb_exit = end_bblock;
8310 end_bblock->cil_code = NULL;
8311 end_bblock->cil_length = 0;
8312 end_bblock->flags |= BB_INDIRECT_JUMP_TARGET;
8313 g_assert (cfg->num_bblocks == 2);
8315 arg_array = cfg->args;
8317 if (header->num_clauses) {
8318 cfg->spvars = g_hash_table_new (NULL, NULL);
8319 cfg->exvars = g_hash_table_new (NULL, NULL);
8321 /* handle exception clauses */
8322 for (i = 0; i < header->num_clauses; ++i) {
8323 MonoBasicBlock *try_bb;
8324 MonoExceptionClause *clause = &header->clauses [i];
8325 GET_BBLOCK (cfg, try_bb, ip + clause->try_offset);
8327 try_bb->real_offset = clause->try_offset;
8328 try_bb->try_start = TRUE;
8329 try_bb->region = ((i + 1) << 8) | clause->flags;
8330 GET_BBLOCK (cfg, tblock, ip + clause->handler_offset);
8331 tblock->real_offset = clause->handler_offset;
8332 tblock->flags |= BB_EXCEPTION_HANDLER;
8335 * Linking the try block with the EH block hinders inlining as we won't be able to
8336 * merge the bblocks from inlining and produce an artificial hole for no good reason.
8338 if (COMPILE_LLVM (cfg))
8339 link_bblock (cfg, try_bb, tblock);
8341 if (*(ip + clause->handler_offset) == CEE_POP)
8342 tblock->flags |= BB_EXCEPTION_DEAD_OBJ;
8344 if (clause->flags == MONO_EXCEPTION_CLAUSE_FINALLY ||
8345 clause->flags == MONO_EXCEPTION_CLAUSE_FILTER ||
8346 clause->flags == MONO_EXCEPTION_CLAUSE_FAULT) {
8347 MONO_INST_NEW (cfg, ins, OP_START_HANDLER);
8348 MONO_ADD_INS (tblock, ins);
8350 if (seq_points && clause->flags != MONO_EXCEPTION_CLAUSE_FINALLY && clause->flags != MONO_EXCEPTION_CLAUSE_FILTER) {
8351 /* finally clauses already have a seq point */
8352 /* seq points for filter clauses are emitted below */
8353 NEW_SEQ_POINT (cfg, ins, clause->handler_offset, TRUE);
8354 MONO_ADD_INS (tblock, ins);
8357 /* todo: is a fault block unsafe to optimize? */
8358 if (clause->flags == MONO_EXCEPTION_CLAUSE_FAULT)
8359 tblock->flags |= BB_EXCEPTION_UNSAFE;
8362 /*printf ("clause try IL_%04x to IL_%04x handler %d at IL_%04x to IL_%04x\n", clause->try_offset, clause->try_offset + clause->try_len, clause->flags, clause->handler_offset, clause->handler_offset + clause->handler_len);
8364 printf ("%s", mono_disasm_code_one (NULL, method, p, &p));
8366 /* catch and filter blocks get the exception object on the stack */
8367 if (clause->flags == MONO_EXCEPTION_CLAUSE_NONE ||
8368 clause->flags == MONO_EXCEPTION_CLAUSE_FILTER) {
8370 /* mostly like handle_stack_args (), but just sets the input args */
8371 /* printf ("handling clause at IL_%04x\n", clause->handler_offset); */
8372 tblock->in_scount = 1;
8373 tblock->in_stack = (MonoInst **)mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*));
8374 tblock->in_stack [0] = mono_create_exvar_for_offset (cfg, clause->handler_offset);
8378 #ifdef MONO_CONTEXT_SET_LLVM_EXC_REG
8379 /* The EH code passes in the exception in a register to both JITted and LLVM compiled code */
8380 if (!cfg->compile_llvm) {
8381 MONO_INST_NEW (cfg, ins, OP_GET_EX_OBJ);
8382 ins->dreg = tblock->in_stack [0]->dreg;
8383 MONO_ADD_INS (tblock, ins);
8386 MonoInst *dummy_use;
8389 * Add a dummy use for the exvar so its liveness info will be
8392 EMIT_NEW_DUMMY_USE (cfg, dummy_use, tblock->in_stack [0]);
8395 if (seq_points && clause->flags == MONO_EXCEPTION_CLAUSE_FILTER) {
8396 NEW_SEQ_POINT (cfg, ins, clause->handler_offset, TRUE);
8397 MONO_ADD_INS (tblock, ins);
8400 if (clause->flags == MONO_EXCEPTION_CLAUSE_FILTER) {
8401 GET_BBLOCK (cfg, tblock, ip + clause->data.filter_offset);
8402 tblock->flags |= BB_EXCEPTION_HANDLER;
8403 tblock->real_offset = clause->data.filter_offset;
8404 tblock->in_scount = 1;
8405 tblock->in_stack = (MonoInst **)mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*));
8406 /* The filter block shares the exvar with the handler block */
8407 tblock->in_stack [0] = mono_create_exvar_for_offset (cfg, clause->handler_offset);
8408 MONO_INST_NEW (cfg, ins, OP_START_HANDLER);
8409 MONO_ADD_INS (tblock, ins);
8413 if (clause->flags != MONO_EXCEPTION_CLAUSE_FILTER &&
8414 clause->data.catch_class &&
8416 mono_class_check_context_used (clause->data.catch_class)) {
8418 * In shared generic code with catch
8419 * clauses containing type variables
8420 * the exception handling code has to
8421 * be able to get to the rgctx.
8422 * Therefore we have to make sure that
8423 * the vtable/mrgctx argument (for
8424 * static or generic methods) or the
8425 * "this" argument (for non-static
8426 * methods) are live.
8428 if ((method->flags & METHOD_ATTRIBUTE_STATIC) ||
8429 mini_method_get_context (method)->method_inst ||
8430 method->klass->valuetype) {
8431 mono_get_vtable_var (cfg);
8433 MonoInst *dummy_use;
8435 EMIT_NEW_DUMMY_USE (cfg, dummy_use, arg_array [0]);
8440 arg_array = (MonoInst **) alloca (sizeof (MonoInst *) * num_args);
8441 cfg->cbb = start_bblock;
8442 cfg->args = arg_array;
8443 mono_save_args (cfg, sig, inline_args);
8446 /* FIRST CODE BLOCK */
8447 NEW_BBLOCK (cfg, tblock);
8448 tblock->cil_code = ip;
8452 ADD_BBLOCK (cfg, tblock);
8454 if (cfg->method == method) {
8455 breakpoint_id = mono_debugger_method_has_breakpoint (method);
8456 if (breakpoint_id) {
8457 MONO_INST_NEW (cfg, ins, OP_BREAK);
8458 MONO_ADD_INS (cfg->cbb, ins);
8462 /* we use a separate basic block for the initialization code */
8463 NEW_BBLOCK (cfg, init_localsbb);
8464 cfg->bb_init = init_localsbb;
8465 init_localsbb->real_offset = cfg->real_offset;
8466 start_bblock->next_bb = init_localsbb;
8467 init_localsbb->next_bb = cfg->cbb;
8468 link_bblock (cfg, start_bblock, init_localsbb);
8469 link_bblock (cfg, init_localsbb, cfg->cbb);
8471 cfg->cbb = init_localsbb;
8473 if (cfg->gsharedvt && cfg->method == method) {
8474 MonoGSharedVtMethodInfo *info;
8475 MonoInst *var, *locals_var;
8478 info = (MonoGSharedVtMethodInfo *)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoGSharedVtMethodInfo));
8479 info->method = cfg->method;
8480 info->count_entries = 16;
8481 info->entries = (MonoRuntimeGenericContextInfoTemplate *)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoRuntimeGenericContextInfoTemplate) * info->count_entries);
8482 cfg->gsharedvt_info = info;
8484 var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
8485 /* prevent it from being register allocated */
8486 //var->flags |= MONO_INST_VOLATILE;
8487 cfg->gsharedvt_info_var = var;
8489 ins = emit_get_rgctx_gsharedvt_method (cfg, mini_method_check_context_used (cfg, method), method, info);
8490 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, var->dreg, ins->dreg);
8492 /* Allocate locals */
8493 locals_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
8494 /* prevent it from being register allocated */
8495 //locals_var->flags |= MONO_INST_VOLATILE;
8496 cfg->gsharedvt_locals_var = locals_var;
8498 dreg = alloc_ireg (cfg);
8499 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI4_MEMBASE, dreg, var->dreg, MONO_STRUCT_OFFSET (MonoGSharedVtMethodRuntimeInfo, locals_size));
8501 MONO_INST_NEW (cfg, ins, OP_LOCALLOC);
8502 ins->dreg = locals_var->dreg;
8504 MONO_ADD_INS (cfg->cbb, ins);
8505 cfg->gsharedvt_locals_var_ins = ins;
8507 cfg->flags |= MONO_CFG_HAS_ALLOCA;
8510 ins->flags |= MONO_INST_INIT;
8514 if (mono_security_core_clr_enabled ()) {
8515 /* check if this is native code, e.g. an icall or a p/invoke */
8516 if (method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE) {
8517 MonoMethod *wrapped = mono_marshal_method_from_wrapper (method);
8519 gboolean pinvk = (wrapped->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL);
8520 gboolean icall = (wrapped->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL);
8522 /* if this ia a native call then it can only be JITted from platform code */
8523 if ((icall || pinvk) && method->klass && method->klass->image) {
8524 if (!mono_security_core_clr_is_platform_image (method->klass->image)) {
8525 MonoException *ex = icall ? mono_get_exception_security () :
8526 mono_get_exception_method_access ();
8527 emit_throw_exception (cfg, ex);
8534 CHECK_CFG_EXCEPTION;
8536 if (header->code_size == 0)
8539 if (get_basic_blocks (cfg, header, cfg->real_offset, ip, end, &err_pos)) {
8544 if (cfg->method == method)
8545 mono_debug_init_method (cfg, cfg->cbb, breakpoint_id);
8547 for (n = 0; n < header->num_locals; ++n) {
8548 if (header->locals [n]->type == MONO_TYPE_VOID && !header->locals [n]->byref)
8553 /* We force the vtable variable here for all shared methods
8554 for the possibility that they might show up in a stack
8555 trace where their exact instantiation is needed. */
8556 if (cfg->gshared && method == cfg->method) {
8557 if ((method->flags & METHOD_ATTRIBUTE_STATIC) ||
8558 mini_method_get_context (method)->method_inst ||
8559 method->klass->valuetype) {
8560 mono_get_vtable_var (cfg);
8562 /* FIXME: Is there a better way to do this?
8563 We need the variable live for the duration
8564 of the whole method. */
8565 cfg->args [0]->flags |= MONO_INST_VOLATILE;
8569 /* add a check for this != NULL to inlined methods */
8570 if (is_virtual_call) {
8573 NEW_ARGLOAD (cfg, arg_ins, 0);
8574 MONO_ADD_INS (cfg->cbb, arg_ins);
8575 MONO_EMIT_NEW_CHECK_THIS (cfg, arg_ins->dreg);
8578 skip_dead_blocks = !dont_verify;
8579 if (skip_dead_blocks) {
8580 original_bb = bb = mono_basic_block_split (method, &cfg->error);
8585 /* we use a spare stack slot in SWITCH and NEWOBJ and others */
8586 stack_start = sp = (MonoInst **)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoInst*) * (header->max_stack + 1));
8589 start_new_bblock = 0;
8591 if (cfg->method == method)
8592 cfg->real_offset = ip - header->code;
8594 cfg->real_offset = inline_offset;
8599 if (start_new_bblock) {
8600 cfg->cbb->cil_length = ip - cfg->cbb->cil_code;
8601 if (start_new_bblock == 2) {
8602 g_assert (ip == tblock->cil_code);
8604 GET_BBLOCK (cfg, tblock, ip);
8606 cfg->cbb->next_bb = tblock;
8608 start_new_bblock = 0;
8609 for (i = 0; i < cfg->cbb->in_scount; ++i) {
8610 if (cfg->verbose_level > 3)
8611 printf ("loading %d from temp %d\n", i, (int)cfg->cbb->in_stack [i]->inst_c0);
8612 EMIT_NEW_TEMPLOAD (cfg, ins, cfg->cbb->in_stack [i]->inst_c0);
8616 g_slist_free (class_inits);
8619 if ((tblock = cfg->cil_offset_to_bb [ip - cfg->cil_start]) && (tblock != cfg->cbb)) {
8620 link_bblock (cfg, cfg->cbb, tblock);
8621 if (sp != stack_start) {
8622 handle_stack_args (cfg, stack_start, sp - stack_start);
8624 CHECK_UNVERIFIABLE (cfg);
8626 cfg->cbb->next_bb = tblock;
8628 for (i = 0; i < cfg->cbb->in_scount; ++i) {
8629 if (cfg->verbose_level > 3)
8630 printf ("loading %d from temp %d\n", i, (int)cfg->cbb->in_stack [i]->inst_c0);
8631 EMIT_NEW_TEMPLOAD (cfg, ins, cfg->cbb->in_stack [i]->inst_c0);
8634 g_slist_free (class_inits);
8639 if (skip_dead_blocks) {
8640 int ip_offset = ip - header->code;
8642 if (ip_offset == bb->end)
8646 int op_size = mono_opcode_size (ip, end);
8647 g_assert (op_size > 0); /*The BB formation pass must catch all bad ops*/
8649 if (cfg->verbose_level > 3) printf ("SKIPPING DEAD OP at %x\n", ip_offset);
8651 if (ip_offset + op_size == bb->end) {
8652 MONO_INST_NEW (cfg, ins, OP_NOP);
8653 MONO_ADD_INS (cfg->cbb, ins);
8654 start_new_bblock = 1;
8662 * Sequence points are points where the debugger can place a breakpoint.
8663 * Currently, we generate these automatically at points where the IL
8666 if (seq_points && ((!sym_seq_points && (sp == stack_start)) || (sym_seq_points && mono_bitset_test_fast (seq_point_locs, ip - header->code)))) {
8668 * Make methods interruptable at the beginning, and at the targets of
8669 * backward branches.
8670 * Also, do this at the start of every bblock in methods with clauses too,
8671 * to be able to handle instructions with inprecise control flow like
8673 * Backward branches are handled at the end of method-to-ir ().
8675 gboolean intr_loc = ip == header->code || (!cfg->cbb->last_ins && cfg->header->num_clauses);
8676 gboolean sym_seq_point = sym_seq_points && mono_bitset_test_fast (seq_point_locs, ip - header->code);
8678 /* Avoid sequence points on empty IL like .volatile */
8679 // FIXME: Enable this
8680 //if (!(cfg->cbb->last_ins && cfg->cbb->last_ins->opcode == OP_SEQ_POINT)) {
8681 NEW_SEQ_POINT (cfg, ins, ip - header->code, intr_loc);
8682 if ((sp != stack_start) && !sym_seq_point)
8683 ins->flags |= MONO_INST_NONEMPTY_STACK;
8684 MONO_ADD_INS (cfg->cbb, ins);
8687 mono_bitset_set_fast (seq_point_set_locs, ip - header->code);
8690 cfg->cbb->real_offset = cfg->real_offset;
8692 if ((cfg->method == method) && cfg->coverage_info) {
8693 guint32 cil_offset = ip - header->code;
8694 cfg->coverage_info->data [cil_offset].cil_code = ip;
8696 /* TODO: Use an increment here */
8697 #if defined(TARGET_X86)
8698 MONO_INST_NEW (cfg, ins, OP_STORE_MEM_IMM);
8699 ins->inst_p0 = &(cfg->coverage_info->data [cil_offset].count);
8701 MONO_ADD_INS (cfg->cbb, ins);
8703 EMIT_NEW_PCONST (cfg, ins, &(cfg->coverage_info->data [cil_offset].count));
8704 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STORE_MEMBASE_IMM, ins->dreg, 0, 1);
8708 if (cfg->verbose_level > 3)
8709 printf ("converting (in B%d: stack: %d) %s", cfg->cbb->block_num, (int)(sp - stack_start), mono_disasm_code_one (NULL, method, ip, NULL));
8713 if (seq_points && !sym_seq_points && sp != stack_start) {
8715 * The C# compiler uses these nops to notify the JIT that it should
8716 * insert seq points.
8718 NEW_SEQ_POINT (cfg, ins, ip - header->code, FALSE);
8719 MONO_ADD_INS (cfg->cbb, ins);
8721 if (cfg->keep_cil_nops)
8722 MONO_INST_NEW (cfg, ins, OP_HARD_NOP);
8724 MONO_INST_NEW (cfg, ins, OP_NOP);
8726 MONO_ADD_INS (cfg->cbb, ins);
8729 if (should_insert_brekpoint (cfg->method)) {
8730 ins = mono_emit_jit_icall (cfg, mono_debugger_agent_user_break, NULL);
8732 MONO_INST_NEW (cfg, ins, OP_NOP);
8735 MONO_ADD_INS (cfg->cbb, ins);
8741 CHECK_STACK_OVF (1);
8742 n = (*ip)-CEE_LDARG_0;
8744 EMIT_NEW_ARGLOAD (cfg, ins, n);
8752 CHECK_STACK_OVF (1);
8753 n = (*ip)-CEE_LDLOC_0;
8755 EMIT_NEW_LOCLOAD (cfg, ins, n);
8764 n = (*ip)-CEE_STLOC_0;
8767 if (!dont_verify_stloc && target_type_is_incompatible (cfg, header->locals [n], *sp))
8769 emit_stloc_ir (cfg, sp, header, n);
8776 CHECK_STACK_OVF (1);
8779 EMIT_NEW_ARGLOAD (cfg, ins, n);
8785 CHECK_STACK_OVF (1);
8788 NEW_ARGLOADA (cfg, ins, n);
8789 MONO_ADD_INS (cfg->cbb, ins);
8799 if (!dont_verify_stloc && target_type_is_incompatible (cfg, param_types [ip [1]], *sp))
8801 EMIT_NEW_ARGSTORE (cfg, ins, n, *sp);
8806 CHECK_STACK_OVF (1);
8809 EMIT_NEW_LOCLOAD (cfg, ins, n);
8813 case CEE_LDLOCA_S: {
8814 unsigned char *tmp_ip;
8816 CHECK_STACK_OVF (1);
8817 CHECK_LOCAL (ip [1]);
8819 if ((tmp_ip = emit_optimized_ldloca_ir (cfg, ip, end, 1))) {
8825 EMIT_NEW_LOCLOADA (cfg, ins, ip [1]);
8834 CHECK_LOCAL (ip [1]);
8835 if (!dont_verify_stloc && target_type_is_incompatible (cfg, header->locals [ip [1]], *sp))
8837 emit_stloc_ir (cfg, sp, header, ip [1]);
8842 CHECK_STACK_OVF (1);
8843 EMIT_NEW_PCONST (cfg, ins, NULL);
8844 ins->type = STACK_OBJ;
8849 CHECK_STACK_OVF (1);
8850 EMIT_NEW_ICONST (cfg, ins, -1);
8863 CHECK_STACK_OVF (1);
8864 EMIT_NEW_ICONST (cfg, ins, (*ip) - CEE_LDC_I4_0);
8870 CHECK_STACK_OVF (1);
8872 EMIT_NEW_ICONST (cfg, ins, *((signed char*)ip));
8878 CHECK_STACK_OVF (1);
8879 EMIT_NEW_ICONST (cfg, ins, (gint32)read32 (ip + 1));
8885 CHECK_STACK_OVF (1);
8886 MONO_INST_NEW (cfg, ins, OP_I8CONST);
8887 ins->type = STACK_I8;
8888 ins->dreg = alloc_dreg (cfg, STACK_I8);
8890 ins->inst_l = (gint64)read64 (ip);
8891 MONO_ADD_INS (cfg->cbb, ins);
8897 gboolean use_aotconst = FALSE;
8899 #ifdef TARGET_POWERPC
8900 /* FIXME: Clean this up */
8901 if (cfg->compile_aot)
8902 use_aotconst = TRUE;
8905 /* FIXME: we should really allocate this only late in the compilation process */
8906 f = (float *)mono_domain_alloc (cfg->domain, sizeof (float));
8908 CHECK_STACK_OVF (1);
8914 EMIT_NEW_AOTCONST (cfg, cons, MONO_PATCH_INFO_R4, f);
8916 dreg = alloc_freg (cfg);
8917 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADR4_MEMBASE, dreg, cons->dreg, 0);
8918 ins->type = cfg->r4_stack_type;
8920 MONO_INST_NEW (cfg, ins, OP_R4CONST);
8921 ins->type = cfg->r4_stack_type;
8922 ins->dreg = alloc_dreg (cfg, STACK_R8);
8924 MONO_ADD_INS (cfg->cbb, ins);
8934 gboolean use_aotconst = FALSE;
8936 #ifdef TARGET_POWERPC
8937 /* FIXME: Clean this up */
8938 if (cfg->compile_aot)
8939 use_aotconst = TRUE;
8942 /* FIXME: we should really allocate this only late in the compilation process */
8943 d = (double *)mono_domain_alloc (cfg->domain, sizeof (double));
8945 CHECK_STACK_OVF (1);
8951 EMIT_NEW_AOTCONST (cfg, cons, MONO_PATCH_INFO_R8, d);
8953 dreg = alloc_freg (cfg);
8954 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADR8_MEMBASE, dreg, cons->dreg, 0);
8955 ins->type = STACK_R8;
8957 MONO_INST_NEW (cfg, ins, OP_R8CONST);
8958 ins->type = STACK_R8;
8959 ins->dreg = alloc_dreg (cfg, STACK_R8);
8961 MONO_ADD_INS (cfg->cbb, ins);
8970 MonoInst *temp, *store;
8972 CHECK_STACK_OVF (1);
8976 temp = mono_compile_create_var (cfg, type_from_stack_type (ins), OP_LOCAL);
8977 EMIT_NEW_TEMPSTORE (cfg, store, temp->inst_c0, ins);
8979 EMIT_NEW_TEMPLOAD (cfg, ins, temp->inst_c0);
8982 EMIT_NEW_TEMPLOAD (cfg, ins, temp->inst_c0);
8995 if (sp [0]->type == STACK_R8)
8996 /* we need to pop the value from the x86 FP stack */
8997 MONO_EMIT_NEW_UNALU (cfg, OP_X86_FPOP, -1, sp [0]->dreg);
9002 MonoMethodSignature *fsig;
9005 INLINE_FAILURE ("jmp");
9006 GSHAREDVT_FAILURE (*ip);
9009 if (stack_start != sp)
9011 token = read32 (ip + 1);
9012 /* FIXME: check the signature matches */
9013 cmethod = mini_get_method (cfg, method, token, NULL, generic_context);
9016 if (cfg->gshared && mono_method_check_context_used (cmethod))
9017 GENERIC_SHARING_FAILURE (CEE_JMP);
9019 emit_instrumentation_call (cfg, mono_profiler_method_leave);
9021 fsig = mono_method_signature (cmethod);
9022 n = fsig->param_count + fsig->hasthis;
9023 if (cfg->llvm_only) {
9026 args = (MonoInst **)mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*) * n);
9027 for (i = 0; i < n; ++i)
9028 EMIT_NEW_ARGLOAD (cfg, args [i], i);
9029 ins = mono_emit_method_call_full (cfg, cmethod, fsig, TRUE, args, NULL, NULL, NULL);
9031 * The code in mono-basic-block.c treats the rest of the code as dead, but we
9032 * have to emit a normal return since llvm expects it.
9035 emit_setret (cfg, ins);
9036 MONO_INST_NEW (cfg, ins, OP_BR);
9037 ins->inst_target_bb = end_bblock;
9038 MONO_ADD_INS (cfg->cbb, ins);
9039 link_bblock (cfg, cfg->cbb, end_bblock);
9042 } else if (cfg->backend->have_op_tail_call) {
9043 /* Handle tail calls similarly to calls */
9046 MONO_INST_NEW_CALL (cfg, call, OP_TAILCALL);
9047 call->method = cmethod;
9048 call->tail_call = TRUE;
9049 call->signature = mono_method_signature (cmethod);
9050 call->args = (MonoInst **)mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*) * n);
9051 call->inst.inst_p0 = cmethod;
9052 for (i = 0; i < n; ++i)
9053 EMIT_NEW_ARGLOAD (cfg, call->args [i], i);
9055 mono_arch_emit_call (cfg, call);
9056 cfg->param_area = MAX(cfg->param_area, call->stack_usage);
9057 MONO_ADD_INS (cfg->cbb, (MonoInst*)call);
9059 for (i = 0; i < num_args; ++i)
9060 /* Prevent arguments from being optimized away */
9061 arg_array [i]->flags |= MONO_INST_VOLATILE;
9063 MONO_INST_NEW_CALL (cfg, call, OP_JMP);
9064 ins = (MonoInst*)call;
9065 ins->inst_p0 = cmethod;
9066 MONO_ADD_INS (cfg->cbb, ins);
9070 start_new_bblock = 1;
9075 MonoMethodSignature *fsig;
9078 token = read32 (ip + 1);
9082 //GSHAREDVT_FAILURE (*ip);
9087 fsig = mini_get_signature (method, token, generic_context);
9089 if (method->dynamic && fsig->pinvoke) {
9093 * This is a call through a function pointer using a pinvoke
9094 * signature. Have to create a wrapper and call that instead.
9095 * FIXME: This is very slow, need to create a wrapper at JIT time
9096 * instead based on the signature.
9098 EMIT_NEW_IMAGECONST (cfg, args [0], method->klass->image);
9099 EMIT_NEW_PCONST (cfg, args [1], fsig);
9101 addr = mono_emit_jit_icall (cfg, mono_get_native_calli_wrapper, args);
9104 n = fsig->param_count + fsig->hasthis;
9108 //g_assert (!virtual_ || fsig->hasthis);
9112 inline_costs += 10 * num_calls++;
9115 * Making generic calls out of gsharedvt methods.
9116 * This needs to be used for all generic calls, not just ones with a gsharedvt signature, to avoid
9117 * patching gshared method addresses into a gsharedvt method.
9119 if (cfg->gsharedvt && mini_is_gsharedvt_signature (fsig)) {
9121 * We pass the address to the gsharedvt trampoline in the rgctx reg
9123 MonoInst *callee = addr;
9125 if (method->wrapper_type != MONO_WRAPPER_DELEGATE_INVOKE)
9127 GSHAREDVT_FAILURE (*ip);
9131 GSHAREDVT_FAILURE (*ip);
9133 addr = emit_get_rgctx_sig (cfg, context_used,
9134 fsig, MONO_RGCTX_INFO_SIG_GSHAREDVT_OUT_TRAMPOLINE_CALLI);
9135 ins = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr, NULL, callee);
9139 /* Prevent inlining of methods with indirect calls */
9140 INLINE_FAILURE ("indirect call");
9142 if (addr->opcode == OP_PCONST || addr->opcode == OP_AOTCONST || addr->opcode == OP_GOT_ENTRY) {
9143 MonoJumpInfoType info_type;
9147 * Instead of emitting an indirect call, emit a direct call
9148 * with the contents of the aotconst as the patch info.
9150 if (addr->opcode == OP_PCONST || addr->opcode == OP_AOTCONST) {
9151 info_type = (MonoJumpInfoType)addr->inst_c1;
9152 info_data = addr->inst_p0;
9154 info_type = (MonoJumpInfoType)addr->inst_right->inst_c1;
9155 info_data = addr->inst_right->inst_left;
9158 if (info_type == MONO_PATCH_INFO_ICALL_ADDR || info_type == MONO_PATCH_INFO_JIT_ICALL_ADDR) {
9159 ins = (MonoInst*)mono_emit_abs_call (cfg, info_type, info_data, fsig, sp);
9164 ins = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr, NULL, NULL);
9168 /* End of call, INS should contain the result of the call, if any */
9170 if (!MONO_TYPE_IS_VOID (fsig->ret)) {
9172 *sp++ = mono_emit_widen_call_res (cfg, ins, fsig);
9175 CHECK_CFG_EXCEPTION;
9179 constrained_class = NULL;
9183 case CEE_CALLVIRT: {
9184 MonoInst *addr = NULL;
9185 MonoMethodSignature *fsig = NULL;
9187 int virtual_ = *ip == CEE_CALLVIRT;
9188 gboolean pass_imt_from_rgctx = FALSE;
9189 MonoInst *imt_arg = NULL;
9190 MonoInst *keep_this_alive = NULL;
9191 gboolean pass_vtable = FALSE;
9192 gboolean pass_mrgctx = FALSE;
9193 MonoInst *vtable_arg = NULL;
9194 gboolean check_this = FALSE;
9195 gboolean supported_tail_call = FALSE;
9196 gboolean tail_call = FALSE;
9197 gboolean need_seq_point = FALSE;
9198 guint32 call_opcode = *ip;
9199 gboolean emit_widen = TRUE;
9200 gboolean push_res = TRUE;
9201 gboolean skip_ret = FALSE;
9202 gboolean delegate_invoke = FALSE;
9203 gboolean direct_icall = FALSE;
9204 gboolean constrained_partial_call = FALSE;
9205 MonoMethod *cil_method;
9208 token = read32 (ip + 1);
9212 cmethod = mini_get_method (cfg, method, token, NULL, generic_context);
9215 cil_method = cmethod;
9217 if (constrained_class) {
9218 if ((constrained_class->byval_arg.type == MONO_TYPE_VAR || constrained_class->byval_arg.type == MONO_TYPE_MVAR) && cfg->gshared) {
9219 if (!mini_is_gsharedvt_klass (constrained_class)) {
9220 g_assert (!cmethod->klass->valuetype);
9221 if (!mini_type_is_reference (&constrained_class->byval_arg))
9222 constrained_partial_call = TRUE;
9226 if (method->wrapper_type != MONO_WRAPPER_NONE) {
9227 if (cfg->verbose_level > 2)
9228 printf ("DM Constrained call to %s\n", mono_type_get_full_name (constrained_class));
9229 if (!((constrained_class->byval_arg.type == MONO_TYPE_VAR ||
9230 constrained_class->byval_arg.type == MONO_TYPE_MVAR) &&
9232 cmethod = mono_get_method_constrained_with_method (image, cil_method, constrained_class, generic_context, &cfg->error);
9236 if (cfg->verbose_level > 2)
9237 printf ("Constrained call to %s\n", mono_type_get_full_name (constrained_class));
9239 if ((constrained_class->byval_arg.type == MONO_TYPE_VAR || constrained_class->byval_arg.type == MONO_TYPE_MVAR) && cfg->gshared) {
9241 * This is needed since get_method_constrained can't find
9242 * the method in klass representing a type var.
9243 * The type var is guaranteed to be a reference type in this
9246 if (!mini_is_gsharedvt_klass (constrained_class))
9247 g_assert (!cmethod->klass->valuetype);
9249 cmethod = mono_get_method_constrained_checked (image, token, constrained_class, generic_context, &cil_method, &cfg->error);
9255 if (!cmethod || mono_loader_get_last_error ()) {
9256 if (mono_loader_get_last_error ()) {
9257 mono_cfg_set_exception (cfg, MONO_EXCEPTION_MONO_ERROR);
9258 mono_error_set_from_loader_error (&cfg->error);
9264 if (!dont_verify && !cfg->skip_visibility) {
9265 MonoMethod *target_method = cil_method;
9266 if (method->is_inflated) {
9267 target_method = mini_get_method_allow_open (method, token, NULL, &(mono_method_get_generic_container (method_definition)->context), &cfg->error);
9270 if (!mono_method_can_access_method (method_definition, target_method) &&
9271 !mono_method_can_access_method (method, cil_method))
9272 METHOD_ACCESS_FAILURE (method, cil_method);
9275 if (mono_security_core_clr_enabled ())
9276 ensure_method_is_allowed_to_call_method (cfg, method, cil_method);
9278 if (!virtual_ && (cmethod->flags & METHOD_ATTRIBUTE_ABSTRACT))
9279 /* MS.NET seems to silently convert this to a callvirt */
9284 * MS.NET accepts non virtual calls to virtual final methods of transparent proxy classes and
9285 * converts to a callvirt.
9287 * tests/bug-515884.il is an example of this behavior
9289 const int test_flags = METHOD_ATTRIBUTE_VIRTUAL | METHOD_ATTRIBUTE_FINAL | METHOD_ATTRIBUTE_STATIC;
9290 const int expected_flags = METHOD_ATTRIBUTE_VIRTUAL | METHOD_ATTRIBUTE_FINAL;
9291 if (!virtual_ && mono_class_is_marshalbyref (cmethod->klass) && (cmethod->flags & test_flags) == expected_flags && cfg->method->wrapper_type == MONO_WRAPPER_NONE)
9295 if (!cmethod->klass->inited)
9296 if (!mono_class_init (cmethod->klass))
9297 TYPE_LOAD_ERROR (cmethod->klass);
9299 fsig = mono_method_signature (cmethod);
9302 if (cmethod->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL &&
9303 mini_class_is_system_array (cmethod->klass)) {
9304 array_rank = cmethod->klass->rank;
9305 } else if ((cmethod->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) && icall_is_direct_callable (cfg, cmethod)) {
9306 direct_icall = TRUE;
9307 } else if (fsig->pinvoke) {
9308 MonoMethod *wrapper = mono_marshal_get_native_wrapper (cmethod, TRUE, cfg->compile_aot);
9309 fsig = mono_method_signature (wrapper);
9310 } else if (constrained_class) {
9312 fsig = mono_method_get_signature_checked (cmethod, image, token, generic_context, &cfg->error);
9316 if (cfg->llvm_only && !cfg->method->wrapper_type)
9317 cfg->signatures = g_slist_prepend_mempool (cfg->mempool, cfg->signatures, fsig);
9319 /* See code below */
9320 if (cmethod->klass == mono_defaults.monitor_class && !strcmp (cmethod->name, "Enter") && mono_method_signature (cmethod)->param_count == 1) {
9321 MonoBasicBlock *tbb;
9323 GET_BBLOCK (cfg, tbb, ip + 5);
9324 if (tbb->try_start && MONO_REGION_FLAGS(tbb->region) == MONO_EXCEPTION_CLAUSE_FINALLY) {
9326 * We want to extend the try block to cover the call, but we can't do it if the
9327 * call is made directly since its followed by an exception check.
9329 direct_icall = FALSE;
9333 mono_save_token_info (cfg, image, token, cil_method);
9335 if (!(seq_point_locs && mono_bitset_test_fast (seq_point_locs, ip + 5 - header->code)))
9336 need_seq_point = TRUE;
9338 /* Don't support calls made using type arguments for now */
9340 if (cfg->gsharedvt) {
9341 if (mini_is_gsharedvt_signature (fsig))
9342 GSHAREDVT_FAILURE (*ip);
9346 if (cmethod->string_ctor && method->wrapper_type != MONO_WRAPPER_RUNTIME_INVOKE)
9347 g_assert_not_reached ();
9349 n = fsig->param_count + fsig->hasthis;
9351 if (!cfg->gshared && cmethod->klass->generic_container)
9355 g_assert (!mono_method_check_context_used (cmethod));
9359 //g_assert (!virtual_ || fsig->hasthis);
9364 * We have the `constrained.' prefix opcode.
9366 if (constrained_class) {
9367 if (mini_is_gsharedvt_klass (constrained_class)) {
9368 if ((cmethod->klass != mono_defaults.object_class) && constrained_class->valuetype && cmethod->klass->valuetype) {
9369 /* The 'Own method' case below */
9370 } else if (cmethod->klass->image != mono_defaults.corlib && !(cmethod->klass->flags & TYPE_ATTRIBUTE_INTERFACE) && !cmethod->klass->valuetype) {
9371 /* 'The type parameter is instantiated as a reference type' case below. */
9373 ins = handle_constrained_gsharedvt_call (cfg, cmethod, fsig, sp, constrained_class, &emit_widen);
9374 CHECK_CFG_EXCEPTION;
9380 if (constrained_partial_call) {
9381 gboolean need_box = TRUE;
9384 * The receiver is a valuetype, but the exact type is not known at compile time. This means the
9385 * called method is not known at compile time either. The called method could end up being
9386 * one of the methods on the parent classes (object/valuetype/enum), in which case we need
9387 * to box the receiver.
9388 * A simple solution would be to box always and make a normal virtual call, but that would
9389 * be bad performance wise.
9391 if (cmethod->klass->flags & TYPE_ATTRIBUTE_INTERFACE && cmethod->klass->generic_class) {
9393 * The parent classes implement no generic interfaces, so the called method will be a vtype method, so no boxing neccessary.
9398 if (!(cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL) && (cmethod->klass == mono_defaults.object_class || cmethod->klass == mono_defaults.enum_class->parent || cmethod->klass == mono_defaults.enum_class)) {
9399 /* The called method is not virtual, i.e. Object:GetType (), the receiver is a vtype, has to box */
9400 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &constrained_class->byval_arg, sp [0]->dreg, 0);
9401 ins->klass = constrained_class;
9402 sp [0] = handle_box (cfg, ins, constrained_class, mono_class_check_context_used (constrained_class));
9403 CHECK_CFG_EXCEPTION;
9404 } else if (need_box) {
9406 MonoBasicBlock *is_ref_bb, *end_bb;
9407 MonoInst *nonbox_call;
9410 * Determine at runtime whenever the called method is defined on object/valuetype/enum, and emit a boxing call
9412 * FIXME: It is possible to inline the called method in a lot of cases, i.e. for T_INT,
9413 * the no-box case goes to a method in Int32, while the box case goes to a method in Enum.
9415 addr = emit_get_rgctx_virt_method (cfg, mono_class_check_context_used (constrained_class), constrained_class, cmethod, MONO_RGCTX_INFO_VIRT_METHOD_CODE);
9417 NEW_BBLOCK (cfg, is_ref_bb);
9418 NEW_BBLOCK (cfg, end_bb);
9420 box_type = emit_get_rgctx_virt_method (cfg, mono_class_check_context_used (constrained_class), constrained_class, cmethod, MONO_RGCTX_INFO_VIRT_METHOD_BOX_TYPE);
9421 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, box_type->dreg, MONO_GSHAREDVT_BOX_TYPE_REF);
9422 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, is_ref_bb);
9425 nonbox_call = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr, NULL, NULL);
9427 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
9430 MONO_START_BB (cfg, is_ref_bb);
9431 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &constrained_class->byval_arg, sp [0]->dreg, 0);
9432 ins->klass = constrained_class;
9433 sp [0] = handle_box (cfg, ins, constrained_class, mono_class_check_context_used (constrained_class));
9434 ins = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr, NULL, NULL);
9436 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
9438 MONO_START_BB (cfg, end_bb);
9441 nonbox_call->dreg = ins->dreg;
9444 g_assert (cmethod->klass->flags & TYPE_ATTRIBUTE_INTERFACE);
9445 addr = emit_get_rgctx_virt_method (cfg, mono_class_check_context_used (constrained_class), constrained_class, cmethod, MONO_RGCTX_INFO_VIRT_METHOD_CODE);
9446 ins = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr, NULL, NULL);
9449 } else if (constrained_class->valuetype && (cmethod->klass == mono_defaults.object_class || cmethod->klass == mono_defaults.enum_class->parent || cmethod->klass == mono_defaults.enum_class)) {
9451 * The type parameter is instantiated as a valuetype,
9452 * but that type doesn't override the method we're
9453 * calling, so we need to box `this'.
9455 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &constrained_class->byval_arg, sp [0]->dreg, 0);
9456 ins->klass = constrained_class;
9457 sp [0] = handle_box (cfg, ins, constrained_class, mono_class_check_context_used (constrained_class));
9458 CHECK_CFG_EXCEPTION;
9459 } else if (!constrained_class->valuetype) {
9460 int dreg = alloc_ireg_ref (cfg);
9463 * The type parameter is instantiated as a reference
9464 * type. We have a managed pointer on the stack, so
9465 * we need to dereference it here.
9467 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, dreg, sp [0]->dreg, 0);
9468 ins->type = STACK_OBJ;
9471 if (cmethod->klass->valuetype) {
9474 /* Interface method */
9477 mono_class_setup_vtable (constrained_class);
9478 CHECK_TYPELOAD (constrained_class);
9479 ioffset = mono_class_interface_offset (constrained_class, cmethod->klass);
9481 TYPE_LOAD_ERROR (constrained_class);
9482 slot = mono_method_get_vtable_slot (cmethod);
9484 TYPE_LOAD_ERROR (cmethod->klass);
9485 cmethod = constrained_class->vtable [ioffset + slot];
9487 if (cmethod->klass == mono_defaults.enum_class) {
9488 /* Enum implements some interfaces, so treat this as the first case */
9489 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &constrained_class->byval_arg, sp [0]->dreg, 0);
9490 ins->klass = constrained_class;
9491 sp [0] = handle_box (cfg, ins, constrained_class, mono_class_check_context_used (constrained_class));
9492 CHECK_CFG_EXCEPTION;
9497 constrained_class = NULL;
9500 if (check_call_signature (cfg, fsig, sp))
9503 if ((cmethod->klass->parent == mono_defaults.multicastdelegate_class) && !strcmp (cmethod->name, "Invoke"))
9504 delegate_invoke = TRUE;
9506 if ((cfg->opt & MONO_OPT_INTRINS) && (ins = mini_emit_inst_for_sharable_method (cfg, cmethod, fsig, sp))) {
9507 if (!MONO_TYPE_IS_VOID (fsig->ret)) {
9508 type_to_eval_stack_type ((cfg), fsig->ret, ins);
9516 * If the callee is a shared method, then its static cctor
9517 * might not get called after the call was patched.
9519 if (cfg->gshared && cmethod->klass != method->klass && cmethod->klass->generic_class && mono_method_is_generic_sharable (cmethod, TRUE) && mono_class_needs_cctor_run (cmethod->klass, method)) {
9520 emit_class_init (cfg, cmethod->klass);
9521 CHECK_TYPELOAD (cmethod->klass);
9524 check_method_sharing (cfg, cmethod, &pass_vtable, &pass_mrgctx);
9527 MonoGenericContext *cmethod_context = mono_method_get_context (cmethod);
9529 context_used = mini_method_check_context_used (cfg, cmethod);
9531 if (context_used && (cmethod->klass->flags & TYPE_ATTRIBUTE_INTERFACE)) {
9532 /* Generic method interface
9533 calls are resolved via a
9534 helper function and don't
9536 if (!cmethod_context || !cmethod_context->method_inst)
9537 pass_imt_from_rgctx = TRUE;
9541 * If a shared method calls another
9542 * shared method then the caller must
9543 * have a generic sharing context
9544 * because the magic trampoline
9545 * requires it. FIXME: We shouldn't
9546 * have to force the vtable/mrgctx
9547 * variable here. Instead there
9548 * should be a flag in the cfg to
9549 * request a generic sharing context.
9552 ((method->flags & METHOD_ATTRIBUTE_STATIC) || method->klass->valuetype))
9553 mono_get_vtable_var (cfg);
9558 vtable_arg = emit_get_rgctx_klass (cfg, context_used, cmethod->klass, MONO_RGCTX_INFO_VTABLE);
9560 MonoVTable *vtable = mono_class_vtable (cfg->domain, cmethod->klass);
9562 CHECK_TYPELOAD (cmethod->klass);
9563 EMIT_NEW_VTABLECONST (cfg, vtable_arg, vtable);
9568 g_assert (!vtable_arg);
9570 if (!cfg->compile_aot) {
9572 * emit_get_rgctx_method () calls mono_class_vtable () so check
9573 * for type load errors before.
9575 mono_class_setup_vtable (cmethod->klass);
9576 CHECK_TYPELOAD (cmethod->klass);
9579 vtable_arg = emit_get_rgctx_method (cfg, context_used, cmethod, MONO_RGCTX_INFO_METHOD_RGCTX);
9581 /* !marshalbyref is needed to properly handle generic methods + remoting */
9582 if ((!(cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL) ||
9583 MONO_METHOD_IS_FINAL (cmethod)) &&
9584 !mono_class_is_marshalbyref (cmethod->klass)) {
9591 if (pass_imt_from_rgctx) {
9592 g_assert (!pass_vtable);
9594 imt_arg = emit_get_rgctx_method (cfg, context_used,
9595 cmethod, MONO_RGCTX_INFO_METHOD);
9599 MONO_EMIT_NEW_CHECK_THIS (cfg, sp [0]->dreg);
9601 /* Calling virtual generic methods */
9602 if (virtual_ && (cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL) &&
9603 !(MONO_METHOD_IS_FINAL (cmethod) &&
9604 cmethod->wrapper_type != MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK) &&
9605 fsig->generic_param_count &&
9606 !(cfg->gsharedvt && mini_is_gsharedvt_signature (fsig)) &&
9608 MonoInst *this_temp, *this_arg_temp, *store;
9609 MonoInst *iargs [4];
9611 g_assert (fsig->is_inflated);
9613 /* Prevent inlining of methods that contain indirect calls */
9614 INLINE_FAILURE ("virtual generic call");
9616 if (cfg->gsharedvt && mini_is_gsharedvt_signature (fsig))
9617 GSHAREDVT_FAILURE (*ip);
9619 if (cfg->backend->have_generalized_imt_thunk && cfg->backend->gshared_supported && cmethod->wrapper_type == MONO_WRAPPER_NONE) {
9620 g_assert (!imt_arg);
9622 g_assert (cmethod->is_inflated);
9623 imt_arg = emit_get_rgctx_method (cfg, context_used,
9624 cmethod, MONO_RGCTX_INFO_METHOD);
9625 ins = mono_emit_method_call_full (cfg, cmethod, fsig, FALSE, sp, sp [0], imt_arg, NULL);
9627 this_temp = mono_compile_create_var (cfg, type_from_stack_type (sp [0]), OP_LOCAL);
9628 NEW_TEMPSTORE (cfg, store, this_temp->inst_c0, sp [0]);
9629 MONO_ADD_INS (cfg->cbb, store);
9631 /* FIXME: This should be a managed pointer */
9632 this_arg_temp = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
9634 EMIT_NEW_TEMPLOAD (cfg, iargs [0], this_temp->inst_c0);
9635 iargs [1] = emit_get_rgctx_method (cfg, context_used,
9636 cmethod, MONO_RGCTX_INFO_METHOD);
9637 EMIT_NEW_TEMPLOADA (cfg, iargs [2], this_arg_temp->inst_c0);
9638 addr = mono_emit_jit_icall (cfg,
9639 mono_helper_compile_generic_method, iargs);
9641 EMIT_NEW_TEMPLOAD (cfg, sp [0], this_arg_temp->inst_c0);
9643 ins = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr, NULL, NULL);
9650 * Implement a workaround for the inherent races involved in locking:
9656 * If a thread abort happens between the call to Monitor.Enter () and the start of the
9657 * try block, the Exit () won't be executed, see:
9658 * http://www.bluebytesoftware.com/blog/2007/01/30/MonitorEnterThreadAbortsAndOrphanedLocks.aspx
9659 * To work around this, we extend such try blocks to include the last x bytes
9660 * of the Monitor.Enter () call.
9662 if (cmethod->klass == mono_defaults.monitor_class && !strcmp (cmethod->name, "Enter") && mono_method_signature (cmethod)->param_count == 1) {
9663 MonoBasicBlock *tbb;
9665 GET_BBLOCK (cfg, tbb, ip + 5);
9667 * Only extend try blocks with a finally, to avoid catching exceptions thrown
9668 * from Monitor.Enter like ArgumentNullException.
9670 if (tbb->try_start && MONO_REGION_FLAGS(tbb->region) == MONO_EXCEPTION_CLAUSE_FINALLY) {
9671 /* Mark this bblock as needing to be extended */
9672 tbb->extend_try_block = TRUE;
9676 /* Conversion to a JIT intrinsic */
9677 if ((cfg->opt & MONO_OPT_INTRINS) && (ins = mini_emit_inst_for_method (cfg, cmethod, fsig, sp))) {
9678 if (!MONO_TYPE_IS_VOID (fsig->ret)) {
9679 type_to_eval_stack_type ((cfg), fsig->ret, ins);
9686 if ((cfg->opt & MONO_OPT_INLINE) &&
9687 (!virtual_ || !(cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL) || MONO_METHOD_IS_FINAL (cmethod)) &&
9688 mono_method_check_inlining (cfg, cmethod)) {
9690 gboolean always = FALSE;
9692 if ((cmethod->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
9693 (cmethod->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)) {
9694 /* Prevent inlining of methods that call wrappers */
9695 INLINE_FAILURE ("wrapper call");
9696 cmethod = mono_marshal_get_native_wrapper (cmethod, TRUE, FALSE);
9700 costs = inline_method (cfg, cmethod, fsig, sp, ip, cfg->real_offset, always);
9702 cfg->real_offset += 5;
9704 if (!MONO_TYPE_IS_VOID (fsig->ret)) {
9705 /* *sp is already set by inline_method */
9710 inline_costs += costs;
9716 /* Tail recursion elimination */
9717 if ((cfg->opt & MONO_OPT_TAILC) && call_opcode == CEE_CALL && cmethod == method && ip [5] == CEE_RET && !vtable_arg) {
9718 gboolean has_vtargs = FALSE;
9721 /* Prevent inlining of methods with tail calls (the call stack would be altered) */
9722 INLINE_FAILURE ("tail call");
9724 /* keep it simple */
9725 for (i = fsig->param_count - 1; i >= 0; i--) {
9726 if (MONO_TYPE_ISSTRUCT (mono_method_signature (cmethod)->params [i]))
9731 for (i = 0; i < n; ++i)
9732 EMIT_NEW_ARGSTORE (cfg, ins, i, sp [i]);
9733 MONO_INST_NEW (cfg, ins, OP_BR);
9734 MONO_ADD_INS (cfg->cbb, ins);
9735 tblock = start_bblock->out_bb [0];
9736 link_bblock (cfg, cfg->cbb, tblock);
9737 ins->inst_target_bb = tblock;
9738 start_new_bblock = 1;
9740 /* skip the CEE_RET, too */
9741 if (ip_in_bb (cfg, cfg->cbb, ip + 5))
9748 inline_costs += 10 * num_calls++;
9751 * Making generic calls out of gsharedvt methods.
9752 * This needs to be used for all generic calls, not just ones with a gsharedvt signature, to avoid
9753 * patching gshared method addresses into a gsharedvt method.
9755 if (cfg->gsharedvt && (mini_is_gsharedvt_signature (fsig) || cmethod->is_inflated || cmethod->klass->generic_class) &&
9756 !(cmethod->klass->rank && cmethod->klass->byval_arg.type != MONO_TYPE_SZARRAY) &&
9757 (!(cfg->llvm_only && virtual_ && (cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL)))) {
9758 MonoRgctxInfoType info_type;
9761 //if (cmethod->klass->flags & TYPE_ATTRIBUTE_INTERFACE)
9762 //GSHAREDVT_FAILURE (*ip);
9763 // disable for possible remoting calls
9764 if (fsig->hasthis && (mono_class_is_marshalbyref (method->klass) || method->klass == mono_defaults.object_class))
9765 GSHAREDVT_FAILURE (*ip);
9766 if (fsig->generic_param_count) {
9767 /* virtual generic call */
9768 g_assert (!imt_arg);
9769 /* Same as the virtual generic case above */
9770 imt_arg = emit_get_rgctx_method (cfg, context_used,
9771 cmethod, MONO_RGCTX_INFO_METHOD);
9772 /* This is not needed, as the trampoline code will pass one, and it might be passed in the same reg as the imt arg */
9774 } else if ((cmethod->klass->flags & TYPE_ATTRIBUTE_INTERFACE) && !imt_arg) {
9775 /* This can happen when we call a fully instantiated iface method */
9776 imt_arg = emit_get_rgctx_method (cfg, context_used,
9777 cmethod, MONO_RGCTX_INFO_METHOD);
9782 if ((cmethod->klass->parent == mono_defaults.multicastdelegate_class) && (!strcmp (cmethod->name, "Invoke")))
9783 keep_this_alive = sp [0];
9785 if (virtual_ && (cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL))
9786 info_type = MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE_VIRT;
9788 info_type = MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE;
9789 addr = emit_get_rgctx_gsharedvt_call (cfg, context_used, fsig, cmethod, info_type);
9791 if (cfg->llvm_only) {
9792 // FIXME: Avoid initializing vtable_arg
9793 ins = emit_llvmonly_calli (cfg, fsig, sp, addr);
9795 ins = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr, imt_arg, vtable_arg);
9800 /* Generic sharing */
9803 * Use this if the callee is gsharedvt sharable too, since
9804 * at runtime we might find an instantiation so the call cannot
9805 * be patched (the 'no_patch' code path in mini-trampolines.c).
9807 if (context_used && !imt_arg && !array_rank && !delegate_invoke &&
9808 (!mono_method_is_generic_sharable_full (cmethod, TRUE, FALSE, FALSE) ||
9809 !mono_class_generic_sharing_enabled (cmethod->klass)) &&
9810 (!virtual_ || MONO_METHOD_IS_FINAL (cmethod) ||
9811 !(cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL))) {
9812 INLINE_FAILURE ("gshared");
9814 g_assert (cfg->gshared && cmethod);
9818 * We are compiling a call to a
9819 * generic method from shared code,
9820 * which means that we have to look up
9821 * the method in the rgctx and do an
9825 MONO_EMIT_NEW_CHECK_THIS (cfg, sp [0]->dreg);
9827 if (cfg->llvm_only) {
9828 if (cfg->gsharedvt && mini_is_gsharedvt_variable_signature (fsig))
9829 addr = emit_get_rgctx_method (cfg, context_used, cmethod, MONO_RGCTX_INFO_GSHAREDVT_OUT_WRAPPER);
9831 addr = emit_get_rgctx_method (cfg, context_used, cmethod, MONO_RGCTX_INFO_GENERIC_METHOD_CODE);
9832 // FIXME: Avoid initializing imt_arg/vtable_arg
9833 ins = emit_llvmonly_calli (cfg, fsig, sp, addr);
9835 addr = emit_get_rgctx_method (cfg, context_used, cmethod, MONO_RGCTX_INFO_GENERIC_METHOD_CODE);
9836 ins = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr, imt_arg, vtable_arg);
9841 /* Direct calls to icalls */
9843 MonoMethod *wrapper;
9846 /* Inline the wrapper */
9847 wrapper = mono_marshal_get_native_wrapper (cmethod, TRUE, cfg->compile_aot);
9849 costs = inline_method (cfg, wrapper, fsig, sp, ip, cfg->real_offset, TRUE);
9850 g_assert (costs > 0);
9851 cfg->real_offset += 5;
9853 if (!MONO_TYPE_IS_VOID (fsig->ret)) {
9854 /* *sp is already set by inline_method */
9859 inline_costs += costs;
9868 if (strcmp (cmethod->name, "Set") == 0) { /* array Set */
9869 MonoInst *val = sp [fsig->param_count];
9871 if (val->type == STACK_OBJ) {
9872 MonoInst *iargs [2];
9877 mono_emit_jit_icall (cfg, mono_helper_stelem_ref_check, iargs);
9880 addr = mini_emit_ldelema_ins (cfg, cmethod, sp, ip, TRUE);
9881 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, fsig->params [fsig->param_count - 1], addr->dreg, 0, val->dreg);
9882 if (cfg->gen_write_barriers && val->type == STACK_OBJ && !(val->opcode == OP_PCONST && val->inst_c0 == 0))
9883 emit_write_barrier (cfg, addr, val);
9884 if (cfg->gen_write_barriers && mini_is_gsharedvt_klass (cmethod->klass))
9885 GSHAREDVT_FAILURE (*ip);
9886 } else if (strcmp (cmethod->name, "Get") == 0) { /* array Get */
9887 addr = mini_emit_ldelema_ins (cfg, cmethod, sp, ip, FALSE);
9889 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, fsig->ret, addr->dreg, 0);
9890 } else if (strcmp (cmethod->name, "Address") == 0) { /* array Address */
9891 if (!cmethod->klass->element_class->valuetype && !readonly)
9892 mini_emit_check_array_type (cfg, sp [0], cmethod->klass);
9893 CHECK_TYPELOAD (cmethod->klass);
9896 addr = mini_emit_ldelema_ins (cfg, cmethod, sp, ip, FALSE);
9899 g_assert_not_reached ();
9906 ins = mini_redirect_call (cfg, cmethod, fsig, sp, virtual_ ? sp [0] : NULL);
9910 /* Tail prefix / tail call optimization */
9912 /* FIXME: Enabling TAILC breaks some inlining/stack trace/etc tests */
9913 /* FIXME: runtime generic context pointer for jumps? */
9914 /* FIXME: handle this for generic sharing eventually */
9915 if ((ins_flag & MONO_INST_TAILCALL) &&
9916 !vtable_arg && !cfg->gshared && is_supported_tail_call (cfg, method, cmethod, fsig, call_opcode))
9917 supported_tail_call = TRUE;
9919 if (supported_tail_call) {
9922 /* Prevent inlining of methods with tail calls (the call stack would be altered) */
9923 INLINE_FAILURE ("tail call");
9925 //printf ("HIT: %s -> %s\n", mono_method_full_name (cfg->method, TRUE), mono_method_full_name (cmethod, TRUE));
9927 if (cfg->backend->have_op_tail_call) {
9928 /* Handle tail calls similarly to normal calls */
9931 emit_instrumentation_call (cfg, mono_profiler_method_leave);
9933 MONO_INST_NEW_CALL (cfg, call, OP_JMP);
9934 call->tail_call = TRUE;
9935 call->method = cmethod;
9936 call->signature = mono_method_signature (cmethod);
9939 * We implement tail calls by storing the actual arguments into the
9940 * argument variables, then emitting a CEE_JMP.
9942 for (i = 0; i < n; ++i) {
9943 /* Prevent argument from being register allocated */
9944 arg_array [i]->flags |= MONO_INST_VOLATILE;
9945 EMIT_NEW_ARGSTORE (cfg, ins, i, sp [i]);
9947 ins = (MonoInst*)call;
9948 ins->inst_p0 = cmethod;
9949 ins->inst_p1 = arg_array [0];
9950 MONO_ADD_INS (cfg->cbb, ins);
9951 link_bblock (cfg, cfg->cbb, end_bblock);
9952 start_new_bblock = 1;
9954 // FIXME: Eliminate unreachable epilogs
9957 * OP_TAILCALL has no return value, so skip the CEE_RET if it is
9958 * only reachable from this call.
9960 GET_BBLOCK (cfg, tblock, ip + 5);
9961 if (tblock == cfg->cbb || tblock->in_count == 0)
9970 * Synchronized wrappers.
9971 * Its hard to determine where to replace a method with its synchronized
9972 * wrapper without causing an infinite recursion. The current solution is
9973 * to add the synchronized wrapper in the trampolines, and to
9974 * change the called method to a dummy wrapper, and resolve that wrapper
9975 * to the real method in mono_jit_compile_method ().
9977 if (cfg->method->wrapper_type == MONO_WRAPPER_SYNCHRONIZED) {
9978 MonoMethod *orig = mono_marshal_method_from_wrapper (cfg->method);
9979 if (cmethod == orig || (cmethod->is_inflated && mono_method_get_declaring_generic_method (cmethod) == orig))
9980 cmethod = mono_marshal_get_synchronized_inner_wrapper (cmethod);
9984 * Virtual calls in llvm-only mode.
9986 if (cfg->llvm_only && virtual_ && cmethod && (cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL)) {
9987 ins = emit_llvmonly_virtual_call (cfg, cmethod, fsig, context_used, sp);
9992 INLINE_FAILURE ("call");
9993 ins = mono_emit_method_call_full (cfg, cmethod, fsig, tail_call, sp, virtual_ ? sp [0] : NULL,
9994 imt_arg, vtable_arg);
9996 if (tail_call && !cfg->llvm_only) {
9997 link_bblock (cfg, cfg->cbb, end_bblock);
9998 start_new_bblock = 1;
10000 // FIXME: Eliminate unreachable epilogs
10003 * OP_TAILCALL has no return value, so skip the CEE_RET if it is
10004 * only reachable from this call.
10006 GET_BBLOCK (cfg, tblock, ip + 5);
10007 if (tblock == cfg->cbb || tblock->in_count == 0)
10014 /* End of call, INS should contain the result of the call, if any */
10016 if (push_res && !MONO_TYPE_IS_VOID (fsig->ret)) {
10019 *sp++ = mono_emit_widen_call_res (cfg, ins, fsig);
10024 if (keep_this_alive) {
10025 MonoInst *dummy_use;
10027 /* See mono_emit_method_call_full () */
10028 EMIT_NEW_DUMMY_USE (cfg, dummy_use, keep_this_alive);
10031 CHECK_CFG_EXCEPTION;
10035 g_assert (*ip == CEE_RET);
10039 constrained_class = NULL;
10040 if (need_seq_point)
10041 emit_seq_point (cfg, method, ip, FALSE, TRUE);
10045 if (cfg->method != method) {
10046 /* return from inlined method */
10048 * If in_count == 0, that means the ret is unreachable due to
10049 * being preceeded by a throw. In that case, inline_method () will
10050 * handle setting the return value
10051 * (test case: test_0_inline_throw ()).
10053 if (return_var && cfg->cbb->in_count) {
10054 MonoType *ret_type = mono_method_signature (method)->ret;
10060 if ((method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD || method->wrapper_type == MONO_WRAPPER_NONE) && target_type_is_incompatible (cfg, ret_type, *sp))
10063 //g_assert (returnvar != -1);
10064 EMIT_NEW_TEMPSTORE (cfg, store, return_var->inst_c0, *sp);
10065 cfg->ret_var_set = TRUE;
10068 emit_instrumentation_call (cfg, mono_profiler_method_leave);
10070 if (cfg->lmf_var && cfg->cbb->in_count && !cfg->llvm_only)
10071 emit_pop_lmf (cfg);
10074 MonoType *ret_type = mini_get_underlying_type (mono_method_signature (method)->ret);
10076 if (seq_points && !sym_seq_points) {
10078 * Place a seq point here too even through the IL stack is not
10079 * empty, so a step over on
10082 * will work correctly.
10084 NEW_SEQ_POINT (cfg, ins, ip - header->code, TRUE);
10085 MONO_ADD_INS (cfg->cbb, ins);
10088 g_assert (!return_var);
10092 if ((method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD || method->wrapper_type == MONO_WRAPPER_NONE) && target_type_is_incompatible (cfg, ret_type, *sp))
10095 emit_setret (cfg, *sp);
10098 if (sp != stack_start)
10100 MONO_INST_NEW (cfg, ins, OP_BR);
10102 ins->inst_target_bb = end_bblock;
10103 MONO_ADD_INS (cfg->cbb, ins);
10104 link_bblock (cfg, cfg->cbb, end_bblock);
10105 start_new_bblock = 1;
10109 MONO_INST_NEW (cfg, ins, OP_BR);
10111 target = ip + 1 + (signed char)(*ip);
10113 GET_BBLOCK (cfg, tblock, target);
10114 link_bblock (cfg, cfg->cbb, tblock);
10115 ins->inst_target_bb = tblock;
10116 if (sp != stack_start) {
10117 handle_stack_args (cfg, stack_start, sp - stack_start);
10119 CHECK_UNVERIFIABLE (cfg);
10121 MONO_ADD_INS (cfg->cbb, ins);
10122 start_new_bblock = 1;
10123 inline_costs += BRANCH_COST;
10137 MONO_INST_NEW (cfg, ins, *ip + BIG_BRANCH_OFFSET);
10139 target = ip + 1 + *(signed char*)ip;
10142 ADD_BINCOND (NULL);
10145 inline_costs += BRANCH_COST;
10149 MONO_INST_NEW (cfg, ins, OP_BR);
10152 target = ip + 4 + (gint32)read32(ip);
10154 GET_BBLOCK (cfg, tblock, target);
10155 link_bblock (cfg, cfg->cbb, tblock);
10156 ins->inst_target_bb = tblock;
10157 if (sp != stack_start) {
10158 handle_stack_args (cfg, stack_start, sp - stack_start);
10160 CHECK_UNVERIFIABLE (cfg);
10163 MONO_ADD_INS (cfg->cbb, ins);
10165 start_new_bblock = 1;
10166 inline_costs += BRANCH_COST;
10168 case CEE_BRFALSE_S:
10173 gboolean is_short = ((*ip) == CEE_BRFALSE_S) || ((*ip) == CEE_BRTRUE_S);
10174 gboolean is_true = ((*ip) == CEE_BRTRUE_S) || ((*ip) == CEE_BRTRUE);
10175 guint32 opsize = is_short ? 1 : 4;
10177 CHECK_OPSIZE (opsize);
10179 if (sp [-1]->type == STACK_VTYPE || sp [-1]->type == STACK_R8)
10182 target = ip + opsize + (is_short ? *(signed char*)ip : (gint32)read32(ip));
10187 GET_BBLOCK (cfg, tblock, target);
10188 link_bblock (cfg, cfg->cbb, tblock);
10189 GET_BBLOCK (cfg, tblock, ip);
10190 link_bblock (cfg, cfg->cbb, tblock);
10192 if (sp != stack_start) {
10193 handle_stack_args (cfg, stack_start, sp - stack_start);
10194 CHECK_UNVERIFIABLE (cfg);
10197 MONO_INST_NEW(cfg, cmp, OP_ICOMPARE_IMM);
10198 cmp->sreg1 = sp [0]->dreg;
10199 type_from_op (cfg, cmp, sp [0], NULL);
10202 #if SIZEOF_REGISTER == 4
10203 if (cmp->opcode == OP_LCOMPARE_IMM) {
10204 /* Convert it to OP_LCOMPARE */
10205 MONO_INST_NEW (cfg, ins, OP_I8CONST);
10206 ins->type = STACK_I8;
10207 ins->dreg = alloc_dreg (cfg, STACK_I8);
10209 MONO_ADD_INS (cfg->cbb, ins);
10210 cmp->opcode = OP_LCOMPARE;
10211 cmp->sreg2 = ins->dreg;
10214 MONO_ADD_INS (cfg->cbb, cmp);
10216 MONO_INST_NEW (cfg, ins, is_true ? CEE_BNE_UN : CEE_BEQ);
10217 type_from_op (cfg, ins, sp [0], NULL);
10218 MONO_ADD_INS (cfg->cbb, ins);
10219 ins->inst_many_bb = (MonoBasicBlock **)mono_mempool_alloc (cfg->mempool, sizeof(gpointer)*2);
10220 GET_BBLOCK (cfg, tblock, target);
10221 ins->inst_true_bb = tblock;
10222 GET_BBLOCK (cfg, tblock, ip);
10223 ins->inst_false_bb = tblock;
10224 start_new_bblock = 2;
10227 inline_costs += BRANCH_COST;
10242 MONO_INST_NEW (cfg, ins, *ip);
10244 target = ip + 4 + (gint32)read32(ip);
10247 ADD_BINCOND (NULL);
10250 inline_costs += BRANCH_COST;
10254 MonoBasicBlock **targets;
10255 MonoBasicBlock *default_bblock;
10256 MonoJumpInfoBBTable *table;
10257 int offset_reg = alloc_preg (cfg);
10258 int target_reg = alloc_preg (cfg);
10259 int table_reg = alloc_preg (cfg);
10260 int sum_reg = alloc_preg (cfg);
10261 gboolean use_op_switch;
10265 n = read32 (ip + 1);
10268 if ((src1->type != STACK_I4) && (src1->type != STACK_PTR))
10272 CHECK_OPSIZE (n * sizeof (guint32));
10273 target = ip + n * sizeof (guint32);
10275 GET_BBLOCK (cfg, default_bblock, target);
10276 default_bblock->flags |= BB_INDIRECT_JUMP_TARGET;
10278 targets = (MonoBasicBlock **)mono_mempool_alloc (cfg->mempool, sizeof (MonoBasicBlock*) * n);
10279 for (i = 0; i < n; ++i) {
10280 GET_BBLOCK (cfg, tblock, target + (gint32)read32(ip));
10281 targets [i] = tblock;
10282 targets [i]->flags |= BB_INDIRECT_JUMP_TARGET;
10286 if (sp != stack_start) {
10288 * Link the current bb with the targets as well, so handle_stack_args
10289 * will set their in_stack correctly.
10291 link_bblock (cfg, cfg->cbb, default_bblock);
10292 for (i = 0; i < n; ++i)
10293 link_bblock (cfg, cfg->cbb, targets [i]);
10295 handle_stack_args (cfg, stack_start, sp - stack_start);
10297 CHECK_UNVERIFIABLE (cfg);
10299 /* Undo the links */
10300 mono_unlink_bblock (cfg, cfg->cbb, default_bblock);
10301 for (i = 0; i < n; ++i)
10302 mono_unlink_bblock (cfg, cfg->cbb, targets [i]);
10305 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ICOMPARE_IMM, -1, src1->dreg, n);
10306 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBGE_UN, default_bblock);
10308 for (i = 0; i < n; ++i)
10309 link_bblock (cfg, cfg->cbb, targets [i]);
10311 table = (MonoJumpInfoBBTable *)mono_mempool_alloc (cfg->mempool, sizeof (MonoJumpInfoBBTable));
10312 table->table = targets;
10313 table->table_size = n;
10315 use_op_switch = FALSE;
10317 /* ARM implements SWITCH statements differently */
10318 /* FIXME: Make it use the generic implementation */
10319 if (!cfg->compile_aot)
10320 use_op_switch = TRUE;
10323 if (COMPILE_LLVM (cfg))
10324 use_op_switch = TRUE;
10326 cfg->cbb->has_jump_table = 1;
10328 if (use_op_switch) {
10329 MONO_INST_NEW (cfg, ins, OP_SWITCH);
10330 ins->sreg1 = src1->dreg;
10331 ins->inst_p0 = table;
10332 ins->inst_many_bb = targets;
10333 ins->klass = (MonoClass *)GUINT_TO_POINTER (n);
10334 MONO_ADD_INS (cfg->cbb, ins);
10336 if (sizeof (gpointer) == 8)
10337 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SHL_IMM, offset_reg, src1->dreg, 3);
10339 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SHL_IMM, offset_reg, src1->dreg, 2);
10341 #if SIZEOF_REGISTER == 8
10342 /* The upper word might not be zero, and we add it to a 64 bit address later */
10343 MONO_EMIT_NEW_UNALU (cfg, OP_ZEXT_I4, offset_reg, offset_reg);
10346 if (cfg->compile_aot) {
10347 MONO_EMIT_NEW_AOTCONST (cfg, table_reg, table, MONO_PATCH_INFO_SWITCH);
10349 MONO_INST_NEW (cfg, ins, OP_JUMP_TABLE);
10350 ins->inst_c1 = MONO_PATCH_INFO_SWITCH;
10351 ins->inst_p0 = table;
10352 ins->dreg = table_reg;
10353 MONO_ADD_INS (cfg->cbb, ins);
10356 /* FIXME: Use load_memindex */
10357 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, sum_reg, table_reg, offset_reg);
10358 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, target_reg, sum_reg, 0);
10359 MONO_EMIT_NEW_UNALU (cfg, OP_BR_REG, -1, target_reg);
10361 start_new_bblock = 1;
10362 inline_costs += (BRANCH_COST * 2);
10375 case CEE_LDIND_REF:
10382 dreg = alloc_freg (cfg);
10385 dreg = alloc_lreg (cfg);
10387 case CEE_LDIND_REF:
10388 dreg = alloc_ireg_ref (cfg);
10391 dreg = alloc_preg (cfg);
10394 NEW_LOAD_MEMBASE (cfg, ins, ldind_to_load_membase (*ip), dreg, sp [0]->dreg, 0);
10395 ins->type = ldind_type [*ip - CEE_LDIND_I1];
10396 if (*ip == CEE_LDIND_R4)
10397 ins->type = cfg->r4_stack_type;
10398 ins->flags |= ins_flag;
10399 MONO_ADD_INS (cfg->cbb, ins);
10401 if (ins_flag & MONO_INST_VOLATILE) {
10402 /* Volatile loads have acquire semantics, see 12.6.7 in Ecma 335 */
10403 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_ACQ);
10408 case CEE_STIND_REF:
10419 if (ins_flag & MONO_INST_VOLATILE) {
10420 /* Volatile stores have release semantics, see 12.6.7 in Ecma 335 */
10421 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_REL);
10424 NEW_STORE_MEMBASE (cfg, ins, stind_to_store_membase (*ip), sp [0]->dreg, 0, sp [1]->dreg);
10425 ins->flags |= ins_flag;
10428 MONO_ADD_INS (cfg->cbb, ins);
10430 if (cfg->gen_write_barriers && *ip == CEE_STIND_REF && method->wrapper_type != MONO_WRAPPER_WRITE_BARRIER && !((sp [1]->opcode == OP_PCONST) && (sp [1]->inst_p0 == 0)))
10431 emit_write_barrier (cfg, sp [0], sp [1]);
10440 MONO_INST_NEW (cfg, ins, (*ip));
10442 ins->sreg1 = sp [0]->dreg;
10443 ins->sreg2 = sp [1]->dreg;
10444 type_from_op (cfg, ins, sp [0], sp [1]);
10446 ins->dreg = alloc_dreg ((cfg), (MonoStackType)(ins)->type);
10448 /* Use the immediate opcodes if possible */
10449 if ((sp [1]->opcode == OP_ICONST) && mono_arch_is_inst_imm (sp [1]->inst_c0)) {
10450 int imm_opcode = mono_op_to_op_imm_noemul (ins->opcode);
10451 if (imm_opcode != -1) {
10452 ins->opcode = imm_opcode;
10453 ins->inst_p1 = (gpointer)(gssize)(sp [1]->inst_c0);
10456 NULLIFY_INS (sp [1]);
10460 MONO_ADD_INS ((cfg)->cbb, (ins));
10462 *sp++ = mono_decompose_opcode (cfg, ins);
10479 MONO_INST_NEW (cfg, ins, (*ip));
10481 ins->sreg1 = sp [0]->dreg;
10482 ins->sreg2 = sp [1]->dreg;
10483 type_from_op (cfg, ins, sp [0], sp [1]);
10485 add_widen_op (cfg, ins, &sp [0], &sp [1]);
10486 ins->dreg = alloc_dreg ((cfg), (MonoStackType)(ins)->type);
10488 /* FIXME: Pass opcode to is_inst_imm */
10490 /* Use the immediate opcodes if possible */
10491 if (((sp [1]->opcode == OP_ICONST) || (sp [1]->opcode == OP_I8CONST)) && mono_arch_is_inst_imm (sp [1]->opcode == OP_ICONST ? sp [1]->inst_c0 : sp [1]->inst_l)) {
10492 int imm_opcode = mono_op_to_op_imm_noemul (ins->opcode);
10493 if (imm_opcode != -1) {
10494 ins->opcode = imm_opcode;
10495 if (sp [1]->opcode == OP_I8CONST) {
10496 #if SIZEOF_REGISTER == 8
10497 ins->inst_imm = sp [1]->inst_l;
10499 ins->inst_ls_word = sp [1]->inst_ls_word;
10500 ins->inst_ms_word = sp [1]->inst_ms_word;
10504 ins->inst_imm = (gssize)(sp [1]->inst_c0);
10507 /* Might be followed by an instruction added by add_widen_op */
10508 if (sp [1]->next == NULL)
10509 NULLIFY_INS (sp [1]);
10512 MONO_ADD_INS ((cfg)->cbb, (ins));
10514 *sp++ = mono_decompose_opcode (cfg, ins);
10527 case CEE_CONV_OVF_I8:
10528 case CEE_CONV_OVF_U8:
10529 case CEE_CONV_R_UN:
10532 /* Special case this earlier so we have long constants in the IR */
10533 if ((((*ip) == CEE_CONV_I8) || ((*ip) == CEE_CONV_U8)) && (sp [-1]->opcode == OP_ICONST)) {
10534 int data = sp [-1]->inst_c0;
10535 sp [-1]->opcode = OP_I8CONST;
10536 sp [-1]->type = STACK_I8;
10537 #if SIZEOF_REGISTER == 8
10538 if ((*ip) == CEE_CONV_U8)
10539 sp [-1]->inst_c0 = (guint32)data;
10541 sp [-1]->inst_c0 = data;
10543 sp [-1]->inst_ls_word = data;
10544 if ((*ip) == CEE_CONV_U8)
10545 sp [-1]->inst_ms_word = 0;
10547 sp [-1]->inst_ms_word = (data < 0) ? -1 : 0;
10549 sp [-1]->dreg = alloc_dreg (cfg, STACK_I8);
10556 case CEE_CONV_OVF_I4:
10557 case CEE_CONV_OVF_I1:
10558 case CEE_CONV_OVF_I2:
10559 case CEE_CONV_OVF_I:
10560 case CEE_CONV_OVF_U:
10563 if (sp [-1]->type == STACK_R8 || sp [-1]->type == STACK_R4) {
10564 ADD_UNOP (CEE_CONV_OVF_I8);
10571 case CEE_CONV_OVF_U1:
10572 case CEE_CONV_OVF_U2:
10573 case CEE_CONV_OVF_U4:
10576 if (sp [-1]->type == STACK_R8 || sp [-1]->type == STACK_R4) {
10577 ADD_UNOP (CEE_CONV_OVF_U8);
10584 case CEE_CONV_OVF_I1_UN:
10585 case CEE_CONV_OVF_I2_UN:
10586 case CEE_CONV_OVF_I4_UN:
10587 case CEE_CONV_OVF_I8_UN:
10588 case CEE_CONV_OVF_U1_UN:
10589 case CEE_CONV_OVF_U2_UN:
10590 case CEE_CONV_OVF_U4_UN:
10591 case CEE_CONV_OVF_U8_UN:
10592 case CEE_CONV_OVF_I_UN:
10593 case CEE_CONV_OVF_U_UN:
10600 CHECK_CFG_EXCEPTION;
10604 case CEE_ADD_OVF_UN:
10606 case CEE_MUL_OVF_UN:
10608 case CEE_SUB_OVF_UN:
10614 GSHAREDVT_FAILURE (*ip);
10617 token = read32 (ip + 1);
10618 klass = mini_get_class (method, token, generic_context);
10619 CHECK_TYPELOAD (klass);
10621 if (generic_class_is_reference_type (cfg, klass)) {
10622 MonoInst *store, *load;
10623 int dreg = alloc_ireg_ref (cfg);
10625 NEW_LOAD_MEMBASE (cfg, load, OP_LOAD_MEMBASE, dreg, sp [1]->dreg, 0);
10626 load->flags |= ins_flag;
10627 MONO_ADD_INS (cfg->cbb, load);
10629 NEW_STORE_MEMBASE (cfg, store, OP_STORE_MEMBASE_REG, sp [0]->dreg, 0, dreg);
10630 store->flags |= ins_flag;
10631 MONO_ADD_INS (cfg->cbb, store);
10633 if (cfg->gen_write_barriers && cfg->method->wrapper_type != MONO_WRAPPER_WRITE_BARRIER)
10634 emit_write_barrier (cfg, sp [0], sp [1]);
10636 mini_emit_stobj (cfg, sp [0], sp [1], klass, FALSE);
10642 int loc_index = -1;
10648 token = read32 (ip + 1);
10649 klass = mini_get_class (method, token, generic_context);
10650 CHECK_TYPELOAD (klass);
10652 /* Optimize the common ldobj+stloc combination */
10655 loc_index = ip [6];
10662 loc_index = ip [5] - CEE_STLOC_0;
10669 if ((loc_index != -1) && ip_in_bb (cfg, cfg->cbb, ip + 5)) {
10670 CHECK_LOCAL (loc_index);
10672 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, sp [0]->dreg, 0);
10673 ins->dreg = cfg->locals [loc_index]->dreg;
10674 ins->flags |= ins_flag;
10677 if (ins_flag & MONO_INST_VOLATILE) {
10678 /* Volatile loads have acquire semantics, see 12.6.7 in Ecma 335 */
10679 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_ACQ);
10685 /* Optimize the ldobj+stobj combination */
10686 /* The reference case ends up being a load+store anyway */
10687 /* Skip this if the operation is volatile. */
10688 if (((ip [5] == CEE_STOBJ) && ip_in_bb (cfg, cfg->cbb, ip + 5) && read32 (ip + 6) == token) && !generic_class_is_reference_type (cfg, klass) && !(ins_flag & MONO_INST_VOLATILE)) {
10693 mini_emit_stobj (cfg, sp [0], sp [1], klass, FALSE);
10700 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, sp [0]->dreg, 0);
10701 ins->flags |= ins_flag;
10704 if (ins_flag & MONO_INST_VOLATILE) {
10705 /* Volatile loads have acquire semantics, see 12.6.7 in Ecma 335 */
10706 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_ACQ);
10715 CHECK_STACK_OVF (1);
10717 n = read32 (ip + 1);
10719 if (method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD) {
10720 EMIT_NEW_PCONST (cfg, ins, mono_method_get_wrapper_data (method, n));
10721 ins->type = STACK_OBJ;
10724 else if (method->wrapper_type != MONO_WRAPPER_NONE) {
10725 MonoInst *iargs [1];
10726 char *str = (char *)mono_method_get_wrapper_data (method, n);
10728 if (cfg->compile_aot)
10729 EMIT_NEW_LDSTRLITCONST (cfg, iargs [0], str);
10731 EMIT_NEW_PCONST (cfg, iargs [0], str);
10732 *sp = mono_emit_jit_icall (cfg, mono_string_new_wrapper, iargs);
10734 if (cfg->opt & MONO_OPT_SHARED) {
10735 MonoInst *iargs [3];
10737 if (cfg->compile_aot) {
10738 cfg->ldstr_list = g_list_prepend (cfg->ldstr_list, GINT_TO_POINTER (n));
10740 EMIT_NEW_DOMAINCONST (cfg, iargs [0]);
10741 EMIT_NEW_IMAGECONST (cfg, iargs [1], image);
10742 EMIT_NEW_ICONST (cfg, iargs [2], mono_metadata_token_index (n));
10743 *sp = mono_emit_jit_icall (cfg, mono_ldstr, iargs);
10744 mono_ldstr (cfg->domain, image, mono_metadata_token_index (n));
10746 if (cfg->cbb->out_of_line) {
10747 MonoInst *iargs [2];
10749 if (image == mono_defaults.corlib) {
10751 * Avoid relocations in AOT and save some space by using a
10752 * version of helper_ldstr specialized to mscorlib.
10754 EMIT_NEW_ICONST (cfg, iargs [0], mono_metadata_token_index (n));
10755 *sp = mono_emit_jit_icall (cfg, mono_helper_ldstr_mscorlib, iargs);
10757 /* Avoid creating the string object */
10758 EMIT_NEW_IMAGECONST (cfg, iargs [0], image);
10759 EMIT_NEW_ICONST (cfg, iargs [1], mono_metadata_token_index (n));
10760 *sp = mono_emit_jit_icall (cfg, mono_helper_ldstr, iargs);
10764 if (cfg->compile_aot) {
10765 NEW_LDSTRCONST (cfg, ins, image, n);
10767 MONO_ADD_INS (cfg->cbb, ins);
10770 NEW_PCONST (cfg, ins, NULL);
10771 ins->type = STACK_OBJ;
10772 ins->inst_p0 = mono_ldstr (cfg->domain, image, mono_metadata_token_index (n));
10774 OUT_OF_MEMORY_FAILURE;
10777 MONO_ADD_INS (cfg->cbb, ins);
10786 MonoInst *iargs [2];
10787 MonoMethodSignature *fsig;
10790 MonoInst *vtable_arg = NULL;
10793 token = read32 (ip + 1);
10794 cmethod = mini_get_method (cfg, method, token, NULL, generic_context);
10797 fsig = mono_method_get_signature_checked (cmethod, image, token, generic_context, &cfg->error);
10800 mono_save_token_info (cfg, image, token, cmethod);
10802 if (!mono_class_init (cmethod->klass))
10803 TYPE_LOAD_ERROR (cmethod->klass);
10805 context_used = mini_method_check_context_used (cfg, cmethod);
10807 if (mono_security_core_clr_enabled ())
10808 ensure_method_is_allowed_to_call_method (cfg, method, cmethod);
10810 if (cfg->gshared && cmethod && cmethod->klass != method->klass && cmethod->klass->generic_class && mono_method_is_generic_sharable (cmethod, TRUE) && mono_class_needs_cctor_run (cmethod->klass, method)) {
10811 emit_class_init (cfg, cmethod->klass);
10812 CHECK_TYPELOAD (cmethod->klass);
10816 if (cfg->gsharedvt) {
10817 if (mini_is_gsharedvt_variable_signature (sig))
10818 GSHAREDVT_FAILURE (*ip);
10822 n = fsig->param_count;
10826 * Generate smaller code for the common newobj <exception> instruction in
10827 * argument checking code.
10829 if (cfg->cbb->out_of_line && cmethod->klass->image == mono_defaults.corlib &&
10830 is_exception_class (cmethod->klass) && n <= 2 &&
10831 ((n < 1) || (!fsig->params [0]->byref && fsig->params [0]->type == MONO_TYPE_STRING)) &&
10832 ((n < 2) || (!fsig->params [1]->byref && fsig->params [1]->type == MONO_TYPE_STRING))) {
10833 MonoInst *iargs [3];
10837 EMIT_NEW_ICONST (cfg, iargs [0], cmethod->klass->type_token);
10840 *sp ++ = mono_emit_jit_icall (cfg, mono_create_corlib_exception_0, iargs);
10843 iargs [1] = sp [0];
10844 *sp ++ = mono_emit_jit_icall (cfg, mono_create_corlib_exception_1, iargs);
10847 iargs [1] = sp [0];
10848 iargs [2] = sp [1];
10849 *sp ++ = mono_emit_jit_icall (cfg, mono_create_corlib_exception_2, iargs);
10852 g_assert_not_reached ();
10860 /* move the args to allow room for 'this' in the first position */
10866 /* check_call_signature () requires sp[0] to be set */
10867 this_ins.type = STACK_OBJ;
10868 sp [0] = &this_ins;
10869 if (check_call_signature (cfg, fsig, sp))
10874 if (mini_class_is_system_array (cmethod->klass)) {
10875 *sp = emit_get_rgctx_method (cfg, context_used,
10876 cmethod, MONO_RGCTX_INFO_METHOD);
10878 /* Avoid varargs in the common case */
10879 if (fsig->param_count == 1)
10880 alloc = mono_emit_jit_icall (cfg, mono_array_new_1, sp);
10881 else if (fsig->param_count == 2)
10882 alloc = mono_emit_jit_icall (cfg, mono_array_new_2, sp);
10883 else if (fsig->param_count == 3)
10884 alloc = mono_emit_jit_icall (cfg, mono_array_new_3, sp);
10885 else if (fsig->param_count == 4)
10886 alloc = mono_emit_jit_icall (cfg, mono_array_new_4, sp);
10888 alloc = handle_array_new (cfg, fsig->param_count, sp, ip);
10889 } else if (cmethod->string_ctor) {
10890 g_assert (!context_used);
10891 g_assert (!vtable_arg);
10892 /* we simply pass a null pointer */
10893 EMIT_NEW_PCONST (cfg, *sp, NULL);
10894 /* now call the string ctor */
10895 alloc = mono_emit_method_call_full (cfg, cmethod, fsig, FALSE, sp, NULL, NULL, NULL);
10897 if (cmethod->klass->valuetype) {
10898 iargs [0] = mono_compile_create_var (cfg, &cmethod->klass->byval_arg, OP_LOCAL);
10899 emit_init_rvar (cfg, iargs [0]->dreg, &cmethod->klass->byval_arg);
10900 EMIT_NEW_TEMPLOADA (cfg, *sp, iargs [0]->inst_c0);
10905 * The code generated by mini_emit_virtual_call () expects
10906 * iargs [0] to be a boxed instance, but luckily the vcall
10907 * will be transformed into a normal call there.
10909 } else if (context_used) {
10910 alloc = handle_alloc (cfg, cmethod->klass, FALSE, context_used);
10913 MonoVTable *vtable = NULL;
10915 if (!cfg->compile_aot)
10916 vtable = mono_class_vtable (cfg->domain, cmethod->klass);
10917 CHECK_TYPELOAD (cmethod->klass);
10920 * TypeInitializationExceptions thrown from the mono_runtime_class_init
10921 * call in mono_jit_runtime_invoke () can abort the finalizer thread.
10922 * As a workaround, we call class cctors before allocating objects.
10924 if (mini_field_access_needs_cctor_run (cfg, method, cmethod->klass, vtable) && !(g_slist_find (class_inits, cmethod->klass))) {
10925 emit_class_init (cfg, cmethod->klass);
10926 if (cfg->verbose_level > 2)
10927 printf ("class %s.%s needs init call for ctor\n", cmethod->klass->name_space, cmethod->klass->name);
10928 class_inits = g_slist_prepend (class_inits, cmethod->klass);
10931 alloc = handle_alloc (cfg, cmethod->klass, FALSE, 0);
10934 CHECK_CFG_EXCEPTION; /*for handle_alloc*/
10937 MONO_EMIT_NEW_UNALU (cfg, OP_NOT_NULL, -1, alloc->dreg);
10939 /* Now call the actual ctor */
10940 handle_ctor_call (cfg, cmethod, fsig, context_used, sp, ip, &inline_costs);
10941 CHECK_CFG_EXCEPTION;
10944 if (alloc == NULL) {
10946 EMIT_NEW_TEMPLOAD (cfg, ins, iargs [0]->inst_c0);
10947 type_to_eval_stack_type (cfg, &ins->klass->byval_arg, ins);
10955 if (!(seq_point_locs && mono_bitset_test_fast (seq_point_locs, ip - header->code)))
10956 emit_seq_point (cfg, method, ip, FALSE, TRUE);
10959 case CEE_CASTCLASS:
10963 token = read32 (ip + 1);
10964 klass = mini_get_class (method, token, generic_context);
10965 CHECK_TYPELOAD (klass);
10966 if (sp [0]->type != STACK_OBJ)
10969 ins = handle_castclass (cfg, klass, *sp, ip, &inline_costs);
10970 CHECK_CFG_EXCEPTION;
10979 token = read32 (ip + 1);
10980 klass = mini_get_class (method, token, generic_context);
10981 CHECK_TYPELOAD (klass);
10982 if (sp [0]->type != STACK_OBJ)
10985 context_used = mini_class_check_context_used (cfg, klass);
10987 if (!context_used && mini_class_has_reference_variant_generic_argument (cfg, klass, context_used)) {
10988 MonoMethod *mono_isinst = mono_marshal_get_isinst_with_cache ();
10989 MonoInst *args [3];
10996 EMIT_NEW_CLASSCONST (cfg, args [1], klass);
10999 idx = get_castclass_cache_idx (cfg);
11000 args [2] = emit_runtime_constant (cfg, MONO_PATCH_INFO_CASTCLASS_CACHE, GINT_TO_POINTER (idx));
11002 *sp++ = mono_emit_method_call (cfg, mono_isinst, args, NULL);
11005 } else if (!context_used && (mono_class_is_marshalbyref (klass) || klass->flags & TYPE_ATTRIBUTE_INTERFACE)) {
11006 MonoMethod *mono_isinst;
11007 MonoInst *iargs [1];
11010 mono_isinst = mono_marshal_get_isinst (klass);
11011 iargs [0] = sp [0];
11013 costs = inline_method (cfg, mono_isinst, mono_method_signature (mono_isinst),
11014 iargs, ip, cfg->real_offset, TRUE);
11015 CHECK_CFG_EXCEPTION;
11016 g_assert (costs > 0);
11019 cfg->real_offset += 5;
11023 inline_costs += costs;
11026 ins = handle_isinst (cfg, klass, *sp, context_used);
11027 CHECK_CFG_EXCEPTION;
11033 case CEE_UNBOX_ANY: {
11034 MonoInst *res, *addr;
11039 token = read32 (ip + 1);
11040 klass = mini_get_class (method, token, generic_context);
11041 CHECK_TYPELOAD (klass);
11043 mono_save_token_info (cfg, image, token, klass);
11045 context_used = mini_class_check_context_used (cfg, klass);
11047 if (mini_is_gsharedvt_klass (klass)) {
11048 res = handle_unbox_gsharedvt (cfg, klass, *sp);
11050 } else if (generic_class_is_reference_type (cfg, klass)) {
11051 res = handle_castclass (cfg, klass, *sp, ip, &inline_costs);
11052 CHECK_CFG_EXCEPTION;
11053 } else if (mono_class_is_nullable (klass)) {
11054 res = handle_unbox_nullable (cfg, *sp, klass, context_used);
11056 addr = handle_unbox (cfg, klass, sp, context_used);
11058 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, addr->dreg, 0);
11069 MonoClass *enum_class;
11070 MonoMethod *has_flag;
11076 token = read32 (ip + 1);
11077 klass = mini_get_class (method, token, generic_context);
11078 CHECK_TYPELOAD (klass);
11080 mono_save_token_info (cfg, image, token, klass);
11082 context_used = mini_class_check_context_used (cfg, klass);
11084 if (generic_class_is_reference_type (cfg, klass)) {
11090 if (klass == mono_defaults.void_class)
11092 if (target_type_is_incompatible (cfg, &klass->byval_arg, *sp))
11094 /* frequent check in generic code: box (struct), brtrue */
11099 * <push int/long ptr>
11102 * constrained. MyFlags
11103 * callvirt instace bool class [mscorlib] System.Enum::HasFlag (class [mscorlib] System.Enum)
11105 * If we find this sequence and the operand types on box and constrained
11106 * are equal, we can emit a specialized instruction sequence instead of
11107 * the very slow HasFlag () call.
11109 if ((cfg->opt & MONO_OPT_INTRINS) &&
11110 /* Cheap checks first. */
11111 ip + 5 + 6 + 5 < end &&
11112 ip [5] == CEE_PREFIX1 &&
11113 ip [6] == CEE_CONSTRAINED_ &&
11114 ip [11] == CEE_CALLVIRT &&
11115 ip_in_bb (cfg, cfg->cbb, ip + 5 + 6 + 5) &&
11116 mono_class_is_enum (klass) &&
11117 (enum_class = mini_get_class (method, read32 (ip + 7), generic_context)) &&
11118 (has_flag = mini_get_method (cfg, method, read32 (ip + 12), NULL, generic_context)) &&
11119 has_flag->klass == mono_defaults.enum_class &&
11120 !strcmp (has_flag->name, "HasFlag") &&
11121 has_flag->signature->hasthis &&
11122 has_flag->signature->param_count == 1) {
11123 CHECK_TYPELOAD (enum_class);
11125 if (enum_class == klass) {
11126 MonoInst *enum_this, *enum_flag;
11131 enum_this = sp [0];
11132 enum_flag = sp [1];
11134 *sp++ = handle_enum_has_flag (cfg, klass, enum_this, enum_flag);
11139 // FIXME: LLVM can't handle the inconsistent bb linking
11140 if (!mono_class_is_nullable (klass) &&
11141 !mini_is_gsharedvt_klass (klass) &&
11142 ip + 5 < end && ip_in_bb (cfg, cfg->cbb, ip + 5) &&
11143 (ip [5] == CEE_BRTRUE ||
11144 ip [5] == CEE_BRTRUE_S ||
11145 ip [5] == CEE_BRFALSE ||
11146 ip [5] == CEE_BRFALSE_S)) {
11147 gboolean is_true = ip [5] == CEE_BRTRUE || ip [5] == CEE_BRTRUE_S;
11149 MonoBasicBlock *true_bb, *false_bb;
11153 if (cfg->verbose_level > 3) {
11154 printf ("converting (in B%d: stack: %d) %s", cfg->cbb->block_num, (int)(sp - stack_start), mono_disasm_code_one (NULL, method, ip, NULL));
11155 printf ("<box+brtrue opt>\n");
11160 case CEE_BRFALSE_S:
11163 target = ip + 1 + (signed char)(*ip);
11170 target = ip + 4 + (gint)(read32 (ip));
11174 g_assert_not_reached ();
11178 * We need to link both bblocks, since it is needed for handling stack
11179 * arguments correctly (See test_0_box_brtrue_opt_regress_81102).
11180 * Branching to only one of them would lead to inconsistencies, so
11181 * generate an ICONST+BRTRUE, the branch opts will get rid of them.
11183 GET_BBLOCK (cfg, true_bb, target);
11184 GET_BBLOCK (cfg, false_bb, ip);
11186 mono_link_bblock (cfg, cfg->cbb, true_bb);
11187 mono_link_bblock (cfg, cfg->cbb, false_bb);
11189 if (sp != stack_start) {
11190 handle_stack_args (cfg, stack_start, sp - stack_start);
11192 CHECK_UNVERIFIABLE (cfg);
11195 if (COMPILE_LLVM (cfg)) {
11196 dreg = alloc_ireg (cfg);
11197 MONO_EMIT_NEW_ICONST (cfg, dreg, 0);
11198 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, dreg, is_true ? 0 : 1);
11200 MONO_EMIT_NEW_BRANCH_BLOCK2 (cfg, OP_IBEQ, true_bb, false_bb);
11202 /* The JIT can't eliminate the iconst+compare */
11203 MONO_INST_NEW (cfg, ins, OP_BR);
11204 ins->inst_target_bb = is_true ? true_bb : false_bb;
11205 MONO_ADD_INS (cfg->cbb, ins);
11208 start_new_bblock = 1;
11212 *sp++ = handle_box (cfg, val, klass, context_used);
11214 CHECK_CFG_EXCEPTION;
11223 token = read32 (ip + 1);
11224 klass = mini_get_class (method, token, generic_context);
11225 CHECK_TYPELOAD (klass);
11227 mono_save_token_info (cfg, image, token, klass);
11229 context_used = mini_class_check_context_used (cfg, klass);
11231 if (mono_class_is_nullable (klass)) {
11234 val = handle_unbox_nullable (cfg, *sp, klass, context_used);
11235 EMIT_NEW_VARLOADA (cfg, ins, get_vreg_to_inst (cfg, val->dreg), &val->klass->byval_arg);
11239 ins = handle_unbox (cfg, klass, sp, context_used);
11252 MonoClassField *field;
11253 #ifndef DISABLE_REMOTING
11257 gboolean is_instance;
11259 gpointer addr = NULL;
11260 gboolean is_special_static;
11262 MonoInst *store_val = NULL;
11263 MonoInst *thread_ins;
11266 is_instance = (op == CEE_LDFLD || op == CEE_LDFLDA || op == CEE_STFLD);
11268 if (op == CEE_STFLD) {
11271 store_val = sp [1];
11276 if (sp [0]->type == STACK_I4 || sp [0]->type == STACK_I8 || sp [0]->type == STACK_R8)
11278 if (*ip != CEE_LDFLD && sp [0]->type == STACK_VTYPE)
11281 if (op == CEE_STSFLD) {
11284 store_val = sp [0];
11289 token = read32 (ip + 1);
11290 if (method->wrapper_type != MONO_WRAPPER_NONE) {
11291 field = (MonoClassField *)mono_method_get_wrapper_data (method, token);
11292 klass = field->parent;
11295 field = mono_field_from_token_checked (image, token, &klass, generic_context, &cfg->error);
11298 if (!dont_verify && !cfg->skip_visibility && !mono_method_can_access_field (method, field))
11299 FIELD_ACCESS_FAILURE (method, field);
11300 mono_class_init (klass);
11302 /* if the class is Critical then transparent code cannot access it's fields */
11303 if (!is_instance && mono_security_core_clr_enabled ())
11304 ensure_method_is_allowed_to_access_field (cfg, method, field);
11306 /* XXX this is technically required but, so far (SL2), no [SecurityCritical] types (not many exists) have
11307 any visible *instance* field (in fact there's a single case for a static field in Marshal) XXX
11308 if (mono_security_core_clr_enabled ())
11309 ensure_method_is_allowed_to_access_field (cfg, method, field);
11312 ftype = mono_field_get_type (field);
11315 * LDFLD etc. is usable on static fields as well, so convert those cases to
11318 if (is_instance && ftype->attrs & FIELD_ATTRIBUTE_STATIC) {
11330 g_assert_not_reached ();
11332 is_instance = FALSE;
11335 context_used = mini_class_check_context_used (cfg, klass);
11337 /* INSTANCE CASE */
11339 foffset = klass->valuetype? field->offset - sizeof (MonoObject): field->offset;
11340 if (op == CEE_STFLD) {
11341 if (target_type_is_incompatible (cfg, field->type, sp [1]))
11343 #ifndef DISABLE_REMOTING
11344 if ((mono_class_is_marshalbyref (klass) && !MONO_CHECK_THIS (sp [0])) || mono_class_is_contextbound (klass) || klass == mono_defaults.marshalbyrefobject_class) {
11345 MonoMethod *stfld_wrapper = mono_marshal_get_stfld_wrapper (field->type);
11346 MonoInst *iargs [5];
11348 GSHAREDVT_FAILURE (op);
11350 iargs [0] = sp [0];
11351 EMIT_NEW_CLASSCONST (cfg, iargs [1], klass);
11352 EMIT_NEW_FIELDCONST (cfg, iargs [2], field);
11353 EMIT_NEW_ICONST (cfg, iargs [3], klass->valuetype ? field->offset - sizeof (MonoObject) :
11355 iargs [4] = sp [1];
11357 if (cfg->opt & MONO_OPT_INLINE || cfg->compile_aot) {
11358 costs = inline_method (cfg, stfld_wrapper, mono_method_signature (stfld_wrapper),
11359 iargs, ip, cfg->real_offset, TRUE);
11360 CHECK_CFG_EXCEPTION;
11361 g_assert (costs > 0);
11363 cfg->real_offset += 5;
11365 inline_costs += costs;
11367 mono_emit_method_call (cfg, stfld_wrapper, iargs, NULL);
11374 MONO_EMIT_NULL_CHECK (cfg, sp [0]->dreg);
11376 if (mini_is_gsharedvt_klass (klass)) {
11377 MonoInst *offset_ins;
11379 context_used = mini_class_check_context_used (cfg, klass);
11381 offset_ins = emit_get_gsharedvt_info (cfg, field, MONO_RGCTX_INFO_FIELD_OFFSET);
11382 /* The value is offset by 1 */
11383 EMIT_NEW_BIALU_IMM (cfg, ins, OP_PSUB_IMM, offset_ins->dreg, offset_ins->dreg, 1);
11384 dreg = alloc_ireg_mp (cfg);
11385 EMIT_NEW_BIALU (cfg, ins, OP_PADD, dreg, sp [0]->dreg, offset_ins->dreg);
11386 /* The decomposition will call mini_emit_stobj () which will emit a wbarrier if needed */
11387 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, store, field->type, dreg, 0, sp [1]->dreg);
11389 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, store, field->type, sp [0]->dreg, foffset, sp [1]->dreg);
11391 if (sp [0]->opcode != OP_LDADDR)
11392 store->flags |= MONO_INST_FAULT;
11394 if (cfg->gen_write_barriers && mini_type_to_stind (cfg, field->type) == CEE_STIND_REF && !(sp [1]->opcode == OP_PCONST && sp [1]->inst_c0 == 0)) {
11395 /* insert call to write barrier */
11399 dreg = alloc_ireg_mp (cfg);
11400 EMIT_NEW_BIALU_IMM (cfg, ptr, OP_PADD_IMM, dreg, sp [0]->dreg, foffset);
11401 emit_write_barrier (cfg, ptr, sp [1]);
11404 store->flags |= ins_flag;
11411 #ifndef DISABLE_REMOTING
11412 if (is_instance && ((mono_class_is_marshalbyref (klass) && !MONO_CHECK_THIS (sp [0])) || mono_class_is_contextbound (klass) || klass == mono_defaults.marshalbyrefobject_class)) {
11413 MonoMethod *wrapper = (op == CEE_LDFLDA) ? mono_marshal_get_ldflda_wrapper (field->type) : mono_marshal_get_ldfld_wrapper (field->type);
11414 MonoInst *iargs [4];
11416 GSHAREDVT_FAILURE (op);
11418 iargs [0] = sp [0];
11419 EMIT_NEW_CLASSCONST (cfg, iargs [1], klass);
11420 EMIT_NEW_FIELDCONST (cfg, iargs [2], field);
11421 EMIT_NEW_ICONST (cfg, iargs [3], klass->valuetype ? field->offset - sizeof (MonoObject) : field->offset);
11422 if (cfg->opt & MONO_OPT_INLINE || cfg->compile_aot) {
11423 costs = inline_method (cfg, wrapper, mono_method_signature (wrapper),
11424 iargs, ip, cfg->real_offset, TRUE);
11425 CHECK_CFG_EXCEPTION;
11426 g_assert (costs > 0);
11428 cfg->real_offset += 5;
11432 inline_costs += costs;
11434 ins = mono_emit_method_call (cfg, wrapper, iargs, NULL);
11440 if (sp [0]->type == STACK_VTYPE) {
11443 /* Have to compute the address of the variable */
11445 var = get_vreg_to_inst (cfg, sp [0]->dreg);
11447 var = mono_compile_create_var_for_vreg (cfg, &klass->byval_arg, OP_LOCAL, sp [0]->dreg);
11449 g_assert (var->klass == klass);
11451 EMIT_NEW_VARLOADA (cfg, ins, var, &var->klass->byval_arg);
11455 if (op == CEE_LDFLDA) {
11456 if (sp [0]->type == STACK_OBJ) {
11457 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, sp [0]->dreg, 0);
11458 MONO_EMIT_NEW_COND_EXC (cfg, EQ, "NullReferenceException");
11461 dreg = alloc_ireg_mp (cfg);
11463 if (mini_is_gsharedvt_klass (klass)) {
11464 MonoInst *offset_ins;
11466 offset_ins = emit_get_gsharedvt_info (cfg, field, MONO_RGCTX_INFO_FIELD_OFFSET);
11467 /* The value is offset by 1 */
11468 EMIT_NEW_BIALU_IMM (cfg, ins, OP_PSUB_IMM, offset_ins->dreg, offset_ins->dreg, 1);
11469 EMIT_NEW_BIALU (cfg, ins, OP_PADD, dreg, sp [0]->dreg, offset_ins->dreg);
11471 EMIT_NEW_BIALU_IMM (cfg, ins, OP_PADD_IMM, dreg, sp [0]->dreg, foffset);
11473 ins->klass = mono_class_from_mono_type (field->type);
11474 ins->type = STACK_MP;
11479 MONO_EMIT_NULL_CHECK (cfg, sp [0]->dreg);
11481 if (mini_is_gsharedvt_klass (klass)) {
11482 MonoInst *offset_ins;
11484 offset_ins = emit_get_gsharedvt_info (cfg, field, MONO_RGCTX_INFO_FIELD_OFFSET);
11485 /* The value is offset by 1 */
11486 EMIT_NEW_BIALU_IMM (cfg, ins, OP_PSUB_IMM, offset_ins->dreg, offset_ins->dreg, 1);
11487 dreg = alloc_ireg_mp (cfg);
11488 EMIT_NEW_BIALU (cfg, ins, OP_PADD, dreg, sp [0]->dreg, offset_ins->dreg);
11489 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, load, field->type, dreg, 0);
11491 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, load, field->type, sp [0]->dreg, foffset);
11493 load->flags |= ins_flag;
11494 if (sp [0]->opcode != OP_LDADDR)
11495 load->flags |= MONO_INST_FAULT;
11507 context_used = mini_class_check_context_used (cfg, klass);
11509 if (ftype->attrs & FIELD_ATTRIBUTE_LITERAL) {
11510 mono_error_set_field_load (&cfg->error, field->parent, field->name, "Using static instructions with literal field");
11514 /* The special_static_fields field is init'd in mono_class_vtable, so it needs
11515 * to be called here.
11517 if (!context_used && !(cfg->opt & MONO_OPT_SHARED)) {
11518 mono_class_vtable (cfg->domain, klass);
11519 CHECK_TYPELOAD (klass);
11521 mono_domain_lock (cfg->domain);
11522 if (cfg->domain->special_static_fields)
11523 addr = g_hash_table_lookup (cfg->domain->special_static_fields, field);
11524 mono_domain_unlock (cfg->domain);
11526 is_special_static = mono_class_field_is_special_static (field);
11528 if (is_special_static && ((gsize)addr & 0x80000000) == 0)
11529 thread_ins = mono_get_thread_intrinsic (cfg);
11533 /* Generate IR to compute the field address */
11534 if (is_special_static && ((gsize)addr & 0x80000000) == 0 && thread_ins && !(cfg->opt & MONO_OPT_SHARED) && !context_used) {
11536 * Fast access to TLS data
11537 * Inline version of get_thread_static_data () in
11541 int idx, static_data_reg, array_reg, dreg;
11543 GSHAREDVT_FAILURE (op);
11545 MONO_ADD_INS (cfg->cbb, thread_ins);
11546 static_data_reg = alloc_ireg (cfg);
11547 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, static_data_reg, thread_ins->dreg, MONO_STRUCT_OFFSET (MonoInternalThread, static_data));
11549 if (cfg->compile_aot) {
11550 int offset_reg, offset2_reg, idx_reg;
11552 /* For TLS variables, this will return the TLS offset */
11553 EMIT_NEW_SFLDACONST (cfg, ins, field);
11554 offset_reg = ins->dreg;
11555 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_IAND_IMM, offset_reg, offset_reg, 0x7fffffff);
11556 idx_reg = alloc_ireg (cfg);
11557 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_IAND_IMM, idx_reg, offset_reg, 0x3f);
11558 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ISHL_IMM, idx_reg, idx_reg, sizeof (gpointer) == 8 ? 3 : 2);
11559 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, static_data_reg, static_data_reg, idx_reg);
11560 array_reg = alloc_ireg (cfg);
11561 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, array_reg, static_data_reg, 0);
11562 offset2_reg = alloc_ireg (cfg);
11563 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ISHR_UN_IMM, offset2_reg, offset_reg, 6);
11564 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_IAND_IMM, offset2_reg, offset2_reg, 0x1ffffff);
11565 dreg = alloc_ireg (cfg);
11566 EMIT_NEW_BIALU (cfg, ins, OP_PADD, dreg, array_reg, offset2_reg);
11568 offset = (gsize)addr & 0x7fffffff;
11569 idx = offset & 0x3f;
11571 array_reg = alloc_ireg (cfg);
11572 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, array_reg, static_data_reg, idx * sizeof (gpointer));
11573 dreg = alloc_ireg (cfg);
11574 EMIT_NEW_BIALU_IMM (cfg, ins, OP_ADD_IMM, dreg, array_reg, ((offset >> 6) & 0x1ffffff));
11576 } else if ((cfg->opt & MONO_OPT_SHARED) ||
11577 (cfg->compile_aot && is_special_static) ||
11578 (context_used && is_special_static)) {
11579 MonoInst *iargs [2];
11581 g_assert (field->parent);
11582 EMIT_NEW_DOMAINCONST (cfg, iargs [0]);
11583 if (context_used) {
11584 iargs [1] = emit_get_rgctx_field (cfg, context_used,
11585 field, MONO_RGCTX_INFO_CLASS_FIELD);
11587 EMIT_NEW_FIELDCONST (cfg, iargs [1], field);
11589 ins = mono_emit_jit_icall (cfg, mono_class_static_field_address, iargs);
11590 } else if (context_used) {
11591 MonoInst *static_data;
11594 g_print ("sharing static field access in %s.%s.%s - depth %d offset %d\n",
11595 method->klass->name_space, method->klass->name, method->name,
11596 depth, field->offset);
11599 if (mono_class_needs_cctor_run (klass, method))
11600 emit_class_init (cfg, klass);
11603 * The pointer we're computing here is
11605 * super_info.static_data + field->offset
11607 static_data = emit_get_rgctx_klass (cfg, context_used,
11608 klass, MONO_RGCTX_INFO_STATIC_DATA);
11610 if (mini_is_gsharedvt_klass (klass)) {
11611 MonoInst *offset_ins;
11613 offset_ins = emit_get_rgctx_field (cfg, context_used, field, MONO_RGCTX_INFO_FIELD_OFFSET);
11614 /* The value is offset by 1 */
11615 EMIT_NEW_BIALU_IMM (cfg, ins, OP_PSUB_IMM, offset_ins->dreg, offset_ins->dreg, 1);
11616 dreg = alloc_ireg_mp (cfg);
11617 EMIT_NEW_BIALU (cfg, ins, OP_PADD, dreg, static_data->dreg, offset_ins->dreg);
11618 } else if (field->offset == 0) {
11621 int addr_reg = mono_alloc_preg (cfg);
11622 EMIT_NEW_BIALU_IMM (cfg, ins, OP_PADD_IMM, addr_reg, static_data->dreg, field->offset);
11624 } else if ((cfg->opt & MONO_OPT_SHARED) || (cfg->compile_aot && addr)) {
11625 MonoInst *iargs [2];
11627 g_assert (field->parent);
11628 EMIT_NEW_DOMAINCONST (cfg, iargs [0]);
11629 EMIT_NEW_FIELDCONST (cfg, iargs [1], field);
11630 ins = mono_emit_jit_icall (cfg, mono_class_static_field_address, iargs);
11632 MonoVTable *vtable = NULL;
11634 if (!cfg->compile_aot)
11635 vtable = mono_class_vtable (cfg->domain, klass);
11636 CHECK_TYPELOAD (klass);
11639 if (mini_field_access_needs_cctor_run (cfg, method, klass, vtable)) {
11640 if (!(g_slist_find (class_inits, klass))) {
11641 emit_class_init (cfg, klass);
11642 if (cfg->verbose_level > 2)
11643 printf ("class %s.%s needs init call for %s\n", klass->name_space, klass->name, mono_field_get_name (field));
11644 class_inits = g_slist_prepend (class_inits, klass);
11647 if (cfg->run_cctors) {
11648 /* This makes so that inline cannot trigger */
11649 /* .cctors: too many apps depend on them */
11650 /* running with a specific order... */
11652 if (! vtable->initialized)
11653 INLINE_FAILURE ("class init");
11654 if (!mono_runtime_class_init_full (vtable, &cfg->error)) {
11655 mono_cfg_set_exception (cfg, MONO_EXCEPTION_MONO_ERROR);
11656 g_assert_not_reached ();
11657 goto exception_exit;
11661 if (cfg->compile_aot)
11662 EMIT_NEW_SFLDACONST (cfg, ins, field);
11665 addr = (char*)mono_vtable_get_static_field_data (vtable) + field->offset;
11667 EMIT_NEW_PCONST (cfg, ins, addr);
11670 MonoInst *iargs [1];
11671 EMIT_NEW_ICONST (cfg, iargs [0], GPOINTER_TO_UINT (addr));
11672 ins = mono_emit_jit_icall (cfg, mono_get_special_static_data, iargs);
11676 /* Generate IR to do the actual load/store operation */
11678 if ((op == CEE_STFLD || op == CEE_STSFLD) && (ins_flag & MONO_INST_VOLATILE)) {
11679 /* Volatile stores have release semantics, see 12.6.7 in Ecma 335 */
11680 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_REL);
11683 if (op == CEE_LDSFLDA) {
11684 ins->klass = mono_class_from_mono_type (ftype);
11685 ins->type = STACK_PTR;
11687 } else if (op == CEE_STSFLD) {
11690 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, store, ftype, ins->dreg, 0, store_val->dreg);
11691 store->flags |= ins_flag;
11693 gboolean is_const = FALSE;
11694 MonoVTable *vtable = NULL;
11695 gpointer addr = NULL;
11697 if (!context_used) {
11698 vtable = mono_class_vtable (cfg->domain, klass);
11699 CHECK_TYPELOAD (klass);
11701 if ((ftype->attrs & FIELD_ATTRIBUTE_INIT_ONLY) && (((addr = mono_aot_readonly_field_override (field)) != NULL) ||
11702 (!context_used && !((cfg->opt & MONO_OPT_SHARED) || cfg->compile_aot) && vtable->initialized))) {
11703 int ro_type = ftype->type;
11705 addr = (char*)mono_vtable_get_static_field_data (vtable) + field->offset;
11706 if (ro_type == MONO_TYPE_VALUETYPE && ftype->data.klass->enumtype) {
11707 ro_type = mono_class_enum_basetype (ftype->data.klass)->type;
11710 GSHAREDVT_FAILURE (op);
11712 /* printf ("RO-FIELD %s.%s:%s\n", klass->name_space, klass->name, mono_field_get_name (field));*/
11715 case MONO_TYPE_BOOLEAN:
11717 EMIT_NEW_ICONST (cfg, *sp, *((guint8 *)addr));
11721 EMIT_NEW_ICONST (cfg, *sp, *((gint8 *)addr));
11724 case MONO_TYPE_CHAR:
11726 EMIT_NEW_ICONST (cfg, *sp, *((guint16 *)addr));
11730 EMIT_NEW_ICONST (cfg, *sp, *((gint16 *)addr));
11735 EMIT_NEW_ICONST (cfg, *sp, *((gint32 *)addr));
11739 EMIT_NEW_ICONST (cfg, *sp, *((guint32 *)addr));
11744 case MONO_TYPE_PTR:
11745 case MONO_TYPE_FNPTR:
11746 EMIT_NEW_PCONST (cfg, *sp, *((gpointer *)addr));
11747 type_to_eval_stack_type ((cfg), field->type, *sp);
11750 case MONO_TYPE_STRING:
11751 case MONO_TYPE_OBJECT:
11752 case MONO_TYPE_CLASS:
11753 case MONO_TYPE_SZARRAY:
11754 case MONO_TYPE_ARRAY:
11755 if (!mono_gc_is_moving ()) {
11756 EMIT_NEW_PCONST (cfg, *sp, *((gpointer *)addr));
11757 type_to_eval_stack_type ((cfg), field->type, *sp);
11765 EMIT_NEW_I8CONST (cfg, *sp, *((gint64 *)addr));
11770 case MONO_TYPE_VALUETYPE:
11780 CHECK_STACK_OVF (1);
11782 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, load, field->type, ins->dreg, 0);
11783 load->flags |= ins_flag;
11789 if ((op == CEE_LDFLD || op == CEE_LDSFLD) && (ins_flag & MONO_INST_VOLATILE)) {
11790 /* Volatile loads have acquire semantics, see 12.6.7 in Ecma 335 */
11791 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_ACQ);
11802 token = read32 (ip + 1);
11803 klass = mini_get_class (method, token, generic_context);
11804 CHECK_TYPELOAD (klass);
11805 if (ins_flag & MONO_INST_VOLATILE) {
11806 /* Volatile stores have release semantics, see 12.6.7 in Ecma 335 */
11807 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_REL);
11809 /* FIXME: should check item at sp [1] is compatible with the type of the store. */
11810 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, sp [0]->dreg, 0, sp [1]->dreg);
11811 ins->flags |= ins_flag;
11812 if (cfg->gen_write_barriers && cfg->method->wrapper_type != MONO_WRAPPER_WRITE_BARRIER &&
11813 generic_class_is_reference_type (cfg, klass)) {
11814 /* insert call to write barrier */
11815 emit_write_barrier (cfg, sp [0], sp [1]);
11827 const char *data_ptr;
11829 guint32 field_token;
11835 token = read32 (ip + 1);
11837 klass = mini_get_class (method, token, generic_context);
11838 CHECK_TYPELOAD (klass);
11840 context_used = mini_class_check_context_used (cfg, klass);
11842 if (sp [0]->type == STACK_I8 || (SIZEOF_VOID_P == 8 && sp [0]->type == STACK_PTR)) {
11843 MONO_INST_NEW (cfg, ins, OP_LCONV_TO_OVF_U4);
11844 ins->sreg1 = sp [0]->dreg;
11845 ins->type = STACK_I4;
11846 ins->dreg = alloc_ireg (cfg);
11847 MONO_ADD_INS (cfg->cbb, ins);
11848 *sp = mono_decompose_opcode (cfg, ins);
11851 if (context_used) {
11852 MonoInst *args [3];
11853 MonoClass *array_class = mono_array_class_get (klass, 1);
11854 MonoMethod *managed_alloc = mono_gc_get_managed_array_allocator (array_class);
11856 /* FIXME: Use OP_NEWARR and decompose later to help abcrem */
11859 args [0] = emit_get_rgctx_klass (cfg, context_used,
11860 array_class, MONO_RGCTX_INFO_VTABLE);
11865 ins = mono_emit_method_call (cfg, managed_alloc, args, NULL);
11867 ins = mono_emit_jit_icall (cfg, ves_icall_array_new_specific, args);
11869 if (cfg->opt & MONO_OPT_SHARED) {
11870 /* Decompose now to avoid problems with references to the domainvar */
11871 MonoInst *iargs [3];
11873 EMIT_NEW_DOMAINCONST (cfg, iargs [0]);
11874 EMIT_NEW_CLASSCONST (cfg, iargs [1], klass);
11875 iargs [2] = sp [0];
11877 ins = mono_emit_jit_icall (cfg, mono_array_new, iargs);
11879 /* Decompose later since it is needed by abcrem */
11880 MonoClass *array_type = mono_array_class_get (klass, 1);
11881 mono_class_vtable (cfg->domain, array_type);
11882 CHECK_TYPELOAD (array_type);
11884 MONO_INST_NEW (cfg, ins, OP_NEWARR);
11885 ins->dreg = alloc_ireg_ref (cfg);
11886 ins->sreg1 = sp [0]->dreg;
11887 ins->inst_newa_class = klass;
11888 ins->type = STACK_OBJ;
11889 ins->klass = array_type;
11890 MONO_ADD_INS (cfg->cbb, ins);
11891 cfg->flags |= MONO_CFG_HAS_ARRAY_ACCESS;
11892 cfg->cbb->has_array_access = TRUE;
11894 /* Needed so mono_emit_load_get_addr () gets called */
11895 mono_get_got_var (cfg);
11905 * we inline/optimize the initialization sequence if possible.
11906 * we should also allocate the array as not cleared, since we spend as much time clearing to 0 as initializing
11907 * for small sizes open code the memcpy
11908 * ensure the rva field is big enough
11910 if ((cfg->opt & MONO_OPT_INTRINS) && ip + 6 < end && ip_in_bb (cfg, cfg->cbb, ip + 6) && (len_ins->opcode == OP_ICONST) && (data_ptr = initialize_array_data (method, cfg->compile_aot, ip, klass, len_ins->inst_c0, &data_size, &field_token))) {
11911 MonoMethod *memcpy_method = get_memcpy_method ();
11912 MonoInst *iargs [3];
11913 int add_reg = alloc_ireg_mp (cfg);
11915 EMIT_NEW_BIALU_IMM (cfg, iargs [0], OP_PADD_IMM, add_reg, ins->dreg, MONO_STRUCT_OFFSET (MonoArray, vector));
11916 if (cfg->compile_aot) {
11917 EMIT_NEW_AOTCONST_TOKEN (cfg, iargs [1], MONO_PATCH_INFO_RVA, method->klass->image, GPOINTER_TO_UINT(field_token), STACK_PTR, NULL);
11919 EMIT_NEW_PCONST (cfg, iargs [1], (char*)data_ptr);
11921 EMIT_NEW_ICONST (cfg, iargs [2], data_size);
11922 mono_emit_method_call (cfg, memcpy_method, iargs, NULL);
11931 if (sp [0]->type != STACK_OBJ)
11934 MONO_INST_NEW (cfg, ins, OP_LDLEN);
11935 ins->dreg = alloc_preg (cfg);
11936 ins->sreg1 = sp [0]->dreg;
11937 ins->type = STACK_I4;
11938 /* This flag will be inherited by the decomposition */
11939 ins->flags |= MONO_INST_FAULT;
11940 MONO_ADD_INS (cfg->cbb, ins);
11941 cfg->flags |= MONO_CFG_HAS_ARRAY_ACCESS;
11942 cfg->cbb->has_array_access = TRUE;
11950 if (sp [0]->type != STACK_OBJ)
11953 cfg->flags |= MONO_CFG_HAS_LDELEMA;
11955 klass = mini_get_class (method, read32 (ip + 1), generic_context);
11956 CHECK_TYPELOAD (klass);
11957 /* we need to make sure that this array is exactly the type it needs
11958 * to be for correctness. the wrappers are lax with their usage
11959 * so we need to ignore them here
11961 if (!klass->valuetype && method->wrapper_type == MONO_WRAPPER_NONE && !readonly) {
11962 MonoClass *array_class = mono_array_class_get (klass, 1);
11963 mini_emit_check_array_type (cfg, sp [0], array_class);
11964 CHECK_TYPELOAD (array_class);
11968 ins = mini_emit_ldelema_1_ins (cfg, klass, sp [0], sp [1], TRUE);
11973 case CEE_LDELEM_I1:
11974 case CEE_LDELEM_U1:
11975 case CEE_LDELEM_I2:
11976 case CEE_LDELEM_U2:
11977 case CEE_LDELEM_I4:
11978 case CEE_LDELEM_U4:
11979 case CEE_LDELEM_I8:
11981 case CEE_LDELEM_R4:
11982 case CEE_LDELEM_R8:
11983 case CEE_LDELEM_REF: {
11989 if (*ip == CEE_LDELEM) {
11991 token = read32 (ip + 1);
11992 klass = mini_get_class (method, token, generic_context);
11993 CHECK_TYPELOAD (klass);
11994 mono_class_init (klass);
11997 klass = array_access_to_klass (*ip);
11999 if (sp [0]->type != STACK_OBJ)
12002 cfg->flags |= MONO_CFG_HAS_LDELEMA;
12004 if (mini_is_gsharedvt_variable_klass (klass)) {
12005 // FIXME-VT: OP_ICONST optimization
12006 addr = mini_emit_ldelema_1_ins (cfg, klass, sp [0], sp [1], TRUE);
12007 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, addr->dreg, 0);
12008 ins->opcode = OP_LOADV_MEMBASE;
12009 } else if (sp [1]->opcode == OP_ICONST) {
12010 int array_reg = sp [0]->dreg;
12011 int index_reg = sp [1]->dreg;
12012 int offset = (mono_class_array_element_size (klass) * sp [1]->inst_c0) + MONO_STRUCT_OFFSET (MonoArray, vector);
12014 if (SIZEOF_REGISTER == 8 && COMPILE_LLVM (cfg))
12015 MONO_EMIT_NEW_UNALU (cfg, OP_ZEXT_I4, index_reg, index_reg);
12017 MONO_EMIT_BOUNDS_CHECK (cfg, array_reg, MonoArray, max_length, index_reg);
12018 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, array_reg, offset);
12020 addr = mini_emit_ldelema_1_ins (cfg, klass, sp [0], sp [1], TRUE);
12021 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, addr->dreg, 0);
12024 if (*ip == CEE_LDELEM)
12031 case CEE_STELEM_I1:
12032 case CEE_STELEM_I2:
12033 case CEE_STELEM_I4:
12034 case CEE_STELEM_I8:
12035 case CEE_STELEM_R4:
12036 case CEE_STELEM_R8:
12037 case CEE_STELEM_REF:
12042 cfg->flags |= MONO_CFG_HAS_LDELEMA;
12044 if (*ip == CEE_STELEM) {
12046 token = read32 (ip + 1);
12047 klass = mini_get_class (method, token, generic_context);
12048 CHECK_TYPELOAD (klass);
12049 mono_class_init (klass);
12052 klass = array_access_to_klass (*ip);
12054 if (sp [0]->type != STACK_OBJ)
12057 emit_array_store (cfg, klass, sp, TRUE);
12059 if (*ip == CEE_STELEM)
12066 case CEE_CKFINITE: {
12070 if (cfg->llvm_only) {
12071 MonoInst *iargs [1];
12073 iargs [0] = sp [0];
12074 *sp++ = mono_emit_jit_icall (cfg, mono_ckfinite, iargs);
12076 MONO_INST_NEW (cfg, ins, OP_CKFINITE);
12077 ins->sreg1 = sp [0]->dreg;
12078 ins->dreg = alloc_freg (cfg);
12079 ins->type = STACK_R8;
12080 MONO_ADD_INS (cfg->cbb, ins);
12082 *sp++ = mono_decompose_opcode (cfg, ins);
12088 case CEE_REFANYVAL: {
12089 MonoInst *src_var, *src;
12091 int klass_reg = alloc_preg (cfg);
12092 int dreg = alloc_preg (cfg);
12094 GSHAREDVT_FAILURE (*ip);
12097 MONO_INST_NEW (cfg, ins, *ip);
12100 klass = mini_get_class (method, read32 (ip + 1), generic_context);
12101 CHECK_TYPELOAD (klass);
12103 context_used = mini_class_check_context_used (cfg, klass);
12106 src_var = get_vreg_to_inst (cfg, sp [0]->dreg);
12108 src_var = mono_compile_create_var_for_vreg (cfg, &mono_defaults.typed_reference_class->byval_arg, OP_LOCAL, sp [0]->dreg);
12109 EMIT_NEW_VARLOADA (cfg, src, src_var, src_var->inst_vtype);
12110 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, src->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, klass));
12112 if (context_used) {
12113 MonoInst *klass_ins;
12115 klass_ins = emit_get_rgctx_klass (cfg, context_used,
12116 klass, MONO_RGCTX_INFO_KLASS);
12119 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, klass_reg, klass_ins->dreg);
12120 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException");
12122 mini_emit_class_check (cfg, klass_reg, klass);
12124 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, dreg, src->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, value));
12125 ins->type = STACK_MP;
12126 ins->klass = klass;
12131 case CEE_MKREFANY: {
12132 MonoInst *loc, *addr;
12134 GSHAREDVT_FAILURE (*ip);
12137 MONO_INST_NEW (cfg, ins, *ip);
12140 klass = mini_get_class (method, read32 (ip + 1), generic_context);
12141 CHECK_TYPELOAD (klass);
12143 context_used = mini_class_check_context_used (cfg, klass);
12145 loc = mono_compile_create_var (cfg, &mono_defaults.typed_reference_class->byval_arg, OP_LOCAL);
12146 EMIT_NEW_TEMPLOADA (cfg, addr, loc->inst_c0);
12148 if (context_used) {
12149 MonoInst *const_ins;
12150 int type_reg = alloc_preg (cfg);
12152 const_ins = emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_KLASS);
12153 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREP_MEMBASE_REG, addr->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, klass), const_ins->dreg);
12154 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ADD_IMM, type_reg, const_ins->dreg, MONO_STRUCT_OFFSET (MonoClass, byval_arg));
12155 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREP_MEMBASE_REG, addr->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, type), type_reg);
12156 } else if (cfg->compile_aot) {
12157 int const_reg = alloc_preg (cfg);
12158 int type_reg = alloc_preg (cfg);
12160 MONO_EMIT_NEW_CLASSCONST (cfg, const_reg, klass);
12161 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREP_MEMBASE_REG, addr->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, klass), const_reg);
12162 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ADD_IMM, type_reg, const_reg, MONO_STRUCT_OFFSET (MonoClass, byval_arg));
12163 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREP_MEMBASE_REG, addr->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, type), type_reg);
12165 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREP_MEMBASE_IMM, addr->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, type), &klass->byval_arg);
12166 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREP_MEMBASE_IMM, addr->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, klass), klass);
12168 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREP_MEMBASE_REG, addr->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, value), sp [0]->dreg);
12170 EMIT_NEW_TEMPLOAD (cfg, ins, loc->inst_c0);
12171 ins->type = STACK_VTYPE;
12172 ins->klass = mono_defaults.typed_reference_class;
12177 case CEE_LDTOKEN: {
12179 MonoClass *handle_class;
12181 CHECK_STACK_OVF (1);
12184 n = read32 (ip + 1);
12186 if (method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD ||
12187 method->wrapper_type == MONO_WRAPPER_SYNCHRONIZED) {
12188 handle = mono_method_get_wrapper_data (method, n);
12189 handle_class = (MonoClass *)mono_method_get_wrapper_data (method, n + 1);
12190 if (handle_class == mono_defaults.typehandle_class)
12191 handle = &((MonoClass*)handle)->byval_arg;
12194 handle = mono_ldtoken_checked (image, n, &handle_class, generic_context, &cfg->error);
12199 mono_class_init (handle_class);
12200 if (cfg->gshared) {
12201 if (mono_metadata_token_table (n) == MONO_TABLE_TYPEDEF ||
12202 mono_metadata_token_table (n) == MONO_TABLE_TYPEREF) {
12203 /* This case handles ldtoken
12204 of an open type, like for
12207 } else if (handle_class == mono_defaults.typehandle_class) {
12208 context_used = mini_class_check_context_used (cfg, mono_class_from_mono_type ((MonoType *)handle));
12209 } else if (handle_class == mono_defaults.fieldhandle_class)
12210 context_used = mini_class_check_context_used (cfg, ((MonoClassField*)handle)->parent);
12211 else if (handle_class == mono_defaults.methodhandle_class)
12212 context_used = mini_method_check_context_used (cfg, (MonoMethod *)handle);
12214 g_assert_not_reached ();
12217 if ((cfg->opt & MONO_OPT_SHARED) &&
12218 method->wrapper_type != MONO_WRAPPER_DYNAMIC_METHOD &&
12219 method->wrapper_type != MONO_WRAPPER_SYNCHRONIZED) {
12220 MonoInst *addr, *vtvar, *iargs [3];
12221 int method_context_used;
12223 method_context_used = mini_method_check_context_used (cfg, method);
12225 vtvar = mono_compile_create_var (cfg, &handle_class->byval_arg, OP_LOCAL);
12227 EMIT_NEW_IMAGECONST (cfg, iargs [0], image);
12228 EMIT_NEW_ICONST (cfg, iargs [1], n);
12229 if (method_context_used) {
12230 iargs [2] = emit_get_rgctx_method (cfg, method_context_used,
12231 method, MONO_RGCTX_INFO_METHOD);
12232 ins = mono_emit_jit_icall (cfg, mono_ldtoken_wrapper_generic_shared, iargs);
12234 EMIT_NEW_PCONST (cfg, iargs [2], generic_context);
12235 ins = mono_emit_jit_icall (cfg, mono_ldtoken_wrapper, iargs);
12237 EMIT_NEW_TEMPLOADA (cfg, addr, vtvar->inst_c0);
12239 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, addr->dreg, 0, ins->dreg);
12241 EMIT_NEW_TEMPLOAD (cfg, ins, vtvar->inst_c0);
12243 if ((ip + 5 < end) && ip_in_bb (cfg, cfg->cbb, ip + 5) &&
12244 ((ip [5] == CEE_CALL) || (ip [5] == CEE_CALLVIRT)) &&
12245 (cmethod = mini_get_method (cfg, method, read32 (ip + 6), NULL, generic_context)) &&
12246 (cmethod->klass == mono_defaults.systemtype_class) &&
12247 (strcmp (cmethod->name, "GetTypeFromHandle") == 0)) {
12248 MonoClass *tclass = mono_class_from_mono_type ((MonoType *)handle);
12250 mono_class_init (tclass);
12251 if (context_used) {
12252 ins = emit_get_rgctx_klass (cfg, context_used,
12253 tclass, MONO_RGCTX_INFO_REFLECTION_TYPE);
12254 } else if (cfg->compile_aot) {
12255 if (method->wrapper_type) {
12256 mono_error_init (&error); //got to do it since there are multiple conditionals below
12257 if (mono_class_get_checked (tclass->image, tclass->type_token, &error) == tclass && !generic_context) {
12258 /* Special case for static synchronized wrappers */
12259 EMIT_NEW_TYPE_FROM_HANDLE_CONST (cfg, ins, tclass->image, tclass->type_token, generic_context);
12261 mono_error_cleanup (&error); /* FIXME don't swallow the error */
12262 /* FIXME: n is not a normal token */
12264 EMIT_NEW_PCONST (cfg, ins, NULL);
12267 EMIT_NEW_TYPE_FROM_HANDLE_CONST (cfg, ins, image, n, generic_context);
12270 MonoReflectionType *rt = mono_type_get_object_checked (cfg->domain, (MonoType *)handle, &cfg->error);
12272 EMIT_NEW_PCONST (cfg, ins, rt);
12274 ins->type = STACK_OBJ;
12275 ins->klass = cmethod->klass;
12278 MonoInst *addr, *vtvar;
12280 vtvar = mono_compile_create_var (cfg, &handle_class->byval_arg, OP_LOCAL);
12282 if (context_used) {
12283 if (handle_class == mono_defaults.typehandle_class) {
12284 ins = emit_get_rgctx_klass (cfg, context_used,
12285 mono_class_from_mono_type ((MonoType *)handle),
12286 MONO_RGCTX_INFO_TYPE);
12287 } else if (handle_class == mono_defaults.methodhandle_class) {
12288 ins = emit_get_rgctx_method (cfg, context_used,
12289 (MonoMethod *)handle, MONO_RGCTX_INFO_METHOD);
12290 } else if (handle_class == mono_defaults.fieldhandle_class) {
12291 ins = emit_get_rgctx_field (cfg, context_used,
12292 (MonoClassField *)handle, MONO_RGCTX_INFO_CLASS_FIELD);
12294 g_assert_not_reached ();
12296 } else if (cfg->compile_aot) {
12297 EMIT_NEW_LDTOKENCONST (cfg, ins, image, n, generic_context);
12299 EMIT_NEW_PCONST (cfg, ins, handle);
12301 EMIT_NEW_TEMPLOADA (cfg, addr, vtvar->inst_c0);
12302 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, addr->dreg, 0, ins->dreg);
12303 EMIT_NEW_TEMPLOAD (cfg, ins, vtvar->inst_c0);
12313 MONO_INST_NEW (cfg, ins, OP_THROW);
12315 ins->sreg1 = sp [0]->dreg;
12317 cfg->cbb->out_of_line = TRUE;
12318 MONO_ADD_INS (cfg->cbb, ins);
12319 MONO_INST_NEW (cfg, ins, OP_NOT_REACHED);
12320 MONO_ADD_INS (cfg->cbb, ins);
12323 link_bblock (cfg, cfg->cbb, end_bblock);
12324 start_new_bblock = 1;
12325 /* This can complicate code generation for llvm since the return value might not be defined */
12326 if (COMPILE_LLVM (cfg))
12327 INLINE_FAILURE ("throw");
12329 case CEE_ENDFINALLY:
12330 /* mono_save_seq_point_info () depends on this */
12331 if (sp != stack_start)
12332 emit_seq_point (cfg, method, ip, FALSE, FALSE);
12333 MONO_INST_NEW (cfg, ins, OP_ENDFINALLY);
12334 MONO_ADD_INS (cfg->cbb, ins);
12336 start_new_bblock = 1;
12339 * Control will leave the method so empty the stack, otherwise
12340 * the next basic block will start with a nonempty stack.
12342 while (sp != stack_start) {
12347 case CEE_LEAVE_S: {
12350 if (*ip == CEE_LEAVE) {
12352 target = ip + 5 + (gint32)read32(ip + 1);
12355 target = ip + 2 + (signed char)(ip [1]);
12358 /* empty the stack */
12359 while (sp != stack_start) {
12364 * If this leave statement is in a catch block, check for a
12365 * pending exception, and rethrow it if necessary.
12366 * We avoid doing this in runtime invoke wrappers, since those are called
12367 * by native code which excepts the wrapper to catch all exceptions.
12369 for (i = 0; i < header->num_clauses; ++i) {
12370 MonoExceptionClause *clause = &header->clauses [i];
12373 * Use <= in the final comparison to handle clauses with multiple
12374 * leave statements, like in bug #78024.
12375 * The ordering of the exception clauses guarantees that we find the
12376 * innermost clause.
12378 if (MONO_OFFSET_IN_HANDLER (clause, ip - header->code) && (clause->flags == MONO_EXCEPTION_CLAUSE_NONE) && (ip - header->code + ((*ip == CEE_LEAVE) ? 5 : 2)) <= (clause->handler_offset + clause->handler_len) && method->wrapper_type != MONO_WRAPPER_RUNTIME_INVOKE) {
12380 MonoBasicBlock *dont_throw;
12385 NEW_TEMPLOAD (cfg, load, mono_find_exvar_for_offset (cfg, clause->handler_offset)->inst_c0);
12388 exc_ins = mono_emit_jit_icall (cfg, mono_thread_get_undeniable_exception, NULL);
12390 NEW_BBLOCK (cfg, dont_throw);
12393 * Currently, we always rethrow the abort exception, despite the
12394 * fact that this is not correct. See thread6.cs for an example.
12395 * But propagating the abort exception is more important than
12396 * getting the sematics right.
12398 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, exc_ins->dreg, 0);
12399 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, dont_throw);
12400 MONO_EMIT_NEW_UNALU (cfg, OP_THROW, -1, exc_ins->dreg);
12402 MONO_START_BB (cfg, dont_throw);
12407 cfg->cbb->try_end = (intptr_t)(ip - header->code);
12410 if ((handlers = mono_find_final_block (cfg, ip, target, MONO_EXCEPTION_CLAUSE_FINALLY))) {
12412 MonoExceptionClause *clause;
12414 for (tmp = handlers; tmp; tmp = tmp->next) {
12415 clause = (MonoExceptionClause *)tmp->data;
12416 tblock = cfg->cil_offset_to_bb [clause->handler_offset];
12418 link_bblock (cfg, cfg->cbb, tblock);
12419 MONO_INST_NEW (cfg, ins, OP_CALL_HANDLER);
12420 ins->inst_target_bb = tblock;
12421 ins->inst_eh_block = clause;
12422 MONO_ADD_INS (cfg->cbb, ins);
12423 cfg->cbb->has_call_handler = 1;
12424 if (COMPILE_LLVM (cfg)) {
12425 MonoBasicBlock *target_bb;
12428 * Link the finally bblock with the target, since it will
12429 * conceptually branch there.
12431 GET_BBLOCK (cfg, tblock, cfg->cil_start + clause->handler_offset + clause->handler_len - 1);
12432 GET_BBLOCK (cfg, target_bb, target);
12433 link_bblock (cfg, tblock, target_bb);
12436 g_list_free (handlers);
12439 MONO_INST_NEW (cfg, ins, OP_BR);
12440 MONO_ADD_INS (cfg->cbb, ins);
12441 GET_BBLOCK (cfg, tblock, target);
12442 link_bblock (cfg, cfg->cbb, tblock);
12443 ins->inst_target_bb = tblock;
12445 start_new_bblock = 1;
12447 if (*ip == CEE_LEAVE)
12456 * Mono specific opcodes
12458 case MONO_CUSTOM_PREFIX: {
12460 g_assert (method->wrapper_type != MONO_WRAPPER_NONE);
12464 case CEE_MONO_ICALL: {
12466 MonoJitICallInfo *info;
12468 token = read32 (ip + 2);
12469 func = mono_method_get_wrapper_data (method, token);
12470 info = mono_find_jit_icall_by_addr (func);
12472 g_error ("Could not find icall address in wrapper %s", mono_method_full_name (method, 1));
12475 CHECK_STACK (info->sig->param_count);
12476 sp -= info->sig->param_count;
12478 ins = mono_emit_jit_icall (cfg, info->func, sp);
12479 if (!MONO_TYPE_IS_VOID (info->sig->ret))
12483 inline_costs += 10 * num_calls++;
12487 case CEE_MONO_LDPTR_CARD_TABLE:
12488 case CEE_MONO_LDPTR_NURSERY_START:
12489 case CEE_MONO_LDPTR_NURSERY_BITS:
12490 case CEE_MONO_LDPTR_INT_REQ_FLAG: {
12491 CHECK_STACK_OVF (1);
12494 case CEE_MONO_LDPTR_CARD_TABLE:
12495 ins = emit_runtime_constant (cfg, MONO_PATCH_INFO_GC_CARD_TABLE_ADDR, NULL);
12497 case CEE_MONO_LDPTR_NURSERY_START:
12498 ins = emit_runtime_constant (cfg, MONO_PATCH_INFO_GC_NURSERY_START, NULL);
12500 case CEE_MONO_LDPTR_NURSERY_BITS:
12501 ins = emit_runtime_constant (cfg, MONO_PATCH_INFO_GC_NURSERY_BITS, NULL);
12503 case CEE_MONO_LDPTR_INT_REQ_FLAG:
12504 ins = emit_runtime_constant (cfg, MONO_PATCH_INFO_INTERRUPTION_REQUEST_FLAG, NULL);
12510 inline_costs += 10 * num_calls++;
12513 case CEE_MONO_LDPTR: {
12516 CHECK_STACK_OVF (1);
12518 token = read32 (ip + 2);
12520 ptr = mono_method_get_wrapper_data (method, token);
12521 EMIT_NEW_PCONST (cfg, ins, ptr);
12524 inline_costs += 10 * num_calls++;
12525 /* Can't embed random pointers into AOT code */
12529 case CEE_MONO_JIT_ICALL_ADDR: {
12530 MonoJitICallInfo *callinfo;
12533 CHECK_STACK_OVF (1);
12535 token = read32 (ip + 2);
12537 ptr = mono_method_get_wrapper_data (method, token);
12538 callinfo = mono_find_jit_icall_by_addr (ptr);
12539 g_assert (callinfo);
12540 EMIT_NEW_JIT_ICALL_ADDRCONST (cfg, ins, (char*)callinfo->name);
12543 inline_costs += 10 * num_calls++;
12546 case CEE_MONO_ICALL_ADDR: {
12547 MonoMethod *cmethod;
12550 CHECK_STACK_OVF (1);
12552 token = read32 (ip + 2);
12554 cmethod = (MonoMethod *)mono_method_get_wrapper_data (method, token);
12556 if (cfg->compile_aot) {
12557 EMIT_NEW_AOTCONST (cfg, ins, MONO_PATCH_INFO_ICALL_ADDR, cmethod);
12559 ptr = mono_lookup_internal_call (cmethod);
12561 EMIT_NEW_PCONST (cfg, ins, ptr);
12567 case CEE_MONO_VTADDR: {
12568 MonoInst *src_var, *src;
12574 src_var = get_vreg_to_inst (cfg, sp [0]->dreg);
12575 EMIT_NEW_VARLOADA ((cfg), (src), src_var, src_var->inst_vtype);
12580 case CEE_MONO_NEWOBJ: {
12581 MonoInst *iargs [2];
12583 CHECK_STACK_OVF (1);
12585 token = read32 (ip + 2);
12586 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
12587 mono_class_init (klass);
12588 NEW_DOMAINCONST (cfg, iargs [0]);
12589 MONO_ADD_INS (cfg->cbb, iargs [0]);
12590 NEW_CLASSCONST (cfg, iargs [1], klass);
12591 MONO_ADD_INS (cfg->cbb, iargs [1]);
12592 *sp++ = mono_emit_jit_icall (cfg, ves_icall_object_new, iargs);
12594 inline_costs += 10 * num_calls++;
12597 case CEE_MONO_OBJADDR:
12600 MONO_INST_NEW (cfg, ins, OP_MOVE);
12601 ins->dreg = alloc_ireg_mp (cfg);
12602 ins->sreg1 = sp [0]->dreg;
12603 ins->type = STACK_MP;
12604 MONO_ADD_INS (cfg->cbb, ins);
12608 case CEE_MONO_LDNATIVEOBJ:
12610 * Similar to LDOBJ, but instead load the unmanaged
12611 * representation of the vtype to the stack.
12616 token = read32 (ip + 2);
12617 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
12618 g_assert (klass->valuetype);
12619 mono_class_init (klass);
12622 MonoInst *src, *dest, *temp;
12625 temp = mono_compile_create_var (cfg, &klass->byval_arg, OP_LOCAL);
12626 temp->backend.is_pinvoke = 1;
12627 EMIT_NEW_TEMPLOADA (cfg, dest, temp->inst_c0);
12628 mini_emit_stobj (cfg, dest, src, klass, TRUE);
12630 EMIT_NEW_TEMPLOAD (cfg, dest, temp->inst_c0);
12631 dest->type = STACK_VTYPE;
12632 dest->klass = klass;
12638 case CEE_MONO_RETOBJ: {
12640 * Same as RET, but return the native representation of a vtype
12643 g_assert (cfg->ret);
12644 g_assert (mono_method_signature (method)->pinvoke);
12649 token = read32 (ip + 2);
12650 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
12652 if (!cfg->vret_addr) {
12653 g_assert (cfg->ret_var_is_local);
12655 EMIT_NEW_VARLOADA (cfg, ins, cfg->ret, cfg->ret->inst_vtype);
12657 EMIT_NEW_RETLOADA (cfg, ins);
12659 mini_emit_stobj (cfg, ins, sp [0], klass, TRUE);
12661 if (sp != stack_start)
12664 MONO_INST_NEW (cfg, ins, OP_BR);
12665 ins->inst_target_bb = end_bblock;
12666 MONO_ADD_INS (cfg->cbb, ins);
12667 link_bblock (cfg, cfg->cbb, end_bblock);
12668 start_new_bblock = 1;
12672 case CEE_MONO_CISINST:
12673 case CEE_MONO_CCASTCLASS: {
12678 token = read32 (ip + 2);
12679 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
12680 if (ip [1] == CEE_MONO_CISINST)
12681 ins = handle_cisinst (cfg, klass, sp [0]);
12683 ins = handle_ccastclass (cfg, klass, sp [0]);
12688 case CEE_MONO_SAVE_LMF:
12689 case CEE_MONO_RESTORE_LMF:
12692 case CEE_MONO_CLASSCONST:
12693 CHECK_STACK_OVF (1);
12695 token = read32 (ip + 2);
12696 EMIT_NEW_CLASSCONST (cfg, ins, mono_method_get_wrapper_data (method, token));
12699 inline_costs += 10 * num_calls++;
12701 case CEE_MONO_NOT_TAKEN:
12702 cfg->cbb->out_of_line = TRUE;
12705 case CEE_MONO_TLS: {
12708 CHECK_STACK_OVF (1);
12710 key = (MonoTlsKey)read32 (ip + 2);
12711 g_assert (key < TLS_KEY_NUM);
12713 ins = mono_create_tls_get (cfg, key);
12715 if (cfg->compile_aot) {
12717 MONO_INST_NEW (cfg, ins, OP_TLS_GET);
12718 ins->dreg = alloc_preg (cfg);
12719 ins->type = STACK_PTR;
12721 g_assert_not_reached ();
12724 ins->type = STACK_PTR;
12725 MONO_ADD_INS (cfg->cbb, ins);
12730 case CEE_MONO_DYN_CALL: {
12731 MonoCallInst *call;
12733 /* It would be easier to call a trampoline, but that would put an
12734 * extra frame on the stack, confusing exception handling. So
12735 * implement it inline using an opcode for now.
12738 if (!cfg->dyn_call_var) {
12739 cfg->dyn_call_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
12740 /* prevent it from being register allocated */
12741 cfg->dyn_call_var->flags |= MONO_INST_VOLATILE;
12744 /* Has to use a call inst since it local regalloc expects it */
12745 MONO_INST_NEW_CALL (cfg, call, OP_DYN_CALL);
12746 ins = (MonoInst*)call;
12748 ins->sreg1 = sp [0]->dreg;
12749 ins->sreg2 = sp [1]->dreg;
12750 MONO_ADD_INS (cfg->cbb, ins);
12752 cfg->param_area = MAX (cfg->param_area, cfg->backend->dyn_call_param_area);
12755 inline_costs += 10 * num_calls++;
12759 case CEE_MONO_MEMORY_BARRIER: {
12761 emit_memory_barrier (cfg, (int)read32 (ip + 2));
12765 case CEE_MONO_JIT_ATTACH: {
12766 MonoInst *args [16], *domain_ins;
12767 MonoInst *ad_ins, *jit_tls_ins;
12768 MonoBasicBlock *next_bb = NULL, *call_bb = NULL;
12770 cfg->attach_cookie = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
12771 cfg->attach_dummy = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
12773 if (mono_threads_is_coop_enabled ()) {
12774 /* AOT code is only used in the root domain */
12775 EMIT_NEW_PCONST (cfg, args [0], cfg->compile_aot ? NULL : cfg->domain);
12776 EMIT_NEW_VARLOADA (cfg, args [1], cfg->attach_dummy, cfg->attach_dummy->inst_vtype);
12777 ins = mono_emit_jit_icall (cfg, mono_jit_thread_attach, args);
12778 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, cfg->attach_cookie->dreg, ins->dreg);
12780 EMIT_NEW_PCONST (cfg, ins, NULL);
12781 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, cfg->attach_cookie->dreg, ins->dreg);
12783 ad_ins = mono_get_domain_intrinsic (cfg);
12784 jit_tls_ins = mono_get_jit_tls_intrinsic (cfg);
12786 if (cfg->backend->have_tls_get && ad_ins && jit_tls_ins) {
12787 NEW_BBLOCK (cfg, next_bb);
12788 NEW_BBLOCK (cfg, call_bb);
12790 if (cfg->compile_aot) {
12791 /* AOT code is only used in the root domain */
12792 EMIT_NEW_PCONST (cfg, domain_ins, NULL);
12794 EMIT_NEW_PCONST (cfg, domain_ins, cfg->domain);
12796 MONO_ADD_INS (cfg->cbb, ad_ins);
12797 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, ad_ins->dreg, domain_ins->dreg);
12798 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, call_bb);
12800 MONO_ADD_INS (cfg->cbb, jit_tls_ins);
12801 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, jit_tls_ins->dreg, 0);
12802 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, call_bb);
12804 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, next_bb);
12805 MONO_START_BB (cfg, call_bb);
12808 /* AOT code is only used in the root domain */
12809 EMIT_NEW_PCONST (cfg, args [0], cfg->compile_aot ? NULL : cfg->domain);
12810 EMIT_NEW_PCONST (cfg, args [1], NULL);
12811 ins = mono_emit_jit_icall (cfg, mono_jit_thread_attach, args);
12812 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, cfg->attach_cookie->dreg, ins->dreg);
12815 MONO_START_BB (cfg, next_bb);
12821 case CEE_MONO_JIT_DETACH: {
12822 MonoInst *args [16];
12824 /* Restore the original domain */
12825 dreg = alloc_ireg (cfg);
12826 EMIT_NEW_UNALU (cfg, args [0], OP_MOVE, dreg, cfg->attach_cookie->dreg);
12827 EMIT_NEW_VARLOADA (cfg, args [1], cfg->attach_dummy, cfg->attach_dummy->inst_vtype);
12828 mono_emit_jit_icall (cfg, mono_jit_thread_detach, args);
12832 case CEE_MONO_CALLI_EXTRA_ARG: {
12834 MonoMethodSignature *fsig;
12838 * This is the same as CEE_CALLI, but passes an additional argument
12839 * to the called method in llvmonly mode.
12840 * This is only used by delegate invoke wrappers to call the
12841 * actual delegate method.
12843 g_assert (method->wrapper_type == MONO_WRAPPER_DELEGATE_INVOKE);
12846 token = read32 (ip + 2);
12854 fsig = mini_get_signature (method, token, generic_context);
12856 if (cfg->llvm_only)
12857 cfg->signatures = g_slist_prepend_mempool (cfg->mempool, cfg->signatures, fsig);
12859 n = fsig->param_count + fsig->hasthis + 1;
12866 if (cfg->llvm_only) {
12868 * The lowest bit of 'arg' determines whenever the callee uses the gsharedvt
12869 * cconv. This is set by mono_init_delegate ().
12871 if (cfg->gsharedvt && mini_is_gsharedvt_variable_signature (fsig)) {
12872 MonoInst *callee = addr;
12873 MonoInst *call, *localloc_ins;
12874 MonoBasicBlock *is_gsharedvt_bb, *end_bb;
12875 int low_bit_reg = alloc_preg (cfg);
12877 NEW_BBLOCK (cfg, is_gsharedvt_bb);
12878 NEW_BBLOCK (cfg, end_bb);
12880 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_PAND_IMM, low_bit_reg, arg->dreg, 1);
12881 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, low_bit_reg, 0);
12882 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, is_gsharedvt_bb);
12884 /* Normal case: callee uses a normal cconv, have to add an out wrapper */
12885 addr = emit_get_rgctx_sig (cfg, context_used,
12886 fsig, MONO_RGCTX_INFO_SIG_GSHAREDVT_OUT_TRAMPOLINE_CALLI);
12888 * ADDR points to a gsharedvt-out wrapper, have to pass <callee, arg> as an extra arg.
12890 MONO_INST_NEW (cfg, ins, OP_LOCALLOC_IMM);
12891 ins->dreg = alloc_preg (cfg);
12892 ins->inst_imm = 2 * SIZEOF_VOID_P;
12893 MONO_ADD_INS (cfg->cbb, ins);
12894 localloc_ins = ins;
12895 cfg->flags |= MONO_CFG_HAS_ALLOCA;
12896 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, localloc_ins->dreg, 0, callee->dreg);
12897 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, localloc_ins->dreg, SIZEOF_VOID_P, arg->dreg);
12899 call = emit_extra_arg_calli (cfg, fsig, sp, localloc_ins->dreg, addr);
12900 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
12902 /* Gsharedvt case: callee uses a gsharedvt cconv, no conversion is needed */
12903 MONO_START_BB (cfg, is_gsharedvt_bb);
12904 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_PXOR_IMM, arg->dreg, arg->dreg, 1);
12905 ins = emit_extra_arg_calli (cfg, fsig, sp, arg->dreg, callee);
12906 ins->dreg = call->dreg;
12908 MONO_START_BB (cfg, end_bb);
12910 /* Caller uses a normal calling conv */
12912 MonoInst *callee = addr;
12913 MonoInst *call, *localloc_ins;
12914 MonoBasicBlock *is_gsharedvt_bb, *end_bb;
12915 int low_bit_reg = alloc_preg (cfg);
12917 NEW_BBLOCK (cfg, is_gsharedvt_bb);
12918 NEW_BBLOCK (cfg, end_bb);
12920 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_PAND_IMM, low_bit_reg, arg->dreg, 1);
12921 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, low_bit_reg, 0);
12922 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, is_gsharedvt_bb);
12924 /* Normal case: callee uses a normal cconv, no conversion is needed */
12925 call = emit_extra_arg_calli (cfg, fsig, sp, arg->dreg, callee);
12926 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
12927 /* Gsharedvt case: callee uses a gsharedvt cconv, have to add an in wrapper */
12928 MONO_START_BB (cfg, is_gsharedvt_bb);
12929 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_PXOR_IMM, arg->dreg, arg->dreg, 1);
12930 NEW_AOTCONST (cfg, addr, MONO_PATCH_INFO_GSHAREDVT_IN_WRAPPER, fsig);
12931 MONO_ADD_INS (cfg->cbb, addr);
12933 * ADDR points to a gsharedvt-in wrapper, have to pass <callee, arg> as an extra arg.
12935 MONO_INST_NEW (cfg, ins, OP_LOCALLOC_IMM);
12936 ins->dreg = alloc_preg (cfg);
12937 ins->inst_imm = 2 * SIZEOF_VOID_P;
12938 MONO_ADD_INS (cfg->cbb, ins);
12939 localloc_ins = ins;
12940 cfg->flags |= MONO_CFG_HAS_ALLOCA;
12941 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, localloc_ins->dreg, 0, callee->dreg);
12942 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, localloc_ins->dreg, SIZEOF_VOID_P, arg->dreg);
12944 ins = emit_extra_arg_calli (cfg, fsig, sp, localloc_ins->dreg, addr);
12945 ins->dreg = call->dreg;
12946 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
12948 MONO_START_BB (cfg, end_bb);
12951 /* Same as CEE_CALLI */
12952 if (cfg->gsharedvt && mini_is_gsharedvt_signature (fsig)) {
12954 * We pass the address to the gsharedvt trampoline in the rgctx reg
12956 MonoInst *callee = addr;
12958 addr = emit_get_rgctx_sig (cfg, context_used,
12959 fsig, MONO_RGCTX_INFO_SIG_GSHAREDVT_OUT_TRAMPOLINE_CALLI);
12960 ins = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr, NULL, callee);
12962 ins = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr, NULL, NULL);
12966 if (!MONO_TYPE_IS_VOID (fsig->ret))
12967 *sp++ = mono_emit_widen_call_res (cfg, ins, fsig);
12969 CHECK_CFG_EXCEPTION;
12973 constrained_class = NULL;
12977 g_error ("opcode 0x%02x 0x%02x not handled", MONO_CUSTOM_PREFIX, ip [1]);
12983 case CEE_PREFIX1: {
12986 case CEE_ARGLIST: {
12987 /* somewhat similar to LDTOKEN */
12988 MonoInst *addr, *vtvar;
12989 CHECK_STACK_OVF (1);
12990 vtvar = mono_compile_create_var (cfg, &mono_defaults.argumenthandle_class->byval_arg, OP_LOCAL);
12992 EMIT_NEW_TEMPLOADA (cfg, addr, vtvar->inst_c0);
12993 EMIT_NEW_UNALU (cfg, ins, OP_ARGLIST, -1, addr->dreg);
12995 EMIT_NEW_TEMPLOAD (cfg, ins, vtvar->inst_c0);
12996 ins->type = STACK_VTYPE;
12997 ins->klass = mono_defaults.argumenthandle_class;
13007 MonoInst *cmp, *arg1, *arg2;
13015 * The following transforms:
13016 * CEE_CEQ into OP_CEQ
13017 * CEE_CGT into OP_CGT
13018 * CEE_CGT_UN into OP_CGT_UN
13019 * CEE_CLT into OP_CLT
13020 * CEE_CLT_UN into OP_CLT_UN
13022 MONO_INST_NEW (cfg, cmp, (OP_CEQ - CEE_CEQ) + ip [1]);
13024 MONO_INST_NEW (cfg, ins, cmp->opcode);
13025 cmp->sreg1 = arg1->dreg;
13026 cmp->sreg2 = arg2->dreg;
13027 type_from_op (cfg, cmp, arg1, arg2);
13029 add_widen_op (cfg, cmp, &arg1, &arg2);
13030 if ((arg1->type == STACK_I8) || ((SIZEOF_VOID_P == 8) && ((arg1->type == STACK_PTR) || (arg1->type == STACK_OBJ) || (arg1->type == STACK_MP))))
13031 cmp->opcode = OP_LCOMPARE;
13032 else if (arg1->type == STACK_R4)
13033 cmp->opcode = OP_RCOMPARE;
13034 else if (arg1->type == STACK_R8)
13035 cmp->opcode = OP_FCOMPARE;
13037 cmp->opcode = OP_ICOMPARE;
13038 MONO_ADD_INS (cfg->cbb, cmp);
13039 ins->type = STACK_I4;
13040 ins->dreg = alloc_dreg (cfg, (MonoStackType)ins->type);
13041 type_from_op (cfg, ins, arg1, arg2);
13043 if (cmp->opcode == OP_FCOMPARE || cmp->opcode == OP_RCOMPARE) {
13045 * The backends expect the fceq opcodes to do the
13048 ins->sreg1 = cmp->sreg1;
13049 ins->sreg2 = cmp->sreg2;
13052 MONO_ADD_INS (cfg->cbb, ins);
13058 MonoInst *argconst;
13059 MonoMethod *cil_method;
13061 CHECK_STACK_OVF (1);
13063 n = read32 (ip + 2);
13064 cmethod = mini_get_method (cfg, method, n, NULL, generic_context);
13067 mono_class_init (cmethod->klass);
13069 mono_save_token_info (cfg, image, n, cmethod);
13071 context_used = mini_method_check_context_used (cfg, cmethod);
13073 cil_method = cmethod;
13074 if (!dont_verify && !cfg->skip_visibility && !mono_method_can_access_method (method, cmethod))
13075 METHOD_ACCESS_FAILURE (method, cil_method);
13077 if (mono_security_core_clr_enabled ())
13078 ensure_method_is_allowed_to_call_method (cfg, method, cmethod);
13081 * Optimize the common case of ldftn+delegate creation
13083 if ((sp > stack_start) && (ip + 6 + 5 < end) && ip_in_bb (cfg, cfg->cbb, ip + 6) && (ip [6] == CEE_NEWOBJ)) {
13084 MonoMethod *ctor_method = mini_get_method (cfg, method, read32 (ip + 7), NULL, generic_context);
13085 if (ctor_method && (ctor_method->klass->parent == mono_defaults.multicastdelegate_class)) {
13086 MonoInst *target_ins, *handle_ins;
13087 MonoMethod *invoke;
13088 int invoke_context_used;
13090 invoke = mono_get_delegate_invoke (ctor_method->klass);
13091 if (!invoke || !mono_method_signature (invoke))
13094 invoke_context_used = mini_method_check_context_used (cfg, invoke);
13096 target_ins = sp [-1];
13098 if (mono_security_core_clr_enabled ())
13099 ensure_method_is_allowed_to_call_method (cfg, method, ctor_method);
13101 if (!(cmethod->flags & METHOD_ATTRIBUTE_STATIC)) {
13102 /*LAME IMPL: We must not add a null check for virtual invoke delegates.*/
13103 if (mono_method_signature (invoke)->param_count == mono_method_signature (cmethod)->param_count) {
13104 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, target_ins->dreg, 0);
13105 MONO_EMIT_NEW_COND_EXC (cfg, EQ, "ArgumentException");
13109 /* FIXME: SGEN support */
13110 if (invoke_context_used == 0 || cfg->llvm_only) {
13112 if (cfg->verbose_level > 3)
13113 g_print ("converting (in B%d: stack: %d) %s", cfg->cbb->block_num, (int)(sp - stack_start), mono_disasm_code_one (NULL, method, ip, NULL));
13114 if ((handle_ins = handle_delegate_ctor (cfg, ctor_method->klass, target_ins, cmethod, context_used, FALSE))) {
13117 CHECK_CFG_EXCEPTION;
13127 argconst = emit_get_rgctx_method (cfg, context_used, cmethod, MONO_RGCTX_INFO_METHOD);
13128 ins = mono_emit_jit_icall (cfg, mono_ldftn, &argconst);
13132 inline_costs += 10 * num_calls++;
13135 case CEE_LDVIRTFTN: {
13136 MonoInst *args [2];
13140 n = read32 (ip + 2);
13141 cmethod = mini_get_method (cfg, method, n, NULL, generic_context);
13144 mono_class_init (cmethod->klass);
13146 context_used = mini_method_check_context_used (cfg, cmethod);
13148 if (mono_security_core_clr_enabled ())
13149 ensure_method_is_allowed_to_call_method (cfg, method, cmethod);
13152 * Optimize the common case of ldvirtftn+delegate creation
13154 if ((sp > stack_start) && (ip + 6 + 5 < end) && ip_in_bb (cfg, cfg->cbb, ip + 6) && (ip [6] == CEE_NEWOBJ) && (ip > header->code && ip [-1] == CEE_DUP)) {
13155 MonoMethod *ctor_method = mini_get_method (cfg, method, read32 (ip + 7), NULL, generic_context);
13156 if (ctor_method && (ctor_method->klass->parent == mono_defaults.multicastdelegate_class)) {
13157 MonoInst *target_ins, *handle_ins;
13158 MonoMethod *invoke;
13159 int invoke_context_used;
13160 gboolean is_virtual = cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL;
13162 invoke = mono_get_delegate_invoke (ctor_method->klass);
13163 if (!invoke || !mono_method_signature (invoke))
13166 invoke_context_used = mini_method_check_context_used (cfg, invoke);
13168 target_ins = sp [-1];
13170 if (mono_security_core_clr_enabled ())
13171 ensure_method_is_allowed_to_call_method (cfg, method, ctor_method);
13173 /* FIXME: SGEN support */
13174 if (invoke_context_used == 0 || cfg->llvm_only) {
13176 if (cfg->verbose_level > 3)
13177 g_print ("converting (in B%d: stack: %d) %s", cfg->cbb->block_num, (int)(sp - stack_start), mono_disasm_code_one (NULL, method, ip, NULL));
13178 if ((handle_ins = handle_delegate_ctor (cfg, ctor_method->klass, target_ins, cmethod, context_used, is_virtual))) {
13181 CHECK_CFG_EXCEPTION;
13194 args [1] = emit_get_rgctx_method (cfg, context_used,
13195 cmethod, MONO_RGCTX_INFO_METHOD);
13198 *sp++ = mono_emit_jit_icall (cfg, mono_ldvirtfn_gshared, args);
13200 *sp++ = mono_emit_jit_icall (cfg, mono_ldvirtfn, args);
13203 inline_costs += 10 * num_calls++;
13207 CHECK_STACK_OVF (1);
13209 n = read16 (ip + 2);
13211 EMIT_NEW_ARGLOAD (cfg, ins, n);
13216 CHECK_STACK_OVF (1);
13218 n = read16 (ip + 2);
13220 NEW_ARGLOADA (cfg, ins, n);
13221 MONO_ADD_INS (cfg->cbb, ins);
13229 n = read16 (ip + 2);
13231 if (!dont_verify_stloc && target_type_is_incompatible (cfg, param_types [n], *sp))
13233 EMIT_NEW_ARGSTORE (cfg, ins, n, *sp);
13237 CHECK_STACK_OVF (1);
13239 n = read16 (ip + 2);
13241 EMIT_NEW_LOCLOAD (cfg, ins, n);
13246 unsigned char *tmp_ip;
13247 CHECK_STACK_OVF (1);
13249 n = read16 (ip + 2);
13252 if ((tmp_ip = emit_optimized_ldloca_ir (cfg, ip, end, 2))) {
13258 EMIT_NEW_LOCLOADA (cfg, ins, n);
13267 n = read16 (ip + 2);
13269 if (!dont_verify_stloc && target_type_is_incompatible (cfg, header->locals [n], *sp))
13271 emit_stloc_ir (cfg, sp, header, n);
13278 if (sp != stack_start)
13280 if (cfg->method != method)
13282 * Inlining this into a loop in a parent could lead to
13283 * stack overflows which is different behavior than the
13284 * non-inlined case, thus disable inlining in this case.
13286 INLINE_FAILURE("localloc");
13288 MONO_INST_NEW (cfg, ins, OP_LOCALLOC);
13289 ins->dreg = alloc_preg (cfg);
13290 ins->sreg1 = sp [0]->dreg;
13291 ins->type = STACK_PTR;
13292 MONO_ADD_INS (cfg->cbb, ins);
13294 cfg->flags |= MONO_CFG_HAS_ALLOCA;
13296 ins->flags |= MONO_INST_INIT;
13301 case CEE_ENDFILTER: {
13302 MonoExceptionClause *clause, *nearest;
13307 if ((sp != stack_start) || (sp [0]->type != STACK_I4))
13309 MONO_INST_NEW (cfg, ins, OP_ENDFILTER);
13310 ins->sreg1 = (*sp)->dreg;
13311 MONO_ADD_INS (cfg->cbb, ins);
13312 start_new_bblock = 1;
13316 for (cc = 0; cc < header->num_clauses; ++cc) {
13317 clause = &header->clauses [cc];
13318 if ((clause->flags & MONO_EXCEPTION_CLAUSE_FILTER) &&
13319 ((ip - header->code) > clause->data.filter_offset && (ip - header->code) <= clause->handler_offset) &&
13320 (!nearest || (clause->data.filter_offset < nearest->data.filter_offset)))
13323 g_assert (nearest);
13324 if ((ip - header->code) != nearest->handler_offset)
13329 case CEE_UNALIGNED_:
13330 ins_flag |= MONO_INST_UNALIGNED;
13331 /* FIXME: record alignment? we can assume 1 for now */
13335 case CEE_VOLATILE_:
13336 ins_flag |= MONO_INST_VOLATILE;
13340 ins_flag |= MONO_INST_TAILCALL;
13341 cfg->flags |= MONO_CFG_HAS_TAIL;
13342 /* Can't inline tail calls at this time */
13343 inline_costs += 100000;
13350 token = read32 (ip + 2);
13351 klass = mini_get_class (method, token, generic_context);
13352 CHECK_TYPELOAD (klass);
13353 if (generic_class_is_reference_type (cfg, klass))
13354 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STORE_MEMBASE_IMM, sp [0]->dreg, 0, 0);
13356 mini_emit_initobj (cfg, *sp, NULL, klass);
13360 case CEE_CONSTRAINED_:
13362 token = read32 (ip + 2);
13363 constrained_class = mini_get_class (method, token, generic_context);
13364 CHECK_TYPELOAD (constrained_class);
13368 case CEE_INITBLK: {
13369 MonoInst *iargs [3];
13373 /* Skip optimized paths for volatile operations. */
13374 if ((ip [1] == CEE_CPBLK) && !(ins_flag & MONO_INST_VOLATILE) && (cfg->opt & MONO_OPT_INTRINS) && (sp [2]->opcode == OP_ICONST) && ((n = sp [2]->inst_c0) <= sizeof (gpointer) * 5)) {
13375 mini_emit_memcpy (cfg, sp [0]->dreg, 0, sp [1]->dreg, 0, sp [2]->inst_c0, 0);
13376 } else if ((ip [1] == CEE_INITBLK) && !(ins_flag & MONO_INST_VOLATILE) && (cfg->opt & MONO_OPT_INTRINS) && (sp [2]->opcode == OP_ICONST) && ((n = sp [2]->inst_c0) <= sizeof (gpointer) * 5) && (sp [1]->opcode == OP_ICONST) && (sp [1]->inst_c0 == 0)) {
13377 /* emit_memset only works when val == 0 */
13378 mini_emit_memset (cfg, sp [0]->dreg, 0, sp [2]->inst_c0, sp [1]->inst_c0, 0);
13381 iargs [0] = sp [0];
13382 iargs [1] = sp [1];
13383 iargs [2] = sp [2];
13384 if (ip [1] == CEE_CPBLK) {
13386 * FIXME: It's unclear whether we should be emitting both the acquire
13387 * and release barriers for cpblk. It is technically both a load and
13388 * store operation, so it seems like that's the sensible thing to do.
13390 * FIXME: We emit full barriers on both sides of the operation for
13391 * simplicity. We should have a separate atomic memcpy method instead.
13393 MonoMethod *memcpy_method = get_memcpy_method ();
13395 if (ins_flag & MONO_INST_VOLATILE)
13396 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_SEQ);
13398 call = mono_emit_method_call (cfg, memcpy_method, iargs, NULL);
13399 call->flags |= ins_flag;
13401 if (ins_flag & MONO_INST_VOLATILE)
13402 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_SEQ);
13404 MonoMethod *memset_method = get_memset_method ();
13405 if (ins_flag & MONO_INST_VOLATILE) {
13406 /* Volatile stores have release semantics, see 12.6.7 in Ecma 335 */
13407 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_REL);
13409 call = mono_emit_method_call (cfg, memset_method, iargs, NULL);
13410 call->flags |= ins_flag;
13421 ins_flag |= MONO_INST_NOTYPECHECK;
13423 ins_flag |= MONO_INST_NORANGECHECK;
13424 /* we ignore the no-nullcheck for now since we
13425 * really do it explicitly only when doing callvirt->call
13429 case CEE_RETHROW: {
13431 int handler_offset = -1;
13433 for (i = 0; i < header->num_clauses; ++i) {
13434 MonoExceptionClause *clause = &header->clauses [i];
13435 if (MONO_OFFSET_IN_HANDLER (clause, ip - header->code) && !(clause->flags & MONO_EXCEPTION_CLAUSE_FINALLY)) {
13436 handler_offset = clause->handler_offset;
13441 cfg->cbb->flags |= BB_EXCEPTION_UNSAFE;
13443 if (handler_offset == -1)
13446 EMIT_NEW_TEMPLOAD (cfg, load, mono_find_exvar_for_offset (cfg, handler_offset)->inst_c0);
13447 MONO_INST_NEW (cfg, ins, OP_RETHROW);
13448 ins->sreg1 = load->dreg;
13449 MONO_ADD_INS (cfg->cbb, ins);
13451 MONO_INST_NEW (cfg, ins, OP_NOT_REACHED);
13452 MONO_ADD_INS (cfg->cbb, ins);
13455 link_bblock (cfg, cfg->cbb, end_bblock);
13456 start_new_bblock = 1;
13464 CHECK_STACK_OVF (1);
13466 token = read32 (ip + 2);
13467 if (mono_metadata_token_table (token) == MONO_TABLE_TYPESPEC && !image_is_dynamic (method->klass->image) && !generic_context) {
13468 MonoType *type = mono_type_create_from_typespec_checked (image, token, &cfg->error);
13471 val = mono_type_size (type, &ialign);
13473 MonoClass *klass = mini_get_class (method, token, generic_context);
13474 CHECK_TYPELOAD (klass);
13476 val = mono_type_size (&klass->byval_arg, &ialign);
13478 if (mini_is_gsharedvt_klass (klass))
13479 GSHAREDVT_FAILURE (*ip);
13481 EMIT_NEW_ICONST (cfg, ins, val);
13486 case CEE_REFANYTYPE: {
13487 MonoInst *src_var, *src;
13489 GSHAREDVT_FAILURE (*ip);
13495 src_var = get_vreg_to_inst (cfg, sp [0]->dreg);
13497 src_var = mono_compile_create_var_for_vreg (cfg, &mono_defaults.typed_reference_class->byval_arg, OP_LOCAL, sp [0]->dreg);
13498 EMIT_NEW_VARLOADA (cfg, src, src_var, src_var->inst_vtype);
13499 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &mono_defaults.typehandle_class->byval_arg, src->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, type));
13504 case CEE_READONLY_:
13517 g_warning ("opcode 0xfe 0x%02x not handled", ip [1]);
13527 g_warning ("opcode 0x%02x not handled", *ip);
13531 if (start_new_bblock != 1)
13534 cfg->cbb->cil_length = ip - cfg->cbb->cil_code;
13535 if (cfg->cbb->next_bb) {
13536 /* This could already be set because of inlining, #693905 */
13537 MonoBasicBlock *bb = cfg->cbb;
13539 while (bb->next_bb)
13541 bb->next_bb = end_bblock;
13543 cfg->cbb->next_bb = end_bblock;
13546 if (cfg->method == method && cfg->domainvar) {
13548 MonoInst *get_domain;
13550 cfg->cbb = init_localsbb;
13552 if ((get_domain = mono_get_domain_intrinsic (cfg))) {
13553 MONO_ADD_INS (cfg->cbb, get_domain);
13555 get_domain = mono_emit_jit_icall (cfg, mono_domain_get, NULL);
13557 NEW_TEMPSTORE (cfg, store, cfg->domainvar->inst_c0, get_domain);
13558 MONO_ADD_INS (cfg->cbb, store);
13561 #if defined(TARGET_POWERPC) || defined(TARGET_X86)
13562 if (cfg->compile_aot)
13563 /* FIXME: The plt slots require a GOT var even if the method doesn't use it */
13564 mono_get_got_var (cfg);
13567 if (cfg->method == method && cfg->got_var)
13568 mono_emit_load_got_addr (cfg);
13570 if (init_localsbb) {
13571 cfg->cbb = init_localsbb;
13573 for (i = 0; i < header->num_locals; ++i) {
13574 emit_init_local (cfg, i, header->locals [i], init_locals);
13578 if (cfg->init_ref_vars && cfg->method == method) {
13579 /* Emit initialization for ref vars */
13580 // FIXME: Avoid duplication initialization for IL locals.
13581 for (i = 0; i < cfg->num_varinfo; ++i) {
13582 MonoInst *ins = cfg->varinfo [i];
13584 if (ins->opcode == OP_LOCAL && ins->type == STACK_OBJ)
13585 MONO_EMIT_NEW_PCONST (cfg, ins->dreg, NULL);
13589 if (cfg->lmf_var && cfg->method == method && !cfg->llvm_only) {
13590 cfg->cbb = init_localsbb;
13591 emit_push_lmf (cfg);
13594 cfg->cbb = init_localsbb;
13595 emit_instrumentation_call (cfg, mono_profiler_method_enter);
13598 MonoBasicBlock *bb;
13601 * Make seq points at backward branch targets interruptable.
13603 for (bb = cfg->bb_entry; bb; bb = bb->next_bb)
13604 if (bb->code && bb->in_count > 1 && bb->code->opcode == OP_SEQ_POINT)
13605 bb->code->flags |= MONO_INST_SINGLE_STEP_LOC;
13608 /* Add a sequence point for method entry/exit events */
13609 if (seq_points && cfg->gen_sdb_seq_points) {
13610 NEW_SEQ_POINT (cfg, ins, METHOD_ENTRY_IL_OFFSET, FALSE);
13611 MONO_ADD_INS (init_localsbb, ins);
13612 NEW_SEQ_POINT (cfg, ins, METHOD_EXIT_IL_OFFSET, FALSE);
13613 MONO_ADD_INS (cfg->bb_exit, ins);
13617 * Add seq points for IL offsets which have line number info, but wasn't generated a seq point during JITting because
13618 * the code they refer to was dead (#11880).
13620 if (sym_seq_points) {
13621 for (i = 0; i < header->code_size; ++i) {
13622 if (mono_bitset_test_fast (seq_point_locs, i) && !mono_bitset_test_fast (seq_point_set_locs, i)) {
13625 NEW_SEQ_POINT (cfg, ins, i, FALSE);
13626 mono_add_seq_point (cfg, NULL, ins, SEQ_POINT_NATIVE_OFFSET_DEAD_CODE);
13633 if (cfg->method == method) {
13634 MonoBasicBlock *bb;
13635 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
13636 bb->region = mono_find_block_region (cfg, bb->real_offset);
13638 mono_create_spvar_for_region (cfg, bb->region);
13639 if (cfg->verbose_level > 2)
13640 printf ("REGION BB%d IL_%04x ID_%08X\n", bb->block_num, bb->real_offset, bb->region);
13644 if (inline_costs < 0) {
13647 /* Method is too large */
13648 mname = mono_method_full_name (method, TRUE);
13649 mono_cfg_set_exception_invalid_program (cfg, g_strdup_printf ("Method %s is too complex.", mname));
13653 if ((cfg->verbose_level > 2) && (cfg->method == method))
13654 mono_print_code (cfg, "AFTER METHOD-TO-IR");
13659 g_assert (!mono_error_ok (&cfg->error));
13663 g_assert (cfg->exception_type != MONO_EXCEPTION_NONE);
13667 set_exception_type_from_invalid_il (cfg, method, ip);
13671 g_slist_free (class_inits);
13672 mono_basic_block_free (original_bb);
13673 cfg->dont_inline = g_list_remove (cfg->dont_inline, method);
13674 cfg->headers_to_free = g_slist_prepend_mempool (cfg->mempool, cfg->headers_to_free, header);
13675 if (cfg->exception_type)
13678 return inline_costs;
13682 store_membase_reg_to_store_membase_imm (int opcode)
13685 case OP_STORE_MEMBASE_REG:
13686 return OP_STORE_MEMBASE_IMM;
13687 case OP_STOREI1_MEMBASE_REG:
13688 return OP_STOREI1_MEMBASE_IMM;
13689 case OP_STOREI2_MEMBASE_REG:
13690 return OP_STOREI2_MEMBASE_IMM;
13691 case OP_STOREI4_MEMBASE_REG:
13692 return OP_STOREI4_MEMBASE_IMM;
13693 case OP_STOREI8_MEMBASE_REG:
13694 return OP_STOREI8_MEMBASE_IMM;
13696 g_assert_not_reached ();
13703 mono_op_to_op_imm (int opcode)
13707 return OP_IADD_IMM;
13709 return OP_ISUB_IMM;
13711 return OP_IDIV_IMM;
13713 return OP_IDIV_UN_IMM;
13715 return OP_IREM_IMM;
13717 return OP_IREM_UN_IMM;
13719 return OP_IMUL_IMM;
13721 return OP_IAND_IMM;
13725 return OP_IXOR_IMM;
13727 return OP_ISHL_IMM;
13729 return OP_ISHR_IMM;
13731 return OP_ISHR_UN_IMM;
13734 return OP_LADD_IMM;
13736 return OP_LSUB_IMM;
13738 return OP_LAND_IMM;
13742 return OP_LXOR_IMM;
13744 return OP_LSHL_IMM;
13746 return OP_LSHR_IMM;
13748 return OP_LSHR_UN_IMM;
13749 #if SIZEOF_REGISTER == 8
13751 return OP_LREM_IMM;
13755 return OP_COMPARE_IMM;
13757 return OP_ICOMPARE_IMM;
13759 return OP_LCOMPARE_IMM;
13761 case OP_STORE_MEMBASE_REG:
13762 return OP_STORE_MEMBASE_IMM;
13763 case OP_STOREI1_MEMBASE_REG:
13764 return OP_STOREI1_MEMBASE_IMM;
13765 case OP_STOREI2_MEMBASE_REG:
13766 return OP_STOREI2_MEMBASE_IMM;
13767 case OP_STOREI4_MEMBASE_REG:
13768 return OP_STOREI4_MEMBASE_IMM;
13770 #if defined(TARGET_X86) || defined (TARGET_AMD64)
13772 return OP_X86_PUSH_IMM;
13773 case OP_X86_COMPARE_MEMBASE_REG:
13774 return OP_X86_COMPARE_MEMBASE_IMM;
13776 #if defined(TARGET_AMD64)
13777 case OP_AMD64_ICOMPARE_MEMBASE_REG:
13778 return OP_AMD64_ICOMPARE_MEMBASE_IMM;
13780 case OP_VOIDCALL_REG:
13781 return OP_VOIDCALL;
13789 return OP_LOCALLOC_IMM;
13796 ldind_to_load_membase (int opcode)
13800 return OP_LOADI1_MEMBASE;
13802 return OP_LOADU1_MEMBASE;
13804 return OP_LOADI2_MEMBASE;
13806 return OP_LOADU2_MEMBASE;
13808 return OP_LOADI4_MEMBASE;
13810 return OP_LOADU4_MEMBASE;
13812 return OP_LOAD_MEMBASE;
13813 case CEE_LDIND_REF:
13814 return OP_LOAD_MEMBASE;
13816 return OP_LOADI8_MEMBASE;
13818 return OP_LOADR4_MEMBASE;
13820 return OP_LOADR8_MEMBASE;
13822 g_assert_not_reached ();
13829 stind_to_store_membase (int opcode)
13833 return OP_STOREI1_MEMBASE_REG;
13835 return OP_STOREI2_MEMBASE_REG;
13837 return OP_STOREI4_MEMBASE_REG;
13839 case CEE_STIND_REF:
13840 return OP_STORE_MEMBASE_REG;
13842 return OP_STOREI8_MEMBASE_REG;
13844 return OP_STORER4_MEMBASE_REG;
13846 return OP_STORER8_MEMBASE_REG;
13848 g_assert_not_reached ();
13855 mono_load_membase_to_load_mem (int opcode)
13857 // FIXME: Add a MONO_ARCH_HAVE_LOAD_MEM macro
13858 #if defined(TARGET_X86) || defined(TARGET_AMD64)
13860 case OP_LOAD_MEMBASE:
13861 return OP_LOAD_MEM;
13862 case OP_LOADU1_MEMBASE:
13863 return OP_LOADU1_MEM;
13864 case OP_LOADU2_MEMBASE:
13865 return OP_LOADU2_MEM;
13866 case OP_LOADI4_MEMBASE:
13867 return OP_LOADI4_MEM;
13868 case OP_LOADU4_MEMBASE:
13869 return OP_LOADU4_MEM;
13870 #if SIZEOF_REGISTER == 8
13871 case OP_LOADI8_MEMBASE:
13872 return OP_LOADI8_MEM;
13881 op_to_op_dest_membase (int store_opcode, int opcode)
13883 #if defined(TARGET_X86)
13884 if (!((store_opcode == OP_STORE_MEMBASE_REG) || (store_opcode == OP_STOREI4_MEMBASE_REG)))
13889 return OP_X86_ADD_MEMBASE_REG;
13891 return OP_X86_SUB_MEMBASE_REG;
13893 return OP_X86_AND_MEMBASE_REG;
13895 return OP_X86_OR_MEMBASE_REG;
13897 return OP_X86_XOR_MEMBASE_REG;
13900 return OP_X86_ADD_MEMBASE_IMM;
13903 return OP_X86_SUB_MEMBASE_IMM;
13906 return OP_X86_AND_MEMBASE_IMM;
13909 return OP_X86_OR_MEMBASE_IMM;
13912 return OP_X86_XOR_MEMBASE_IMM;
13918 #if defined(TARGET_AMD64)
13919 if (!((store_opcode == OP_STORE_MEMBASE_REG) || (store_opcode == OP_STOREI4_MEMBASE_REG) || (store_opcode == OP_STOREI8_MEMBASE_REG)))
13924 return OP_X86_ADD_MEMBASE_REG;
13926 return OP_X86_SUB_MEMBASE_REG;
13928 return OP_X86_AND_MEMBASE_REG;
13930 return OP_X86_OR_MEMBASE_REG;
13932 return OP_X86_XOR_MEMBASE_REG;
13934 return OP_X86_ADD_MEMBASE_IMM;
13936 return OP_X86_SUB_MEMBASE_IMM;
13938 return OP_X86_AND_MEMBASE_IMM;
13940 return OP_X86_OR_MEMBASE_IMM;
13942 return OP_X86_XOR_MEMBASE_IMM;
13944 return OP_AMD64_ADD_MEMBASE_REG;
13946 return OP_AMD64_SUB_MEMBASE_REG;
13948 return OP_AMD64_AND_MEMBASE_REG;
13950 return OP_AMD64_OR_MEMBASE_REG;
13952 return OP_AMD64_XOR_MEMBASE_REG;
13955 return OP_AMD64_ADD_MEMBASE_IMM;
13958 return OP_AMD64_SUB_MEMBASE_IMM;
13961 return OP_AMD64_AND_MEMBASE_IMM;
13964 return OP_AMD64_OR_MEMBASE_IMM;
13967 return OP_AMD64_XOR_MEMBASE_IMM;
13977 op_to_op_store_membase (int store_opcode, int opcode)
13979 #if defined(TARGET_X86) || defined(TARGET_AMD64)
13982 if (store_opcode == OP_STOREI1_MEMBASE_REG)
13983 return OP_X86_SETEQ_MEMBASE;
13985 if (store_opcode == OP_STOREI1_MEMBASE_REG)
13986 return OP_X86_SETNE_MEMBASE;
13994 op_to_op_src1_membase (MonoCompile *cfg, int load_opcode, int opcode)
13997 /* FIXME: This has sign extension issues */
13999 if ((opcode == OP_ICOMPARE_IMM) && (load_opcode == OP_LOADU1_MEMBASE))
14000 return OP_X86_COMPARE_MEMBASE8_IMM;
14003 if (!((load_opcode == OP_LOAD_MEMBASE) || (load_opcode == OP_LOADI4_MEMBASE) || (load_opcode == OP_LOADU4_MEMBASE)))
14008 return OP_X86_PUSH_MEMBASE;
14009 case OP_COMPARE_IMM:
14010 case OP_ICOMPARE_IMM:
14011 return OP_X86_COMPARE_MEMBASE_IMM;
14014 return OP_X86_COMPARE_MEMBASE_REG;
14018 #ifdef TARGET_AMD64
14019 /* FIXME: This has sign extension issues */
14021 if ((opcode == OP_ICOMPARE_IMM) && (load_opcode == OP_LOADU1_MEMBASE))
14022 return OP_X86_COMPARE_MEMBASE8_IMM;
14027 if ((load_opcode == OP_LOAD_MEMBASE && !cfg->backend->ilp32) || (load_opcode == OP_LOADI8_MEMBASE))
14028 return OP_X86_PUSH_MEMBASE;
14030 /* FIXME: This only works for 32 bit immediates
14031 case OP_COMPARE_IMM:
14032 case OP_LCOMPARE_IMM:
14033 if ((load_opcode == OP_LOAD_MEMBASE) || (load_opcode == OP_LOADI8_MEMBASE))
14034 return OP_AMD64_COMPARE_MEMBASE_IMM;
14036 case OP_ICOMPARE_IMM:
14037 if ((load_opcode == OP_LOADI4_MEMBASE) || (load_opcode == OP_LOADU4_MEMBASE))
14038 return OP_AMD64_ICOMPARE_MEMBASE_IMM;
14042 if (cfg->backend->ilp32 && load_opcode == OP_LOAD_MEMBASE)
14043 return OP_AMD64_ICOMPARE_MEMBASE_REG;
14044 if ((load_opcode == OP_LOAD_MEMBASE && !cfg->backend->ilp32) || (load_opcode == OP_LOADI8_MEMBASE))
14045 return OP_AMD64_COMPARE_MEMBASE_REG;
14048 if ((load_opcode == OP_LOADI4_MEMBASE) || (load_opcode == OP_LOADU4_MEMBASE))
14049 return OP_AMD64_ICOMPARE_MEMBASE_REG;
14058 op_to_op_src2_membase (MonoCompile *cfg, int load_opcode, int opcode)
14061 if (!((load_opcode == OP_LOAD_MEMBASE) || (load_opcode == OP_LOADI4_MEMBASE) || (load_opcode == OP_LOADU4_MEMBASE)))
14067 return OP_X86_COMPARE_REG_MEMBASE;
14069 return OP_X86_ADD_REG_MEMBASE;
14071 return OP_X86_SUB_REG_MEMBASE;
14073 return OP_X86_AND_REG_MEMBASE;
14075 return OP_X86_OR_REG_MEMBASE;
14077 return OP_X86_XOR_REG_MEMBASE;
14081 #ifdef TARGET_AMD64
14082 if ((load_opcode == OP_LOADI4_MEMBASE) || (load_opcode == OP_LOADU4_MEMBASE) || (load_opcode == OP_LOAD_MEMBASE && cfg->backend->ilp32)) {
14085 return OP_AMD64_ICOMPARE_REG_MEMBASE;
14087 return OP_X86_ADD_REG_MEMBASE;
14089 return OP_X86_SUB_REG_MEMBASE;
14091 return OP_X86_AND_REG_MEMBASE;
14093 return OP_X86_OR_REG_MEMBASE;
14095 return OP_X86_XOR_REG_MEMBASE;
14097 } else if ((load_opcode == OP_LOADI8_MEMBASE) || (load_opcode == OP_LOAD_MEMBASE && !cfg->backend->ilp32)) {
14101 return OP_AMD64_COMPARE_REG_MEMBASE;
14103 return OP_AMD64_ADD_REG_MEMBASE;
14105 return OP_AMD64_SUB_REG_MEMBASE;
14107 return OP_AMD64_AND_REG_MEMBASE;
14109 return OP_AMD64_OR_REG_MEMBASE;
14111 return OP_AMD64_XOR_REG_MEMBASE;
14120 mono_op_to_op_imm_noemul (int opcode)
14123 #if SIZEOF_REGISTER == 4 && !defined(MONO_ARCH_NO_EMULATE_LONG_SHIFT_OPS)
14129 #if defined(MONO_ARCH_EMULATE_MUL_DIV) || defined(MONO_ARCH_EMULATE_DIV)
14136 #if defined(MONO_ARCH_EMULATE_MUL_DIV)
14141 return mono_op_to_op_imm (opcode);
14146 * mono_handle_global_vregs:
14148 * Make vregs used in more than one bblock 'global', i.e. allocate a variable
14152 mono_handle_global_vregs (MonoCompile *cfg)
14154 gint32 *vreg_to_bb;
14155 MonoBasicBlock *bb;
14158 vreg_to_bb = (gint32 *)mono_mempool_alloc0 (cfg->mempool, sizeof (gint32*) * cfg->next_vreg + 1);
14160 #ifdef MONO_ARCH_SIMD_INTRINSICS
14161 if (cfg->uses_simd_intrinsics)
14162 mono_simd_simplify_indirection (cfg);
14165 /* Find local vregs used in more than one bb */
14166 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
14167 MonoInst *ins = bb->code;
14168 int block_num = bb->block_num;
14170 if (cfg->verbose_level > 2)
14171 printf ("\nHANDLE-GLOBAL-VREGS BLOCK %d:\n", bb->block_num);
14174 for (; ins; ins = ins->next) {
14175 const char *spec = INS_INFO (ins->opcode);
14176 int regtype = 0, regindex;
14179 if (G_UNLIKELY (cfg->verbose_level > 2))
14180 mono_print_ins (ins);
14182 g_assert (ins->opcode >= MONO_CEE_LAST);
14184 for (regindex = 0; regindex < 4; regindex ++) {
14187 if (regindex == 0) {
14188 regtype = spec [MONO_INST_DEST];
14189 if (regtype == ' ')
14192 } else if (regindex == 1) {
14193 regtype = spec [MONO_INST_SRC1];
14194 if (regtype == ' ')
14197 } else if (regindex == 2) {
14198 regtype = spec [MONO_INST_SRC2];
14199 if (regtype == ' ')
14202 } else if (regindex == 3) {
14203 regtype = spec [MONO_INST_SRC3];
14204 if (regtype == ' ')
14209 #if SIZEOF_REGISTER == 4
14210 /* In the LLVM case, the long opcodes are not decomposed */
14211 if (regtype == 'l' && !COMPILE_LLVM (cfg)) {
14213 * Since some instructions reference the original long vreg,
14214 * and some reference the two component vregs, it is quite hard
14215 * to determine when it needs to be global. So be conservative.
14217 if (!get_vreg_to_inst (cfg, vreg)) {
14218 mono_compile_create_var_for_vreg (cfg, &mono_defaults.int64_class->byval_arg, OP_LOCAL, vreg);
14220 if (cfg->verbose_level > 2)
14221 printf ("LONG VREG R%d made global.\n", vreg);
14225 * Make the component vregs volatile since the optimizations can
14226 * get confused otherwise.
14228 get_vreg_to_inst (cfg, MONO_LVREG_LS (vreg))->flags |= MONO_INST_VOLATILE;
14229 get_vreg_to_inst (cfg, MONO_LVREG_MS (vreg))->flags |= MONO_INST_VOLATILE;
14233 g_assert (vreg != -1);
14235 prev_bb = vreg_to_bb [vreg];
14236 if (prev_bb == 0) {
14237 /* 0 is a valid block num */
14238 vreg_to_bb [vreg] = block_num + 1;
14239 } else if ((prev_bb != block_num + 1) && (prev_bb != -1)) {
14240 if (((regtype == 'i' && (vreg < MONO_MAX_IREGS))) || (regtype == 'f' && (vreg < MONO_MAX_FREGS)))
14243 if (!get_vreg_to_inst (cfg, vreg)) {
14244 if (G_UNLIKELY (cfg->verbose_level > 2))
14245 printf ("VREG R%d used in BB%d and BB%d made global.\n", vreg, vreg_to_bb [vreg], block_num);
14249 if (vreg_is_ref (cfg, vreg))
14250 mono_compile_create_var_for_vreg (cfg, &mono_defaults.object_class->byval_arg, OP_LOCAL, vreg);
14252 mono_compile_create_var_for_vreg (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL, vreg);
14255 mono_compile_create_var_for_vreg (cfg, &mono_defaults.int64_class->byval_arg, OP_LOCAL, vreg);
14258 mono_compile_create_var_for_vreg (cfg, &mono_defaults.double_class->byval_arg, OP_LOCAL, vreg);
14261 mono_compile_create_var_for_vreg (cfg, &ins->klass->byval_arg, OP_LOCAL, vreg);
14264 g_assert_not_reached ();
14268 /* Flag as having been used in more than one bb */
14269 vreg_to_bb [vreg] = -1;
14275 /* If a variable is used in only one bblock, convert it into a local vreg */
14276 for (i = 0; i < cfg->num_varinfo; i++) {
14277 MonoInst *var = cfg->varinfo [i];
14278 MonoMethodVar *vmv = MONO_VARINFO (cfg, i);
14280 switch (var->type) {
14286 #if SIZEOF_REGISTER == 8
14289 #if !defined(TARGET_X86)
14290 /* Enabling this screws up the fp stack on x86 */
14293 if (mono_arch_is_soft_float ())
14297 if (var->type == STACK_VTYPE && cfg->gsharedvt && mini_is_gsharedvt_variable_type (var->inst_vtype))
14301 /* Arguments are implicitly global */
14302 /* Putting R4 vars into registers doesn't work currently */
14303 /* The gsharedvt vars are implicitly referenced by ldaddr opcodes, but those opcodes are only generated later */
14304 if ((var->opcode != OP_ARG) && (var != cfg->ret) && !(var->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT)) && (vreg_to_bb [var->dreg] != -1) && (var->klass->byval_arg.type != MONO_TYPE_R4) && !cfg->disable_vreg_to_lvreg && var != cfg->gsharedvt_info_var && var != cfg->gsharedvt_locals_var && var != cfg->lmf_addr_var) {
14306 * Make that the variable's liveness interval doesn't contain a call, since
14307 * that would cause the lvreg to be spilled, making the whole optimization
14310 /* This is too slow for JIT compilation */
14312 if (cfg->compile_aot && vreg_to_bb [var->dreg]) {
14314 int def_index, call_index, ins_index;
14315 gboolean spilled = FALSE;
14320 for (ins = vreg_to_bb [var->dreg]->code; ins; ins = ins->next) {
14321 const char *spec = INS_INFO (ins->opcode);
14323 if ((spec [MONO_INST_DEST] != ' ') && (ins->dreg == var->dreg))
14324 def_index = ins_index;
14326 if (((spec [MONO_INST_SRC1] != ' ') && (ins->sreg1 == var->dreg)) ||
14327 ((spec [MONO_INST_SRC1] != ' ') && (ins->sreg1 == var->dreg))) {
14328 if (call_index > def_index) {
14334 if (MONO_IS_CALL (ins))
14335 call_index = ins_index;
14345 if (G_UNLIKELY (cfg->verbose_level > 2))
14346 printf ("CONVERTED R%d(%d) TO VREG.\n", var->dreg, vmv->idx);
14347 var->flags |= MONO_INST_IS_DEAD;
14348 cfg->vreg_to_inst [var->dreg] = NULL;
14355 * Compress the varinfo and vars tables so the liveness computation is faster and
14356 * takes up less space.
14359 for (i = 0; i < cfg->num_varinfo; ++i) {
14360 MonoInst *var = cfg->varinfo [i];
14361 if (pos < i && cfg->locals_start == i)
14362 cfg->locals_start = pos;
14363 if (!(var->flags & MONO_INST_IS_DEAD)) {
14365 cfg->varinfo [pos] = cfg->varinfo [i];
14366 cfg->varinfo [pos]->inst_c0 = pos;
14367 memcpy (&cfg->vars [pos], &cfg->vars [i], sizeof (MonoMethodVar));
14368 cfg->vars [pos].idx = pos;
14369 #if SIZEOF_REGISTER == 4
14370 if (cfg->varinfo [pos]->type == STACK_I8) {
14371 /* Modify the two component vars too */
14374 var1 = get_vreg_to_inst (cfg, MONO_LVREG_LS (cfg->varinfo [pos]->dreg));
14375 var1->inst_c0 = pos;
14376 var1 = get_vreg_to_inst (cfg, MONO_LVREG_MS (cfg->varinfo [pos]->dreg));
14377 var1->inst_c0 = pos;
14384 cfg->num_varinfo = pos;
14385 if (cfg->locals_start > cfg->num_varinfo)
14386 cfg->locals_start = cfg->num_varinfo;
14390 * mono_allocate_gsharedvt_vars:
14392 * Allocate variables with gsharedvt types to entries in the MonoGSharedVtMethodRuntimeInfo.entries array.
14393 * Initialize cfg->gsharedvt_vreg_to_idx with the mapping between vregs and indexes.
14396 mono_allocate_gsharedvt_vars (MonoCompile *cfg)
14400 cfg->gsharedvt_vreg_to_idx = (int *)mono_mempool_alloc0 (cfg->mempool, sizeof (int) * cfg->next_vreg);
14402 for (i = 0; i < cfg->num_varinfo; ++i) {
14403 MonoInst *ins = cfg->varinfo [i];
14406 if (mini_is_gsharedvt_variable_type (ins->inst_vtype)) {
14407 if (i >= cfg->locals_start) {
14409 idx = get_gsharedvt_info_slot (cfg, ins->inst_vtype, MONO_RGCTX_INFO_LOCAL_OFFSET);
14410 cfg->gsharedvt_vreg_to_idx [ins->dreg] = idx + 1;
14411 ins->opcode = OP_GSHAREDVT_LOCAL;
14412 ins->inst_imm = idx;
14415 cfg->gsharedvt_vreg_to_idx [ins->dreg] = -1;
14416 ins->opcode = OP_GSHAREDVT_ARG_REGOFFSET;
14423 * mono_spill_global_vars:
14425 * Generate spill code for variables which are not allocated to registers,
14426 * and replace vregs with their allocated hregs. *need_local_opts is set to TRUE if
14427 * code is generated which could be optimized by the local optimization passes.
14430 mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local_opts)
14432 MonoBasicBlock *bb;
14434 int orig_next_vreg;
14435 guint32 *vreg_to_lvreg;
14437 guint32 i, lvregs_len;
14438 gboolean dest_has_lvreg = FALSE;
14439 MonoStackType stacktypes [128];
14440 MonoInst **live_range_start, **live_range_end;
14441 MonoBasicBlock **live_range_start_bb, **live_range_end_bb;
14443 *need_local_opts = FALSE;
14445 memset (spec2, 0, sizeof (spec2));
14447 /* FIXME: Move this function to mini.c */
14448 stacktypes ['i'] = STACK_PTR;
14449 stacktypes ['l'] = STACK_I8;
14450 stacktypes ['f'] = STACK_R8;
14451 #ifdef MONO_ARCH_SIMD_INTRINSICS
14452 stacktypes ['x'] = STACK_VTYPE;
14455 #if SIZEOF_REGISTER == 4
14456 /* Create MonoInsts for longs */
14457 for (i = 0; i < cfg->num_varinfo; i++) {
14458 MonoInst *ins = cfg->varinfo [i];
14460 if ((ins->opcode != OP_REGVAR) && !(ins->flags & MONO_INST_IS_DEAD)) {
14461 switch (ins->type) {
14466 if (ins->type == STACK_R8 && !COMPILE_SOFT_FLOAT (cfg))
14469 g_assert (ins->opcode == OP_REGOFFSET);
14471 tree = get_vreg_to_inst (cfg, MONO_LVREG_LS (ins->dreg));
14473 tree->opcode = OP_REGOFFSET;
14474 tree->inst_basereg = ins->inst_basereg;
14475 tree->inst_offset = ins->inst_offset + MINI_LS_WORD_OFFSET;
14477 tree = get_vreg_to_inst (cfg, MONO_LVREG_MS (ins->dreg));
14479 tree->opcode = OP_REGOFFSET;
14480 tree->inst_basereg = ins->inst_basereg;
14481 tree->inst_offset = ins->inst_offset + MINI_MS_WORD_OFFSET;
14491 if (cfg->compute_gc_maps) {
14492 /* registers need liveness info even for !non refs */
14493 for (i = 0; i < cfg->num_varinfo; i++) {
14494 MonoInst *ins = cfg->varinfo [i];
14496 if (ins->opcode == OP_REGVAR)
14497 ins->flags |= MONO_INST_GC_TRACK;
14501 /* FIXME: widening and truncation */
14504 * As an optimization, when a variable allocated to the stack is first loaded into
14505 * an lvreg, we will remember the lvreg and use it the next time instead of loading
14506 * the variable again.
14508 orig_next_vreg = cfg->next_vreg;
14509 vreg_to_lvreg = (guint32 *)mono_mempool_alloc0 (cfg->mempool, sizeof (guint32) * cfg->next_vreg);
14510 lvregs = (guint32 *)mono_mempool_alloc (cfg->mempool, sizeof (guint32) * 1024);
14514 * These arrays contain the first and last instructions accessing a given
14516 * Since we emit bblocks in the same order we process them here, and we
14517 * don't split live ranges, these will precisely describe the live range of
14518 * the variable, i.e. the instruction range where a valid value can be found
14519 * in the variables location.
14520 * The live range is computed using the liveness info computed by the liveness pass.
14521 * We can't use vmv->range, since that is an abstract live range, and we need
14522 * one which is instruction precise.
14523 * FIXME: Variables used in out-of-line bblocks have a hole in their live range.
14525 /* FIXME: Only do this if debugging info is requested */
14526 live_range_start = g_new0 (MonoInst*, cfg->next_vreg);
14527 live_range_end = g_new0 (MonoInst*, cfg->next_vreg);
14528 live_range_start_bb = g_new (MonoBasicBlock*, cfg->next_vreg);
14529 live_range_end_bb = g_new (MonoBasicBlock*, cfg->next_vreg);
14531 /* Add spill loads/stores */
14532 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
14535 if (cfg->verbose_level > 2)
14536 printf ("\nSPILL BLOCK %d:\n", bb->block_num);
14538 /* Clear vreg_to_lvreg array */
14539 for (i = 0; i < lvregs_len; i++)
14540 vreg_to_lvreg [lvregs [i]] = 0;
14544 MONO_BB_FOR_EACH_INS (bb, ins) {
14545 const char *spec = INS_INFO (ins->opcode);
14546 int regtype, srcindex, sreg, tmp_reg, prev_dreg, num_sregs;
14547 gboolean store, no_lvreg;
14548 int sregs [MONO_MAX_SRC_REGS];
14550 if (G_UNLIKELY (cfg->verbose_level > 2))
14551 mono_print_ins (ins);
14553 if (ins->opcode == OP_NOP)
14557 * We handle LDADDR here as well, since it can only be decomposed
14558 * when variable addresses are known.
14560 if (ins->opcode == OP_LDADDR) {
14561 MonoInst *var = (MonoInst *)ins->inst_p0;
14563 if (var->opcode == OP_VTARG_ADDR) {
14564 /* Happens on SPARC/S390 where vtypes are passed by reference */
14565 MonoInst *vtaddr = var->inst_left;
14566 if (vtaddr->opcode == OP_REGVAR) {
14567 ins->opcode = OP_MOVE;
14568 ins->sreg1 = vtaddr->dreg;
14570 else if (var->inst_left->opcode == OP_REGOFFSET) {
14571 ins->opcode = OP_LOAD_MEMBASE;
14572 ins->inst_basereg = vtaddr->inst_basereg;
14573 ins->inst_offset = vtaddr->inst_offset;
14576 } else if (cfg->gsharedvt && cfg->gsharedvt_vreg_to_idx [var->dreg] < 0) {
14577 /* gsharedvt arg passed by ref */
14578 g_assert (var->opcode == OP_GSHAREDVT_ARG_REGOFFSET);
14580 ins->opcode = OP_LOAD_MEMBASE;
14581 ins->inst_basereg = var->inst_basereg;
14582 ins->inst_offset = var->inst_offset;
14583 } else if (cfg->gsharedvt && cfg->gsharedvt_vreg_to_idx [var->dreg]) {
14584 MonoInst *load, *load2, *load3;
14585 int idx = cfg->gsharedvt_vreg_to_idx [var->dreg] - 1;
14586 int reg1, reg2, reg3;
14587 MonoInst *info_var = cfg->gsharedvt_info_var;
14588 MonoInst *locals_var = cfg->gsharedvt_locals_var;
14592 * Compute the address of the local as gsharedvt_locals_var + gsharedvt_info_var->locals_offsets [idx].
14595 g_assert (var->opcode == OP_GSHAREDVT_LOCAL);
14597 g_assert (info_var);
14598 g_assert (locals_var);
14600 /* Mark the instruction used to compute the locals var as used */
14601 cfg->gsharedvt_locals_var_ins = NULL;
14603 /* Load the offset */
14604 if (info_var->opcode == OP_REGOFFSET) {
14605 reg1 = alloc_ireg (cfg);
14606 NEW_LOAD_MEMBASE (cfg, load, OP_LOAD_MEMBASE, reg1, info_var->inst_basereg, info_var->inst_offset);
14607 } else if (info_var->opcode == OP_REGVAR) {
14609 reg1 = info_var->dreg;
14611 g_assert_not_reached ();
14613 reg2 = alloc_ireg (cfg);
14614 NEW_LOAD_MEMBASE (cfg, load2, OP_LOADI4_MEMBASE, reg2, reg1, MONO_STRUCT_OFFSET (MonoGSharedVtMethodRuntimeInfo, entries) + (idx * sizeof (gpointer)));
14615 /* Load the locals area address */
14616 reg3 = alloc_ireg (cfg);
14617 if (locals_var->opcode == OP_REGOFFSET) {
14618 NEW_LOAD_MEMBASE (cfg, load3, OP_LOAD_MEMBASE, reg3, locals_var->inst_basereg, locals_var->inst_offset);
14619 } else if (locals_var->opcode == OP_REGVAR) {
14620 NEW_UNALU (cfg, load3, OP_MOVE, reg3, locals_var->dreg);
14622 g_assert_not_reached ();
14624 /* Compute the address */
14625 ins->opcode = OP_PADD;
14629 mono_bblock_insert_before_ins (bb, ins, load3);
14630 mono_bblock_insert_before_ins (bb, load3, load2);
14632 mono_bblock_insert_before_ins (bb, load2, load);
14634 g_assert (var->opcode == OP_REGOFFSET);
14636 ins->opcode = OP_ADD_IMM;
14637 ins->sreg1 = var->inst_basereg;
14638 ins->inst_imm = var->inst_offset;
14641 *need_local_opts = TRUE;
14642 spec = INS_INFO (ins->opcode);
14645 if (ins->opcode < MONO_CEE_LAST) {
14646 mono_print_ins (ins);
14647 g_assert_not_reached ();
14651 * Store opcodes have destbasereg in the dreg, but in reality, it is an
14655 if (MONO_IS_STORE_MEMBASE (ins)) {
14656 tmp_reg = ins->dreg;
14657 ins->dreg = ins->sreg2;
14658 ins->sreg2 = tmp_reg;
14661 spec2 [MONO_INST_DEST] = ' ';
14662 spec2 [MONO_INST_SRC1] = spec [MONO_INST_SRC1];
14663 spec2 [MONO_INST_SRC2] = spec [MONO_INST_DEST];
14664 spec2 [MONO_INST_SRC3] = ' ';
14666 } else if (MONO_IS_STORE_MEMINDEX (ins))
14667 g_assert_not_reached ();
14672 if (G_UNLIKELY (cfg->verbose_level > 2)) {
14673 printf ("\t %.3s %d", spec, ins->dreg);
14674 num_sregs = mono_inst_get_src_registers (ins, sregs);
14675 for (srcindex = 0; srcindex < num_sregs; ++srcindex)
14676 printf (" %d", sregs [srcindex]);
14683 regtype = spec [MONO_INST_DEST];
14684 g_assert (((ins->dreg == -1) && (regtype == ' ')) || ((ins->dreg != -1) && (regtype != ' ')));
14687 if ((ins->dreg != -1) && get_vreg_to_inst (cfg, ins->dreg)) {
14688 MonoInst *var = get_vreg_to_inst (cfg, ins->dreg);
14689 MonoInst *store_ins;
14691 MonoInst *def_ins = ins;
14692 int dreg = ins->dreg; /* The original vreg */
14694 store_opcode = mono_type_to_store_membase (cfg, var->inst_vtype);
14696 if (var->opcode == OP_REGVAR) {
14697 ins->dreg = var->dreg;
14698 } else if ((ins->dreg == ins->sreg1) && (spec [MONO_INST_DEST] == 'i') && (spec [MONO_INST_SRC1] == 'i') && !vreg_to_lvreg [ins->dreg] && (op_to_op_dest_membase (store_opcode, ins->opcode) != -1)) {
14700 * Instead of emitting a load+store, use a _membase opcode.
14702 g_assert (var->opcode == OP_REGOFFSET);
14703 if (ins->opcode == OP_MOVE) {
14707 ins->opcode = op_to_op_dest_membase (store_opcode, ins->opcode);
14708 ins->inst_basereg = var->inst_basereg;
14709 ins->inst_offset = var->inst_offset;
14712 spec = INS_INFO (ins->opcode);
14716 g_assert (var->opcode == OP_REGOFFSET);
14718 prev_dreg = ins->dreg;
14720 /* Invalidate any previous lvreg for this vreg */
14721 vreg_to_lvreg [ins->dreg] = 0;
14725 if (COMPILE_SOFT_FLOAT (cfg) && store_opcode == OP_STORER8_MEMBASE_REG) {
14727 store_opcode = OP_STOREI8_MEMBASE_REG;
14730 ins->dreg = alloc_dreg (cfg, stacktypes [regtype]);
14732 #if SIZEOF_REGISTER != 8
14733 if (regtype == 'l') {
14734 NEW_STORE_MEMBASE (cfg, store_ins, OP_STOREI4_MEMBASE_REG, var->inst_basereg, var->inst_offset + MINI_LS_WORD_OFFSET, MONO_LVREG_LS (ins->dreg));
14735 mono_bblock_insert_after_ins (bb, ins, store_ins);
14736 NEW_STORE_MEMBASE (cfg, store_ins, OP_STOREI4_MEMBASE_REG, var->inst_basereg, var->inst_offset + MINI_MS_WORD_OFFSET, MONO_LVREG_MS (ins->dreg));
14737 mono_bblock_insert_after_ins (bb, ins, store_ins);
14738 def_ins = store_ins;
14743 g_assert (store_opcode != OP_STOREV_MEMBASE);
14745 /* Try to fuse the store into the instruction itself */
14746 /* FIXME: Add more instructions */
14747 if (!lvreg && ((ins->opcode == OP_ICONST) || ((ins->opcode == OP_I8CONST) && (ins->inst_c0 == 0)))) {
14748 ins->opcode = store_membase_reg_to_store_membase_imm (store_opcode);
14749 ins->inst_imm = ins->inst_c0;
14750 ins->inst_destbasereg = var->inst_basereg;
14751 ins->inst_offset = var->inst_offset;
14752 spec = INS_INFO (ins->opcode);
14753 } else if (!lvreg && ((ins->opcode == OP_MOVE) || (ins->opcode == OP_FMOVE) || (ins->opcode == OP_LMOVE) || (ins->opcode == OP_RMOVE))) {
14754 ins->opcode = store_opcode;
14755 ins->inst_destbasereg = var->inst_basereg;
14756 ins->inst_offset = var->inst_offset;
14760 tmp_reg = ins->dreg;
14761 ins->dreg = ins->sreg2;
14762 ins->sreg2 = tmp_reg;
14765 spec2 [MONO_INST_DEST] = ' ';
14766 spec2 [MONO_INST_SRC1] = spec [MONO_INST_SRC1];
14767 spec2 [MONO_INST_SRC2] = spec [MONO_INST_DEST];
14768 spec2 [MONO_INST_SRC3] = ' ';
14770 } else if (!lvreg && (op_to_op_store_membase (store_opcode, ins->opcode) != -1)) {
14771 // FIXME: The backends expect the base reg to be in inst_basereg
14772 ins->opcode = op_to_op_store_membase (store_opcode, ins->opcode);
14774 ins->inst_basereg = var->inst_basereg;
14775 ins->inst_offset = var->inst_offset;
14776 spec = INS_INFO (ins->opcode);
14778 /* printf ("INS: "); mono_print_ins (ins); */
14779 /* Create a store instruction */
14780 NEW_STORE_MEMBASE (cfg, store_ins, store_opcode, var->inst_basereg, var->inst_offset, ins->dreg);
14782 /* Insert it after the instruction */
14783 mono_bblock_insert_after_ins (bb, ins, store_ins);
14785 def_ins = store_ins;
14788 * We can't assign ins->dreg to var->dreg here, since the
14789 * sregs could use it. So set a flag, and do it after
14792 if ((!cfg->backend->use_fpstack || ((store_opcode != OP_STORER8_MEMBASE_REG) && (store_opcode != OP_STORER4_MEMBASE_REG))) && !((var)->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT)))
14793 dest_has_lvreg = TRUE;
14798 if (def_ins && !live_range_start [dreg]) {
14799 live_range_start [dreg] = def_ins;
14800 live_range_start_bb [dreg] = bb;
14803 if (cfg->compute_gc_maps && def_ins && (var->flags & MONO_INST_GC_TRACK)) {
14806 MONO_INST_NEW (cfg, tmp, OP_GC_LIVENESS_DEF);
14807 tmp->inst_c1 = dreg;
14808 mono_bblock_insert_after_ins (bb, def_ins, tmp);
14815 num_sregs = mono_inst_get_src_registers (ins, sregs);
14816 for (srcindex = 0; srcindex < 3; ++srcindex) {
14817 regtype = spec [MONO_INST_SRC1 + srcindex];
14818 sreg = sregs [srcindex];
14820 g_assert (((sreg == -1) && (regtype == ' ')) || ((sreg != -1) && (regtype != ' ')));
14821 if ((sreg != -1) && get_vreg_to_inst (cfg, sreg)) {
14822 MonoInst *var = get_vreg_to_inst (cfg, sreg);
14823 MonoInst *use_ins = ins;
14824 MonoInst *load_ins;
14825 guint32 load_opcode;
14827 if (var->opcode == OP_REGVAR) {
14828 sregs [srcindex] = var->dreg;
14829 //mono_inst_set_src_registers (ins, sregs);
14830 live_range_end [sreg] = use_ins;
14831 live_range_end_bb [sreg] = bb;
14833 if (cfg->compute_gc_maps && var->dreg < orig_next_vreg && (var->flags & MONO_INST_GC_TRACK)) {
14836 MONO_INST_NEW (cfg, tmp, OP_GC_LIVENESS_USE);
14837 /* var->dreg is a hreg */
14838 tmp->inst_c1 = sreg;
14839 mono_bblock_insert_after_ins (bb, ins, tmp);
14845 g_assert (var->opcode == OP_REGOFFSET);
14847 load_opcode = mono_type_to_load_membase (cfg, var->inst_vtype);
14849 g_assert (load_opcode != OP_LOADV_MEMBASE);
14851 if (vreg_to_lvreg [sreg]) {
14852 g_assert (vreg_to_lvreg [sreg] != -1);
14854 /* The variable is already loaded to an lvreg */
14855 if (G_UNLIKELY (cfg->verbose_level > 2))
14856 printf ("\t\tUse lvreg R%d for R%d.\n", vreg_to_lvreg [sreg], sreg);
14857 sregs [srcindex] = vreg_to_lvreg [sreg];
14858 //mono_inst_set_src_registers (ins, sregs);
14862 /* Try to fuse the load into the instruction */
14863 if ((srcindex == 0) && (op_to_op_src1_membase (cfg, load_opcode, ins->opcode) != -1)) {
14864 ins->opcode = op_to_op_src1_membase (cfg, load_opcode, ins->opcode);
14865 sregs [0] = var->inst_basereg;
14866 //mono_inst_set_src_registers (ins, sregs);
14867 ins->inst_offset = var->inst_offset;
14868 } else if ((srcindex == 1) && (op_to_op_src2_membase (cfg, load_opcode, ins->opcode) != -1)) {
14869 ins->opcode = op_to_op_src2_membase (cfg, load_opcode, ins->opcode);
14870 sregs [1] = var->inst_basereg;
14871 //mono_inst_set_src_registers (ins, sregs);
14872 ins->inst_offset = var->inst_offset;
14874 if (MONO_IS_REAL_MOVE (ins)) {
14875 ins->opcode = OP_NOP;
14878 //printf ("%d ", srcindex); mono_print_ins (ins);
14880 sreg = alloc_dreg (cfg, stacktypes [regtype]);
14882 if ((!cfg->backend->use_fpstack || ((load_opcode != OP_LOADR8_MEMBASE) && (load_opcode != OP_LOADR4_MEMBASE))) && !((var)->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT)) && !no_lvreg) {
14883 if (var->dreg == prev_dreg) {
14885 * sreg refers to the value loaded by the load
14886 * emitted below, but we need to use ins->dreg
14887 * since it refers to the store emitted earlier.
14891 g_assert (sreg != -1);
14892 vreg_to_lvreg [var->dreg] = sreg;
14893 g_assert (lvregs_len < 1024);
14894 lvregs [lvregs_len ++] = var->dreg;
14898 sregs [srcindex] = sreg;
14899 //mono_inst_set_src_registers (ins, sregs);
14901 #if SIZEOF_REGISTER != 8
14902 if (regtype == 'l') {
14903 NEW_LOAD_MEMBASE (cfg, load_ins, OP_LOADI4_MEMBASE, MONO_LVREG_MS (sreg), var->inst_basereg, var->inst_offset + MINI_MS_WORD_OFFSET);
14904 mono_bblock_insert_before_ins (bb, ins, load_ins);
14905 NEW_LOAD_MEMBASE (cfg, load_ins, OP_LOADI4_MEMBASE, MONO_LVREG_LS (sreg), var->inst_basereg, var->inst_offset + MINI_LS_WORD_OFFSET);
14906 mono_bblock_insert_before_ins (bb, ins, load_ins);
14907 use_ins = load_ins;
14912 #if SIZEOF_REGISTER == 4
14913 g_assert (load_opcode != OP_LOADI8_MEMBASE);
14915 NEW_LOAD_MEMBASE (cfg, load_ins, load_opcode, sreg, var->inst_basereg, var->inst_offset);
14916 mono_bblock_insert_before_ins (bb, ins, load_ins);
14917 use_ins = load_ins;
14921 if (var->dreg < orig_next_vreg) {
14922 live_range_end [var->dreg] = use_ins;
14923 live_range_end_bb [var->dreg] = bb;
14926 if (cfg->compute_gc_maps && var->dreg < orig_next_vreg && (var->flags & MONO_INST_GC_TRACK)) {
14929 MONO_INST_NEW (cfg, tmp, OP_GC_LIVENESS_USE);
14930 tmp->inst_c1 = var->dreg;
14931 mono_bblock_insert_after_ins (bb, ins, tmp);
14935 mono_inst_set_src_registers (ins, sregs);
14937 if (dest_has_lvreg) {
14938 g_assert (ins->dreg != -1);
14939 vreg_to_lvreg [prev_dreg] = ins->dreg;
14940 g_assert (lvregs_len < 1024);
14941 lvregs [lvregs_len ++] = prev_dreg;
14942 dest_has_lvreg = FALSE;
14946 tmp_reg = ins->dreg;
14947 ins->dreg = ins->sreg2;
14948 ins->sreg2 = tmp_reg;
14951 if (MONO_IS_CALL (ins)) {
14952 /* Clear vreg_to_lvreg array */
14953 for (i = 0; i < lvregs_len; i++)
14954 vreg_to_lvreg [lvregs [i]] = 0;
14956 } else if (ins->opcode == OP_NOP) {
14958 MONO_INST_NULLIFY_SREGS (ins);
14961 if (cfg->verbose_level > 2)
14962 mono_print_ins_index (1, ins);
14965 /* Extend the live range based on the liveness info */
14966 if (cfg->compute_precise_live_ranges && bb->live_out_set && bb->code) {
14967 for (i = 0; i < cfg->num_varinfo; i ++) {
14968 MonoMethodVar *vi = MONO_VARINFO (cfg, i);
14970 if (vreg_is_volatile (cfg, vi->vreg))
14971 /* The liveness info is incomplete */
14974 if (mono_bitset_test_fast (bb->live_in_set, i) && !live_range_start [vi->vreg]) {
14975 /* Live from at least the first ins of this bb */
14976 live_range_start [vi->vreg] = bb->code;
14977 live_range_start_bb [vi->vreg] = bb;
14980 if (mono_bitset_test_fast (bb->live_out_set, i)) {
14981 /* Live at least until the last ins of this bb */
14982 live_range_end [vi->vreg] = bb->last_ins;
14983 live_range_end_bb [vi->vreg] = bb;
14990 * Emit LIVERANGE_START/LIVERANGE_END opcodes, the backend will implement them
14991 * by storing the current native offset into MonoMethodVar->live_range_start/end.
14993 if (cfg->backend->have_liverange_ops && cfg->compute_precise_live_ranges && cfg->comp_done & MONO_COMP_LIVENESS) {
14994 for (i = 0; i < cfg->num_varinfo; ++i) {
14995 int vreg = MONO_VARINFO (cfg, i)->vreg;
14998 if (live_range_start [vreg]) {
14999 MONO_INST_NEW (cfg, ins, OP_LIVERANGE_START);
15001 ins->inst_c1 = vreg;
15002 mono_bblock_insert_after_ins (live_range_start_bb [vreg], live_range_start [vreg], ins);
15004 if (live_range_end [vreg]) {
15005 MONO_INST_NEW (cfg, ins, OP_LIVERANGE_END);
15007 ins->inst_c1 = vreg;
15008 if (live_range_end [vreg] == live_range_end_bb [vreg]->last_ins)
15009 mono_add_ins_to_end (live_range_end_bb [vreg], ins);
15011 mono_bblock_insert_after_ins (live_range_end_bb [vreg], live_range_end [vreg], ins);
15016 if (cfg->gsharedvt_locals_var_ins) {
15017 /* Nullify if unused */
15018 cfg->gsharedvt_locals_var_ins->opcode = OP_PCONST;
15019 cfg->gsharedvt_locals_var_ins->inst_imm = 0;
15022 g_free (live_range_start);
15023 g_free (live_range_end);
15024 g_free (live_range_start_bb);
15025 g_free (live_range_end_bb);
15030 * - use 'iadd' instead of 'int_add'
15031 * - handling ovf opcodes: decompose in method_to_ir.
15032 * - unify iregs/fregs
15033 * -> partly done, the missing parts are:
15034 * - a more complete unification would involve unifying the hregs as well, so
15035 * code wouldn't need if (fp) all over the place. but that would mean the hregs
15036 * would no longer map to the machine hregs, so the code generators would need to
15037 * be modified. Also, on ia64 for example, niregs + nfregs > 256 -> bitmasks
15038 * wouldn't work any more. Duplicating the code in mono_local_regalloc () into
15039 * fp/non-fp branches speeds it up by about 15%.
15040 * - use sext/zext opcodes instead of shifts
15042 * - get rid of TEMPLOADs if possible and use vregs instead
15043 * - clean up usage of OP_P/OP_ opcodes
15044 * - cleanup usage of DUMMY_USE
15045 * - cleanup the setting of ins->type for MonoInst's which are pushed on the
15047 * - set the stack type and allocate a dreg in the EMIT_NEW macros
15048 * - get rid of all the <foo>2 stuff when the new JIT is ready.
15049 * - make sure handle_stack_args () is called before the branch is emitted
15050 * - when the new IR is done, get rid of all unused stuff
15051 * - COMPARE/BEQ as separate instructions or unify them ?
15052 * - keeping them separate allows specialized compare instructions like
15053 * compare_imm, compare_membase
15054 * - most back ends unify fp compare+branch, fp compare+ceq
15055 * - integrate mono_save_args into inline_method
15056 * - get rid of the empty bblocks created by MONO_EMIT_NEW_BRACH_BLOCK2
15057 * - handle long shift opts on 32 bit platforms somehow: they require
15058 * 3 sregs (2 for arg1 and 1 for arg2)
15059 * - make byref a 'normal' type.
15060 * - use vregs for bb->out_stacks if possible, handle_global_vreg will make them a
15061 * variable if needed.
15062 * - do not start a new IL level bblock when cfg->cbb is changed by a function call
15063 * like inline_method.
15064 * - remove inlining restrictions
15065 * - fix LNEG and enable cfold of INEG
15066 * - generalize x86 optimizations like ldelema as a peephole optimization
15067 * - add store_mem_imm for amd64
15068 * - optimize the loading of the interruption flag in the managed->native wrappers
15069 * - avoid special handling of OP_NOP in passes
15070 * - move code inserting instructions into one function/macro.
15071 * - try a coalescing phase after liveness analysis
15072 * - add float -> vreg conversion + local optimizations on !x86
15073 * - figure out how to handle decomposed branches during optimizations, ie.
15074 * compare+branch, op_jump_table+op_br etc.
15075 * - promote RuntimeXHandles to vregs
15076 * - vtype cleanups:
15077 * - add a NEW_VARLOADA_VREG macro
15078 * - the vtype optimizations are blocked by the LDADDR opcodes generated for
15079 * accessing vtype fields.
15080 * - get rid of I8CONST on 64 bit platforms
15081 * - dealing with the increase in code size due to branches created during opcode
15083 * - use extended basic blocks
15084 * - all parts of the JIT
15085 * - handle_global_vregs () && local regalloc
15086 * - avoid introducing global vregs during decomposition, like 'vtable' in isinst
15087 * - sources of increase in code size:
15090 * - isinst and castclass
15091 * - lvregs not allocated to global registers even if used multiple times
15092 * - call cctors outside the JIT, to make -v output more readable and JIT timings more
15094 * - check for fp stack leakage in other opcodes too. (-> 'exceptions' optimization)
15095 * - add all micro optimizations from the old JIT
15096 * - put tree optimizations into the deadce pass
15097 * - decompose op_start_handler/op_endfilter/op_endfinally earlier using an arch
15098 * specific function.
15099 * - unify the float comparison opcodes with the other comparison opcodes, i.e.
15100 * fcompare + branchCC.
15101 * - create a helper function for allocating a stack slot, taking into account
15102 * MONO_CFG_HAS_SPILLUP.
15104 * - merge the ia64 switch changes.
15105 * - optimize mono_regstate2_alloc_int/float.
15106 * - fix the pessimistic handling of variables accessed in exception handler blocks.
15107 * - need to write a tree optimization pass, but the creation of trees is difficult, i.e.
15108 * parts of the tree could be separated by other instructions, killing the tree
15109 * arguments, or stores killing loads etc. Also, should we fold loads into other
15110 * instructions if the result of the load is used multiple times ?
15111 * - make the REM_IMM optimization in mini-x86.c arch-independent.
15112 * - LAST MERGE: 108395.
15113 * - when returning vtypes in registers, generate IR and append it to the end of the
15114 * last bb instead of doing it in the epilog.
15115 * - change the store opcodes so they use sreg1 instead of dreg to store the base register.
15123 - When to decompose opcodes:
15124 - earlier: this makes some optimizations hard to implement, since the low level IR
15125 no longer contains the neccessary information. But it is easier to do.
15126 - later: harder to implement, enables more optimizations.
15127 - Branches inside bblocks:
15128 - created when decomposing complex opcodes.
15129 - branches to another bblock: harmless, but not tracked by the branch
15130 optimizations, so need to branch to a label at the start of the bblock.
15131 - branches to inside the same bblock: very problematic, trips up the local
15132 reg allocator. Can be fixed by spitting the current bblock, but that is a
15133 complex operation, since some local vregs can become global vregs etc.
15134 - Local/global vregs:
15135 - local vregs: temporary vregs used inside one bblock. Assigned to hregs by the
15136 local register allocator.
15137 - global vregs: used in more than one bblock. Have an associated MonoMethodVar
15138 structure, created by mono_create_var (). Assigned to hregs or the stack by
15139 the global register allocator.
15140 - When to do optimizations like alu->alu_imm:
15141 - earlier -> saves work later on since the IR will be smaller/simpler
15142 - later -> can work on more instructions
15143 - Handling of valuetypes:
15144 - When a vtype is pushed on the stack, a new temporary is created, an
15145 instruction computing its address (LDADDR) is emitted and pushed on
15146 the stack. Need to optimize cases when the vtype is used immediately as in
15147 argument passing, stloc etc.
15148 - Instead of the to_end stuff in the old JIT, simply call the function handling
15149 the values on the stack before emitting the last instruction of the bb.
15152 #endif /* DISABLE_JIT */