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/debug-mono-symfile.h>
60 #include <mono/utils/mono-compiler.h>
61 #include <mono/utils/mono-memory-model.h>
62 #include <mono/metadata/mono-basic-block.h>
68 #include "jit-icalls.h"
70 #include "debugger-agent.h"
71 #include "seq-points.h"
72 #include "aot-compiler.h"
73 #include "mini-llvm.h"
75 #define BRANCH_COST 10
76 #define INLINE_LENGTH_LIMIT 20
78 /* These have 'cfg' as an implicit argument */
79 #define INLINE_FAILURE(msg) do { \
80 if ((cfg->method != cfg->current_method) && (cfg->current_method->wrapper_type == MONO_WRAPPER_NONE)) { \
81 inline_failure (cfg, msg); \
82 goto exception_exit; \
85 #define CHECK_CFG_EXCEPTION do {\
86 if (cfg->exception_type != MONO_EXCEPTION_NONE) \
87 goto exception_exit; \
89 #define METHOD_ACCESS_FAILURE(method, cmethod) do { \
90 method_access_failure ((cfg), (method), (cmethod)); \
91 goto exception_exit; \
93 #define FIELD_ACCESS_FAILURE(method, field) do { \
94 field_access_failure ((cfg), (method), (field)); \
95 goto exception_exit; \
97 #define GENERIC_SHARING_FAILURE(opcode) do { \
99 gshared_failure (cfg, opcode, __FILE__, __LINE__); \
100 goto exception_exit; \
103 #define GSHAREDVT_FAILURE(opcode) do { \
104 if (cfg->gsharedvt) { \
105 gsharedvt_failure (cfg, opcode, __FILE__, __LINE__); \
106 goto exception_exit; \
109 #define OUT_OF_MEMORY_FAILURE do { \
110 mono_cfg_set_exception (cfg, MONO_EXCEPTION_OUT_OF_MEMORY); \
111 goto exception_exit; \
113 #define DISABLE_AOT(cfg) do { \
114 if ((cfg)->verbose_level >= 2) \
115 printf ("AOT disabled: %s:%d\n", __FILE__, __LINE__); \
116 (cfg)->disable_aot = TRUE; \
118 #define LOAD_ERROR do { \
119 break_on_unverified (); \
120 mono_cfg_set_exception (cfg, MONO_EXCEPTION_TYPE_LOAD); \
121 goto exception_exit; \
124 #define TYPE_LOAD_ERROR(klass) do { \
125 cfg->exception_ptr = klass; \
129 #define CHECK_CFG_ERROR do {\
130 if (!mono_error_ok (&cfg->error)) { \
131 mono_cfg_set_exception (cfg, MONO_EXCEPTION_MONO_ERROR); \
132 goto mono_error_exit; \
136 /* Determine whenever 'ins' represents a load of the 'this' argument */
137 #define MONO_CHECK_THIS(ins) (mono_method_signature (cfg->method)->hasthis && ((ins)->opcode == OP_MOVE) && ((ins)->sreg1 == cfg->args [0]->dreg))
139 static int ldind_to_load_membase (int opcode);
140 static int stind_to_store_membase (int opcode);
142 int mono_op_to_op_imm (int opcode);
143 int mono_op_to_op_imm_noemul (int opcode);
145 MONO_API MonoInst* mono_emit_native_call (MonoCompile *cfg, gconstpointer func, MonoMethodSignature *sig, MonoInst **args);
147 static int inline_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **sp,
148 guchar *ip, guint real_offset, gboolean inline_always);
150 /* helper methods signatures */
151 static MonoMethodSignature *helper_sig_domain_get;
152 static MonoMethodSignature *helper_sig_rgctx_lazy_fetch_trampoline;
155 * Instruction metadata
163 #define MINI_OP(a,b,dest,src1,src2) dest, src1, src2, ' ',
164 #define MINI_OP3(a,b,dest,src1,src2,src3) dest, src1, src2, src3,
170 #if SIZEOF_REGISTER == 8 && SIZEOF_REGISTER == SIZEOF_VOID_P
175 /* keep in sync with the enum in mini.h */
178 #include "mini-ops.h"
183 #define MINI_OP(a,b,dest,src1,src2) ((src2) != NONE ? 2 : ((src1) != NONE ? 1 : 0)),
184 #define MINI_OP3(a,b,dest,src1,src2,src3) ((src3) != NONE ? 3 : ((src2) != NONE ? 2 : ((src1) != NONE ? 1 : 0))),
186 * This should contain the index of the last sreg + 1. This is not the same
187 * as the number of sregs for opcodes like IA64_CMP_EQ_IMM.
189 const gint8 ins_sreg_counts[] = {
190 #include "mini-ops.h"
195 #define MONO_INIT_VARINFO(vi,id) do { \
196 (vi)->range.first_use.pos.bid = 0xffff; \
202 mono_alloc_ireg (MonoCompile *cfg)
204 return alloc_ireg (cfg);
208 mono_alloc_lreg (MonoCompile *cfg)
210 return alloc_lreg (cfg);
214 mono_alloc_freg (MonoCompile *cfg)
216 return alloc_freg (cfg);
220 mono_alloc_preg (MonoCompile *cfg)
222 return alloc_preg (cfg);
226 mono_alloc_dreg (MonoCompile *cfg, MonoStackType stack_type)
228 return alloc_dreg (cfg, stack_type);
232 * mono_alloc_ireg_ref:
234 * Allocate an IREG, and mark it as holding a GC ref.
237 mono_alloc_ireg_ref (MonoCompile *cfg)
239 return alloc_ireg_ref (cfg);
243 * mono_alloc_ireg_mp:
245 * Allocate an IREG, and mark it as holding a managed pointer.
248 mono_alloc_ireg_mp (MonoCompile *cfg)
250 return alloc_ireg_mp (cfg);
254 * mono_alloc_ireg_copy:
256 * Allocate an IREG with the same GC type as VREG.
259 mono_alloc_ireg_copy (MonoCompile *cfg, guint32 vreg)
261 if (vreg_is_ref (cfg, vreg))
262 return alloc_ireg_ref (cfg);
263 else if (vreg_is_mp (cfg, vreg))
264 return alloc_ireg_mp (cfg);
266 return alloc_ireg (cfg);
270 mono_type_to_regmove (MonoCompile *cfg, MonoType *type)
275 type = mini_get_underlying_type (type);
277 switch (type->type) {
290 case MONO_TYPE_FNPTR:
292 case MONO_TYPE_CLASS:
293 case MONO_TYPE_STRING:
294 case MONO_TYPE_OBJECT:
295 case MONO_TYPE_SZARRAY:
296 case MONO_TYPE_ARRAY:
300 #if SIZEOF_REGISTER == 8
306 return cfg->r4fp ? OP_RMOVE : OP_FMOVE;
309 case MONO_TYPE_VALUETYPE:
310 if (type->data.klass->enumtype) {
311 type = mono_class_enum_basetype (type->data.klass);
314 if (MONO_CLASS_IS_SIMD (cfg, mono_class_from_mono_type (type)))
317 case MONO_TYPE_TYPEDBYREF:
319 case MONO_TYPE_GENERICINST:
320 type = &type->data.generic_class->container_class->byval_arg;
324 g_assert (cfg->gshared);
325 if (mini_type_var_is_vt (type))
328 return mono_type_to_regmove (cfg, mini_get_underlying_type (type));
330 g_error ("unknown type 0x%02x in type_to_regstore", type->type);
336 mono_print_bb (MonoBasicBlock *bb, const char *msg)
341 printf ("\n%s %d: [IN: ", msg, bb->block_num);
342 for (i = 0; i < bb->in_count; ++i)
343 printf (" BB%d(%d)", bb->in_bb [i]->block_num, bb->in_bb [i]->dfn);
345 for (i = 0; i < bb->out_count; ++i)
346 printf (" BB%d(%d)", bb->out_bb [i]->block_num, bb->out_bb [i]->dfn);
348 for (tree = bb->code; tree; tree = tree->next)
349 mono_print_ins_index (-1, tree);
353 mono_create_helper_signatures (void)
355 helper_sig_domain_get = mono_create_icall_signature ("ptr");
356 helper_sig_rgctx_lazy_fetch_trampoline = mono_create_icall_signature ("ptr ptr");
359 static MONO_NEVER_INLINE void
360 break_on_unverified (void)
362 if (mini_get_debug_options ()->break_on_unverified)
366 static MONO_NEVER_INLINE void
367 method_access_failure (MonoCompile *cfg, MonoMethod *method, MonoMethod *cil_method)
369 char *method_fname = mono_method_full_name (method, TRUE);
370 char *cil_method_fname = mono_method_full_name (cil_method, TRUE);
371 mono_cfg_set_exception (cfg, MONO_EXCEPTION_METHOD_ACCESS);
372 cfg->exception_message = g_strdup_printf ("Method `%s' is inaccessible from method `%s'\n", cil_method_fname, method_fname);
373 g_free (method_fname);
374 g_free (cil_method_fname);
377 static MONO_NEVER_INLINE void
378 field_access_failure (MonoCompile *cfg, MonoMethod *method, MonoClassField *field)
380 char *method_fname = mono_method_full_name (method, TRUE);
381 char *field_fname = mono_field_full_name (field);
382 mono_cfg_set_exception (cfg, MONO_EXCEPTION_FIELD_ACCESS);
383 cfg->exception_message = g_strdup_printf ("Field `%s' is inaccessible from method `%s'\n", field_fname, method_fname);
384 g_free (method_fname);
385 g_free (field_fname);
388 static MONO_NEVER_INLINE void
389 inline_failure (MonoCompile *cfg, const char *msg)
391 if (cfg->verbose_level >= 2)
392 printf ("inline failed: %s\n", msg);
393 mono_cfg_set_exception (cfg, MONO_EXCEPTION_INLINE_FAILED);
396 static MONO_NEVER_INLINE void
397 gshared_failure (MonoCompile *cfg, int opcode, const char *file, int line)
399 if (cfg->verbose_level > 2) \
400 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);
401 mono_cfg_set_exception (cfg, MONO_EXCEPTION_GENERIC_SHARING_FAILED);
404 static MONO_NEVER_INLINE void
405 gsharedvt_failure (MonoCompile *cfg, int opcode, const char *file, int line)
407 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);
408 if (cfg->verbose_level >= 2)
409 printf ("%s\n", cfg->exception_message);
410 mono_cfg_set_exception (cfg, MONO_EXCEPTION_GENERIC_SHARING_FAILED);
414 * When using gsharedvt, some instatiations might be verifiable, and some might be not. i.e.
415 * foo<T> (int i) { ldarg.0; box T; }
417 #define UNVERIFIED do { \
418 if (cfg->gsharedvt) { \
419 if (cfg->verbose_level > 2) \
420 printf ("gsharedvt method failed to verify, falling back to instantiation.\n"); \
421 mono_cfg_set_exception (cfg, MONO_EXCEPTION_GENERIC_SHARING_FAILED); \
422 goto exception_exit; \
424 break_on_unverified (); \
428 #define GET_BBLOCK(cfg,tblock,ip) do { \
429 (tblock) = cfg->cil_offset_to_bb [(ip) - cfg->cil_start]; \
431 if ((ip) >= end || (ip) < header->code) UNVERIFIED; \
432 NEW_BBLOCK (cfg, (tblock)); \
433 (tblock)->cil_code = (ip); \
434 ADD_BBLOCK (cfg, (tblock)); \
438 #if defined(TARGET_X86) || defined(TARGET_AMD64)
439 #define EMIT_NEW_X86_LEA(cfg,dest,sr1,sr2,shift,imm) do { \
440 MONO_INST_NEW (cfg, dest, OP_X86_LEA); \
441 (dest)->dreg = alloc_ireg_mp ((cfg)); \
442 (dest)->sreg1 = (sr1); \
443 (dest)->sreg2 = (sr2); \
444 (dest)->inst_imm = (imm); \
445 (dest)->backend.shift_amount = (shift); \
446 MONO_ADD_INS ((cfg)->cbb, (dest)); \
450 /* Emit conversions so both operands of a binary opcode are of the same type */
452 add_widen_op (MonoCompile *cfg, MonoInst *ins, MonoInst **arg1_ref, MonoInst **arg2_ref)
454 MonoInst *arg1 = *arg1_ref;
455 MonoInst *arg2 = *arg2_ref;
458 ((arg1->type == STACK_R4 && arg2->type == STACK_R8) ||
459 (arg1->type == STACK_R8 && arg2->type == STACK_R4))) {
462 /* Mixing r4/r8 is allowed by the spec */
463 if (arg1->type == STACK_R4) {
464 int dreg = alloc_freg (cfg);
466 EMIT_NEW_UNALU (cfg, conv, OP_RCONV_TO_R8, dreg, arg1->dreg);
467 conv->type = STACK_R8;
471 if (arg2->type == STACK_R4) {
472 int dreg = alloc_freg (cfg);
474 EMIT_NEW_UNALU (cfg, conv, OP_RCONV_TO_R8, dreg, arg2->dreg);
475 conv->type = STACK_R8;
481 #if SIZEOF_REGISTER == 8
482 /* FIXME: Need to add many more cases */
483 if ((arg1)->type == STACK_PTR && (arg2)->type == STACK_I4) {
486 int dr = alloc_preg (cfg);
487 EMIT_NEW_UNALU (cfg, widen, OP_SEXT_I4, dr, (arg2)->dreg);
488 (ins)->sreg2 = widen->dreg;
493 #define ADD_BINOP(op) do { \
494 MONO_INST_NEW (cfg, ins, (op)); \
496 ins->sreg1 = sp [0]->dreg; \
497 ins->sreg2 = sp [1]->dreg; \
498 type_from_op (cfg, ins, sp [0], sp [1]); \
500 /* Have to insert a widening op */ \
501 add_widen_op (cfg, ins, &sp [0], &sp [1]); \
502 ins->dreg = alloc_dreg ((cfg), (MonoStackType)(ins)->type); \
503 MONO_ADD_INS ((cfg)->cbb, (ins)); \
504 *sp++ = mono_decompose_opcode ((cfg), (ins)); \
507 #define ADD_UNOP(op) do { \
508 MONO_INST_NEW (cfg, ins, (op)); \
510 ins->sreg1 = sp [0]->dreg; \
511 type_from_op (cfg, ins, sp [0], NULL); \
513 (ins)->dreg = alloc_dreg ((cfg), (MonoStackType)(ins)->type); \
514 MONO_ADD_INS ((cfg)->cbb, (ins)); \
515 *sp++ = mono_decompose_opcode (cfg, ins); \
518 #define ADD_BINCOND(next_block) do { \
521 MONO_INST_NEW(cfg, cmp, OP_COMPARE); \
522 cmp->sreg1 = sp [0]->dreg; \
523 cmp->sreg2 = sp [1]->dreg; \
524 type_from_op (cfg, cmp, sp [0], sp [1]); \
526 add_widen_op (cfg, cmp, &sp [0], &sp [1]); \
527 type_from_op (cfg, ins, sp [0], sp [1]); \
528 ins->inst_many_bb = (MonoBasicBlock **)mono_mempool_alloc (cfg->mempool, sizeof(gpointer)*2); \
529 GET_BBLOCK (cfg, tblock, target); \
530 link_bblock (cfg, cfg->cbb, tblock); \
531 ins->inst_true_bb = tblock; \
532 if ((next_block)) { \
533 link_bblock (cfg, cfg->cbb, (next_block)); \
534 ins->inst_false_bb = (next_block); \
535 start_new_bblock = 1; \
537 GET_BBLOCK (cfg, tblock, ip); \
538 link_bblock (cfg, cfg->cbb, tblock); \
539 ins->inst_false_bb = tblock; \
540 start_new_bblock = 2; \
542 if (sp != stack_start) { \
543 handle_stack_args (cfg, stack_start, sp - stack_start); \
544 CHECK_UNVERIFIABLE (cfg); \
546 MONO_ADD_INS (cfg->cbb, cmp); \
547 MONO_ADD_INS (cfg->cbb, ins); \
551 * link_bblock: Links two basic blocks
553 * links two basic blocks in the control flow graph, the 'from'
554 * argument is the starting block and the 'to' argument is the block
555 * the control flow ends to after 'from'.
558 link_bblock (MonoCompile *cfg, MonoBasicBlock *from, MonoBasicBlock* to)
560 MonoBasicBlock **newa;
564 if (from->cil_code) {
566 printf ("edge from IL%04x to IL_%04x\n", from->cil_code - cfg->cil_code, to->cil_code - cfg->cil_code);
568 printf ("edge from IL%04x to exit\n", from->cil_code - cfg->cil_code);
571 printf ("edge from entry to IL_%04x\n", to->cil_code - cfg->cil_code);
573 printf ("edge from entry to exit\n");
578 for (i = 0; i < from->out_count; ++i) {
579 if (to == from->out_bb [i]) {
585 newa = (MonoBasicBlock **)mono_mempool_alloc (cfg->mempool, sizeof (gpointer) * (from->out_count + 1));
586 for (i = 0; i < from->out_count; ++i) {
587 newa [i] = from->out_bb [i];
595 for (i = 0; i < to->in_count; ++i) {
596 if (from == to->in_bb [i]) {
602 newa = (MonoBasicBlock **)mono_mempool_alloc (cfg->mempool, sizeof (gpointer) * (to->in_count + 1));
603 for (i = 0; i < to->in_count; ++i) {
604 newa [i] = to->in_bb [i];
613 mono_link_bblock (MonoCompile *cfg, MonoBasicBlock *from, MonoBasicBlock* to)
615 link_bblock (cfg, from, to);
619 * mono_find_block_region:
621 * We mark each basic block with a region ID. We use that to avoid BB
622 * optimizations when blocks are in different regions.
625 * A region token that encodes where this region is, and information
626 * about the clause owner for this block.
628 * The region encodes the try/catch/filter clause that owns this block
629 * as well as the type. -1 is a special value that represents a block
630 * that is in none of try/catch/filter.
633 mono_find_block_region (MonoCompile *cfg, int offset)
635 MonoMethodHeader *header = cfg->header;
636 MonoExceptionClause *clause;
639 for (i = 0; i < header->num_clauses; ++i) {
640 clause = &header->clauses [i];
641 if ((clause->flags == MONO_EXCEPTION_CLAUSE_FILTER) && (offset >= clause->data.filter_offset) &&
642 (offset < (clause->handler_offset)))
643 return ((i + 1) << 8) | MONO_REGION_FILTER | clause->flags;
645 if (MONO_OFFSET_IN_HANDLER (clause, offset)) {
646 if (clause->flags == MONO_EXCEPTION_CLAUSE_FINALLY)
647 return ((i + 1) << 8) | MONO_REGION_FINALLY | clause->flags;
648 else if (clause->flags == MONO_EXCEPTION_CLAUSE_FAULT)
649 return ((i + 1) << 8) | MONO_REGION_FAULT | clause->flags;
651 return ((i + 1) << 8) | MONO_REGION_CATCH | clause->flags;
654 for (i = 0; i < header->num_clauses; ++i) {
655 clause = &header->clauses [i];
657 if (MONO_OFFSET_IN_CLAUSE (clause, offset))
658 return ((i + 1) << 8) | clause->flags;
665 mono_find_final_block (MonoCompile *cfg, unsigned char *ip, unsigned char *target, int type)
667 MonoMethodHeader *header = cfg->header;
668 MonoExceptionClause *clause;
672 for (i = 0; i < header->num_clauses; ++i) {
673 clause = &header->clauses [i];
674 if (MONO_OFFSET_IN_CLAUSE (clause, (ip - header->code)) &&
675 (!MONO_OFFSET_IN_CLAUSE (clause, (target - header->code)))) {
676 if (clause->flags == type)
677 res = g_list_append (res, clause);
684 mono_create_spvar_for_region (MonoCompile *cfg, int region)
688 var = (MonoInst *)g_hash_table_lookup (cfg->spvars, GINT_TO_POINTER (region));
692 var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
693 /* prevent it from being register allocated */
694 var->flags |= MONO_INST_VOLATILE;
696 g_hash_table_insert (cfg->spvars, GINT_TO_POINTER (region), var);
700 mono_find_exvar_for_offset (MonoCompile *cfg, int offset)
702 return (MonoInst *)g_hash_table_lookup (cfg->exvars, GINT_TO_POINTER (offset));
706 mono_create_exvar_for_offset (MonoCompile *cfg, int offset)
710 var = (MonoInst *)g_hash_table_lookup (cfg->exvars, GINT_TO_POINTER (offset));
714 var = mono_compile_create_var (cfg, &mono_defaults.object_class->byval_arg, OP_LOCAL);
715 /* prevent it from being register allocated */
716 var->flags |= MONO_INST_VOLATILE;
718 g_hash_table_insert (cfg->exvars, GINT_TO_POINTER (offset), var);
724 * Returns the type used in the eval stack when @type is loaded.
725 * FIXME: return a MonoType/MonoClass for the byref and VALUETYPE cases.
728 type_to_eval_stack_type (MonoCompile *cfg, MonoType *type, MonoInst *inst)
732 type = mini_get_underlying_type (type);
733 inst->klass = klass = mono_class_from_mono_type (type);
735 inst->type = STACK_MP;
740 switch (type->type) {
742 inst->type = STACK_INV;
750 inst->type = STACK_I4;
755 case MONO_TYPE_FNPTR:
756 inst->type = STACK_PTR;
758 case MONO_TYPE_CLASS:
759 case MONO_TYPE_STRING:
760 case MONO_TYPE_OBJECT:
761 case MONO_TYPE_SZARRAY:
762 case MONO_TYPE_ARRAY:
763 inst->type = STACK_OBJ;
767 inst->type = STACK_I8;
770 inst->type = cfg->r4_stack_type;
773 inst->type = STACK_R8;
775 case MONO_TYPE_VALUETYPE:
776 if (type->data.klass->enumtype) {
777 type = mono_class_enum_basetype (type->data.klass);
781 inst->type = STACK_VTYPE;
784 case MONO_TYPE_TYPEDBYREF:
785 inst->klass = mono_defaults.typed_reference_class;
786 inst->type = STACK_VTYPE;
788 case MONO_TYPE_GENERICINST:
789 type = &type->data.generic_class->container_class->byval_arg;
793 g_assert (cfg->gshared);
794 if (mini_is_gsharedvt_type (type)) {
795 g_assert (cfg->gsharedvt);
796 inst->type = STACK_VTYPE;
798 type_to_eval_stack_type (cfg, mini_get_underlying_type (type), inst);
802 g_error ("unknown type 0x%02x in eval stack type", type->type);
807 * The following tables are used to quickly validate the IL code in type_from_op ().
810 bin_num_table [STACK_MAX] [STACK_MAX] = {
811 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
812 {STACK_INV, STACK_I4, STACK_INV, STACK_PTR, STACK_INV, STACK_MP, STACK_INV, STACK_INV},
813 {STACK_INV, STACK_INV, STACK_I8, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
814 {STACK_INV, STACK_PTR, STACK_INV, STACK_PTR, STACK_INV, STACK_MP, STACK_INV, STACK_INV},
815 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_R8, STACK_INV, STACK_INV, STACK_INV, STACK_R8},
816 {STACK_INV, STACK_MP, STACK_INV, STACK_MP, STACK_INV, STACK_PTR, STACK_INV, STACK_INV},
817 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
818 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
819 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_R8, STACK_INV, STACK_INV, STACK_INV, STACK_R4}
824 STACK_INV, STACK_I4, STACK_I8, STACK_PTR, STACK_R8, STACK_INV, STACK_INV, STACK_INV, STACK_R4
827 /* reduce the size of this table */
829 bin_int_table [STACK_MAX] [STACK_MAX] = {
830 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
831 {STACK_INV, STACK_I4, STACK_INV, STACK_PTR, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
832 {STACK_INV, STACK_INV, STACK_I8, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
833 {STACK_INV, STACK_PTR, STACK_INV, STACK_PTR, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
834 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
835 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
836 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
837 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV}
841 bin_comp_table [STACK_MAX] [STACK_MAX] = {
842 /* Inv i L p F & O vt r4 */
844 {0, 1, 0, 1, 0, 0, 0, 0}, /* i, int32 */
845 {0, 0, 1, 0, 0, 0, 0, 0}, /* L, int64 */
846 {0, 1, 0, 1, 0, 2, 4, 0}, /* p, ptr */
847 {0, 0, 0, 0, 1, 0, 0, 0, 1}, /* F, R8 */
848 {0, 0, 0, 2, 0, 1, 0, 0}, /* &, managed pointer */
849 {0, 0, 0, 4, 0, 0, 3, 0}, /* O, reference */
850 {0, 0, 0, 0, 0, 0, 0, 0}, /* vt value type */
851 {0, 0, 0, 0, 1, 0, 0, 0, 1}, /* r, r4 */
854 /* reduce the size of this table */
856 shift_table [STACK_MAX] [STACK_MAX] = {
857 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
858 {STACK_INV, STACK_I4, STACK_INV, STACK_I4, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
859 {STACK_INV, STACK_I8, STACK_INV, STACK_I8, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
860 {STACK_INV, STACK_PTR, STACK_INV, STACK_PTR, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
861 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
862 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
863 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
864 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV}
868 * Tables to map from the non-specific opcode to the matching
869 * type-specific opcode.
871 /* handles from CEE_ADD to CEE_SHR_UN (CEE_REM_UN for floats) */
873 binops_op_map [STACK_MAX] = {
874 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
877 /* handles from CEE_NEG to CEE_CONV_U8 */
879 unops_op_map [STACK_MAX] = {
880 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
883 /* handles from CEE_CONV_U2 to CEE_SUB_OVF_UN */
885 ovfops_op_map [STACK_MAX] = {
886 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
889 /* handles from CEE_CONV_OVF_I1_UN to CEE_CONV_OVF_U_UN */
891 ovf2ops_op_map [STACK_MAX] = {
892 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
895 /* handles from CEE_CONV_OVF_I1 to CEE_CONV_OVF_U8 */
897 ovf3ops_op_map [STACK_MAX] = {
898 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
901 /* handles from CEE_BEQ to CEE_BLT_UN */
903 beqops_op_map [STACK_MAX] = {
904 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
907 /* handles from CEE_CEQ to CEE_CLT_UN */
909 ceqops_op_map [STACK_MAX] = {
910 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
914 * Sets ins->type (the type on the eval stack) according to the
915 * type of the opcode and the arguments to it.
916 * Invalid IL code is marked by setting ins->type to the invalid value STACK_INV.
918 * FIXME: this function sets ins->type unconditionally in some cases, but
919 * it should set it to invalid for some types (a conv.x on an object)
922 type_from_op (MonoCompile *cfg, MonoInst *ins, MonoInst *src1, MonoInst *src2)
924 switch (ins->opcode) {
931 /* FIXME: check unverifiable args for STACK_MP */
932 ins->type = bin_num_table [src1->type] [src2->type];
933 ins->opcode += binops_op_map [ins->type];
940 ins->type = bin_int_table [src1->type] [src2->type];
941 ins->opcode += binops_op_map [ins->type];
946 ins->type = shift_table [src1->type] [src2->type];
947 ins->opcode += binops_op_map [ins->type];
952 ins->type = bin_comp_table [src1->type] [src2->type] ? STACK_I4: STACK_INV;
953 if ((src1->type == STACK_I8) || ((SIZEOF_VOID_P == 8) && ((src1->type == STACK_PTR) || (src1->type == STACK_OBJ) || (src1->type == STACK_MP))))
954 ins->opcode = OP_LCOMPARE;
955 else if (src1->type == STACK_R4)
956 ins->opcode = OP_RCOMPARE;
957 else if (src1->type == STACK_R8)
958 ins->opcode = OP_FCOMPARE;
960 ins->opcode = OP_ICOMPARE;
962 case OP_ICOMPARE_IMM:
963 ins->type = bin_comp_table [src1->type] [src1->type] ? STACK_I4 : STACK_INV;
964 if ((src1->type == STACK_I8) || ((SIZEOF_VOID_P == 8) && ((src1->type == STACK_PTR) || (src1->type == STACK_OBJ) || (src1->type == STACK_MP))))
965 ins->opcode = OP_LCOMPARE_IMM;
977 ins->opcode += beqops_op_map [src1->type];
980 ins->type = bin_comp_table [src1->type] [src2->type] ? STACK_I4: STACK_INV;
981 ins->opcode += ceqops_op_map [src1->type];
987 ins->type = (bin_comp_table [src1->type] [src2->type] & 1) ? STACK_I4: STACK_INV;
988 ins->opcode += ceqops_op_map [src1->type];
992 ins->type = neg_table [src1->type];
993 ins->opcode += unops_op_map [ins->type];
996 if (src1->type >= STACK_I4 && src1->type <= STACK_PTR)
997 ins->type = src1->type;
999 ins->type = STACK_INV;
1000 ins->opcode += unops_op_map [ins->type];
1006 ins->type = STACK_I4;
1007 ins->opcode += unops_op_map [src1->type];
1010 ins->type = STACK_R8;
1011 switch (src1->type) {
1014 ins->opcode = OP_ICONV_TO_R_UN;
1017 ins->opcode = OP_LCONV_TO_R_UN;
1021 case CEE_CONV_OVF_I1:
1022 case CEE_CONV_OVF_U1:
1023 case CEE_CONV_OVF_I2:
1024 case CEE_CONV_OVF_U2:
1025 case CEE_CONV_OVF_I4:
1026 case CEE_CONV_OVF_U4:
1027 ins->type = STACK_I4;
1028 ins->opcode += ovf3ops_op_map [src1->type];
1030 case CEE_CONV_OVF_I_UN:
1031 case CEE_CONV_OVF_U_UN:
1032 ins->type = STACK_PTR;
1033 ins->opcode += ovf2ops_op_map [src1->type];
1035 case CEE_CONV_OVF_I1_UN:
1036 case CEE_CONV_OVF_I2_UN:
1037 case CEE_CONV_OVF_I4_UN:
1038 case CEE_CONV_OVF_U1_UN:
1039 case CEE_CONV_OVF_U2_UN:
1040 case CEE_CONV_OVF_U4_UN:
1041 ins->type = STACK_I4;
1042 ins->opcode += ovf2ops_op_map [src1->type];
1045 ins->type = STACK_PTR;
1046 switch (src1->type) {
1048 ins->opcode = OP_ICONV_TO_U;
1052 #if SIZEOF_VOID_P == 8
1053 ins->opcode = OP_LCONV_TO_U;
1055 ins->opcode = OP_MOVE;
1059 ins->opcode = OP_LCONV_TO_U;
1062 ins->opcode = OP_FCONV_TO_U;
1068 ins->type = STACK_I8;
1069 ins->opcode += unops_op_map [src1->type];
1071 case CEE_CONV_OVF_I8:
1072 case CEE_CONV_OVF_U8:
1073 ins->type = STACK_I8;
1074 ins->opcode += ovf3ops_op_map [src1->type];
1076 case CEE_CONV_OVF_U8_UN:
1077 case CEE_CONV_OVF_I8_UN:
1078 ins->type = STACK_I8;
1079 ins->opcode += ovf2ops_op_map [src1->type];
1082 ins->type = cfg->r4_stack_type;
1083 ins->opcode += unops_op_map [src1->type];
1086 ins->type = STACK_R8;
1087 ins->opcode += unops_op_map [src1->type];
1090 ins->type = STACK_R8;
1094 ins->type = STACK_I4;
1095 ins->opcode += ovfops_op_map [src1->type];
1098 case CEE_CONV_OVF_I:
1099 case CEE_CONV_OVF_U:
1100 ins->type = STACK_PTR;
1101 ins->opcode += ovfops_op_map [src1->type];
1104 case CEE_ADD_OVF_UN:
1106 case CEE_MUL_OVF_UN:
1108 case CEE_SUB_OVF_UN:
1109 ins->type = bin_num_table [src1->type] [src2->type];
1110 ins->opcode += ovfops_op_map [src1->type];
1111 if (ins->type == STACK_R8)
1112 ins->type = STACK_INV;
1114 case OP_LOAD_MEMBASE:
1115 ins->type = STACK_PTR;
1117 case OP_LOADI1_MEMBASE:
1118 case OP_LOADU1_MEMBASE:
1119 case OP_LOADI2_MEMBASE:
1120 case OP_LOADU2_MEMBASE:
1121 case OP_LOADI4_MEMBASE:
1122 case OP_LOADU4_MEMBASE:
1123 ins->type = STACK_PTR;
1125 case OP_LOADI8_MEMBASE:
1126 ins->type = STACK_I8;
1128 case OP_LOADR4_MEMBASE:
1129 ins->type = cfg->r4_stack_type;
1131 case OP_LOADR8_MEMBASE:
1132 ins->type = STACK_R8;
1135 g_error ("opcode 0x%04x not handled in type from op", ins->opcode);
1139 if (ins->type == STACK_MP)
1140 ins->klass = mono_defaults.object_class;
1145 STACK_I4, STACK_I4, STACK_I4, STACK_I4, STACK_I4, STACK_I4, STACK_I8, STACK_PTR, STACK_R8, STACK_R8, STACK_OBJ
1151 param_table [STACK_MAX] [STACK_MAX] = {
1156 check_values_to_signature (MonoInst *args, MonoType *this_ins, MonoMethodSignature *sig)
1161 switch (args->type) {
1171 for (i = 0; i < sig->param_count; ++i) {
1172 switch (args [i].type) {
1176 if (!sig->params [i]->byref)
1180 if (sig->params [i]->byref)
1182 switch (sig->params [i]->type) {
1183 case MONO_TYPE_CLASS:
1184 case MONO_TYPE_STRING:
1185 case MONO_TYPE_OBJECT:
1186 case MONO_TYPE_SZARRAY:
1187 case MONO_TYPE_ARRAY:
1194 if (sig->params [i]->byref)
1196 if (sig->params [i]->type != MONO_TYPE_R4 && sig->params [i]->type != MONO_TYPE_R8)
1205 /*if (!param_table [args [i].type] [sig->params [i]->type])
1213 * When we need a pointer to the current domain many times in a method, we
1214 * call mono_domain_get() once and we store the result in a local variable.
1215 * This function returns the variable that represents the MonoDomain*.
1217 inline static MonoInst *
1218 mono_get_domainvar (MonoCompile *cfg)
1220 if (!cfg->domainvar)
1221 cfg->domainvar = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
1222 return cfg->domainvar;
1226 * The got_var contains the address of the Global Offset Table when AOT
1230 mono_get_got_var (MonoCompile *cfg)
1232 if (!cfg->compile_aot || !cfg->backend->need_got_var)
1234 if (!cfg->got_var) {
1235 cfg->got_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
1237 return cfg->got_var;
1241 mono_get_vtable_var (MonoCompile *cfg)
1243 g_assert (cfg->gshared);
1245 if (!cfg->rgctx_var) {
1246 cfg->rgctx_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
1247 /* force the var to be stack allocated */
1248 cfg->rgctx_var->flags |= MONO_INST_VOLATILE;
1251 return cfg->rgctx_var;
1255 type_from_stack_type (MonoInst *ins) {
1256 switch (ins->type) {
1257 case STACK_I4: return &mono_defaults.int32_class->byval_arg;
1258 case STACK_I8: return &mono_defaults.int64_class->byval_arg;
1259 case STACK_PTR: return &mono_defaults.int_class->byval_arg;
1260 case STACK_R4: return &mono_defaults.single_class->byval_arg;
1261 case STACK_R8: return &mono_defaults.double_class->byval_arg;
1263 return &ins->klass->this_arg;
1264 case STACK_OBJ: return &mono_defaults.object_class->byval_arg;
1265 case STACK_VTYPE: return &ins->klass->byval_arg;
1267 g_error ("stack type %d to monotype not handled\n", ins->type);
1272 static G_GNUC_UNUSED int
1273 type_to_stack_type (MonoCompile *cfg, MonoType *t)
1275 t = mono_type_get_underlying_type (t);
1287 case MONO_TYPE_FNPTR:
1289 case MONO_TYPE_CLASS:
1290 case MONO_TYPE_STRING:
1291 case MONO_TYPE_OBJECT:
1292 case MONO_TYPE_SZARRAY:
1293 case MONO_TYPE_ARRAY:
1299 return cfg->r4_stack_type;
1302 case MONO_TYPE_VALUETYPE:
1303 case MONO_TYPE_TYPEDBYREF:
1305 case MONO_TYPE_GENERICINST:
1306 if (mono_type_generic_inst_is_valuetype (t))
1312 g_assert_not_reached ();
1319 array_access_to_klass (int opcode)
1323 return mono_defaults.byte_class;
1325 return mono_defaults.uint16_class;
1328 return mono_defaults.int_class;
1331 return mono_defaults.sbyte_class;
1334 return mono_defaults.int16_class;
1337 return mono_defaults.int32_class;
1339 return mono_defaults.uint32_class;
1342 return mono_defaults.int64_class;
1345 return mono_defaults.single_class;
1348 return mono_defaults.double_class;
1349 case CEE_LDELEM_REF:
1350 case CEE_STELEM_REF:
1351 return mono_defaults.object_class;
1353 g_assert_not_reached ();
1359 * We try to share variables when possible
1362 mono_compile_get_interface_var (MonoCompile *cfg, int slot, MonoInst *ins)
1367 /* inlining can result in deeper stacks */
1368 if (slot >= cfg->header->max_stack)
1369 return mono_compile_create_var (cfg, type_from_stack_type (ins), OP_LOCAL);
1371 pos = ins->type - 1 + slot * STACK_MAX;
1373 switch (ins->type) {
1380 if ((vnum = cfg->intvars [pos]))
1381 return cfg->varinfo [vnum];
1382 res = mono_compile_create_var (cfg, type_from_stack_type (ins), OP_LOCAL);
1383 cfg->intvars [pos] = res->inst_c0;
1386 res = mono_compile_create_var (cfg, type_from_stack_type (ins), OP_LOCAL);
1392 mono_save_token_info (MonoCompile *cfg, MonoImage *image, guint32 token, gpointer key)
1395 * Don't use this if a generic_context is set, since that means AOT can't
1396 * look up the method using just the image+token.
1397 * table == 0 means this is a reference made from a wrapper.
1399 if (cfg->compile_aot && !cfg->generic_context && (mono_metadata_token_table (token) > 0)) {
1400 MonoJumpInfoToken *jump_info_token = (MonoJumpInfoToken *)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoJumpInfoToken));
1401 jump_info_token->image = image;
1402 jump_info_token->token = token;
1403 g_hash_table_insert (cfg->token_info_hash, key, jump_info_token);
1408 * This function is called to handle items that are left on the evaluation stack
1409 * at basic block boundaries. What happens is that we save the values to local variables
1410 * and we reload them later when first entering the target basic block (with the
1411 * handle_loaded_temps () function).
1412 * A single joint point will use the same variables (stored in the array bb->out_stack or
1413 * bb->in_stack, if the basic block is before or after the joint point).
1415 * This function needs to be called _before_ emitting the last instruction of
1416 * the bb (i.e. before emitting a branch).
1417 * If the stack merge fails at a join point, cfg->unverifiable is set.
1420 handle_stack_args (MonoCompile *cfg, MonoInst **sp, int count)
1423 MonoBasicBlock *bb = cfg->cbb;
1424 MonoBasicBlock *outb;
1425 MonoInst *inst, **locals;
1430 if (cfg->verbose_level > 3)
1431 printf ("%d item(s) on exit from B%d\n", count, bb->block_num);
1432 if (!bb->out_scount) {
1433 bb->out_scount = count;
1434 //printf ("bblock %d has out:", bb->block_num);
1436 for (i = 0; i < bb->out_count; ++i) {
1437 outb = bb->out_bb [i];
1438 /* exception handlers are linked, but they should not be considered for stack args */
1439 if (outb->flags & BB_EXCEPTION_HANDLER)
1441 //printf (" %d", outb->block_num);
1442 if (outb->in_stack) {
1444 bb->out_stack = outb->in_stack;
1450 bb->out_stack = (MonoInst **)mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*) * count);
1451 for (i = 0; i < count; ++i) {
1453 * try to reuse temps already allocated for this purpouse, if they occupy the same
1454 * stack slot and if they are of the same type.
1455 * This won't cause conflicts since if 'local' is used to
1456 * store one of the values in the in_stack of a bblock, then
1457 * the same variable will be used for the same outgoing stack
1459 * This doesn't work when inlining methods, since the bblocks
1460 * in the inlined methods do not inherit their in_stack from
1461 * the bblock they are inlined to. See bug #58863 for an
1464 if (cfg->inlined_method)
1465 bb->out_stack [i] = mono_compile_create_var (cfg, type_from_stack_type (sp [i]), OP_LOCAL);
1467 bb->out_stack [i] = mono_compile_get_interface_var (cfg, i, sp [i]);
1472 for (i = 0; i < bb->out_count; ++i) {
1473 outb = bb->out_bb [i];
1474 /* exception handlers are linked, but they should not be considered for stack args */
1475 if (outb->flags & BB_EXCEPTION_HANDLER)
1477 if (outb->in_scount) {
1478 if (outb->in_scount != bb->out_scount) {
1479 cfg->unverifiable = TRUE;
1482 continue; /* check they are the same locals */
1484 outb->in_scount = count;
1485 outb->in_stack = bb->out_stack;
1488 locals = bb->out_stack;
1490 for (i = 0; i < count; ++i) {
1491 EMIT_NEW_TEMPSTORE (cfg, inst, locals [i]->inst_c0, sp [i]);
1492 inst->cil_code = sp [i]->cil_code;
1493 sp [i] = locals [i];
1494 if (cfg->verbose_level > 3)
1495 printf ("storing %d to temp %d\n", i, (int)locals [i]->inst_c0);
1499 * It is possible that the out bblocks already have in_stack assigned, and
1500 * the in_stacks differ. In this case, we will store to all the different
1507 /* Find a bblock which has a different in_stack */
1509 while (bindex < bb->out_count) {
1510 outb = bb->out_bb [bindex];
1511 /* exception handlers are linked, but they should not be considered for stack args */
1512 if (outb->flags & BB_EXCEPTION_HANDLER) {
1516 if (outb->in_stack != locals) {
1517 for (i = 0; i < count; ++i) {
1518 EMIT_NEW_TEMPSTORE (cfg, inst, outb->in_stack [i]->inst_c0, sp [i]);
1519 inst->cil_code = sp [i]->cil_code;
1520 sp [i] = locals [i];
1521 if (cfg->verbose_level > 3)
1522 printf ("storing %d to temp %d\n", i, (int)outb->in_stack [i]->inst_c0);
1524 locals = outb->in_stack;
1534 emit_runtime_constant (MonoCompile *cfg, MonoJumpInfoType patch_type, gpointer data)
1538 if (cfg->compile_aot) {
1539 EMIT_NEW_AOTCONST (cfg, ins, patch_type, data);
1544 ji.type = patch_type;
1545 ji.data.target = data;
1546 target = mono_resolve_patch_target (NULL, cfg->domain, NULL, &ji, FALSE);
1548 EMIT_NEW_PCONST (cfg, ins, target);
1554 mini_emit_interface_bitmap_check (MonoCompile *cfg, int intf_bit_reg, int base_reg, int offset, MonoClass *klass)
1556 int ibitmap_reg = alloc_preg (cfg);
1557 #ifdef COMPRESSED_INTERFACE_BITMAP
1559 MonoInst *res, *ins;
1560 NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, ibitmap_reg, base_reg, offset);
1561 MONO_ADD_INS (cfg->cbb, ins);
1563 args [1] = emit_runtime_constant (cfg, MONO_PATCH_INFO_IID, klass);
1564 res = mono_emit_jit_icall (cfg, mono_class_interface_match, args);
1565 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, intf_bit_reg, res->dreg);
1567 int ibitmap_byte_reg = alloc_preg (cfg);
1569 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, ibitmap_reg, base_reg, offset);
1571 if (cfg->compile_aot) {
1572 int iid_reg = alloc_preg (cfg);
1573 int shifted_iid_reg = alloc_preg (cfg);
1574 int ibitmap_byte_address_reg = alloc_preg (cfg);
1575 int masked_iid_reg = alloc_preg (cfg);
1576 int iid_one_bit_reg = alloc_preg (cfg);
1577 int iid_bit_reg = alloc_preg (cfg);
1578 MONO_EMIT_NEW_AOTCONST (cfg, iid_reg, klass, MONO_PATCH_INFO_IID);
1579 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SHR_IMM, shifted_iid_reg, iid_reg, 3);
1580 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, ibitmap_byte_address_reg, ibitmap_reg, shifted_iid_reg);
1581 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU1_MEMBASE, ibitmap_byte_reg, ibitmap_byte_address_reg, 0);
1582 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_IAND_IMM, masked_iid_reg, iid_reg, 7);
1583 MONO_EMIT_NEW_ICONST (cfg, iid_one_bit_reg, 1);
1584 MONO_EMIT_NEW_BIALU (cfg, OP_ISHL, iid_bit_reg, iid_one_bit_reg, masked_iid_reg);
1585 MONO_EMIT_NEW_BIALU (cfg, OP_IAND, intf_bit_reg, ibitmap_byte_reg, iid_bit_reg);
1587 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI1_MEMBASE, ibitmap_byte_reg, ibitmap_reg, klass->interface_id >> 3);
1588 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_AND_IMM, intf_bit_reg, ibitmap_byte_reg, 1 << (klass->interface_id & 7));
1594 * Emit code which loads into "intf_bit_reg" a nonzero value if the MonoClass
1595 * stored in "klass_reg" implements the interface "klass".
1598 mini_emit_load_intf_bit_reg_class (MonoCompile *cfg, int intf_bit_reg, int klass_reg, MonoClass *klass)
1600 mini_emit_interface_bitmap_check (cfg, intf_bit_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, interface_bitmap), klass);
1604 * Emit code which loads into "intf_bit_reg" a nonzero value if the MonoVTable
1605 * stored in "vtable_reg" implements the interface "klass".
1608 mini_emit_load_intf_bit_reg_vtable (MonoCompile *cfg, int intf_bit_reg, int vtable_reg, MonoClass *klass)
1610 mini_emit_interface_bitmap_check (cfg, intf_bit_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, interface_bitmap), klass);
1614 * Emit code which checks whenever the interface id of @klass is smaller than
1615 * than the value given by max_iid_reg.
1618 mini_emit_max_iid_check (MonoCompile *cfg, int max_iid_reg, MonoClass *klass,
1619 MonoBasicBlock *false_target)
1621 if (cfg->compile_aot) {
1622 int iid_reg = alloc_preg (cfg);
1623 MONO_EMIT_NEW_AOTCONST (cfg, iid_reg, klass, MONO_PATCH_INFO_IID);
1624 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, max_iid_reg, iid_reg);
1627 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, max_iid_reg, klass->interface_id);
1629 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBLT_UN, false_target);
1631 MONO_EMIT_NEW_COND_EXC (cfg, LT_UN, "InvalidCastException");
1634 /* Same as above, but obtains max_iid from a vtable */
1636 mini_emit_max_iid_check_vtable (MonoCompile *cfg, int vtable_reg, MonoClass *klass,
1637 MonoBasicBlock *false_target)
1639 int max_iid_reg = alloc_preg (cfg);
1641 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU2_MEMBASE, max_iid_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, max_interface_id));
1642 mini_emit_max_iid_check (cfg, max_iid_reg, klass, false_target);
1645 /* Same as above, but obtains max_iid from a klass */
1647 mini_emit_max_iid_check_class (MonoCompile *cfg, int klass_reg, MonoClass *klass,
1648 MonoBasicBlock *false_target)
1650 int max_iid_reg = alloc_preg (cfg);
1652 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU2_MEMBASE, max_iid_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, max_interface_id));
1653 mini_emit_max_iid_check (cfg, max_iid_reg, klass, false_target);
1657 mini_emit_isninst_cast_inst (MonoCompile *cfg, int klass_reg, MonoClass *klass, MonoInst *klass_ins, MonoBasicBlock *false_target, MonoBasicBlock *true_target)
1659 int idepth_reg = alloc_preg (cfg);
1660 int stypes_reg = alloc_preg (cfg);
1661 int stype = alloc_preg (cfg);
1663 mono_class_setup_supertypes (klass);
1665 if (klass->idepth > MONO_DEFAULT_SUPERTABLE_SIZE) {
1666 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU2_MEMBASE, idepth_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, idepth));
1667 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, idepth_reg, klass->idepth);
1668 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBLT_UN, false_target);
1670 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, stypes_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, supertypes));
1671 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, stype, stypes_reg, ((klass->idepth - 1) * SIZEOF_VOID_P));
1673 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, stype, klass_ins->dreg);
1674 } else if (cfg->compile_aot) {
1675 int const_reg = alloc_preg (cfg);
1676 MONO_EMIT_NEW_CLASSCONST (cfg, const_reg, klass);
1677 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, stype, const_reg);
1679 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, stype, klass);
1681 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, true_target);
1685 mini_emit_isninst_cast (MonoCompile *cfg, int klass_reg, MonoClass *klass, MonoBasicBlock *false_target, MonoBasicBlock *true_target)
1687 mini_emit_isninst_cast_inst (cfg, klass_reg, klass, NULL, false_target, true_target);
1691 mini_emit_iface_cast (MonoCompile *cfg, int vtable_reg, MonoClass *klass, MonoBasicBlock *false_target, MonoBasicBlock *true_target)
1693 int intf_reg = alloc_preg (cfg);
1695 mini_emit_max_iid_check_vtable (cfg, vtable_reg, klass, false_target);
1696 mini_emit_load_intf_bit_reg_vtable (cfg, intf_reg, vtable_reg, klass);
1697 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, intf_reg, 0);
1699 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, true_target);
1701 MONO_EMIT_NEW_COND_EXC (cfg, EQ, "InvalidCastException");
1705 * Variant of the above that takes a register to the class, not the vtable.
1708 mini_emit_iface_class_cast (MonoCompile *cfg, int klass_reg, MonoClass *klass, MonoBasicBlock *false_target, MonoBasicBlock *true_target)
1710 int intf_bit_reg = alloc_preg (cfg);
1712 mini_emit_max_iid_check_class (cfg, klass_reg, klass, false_target);
1713 mini_emit_load_intf_bit_reg_class (cfg, intf_bit_reg, klass_reg, klass);
1714 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, intf_bit_reg, 0);
1716 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, true_target);
1718 MONO_EMIT_NEW_COND_EXC (cfg, EQ, "InvalidCastException");
1722 mini_emit_class_check_inst (MonoCompile *cfg, int klass_reg, MonoClass *klass, MonoInst *klass_inst)
1725 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, klass_reg, klass_inst->dreg);
1727 MonoInst *ins = emit_runtime_constant (cfg, MONO_PATCH_INFO_CLASS, klass);
1728 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, klass_reg, ins->dreg);
1730 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException");
1734 mini_emit_class_check (MonoCompile *cfg, int klass_reg, MonoClass *klass)
1736 mini_emit_class_check_inst (cfg, klass_reg, klass, NULL);
1740 mini_emit_class_check_branch (MonoCompile *cfg, int klass_reg, MonoClass *klass, int branch_op, MonoBasicBlock *target)
1742 if (cfg->compile_aot) {
1743 int const_reg = alloc_preg (cfg);
1744 MONO_EMIT_NEW_CLASSCONST (cfg, const_reg, klass);
1745 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, klass_reg, const_reg);
1747 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, klass_reg, klass);
1749 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, branch_op, target);
1753 mini_emit_castclass (MonoCompile *cfg, int obj_reg, int klass_reg, MonoClass *klass, MonoBasicBlock *object_is_null);
1756 mini_emit_castclass_inst (MonoCompile *cfg, int obj_reg, int klass_reg, MonoClass *klass, MonoInst *klass_inst, MonoBasicBlock *object_is_null)
1759 int rank_reg = alloc_preg (cfg);
1760 int eclass_reg = alloc_preg (cfg);
1762 g_assert (!klass_inst);
1763 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU1_MEMBASE, rank_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, rank));
1764 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, rank_reg, klass->rank);
1765 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException");
1766 // MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
1767 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, eclass_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, cast_class));
1768 if (klass->cast_class == mono_defaults.object_class) {
1769 int parent_reg = alloc_preg (cfg);
1770 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, parent_reg, eclass_reg, MONO_STRUCT_OFFSET (MonoClass, parent));
1771 mini_emit_class_check_branch (cfg, parent_reg, mono_defaults.enum_class->parent, OP_PBNE_UN, object_is_null);
1772 mini_emit_class_check (cfg, eclass_reg, mono_defaults.enum_class);
1773 } else if (klass->cast_class == mono_defaults.enum_class->parent) {
1774 mini_emit_class_check_branch (cfg, eclass_reg, mono_defaults.enum_class->parent, OP_PBEQ, object_is_null);
1775 mini_emit_class_check (cfg, eclass_reg, mono_defaults.enum_class);
1776 } else if (klass->cast_class == mono_defaults.enum_class) {
1777 mini_emit_class_check (cfg, eclass_reg, mono_defaults.enum_class);
1778 } else if (klass->cast_class->flags & TYPE_ATTRIBUTE_INTERFACE) {
1779 mini_emit_iface_class_cast (cfg, eclass_reg, klass->cast_class, NULL, NULL);
1781 // Pass -1 as obj_reg to skip the check below for arrays of arrays
1782 mini_emit_castclass (cfg, -1, eclass_reg, klass->cast_class, object_is_null);
1785 if ((klass->rank == 1) && (klass->byval_arg.type == MONO_TYPE_SZARRAY) && (obj_reg != -1)) {
1786 /* Check that the object is a vector too */
1787 int bounds_reg = alloc_preg (cfg);
1788 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, bounds_reg, obj_reg, MONO_STRUCT_OFFSET (MonoArray, bounds));
1789 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, bounds_reg, 0);
1790 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException");
1793 int idepth_reg = alloc_preg (cfg);
1794 int stypes_reg = alloc_preg (cfg);
1795 int stype = alloc_preg (cfg);
1797 mono_class_setup_supertypes (klass);
1799 if (klass->idepth > MONO_DEFAULT_SUPERTABLE_SIZE) {
1800 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU2_MEMBASE, idepth_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, idepth));
1801 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, idepth_reg, klass->idepth);
1802 MONO_EMIT_NEW_COND_EXC (cfg, LT_UN, "InvalidCastException");
1804 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, stypes_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, supertypes));
1805 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, stype, stypes_reg, ((klass->idepth - 1) * SIZEOF_VOID_P));
1806 mini_emit_class_check_inst (cfg, stype, klass, klass_inst);
1811 mini_emit_castclass (MonoCompile *cfg, int obj_reg, int klass_reg, MonoClass *klass, MonoBasicBlock *object_is_null)
1813 mini_emit_castclass_inst (cfg, obj_reg, klass_reg, klass, NULL, object_is_null);
1817 mini_emit_memset (MonoCompile *cfg, int destreg, int offset, int size, int val, int align)
1821 g_assert (val == 0);
1826 if ((size <= SIZEOF_REGISTER) && (size <= align)) {
1829 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREI1_MEMBASE_IMM, destreg, offset, val);
1832 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREI2_MEMBASE_IMM, destreg, offset, val);
1835 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREI4_MEMBASE_IMM, destreg, offset, val);
1837 #if SIZEOF_REGISTER == 8
1839 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREI8_MEMBASE_IMM, destreg, offset, val);
1845 val_reg = alloc_preg (cfg);
1847 if (SIZEOF_REGISTER == 8)
1848 MONO_EMIT_NEW_I8CONST (cfg, val_reg, val);
1850 MONO_EMIT_NEW_ICONST (cfg, val_reg, val);
1853 /* This could be optimized further if neccesary */
1855 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI1_MEMBASE_REG, destreg, offset, val_reg);
1862 if (!cfg->backend->no_unaligned_access && SIZEOF_REGISTER == 8) {
1864 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI4_MEMBASE_REG, destreg, offset, val_reg);
1869 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI8_MEMBASE_REG, destreg, offset, val_reg);
1876 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI4_MEMBASE_REG, destreg, offset, val_reg);
1881 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI2_MEMBASE_REG, destreg, offset, val_reg);
1886 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI1_MEMBASE_REG, destreg, offset, val_reg);
1893 mini_emit_memcpy (MonoCompile *cfg, int destreg, int doffset, int srcreg, int soffset, int size, int align)
1900 /*FIXME arbitrary hack to avoid unbound code expansion.*/
1901 g_assert (size < 10000);
1904 /* This could be optimized further if neccesary */
1906 cur_reg = alloc_preg (cfg);
1907 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI1_MEMBASE, cur_reg, srcreg, soffset);
1908 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI1_MEMBASE_REG, destreg, doffset, cur_reg);
1915 if (!cfg->backend->no_unaligned_access && SIZEOF_REGISTER == 8) {
1917 cur_reg = alloc_preg (cfg);
1918 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI8_MEMBASE, cur_reg, srcreg, soffset);
1919 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI8_MEMBASE_REG, destreg, doffset, cur_reg);
1927 cur_reg = alloc_preg (cfg);
1928 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI4_MEMBASE, cur_reg, srcreg, soffset);
1929 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI4_MEMBASE_REG, destreg, doffset, cur_reg);
1935 cur_reg = alloc_preg (cfg);
1936 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI2_MEMBASE, cur_reg, srcreg, soffset);
1937 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI2_MEMBASE_REG, destreg, doffset, cur_reg);
1943 cur_reg = alloc_preg (cfg);
1944 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI1_MEMBASE, cur_reg, srcreg, soffset);
1945 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI1_MEMBASE_REG, destreg, doffset, cur_reg);
1953 emit_tls_set (MonoCompile *cfg, int sreg1, MonoTlsKey tls_key)
1957 if (cfg->compile_aot) {
1958 EMIT_NEW_TLS_OFFSETCONST (cfg, c, tls_key);
1959 MONO_INST_NEW (cfg, ins, OP_TLS_SET_REG);
1961 ins->sreg2 = c->dreg;
1962 MONO_ADD_INS (cfg->cbb, ins);
1964 MONO_INST_NEW (cfg, ins, OP_TLS_SET);
1966 ins->inst_offset = mini_get_tls_offset (tls_key);
1967 MONO_ADD_INS (cfg->cbb, ins);
1974 * Emit IR to push the current LMF onto the LMF stack.
1977 emit_push_lmf (MonoCompile *cfg)
1980 * Emit IR to push the LMF:
1981 * lmf_addr = <lmf_addr from tls>
1982 * lmf->lmf_addr = lmf_addr
1983 * lmf->prev_lmf = *lmf_addr
1986 int lmf_reg, prev_lmf_reg;
1987 MonoInst *ins, *lmf_ins;
1992 if (cfg->lmf_ir_mono_lmf && mini_tls_get_supported (cfg, TLS_KEY_LMF)) {
1993 /* Load current lmf */
1994 lmf_ins = mono_get_lmf_intrinsic (cfg);
1996 MONO_ADD_INS (cfg->cbb, lmf_ins);
1997 EMIT_NEW_VARLOADA (cfg, ins, cfg->lmf_var, NULL);
1998 lmf_reg = ins->dreg;
1999 /* Save previous_lmf */
2000 EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, lmf_reg, MONO_STRUCT_OFFSET (MonoLMF, previous_lmf), lmf_ins->dreg);
2002 emit_tls_set (cfg, lmf_reg, TLS_KEY_LMF);
2005 * Store lmf_addr in a variable, so it can be allocated to a global register.
2007 if (!cfg->lmf_addr_var)
2008 cfg->lmf_addr_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
2011 ins = mono_get_jit_tls_intrinsic (cfg);
2013 int jit_tls_dreg = ins->dreg;
2015 MONO_ADD_INS (cfg->cbb, ins);
2016 lmf_reg = alloc_preg (cfg);
2017 EMIT_NEW_BIALU_IMM (cfg, lmf_ins, OP_PADD_IMM, lmf_reg, jit_tls_dreg, MONO_STRUCT_OFFSET (MonoJitTlsData, lmf));
2019 lmf_ins = mono_emit_jit_icall (cfg, mono_get_lmf_addr, NULL);
2022 lmf_ins = mono_get_lmf_addr_intrinsic (cfg);
2024 MONO_ADD_INS (cfg->cbb, lmf_ins);
2027 MonoInst *args [16], *jit_tls_ins, *ins;
2029 /* Inline mono_get_lmf_addr () */
2030 /* jit_tls = pthread_getspecific (mono_jit_tls_id); lmf_addr = &jit_tls->lmf; */
2032 /* Load mono_jit_tls_id */
2033 if (cfg->compile_aot)
2034 EMIT_NEW_AOTCONST (cfg, args [0], MONO_PATCH_INFO_JIT_TLS_ID, NULL);
2036 EMIT_NEW_ICONST (cfg, args [0], mono_jit_tls_id);
2037 /* call pthread_getspecific () */
2038 jit_tls_ins = mono_emit_jit_icall (cfg, pthread_getspecific, args);
2039 /* lmf_addr = &jit_tls->lmf */
2040 EMIT_NEW_BIALU_IMM (cfg, ins, OP_PADD_IMM, cfg->lmf_addr_var->dreg, jit_tls_ins->dreg, MONO_STRUCT_OFFSET (MonoJitTlsData, lmf));
2043 lmf_ins = mono_emit_jit_icall (cfg, mono_get_lmf_addr, NULL);
2047 lmf_ins->dreg = cfg->lmf_addr_var->dreg;
2049 EMIT_NEW_VARLOADA (cfg, ins, cfg->lmf_var, NULL);
2050 lmf_reg = ins->dreg;
2052 prev_lmf_reg = alloc_preg (cfg);
2053 /* Save previous_lmf */
2054 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, prev_lmf_reg, cfg->lmf_addr_var->dreg, 0);
2055 EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, lmf_reg, MONO_STRUCT_OFFSET (MonoLMF, previous_lmf), prev_lmf_reg);
2057 EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, cfg->lmf_addr_var->dreg, 0, lmf_reg);
2064 * Emit IR to pop the current LMF from the LMF stack.
2067 emit_pop_lmf (MonoCompile *cfg)
2069 int lmf_reg, lmf_addr_reg, prev_lmf_reg;
2075 EMIT_NEW_VARLOADA (cfg, ins, cfg->lmf_var, NULL);
2076 lmf_reg = ins->dreg;
2078 if (cfg->lmf_ir_mono_lmf && mini_tls_get_supported (cfg, TLS_KEY_LMF)) {
2079 /* Load previous_lmf */
2080 prev_lmf_reg = alloc_preg (cfg);
2081 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, prev_lmf_reg, lmf_reg, MONO_STRUCT_OFFSET (MonoLMF, previous_lmf));
2083 emit_tls_set (cfg, prev_lmf_reg, TLS_KEY_LMF);
2086 * Emit IR to pop the LMF:
2087 * *(lmf->lmf_addr) = lmf->prev_lmf
2089 /* This could be called before emit_push_lmf () */
2090 if (!cfg->lmf_addr_var)
2091 cfg->lmf_addr_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
2092 lmf_addr_reg = cfg->lmf_addr_var->dreg;
2094 prev_lmf_reg = alloc_preg (cfg);
2095 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, prev_lmf_reg, lmf_reg, MONO_STRUCT_OFFSET (MonoLMF, previous_lmf));
2096 EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, lmf_addr_reg, 0, prev_lmf_reg);
2101 emit_instrumentation_call (MonoCompile *cfg, void *func)
2103 MonoInst *iargs [1];
2106 * Avoid instrumenting inlined methods since it can
2107 * distort profiling results.
2109 if (cfg->method != cfg->current_method)
2112 if (cfg->prof_options & MONO_PROFILE_ENTER_LEAVE) {
2113 EMIT_NEW_METHODCONST (cfg, iargs [0], cfg->method);
2114 mono_emit_jit_icall (cfg, func, iargs);
2119 ret_type_to_call_opcode (MonoCompile *cfg, MonoType *type, int calli, int virt)
2122 type = mini_get_underlying_type (type);
2123 switch (type->type) {
2124 case MONO_TYPE_VOID:
2125 return calli? OP_VOIDCALL_REG: virt? OP_VOIDCALL_MEMBASE: OP_VOIDCALL;
2132 return calli? OP_CALL_REG: virt? OP_CALL_MEMBASE: OP_CALL;
2136 case MONO_TYPE_FNPTR:
2137 return calli? OP_CALL_REG: virt? OP_CALL_MEMBASE: OP_CALL;
2138 case MONO_TYPE_CLASS:
2139 case MONO_TYPE_STRING:
2140 case MONO_TYPE_OBJECT:
2141 case MONO_TYPE_SZARRAY:
2142 case MONO_TYPE_ARRAY:
2143 return calli? OP_CALL_REG: virt? OP_CALL_MEMBASE: OP_CALL;
2146 return calli? OP_LCALL_REG: virt? OP_LCALL_MEMBASE: OP_LCALL;
2149 return calli? OP_RCALL_REG: virt? OP_RCALL_MEMBASE: OP_RCALL;
2151 return calli? OP_FCALL_REG: virt? OP_FCALL_MEMBASE: OP_FCALL;
2153 return calli? OP_FCALL_REG: virt? OP_FCALL_MEMBASE: OP_FCALL;
2154 case MONO_TYPE_VALUETYPE:
2155 if (type->data.klass->enumtype) {
2156 type = mono_class_enum_basetype (type->data.klass);
2159 return calli? OP_VCALL_REG: virt? OP_VCALL_MEMBASE: OP_VCALL;
2160 case MONO_TYPE_TYPEDBYREF:
2161 return calli? OP_VCALL_REG: virt? OP_VCALL_MEMBASE: OP_VCALL;
2162 case MONO_TYPE_GENERICINST:
2163 type = &type->data.generic_class->container_class->byval_arg;
2166 case MONO_TYPE_MVAR:
2168 return calli? OP_VCALL_REG: virt? OP_VCALL_MEMBASE: OP_VCALL;
2170 g_error ("unknown type 0x%02x in ret_type_to_call_opcode", type->type);
2176 * target_type_is_incompatible:
2177 * @cfg: MonoCompile context
2179 * Check that the item @arg on the evaluation stack can be stored
2180 * in the target type (can be a local, or field, etc).
2181 * The cfg arg can be used to check if we need verification or just
2184 * Returns: non-0 value if arg can't be stored on a target.
2187 target_type_is_incompatible (MonoCompile *cfg, MonoType *target, MonoInst *arg)
2189 MonoType *simple_type;
2192 if (target->byref) {
2193 /* FIXME: check that the pointed to types match */
2194 if (arg->type == STACK_MP) {
2195 MonoClass *base_class = mono_class_from_mono_type (target);
2196 /* This is needed to handle gshared types + ldaddr */
2197 simple_type = mini_get_underlying_type (&base_class->byval_arg);
2198 return target->type != MONO_TYPE_I && arg->klass != base_class && arg->klass != mono_class_from_mono_type (simple_type);
2200 if (arg->type == STACK_PTR)
2205 simple_type = mini_get_underlying_type (target);
2206 switch (simple_type->type) {
2207 case MONO_TYPE_VOID:
2215 if (arg->type != STACK_I4 && arg->type != STACK_PTR)
2219 /* STACK_MP is needed when setting pinned locals */
2220 if (arg->type != STACK_I4 && arg->type != STACK_PTR && arg->type != STACK_MP)
2225 case MONO_TYPE_FNPTR:
2227 * Some opcodes like ldloca returns 'transient pointers' which can be stored in
2228 * in native int. (#688008).
2230 if (arg->type != STACK_I4 && arg->type != STACK_PTR && arg->type != STACK_MP)
2233 case MONO_TYPE_CLASS:
2234 case MONO_TYPE_STRING:
2235 case MONO_TYPE_OBJECT:
2236 case MONO_TYPE_SZARRAY:
2237 case MONO_TYPE_ARRAY:
2238 if (arg->type != STACK_OBJ)
2240 /* FIXME: check type compatibility */
2244 if (arg->type != STACK_I8)
2248 if (arg->type != cfg->r4_stack_type)
2252 if (arg->type != STACK_R8)
2255 case MONO_TYPE_VALUETYPE:
2256 if (arg->type != STACK_VTYPE)
2258 klass = mono_class_from_mono_type (simple_type);
2259 if (klass != arg->klass)
2262 case MONO_TYPE_TYPEDBYREF:
2263 if (arg->type != STACK_VTYPE)
2265 klass = mono_class_from_mono_type (simple_type);
2266 if (klass != arg->klass)
2269 case MONO_TYPE_GENERICINST:
2270 if (mono_type_generic_inst_is_valuetype (simple_type)) {
2271 MonoClass *target_class;
2272 if (arg->type != STACK_VTYPE)
2274 klass = mono_class_from_mono_type (simple_type);
2275 target_class = mono_class_from_mono_type (target);
2276 /* The second cases is needed when doing partial sharing */
2277 if (klass != arg->klass && target_class != arg->klass && target_class != mono_class_from_mono_type (mini_get_underlying_type (&arg->klass->byval_arg)))
2281 if (arg->type != STACK_OBJ)
2283 /* FIXME: check type compatibility */
2287 case MONO_TYPE_MVAR:
2288 g_assert (cfg->gshared);
2289 if (mini_type_var_is_vt (simple_type)) {
2290 if (arg->type != STACK_VTYPE)
2293 if (arg->type != STACK_OBJ)
2298 g_error ("unknown type 0x%02x in target_type_is_incompatible", simple_type->type);
2304 * Prepare arguments for passing to a function call.
2305 * Return a non-zero value if the arguments can't be passed to the given
2307 * The type checks are not yet complete and some conversions may need
2308 * casts on 32 or 64 bit architectures.
2310 * FIXME: implement this using target_type_is_incompatible ()
2313 check_call_signature (MonoCompile *cfg, MonoMethodSignature *sig, MonoInst **args)
2315 MonoType *simple_type;
2319 if (args [0]->type != STACK_OBJ && args [0]->type != STACK_MP && args [0]->type != STACK_PTR)
2323 for (i = 0; i < sig->param_count; ++i) {
2324 if (sig->params [i]->byref) {
2325 if (args [i]->type != STACK_MP && args [i]->type != STACK_PTR)
2329 simple_type = mini_get_underlying_type (sig->params [i]);
2331 switch (simple_type->type) {
2332 case MONO_TYPE_VOID:
2341 if (args [i]->type != STACK_I4 && args [i]->type != STACK_PTR)
2347 case MONO_TYPE_FNPTR:
2348 if (args [i]->type != STACK_I4 && args [i]->type != STACK_PTR && args [i]->type != STACK_MP && args [i]->type != STACK_OBJ)
2351 case MONO_TYPE_CLASS:
2352 case MONO_TYPE_STRING:
2353 case MONO_TYPE_OBJECT:
2354 case MONO_TYPE_SZARRAY:
2355 case MONO_TYPE_ARRAY:
2356 if (args [i]->type != STACK_OBJ)
2361 if (args [i]->type != STACK_I8)
2365 if (args [i]->type != cfg->r4_stack_type)
2369 if (args [i]->type != STACK_R8)
2372 case MONO_TYPE_VALUETYPE:
2373 if (simple_type->data.klass->enumtype) {
2374 simple_type = mono_class_enum_basetype (simple_type->data.klass);
2377 if (args [i]->type != STACK_VTYPE)
2380 case MONO_TYPE_TYPEDBYREF:
2381 if (args [i]->type != STACK_VTYPE)
2384 case MONO_TYPE_GENERICINST:
2385 simple_type = &simple_type->data.generic_class->container_class->byval_arg;
2388 case MONO_TYPE_MVAR:
2390 if (args [i]->type != STACK_VTYPE)
2394 g_error ("unknown type 0x%02x in check_call_signature",
2402 callvirt_to_call (int opcode)
2405 case OP_CALL_MEMBASE:
2407 case OP_VOIDCALL_MEMBASE:
2409 case OP_FCALL_MEMBASE:
2411 case OP_RCALL_MEMBASE:
2413 case OP_VCALL_MEMBASE:
2415 case OP_LCALL_MEMBASE:
2418 g_assert_not_reached ();
2425 callvirt_to_call_reg (int opcode)
2428 case OP_CALL_MEMBASE:
2430 case OP_VOIDCALL_MEMBASE:
2431 return OP_VOIDCALL_REG;
2432 case OP_FCALL_MEMBASE:
2433 return OP_FCALL_REG;
2434 case OP_RCALL_MEMBASE:
2435 return OP_RCALL_REG;
2436 case OP_VCALL_MEMBASE:
2437 return OP_VCALL_REG;
2438 case OP_LCALL_MEMBASE:
2439 return OP_LCALL_REG;
2441 g_assert_not_reached ();
2447 /* Either METHOD or IMT_ARG needs to be set */
2449 emit_imt_argument (MonoCompile *cfg, MonoCallInst *call, MonoMethod *method, MonoInst *imt_arg)
2453 if (COMPILE_LLVM (cfg)) {
2455 method_reg = alloc_preg (cfg);
2456 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, method_reg, imt_arg->dreg);
2458 MonoInst *ins = emit_runtime_constant (cfg, MONO_PATCH_INFO_METHODCONST, method);
2459 method_reg = ins->dreg;
2463 call->imt_arg_reg = method_reg;
2465 mono_call_inst_add_outarg_reg (cfg, call, method_reg, MONO_ARCH_IMT_REG, FALSE);
2470 method_reg = alloc_preg (cfg);
2471 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, method_reg, imt_arg->dreg);
2473 MonoInst *ins = emit_runtime_constant (cfg, MONO_PATCH_INFO_METHODCONST, method);
2474 method_reg = ins->dreg;
2477 mono_call_inst_add_outarg_reg (cfg, call, method_reg, MONO_ARCH_IMT_REG, FALSE);
2480 static MonoJumpInfo *
2481 mono_patch_info_new (MonoMemPool *mp, int ip, MonoJumpInfoType type, gconstpointer target)
2483 MonoJumpInfo *ji = (MonoJumpInfo *)mono_mempool_alloc (mp, sizeof (MonoJumpInfo));
2487 ji->data.target = target;
2493 mini_class_check_context_used (MonoCompile *cfg, MonoClass *klass)
2496 return mono_class_check_context_used (klass);
2502 mini_method_check_context_used (MonoCompile *cfg, MonoMethod *method)
2505 return mono_method_check_context_used (method);
2511 * check_method_sharing:
2513 * Check whenever the vtable or an mrgctx needs to be passed when calling CMETHOD.
2516 check_method_sharing (MonoCompile *cfg, MonoMethod *cmethod, gboolean *out_pass_vtable, gboolean *out_pass_mrgctx)
2518 gboolean pass_vtable = FALSE;
2519 gboolean pass_mrgctx = FALSE;
2521 if (((cmethod->flags & METHOD_ATTRIBUTE_STATIC) || cmethod->klass->valuetype) &&
2522 (cmethod->klass->generic_class || cmethod->klass->generic_container)) {
2523 gboolean sharable = FALSE;
2525 if (mono_method_is_generic_sharable_full (cmethod, TRUE, TRUE, TRUE))
2529 * Pass vtable iff target method might
2530 * be shared, which means that sharing
2531 * is enabled for its class and its
2532 * context is sharable (and it's not a
2535 if (sharable && !(mini_method_get_context (cmethod) && mini_method_get_context (cmethod)->method_inst))
2539 if (mini_method_get_context (cmethod) &&
2540 mini_method_get_context (cmethod)->method_inst) {
2541 g_assert (!pass_vtable);
2543 if (mono_method_is_generic_sharable_full (cmethod, TRUE, TRUE, TRUE)) {
2546 if (cfg->gsharedvt && mini_is_gsharedvt_signature (mono_method_signature (cmethod)))
2551 if (out_pass_vtable)
2552 *out_pass_vtable = pass_vtable;
2553 if (out_pass_mrgctx)
2554 *out_pass_mrgctx = pass_mrgctx;
2557 inline static MonoCallInst *
2558 mono_emit_call_args (MonoCompile *cfg, MonoMethodSignature *sig,
2559 MonoInst **args, int calli, int virtual_, int tail, int rgctx, int unbox_trampoline)
2563 #ifdef MONO_ARCH_SOFT_FLOAT_FALLBACK
2571 emit_instrumentation_call (cfg, mono_profiler_method_leave);
2573 MONO_INST_NEW_CALL (cfg, call, OP_TAILCALL);
2575 MONO_INST_NEW_CALL (cfg, call, ret_type_to_call_opcode (cfg, sig->ret, calli, virtual_));
2578 call->signature = sig;
2579 call->rgctx_reg = rgctx;
2580 sig_ret = mini_get_underlying_type (sig->ret);
2582 type_to_eval_stack_type ((cfg), sig_ret, &call->inst);
2585 if (mini_type_is_vtype (sig_ret)) {
2586 call->vret_var = cfg->vret_addr;
2587 //g_assert_not_reached ();
2589 } else if (mini_type_is_vtype (sig_ret)) {
2590 MonoInst *temp = mono_compile_create_var (cfg, sig_ret, OP_LOCAL);
2593 temp->backend.is_pinvoke = sig->pinvoke;
2596 * We use a new opcode OP_OUTARG_VTRETADDR instead of LDADDR for emitting the
2597 * address of return value to increase optimization opportunities.
2598 * Before vtype decomposition, the dreg of the call ins itself represents the
2599 * fact the call modifies the return value. After decomposition, the call will
2600 * be transformed into one of the OP_VOIDCALL opcodes, and the VTRETADDR opcode
2601 * will be transformed into an LDADDR.
2603 MONO_INST_NEW (cfg, loada, OP_OUTARG_VTRETADDR);
2604 loada->dreg = alloc_preg (cfg);
2605 loada->inst_p0 = temp;
2606 /* We reference the call too since call->dreg could change during optimization */
2607 loada->inst_p1 = call;
2608 MONO_ADD_INS (cfg->cbb, loada);
2610 call->inst.dreg = temp->dreg;
2612 call->vret_var = loada;
2613 } else if (!MONO_TYPE_IS_VOID (sig_ret))
2614 call->inst.dreg = alloc_dreg (cfg, (MonoStackType)call->inst.type);
2616 #ifdef MONO_ARCH_SOFT_FLOAT_FALLBACK
2617 if (COMPILE_SOFT_FLOAT (cfg)) {
2619 * If the call has a float argument, we would need to do an r8->r4 conversion using
2620 * an icall, but that cannot be done during the call sequence since it would clobber
2621 * the call registers + the stack. So we do it before emitting the call.
2623 for (i = 0; i < sig->param_count + sig->hasthis; ++i) {
2625 MonoInst *in = call->args [i];
2627 if (i >= sig->hasthis)
2628 t = sig->params [i - sig->hasthis];
2630 t = &mono_defaults.int_class->byval_arg;
2631 t = mono_type_get_underlying_type (t);
2633 if (!t->byref && t->type == MONO_TYPE_R4) {
2634 MonoInst *iargs [1];
2638 conv = mono_emit_jit_icall (cfg, mono_fload_r4_arg, iargs);
2640 /* The result will be in an int vreg */
2641 call->args [i] = conv;
2647 call->need_unbox_trampoline = unbox_trampoline;
2650 if (COMPILE_LLVM (cfg))
2651 mono_llvm_emit_call (cfg, call);
2653 mono_arch_emit_call (cfg, call);
2655 mono_arch_emit_call (cfg, call);
2658 cfg->param_area = MAX (cfg->param_area, call->stack_usage);
2659 cfg->flags |= MONO_CFG_HAS_CALLS;
2665 set_rgctx_arg (MonoCompile *cfg, MonoCallInst *call, int rgctx_reg, MonoInst *rgctx_arg)
2667 mono_call_inst_add_outarg_reg (cfg, call, rgctx_reg, MONO_ARCH_RGCTX_REG, FALSE);
2668 cfg->uses_rgctx_reg = TRUE;
2669 call->rgctx_reg = TRUE;
2671 call->rgctx_arg_reg = rgctx_reg;
2675 inline static MonoInst*
2676 mono_emit_calli (MonoCompile *cfg, MonoMethodSignature *sig, MonoInst **args, MonoInst *addr, MonoInst *imt_arg, MonoInst *rgctx_arg)
2681 gboolean check_sp = FALSE;
2683 if (cfg->check_pinvoke_callconv && cfg->method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE) {
2684 WrapperInfo *info = mono_marshal_get_wrapper_info (cfg->method);
2686 if (info && info->subtype == WRAPPER_SUBTYPE_PINVOKE)
2691 rgctx_reg = mono_alloc_preg (cfg);
2692 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, rgctx_reg, rgctx_arg->dreg);
2696 if (!cfg->stack_inbalance_var)
2697 cfg->stack_inbalance_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
2699 MONO_INST_NEW (cfg, ins, OP_GET_SP);
2700 ins->dreg = cfg->stack_inbalance_var->dreg;
2701 MONO_ADD_INS (cfg->cbb, ins);
2704 call = mono_emit_call_args (cfg, sig, args, TRUE, FALSE, FALSE, rgctx_arg ? TRUE : FALSE, FALSE);
2706 call->inst.sreg1 = addr->dreg;
2709 emit_imt_argument (cfg, call, NULL, imt_arg);
2711 MONO_ADD_INS (cfg->cbb, (MonoInst*)call);
2716 sp_reg = mono_alloc_preg (cfg);
2718 MONO_INST_NEW (cfg, ins, OP_GET_SP);
2720 MONO_ADD_INS (cfg->cbb, ins);
2722 /* Restore the stack so we don't crash when throwing the exception */
2723 MONO_INST_NEW (cfg, ins, OP_SET_SP);
2724 ins->sreg1 = cfg->stack_inbalance_var->dreg;
2725 MONO_ADD_INS (cfg->cbb, ins);
2727 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, cfg->stack_inbalance_var->dreg, sp_reg);
2728 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "ExecutionEngineException");
2732 set_rgctx_arg (cfg, call, rgctx_reg, rgctx_arg);
2734 return (MonoInst*)call;
2738 emit_get_gsharedvt_info_klass (MonoCompile *cfg, MonoClass *klass, MonoRgctxInfoType rgctx_type);
2741 emit_get_rgctx_method (MonoCompile *cfg, int context_used, MonoMethod *cmethod, MonoRgctxInfoType rgctx_type);
2743 emit_get_rgctx_klass (MonoCompile *cfg, int context_used, MonoClass *klass, MonoRgctxInfoType rgctx_type);
2746 mono_emit_method_call_full (MonoCompile *cfg, MonoMethod *method, MonoMethodSignature *sig, gboolean tail,
2747 MonoInst **args, MonoInst *this_ins, MonoInst *imt_arg, MonoInst *rgctx_arg)
2749 #ifndef DISABLE_REMOTING
2750 gboolean might_be_remote = FALSE;
2752 gboolean virtual_ = this_ins != NULL;
2753 gboolean enable_for_aot = TRUE;
2756 MonoInst *call_target = NULL;
2758 gboolean need_unbox_trampoline;
2761 sig = mono_method_signature (method);
2763 if (cfg->llvm_only && (method->klass->flags & TYPE_ATTRIBUTE_INTERFACE)) {
2764 MonoInst *icall_args [16];
2767 // FIXME: Optimize this
2769 guint32 imt_slot = mono_method_get_imt_slot (method);
2771 icall_args [0] = this_ins;
2772 EMIT_NEW_ICONST (cfg, icall_args [1], imt_slot);
2774 icall_args [2] = imt_arg;
2776 EMIT_NEW_AOTCONST (cfg, ins, MONO_PATCH_INFO_METHODCONST, method);
2777 icall_args [2] = ins;
2779 EMIT_NEW_PCONST (cfg, icall_args [3], NULL);
2781 call_target = mono_emit_jit_icall (cfg, mono_resolve_iface_call, icall_args);
2785 rgctx_reg = mono_alloc_preg (cfg);
2786 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, rgctx_reg, rgctx_arg->dreg);
2789 if (method->string_ctor) {
2790 /* Create the real signature */
2791 /* FIXME: Cache these */
2792 MonoMethodSignature *ctor_sig = mono_metadata_signature_dup_mempool (cfg->mempool, sig);
2793 ctor_sig->ret = &mono_defaults.string_class->byval_arg;
2798 context_used = mini_method_check_context_used (cfg, method);
2800 #ifndef DISABLE_REMOTING
2801 might_be_remote = this_ins && sig->hasthis &&
2802 (mono_class_is_marshalbyref (method->klass) || method->klass == mono_defaults.object_class) &&
2803 !(method->flags & METHOD_ATTRIBUTE_VIRTUAL) && (!MONO_CHECK_THIS (this_ins) || context_used);
2805 if (might_be_remote && context_used) {
2808 g_assert (cfg->gshared);
2810 addr = emit_get_rgctx_method (cfg, context_used, method, MONO_RGCTX_INFO_REMOTING_INVOKE_WITH_CHECK);
2812 return mono_emit_calli (cfg, sig, args, addr, NULL, NULL);
2816 if (cfg->llvm_only && !call_target && virtual_ && (method->flags & METHOD_ATTRIBUTE_VIRTUAL)) {
2817 // FIXME: Vcall optimizations below
2818 MonoInst *icall_args [16];
2822 if (sig->generic_param_count) {
2824 * Generic virtual call, pass the concrete method as the imt argument.
2826 imt_arg = emit_get_rgctx_method (cfg, context_used,
2827 method, MONO_RGCTX_INFO_METHOD);
2830 // FIXME: Optimize this
2832 int slot = mono_method_get_vtable_index (method);
2834 icall_args [0] = this_ins;
2835 EMIT_NEW_ICONST (cfg, icall_args [1], slot);
2837 icall_args [2] = imt_arg;
2839 EMIT_NEW_PCONST (cfg, ins, NULL);
2840 icall_args [2] = ins;
2842 rgctx_reg = alloc_preg (cfg);
2843 MONO_EMIT_NEW_PCONST (cfg, rgctx_reg, NULL);
2844 EMIT_NEW_VARLOADA_VREG (cfg, icall_args [3], rgctx_reg, &mono_defaults.int_class->byval_arg);
2845 call_target = mono_emit_jit_icall (cfg, mono_resolve_vcall, icall_args);
2848 need_unbox_trampoline = method->klass == mono_defaults.object_class || (method->klass->flags & TYPE_ATTRIBUTE_INTERFACE);
2850 call = mono_emit_call_args (cfg, sig, args, FALSE, virtual_, tail, rgctx_arg ? TRUE : FALSE, need_unbox_trampoline);
2852 #ifndef DISABLE_REMOTING
2853 if (might_be_remote)
2854 call->method = mono_marshal_get_remoting_invoke_with_check (method);
2857 call->method = method;
2858 call->inst.flags |= MONO_INST_HAS_METHOD;
2859 call->inst.inst_left = this_ins;
2860 call->tail_call = tail;
2863 int vtable_reg, slot_reg, this_reg;
2866 this_reg = this_ins->dreg;
2868 if (!cfg->llvm_only && (method->klass->parent == mono_defaults.multicastdelegate_class) && !strcmp (method->name, "Invoke")) {
2869 MonoInst *dummy_use;
2871 MONO_EMIT_NULL_CHECK (cfg, this_reg);
2873 /* Make a call to delegate->invoke_impl */
2874 call->inst.inst_basereg = this_reg;
2875 call->inst.inst_offset = MONO_STRUCT_OFFSET (MonoDelegate, invoke_impl);
2876 MONO_ADD_INS (cfg->cbb, (MonoInst*)call);
2878 /* We must emit a dummy use here because the delegate trampoline will
2879 replace the 'this' argument with the delegate target making this activation
2880 no longer a root for the delegate.
2881 This is an issue for delegates that target collectible code such as dynamic
2882 methods of GC'able assemblies.
2884 For a test case look into #667921.
2886 FIXME: a dummy use is not the best way to do it as the local register allocator
2887 will put it on a caller save register and spil it around the call.
2888 Ideally, we would either put it on a callee save register or only do the store part.
2890 EMIT_NEW_DUMMY_USE (cfg, dummy_use, args [0]);
2892 return (MonoInst*)call;
2895 if ((!cfg->compile_aot || enable_for_aot) &&
2896 (!(method->flags & METHOD_ATTRIBUTE_VIRTUAL) ||
2897 (MONO_METHOD_IS_FINAL (method) &&
2898 method->wrapper_type != MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK)) &&
2899 !(mono_class_is_marshalbyref (method->klass) && context_used)) {
2901 * the method is not virtual, we just need to ensure this is not null
2902 * and then we can call the method directly.
2904 #ifndef DISABLE_REMOTING
2905 if (mono_class_is_marshalbyref (method->klass) || method->klass == mono_defaults.object_class) {
2907 * The check above ensures method is not gshared, this is needed since
2908 * gshared methods can't have wrappers.
2910 method = call->method = mono_marshal_get_remoting_invoke_with_check (method);
2914 if (!method->string_ctor)
2915 MONO_EMIT_NEW_CHECK_THIS (cfg, this_reg);
2917 call->inst.opcode = callvirt_to_call (call->inst.opcode);
2918 } else if ((method->flags & METHOD_ATTRIBUTE_VIRTUAL) && MONO_METHOD_IS_FINAL (method)) {
2920 * the method is virtual, but we can statically dispatch since either
2921 * it's class or the method itself are sealed.
2922 * But first we need to ensure it's not a null reference.
2924 MONO_EMIT_NEW_CHECK_THIS (cfg, this_reg);
2926 call->inst.opcode = callvirt_to_call (call->inst.opcode);
2927 } else if (call_target) {
2928 vtable_reg = alloc_preg (cfg);
2929 MONO_EMIT_NEW_LOAD_MEMBASE_FAULT (cfg, vtable_reg, this_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
2931 call->inst.opcode = callvirt_to_call_reg (call->inst.opcode);
2932 call->inst.sreg1 = call_target->dreg;
2933 call->inst.flags &= !MONO_INST_HAS_METHOD;
2935 vtable_reg = alloc_preg (cfg);
2936 MONO_EMIT_NEW_LOAD_MEMBASE_FAULT (cfg, vtable_reg, this_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
2937 if (method->klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
2938 guint32 imt_slot = mono_method_get_imt_slot (method);
2939 emit_imt_argument (cfg, call, call->method, imt_arg);
2940 slot_reg = vtable_reg;
2941 offset = ((gint32)imt_slot - MONO_IMT_SIZE) * SIZEOF_VOID_P;
2943 slot_reg = vtable_reg;
2944 offset = MONO_STRUCT_OFFSET (MonoVTable, vtable) +
2945 ((mono_method_get_vtable_index (method)) * (SIZEOF_VOID_P));
2947 g_assert (mono_method_signature (method)->generic_param_count);
2948 emit_imt_argument (cfg, call, call->method, imt_arg);
2952 call->inst.sreg1 = slot_reg;
2953 call->inst.inst_offset = offset;
2954 call->is_virtual = TRUE;
2958 MONO_ADD_INS (cfg->cbb, (MonoInst*)call);
2961 set_rgctx_arg (cfg, call, rgctx_reg, rgctx_arg);
2963 return (MonoInst*)call;
2967 mono_emit_method_call (MonoCompile *cfg, MonoMethod *method, MonoInst **args, MonoInst *this_ins)
2969 return mono_emit_method_call_full (cfg, method, mono_method_signature (method), FALSE, args, this_ins, NULL, NULL);
2973 mono_emit_native_call (MonoCompile *cfg, gconstpointer func, MonoMethodSignature *sig,
2980 call = mono_emit_call_args (cfg, sig, args, FALSE, FALSE, FALSE, FALSE, FALSE);
2983 MONO_ADD_INS (cfg->cbb, (MonoInst*)call);
2985 return (MonoInst*)call;
2989 mono_emit_jit_icall (MonoCompile *cfg, gconstpointer func, MonoInst **args)
2991 MonoJitICallInfo *info = mono_find_jit_icall_by_addr (func);
2995 return mono_emit_native_call (cfg, mono_icall_get_wrapper (info), info->sig, args);
2999 * mono_emit_abs_call:
3001 * Emit a call to the runtime function described by PATCH_TYPE and DATA.
3003 inline static MonoInst*
3004 mono_emit_abs_call (MonoCompile *cfg, MonoJumpInfoType patch_type, gconstpointer data,
3005 MonoMethodSignature *sig, MonoInst **args)
3007 MonoJumpInfo *ji = mono_patch_info_new (cfg->mempool, 0, patch_type, data);
3011 * We pass ji as the call address, the PATCH_INFO_ABS resolving code will
3014 if (cfg->abs_patches == NULL)
3015 cfg->abs_patches = g_hash_table_new (NULL, NULL);
3016 g_hash_table_insert (cfg->abs_patches, ji, ji);
3017 ins = mono_emit_native_call (cfg, ji, sig, args);
3018 ((MonoCallInst*)ins)->fptr_is_patch = TRUE;
3022 static MonoMethodSignature*
3023 sig_to_rgctx_sig (MonoMethodSignature *sig)
3025 // FIXME: memory allocation
3026 MonoMethodSignature *res;
3029 res = (MonoMethodSignature *)g_malloc (MONO_SIZEOF_METHOD_SIGNATURE + (sig->param_count + 1) * sizeof (MonoType*));
3030 memcpy (res, sig, MONO_SIZEOF_METHOD_SIGNATURE);
3031 res->param_count = sig->param_count + 1;
3032 for (i = 0; i < sig->param_count; ++i)
3033 res->params [i] = sig->params [i];
3034 res->params [sig->param_count] = &mono_defaults.int_class->this_arg;
3038 /* Make an indirect call to FSIG passing an additional argument */
3040 emit_extra_arg_calli (MonoCompile *cfg, MonoMethodSignature *fsig, MonoInst **orig_args, int arg_reg, MonoInst *call_target)
3042 MonoMethodSignature *csig;
3043 MonoInst *args_buf [16];
3045 int i, pindex, tmp_reg;
3047 /* Make a call with an rgctx/extra arg */
3048 if (fsig->param_count + 2 < 16)
3051 args = (MonoInst **)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoInst*) * (fsig->param_count + 2));
3054 args [pindex ++] = orig_args [0];
3055 for (i = 0; i < fsig->param_count; ++i)
3056 args [pindex ++] = orig_args [fsig->hasthis + i];
3057 tmp_reg = alloc_preg (cfg);
3058 EMIT_NEW_UNALU (cfg, args [pindex], OP_MOVE, tmp_reg, arg_reg);
3059 csig = sig_to_rgctx_sig (fsig);
3060 return mono_emit_calli (cfg, csig, args, call_target, NULL, NULL);
3063 /* Emit an indirect call to the function descriptor ADDR */
3065 emit_llvmonly_calli (MonoCompile *cfg, MonoMethodSignature *fsig, MonoInst **args, MonoInst *addr)
3067 int addr_reg, arg_reg;
3068 MonoInst *call_target;
3070 g_assert (cfg->llvm_only);
3073 * addr points to a <addr, arg> pair, load both of them, and
3074 * make a call to addr, passing arg as an extra arg.
3076 addr_reg = alloc_preg (cfg);
3077 EMIT_NEW_LOAD_MEMBASE (cfg, call_target, OP_LOAD_MEMBASE, addr_reg, addr->dreg, 0);
3078 arg_reg = alloc_preg (cfg);
3079 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, arg_reg, addr->dreg, sizeof (gpointer));
3081 return emit_extra_arg_calli (cfg, fsig, args, arg_reg, call_target);
3085 direct_icalls_enabled (MonoCompile *cfg)
3087 /* LLVM on amd64 can't handle calls to non-32 bit addresses */
3089 if (cfg->compile_llvm)
3092 if (cfg->gen_sdb_seq_points || cfg->disable_direct_icalls)
3098 mono_emit_jit_icall_by_info (MonoCompile *cfg, MonoJitICallInfo *info, MonoInst **args)
3101 * Call the jit icall without a wrapper if possible.
3102 * The wrapper is needed for the following reasons:
3103 * - to handle exceptions thrown using mono_raise_exceptions () from the
3104 * icall function. The EH code needs the lmf frame pushed by the
3105 * wrapper to be able to unwind back to managed code.
3106 * - to be able to do stack walks for asynchronously suspended
3107 * threads when debugging.
3109 if (info->no_raise && direct_icalls_enabled (cfg)) {
3113 if (!info->wrapper_method) {
3114 name = g_strdup_printf ("__icall_wrapper_%s", info->name);
3115 info->wrapper_method = mono_marshal_get_icall_wrapper (info->sig, name, info->func, TRUE);
3117 mono_memory_barrier ();
3121 * Inline the wrapper method, which is basically a call to the C icall, and
3122 * an exception check.
3124 costs = inline_method (cfg, info->wrapper_method, NULL,
3125 args, NULL, cfg->real_offset, TRUE);
3126 g_assert (costs > 0);
3127 g_assert (!MONO_TYPE_IS_VOID (info->sig->ret));
3131 return mono_emit_native_call (cfg, mono_icall_get_wrapper (info), info->sig, args);
3136 mono_emit_widen_call_res (MonoCompile *cfg, MonoInst *ins, MonoMethodSignature *fsig)
3138 if (!MONO_TYPE_IS_VOID (fsig->ret)) {
3139 if ((fsig->pinvoke || LLVM_ENABLED) && !fsig->ret->byref) {
3143 * Native code might return non register sized integers
3144 * without initializing the upper bits.
3146 switch (mono_type_to_load_membase (cfg, fsig->ret)) {
3147 case OP_LOADI1_MEMBASE:
3148 widen_op = OP_ICONV_TO_I1;
3150 case OP_LOADU1_MEMBASE:
3151 widen_op = OP_ICONV_TO_U1;
3153 case OP_LOADI2_MEMBASE:
3154 widen_op = OP_ICONV_TO_I2;
3156 case OP_LOADU2_MEMBASE:
3157 widen_op = OP_ICONV_TO_U2;
3163 if (widen_op != -1) {
3164 int dreg = alloc_preg (cfg);
3167 EMIT_NEW_UNALU (cfg, widen, widen_op, dreg, ins->dreg);
3168 widen->type = ins->type;
3178 get_memcpy_method (void)
3180 static MonoMethod *memcpy_method = NULL;
3181 if (!memcpy_method) {
3182 memcpy_method = mono_class_get_method_from_name (mono_defaults.string_class, "memcpy", 3);
3184 g_error ("Old corlib found. Install a new one");
3186 return memcpy_method;
3190 create_write_barrier_bitmap (MonoCompile *cfg, MonoClass *klass, unsigned *wb_bitmap, int offset)
3192 MonoClassField *field;
3193 gpointer iter = NULL;
3195 while ((field = mono_class_get_fields (klass, &iter))) {
3198 if (field->type->attrs & FIELD_ATTRIBUTE_STATIC)
3200 foffset = klass->valuetype ? field->offset - sizeof (MonoObject): field->offset;
3201 if (mini_type_is_reference (mono_field_get_type (field))) {
3202 g_assert ((foffset % SIZEOF_VOID_P) == 0);
3203 *wb_bitmap |= 1 << ((offset + foffset) / SIZEOF_VOID_P);
3205 MonoClass *field_class = mono_class_from_mono_type (field->type);
3206 if (field_class->has_references)
3207 create_write_barrier_bitmap (cfg, field_class, wb_bitmap, offset + foffset);
3213 emit_write_barrier (MonoCompile *cfg, MonoInst *ptr, MonoInst *value)
3215 int card_table_shift_bits;
3216 gpointer card_table_mask;
3218 MonoInst *dummy_use;
3219 int nursery_shift_bits;
3220 size_t nursery_size;
3222 if (!cfg->gen_write_barriers)
3225 card_table = mono_gc_get_card_table (&card_table_shift_bits, &card_table_mask);
3227 mono_gc_get_nursery (&nursery_shift_bits, &nursery_size);
3229 if (cfg->backend->have_card_table_wb && !cfg->compile_aot && card_table && nursery_shift_bits > 0 && !COMPILE_LLVM (cfg)) {
3232 MONO_INST_NEW (cfg, wbarrier, OP_CARD_TABLE_WBARRIER);
3233 wbarrier->sreg1 = ptr->dreg;
3234 wbarrier->sreg2 = value->dreg;
3235 MONO_ADD_INS (cfg->cbb, wbarrier);
3236 } else if (card_table && !cfg->compile_aot && !mono_gc_card_table_nursery_check ()) {
3237 int offset_reg = alloc_preg (cfg);
3241 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SHR_UN_IMM, offset_reg, ptr->dreg, card_table_shift_bits);
3242 if (card_table_mask)
3243 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_PAND_IMM, offset_reg, offset_reg, card_table_mask);
3245 /*We can't use PADD_IMM since the cardtable might end up in high addresses and amd64 doesn't support
3246 * IMM's larger than 32bits.
3248 ins = emit_runtime_constant (cfg, MONO_PATCH_INFO_GC_CARD_TABLE_ADDR, NULL);
3249 card_reg = ins->dreg;
3251 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, offset_reg, offset_reg, card_reg);
3252 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREI1_MEMBASE_IMM, offset_reg, 0, 1);
3254 MonoMethod *write_barrier = mono_gc_get_write_barrier ();
3255 mono_emit_method_call (cfg, write_barrier, &ptr, NULL);
3258 EMIT_NEW_DUMMY_USE (cfg, dummy_use, value);
3262 mono_emit_wb_aware_memcpy (MonoCompile *cfg, MonoClass *klass, MonoInst *iargs[4], int size, int align)
3264 int dest_ptr_reg, tmp_reg, destreg, srcreg, offset;
3265 unsigned need_wb = 0;
3270 /*types with references can't have alignment smaller than sizeof(void*) */
3271 if (align < SIZEOF_VOID_P)
3274 /*This value cannot be bigger than 32 due to the way we calculate the required wb bitmap.*/
3275 if (size > 32 * SIZEOF_VOID_P)
3278 create_write_barrier_bitmap (cfg, klass, &need_wb, 0);
3280 /* We don't unroll more than 5 stores to avoid code bloat. */
3281 if (size > 5 * SIZEOF_VOID_P) {
3282 /*This is harmless and simplify mono_gc_wbarrier_value_copy_bitmap */
3283 size += (SIZEOF_VOID_P - 1);
3284 size &= ~(SIZEOF_VOID_P - 1);
3286 EMIT_NEW_ICONST (cfg, iargs [2], size);
3287 EMIT_NEW_ICONST (cfg, iargs [3], need_wb);
3288 mono_emit_jit_icall (cfg, mono_gc_wbarrier_value_copy_bitmap, iargs);
3292 destreg = iargs [0]->dreg;
3293 srcreg = iargs [1]->dreg;
3296 dest_ptr_reg = alloc_preg (cfg);
3297 tmp_reg = alloc_preg (cfg);
3300 EMIT_NEW_UNALU (cfg, iargs [0], OP_MOVE, dest_ptr_reg, destreg);
3302 while (size >= SIZEOF_VOID_P) {
3303 MonoInst *load_inst;
3304 MONO_INST_NEW (cfg, load_inst, OP_LOAD_MEMBASE);
3305 load_inst->dreg = tmp_reg;
3306 load_inst->inst_basereg = srcreg;
3307 load_inst->inst_offset = offset;
3308 MONO_ADD_INS (cfg->cbb, load_inst);
3310 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREP_MEMBASE_REG, dest_ptr_reg, 0, tmp_reg);
3313 emit_write_barrier (cfg, iargs [0], load_inst);
3315 offset += SIZEOF_VOID_P;
3316 size -= SIZEOF_VOID_P;
3319 /*tmp += sizeof (void*)*/
3320 if (size >= SIZEOF_VOID_P) {
3321 NEW_BIALU_IMM (cfg, iargs [0], OP_PADD_IMM, dest_ptr_reg, dest_ptr_reg, SIZEOF_VOID_P);
3322 MONO_ADD_INS (cfg->cbb, iargs [0]);
3326 /* Those cannot be references since size < sizeof (void*) */
3328 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI4_MEMBASE, tmp_reg, srcreg, offset);
3329 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI4_MEMBASE_REG, destreg, offset, tmp_reg);
3335 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI2_MEMBASE, tmp_reg, srcreg, offset);
3336 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI2_MEMBASE_REG, destreg, offset, tmp_reg);
3342 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI1_MEMBASE, tmp_reg, srcreg, offset);
3343 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI1_MEMBASE_REG, destreg, offset, tmp_reg);
3352 * Emit code to copy a valuetype of type @klass whose address is stored in
3353 * @src->dreg to memory whose address is stored at @dest->dreg.
3356 mini_emit_stobj (MonoCompile *cfg, MonoInst *dest, MonoInst *src, MonoClass *klass, gboolean native)
3358 MonoInst *iargs [4];
3361 MonoMethod *memcpy_method;
3362 MonoInst *size_ins = NULL;
3363 MonoInst *memcpy_ins = NULL;
3367 klass = mono_class_from_mono_type (mini_get_underlying_type (&klass->byval_arg));
3370 * This check breaks with spilled vars... need to handle it during verification anyway.
3371 * g_assert (klass && klass == src->klass && klass == dest->klass);
3374 if (mini_is_gsharedvt_klass (klass)) {
3376 size_ins = emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_VALUE_SIZE);
3377 memcpy_ins = emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_MEMCPY);
3381 n = mono_class_native_size (klass, &align);
3383 n = mono_class_value_size (klass, &align);
3385 /* if native is true there should be no references in the struct */
3386 if (cfg->gen_write_barriers && (klass->has_references || size_ins) && !native) {
3387 /* Avoid barriers when storing to the stack */
3388 if (!((dest->opcode == OP_ADD_IMM && dest->sreg1 == cfg->frame_reg) ||
3389 (dest->opcode == OP_LDADDR))) {
3395 context_used = mini_class_check_context_used (cfg, klass);
3397 /* It's ok to intrinsify under gsharing since shared code types are layout stable. */
3398 if (!size_ins && (cfg->opt & MONO_OPT_INTRINS) && mono_emit_wb_aware_memcpy (cfg, klass, iargs, n, align)) {
3400 } else if (context_used) {
3401 iargs [2] = emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_KLASS);
3403 iargs [2] = emit_runtime_constant (cfg, MONO_PATCH_INFO_CLASS, klass);
3404 if (!cfg->compile_aot)
3405 mono_class_compute_gc_descriptor (klass);
3409 mono_emit_jit_icall (cfg, mono_gsharedvt_value_copy, iargs);
3411 mono_emit_jit_icall (cfg, mono_value_copy, iargs);
3416 if (!size_ins && (cfg->opt & MONO_OPT_INTRINS) && n <= sizeof (gpointer) * 8) {
3417 /* FIXME: Optimize the case when src/dest is OP_LDADDR */
3418 mini_emit_memcpy (cfg, dest->dreg, 0, src->dreg, 0, n, align);
3423 iargs [2] = size_ins;
3425 EMIT_NEW_ICONST (cfg, iargs [2], n);
3427 memcpy_method = get_memcpy_method ();
3429 mono_emit_calli (cfg, mono_method_signature (memcpy_method), iargs, memcpy_ins, NULL, NULL);
3431 mono_emit_method_call (cfg, memcpy_method, iargs, NULL);
3436 get_memset_method (void)
3438 static MonoMethod *memset_method = NULL;
3439 if (!memset_method) {
3440 memset_method = mono_class_get_method_from_name (mono_defaults.string_class, "memset", 3);
3442 g_error ("Old corlib found. Install a new one");
3444 return memset_method;
3448 mini_emit_initobj (MonoCompile *cfg, MonoInst *dest, const guchar *ip, MonoClass *klass)
3450 MonoInst *iargs [3];
3453 MonoMethod *memset_method;
3454 MonoInst *size_ins = NULL;
3455 MonoInst *bzero_ins = NULL;
3456 static MonoMethod *bzero_method;
3458 /* FIXME: Optimize this for the case when dest is an LDADDR */
3459 mono_class_init (klass);
3460 if (mini_is_gsharedvt_klass (klass)) {
3461 size_ins = emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_VALUE_SIZE);
3462 bzero_ins = emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_BZERO);
3464 bzero_method = mono_class_get_method_from_name (mono_defaults.string_class, "bzero_aligned_1", 2);
3465 g_assert (bzero_method);
3467 iargs [1] = size_ins;
3468 mono_emit_calli (cfg, mono_method_signature (bzero_method), iargs, bzero_ins, NULL, NULL);
3472 klass = mono_class_from_mono_type (mini_get_underlying_type (&klass->byval_arg));
3474 n = mono_class_value_size (klass, &align);
3476 if (n <= sizeof (gpointer) * 8) {
3477 mini_emit_memset (cfg, dest->dreg, 0, n, 0, align);
3480 memset_method = get_memset_method ();
3482 EMIT_NEW_ICONST (cfg, iargs [1], 0);
3483 EMIT_NEW_ICONST (cfg, iargs [2], n);
3484 mono_emit_method_call (cfg, memset_method, iargs, NULL);
3491 * Emit IR to return either the this pointer for instance method,
3492 * or the mrgctx for static methods.
3495 emit_get_rgctx (MonoCompile *cfg, MonoMethod *method, int context_used)
3497 MonoInst *this_ins = NULL;
3499 g_assert (cfg->gshared);
3501 if (!(method->flags & METHOD_ATTRIBUTE_STATIC) &&
3502 !(context_used & MONO_GENERIC_CONTEXT_USED_METHOD) &&
3503 !method->klass->valuetype)
3504 EMIT_NEW_ARGLOAD (cfg, this_ins, 0);
3506 if (context_used & MONO_GENERIC_CONTEXT_USED_METHOD) {
3507 MonoInst *mrgctx_loc, *mrgctx_var;
3509 g_assert (!this_ins);
3510 g_assert (method->is_inflated && mono_method_get_context (method)->method_inst);
3512 mrgctx_loc = mono_get_vtable_var (cfg);
3513 EMIT_NEW_TEMPLOAD (cfg, mrgctx_var, mrgctx_loc->inst_c0);
3516 } else if (method->flags & METHOD_ATTRIBUTE_STATIC || method->klass->valuetype) {
3517 MonoInst *vtable_loc, *vtable_var;
3519 g_assert (!this_ins);
3521 vtable_loc = mono_get_vtable_var (cfg);
3522 EMIT_NEW_TEMPLOAD (cfg, vtable_var, vtable_loc->inst_c0);
3524 if (method->is_inflated && mono_method_get_context (method)->method_inst) {
3525 MonoInst *mrgctx_var = vtable_var;
3528 vtable_reg = alloc_preg (cfg);
3529 EMIT_NEW_LOAD_MEMBASE (cfg, vtable_var, OP_LOAD_MEMBASE, vtable_reg, mrgctx_var->dreg, MONO_STRUCT_OFFSET (MonoMethodRuntimeGenericContext, class_vtable));
3530 vtable_var->type = STACK_PTR;
3538 vtable_reg = alloc_preg (cfg);
3539 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, vtable_reg, this_ins->dreg, MONO_STRUCT_OFFSET (MonoObject, vtable));
3544 static MonoJumpInfoRgctxEntry *
3545 mono_patch_info_rgctx_entry_new (MonoMemPool *mp, MonoMethod *method, gboolean in_mrgctx, MonoJumpInfoType patch_type, gconstpointer patch_data, MonoRgctxInfoType info_type)
3547 MonoJumpInfoRgctxEntry *res = (MonoJumpInfoRgctxEntry *)mono_mempool_alloc0 (mp, sizeof (MonoJumpInfoRgctxEntry));
3548 res->method = method;
3549 res->in_mrgctx = in_mrgctx;
3550 res->data = (MonoJumpInfo *)mono_mempool_alloc0 (mp, sizeof (MonoJumpInfo));
3551 res->data->type = patch_type;
3552 res->data->data.target = patch_data;
3553 res->info_type = info_type;
3558 static inline MonoInst*
3559 emit_rgctx_fetch_inline (MonoCompile *cfg, MonoInst *rgctx, MonoJumpInfoRgctxEntry *entry)
3561 MonoInst *args [16];
3564 // FIXME: No fastpath since the slot is not a compile time constant
3566 EMIT_NEW_AOTCONST (cfg, args [1], MONO_PATCH_INFO_RGCTX_SLOT_INDEX, entry);
3567 if (entry->in_mrgctx)
3568 call = mono_emit_jit_icall (cfg, mono_fill_method_rgctx, args);
3570 call = mono_emit_jit_icall (cfg, mono_fill_class_rgctx, args);
3574 * FIXME: This can be called during decompose, which is a problem since it creates
3576 * Also, the fastpath doesn't work since the slot number is dynamically allocated.
3578 int i, slot, depth, index, rgctx_reg, val_reg, res_reg;
3580 MonoBasicBlock *is_null_bb, *end_bb;
3581 MonoInst *res, *ins, *call;
3584 slot = mini_get_rgctx_entry_slot (entry);
3586 mrgctx = MONO_RGCTX_SLOT_IS_MRGCTX (slot);
3587 index = MONO_RGCTX_SLOT_INDEX (slot);
3589 index += MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT / sizeof (gpointer);
3590 for (depth = 0; ; ++depth) {
3591 int size = mono_class_rgctx_get_array_size (depth, mrgctx);
3593 if (index < size - 1)
3598 NEW_BBLOCK (cfg, end_bb);
3599 NEW_BBLOCK (cfg, is_null_bb);
3602 rgctx_reg = rgctx->dreg;
3604 rgctx_reg = alloc_preg (cfg);
3606 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, rgctx_reg, rgctx->dreg, MONO_STRUCT_OFFSET (MonoVTable, runtime_generic_context));
3607 // FIXME: Avoid this check by allocating the table when the vtable is created etc.
3608 NEW_BBLOCK (cfg, is_null_bb);
3610 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, rgctx_reg, 0);
3611 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, is_null_bb);
3614 for (i = 0; i < depth; ++i) {
3615 int array_reg = alloc_preg (cfg);
3617 /* load ptr to next array */
3618 if (mrgctx && i == 0)
3619 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, array_reg, rgctx_reg, MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT);
3621 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, array_reg, rgctx_reg, 0);
3622 rgctx_reg = array_reg;
3623 /* is the ptr null? */
3624 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, rgctx_reg, 0);
3625 /* if yes, jump to actual trampoline */
3626 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, is_null_bb);
3630 val_reg = alloc_preg (cfg);
3631 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, val_reg, rgctx_reg, (index + 1) * sizeof (gpointer));
3632 /* is the slot null? */
3633 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, val_reg, 0);
3634 /* if yes, jump to actual trampoline */
3635 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, is_null_bb);
3638 res_reg = alloc_preg (cfg);
3639 MONO_INST_NEW (cfg, ins, OP_MOVE);
3640 ins->dreg = res_reg;
3641 ins->sreg1 = val_reg;
3642 MONO_ADD_INS (cfg->cbb, ins);
3644 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
3647 MONO_START_BB (cfg, is_null_bb);
3649 EMIT_NEW_ICONST (cfg, args [1], index);
3651 call = mono_emit_jit_icall (cfg, mono_fill_method_rgctx, args);
3653 call = mono_emit_jit_icall (cfg, mono_fill_class_rgctx, args);
3654 MONO_INST_NEW (cfg, ins, OP_MOVE);
3655 ins->dreg = res_reg;
3656 ins->sreg1 = call->dreg;
3657 MONO_ADD_INS (cfg->cbb, ins);
3658 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
3660 MONO_START_BB (cfg, end_bb);
3669 * Emit IR to load the value of the rgctx entry ENTRY from the rgctx
3672 static inline MonoInst*
3673 emit_rgctx_fetch (MonoCompile *cfg, MonoInst *rgctx, MonoJumpInfoRgctxEntry *entry)
3676 return emit_rgctx_fetch_inline (cfg, rgctx, entry);
3678 return mono_emit_abs_call (cfg, MONO_PATCH_INFO_RGCTX_FETCH, entry, helper_sig_rgctx_lazy_fetch_trampoline, &rgctx);
3682 emit_get_rgctx_klass (MonoCompile *cfg, int context_used,
3683 MonoClass *klass, MonoRgctxInfoType rgctx_type)
3685 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);
3686 MonoInst *rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
3688 return emit_rgctx_fetch (cfg, rgctx, entry);
3692 emit_get_rgctx_sig (MonoCompile *cfg, int context_used,
3693 MonoMethodSignature *sig, MonoRgctxInfoType rgctx_type)
3695 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);
3696 MonoInst *rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
3698 return emit_rgctx_fetch (cfg, rgctx, entry);
3702 emit_get_rgctx_gsharedvt_call (MonoCompile *cfg, int context_used,
3703 MonoMethodSignature *sig, MonoMethod *cmethod, MonoRgctxInfoType rgctx_type)
3705 MonoJumpInfoGSharedVtCall *call_info;
3706 MonoJumpInfoRgctxEntry *entry;
3709 call_info = (MonoJumpInfoGSharedVtCall *)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoJumpInfoGSharedVtCall));
3710 call_info->sig = sig;
3711 call_info->method = cmethod;
3713 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);
3714 rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
3716 return emit_rgctx_fetch (cfg, rgctx, entry);
3720 * emit_get_rgctx_virt_method:
3722 * Return data for method VIRT_METHOD for a receiver of type KLASS.
3725 emit_get_rgctx_virt_method (MonoCompile *cfg, int context_used,
3726 MonoClass *klass, MonoMethod *virt_method, MonoRgctxInfoType rgctx_type)
3728 MonoJumpInfoVirtMethod *info;
3729 MonoJumpInfoRgctxEntry *entry;
3732 info = (MonoJumpInfoVirtMethod *)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoJumpInfoVirtMethod));
3733 info->klass = klass;
3734 info->method = virt_method;
3736 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);
3737 rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
3739 return emit_rgctx_fetch (cfg, rgctx, entry);
3743 emit_get_rgctx_gsharedvt_method (MonoCompile *cfg, int context_used,
3744 MonoMethod *cmethod, MonoGSharedVtMethodInfo *info)
3746 MonoJumpInfoRgctxEntry *entry;
3749 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);
3750 rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
3752 return emit_rgctx_fetch (cfg, rgctx, entry);
3756 * emit_get_rgctx_method:
3758 * Emit IR to load the property RGCTX_TYPE of CMETHOD. If context_used is 0, emit
3759 * normal constants, else emit a load from the rgctx.
3762 emit_get_rgctx_method (MonoCompile *cfg, int context_used,
3763 MonoMethod *cmethod, MonoRgctxInfoType rgctx_type)
3765 if (!context_used) {
3768 switch (rgctx_type) {
3769 case MONO_RGCTX_INFO_METHOD:
3770 EMIT_NEW_METHODCONST (cfg, ins, cmethod);
3772 case MONO_RGCTX_INFO_METHOD_RGCTX:
3773 EMIT_NEW_METHOD_RGCTX_CONST (cfg, ins, cmethod);
3776 g_assert_not_reached ();
3779 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);
3780 MonoInst *rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
3782 return emit_rgctx_fetch (cfg, rgctx, entry);
3787 emit_get_rgctx_field (MonoCompile *cfg, int context_used,
3788 MonoClassField *field, MonoRgctxInfoType rgctx_type)
3790 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);
3791 MonoInst *rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
3793 return emit_rgctx_fetch (cfg, rgctx, entry);
3797 get_gsharedvt_info_slot (MonoCompile *cfg, gpointer data, MonoRgctxInfoType rgctx_type)
3799 MonoGSharedVtMethodInfo *info = cfg->gsharedvt_info;
3800 MonoRuntimeGenericContextInfoTemplate *template_;
3805 for (i = 0; i < info->num_entries; ++i) {
3806 MonoRuntimeGenericContextInfoTemplate *otemplate = &info->entries [i];
3808 if (otemplate->info_type == rgctx_type && otemplate->data == data && rgctx_type != MONO_RGCTX_INFO_LOCAL_OFFSET)
3812 if (info->num_entries == info->count_entries) {
3813 MonoRuntimeGenericContextInfoTemplate *new_entries;
3814 int new_count_entries = info->count_entries ? info->count_entries * 2 : 16;
3816 new_entries = (MonoRuntimeGenericContextInfoTemplate *)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoRuntimeGenericContextInfoTemplate) * new_count_entries);
3818 memcpy (new_entries, info->entries, sizeof (MonoRuntimeGenericContextInfoTemplate) * info->count_entries);
3819 info->entries = new_entries;
3820 info->count_entries = new_count_entries;
3823 idx = info->num_entries;
3824 template_ = &info->entries [idx];
3825 template_->info_type = rgctx_type;
3826 template_->data = data;
3828 info->num_entries ++;
3834 * emit_get_gsharedvt_info:
3836 * This is similar to emit_get_rgctx_.., but loads the data from the gsharedvt info var instead of calling an rgctx fetch trampoline.
3839 emit_get_gsharedvt_info (MonoCompile *cfg, gpointer data, MonoRgctxInfoType rgctx_type)
3844 idx = get_gsharedvt_info_slot (cfg, data, rgctx_type);
3845 /* Load info->entries [idx] */
3846 dreg = alloc_preg (cfg);
3847 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, dreg, cfg->gsharedvt_info_var->dreg, MONO_STRUCT_OFFSET (MonoGSharedVtMethodRuntimeInfo, entries) + (idx * sizeof (gpointer)));
3853 emit_get_gsharedvt_info_klass (MonoCompile *cfg, MonoClass *klass, MonoRgctxInfoType rgctx_type)
3855 return emit_get_gsharedvt_info (cfg, &klass->byval_arg, rgctx_type);
3859 * On return the caller must check @klass for load errors.
3862 emit_class_init (MonoCompile *cfg, MonoClass *klass)
3864 MonoInst *vtable_arg;
3867 context_used = mini_class_check_context_used (cfg, klass);
3870 vtable_arg = emit_get_rgctx_klass (cfg, context_used,
3871 klass, MONO_RGCTX_INFO_VTABLE);
3873 MonoVTable *vtable = mono_class_vtable (cfg->domain, klass);
3877 EMIT_NEW_VTABLECONST (cfg, vtable_arg, vtable);
3880 if (!COMPILE_LLVM (cfg) && cfg->backend->have_op_generic_class_init) {
3884 * Using an opcode instead of emitting IR here allows the hiding of the call inside the opcode,
3885 * so this doesn't have to clobber any regs and it doesn't break basic blocks.
3887 MONO_INST_NEW (cfg, ins, OP_GENERIC_CLASS_INIT);
3888 ins->sreg1 = vtable_arg->dreg;
3889 MONO_ADD_INS (cfg->cbb, ins);
3891 static int byte_offset = -1;
3892 static guint8 bitmask;
3893 int bits_reg, inited_reg;
3894 MonoBasicBlock *inited_bb;
3895 MonoInst *args [16];
3897 if (byte_offset < 0)
3898 mono_marshal_find_bitfield_offset (MonoVTable, initialized, &byte_offset, &bitmask);
3900 bits_reg = alloc_ireg (cfg);
3901 inited_reg = alloc_ireg (cfg);
3903 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU1_MEMBASE, bits_reg, vtable_arg->dreg, byte_offset);
3904 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_IAND_IMM, inited_reg, bits_reg, bitmask);
3906 NEW_BBLOCK (cfg, inited_bb);
3908 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, inited_reg, 0);
3909 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBNE_UN, inited_bb);
3911 args [0] = vtable_arg;
3912 mono_emit_jit_icall (cfg, mono_generic_class_init, args);
3914 MONO_START_BB (cfg, inited_bb);
3919 emit_seq_point (MonoCompile *cfg, MonoMethod *method, guint8* ip, gboolean intr_loc, gboolean nonempty_stack)
3923 if (cfg->gen_seq_points && cfg->method == method) {
3924 NEW_SEQ_POINT (cfg, ins, ip - cfg->header->code, intr_loc);
3926 ins->flags |= MONO_INST_NONEMPTY_STACK;
3927 MONO_ADD_INS (cfg->cbb, ins);
3932 save_cast_details (MonoCompile *cfg, MonoClass *klass, int obj_reg, gboolean null_check)
3934 if (mini_get_debug_options ()->better_cast_details) {
3935 int vtable_reg = alloc_preg (cfg);
3936 int klass_reg = alloc_preg (cfg);
3937 MonoBasicBlock *is_null_bb = NULL;
3939 int to_klass_reg, context_used;
3942 NEW_BBLOCK (cfg, is_null_bb);
3944 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, obj_reg, 0);
3945 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, is_null_bb);
3948 tls_get = mono_get_jit_tls_intrinsic (cfg);
3950 fprintf (stderr, "error: --debug=casts not supported on this platform.\n.");
3954 MONO_ADD_INS (cfg->cbb, tls_get);
3955 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, vtable_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
3956 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
3958 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, tls_get->dreg, MONO_STRUCT_OFFSET (MonoJitTlsData, class_cast_from), klass_reg);
3960 context_used = mini_class_check_context_used (cfg, klass);
3962 MonoInst *class_ins;
3964 class_ins = emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_KLASS);
3965 to_klass_reg = class_ins->dreg;
3967 to_klass_reg = alloc_preg (cfg);
3968 MONO_EMIT_NEW_CLASSCONST (cfg, to_klass_reg, klass);
3970 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, tls_get->dreg, MONO_STRUCT_OFFSET (MonoJitTlsData, class_cast_to), to_klass_reg);
3973 MONO_START_BB (cfg, is_null_bb);
3978 reset_cast_details (MonoCompile *cfg)
3980 /* Reset the variables holding the cast details */
3981 if (mini_get_debug_options ()->better_cast_details) {
3982 MonoInst *tls_get = mono_get_jit_tls_intrinsic (cfg);
3984 MONO_ADD_INS (cfg->cbb, tls_get);
3985 /* It is enough to reset the from field */
3986 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STORE_MEMBASE_IMM, tls_get->dreg, MONO_STRUCT_OFFSET (MonoJitTlsData, class_cast_from), 0);
3991 * On return the caller must check @array_class for load errors
3994 mini_emit_check_array_type (MonoCompile *cfg, MonoInst *obj, MonoClass *array_class)
3996 int vtable_reg = alloc_preg (cfg);
3999 context_used = mini_class_check_context_used (cfg, array_class);
4001 save_cast_details (cfg, array_class, obj->dreg, FALSE);
4003 MONO_EMIT_NEW_LOAD_MEMBASE_FAULT (cfg, vtable_reg, obj->dreg, MONO_STRUCT_OFFSET (MonoObject, vtable));
4005 if (cfg->opt & MONO_OPT_SHARED) {
4006 int class_reg = alloc_preg (cfg);
4009 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, class_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4010 ins = emit_runtime_constant (cfg, MONO_PATCH_INFO_CLASS, array_class);
4011 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, class_reg, ins->dreg);
4012 } else if (context_used) {
4013 MonoInst *vtable_ins;
4015 vtable_ins = emit_get_rgctx_klass (cfg, context_used, array_class, MONO_RGCTX_INFO_VTABLE);
4016 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, vtable_reg, vtable_ins->dreg);
4018 if (cfg->compile_aot) {
4022 if (!(vtable = mono_class_vtable (cfg->domain, array_class)))
4024 vt_reg = alloc_preg (cfg);
4025 MONO_EMIT_NEW_VTABLECONST (cfg, vt_reg, vtable);
4026 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, vtable_reg, vt_reg);
4029 if (!(vtable = mono_class_vtable (cfg->domain, array_class)))
4031 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, vtable_reg, vtable);
4035 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "ArrayTypeMismatchException");
4037 reset_cast_details (cfg);
4041 * Handles unbox of a Nullable<T>. If context_used is non zero, then shared
4042 * generic code is generated.
4045 handle_unbox_nullable (MonoCompile* cfg, MonoInst* val, MonoClass* klass, int context_used)
4047 MonoMethod* method = mono_class_get_method_from_name (klass, "Unbox", 1);
4050 MonoInst *rgctx, *addr;
4052 /* FIXME: What if the class is shared? We might not
4053 have to get the address of the method from the
4055 addr = emit_get_rgctx_method (cfg, context_used, method,
4056 MONO_RGCTX_INFO_GENERIC_METHOD_CODE);
4057 if (cfg->llvm_only && cfg->gsharedvt) {
4058 return emit_llvmonly_calli (cfg, mono_method_signature (method), &val, addr);
4060 rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
4062 return mono_emit_calli (cfg, mono_method_signature (method), &val, addr, NULL, rgctx);
4065 gboolean pass_vtable, pass_mrgctx;
4066 MonoInst *rgctx_arg = NULL;
4068 check_method_sharing (cfg, method, &pass_vtable, &pass_mrgctx);
4069 g_assert (!pass_mrgctx);
4072 MonoVTable *vtable = mono_class_vtable (cfg->domain, method->klass);
4075 EMIT_NEW_VTABLECONST (cfg, rgctx_arg, vtable);
4078 return mono_emit_method_call_full (cfg, method, NULL, FALSE, &val, NULL, NULL, rgctx_arg);
4083 handle_unbox (MonoCompile *cfg, MonoClass *klass, MonoInst **sp, int context_used)
4087 int vtable_reg = alloc_dreg (cfg ,STACK_PTR);
4088 int klass_reg = alloc_dreg (cfg ,STACK_PTR);
4089 int eclass_reg = alloc_dreg (cfg ,STACK_PTR);
4090 int rank_reg = alloc_dreg (cfg ,STACK_I4);
4092 obj_reg = sp [0]->dreg;
4093 MONO_EMIT_NEW_LOAD_MEMBASE_FAULT (cfg, vtable_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
4094 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU1_MEMBASE, rank_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, rank));
4096 /* FIXME: generics */
4097 g_assert (klass->rank == 0);
4100 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, rank_reg, 0);
4101 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException");
4103 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4104 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, eclass_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, element_class));
4107 MonoInst *element_class;
4109 /* This assertion is from the unboxcast insn */
4110 g_assert (klass->rank == 0);
4112 element_class = emit_get_rgctx_klass (cfg, context_used,
4113 klass, MONO_RGCTX_INFO_ELEMENT_KLASS);
4115 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, eclass_reg, element_class->dreg);
4116 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException");
4118 save_cast_details (cfg, klass->element_class, obj_reg, FALSE);
4119 mini_emit_class_check (cfg, eclass_reg, klass->element_class);
4120 reset_cast_details (cfg);
4123 NEW_BIALU_IMM (cfg, add, OP_ADD_IMM, alloc_dreg (cfg, STACK_MP), obj_reg, sizeof (MonoObject));
4124 MONO_ADD_INS (cfg->cbb, add);
4125 add->type = STACK_MP;
4132 handle_unbox_gsharedvt (MonoCompile *cfg, MonoClass *klass, MonoInst *obj)
4134 MonoInst *addr, *klass_inst, *is_ref, *args[16];
4135 MonoBasicBlock *is_ref_bb, *is_nullable_bb, *end_bb;
4139 klass_inst = emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_KLASS);
4145 args [1] = klass_inst;
4148 obj = mono_emit_jit_icall (cfg, mono_object_castclass_unbox, args);
4150 NEW_BBLOCK (cfg, is_ref_bb);
4151 NEW_BBLOCK (cfg, is_nullable_bb);
4152 NEW_BBLOCK (cfg, end_bb);
4153 is_ref = emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_CLASS_BOX_TYPE);
4154 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, is_ref->dreg, MONO_GSHAREDVT_BOX_TYPE_REF);
4155 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, is_ref_bb);
4157 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, is_ref->dreg, MONO_GSHAREDVT_BOX_TYPE_NULLABLE);
4158 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, is_nullable_bb);
4160 /* This will contain either the address of the unboxed vtype, or an address of the temporary where the ref is stored */
4161 addr_reg = alloc_dreg (cfg, STACK_MP);
4165 NEW_BIALU_IMM (cfg, addr, OP_ADD_IMM, addr_reg, obj->dreg, sizeof (MonoObject));
4166 MONO_ADD_INS (cfg->cbb, addr);
4168 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4171 MONO_START_BB (cfg, is_ref_bb);
4173 /* Save the ref to a temporary */
4174 dreg = alloc_ireg (cfg);
4175 EMIT_NEW_VARLOADA_VREG (cfg, addr, dreg, &klass->byval_arg);
4176 addr->dreg = addr_reg;
4177 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, addr->dreg, 0, obj->dreg);
4178 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4181 MONO_START_BB (cfg, is_nullable_bb);
4184 MonoInst *addr = emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_NULLABLE_CLASS_UNBOX);
4185 MonoInst *unbox_call;
4186 MonoMethodSignature *unbox_sig;
4188 unbox_sig = (MonoMethodSignature *)mono_mempool_alloc0 (cfg->mempool, MONO_SIZEOF_METHOD_SIGNATURE + (1 * sizeof (MonoType *)));
4189 unbox_sig->ret = &klass->byval_arg;
4190 unbox_sig->param_count = 1;
4191 unbox_sig->params [0] = &mono_defaults.object_class->byval_arg;
4194 unbox_call = emit_llvmonly_calli (cfg, unbox_sig, &obj, addr);
4196 unbox_call = mono_emit_calli (cfg, unbox_sig, &obj, addr, NULL, NULL);
4198 EMIT_NEW_VARLOADA_VREG (cfg, addr, unbox_call->dreg, &klass->byval_arg);
4199 addr->dreg = addr_reg;
4202 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4205 MONO_START_BB (cfg, end_bb);
4208 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, addr_reg, 0);
4214 * Returns NULL and set the cfg exception on error.
4217 handle_alloc (MonoCompile *cfg, MonoClass *klass, gboolean for_box, int context_used)
4219 MonoInst *iargs [2];
4224 MonoRgctxInfoType rgctx_info;
4225 MonoInst *iargs [2];
4226 gboolean known_instance_size = !mini_is_gsharedvt_klass (klass);
4228 MonoMethod *managed_alloc = mono_gc_get_managed_allocator (klass, for_box, known_instance_size);
4230 if (cfg->opt & MONO_OPT_SHARED)
4231 rgctx_info = MONO_RGCTX_INFO_KLASS;
4233 rgctx_info = MONO_RGCTX_INFO_VTABLE;
4234 data = emit_get_rgctx_klass (cfg, context_used, klass, rgctx_info);
4236 if (cfg->opt & MONO_OPT_SHARED) {
4237 EMIT_NEW_DOMAINCONST (cfg, iargs [0]);
4239 alloc_ftn = mono_object_new;
4242 alloc_ftn = mono_object_new_specific;
4245 if (managed_alloc && !(cfg->opt & MONO_OPT_SHARED)) {
4246 if (known_instance_size) {
4247 int size = mono_class_instance_size (klass);
4248 if (size < sizeof (MonoObject))
4249 g_error ("Invalid size %d for class %s", size, mono_type_get_full_name (klass));
4251 EMIT_NEW_ICONST (cfg, iargs [1], mono_gc_get_aligned_size_for_allocator (size));
4253 return mono_emit_method_call (cfg, managed_alloc, iargs, NULL);
4256 return mono_emit_jit_icall (cfg, alloc_ftn, iargs);
4259 if (cfg->opt & MONO_OPT_SHARED) {
4260 EMIT_NEW_DOMAINCONST (cfg, iargs [0]);
4261 EMIT_NEW_CLASSCONST (cfg, iargs [1], klass);
4263 alloc_ftn = mono_object_new;
4264 } else if (cfg->compile_aot && cfg->cbb->out_of_line && klass->type_token && klass->image == mono_defaults.corlib && !klass->generic_class) {
4265 /* This happens often in argument checking code, eg. throw new FooException... */
4266 /* Avoid relocations and save some space by calling a helper function specialized to mscorlib */
4267 EMIT_NEW_ICONST (cfg, iargs [0], mono_metadata_token_index (klass->type_token));
4268 return mono_emit_jit_icall (cfg, mono_helper_newobj_mscorlib, iargs);
4270 MonoVTable *vtable = mono_class_vtable (cfg->domain, klass);
4271 MonoMethod *managed_alloc = NULL;
4275 mono_cfg_set_exception (cfg, MONO_EXCEPTION_TYPE_LOAD);
4276 cfg->exception_ptr = klass;
4280 managed_alloc = mono_gc_get_managed_allocator (klass, for_box, TRUE);
4282 if (managed_alloc) {
4283 int size = mono_class_instance_size (klass);
4284 if (size < sizeof (MonoObject))
4285 g_error ("Invalid size %d for class %s", size, mono_type_get_full_name (klass));
4287 EMIT_NEW_VTABLECONST (cfg, iargs [0], vtable);
4288 EMIT_NEW_ICONST (cfg, iargs [1], mono_gc_get_aligned_size_for_allocator (size));
4289 return mono_emit_method_call (cfg, managed_alloc, iargs, NULL);
4291 alloc_ftn = mono_class_get_allocation_ftn (vtable, for_box, &pass_lw);
4293 guint32 lw = vtable->klass->instance_size;
4294 lw = ((lw + (sizeof (gpointer) - 1)) & ~(sizeof (gpointer) - 1)) / sizeof (gpointer);
4295 EMIT_NEW_ICONST (cfg, iargs [0], lw);
4296 EMIT_NEW_VTABLECONST (cfg, iargs [1], vtable);
4299 EMIT_NEW_VTABLECONST (cfg, iargs [0], vtable);
4303 return mono_emit_jit_icall (cfg, alloc_ftn, iargs);
4307 * Returns NULL and set the cfg exception on error.
4310 handle_box (MonoCompile *cfg, MonoInst *val, MonoClass *klass, int context_used)
4312 MonoInst *alloc, *ins;
4314 if (mono_class_is_nullable (klass)) {
4315 MonoMethod* method = mono_class_get_method_from_name (klass, "Box", 1);
4318 if (cfg->llvm_only && cfg->gsharedvt) {
4319 MonoInst *addr = emit_get_rgctx_method (cfg, context_used, method,
4320 MONO_RGCTX_INFO_GENERIC_METHOD_CODE);
4321 return emit_llvmonly_calli (cfg, mono_method_signature (method), &val, addr);
4323 /* FIXME: What if the class is shared? We might not
4324 have to get the method address from the RGCTX. */
4325 MonoInst *addr = emit_get_rgctx_method (cfg, context_used, method,
4326 MONO_RGCTX_INFO_GENERIC_METHOD_CODE);
4327 MonoInst *rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
4329 return mono_emit_calli (cfg, mono_method_signature (method), &val, addr, NULL, rgctx);
4332 gboolean pass_vtable, pass_mrgctx;
4333 MonoInst *rgctx_arg = NULL;
4335 check_method_sharing (cfg, method, &pass_vtable, &pass_mrgctx);
4336 g_assert (!pass_mrgctx);
4339 MonoVTable *vtable = mono_class_vtable (cfg->domain, method->klass);
4342 EMIT_NEW_VTABLECONST (cfg, rgctx_arg, vtable);
4345 return mono_emit_method_call_full (cfg, method, NULL, FALSE, &val, NULL, NULL, rgctx_arg);
4349 if (mini_is_gsharedvt_klass (klass)) {
4350 MonoBasicBlock *is_ref_bb, *is_nullable_bb, *end_bb;
4351 MonoInst *res, *is_ref, *src_var, *addr;
4354 dreg = alloc_ireg (cfg);
4356 NEW_BBLOCK (cfg, is_ref_bb);
4357 NEW_BBLOCK (cfg, is_nullable_bb);
4358 NEW_BBLOCK (cfg, end_bb);
4359 is_ref = emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_CLASS_BOX_TYPE);
4360 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, is_ref->dreg, MONO_GSHAREDVT_BOX_TYPE_REF);
4361 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, is_ref_bb);
4363 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, is_ref->dreg, MONO_GSHAREDVT_BOX_TYPE_NULLABLE);
4364 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, is_nullable_bb);
4367 alloc = handle_alloc (cfg, klass, TRUE, context_used);
4370 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, alloc->dreg, sizeof (MonoObject), val->dreg);
4371 ins->opcode = OP_STOREV_MEMBASE;
4373 EMIT_NEW_UNALU (cfg, res, OP_MOVE, dreg, alloc->dreg);
4374 res->type = STACK_OBJ;
4376 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4379 MONO_START_BB (cfg, is_ref_bb);
4381 /* val is a vtype, so has to load the value manually */
4382 src_var = get_vreg_to_inst (cfg, val->dreg);
4384 src_var = mono_compile_create_var_for_vreg (cfg, &klass->byval_arg, OP_LOCAL, val->dreg);
4385 EMIT_NEW_VARLOADA (cfg, addr, src_var, src_var->inst_vtype);
4386 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, dreg, addr->dreg, 0);
4387 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4390 MONO_START_BB (cfg, is_nullable_bb);
4393 MonoInst *addr = emit_get_gsharedvt_info_klass (cfg, klass,
4394 MONO_RGCTX_INFO_NULLABLE_CLASS_BOX);
4396 MonoMethodSignature *box_sig;
4399 * klass is Nullable<T>, need to call Nullable<T>.Box () using a gsharedvt signature, but we cannot
4400 * construct that method at JIT time, so have to do things by hand.
4402 box_sig = (MonoMethodSignature *)mono_mempool_alloc0 (cfg->mempool, MONO_SIZEOF_METHOD_SIGNATURE + (1 * sizeof (MonoType *)));
4403 box_sig->ret = &mono_defaults.object_class->byval_arg;
4404 box_sig->param_count = 1;
4405 box_sig->params [0] = &klass->byval_arg;
4408 box_call = emit_llvmonly_calli (cfg, box_sig, &val, addr);
4410 box_call = mono_emit_calli (cfg, box_sig, &val, addr, NULL, NULL);
4411 EMIT_NEW_UNALU (cfg, res, OP_MOVE, dreg, box_call->dreg);
4412 res->type = STACK_OBJ;
4416 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4418 MONO_START_BB (cfg, end_bb);
4422 alloc = handle_alloc (cfg, klass, TRUE, context_used);
4426 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, alloc->dreg, sizeof (MonoObject), val->dreg);
4432 mini_class_has_reference_variant_generic_argument (MonoCompile *cfg, MonoClass *klass, int context_used)
4435 MonoGenericContainer *container;
4436 MonoGenericInst *ginst;
4438 if (klass->generic_class) {
4439 container = klass->generic_class->container_class->generic_container;
4440 ginst = klass->generic_class->context.class_inst;
4441 } else if (klass->generic_container && context_used) {
4442 container = klass->generic_container;
4443 ginst = container->context.class_inst;
4448 for (i = 0; i < container->type_argc; ++i) {
4450 if (!(mono_generic_container_get_param_info (container, i)->flags & (MONO_GEN_PARAM_VARIANT|MONO_GEN_PARAM_COVARIANT)))
4452 type = ginst->type_argv [i];
4453 if (mini_type_is_reference (type))
4459 static GHashTable* direct_icall_type_hash;
4462 icall_is_direct_callable (MonoCompile *cfg, MonoMethod *cmethod)
4464 /* LLVM on amd64 can't handle calls to non-32 bit addresses */
4465 if (!direct_icalls_enabled (cfg))
4469 * An icall is directly callable if it doesn't directly or indirectly call mono_raise_exception ().
4470 * Whitelist a few icalls for now.
4472 if (!direct_icall_type_hash) {
4473 GHashTable *h = g_hash_table_new (g_str_hash, g_str_equal);
4475 g_hash_table_insert (h, (char*)"Decimal", GUINT_TO_POINTER (1));
4476 g_hash_table_insert (h, (char*)"Number", GUINT_TO_POINTER (1));
4477 g_hash_table_insert (h, (char*)"Buffer", GUINT_TO_POINTER (1));
4478 g_hash_table_insert (h, (char*)"Monitor", GUINT_TO_POINTER (1));
4479 mono_memory_barrier ();
4480 direct_icall_type_hash = h;
4483 if (cmethod->klass == mono_defaults.math_class)
4485 /* No locking needed */
4486 if (cmethod->klass->image == mono_defaults.corlib && g_hash_table_lookup (direct_icall_type_hash, cmethod->klass->name))
4491 #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)
4494 emit_castclass_with_cache (MonoCompile *cfg, MonoClass *klass, MonoInst **args)
4496 MonoMethod *mono_castclass;
4499 mono_castclass = mono_marshal_get_castclass_with_cache ();
4501 save_cast_details (cfg, klass, args [0]->dreg, TRUE);
4502 res = mono_emit_method_call (cfg, mono_castclass, args, NULL);
4503 reset_cast_details (cfg);
4509 get_castclass_cache_idx (MonoCompile *cfg)
4511 /* Each CASTCLASS_CACHE patch needs a unique index which identifies the call site */
4512 cfg->castclass_cache_index ++;
4513 return (cfg->method_index << 16) | cfg->castclass_cache_index;
4517 emit_castclass_with_cache_nonshared (MonoCompile *cfg, MonoInst *obj, MonoClass *klass)
4526 EMIT_NEW_CLASSCONST (cfg, args [1], klass);
4529 idx = get_castclass_cache_idx (cfg);
4530 args [2] = emit_runtime_constant (cfg, MONO_PATCH_INFO_CASTCLASS_CACHE, GINT_TO_POINTER (idx));
4532 /*The wrapper doesn't inline well so the bloat of inlining doesn't pay off.*/
4533 return emit_castclass_with_cache (cfg, klass, args);
4537 * Returns NULL and set the cfg exception on error.
4540 handle_castclass (MonoCompile *cfg, MonoClass *klass, MonoInst *src, guint8 *ip, int *inline_costs)
4542 MonoBasicBlock *is_null_bb;
4543 int obj_reg = src->dreg;
4544 int vtable_reg = alloc_preg (cfg);
4546 MonoInst *klass_inst = NULL, *res;
4548 context_used = mini_class_check_context_used (cfg, klass);
4550 if (!context_used && mini_class_has_reference_variant_generic_argument (cfg, klass, context_used)) {
4551 res = emit_castclass_with_cache_nonshared (cfg, src, klass);
4552 (*inline_costs) += 2;
4554 } else if (!context_used && (mono_class_is_marshalbyref (klass) || klass->flags & TYPE_ATTRIBUTE_INTERFACE)) {
4555 MonoMethod *mono_castclass;
4556 MonoInst *iargs [1];
4559 mono_castclass = mono_marshal_get_castclass (klass);
4562 save_cast_details (cfg, klass, src->dreg, TRUE);
4563 costs = inline_method (cfg, mono_castclass, mono_method_signature (mono_castclass),
4564 iargs, ip, cfg->real_offset, TRUE);
4565 reset_cast_details (cfg);
4566 CHECK_CFG_EXCEPTION;
4567 g_assert (costs > 0);
4569 cfg->real_offset += 5;
4571 (*inline_costs) += costs;
4579 if(mini_class_has_reference_variant_generic_argument (cfg, klass, context_used) || is_complex_isinst (klass)) {
4580 MonoInst *cache_ins;
4582 cache_ins = emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_CAST_CACHE);
4587 /* klass - it's the second element of the cache entry*/
4588 EMIT_NEW_LOAD_MEMBASE (cfg, args [1], OP_LOAD_MEMBASE, alloc_preg (cfg), cache_ins->dreg, sizeof (gpointer));
4591 args [2] = cache_ins;
4593 return emit_castclass_with_cache (cfg, klass, args);
4596 klass_inst = emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_KLASS);
4599 NEW_BBLOCK (cfg, is_null_bb);
4601 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, obj_reg, 0);
4602 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, is_null_bb);
4604 save_cast_details (cfg, klass, obj_reg, FALSE);
4606 if (klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
4607 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, vtable_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
4608 mini_emit_iface_cast (cfg, vtable_reg, klass, NULL, NULL);
4610 int klass_reg = alloc_preg (cfg);
4612 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, vtable_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
4614 if (!klass->rank && !cfg->compile_aot && !(cfg->opt & MONO_OPT_SHARED) && (klass->flags & TYPE_ATTRIBUTE_SEALED)) {
4615 /* the remoting code is broken, access the class for now */
4616 if (0) { /*FIXME what exactly is broken? This change refers to r39380 from 2005 and mention some remoting fixes were due.*/
4617 MonoVTable *vt = mono_class_vtable (cfg->domain, klass);
4619 mono_cfg_set_exception (cfg, MONO_EXCEPTION_TYPE_LOAD);
4620 cfg->exception_ptr = klass;
4623 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, vtable_reg, vt);
4625 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4626 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, klass_reg, klass);
4628 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException");
4630 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4631 mini_emit_castclass_inst (cfg, obj_reg, klass_reg, klass, klass_inst, is_null_bb);
4635 MONO_START_BB (cfg, is_null_bb);
4637 reset_cast_details (cfg);
4646 * Returns NULL and set the cfg exception on error.
4649 handle_isinst (MonoCompile *cfg, MonoClass *klass, MonoInst *src, int context_used)
4652 MonoBasicBlock *is_null_bb, *false_bb, *end_bb;
4653 int obj_reg = src->dreg;
4654 int vtable_reg = alloc_preg (cfg);
4655 int res_reg = alloc_ireg_ref (cfg);
4656 MonoInst *klass_inst = NULL;
4661 if(mini_class_has_reference_variant_generic_argument (cfg, klass, context_used) || is_complex_isinst (klass)) {
4662 MonoMethod *mono_isinst = mono_marshal_get_isinst_with_cache ();
4663 MonoInst *cache_ins;
4665 cache_ins = emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_CAST_CACHE);
4670 /* klass - it's the second element of the cache entry*/
4671 EMIT_NEW_LOAD_MEMBASE (cfg, args [1], OP_LOAD_MEMBASE, alloc_preg (cfg), cache_ins->dreg, sizeof (gpointer));
4674 args [2] = cache_ins;
4676 return mono_emit_method_call (cfg, mono_isinst, args, NULL);
4679 klass_inst = emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_KLASS);
4682 NEW_BBLOCK (cfg, is_null_bb);
4683 NEW_BBLOCK (cfg, false_bb);
4684 NEW_BBLOCK (cfg, end_bb);
4686 /* Do the assignment at the beginning, so the other assignment can be if converted */
4687 EMIT_NEW_UNALU (cfg, ins, OP_MOVE, res_reg, obj_reg);
4688 ins->type = STACK_OBJ;
4691 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, obj_reg, 0);
4692 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, is_null_bb);
4694 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, vtable_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
4696 if (klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
4697 g_assert (!context_used);
4698 /* the is_null_bb target simply copies the input register to the output */
4699 mini_emit_iface_cast (cfg, vtable_reg, klass, false_bb, is_null_bb);
4701 int klass_reg = alloc_preg (cfg);
4704 int rank_reg = alloc_preg (cfg);
4705 int eclass_reg = alloc_preg (cfg);
4707 g_assert (!context_used);
4708 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU1_MEMBASE, rank_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, rank));
4709 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, rank_reg, klass->rank);
4710 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, false_bb);
4711 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4712 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, eclass_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, cast_class));
4713 if (klass->cast_class == mono_defaults.object_class) {
4714 int parent_reg = alloc_preg (cfg);
4715 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, parent_reg, eclass_reg, MONO_STRUCT_OFFSET (MonoClass, parent));
4716 mini_emit_class_check_branch (cfg, parent_reg, mono_defaults.enum_class->parent, OP_PBNE_UN, is_null_bb);
4717 mini_emit_class_check_branch (cfg, eclass_reg, mono_defaults.enum_class, OP_PBEQ, is_null_bb);
4718 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, false_bb);
4719 } else if (klass->cast_class == mono_defaults.enum_class->parent) {
4720 mini_emit_class_check_branch (cfg, eclass_reg, mono_defaults.enum_class->parent, OP_PBEQ, is_null_bb);
4721 mini_emit_class_check_branch (cfg, eclass_reg, mono_defaults.enum_class, OP_PBEQ, is_null_bb);
4722 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, false_bb);
4723 } else if (klass->cast_class == mono_defaults.enum_class) {
4724 mini_emit_class_check_branch (cfg, eclass_reg, mono_defaults.enum_class, OP_PBEQ, is_null_bb);
4725 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, false_bb);
4726 } else if (klass->cast_class->flags & TYPE_ATTRIBUTE_INTERFACE) {
4727 mini_emit_iface_class_cast (cfg, eclass_reg, klass->cast_class, false_bb, is_null_bb);
4729 if ((klass->rank == 1) && (klass->byval_arg.type == MONO_TYPE_SZARRAY)) {
4730 /* Check that the object is a vector too */
4731 int bounds_reg = alloc_preg (cfg);
4732 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, bounds_reg, obj_reg, MONO_STRUCT_OFFSET (MonoArray, bounds));
4733 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, bounds_reg, 0);
4734 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, false_bb);
4737 /* the is_null_bb target simply copies the input register to the output */
4738 mini_emit_isninst_cast (cfg, eclass_reg, klass->cast_class, false_bb, is_null_bb);
4740 } else if (mono_class_is_nullable (klass)) {
4741 g_assert (!context_used);
4742 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4743 /* the is_null_bb target simply copies the input register to the output */
4744 mini_emit_isninst_cast (cfg, klass_reg, klass->cast_class, false_bb, is_null_bb);
4746 if (!cfg->compile_aot && !(cfg->opt & MONO_OPT_SHARED) && (klass->flags & TYPE_ATTRIBUTE_SEALED)) {
4747 g_assert (!context_used);
4748 /* the remoting code is broken, access the class for now */
4749 if (0) {/*FIXME what exactly is broken? This change refers to r39380 from 2005 and mention some remoting fixes were due.*/
4750 MonoVTable *vt = mono_class_vtable (cfg->domain, klass);
4752 mono_cfg_set_exception (cfg, MONO_EXCEPTION_TYPE_LOAD);
4753 cfg->exception_ptr = klass;
4756 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, vtable_reg, vt);
4758 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4759 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, klass_reg, klass);
4761 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, false_bb);
4762 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, is_null_bb);
4764 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4765 /* the is_null_bb target simply copies the input register to the output */
4766 mini_emit_isninst_cast_inst (cfg, klass_reg, klass, klass_inst, false_bb, is_null_bb);
4771 MONO_START_BB (cfg, false_bb);
4773 MONO_EMIT_NEW_PCONST (cfg, res_reg, 0);
4774 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4776 MONO_START_BB (cfg, is_null_bb);
4778 MONO_START_BB (cfg, end_bb);
4784 handle_cisinst (MonoCompile *cfg, MonoClass *klass, MonoInst *src)
4786 /* This opcode takes as input an object reference and a class, and returns:
4787 0) if the object is an instance of the class,
4788 1) if the object is not instance of the class,
4789 2) if the object is a proxy whose type cannot be determined */
4792 #ifndef DISABLE_REMOTING
4793 MonoBasicBlock *true_bb, *false_bb, *false2_bb, *end_bb, *no_proxy_bb, *interface_fail_bb;
4795 MonoBasicBlock *true_bb, *false_bb, *end_bb;
4797 int obj_reg = src->dreg;
4798 int dreg = alloc_ireg (cfg);
4800 #ifndef DISABLE_REMOTING
4801 int klass_reg = alloc_preg (cfg);
4804 NEW_BBLOCK (cfg, true_bb);
4805 NEW_BBLOCK (cfg, false_bb);
4806 NEW_BBLOCK (cfg, end_bb);
4807 #ifndef DISABLE_REMOTING
4808 NEW_BBLOCK (cfg, false2_bb);
4809 NEW_BBLOCK (cfg, no_proxy_bb);
4812 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, obj_reg, 0);
4813 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, false_bb);
4815 if (klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
4816 #ifndef DISABLE_REMOTING
4817 NEW_BBLOCK (cfg, interface_fail_bb);
4820 tmp_reg = alloc_preg (cfg);
4821 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
4822 #ifndef DISABLE_REMOTING
4823 mini_emit_iface_cast (cfg, tmp_reg, klass, interface_fail_bb, true_bb);
4824 MONO_START_BB (cfg, interface_fail_bb);
4825 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, tmp_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4827 mini_emit_class_check_branch (cfg, klass_reg, mono_defaults.transparent_proxy_class, OP_PBNE_UN, false_bb);
4829 tmp_reg = alloc_preg (cfg);
4830 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoTransparentProxy, custom_type_info));
4831 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, tmp_reg, 0);
4832 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, false2_bb);
4834 mini_emit_iface_cast (cfg, tmp_reg, klass, false_bb, true_bb);
4837 #ifndef DISABLE_REMOTING
4838 tmp_reg = alloc_preg (cfg);
4839 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
4840 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, tmp_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4842 mini_emit_class_check_branch (cfg, klass_reg, mono_defaults.transparent_proxy_class, OP_PBNE_UN, no_proxy_bb);
4843 tmp_reg = alloc_preg (cfg);
4844 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoTransparentProxy, remote_class));
4845 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, tmp_reg, MONO_STRUCT_OFFSET (MonoRemoteClass, proxy_class));
4847 tmp_reg = alloc_preg (cfg);
4848 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoTransparentProxy, custom_type_info));
4849 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, tmp_reg, 0);
4850 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, no_proxy_bb);
4852 mini_emit_isninst_cast (cfg, klass_reg, klass, false2_bb, true_bb);
4853 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, false2_bb);
4855 MONO_START_BB (cfg, no_proxy_bb);
4857 mini_emit_isninst_cast (cfg, klass_reg, klass, false_bb, true_bb);
4859 g_error ("transparent proxy support is disabled while trying to JIT code that uses it");
4863 MONO_START_BB (cfg, false_bb);
4865 MONO_EMIT_NEW_ICONST (cfg, dreg, 1);
4866 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4868 #ifndef DISABLE_REMOTING
4869 MONO_START_BB (cfg, false2_bb);
4871 MONO_EMIT_NEW_ICONST (cfg, dreg, 2);
4872 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4875 MONO_START_BB (cfg, true_bb);
4877 MONO_EMIT_NEW_ICONST (cfg, dreg, 0);
4879 MONO_START_BB (cfg, end_bb);
4882 MONO_INST_NEW (cfg, ins, OP_ICONST);
4884 ins->type = STACK_I4;
4890 handle_ccastclass (MonoCompile *cfg, MonoClass *klass, MonoInst *src)
4892 /* This opcode takes as input an object reference and a class, and returns:
4893 0) if the object is an instance of the class,
4894 1) if the object is a proxy whose type cannot be determined
4895 an InvalidCastException exception is thrown otherwhise*/
4898 #ifndef DISABLE_REMOTING
4899 MonoBasicBlock *end_bb, *ok_result_bb, *no_proxy_bb, *interface_fail_bb, *fail_1_bb;
4901 MonoBasicBlock *ok_result_bb;
4903 int obj_reg = src->dreg;
4904 int dreg = alloc_ireg (cfg);
4905 int tmp_reg = alloc_preg (cfg);
4907 #ifndef DISABLE_REMOTING
4908 int klass_reg = alloc_preg (cfg);
4909 NEW_BBLOCK (cfg, end_bb);
4912 NEW_BBLOCK (cfg, ok_result_bb);
4914 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, obj_reg, 0);
4915 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, ok_result_bb);
4917 save_cast_details (cfg, klass, obj_reg, FALSE);
4919 if (klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
4920 #ifndef DISABLE_REMOTING
4921 NEW_BBLOCK (cfg, interface_fail_bb);
4923 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
4924 mini_emit_iface_cast (cfg, tmp_reg, klass, interface_fail_bb, ok_result_bb);
4925 MONO_START_BB (cfg, interface_fail_bb);
4926 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, tmp_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4928 mini_emit_class_check (cfg, klass_reg, mono_defaults.transparent_proxy_class);
4930 tmp_reg = alloc_preg (cfg);
4931 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoTransparentProxy, custom_type_info));
4932 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, tmp_reg, 0);
4933 MONO_EMIT_NEW_COND_EXC (cfg, EQ, "InvalidCastException");
4935 MONO_EMIT_NEW_ICONST (cfg, dreg, 1);
4936 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4938 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
4939 mini_emit_iface_cast (cfg, tmp_reg, klass, NULL, NULL);
4940 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, ok_result_bb);
4943 #ifndef DISABLE_REMOTING
4944 NEW_BBLOCK (cfg, no_proxy_bb);
4946 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
4947 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, tmp_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4948 mini_emit_class_check_branch (cfg, klass_reg, mono_defaults.transparent_proxy_class, OP_PBNE_UN, no_proxy_bb);
4950 tmp_reg = alloc_preg (cfg);
4951 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoTransparentProxy, remote_class));
4952 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, tmp_reg, MONO_STRUCT_OFFSET (MonoRemoteClass, proxy_class));
4954 tmp_reg = alloc_preg (cfg);
4955 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoTransparentProxy, custom_type_info));
4956 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, tmp_reg, 0);
4957 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, no_proxy_bb);
4959 NEW_BBLOCK (cfg, fail_1_bb);
4961 mini_emit_isninst_cast (cfg, klass_reg, klass, fail_1_bb, ok_result_bb);
4963 MONO_START_BB (cfg, fail_1_bb);
4965 MONO_EMIT_NEW_ICONST (cfg, dreg, 1);
4966 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4968 MONO_START_BB (cfg, no_proxy_bb);
4970 mini_emit_castclass (cfg, obj_reg, klass_reg, klass, ok_result_bb);
4972 g_error ("Transparent proxy support is disabled while trying to JIT code that uses it");
4976 MONO_START_BB (cfg, ok_result_bb);
4978 MONO_EMIT_NEW_ICONST (cfg, dreg, 0);
4980 #ifndef DISABLE_REMOTING
4981 MONO_START_BB (cfg, end_bb);
4985 MONO_INST_NEW (cfg, ins, OP_ICONST);
4987 ins->type = STACK_I4;
4992 static G_GNUC_UNUSED MonoInst*
4993 handle_enum_has_flag (MonoCompile *cfg, MonoClass *klass, MonoInst *enum_this, MonoInst *enum_flag)
4995 MonoType *enum_type = mono_type_get_underlying_type (&klass->byval_arg);
4996 guint32 load_opc = mono_type_to_load_membase (cfg, enum_type);
4999 switch (enum_type->type) {
5002 #if SIZEOF_REGISTER == 8
5014 MonoInst *load, *and_, *cmp, *ceq;
5015 int enum_reg = is_i4 ? alloc_ireg (cfg) : alloc_lreg (cfg);
5016 int and_reg = is_i4 ? alloc_ireg (cfg) : alloc_lreg (cfg);
5017 int dest_reg = alloc_ireg (cfg);
5019 EMIT_NEW_LOAD_MEMBASE (cfg, load, load_opc, enum_reg, enum_this->dreg, 0);
5020 EMIT_NEW_BIALU (cfg, and_, is_i4 ? OP_IAND : OP_LAND, and_reg, enum_reg, enum_flag->dreg);
5021 EMIT_NEW_BIALU (cfg, cmp, is_i4 ? OP_ICOMPARE : OP_LCOMPARE, -1, and_reg, enum_flag->dreg);
5022 EMIT_NEW_UNALU (cfg, ceq, is_i4 ? OP_ICEQ : OP_LCEQ, dest_reg, -1);
5024 ceq->type = STACK_I4;
5027 load = mono_decompose_opcode (cfg, load);
5028 and_ = mono_decompose_opcode (cfg, and_);
5029 cmp = mono_decompose_opcode (cfg, cmp);
5030 ceq = mono_decompose_opcode (cfg, ceq);
5038 * Returns NULL and set the cfg exception on error.
5040 static G_GNUC_UNUSED MonoInst*
5041 handle_delegate_ctor (MonoCompile *cfg, MonoClass *klass, MonoInst *target, MonoMethod *method, int context_used, gboolean virtual_)
5045 gpointer trampoline;
5046 MonoInst *obj, *method_ins, *tramp_ins;
5050 if (virtual_ && !cfg->llvm_only) {
5051 MonoMethod *invoke = mono_get_delegate_invoke (klass);
5054 if (!mono_get_delegate_virtual_invoke_impl (mono_method_signature (invoke), context_used ? NULL : method))
5058 obj = handle_alloc (cfg, klass, FALSE, mono_class_check_context_used (klass));
5062 if (cfg->llvm_only) {
5063 MonoInst *args [16];
5066 * If the method to be called needs an rgctx, we can't fall back to mono_delegate_ctor (), since it might receive
5067 * the address of a gshared method. So use a JIT icall.
5068 * FIXME: Optimize this.
5072 args [2] = emit_get_rgctx_method (cfg, context_used, method, MONO_RGCTX_INFO_METHOD);
5073 mono_emit_jit_icall (cfg, virtual_ ? mono_init_delegate_virtual : mono_init_delegate, args);
5078 /* Inline the contents of mono_delegate_ctor */
5080 /* Set target field */
5081 /* Optimize away setting of NULL target */
5082 if (!(target->opcode == OP_PCONST && target->inst_p0 == 0)) {
5083 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, target), target->dreg);
5084 if (cfg->gen_write_barriers) {
5085 dreg = alloc_preg (cfg);
5086 EMIT_NEW_BIALU_IMM (cfg, ptr, OP_PADD_IMM, dreg, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, target));
5087 emit_write_barrier (cfg, ptr, target);
5091 /* Set method field */
5092 method_ins = emit_get_rgctx_method (cfg, context_used, method, MONO_RGCTX_INFO_METHOD);
5093 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, method), method_ins->dreg);
5096 * To avoid looking up the compiled code belonging to the target method
5097 * in mono_delegate_trampoline (), we allocate a per-domain memory slot to
5098 * store it, and we fill it after the method has been compiled.
5100 if (!method->dynamic && !(cfg->opt & MONO_OPT_SHARED)) {
5101 MonoInst *code_slot_ins;
5104 code_slot_ins = emit_get_rgctx_method (cfg, context_used, method, MONO_RGCTX_INFO_METHOD_DELEGATE_CODE);
5106 domain = mono_domain_get ();
5107 mono_domain_lock (domain);
5108 if (!domain_jit_info (domain)->method_code_hash)
5109 domain_jit_info (domain)->method_code_hash = g_hash_table_new (NULL, NULL);
5110 code_slot = (guint8 **)g_hash_table_lookup (domain_jit_info (domain)->method_code_hash, method);
5112 code_slot = (guint8 **)mono_domain_alloc0 (domain, sizeof (gpointer));
5113 g_hash_table_insert (domain_jit_info (domain)->method_code_hash, method, code_slot);
5115 mono_domain_unlock (domain);
5117 code_slot_ins = emit_runtime_constant (cfg, MONO_PATCH_INFO_METHOD_CODE_SLOT, method);
5119 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, method_code), code_slot_ins->dreg);
5122 if (cfg->compile_aot) {
5123 MonoDelegateClassMethodPair *del_tramp;
5125 del_tramp = (MonoDelegateClassMethodPair *)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoDelegateClassMethodPair));
5126 del_tramp->klass = klass;
5127 del_tramp->method = context_used ? NULL : method;
5128 del_tramp->is_virtual = virtual_;
5129 EMIT_NEW_AOTCONST (cfg, tramp_ins, MONO_PATCH_INFO_DELEGATE_TRAMPOLINE, del_tramp);
5132 trampoline = mono_create_delegate_virtual_trampoline (cfg->domain, klass, context_used ? NULL : method);
5134 trampoline = mono_create_delegate_trampoline_info (cfg->domain, klass, context_used ? NULL : method);
5135 EMIT_NEW_PCONST (cfg, tramp_ins, trampoline);
5138 /* Set invoke_impl field */
5140 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, invoke_impl), tramp_ins->dreg);
5142 dreg = alloc_preg (cfg);
5143 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, dreg, tramp_ins->dreg, MONO_STRUCT_OFFSET (MonoDelegateTrampInfo, invoke_impl));
5144 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, invoke_impl), dreg);
5146 dreg = alloc_preg (cfg);
5147 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, dreg, tramp_ins->dreg, MONO_STRUCT_OFFSET (MonoDelegateTrampInfo, method_ptr));
5148 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, method_ptr), dreg);
5151 dreg = alloc_preg (cfg);
5152 MONO_EMIT_NEW_ICONST (cfg, dreg, virtual_ ? 1 : 0);
5153 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI1_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, method_is_virtual), dreg);
5155 /* All the checks which are in mono_delegate_ctor () are done by the delegate trampoline */
5161 handle_array_new (MonoCompile *cfg, int rank, MonoInst **sp, unsigned char *ip)
5163 MonoJitICallInfo *info;
5165 /* Need to register the icall so it gets an icall wrapper */
5166 info = mono_get_array_new_va_icall (rank);
5168 cfg->flags |= MONO_CFG_HAS_VARARGS;
5170 /* mono_array_new_va () needs a vararg calling convention */
5171 cfg->disable_llvm = TRUE;
5173 /* FIXME: This uses info->sig, but it should use the signature of the wrapper */
5174 return mono_emit_native_call (cfg, mono_icall_get_wrapper (info), info->sig, sp);
5178 * handle_constrained_gsharedvt_call:
5180 * Handle constrained calls where the receiver is a gsharedvt type.
5181 * Return the instruction representing the call. Set the cfg exception on failure.
5184 handle_constrained_gsharedvt_call (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **sp, MonoClass *constrained_class,
5185 gboolean *ref_emit_widen)
5187 MonoInst *ins = NULL;
5188 gboolean emit_widen = *ref_emit_widen;
5191 * Constrained calls need to behave differently at runtime dependending on whenever the receiver is instantiated as ref type or as a vtype.
5192 * 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
5193 * pack the arguments into an array, and do the rest of the work in in an icall.
5195 if (((cmethod->klass == mono_defaults.object_class) || (cmethod->klass->flags & TYPE_ATTRIBUTE_INTERFACE) || (!cmethod->klass->valuetype && cmethod->klass->image != mono_defaults.corlib)) &&
5196 (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)) &&
5197 (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]))))) {
5198 MonoInst *args [16];
5201 * This case handles calls to
5202 * - object:ToString()/Equals()/GetHashCode(),
5203 * - System.IComparable<T>:CompareTo()
5204 * - System.IEquatable<T>:Equals ()
5205 * plus some simple interface calls enough to support AsyncTaskMethodBuilder.
5209 if (mono_method_check_context_used (cmethod))
5210 args [1] = emit_get_rgctx_method (cfg, mono_method_check_context_used (cmethod), cmethod, MONO_RGCTX_INFO_METHOD);
5212 EMIT_NEW_METHODCONST (cfg, args [1], cmethod);
5213 args [2] = emit_get_rgctx_klass (cfg, mono_class_check_context_used (constrained_class), constrained_class, MONO_RGCTX_INFO_KLASS);
5215 /* !fsig->hasthis is for the wrapper for the Object.GetType () icall */
5216 if (fsig->hasthis && fsig->param_count) {
5217 /* Pass the arguments using a localloc-ed array using the format expected by runtime_invoke () */
5218 MONO_INST_NEW (cfg, ins, OP_LOCALLOC_IMM);
5219 ins->dreg = alloc_preg (cfg);
5220 ins->inst_imm = fsig->param_count * sizeof (mgreg_t);
5221 MONO_ADD_INS (cfg->cbb, ins);
5224 if (mini_is_gsharedvt_type (fsig->params [0])) {
5225 int addr_reg, deref_arg_reg;
5227 ins = emit_get_gsharedvt_info_klass (cfg, mono_class_from_mono_type (fsig->params [0]), MONO_RGCTX_INFO_CLASS_BOX_TYPE);
5228 deref_arg_reg = alloc_preg (cfg);
5229 /* deref_arg = BOX_TYPE != MONO_GSHAREDVT_BOX_TYPE_VTYPE */
5230 EMIT_NEW_BIALU_IMM (cfg, args [3], OP_ISUB_IMM, deref_arg_reg, ins->dreg, 1);
5232 EMIT_NEW_VARLOADA_VREG (cfg, ins, sp [1]->dreg, fsig->params [0]);
5233 addr_reg = ins->dreg;
5234 EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, args [4]->dreg, 0, addr_reg);
5236 EMIT_NEW_ICONST (cfg, args [3], 0);
5237 EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, args [4]->dreg, 0, sp [1]->dreg);
5240 EMIT_NEW_ICONST (cfg, args [3], 0);
5241 EMIT_NEW_ICONST (cfg, args [4], 0);
5243 ins = mono_emit_jit_icall (cfg, mono_gsharedvt_constrained_call, args);
5246 if (mini_is_gsharedvt_type (fsig->ret)) {
5247 ins = handle_unbox_gsharedvt (cfg, mono_class_from_mono_type (fsig->ret), ins);
5248 } else if (MONO_TYPE_IS_PRIMITIVE (fsig->ret) || MONO_TYPE_ISSTRUCT (fsig->ret)) {
5252 NEW_BIALU_IMM (cfg, add, OP_ADD_IMM, alloc_dreg (cfg, STACK_MP), ins->dreg, sizeof (MonoObject));
5253 MONO_ADD_INS (cfg->cbb, add);
5255 NEW_LOAD_MEMBASE_TYPE (cfg, ins, fsig->ret, add->dreg, 0);
5256 MONO_ADD_INS (cfg->cbb, ins);
5257 /* ins represents the call result */
5260 GSHAREDVT_FAILURE (CEE_CALLVIRT);
5263 *ref_emit_widen = emit_widen;
5272 mono_emit_load_got_addr (MonoCompile *cfg)
5274 MonoInst *getaddr, *dummy_use;
5276 if (!cfg->got_var || cfg->got_var_allocated)
5279 MONO_INST_NEW (cfg, getaddr, OP_LOAD_GOTADDR);
5280 getaddr->cil_code = cfg->header->code;
5281 getaddr->dreg = cfg->got_var->dreg;
5283 /* Add it to the start of the first bblock */
5284 if (cfg->bb_entry->code) {
5285 getaddr->next = cfg->bb_entry->code;
5286 cfg->bb_entry->code = getaddr;
5289 MONO_ADD_INS (cfg->bb_entry, getaddr);
5291 cfg->got_var_allocated = TRUE;
5294 * Add a dummy use to keep the got_var alive, since real uses might
5295 * only be generated by the back ends.
5296 * Add it to end_bblock, so the variable's lifetime covers the whole
5298 * It would be better to make the usage of the got var explicit in all
5299 * cases when the backend needs it (i.e. calls, throw etc.), so this
5300 * wouldn't be needed.
5302 NEW_DUMMY_USE (cfg, dummy_use, cfg->got_var);
5303 MONO_ADD_INS (cfg->bb_exit, dummy_use);
5306 static int inline_limit;
5307 static gboolean inline_limit_inited;
5310 mono_method_check_inlining (MonoCompile *cfg, MonoMethod *method)
5312 MonoMethodHeaderSummary header;
5314 #ifdef MONO_ARCH_SOFT_FLOAT_FALLBACK
5315 MonoMethodSignature *sig = mono_method_signature (method);
5319 if (cfg->disable_inline)
5324 if (cfg->inline_depth > 10)
5327 if (!mono_method_get_header_summary (method, &header))
5330 /*runtime, icall and pinvoke are checked by summary call*/
5331 if ((method->iflags & METHOD_IMPL_ATTRIBUTE_NOINLINING) ||
5332 (method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED) ||
5333 (mono_class_is_marshalbyref (method->klass)) ||
5337 /* also consider num_locals? */
5338 /* Do the size check early to avoid creating vtables */
5339 if (!inline_limit_inited) {
5340 if (g_getenv ("MONO_INLINELIMIT"))
5341 inline_limit = atoi (g_getenv ("MONO_INLINELIMIT"));
5343 inline_limit = INLINE_LENGTH_LIMIT;
5344 inline_limit_inited = TRUE;
5346 if (header.code_size >= inline_limit && !(method->iflags & METHOD_IMPL_ATTRIBUTE_AGGRESSIVE_INLINING))
5350 * if we can initialize the class of the method right away, we do,
5351 * otherwise we don't allow inlining if the class needs initialization,
5352 * since it would mean inserting a call to mono_runtime_class_init()
5353 * inside the inlined code
5355 if (!(cfg->opt & MONO_OPT_SHARED)) {
5356 /* The AggressiveInlining hint is a good excuse to force that cctor to run. */
5357 if (method->iflags & METHOD_IMPL_ATTRIBUTE_AGGRESSIVE_INLINING) {
5358 vtable = mono_class_vtable (cfg->domain, method->klass);
5361 if (!cfg->compile_aot)
5362 mono_runtime_class_init (vtable);
5363 } else if (method->klass->flags & TYPE_ATTRIBUTE_BEFORE_FIELD_INIT) {
5364 if (cfg->run_cctors && method->klass->has_cctor) {
5365 /*FIXME it would easier and lazier to just use mono_class_try_get_vtable */
5366 if (!method->klass->runtime_info)
5367 /* No vtable created yet */
5369 vtable = mono_class_vtable (cfg->domain, method->klass);
5372 /* This makes so that inline cannot trigger */
5373 /* .cctors: too many apps depend on them */
5374 /* running with a specific order... */
5375 if (! vtable->initialized)
5377 mono_runtime_class_init (vtable);
5379 } else if (mono_class_needs_cctor_run (method->klass, NULL)) {
5380 if (!method->klass->runtime_info)
5381 /* No vtable created yet */
5383 vtable = mono_class_vtable (cfg->domain, method->klass);
5386 if (!vtable->initialized)
5391 * If we're compiling for shared code
5392 * the cctor will need to be run at aot method load time, for example,
5393 * or at the end of the compilation of the inlining method.
5395 if (mono_class_needs_cctor_run (method->klass, NULL) && !((method->klass->flags & TYPE_ATTRIBUTE_BEFORE_FIELD_INIT)))
5399 #ifdef MONO_ARCH_SOFT_FLOAT_FALLBACK
5400 if (mono_arch_is_soft_float ()) {
5402 if (sig->ret && sig->ret->type == MONO_TYPE_R4)
5404 for (i = 0; i < sig->param_count; ++i)
5405 if (!sig->params [i]->byref && sig->params [i]->type == MONO_TYPE_R4)
5410 if (g_list_find (cfg->dont_inline, method))
5417 mini_field_access_needs_cctor_run (MonoCompile *cfg, MonoMethod *method, MonoClass *klass, MonoVTable *vtable)
5419 if (!cfg->compile_aot) {
5421 if (vtable->initialized)
5425 if (klass->flags & TYPE_ATTRIBUTE_BEFORE_FIELD_INIT) {
5426 if (cfg->method == method)
5430 if (!mono_class_needs_cctor_run (klass, method))
5433 if (! (method->flags & METHOD_ATTRIBUTE_STATIC) && (klass == method->klass))
5434 /* The initialization is already done before the method is called */
5441 mini_emit_ldelema_1_ins (MonoCompile *cfg, MonoClass *klass, MonoInst *arr, MonoInst *index, gboolean bcheck)
5445 int mult_reg, add_reg, array_reg, index_reg, index2_reg;
5448 if (mini_is_gsharedvt_variable_klass (klass)) {
5451 mono_class_init (klass);
5452 size = mono_class_array_element_size (klass);
5455 mult_reg = alloc_preg (cfg);
5456 array_reg = arr->dreg;
5457 index_reg = index->dreg;
5459 #if SIZEOF_REGISTER == 8
5460 /* The array reg is 64 bits but the index reg is only 32 */
5461 if (COMPILE_LLVM (cfg)) {
5463 index2_reg = index_reg;
5465 index2_reg = alloc_preg (cfg);
5466 MONO_EMIT_NEW_UNALU (cfg, OP_SEXT_I4, index2_reg, index_reg);
5469 if (index->type == STACK_I8) {
5470 index2_reg = alloc_preg (cfg);
5471 MONO_EMIT_NEW_UNALU (cfg, OP_LCONV_TO_I4, index2_reg, index_reg);
5473 index2_reg = index_reg;
5478 MONO_EMIT_BOUNDS_CHECK (cfg, array_reg, MonoArray, max_length, index2_reg);
5480 #if defined(TARGET_X86) || defined(TARGET_AMD64)
5481 if (size == 1 || size == 2 || size == 4 || size == 8) {
5482 static const int fast_log2 [] = { 1, 0, 1, -1, 2, -1, -1, -1, 3 };
5484 EMIT_NEW_X86_LEA (cfg, ins, array_reg, index2_reg, fast_log2 [size], MONO_STRUCT_OFFSET (MonoArray, vector));
5485 ins->klass = mono_class_get_element_class (klass);
5486 ins->type = STACK_MP;
5492 add_reg = alloc_ireg_mp (cfg);
5495 MonoInst *rgctx_ins;
5498 g_assert (cfg->gshared);
5499 context_used = mini_class_check_context_used (cfg, klass);
5500 g_assert (context_used);
5501 rgctx_ins = emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_ARRAY_ELEMENT_SIZE);
5502 MONO_EMIT_NEW_BIALU (cfg, OP_IMUL, mult_reg, index2_reg, rgctx_ins->dreg);
5504 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_MUL_IMM, mult_reg, index2_reg, size);
5506 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, add_reg, array_reg, mult_reg);
5507 NEW_BIALU_IMM (cfg, ins, OP_PADD_IMM, add_reg, add_reg, MONO_STRUCT_OFFSET (MonoArray, vector));
5508 ins->klass = mono_class_get_element_class (klass);
5509 ins->type = STACK_MP;
5510 MONO_ADD_INS (cfg->cbb, ins);
5516 mini_emit_ldelema_2_ins (MonoCompile *cfg, MonoClass *klass, MonoInst *arr, MonoInst *index_ins1, MonoInst *index_ins2)
5518 int bounds_reg = alloc_preg (cfg);
5519 int add_reg = alloc_ireg_mp (cfg);
5520 int mult_reg = alloc_preg (cfg);
5521 int mult2_reg = alloc_preg (cfg);
5522 int low1_reg = alloc_preg (cfg);
5523 int low2_reg = alloc_preg (cfg);
5524 int high1_reg = alloc_preg (cfg);
5525 int high2_reg = alloc_preg (cfg);
5526 int realidx1_reg = alloc_preg (cfg);
5527 int realidx2_reg = alloc_preg (cfg);
5528 int sum_reg = alloc_preg (cfg);
5529 int index1, index2, tmpreg;
5533 mono_class_init (klass);
5534 size = mono_class_array_element_size (klass);
5536 index1 = index_ins1->dreg;
5537 index2 = index_ins2->dreg;
5539 #if SIZEOF_REGISTER == 8
5540 /* The array reg is 64 bits but the index reg is only 32 */
5541 if (COMPILE_LLVM (cfg)) {
5544 tmpreg = alloc_preg (cfg);
5545 MONO_EMIT_NEW_UNALU (cfg, OP_SEXT_I4, tmpreg, index1);
5547 tmpreg = alloc_preg (cfg);
5548 MONO_EMIT_NEW_UNALU (cfg, OP_SEXT_I4, tmpreg, index2);
5552 // FIXME: Do we need to do something here for i8 indexes, like in ldelema_1_ins ?
5556 /* range checking */
5557 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, bounds_reg,
5558 arr->dreg, MONO_STRUCT_OFFSET (MonoArray, bounds));
5560 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI4_MEMBASE, low1_reg,
5561 bounds_reg, MONO_STRUCT_OFFSET (MonoArrayBounds, lower_bound));
5562 MONO_EMIT_NEW_BIALU (cfg, OP_PSUB, realidx1_reg, index1, low1_reg);
5563 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI4_MEMBASE, high1_reg,
5564 bounds_reg, MONO_STRUCT_OFFSET (MonoArrayBounds, length));
5565 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, high1_reg, realidx1_reg);
5566 MONO_EMIT_NEW_COND_EXC (cfg, LE_UN, "IndexOutOfRangeException");
5568 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI4_MEMBASE, low2_reg,
5569 bounds_reg, sizeof (MonoArrayBounds) + MONO_STRUCT_OFFSET (MonoArrayBounds, lower_bound));
5570 MONO_EMIT_NEW_BIALU (cfg, OP_PSUB, realidx2_reg, index2, low2_reg);
5571 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI4_MEMBASE, high2_reg,
5572 bounds_reg, sizeof (MonoArrayBounds) + MONO_STRUCT_OFFSET (MonoArrayBounds, length));
5573 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, high2_reg, realidx2_reg);
5574 MONO_EMIT_NEW_COND_EXC (cfg, LE_UN, "IndexOutOfRangeException");
5576 MONO_EMIT_NEW_BIALU (cfg, OP_PMUL, mult_reg, high2_reg, realidx1_reg);
5577 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, sum_reg, mult_reg, realidx2_reg);
5578 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_PMUL_IMM, mult2_reg, sum_reg, size);
5579 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, add_reg, mult2_reg, arr->dreg);
5580 NEW_BIALU_IMM (cfg, ins, OP_PADD_IMM, add_reg, add_reg, MONO_STRUCT_OFFSET (MonoArray, vector));
5582 ins->type = STACK_MP;
5584 MONO_ADD_INS (cfg->cbb, ins);
5590 mini_emit_ldelema_ins (MonoCompile *cfg, MonoMethod *cmethod, MonoInst **sp, unsigned char *ip, gboolean is_set)
5594 MonoMethod *addr_method;
5596 MonoClass *eclass = cmethod->klass->element_class;
5598 rank = mono_method_signature (cmethod)->param_count - (is_set? 1: 0);
5601 return mini_emit_ldelema_1_ins (cfg, eclass, sp [0], sp [1], TRUE);
5603 /* emit_ldelema_2 depends on OP_LMUL */
5604 if (!cfg->backend->emulate_mul_div && rank == 2 && (cfg->opt & MONO_OPT_INTRINS) && !mini_is_gsharedvt_variable_klass (eclass)) {
5605 return mini_emit_ldelema_2_ins (cfg, eclass, sp [0], sp [1], sp [2]);
5608 if (mini_is_gsharedvt_variable_klass (eclass))
5611 element_size = mono_class_array_element_size (eclass);
5612 addr_method = mono_marshal_get_array_address (rank, element_size);
5613 addr = mono_emit_method_call (cfg, addr_method, sp, NULL);
5618 static MonoBreakPolicy
5619 always_insert_breakpoint (MonoMethod *method)
5621 return MONO_BREAK_POLICY_ALWAYS;
5624 static MonoBreakPolicyFunc break_policy_func = always_insert_breakpoint;
5627 * mono_set_break_policy:
5628 * policy_callback: the new callback function
5630 * Allow embedders to decide wherther to actually obey breakpoint instructions
5631 * (both break IL instructions and Debugger.Break () method calls), for example
5632 * to not allow an app to be aborted by a perfectly valid IL opcode when executing
5633 * untrusted or semi-trusted code.
5635 * @policy_callback will be called every time a break point instruction needs to
5636 * be inserted with the method argument being the method that calls Debugger.Break()
5637 * or has the IL break instruction. The callback should return #MONO_BREAK_POLICY_NEVER
5638 * if it wants the breakpoint to not be effective in the given method.
5639 * #MONO_BREAK_POLICY_ALWAYS is the default.
5642 mono_set_break_policy (MonoBreakPolicyFunc policy_callback)
5644 if (policy_callback)
5645 break_policy_func = policy_callback;
5647 break_policy_func = always_insert_breakpoint;
5651 should_insert_brekpoint (MonoMethod *method) {
5652 switch (break_policy_func (method)) {
5653 case MONO_BREAK_POLICY_ALWAYS:
5655 case MONO_BREAK_POLICY_NEVER:
5657 case MONO_BREAK_POLICY_ON_DBG:
5658 g_warning ("mdb no longer supported");
5661 g_warning ("Incorrect value returned from break policy callback");
5666 /* optimize the simple GetGenericValueImpl/SetGenericValueImpl generic icalls */
5668 emit_array_generic_access (MonoCompile *cfg, MonoMethodSignature *fsig, MonoInst **args, int is_set)
5670 MonoInst *addr, *store, *load;
5671 MonoClass *eklass = mono_class_from_mono_type (fsig->params [2]);
5673 /* the bounds check is already done by the callers */
5674 addr = mini_emit_ldelema_1_ins (cfg, eklass, args [0], args [1], FALSE);
5676 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, load, &eklass->byval_arg, args [2]->dreg, 0);
5677 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, store, &eklass->byval_arg, addr->dreg, 0, load->dreg);
5678 if (mini_type_is_reference (fsig->params [2]))
5679 emit_write_barrier (cfg, addr, load);
5681 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, load, &eklass->byval_arg, addr->dreg, 0);
5682 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, store, &eklass->byval_arg, args [2]->dreg, 0, load->dreg);
5689 generic_class_is_reference_type (MonoCompile *cfg, MonoClass *klass)
5691 return mini_type_is_reference (&klass->byval_arg);
5695 emit_array_store (MonoCompile *cfg, MonoClass *klass, MonoInst **sp, gboolean safety_checks)
5697 if (safety_checks && generic_class_is_reference_type (cfg, klass) &&
5698 !(sp [2]->opcode == OP_PCONST && sp [2]->inst_p0 == NULL)) {
5699 MonoClass *obj_array = mono_array_class_get_cached (mono_defaults.object_class, 1);
5700 MonoMethod *helper = mono_marshal_get_virtual_stelemref (obj_array);
5701 MonoInst *iargs [3];
5704 mono_class_setup_vtable (obj_array);
5705 g_assert (helper->slot);
5707 if (sp [0]->type != STACK_OBJ)
5709 if (sp [2]->type != STACK_OBJ)
5716 return mono_emit_method_call (cfg, helper, iargs, sp [0]);
5720 if (mini_is_gsharedvt_variable_klass (klass)) {
5723 // FIXME-VT: OP_ICONST optimization
5724 addr = mini_emit_ldelema_1_ins (cfg, klass, sp [0], sp [1], TRUE);
5725 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, addr->dreg, 0, sp [2]->dreg);
5726 ins->opcode = OP_STOREV_MEMBASE;
5727 } else if (sp [1]->opcode == OP_ICONST) {
5728 int array_reg = sp [0]->dreg;
5729 int index_reg = sp [1]->dreg;
5730 int offset = (mono_class_array_element_size (klass) * sp [1]->inst_c0) + MONO_STRUCT_OFFSET (MonoArray, vector);
5732 if (SIZEOF_REGISTER == 8 && COMPILE_LLVM (cfg))
5733 MONO_EMIT_NEW_UNALU (cfg, OP_ZEXT_I4, index_reg, index_reg);
5736 MONO_EMIT_BOUNDS_CHECK (cfg, array_reg, MonoArray, max_length, index_reg);
5737 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, array_reg, offset, sp [2]->dreg);
5739 MonoInst *addr = mini_emit_ldelema_1_ins (cfg, klass, sp [0], sp [1], safety_checks);
5740 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, addr->dreg, 0, sp [2]->dreg);
5741 if (generic_class_is_reference_type (cfg, klass))
5742 emit_write_barrier (cfg, addr, sp [2]);
5749 emit_array_unsafe_access (MonoCompile *cfg, MonoMethodSignature *fsig, MonoInst **args, int is_set)
5754 eklass = mono_class_from_mono_type (fsig->params [2]);
5756 eklass = mono_class_from_mono_type (fsig->ret);
5759 return emit_array_store (cfg, eklass, args, FALSE);
5761 MonoInst *ins, *addr = mini_emit_ldelema_1_ins (cfg, eklass, args [0], args [1], FALSE);
5762 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &eklass->byval_arg, addr->dreg, 0);
5768 is_unsafe_mov_compatible (MonoCompile *cfg, MonoClass *param_klass, MonoClass *return_klass)
5771 int param_size, return_size;
5773 param_klass = mono_class_from_mono_type (mini_get_underlying_type (¶m_klass->byval_arg));
5774 return_klass = mono_class_from_mono_type (mini_get_underlying_type (&return_klass->byval_arg));
5776 if (cfg->verbose_level > 3)
5777 printf ("[UNSAFE-MOV-INTRISIC] %s <- %s\n", return_klass->name, param_klass->name);
5779 //Don't allow mixing reference types with value types
5780 if (param_klass->valuetype != return_klass->valuetype) {
5781 if (cfg->verbose_level > 3)
5782 printf ("[UNSAFE-MOV-INTRISIC]\tone of the args is a valuetype and the other is not\n");
5786 if (!param_klass->valuetype) {
5787 if (cfg->verbose_level > 3)
5788 printf ("[UNSAFE-MOV-INTRISIC]\targs are reference types\n");
5793 if (param_klass->has_references || return_klass->has_references)
5796 /* Avoid mixing structs and primitive types/enums, they need to be handled differently in the JIT */
5797 if ((MONO_TYPE_ISSTRUCT (¶m_klass->byval_arg) && !MONO_TYPE_ISSTRUCT (&return_klass->byval_arg)) ||
5798 (!MONO_TYPE_ISSTRUCT (¶m_klass->byval_arg) && MONO_TYPE_ISSTRUCT (&return_klass->byval_arg))) {
5799 if (cfg->verbose_level > 3)
5800 printf ("[UNSAFE-MOV-INTRISIC]\tmixing structs and scalars\n");
5804 if (param_klass->byval_arg.type == MONO_TYPE_R4 || param_klass->byval_arg.type == MONO_TYPE_R8 ||
5805 return_klass->byval_arg.type == MONO_TYPE_R4 || return_klass->byval_arg.type == MONO_TYPE_R8) {
5806 if (cfg->verbose_level > 3)
5807 printf ("[UNSAFE-MOV-INTRISIC]\tfloat or double are not supported\n");
5811 param_size = mono_class_value_size (param_klass, &align);
5812 return_size = mono_class_value_size (return_klass, &align);
5814 //We can do it if sizes match
5815 if (param_size == return_size) {
5816 if (cfg->verbose_level > 3)
5817 printf ("[UNSAFE-MOV-INTRISIC]\tsame size\n");
5821 //No simple way to handle struct if sizes don't match
5822 if (MONO_TYPE_ISSTRUCT (¶m_klass->byval_arg)) {
5823 if (cfg->verbose_level > 3)
5824 printf ("[UNSAFE-MOV-INTRISIC]\tsize mismatch and type is a struct\n");
5829 * Same reg size category.
5830 * A quick note on why we don't require widening here.
5831 * The intrinsic is "R Array.UnsafeMov<S,R> (S s)".
5833 * Since the source value comes from a function argument, the JIT will already have
5834 * the value in a VREG and performed any widening needed before (say, when loading from a field).
5836 if (param_size <= 4 && return_size <= 4) {
5837 if (cfg->verbose_level > 3)
5838 printf ("[UNSAFE-MOV-INTRISIC]\tsize mismatch but both are of the same reg class\n");
5846 emit_array_unsafe_mov (MonoCompile *cfg, MonoMethodSignature *fsig, MonoInst **args)
5848 MonoClass *param_klass = mono_class_from_mono_type (fsig->params [0]);
5849 MonoClass *return_klass = mono_class_from_mono_type (fsig->ret);
5851 if (mini_is_gsharedvt_variable_type (fsig->ret))
5854 //Valuetypes that are semantically equivalent or numbers than can be widened to
5855 if (is_unsafe_mov_compatible (cfg, param_klass, return_klass))
5858 //Arrays of valuetypes that are semantically equivalent
5859 if (param_klass->rank == 1 && return_klass->rank == 1 && is_unsafe_mov_compatible (cfg, param_klass->element_class, return_klass->element_class))
5866 mini_emit_inst_for_ctor (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
5868 #ifdef MONO_ARCH_SIMD_INTRINSICS
5869 MonoInst *ins = NULL;
5871 if (cfg->opt & MONO_OPT_SIMD) {
5872 ins = mono_emit_simd_intrinsics (cfg, cmethod, fsig, args);
5878 return mono_emit_native_types_intrinsics (cfg, cmethod, fsig, args);
5882 emit_memory_barrier (MonoCompile *cfg, int kind)
5884 MonoInst *ins = NULL;
5885 MONO_INST_NEW (cfg, ins, OP_MEMORY_BARRIER);
5886 MONO_ADD_INS (cfg->cbb, ins);
5887 ins->backend.memory_barrier_kind = kind;
5893 llvm_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
5895 MonoInst *ins = NULL;
5898 /* The LLVM backend supports these intrinsics */
5899 if (cmethod->klass == mono_defaults.math_class) {
5900 if (strcmp (cmethod->name, "Sin") == 0) {
5902 } else if (strcmp (cmethod->name, "Cos") == 0) {
5904 } else if (strcmp (cmethod->name, "Sqrt") == 0) {
5906 } else if (strcmp (cmethod->name, "Abs") == 0 && fsig->params [0]->type == MONO_TYPE_R8) {
5910 if (opcode && fsig->param_count == 1) {
5911 MONO_INST_NEW (cfg, ins, opcode);
5912 ins->type = STACK_R8;
5913 ins->dreg = mono_alloc_freg (cfg);
5914 ins->sreg1 = args [0]->dreg;
5915 MONO_ADD_INS (cfg->cbb, ins);
5919 if (cfg->opt & MONO_OPT_CMOV) {
5920 if (strcmp (cmethod->name, "Min") == 0) {
5921 if (fsig->params [0]->type == MONO_TYPE_I4)
5923 if (fsig->params [0]->type == MONO_TYPE_U4)
5924 opcode = OP_IMIN_UN;
5925 else if (fsig->params [0]->type == MONO_TYPE_I8)
5927 else if (fsig->params [0]->type == MONO_TYPE_U8)
5928 opcode = OP_LMIN_UN;
5929 } else if (strcmp (cmethod->name, "Max") == 0) {
5930 if (fsig->params [0]->type == MONO_TYPE_I4)
5932 if (fsig->params [0]->type == MONO_TYPE_U4)
5933 opcode = OP_IMAX_UN;
5934 else if (fsig->params [0]->type == MONO_TYPE_I8)
5936 else if (fsig->params [0]->type == MONO_TYPE_U8)
5937 opcode = OP_LMAX_UN;
5941 if (opcode && fsig->param_count == 2) {
5942 MONO_INST_NEW (cfg, ins, opcode);
5943 ins->type = fsig->params [0]->type == MONO_TYPE_I4 ? STACK_I4 : STACK_I8;
5944 ins->dreg = mono_alloc_ireg (cfg);
5945 ins->sreg1 = args [0]->dreg;
5946 ins->sreg2 = args [1]->dreg;
5947 MONO_ADD_INS (cfg->cbb, ins);
5955 mini_emit_inst_for_sharable_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
5957 if (cmethod->klass == mono_defaults.array_class) {
5958 if (strcmp (cmethod->name, "UnsafeStore") == 0)
5959 return emit_array_unsafe_access (cfg, fsig, args, TRUE);
5960 else if (strcmp (cmethod->name, "UnsafeLoad") == 0)
5961 return emit_array_unsafe_access (cfg, fsig, args, FALSE);
5962 else if (strcmp (cmethod->name, "UnsafeMov") == 0)
5963 return emit_array_unsafe_mov (cfg, fsig, args);
5970 mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
5972 MonoInst *ins = NULL;
5974 static MonoClass *runtime_helpers_class = NULL;
5975 if (! runtime_helpers_class)
5976 runtime_helpers_class = mono_class_from_name (mono_defaults.corlib,
5977 "System.Runtime.CompilerServices", "RuntimeHelpers");
5979 if (cmethod->klass == mono_defaults.string_class) {
5980 if (strcmp (cmethod->name, "get_Chars") == 0 && fsig->param_count + fsig->hasthis == 2) {
5981 int dreg = alloc_ireg (cfg);
5982 int index_reg = alloc_preg (cfg);
5983 int add_reg = alloc_preg (cfg);
5985 #if SIZEOF_REGISTER == 8
5986 if (COMPILE_LLVM (cfg)) {
5987 MONO_EMIT_NEW_UNALU (cfg, OP_ZEXT_I4, index_reg, args [1]->dreg);
5989 /* The array reg is 64 bits but the index reg is only 32 */
5990 MONO_EMIT_NEW_UNALU (cfg, OP_SEXT_I4, index_reg, args [1]->dreg);
5993 index_reg = args [1]->dreg;
5995 MONO_EMIT_BOUNDS_CHECK (cfg, args [0]->dreg, MonoString, length, index_reg);
5997 #if defined(TARGET_X86) || defined(TARGET_AMD64)
5998 EMIT_NEW_X86_LEA (cfg, ins, args [0]->dreg, index_reg, 1, MONO_STRUCT_OFFSET (MonoString, chars));
5999 add_reg = ins->dreg;
6000 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADU2_MEMBASE, dreg,
6003 int mult_reg = alloc_preg (cfg);
6004 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SHL_IMM, mult_reg, index_reg, 1);
6005 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, add_reg, mult_reg, args [0]->dreg);
6006 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADU2_MEMBASE, dreg,
6007 add_reg, MONO_STRUCT_OFFSET (MonoString, chars));
6009 type_from_op (cfg, ins, NULL, NULL);
6011 } else if (strcmp (cmethod->name, "get_Length") == 0 && fsig->param_count + fsig->hasthis == 1) {
6012 int dreg = alloc_ireg (cfg);
6013 /* Decompose later to allow more optimizations */
6014 EMIT_NEW_UNALU (cfg, ins, OP_STRLEN, dreg, args [0]->dreg);
6015 ins->type = STACK_I4;
6016 ins->flags |= MONO_INST_FAULT;
6017 cfg->cbb->has_array_access = TRUE;
6018 cfg->flags |= MONO_CFG_HAS_ARRAY_ACCESS;
6023 } else if (cmethod->klass == mono_defaults.object_class) {
6025 if (strcmp (cmethod->name, "GetType") == 0 && fsig->param_count + fsig->hasthis == 1) {
6026 int dreg = alloc_ireg_ref (cfg);
6027 int vt_reg = alloc_preg (cfg);
6028 MONO_EMIT_NEW_LOAD_MEMBASE_FAULT (cfg, vt_reg, args [0]->dreg, MONO_STRUCT_OFFSET (MonoObject, vtable));
6029 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, dreg, vt_reg, MONO_STRUCT_OFFSET (MonoVTable, type));
6030 type_from_op (cfg, ins, NULL, NULL);
6033 } else if (!cfg->backend->emulate_mul_div && strcmp (cmethod->name, "InternalGetHashCode") == 0 && fsig->param_count == 1 && !mono_gc_is_moving ()) {
6034 int dreg = alloc_ireg (cfg);
6035 int t1 = alloc_ireg (cfg);
6037 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SHL_IMM, t1, args [0]->dreg, 3);
6038 EMIT_NEW_BIALU_IMM (cfg, ins, OP_MUL_IMM, dreg, t1, 2654435761u);
6039 ins->type = STACK_I4;
6042 } else if (strcmp (cmethod->name, ".ctor") == 0 && fsig->param_count == 0) {
6043 MONO_INST_NEW (cfg, ins, OP_NOP);
6044 MONO_ADD_INS (cfg->cbb, ins);
6048 } else if (cmethod->klass == mono_defaults.array_class) {
6049 if (strcmp (cmethod->name, "GetGenericValueImpl") == 0 && fsig->param_count + fsig->hasthis == 3 && !cfg->gsharedvt)
6050 return emit_array_generic_access (cfg, fsig, args, FALSE);
6051 else if (strcmp (cmethod->name, "SetGenericValueImpl") == 0 && fsig->param_count + fsig->hasthis == 3 && !cfg->gsharedvt)
6052 return emit_array_generic_access (cfg, fsig, args, TRUE);
6054 #ifndef MONO_BIG_ARRAYS
6056 * This is an inline version of GetLength/GetLowerBound(0) used frequently in
6059 else if (((strcmp (cmethod->name, "GetLength") == 0 && fsig->param_count + fsig->hasthis == 2) ||
6060 (strcmp (cmethod->name, "GetLowerBound") == 0 && fsig->param_count + fsig->hasthis == 2)) &&
6061 args [1]->opcode == OP_ICONST && args [1]->inst_c0 == 0) {
6062 int dreg = alloc_ireg (cfg);
6063 int bounds_reg = alloc_ireg_mp (cfg);
6064 MonoBasicBlock *end_bb, *szarray_bb;
6065 gboolean get_length = strcmp (cmethod->name, "GetLength") == 0;
6067 NEW_BBLOCK (cfg, end_bb);
6068 NEW_BBLOCK (cfg, szarray_bb);
6070 EMIT_NEW_LOAD_MEMBASE_FAULT (cfg, ins, OP_LOAD_MEMBASE, bounds_reg,
6071 args [0]->dreg, MONO_STRUCT_OFFSET (MonoArray, bounds));
6072 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, bounds_reg, 0);
6073 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, szarray_bb);
6074 /* Non-szarray case */
6076 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADI4_MEMBASE, dreg,
6077 bounds_reg, MONO_STRUCT_OFFSET (MonoArrayBounds, length));
6079 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADI4_MEMBASE, dreg,
6080 bounds_reg, MONO_STRUCT_OFFSET (MonoArrayBounds, lower_bound));
6081 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
6082 MONO_START_BB (cfg, szarray_bb);
6085 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADI4_MEMBASE, dreg,
6086 args [0]->dreg, MONO_STRUCT_OFFSET (MonoArray, max_length));
6088 MONO_EMIT_NEW_ICONST (cfg, dreg, 0);
6089 MONO_START_BB (cfg, end_bb);
6091 EMIT_NEW_UNALU (cfg, ins, OP_MOVE, dreg, dreg);
6092 ins->type = STACK_I4;
6098 if (cmethod->name [0] != 'g')
6101 if (strcmp (cmethod->name, "get_Rank") == 0 && fsig->param_count + fsig->hasthis == 1) {
6102 int dreg = alloc_ireg (cfg);
6103 int vtable_reg = alloc_preg (cfg);
6104 MONO_EMIT_NEW_LOAD_MEMBASE_OP_FAULT (cfg, OP_LOAD_MEMBASE, vtable_reg,
6105 args [0]->dreg, MONO_STRUCT_OFFSET (MonoObject, vtable));
6106 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADU1_MEMBASE, dreg,
6107 vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, rank));
6108 type_from_op (cfg, ins, NULL, NULL);
6111 } else if (strcmp (cmethod->name, "get_Length") == 0 && fsig->param_count + fsig->hasthis == 1) {
6112 int dreg = alloc_ireg (cfg);
6114 EMIT_NEW_LOAD_MEMBASE_FAULT (cfg, ins, OP_LOADI4_MEMBASE, dreg,
6115 args [0]->dreg, MONO_STRUCT_OFFSET (MonoArray, max_length));
6116 type_from_op (cfg, ins, NULL, NULL);
6121 } else if (cmethod->klass == runtime_helpers_class) {
6123 if (strcmp (cmethod->name, "get_OffsetToStringData") == 0 && fsig->param_count == 0) {
6124 EMIT_NEW_ICONST (cfg, ins, MONO_STRUCT_OFFSET (MonoString, chars));
6128 } else if (cmethod->klass == mono_defaults.thread_class) {
6129 if (strcmp (cmethod->name, "SpinWait_nop") == 0 && fsig->param_count == 0) {
6130 MONO_INST_NEW (cfg, ins, OP_RELAXED_NOP);
6131 MONO_ADD_INS (cfg->cbb, ins);
6133 } else if (strcmp (cmethod->name, "MemoryBarrier") == 0 && fsig->param_count == 0) {
6134 return emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_SEQ);
6135 } else if (!strcmp (cmethod->name, "VolatileRead") && fsig->param_count == 1) {
6137 gboolean is_ref = mini_type_is_reference (fsig->params [0]);
6139 if (fsig->params [0]->type == MONO_TYPE_I1)
6140 opcode = OP_LOADI1_MEMBASE;
6141 else if (fsig->params [0]->type == MONO_TYPE_U1)
6142 opcode = OP_LOADU1_MEMBASE;
6143 else if (fsig->params [0]->type == MONO_TYPE_I2)
6144 opcode = OP_LOADI2_MEMBASE;
6145 else if (fsig->params [0]->type == MONO_TYPE_U2)
6146 opcode = OP_LOADU2_MEMBASE;
6147 else if (fsig->params [0]->type == MONO_TYPE_I4)
6148 opcode = OP_LOADI4_MEMBASE;
6149 else if (fsig->params [0]->type == MONO_TYPE_U4)
6150 opcode = OP_LOADU4_MEMBASE;
6151 else if (fsig->params [0]->type == MONO_TYPE_I8 || fsig->params [0]->type == MONO_TYPE_U8)
6152 opcode = OP_LOADI8_MEMBASE;
6153 else if (fsig->params [0]->type == MONO_TYPE_R4)
6154 opcode = OP_LOADR4_MEMBASE;
6155 else if (fsig->params [0]->type == MONO_TYPE_R8)
6156 opcode = OP_LOADR8_MEMBASE;
6157 else if (is_ref || fsig->params [0]->type == MONO_TYPE_I || fsig->params [0]->type == MONO_TYPE_U)
6158 opcode = OP_LOAD_MEMBASE;
6161 MONO_INST_NEW (cfg, ins, opcode);
6162 ins->inst_basereg = args [0]->dreg;
6163 ins->inst_offset = 0;
6164 MONO_ADD_INS (cfg->cbb, ins);
6166 switch (fsig->params [0]->type) {
6173 ins->dreg = mono_alloc_ireg (cfg);
6174 ins->type = STACK_I4;
6178 ins->dreg = mono_alloc_lreg (cfg);
6179 ins->type = STACK_I8;
6183 ins->dreg = mono_alloc_ireg (cfg);
6184 #if SIZEOF_REGISTER == 8
6185 ins->type = STACK_I8;
6187 ins->type = STACK_I4;
6192 ins->dreg = mono_alloc_freg (cfg);
6193 ins->type = STACK_R8;
6196 g_assert (mini_type_is_reference (fsig->params [0]));
6197 ins->dreg = mono_alloc_ireg_ref (cfg);
6198 ins->type = STACK_OBJ;
6202 if (opcode == OP_LOADI8_MEMBASE)
6203 ins = mono_decompose_opcode (cfg, ins);
6205 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_ACQ);
6209 } else if (!strcmp (cmethod->name, "VolatileWrite") && fsig->param_count == 2) {
6211 gboolean is_ref = mini_type_is_reference (fsig->params [0]);
6213 if (fsig->params [0]->type == MONO_TYPE_I1 || fsig->params [0]->type == MONO_TYPE_U1)
6214 opcode = OP_STOREI1_MEMBASE_REG;
6215 else if (fsig->params [0]->type == MONO_TYPE_I2 || fsig->params [0]->type == MONO_TYPE_U2)
6216 opcode = OP_STOREI2_MEMBASE_REG;
6217 else if (fsig->params [0]->type == MONO_TYPE_I4 || fsig->params [0]->type == MONO_TYPE_U4)
6218 opcode = OP_STOREI4_MEMBASE_REG;
6219 else if (fsig->params [0]->type == MONO_TYPE_I8 || fsig->params [0]->type == MONO_TYPE_U8)
6220 opcode = OP_STOREI8_MEMBASE_REG;
6221 else if (fsig->params [0]->type == MONO_TYPE_R4)
6222 opcode = OP_STORER4_MEMBASE_REG;
6223 else if (fsig->params [0]->type == MONO_TYPE_R8)
6224 opcode = OP_STORER8_MEMBASE_REG;
6225 else if (is_ref || fsig->params [0]->type == MONO_TYPE_I || fsig->params [0]->type == MONO_TYPE_U)
6226 opcode = OP_STORE_MEMBASE_REG;
6229 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_REL);
6231 MONO_INST_NEW (cfg, ins, opcode);
6232 ins->sreg1 = args [1]->dreg;
6233 ins->inst_destbasereg = args [0]->dreg;
6234 ins->inst_offset = 0;
6235 MONO_ADD_INS (cfg->cbb, ins);
6237 if (opcode == OP_STOREI8_MEMBASE_REG)
6238 ins = mono_decompose_opcode (cfg, ins);
6243 } else if (cmethod->klass->image == mono_defaults.corlib &&
6244 (strcmp (cmethod->klass->name_space, "System.Threading") == 0) &&
6245 (strcmp (cmethod->klass->name, "Interlocked") == 0)) {
6248 #if SIZEOF_REGISTER == 8
6249 if (!cfg->llvm_only && strcmp (cmethod->name, "Read") == 0 && fsig->param_count == 1 && (fsig->params [0]->type == MONO_TYPE_I8)) {
6250 if (!cfg->llvm_only && mono_arch_opcode_supported (OP_ATOMIC_LOAD_I8)) {
6251 MONO_INST_NEW (cfg, ins, OP_ATOMIC_LOAD_I8);
6252 ins->dreg = mono_alloc_preg (cfg);
6253 ins->sreg1 = args [0]->dreg;
6254 ins->type = STACK_I8;
6255 ins->backend.memory_barrier_kind = MONO_MEMORY_BARRIER_SEQ;
6256 MONO_ADD_INS (cfg->cbb, ins);
6260 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_SEQ);
6262 /* 64 bit reads are already atomic */
6263 MONO_INST_NEW (cfg, load_ins, OP_LOADI8_MEMBASE);
6264 load_ins->dreg = mono_alloc_preg (cfg);
6265 load_ins->inst_basereg = args [0]->dreg;
6266 load_ins->inst_offset = 0;
6267 load_ins->type = STACK_I8;
6268 MONO_ADD_INS (cfg->cbb, load_ins);
6270 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_SEQ);
6277 if (strcmp (cmethod->name, "Increment") == 0 && fsig->param_count == 1) {
6278 MonoInst *ins_iconst;
6281 if (fsig->params [0]->type == MONO_TYPE_I4) {
6282 opcode = OP_ATOMIC_ADD_I4;
6283 cfg->has_atomic_add_i4 = TRUE;
6285 #if SIZEOF_REGISTER == 8
6286 else if (fsig->params [0]->type == MONO_TYPE_I8)
6287 opcode = OP_ATOMIC_ADD_I8;
6290 if (!mono_arch_opcode_supported (opcode))
6292 MONO_INST_NEW (cfg, ins_iconst, OP_ICONST);
6293 ins_iconst->inst_c0 = 1;
6294 ins_iconst->dreg = mono_alloc_ireg (cfg);
6295 MONO_ADD_INS (cfg->cbb, ins_iconst);
6297 MONO_INST_NEW (cfg, ins, opcode);
6298 ins->dreg = mono_alloc_ireg (cfg);
6299 ins->inst_basereg = args [0]->dreg;
6300 ins->inst_offset = 0;
6301 ins->sreg2 = ins_iconst->dreg;
6302 ins->type = (opcode == OP_ATOMIC_ADD_I4) ? STACK_I4 : STACK_I8;
6303 MONO_ADD_INS (cfg->cbb, ins);
6305 } else if (strcmp (cmethod->name, "Decrement") == 0 && fsig->param_count == 1) {
6306 MonoInst *ins_iconst;
6309 if (fsig->params [0]->type == MONO_TYPE_I4) {
6310 opcode = OP_ATOMIC_ADD_I4;
6311 cfg->has_atomic_add_i4 = TRUE;
6313 #if SIZEOF_REGISTER == 8
6314 else if (fsig->params [0]->type == MONO_TYPE_I8)
6315 opcode = OP_ATOMIC_ADD_I8;
6318 if (!mono_arch_opcode_supported (opcode))
6320 MONO_INST_NEW (cfg, ins_iconst, OP_ICONST);
6321 ins_iconst->inst_c0 = -1;
6322 ins_iconst->dreg = mono_alloc_ireg (cfg);
6323 MONO_ADD_INS (cfg->cbb, ins_iconst);
6325 MONO_INST_NEW (cfg, ins, opcode);
6326 ins->dreg = mono_alloc_ireg (cfg);
6327 ins->inst_basereg = args [0]->dreg;
6328 ins->inst_offset = 0;
6329 ins->sreg2 = ins_iconst->dreg;
6330 ins->type = (opcode == OP_ATOMIC_ADD_I4) ? STACK_I4 : STACK_I8;
6331 MONO_ADD_INS (cfg->cbb, ins);
6333 } else if (strcmp (cmethod->name, "Add") == 0 && fsig->param_count == 2) {
6336 if (fsig->params [0]->type == MONO_TYPE_I4) {
6337 opcode = OP_ATOMIC_ADD_I4;
6338 cfg->has_atomic_add_i4 = TRUE;
6340 #if SIZEOF_REGISTER == 8
6341 else if (fsig->params [0]->type == MONO_TYPE_I8)
6342 opcode = OP_ATOMIC_ADD_I8;
6345 if (!mono_arch_opcode_supported (opcode))
6347 MONO_INST_NEW (cfg, ins, opcode);
6348 ins->dreg = mono_alloc_ireg (cfg);
6349 ins->inst_basereg = args [0]->dreg;
6350 ins->inst_offset = 0;
6351 ins->sreg2 = args [1]->dreg;
6352 ins->type = (opcode == OP_ATOMIC_ADD_I4) ? STACK_I4 : STACK_I8;
6353 MONO_ADD_INS (cfg->cbb, ins);
6356 else if (strcmp (cmethod->name, "Exchange") == 0 && fsig->param_count == 2) {
6357 MonoInst *f2i = NULL, *i2f;
6358 guint32 opcode, f2i_opcode, i2f_opcode;
6359 gboolean is_ref = mini_type_is_reference (fsig->params [0]);
6360 gboolean is_float = fsig->params [0]->type == MONO_TYPE_R4 || fsig->params [0]->type == MONO_TYPE_R8;
6362 if (fsig->params [0]->type == MONO_TYPE_I4 ||
6363 fsig->params [0]->type == MONO_TYPE_R4) {
6364 opcode = OP_ATOMIC_EXCHANGE_I4;
6365 f2i_opcode = OP_MOVE_F_TO_I4;
6366 i2f_opcode = OP_MOVE_I4_TO_F;
6367 cfg->has_atomic_exchange_i4 = TRUE;
6369 #if SIZEOF_REGISTER == 8
6371 fsig->params [0]->type == MONO_TYPE_I8 ||
6372 fsig->params [0]->type == MONO_TYPE_R8 ||
6373 fsig->params [0]->type == MONO_TYPE_I) {
6374 opcode = OP_ATOMIC_EXCHANGE_I8;
6375 f2i_opcode = OP_MOVE_F_TO_I8;
6376 i2f_opcode = OP_MOVE_I8_TO_F;
6379 else if (is_ref || fsig->params [0]->type == MONO_TYPE_I) {
6380 opcode = OP_ATOMIC_EXCHANGE_I4;
6381 cfg->has_atomic_exchange_i4 = TRUE;
6387 if (!mono_arch_opcode_supported (opcode))
6391 /* TODO: Decompose these opcodes instead of bailing here. */
6392 if (COMPILE_SOFT_FLOAT (cfg))
6395 MONO_INST_NEW (cfg, f2i, f2i_opcode);
6396 f2i->dreg = mono_alloc_ireg (cfg);
6397 f2i->sreg1 = args [1]->dreg;
6398 if (f2i_opcode == OP_MOVE_F_TO_I4)
6399 f2i->backend.spill_var = mini_get_int_to_float_spill_area (cfg);
6400 MONO_ADD_INS (cfg->cbb, f2i);
6403 MONO_INST_NEW (cfg, ins, opcode);
6404 ins->dreg = is_ref ? mono_alloc_ireg_ref (cfg) : mono_alloc_ireg (cfg);
6405 ins->inst_basereg = args [0]->dreg;
6406 ins->inst_offset = 0;
6407 ins->sreg2 = is_float ? f2i->dreg : args [1]->dreg;
6408 MONO_ADD_INS (cfg->cbb, ins);
6410 switch (fsig->params [0]->type) {
6412 ins->type = STACK_I4;
6415 ins->type = STACK_I8;
6418 #if SIZEOF_REGISTER == 8
6419 ins->type = STACK_I8;
6421 ins->type = STACK_I4;
6426 ins->type = STACK_R8;
6429 g_assert (mini_type_is_reference (fsig->params [0]));
6430 ins->type = STACK_OBJ;
6435 MONO_INST_NEW (cfg, i2f, i2f_opcode);
6436 i2f->dreg = mono_alloc_freg (cfg);
6437 i2f->sreg1 = ins->dreg;
6438 i2f->type = STACK_R8;
6439 if (i2f_opcode == OP_MOVE_I4_TO_F)
6440 i2f->backend.spill_var = mini_get_int_to_float_spill_area (cfg);
6441 MONO_ADD_INS (cfg->cbb, i2f);
6446 if (cfg->gen_write_barriers && is_ref)
6447 emit_write_barrier (cfg, args [0], args [1]);
6449 else if ((strcmp (cmethod->name, "CompareExchange") == 0) && fsig->param_count == 3) {
6450 MonoInst *f2i_new = NULL, *f2i_cmp = NULL, *i2f;
6451 guint32 opcode, f2i_opcode, i2f_opcode;
6452 gboolean is_ref = mini_type_is_reference (fsig->params [1]);
6453 gboolean is_float = fsig->params [1]->type == MONO_TYPE_R4 || fsig->params [1]->type == MONO_TYPE_R8;
6455 if (fsig->params [1]->type == MONO_TYPE_I4 ||
6456 fsig->params [1]->type == MONO_TYPE_R4) {
6457 opcode = OP_ATOMIC_CAS_I4;
6458 f2i_opcode = OP_MOVE_F_TO_I4;
6459 i2f_opcode = OP_MOVE_I4_TO_F;
6460 cfg->has_atomic_cas_i4 = TRUE;
6462 #if SIZEOF_REGISTER == 8
6464 fsig->params [1]->type == MONO_TYPE_I8 ||
6465 fsig->params [1]->type == MONO_TYPE_R8 ||
6466 fsig->params [1]->type == MONO_TYPE_I) {
6467 opcode = OP_ATOMIC_CAS_I8;
6468 f2i_opcode = OP_MOVE_F_TO_I8;
6469 i2f_opcode = OP_MOVE_I8_TO_F;
6472 else if (is_ref || fsig->params [1]->type == MONO_TYPE_I) {
6473 opcode = OP_ATOMIC_CAS_I4;
6474 cfg->has_atomic_cas_i4 = TRUE;
6480 if (!mono_arch_opcode_supported (opcode))
6484 /* TODO: Decompose these opcodes instead of bailing here. */
6485 if (COMPILE_SOFT_FLOAT (cfg))
6488 MONO_INST_NEW (cfg, f2i_new, f2i_opcode);
6489 f2i_new->dreg = mono_alloc_ireg (cfg);
6490 f2i_new->sreg1 = args [1]->dreg;
6491 if (f2i_opcode == OP_MOVE_F_TO_I4)
6492 f2i_new->backend.spill_var = mini_get_int_to_float_spill_area (cfg);
6493 MONO_ADD_INS (cfg->cbb, f2i_new);
6495 MONO_INST_NEW (cfg, f2i_cmp, f2i_opcode);
6496 f2i_cmp->dreg = mono_alloc_ireg (cfg);
6497 f2i_cmp->sreg1 = args [2]->dreg;
6498 if (f2i_opcode == OP_MOVE_F_TO_I4)
6499 f2i_cmp->backend.spill_var = mini_get_int_to_float_spill_area (cfg);
6500 MONO_ADD_INS (cfg->cbb, f2i_cmp);
6503 MONO_INST_NEW (cfg, ins, opcode);
6504 ins->dreg = is_ref ? alloc_ireg_ref (cfg) : alloc_ireg (cfg);
6505 ins->sreg1 = args [0]->dreg;
6506 ins->sreg2 = is_float ? f2i_new->dreg : args [1]->dreg;
6507 ins->sreg3 = is_float ? f2i_cmp->dreg : args [2]->dreg;
6508 MONO_ADD_INS (cfg->cbb, ins);
6510 switch (fsig->params [1]->type) {
6512 ins->type = STACK_I4;
6515 ins->type = STACK_I8;
6518 #if SIZEOF_REGISTER == 8
6519 ins->type = STACK_I8;
6521 ins->type = STACK_I4;
6525 ins->type = cfg->r4_stack_type;
6528 ins->type = STACK_R8;
6531 g_assert (mini_type_is_reference (fsig->params [1]));
6532 ins->type = STACK_OBJ;
6537 MONO_INST_NEW (cfg, i2f, i2f_opcode);
6538 i2f->dreg = mono_alloc_freg (cfg);
6539 i2f->sreg1 = ins->dreg;
6540 i2f->type = STACK_R8;
6541 if (i2f_opcode == OP_MOVE_I4_TO_F)
6542 i2f->backend.spill_var = mini_get_int_to_float_spill_area (cfg);
6543 MONO_ADD_INS (cfg->cbb, i2f);
6548 if (cfg->gen_write_barriers && is_ref)
6549 emit_write_barrier (cfg, args [0], args [1]);
6551 else if ((strcmp (cmethod->name, "CompareExchange") == 0) && fsig->param_count == 4 &&
6552 fsig->params [1]->type == MONO_TYPE_I4) {
6553 MonoInst *cmp, *ceq;
6555 if (!mono_arch_opcode_supported (OP_ATOMIC_CAS_I4))
6558 /* int32 r = CAS (location, value, comparand); */
6559 MONO_INST_NEW (cfg, ins, OP_ATOMIC_CAS_I4);
6560 ins->dreg = alloc_ireg (cfg);
6561 ins->sreg1 = args [0]->dreg;
6562 ins->sreg2 = args [1]->dreg;
6563 ins->sreg3 = args [2]->dreg;
6564 ins->type = STACK_I4;
6565 MONO_ADD_INS (cfg->cbb, ins);
6567 /* bool result = r == comparand; */
6568 MONO_INST_NEW (cfg, cmp, OP_ICOMPARE);
6569 cmp->sreg1 = ins->dreg;
6570 cmp->sreg2 = args [2]->dreg;
6571 cmp->type = STACK_I4;
6572 MONO_ADD_INS (cfg->cbb, cmp);
6574 MONO_INST_NEW (cfg, ceq, OP_ICEQ);
6575 ceq->dreg = alloc_ireg (cfg);
6576 ceq->type = STACK_I4;
6577 MONO_ADD_INS (cfg->cbb, ceq);
6579 /* *success = result; */
6580 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI1_MEMBASE_REG, args [3]->dreg, 0, ceq->dreg);
6582 cfg->has_atomic_cas_i4 = TRUE;
6584 else if (strcmp (cmethod->name, "MemoryBarrier") == 0 && fsig->param_count == 0)
6585 ins = emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_SEQ);
6589 } else if (cmethod->klass->image == mono_defaults.corlib &&
6590 (strcmp (cmethod->klass->name_space, "System.Threading") == 0) &&
6591 (strcmp (cmethod->klass->name, "Volatile") == 0)) {
6594 if (!cfg->llvm_only && !strcmp (cmethod->name, "Read") && fsig->param_count == 1) {
6596 gboolean is_ref = mini_type_is_reference (fsig->params [0]);
6597 gboolean is_float = fsig->params [0]->type == MONO_TYPE_R4 || fsig->params [0]->type == MONO_TYPE_R8;
6599 if (fsig->params [0]->type == MONO_TYPE_I1)
6600 opcode = OP_ATOMIC_LOAD_I1;
6601 else if (fsig->params [0]->type == MONO_TYPE_U1 || fsig->params [0]->type == MONO_TYPE_BOOLEAN)
6602 opcode = OP_ATOMIC_LOAD_U1;
6603 else if (fsig->params [0]->type == MONO_TYPE_I2)
6604 opcode = OP_ATOMIC_LOAD_I2;
6605 else if (fsig->params [0]->type == MONO_TYPE_U2)
6606 opcode = OP_ATOMIC_LOAD_U2;
6607 else if (fsig->params [0]->type == MONO_TYPE_I4)
6608 opcode = OP_ATOMIC_LOAD_I4;
6609 else if (fsig->params [0]->type == MONO_TYPE_U4)
6610 opcode = OP_ATOMIC_LOAD_U4;
6611 else if (fsig->params [0]->type == MONO_TYPE_R4)
6612 opcode = OP_ATOMIC_LOAD_R4;
6613 else if (fsig->params [0]->type == MONO_TYPE_R8)
6614 opcode = OP_ATOMIC_LOAD_R8;
6615 #if SIZEOF_REGISTER == 8
6616 else if (fsig->params [0]->type == MONO_TYPE_I8 || fsig->params [0]->type == MONO_TYPE_I)
6617 opcode = OP_ATOMIC_LOAD_I8;
6618 else if (is_ref || fsig->params [0]->type == MONO_TYPE_U8 || fsig->params [0]->type == MONO_TYPE_U)
6619 opcode = OP_ATOMIC_LOAD_U8;
6621 else if (fsig->params [0]->type == MONO_TYPE_I)
6622 opcode = OP_ATOMIC_LOAD_I4;
6623 else if (is_ref || fsig->params [0]->type == MONO_TYPE_U)
6624 opcode = OP_ATOMIC_LOAD_U4;
6628 if (!mono_arch_opcode_supported (opcode))
6631 MONO_INST_NEW (cfg, ins, opcode);
6632 ins->dreg = is_ref ? mono_alloc_ireg_ref (cfg) : (is_float ? mono_alloc_freg (cfg) : mono_alloc_ireg (cfg));
6633 ins->sreg1 = args [0]->dreg;
6634 ins->backend.memory_barrier_kind = MONO_MEMORY_BARRIER_ACQ;
6635 MONO_ADD_INS (cfg->cbb, ins);
6637 switch (fsig->params [0]->type) {
6638 case MONO_TYPE_BOOLEAN:
6645 ins->type = STACK_I4;
6649 ins->type = STACK_I8;
6653 #if SIZEOF_REGISTER == 8
6654 ins->type = STACK_I8;
6656 ins->type = STACK_I4;
6660 ins->type = cfg->r4_stack_type;
6663 ins->type = STACK_R8;
6666 g_assert (mini_type_is_reference (fsig->params [0]));
6667 ins->type = STACK_OBJ;
6673 if (!cfg->llvm_only && !strcmp (cmethod->name, "Write") && fsig->param_count == 2) {
6675 gboolean is_ref = mini_type_is_reference (fsig->params [0]);
6677 if (fsig->params [0]->type == MONO_TYPE_I1)
6678 opcode = OP_ATOMIC_STORE_I1;
6679 else if (fsig->params [0]->type == MONO_TYPE_U1 || fsig->params [0]->type == MONO_TYPE_BOOLEAN)
6680 opcode = OP_ATOMIC_STORE_U1;
6681 else if (fsig->params [0]->type == MONO_TYPE_I2)
6682 opcode = OP_ATOMIC_STORE_I2;
6683 else if (fsig->params [0]->type == MONO_TYPE_U2)
6684 opcode = OP_ATOMIC_STORE_U2;
6685 else if (fsig->params [0]->type == MONO_TYPE_I4)
6686 opcode = OP_ATOMIC_STORE_I4;
6687 else if (fsig->params [0]->type == MONO_TYPE_U4)
6688 opcode = OP_ATOMIC_STORE_U4;
6689 else if (fsig->params [0]->type == MONO_TYPE_R4)
6690 opcode = OP_ATOMIC_STORE_R4;
6691 else if (fsig->params [0]->type == MONO_TYPE_R8)
6692 opcode = OP_ATOMIC_STORE_R8;
6693 #if SIZEOF_REGISTER == 8
6694 else if (fsig->params [0]->type == MONO_TYPE_I8 || fsig->params [0]->type == MONO_TYPE_I)
6695 opcode = OP_ATOMIC_STORE_I8;
6696 else if (is_ref || fsig->params [0]->type == MONO_TYPE_U8 || fsig->params [0]->type == MONO_TYPE_U)
6697 opcode = OP_ATOMIC_STORE_U8;
6699 else if (fsig->params [0]->type == MONO_TYPE_I)
6700 opcode = OP_ATOMIC_STORE_I4;
6701 else if (is_ref || fsig->params [0]->type == MONO_TYPE_U)
6702 opcode = OP_ATOMIC_STORE_U4;
6706 if (!mono_arch_opcode_supported (opcode))
6709 MONO_INST_NEW (cfg, ins, opcode);
6710 ins->dreg = args [0]->dreg;
6711 ins->sreg1 = args [1]->dreg;
6712 ins->backend.memory_barrier_kind = MONO_MEMORY_BARRIER_REL;
6713 MONO_ADD_INS (cfg->cbb, ins);
6715 if (cfg->gen_write_barriers && is_ref)
6716 emit_write_barrier (cfg, args [0], args [1]);
6722 } else if (cmethod->klass->image == mono_defaults.corlib &&
6723 (strcmp (cmethod->klass->name_space, "System.Diagnostics") == 0) &&
6724 (strcmp (cmethod->klass->name, "Debugger") == 0)) {
6725 if (!strcmp (cmethod->name, "Break") && fsig->param_count == 0) {
6726 if (should_insert_brekpoint (cfg->method)) {
6727 ins = mono_emit_jit_icall (cfg, mono_debugger_agent_user_break, NULL);
6729 MONO_INST_NEW (cfg, ins, OP_NOP);
6730 MONO_ADD_INS (cfg->cbb, ins);
6734 } else if (cmethod->klass->image == mono_defaults.corlib &&
6735 (strcmp (cmethod->klass->name_space, "System") == 0) &&
6736 (strcmp (cmethod->klass->name, "Environment") == 0)) {
6737 if (!strcmp (cmethod->name, "get_IsRunningOnWindows") && fsig->param_count == 0) {
6739 EMIT_NEW_ICONST (cfg, ins, 1);
6741 EMIT_NEW_ICONST (cfg, ins, 0);
6744 } else if (cmethod->klass->image == mono_defaults.corlib &&
6745 (strcmp (cmethod->klass->name_space, "System.Reflection") == 0) &&
6746 (strcmp (cmethod->klass->name, "Assembly") == 0)) {
6747 if (cfg->llvm_only && !strcmp (cmethod->name, "GetExecutingAssembly")) {
6748 /* No stack walks are current available, so implement this as an intrinsic */
6749 MonoInst *assembly_ins;
6751 EMIT_NEW_AOTCONST (cfg, assembly_ins, MONO_PATCH_INFO_IMAGE, cfg->method->klass->image);
6752 ins = mono_emit_jit_icall (cfg, mono_get_assembly_object, &assembly_ins);
6755 } else if (cmethod->klass == mono_defaults.math_class) {
6757 * There is general branchless code for Min/Max, but it does not work for
6759 * http://everything2.com/?node_id=1051618
6761 } else if (((!strcmp (cmethod->klass->image->assembly->aname.name, "MonoMac") ||
6762 !strcmp (cmethod->klass->image->assembly->aname.name, "monotouch")) &&
6763 !strcmp (cmethod->klass->name_space, "XamCore.ObjCRuntime") &&
6764 !strcmp (cmethod->klass->name, "Selector")) ||
6765 (!strcmp (cmethod->klass->image->assembly->aname.name, "Xamarin.iOS") &&
6766 !strcmp (cmethod->klass->name_space, "ObjCRuntime") &&
6767 !strcmp (cmethod->klass->name, "Selector"))
6769 if (cfg->backend->have_objc_get_selector &&
6770 !strcmp (cmethod->name, "GetHandle") && fsig->param_count == 1 &&
6771 (args [0]->opcode == OP_GOT_ENTRY || args [0]->opcode == OP_AOTCONST) &&
6774 MonoJumpInfoToken *ji;
6777 cfg->disable_llvm = TRUE;
6779 if (args [0]->opcode == OP_GOT_ENTRY) {
6780 pi = (MonoInst *)args [0]->inst_p1;
6781 g_assert (pi->opcode == OP_PATCH_INFO);
6782 g_assert (GPOINTER_TO_INT (pi->inst_p1) == MONO_PATCH_INFO_LDSTR);
6783 ji = (MonoJumpInfoToken *)pi->inst_p0;
6785 g_assert (GPOINTER_TO_INT (args [0]->inst_p1) == MONO_PATCH_INFO_LDSTR);
6786 ji = (MonoJumpInfoToken *)args [0]->inst_p0;
6789 NULLIFY_INS (args [0]);
6792 s = mono_ldstr (cfg->domain, ji->image, mono_metadata_token_index (ji->token));
6793 MONO_INST_NEW (cfg, ins, OP_OBJC_GET_SELECTOR);
6794 ins->dreg = mono_alloc_ireg (cfg);
6796 ins->inst_p0 = mono_string_to_utf8 (s);
6797 MONO_ADD_INS (cfg->cbb, ins);
6802 #ifdef MONO_ARCH_SIMD_INTRINSICS
6803 if (cfg->opt & MONO_OPT_SIMD) {
6804 ins = mono_emit_simd_intrinsics (cfg, cmethod, fsig, args);
6810 ins = mono_emit_native_types_intrinsics (cfg, cmethod, fsig, args);
6814 if (COMPILE_LLVM (cfg)) {
6815 ins = llvm_emit_inst_for_method (cfg, cmethod, fsig, args);
6820 return mono_arch_emit_inst_for_method (cfg, cmethod, fsig, args);
6824 * This entry point could be used later for arbitrary method
6827 inline static MonoInst*
6828 mini_redirect_call (MonoCompile *cfg, MonoMethod *method,
6829 MonoMethodSignature *signature, MonoInst **args, MonoInst *this_ins)
6831 if (method->klass == mono_defaults.string_class) {
6832 /* managed string allocation support */
6833 if (strcmp (method->name, "InternalAllocateStr") == 0 && !(mono_profiler_events & MONO_PROFILE_ALLOCATIONS) && !(cfg->opt & MONO_OPT_SHARED)) {
6834 MonoInst *iargs [2];
6835 MonoVTable *vtable = mono_class_vtable (cfg->domain, method->klass);
6836 MonoMethod *managed_alloc = NULL;
6838 g_assert (vtable); /*Should not fail since it System.String*/
6839 #ifndef MONO_CROSS_COMPILE
6840 managed_alloc = mono_gc_get_managed_allocator (method->klass, FALSE, FALSE);
6844 EMIT_NEW_VTABLECONST (cfg, iargs [0], vtable);
6845 iargs [1] = args [0];
6846 return mono_emit_method_call (cfg, managed_alloc, iargs, this_ins);
6853 mono_save_args (MonoCompile *cfg, MonoMethodSignature *sig, MonoInst **sp)
6855 MonoInst *store, *temp;
6858 for (i = 0; i < sig->param_count + sig->hasthis; ++i) {
6859 MonoType *argtype = (sig->hasthis && (i == 0)) ? type_from_stack_type (*sp) : sig->params [i - sig->hasthis];
6862 * FIXME: We should use *args++ = sp [0], but that would mean the arg
6863 * would be different than the MonoInst's used to represent arguments, and
6864 * the ldelema implementation can't deal with that.
6865 * Solution: When ldelema is used on an inline argument, create a var for
6866 * it, emit ldelema on that var, and emit the saving code below in
6867 * inline_method () if needed.
6869 temp = mono_compile_create_var (cfg, argtype, OP_LOCAL);
6870 cfg->args [i] = temp;
6871 /* This uses cfg->args [i] which is set by the preceeding line */
6872 EMIT_NEW_ARGSTORE (cfg, store, i, *sp);
6873 store->cil_code = sp [0]->cil_code;
6878 #define MONO_INLINE_CALLED_LIMITED_METHODS 1
6879 #define MONO_INLINE_CALLER_LIMITED_METHODS 1
6881 #if (MONO_INLINE_CALLED_LIMITED_METHODS)
6883 check_inline_called_method_name_limit (MonoMethod *called_method)
6886 static const char *limit = NULL;
6888 if (limit == NULL) {
6889 const char *limit_string = g_getenv ("MONO_INLINE_CALLED_METHOD_NAME_LIMIT");
6891 if (limit_string != NULL)
6892 limit = limit_string;
6897 if (limit [0] != '\0') {
6898 char *called_method_name = mono_method_full_name (called_method, TRUE);
6900 strncmp_result = strncmp (called_method_name, limit, strlen (limit));
6901 g_free (called_method_name);
6903 //return (strncmp_result <= 0);
6904 return (strncmp_result == 0);
6911 #if (MONO_INLINE_CALLER_LIMITED_METHODS)
6913 check_inline_caller_method_name_limit (MonoMethod *caller_method)
6916 static const char *limit = NULL;
6918 if (limit == NULL) {
6919 const char *limit_string = g_getenv ("MONO_INLINE_CALLER_METHOD_NAME_LIMIT");
6920 if (limit_string != NULL) {
6921 limit = limit_string;
6927 if (limit [0] != '\0') {
6928 char *caller_method_name = mono_method_full_name (caller_method, TRUE);
6930 strncmp_result = strncmp (caller_method_name, limit, strlen (limit));
6931 g_free (caller_method_name);
6933 //return (strncmp_result <= 0);
6934 return (strncmp_result == 0);
6942 emit_init_rvar (MonoCompile *cfg, int dreg, MonoType *rtype)
6944 static double r8_0 = 0.0;
6945 static float r4_0 = 0.0;
6949 rtype = mini_get_underlying_type (rtype);
6953 MONO_EMIT_NEW_PCONST (cfg, dreg, NULL);
6954 } else if (t >= MONO_TYPE_BOOLEAN && t <= MONO_TYPE_U4) {
6955 MONO_EMIT_NEW_ICONST (cfg, dreg, 0);
6956 } else if (t == MONO_TYPE_I8 || t == MONO_TYPE_U8) {
6957 MONO_EMIT_NEW_I8CONST (cfg, dreg, 0);
6958 } else if (cfg->r4fp && t == MONO_TYPE_R4) {
6959 MONO_INST_NEW (cfg, ins, OP_R4CONST);
6960 ins->type = STACK_R4;
6961 ins->inst_p0 = (void*)&r4_0;
6963 MONO_ADD_INS (cfg->cbb, ins);
6964 } else if (t == MONO_TYPE_R4 || t == MONO_TYPE_R8) {
6965 MONO_INST_NEW (cfg, ins, OP_R8CONST);
6966 ins->type = STACK_R8;
6967 ins->inst_p0 = (void*)&r8_0;
6969 MONO_ADD_INS (cfg->cbb, ins);
6970 } else if ((t == MONO_TYPE_VALUETYPE) || (t == MONO_TYPE_TYPEDBYREF) ||
6971 ((t == MONO_TYPE_GENERICINST) && mono_type_generic_inst_is_valuetype (rtype))) {
6972 MONO_EMIT_NEW_VZERO (cfg, dreg, mono_class_from_mono_type (rtype));
6973 } else if (((t == MONO_TYPE_VAR) || (t == MONO_TYPE_MVAR)) && mini_type_var_is_vt (rtype)) {
6974 MONO_EMIT_NEW_VZERO (cfg, dreg, mono_class_from_mono_type (rtype));
6976 MONO_EMIT_NEW_PCONST (cfg, dreg, NULL);
6981 emit_dummy_init_rvar (MonoCompile *cfg, int dreg, MonoType *rtype)
6985 rtype = mini_get_underlying_type (rtype);
6989 MONO_EMIT_NEW_DUMMY_INIT (cfg, dreg, OP_DUMMY_PCONST);
6990 } else if (t >= MONO_TYPE_BOOLEAN && t <= MONO_TYPE_U4) {
6991 MONO_EMIT_NEW_DUMMY_INIT (cfg, dreg, OP_DUMMY_ICONST);
6992 } else if (t == MONO_TYPE_I8 || t == MONO_TYPE_U8) {
6993 MONO_EMIT_NEW_DUMMY_INIT (cfg, dreg, OP_DUMMY_I8CONST);
6994 } else if (cfg->r4fp && t == MONO_TYPE_R4) {
6995 MONO_EMIT_NEW_DUMMY_INIT (cfg, dreg, OP_DUMMY_R4CONST);
6996 } else if (t == MONO_TYPE_R4 || t == MONO_TYPE_R8) {
6997 MONO_EMIT_NEW_DUMMY_INIT (cfg, dreg, OP_DUMMY_R8CONST);
6998 } else if ((t == MONO_TYPE_VALUETYPE) || (t == MONO_TYPE_TYPEDBYREF) ||
6999 ((t == MONO_TYPE_GENERICINST) && mono_type_generic_inst_is_valuetype (rtype))) {
7000 MONO_EMIT_NEW_DUMMY_INIT (cfg, dreg, OP_DUMMY_VZERO);
7001 } else if (((t == MONO_TYPE_VAR) || (t == MONO_TYPE_MVAR)) && mini_type_var_is_vt (rtype)) {
7002 MONO_EMIT_NEW_DUMMY_INIT (cfg, dreg, OP_DUMMY_VZERO);
7004 emit_init_rvar (cfg, dreg, rtype);
7008 /* If INIT is FALSE, emit dummy initialization statements to keep the IR valid */
7010 emit_init_local (MonoCompile *cfg, int local, MonoType *type, gboolean init)
7012 MonoInst *var = cfg->locals [local];
7013 if (COMPILE_SOFT_FLOAT (cfg)) {
7015 int reg = alloc_dreg (cfg, (MonoStackType)var->type);
7016 emit_init_rvar (cfg, reg, type);
7017 EMIT_NEW_LOCSTORE (cfg, store, local, cfg->cbb->last_ins);
7020 emit_init_rvar (cfg, var->dreg, type);
7022 emit_dummy_init_rvar (cfg, var->dreg, type);
7029 * Return the cost of inlining CMETHOD.
7032 inline_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **sp,
7033 guchar *ip, guint real_offset, gboolean inline_always)
7035 MonoInst *ins, *rvar = NULL;
7036 MonoMethodHeader *cheader;
7037 MonoBasicBlock *ebblock, *sbblock;
7039 MonoMethod *prev_inlined_method;
7040 MonoInst **prev_locals, **prev_args;
7041 MonoType **prev_arg_types;
7042 guint prev_real_offset;
7043 GHashTable *prev_cbb_hash;
7044 MonoBasicBlock **prev_cil_offset_to_bb;
7045 MonoBasicBlock *prev_cbb;
7046 unsigned char* prev_cil_start;
7047 guint32 prev_cil_offset_to_bb_len;
7048 MonoMethod *prev_current_method;
7049 MonoGenericContext *prev_generic_context;
7050 gboolean ret_var_set, prev_ret_var_set, prev_disable_inline, virtual_ = FALSE;
7052 g_assert (cfg->exception_type == MONO_EXCEPTION_NONE);
7054 #if (MONO_INLINE_CALLED_LIMITED_METHODS)
7055 if ((! inline_always) && ! check_inline_called_method_name_limit (cmethod))
7058 #if (MONO_INLINE_CALLER_LIMITED_METHODS)
7059 if ((! inline_always) && ! check_inline_caller_method_name_limit (cfg->method))
7064 fsig = mono_method_signature (cmethod);
7066 if (cfg->verbose_level > 2)
7067 printf ("INLINE START %p %s -> %s\n", cmethod, mono_method_full_name (cfg->method, TRUE), mono_method_full_name (cmethod, TRUE));
7069 if (!cmethod->inline_info) {
7070 cfg->stat_inlineable_methods++;
7071 cmethod->inline_info = 1;
7074 /* allocate local variables */
7075 cheader = mono_method_get_header (cmethod);
7077 if (cheader == NULL || mono_loader_get_last_error ()) {
7078 MonoLoaderError *error = mono_loader_get_last_error ();
7081 mono_metadata_free_mh (cheader);
7082 if (inline_always && error)
7083 mono_cfg_set_exception (cfg, error->exception_type);
7085 mono_loader_clear_error ();
7089 /*Must verify before creating locals as it can cause the JIT to assert.*/
7090 if (mono_compile_is_broken (cfg, cmethod, FALSE)) {
7091 mono_metadata_free_mh (cheader);
7095 /* allocate space to store the return value */
7096 if (!MONO_TYPE_IS_VOID (fsig->ret)) {
7097 rvar = mono_compile_create_var (cfg, fsig->ret, OP_LOCAL);
7100 prev_locals = cfg->locals;
7101 cfg->locals = (MonoInst **)mono_mempool_alloc0 (cfg->mempool, cheader->num_locals * sizeof (MonoInst*));
7102 for (i = 0; i < cheader->num_locals; ++i)
7103 cfg->locals [i] = mono_compile_create_var (cfg, cheader->locals [i], OP_LOCAL);
7105 /* allocate start and end blocks */
7106 /* This is needed so if the inline is aborted, we can clean up */
7107 NEW_BBLOCK (cfg, sbblock);
7108 sbblock->real_offset = real_offset;
7110 NEW_BBLOCK (cfg, ebblock);
7111 ebblock->block_num = cfg->num_bblocks++;
7112 ebblock->real_offset = real_offset;
7114 prev_args = cfg->args;
7115 prev_arg_types = cfg->arg_types;
7116 prev_inlined_method = cfg->inlined_method;
7117 cfg->inlined_method = cmethod;
7118 cfg->ret_var_set = FALSE;
7119 cfg->inline_depth ++;
7120 prev_real_offset = cfg->real_offset;
7121 prev_cbb_hash = cfg->cbb_hash;
7122 prev_cil_offset_to_bb = cfg->cil_offset_to_bb;
7123 prev_cil_offset_to_bb_len = cfg->cil_offset_to_bb_len;
7124 prev_cil_start = cfg->cil_start;
7125 prev_cbb = cfg->cbb;
7126 prev_current_method = cfg->current_method;
7127 prev_generic_context = cfg->generic_context;
7128 prev_ret_var_set = cfg->ret_var_set;
7129 prev_disable_inline = cfg->disable_inline;
7131 if (ip && *ip == CEE_CALLVIRT && !(cmethod->flags & METHOD_ATTRIBUTE_STATIC))
7134 costs = mono_method_to_ir (cfg, cmethod, sbblock, ebblock, rvar, sp, real_offset, virtual_);
7136 ret_var_set = cfg->ret_var_set;
7138 cfg->inlined_method = prev_inlined_method;
7139 cfg->real_offset = prev_real_offset;
7140 cfg->cbb_hash = prev_cbb_hash;
7141 cfg->cil_offset_to_bb = prev_cil_offset_to_bb;
7142 cfg->cil_offset_to_bb_len = prev_cil_offset_to_bb_len;
7143 cfg->cil_start = prev_cil_start;
7144 cfg->locals = prev_locals;
7145 cfg->args = prev_args;
7146 cfg->arg_types = prev_arg_types;
7147 cfg->current_method = prev_current_method;
7148 cfg->generic_context = prev_generic_context;
7149 cfg->ret_var_set = prev_ret_var_set;
7150 cfg->disable_inline = prev_disable_inline;
7151 cfg->inline_depth --;
7153 if ((costs >= 0 && costs < 60) || inline_always || (costs >= 0 && (cmethod->iflags & METHOD_IMPL_ATTRIBUTE_AGGRESSIVE_INLINING))) {
7154 if (cfg->verbose_level > 2)
7155 printf ("INLINE END %s -> %s\n", mono_method_full_name (cfg->method, TRUE), mono_method_full_name (cmethod, TRUE));
7157 cfg->stat_inlined_methods++;
7159 /* always add some code to avoid block split failures */
7160 MONO_INST_NEW (cfg, ins, OP_NOP);
7161 MONO_ADD_INS (prev_cbb, ins);
7163 prev_cbb->next_bb = sbblock;
7164 link_bblock (cfg, prev_cbb, sbblock);
7167 * Get rid of the begin and end bblocks if possible to aid local
7170 mono_merge_basic_blocks (cfg, prev_cbb, sbblock);
7172 if ((prev_cbb->out_count == 1) && (prev_cbb->out_bb [0]->in_count == 1) && (prev_cbb->out_bb [0] != ebblock))
7173 mono_merge_basic_blocks (cfg, prev_cbb, prev_cbb->out_bb [0]);
7175 if ((ebblock->in_count == 1) && ebblock->in_bb [0]->out_count == 1) {
7176 MonoBasicBlock *prev = ebblock->in_bb [0];
7177 mono_merge_basic_blocks (cfg, prev, ebblock);
7179 if ((prev_cbb->out_count == 1) && (prev_cbb->out_bb [0]->in_count == 1) && (prev_cbb->out_bb [0] == prev)) {
7180 mono_merge_basic_blocks (cfg, prev_cbb, prev);
7181 cfg->cbb = prev_cbb;
7185 * Its possible that the rvar is set in some prev bblock, but not in others.
7191 for (i = 0; i < ebblock->in_count; ++i) {
7192 bb = ebblock->in_bb [i];
7194 if (bb->last_ins && bb->last_ins->opcode == OP_NOT_REACHED) {
7197 emit_init_rvar (cfg, rvar->dreg, fsig->ret);
7207 * If the inlined method contains only a throw, then the ret var is not
7208 * set, so set it to a dummy value.
7211 emit_init_rvar (cfg, rvar->dreg, fsig->ret);
7213 EMIT_NEW_TEMPLOAD (cfg, ins, rvar->inst_c0);
7216 cfg->headers_to_free = g_slist_prepend_mempool (cfg->mempool, cfg->headers_to_free, cheader);
7219 if (cfg->verbose_level > 2)
7220 printf ("INLINE ABORTED %s (cost %d)\n", mono_method_full_name (cmethod, TRUE), costs);
7221 cfg->exception_type = MONO_EXCEPTION_NONE;
7222 mono_loader_clear_error ();
7224 /* This gets rid of the newly added bblocks */
7225 cfg->cbb = prev_cbb;
7227 cfg->headers_to_free = g_slist_prepend_mempool (cfg->mempool, cfg->headers_to_free, cheader);
7232 * Some of these comments may well be out-of-date.
7233 * Design decisions: we do a single pass over the IL code (and we do bblock
7234 * splitting/merging in the few cases when it's required: a back jump to an IL
7235 * address that was not already seen as bblock starting point).
7236 * Code is validated as we go (full verification is still better left to metadata/verify.c).
7237 * Complex operations are decomposed in simpler ones right away. We need to let the
7238 * arch-specific code peek and poke inside this process somehow (except when the
7239 * optimizations can take advantage of the full semantic info of coarse opcodes).
7240 * All the opcodes of the form opcode.s are 'normalized' to opcode.
7241 * MonoInst->opcode initially is the IL opcode or some simplification of that
7242 * (OP_LOAD, OP_STORE). The arch-specific code may rearrange it to an arch-specific
7243 * opcode with value bigger than OP_LAST.
7244 * At this point the IR can be handed over to an interpreter, a dumb code generator
7245 * or to the optimizing code generator that will translate it to SSA form.
7247 * Profiling directed optimizations.
7248 * We may compile by default with few or no optimizations and instrument the code
7249 * or the user may indicate what methods to optimize the most either in a config file
7250 * or through repeated runs where the compiler applies offline the optimizations to
7251 * each method and then decides if it was worth it.
7254 #define CHECK_TYPE(ins) if (!(ins)->type) UNVERIFIED
7255 #define CHECK_STACK(num) if ((sp - stack_start) < (num)) UNVERIFIED
7256 #define CHECK_STACK_OVF(num) if (((sp - stack_start) + (num)) > header->max_stack) UNVERIFIED
7257 #define CHECK_ARG(num) if ((unsigned)(num) >= (unsigned)num_args) UNVERIFIED
7258 #define CHECK_LOCAL(num) if ((unsigned)(num) >= (unsigned)header->num_locals) UNVERIFIED
7259 #define CHECK_OPSIZE(size) if (ip + size > end) UNVERIFIED
7260 #define CHECK_UNVERIFIABLE(cfg) if (cfg->unverifiable) UNVERIFIED
7261 #define CHECK_TYPELOAD(klass) if (!(klass) || (klass)->exception_type) TYPE_LOAD_ERROR ((klass))
7263 /* offset from br.s -> br like opcodes */
7264 #define BIG_BRANCH_OFFSET 13
7267 ip_in_bb (MonoCompile *cfg, MonoBasicBlock *bb, const guint8* ip)
7269 MonoBasicBlock *b = cfg->cil_offset_to_bb [ip - cfg->cil_start];
7271 return b == NULL || b == bb;
7275 get_basic_blocks (MonoCompile *cfg, MonoMethodHeader* header, guint real_offset, unsigned char *start, unsigned char *end, unsigned char **pos)
7277 unsigned char *ip = start;
7278 unsigned char *target;
7281 MonoBasicBlock *bblock;
7282 const MonoOpcode *opcode;
7285 cli_addr = ip - start;
7286 i = mono_opcode_value ((const guint8 **)&ip, end);
7289 opcode = &mono_opcodes [i];
7290 switch (opcode->argument) {
7291 case MonoInlineNone:
7294 case MonoInlineString:
7295 case MonoInlineType:
7296 case MonoInlineField:
7297 case MonoInlineMethod:
7300 case MonoShortInlineR:
7307 case MonoShortInlineVar:
7308 case MonoShortInlineI:
7311 case MonoShortInlineBrTarget:
7312 target = start + cli_addr + 2 + (signed char)ip [1];
7313 GET_BBLOCK (cfg, bblock, target);
7316 GET_BBLOCK (cfg, bblock, ip);
7318 case MonoInlineBrTarget:
7319 target = start + cli_addr + 5 + (gint32)read32 (ip + 1);
7320 GET_BBLOCK (cfg, bblock, target);
7323 GET_BBLOCK (cfg, bblock, ip);
7325 case MonoInlineSwitch: {
7326 guint32 n = read32 (ip + 1);
7329 cli_addr += 5 + 4 * n;
7330 target = start + cli_addr;
7331 GET_BBLOCK (cfg, bblock, target);
7333 for (j = 0; j < n; ++j) {
7334 target = start + cli_addr + (gint32)read32 (ip);
7335 GET_BBLOCK (cfg, bblock, target);
7345 g_assert_not_reached ();
7348 if (i == CEE_THROW) {
7349 unsigned char *bb_start = ip - 1;
7351 /* Find the start of the bblock containing the throw */
7353 while ((bb_start >= start) && !bblock) {
7354 bblock = cfg->cil_offset_to_bb [(bb_start) - start];
7358 bblock->out_of_line = 1;
7368 static inline MonoMethod *
7369 mini_get_method_allow_open (MonoMethod *m, guint32 token, MonoClass *klass, MonoGenericContext *context)
7373 if (m->wrapper_type != MONO_WRAPPER_NONE) {
7374 method = (MonoMethod *)mono_method_get_wrapper_data (m, token);
7377 method = mono_class_inflate_generic_method_checked (method, context, &error);
7378 g_assert (mono_error_ok (&error)); /* FIXME don't swallow the error */
7381 method = mono_get_method_full (m->klass->image, token, klass, context);
7387 static inline MonoMethod *
7388 mini_get_method (MonoCompile *cfg, MonoMethod *m, guint32 token, MonoClass *klass, MonoGenericContext *context)
7390 MonoMethod *method = mini_get_method_allow_open (m, token, klass, context);
7392 if (method && cfg && !cfg->gshared && mono_class_is_open_constructed_type (&method->klass->byval_arg))
7398 static inline MonoClass*
7399 mini_get_class (MonoMethod *method, guint32 token, MonoGenericContext *context)
7404 if (method->wrapper_type != MONO_WRAPPER_NONE) {
7405 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
7407 klass = mono_class_inflate_generic_class (klass, context);
7409 klass = mono_class_get_and_inflate_typespec_checked (method->klass->image, token, context, &error);
7410 mono_error_cleanup (&error); /* FIXME don't swallow the error */
7413 mono_class_init (klass);
7417 static inline MonoMethodSignature*
7418 mini_get_signature (MonoMethod *method, guint32 token, MonoGenericContext *context)
7420 MonoMethodSignature *fsig;
7422 if (method->wrapper_type != MONO_WRAPPER_NONE) {
7423 fsig = (MonoMethodSignature *)mono_method_get_wrapper_data (method, token);
7425 fsig = mono_metadata_parse_signature (method->klass->image, token);
7429 fsig = mono_inflate_generic_signature(fsig, context, &error);
7431 g_assert(mono_error_ok(&error));
7437 throw_exception (void)
7439 static MonoMethod *method = NULL;
7442 MonoSecurityManager *secman = mono_security_manager_get_methods ();
7443 method = mono_class_get_method_from_name (secman->securitymanager, "ThrowException", 1);
7450 emit_throw_exception (MonoCompile *cfg, MonoException *ex)
7452 MonoMethod *thrower = throw_exception ();
7455 EMIT_NEW_PCONST (cfg, args [0], ex);
7456 mono_emit_method_call (cfg, thrower, args, NULL);
7460 * Return the original method is a wrapper is specified. We can only access
7461 * the custom attributes from the original method.
7464 get_original_method (MonoMethod *method)
7466 if (method->wrapper_type == MONO_WRAPPER_NONE)
7469 /* native code (which is like Critical) can call any managed method XXX FIXME XXX to validate all usages */
7470 if (method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED)
7473 /* in other cases we need to find the original method */
7474 return mono_marshal_method_from_wrapper (method);
7478 ensure_method_is_allowed_to_access_field (MonoCompile *cfg, MonoMethod *caller, MonoClassField *field)
7480 /* we can't get the coreclr security level on wrappers since they don't have the attributes */
7481 MonoException *ex = mono_security_core_clr_is_field_access_allowed (get_original_method (caller), field);
7483 emit_throw_exception (cfg, ex);
7487 ensure_method_is_allowed_to_call_method (MonoCompile *cfg, MonoMethod *caller, MonoMethod *callee)
7489 /* we can't get the coreclr security level on wrappers since they don't have the attributes */
7490 MonoException *ex = mono_security_core_clr_is_call_allowed (get_original_method (caller), callee);
7492 emit_throw_exception (cfg, ex);
7496 * Check that the IL instructions at ip are the array initialization
7497 * sequence and return the pointer to the data and the size.
7500 initialize_array_data (MonoMethod *method, gboolean aot, unsigned char *ip, MonoClass *klass, guint32 len, int *out_size, guint32 *out_field_token)
7503 * newarr[System.Int32]
7505 * ldtoken field valuetype ...
7506 * call void class [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(class [mscorlib]System.Array, valuetype [mscorlib]System.RuntimeFieldHandle)
7508 if (ip [0] == CEE_DUP && ip [1] == CEE_LDTOKEN && ip [5] == 0x4 && ip [6] == CEE_CALL) {
7510 guint32 token = read32 (ip + 7);
7511 guint32 field_token = read32 (ip + 2);
7512 guint32 field_index = field_token & 0xffffff;
7514 const char *data_ptr;
7516 MonoMethod *cmethod;
7517 MonoClass *dummy_class;
7518 MonoClassField *field = mono_field_from_token_checked (method->klass->image, field_token, &dummy_class, NULL, &error);
7522 mono_error_cleanup (&error); /* FIXME don't swallow the error */
7526 *out_field_token = field_token;
7528 cmethod = mini_get_method (NULL, method, token, NULL, NULL);
7531 if (strcmp (cmethod->name, "InitializeArray") || strcmp (cmethod->klass->name, "RuntimeHelpers") || cmethod->klass->image != mono_defaults.corlib)
7533 switch (mono_type_get_underlying_type (&klass->byval_arg)->type) {
7534 case MONO_TYPE_BOOLEAN:
7538 /* we need to swap on big endian, so punt. Should we handle R4 and R8 as well? */
7539 #if TARGET_BYTE_ORDER == G_LITTLE_ENDIAN
7540 case MONO_TYPE_CHAR:
7557 if (size > mono_type_size (field->type, &dummy_align))
7560 /*g_print ("optimized in %s: size: %d, numelems: %d\n", method->name, size, newarr->inst_newa_len->inst_c0);*/
7561 if (!image_is_dynamic (method->klass->image)) {
7562 field_index = read32 (ip + 2) & 0xffffff;
7563 mono_metadata_field_info (method->klass->image, field_index - 1, NULL, &rva, NULL);
7564 data_ptr = mono_image_rva_map (method->klass->image, rva);
7565 /*g_print ("field: 0x%08x, rva: %d, rva_ptr: %p\n", read32 (ip + 2), rva, data_ptr);*/
7566 /* for aot code we do the lookup on load */
7567 if (aot && data_ptr)
7568 return (const char *)GUINT_TO_POINTER (rva);
7570 /*FIXME is it possible to AOT a SRE assembly not meant to be saved? */
7572 data_ptr = mono_field_get_data (field);
7580 set_exception_type_from_invalid_il (MonoCompile *cfg, MonoMethod *method, unsigned char *ip)
7582 char *method_fname = mono_method_full_name (method, TRUE);
7584 MonoMethodHeader *header = mono_method_get_header (method);
7586 if (header->code_size == 0)
7587 method_code = g_strdup ("method body is empty.");
7589 method_code = mono_disasm_code_one (NULL, method, ip, NULL);
7590 mono_cfg_set_exception (cfg, MONO_EXCEPTION_INVALID_PROGRAM);
7591 cfg->exception_message = g_strdup_printf ("Invalid IL code in %s: %s\n", method_fname, method_code);
7592 g_free (method_fname);
7593 g_free (method_code);
7594 cfg->headers_to_free = g_slist_prepend_mempool (cfg->mempool, cfg->headers_to_free, header);
7598 set_exception_object (MonoCompile *cfg, MonoException *exception)
7600 mono_cfg_set_exception (cfg, MONO_EXCEPTION_OBJECT_SUPPLIED);
7601 MONO_GC_REGISTER_ROOT_SINGLE (cfg->exception_ptr, MONO_ROOT_SOURCE_JIT, "jit exception");
7602 cfg->exception_ptr = exception;
7606 emit_stloc_ir (MonoCompile *cfg, MonoInst **sp, MonoMethodHeader *header, int n)
7609 guint32 opcode = mono_type_to_regmove (cfg, header->locals [n]);
7610 if ((opcode == OP_MOVE) && cfg->cbb->last_ins == sp [0] &&
7611 ((sp [0]->opcode == OP_ICONST) || (sp [0]->opcode == OP_I8CONST))) {
7612 /* Optimize reg-reg moves away */
7614 * Can't optimize other opcodes, since sp[0] might point to
7615 * the last ins of a decomposed opcode.
7617 sp [0]->dreg = (cfg)->locals [n]->dreg;
7619 EMIT_NEW_LOCSTORE (cfg, ins, n, *sp);
7624 * ldloca inhibits many optimizations so try to get rid of it in common
7627 static inline unsigned char *
7628 emit_optimized_ldloca_ir (MonoCompile *cfg, unsigned char *ip, unsigned char *end, int size)
7638 local = read16 (ip + 2);
7642 if (ip + 6 < end && (ip [0] == CEE_PREFIX1) && (ip [1] == CEE_INITOBJ) && ip_in_bb (cfg, cfg->cbb, ip + 1)) {
7643 /* From the INITOBJ case */
7644 token = read32 (ip + 2);
7645 klass = mini_get_class (cfg->current_method, token, cfg->generic_context);
7646 CHECK_TYPELOAD (klass);
7647 type = mini_get_underlying_type (&klass->byval_arg);
7648 emit_init_local (cfg, local, type, TRUE);
7656 is_exception_class (MonoClass *klass)
7659 if (klass == mono_defaults.exception_class)
7661 klass = klass->parent;
7667 * is_jit_optimizer_disabled:
7669 * Determine whenever M's assembly has a DebuggableAttribute with the
7670 * IsJITOptimizerDisabled flag set.
7673 is_jit_optimizer_disabled (MonoMethod *m)
7675 MonoAssembly *ass = m->klass->image->assembly;
7676 MonoCustomAttrInfo* attrs;
7677 static MonoClass *klass;
7679 gboolean val = FALSE;
7682 if (ass->jit_optimizer_disabled_inited)
7683 return ass->jit_optimizer_disabled;
7686 klass = mono_class_from_name (mono_defaults.corlib, "System.Diagnostics", "DebuggableAttribute");
7689 ass->jit_optimizer_disabled = FALSE;
7690 mono_memory_barrier ();
7691 ass->jit_optimizer_disabled_inited = TRUE;
7695 attrs = mono_custom_attrs_from_assembly (ass);
7697 for (i = 0; i < attrs->num_attrs; ++i) {
7698 MonoCustomAttrEntry *attr = &attrs->attrs [i];
7700 MonoMethodSignature *sig;
7702 if (!attr->ctor || attr->ctor->klass != klass)
7704 /* Decode the attribute. See reflection.c */
7705 p = (const char*)attr->data;
7706 g_assert (read16 (p) == 0x0001);
7709 // FIXME: Support named parameters
7710 sig = mono_method_signature (attr->ctor);
7711 if (sig->param_count != 2 || sig->params [0]->type != MONO_TYPE_BOOLEAN || sig->params [1]->type != MONO_TYPE_BOOLEAN)
7713 /* Two boolean arguments */
7717 mono_custom_attrs_free (attrs);
7720 ass->jit_optimizer_disabled = val;
7721 mono_memory_barrier ();
7722 ass->jit_optimizer_disabled_inited = TRUE;
7728 is_supported_tail_call (MonoCompile *cfg, MonoMethod *method, MonoMethod *cmethod, MonoMethodSignature *fsig, int call_opcode)
7730 gboolean supported_tail_call;
7733 supported_tail_call = mono_arch_tail_call_supported (cfg, mono_method_signature (method), mono_method_signature (cmethod));
7735 for (i = 0; i < fsig->param_count; ++i) {
7736 if (fsig->params [i]->byref || fsig->params [i]->type == MONO_TYPE_PTR || fsig->params [i]->type == MONO_TYPE_FNPTR)
7737 /* These can point to the current method's stack */
7738 supported_tail_call = FALSE;
7740 if (fsig->hasthis && cmethod->klass->valuetype)
7741 /* this might point to the current method's stack */
7742 supported_tail_call = FALSE;
7743 if (cmethod->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)
7744 supported_tail_call = FALSE;
7745 if (cfg->method->save_lmf)
7746 supported_tail_call = FALSE;
7747 if (cmethod->wrapper_type && cmethod->wrapper_type != MONO_WRAPPER_DYNAMIC_METHOD)
7748 supported_tail_call = FALSE;
7749 if (call_opcode != CEE_CALL)
7750 supported_tail_call = FALSE;
7752 /* Debugging support */
7754 if (supported_tail_call) {
7755 if (!mono_debug_count ())
7756 supported_tail_call = FALSE;
7760 return supported_tail_call;
7766 * Handle calls made to ctors from NEWOBJ opcodes.
7769 handle_ctor_call (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, int context_used,
7770 MonoInst **sp, guint8 *ip, int *inline_costs)
7772 MonoInst *vtable_arg = NULL, *callvirt_this_arg = NULL, *ins;
7774 if (cmethod->klass->valuetype && mono_class_generic_sharing_enabled (cmethod->klass) &&
7775 mono_method_is_generic_sharable (cmethod, TRUE)) {
7776 if (cmethod->is_inflated && mono_method_get_context (cmethod)->method_inst) {
7777 mono_class_vtable (cfg->domain, cmethod->klass);
7778 CHECK_TYPELOAD (cmethod->klass);
7780 vtable_arg = emit_get_rgctx_method (cfg, context_used,
7781 cmethod, MONO_RGCTX_INFO_METHOD_RGCTX);
7784 vtable_arg = emit_get_rgctx_klass (cfg, context_used,
7785 cmethod->klass, MONO_RGCTX_INFO_VTABLE);
7787 MonoVTable *vtable = mono_class_vtable (cfg->domain, cmethod->klass);
7789 CHECK_TYPELOAD (cmethod->klass);
7790 EMIT_NEW_VTABLECONST (cfg, vtable_arg, vtable);
7795 /* Avoid virtual calls to ctors if possible */
7796 if (mono_class_is_marshalbyref (cmethod->klass))
7797 callvirt_this_arg = sp [0];
7799 if (cmethod && (cfg->opt & MONO_OPT_INTRINS) && (ins = mini_emit_inst_for_ctor (cfg, cmethod, fsig, sp))) {
7800 g_assert (MONO_TYPE_IS_VOID (fsig->ret));
7801 CHECK_CFG_EXCEPTION;
7802 } else if ((cfg->opt & MONO_OPT_INLINE) && cmethod && !context_used && !vtable_arg &&
7803 mono_method_check_inlining (cfg, cmethod) &&
7804 !mono_class_is_subclass_of (cmethod->klass, mono_defaults.exception_class, FALSE)) {
7807 if ((costs = inline_method (cfg, cmethod, fsig, sp, ip, cfg->real_offset, FALSE))) {
7808 cfg->real_offset += 5;
7810 *inline_costs += costs - 5;
7812 INLINE_FAILURE ("inline failure");
7813 // FIXME-VT: Clean this up
7814 if (cfg->gsharedvt && mini_is_gsharedvt_signature (fsig))
7815 GSHAREDVT_FAILURE(*ip);
7816 mono_emit_method_call_full (cfg, cmethod, fsig, FALSE, sp, callvirt_this_arg, NULL, NULL);
7818 } else if (cfg->gsharedvt && mini_is_gsharedvt_signature (fsig)) {
7821 addr = emit_get_rgctx_gsharedvt_call (cfg, context_used, fsig, cmethod, MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE);
7823 if (cfg->llvm_only) {
7824 // FIXME: Avoid initializing vtable_arg
7825 emit_llvmonly_calli (cfg, fsig, sp, addr);
7827 mono_emit_calli (cfg, fsig, sp, addr, NULL, vtable_arg);
7829 } else if (context_used &&
7830 ((!mono_method_is_generic_sharable_full (cmethod, TRUE, FALSE, FALSE) ||
7831 !mono_class_generic_sharing_enabled (cmethod->klass)) || cfg->gsharedvt)) {
7832 MonoInst *cmethod_addr;
7834 /* Generic calls made out of gsharedvt methods cannot be patched, so use an indirect call */
7836 if (cfg->llvm_only) {
7837 MonoInst *addr = emit_get_rgctx_method (cfg, context_used, cmethod,
7838 MONO_RGCTX_INFO_GENERIC_METHOD_CODE);
7839 emit_llvmonly_calli (cfg, fsig, sp, addr);
7841 cmethod_addr = emit_get_rgctx_method (cfg, context_used,
7842 cmethod, MONO_RGCTX_INFO_GENERIC_METHOD_CODE);
7844 mono_emit_calli (cfg, fsig, sp, cmethod_addr, NULL, vtable_arg);
7847 INLINE_FAILURE ("ctor call");
7848 ins = mono_emit_method_call_full (cfg, cmethod, fsig, FALSE, sp,
7849 callvirt_this_arg, NULL, vtable_arg);
7856 emit_setret (MonoCompile *cfg, MonoInst *val)
7858 MonoType *ret_type = mini_get_underlying_type (mono_method_signature (cfg->method)->ret);
7861 if (mini_type_to_stind (cfg, ret_type) == CEE_STOBJ) {
7864 if (!cfg->vret_addr) {
7865 EMIT_NEW_VARSTORE (cfg, ins, cfg->ret, ret_type, val);
7867 EMIT_NEW_RETLOADA (cfg, ret_addr);
7869 EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STOREV_MEMBASE, ret_addr->dreg, 0, val->dreg);
7870 ins->klass = mono_class_from_mono_type (ret_type);
7873 #ifdef MONO_ARCH_SOFT_FLOAT_FALLBACK
7874 if (COMPILE_SOFT_FLOAT (cfg) && !ret_type->byref && ret_type->type == MONO_TYPE_R4) {
7875 MonoInst *iargs [1];
7879 conv = mono_emit_jit_icall (cfg, mono_fload_r4_arg, iargs);
7880 mono_arch_emit_setret (cfg, cfg->method, conv);
7882 mono_arch_emit_setret (cfg, cfg->method, val);
7885 mono_arch_emit_setret (cfg, cfg->method, val);
7891 * mono_method_to_ir:
7893 * Translate the .net IL into linear IR.
7896 mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_bblock, MonoBasicBlock *end_bblock,
7897 MonoInst *return_var, MonoInst **inline_args,
7898 guint inline_offset, gboolean is_virtual_call)
7901 MonoInst *ins, **sp, **stack_start;
7902 MonoBasicBlock *tblock = NULL, *init_localsbb = NULL;
7903 MonoSimpleBasicBlock *bb = NULL, *original_bb = NULL;
7904 MonoMethod *cmethod, *method_definition;
7905 MonoInst **arg_array;
7906 MonoMethodHeader *header;
7908 guint32 token, ins_flag;
7910 MonoClass *constrained_class = NULL;
7911 unsigned char *ip, *end, *target, *err_pos;
7912 MonoMethodSignature *sig;
7913 MonoGenericContext *generic_context = NULL;
7914 MonoGenericContainer *generic_container = NULL;
7915 MonoType **param_types;
7916 int i, n, start_new_bblock, dreg;
7917 int num_calls = 0, inline_costs = 0;
7918 int breakpoint_id = 0;
7920 GSList *class_inits = NULL;
7921 gboolean dont_verify, dont_verify_stloc, readonly = FALSE;
7923 gboolean init_locals, seq_points, skip_dead_blocks;
7924 gboolean sym_seq_points = FALSE;
7925 MonoDebugMethodInfo *minfo;
7926 MonoBitSet *seq_point_locs = NULL;
7927 MonoBitSet *seq_point_set_locs = NULL;
7929 cfg->disable_inline = is_jit_optimizer_disabled (method);
7931 /* serialization and xdomain stuff may need access to private fields and methods */
7932 dont_verify = method->klass->image->assembly->corlib_internal? TRUE: FALSE;
7933 dont_verify |= method->wrapper_type == MONO_WRAPPER_XDOMAIN_INVOKE;
7934 dont_verify |= method->wrapper_type == MONO_WRAPPER_XDOMAIN_DISPATCH;
7935 dont_verify |= method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE; /* bug #77896 */
7936 dont_verify |= method->wrapper_type == MONO_WRAPPER_COMINTEROP;
7937 dont_verify |= method->wrapper_type == MONO_WRAPPER_COMINTEROP_INVOKE;
7939 /* still some type unsafety issues in marshal wrappers... (unknown is PtrToStructure) */
7940 dont_verify_stloc = method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE;
7941 dont_verify_stloc |= method->wrapper_type == MONO_WRAPPER_UNKNOWN;
7942 dont_verify_stloc |= method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED;
7943 dont_verify_stloc |= method->wrapper_type == MONO_WRAPPER_STELEMREF;
7945 image = method->klass->image;
7946 header = mono_method_get_header (method);
7948 MonoLoaderError *error;
7950 if ((error = mono_loader_get_last_error ())) {
7951 mono_cfg_set_exception (cfg, error->exception_type);
7953 mono_cfg_set_exception (cfg, MONO_EXCEPTION_INVALID_PROGRAM);
7954 cfg->exception_message = g_strdup_printf ("Missing or incorrect header for method %s", cfg->method->name);
7956 goto exception_exit;
7958 generic_container = mono_method_get_generic_container (method);
7959 sig = mono_method_signature (method);
7960 num_args = sig->hasthis + sig->param_count;
7961 ip = (unsigned char*)header->code;
7962 cfg->cil_start = ip;
7963 end = ip + header->code_size;
7964 cfg->stat_cil_code_size += header->code_size;
7966 seq_points = cfg->gen_seq_points && cfg->method == method;
7968 if (method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED) {
7969 /* We could hit a seq point before attaching to the JIT (#8338) */
7973 if (cfg->gen_sdb_seq_points && cfg->method == method) {
7974 minfo = mono_debug_lookup_method (method);
7976 MonoSymSeqPoint *sps;
7977 int i, n_il_offsets;
7979 mono_debug_get_seq_points (minfo, NULL, NULL, NULL, &sps, &n_il_offsets);
7980 seq_point_locs = mono_bitset_mem_new (mono_mempool_alloc0 (cfg->mempool, mono_bitset_alloc_size (header->code_size, 0)), header->code_size, 0);
7981 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);
7982 sym_seq_points = TRUE;
7983 for (i = 0; i < n_il_offsets; ++i) {
7984 if (sps [i].il_offset < header->code_size)
7985 mono_bitset_set_fast (seq_point_locs, sps [i].il_offset);
7988 } else if (!method->wrapper_type && !method->dynamic && mono_debug_image_has_debug_info (method->klass->image)) {
7989 /* Methods without line number info like auto-generated property accessors */
7990 seq_point_locs = mono_bitset_mem_new (mono_mempool_alloc0 (cfg->mempool, mono_bitset_alloc_size (header->code_size, 0)), header->code_size, 0);
7991 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);
7992 sym_seq_points = TRUE;
7997 * Methods without init_locals set could cause asserts in various passes
7998 * (#497220). To work around this, we emit dummy initialization opcodes
7999 * (OP_DUMMY_ICONST etc.) which generate no code. These are only supported
8000 * on some platforms.
8002 if ((cfg->opt & MONO_OPT_UNSAFE) && cfg->backend->have_dummy_init)
8003 init_locals = header->init_locals;
8007 method_definition = method;
8008 while (method_definition->is_inflated) {
8009 MonoMethodInflated *imethod = (MonoMethodInflated *) method_definition;
8010 method_definition = imethod->declaring;
8013 /* SkipVerification is not allowed if core-clr is enabled */
8014 if (!dont_verify && mini_assembly_can_skip_verification (cfg->domain, method)) {
8016 dont_verify_stloc = TRUE;
8019 if (sig->is_inflated)
8020 generic_context = mono_method_get_context (method);
8021 else if (generic_container)
8022 generic_context = &generic_container->context;
8023 cfg->generic_context = generic_context;
8026 g_assert (!sig->has_type_parameters);
8028 if (sig->generic_param_count && method->wrapper_type == MONO_WRAPPER_NONE) {
8029 g_assert (method->is_inflated);
8030 g_assert (mono_method_get_context (method)->method_inst);
8032 if (method->is_inflated && mono_method_get_context (method)->method_inst)
8033 g_assert (sig->generic_param_count);
8035 if (cfg->method == method) {
8036 cfg->real_offset = 0;
8038 cfg->real_offset = inline_offset;
8041 cfg->cil_offset_to_bb = (MonoBasicBlock **)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoBasicBlock*) * header->code_size);
8042 cfg->cil_offset_to_bb_len = header->code_size;
8044 cfg->current_method = method;
8046 if (cfg->verbose_level > 2)
8047 printf ("method to IR %s\n", mono_method_full_name (method, TRUE));
8049 param_types = (MonoType **)mono_mempool_alloc (cfg->mempool, sizeof (MonoType*) * num_args);
8051 param_types [0] = method->klass->valuetype?&method->klass->this_arg:&method->klass->byval_arg;
8052 for (n = 0; n < sig->param_count; ++n)
8053 param_types [n + sig->hasthis] = sig->params [n];
8054 cfg->arg_types = param_types;
8056 cfg->dont_inline = g_list_prepend (cfg->dont_inline, method);
8057 if (cfg->method == method) {
8059 if (cfg->prof_options & MONO_PROFILE_INS_COVERAGE)
8060 cfg->coverage_info = mono_profiler_coverage_alloc (cfg->method, header->code_size);
8063 NEW_BBLOCK (cfg, start_bblock);
8064 cfg->bb_entry = start_bblock;
8065 start_bblock->cil_code = NULL;
8066 start_bblock->cil_length = 0;
8069 NEW_BBLOCK (cfg, end_bblock);
8070 cfg->bb_exit = end_bblock;
8071 end_bblock->cil_code = NULL;
8072 end_bblock->cil_length = 0;
8073 end_bblock->flags |= BB_INDIRECT_JUMP_TARGET;
8074 g_assert (cfg->num_bblocks == 2);
8076 arg_array = cfg->args;
8078 if (header->num_clauses) {
8079 cfg->spvars = g_hash_table_new (NULL, NULL);
8080 cfg->exvars = g_hash_table_new (NULL, NULL);
8082 /* handle exception clauses */
8083 for (i = 0; i < header->num_clauses; ++i) {
8084 MonoBasicBlock *try_bb;
8085 MonoExceptionClause *clause = &header->clauses [i];
8086 GET_BBLOCK (cfg, try_bb, ip + clause->try_offset);
8088 try_bb->real_offset = clause->try_offset;
8089 try_bb->try_start = TRUE;
8090 try_bb->region = ((i + 1) << 8) | clause->flags;
8091 GET_BBLOCK (cfg, tblock, ip + clause->handler_offset);
8092 tblock->real_offset = clause->handler_offset;
8093 tblock->flags |= BB_EXCEPTION_HANDLER;
8096 * Linking the try block with the EH block hinders inlining as we won't be able to
8097 * merge the bblocks from inlining and produce an artificial hole for no good reason.
8099 if (COMPILE_LLVM (cfg))
8100 link_bblock (cfg, try_bb, tblock);
8102 if (*(ip + clause->handler_offset) == CEE_POP)
8103 tblock->flags |= BB_EXCEPTION_DEAD_OBJ;
8105 if (clause->flags == MONO_EXCEPTION_CLAUSE_FINALLY ||
8106 clause->flags == MONO_EXCEPTION_CLAUSE_FILTER ||
8107 clause->flags == MONO_EXCEPTION_CLAUSE_FAULT) {
8108 MONO_INST_NEW (cfg, ins, OP_START_HANDLER);
8109 MONO_ADD_INS (tblock, ins);
8111 if (seq_points && clause->flags != MONO_EXCEPTION_CLAUSE_FINALLY && clause->flags != MONO_EXCEPTION_CLAUSE_FILTER) {
8112 /* finally clauses already have a seq point */
8113 /* seq points for filter clauses are emitted below */
8114 NEW_SEQ_POINT (cfg, ins, clause->handler_offset, TRUE);
8115 MONO_ADD_INS (tblock, ins);
8118 /* todo: is a fault block unsafe to optimize? */
8119 if (clause->flags == MONO_EXCEPTION_CLAUSE_FAULT)
8120 tblock->flags |= BB_EXCEPTION_UNSAFE;
8123 /*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);
8125 printf ("%s", mono_disasm_code_one (NULL, method, p, &p));
8127 /* catch and filter blocks get the exception object on the stack */
8128 if (clause->flags == MONO_EXCEPTION_CLAUSE_NONE ||
8129 clause->flags == MONO_EXCEPTION_CLAUSE_FILTER) {
8131 /* mostly like handle_stack_args (), but just sets the input args */
8132 /* printf ("handling clause at IL_%04x\n", clause->handler_offset); */
8133 tblock->in_scount = 1;
8134 tblock->in_stack = (MonoInst **)mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*));
8135 tblock->in_stack [0] = mono_create_exvar_for_offset (cfg, clause->handler_offset);
8139 #ifdef MONO_CONTEXT_SET_LLVM_EXC_REG
8140 /* The EH code passes in the exception in a register to both JITted and LLVM compiled code */
8141 if (!cfg->compile_llvm) {
8142 MONO_INST_NEW (cfg, ins, OP_GET_EX_OBJ);
8143 ins->dreg = tblock->in_stack [0]->dreg;
8144 MONO_ADD_INS (tblock, ins);
8147 MonoInst *dummy_use;
8150 * Add a dummy use for the exvar so its liveness info will be
8153 EMIT_NEW_DUMMY_USE (cfg, dummy_use, tblock->in_stack [0]);
8156 if (seq_points && clause->flags == MONO_EXCEPTION_CLAUSE_FILTER) {
8157 NEW_SEQ_POINT (cfg, ins, clause->handler_offset, TRUE);
8158 MONO_ADD_INS (tblock, ins);
8161 if (clause->flags == MONO_EXCEPTION_CLAUSE_FILTER) {
8162 GET_BBLOCK (cfg, tblock, ip + clause->data.filter_offset);
8163 tblock->flags |= BB_EXCEPTION_HANDLER;
8164 tblock->real_offset = clause->data.filter_offset;
8165 tblock->in_scount = 1;
8166 tblock->in_stack = (MonoInst **)mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*));
8167 /* The filter block shares the exvar with the handler block */
8168 tblock->in_stack [0] = mono_create_exvar_for_offset (cfg, clause->handler_offset);
8169 MONO_INST_NEW (cfg, ins, OP_START_HANDLER);
8170 MONO_ADD_INS (tblock, ins);
8174 if (clause->flags != MONO_EXCEPTION_CLAUSE_FILTER &&
8175 clause->data.catch_class &&
8177 mono_class_check_context_used (clause->data.catch_class)) {
8179 * In shared generic code with catch
8180 * clauses containing type variables
8181 * the exception handling code has to
8182 * be able to get to the rgctx.
8183 * Therefore we have to make sure that
8184 * the vtable/mrgctx argument (for
8185 * static or generic methods) or the
8186 * "this" argument (for non-static
8187 * methods) are live.
8189 if ((method->flags & METHOD_ATTRIBUTE_STATIC) ||
8190 mini_method_get_context (method)->method_inst ||
8191 method->klass->valuetype) {
8192 mono_get_vtable_var (cfg);
8194 MonoInst *dummy_use;
8196 EMIT_NEW_DUMMY_USE (cfg, dummy_use, arg_array [0]);
8201 arg_array = (MonoInst **) alloca (sizeof (MonoInst *) * num_args);
8202 cfg->cbb = start_bblock;
8203 cfg->args = arg_array;
8204 mono_save_args (cfg, sig, inline_args);
8207 /* FIRST CODE BLOCK */
8208 NEW_BBLOCK (cfg, tblock);
8209 tblock->cil_code = ip;
8213 ADD_BBLOCK (cfg, tblock);
8215 if (cfg->method == method) {
8216 breakpoint_id = mono_debugger_method_has_breakpoint (method);
8217 if (breakpoint_id) {
8218 MONO_INST_NEW (cfg, ins, OP_BREAK);
8219 MONO_ADD_INS (cfg->cbb, ins);
8223 /* we use a separate basic block for the initialization code */
8224 NEW_BBLOCK (cfg, init_localsbb);
8225 cfg->bb_init = init_localsbb;
8226 init_localsbb->real_offset = cfg->real_offset;
8227 start_bblock->next_bb = init_localsbb;
8228 init_localsbb->next_bb = cfg->cbb;
8229 link_bblock (cfg, start_bblock, init_localsbb);
8230 link_bblock (cfg, init_localsbb, cfg->cbb);
8232 cfg->cbb = init_localsbb;
8234 if (cfg->gsharedvt && cfg->method == method) {
8235 MonoGSharedVtMethodInfo *info;
8236 MonoInst *var, *locals_var;
8239 info = (MonoGSharedVtMethodInfo *)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoGSharedVtMethodInfo));
8240 info->method = cfg->method;
8241 info->count_entries = 16;
8242 info->entries = (MonoRuntimeGenericContextInfoTemplate *)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoRuntimeGenericContextInfoTemplate) * info->count_entries);
8243 cfg->gsharedvt_info = info;
8245 var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
8246 /* prevent it from being register allocated */
8247 //var->flags |= MONO_INST_VOLATILE;
8248 cfg->gsharedvt_info_var = var;
8250 ins = emit_get_rgctx_gsharedvt_method (cfg, mini_method_check_context_used (cfg, method), method, info);
8251 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, var->dreg, ins->dreg);
8253 /* Allocate locals */
8254 locals_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
8255 /* prevent it from being register allocated */
8256 //locals_var->flags |= MONO_INST_VOLATILE;
8257 cfg->gsharedvt_locals_var = locals_var;
8259 dreg = alloc_ireg (cfg);
8260 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI4_MEMBASE, dreg, var->dreg, MONO_STRUCT_OFFSET (MonoGSharedVtMethodRuntimeInfo, locals_size));
8262 MONO_INST_NEW (cfg, ins, OP_LOCALLOC);
8263 ins->dreg = locals_var->dreg;
8265 MONO_ADD_INS (cfg->cbb, ins);
8266 cfg->gsharedvt_locals_var_ins = ins;
8268 cfg->flags |= MONO_CFG_HAS_ALLOCA;
8271 ins->flags |= MONO_INST_INIT;
8275 if (mono_security_core_clr_enabled ()) {
8276 /* check if this is native code, e.g. an icall or a p/invoke */
8277 if (method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE) {
8278 MonoMethod *wrapped = mono_marshal_method_from_wrapper (method);
8280 gboolean pinvk = (wrapped->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL);
8281 gboolean icall = (wrapped->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL);
8283 /* if this ia a native call then it can only be JITted from platform code */
8284 if ((icall || pinvk) && method->klass && method->klass->image) {
8285 if (!mono_security_core_clr_is_platform_image (method->klass->image)) {
8286 MonoException *ex = icall ? mono_get_exception_security () :
8287 mono_get_exception_method_access ();
8288 emit_throw_exception (cfg, ex);
8295 CHECK_CFG_EXCEPTION;
8297 if (header->code_size == 0)
8300 if (get_basic_blocks (cfg, header, cfg->real_offset, ip, end, &err_pos)) {
8305 if (cfg->method == method)
8306 mono_debug_init_method (cfg, cfg->cbb, breakpoint_id);
8308 for (n = 0; n < header->num_locals; ++n) {
8309 if (header->locals [n]->type == MONO_TYPE_VOID && !header->locals [n]->byref)
8314 /* We force the vtable variable here for all shared methods
8315 for the possibility that they might show up in a stack
8316 trace where their exact instantiation is needed. */
8317 if (cfg->gshared && method == cfg->method) {
8318 if ((method->flags & METHOD_ATTRIBUTE_STATIC) ||
8319 mini_method_get_context (method)->method_inst ||
8320 method->klass->valuetype) {
8321 mono_get_vtable_var (cfg);
8323 /* FIXME: Is there a better way to do this?
8324 We need the variable live for the duration
8325 of the whole method. */
8326 cfg->args [0]->flags |= MONO_INST_VOLATILE;
8330 /* add a check for this != NULL to inlined methods */
8331 if (is_virtual_call) {
8334 NEW_ARGLOAD (cfg, arg_ins, 0);
8335 MONO_ADD_INS (cfg->cbb, arg_ins);
8336 MONO_EMIT_NEW_CHECK_THIS (cfg, arg_ins->dreg);
8339 skip_dead_blocks = !dont_verify;
8340 if (skip_dead_blocks) {
8341 original_bb = bb = mono_basic_block_split (method, &cfg->error);
8346 /* we use a spare stack slot in SWITCH and NEWOBJ and others */
8347 stack_start = sp = (MonoInst **)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoInst*) * (header->max_stack + 1));
8350 start_new_bblock = 0;
8352 if (cfg->method == method)
8353 cfg->real_offset = ip - header->code;
8355 cfg->real_offset = inline_offset;
8360 if (start_new_bblock) {
8361 cfg->cbb->cil_length = ip - cfg->cbb->cil_code;
8362 if (start_new_bblock == 2) {
8363 g_assert (ip == tblock->cil_code);
8365 GET_BBLOCK (cfg, tblock, ip);
8367 cfg->cbb->next_bb = tblock;
8369 start_new_bblock = 0;
8370 for (i = 0; i < cfg->cbb->in_scount; ++i) {
8371 if (cfg->verbose_level > 3)
8372 printf ("loading %d from temp %d\n", i, (int)cfg->cbb->in_stack [i]->inst_c0);
8373 EMIT_NEW_TEMPLOAD (cfg, ins, cfg->cbb->in_stack [i]->inst_c0);
8377 g_slist_free (class_inits);
8380 if ((tblock = cfg->cil_offset_to_bb [ip - cfg->cil_start]) && (tblock != cfg->cbb)) {
8381 link_bblock (cfg, cfg->cbb, tblock);
8382 if (sp != stack_start) {
8383 handle_stack_args (cfg, stack_start, sp - stack_start);
8385 CHECK_UNVERIFIABLE (cfg);
8387 cfg->cbb->next_bb = tblock;
8389 for (i = 0; i < cfg->cbb->in_scount; ++i) {
8390 if (cfg->verbose_level > 3)
8391 printf ("loading %d from temp %d\n", i, (int)cfg->cbb->in_stack [i]->inst_c0);
8392 EMIT_NEW_TEMPLOAD (cfg, ins, cfg->cbb->in_stack [i]->inst_c0);
8395 g_slist_free (class_inits);
8400 if (skip_dead_blocks) {
8401 int ip_offset = ip - header->code;
8403 if (ip_offset == bb->end)
8407 int op_size = mono_opcode_size (ip, end);
8408 g_assert (op_size > 0); /*The BB formation pass must catch all bad ops*/
8410 if (cfg->verbose_level > 3) printf ("SKIPPING DEAD OP at %x\n", ip_offset);
8412 if (ip_offset + op_size == bb->end) {
8413 MONO_INST_NEW (cfg, ins, OP_NOP);
8414 MONO_ADD_INS (cfg->cbb, ins);
8415 start_new_bblock = 1;
8423 * Sequence points are points where the debugger can place a breakpoint.
8424 * Currently, we generate these automatically at points where the IL
8427 if (seq_points && ((!sym_seq_points && (sp == stack_start)) || (sym_seq_points && mono_bitset_test_fast (seq_point_locs, ip - header->code)))) {
8429 * Make methods interruptable at the beginning, and at the targets of
8430 * backward branches.
8431 * Also, do this at the start of every bblock in methods with clauses too,
8432 * to be able to handle instructions with inprecise control flow like
8434 * Backward branches are handled at the end of method-to-ir ().
8436 gboolean intr_loc = ip == header->code || (!cfg->cbb->last_ins && cfg->header->num_clauses);
8437 gboolean sym_seq_point = sym_seq_points && mono_bitset_test_fast (seq_point_locs, ip - header->code);
8439 /* Avoid sequence points on empty IL like .volatile */
8440 // FIXME: Enable this
8441 //if (!(cfg->cbb->last_ins && cfg->cbb->last_ins->opcode == OP_SEQ_POINT)) {
8442 NEW_SEQ_POINT (cfg, ins, ip - header->code, intr_loc);
8443 if ((sp != stack_start) && !sym_seq_point)
8444 ins->flags |= MONO_INST_NONEMPTY_STACK;
8445 MONO_ADD_INS (cfg->cbb, ins);
8448 mono_bitset_set_fast (seq_point_set_locs, ip - header->code);
8451 cfg->cbb->real_offset = cfg->real_offset;
8453 if ((cfg->method == method) && cfg->coverage_info) {
8454 guint32 cil_offset = ip - header->code;
8455 cfg->coverage_info->data [cil_offset].cil_code = ip;
8457 /* TODO: Use an increment here */
8458 #if defined(TARGET_X86)
8459 MONO_INST_NEW (cfg, ins, OP_STORE_MEM_IMM);
8460 ins->inst_p0 = &(cfg->coverage_info->data [cil_offset].count);
8462 MONO_ADD_INS (cfg->cbb, ins);
8464 EMIT_NEW_PCONST (cfg, ins, &(cfg->coverage_info->data [cil_offset].count));
8465 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STORE_MEMBASE_IMM, ins->dreg, 0, 1);
8469 if (cfg->verbose_level > 3)
8470 printf ("converting (in B%d: stack: %d) %s", cfg->cbb->block_num, (int)(sp - stack_start), mono_disasm_code_one (NULL, method, ip, NULL));
8474 if (seq_points && !sym_seq_points && sp != stack_start) {
8476 * The C# compiler uses these nops to notify the JIT that it should
8477 * insert seq points.
8479 NEW_SEQ_POINT (cfg, ins, ip - header->code, FALSE);
8480 MONO_ADD_INS (cfg->cbb, ins);
8482 if (cfg->keep_cil_nops)
8483 MONO_INST_NEW (cfg, ins, OP_HARD_NOP);
8485 MONO_INST_NEW (cfg, ins, OP_NOP);
8487 MONO_ADD_INS (cfg->cbb, ins);
8490 if (should_insert_brekpoint (cfg->method)) {
8491 ins = mono_emit_jit_icall (cfg, mono_debugger_agent_user_break, NULL);
8493 MONO_INST_NEW (cfg, ins, OP_NOP);
8496 MONO_ADD_INS (cfg->cbb, ins);
8502 CHECK_STACK_OVF (1);
8503 n = (*ip)-CEE_LDARG_0;
8505 EMIT_NEW_ARGLOAD (cfg, ins, n);
8513 CHECK_STACK_OVF (1);
8514 n = (*ip)-CEE_LDLOC_0;
8516 EMIT_NEW_LOCLOAD (cfg, ins, n);
8525 n = (*ip)-CEE_STLOC_0;
8528 if (!dont_verify_stloc && target_type_is_incompatible (cfg, header->locals [n], *sp))
8530 emit_stloc_ir (cfg, sp, header, n);
8537 CHECK_STACK_OVF (1);
8540 EMIT_NEW_ARGLOAD (cfg, ins, n);
8546 CHECK_STACK_OVF (1);
8549 NEW_ARGLOADA (cfg, ins, n);
8550 MONO_ADD_INS (cfg->cbb, ins);
8560 if (!dont_verify_stloc && target_type_is_incompatible (cfg, param_types [ip [1]], *sp))
8562 EMIT_NEW_ARGSTORE (cfg, ins, n, *sp);
8567 CHECK_STACK_OVF (1);
8570 EMIT_NEW_LOCLOAD (cfg, ins, n);
8574 case CEE_LDLOCA_S: {
8575 unsigned char *tmp_ip;
8577 CHECK_STACK_OVF (1);
8578 CHECK_LOCAL (ip [1]);
8580 if ((tmp_ip = emit_optimized_ldloca_ir (cfg, ip, end, 1))) {
8586 EMIT_NEW_LOCLOADA (cfg, ins, ip [1]);
8595 CHECK_LOCAL (ip [1]);
8596 if (!dont_verify_stloc && target_type_is_incompatible (cfg, header->locals [ip [1]], *sp))
8598 emit_stloc_ir (cfg, sp, header, ip [1]);
8603 CHECK_STACK_OVF (1);
8604 EMIT_NEW_PCONST (cfg, ins, NULL);
8605 ins->type = STACK_OBJ;
8610 CHECK_STACK_OVF (1);
8611 EMIT_NEW_ICONST (cfg, ins, -1);
8624 CHECK_STACK_OVF (1);
8625 EMIT_NEW_ICONST (cfg, ins, (*ip) - CEE_LDC_I4_0);
8631 CHECK_STACK_OVF (1);
8633 EMIT_NEW_ICONST (cfg, ins, *((signed char*)ip));
8639 CHECK_STACK_OVF (1);
8640 EMIT_NEW_ICONST (cfg, ins, (gint32)read32 (ip + 1));
8646 CHECK_STACK_OVF (1);
8647 MONO_INST_NEW (cfg, ins, OP_I8CONST);
8648 ins->type = STACK_I8;
8649 ins->dreg = alloc_dreg (cfg, STACK_I8);
8651 ins->inst_l = (gint64)read64 (ip);
8652 MONO_ADD_INS (cfg->cbb, ins);
8658 gboolean use_aotconst = FALSE;
8660 #ifdef TARGET_POWERPC
8661 /* FIXME: Clean this up */
8662 if (cfg->compile_aot)
8663 use_aotconst = TRUE;
8666 /* FIXME: we should really allocate this only late in the compilation process */
8667 f = (float *)mono_domain_alloc (cfg->domain, sizeof (float));
8669 CHECK_STACK_OVF (1);
8675 EMIT_NEW_AOTCONST (cfg, cons, MONO_PATCH_INFO_R4, f);
8677 dreg = alloc_freg (cfg);
8678 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADR4_MEMBASE, dreg, cons->dreg, 0);
8679 ins->type = cfg->r4_stack_type;
8681 MONO_INST_NEW (cfg, ins, OP_R4CONST);
8682 ins->type = cfg->r4_stack_type;
8683 ins->dreg = alloc_dreg (cfg, STACK_R8);
8685 MONO_ADD_INS (cfg->cbb, ins);
8695 gboolean use_aotconst = FALSE;
8697 #ifdef TARGET_POWERPC
8698 /* FIXME: Clean this up */
8699 if (cfg->compile_aot)
8700 use_aotconst = TRUE;
8703 /* FIXME: we should really allocate this only late in the compilation process */
8704 d = (double *)mono_domain_alloc (cfg->domain, sizeof (double));
8706 CHECK_STACK_OVF (1);
8712 EMIT_NEW_AOTCONST (cfg, cons, MONO_PATCH_INFO_R8, d);
8714 dreg = alloc_freg (cfg);
8715 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADR8_MEMBASE, dreg, cons->dreg, 0);
8716 ins->type = STACK_R8;
8718 MONO_INST_NEW (cfg, ins, OP_R8CONST);
8719 ins->type = STACK_R8;
8720 ins->dreg = alloc_dreg (cfg, STACK_R8);
8722 MONO_ADD_INS (cfg->cbb, ins);
8731 MonoInst *temp, *store;
8733 CHECK_STACK_OVF (1);
8737 temp = mono_compile_create_var (cfg, type_from_stack_type (ins), OP_LOCAL);
8738 EMIT_NEW_TEMPSTORE (cfg, store, temp->inst_c0, ins);
8740 EMIT_NEW_TEMPLOAD (cfg, ins, temp->inst_c0);
8743 EMIT_NEW_TEMPLOAD (cfg, ins, temp->inst_c0);
8756 if (sp [0]->type == STACK_R8)
8757 /* we need to pop the value from the x86 FP stack */
8758 MONO_EMIT_NEW_UNALU (cfg, OP_X86_FPOP, -1, sp [0]->dreg);
8763 MonoMethodSignature *fsig;
8766 INLINE_FAILURE ("jmp");
8767 GSHAREDVT_FAILURE (*ip);
8770 if (stack_start != sp)
8772 token = read32 (ip + 1);
8773 /* FIXME: check the signature matches */
8774 cmethod = mini_get_method (cfg, method, token, NULL, generic_context);
8776 if (!cmethod || mono_loader_get_last_error ())
8779 if (cfg->gshared && mono_method_check_context_used (cmethod))
8780 GENERIC_SHARING_FAILURE (CEE_JMP);
8782 emit_instrumentation_call (cfg, mono_profiler_method_leave);
8784 fsig = mono_method_signature (cmethod);
8785 n = fsig->param_count + fsig->hasthis;
8786 if (cfg->llvm_only) {
8789 args = (MonoInst **)mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*) * n);
8790 for (i = 0; i < n; ++i)
8791 EMIT_NEW_ARGLOAD (cfg, args [i], i);
8792 ins = mono_emit_method_call_full (cfg, cmethod, fsig, TRUE, args, NULL, NULL, NULL);
8794 * The code in mono-basic-block.c treats the rest of the code as dead, but we
8795 * have to emit a normal return since llvm expects it.
8798 emit_setret (cfg, ins);
8799 MONO_INST_NEW (cfg, ins, OP_BR);
8800 ins->inst_target_bb = end_bblock;
8801 MONO_ADD_INS (cfg->cbb, ins);
8802 link_bblock (cfg, cfg->cbb, end_bblock);
8805 } else if (cfg->backend->have_op_tail_call) {
8806 /* Handle tail calls similarly to calls */
8809 MONO_INST_NEW_CALL (cfg, call, OP_TAILCALL);
8810 call->method = cmethod;
8811 call->tail_call = TRUE;
8812 call->signature = mono_method_signature (cmethod);
8813 call->args = (MonoInst **)mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*) * n);
8814 call->inst.inst_p0 = cmethod;
8815 for (i = 0; i < n; ++i)
8816 EMIT_NEW_ARGLOAD (cfg, call->args [i], i);
8818 mono_arch_emit_call (cfg, call);
8819 cfg->param_area = MAX(cfg->param_area, call->stack_usage);
8820 MONO_ADD_INS (cfg->cbb, (MonoInst*)call);
8822 for (i = 0; i < num_args; ++i)
8823 /* Prevent arguments from being optimized away */
8824 arg_array [i]->flags |= MONO_INST_VOLATILE;
8826 MONO_INST_NEW_CALL (cfg, call, OP_JMP);
8827 ins = (MonoInst*)call;
8828 ins->inst_p0 = cmethod;
8829 MONO_ADD_INS (cfg->cbb, ins);
8833 start_new_bblock = 1;
8838 MonoMethodSignature *fsig;
8841 token = read32 (ip + 1);
8845 //GSHAREDVT_FAILURE (*ip);
8850 fsig = mini_get_signature (method, token, generic_context);
8852 if (method->dynamic && fsig->pinvoke) {
8856 * This is a call through a function pointer using a pinvoke
8857 * signature. Have to create a wrapper and call that instead.
8858 * FIXME: This is very slow, need to create a wrapper at JIT time
8859 * instead based on the signature.
8861 EMIT_NEW_IMAGECONST (cfg, args [0], method->klass->image);
8862 EMIT_NEW_PCONST (cfg, args [1], fsig);
8864 addr = mono_emit_jit_icall (cfg, mono_get_native_calli_wrapper, args);
8867 n = fsig->param_count + fsig->hasthis;
8871 //g_assert (!virtual_ || fsig->hasthis);
8875 inline_costs += 10 * num_calls++;
8878 * Making generic calls out of gsharedvt methods.
8879 * This needs to be used for all generic calls, not just ones with a gsharedvt signature, to avoid
8880 * patching gshared method addresses into a gsharedvt method.
8882 if (cfg->gsharedvt && mini_is_gsharedvt_signature (fsig)) {
8884 * We pass the address to the gsharedvt trampoline in the rgctx reg
8886 MonoInst *callee = addr;
8888 if (method->wrapper_type != MONO_WRAPPER_DELEGATE_INVOKE)
8890 GSHAREDVT_FAILURE (*ip);
8894 GSHAREDVT_FAILURE (*ip);
8896 addr = emit_get_rgctx_sig (cfg, context_used,
8897 fsig, MONO_RGCTX_INFO_SIG_GSHAREDVT_OUT_TRAMPOLINE_CALLI);
8898 ins = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr, NULL, callee);
8902 /* Prevent inlining of methods with indirect calls */
8903 INLINE_FAILURE ("indirect call");
8905 if (addr->opcode == OP_PCONST || addr->opcode == OP_AOTCONST || addr->opcode == OP_GOT_ENTRY) {
8906 MonoJumpInfoType info_type;
8910 * Instead of emitting an indirect call, emit a direct call
8911 * with the contents of the aotconst as the patch info.
8913 if (addr->opcode == OP_PCONST || addr->opcode == OP_AOTCONST) {
8914 info_type = (MonoJumpInfoType)addr->inst_c1;
8915 info_data = addr->inst_p0;
8917 info_type = (MonoJumpInfoType)addr->inst_right->inst_c1;
8918 info_data = addr->inst_right->inst_left;
8921 if (info_type == MONO_PATCH_INFO_ICALL_ADDR || info_type == MONO_PATCH_INFO_JIT_ICALL_ADDR) {
8922 ins = (MonoInst*)mono_emit_abs_call (cfg, info_type, info_data, fsig, sp);
8927 ins = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr, NULL, NULL);
8931 /* End of call, INS should contain the result of the call, if any */
8933 if (!MONO_TYPE_IS_VOID (fsig->ret)) {
8935 *sp++ = mono_emit_widen_call_res (cfg, ins, fsig);
8938 CHECK_CFG_EXCEPTION;
8942 constrained_class = NULL;
8946 case CEE_CALLVIRT: {
8947 MonoInst *addr = NULL;
8948 MonoMethodSignature *fsig = NULL;
8950 int virtual_ = *ip == CEE_CALLVIRT;
8951 gboolean pass_imt_from_rgctx = FALSE;
8952 MonoInst *imt_arg = NULL;
8953 MonoInst *keep_this_alive = NULL;
8954 gboolean pass_vtable = FALSE;
8955 gboolean pass_mrgctx = FALSE;
8956 MonoInst *vtable_arg = NULL;
8957 gboolean check_this = FALSE;
8958 gboolean supported_tail_call = FALSE;
8959 gboolean tail_call = FALSE;
8960 gboolean need_seq_point = FALSE;
8961 guint32 call_opcode = *ip;
8962 gboolean emit_widen = TRUE;
8963 gboolean push_res = TRUE;
8964 gboolean skip_ret = FALSE;
8965 gboolean delegate_invoke = FALSE;
8966 gboolean direct_icall = FALSE;
8967 gboolean constrained_partial_call = FALSE;
8968 MonoMethod *cil_method;
8971 token = read32 (ip + 1);
8975 cmethod = mini_get_method (cfg, method, token, NULL, generic_context);
8976 cil_method = cmethod;
8978 if (constrained_class) {
8979 if ((constrained_class->byval_arg.type == MONO_TYPE_VAR || constrained_class->byval_arg.type == MONO_TYPE_MVAR) && cfg->gshared) {
8980 if (!mini_is_gsharedvt_klass (constrained_class)) {
8981 g_assert (!cmethod->klass->valuetype);
8982 if (!mini_type_is_reference (&constrained_class->byval_arg))
8983 constrained_partial_call = TRUE;
8987 if (method->wrapper_type != MONO_WRAPPER_NONE) {
8988 if (cfg->verbose_level > 2)
8989 printf ("DM Constrained call to %s\n", mono_type_get_full_name (constrained_class));
8990 if (!((constrained_class->byval_arg.type == MONO_TYPE_VAR ||
8991 constrained_class->byval_arg.type == MONO_TYPE_MVAR) &&
8993 cmethod = mono_get_method_constrained_with_method (image, cil_method, constrained_class, generic_context, &cfg->error);
8997 if (cfg->verbose_level > 2)
8998 printf ("Constrained call to %s\n", mono_type_get_full_name (constrained_class));
9000 if ((constrained_class->byval_arg.type == MONO_TYPE_VAR || constrained_class->byval_arg.type == MONO_TYPE_MVAR) && cfg->gshared) {
9002 * This is needed since get_method_constrained can't find
9003 * the method in klass representing a type var.
9004 * The type var is guaranteed to be a reference type in this
9007 if (!mini_is_gsharedvt_klass (constrained_class))
9008 g_assert (!cmethod->klass->valuetype);
9010 cmethod = mono_get_method_constrained_checked (image, token, constrained_class, generic_context, &cil_method, &cfg->error);
9016 if (!cmethod || mono_loader_get_last_error ())
9018 if (!dont_verify && !cfg->skip_visibility) {
9019 MonoMethod *target_method = cil_method;
9020 if (method->is_inflated) {
9021 target_method = mini_get_method_allow_open (method, token, NULL, &(mono_method_get_generic_container (method_definition)->context));
9023 if (!mono_method_can_access_method (method_definition, target_method) &&
9024 !mono_method_can_access_method (method, cil_method))
9025 METHOD_ACCESS_FAILURE (method, cil_method);
9028 if (mono_security_core_clr_enabled ())
9029 ensure_method_is_allowed_to_call_method (cfg, method, cil_method);
9031 if (!virtual_ && (cmethod->flags & METHOD_ATTRIBUTE_ABSTRACT))
9032 /* MS.NET seems to silently convert this to a callvirt */
9037 * MS.NET accepts non virtual calls to virtual final methods of transparent proxy classes and
9038 * converts to a callvirt.
9040 * tests/bug-515884.il is an example of this behavior
9042 const int test_flags = METHOD_ATTRIBUTE_VIRTUAL | METHOD_ATTRIBUTE_FINAL | METHOD_ATTRIBUTE_STATIC;
9043 const int expected_flags = METHOD_ATTRIBUTE_VIRTUAL | METHOD_ATTRIBUTE_FINAL;
9044 if (!virtual_ && mono_class_is_marshalbyref (cmethod->klass) && (cmethod->flags & test_flags) == expected_flags && cfg->method->wrapper_type == MONO_WRAPPER_NONE)
9048 if (!cmethod->klass->inited)
9049 if (!mono_class_init (cmethod->klass))
9050 TYPE_LOAD_ERROR (cmethod->klass);
9052 fsig = mono_method_signature (cmethod);
9055 if (cmethod->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL &&
9056 mini_class_is_system_array (cmethod->klass)) {
9057 array_rank = cmethod->klass->rank;
9058 } else if ((cmethod->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) && icall_is_direct_callable (cfg, cmethod)) {
9059 direct_icall = TRUE;
9060 } else if (fsig->pinvoke) {
9061 MonoMethod *wrapper = mono_marshal_get_native_wrapper (cmethod, TRUE, cfg->compile_aot);
9062 fsig = mono_method_signature (wrapper);
9063 } else if (constrained_class) {
9065 fsig = mono_method_get_signature_checked (cmethod, image, token, generic_context, &cfg->error);
9069 if (cfg->llvm_only && !cfg->method->wrapper_type)
9070 cfg->signatures = g_slist_prepend_mempool (cfg->mempool, cfg->signatures, fsig);
9072 /* See code below */
9073 if (cmethod->klass == mono_defaults.monitor_class && !strcmp (cmethod->name, "Enter") && mono_method_signature (cmethod)->param_count == 1) {
9074 MonoBasicBlock *tbb;
9076 GET_BBLOCK (cfg, tbb, ip + 5);
9077 if (tbb->try_start && MONO_REGION_FLAGS(tbb->region) == MONO_EXCEPTION_CLAUSE_FINALLY) {
9079 * We want to extend the try block to cover the call, but we can't do it if the
9080 * call is made directly since its followed by an exception check.
9082 direct_icall = FALSE;
9086 mono_save_token_info (cfg, image, token, cil_method);
9088 if (!(seq_point_locs && mono_bitset_test_fast (seq_point_locs, ip + 5 - header->code)))
9089 need_seq_point = TRUE;
9091 /* Don't support calls made using type arguments for now */
9093 if (cfg->gsharedvt) {
9094 if (mini_is_gsharedvt_signature (fsig))
9095 GSHAREDVT_FAILURE (*ip);
9099 if (cmethod->string_ctor && method->wrapper_type != MONO_WRAPPER_RUNTIME_INVOKE)
9100 g_assert_not_reached ();
9102 n = fsig->param_count + fsig->hasthis;
9104 if (!cfg->gshared && cmethod->klass->generic_container)
9108 g_assert (!mono_method_check_context_used (cmethod));
9112 //g_assert (!virtual_ || fsig->hasthis);
9117 * We have the `constrained.' prefix opcode.
9119 if (constrained_class) {
9120 if (mini_is_gsharedvt_klass (constrained_class)) {
9121 if ((cmethod->klass != mono_defaults.object_class) && constrained_class->valuetype && cmethod->klass->valuetype) {
9122 /* The 'Own method' case below */
9123 } else if (cmethod->klass->image != mono_defaults.corlib && !(cmethod->klass->flags & TYPE_ATTRIBUTE_INTERFACE) && !cmethod->klass->valuetype) {
9124 /* 'The type parameter is instantiated as a reference type' case below. */
9126 ins = handle_constrained_gsharedvt_call (cfg, cmethod, fsig, sp, constrained_class, &emit_widen);
9127 CHECK_CFG_EXCEPTION;
9133 if (constrained_partial_call) {
9134 gboolean need_box = TRUE;
9137 * The receiver is a valuetype, but the exact type is not known at compile time. This means the
9138 * called method is not known at compile time either. The called method could end up being
9139 * one of the methods on the parent classes (object/valuetype/enum), in which case we need
9140 * to box the receiver.
9141 * A simple solution would be to box always and make a normal virtual call, but that would
9142 * be bad performance wise.
9144 if (cmethod->klass->flags & TYPE_ATTRIBUTE_INTERFACE && cmethod->klass->generic_class) {
9146 * The parent classes implement no generic interfaces, so the called method will be a vtype method, so no boxing neccessary.
9151 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)) {
9152 /* The called method is not virtual, i.e. Object:GetType (), the receiver is a vtype, has to box */
9153 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &constrained_class->byval_arg, sp [0]->dreg, 0);
9154 ins->klass = constrained_class;
9155 sp [0] = handle_box (cfg, ins, constrained_class, mono_class_check_context_used (constrained_class));
9156 CHECK_CFG_EXCEPTION;
9157 } else if (need_box) {
9159 MonoBasicBlock *is_ref_bb, *end_bb;
9160 MonoInst *nonbox_call;
9163 * Determine at runtime whenever the called method is defined on object/valuetype/enum, and emit a boxing call
9165 * FIXME: It is possible to inline the called method in a lot of cases, i.e. for T_INT,
9166 * the no-box case goes to a method in Int32, while the box case goes to a method in Enum.
9168 addr = emit_get_rgctx_virt_method (cfg, mono_class_check_context_used (constrained_class), constrained_class, cmethod, MONO_RGCTX_INFO_VIRT_METHOD_CODE);
9170 NEW_BBLOCK (cfg, is_ref_bb);
9171 NEW_BBLOCK (cfg, end_bb);
9173 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);
9174 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, box_type->dreg, MONO_GSHAREDVT_BOX_TYPE_REF);
9175 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, is_ref_bb);
9178 nonbox_call = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr, NULL, NULL);
9180 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
9183 MONO_START_BB (cfg, is_ref_bb);
9184 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &constrained_class->byval_arg, sp [0]->dreg, 0);
9185 ins->klass = constrained_class;
9186 sp [0] = handle_box (cfg, ins, constrained_class, mono_class_check_context_used (constrained_class));
9187 ins = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr, NULL, NULL);
9189 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
9191 MONO_START_BB (cfg, end_bb);
9194 nonbox_call->dreg = ins->dreg;
9197 g_assert (cmethod->klass->flags & TYPE_ATTRIBUTE_INTERFACE);
9198 addr = emit_get_rgctx_virt_method (cfg, mono_class_check_context_used (constrained_class), constrained_class, cmethod, MONO_RGCTX_INFO_VIRT_METHOD_CODE);
9199 ins = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr, NULL, NULL);
9202 } else if (constrained_class->valuetype && (cmethod->klass == mono_defaults.object_class || cmethod->klass == mono_defaults.enum_class->parent || cmethod->klass == mono_defaults.enum_class)) {
9204 * The type parameter is instantiated as a valuetype,
9205 * but that type doesn't override the method we're
9206 * calling, so we need to box `this'.
9208 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &constrained_class->byval_arg, sp [0]->dreg, 0);
9209 ins->klass = constrained_class;
9210 sp [0] = handle_box (cfg, ins, constrained_class, mono_class_check_context_used (constrained_class));
9211 CHECK_CFG_EXCEPTION;
9212 } else if (!constrained_class->valuetype) {
9213 int dreg = alloc_ireg_ref (cfg);
9216 * The type parameter is instantiated as a reference
9217 * type. We have a managed pointer on the stack, so
9218 * we need to dereference it here.
9220 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, dreg, sp [0]->dreg, 0);
9221 ins->type = STACK_OBJ;
9224 if (cmethod->klass->valuetype) {
9227 /* Interface method */
9230 mono_class_setup_vtable (constrained_class);
9231 CHECK_TYPELOAD (constrained_class);
9232 ioffset = mono_class_interface_offset (constrained_class, cmethod->klass);
9234 TYPE_LOAD_ERROR (constrained_class);
9235 slot = mono_method_get_vtable_slot (cmethod);
9237 TYPE_LOAD_ERROR (cmethod->klass);
9238 cmethod = constrained_class->vtable [ioffset + slot];
9240 if (cmethod->klass == mono_defaults.enum_class) {
9241 /* Enum implements some interfaces, so treat this as the first case */
9242 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &constrained_class->byval_arg, sp [0]->dreg, 0);
9243 ins->klass = constrained_class;
9244 sp [0] = handle_box (cfg, ins, constrained_class, mono_class_check_context_used (constrained_class));
9245 CHECK_CFG_EXCEPTION;
9250 constrained_class = NULL;
9253 if (check_call_signature (cfg, fsig, sp))
9256 if ((cmethod->klass->parent == mono_defaults.multicastdelegate_class) && !strcmp (cmethod->name, "Invoke"))
9257 delegate_invoke = TRUE;
9259 if ((cfg->opt & MONO_OPT_INTRINS) && (ins = mini_emit_inst_for_sharable_method (cfg, cmethod, fsig, sp))) {
9260 if (!MONO_TYPE_IS_VOID (fsig->ret)) {
9261 type_to_eval_stack_type ((cfg), fsig->ret, ins);
9269 * If the callee is a shared method, then its static cctor
9270 * might not get called after the call was patched.
9272 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)) {
9273 emit_class_init (cfg, cmethod->klass);
9274 CHECK_TYPELOAD (cmethod->klass);
9277 check_method_sharing (cfg, cmethod, &pass_vtable, &pass_mrgctx);
9280 MonoGenericContext *cmethod_context = mono_method_get_context (cmethod);
9282 context_used = mini_method_check_context_used (cfg, cmethod);
9284 if (context_used && (cmethod->klass->flags & TYPE_ATTRIBUTE_INTERFACE)) {
9285 /* Generic method interface
9286 calls are resolved via a
9287 helper function and don't
9289 if (!cmethod_context || !cmethod_context->method_inst)
9290 pass_imt_from_rgctx = TRUE;
9294 * If a shared method calls another
9295 * shared method then the caller must
9296 * have a generic sharing context
9297 * because the magic trampoline
9298 * requires it. FIXME: We shouldn't
9299 * have to force the vtable/mrgctx
9300 * variable here. Instead there
9301 * should be a flag in the cfg to
9302 * request a generic sharing context.
9305 ((method->flags & METHOD_ATTRIBUTE_STATIC) || method->klass->valuetype))
9306 mono_get_vtable_var (cfg);
9311 vtable_arg = emit_get_rgctx_klass (cfg, context_used, cmethod->klass, MONO_RGCTX_INFO_VTABLE);
9313 MonoVTable *vtable = mono_class_vtable (cfg->domain, cmethod->klass);
9315 CHECK_TYPELOAD (cmethod->klass);
9316 EMIT_NEW_VTABLECONST (cfg, vtable_arg, vtable);
9321 g_assert (!vtable_arg);
9323 if (!cfg->compile_aot) {
9325 * emit_get_rgctx_method () calls mono_class_vtable () so check
9326 * for type load errors before.
9328 mono_class_setup_vtable (cmethod->klass);
9329 CHECK_TYPELOAD (cmethod->klass);
9332 vtable_arg = emit_get_rgctx_method (cfg, context_used, cmethod, MONO_RGCTX_INFO_METHOD_RGCTX);
9334 /* !marshalbyref is needed to properly handle generic methods + remoting */
9335 if ((!(cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL) ||
9336 MONO_METHOD_IS_FINAL (cmethod)) &&
9337 !mono_class_is_marshalbyref (cmethod->klass)) {
9344 if (pass_imt_from_rgctx) {
9345 g_assert (!pass_vtable);
9347 imt_arg = emit_get_rgctx_method (cfg, context_used,
9348 cmethod, MONO_RGCTX_INFO_METHOD);
9352 MONO_EMIT_NEW_CHECK_THIS (cfg, sp [0]->dreg);
9354 /* Calling virtual generic methods */
9355 if (virtual_ && (cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL) &&
9356 !(MONO_METHOD_IS_FINAL (cmethod) &&
9357 cmethod->wrapper_type != MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK) &&
9358 fsig->generic_param_count &&
9359 !(cfg->gsharedvt && mini_is_gsharedvt_signature (fsig)) &&
9361 MonoInst *this_temp, *this_arg_temp, *store;
9362 MonoInst *iargs [4];
9364 g_assert (fsig->is_inflated);
9366 /* Prevent inlining of methods that contain indirect calls */
9367 INLINE_FAILURE ("virtual generic call");
9369 if (cfg->gsharedvt && mini_is_gsharedvt_signature (fsig))
9370 GSHAREDVT_FAILURE (*ip);
9372 if (cfg->backend->have_generalized_imt_thunk && cfg->backend->gshared_supported && cmethod->wrapper_type == MONO_WRAPPER_NONE) {
9373 g_assert (!imt_arg);
9375 g_assert (cmethod->is_inflated);
9376 imt_arg = emit_get_rgctx_method (cfg, context_used,
9377 cmethod, MONO_RGCTX_INFO_METHOD);
9378 ins = mono_emit_method_call_full (cfg, cmethod, fsig, FALSE, sp, sp [0], imt_arg, NULL);
9380 this_temp = mono_compile_create_var (cfg, type_from_stack_type (sp [0]), OP_LOCAL);
9381 NEW_TEMPSTORE (cfg, store, this_temp->inst_c0, sp [0]);
9382 MONO_ADD_INS (cfg->cbb, store);
9384 /* FIXME: This should be a managed pointer */
9385 this_arg_temp = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
9387 EMIT_NEW_TEMPLOAD (cfg, iargs [0], this_temp->inst_c0);
9388 iargs [1] = emit_get_rgctx_method (cfg, context_used,
9389 cmethod, MONO_RGCTX_INFO_METHOD);
9390 EMIT_NEW_TEMPLOADA (cfg, iargs [2], this_arg_temp->inst_c0);
9391 addr = mono_emit_jit_icall (cfg,
9392 mono_helper_compile_generic_method, iargs);
9394 EMIT_NEW_TEMPLOAD (cfg, sp [0], this_arg_temp->inst_c0);
9396 ins = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr, NULL, NULL);
9403 * Implement a workaround for the inherent races involved in locking:
9409 * If a thread abort happens between the call to Monitor.Enter () and the start of the
9410 * try block, the Exit () won't be executed, see:
9411 * http://www.bluebytesoftware.com/blog/2007/01/30/MonitorEnterThreadAbortsAndOrphanedLocks.aspx
9412 * To work around this, we extend such try blocks to include the last x bytes
9413 * of the Monitor.Enter () call.
9415 if (cmethod->klass == mono_defaults.monitor_class && !strcmp (cmethod->name, "Enter") && mono_method_signature (cmethod)->param_count == 1) {
9416 MonoBasicBlock *tbb;
9418 GET_BBLOCK (cfg, tbb, ip + 5);
9420 * Only extend try blocks with a finally, to avoid catching exceptions thrown
9421 * from Monitor.Enter like ArgumentNullException.
9423 if (tbb->try_start && MONO_REGION_FLAGS(tbb->region) == MONO_EXCEPTION_CLAUSE_FINALLY) {
9424 /* Mark this bblock as needing to be extended */
9425 tbb->extend_try_block = TRUE;
9429 /* Conversion to a JIT intrinsic */
9430 if ((cfg->opt & MONO_OPT_INTRINS) && (ins = mini_emit_inst_for_method (cfg, cmethod, fsig, sp))) {
9431 if (!MONO_TYPE_IS_VOID (fsig->ret)) {
9432 type_to_eval_stack_type ((cfg), fsig->ret, ins);
9439 if ((cfg->opt & MONO_OPT_INLINE) &&
9440 (!virtual_ || !(cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL) || MONO_METHOD_IS_FINAL (cmethod)) &&
9441 mono_method_check_inlining (cfg, cmethod)) {
9443 gboolean always = FALSE;
9445 if ((cmethod->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
9446 (cmethod->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)) {
9447 /* Prevent inlining of methods that call wrappers */
9448 INLINE_FAILURE ("wrapper call");
9449 cmethod = mono_marshal_get_native_wrapper (cmethod, TRUE, FALSE);
9453 costs = inline_method (cfg, cmethod, fsig, sp, ip, cfg->real_offset, always);
9455 cfg->real_offset += 5;
9457 if (!MONO_TYPE_IS_VOID (fsig->ret)) {
9458 /* *sp is already set by inline_method */
9463 inline_costs += costs;
9469 /* Tail recursion elimination */
9470 if ((cfg->opt & MONO_OPT_TAILC) && call_opcode == CEE_CALL && cmethod == method && ip [5] == CEE_RET && !vtable_arg) {
9471 gboolean has_vtargs = FALSE;
9474 /* Prevent inlining of methods with tail calls (the call stack would be altered) */
9475 INLINE_FAILURE ("tail call");
9477 /* keep it simple */
9478 for (i = fsig->param_count - 1; i >= 0; i--) {
9479 if (MONO_TYPE_ISSTRUCT (mono_method_signature (cmethod)->params [i]))
9484 for (i = 0; i < n; ++i)
9485 EMIT_NEW_ARGSTORE (cfg, ins, i, sp [i]);
9486 MONO_INST_NEW (cfg, ins, OP_BR);
9487 MONO_ADD_INS (cfg->cbb, ins);
9488 tblock = start_bblock->out_bb [0];
9489 link_bblock (cfg, cfg->cbb, tblock);
9490 ins->inst_target_bb = tblock;
9491 start_new_bblock = 1;
9493 /* skip the CEE_RET, too */
9494 if (ip_in_bb (cfg, cfg->cbb, ip + 5))
9501 inline_costs += 10 * num_calls++;
9504 * Making generic calls out of gsharedvt methods.
9505 * This needs to be used for all generic calls, not just ones with a gsharedvt signature, to avoid
9506 * patching gshared method addresses into a gsharedvt method.
9508 if (cfg->gsharedvt && (mini_is_gsharedvt_signature (fsig) || cmethod->is_inflated || cmethod->klass->generic_class) &&
9509 !(cmethod->klass->rank && cmethod->klass->byval_arg.type != MONO_TYPE_SZARRAY) &&
9510 (!(cfg->llvm_only && virtual_))) {
9511 MonoRgctxInfoType info_type;
9514 //if (cmethod->klass->flags & TYPE_ATTRIBUTE_INTERFACE)
9515 //GSHAREDVT_FAILURE (*ip);
9516 // disable for possible remoting calls
9517 if (fsig->hasthis && (mono_class_is_marshalbyref (method->klass) || method->klass == mono_defaults.object_class))
9518 GSHAREDVT_FAILURE (*ip);
9519 if (fsig->generic_param_count) {
9520 /* virtual generic call */
9521 g_assert (!imt_arg);
9522 /* Same as the virtual generic case above */
9523 imt_arg = emit_get_rgctx_method (cfg, context_used,
9524 cmethod, MONO_RGCTX_INFO_METHOD);
9525 /* This is not needed, as the trampoline code will pass one, and it might be passed in the same reg as the imt arg */
9527 } else if ((cmethod->klass->flags & TYPE_ATTRIBUTE_INTERFACE) && !imt_arg) {
9528 /* This can happen when we call a fully instantiated iface method */
9529 imt_arg = emit_get_rgctx_method (cfg, context_used,
9530 cmethod, MONO_RGCTX_INFO_METHOD);
9535 if ((cmethod->klass->parent == mono_defaults.multicastdelegate_class) && (!strcmp (cmethod->name, "Invoke")))
9536 keep_this_alive = sp [0];
9538 if (virtual_ && (cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL))
9539 info_type = MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE_VIRT;
9541 info_type = MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE;
9542 addr = emit_get_rgctx_gsharedvt_call (cfg, context_used, fsig, cmethod, info_type);
9544 if (cfg->llvm_only) {
9545 // FIXME: Avoid initializing vtable_arg
9546 ins = emit_llvmonly_calli (cfg, fsig, sp, addr);
9548 ins = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr, imt_arg, vtable_arg);
9553 /* Generic sharing */
9556 * Use this if the callee is gsharedvt sharable too, since
9557 * at runtime we might find an instantiation so the call cannot
9558 * be patched (the 'no_patch' code path in mini-trampolines.c).
9560 if (context_used && !imt_arg && !array_rank && !delegate_invoke &&
9561 (!mono_method_is_generic_sharable_full (cmethod, TRUE, FALSE, FALSE) ||
9562 !mono_class_generic_sharing_enabled (cmethod->klass)) &&
9563 (!virtual_ || MONO_METHOD_IS_FINAL (cmethod) ||
9564 !(cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL))) {
9565 INLINE_FAILURE ("gshared");
9567 g_assert (cfg->gshared && cmethod);
9571 * We are compiling a call to a
9572 * generic method from shared code,
9573 * which means that we have to look up
9574 * the method in the rgctx and do an
9578 MONO_EMIT_NEW_CHECK_THIS (cfg, sp [0]->dreg);
9580 addr = emit_get_rgctx_method (cfg, context_used, cmethod, MONO_RGCTX_INFO_GENERIC_METHOD_CODE);
9581 if (cfg->llvm_only) {
9582 // FIXME: Avoid initializing imt_arg/vtable_arg
9583 ins = emit_llvmonly_calli (cfg, fsig, sp, addr);
9585 ins = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr, imt_arg, vtable_arg);
9590 /* Direct calls to icalls */
9592 MonoMethod *wrapper;
9595 /* Inline the wrapper */
9596 wrapper = mono_marshal_get_native_wrapper (cmethod, TRUE, cfg->compile_aot);
9598 costs = inline_method (cfg, wrapper, fsig, sp, ip, cfg->real_offset, TRUE);
9599 g_assert (costs > 0);
9600 cfg->real_offset += 5;
9602 if (!MONO_TYPE_IS_VOID (fsig->ret)) {
9603 /* *sp is already set by inline_method */
9608 inline_costs += costs;
9617 if (strcmp (cmethod->name, "Set") == 0) { /* array Set */
9618 MonoInst *val = sp [fsig->param_count];
9620 if (val->type == STACK_OBJ) {
9621 MonoInst *iargs [2];
9626 mono_emit_jit_icall (cfg, mono_helper_stelem_ref_check, iargs);
9629 addr = mini_emit_ldelema_ins (cfg, cmethod, sp, ip, TRUE);
9630 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, fsig->params [fsig->param_count - 1], addr->dreg, 0, val->dreg);
9631 if (cfg->gen_write_barriers && val->type == STACK_OBJ && !(val->opcode == OP_PCONST && val->inst_c0 == 0))
9632 emit_write_barrier (cfg, addr, val);
9633 if (cfg->gen_write_barriers && mini_is_gsharedvt_klass (cmethod->klass))
9634 GSHAREDVT_FAILURE (*ip);
9635 } else if (strcmp (cmethod->name, "Get") == 0) { /* array Get */
9636 addr = mini_emit_ldelema_ins (cfg, cmethod, sp, ip, FALSE);
9638 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, fsig->ret, addr->dreg, 0);
9639 } else if (strcmp (cmethod->name, "Address") == 0) { /* array Address */
9640 if (!cmethod->klass->element_class->valuetype && !readonly)
9641 mini_emit_check_array_type (cfg, sp [0], cmethod->klass);
9642 CHECK_TYPELOAD (cmethod->klass);
9645 addr = mini_emit_ldelema_ins (cfg, cmethod, sp, ip, FALSE);
9648 g_assert_not_reached ();
9655 ins = mini_redirect_call (cfg, cmethod, fsig, sp, virtual_ ? sp [0] : NULL);
9659 /* Tail prefix / tail call optimization */
9661 /* FIXME: Enabling TAILC breaks some inlining/stack trace/etc tests */
9662 /* FIXME: runtime generic context pointer for jumps? */
9663 /* FIXME: handle this for generic sharing eventually */
9664 if ((ins_flag & MONO_INST_TAILCALL) &&
9665 !vtable_arg && !cfg->gshared && is_supported_tail_call (cfg, method, cmethod, fsig, call_opcode))
9666 supported_tail_call = TRUE;
9668 if (supported_tail_call) {
9671 /* Prevent inlining of methods with tail calls (the call stack would be altered) */
9672 INLINE_FAILURE ("tail call");
9674 //printf ("HIT: %s -> %s\n", mono_method_full_name (cfg->method, TRUE), mono_method_full_name (cmethod, TRUE));
9676 if (cfg->backend->have_op_tail_call) {
9677 /* Handle tail calls similarly to normal calls */
9680 emit_instrumentation_call (cfg, mono_profiler_method_leave);
9682 MONO_INST_NEW_CALL (cfg, call, OP_JMP);
9683 call->tail_call = TRUE;
9684 call->method = cmethod;
9685 call->signature = mono_method_signature (cmethod);
9688 * We implement tail calls by storing the actual arguments into the
9689 * argument variables, then emitting a CEE_JMP.
9691 for (i = 0; i < n; ++i) {
9692 /* Prevent argument from being register allocated */
9693 arg_array [i]->flags |= MONO_INST_VOLATILE;
9694 EMIT_NEW_ARGSTORE (cfg, ins, i, sp [i]);
9696 ins = (MonoInst*)call;
9697 ins->inst_p0 = cmethod;
9698 ins->inst_p1 = arg_array [0];
9699 MONO_ADD_INS (cfg->cbb, ins);
9700 link_bblock (cfg, cfg->cbb, end_bblock);
9701 start_new_bblock = 1;
9703 // FIXME: Eliminate unreachable epilogs
9706 * OP_TAILCALL has no return value, so skip the CEE_RET if it is
9707 * only reachable from this call.
9709 GET_BBLOCK (cfg, tblock, ip + 5);
9710 if (tblock == cfg->cbb || tblock->in_count == 0)
9719 * Synchronized wrappers.
9720 * Its hard to determine where to replace a method with its synchronized
9721 * wrapper without causing an infinite recursion. The current solution is
9722 * to add the synchronized wrapper in the trampolines, and to
9723 * change the called method to a dummy wrapper, and resolve that wrapper
9724 * to the real method in mono_jit_compile_method ().
9726 if (cfg->method->wrapper_type == MONO_WRAPPER_SYNCHRONIZED) {
9727 MonoMethod *orig = mono_marshal_method_from_wrapper (cfg->method);
9728 if (cmethod == orig || (cmethod->is_inflated && mono_method_get_declaring_generic_method (cmethod) == orig))
9729 cmethod = mono_marshal_get_synchronized_inner_wrapper (cmethod);
9733 * Virtual calls in llvm-only mode.
9735 if (cfg->llvm_only && virtual_ && cmethod && (cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL)) {
9736 MonoInst *icall_args [16];
9737 MonoInst *call_target;
9739 gboolean is_iface = cmethod->klass->flags & TYPE_ATTRIBUTE_INTERFACE;
9741 MONO_EMIT_NULL_CHECK (cfg, sp [0]->dreg);
9743 // FIXME: Optimize this
9748 slot = mono_method_get_imt_slot (cmethod);
9750 slot = mono_method_get_vtable_index (cmethod);
9752 icall_args [0] = sp [0];
9753 EMIT_NEW_ICONST (cfg, icall_args [1], slot);
9755 if (fsig->generic_param_count) {
9756 /* virtual generic call */
9757 g_assert (!imt_arg);
9758 /* Same as the virtual generic case above */
9759 imt_arg = emit_get_rgctx_method (cfg, context_used,
9760 cmethod, MONO_RGCTX_INFO_METHOD);
9761 icall_args [2] = imt_arg;
9762 } else if (imt_arg) {
9763 icall_args [2] = imt_arg;
9765 EMIT_NEW_AOTCONST (cfg, ins, MONO_PATCH_INFO_METHODCONST, cmethod);
9766 icall_args [2] = ins;
9769 // FIXME: For generic virtual calls, avoid computing the rgctx twice
9771 arg_reg = alloc_preg (cfg);
9772 MONO_EMIT_NEW_PCONST (cfg, arg_reg, NULL);
9773 EMIT_NEW_VARLOADA_VREG (cfg, icall_args [3], arg_reg, &mono_defaults.int_class->byval_arg);
9775 if (cfg->gsharedvt && mini_is_gsharedvt_variable_signature (fsig)) {
9777 * We handle virtual calls made from gsharedvt methods here instead
9778 * of the gsharedvt block above.
9781 call_target = mono_emit_jit_icall (cfg, mono_resolve_iface_call_gsharedvt, icall_args);
9783 call_target = mono_emit_jit_icall (cfg, mono_resolve_vcall_gsharedvt, icall_args);
9786 call_target = mono_emit_jit_icall (cfg, mono_resolve_iface_call, icall_args);
9788 call_target = mono_emit_jit_icall (cfg, mono_resolve_vcall, icall_args);
9792 * Pass the extra argument even if the callee doesn't receive it, most calling
9793 * calling conventions allow this.
9795 ins = emit_extra_arg_calli (cfg, fsig, sp, arg_reg, call_target);
9800 INLINE_FAILURE ("call");
9801 ins = mono_emit_method_call_full (cfg, cmethod, fsig, tail_call, sp, virtual_ ? sp [0] : NULL,
9802 imt_arg, vtable_arg);
9804 if (tail_call && !cfg->llvm_only) {
9805 link_bblock (cfg, cfg->cbb, end_bblock);
9806 start_new_bblock = 1;
9808 // FIXME: Eliminate unreachable epilogs
9811 * OP_TAILCALL has no return value, so skip the CEE_RET if it is
9812 * only reachable from this call.
9814 GET_BBLOCK (cfg, tblock, ip + 5);
9815 if (tblock == cfg->cbb || tblock->in_count == 0)
9822 /* End of call, INS should contain the result of the call, if any */
9824 if (push_res && !MONO_TYPE_IS_VOID (fsig->ret)) {
9827 *sp++ = mono_emit_widen_call_res (cfg, ins, fsig);
9832 if (keep_this_alive) {
9833 MonoInst *dummy_use;
9835 /* See mono_emit_method_call_full () */
9836 EMIT_NEW_DUMMY_USE (cfg, dummy_use, keep_this_alive);
9839 CHECK_CFG_EXCEPTION;
9843 g_assert (*ip == CEE_RET);
9847 constrained_class = NULL;
9849 emit_seq_point (cfg, method, ip, FALSE, TRUE);
9853 if (cfg->method != method) {
9854 /* return from inlined method */
9856 * If in_count == 0, that means the ret is unreachable due to
9857 * being preceeded by a throw. In that case, inline_method () will
9858 * handle setting the return value
9859 * (test case: test_0_inline_throw ()).
9861 if (return_var && cfg->cbb->in_count) {
9862 MonoType *ret_type = mono_method_signature (method)->ret;
9868 if ((method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD || method->wrapper_type == MONO_WRAPPER_NONE) && target_type_is_incompatible (cfg, ret_type, *sp))
9871 //g_assert (returnvar != -1);
9872 EMIT_NEW_TEMPSTORE (cfg, store, return_var->inst_c0, *sp);
9873 cfg->ret_var_set = TRUE;
9876 emit_instrumentation_call (cfg, mono_profiler_method_leave);
9878 if (cfg->lmf_var && cfg->cbb->in_count && !cfg->llvm_only)
9882 MonoType *ret_type = mini_get_underlying_type (mono_method_signature (method)->ret);
9884 if (seq_points && !sym_seq_points) {
9886 * Place a seq point here too even through the IL stack is not
9887 * empty, so a step over on
9890 * will work correctly.
9892 NEW_SEQ_POINT (cfg, ins, ip - header->code, TRUE);
9893 MONO_ADD_INS (cfg->cbb, ins);
9896 g_assert (!return_var);
9900 if ((method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD || method->wrapper_type == MONO_WRAPPER_NONE) && target_type_is_incompatible (cfg, ret_type, *sp))
9903 emit_setret (cfg, *sp);
9906 if (sp != stack_start)
9908 MONO_INST_NEW (cfg, ins, OP_BR);
9910 ins->inst_target_bb = end_bblock;
9911 MONO_ADD_INS (cfg->cbb, ins);
9912 link_bblock (cfg, cfg->cbb, end_bblock);
9913 start_new_bblock = 1;
9917 MONO_INST_NEW (cfg, ins, OP_BR);
9919 target = ip + 1 + (signed char)(*ip);
9921 GET_BBLOCK (cfg, tblock, target);
9922 link_bblock (cfg, cfg->cbb, tblock);
9923 ins->inst_target_bb = tblock;
9924 if (sp != stack_start) {
9925 handle_stack_args (cfg, stack_start, sp - stack_start);
9927 CHECK_UNVERIFIABLE (cfg);
9929 MONO_ADD_INS (cfg->cbb, ins);
9930 start_new_bblock = 1;
9931 inline_costs += BRANCH_COST;
9945 MONO_INST_NEW (cfg, ins, *ip + BIG_BRANCH_OFFSET);
9947 target = ip + 1 + *(signed char*)ip;
9953 inline_costs += BRANCH_COST;
9957 MONO_INST_NEW (cfg, ins, OP_BR);
9960 target = ip + 4 + (gint32)read32(ip);
9962 GET_BBLOCK (cfg, tblock, target);
9963 link_bblock (cfg, cfg->cbb, tblock);
9964 ins->inst_target_bb = tblock;
9965 if (sp != stack_start) {
9966 handle_stack_args (cfg, stack_start, sp - stack_start);
9968 CHECK_UNVERIFIABLE (cfg);
9971 MONO_ADD_INS (cfg->cbb, ins);
9973 start_new_bblock = 1;
9974 inline_costs += BRANCH_COST;
9981 gboolean is_short = ((*ip) == CEE_BRFALSE_S) || ((*ip) == CEE_BRTRUE_S);
9982 gboolean is_true = ((*ip) == CEE_BRTRUE_S) || ((*ip) == CEE_BRTRUE);
9983 guint32 opsize = is_short ? 1 : 4;
9985 CHECK_OPSIZE (opsize);
9987 if (sp [-1]->type == STACK_VTYPE || sp [-1]->type == STACK_R8)
9990 target = ip + opsize + (is_short ? *(signed char*)ip : (gint32)read32(ip));
9995 GET_BBLOCK (cfg, tblock, target);
9996 link_bblock (cfg, cfg->cbb, tblock);
9997 GET_BBLOCK (cfg, tblock, ip);
9998 link_bblock (cfg, cfg->cbb, tblock);
10000 if (sp != stack_start) {
10001 handle_stack_args (cfg, stack_start, sp - stack_start);
10002 CHECK_UNVERIFIABLE (cfg);
10005 MONO_INST_NEW(cfg, cmp, OP_ICOMPARE_IMM);
10006 cmp->sreg1 = sp [0]->dreg;
10007 type_from_op (cfg, cmp, sp [0], NULL);
10010 #if SIZEOF_REGISTER == 4
10011 if (cmp->opcode == OP_LCOMPARE_IMM) {
10012 /* Convert it to OP_LCOMPARE */
10013 MONO_INST_NEW (cfg, ins, OP_I8CONST);
10014 ins->type = STACK_I8;
10015 ins->dreg = alloc_dreg (cfg, STACK_I8);
10017 MONO_ADD_INS (cfg->cbb, ins);
10018 cmp->opcode = OP_LCOMPARE;
10019 cmp->sreg2 = ins->dreg;
10022 MONO_ADD_INS (cfg->cbb, cmp);
10024 MONO_INST_NEW (cfg, ins, is_true ? CEE_BNE_UN : CEE_BEQ);
10025 type_from_op (cfg, ins, sp [0], NULL);
10026 MONO_ADD_INS (cfg->cbb, ins);
10027 ins->inst_many_bb = (MonoBasicBlock **)mono_mempool_alloc (cfg->mempool, sizeof(gpointer)*2);
10028 GET_BBLOCK (cfg, tblock, target);
10029 ins->inst_true_bb = tblock;
10030 GET_BBLOCK (cfg, tblock, ip);
10031 ins->inst_false_bb = tblock;
10032 start_new_bblock = 2;
10035 inline_costs += BRANCH_COST;
10050 MONO_INST_NEW (cfg, ins, *ip);
10052 target = ip + 4 + (gint32)read32(ip);
10055 ADD_BINCOND (NULL);
10058 inline_costs += BRANCH_COST;
10062 MonoBasicBlock **targets;
10063 MonoBasicBlock *default_bblock;
10064 MonoJumpInfoBBTable *table;
10065 int offset_reg = alloc_preg (cfg);
10066 int target_reg = alloc_preg (cfg);
10067 int table_reg = alloc_preg (cfg);
10068 int sum_reg = alloc_preg (cfg);
10069 gboolean use_op_switch;
10073 n = read32 (ip + 1);
10076 if ((src1->type != STACK_I4) && (src1->type != STACK_PTR))
10080 CHECK_OPSIZE (n * sizeof (guint32));
10081 target = ip + n * sizeof (guint32);
10083 GET_BBLOCK (cfg, default_bblock, target);
10084 default_bblock->flags |= BB_INDIRECT_JUMP_TARGET;
10086 targets = (MonoBasicBlock **)mono_mempool_alloc (cfg->mempool, sizeof (MonoBasicBlock*) * n);
10087 for (i = 0; i < n; ++i) {
10088 GET_BBLOCK (cfg, tblock, target + (gint32)read32(ip));
10089 targets [i] = tblock;
10090 targets [i]->flags |= BB_INDIRECT_JUMP_TARGET;
10094 if (sp != stack_start) {
10096 * Link the current bb with the targets as well, so handle_stack_args
10097 * will set their in_stack correctly.
10099 link_bblock (cfg, cfg->cbb, default_bblock);
10100 for (i = 0; i < n; ++i)
10101 link_bblock (cfg, cfg->cbb, targets [i]);
10103 handle_stack_args (cfg, stack_start, sp - stack_start);
10105 CHECK_UNVERIFIABLE (cfg);
10107 /* Undo the links */
10108 mono_unlink_bblock (cfg, cfg->cbb, default_bblock);
10109 for (i = 0; i < n; ++i)
10110 mono_unlink_bblock (cfg, cfg->cbb, targets [i]);
10113 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ICOMPARE_IMM, -1, src1->dreg, n);
10114 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBGE_UN, default_bblock);
10116 for (i = 0; i < n; ++i)
10117 link_bblock (cfg, cfg->cbb, targets [i]);
10119 table = (MonoJumpInfoBBTable *)mono_mempool_alloc (cfg->mempool, sizeof (MonoJumpInfoBBTable));
10120 table->table = targets;
10121 table->table_size = n;
10123 use_op_switch = FALSE;
10125 /* ARM implements SWITCH statements differently */
10126 /* FIXME: Make it use the generic implementation */
10127 if (!cfg->compile_aot)
10128 use_op_switch = TRUE;
10131 if (COMPILE_LLVM (cfg))
10132 use_op_switch = TRUE;
10134 cfg->cbb->has_jump_table = 1;
10136 if (use_op_switch) {
10137 MONO_INST_NEW (cfg, ins, OP_SWITCH);
10138 ins->sreg1 = src1->dreg;
10139 ins->inst_p0 = table;
10140 ins->inst_many_bb = targets;
10141 ins->klass = (MonoClass *)GUINT_TO_POINTER (n);
10142 MONO_ADD_INS (cfg->cbb, ins);
10144 if (sizeof (gpointer) == 8)
10145 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SHL_IMM, offset_reg, src1->dreg, 3);
10147 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SHL_IMM, offset_reg, src1->dreg, 2);
10149 #if SIZEOF_REGISTER == 8
10150 /* The upper word might not be zero, and we add it to a 64 bit address later */
10151 MONO_EMIT_NEW_UNALU (cfg, OP_ZEXT_I4, offset_reg, offset_reg);
10154 if (cfg->compile_aot) {
10155 MONO_EMIT_NEW_AOTCONST (cfg, table_reg, table, MONO_PATCH_INFO_SWITCH);
10157 MONO_INST_NEW (cfg, ins, OP_JUMP_TABLE);
10158 ins->inst_c1 = MONO_PATCH_INFO_SWITCH;
10159 ins->inst_p0 = table;
10160 ins->dreg = table_reg;
10161 MONO_ADD_INS (cfg->cbb, ins);
10164 /* FIXME: Use load_memindex */
10165 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, sum_reg, table_reg, offset_reg);
10166 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, target_reg, sum_reg, 0);
10167 MONO_EMIT_NEW_UNALU (cfg, OP_BR_REG, -1, target_reg);
10169 start_new_bblock = 1;
10170 inline_costs += (BRANCH_COST * 2);
10183 case CEE_LDIND_REF:
10190 dreg = alloc_freg (cfg);
10193 dreg = alloc_lreg (cfg);
10195 case CEE_LDIND_REF:
10196 dreg = alloc_ireg_ref (cfg);
10199 dreg = alloc_preg (cfg);
10202 NEW_LOAD_MEMBASE (cfg, ins, ldind_to_load_membase (*ip), dreg, sp [0]->dreg, 0);
10203 ins->type = ldind_type [*ip - CEE_LDIND_I1];
10204 if (*ip == CEE_LDIND_R4)
10205 ins->type = cfg->r4_stack_type;
10206 ins->flags |= ins_flag;
10207 MONO_ADD_INS (cfg->cbb, ins);
10209 if (ins_flag & MONO_INST_VOLATILE) {
10210 /* Volatile loads have acquire semantics, see 12.6.7 in Ecma 335 */
10211 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_ACQ);
10216 case CEE_STIND_REF:
10227 if (ins_flag & MONO_INST_VOLATILE) {
10228 /* Volatile stores have release semantics, see 12.6.7 in Ecma 335 */
10229 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_REL);
10232 NEW_STORE_MEMBASE (cfg, ins, stind_to_store_membase (*ip), sp [0]->dreg, 0, sp [1]->dreg);
10233 ins->flags |= ins_flag;
10236 MONO_ADD_INS (cfg->cbb, ins);
10238 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)))
10239 emit_write_barrier (cfg, sp [0], sp [1]);
10248 MONO_INST_NEW (cfg, ins, (*ip));
10250 ins->sreg1 = sp [0]->dreg;
10251 ins->sreg2 = sp [1]->dreg;
10252 type_from_op (cfg, ins, sp [0], sp [1]);
10254 ins->dreg = alloc_dreg ((cfg), (MonoStackType)(ins)->type);
10256 /* Use the immediate opcodes if possible */
10257 if ((sp [1]->opcode == OP_ICONST) && mono_arch_is_inst_imm (sp [1]->inst_c0)) {
10258 int imm_opcode = mono_op_to_op_imm_noemul (ins->opcode);
10259 if (imm_opcode != -1) {
10260 ins->opcode = imm_opcode;
10261 ins->inst_p1 = (gpointer)(gssize)(sp [1]->inst_c0);
10264 NULLIFY_INS (sp [1]);
10268 MONO_ADD_INS ((cfg)->cbb, (ins));
10270 *sp++ = mono_decompose_opcode (cfg, ins);
10287 MONO_INST_NEW (cfg, ins, (*ip));
10289 ins->sreg1 = sp [0]->dreg;
10290 ins->sreg2 = sp [1]->dreg;
10291 type_from_op (cfg, ins, sp [0], sp [1]);
10293 add_widen_op (cfg, ins, &sp [0], &sp [1]);
10294 ins->dreg = alloc_dreg ((cfg), (MonoStackType)(ins)->type);
10296 /* FIXME: Pass opcode to is_inst_imm */
10298 /* Use the immediate opcodes if possible */
10299 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)) {
10302 imm_opcode = mono_op_to_op_imm_noemul (ins->opcode);
10303 #if defined(MONO_ARCH_EMULATE_MUL_DIV) || defined(MONO_ARCH_EMULATE_DIV)
10304 /* Keep emulated opcodes which are optimized away later */
10305 if ((ins->opcode == OP_IREM_UN || ins->opcode == OP_IDIV_UN_IMM) && (cfg->opt & (MONO_OPT_CONSPROP | MONO_OPT_COPYPROP)) && sp [1]->opcode == OP_ICONST && mono_is_power_of_two (sp [1]->inst_c0) >= 0) {
10306 imm_opcode = mono_op_to_op_imm (ins->opcode);
10309 if (imm_opcode != -1) {
10310 ins->opcode = imm_opcode;
10311 if (sp [1]->opcode == OP_I8CONST) {
10312 #if SIZEOF_REGISTER == 8
10313 ins->inst_imm = sp [1]->inst_l;
10315 ins->inst_ls_word = sp [1]->inst_ls_word;
10316 ins->inst_ms_word = sp [1]->inst_ms_word;
10320 ins->inst_imm = (gssize)(sp [1]->inst_c0);
10323 /* Might be followed by an instruction added by add_widen_op */
10324 if (sp [1]->next == NULL)
10325 NULLIFY_INS (sp [1]);
10328 MONO_ADD_INS ((cfg)->cbb, (ins));
10330 *sp++ = mono_decompose_opcode (cfg, ins);
10343 case CEE_CONV_OVF_I8:
10344 case CEE_CONV_OVF_U8:
10345 case CEE_CONV_R_UN:
10348 /* Special case this earlier so we have long constants in the IR */
10349 if ((((*ip) == CEE_CONV_I8) || ((*ip) == CEE_CONV_U8)) && (sp [-1]->opcode == OP_ICONST)) {
10350 int data = sp [-1]->inst_c0;
10351 sp [-1]->opcode = OP_I8CONST;
10352 sp [-1]->type = STACK_I8;
10353 #if SIZEOF_REGISTER == 8
10354 if ((*ip) == CEE_CONV_U8)
10355 sp [-1]->inst_c0 = (guint32)data;
10357 sp [-1]->inst_c0 = data;
10359 sp [-1]->inst_ls_word = data;
10360 if ((*ip) == CEE_CONV_U8)
10361 sp [-1]->inst_ms_word = 0;
10363 sp [-1]->inst_ms_word = (data < 0) ? -1 : 0;
10365 sp [-1]->dreg = alloc_dreg (cfg, STACK_I8);
10372 case CEE_CONV_OVF_I4:
10373 case CEE_CONV_OVF_I1:
10374 case CEE_CONV_OVF_I2:
10375 case CEE_CONV_OVF_I:
10376 case CEE_CONV_OVF_U:
10379 if (sp [-1]->type == STACK_R8 || sp [-1]->type == STACK_R4) {
10380 ADD_UNOP (CEE_CONV_OVF_I8);
10387 case CEE_CONV_OVF_U1:
10388 case CEE_CONV_OVF_U2:
10389 case CEE_CONV_OVF_U4:
10392 if (sp [-1]->type == STACK_R8 || sp [-1]->type == STACK_R4) {
10393 ADD_UNOP (CEE_CONV_OVF_U8);
10400 case CEE_CONV_OVF_I1_UN:
10401 case CEE_CONV_OVF_I2_UN:
10402 case CEE_CONV_OVF_I4_UN:
10403 case CEE_CONV_OVF_I8_UN:
10404 case CEE_CONV_OVF_U1_UN:
10405 case CEE_CONV_OVF_U2_UN:
10406 case CEE_CONV_OVF_U4_UN:
10407 case CEE_CONV_OVF_U8_UN:
10408 case CEE_CONV_OVF_I_UN:
10409 case CEE_CONV_OVF_U_UN:
10416 CHECK_CFG_EXCEPTION;
10420 case CEE_ADD_OVF_UN:
10422 case CEE_MUL_OVF_UN:
10424 case CEE_SUB_OVF_UN:
10430 GSHAREDVT_FAILURE (*ip);
10433 token = read32 (ip + 1);
10434 klass = mini_get_class (method, token, generic_context);
10435 CHECK_TYPELOAD (klass);
10437 if (generic_class_is_reference_type (cfg, klass)) {
10438 MonoInst *store, *load;
10439 int dreg = alloc_ireg_ref (cfg);
10441 NEW_LOAD_MEMBASE (cfg, load, OP_LOAD_MEMBASE, dreg, sp [1]->dreg, 0);
10442 load->flags |= ins_flag;
10443 MONO_ADD_INS (cfg->cbb, load);
10445 NEW_STORE_MEMBASE (cfg, store, OP_STORE_MEMBASE_REG, sp [0]->dreg, 0, dreg);
10446 store->flags |= ins_flag;
10447 MONO_ADD_INS (cfg->cbb, store);
10449 if (cfg->gen_write_barriers && cfg->method->wrapper_type != MONO_WRAPPER_WRITE_BARRIER)
10450 emit_write_barrier (cfg, sp [0], sp [1]);
10452 mini_emit_stobj (cfg, sp [0], sp [1], klass, FALSE);
10458 int loc_index = -1;
10464 token = read32 (ip + 1);
10465 klass = mini_get_class (method, token, generic_context);
10466 CHECK_TYPELOAD (klass);
10468 /* Optimize the common ldobj+stloc combination */
10471 loc_index = ip [6];
10478 loc_index = ip [5] - CEE_STLOC_0;
10485 if ((loc_index != -1) && ip_in_bb (cfg, cfg->cbb, ip + 5)) {
10486 CHECK_LOCAL (loc_index);
10488 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, sp [0]->dreg, 0);
10489 ins->dreg = cfg->locals [loc_index]->dreg;
10490 ins->flags |= ins_flag;
10493 if (ins_flag & MONO_INST_VOLATILE) {
10494 /* Volatile loads have acquire semantics, see 12.6.7 in Ecma 335 */
10495 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_ACQ);
10501 /* Optimize the ldobj+stobj combination */
10502 /* The reference case ends up being a load+store anyway */
10503 /* Skip this if the operation is volatile. */
10504 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)) {
10509 mini_emit_stobj (cfg, sp [0], sp [1], klass, FALSE);
10516 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, sp [0]->dreg, 0);
10517 ins->flags |= ins_flag;
10520 if (ins_flag & MONO_INST_VOLATILE) {
10521 /* Volatile loads have acquire semantics, see 12.6.7 in Ecma 335 */
10522 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_ACQ);
10531 CHECK_STACK_OVF (1);
10533 n = read32 (ip + 1);
10535 if (method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD) {
10536 EMIT_NEW_PCONST (cfg, ins, mono_method_get_wrapper_data (method, n));
10537 ins->type = STACK_OBJ;
10540 else if (method->wrapper_type != MONO_WRAPPER_NONE) {
10541 MonoInst *iargs [1];
10542 char *str = (char *)mono_method_get_wrapper_data (method, n);
10544 if (cfg->compile_aot)
10545 EMIT_NEW_LDSTRLITCONST (cfg, iargs [0], str);
10547 EMIT_NEW_PCONST (cfg, iargs [0], str);
10548 *sp = mono_emit_jit_icall (cfg, mono_string_new_wrapper, iargs);
10550 if (cfg->opt & MONO_OPT_SHARED) {
10551 MonoInst *iargs [3];
10553 if (cfg->compile_aot) {
10554 cfg->ldstr_list = g_list_prepend (cfg->ldstr_list, GINT_TO_POINTER (n));
10556 EMIT_NEW_DOMAINCONST (cfg, iargs [0]);
10557 EMIT_NEW_IMAGECONST (cfg, iargs [1], image);
10558 EMIT_NEW_ICONST (cfg, iargs [2], mono_metadata_token_index (n));
10559 *sp = mono_emit_jit_icall (cfg, mono_ldstr, iargs);
10560 mono_ldstr (cfg->domain, image, mono_metadata_token_index (n));
10562 if (cfg->cbb->out_of_line) {
10563 MonoInst *iargs [2];
10565 if (image == mono_defaults.corlib) {
10567 * Avoid relocations in AOT and save some space by using a
10568 * version of helper_ldstr specialized to mscorlib.
10570 EMIT_NEW_ICONST (cfg, iargs [0], mono_metadata_token_index (n));
10571 *sp = mono_emit_jit_icall (cfg, mono_helper_ldstr_mscorlib, iargs);
10573 /* Avoid creating the string object */
10574 EMIT_NEW_IMAGECONST (cfg, iargs [0], image);
10575 EMIT_NEW_ICONST (cfg, iargs [1], mono_metadata_token_index (n));
10576 *sp = mono_emit_jit_icall (cfg, mono_helper_ldstr, iargs);
10580 if (cfg->compile_aot) {
10581 NEW_LDSTRCONST (cfg, ins, image, n);
10583 MONO_ADD_INS (cfg->cbb, ins);
10586 NEW_PCONST (cfg, ins, NULL);
10587 ins->type = STACK_OBJ;
10588 ins->inst_p0 = mono_ldstr (cfg->domain, image, mono_metadata_token_index (n));
10590 OUT_OF_MEMORY_FAILURE;
10593 MONO_ADD_INS (cfg->cbb, ins);
10602 MonoInst *iargs [2];
10603 MonoMethodSignature *fsig;
10606 MonoInst *vtable_arg = NULL;
10609 token = read32 (ip + 1);
10610 cmethod = mini_get_method (cfg, method, token, NULL, generic_context);
10611 if (!cmethod || mono_loader_get_last_error ())
10613 fsig = mono_method_get_signature_checked (cmethod, image, token, generic_context, &cfg->error);
10616 mono_save_token_info (cfg, image, token, cmethod);
10618 if (!mono_class_init (cmethod->klass))
10619 TYPE_LOAD_ERROR (cmethod->klass);
10621 context_used = mini_method_check_context_used (cfg, cmethod);
10623 if (mono_security_core_clr_enabled ())
10624 ensure_method_is_allowed_to_call_method (cfg, method, cmethod);
10626 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)) {
10627 emit_class_init (cfg, cmethod->klass);
10628 CHECK_TYPELOAD (cmethod->klass);
10632 if (cfg->gsharedvt) {
10633 if (mini_is_gsharedvt_variable_signature (sig))
10634 GSHAREDVT_FAILURE (*ip);
10638 n = fsig->param_count;
10642 * Generate smaller code for the common newobj <exception> instruction in
10643 * argument checking code.
10645 if (cfg->cbb->out_of_line && cmethod->klass->image == mono_defaults.corlib &&
10646 is_exception_class (cmethod->klass) && n <= 2 &&
10647 ((n < 1) || (!fsig->params [0]->byref && fsig->params [0]->type == MONO_TYPE_STRING)) &&
10648 ((n < 2) || (!fsig->params [1]->byref && fsig->params [1]->type == MONO_TYPE_STRING))) {
10649 MonoInst *iargs [3];
10653 EMIT_NEW_ICONST (cfg, iargs [0], cmethod->klass->type_token);
10656 *sp ++ = mono_emit_jit_icall (cfg, mono_create_corlib_exception_0, iargs);
10659 iargs [1] = sp [0];
10660 *sp ++ = mono_emit_jit_icall (cfg, mono_create_corlib_exception_1, iargs);
10663 iargs [1] = sp [0];
10664 iargs [2] = sp [1];
10665 *sp ++ = mono_emit_jit_icall (cfg, mono_create_corlib_exception_2, iargs);
10668 g_assert_not_reached ();
10676 /* move the args to allow room for 'this' in the first position */
10682 /* check_call_signature () requires sp[0] to be set */
10683 this_ins.type = STACK_OBJ;
10684 sp [0] = &this_ins;
10685 if (check_call_signature (cfg, fsig, sp))
10690 if (mini_class_is_system_array (cmethod->klass)) {
10691 *sp = emit_get_rgctx_method (cfg, context_used,
10692 cmethod, MONO_RGCTX_INFO_METHOD);
10694 /* Avoid varargs in the common case */
10695 if (fsig->param_count == 1)
10696 alloc = mono_emit_jit_icall (cfg, mono_array_new_1, sp);
10697 else if (fsig->param_count == 2)
10698 alloc = mono_emit_jit_icall (cfg, mono_array_new_2, sp);
10699 else if (fsig->param_count == 3)
10700 alloc = mono_emit_jit_icall (cfg, mono_array_new_3, sp);
10701 else if (fsig->param_count == 4)
10702 alloc = mono_emit_jit_icall (cfg, mono_array_new_4, sp);
10704 alloc = handle_array_new (cfg, fsig->param_count, sp, ip);
10705 } else if (cmethod->string_ctor) {
10706 g_assert (!context_used);
10707 g_assert (!vtable_arg);
10708 /* we simply pass a null pointer */
10709 EMIT_NEW_PCONST (cfg, *sp, NULL);
10710 /* now call the string ctor */
10711 alloc = mono_emit_method_call_full (cfg, cmethod, fsig, FALSE, sp, NULL, NULL, NULL);
10713 if (cmethod->klass->valuetype) {
10714 iargs [0] = mono_compile_create_var (cfg, &cmethod->klass->byval_arg, OP_LOCAL);
10715 emit_init_rvar (cfg, iargs [0]->dreg, &cmethod->klass->byval_arg);
10716 EMIT_NEW_TEMPLOADA (cfg, *sp, iargs [0]->inst_c0);
10721 * The code generated by mini_emit_virtual_call () expects
10722 * iargs [0] to be a boxed instance, but luckily the vcall
10723 * will be transformed into a normal call there.
10725 } else if (context_used) {
10726 alloc = handle_alloc (cfg, cmethod->klass, FALSE, context_used);
10729 MonoVTable *vtable = NULL;
10731 if (!cfg->compile_aot)
10732 vtable = mono_class_vtable (cfg->domain, cmethod->klass);
10733 CHECK_TYPELOAD (cmethod->klass);
10736 * TypeInitializationExceptions thrown from the mono_runtime_class_init
10737 * call in mono_jit_runtime_invoke () can abort the finalizer thread.
10738 * As a workaround, we call class cctors before allocating objects.
10740 if (mini_field_access_needs_cctor_run (cfg, method, cmethod->klass, vtable) && !(g_slist_find (class_inits, cmethod->klass))) {
10741 emit_class_init (cfg, cmethod->klass);
10742 if (cfg->verbose_level > 2)
10743 printf ("class %s.%s needs init call for ctor\n", cmethod->klass->name_space, cmethod->klass->name);
10744 class_inits = g_slist_prepend (class_inits, cmethod->klass);
10747 alloc = handle_alloc (cfg, cmethod->klass, FALSE, 0);
10750 CHECK_CFG_EXCEPTION; /*for handle_alloc*/
10753 MONO_EMIT_NEW_UNALU (cfg, OP_NOT_NULL, -1, alloc->dreg);
10755 /* Now call the actual ctor */
10756 handle_ctor_call (cfg, cmethod, fsig, context_used, sp, ip, &inline_costs);
10757 CHECK_CFG_EXCEPTION;
10760 if (alloc == NULL) {
10762 EMIT_NEW_TEMPLOAD (cfg, ins, iargs [0]->inst_c0);
10763 type_to_eval_stack_type (cfg, &ins->klass->byval_arg, ins);
10771 if (!(seq_point_locs && mono_bitset_test_fast (seq_point_locs, ip - header->code)))
10772 emit_seq_point (cfg, method, ip, FALSE, TRUE);
10775 case CEE_CASTCLASS:
10779 token = read32 (ip + 1);
10780 klass = mini_get_class (method, token, generic_context);
10781 CHECK_TYPELOAD (klass);
10782 if (sp [0]->type != STACK_OBJ)
10785 ins = handle_castclass (cfg, klass, *sp, ip, &inline_costs);
10786 CHECK_CFG_EXCEPTION;
10795 token = read32 (ip + 1);
10796 klass = mini_get_class (method, token, generic_context);
10797 CHECK_TYPELOAD (klass);
10798 if (sp [0]->type != STACK_OBJ)
10801 context_used = mini_class_check_context_used (cfg, klass);
10803 if (!context_used && mini_class_has_reference_variant_generic_argument (cfg, klass, context_used)) {
10804 MonoMethod *mono_isinst = mono_marshal_get_isinst_with_cache ();
10805 MonoInst *args [3];
10812 EMIT_NEW_CLASSCONST (cfg, args [1], klass);
10815 idx = get_castclass_cache_idx (cfg);
10816 args [2] = emit_runtime_constant (cfg, MONO_PATCH_INFO_CASTCLASS_CACHE, GINT_TO_POINTER (idx));
10818 *sp++ = mono_emit_method_call (cfg, mono_isinst, args, NULL);
10821 } else if (!context_used && (mono_class_is_marshalbyref (klass) || klass->flags & TYPE_ATTRIBUTE_INTERFACE)) {
10822 MonoMethod *mono_isinst;
10823 MonoInst *iargs [1];
10826 mono_isinst = mono_marshal_get_isinst (klass);
10827 iargs [0] = sp [0];
10829 costs = inline_method (cfg, mono_isinst, mono_method_signature (mono_isinst),
10830 iargs, ip, cfg->real_offset, TRUE);
10831 CHECK_CFG_EXCEPTION;
10832 g_assert (costs > 0);
10835 cfg->real_offset += 5;
10839 inline_costs += costs;
10842 ins = handle_isinst (cfg, klass, *sp, context_used);
10843 CHECK_CFG_EXCEPTION;
10849 case CEE_UNBOX_ANY: {
10850 MonoInst *res, *addr;
10855 token = read32 (ip + 1);
10856 klass = mini_get_class (method, token, generic_context);
10857 CHECK_TYPELOAD (klass);
10859 mono_save_token_info (cfg, image, token, klass);
10861 context_used = mini_class_check_context_used (cfg, klass);
10863 if (mini_is_gsharedvt_klass (klass)) {
10864 res = handle_unbox_gsharedvt (cfg, klass, *sp);
10866 } else if (generic_class_is_reference_type (cfg, klass)) {
10867 res = handle_castclass (cfg, klass, *sp, ip, &inline_costs);
10868 CHECK_CFG_EXCEPTION;
10869 } else if (mono_class_is_nullable (klass)) {
10870 res = handle_unbox_nullable (cfg, *sp, klass, context_used);
10872 addr = handle_unbox (cfg, klass, sp, context_used);
10874 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, addr->dreg, 0);
10885 MonoClass *enum_class;
10886 MonoMethod *has_flag;
10892 token = read32 (ip + 1);
10893 klass = mini_get_class (method, token, generic_context);
10894 CHECK_TYPELOAD (klass);
10896 mono_save_token_info (cfg, image, token, klass);
10898 context_used = mini_class_check_context_used (cfg, klass);
10900 if (generic_class_is_reference_type (cfg, klass)) {
10906 if (klass == mono_defaults.void_class)
10908 if (target_type_is_incompatible (cfg, &klass->byval_arg, *sp))
10910 /* frequent check in generic code: box (struct), brtrue */
10915 * <push int/long ptr>
10918 * constrained. MyFlags
10919 * callvirt instace bool class [mscorlib] System.Enum::HasFlag (class [mscorlib] System.Enum)
10921 * If we find this sequence and the operand types on box and constrained
10922 * are equal, we can emit a specialized instruction sequence instead of
10923 * the very slow HasFlag () call.
10925 if ((cfg->opt & MONO_OPT_INTRINS) &&
10926 /* Cheap checks first. */
10927 ip + 5 + 6 + 5 < end &&
10928 ip [5] == CEE_PREFIX1 &&
10929 ip [6] == CEE_CONSTRAINED_ &&
10930 ip [11] == CEE_CALLVIRT &&
10931 ip_in_bb (cfg, cfg->cbb, ip + 5 + 6 + 5) &&
10932 mono_class_is_enum (klass) &&
10933 (enum_class = mini_get_class (method, read32 (ip + 7), generic_context)) &&
10934 (has_flag = mini_get_method (cfg, method, read32 (ip + 12), NULL, generic_context)) &&
10935 has_flag->klass == mono_defaults.enum_class &&
10936 !strcmp (has_flag->name, "HasFlag") &&
10937 has_flag->signature->hasthis &&
10938 has_flag->signature->param_count == 1) {
10939 CHECK_TYPELOAD (enum_class);
10941 if (enum_class == klass) {
10942 MonoInst *enum_this, *enum_flag;
10947 enum_this = sp [0];
10948 enum_flag = sp [1];
10950 *sp++ = handle_enum_has_flag (cfg, klass, enum_this, enum_flag);
10955 // FIXME: LLVM can't handle the inconsistent bb linking
10956 if (!mono_class_is_nullable (klass) &&
10957 !mini_is_gsharedvt_klass (klass) &&
10958 ip + 5 < end && ip_in_bb (cfg, cfg->cbb, ip + 5) &&
10959 (ip [5] == CEE_BRTRUE ||
10960 ip [5] == CEE_BRTRUE_S ||
10961 ip [5] == CEE_BRFALSE ||
10962 ip [5] == CEE_BRFALSE_S)) {
10963 gboolean is_true = ip [5] == CEE_BRTRUE || ip [5] == CEE_BRTRUE_S;
10965 MonoBasicBlock *true_bb, *false_bb;
10969 if (cfg->verbose_level > 3) {
10970 printf ("converting (in B%d: stack: %d) %s", cfg->cbb->block_num, (int)(sp - stack_start), mono_disasm_code_one (NULL, method, ip, NULL));
10971 printf ("<box+brtrue opt>\n");
10976 case CEE_BRFALSE_S:
10979 target = ip + 1 + (signed char)(*ip);
10986 target = ip + 4 + (gint)(read32 (ip));
10990 g_assert_not_reached ();
10994 * We need to link both bblocks, since it is needed for handling stack
10995 * arguments correctly (See test_0_box_brtrue_opt_regress_81102).
10996 * Branching to only one of them would lead to inconsistencies, so
10997 * generate an ICONST+BRTRUE, the branch opts will get rid of them.
10999 GET_BBLOCK (cfg, true_bb, target);
11000 GET_BBLOCK (cfg, false_bb, ip);
11002 mono_link_bblock (cfg, cfg->cbb, true_bb);
11003 mono_link_bblock (cfg, cfg->cbb, false_bb);
11005 if (sp != stack_start) {
11006 handle_stack_args (cfg, stack_start, sp - stack_start);
11008 CHECK_UNVERIFIABLE (cfg);
11011 if (COMPILE_LLVM (cfg)) {
11012 dreg = alloc_ireg (cfg);
11013 MONO_EMIT_NEW_ICONST (cfg, dreg, 0);
11014 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, dreg, is_true ? 0 : 1);
11016 MONO_EMIT_NEW_BRANCH_BLOCK2 (cfg, OP_IBEQ, true_bb, false_bb);
11018 /* The JIT can't eliminate the iconst+compare */
11019 MONO_INST_NEW (cfg, ins, OP_BR);
11020 ins->inst_target_bb = is_true ? true_bb : false_bb;
11021 MONO_ADD_INS (cfg->cbb, ins);
11024 start_new_bblock = 1;
11028 *sp++ = handle_box (cfg, val, klass, context_used);
11030 CHECK_CFG_EXCEPTION;
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 (mono_class_is_nullable (klass)) {
11050 val = handle_unbox_nullable (cfg, *sp, klass, context_used);
11051 EMIT_NEW_VARLOADA (cfg, ins, get_vreg_to_inst (cfg, val->dreg), &val->klass->byval_arg);
11055 ins = handle_unbox (cfg, klass, sp, context_used);
11068 MonoClassField *field;
11069 #ifndef DISABLE_REMOTING
11073 gboolean is_instance;
11075 gpointer addr = NULL;
11076 gboolean is_special_static;
11078 MonoInst *store_val = NULL;
11079 MonoInst *thread_ins;
11082 is_instance = (op == CEE_LDFLD || op == CEE_LDFLDA || op == CEE_STFLD);
11084 if (op == CEE_STFLD) {
11087 store_val = sp [1];
11092 if (sp [0]->type == STACK_I4 || sp [0]->type == STACK_I8 || sp [0]->type == STACK_R8)
11094 if (*ip != CEE_LDFLD && sp [0]->type == STACK_VTYPE)
11097 if (op == CEE_STSFLD) {
11100 store_val = sp [0];
11105 token = read32 (ip + 1);
11106 if (method->wrapper_type != MONO_WRAPPER_NONE) {
11107 field = (MonoClassField *)mono_method_get_wrapper_data (method, token);
11108 klass = field->parent;
11111 field = mono_field_from_token_checked (image, token, &klass, generic_context, &cfg->error);
11114 if (!dont_verify && !cfg->skip_visibility && !mono_method_can_access_field (method, field))
11115 FIELD_ACCESS_FAILURE (method, field);
11116 mono_class_init (klass);
11118 /* if the class is Critical then transparent code cannot access it's fields */
11119 if (!is_instance && mono_security_core_clr_enabled ())
11120 ensure_method_is_allowed_to_access_field (cfg, method, field);
11122 /* XXX this is technically required but, so far (SL2), no [SecurityCritical] types (not many exists) have
11123 any visible *instance* field (in fact there's a single case for a static field in Marshal) XXX
11124 if (mono_security_core_clr_enabled ())
11125 ensure_method_is_allowed_to_access_field (cfg, method, field);
11128 ftype = mono_field_get_type (field);
11131 * LDFLD etc. is usable on static fields as well, so convert those cases to
11134 if (is_instance && ftype->attrs & FIELD_ATTRIBUTE_STATIC) {
11146 g_assert_not_reached ();
11148 is_instance = FALSE;
11151 context_used = mini_class_check_context_used (cfg, klass);
11153 /* INSTANCE CASE */
11155 foffset = klass->valuetype? field->offset - sizeof (MonoObject): field->offset;
11156 if (op == CEE_STFLD) {
11157 if (target_type_is_incompatible (cfg, field->type, sp [1]))
11159 #ifndef DISABLE_REMOTING
11160 if ((mono_class_is_marshalbyref (klass) && !MONO_CHECK_THIS (sp [0])) || mono_class_is_contextbound (klass) || klass == mono_defaults.marshalbyrefobject_class) {
11161 MonoMethod *stfld_wrapper = mono_marshal_get_stfld_wrapper (field->type);
11162 MonoInst *iargs [5];
11164 GSHAREDVT_FAILURE (op);
11166 iargs [0] = sp [0];
11167 EMIT_NEW_CLASSCONST (cfg, iargs [1], klass);
11168 EMIT_NEW_FIELDCONST (cfg, iargs [2], field);
11169 EMIT_NEW_ICONST (cfg, iargs [3], klass->valuetype ? field->offset - sizeof (MonoObject) :
11171 iargs [4] = sp [1];
11173 if (cfg->opt & MONO_OPT_INLINE || cfg->compile_aot) {
11174 costs = inline_method (cfg, stfld_wrapper, mono_method_signature (stfld_wrapper),
11175 iargs, ip, cfg->real_offset, TRUE);
11176 CHECK_CFG_EXCEPTION;
11177 g_assert (costs > 0);
11179 cfg->real_offset += 5;
11181 inline_costs += costs;
11183 mono_emit_method_call (cfg, stfld_wrapper, iargs, NULL);
11190 MONO_EMIT_NULL_CHECK (cfg, sp [0]->dreg);
11192 if (mini_is_gsharedvt_klass (klass)) {
11193 MonoInst *offset_ins;
11195 context_used = mini_class_check_context_used (cfg, klass);
11197 offset_ins = emit_get_gsharedvt_info (cfg, field, MONO_RGCTX_INFO_FIELD_OFFSET);
11198 /* The value is offset by 1 */
11199 EMIT_NEW_BIALU_IMM (cfg, ins, OP_PSUB_IMM, offset_ins->dreg, offset_ins->dreg, 1);
11200 dreg = alloc_ireg_mp (cfg);
11201 EMIT_NEW_BIALU (cfg, ins, OP_PADD, dreg, sp [0]->dreg, offset_ins->dreg);
11202 /* The decomposition will call mini_emit_stobj () which will emit a wbarrier if needed */
11203 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, store, field->type, dreg, 0, sp [1]->dreg);
11205 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, store, field->type, sp [0]->dreg, foffset, sp [1]->dreg);
11207 if (sp [0]->opcode != OP_LDADDR)
11208 store->flags |= MONO_INST_FAULT;
11210 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)) {
11211 /* insert call to write barrier */
11215 dreg = alloc_ireg_mp (cfg);
11216 EMIT_NEW_BIALU_IMM (cfg, ptr, OP_PADD_IMM, dreg, sp [0]->dreg, foffset);
11217 emit_write_barrier (cfg, ptr, sp [1]);
11220 store->flags |= ins_flag;
11227 #ifndef DISABLE_REMOTING
11228 if (is_instance && ((mono_class_is_marshalbyref (klass) && !MONO_CHECK_THIS (sp [0])) || mono_class_is_contextbound (klass) || klass == mono_defaults.marshalbyrefobject_class)) {
11229 MonoMethod *wrapper = (op == CEE_LDFLDA) ? mono_marshal_get_ldflda_wrapper (field->type) : mono_marshal_get_ldfld_wrapper (field->type);
11230 MonoInst *iargs [4];
11232 GSHAREDVT_FAILURE (op);
11234 iargs [0] = sp [0];
11235 EMIT_NEW_CLASSCONST (cfg, iargs [1], klass);
11236 EMIT_NEW_FIELDCONST (cfg, iargs [2], field);
11237 EMIT_NEW_ICONST (cfg, iargs [3], klass->valuetype ? field->offset - sizeof (MonoObject) : field->offset);
11238 if (cfg->opt & MONO_OPT_INLINE || cfg->compile_aot) {
11239 costs = inline_method (cfg, wrapper, mono_method_signature (wrapper),
11240 iargs, ip, cfg->real_offset, TRUE);
11241 CHECK_CFG_EXCEPTION;
11242 g_assert (costs > 0);
11244 cfg->real_offset += 5;
11248 inline_costs += costs;
11250 ins = mono_emit_method_call (cfg, wrapper, iargs, NULL);
11256 if (sp [0]->type == STACK_VTYPE) {
11259 /* Have to compute the address of the variable */
11261 var = get_vreg_to_inst (cfg, sp [0]->dreg);
11263 var = mono_compile_create_var_for_vreg (cfg, &klass->byval_arg, OP_LOCAL, sp [0]->dreg);
11265 g_assert (var->klass == klass);
11267 EMIT_NEW_VARLOADA (cfg, ins, var, &var->klass->byval_arg);
11271 if (op == CEE_LDFLDA) {
11272 if (sp [0]->type == STACK_OBJ) {
11273 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, sp [0]->dreg, 0);
11274 MONO_EMIT_NEW_COND_EXC (cfg, EQ, "NullReferenceException");
11277 dreg = alloc_ireg_mp (cfg);
11279 if (mini_is_gsharedvt_klass (klass)) {
11280 MonoInst *offset_ins;
11282 offset_ins = emit_get_gsharedvt_info (cfg, field, MONO_RGCTX_INFO_FIELD_OFFSET);
11283 /* The value is offset by 1 */
11284 EMIT_NEW_BIALU_IMM (cfg, ins, OP_PSUB_IMM, offset_ins->dreg, offset_ins->dreg, 1);
11285 EMIT_NEW_BIALU (cfg, ins, OP_PADD, dreg, sp [0]->dreg, offset_ins->dreg);
11287 EMIT_NEW_BIALU_IMM (cfg, ins, OP_PADD_IMM, dreg, sp [0]->dreg, foffset);
11289 ins->klass = mono_class_from_mono_type (field->type);
11290 ins->type = STACK_MP;
11295 MONO_EMIT_NULL_CHECK (cfg, sp [0]->dreg);
11297 if (mini_is_gsharedvt_klass (klass)) {
11298 MonoInst *offset_ins;
11300 offset_ins = emit_get_gsharedvt_info (cfg, field, MONO_RGCTX_INFO_FIELD_OFFSET);
11301 /* The value is offset by 1 */
11302 EMIT_NEW_BIALU_IMM (cfg, ins, OP_PSUB_IMM, offset_ins->dreg, offset_ins->dreg, 1);
11303 dreg = alloc_ireg_mp (cfg);
11304 EMIT_NEW_BIALU (cfg, ins, OP_PADD, dreg, sp [0]->dreg, offset_ins->dreg);
11305 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, load, field->type, dreg, 0);
11307 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, load, field->type, sp [0]->dreg, foffset);
11309 load->flags |= ins_flag;
11310 if (sp [0]->opcode != OP_LDADDR)
11311 load->flags |= MONO_INST_FAULT;
11323 context_used = mini_class_check_context_used (cfg, klass);
11325 if (ftype->attrs & FIELD_ATTRIBUTE_LITERAL)
11328 /* The special_static_fields field is init'd in mono_class_vtable, so it needs
11329 * to be called here.
11331 if (!context_used && !(cfg->opt & MONO_OPT_SHARED)) {
11332 mono_class_vtable (cfg->domain, klass);
11333 CHECK_TYPELOAD (klass);
11335 mono_domain_lock (cfg->domain);
11336 if (cfg->domain->special_static_fields)
11337 addr = g_hash_table_lookup (cfg->domain->special_static_fields, field);
11338 mono_domain_unlock (cfg->domain);
11340 is_special_static = mono_class_field_is_special_static (field);
11342 if (is_special_static && ((gsize)addr & 0x80000000) == 0)
11343 thread_ins = mono_get_thread_intrinsic (cfg);
11347 /* Generate IR to compute the field address */
11348 if (is_special_static && ((gsize)addr & 0x80000000) == 0 && thread_ins && !(cfg->opt & MONO_OPT_SHARED) && !context_used) {
11350 * Fast access to TLS data
11351 * Inline version of get_thread_static_data () in
11355 int idx, static_data_reg, array_reg, dreg;
11357 GSHAREDVT_FAILURE (op);
11359 MONO_ADD_INS (cfg->cbb, thread_ins);
11360 static_data_reg = alloc_ireg (cfg);
11361 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, static_data_reg, thread_ins->dreg, MONO_STRUCT_OFFSET (MonoInternalThread, static_data));
11363 if (cfg->compile_aot) {
11364 int offset_reg, offset2_reg, idx_reg;
11366 /* For TLS variables, this will return the TLS offset */
11367 EMIT_NEW_SFLDACONST (cfg, ins, field);
11368 offset_reg = ins->dreg;
11369 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_IAND_IMM, offset_reg, offset_reg, 0x7fffffff);
11370 idx_reg = alloc_ireg (cfg);
11371 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_IAND_IMM, idx_reg, offset_reg, 0x3f);
11372 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ISHL_IMM, idx_reg, idx_reg, sizeof (gpointer) == 8 ? 3 : 2);
11373 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, static_data_reg, static_data_reg, idx_reg);
11374 array_reg = alloc_ireg (cfg);
11375 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, array_reg, static_data_reg, 0);
11376 offset2_reg = alloc_ireg (cfg);
11377 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ISHR_UN_IMM, offset2_reg, offset_reg, 6);
11378 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_IAND_IMM, offset2_reg, offset2_reg, 0x1ffffff);
11379 dreg = alloc_ireg (cfg);
11380 EMIT_NEW_BIALU (cfg, ins, OP_PADD, dreg, array_reg, offset2_reg);
11382 offset = (gsize)addr & 0x7fffffff;
11383 idx = offset & 0x3f;
11385 array_reg = alloc_ireg (cfg);
11386 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, array_reg, static_data_reg, idx * sizeof (gpointer));
11387 dreg = alloc_ireg (cfg);
11388 EMIT_NEW_BIALU_IMM (cfg, ins, OP_ADD_IMM, dreg, array_reg, ((offset >> 6) & 0x1ffffff));
11390 } else if ((cfg->opt & MONO_OPT_SHARED) ||
11391 (cfg->compile_aot && is_special_static) ||
11392 (context_used && is_special_static)) {
11393 MonoInst *iargs [2];
11395 g_assert (field->parent);
11396 EMIT_NEW_DOMAINCONST (cfg, iargs [0]);
11397 if (context_used) {
11398 iargs [1] = emit_get_rgctx_field (cfg, context_used,
11399 field, MONO_RGCTX_INFO_CLASS_FIELD);
11401 EMIT_NEW_FIELDCONST (cfg, iargs [1], field);
11403 ins = mono_emit_jit_icall (cfg, mono_class_static_field_address, iargs);
11404 } else if (context_used) {
11405 MonoInst *static_data;
11408 g_print ("sharing static field access in %s.%s.%s - depth %d offset %d\n",
11409 method->klass->name_space, method->klass->name, method->name,
11410 depth, field->offset);
11413 if (mono_class_needs_cctor_run (klass, method))
11414 emit_class_init (cfg, klass);
11417 * The pointer we're computing here is
11419 * super_info.static_data + field->offset
11421 static_data = emit_get_rgctx_klass (cfg, context_used,
11422 klass, MONO_RGCTX_INFO_STATIC_DATA);
11424 if (mini_is_gsharedvt_klass (klass)) {
11425 MonoInst *offset_ins;
11427 offset_ins = emit_get_rgctx_field (cfg, context_used, field, MONO_RGCTX_INFO_FIELD_OFFSET);
11428 /* The value is offset by 1 */
11429 EMIT_NEW_BIALU_IMM (cfg, ins, OP_PSUB_IMM, offset_ins->dreg, offset_ins->dreg, 1);
11430 dreg = alloc_ireg_mp (cfg);
11431 EMIT_NEW_BIALU (cfg, ins, OP_PADD, dreg, static_data->dreg, offset_ins->dreg);
11432 } else if (field->offset == 0) {
11435 int addr_reg = mono_alloc_preg (cfg);
11436 EMIT_NEW_BIALU_IMM (cfg, ins, OP_PADD_IMM, addr_reg, static_data->dreg, field->offset);
11438 } else if ((cfg->opt & MONO_OPT_SHARED) || (cfg->compile_aot && addr)) {
11439 MonoInst *iargs [2];
11441 g_assert (field->parent);
11442 EMIT_NEW_DOMAINCONST (cfg, iargs [0]);
11443 EMIT_NEW_FIELDCONST (cfg, iargs [1], field);
11444 ins = mono_emit_jit_icall (cfg, mono_class_static_field_address, iargs);
11446 MonoVTable *vtable = NULL;
11448 if (!cfg->compile_aot)
11449 vtable = mono_class_vtable (cfg->domain, klass);
11450 CHECK_TYPELOAD (klass);
11453 if (mini_field_access_needs_cctor_run (cfg, method, klass, vtable)) {
11454 if (!(g_slist_find (class_inits, klass))) {
11455 emit_class_init (cfg, klass);
11456 if (cfg->verbose_level > 2)
11457 printf ("class %s.%s needs init call for %s\n", klass->name_space, klass->name, mono_field_get_name (field));
11458 class_inits = g_slist_prepend (class_inits, klass);
11461 if (cfg->run_cctors) {
11463 /* This makes so that inline cannot trigger */
11464 /* .cctors: too many apps depend on them */
11465 /* running with a specific order... */
11467 if (! vtable->initialized)
11468 INLINE_FAILURE ("class init");
11469 ex = mono_runtime_class_init_full (vtable, FALSE);
11471 set_exception_object (cfg, ex);
11472 goto exception_exit;
11476 if (cfg->compile_aot)
11477 EMIT_NEW_SFLDACONST (cfg, ins, field);
11480 addr = (char*)mono_vtable_get_static_field_data (vtable) + field->offset;
11482 EMIT_NEW_PCONST (cfg, ins, addr);
11485 MonoInst *iargs [1];
11486 EMIT_NEW_ICONST (cfg, iargs [0], GPOINTER_TO_UINT (addr));
11487 ins = mono_emit_jit_icall (cfg, mono_get_special_static_data, iargs);
11491 /* Generate IR to do the actual load/store operation */
11493 if ((op == CEE_STFLD || op == CEE_STSFLD) && (ins_flag & MONO_INST_VOLATILE)) {
11494 /* Volatile stores have release semantics, see 12.6.7 in Ecma 335 */
11495 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_REL);
11498 if (op == CEE_LDSFLDA) {
11499 ins->klass = mono_class_from_mono_type (ftype);
11500 ins->type = STACK_PTR;
11502 } else if (op == CEE_STSFLD) {
11505 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, store, ftype, ins->dreg, 0, store_val->dreg);
11506 store->flags |= ins_flag;
11508 gboolean is_const = FALSE;
11509 MonoVTable *vtable = NULL;
11510 gpointer addr = NULL;
11512 if (!context_used) {
11513 vtable = mono_class_vtable (cfg->domain, klass);
11514 CHECK_TYPELOAD (klass);
11516 if ((ftype->attrs & FIELD_ATTRIBUTE_INIT_ONLY) && (((addr = mono_aot_readonly_field_override (field)) != NULL) ||
11517 (!context_used && !((cfg->opt & MONO_OPT_SHARED) || cfg->compile_aot) && vtable->initialized))) {
11518 int ro_type = ftype->type;
11520 addr = (char*)mono_vtable_get_static_field_data (vtable) + field->offset;
11521 if (ro_type == MONO_TYPE_VALUETYPE && ftype->data.klass->enumtype) {
11522 ro_type = mono_class_enum_basetype (ftype->data.klass)->type;
11525 GSHAREDVT_FAILURE (op);
11527 /* printf ("RO-FIELD %s.%s:%s\n", klass->name_space, klass->name, mono_field_get_name (field));*/
11530 case MONO_TYPE_BOOLEAN:
11532 EMIT_NEW_ICONST (cfg, *sp, *((guint8 *)addr));
11536 EMIT_NEW_ICONST (cfg, *sp, *((gint8 *)addr));
11539 case MONO_TYPE_CHAR:
11541 EMIT_NEW_ICONST (cfg, *sp, *((guint16 *)addr));
11545 EMIT_NEW_ICONST (cfg, *sp, *((gint16 *)addr));
11550 EMIT_NEW_ICONST (cfg, *sp, *((gint32 *)addr));
11554 EMIT_NEW_ICONST (cfg, *sp, *((guint32 *)addr));
11559 case MONO_TYPE_PTR:
11560 case MONO_TYPE_FNPTR:
11561 EMIT_NEW_PCONST (cfg, *sp, *((gpointer *)addr));
11562 type_to_eval_stack_type ((cfg), field->type, *sp);
11565 case MONO_TYPE_STRING:
11566 case MONO_TYPE_OBJECT:
11567 case MONO_TYPE_CLASS:
11568 case MONO_TYPE_SZARRAY:
11569 case MONO_TYPE_ARRAY:
11570 if (!mono_gc_is_moving ()) {
11571 EMIT_NEW_PCONST (cfg, *sp, *((gpointer *)addr));
11572 type_to_eval_stack_type ((cfg), field->type, *sp);
11580 EMIT_NEW_I8CONST (cfg, *sp, *((gint64 *)addr));
11585 case MONO_TYPE_VALUETYPE:
11595 CHECK_STACK_OVF (1);
11597 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, load, field->type, ins->dreg, 0);
11598 load->flags |= ins_flag;
11604 if ((op == CEE_LDFLD || op == CEE_LDSFLD) && (ins_flag & MONO_INST_VOLATILE)) {
11605 /* Volatile loads have acquire semantics, see 12.6.7 in Ecma 335 */
11606 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_ACQ);
11617 token = read32 (ip + 1);
11618 klass = mini_get_class (method, token, generic_context);
11619 CHECK_TYPELOAD (klass);
11620 if (ins_flag & MONO_INST_VOLATILE) {
11621 /* Volatile stores have release semantics, see 12.6.7 in Ecma 335 */
11622 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_REL);
11624 /* FIXME: should check item at sp [1] is compatible with the type of the store. */
11625 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, sp [0]->dreg, 0, sp [1]->dreg);
11626 ins->flags |= ins_flag;
11627 if (cfg->gen_write_barriers && cfg->method->wrapper_type != MONO_WRAPPER_WRITE_BARRIER &&
11628 generic_class_is_reference_type (cfg, klass)) {
11629 /* insert call to write barrier */
11630 emit_write_barrier (cfg, sp [0], sp [1]);
11642 const char *data_ptr;
11644 guint32 field_token;
11650 token = read32 (ip + 1);
11652 klass = mini_get_class (method, token, generic_context);
11653 CHECK_TYPELOAD (klass);
11655 context_used = mini_class_check_context_used (cfg, klass);
11657 if (sp [0]->type == STACK_I8 || (SIZEOF_VOID_P == 8 && sp [0]->type == STACK_PTR)) {
11658 MONO_INST_NEW (cfg, ins, OP_LCONV_TO_OVF_U4);
11659 ins->sreg1 = sp [0]->dreg;
11660 ins->type = STACK_I4;
11661 ins->dreg = alloc_ireg (cfg);
11662 MONO_ADD_INS (cfg->cbb, ins);
11663 *sp = mono_decompose_opcode (cfg, ins);
11666 if (context_used) {
11667 MonoInst *args [3];
11668 MonoClass *array_class = mono_array_class_get (klass, 1);
11669 MonoMethod *managed_alloc = mono_gc_get_managed_array_allocator (array_class);
11671 /* FIXME: Use OP_NEWARR and decompose later to help abcrem */
11674 args [0] = emit_get_rgctx_klass (cfg, context_used,
11675 array_class, MONO_RGCTX_INFO_VTABLE);
11680 ins = mono_emit_method_call (cfg, managed_alloc, args, NULL);
11682 ins = mono_emit_jit_icall (cfg, mono_array_new_specific, args);
11684 if (cfg->opt & MONO_OPT_SHARED) {
11685 /* Decompose now to avoid problems with references to the domainvar */
11686 MonoInst *iargs [3];
11688 EMIT_NEW_DOMAINCONST (cfg, iargs [0]);
11689 EMIT_NEW_CLASSCONST (cfg, iargs [1], klass);
11690 iargs [2] = sp [0];
11692 ins = mono_emit_jit_icall (cfg, mono_array_new, iargs);
11694 /* Decompose later since it is needed by abcrem */
11695 MonoClass *array_type = mono_array_class_get (klass, 1);
11696 mono_class_vtable (cfg->domain, array_type);
11697 CHECK_TYPELOAD (array_type);
11699 MONO_INST_NEW (cfg, ins, OP_NEWARR);
11700 ins->dreg = alloc_ireg_ref (cfg);
11701 ins->sreg1 = sp [0]->dreg;
11702 ins->inst_newa_class = klass;
11703 ins->type = STACK_OBJ;
11704 ins->klass = array_type;
11705 MONO_ADD_INS (cfg->cbb, ins);
11706 cfg->flags |= MONO_CFG_HAS_ARRAY_ACCESS;
11707 cfg->cbb->has_array_access = TRUE;
11709 /* Needed so mono_emit_load_get_addr () gets called */
11710 mono_get_got_var (cfg);
11720 * we inline/optimize the initialization sequence if possible.
11721 * we should also allocate the array as not cleared, since we spend as much time clearing to 0 as initializing
11722 * for small sizes open code the memcpy
11723 * ensure the rva field is big enough
11725 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))) {
11726 MonoMethod *memcpy_method = get_memcpy_method ();
11727 MonoInst *iargs [3];
11728 int add_reg = alloc_ireg_mp (cfg);
11730 EMIT_NEW_BIALU_IMM (cfg, iargs [0], OP_PADD_IMM, add_reg, ins->dreg, MONO_STRUCT_OFFSET (MonoArray, vector));
11731 if (cfg->compile_aot) {
11732 EMIT_NEW_AOTCONST_TOKEN (cfg, iargs [1], MONO_PATCH_INFO_RVA, method->klass->image, GPOINTER_TO_UINT(field_token), STACK_PTR, NULL);
11734 EMIT_NEW_PCONST (cfg, iargs [1], (char*)data_ptr);
11736 EMIT_NEW_ICONST (cfg, iargs [2], data_size);
11737 mono_emit_method_call (cfg, memcpy_method, iargs, NULL);
11746 if (sp [0]->type != STACK_OBJ)
11749 MONO_INST_NEW (cfg, ins, OP_LDLEN);
11750 ins->dreg = alloc_preg (cfg);
11751 ins->sreg1 = sp [0]->dreg;
11752 ins->type = STACK_I4;
11753 /* This flag will be inherited by the decomposition */
11754 ins->flags |= MONO_INST_FAULT;
11755 MONO_ADD_INS (cfg->cbb, ins);
11756 cfg->flags |= MONO_CFG_HAS_ARRAY_ACCESS;
11757 cfg->cbb->has_array_access = TRUE;
11765 if (sp [0]->type != STACK_OBJ)
11768 cfg->flags |= MONO_CFG_HAS_LDELEMA;
11770 klass = mini_get_class (method, read32 (ip + 1), generic_context);
11771 CHECK_TYPELOAD (klass);
11772 /* we need to make sure that this array is exactly the type it needs
11773 * to be for correctness. the wrappers are lax with their usage
11774 * so we need to ignore them here
11776 if (!klass->valuetype && method->wrapper_type == MONO_WRAPPER_NONE && !readonly) {
11777 MonoClass *array_class = mono_array_class_get (klass, 1);
11778 mini_emit_check_array_type (cfg, sp [0], array_class);
11779 CHECK_TYPELOAD (array_class);
11783 ins = mini_emit_ldelema_1_ins (cfg, klass, sp [0], sp [1], TRUE);
11788 case CEE_LDELEM_I1:
11789 case CEE_LDELEM_U1:
11790 case CEE_LDELEM_I2:
11791 case CEE_LDELEM_U2:
11792 case CEE_LDELEM_I4:
11793 case CEE_LDELEM_U4:
11794 case CEE_LDELEM_I8:
11796 case CEE_LDELEM_R4:
11797 case CEE_LDELEM_R8:
11798 case CEE_LDELEM_REF: {
11804 if (*ip == CEE_LDELEM) {
11806 token = read32 (ip + 1);
11807 klass = mini_get_class (method, token, generic_context);
11808 CHECK_TYPELOAD (klass);
11809 mono_class_init (klass);
11812 klass = array_access_to_klass (*ip);
11814 if (sp [0]->type != STACK_OBJ)
11817 cfg->flags |= MONO_CFG_HAS_LDELEMA;
11819 if (mini_is_gsharedvt_variable_klass (klass)) {
11820 // FIXME-VT: OP_ICONST optimization
11821 addr = mini_emit_ldelema_1_ins (cfg, klass, sp [0], sp [1], TRUE);
11822 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, addr->dreg, 0);
11823 ins->opcode = OP_LOADV_MEMBASE;
11824 } else if (sp [1]->opcode == OP_ICONST) {
11825 int array_reg = sp [0]->dreg;
11826 int index_reg = sp [1]->dreg;
11827 int offset = (mono_class_array_element_size (klass) * sp [1]->inst_c0) + MONO_STRUCT_OFFSET (MonoArray, vector);
11829 if (SIZEOF_REGISTER == 8 && COMPILE_LLVM (cfg))
11830 MONO_EMIT_NEW_UNALU (cfg, OP_ZEXT_I4, index_reg, index_reg);
11832 MONO_EMIT_BOUNDS_CHECK (cfg, array_reg, MonoArray, max_length, index_reg);
11833 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, array_reg, offset);
11835 addr = mini_emit_ldelema_1_ins (cfg, klass, sp [0], sp [1], TRUE);
11836 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, addr->dreg, 0);
11839 if (*ip == CEE_LDELEM)
11846 case CEE_STELEM_I1:
11847 case CEE_STELEM_I2:
11848 case CEE_STELEM_I4:
11849 case CEE_STELEM_I8:
11850 case CEE_STELEM_R4:
11851 case CEE_STELEM_R8:
11852 case CEE_STELEM_REF:
11857 cfg->flags |= MONO_CFG_HAS_LDELEMA;
11859 if (*ip == CEE_STELEM) {
11861 token = read32 (ip + 1);
11862 klass = mini_get_class (method, token, generic_context);
11863 CHECK_TYPELOAD (klass);
11864 mono_class_init (klass);
11867 klass = array_access_to_klass (*ip);
11869 if (sp [0]->type != STACK_OBJ)
11872 emit_array_store (cfg, klass, sp, TRUE);
11874 if (*ip == CEE_STELEM)
11881 case CEE_CKFINITE: {
11885 if (cfg->llvm_only) {
11886 MonoInst *iargs [1];
11888 iargs [0] = sp [0];
11889 *sp++ = mono_emit_jit_icall (cfg, mono_ckfinite, iargs);
11891 MONO_INST_NEW (cfg, ins, OP_CKFINITE);
11892 ins->sreg1 = sp [0]->dreg;
11893 ins->dreg = alloc_freg (cfg);
11894 ins->type = STACK_R8;
11895 MONO_ADD_INS (cfg->cbb, ins);
11897 *sp++ = mono_decompose_opcode (cfg, ins);
11903 case CEE_REFANYVAL: {
11904 MonoInst *src_var, *src;
11906 int klass_reg = alloc_preg (cfg);
11907 int dreg = alloc_preg (cfg);
11909 GSHAREDVT_FAILURE (*ip);
11912 MONO_INST_NEW (cfg, ins, *ip);
11915 klass = mini_get_class (method, read32 (ip + 1), generic_context);
11916 CHECK_TYPELOAD (klass);
11918 context_used = mini_class_check_context_used (cfg, klass);
11921 src_var = get_vreg_to_inst (cfg, sp [0]->dreg);
11923 src_var = mono_compile_create_var_for_vreg (cfg, &mono_defaults.typed_reference_class->byval_arg, OP_LOCAL, sp [0]->dreg);
11924 EMIT_NEW_VARLOADA (cfg, src, src_var, src_var->inst_vtype);
11925 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, src->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, klass));
11927 if (context_used) {
11928 MonoInst *klass_ins;
11930 klass_ins = emit_get_rgctx_klass (cfg, context_used,
11931 klass, MONO_RGCTX_INFO_KLASS);
11934 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, klass_reg, klass_ins->dreg);
11935 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException");
11937 mini_emit_class_check (cfg, klass_reg, klass);
11939 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, dreg, src->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, value));
11940 ins->type = STACK_MP;
11941 ins->klass = klass;
11946 case CEE_MKREFANY: {
11947 MonoInst *loc, *addr;
11949 GSHAREDVT_FAILURE (*ip);
11952 MONO_INST_NEW (cfg, ins, *ip);
11955 klass = mini_get_class (method, read32 (ip + 1), generic_context);
11956 CHECK_TYPELOAD (klass);
11958 context_used = mini_class_check_context_used (cfg, klass);
11960 loc = mono_compile_create_var (cfg, &mono_defaults.typed_reference_class->byval_arg, OP_LOCAL);
11961 EMIT_NEW_TEMPLOADA (cfg, addr, loc->inst_c0);
11963 if (context_used) {
11964 MonoInst *const_ins;
11965 int type_reg = alloc_preg (cfg);
11967 const_ins = emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_KLASS);
11968 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREP_MEMBASE_REG, addr->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, klass), const_ins->dreg);
11969 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ADD_IMM, type_reg, const_ins->dreg, MONO_STRUCT_OFFSET (MonoClass, byval_arg));
11970 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREP_MEMBASE_REG, addr->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, type), type_reg);
11971 } else if (cfg->compile_aot) {
11972 int const_reg = alloc_preg (cfg);
11973 int type_reg = alloc_preg (cfg);
11975 MONO_EMIT_NEW_CLASSCONST (cfg, const_reg, klass);
11976 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREP_MEMBASE_REG, addr->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, klass), const_reg);
11977 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ADD_IMM, type_reg, const_reg, MONO_STRUCT_OFFSET (MonoClass, byval_arg));
11978 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREP_MEMBASE_REG, addr->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, type), type_reg);
11980 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREP_MEMBASE_IMM, addr->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, type), &klass->byval_arg);
11981 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREP_MEMBASE_IMM, addr->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, klass), klass);
11983 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREP_MEMBASE_REG, addr->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, value), sp [0]->dreg);
11985 EMIT_NEW_TEMPLOAD (cfg, ins, loc->inst_c0);
11986 ins->type = STACK_VTYPE;
11987 ins->klass = mono_defaults.typed_reference_class;
11992 case CEE_LDTOKEN: {
11994 MonoClass *handle_class;
11996 CHECK_STACK_OVF (1);
11999 n = read32 (ip + 1);
12001 if (method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD ||
12002 method->wrapper_type == MONO_WRAPPER_SYNCHRONIZED) {
12003 handle = mono_method_get_wrapper_data (method, n);
12004 handle_class = (MonoClass *)mono_method_get_wrapper_data (method, n + 1);
12005 if (handle_class == mono_defaults.typehandle_class)
12006 handle = &((MonoClass*)handle)->byval_arg;
12009 handle = mono_ldtoken_checked (image, n, &handle_class, generic_context, &cfg->error);
12014 mono_class_init (handle_class);
12015 if (cfg->gshared) {
12016 if (mono_metadata_token_table (n) == MONO_TABLE_TYPEDEF ||
12017 mono_metadata_token_table (n) == MONO_TABLE_TYPEREF) {
12018 /* This case handles ldtoken
12019 of an open type, like for
12022 } else if (handle_class == mono_defaults.typehandle_class) {
12023 context_used = mini_class_check_context_used (cfg, mono_class_from_mono_type ((MonoType *)handle));
12024 } else if (handle_class == mono_defaults.fieldhandle_class)
12025 context_used = mini_class_check_context_used (cfg, ((MonoClassField*)handle)->parent);
12026 else if (handle_class == mono_defaults.methodhandle_class)
12027 context_used = mini_method_check_context_used (cfg, (MonoMethod *)handle);
12029 g_assert_not_reached ();
12032 if ((cfg->opt & MONO_OPT_SHARED) &&
12033 method->wrapper_type != MONO_WRAPPER_DYNAMIC_METHOD &&
12034 method->wrapper_type != MONO_WRAPPER_SYNCHRONIZED) {
12035 MonoInst *addr, *vtvar, *iargs [3];
12036 int method_context_used;
12038 method_context_used = mini_method_check_context_used (cfg, method);
12040 vtvar = mono_compile_create_var (cfg, &handle_class->byval_arg, OP_LOCAL);
12042 EMIT_NEW_IMAGECONST (cfg, iargs [0], image);
12043 EMIT_NEW_ICONST (cfg, iargs [1], n);
12044 if (method_context_used) {
12045 iargs [2] = emit_get_rgctx_method (cfg, method_context_used,
12046 method, MONO_RGCTX_INFO_METHOD);
12047 ins = mono_emit_jit_icall (cfg, mono_ldtoken_wrapper_generic_shared, iargs);
12049 EMIT_NEW_PCONST (cfg, iargs [2], generic_context);
12050 ins = mono_emit_jit_icall (cfg, mono_ldtoken_wrapper, iargs);
12052 EMIT_NEW_TEMPLOADA (cfg, addr, vtvar->inst_c0);
12054 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, addr->dreg, 0, ins->dreg);
12056 EMIT_NEW_TEMPLOAD (cfg, ins, vtvar->inst_c0);
12058 if ((ip + 5 < end) && ip_in_bb (cfg, cfg->cbb, ip + 5) &&
12059 ((ip [5] == CEE_CALL) || (ip [5] == CEE_CALLVIRT)) &&
12060 (cmethod = mini_get_method (cfg, method, read32 (ip + 6), NULL, generic_context)) &&
12061 (cmethod->klass == mono_defaults.systemtype_class) &&
12062 (strcmp (cmethod->name, "GetTypeFromHandle") == 0)) {
12063 MonoClass *tclass = mono_class_from_mono_type ((MonoType *)handle);
12065 mono_class_init (tclass);
12066 if (context_used) {
12067 ins = emit_get_rgctx_klass (cfg, context_used,
12068 tclass, MONO_RGCTX_INFO_REFLECTION_TYPE);
12069 } else if (cfg->compile_aot) {
12070 if (method->wrapper_type) {
12071 mono_error_init (&error); //got to do it since there are multiple conditionals below
12072 if (mono_class_get_checked (tclass->image, tclass->type_token, &error) == tclass && !generic_context) {
12073 /* Special case for static synchronized wrappers */
12074 EMIT_NEW_TYPE_FROM_HANDLE_CONST (cfg, ins, tclass->image, tclass->type_token, generic_context);
12076 mono_error_cleanup (&error); /* FIXME don't swallow the error */
12077 /* FIXME: n is not a normal token */
12079 EMIT_NEW_PCONST (cfg, ins, NULL);
12082 EMIT_NEW_TYPE_FROM_HANDLE_CONST (cfg, ins, image, n, generic_context);
12085 EMIT_NEW_PCONST (cfg, ins, mono_type_get_object (cfg->domain, (MonoType *)handle));
12087 ins->type = STACK_OBJ;
12088 ins->klass = cmethod->klass;
12091 MonoInst *addr, *vtvar;
12093 vtvar = mono_compile_create_var (cfg, &handle_class->byval_arg, OP_LOCAL);
12095 if (context_used) {
12096 if (handle_class == mono_defaults.typehandle_class) {
12097 ins = emit_get_rgctx_klass (cfg, context_used,
12098 mono_class_from_mono_type ((MonoType *)handle),
12099 MONO_RGCTX_INFO_TYPE);
12100 } else if (handle_class == mono_defaults.methodhandle_class) {
12101 ins = emit_get_rgctx_method (cfg, context_used,
12102 (MonoMethod *)handle, MONO_RGCTX_INFO_METHOD);
12103 } else if (handle_class == mono_defaults.fieldhandle_class) {
12104 ins = emit_get_rgctx_field (cfg, context_used,
12105 (MonoClassField *)handle, MONO_RGCTX_INFO_CLASS_FIELD);
12107 g_assert_not_reached ();
12109 } else if (cfg->compile_aot) {
12110 EMIT_NEW_LDTOKENCONST (cfg, ins, image, n, generic_context);
12112 EMIT_NEW_PCONST (cfg, ins, handle);
12114 EMIT_NEW_TEMPLOADA (cfg, addr, vtvar->inst_c0);
12115 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, addr->dreg, 0, ins->dreg);
12116 EMIT_NEW_TEMPLOAD (cfg, ins, vtvar->inst_c0);
12126 MONO_INST_NEW (cfg, ins, OP_THROW);
12128 ins->sreg1 = sp [0]->dreg;
12130 cfg->cbb->out_of_line = TRUE;
12131 MONO_ADD_INS (cfg->cbb, ins);
12132 MONO_INST_NEW (cfg, ins, OP_NOT_REACHED);
12133 MONO_ADD_INS (cfg->cbb, ins);
12136 link_bblock (cfg, cfg->cbb, end_bblock);
12137 start_new_bblock = 1;
12138 /* This can complicate code generation for llvm since the return value might not be defined */
12139 if (COMPILE_LLVM (cfg))
12140 INLINE_FAILURE ("throw");
12142 case CEE_ENDFINALLY:
12143 /* mono_save_seq_point_info () depends on this */
12144 if (sp != stack_start)
12145 emit_seq_point (cfg, method, ip, FALSE, FALSE);
12146 MONO_INST_NEW (cfg, ins, OP_ENDFINALLY);
12147 MONO_ADD_INS (cfg->cbb, ins);
12149 start_new_bblock = 1;
12152 * Control will leave the method so empty the stack, otherwise
12153 * the next basic block will start with a nonempty stack.
12155 while (sp != stack_start) {
12160 case CEE_LEAVE_S: {
12163 if (*ip == CEE_LEAVE) {
12165 target = ip + 5 + (gint32)read32(ip + 1);
12168 target = ip + 2 + (signed char)(ip [1]);
12171 /* empty the stack */
12172 while (sp != stack_start) {
12177 * If this leave statement is in a catch block, check for a
12178 * pending exception, and rethrow it if necessary.
12179 * We avoid doing this in runtime invoke wrappers, since those are called
12180 * by native code which excepts the wrapper to catch all exceptions.
12182 for (i = 0; i < header->num_clauses; ++i) {
12183 MonoExceptionClause *clause = &header->clauses [i];
12186 * Use <= in the final comparison to handle clauses with multiple
12187 * leave statements, like in bug #78024.
12188 * The ordering of the exception clauses guarantees that we find the
12189 * innermost clause.
12191 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) {
12193 MonoBasicBlock *dont_throw;
12198 NEW_TEMPLOAD (cfg, load, mono_find_exvar_for_offset (cfg, clause->handler_offset)->inst_c0);
12201 exc_ins = mono_emit_jit_icall (cfg, mono_thread_get_undeniable_exception, NULL);
12203 NEW_BBLOCK (cfg, dont_throw);
12206 * Currently, we always rethrow the abort exception, despite the
12207 * fact that this is not correct. See thread6.cs for an example.
12208 * But propagating the abort exception is more important than
12209 * getting the sematics right.
12211 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, exc_ins->dreg, 0);
12212 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, dont_throw);
12213 MONO_EMIT_NEW_UNALU (cfg, OP_THROW, -1, exc_ins->dreg);
12215 MONO_START_BB (cfg, dont_throw);
12220 cfg->cbb->try_end = (intptr_t)(ip - header->code);
12223 if ((handlers = mono_find_final_block (cfg, ip, target, MONO_EXCEPTION_CLAUSE_FINALLY))) {
12225 MonoExceptionClause *clause;
12227 for (tmp = handlers; tmp; tmp = tmp->next) {
12228 clause = (MonoExceptionClause *)tmp->data;
12229 tblock = cfg->cil_offset_to_bb [clause->handler_offset];
12231 link_bblock (cfg, cfg->cbb, tblock);
12232 MONO_INST_NEW (cfg, ins, OP_CALL_HANDLER);
12233 ins->inst_target_bb = tblock;
12234 ins->inst_eh_block = clause;
12235 MONO_ADD_INS (cfg->cbb, ins);
12236 cfg->cbb->has_call_handler = 1;
12237 if (COMPILE_LLVM (cfg)) {
12238 MonoBasicBlock *target_bb;
12241 * Link the finally bblock with the target, since it will
12242 * conceptually branch there.
12243 * FIXME: Have to link the bblock containing the endfinally.
12245 GET_BBLOCK (cfg, target_bb, target);
12246 link_bblock (cfg, tblock, target_bb);
12249 g_list_free (handlers);
12252 MONO_INST_NEW (cfg, ins, OP_BR);
12253 MONO_ADD_INS (cfg->cbb, ins);
12254 GET_BBLOCK (cfg, tblock, target);
12255 link_bblock (cfg, cfg->cbb, tblock);
12256 ins->inst_target_bb = tblock;
12258 start_new_bblock = 1;
12260 if (*ip == CEE_LEAVE)
12269 * Mono specific opcodes
12271 case MONO_CUSTOM_PREFIX: {
12273 g_assert (method->wrapper_type != MONO_WRAPPER_NONE);
12277 case CEE_MONO_ICALL: {
12279 MonoJitICallInfo *info;
12281 token = read32 (ip + 2);
12282 func = mono_method_get_wrapper_data (method, token);
12283 info = mono_find_jit_icall_by_addr (func);
12285 g_error ("Could not find icall address in wrapper %s", mono_method_full_name (method, 1));
12288 CHECK_STACK (info->sig->param_count);
12289 sp -= info->sig->param_count;
12291 ins = mono_emit_jit_icall (cfg, info->func, sp);
12292 if (!MONO_TYPE_IS_VOID (info->sig->ret))
12296 inline_costs += 10 * num_calls++;
12300 case CEE_MONO_LDPTR_CARD_TABLE:
12301 case CEE_MONO_LDPTR_NURSERY_START:
12302 case CEE_MONO_LDPTR_NURSERY_BITS:
12303 case CEE_MONO_LDPTR_INT_REQ_FLAG: {
12304 CHECK_STACK_OVF (1);
12307 case CEE_MONO_LDPTR_CARD_TABLE:
12308 ins = emit_runtime_constant (cfg, MONO_PATCH_INFO_GC_CARD_TABLE_ADDR, NULL);
12310 case CEE_MONO_LDPTR_NURSERY_START:
12311 ins = emit_runtime_constant (cfg, MONO_PATCH_INFO_GC_NURSERY_START, NULL);
12313 case CEE_MONO_LDPTR_NURSERY_BITS:
12314 ins = emit_runtime_constant (cfg, MONO_PATCH_INFO_GC_NURSERY_BITS, NULL);
12316 case CEE_MONO_LDPTR_INT_REQ_FLAG:
12317 ins = emit_runtime_constant (cfg, MONO_PATCH_INFO_INTERRUPTION_REQUEST_FLAG, NULL);
12323 inline_costs += 10 * num_calls++;
12326 case CEE_MONO_LDPTR: {
12329 CHECK_STACK_OVF (1);
12331 token = read32 (ip + 2);
12333 ptr = mono_method_get_wrapper_data (method, token);
12334 EMIT_NEW_PCONST (cfg, ins, ptr);
12337 inline_costs += 10 * num_calls++;
12338 /* Can't embed random pointers into AOT code */
12342 case CEE_MONO_JIT_ICALL_ADDR: {
12343 MonoJitICallInfo *callinfo;
12346 CHECK_STACK_OVF (1);
12348 token = read32 (ip + 2);
12350 ptr = mono_method_get_wrapper_data (method, token);
12351 callinfo = mono_find_jit_icall_by_addr (ptr);
12352 g_assert (callinfo);
12353 EMIT_NEW_JIT_ICALL_ADDRCONST (cfg, ins, (char*)callinfo->name);
12356 inline_costs += 10 * num_calls++;
12359 case CEE_MONO_ICALL_ADDR: {
12360 MonoMethod *cmethod;
12363 CHECK_STACK_OVF (1);
12365 token = read32 (ip + 2);
12367 cmethod = (MonoMethod *)mono_method_get_wrapper_data (method, token);
12369 if (cfg->compile_aot) {
12370 EMIT_NEW_AOTCONST (cfg, ins, MONO_PATCH_INFO_ICALL_ADDR, cmethod);
12372 ptr = mono_lookup_internal_call (cmethod);
12374 EMIT_NEW_PCONST (cfg, ins, ptr);
12380 case CEE_MONO_VTADDR: {
12381 MonoInst *src_var, *src;
12387 src_var = get_vreg_to_inst (cfg, sp [0]->dreg);
12388 EMIT_NEW_VARLOADA ((cfg), (src), src_var, src_var->inst_vtype);
12393 case CEE_MONO_NEWOBJ: {
12394 MonoInst *iargs [2];
12396 CHECK_STACK_OVF (1);
12398 token = read32 (ip + 2);
12399 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
12400 mono_class_init (klass);
12401 NEW_DOMAINCONST (cfg, iargs [0]);
12402 MONO_ADD_INS (cfg->cbb, iargs [0]);
12403 NEW_CLASSCONST (cfg, iargs [1], klass);
12404 MONO_ADD_INS (cfg->cbb, iargs [1]);
12405 *sp++ = mono_emit_jit_icall (cfg, mono_object_new, iargs);
12407 inline_costs += 10 * num_calls++;
12410 case CEE_MONO_OBJADDR:
12413 MONO_INST_NEW (cfg, ins, OP_MOVE);
12414 ins->dreg = alloc_ireg_mp (cfg);
12415 ins->sreg1 = sp [0]->dreg;
12416 ins->type = STACK_MP;
12417 MONO_ADD_INS (cfg->cbb, ins);
12421 case CEE_MONO_LDNATIVEOBJ:
12423 * Similar to LDOBJ, but instead load the unmanaged
12424 * representation of the vtype to the stack.
12429 token = read32 (ip + 2);
12430 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
12431 g_assert (klass->valuetype);
12432 mono_class_init (klass);
12435 MonoInst *src, *dest, *temp;
12438 temp = mono_compile_create_var (cfg, &klass->byval_arg, OP_LOCAL);
12439 temp->backend.is_pinvoke = 1;
12440 EMIT_NEW_TEMPLOADA (cfg, dest, temp->inst_c0);
12441 mini_emit_stobj (cfg, dest, src, klass, TRUE);
12443 EMIT_NEW_TEMPLOAD (cfg, dest, temp->inst_c0);
12444 dest->type = STACK_VTYPE;
12445 dest->klass = klass;
12451 case CEE_MONO_RETOBJ: {
12453 * Same as RET, but return the native representation of a vtype
12456 g_assert (cfg->ret);
12457 g_assert (mono_method_signature (method)->pinvoke);
12462 token = read32 (ip + 2);
12463 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
12465 if (!cfg->vret_addr) {
12466 g_assert (cfg->ret_var_is_local);
12468 EMIT_NEW_VARLOADA (cfg, ins, cfg->ret, cfg->ret->inst_vtype);
12470 EMIT_NEW_RETLOADA (cfg, ins);
12472 mini_emit_stobj (cfg, ins, sp [0], klass, TRUE);
12474 if (sp != stack_start)
12477 MONO_INST_NEW (cfg, ins, OP_BR);
12478 ins->inst_target_bb = end_bblock;
12479 MONO_ADD_INS (cfg->cbb, ins);
12480 link_bblock (cfg, cfg->cbb, end_bblock);
12481 start_new_bblock = 1;
12485 case CEE_MONO_CISINST:
12486 case CEE_MONO_CCASTCLASS: {
12491 token = read32 (ip + 2);
12492 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
12493 if (ip [1] == CEE_MONO_CISINST)
12494 ins = handle_cisinst (cfg, klass, sp [0]);
12496 ins = handle_ccastclass (cfg, klass, sp [0]);
12501 case CEE_MONO_SAVE_LMF:
12502 case CEE_MONO_RESTORE_LMF:
12505 case CEE_MONO_CLASSCONST:
12506 CHECK_STACK_OVF (1);
12508 token = read32 (ip + 2);
12509 EMIT_NEW_CLASSCONST (cfg, ins, mono_method_get_wrapper_data (method, token));
12512 inline_costs += 10 * num_calls++;
12514 case CEE_MONO_NOT_TAKEN:
12515 cfg->cbb->out_of_line = TRUE;
12518 case CEE_MONO_TLS: {
12521 CHECK_STACK_OVF (1);
12523 key = (MonoTlsKey)read32 (ip + 2);
12524 g_assert (key < TLS_KEY_NUM);
12526 ins = mono_create_tls_get (cfg, key);
12528 if (cfg->compile_aot) {
12530 MONO_INST_NEW (cfg, ins, OP_TLS_GET);
12531 ins->dreg = alloc_preg (cfg);
12532 ins->type = STACK_PTR;
12534 g_assert_not_reached ();
12537 ins->type = STACK_PTR;
12538 MONO_ADD_INS (cfg->cbb, ins);
12543 case CEE_MONO_DYN_CALL: {
12544 MonoCallInst *call;
12546 /* It would be easier to call a trampoline, but that would put an
12547 * extra frame on the stack, confusing exception handling. So
12548 * implement it inline using an opcode for now.
12551 if (!cfg->dyn_call_var) {
12552 cfg->dyn_call_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
12553 /* prevent it from being register allocated */
12554 cfg->dyn_call_var->flags |= MONO_INST_VOLATILE;
12557 /* Has to use a call inst since it local regalloc expects it */
12558 MONO_INST_NEW_CALL (cfg, call, OP_DYN_CALL);
12559 ins = (MonoInst*)call;
12561 ins->sreg1 = sp [0]->dreg;
12562 ins->sreg2 = sp [1]->dreg;
12563 MONO_ADD_INS (cfg->cbb, ins);
12565 cfg->param_area = MAX (cfg->param_area, cfg->backend->dyn_call_param_area);
12568 inline_costs += 10 * num_calls++;
12572 case CEE_MONO_MEMORY_BARRIER: {
12574 emit_memory_barrier (cfg, (int)read32 (ip + 2));
12578 case CEE_MONO_JIT_ATTACH: {
12579 MonoInst *args [16], *domain_ins;
12580 MonoInst *ad_ins, *jit_tls_ins;
12581 MonoBasicBlock *next_bb = NULL, *call_bb = NULL;
12583 cfg->orig_domain_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
12585 EMIT_NEW_PCONST (cfg, ins, NULL);
12586 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, cfg->orig_domain_var->dreg, ins->dreg);
12588 ad_ins = mono_get_domain_intrinsic (cfg);
12589 jit_tls_ins = mono_get_jit_tls_intrinsic (cfg);
12591 if (cfg->backend->have_tls_get && ad_ins && jit_tls_ins) {
12592 NEW_BBLOCK (cfg, next_bb);
12593 NEW_BBLOCK (cfg, call_bb);
12595 if (cfg->compile_aot) {
12596 /* AOT code is only used in the root domain */
12597 EMIT_NEW_PCONST (cfg, domain_ins, NULL);
12599 EMIT_NEW_PCONST (cfg, domain_ins, cfg->domain);
12601 MONO_ADD_INS (cfg->cbb, ad_ins);
12602 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, ad_ins->dreg, domain_ins->dreg);
12603 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, call_bb);
12605 MONO_ADD_INS (cfg->cbb, jit_tls_ins);
12606 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, jit_tls_ins->dreg, 0);
12607 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, call_bb);
12609 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, next_bb);
12610 MONO_START_BB (cfg, call_bb);
12613 if (cfg->compile_aot) {
12614 /* AOT code is only used in the root domain */
12615 EMIT_NEW_PCONST (cfg, args [0], NULL);
12617 EMIT_NEW_PCONST (cfg, args [0], cfg->domain);
12619 ins = mono_emit_jit_icall (cfg, mono_jit_thread_attach, args);
12620 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, cfg->orig_domain_var->dreg, ins->dreg);
12623 MONO_START_BB (cfg, next_bb);
12627 case CEE_MONO_JIT_DETACH: {
12628 MonoInst *args [16];
12630 /* Restore the original domain */
12631 dreg = alloc_ireg (cfg);
12632 EMIT_NEW_UNALU (cfg, args [0], OP_MOVE, dreg, cfg->orig_domain_var->dreg);
12633 mono_emit_jit_icall (cfg, mono_jit_set_domain, args);
12638 g_error ("opcode 0x%02x 0x%02x not handled", MONO_CUSTOM_PREFIX, ip [1]);
12644 case CEE_PREFIX1: {
12647 case CEE_ARGLIST: {
12648 /* somewhat similar to LDTOKEN */
12649 MonoInst *addr, *vtvar;
12650 CHECK_STACK_OVF (1);
12651 vtvar = mono_compile_create_var (cfg, &mono_defaults.argumenthandle_class->byval_arg, OP_LOCAL);
12653 EMIT_NEW_TEMPLOADA (cfg, addr, vtvar->inst_c0);
12654 EMIT_NEW_UNALU (cfg, ins, OP_ARGLIST, -1, addr->dreg);
12656 EMIT_NEW_TEMPLOAD (cfg, ins, vtvar->inst_c0);
12657 ins->type = STACK_VTYPE;
12658 ins->klass = mono_defaults.argumenthandle_class;
12668 MonoInst *cmp, *arg1, *arg2;
12676 * The following transforms:
12677 * CEE_CEQ into OP_CEQ
12678 * CEE_CGT into OP_CGT
12679 * CEE_CGT_UN into OP_CGT_UN
12680 * CEE_CLT into OP_CLT
12681 * CEE_CLT_UN into OP_CLT_UN
12683 MONO_INST_NEW (cfg, cmp, (OP_CEQ - CEE_CEQ) + ip [1]);
12685 MONO_INST_NEW (cfg, ins, cmp->opcode);
12686 cmp->sreg1 = arg1->dreg;
12687 cmp->sreg2 = arg2->dreg;
12688 type_from_op (cfg, cmp, arg1, arg2);
12690 add_widen_op (cfg, cmp, &arg1, &arg2);
12691 if ((arg1->type == STACK_I8) || ((SIZEOF_VOID_P == 8) && ((arg1->type == STACK_PTR) || (arg1->type == STACK_OBJ) || (arg1->type == STACK_MP))))
12692 cmp->opcode = OP_LCOMPARE;
12693 else if (arg1->type == STACK_R4)
12694 cmp->opcode = OP_RCOMPARE;
12695 else if (arg1->type == STACK_R8)
12696 cmp->opcode = OP_FCOMPARE;
12698 cmp->opcode = OP_ICOMPARE;
12699 MONO_ADD_INS (cfg->cbb, cmp);
12700 ins->type = STACK_I4;
12701 ins->dreg = alloc_dreg (cfg, (MonoStackType)ins->type);
12702 type_from_op (cfg, ins, arg1, arg2);
12704 if (cmp->opcode == OP_FCOMPARE || cmp->opcode == OP_RCOMPARE) {
12706 * The backends expect the fceq opcodes to do the
12709 ins->sreg1 = cmp->sreg1;
12710 ins->sreg2 = cmp->sreg2;
12713 MONO_ADD_INS (cfg->cbb, ins);
12719 MonoInst *argconst;
12720 MonoMethod *cil_method;
12722 CHECK_STACK_OVF (1);
12724 n = read32 (ip + 2);
12725 cmethod = mini_get_method (cfg, method, n, NULL, generic_context);
12726 if (!cmethod || mono_loader_get_last_error ())
12728 mono_class_init (cmethod->klass);
12730 mono_save_token_info (cfg, image, n, cmethod);
12732 context_used = mini_method_check_context_used (cfg, cmethod);
12734 cil_method = cmethod;
12735 if (!dont_verify && !cfg->skip_visibility && !mono_method_can_access_method (method, cmethod))
12736 METHOD_ACCESS_FAILURE (method, cil_method);
12738 if (mono_security_core_clr_enabled ())
12739 ensure_method_is_allowed_to_call_method (cfg, method, cmethod);
12742 * Optimize the common case of ldftn+delegate creation
12744 if ((sp > stack_start) && (ip + 6 + 5 < end) && ip_in_bb (cfg, cfg->cbb, ip + 6) && (ip [6] == CEE_NEWOBJ)) {
12745 MonoMethod *ctor_method = mini_get_method (cfg, method, read32 (ip + 7), NULL, generic_context);
12746 if (ctor_method && (ctor_method->klass->parent == mono_defaults.multicastdelegate_class)) {
12747 MonoInst *target_ins, *handle_ins;
12748 MonoMethod *invoke;
12749 int invoke_context_used;
12751 invoke = mono_get_delegate_invoke (ctor_method->klass);
12752 if (!invoke || !mono_method_signature (invoke))
12755 invoke_context_used = mini_method_check_context_used (cfg, invoke);
12757 target_ins = sp [-1];
12759 if (mono_security_core_clr_enabled ())
12760 ensure_method_is_allowed_to_call_method (cfg, method, ctor_method);
12762 if (!(cmethod->flags & METHOD_ATTRIBUTE_STATIC)) {
12763 /*LAME IMPL: We must not add a null check for virtual invoke delegates.*/
12764 if (mono_method_signature (invoke)->param_count == mono_method_signature (cmethod)->param_count) {
12765 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, target_ins->dreg, 0);
12766 MONO_EMIT_NEW_COND_EXC (cfg, EQ, "ArgumentException");
12770 /* FIXME: SGEN support */
12771 if (invoke_context_used == 0 || cfg->llvm_only) {
12773 if (cfg->verbose_level > 3)
12774 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));
12775 if ((handle_ins = handle_delegate_ctor (cfg, ctor_method->klass, target_ins, cmethod, context_used, FALSE))) {
12778 CHECK_CFG_EXCEPTION;
12788 argconst = emit_get_rgctx_method (cfg, context_used, cmethod, MONO_RGCTX_INFO_METHOD);
12789 ins = mono_emit_jit_icall (cfg, mono_ldftn, &argconst);
12793 inline_costs += 10 * num_calls++;
12796 case CEE_LDVIRTFTN: {
12797 MonoInst *args [2];
12801 n = read32 (ip + 2);
12802 cmethod = mini_get_method (cfg, method, n, NULL, generic_context);
12803 if (!cmethod || mono_loader_get_last_error ())
12805 mono_class_init (cmethod->klass);
12807 context_used = mini_method_check_context_used (cfg, cmethod);
12809 if (mono_security_core_clr_enabled ())
12810 ensure_method_is_allowed_to_call_method (cfg, method, cmethod);
12813 * Optimize the common case of ldvirtftn+delegate creation
12815 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)) {
12816 MonoMethod *ctor_method = mini_get_method (cfg, method, read32 (ip + 7), NULL, generic_context);
12817 if (ctor_method && (ctor_method->klass->parent == mono_defaults.multicastdelegate_class)) {
12818 MonoInst *target_ins, *handle_ins;
12819 MonoMethod *invoke;
12820 int invoke_context_used;
12821 gboolean is_virtual = cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL;
12823 invoke = mono_get_delegate_invoke (ctor_method->klass);
12824 if (!invoke || !mono_method_signature (invoke))
12827 invoke_context_used = mini_method_check_context_used (cfg, invoke);
12829 target_ins = sp [-1];
12831 if (mono_security_core_clr_enabled ())
12832 ensure_method_is_allowed_to_call_method (cfg, method, ctor_method);
12834 /* FIXME: SGEN support */
12835 if (invoke_context_used == 0 || cfg->llvm_only) {
12837 if (cfg->verbose_level > 3)
12838 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));
12839 if ((handle_ins = handle_delegate_ctor (cfg, ctor_method->klass, target_ins, cmethod, context_used, is_virtual))) {
12842 CHECK_CFG_EXCEPTION;
12855 args [1] = emit_get_rgctx_method (cfg, context_used,
12856 cmethod, MONO_RGCTX_INFO_METHOD);
12859 *sp++ = mono_emit_jit_icall (cfg, mono_ldvirtfn_gshared, args);
12861 *sp++ = mono_emit_jit_icall (cfg, mono_ldvirtfn, args);
12864 inline_costs += 10 * num_calls++;
12868 CHECK_STACK_OVF (1);
12870 n = read16 (ip + 2);
12872 EMIT_NEW_ARGLOAD (cfg, ins, n);
12877 CHECK_STACK_OVF (1);
12879 n = read16 (ip + 2);
12881 NEW_ARGLOADA (cfg, ins, n);
12882 MONO_ADD_INS (cfg->cbb, ins);
12890 n = read16 (ip + 2);
12892 if (!dont_verify_stloc && target_type_is_incompatible (cfg, param_types [n], *sp))
12894 EMIT_NEW_ARGSTORE (cfg, ins, n, *sp);
12898 CHECK_STACK_OVF (1);
12900 n = read16 (ip + 2);
12902 EMIT_NEW_LOCLOAD (cfg, ins, n);
12907 unsigned char *tmp_ip;
12908 CHECK_STACK_OVF (1);
12910 n = read16 (ip + 2);
12913 if ((tmp_ip = emit_optimized_ldloca_ir (cfg, ip, end, 2))) {
12919 EMIT_NEW_LOCLOADA (cfg, ins, n);
12928 n = read16 (ip + 2);
12930 if (!dont_verify_stloc && target_type_is_incompatible (cfg, header->locals [n], *sp))
12932 emit_stloc_ir (cfg, sp, header, n);
12939 if (sp != stack_start)
12941 if (cfg->method != method)
12943 * Inlining this into a loop in a parent could lead to
12944 * stack overflows which is different behavior than the
12945 * non-inlined case, thus disable inlining in this case.
12947 INLINE_FAILURE("localloc");
12949 MONO_INST_NEW (cfg, ins, OP_LOCALLOC);
12950 ins->dreg = alloc_preg (cfg);
12951 ins->sreg1 = sp [0]->dreg;
12952 ins->type = STACK_PTR;
12953 MONO_ADD_INS (cfg->cbb, ins);
12955 cfg->flags |= MONO_CFG_HAS_ALLOCA;
12957 ins->flags |= MONO_INST_INIT;
12962 case CEE_ENDFILTER: {
12963 MonoExceptionClause *clause, *nearest;
12968 if ((sp != stack_start) || (sp [0]->type != STACK_I4))
12970 MONO_INST_NEW (cfg, ins, OP_ENDFILTER);
12971 ins->sreg1 = (*sp)->dreg;
12972 MONO_ADD_INS (cfg->cbb, ins);
12973 start_new_bblock = 1;
12977 for (cc = 0; cc < header->num_clauses; ++cc) {
12978 clause = &header->clauses [cc];
12979 if ((clause->flags & MONO_EXCEPTION_CLAUSE_FILTER) &&
12980 ((ip - header->code) > clause->data.filter_offset && (ip - header->code) <= clause->handler_offset) &&
12981 (!nearest || (clause->data.filter_offset < nearest->data.filter_offset)))
12984 g_assert (nearest);
12985 if ((ip - header->code) != nearest->handler_offset)
12990 case CEE_UNALIGNED_:
12991 ins_flag |= MONO_INST_UNALIGNED;
12992 /* FIXME: record alignment? we can assume 1 for now */
12996 case CEE_VOLATILE_:
12997 ins_flag |= MONO_INST_VOLATILE;
13001 ins_flag |= MONO_INST_TAILCALL;
13002 cfg->flags |= MONO_CFG_HAS_TAIL;
13003 /* Can't inline tail calls at this time */
13004 inline_costs += 100000;
13011 token = read32 (ip + 2);
13012 klass = mini_get_class (method, token, generic_context);
13013 CHECK_TYPELOAD (klass);
13014 if (generic_class_is_reference_type (cfg, klass))
13015 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STORE_MEMBASE_IMM, sp [0]->dreg, 0, 0);
13017 mini_emit_initobj (cfg, *sp, NULL, klass);
13021 case CEE_CONSTRAINED_:
13023 token = read32 (ip + 2);
13024 constrained_class = mini_get_class (method, token, generic_context);
13025 CHECK_TYPELOAD (constrained_class);
13029 case CEE_INITBLK: {
13030 MonoInst *iargs [3];
13034 /* Skip optimized paths for volatile operations. */
13035 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)) {
13036 mini_emit_memcpy (cfg, sp [0]->dreg, 0, sp [1]->dreg, 0, sp [2]->inst_c0, 0);
13037 } 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)) {
13038 /* emit_memset only works when val == 0 */
13039 mini_emit_memset (cfg, sp [0]->dreg, 0, sp [2]->inst_c0, sp [1]->inst_c0, 0);
13042 iargs [0] = sp [0];
13043 iargs [1] = sp [1];
13044 iargs [2] = sp [2];
13045 if (ip [1] == CEE_CPBLK) {
13047 * FIXME: It's unclear whether we should be emitting both the acquire
13048 * and release barriers for cpblk. It is technically both a load and
13049 * store operation, so it seems like that's the sensible thing to do.
13051 * FIXME: We emit full barriers on both sides of the operation for
13052 * simplicity. We should have a separate atomic memcpy method instead.
13054 MonoMethod *memcpy_method = get_memcpy_method ();
13056 if (ins_flag & MONO_INST_VOLATILE)
13057 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_SEQ);
13059 call = mono_emit_method_call (cfg, memcpy_method, iargs, NULL);
13060 call->flags |= ins_flag;
13062 if (ins_flag & MONO_INST_VOLATILE)
13063 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_SEQ);
13065 MonoMethod *memset_method = get_memset_method ();
13066 if (ins_flag & MONO_INST_VOLATILE) {
13067 /* Volatile stores have release semantics, see 12.6.7 in Ecma 335 */
13068 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_REL);
13070 call = mono_emit_method_call (cfg, memset_method, iargs, NULL);
13071 call->flags |= ins_flag;
13082 ins_flag |= MONO_INST_NOTYPECHECK;
13084 ins_flag |= MONO_INST_NORANGECHECK;
13085 /* we ignore the no-nullcheck for now since we
13086 * really do it explicitly only when doing callvirt->call
13090 case CEE_RETHROW: {
13092 int handler_offset = -1;
13094 for (i = 0; i < header->num_clauses; ++i) {
13095 MonoExceptionClause *clause = &header->clauses [i];
13096 if (MONO_OFFSET_IN_HANDLER (clause, ip - header->code) && !(clause->flags & MONO_EXCEPTION_CLAUSE_FINALLY)) {
13097 handler_offset = clause->handler_offset;
13102 cfg->cbb->flags |= BB_EXCEPTION_UNSAFE;
13104 if (handler_offset == -1)
13107 EMIT_NEW_TEMPLOAD (cfg, load, mono_find_exvar_for_offset (cfg, handler_offset)->inst_c0);
13108 MONO_INST_NEW (cfg, ins, OP_RETHROW);
13109 ins->sreg1 = load->dreg;
13110 MONO_ADD_INS (cfg->cbb, ins);
13112 MONO_INST_NEW (cfg, ins, OP_NOT_REACHED);
13113 MONO_ADD_INS (cfg->cbb, ins);
13116 link_bblock (cfg, cfg->cbb, end_bblock);
13117 start_new_bblock = 1;
13125 CHECK_STACK_OVF (1);
13127 token = read32 (ip + 2);
13128 if (mono_metadata_token_table (token) == MONO_TABLE_TYPESPEC && !image_is_dynamic (method->klass->image) && !generic_context) {
13129 MonoType *type = mono_type_create_from_typespec_checked (image, token, &cfg->error);
13132 val = mono_type_size (type, &ialign);
13134 MonoClass *klass = mini_get_class (method, token, generic_context);
13135 CHECK_TYPELOAD (klass);
13137 val = mono_type_size (&klass->byval_arg, &ialign);
13139 if (mini_is_gsharedvt_klass (klass))
13140 GSHAREDVT_FAILURE (*ip);
13142 EMIT_NEW_ICONST (cfg, ins, val);
13147 case CEE_REFANYTYPE: {
13148 MonoInst *src_var, *src;
13150 GSHAREDVT_FAILURE (*ip);
13156 src_var = get_vreg_to_inst (cfg, sp [0]->dreg);
13158 src_var = mono_compile_create_var_for_vreg (cfg, &mono_defaults.typed_reference_class->byval_arg, OP_LOCAL, sp [0]->dreg);
13159 EMIT_NEW_VARLOADA (cfg, src, src_var, src_var->inst_vtype);
13160 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &mono_defaults.typehandle_class->byval_arg, src->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, type));
13165 case CEE_READONLY_:
13178 g_warning ("opcode 0xfe 0x%02x not handled", ip [1]);
13188 g_warning ("opcode 0x%02x not handled", *ip);
13192 if (start_new_bblock != 1)
13195 cfg->cbb->cil_length = ip - cfg->cbb->cil_code;
13196 if (cfg->cbb->next_bb) {
13197 /* This could already be set because of inlining, #693905 */
13198 MonoBasicBlock *bb = cfg->cbb;
13200 while (bb->next_bb)
13202 bb->next_bb = end_bblock;
13204 cfg->cbb->next_bb = end_bblock;
13207 if (cfg->method == method && cfg->domainvar) {
13209 MonoInst *get_domain;
13211 cfg->cbb = init_localsbb;
13213 if ((get_domain = mono_get_domain_intrinsic (cfg))) {
13214 MONO_ADD_INS (cfg->cbb, get_domain);
13216 get_domain = mono_emit_jit_icall (cfg, mono_domain_get, NULL);
13218 NEW_TEMPSTORE (cfg, store, cfg->domainvar->inst_c0, get_domain);
13219 MONO_ADD_INS (cfg->cbb, store);
13222 #if defined(TARGET_POWERPC) || defined(TARGET_X86)
13223 if (cfg->compile_aot)
13224 /* FIXME: The plt slots require a GOT var even if the method doesn't use it */
13225 mono_get_got_var (cfg);
13228 if (cfg->method == method && cfg->got_var)
13229 mono_emit_load_got_addr (cfg);
13231 if (init_localsbb) {
13232 cfg->cbb = init_localsbb;
13234 for (i = 0; i < header->num_locals; ++i) {
13235 emit_init_local (cfg, i, header->locals [i], init_locals);
13239 if (cfg->init_ref_vars && cfg->method == method) {
13240 /* Emit initialization for ref vars */
13241 // FIXME: Avoid duplication initialization for IL locals.
13242 for (i = 0; i < cfg->num_varinfo; ++i) {
13243 MonoInst *ins = cfg->varinfo [i];
13245 if (ins->opcode == OP_LOCAL && ins->type == STACK_OBJ)
13246 MONO_EMIT_NEW_PCONST (cfg, ins->dreg, NULL);
13250 if (cfg->lmf_var && cfg->method == method && !cfg->llvm_only) {
13251 cfg->cbb = init_localsbb;
13252 emit_push_lmf (cfg);
13255 cfg->cbb = init_localsbb;
13256 emit_instrumentation_call (cfg, mono_profiler_method_enter);
13259 MonoBasicBlock *bb;
13262 * Make seq points at backward branch targets interruptable.
13264 for (bb = cfg->bb_entry; bb; bb = bb->next_bb)
13265 if (bb->code && bb->in_count > 1 && bb->code->opcode == OP_SEQ_POINT)
13266 bb->code->flags |= MONO_INST_SINGLE_STEP_LOC;
13269 /* Add a sequence point for method entry/exit events */
13270 if (seq_points && cfg->gen_sdb_seq_points) {
13271 NEW_SEQ_POINT (cfg, ins, METHOD_ENTRY_IL_OFFSET, FALSE);
13272 MONO_ADD_INS (init_localsbb, ins);
13273 NEW_SEQ_POINT (cfg, ins, METHOD_EXIT_IL_OFFSET, FALSE);
13274 MONO_ADD_INS (cfg->bb_exit, ins);
13278 * Add seq points for IL offsets which have line number info, but wasn't generated a seq point during JITting because
13279 * the code they refer to was dead (#11880).
13281 if (sym_seq_points) {
13282 for (i = 0; i < header->code_size; ++i) {
13283 if (mono_bitset_test_fast (seq_point_locs, i) && !mono_bitset_test_fast (seq_point_set_locs, i)) {
13286 NEW_SEQ_POINT (cfg, ins, i, FALSE);
13287 mono_add_seq_point (cfg, NULL, ins, SEQ_POINT_NATIVE_OFFSET_DEAD_CODE);
13294 if (cfg->method == method) {
13295 MonoBasicBlock *bb;
13296 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
13297 bb->region = mono_find_block_region (cfg, bb->real_offset);
13299 mono_create_spvar_for_region (cfg, bb->region);
13300 if (cfg->verbose_level > 2)
13301 printf ("REGION BB%d IL_%04x ID_%08X\n", bb->block_num, bb->real_offset, bb->region);
13305 if (inline_costs < 0) {
13308 /* Method is too large */
13309 mname = mono_method_full_name (method, TRUE);
13310 mono_cfg_set_exception (cfg, MONO_EXCEPTION_INVALID_PROGRAM);
13311 cfg->exception_message = g_strdup_printf ("Method %s is too complex.", mname);
13315 if ((cfg->verbose_level > 2) && (cfg->method == method))
13316 mono_print_code (cfg, "AFTER METHOD-TO-IR");
13321 g_assert (!mono_error_ok (&cfg->error));
13325 g_assert (cfg->exception_type != MONO_EXCEPTION_NONE);
13329 set_exception_type_from_invalid_il (cfg, method, ip);
13333 g_slist_free (class_inits);
13334 mono_basic_block_free (original_bb);
13335 cfg->dont_inline = g_list_remove (cfg->dont_inline, method);
13336 cfg->headers_to_free = g_slist_prepend_mempool (cfg->mempool, cfg->headers_to_free, header);
13337 if (cfg->exception_type)
13340 return inline_costs;
13344 store_membase_reg_to_store_membase_imm (int opcode)
13347 case OP_STORE_MEMBASE_REG:
13348 return OP_STORE_MEMBASE_IMM;
13349 case OP_STOREI1_MEMBASE_REG:
13350 return OP_STOREI1_MEMBASE_IMM;
13351 case OP_STOREI2_MEMBASE_REG:
13352 return OP_STOREI2_MEMBASE_IMM;
13353 case OP_STOREI4_MEMBASE_REG:
13354 return OP_STOREI4_MEMBASE_IMM;
13355 case OP_STOREI8_MEMBASE_REG:
13356 return OP_STOREI8_MEMBASE_IMM;
13358 g_assert_not_reached ();
13365 mono_op_to_op_imm (int opcode)
13369 return OP_IADD_IMM;
13371 return OP_ISUB_IMM;
13373 return OP_IDIV_IMM;
13375 return OP_IDIV_UN_IMM;
13377 return OP_IREM_IMM;
13379 return OP_IREM_UN_IMM;
13381 return OP_IMUL_IMM;
13383 return OP_IAND_IMM;
13387 return OP_IXOR_IMM;
13389 return OP_ISHL_IMM;
13391 return OP_ISHR_IMM;
13393 return OP_ISHR_UN_IMM;
13396 return OP_LADD_IMM;
13398 return OP_LSUB_IMM;
13400 return OP_LAND_IMM;
13404 return OP_LXOR_IMM;
13406 return OP_LSHL_IMM;
13408 return OP_LSHR_IMM;
13410 return OP_LSHR_UN_IMM;
13411 #if SIZEOF_REGISTER == 8
13413 return OP_LREM_IMM;
13417 return OP_COMPARE_IMM;
13419 return OP_ICOMPARE_IMM;
13421 return OP_LCOMPARE_IMM;
13423 case OP_STORE_MEMBASE_REG:
13424 return OP_STORE_MEMBASE_IMM;
13425 case OP_STOREI1_MEMBASE_REG:
13426 return OP_STOREI1_MEMBASE_IMM;
13427 case OP_STOREI2_MEMBASE_REG:
13428 return OP_STOREI2_MEMBASE_IMM;
13429 case OP_STOREI4_MEMBASE_REG:
13430 return OP_STOREI4_MEMBASE_IMM;
13432 #if defined(TARGET_X86) || defined (TARGET_AMD64)
13434 return OP_X86_PUSH_IMM;
13435 case OP_X86_COMPARE_MEMBASE_REG:
13436 return OP_X86_COMPARE_MEMBASE_IMM;
13438 #if defined(TARGET_AMD64)
13439 case OP_AMD64_ICOMPARE_MEMBASE_REG:
13440 return OP_AMD64_ICOMPARE_MEMBASE_IMM;
13442 case OP_VOIDCALL_REG:
13443 return OP_VOIDCALL;
13451 return OP_LOCALLOC_IMM;
13458 ldind_to_load_membase (int opcode)
13462 return OP_LOADI1_MEMBASE;
13464 return OP_LOADU1_MEMBASE;
13466 return OP_LOADI2_MEMBASE;
13468 return OP_LOADU2_MEMBASE;
13470 return OP_LOADI4_MEMBASE;
13472 return OP_LOADU4_MEMBASE;
13474 return OP_LOAD_MEMBASE;
13475 case CEE_LDIND_REF:
13476 return OP_LOAD_MEMBASE;
13478 return OP_LOADI8_MEMBASE;
13480 return OP_LOADR4_MEMBASE;
13482 return OP_LOADR8_MEMBASE;
13484 g_assert_not_reached ();
13491 stind_to_store_membase (int opcode)
13495 return OP_STOREI1_MEMBASE_REG;
13497 return OP_STOREI2_MEMBASE_REG;
13499 return OP_STOREI4_MEMBASE_REG;
13501 case CEE_STIND_REF:
13502 return OP_STORE_MEMBASE_REG;
13504 return OP_STOREI8_MEMBASE_REG;
13506 return OP_STORER4_MEMBASE_REG;
13508 return OP_STORER8_MEMBASE_REG;
13510 g_assert_not_reached ();
13517 mono_load_membase_to_load_mem (int opcode)
13519 // FIXME: Add a MONO_ARCH_HAVE_LOAD_MEM macro
13520 #if defined(TARGET_X86) || defined(TARGET_AMD64)
13522 case OP_LOAD_MEMBASE:
13523 return OP_LOAD_MEM;
13524 case OP_LOADU1_MEMBASE:
13525 return OP_LOADU1_MEM;
13526 case OP_LOADU2_MEMBASE:
13527 return OP_LOADU2_MEM;
13528 case OP_LOADI4_MEMBASE:
13529 return OP_LOADI4_MEM;
13530 case OP_LOADU4_MEMBASE:
13531 return OP_LOADU4_MEM;
13532 #if SIZEOF_REGISTER == 8
13533 case OP_LOADI8_MEMBASE:
13534 return OP_LOADI8_MEM;
13543 op_to_op_dest_membase (int store_opcode, int opcode)
13545 #if defined(TARGET_X86)
13546 if (!((store_opcode == OP_STORE_MEMBASE_REG) || (store_opcode == OP_STOREI4_MEMBASE_REG)))
13551 return OP_X86_ADD_MEMBASE_REG;
13553 return OP_X86_SUB_MEMBASE_REG;
13555 return OP_X86_AND_MEMBASE_REG;
13557 return OP_X86_OR_MEMBASE_REG;
13559 return OP_X86_XOR_MEMBASE_REG;
13562 return OP_X86_ADD_MEMBASE_IMM;
13565 return OP_X86_SUB_MEMBASE_IMM;
13568 return OP_X86_AND_MEMBASE_IMM;
13571 return OP_X86_OR_MEMBASE_IMM;
13574 return OP_X86_XOR_MEMBASE_IMM;
13580 #if defined(TARGET_AMD64)
13581 if (!((store_opcode == OP_STORE_MEMBASE_REG) || (store_opcode == OP_STOREI4_MEMBASE_REG) || (store_opcode == OP_STOREI8_MEMBASE_REG)))
13586 return OP_X86_ADD_MEMBASE_REG;
13588 return OP_X86_SUB_MEMBASE_REG;
13590 return OP_X86_AND_MEMBASE_REG;
13592 return OP_X86_OR_MEMBASE_REG;
13594 return OP_X86_XOR_MEMBASE_REG;
13596 return OP_X86_ADD_MEMBASE_IMM;
13598 return OP_X86_SUB_MEMBASE_IMM;
13600 return OP_X86_AND_MEMBASE_IMM;
13602 return OP_X86_OR_MEMBASE_IMM;
13604 return OP_X86_XOR_MEMBASE_IMM;
13606 return OP_AMD64_ADD_MEMBASE_REG;
13608 return OP_AMD64_SUB_MEMBASE_REG;
13610 return OP_AMD64_AND_MEMBASE_REG;
13612 return OP_AMD64_OR_MEMBASE_REG;
13614 return OP_AMD64_XOR_MEMBASE_REG;
13617 return OP_AMD64_ADD_MEMBASE_IMM;
13620 return OP_AMD64_SUB_MEMBASE_IMM;
13623 return OP_AMD64_AND_MEMBASE_IMM;
13626 return OP_AMD64_OR_MEMBASE_IMM;
13629 return OP_AMD64_XOR_MEMBASE_IMM;
13639 op_to_op_store_membase (int store_opcode, int opcode)
13641 #if defined(TARGET_X86) || defined(TARGET_AMD64)
13644 if (store_opcode == OP_STOREI1_MEMBASE_REG)
13645 return OP_X86_SETEQ_MEMBASE;
13647 if (store_opcode == OP_STOREI1_MEMBASE_REG)
13648 return OP_X86_SETNE_MEMBASE;
13656 op_to_op_src1_membase (MonoCompile *cfg, int load_opcode, int opcode)
13659 /* FIXME: This has sign extension issues */
13661 if ((opcode == OP_ICOMPARE_IMM) && (load_opcode == OP_LOADU1_MEMBASE))
13662 return OP_X86_COMPARE_MEMBASE8_IMM;
13665 if (!((load_opcode == OP_LOAD_MEMBASE) || (load_opcode == OP_LOADI4_MEMBASE) || (load_opcode == OP_LOADU4_MEMBASE)))
13670 return OP_X86_PUSH_MEMBASE;
13671 case OP_COMPARE_IMM:
13672 case OP_ICOMPARE_IMM:
13673 return OP_X86_COMPARE_MEMBASE_IMM;
13676 return OP_X86_COMPARE_MEMBASE_REG;
13680 #ifdef TARGET_AMD64
13681 /* FIXME: This has sign extension issues */
13683 if ((opcode == OP_ICOMPARE_IMM) && (load_opcode == OP_LOADU1_MEMBASE))
13684 return OP_X86_COMPARE_MEMBASE8_IMM;
13689 if ((load_opcode == OP_LOAD_MEMBASE && !cfg->backend->ilp32) || (load_opcode == OP_LOADI8_MEMBASE))
13690 return OP_X86_PUSH_MEMBASE;
13692 /* FIXME: This only works for 32 bit immediates
13693 case OP_COMPARE_IMM:
13694 case OP_LCOMPARE_IMM:
13695 if ((load_opcode == OP_LOAD_MEMBASE) || (load_opcode == OP_LOADI8_MEMBASE))
13696 return OP_AMD64_COMPARE_MEMBASE_IMM;
13698 case OP_ICOMPARE_IMM:
13699 if ((load_opcode == OP_LOADI4_MEMBASE) || (load_opcode == OP_LOADU4_MEMBASE))
13700 return OP_AMD64_ICOMPARE_MEMBASE_IMM;
13704 if (cfg->backend->ilp32 && load_opcode == OP_LOAD_MEMBASE)
13705 return OP_AMD64_ICOMPARE_MEMBASE_REG;
13706 if ((load_opcode == OP_LOAD_MEMBASE && !cfg->backend->ilp32) || (load_opcode == OP_LOADI8_MEMBASE))
13707 return OP_AMD64_COMPARE_MEMBASE_REG;
13710 if ((load_opcode == OP_LOADI4_MEMBASE) || (load_opcode == OP_LOADU4_MEMBASE))
13711 return OP_AMD64_ICOMPARE_MEMBASE_REG;
13720 op_to_op_src2_membase (MonoCompile *cfg, int load_opcode, int opcode)
13723 if (!((load_opcode == OP_LOAD_MEMBASE) || (load_opcode == OP_LOADI4_MEMBASE) || (load_opcode == OP_LOADU4_MEMBASE)))
13729 return OP_X86_COMPARE_REG_MEMBASE;
13731 return OP_X86_ADD_REG_MEMBASE;
13733 return OP_X86_SUB_REG_MEMBASE;
13735 return OP_X86_AND_REG_MEMBASE;
13737 return OP_X86_OR_REG_MEMBASE;
13739 return OP_X86_XOR_REG_MEMBASE;
13743 #ifdef TARGET_AMD64
13744 if ((load_opcode == OP_LOADI4_MEMBASE) || (load_opcode == OP_LOADU4_MEMBASE) || (load_opcode == OP_LOAD_MEMBASE && cfg->backend->ilp32)) {
13747 return OP_AMD64_ICOMPARE_REG_MEMBASE;
13749 return OP_X86_ADD_REG_MEMBASE;
13751 return OP_X86_SUB_REG_MEMBASE;
13753 return OP_X86_AND_REG_MEMBASE;
13755 return OP_X86_OR_REG_MEMBASE;
13757 return OP_X86_XOR_REG_MEMBASE;
13759 } else if ((load_opcode == OP_LOADI8_MEMBASE) || (load_opcode == OP_LOAD_MEMBASE && !cfg->backend->ilp32)) {
13763 return OP_AMD64_COMPARE_REG_MEMBASE;
13765 return OP_AMD64_ADD_REG_MEMBASE;
13767 return OP_AMD64_SUB_REG_MEMBASE;
13769 return OP_AMD64_AND_REG_MEMBASE;
13771 return OP_AMD64_OR_REG_MEMBASE;
13773 return OP_AMD64_XOR_REG_MEMBASE;
13782 mono_op_to_op_imm_noemul (int opcode)
13785 #if SIZEOF_REGISTER == 4 && !defined(MONO_ARCH_NO_EMULATE_LONG_SHIFT_OPS)
13791 #if defined(MONO_ARCH_EMULATE_MUL_DIV) || defined(MONO_ARCH_EMULATE_DIV)
13798 #if defined(MONO_ARCH_EMULATE_MUL_DIV)
13803 return mono_op_to_op_imm (opcode);
13808 * mono_handle_global_vregs:
13810 * Make vregs used in more than one bblock 'global', i.e. allocate a variable
13814 mono_handle_global_vregs (MonoCompile *cfg)
13816 gint32 *vreg_to_bb;
13817 MonoBasicBlock *bb;
13820 vreg_to_bb = (gint32 *)mono_mempool_alloc0 (cfg->mempool, sizeof (gint32*) * cfg->next_vreg + 1);
13822 #ifdef MONO_ARCH_SIMD_INTRINSICS
13823 if (cfg->uses_simd_intrinsics)
13824 mono_simd_simplify_indirection (cfg);
13827 /* Find local vregs used in more than one bb */
13828 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
13829 MonoInst *ins = bb->code;
13830 int block_num = bb->block_num;
13832 if (cfg->verbose_level > 2)
13833 printf ("\nHANDLE-GLOBAL-VREGS BLOCK %d:\n", bb->block_num);
13836 for (; ins; ins = ins->next) {
13837 const char *spec = INS_INFO (ins->opcode);
13838 int regtype = 0, regindex;
13841 if (G_UNLIKELY (cfg->verbose_level > 2))
13842 mono_print_ins (ins);
13844 g_assert (ins->opcode >= MONO_CEE_LAST);
13846 for (regindex = 0; regindex < 4; regindex ++) {
13849 if (regindex == 0) {
13850 regtype = spec [MONO_INST_DEST];
13851 if (regtype == ' ')
13854 } else if (regindex == 1) {
13855 regtype = spec [MONO_INST_SRC1];
13856 if (regtype == ' ')
13859 } else if (regindex == 2) {
13860 regtype = spec [MONO_INST_SRC2];
13861 if (regtype == ' ')
13864 } else if (regindex == 3) {
13865 regtype = spec [MONO_INST_SRC3];
13866 if (regtype == ' ')
13871 #if SIZEOF_REGISTER == 4
13872 /* In the LLVM case, the long opcodes are not decomposed */
13873 if (regtype == 'l' && !COMPILE_LLVM (cfg)) {
13875 * Since some instructions reference the original long vreg,
13876 * and some reference the two component vregs, it is quite hard
13877 * to determine when it needs to be global. So be conservative.
13879 if (!get_vreg_to_inst (cfg, vreg)) {
13880 mono_compile_create_var_for_vreg (cfg, &mono_defaults.int64_class->byval_arg, OP_LOCAL, vreg);
13882 if (cfg->verbose_level > 2)
13883 printf ("LONG VREG R%d made global.\n", vreg);
13887 * Make the component vregs volatile since the optimizations can
13888 * get confused otherwise.
13890 get_vreg_to_inst (cfg, vreg + 1)->flags |= MONO_INST_VOLATILE;
13891 get_vreg_to_inst (cfg, vreg + 2)->flags |= MONO_INST_VOLATILE;
13895 g_assert (vreg != -1);
13897 prev_bb = vreg_to_bb [vreg];
13898 if (prev_bb == 0) {
13899 /* 0 is a valid block num */
13900 vreg_to_bb [vreg] = block_num + 1;
13901 } else if ((prev_bb != block_num + 1) && (prev_bb != -1)) {
13902 if (((regtype == 'i' && (vreg < MONO_MAX_IREGS))) || (regtype == 'f' && (vreg < MONO_MAX_FREGS)))
13905 if (!get_vreg_to_inst (cfg, vreg)) {
13906 if (G_UNLIKELY (cfg->verbose_level > 2))
13907 printf ("VREG R%d used in BB%d and BB%d made global.\n", vreg, vreg_to_bb [vreg], block_num);
13911 if (vreg_is_ref (cfg, vreg))
13912 mono_compile_create_var_for_vreg (cfg, &mono_defaults.object_class->byval_arg, OP_LOCAL, vreg);
13914 mono_compile_create_var_for_vreg (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL, vreg);
13917 mono_compile_create_var_for_vreg (cfg, &mono_defaults.int64_class->byval_arg, OP_LOCAL, vreg);
13920 mono_compile_create_var_for_vreg (cfg, &mono_defaults.double_class->byval_arg, OP_LOCAL, vreg);
13923 mono_compile_create_var_for_vreg (cfg, &ins->klass->byval_arg, OP_LOCAL, vreg);
13926 g_assert_not_reached ();
13930 /* Flag as having been used in more than one bb */
13931 vreg_to_bb [vreg] = -1;
13937 /* If a variable is used in only one bblock, convert it into a local vreg */
13938 for (i = 0; i < cfg->num_varinfo; i++) {
13939 MonoInst *var = cfg->varinfo [i];
13940 MonoMethodVar *vmv = MONO_VARINFO (cfg, i);
13942 switch (var->type) {
13948 #if SIZEOF_REGISTER == 8
13951 #if !defined(TARGET_X86)
13952 /* Enabling this screws up the fp stack on x86 */
13955 if (mono_arch_is_soft_float ())
13959 if (var->type == STACK_VTYPE && cfg->gsharedvt && mini_is_gsharedvt_variable_type (var->inst_vtype))
13963 /* Arguments are implicitly global */
13964 /* Putting R4 vars into registers doesn't work currently */
13965 /* The gsharedvt vars are implicitly referenced by ldaddr opcodes, but those opcodes are only generated later */
13966 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) {
13968 * Make that the variable's liveness interval doesn't contain a call, since
13969 * that would cause the lvreg to be spilled, making the whole optimization
13972 /* This is too slow for JIT compilation */
13974 if (cfg->compile_aot && vreg_to_bb [var->dreg]) {
13976 int def_index, call_index, ins_index;
13977 gboolean spilled = FALSE;
13982 for (ins = vreg_to_bb [var->dreg]->code; ins; ins = ins->next) {
13983 const char *spec = INS_INFO (ins->opcode);
13985 if ((spec [MONO_INST_DEST] != ' ') && (ins->dreg == var->dreg))
13986 def_index = ins_index;
13988 if (((spec [MONO_INST_SRC1] != ' ') && (ins->sreg1 == var->dreg)) ||
13989 ((spec [MONO_INST_SRC1] != ' ') && (ins->sreg1 == var->dreg))) {
13990 if (call_index > def_index) {
13996 if (MONO_IS_CALL (ins))
13997 call_index = ins_index;
14007 if (G_UNLIKELY (cfg->verbose_level > 2))
14008 printf ("CONVERTED R%d(%d) TO VREG.\n", var->dreg, vmv->idx);
14009 var->flags |= MONO_INST_IS_DEAD;
14010 cfg->vreg_to_inst [var->dreg] = NULL;
14017 * Compress the varinfo and vars tables so the liveness computation is faster and
14018 * takes up less space.
14021 for (i = 0; i < cfg->num_varinfo; ++i) {
14022 MonoInst *var = cfg->varinfo [i];
14023 if (pos < i && cfg->locals_start == i)
14024 cfg->locals_start = pos;
14025 if (!(var->flags & MONO_INST_IS_DEAD)) {
14027 cfg->varinfo [pos] = cfg->varinfo [i];
14028 cfg->varinfo [pos]->inst_c0 = pos;
14029 memcpy (&cfg->vars [pos], &cfg->vars [i], sizeof (MonoMethodVar));
14030 cfg->vars [pos].idx = pos;
14031 #if SIZEOF_REGISTER == 4
14032 if (cfg->varinfo [pos]->type == STACK_I8) {
14033 /* Modify the two component vars too */
14036 var1 = get_vreg_to_inst (cfg, cfg->varinfo [pos]->dreg + 1);
14037 var1->inst_c0 = pos;
14038 var1 = get_vreg_to_inst (cfg, cfg->varinfo [pos]->dreg + 2);
14039 var1->inst_c0 = pos;
14046 cfg->num_varinfo = pos;
14047 if (cfg->locals_start > cfg->num_varinfo)
14048 cfg->locals_start = cfg->num_varinfo;
14052 * mono_allocate_gsharedvt_vars:
14054 * Allocate variables with gsharedvt types to entries in the MonoGSharedVtMethodRuntimeInfo.entries array.
14055 * Initialize cfg->gsharedvt_vreg_to_idx with the mapping between vregs and indexes.
14058 mono_allocate_gsharedvt_vars (MonoCompile *cfg)
14062 cfg->gsharedvt_vreg_to_idx = (int *)mono_mempool_alloc0 (cfg->mempool, sizeof (int) * cfg->next_vreg);
14064 for (i = 0; i < cfg->num_varinfo; ++i) {
14065 MonoInst *ins = cfg->varinfo [i];
14068 if (mini_is_gsharedvt_variable_type (ins->inst_vtype)) {
14069 if (i >= cfg->locals_start) {
14071 idx = get_gsharedvt_info_slot (cfg, ins->inst_vtype, MONO_RGCTX_INFO_LOCAL_OFFSET);
14072 cfg->gsharedvt_vreg_to_idx [ins->dreg] = idx + 1;
14073 ins->opcode = OP_GSHAREDVT_LOCAL;
14074 ins->inst_imm = idx;
14077 cfg->gsharedvt_vreg_to_idx [ins->dreg] = -1;
14078 ins->opcode = OP_GSHAREDVT_ARG_REGOFFSET;
14085 * mono_spill_global_vars:
14087 * Generate spill code for variables which are not allocated to registers,
14088 * and replace vregs with their allocated hregs. *need_local_opts is set to TRUE if
14089 * code is generated which could be optimized by the local optimization passes.
14092 mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local_opts)
14094 MonoBasicBlock *bb;
14096 int orig_next_vreg;
14097 guint32 *vreg_to_lvreg;
14099 guint32 i, lvregs_len;
14100 gboolean dest_has_lvreg = FALSE;
14101 MonoStackType stacktypes [128];
14102 MonoInst **live_range_start, **live_range_end;
14103 MonoBasicBlock **live_range_start_bb, **live_range_end_bb;
14105 *need_local_opts = FALSE;
14107 memset (spec2, 0, sizeof (spec2));
14109 /* FIXME: Move this function to mini.c */
14110 stacktypes ['i'] = STACK_PTR;
14111 stacktypes ['l'] = STACK_I8;
14112 stacktypes ['f'] = STACK_R8;
14113 #ifdef MONO_ARCH_SIMD_INTRINSICS
14114 stacktypes ['x'] = STACK_VTYPE;
14117 #if SIZEOF_REGISTER == 4
14118 /* Create MonoInsts for longs */
14119 for (i = 0; i < cfg->num_varinfo; i++) {
14120 MonoInst *ins = cfg->varinfo [i];
14122 if ((ins->opcode != OP_REGVAR) && !(ins->flags & MONO_INST_IS_DEAD)) {
14123 switch (ins->type) {
14128 if (ins->type == STACK_R8 && !COMPILE_SOFT_FLOAT (cfg))
14131 g_assert (ins->opcode == OP_REGOFFSET);
14133 tree = get_vreg_to_inst (cfg, ins->dreg + 1);
14135 tree->opcode = OP_REGOFFSET;
14136 tree->inst_basereg = ins->inst_basereg;
14137 tree->inst_offset = ins->inst_offset + MINI_LS_WORD_OFFSET;
14139 tree = get_vreg_to_inst (cfg, ins->dreg + 2);
14141 tree->opcode = OP_REGOFFSET;
14142 tree->inst_basereg = ins->inst_basereg;
14143 tree->inst_offset = ins->inst_offset + MINI_MS_WORD_OFFSET;
14153 if (cfg->compute_gc_maps) {
14154 /* registers need liveness info even for !non refs */
14155 for (i = 0; i < cfg->num_varinfo; i++) {
14156 MonoInst *ins = cfg->varinfo [i];
14158 if (ins->opcode == OP_REGVAR)
14159 ins->flags |= MONO_INST_GC_TRACK;
14163 /* FIXME: widening and truncation */
14166 * As an optimization, when a variable allocated to the stack is first loaded into
14167 * an lvreg, we will remember the lvreg and use it the next time instead of loading
14168 * the variable again.
14170 orig_next_vreg = cfg->next_vreg;
14171 vreg_to_lvreg = (guint32 *)mono_mempool_alloc0 (cfg->mempool, sizeof (guint32) * cfg->next_vreg);
14172 lvregs = (guint32 *)mono_mempool_alloc (cfg->mempool, sizeof (guint32) * 1024);
14176 * These arrays contain the first and last instructions accessing a given
14178 * Since we emit bblocks in the same order we process them here, and we
14179 * don't split live ranges, these will precisely describe the live range of
14180 * the variable, i.e. the instruction range where a valid value can be found
14181 * in the variables location.
14182 * The live range is computed using the liveness info computed by the liveness pass.
14183 * We can't use vmv->range, since that is an abstract live range, and we need
14184 * one which is instruction precise.
14185 * FIXME: Variables used in out-of-line bblocks have a hole in their live range.
14187 /* FIXME: Only do this if debugging info is requested */
14188 live_range_start = g_new0 (MonoInst*, cfg->next_vreg);
14189 live_range_end = g_new0 (MonoInst*, cfg->next_vreg);
14190 live_range_start_bb = g_new (MonoBasicBlock*, cfg->next_vreg);
14191 live_range_end_bb = g_new (MonoBasicBlock*, cfg->next_vreg);
14193 /* Add spill loads/stores */
14194 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
14197 if (cfg->verbose_level > 2)
14198 printf ("\nSPILL BLOCK %d:\n", bb->block_num);
14200 /* Clear vreg_to_lvreg array */
14201 for (i = 0; i < lvregs_len; i++)
14202 vreg_to_lvreg [lvregs [i]] = 0;
14206 MONO_BB_FOR_EACH_INS (bb, ins) {
14207 const char *spec = INS_INFO (ins->opcode);
14208 int regtype, srcindex, sreg, tmp_reg, prev_dreg, num_sregs;
14209 gboolean store, no_lvreg;
14210 int sregs [MONO_MAX_SRC_REGS];
14212 if (G_UNLIKELY (cfg->verbose_level > 2))
14213 mono_print_ins (ins);
14215 if (ins->opcode == OP_NOP)
14219 * We handle LDADDR here as well, since it can only be decomposed
14220 * when variable addresses are known.
14222 if (ins->opcode == OP_LDADDR) {
14223 MonoInst *var = (MonoInst *)ins->inst_p0;
14225 if (var->opcode == OP_VTARG_ADDR) {
14226 /* Happens on SPARC/S390 where vtypes are passed by reference */
14227 MonoInst *vtaddr = var->inst_left;
14228 if (vtaddr->opcode == OP_REGVAR) {
14229 ins->opcode = OP_MOVE;
14230 ins->sreg1 = vtaddr->dreg;
14232 else if (var->inst_left->opcode == OP_REGOFFSET) {
14233 ins->opcode = OP_LOAD_MEMBASE;
14234 ins->inst_basereg = vtaddr->inst_basereg;
14235 ins->inst_offset = vtaddr->inst_offset;
14238 } else if (cfg->gsharedvt && cfg->gsharedvt_vreg_to_idx [var->dreg] < 0) {
14239 /* gsharedvt arg passed by ref */
14240 g_assert (var->opcode == OP_GSHAREDVT_ARG_REGOFFSET);
14242 ins->opcode = OP_LOAD_MEMBASE;
14243 ins->inst_basereg = var->inst_basereg;
14244 ins->inst_offset = var->inst_offset;
14245 } else if (cfg->gsharedvt && cfg->gsharedvt_vreg_to_idx [var->dreg]) {
14246 MonoInst *load, *load2, *load3;
14247 int idx = cfg->gsharedvt_vreg_to_idx [var->dreg] - 1;
14248 int reg1, reg2, reg3;
14249 MonoInst *info_var = cfg->gsharedvt_info_var;
14250 MonoInst *locals_var = cfg->gsharedvt_locals_var;
14254 * Compute the address of the local as gsharedvt_locals_var + gsharedvt_info_var->locals_offsets [idx].
14257 g_assert (var->opcode == OP_GSHAREDVT_LOCAL);
14259 g_assert (info_var);
14260 g_assert (locals_var);
14262 /* Mark the instruction used to compute the locals var as used */
14263 cfg->gsharedvt_locals_var_ins = NULL;
14265 /* Load the offset */
14266 if (info_var->opcode == OP_REGOFFSET) {
14267 reg1 = alloc_ireg (cfg);
14268 NEW_LOAD_MEMBASE (cfg, load, OP_LOAD_MEMBASE, reg1, info_var->inst_basereg, info_var->inst_offset);
14269 } else if (info_var->opcode == OP_REGVAR) {
14271 reg1 = info_var->dreg;
14273 g_assert_not_reached ();
14275 reg2 = alloc_ireg (cfg);
14276 NEW_LOAD_MEMBASE (cfg, load2, OP_LOADI4_MEMBASE, reg2, reg1, MONO_STRUCT_OFFSET (MonoGSharedVtMethodRuntimeInfo, entries) + (idx * sizeof (gpointer)));
14277 /* Load the locals area address */
14278 reg3 = alloc_ireg (cfg);
14279 if (locals_var->opcode == OP_REGOFFSET) {
14280 NEW_LOAD_MEMBASE (cfg, load3, OP_LOAD_MEMBASE, reg3, locals_var->inst_basereg, locals_var->inst_offset);
14281 } else if (locals_var->opcode == OP_REGVAR) {
14282 NEW_UNALU (cfg, load3, OP_MOVE, reg3, locals_var->dreg);
14284 g_assert_not_reached ();
14286 /* Compute the address */
14287 ins->opcode = OP_PADD;
14291 mono_bblock_insert_before_ins (bb, ins, load3);
14292 mono_bblock_insert_before_ins (bb, load3, load2);
14294 mono_bblock_insert_before_ins (bb, load2, load);
14296 g_assert (var->opcode == OP_REGOFFSET);
14298 ins->opcode = OP_ADD_IMM;
14299 ins->sreg1 = var->inst_basereg;
14300 ins->inst_imm = var->inst_offset;
14303 *need_local_opts = TRUE;
14304 spec = INS_INFO (ins->opcode);
14307 if (ins->opcode < MONO_CEE_LAST) {
14308 mono_print_ins (ins);
14309 g_assert_not_reached ();
14313 * Store opcodes have destbasereg in the dreg, but in reality, it is an
14317 if (MONO_IS_STORE_MEMBASE (ins)) {
14318 tmp_reg = ins->dreg;
14319 ins->dreg = ins->sreg2;
14320 ins->sreg2 = tmp_reg;
14323 spec2 [MONO_INST_DEST] = ' ';
14324 spec2 [MONO_INST_SRC1] = spec [MONO_INST_SRC1];
14325 spec2 [MONO_INST_SRC2] = spec [MONO_INST_DEST];
14326 spec2 [MONO_INST_SRC3] = ' ';
14328 } else if (MONO_IS_STORE_MEMINDEX (ins))
14329 g_assert_not_reached ();
14334 if (G_UNLIKELY (cfg->verbose_level > 2)) {
14335 printf ("\t %.3s %d", spec, ins->dreg);
14336 num_sregs = mono_inst_get_src_registers (ins, sregs);
14337 for (srcindex = 0; srcindex < num_sregs; ++srcindex)
14338 printf (" %d", sregs [srcindex]);
14345 regtype = spec [MONO_INST_DEST];
14346 g_assert (((ins->dreg == -1) && (regtype == ' ')) || ((ins->dreg != -1) && (regtype != ' ')));
14349 if ((ins->dreg != -1) && get_vreg_to_inst (cfg, ins->dreg)) {
14350 MonoInst *var = get_vreg_to_inst (cfg, ins->dreg);
14351 MonoInst *store_ins;
14353 MonoInst *def_ins = ins;
14354 int dreg = ins->dreg; /* The original vreg */
14356 store_opcode = mono_type_to_store_membase (cfg, var->inst_vtype);
14358 if (var->opcode == OP_REGVAR) {
14359 ins->dreg = var->dreg;
14360 } 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)) {
14362 * Instead of emitting a load+store, use a _membase opcode.
14364 g_assert (var->opcode == OP_REGOFFSET);
14365 if (ins->opcode == OP_MOVE) {
14369 ins->opcode = op_to_op_dest_membase (store_opcode, ins->opcode);
14370 ins->inst_basereg = var->inst_basereg;
14371 ins->inst_offset = var->inst_offset;
14374 spec = INS_INFO (ins->opcode);
14378 g_assert (var->opcode == OP_REGOFFSET);
14380 prev_dreg = ins->dreg;
14382 /* Invalidate any previous lvreg for this vreg */
14383 vreg_to_lvreg [ins->dreg] = 0;
14387 if (COMPILE_SOFT_FLOAT (cfg) && store_opcode == OP_STORER8_MEMBASE_REG) {
14389 store_opcode = OP_STOREI8_MEMBASE_REG;
14392 ins->dreg = alloc_dreg (cfg, stacktypes [regtype]);
14394 #if SIZEOF_REGISTER != 8
14395 if (regtype == 'l') {
14396 NEW_STORE_MEMBASE (cfg, store_ins, OP_STOREI4_MEMBASE_REG, var->inst_basereg, var->inst_offset + MINI_LS_WORD_OFFSET, ins->dreg + 1);
14397 mono_bblock_insert_after_ins (bb, ins, store_ins);
14398 NEW_STORE_MEMBASE (cfg, store_ins, OP_STOREI4_MEMBASE_REG, var->inst_basereg, var->inst_offset + MINI_MS_WORD_OFFSET, ins->dreg + 2);
14399 mono_bblock_insert_after_ins (bb, ins, store_ins);
14400 def_ins = store_ins;
14405 g_assert (store_opcode != OP_STOREV_MEMBASE);
14407 /* Try to fuse the store into the instruction itself */
14408 /* FIXME: Add more instructions */
14409 if (!lvreg && ((ins->opcode == OP_ICONST) || ((ins->opcode == OP_I8CONST) && (ins->inst_c0 == 0)))) {
14410 ins->opcode = store_membase_reg_to_store_membase_imm (store_opcode);
14411 ins->inst_imm = ins->inst_c0;
14412 ins->inst_destbasereg = var->inst_basereg;
14413 ins->inst_offset = var->inst_offset;
14414 spec = INS_INFO (ins->opcode);
14415 } else if (!lvreg && ((ins->opcode == OP_MOVE) || (ins->opcode == OP_FMOVE) || (ins->opcode == OP_LMOVE) || (ins->opcode == OP_RMOVE))) {
14416 ins->opcode = store_opcode;
14417 ins->inst_destbasereg = var->inst_basereg;
14418 ins->inst_offset = var->inst_offset;
14422 tmp_reg = ins->dreg;
14423 ins->dreg = ins->sreg2;
14424 ins->sreg2 = tmp_reg;
14427 spec2 [MONO_INST_DEST] = ' ';
14428 spec2 [MONO_INST_SRC1] = spec [MONO_INST_SRC1];
14429 spec2 [MONO_INST_SRC2] = spec [MONO_INST_DEST];
14430 spec2 [MONO_INST_SRC3] = ' ';
14432 } else if (!lvreg && (op_to_op_store_membase (store_opcode, ins->opcode) != -1)) {
14433 // FIXME: The backends expect the base reg to be in inst_basereg
14434 ins->opcode = op_to_op_store_membase (store_opcode, ins->opcode);
14436 ins->inst_basereg = var->inst_basereg;
14437 ins->inst_offset = var->inst_offset;
14438 spec = INS_INFO (ins->opcode);
14440 /* printf ("INS: "); mono_print_ins (ins); */
14441 /* Create a store instruction */
14442 NEW_STORE_MEMBASE (cfg, store_ins, store_opcode, var->inst_basereg, var->inst_offset, ins->dreg);
14444 /* Insert it after the instruction */
14445 mono_bblock_insert_after_ins (bb, ins, store_ins);
14447 def_ins = store_ins;
14450 * We can't assign ins->dreg to var->dreg here, since the
14451 * sregs could use it. So set a flag, and do it after
14454 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)))
14455 dest_has_lvreg = TRUE;
14460 if (def_ins && !live_range_start [dreg]) {
14461 live_range_start [dreg] = def_ins;
14462 live_range_start_bb [dreg] = bb;
14465 if (cfg->compute_gc_maps && def_ins && (var->flags & MONO_INST_GC_TRACK)) {
14468 MONO_INST_NEW (cfg, tmp, OP_GC_LIVENESS_DEF);
14469 tmp->inst_c1 = dreg;
14470 mono_bblock_insert_after_ins (bb, def_ins, tmp);
14477 num_sregs = mono_inst_get_src_registers (ins, sregs);
14478 for (srcindex = 0; srcindex < 3; ++srcindex) {
14479 regtype = spec [MONO_INST_SRC1 + srcindex];
14480 sreg = sregs [srcindex];
14482 g_assert (((sreg == -1) && (regtype == ' ')) || ((sreg != -1) && (regtype != ' ')));
14483 if ((sreg != -1) && get_vreg_to_inst (cfg, sreg)) {
14484 MonoInst *var = get_vreg_to_inst (cfg, sreg);
14485 MonoInst *use_ins = ins;
14486 MonoInst *load_ins;
14487 guint32 load_opcode;
14489 if (var->opcode == OP_REGVAR) {
14490 sregs [srcindex] = var->dreg;
14491 //mono_inst_set_src_registers (ins, sregs);
14492 live_range_end [sreg] = use_ins;
14493 live_range_end_bb [sreg] = bb;
14495 if (cfg->compute_gc_maps && var->dreg < orig_next_vreg && (var->flags & MONO_INST_GC_TRACK)) {
14498 MONO_INST_NEW (cfg, tmp, OP_GC_LIVENESS_USE);
14499 /* var->dreg is a hreg */
14500 tmp->inst_c1 = sreg;
14501 mono_bblock_insert_after_ins (bb, ins, tmp);
14507 g_assert (var->opcode == OP_REGOFFSET);
14509 load_opcode = mono_type_to_load_membase (cfg, var->inst_vtype);
14511 g_assert (load_opcode != OP_LOADV_MEMBASE);
14513 if (vreg_to_lvreg [sreg]) {
14514 g_assert (vreg_to_lvreg [sreg] != -1);
14516 /* The variable is already loaded to an lvreg */
14517 if (G_UNLIKELY (cfg->verbose_level > 2))
14518 printf ("\t\tUse lvreg R%d for R%d.\n", vreg_to_lvreg [sreg], sreg);
14519 sregs [srcindex] = vreg_to_lvreg [sreg];
14520 //mono_inst_set_src_registers (ins, sregs);
14524 /* Try to fuse the load into the instruction */
14525 if ((srcindex == 0) && (op_to_op_src1_membase (cfg, load_opcode, ins->opcode) != -1)) {
14526 ins->opcode = op_to_op_src1_membase (cfg, load_opcode, ins->opcode);
14527 sregs [0] = var->inst_basereg;
14528 //mono_inst_set_src_registers (ins, sregs);
14529 ins->inst_offset = var->inst_offset;
14530 } else if ((srcindex == 1) && (op_to_op_src2_membase (cfg, load_opcode, ins->opcode) != -1)) {
14531 ins->opcode = op_to_op_src2_membase (cfg, load_opcode, ins->opcode);
14532 sregs [1] = var->inst_basereg;
14533 //mono_inst_set_src_registers (ins, sregs);
14534 ins->inst_offset = var->inst_offset;
14536 if (MONO_IS_REAL_MOVE (ins)) {
14537 ins->opcode = OP_NOP;
14540 //printf ("%d ", srcindex); mono_print_ins (ins);
14542 sreg = alloc_dreg (cfg, stacktypes [regtype]);
14544 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) {
14545 if (var->dreg == prev_dreg) {
14547 * sreg refers to the value loaded by the load
14548 * emitted below, but we need to use ins->dreg
14549 * since it refers to the store emitted earlier.
14553 g_assert (sreg != -1);
14554 vreg_to_lvreg [var->dreg] = sreg;
14555 g_assert (lvregs_len < 1024);
14556 lvregs [lvregs_len ++] = var->dreg;
14560 sregs [srcindex] = sreg;
14561 //mono_inst_set_src_registers (ins, sregs);
14563 #if SIZEOF_REGISTER != 8
14564 if (regtype == 'l') {
14565 NEW_LOAD_MEMBASE (cfg, load_ins, OP_LOADI4_MEMBASE, sreg + 2, var->inst_basereg, var->inst_offset + MINI_MS_WORD_OFFSET);
14566 mono_bblock_insert_before_ins (bb, ins, load_ins);
14567 NEW_LOAD_MEMBASE (cfg, load_ins, OP_LOADI4_MEMBASE, sreg + 1, var->inst_basereg, var->inst_offset + MINI_LS_WORD_OFFSET);
14568 mono_bblock_insert_before_ins (bb, ins, load_ins);
14569 use_ins = load_ins;
14574 #if SIZEOF_REGISTER == 4
14575 g_assert (load_opcode != OP_LOADI8_MEMBASE);
14577 NEW_LOAD_MEMBASE (cfg, load_ins, load_opcode, sreg, var->inst_basereg, var->inst_offset);
14578 mono_bblock_insert_before_ins (bb, ins, load_ins);
14579 use_ins = load_ins;
14583 if (var->dreg < orig_next_vreg) {
14584 live_range_end [var->dreg] = use_ins;
14585 live_range_end_bb [var->dreg] = bb;
14588 if (cfg->compute_gc_maps && var->dreg < orig_next_vreg && (var->flags & MONO_INST_GC_TRACK)) {
14591 MONO_INST_NEW (cfg, tmp, OP_GC_LIVENESS_USE);
14592 tmp->inst_c1 = var->dreg;
14593 mono_bblock_insert_after_ins (bb, ins, tmp);
14597 mono_inst_set_src_registers (ins, sregs);
14599 if (dest_has_lvreg) {
14600 g_assert (ins->dreg != -1);
14601 vreg_to_lvreg [prev_dreg] = ins->dreg;
14602 g_assert (lvregs_len < 1024);
14603 lvregs [lvregs_len ++] = prev_dreg;
14604 dest_has_lvreg = FALSE;
14608 tmp_reg = ins->dreg;
14609 ins->dreg = ins->sreg2;
14610 ins->sreg2 = tmp_reg;
14613 if (MONO_IS_CALL (ins)) {
14614 /* Clear vreg_to_lvreg array */
14615 for (i = 0; i < lvregs_len; i++)
14616 vreg_to_lvreg [lvregs [i]] = 0;
14618 } else if (ins->opcode == OP_NOP) {
14620 MONO_INST_NULLIFY_SREGS (ins);
14623 if (cfg->verbose_level > 2)
14624 mono_print_ins_index (1, ins);
14627 /* Extend the live range based on the liveness info */
14628 if (cfg->compute_precise_live_ranges && bb->live_out_set && bb->code) {
14629 for (i = 0; i < cfg->num_varinfo; i ++) {
14630 MonoMethodVar *vi = MONO_VARINFO (cfg, i);
14632 if (vreg_is_volatile (cfg, vi->vreg))
14633 /* The liveness info is incomplete */
14636 if (mono_bitset_test_fast (bb->live_in_set, i) && !live_range_start [vi->vreg]) {
14637 /* Live from at least the first ins of this bb */
14638 live_range_start [vi->vreg] = bb->code;
14639 live_range_start_bb [vi->vreg] = bb;
14642 if (mono_bitset_test_fast (bb->live_out_set, i)) {
14643 /* Live at least until the last ins of this bb */
14644 live_range_end [vi->vreg] = bb->last_ins;
14645 live_range_end_bb [vi->vreg] = bb;
14652 * Emit LIVERANGE_START/LIVERANGE_END opcodes, the backend will implement them
14653 * by storing the current native offset into MonoMethodVar->live_range_start/end.
14655 if (cfg->backend->have_liverange_ops && cfg->compute_precise_live_ranges && cfg->comp_done & MONO_COMP_LIVENESS) {
14656 for (i = 0; i < cfg->num_varinfo; ++i) {
14657 int vreg = MONO_VARINFO (cfg, i)->vreg;
14660 if (live_range_start [vreg]) {
14661 MONO_INST_NEW (cfg, ins, OP_LIVERANGE_START);
14663 ins->inst_c1 = vreg;
14664 mono_bblock_insert_after_ins (live_range_start_bb [vreg], live_range_start [vreg], ins);
14666 if (live_range_end [vreg]) {
14667 MONO_INST_NEW (cfg, ins, OP_LIVERANGE_END);
14669 ins->inst_c1 = vreg;
14670 if (live_range_end [vreg] == live_range_end_bb [vreg]->last_ins)
14671 mono_add_ins_to_end (live_range_end_bb [vreg], ins);
14673 mono_bblock_insert_after_ins (live_range_end_bb [vreg], live_range_end [vreg], ins);
14678 if (cfg->gsharedvt_locals_var_ins) {
14679 /* Nullify if unused */
14680 cfg->gsharedvt_locals_var_ins->opcode = OP_PCONST;
14681 cfg->gsharedvt_locals_var_ins->inst_imm = 0;
14684 g_free (live_range_start);
14685 g_free (live_range_end);
14686 g_free (live_range_start_bb);
14687 g_free (live_range_end_bb);
14692 * - use 'iadd' instead of 'int_add'
14693 * - handling ovf opcodes: decompose in method_to_ir.
14694 * - unify iregs/fregs
14695 * -> partly done, the missing parts are:
14696 * - a more complete unification would involve unifying the hregs as well, so
14697 * code wouldn't need if (fp) all over the place. but that would mean the hregs
14698 * would no longer map to the machine hregs, so the code generators would need to
14699 * be modified. Also, on ia64 for example, niregs + nfregs > 256 -> bitmasks
14700 * wouldn't work any more. Duplicating the code in mono_local_regalloc () into
14701 * fp/non-fp branches speeds it up by about 15%.
14702 * - use sext/zext opcodes instead of shifts
14704 * - get rid of TEMPLOADs if possible and use vregs instead
14705 * - clean up usage of OP_P/OP_ opcodes
14706 * - cleanup usage of DUMMY_USE
14707 * - cleanup the setting of ins->type for MonoInst's which are pushed on the
14709 * - set the stack type and allocate a dreg in the EMIT_NEW macros
14710 * - get rid of all the <foo>2 stuff when the new JIT is ready.
14711 * - make sure handle_stack_args () is called before the branch is emitted
14712 * - when the new IR is done, get rid of all unused stuff
14713 * - COMPARE/BEQ as separate instructions or unify them ?
14714 * - keeping them separate allows specialized compare instructions like
14715 * compare_imm, compare_membase
14716 * - most back ends unify fp compare+branch, fp compare+ceq
14717 * - integrate mono_save_args into inline_method
14718 * - get rid of the empty bblocks created by MONO_EMIT_NEW_BRACH_BLOCK2
14719 * - handle long shift opts on 32 bit platforms somehow: they require
14720 * 3 sregs (2 for arg1 and 1 for arg2)
14721 * - make byref a 'normal' type.
14722 * - use vregs for bb->out_stacks if possible, handle_global_vreg will make them a
14723 * variable if needed.
14724 * - do not start a new IL level bblock when cfg->cbb is changed by a function call
14725 * like inline_method.
14726 * - remove inlining restrictions
14727 * - fix LNEG and enable cfold of INEG
14728 * - generalize x86 optimizations like ldelema as a peephole optimization
14729 * - add store_mem_imm for amd64
14730 * - optimize the loading of the interruption flag in the managed->native wrappers
14731 * - avoid special handling of OP_NOP in passes
14732 * - move code inserting instructions into one function/macro.
14733 * - try a coalescing phase after liveness analysis
14734 * - add float -> vreg conversion + local optimizations on !x86
14735 * - figure out how to handle decomposed branches during optimizations, ie.
14736 * compare+branch, op_jump_table+op_br etc.
14737 * - promote RuntimeXHandles to vregs
14738 * - vtype cleanups:
14739 * - add a NEW_VARLOADA_VREG macro
14740 * - the vtype optimizations are blocked by the LDADDR opcodes generated for
14741 * accessing vtype fields.
14742 * - get rid of I8CONST on 64 bit platforms
14743 * - dealing with the increase in code size due to branches created during opcode
14745 * - use extended basic blocks
14746 * - all parts of the JIT
14747 * - handle_global_vregs () && local regalloc
14748 * - avoid introducing global vregs during decomposition, like 'vtable' in isinst
14749 * - sources of increase in code size:
14752 * - isinst and castclass
14753 * - lvregs not allocated to global registers even if used multiple times
14754 * - call cctors outside the JIT, to make -v output more readable and JIT timings more
14756 * - check for fp stack leakage in other opcodes too. (-> 'exceptions' optimization)
14757 * - add all micro optimizations from the old JIT
14758 * - put tree optimizations into the deadce pass
14759 * - decompose op_start_handler/op_endfilter/op_endfinally earlier using an arch
14760 * specific function.
14761 * - unify the float comparison opcodes with the other comparison opcodes, i.e.
14762 * fcompare + branchCC.
14763 * - create a helper function for allocating a stack slot, taking into account
14764 * MONO_CFG_HAS_SPILLUP.
14766 * - merge the ia64 switch changes.
14767 * - optimize mono_regstate2_alloc_int/float.
14768 * - fix the pessimistic handling of variables accessed in exception handler blocks.
14769 * - need to write a tree optimization pass, but the creation of trees is difficult, i.e.
14770 * parts of the tree could be separated by other instructions, killing the tree
14771 * arguments, or stores killing loads etc. Also, should we fold loads into other
14772 * instructions if the result of the load is used multiple times ?
14773 * - make the REM_IMM optimization in mini-x86.c arch-independent.
14774 * - LAST MERGE: 108395.
14775 * - when returning vtypes in registers, generate IR and append it to the end of the
14776 * last bb instead of doing it in the epilog.
14777 * - change the store opcodes so they use sreg1 instead of dreg to store the base register.
14785 - When to decompose opcodes:
14786 - earlier: this makes some optimizations hard to implement, since the low level IR
14787 no longer contains the neccessary information. But it is easier to do.
14788 - later: harder to implement, enables more optimizations.
14789 - Branches inside bblocks:
14790 - created when decomposing complex opcodes.
14791 - branches to another bblock: harmless, but not tracked by the branch
14792 optimizations, so need to branch to a label at the start of the bblock.
14793 - branches to inside the same bblock: very problematic, trips up the local
14794 reg allocator. Can be fixed by spitting the current bblock, but that is a
14795 complex operation, since some local vregs can become global vregs etc.
14796 - Local/global vregs:
14797 - local vregs: temporary vregs used inside one bblock. Assigned to hregs by the
14798 local register allocator.
14799 - global vregs: used in more than one bblock. Have an associated MonoMethodVar
14800 structure, created by mono_create_var (). Assigned to hregs or the stack by
14801 the global register allocator.
14802 - When to do optimizations like alu->alu_imm:
14803 - earlier -> saves work later on since the IR will be smaller/simpler
14804 - later -> can work on more instructions
14805 - Handling of valuetypes:
14806 - When a vtype is pushed on the stack, a new temporary is created, an
14807 instruction computing its address (LDADDR) is emitted and pushed on
14808 the stack. Need to optimize cases when the vtype is used immediately as in
14809 argument passing, stloc etc.
14810 - Instead of the to_end stuff in the old JIT, simply call the function handling
14811 the values on the stack before emitting the last instruction of the bb.
14814 #endif /* DISABLE_JIT */