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/gc-internal.h>
53 #include <mono/metadata/security-manager.h>
54 #include <mono/metadata/threads-types.h>
55 #include <mono/metadata/security-core-clr.h>
56 #include <mono/metadata/monitor.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"
73 #define BRANCH_COST 10
74 #define INLINE_LENGTH_LIMIT 20
76 /* These have 'cfg' as an implicit argument */
77 #define INLINE_FAILURE(msg) do { \
78 if ((cfg->method != cfg->current_method) && (cfg->current_method->wrapper_type == MONO_WRAPPER_NONE)) { \
79 inline_failure (cfg, msg); \
80 goto exception_exit; \
83 #define CHECK_CFG_EXCEPTION do {\
84 if (cfg->exception_type != MONO_EXCEPTION_NONE) \
85 goto exception_exit; \
87 #define METHOD_ACCESS_FAILURE(method, cmethod) do { \
88 method_access_failure ((cfg), (method), (cmethod)); \
89 goto exception_exit; \
91 #define FIELD_ACCESS_FAILURE(method, field) do { \
92 field_access_failure ((cfg), (method), (field)); \
93 goto exception_exit; \
95 #define GENERIC_SHARING_FAILURE(opcode) do { \
97 gshared_failure (cfg, opcode, __FILE__, __LINE__); \
98 goto exception_exit; \
101 #define GSHAREDVT_FAILURE(opcode) do { \
102 if (cfg->gsharedvt) { \
103 gsharedvt_failure (cfg, opcode, __FILE__, __LINE__); \
104 goto exception_exit; \
107 #define OUT_OF_MEMORY_FAILURE do { \
108 mono_cfg_set_exception (cfg, MONO_EXCEPTION_OUT_OF_MEMORY); \
109 goto exception_exit; \
111 #define DISABLE_AOT(cfg) do { \
112 if ((cfg)->verbose_level >= 2) \
113 printf ("AOT disabled: %s:%d\n", __FILE__, __LINE__); \
114 (cfg)->disable_aot = TRUE; \
116 #define LOAD_ERROR do { \
117 break_on_unverified (); \
118 mono_cfg_set_exception (cfg, MONO_EXCEPTION_TYPE_LOAD); \
119 goto exception_exit; \
122 #define TYPE_LOAD_ERROR(klass) do { \
123 cfg->exception_ptr = klass; \
127 #define CHECK_CFG_ERROR do {\
128 if (!mono_error_ok (&cfg->error)) { \
129 mono_cfg_set_exception (cfg, MONO_EXCEPTION_MONO_ERROR); \
130 goto mono_error_exit; \
134 /* Determine whenever 'ins' represents a load of the 'this' argument */
135 #define MONO_CHECK_THIS(ins) (mono_method_signature (cfg->method)->hasthis && ((ins)->opcode == OP_MOVE) && ((ins)->sreg1 == cfg->args [0]->dreg))
137 static int ldind_to_load_membase (int opcode);
138 static int stind_to_store_membase (int opcode);
140 int mono_op_to_op_imm (int opcode);
141 int mono_op_to_op_imm_noemul (int opcode);
143 MONO_API MonoInst* mono_emit_native_call (MonoCompile *cfg, gconstpointer func, MonoMethodSignature *sig, MonoInst **args);
145 static int inline_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **sp,
146 guchar *ip, guint real_offset, gboolean inline_always, MonoBasicBlock **out_cbb);
148 /* helper methods signatures */
149 static MonoMethodSignature *helper_sig_class_init_trampoline;
150 static MonoMethodSignature *helper_sig_domain_get;
151 static MonoMethodSignature *helper_sig_generic_class_init_trampoline;
152 static MonoMethodSignature *helper_sig_generic_class_init_trampoline_llvm;
153 static MonoMethodSignature *helper_sig_rgctx_lazy_fetch_trampoline;
154 static MonoMethodSignature *helper_sig_monitor_enter_exit_trampoline;
155 static MonoMethodSignature *helper_sig_monitor_enter_exit_trampoline_llvm;
156 static MonoMethodSignature *helper_sig_monitor_enter_v4_trampoline_llvm;
159 * Instruction metadata
167 #define MINI_OP(a,b,dest,src1,src2) dest, src1, src2, ' ',
168 #define MINI_OP3(a,b,dest,src1,src2,src3) dest, src1, src2, src3,
174 #if SIZEOF_REGISTER == 8 && SIZEOF_REGISTER == SIZEOF_VOID_P
179 /* keep in sync with the enum in mini.h */
182 #include "mini-ops.h"
187 #define MINI_OP(a,b,dest,src1,src2) ((src2) != NONE ? 2 : ((src1) != NONE ? 1 : 0)),
188 #define MINI_OP3(a,b,dest,src1,src2,src3) ((src3) != NONE ? 3 : ((src2) != NONE ? 2 : ((src1) != NONE ? 1 : 0))),
190 * This should contain the index of the last sreg + 1. This is not the same
191 * as the number of sregs for opcodes like IA64_CMP_EQ_IMM.
193 const gint8 ins_sreg_counts[] = {
194 #include "mini-ops.h"
199 #define MONO_INIT_VARINFO(vi,id) do { \
200 (vi)->range.first_use.pos.bid = 0xffff; \
206 mono_alloc_ireg (MonoCompile *cfg)
208 return alloc_ireg (cfg);
212 mono_alloc_lreg (MonoCompile *cfg)
214 return alloc_lreg (cfg);
218 mono_alloc_freg (MonoCompile *cfg)
220 return alloc_freg (cfg);
224 mono_alloc_preg (MonoCompile *cfg)
226 return alloc_preg (cfg);
230 mono_alloc_dreg (MonoCompile *cfg, MonoStackType stack_type)
232 return alloc_dreg (cfg, stack_type);
236 * mono_alloc_ireg_ref:
238 * Allocate an IREG, and mark it as holding a GC ref.
241 mono_alloc_ireg_ref (MonoCompile *cfg)
243 return alloc_ireg_ref (cfg);
247 * mono_alloc_ireg_mp:
249 * Allocate an IREG, and mark it as holding a managed pointer.
252 mono_alloc_ireg_mp (MonoCompile *cfg)
254 return alloc_ireg_mp (cfg);
258 * mono_alloc_ireg_copy:
260 * Allocate an IREG with the same GC type as VREG.
263 mono_alloc_ireg_copy (MonoCompile *cfg, guint32 vreg)
265 if (vreg_is_ref (cfg, vreg))
266 return alloc_ireg_ref (cfg);
267 else if (vreg_is_mp (cfg, vreg))
268 return alloc_ireg_mp (cfg);
270 return alloc_ireg (cfg);
274 mono_type_to_regmove (MonoCompile *cfg, MonoType *type)
279 type = mini_replace_type (type);
281 switch (type->type) {
284 case MONO_TYPE_BOOLEAN:
296 case MONO_TYPE_FNPTR:
298 case MONO_TYPE_CLASS:
299 case MONO_TYPE_STRING:
300 case MONO_TYPE_OBJECT:
301 case MONO_TYPE_SZARRAY:
302 case MONO_TYPE_ARRAY:
306 #if SIZEOF_REGISTER == 8
312 return cfg->r4fp ? OP_RMOVE : OP_FMOVE;
315 case MONO_TYPE_VALUETYPE:
316 if (type->data.klass->enumtype) {
317 type = mono_class_enum_basetype (type->data.klass);
320 if (MONO_CLASS_IS_SIMD (cfg, mono_class_from_mono_type (type)))
323 case MONO_TYPE_TYPEDBYREF:
325 case MONO_TYPE_GENERICINST:
326 type = &type->data.generic_class->container_class->byval_arg;
330 g_assert (cfg->generic_sharing_context);
331 if (mini_type_var_is_vt (cfg, type))
336 g_error ("unknown type 0x%02x in type_to_regstore", type->type);
342 mono_print_bb (MonoBasicBlock *bb, const char *msg)
347 printf ("\n%s %d: [IN: ", msg, bb->block_num);
348 for (i = 0; i < bb->in_count; ++i)
349 printf (" BB%d(%d)", bb->in_bb [i]->block_num, bb->in_bb [i]->dfn);
351 for (i = 0; i < bb->out_count; ++i)
352 printf (" BB%d(%d)", bb->out_bb [i]->block_num, bb->out_bb [i]->dfn);
354 for (tree = bb->code; tree; tree = tree->next)
355 mono_print_ins_index (-1, tree);
359 mono_create_helper_signatures (void)
361 helper_sig_domain_get = mono_create_icall_signature ("ptr");
362 helper_sig_class_init_trampoline = mono_create_icall_signature ("void");
363 helper_sig_generic_class_init_trampoline = mono_create_icall_signature ("void");
364 helper_sig_generic_class_init_trampoline_llvm = mono_create_icall_signature ("void ptr");
365 helper_sig_rgctx_lazy_fetch_trampoline = mono_create_icall_signature ("ptr ptr");
366 helper_sig_monitor_enter_exit_trampoline = mono_create_icall_signature ("void");
367 helper_sig_monitor_enter_exit_trampoline_llvm = mono_create_icall_signature ("void object");
368 helper_sig_monitor_enter_v4_trampoline_llvm = mono_create_icall_signature ("void object ptr");
371 static MONO_NEVER_INLINE void
372 break_on_unverified (void)
374 if (mini_get_debug_options ()->break_on_unverified)
378 static MONO_NEVER_INLINE void
379 method_access_failure (MonoCompile *cfg, MonoMethod *method, MonoMethod *cil_method)
381 char *method_fname = mono_method_full_name (method, TRUE);
382 char *cil_method_fname = mono_method_full_name (cil_method, TRUE);
383 mono_cfg_set_exception (cfg, MONO_EXCEPTION_METHOD_ACCESS);
384 cfg->exception_message = g_strdup_printf ("Method `%s' is inaccessible from method `%s'\n", cil_method_fname, method_fname);
385 g_free (method_fname);
386 g_free (cil_method_fname);
389 static MONO_NEVER_INLINE void
390 field_access_failure (MonoCompile *cfg, MonoMethod *method, MonoClassField *field)
392 char *method_fname = mono_method_full_name (method, TRUE);
393 char *field_fname = mono_field_full_name (field);
394 mono_cfg_set_exception (cfg, MONO_EXCEPTION_FIELD_ACCESS);
395 cfg->exception_message = g_strdup_printf ("Field `%s' is inaccessible from method `%s'\n", field_fname, method_fname);
396 g_free (method_fname);
397 g_free (field_fname);
400 static MONO_NEVER_INLINE void
401 inline_failure (MonoCompile *cfg, const char *msg)
403 if (cfg->verbose_level >= 2)
404 printf ("inline failed: %s\n", msg);
405 mono_cfg_set_exception (cfg, MONO_EXCEPTION_INLINE_FAILED);
408 static MONO_NEVER_INLINE void
409 gshared_failure (MonoCompile *cfg, int opcode, const char *file, int line)
411 if (cfg->verbose_level > 2) \
412 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__);
413 mono_cfg_set_exception (cfg, MONO_EXCEPTION_GENERIC_SHARING_FAILED);
416 static MONO_NEVER_INLINE void
417 gsharedvt_failure (MonoCompile *cfg, int opcode, const char *file, int line)
419 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);
420 if (cfg->verbose_level >= 2)
421 printf ("%s\n", cfg->exception_message);
422 mono_cfg_set_exception (cfg, MONO_EXCEPTION_GENERIC_SHARING_FAILED);
426 * When using gsharedvt, some instatiations might be verifiable, and some might be not. i.e.
427 * foo<T> (int i) { ldarg.0; box T; }
429 #define UNVERIFIED do { \
430 if (cfg->gsharedvt) { \
431 if (cfg->verbose_level > 2) \
432 printf ("gsharedvt method failed to verify, falling back to instantiation.\n"); \
433 mono_cfg_set_exception (cfg, MONO_EXCEPTION_GENERIC_SHARING_FAILED); \
434 goto exception_exit; \
436 break_on_unverified (); \
440 #define GET_BBLOCK(cfg,tblock,ip) do { \
441 (tblock) = cfg->cil_offset_to_bb [(ip) - cfg->cil_start]; \
443 if ((ip) >= end || (ip) < header->code) UNVERIFIED; \
444 NEW_BBLOCK (cfg, (tblock)); \
445 (tblock)->cil_code = (ip); \
446 ADD_BBLOCK (cfg, (tblock)); \
450 #if defined(TARGET_X86) || defined(TARGET_AMD64)
451 #define EMIT_NEW_X86_LEA(cfg,dest,sr1,sr2,shift,imm) do { \
452 MONO_INST_NEW (cfg, dest, OP_X86_LEA); \
453 (dest)->dreg = alloc_ireg_mp ((cfg)); \
454 (dest)->sreg1 = (sr1); \
455 (dest)->sreg2 = (sr2); \
456 (dest)->inst_imm = (imm); \
457 (dest)->backend.shift_amount = (shift); \
458 MONO_ADD_INS ((cfg)->cbb, (dest)); \
462 /* Emit conversions so both operands of a binary opcode are of the same type */
464 add_widen_op (MonoCompile *cfg, MonoInst *ins, MonoInst **arg1_ref, MonoInst **arg2_ref)
466 MonoInst *arg1 = *arg1_ref;
467 MonoInst *arg2 = *arg2_ref;
470 ((arg1->type == STACK_R4 && arg2->type == STACK_R8) ||
471 (arg1->type == STACK_R8 && arg2->type == STACK_R4))) {
474 /* Mixing r4/r8 is allowed by the spec */
475 if (arg1->type == STACK_R4) {
476 int dreg = alloc_freg (cfg);
478 EMIT_NEW_UNALU (cfg, conv, OP_RCONV_TO_R8, dreg, arg1->dreg);
479 conv->type = STACK_R8;
483 if (arg2->type == STACK_R4) {
484 int dreg = alloc_freg (cfg);
486 EMIT_NEW_UNALU (cfg, conv, OP_RCONV_TO_R8, dreg, arg2->dreg);
487 conv->type = STACK_R8;
493 #if SIZEOF_REGISTER == 8
494 /* FIXME: Need to add many more cases */
495 if ((arg1)->type == STACK_PTR && (arg2)->type == STACK_I4) {
498 int dr = alloc_preg (cfg);
499 EMIT_NEW_UNALU (cfg, widen, OP_SEXT_I4, dr, (arg2)->dreg);
500 (ins)->sreg2 = widen->dreg;
505 #define ADD_BINOP(op) do { \
506 MONO_INST_NEW (cfg, ins, (op)); \
508 ins->sreg1 = sp [0]->dreg; \
509 ins->sreg2 = sp [1]->dreg; \
510 type_from_op (cfg, ins, sp [0], sp [1]); \
512 /* Have to insert a widening op */ \
513 add_widen_op (cfg, ins, &sp [0], &sp [1]); \
514 ins->dreg = alloc_dreg ((cfg), (ins)->type); \
515 MONO_ADD_INS ((cfg)->cbb, (ins)); \
516 *sp++ = mono_decompose_opcode ((cfg), (ins)); \
519 #define ADD_UNOP(op) do { \
520 MONO_INST_NEW (cfg, ins, (op)); \
522 ins->sreg1 = sp [0]->dreg; \
523 type_from_op (cfg, ins, sp [0], NULL); \
525 (ins)->dreg = alloc_dreg ((cfg), (ins)->type); \
526 MONO_ADD_INS ((cfg)->cbb, (ins)); \
527 *sp++ = mono_decompose_opcode (cfg, ins); \
530 #define ADD_BINCOND(next_block) do { \
533 MONO_INST_NEW(cfg, cmp, OP_COMPARE); \
534 cmp->sreg1 = sp [0]->dreg; \
535 cmp->sreg2 = sp [1]->dreg; \
536 type_from_op (cfg, cmp, sp [0], sp [1]); \
538 add_widen_op (cfg, cmp, &sp [0], &sp [1]); \
539 type_from_op (cfg, ins, sp [0], sp [1]); \
540 ins->inst_many_bb = mono_mempool_alloc (cfg->mempool, sizeof(gpointer)*2); \
541 GET_BBLOCK (cfg, tblock, target); \
542 link_bblock (cfg, bblock, tblock); \
543 ins->inst_true_bb = tblock; \
544 if ((next_block)) { \
545 link_bblock (cfg, bblock, (next_block)); \
546 ins->inst_false_bb = (next_block); \
547 start_new_bblock = 1; \
549 GET_BBLOCK (cfg, tblock, ip); \
550 link_bblock (cfg, bblock, tblock); \
551 ins->inst_false_bb = tblock; \
552 start_new_bblock = 2; \
554 if (sp != stack_start) { \
555 handle_stack_args (cfg, stack_start, sp - stack_start); \
556 CHECK_UNVERIFIABLE (cfg); \
558 MONO_ADD_INS (bblock, cmp); \
559 MONO_ADD_INS (bblock, ins); \
563 * link_bblock: Links two basic blocks
565 * links two basic blocks in the control flow graph, the 'from'
566 * argument is the starting block and the 'to' argument is the block
567 * the control flow ends to after 'from'.
570 link_bblock (MonoCompile *cfg, MonoBasicBlock *from, MonoBasicBlock* to)
572 MonoBasicBlock **newa;
576 if (from->cil_code) {
578 printf ("edge from IL%04x to IL_%04x\n", from->cil_code - cfg->cil_code, to->cil_code - cfg->cil_code);
580 printf ("edge from IL%04x to exit\n", from->cil_code - cfg->cil_code);
583 printf ("edge from entry to IL_%04x\n", to->cil_code - cfg->cil_code);
585 printf ("edge from entry to exit\n");
590 for (i = 0; i < from->out_count; ++i) {
591 if (to == from->out_bb [i]) {
597 newa = mono_mempool_alloc (cfg->mempool, sizeof (gpointer) * (from->out_count + 1));
598 for (i = 0; i < from->out_count; ++i) {
599 newa [i] = from->out_bb [i];
607 for (i = 0; i < to->in_count; ++i) {
608 if (from == to->in_bb [i]) {
614 newa = mono_mempool_alloc (cfg->mempool, sizeof (gpointer) * (to->in_count + 1));
615 for (i = 0; i < to->in_count; ++i) {
616 newa [i] = to->in_bb [i];
625 mono_link_bblock (MonoCompile *cfg, MonoBasicBlock *from, MonoBasicBlock* to)
627 link_bblock (cfg, from, to);
631 * mono_find_block_region:
633 * We mark each basic block with a region ID. We use that to avoid BB
634 * optimizations when blocks are in different regions.
637 * A region token that encodes where this region is, and information
638 * about the clause owner for this block.
640 * The region encodes the try/catch/filter clause that owns this block
641 * as well as the type. -1 is a special value that represents a block
642 * that is in none of try/catch/filter.
645 mono_find_block_region (MonoCompile *cfg, int offset)
647 MonoMethodHeader *header = cfg->header;
648 MonoExceptionClause *clause;
651 for (i = 0; i < header->num_clauses; ++i) {
652 clause = &header->clauses [i];
653 if ((clause->flags == MONO_EXCEPTION_CLAUSE_FILTER) && (offset >= clause->data.filter_offset) &&
654 (offset < (clause->handler_offset)))
655 return ((i + 1) << 8) | MONO_REGION_FILTER | clause->flags;
657 if (MONO_OFFSET_IN_HANDLER (clause, offset)) {
658 if (clause->flags == MONO_EXCEPTION_CLAUSE_FINALLY)
659 return ((i + 1) << 8) | MONO_REGION_FINALLY | clause->flags;
660 else if (clause->flags == MONO_EXCEPTION_CLAUSE_FAULT)
661 return ((i + 1) << 8) | MONO_REGION_FAULT | clause->flags;
663 return ((i + 1) << 8) | MONO_REGION_CATCH | clause->flags;
666 if (MONO_OFFSET_IN_CLAUSE (clause, offset))
667 return ((i + 1) << 8) | clause->flags;
674 mono_find_final_block (MonoCompile *cfg, unsigned char *ip, unsigned char *target, int type)
676 MonoMethodHeader *header = cfg->header;
677 MonoExceptionClause *clause;
681 for (i = 0; i < header->num_clauses; ++i) {
682 clause = &header->clauses [i];
683 if (MONO_OFFSET_IN_CLAUSE (clause, (ip - header->code)) &&
684 (!MONO_OFFSET_IN_CLAUSE (clause, (target - header->code)))) {
685 if (clause->flags == type)
686 res = g_list_append (res, clause);
693 mono_create_spvar_for_region (MonoCompile *cfg, int region)
697 var = g_hash_table_lookup (cfg->spvars, GINT_TO_POINTER (region));
701 var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
702 /* prevent it from being register allocated */
703 var->flags |= MONO_INST_VOLATILE;
705 g_hash_table_insert (cfg->spvars, GINT_TO_POINTER (region), var);
709 mono_find_exvar_for_offset (MonoCompile *cfg, int offset)
711 return g_hash_table_lookup (cfg->exvars, GINT_TO_POINTER (offset));
715 mono_create_exvar_for_offset (MonoCompile *cfg, int offset)
719 var = g_hash_table_lookup (cfg->exvars, GINT_TO_POINTER (offset));
723 var = mono_compile_create_var (cfg, &mono_defaults.object_class->byval_arg, OP_LOCAL);
724 /* prevent it from being register allocated */
725 var->flags |= MONO_INST_VOLATILE;
727 g_hash_table_insert (cfg->exvars, GINT_TO_POINTER (offset), var);
733 * Returns the type used in the eval stack when @type is loaded.
734 * FIXME: return a MonoType/MonoClass for the byref and VALUETYPE cases.
737 type_to_eval_stack_type (MonoCompile *cfg, MonoType *type, MonoInst *inst)
741 type = mini_replace_type (type);
742 inst->klass = klass = mono_class_from_mono_type (type);
744 inst->type = STACK_MP;
749 switch (type->type) {
751 inst->type = STACK_INV;
755 case MONO_TYPE_BOOLEAN:
761 inst->type = STACK_I4;
766 case MONO_TYPE_FNPTR:
767 inst->type = STACK_PTR;
769 case MONO_TYPE_CLASS:
770 case MONO_TYPE_STRING:
771 case MONO_TYPE_OBJECT:
772 case MONO_TYPE_SZARRAY:
773 case MONO_TYPE_ARRAY:
774 inst->type = STACK_OBJ;
778 inst->type = STACK_I8;
781 inst->type = cfg->r4_stack_type;
784 inst->type = STACK_R8;
786 case MONO_TYPE_VALUETYPE:
787 if (type->data.klass->enumtype) {
788 type = mono_class_enum_basetype (type->data.klass);
792 inst->type = STACK_VTYPE;
795 case MONO_TYPE_TYPEDBYREF:
796 inst->klass = mono_defaults.typed_reference_class;
797 inst->type = STACK_VTYPE;
799 case MONO_TYPE_GENERICINST:
800 type = &type->data.generic_class->container_class->byval_arg;
804 g_assert (cfg->generic_sharing_context);
805 if (mini_is_gsharedvt_type (cfg, type)) {
806 g_assert (cfg->gsharedvt);
807 inst->type = STACK_VTYPE;
809 inst->type = STACK_OBJ;
813 g_error ("unknown type 0x%02x in eval stack type", type->type);
818 * The following tables are used to quickly validate the IL code in type_from_op ().
821 bin_num_table [STACK_MAX] [STACK_MAX] = {
822 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
823 {STACK_INV, STACK_I4, STACK_INV, STACK_PTR, STACK_INV, STACK_MP, STACK_INV, STACK_INV},
824 {STACK_INV, STACK_INV, STACK_I8, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
825 {STACK_INV, STACK_PTR, STACK_INV, STACK_PTR, STACK_INV, STACK_MP, STACK_INV, STACK_INV},
826 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_R8, STACK_INV, STACK_INV, STACK_INV, STACK_R8},
827 {STACK_INV, STACK_MP, STACK_INV, STACK_MP, STACK_INV, STACK_PTR, STACK_INV, STACK_INV},
828 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
829 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
830 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_R8, STACK_INV, STACK_INV, STACK_INV, STACK_R4}
835 STACK_INV, STACK_I4, STACK_I8, STACK_PTR, STACK_R8, STACK_INV, STACK_INV, STACK_INV, STACK_R4
838 /* reduce the size of this table */
840 bin_int_table [STACK_MAX] [STACK_MAX] = {
841 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
842 {STACK_INV, STACK_I4, STACK_INV, STACK_PTR, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
843 {STACK_INV, STACK_INV, STACK_I8, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
844 {STACK_INV, STACK_PTR, STACK_INV, STACK_PTR, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
845 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
846 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
847 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
848 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV}
852 bin_comp_table [STACK_MAX] [STACK_MAX] = {
853 /* Inv i L p F & O vt r4 */
855 {0, 1, 0, 1, 0, 0, 0, 0}, /* i, int32 */
856 {0, 0, 1, 0, 0, 0, 0, 0}, /* L, int64 */
857 {0, 1, 0, 1, 0, 2, 4, 0}, /* p, ptr */
858 {0, 0, 0, 0, 1, 0, 0, 0, 1}, /* F, R8 */
859 {0, 0, 0, 2, 0, 1, 0, 0}, /* &, managed pointer */
860 {0, 0, 0, 4, 0, 0, 3, 0}, /* O, reference */
861 {0, 0, 0, 0, 0, 0, 0, 0}, /* vt value type */
862 {0, 0, 0, 0, 1, 0, 0, 0, 1}, /* r, r4 */
865 /* reduce the size of this table */
867 shift_table [STACK_MAX] [STACK_MAX] = {
868 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
869 {STACK_INV, STACK_I4, STACK_INV, STACK_I4, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
870 {STACK_INV, STACK_I8, STACK_INV, STACK_I8, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
871 {STACK_INV, STACK_PTR, STACK_INV, STACK_PTR, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
872 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
873 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
874 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
875 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV}
879 * Tables to map from the non-specific opcode to the matching
880 * type-specific opcode.
882 /* handles from CEE_ADD to CEE_SHR_UN (CEE_REM_UN for floats) */
884 binops_op_map [STACK_MAX] = {
885 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
888 /* handles from CEE_NEG to CEE_CONV_U8 */
890 unops_op_map [STACK_MAX] = {
891 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
894 /* handles from CEE_CONV_U2 to CEE_SUB_OVF_UN */
896 ovfops_op_map [STACK_MAX] = {
897 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
900 /* handles from CEE_CONV_OVF_I1_UN to CEE_CONV_OVF_U_UN */
902 ovf2ops_op_map [STACK_MAX] = {
903 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
906 /* handles from CEE_CONV_OVF_I1 to CEE_CONV_OVF_U8 */
908 ovf3ops_op_map [STACK_MAX] = {
909 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
912 /* handles from CEE_BEQ to CEE_BLT_UN */
914 beqops_op_map [STACK_MAX] = {
915 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
918 /* handles from CEE_CEQ to CEE_CLT_UN */
920 ceqops_op_map [STACK_MAX] = {
921 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
925 * Sets ins->type (the type on the eval stack) according to the
926 * type of the opcode and the arguments to it.
927 * Invalid IL code is marked by setting ins->type to the invalid value STACK_INV.
929 * FIXME: this function sets ins->type unconditionally in some cases, but
930 * it should set it to invalid for some types (a conv.x on an object)
933 type_from_op (MonoCompile *cfg, MonoInst *ins, MonoInst *src1, MonoInst *src2)
935 switch (ins->opcode) {
942 /* FIXME: check unverifiable args for STACK_MP */
943 ins->type = bin_num_table [src1->type] [src2->type];
944 ins->opcode += binops_op_map [ins->type];
951 ins->type = bin_int_table [src1->type] [src2->type];
952 ins->opcode += binops_op_map [ins->type];
957 ins->type = shift_table [src1->type] [src2->type];
958 ins->opcode += binops_op_map [ins->type];
963 ins->type = bin_comp_table [src1->type] [src2->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;
966 else if (src1->type == STACK_R4)
967 ins->opcode = OP_RCOMPARE;
968 else if (src1->type == STACK_R8)
969 ins->opcode = OP_FCOMPARE;
971 ins->opcode = OP_ICOMPARE;
973 case OP_ICOMPARE_IMM:
974 ins->type = bin_comp_table [src1->type] [src1->type] ? STACK_I4 : STACK_INV;
975 if ((src1->type == STACK_I8) || ((SIZEOF_VOID_P == 8) && ((src1->type == STACK_PTR) || (src1->type == STACK_OBJ) || (src1->type == STACK_MP))))
976 ins->opcode = OP_LCOMPARE_IMM;
988 ins->opcode += beqops_op_map [src1->type];
991 ins->type = bin_comp_table [src1->type] [src2->type] ? STACK_I4: STACK_INV;
992 ins->opcode += ceqops_op_map [src1->type];
998 ins->type = (bin_comp_table [src1->type] [src2->type] & 1) ? STACK_I4: STACK_INV;
999 ins->opcode += ceqops_op_map [src1->type];
1003 ins->type = neg_table [src1->type];
1004 ins->opcode += unops_op_map [ins->type];
1007 if (src1->type >= STACK_I4 && src1->type <= STACK_PTR)
1008 ins->type = src1->type;
1010 ins->type = STACK_INV;
1011 ins->opcode += unops_op_map [ins->type];
1017 ins->type = STACK_I4;
1018 ins->opcode += unops_op_map [src1->type];
1021 ins->type = STACK_R8;
1022 switch (src1->type) {
1025 ins->opcode = OP_ICONV_TO_R_UN;
1028 ins->opcode = OP_LCONV_TO_R_UN;
1032 case CEE_CONV_OVF_I1:
1033 case CEE_CONV_OVF_U1:
1034 case CEE_CONV_OVF_I2:
1035 case CEE_CONV_OVF_U2:
1036 case CEE_CONV_OVF_I4:
1037 case CEE_CONV_OVF_U4:
1038 ins->type = STACK_I4;
1039 ins->opcode += ovf3ops_op_map [src1->type];
1041 case CEE_CONV_OVF_I_UN:
1042 case CEE_CONV_OVF_U_UN:
1043 ins->type = STACK_PTR;
1044 ins->opcode += ovf2ops_op_map [src1->type];
1046 case CEE_CONV_OVF_I1_UN:
1047 case CEE_CONV_OVF_I2_UN:
1048 case CEE_CONV_OVF_I4_UN:
1049 case CEE_CONV_OVF_U1_UN:
1050 case CEE_CONV_OVF_U2_UN:
1051 case CEE_CONV_OVF_U4_UN:
1052 ins->type = STACK_I4;
1053 ins->opcode += ovf2ops_op_map [src1->type];
1056 ins->type = STACK_PTR;
1057 switch (src1->type) {
1059 ins->opcode = OP_ICONV_TO_U;
1063 #if SIZEOF_VOID_P == 8
1064 ins->opcode = OP_LCONV_TO_U;
1066 ins->opcode = OP_MOVE;
1070 ins->opcode = OP_LCONV_TO_U;
1073 ins->opcode = OP_FCONV_TO_U;
1079 ins->type = STACK_I8;
1080 ins->opcode += unops_op_map [src1->type];
1082 case CEE_CONV_OVF_I8:
1083 case CEE_CONV_OVF_U8:
1084 ins->type = STACK_I8;
1085 ins->opcode += ovf3ops_op_map [src1->type];
1087 case CEE_CONV_OVF_U8_UN:
1088 case CEE_CONV_OVF_I8_UN:
1089 ins->type = STACK_I8;
1090 ins->opcode += ovf2ops_op_map [src1->type];
1093 ins->type = cfg->r4_stack_type;
1094 ins->opcode += unops_op_map [src1->type];
1097 ins->type = STACK_R8;
1098 ins->opcode += unops_op_map [src1->type];
1101 ins->type = STACK_R8;
1105 ins->type = STACK_I4;
1106 ins->opcode += ovfops_op_map [src1->type];
1109 case CEE_CONV_OVF_I:
1110 case CEE_CONV_OVF_U:
1111 ins->type = STACK_PTR;
1112 ins->opcode += ovfops_op_map [src1->type];
1115 case CEE_ADD_OVF_UN:
1117 case CEE_MUL_OVF_UN:
1119 case CEE_SUB_OVF_UN:
1120 ins->type = bin_num_table [src1->type] [src2->type];
1121 ins->opcode += ovfops_op_map [src1->type];
1122 if (ins->type == STACK_R8)
1123 ins->type = STACK_INV;
1125 case OP_LOAD_MEMBASE:
1126 ins->type = STACK_PTR;
1128 case OP_LOADI1_MEMBASE:
1129 case OP_LOADU1_MEMBASE:
1130 case OP_LOADI2_MEMBASE:
1131 case OP_LOADU2_MEMBASE:
1132 case OP_LOADI4_MEMBASE:
1133 case OP_LOADU4_MEMBASE:
1134 ins->type = STACK_PTR;
1136 case OP_LOADI8_MEMBASE:
1137 ins->type = STACK_I8;
1139 case OP_LOADR4_MEMBASE:
1140 ins->type = cfg->r4_stack_type;
1142 case OP_LOADR8_MEMBASE:
1143 ins->type = STACK_R8;
1146 g_error ("opcode 0x%04x not handled in type from op", ins->opcode);
1150 if (ins->type == STACK_MP)
1151 ins->klass = mono_defaults.object_class;
1156 STACK_I4, STACK_I4, STACK_I4, STACK_I4, STACK_I4, STACK_I4, STACK_I8, STACK_PTR, STACK_R8, STACK_R8, STACK_OBJ
1162 param_table [STACK_MAX] [STACK_MAX] = {
1167 check_values_to_signature (MonoInst *args, MonoType *this, MonoMethodSignature *sig) {
1171 switch (args->type) {
1181 for (i = 0; i < sig->param_count; ++i) {
1182 switch (args [i].type) {
1186 if (!sig->params [i]->byref)
1190 if (sig->params [i]->byref)
1192 switch (sig->params [i]->type) {
1193 case MONO_TYPE_CLASS:
1194 case MONO_TYPE_STRING:
1195 case MONO_TYPE_OBJECT:
1196 case MONO_TYPE_SZARRAY:
1197 case MONO_TYPE_ARRAY:
1204 if (sig->params [i]->byref)
1206 if (sig->params [i]->type != MONO_TYPE_R4 && sig->params [i]->type != MONO_TYPE_R8)
1215 /*if (!param_table [args [i].type] [sig->params [i]->type])
1223 * When we need a pointer to the current domain many times in a method, we
1224 * call mono_domain_get() once and we store the result in a local variable.
1225 * This function returns the variable that represents the MonoDomain*.
1227 inline static MonoInst *
1228 mono_get_domainvar (MonoCompile *cfg)
1230 if (!cfg->domainvar)
1231 cfg->domainvar = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
1232 return cfg->domainvar;
1236 * The got_var contains the address of the Global Offset Table when AOT
1240 mono_get_got_var (MonoCompile *cfg)
1242 #ifdef MONO_ARCH_NEED_GOT_VAR
1243 if (!cfg->compile_aot)
1245 if (!cfg->got_var) {
1246 cfg->got_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
1248 return cfg->got_var;
1255 mono_get_vtable_var (MonoCompile *cfg)
1257 g_assert (cfg->generic_sharing_context);
1259 if (!cfg->rgctx_var) {
1260 cfg->rgctx_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
1261 /* force the var to be stack allocated */
1262 cfg->rgctx_var->flags |= MONO_INST_VOLATILE;
1265 return cfg->rgctx_var;
1269 type_from_stack_type (MonoInst *ins) {
1270 switch (ins->type) {
1271 case STACK_I4: return &mono_defaults.int32_class->byval_arg;
1272 case STACK_I8: return &mono_defaults.int64_class->byval_arg;
1273 case STACK_PTR: return &mono_defaults.int_class->byval_arg;
1274 case STACK_R4: return &mono_defaults.single_class->byval_arg;
1275 case STACK_R8: return &mono_defaults.double_class->byval_arg;
1277 return &ins->klass->this_arg;
1278 case STACK_OBJ: return &mono_defaults.object_class->byval_arg;
1279 case STACK_VTYPE: return &ins->klass->byval_arg;
1281 g_error ("stack type %d to monotype not handled\n", ins->type);
1286 static G_GNUC_UNUSED int
1287 type_to_stack_type (MonoCompile *cfg, MonoType *t)
1289 t = mono_type_get_underlying_type (t);
1293 case MONO_TYPE_BOOLEAN:
1296 case MONO_TYPE_CHAR:
1303 case MONO_TYPE_FNPTR:
1305 case MONO_TYPE_CLASS:
1306 case MONO_TYPE_STRING:
1307 case MONO_TYPE_OBJECT:
1308 case MONO_TYPE_SZARRAY:
1309 case MONO_TYPE_ARRAY:
1315 return cfg->r4_stack_type;
1318 case MONO_TYPE_VALUETYPE:
1319 case MONO_TYPE_TYPEDBYREF:
1321 case MONO_TYPE_GENERICINST:
1322 if (mono_type_generic_inst_is_valuetype (t))
1328 g_assert_not_reached ();
1335 array_access_to_klass (int opcode)
1339 return mono_defaults.byte_class;
1341 return mono_defaults.uint16_class;
1344 return mono_defaults.int_class;
1347 return mono_defaults.sbyte_class;
1350 return mono_defaults.int16_class;
1353 return mono_defaults.int32_class;
1355 return mono_defaults.uint32_class;
1358 return mono_defaults.int64_class;
1361 return mono_defaults.single_class;
1364 return mono_defaults.double_class;
1365 case CEE_LDELEM_REF:
1366 case CEE_STELEM_REF:
1367 return mono_defaults.object_class;
1369 g_assert_not_reached ();
1375 * We try to share variables when possible
1378 mono_compile_get_interface_var (MonoCompile *cfg, int slot, MonoInst *ins)
1383 /* inlining can result in deeper stacks */
1384 if (slot >= cfg->header->max_stack)
1385 return mono_compile_create_var (cfg, type_from_stack_type (ins), OP_LOCAL);
1387 pos = ins->type - 1 + slot * STACK_MAX;
1389 switch (ins->type) {
1396 if ((vnum = cfg->intvars [pos]))
1397 return cfg->varinfo [vnum];
1398 res = mono_compile_create_var (cfg, type_from_stack_type (ins), OP_LOCAL);
1399 cfg->intvars [pos] = res->inst_c0;
1402 res = mono_compile_create_var (cfg, type_from_stack_type (ins), OP_LOCAL);
1408 mono_save_token_info (MonoCompile *cfg, MonoImage *image, guint32 token, gpointer key)
1411 * Don't use this if a generic_context is set, since that means AOT can't
1412 * look up the method using just the image+token.
1413 * table == 0 means this is a reference made from a wrapper.
1415 if (cfg->compile_aot && !cfg->generic_context && (mono_metadata_token_table (token) > 0)) {
1416 MonoJumpInfoToken *jump_info_token = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoJumpInfoToken));
1417 jump_info_token->image = image;
1418 jump_info_token->token = token;
1419 g_hash_table_insert (cfg->token_info_hash, key, jump_info_token);
1424 * This function is called to handle items that are left on the evaluation stack
1425 * at basic block boundaries. What happens is that we save the values to local variables
1426 * and we reload them later when first entering the target basic block (with the
1427 * handle_loaded_temps () function).
1428 * A single joint point will use the same variables (stored in the array bb->out_stack or
1429 * bb->in_stack, if the basic block is before or after the joint point).
1431 * This function needs to be called _before_ emitting the last instruction of
1432 * the bb (i.e. before emitting a branch).
1433 * If the stack merge fails at a join point, cfg->unverifiable is set.
1436 handle_stack_args (MonoCompile *cfg, MonoInst **sp, int count)
1439 MonoBasicBlock *bb = cfg->cbb;
1440 MonoBasicBlock *outb;
1441 MonoInst *inst, **locals;
1446 if (cfg->verbose_level > 3)
1447 printf ("%d item(s) on exit from B%d\n", count, bb->block_num);
1448 if (!bb->out_scount) {
1449 bb->out_scount = count;
1450 //printf ("bblock %d has out:", bb->block_num);
1452 for (i = 0; i < bb->out_count; ++i) {
1453 outb = bb->out_bb [i];
1454 /* exception handlers are linked, but they should not be considered for stack args */
1455 if (outb->flags & BB_EXCEPTION_HANDLER)
1457 //printf (" %d", outb->block_num);
1458 if (outb->in_stack) {
1460 bb->out_stack = outb->in_stack;
1466 bb->out_stack = mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*) * count);
1467 for (i = 0; i < count; ++i) {
1469 * try to reuse temps already allocated for this purpouse, if they occupy the same
1470 * stack slot and if they are of the same type.
1471 * This won't cause conflicts since if 'local' is used to
1472 * store one of the values in the in_stack of a bblock, then
1473 * the same variable will be used for the same outgoing stack
1475 * This doesn't work when inlining methods, since the bblocks
1476 * in the inlined methods do not inherit their in_stack from
1477 * the bblock they are inlined to. See bug #58863 for an
1480 if (cfg->inlined_method)
1481 bb->out_stack [i] = mono_compile_create_var (cfg, type_from_stack_type (sp [i]), OP_LOCAL);
1483 bb->out_stack [i] = mono_compile_get_interface_var (cfg, i, sp [i]);
1488 for (i = 0; i < bb->out_count; ++i) {
1489 outb = bb->out_bb [i];
1490 /* exception handlers are linked, but they should not be considered for stack args */
1491 if (outb->flags & BB_EXCEPTION_HANDLER)
1493 if (outb->in_scount) {
1494 if (outb->in_scount != bb->out_scount) {
1495 cfg->unverifiable = TRUE;
1498 continue; /* check they are the same locals */
1500 outb->in_scount = count;
1501 outb->in_stack = bb->out_stack;
1504 locals = bb->out_stack;
1506 for (i = 0; i < count; ++i) {
1507 EMIT_NEW_TEMPSTORE (cfg, inst, locals [i]->inst_c0, sp [i]);
1508 inst->cil_code = sp [i]->cil_code;
1509 sp [i] = locals [i];
1510 if (cfg->verbose_level > 3)
1511 printf ("storing %d to temp %d\n", i, (int)locals [i]->inst_c0);
1515 * It is possible that the out bblocks already have in_stack assigned, and
1516 * the in_stacks differ. In this case, we will store to all the different
1523 /* Find a bblock which has a different in_stack */
1525 while (bindex < bb->out_count) {
1526 outb = bb->out_bb [bindex];
1527 /* exception handlers are linked, but they should not be considered for stack args */
1528 if (outb->flags & BB_EXCEPTION_HANDLER) {
1532 if (outb->in_stack != locals) {
1533 for (i = 0; i < count; ++i) {
1534 EMIT_NEW_TEMPSTORE (cfg, inst, outb->in_stack [i]->inst_c0, sp [i]);
1535 inst->cil_code = sp [i]->cil_code;
1536 sp [i] = locals [i];
1537 if (cfg->verbose_level > 3)
1538 printf ("storing %d to temp %d\n", i, (int)outb->in_stack [i]->inst_c0);
1540 locals = outb->in_stack;
1549 /* Emit code which loads interface_offsets [klass->interface_id]
1550 * The array is stored in memory before vtable.
1553 mini_emit_load_intf_reg_vtable (MonoCompile *cfg, int intf_reg, int vtable_reg, MonoClass *klass)
1555 if (cfg->compile_aot) {
1556 int ioffset_reg = alloc_preg (cfg);
1557 int iid_reg = alloc_preg (cfg);
1559 MONO_EMIT_NEW_AOTCONST (cfg, iid_reg, klass, MONO_PATCH_INFO_ADJUSTED_IID);
1560 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, ioffset_reg, iid_reg, vtable_reg);
1561 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, intf_reg, ioffset_reg, 0);
1564 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, intf_reg, vtable_reg, -((klass->interface_id + 1) * SIZEOF_VOID_P));
1569 mini_emit_interface_bitmap_check (MonoCompile *cfg, int intf_bit_reg, int base_reg, int offset, MonoClass *klass)
1571 int ibitmap_reg = alloc_preg (cfg);
1572 #ifdef COMPRESSED_INTERFACE_BITMAP
1574 MonoInst *res, *ins;
1575 NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, ibitmap_reg, base_reg, offset);
1576 MONO_ADD_INS (cfg->cbb, ins);
1578 if (cfg->compile_aot)
1579 EMIT_NEW_AOTCONST (cfg, args [1], MONO_PATCH_INFO_IID, klass);
1581 EMIT_NEW_ICONST (cfg, args [1], klass->interface_id);
1582 res = mono_emit_jit_icall (cfg, mono_class_interface_match, args);
1583 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, intf_bit_reg, res->dreg);
1585 int ibitmap_byte_reg = alloc_preg (cfg);
1587 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, ibitmap_reg, base_reg, offset);
1589 if (cfg->compile_aot) {
1590 int iid_reg = alloc_preg (cfg);
1591 int shifted_iid_reg = alloc_preg (cfg);
1592 int ibitmap_byte_address_reg = alloc_preg (cfg);
1593 int masked_iid_reg = alloc_preg (cfg);
1594 int iid_one_bit_reg = alloc_preg (cfg);
1595 int iid_bit_reg = alloc_preg (cfg);
1596 MONO_EMIT_NEW_AOTCONST (cfg, iid_reg, klass, MONO_PATCH_INFO_IID);
1597 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SHR_IMM, shifted_iid_reg, iid_reg, 3);
1598 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, ibitmap_byte_address_reg, ibitmap_reg, shifted_iid_reg);
1599 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU1_MEMBASE, ibitmap_byte_reg, ibitmap_byte_address_reg, 0);
1600 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_IAND_IMM, masked_iid_reg, iid_reg, 7);
1601 MONO_EMIT_NEW_ICONST (cfg, iid_one_bit_reg, 1);
1602 MONO_EMIT_NEW_BIALU (cfg, OP_ISHL, iid_bit_reg, iid_one_bit_reg, masked_iid_reg);
1603 MONO_EMIT_NEW_BIALU (cfg, OP_IAND, intf_bit_reg, ibitmap_byte_reg, iid_bit_reg);
1605 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI1_MEMBASE, ibitmap_byte_reg, ibitmap_reg, klass->interface_id >> 3);
1606 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_AND_IMM, intf_bit_reg, ibitmap_byte_reg, 1 << (klass->interface_id & 7));
1612 * Emit code which loads into "intf_bit_reg" a nonzero value if the MonoClass
1613 * stored in "klass_reg" implements the interface "klass".
1616 mini_emit_load_intf_bit_reg_class (MonoCompile *cfg, int intf_bit_reg, int klass_reg, MonoClass *klass)
1618 mini_emit_interface_bitmap_check (cfg, intf_bit_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, interface_bitmap), klass);
1622 * Emit code which loads into "intf_bit_reg" a nonzero value if the MonoVTable
1623 * stored in "vtable_reg" implements the interface "klass".
1626 mini_emit_load_intf_bit_reg_vtable (MonoCompile *cfg, int intf_bit_reg, int vtable_reg, MonoClass *klass)
1628 mini_emit_interface_bitmap_check (cfg, intf_bit_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, interface_bitmap), klass);
1632 * Emit code which checks whenever the interface id of @klass is smaller than
1633 * than the value given by max_iid_reg.
1636 mini_emit_max_iid_check (MonoCompile *cfg, int max_iid_reg, MonoClass *klass,
1637 MonoBasicBlock *false_target)
1639 if (cfg->compile_aot) {
1640 int iid_reg = alloc_preg (cfg);
1641 MONO_EMIT_NEW_AOTCONST (cfg, iid_reg, klass, MONO_PATCH_INFO_IID);
1642 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, max_iid_reg, iid_reg);
1645 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, max_iid_reg, klass->interface_id);
1647 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBLT_UN, false_target);
1649 MONO_EMIT_NEW_COND_EXC (cfg, LT_UN, "InvalidCastException");
1652 /* Same as above, but obtains max_iid from a vtable */
1654 mini_emit_max_iid_check_vtable (MonoCompile *cfg, int vtable_reg, MonoClass *klass,
1655 MonoBasicBlock *false_target)
1657 int max_iid_reg = alloc_preg (cfg);
1659 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU2_MEMBASE, max_iid_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, max_interface_id));
1660 mini_emit_max_iid_check (cfg, max_iid_reg, klass, false_target);
1663 /* Same as above, but obtains max_iid from a klass */
1665 mini_emit_max_iid_check_class (MonoCompile *cfg, int klass_reg, MonoClass *klass,
1666 MonoBasicBlock *false_target)
1668 int max_iid_reg = alloc_preg (cfg);
1670 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU2_MEMBASE, max_iid_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, max_interface_id));
1671 mini_emit_max_iid_check (cfg, max_iid_reg, klass, false_target);
1675 mini_emit_isninst_cast_inst (MonoCompile *cfg, int klass_reg, MonoClass *klass, MonoInst *klass_ins, MonoBasicBlock *false_target, MonoBasicBlock *true_target)
1677 int idepth_reg = alloc_preg (cfg);
1678 int stypes_reg = alloc_preg (cfg);
1679 int stype = alloc_preg (cfg);
1681 mono_class_setup_supertypes (klass);
1683 if (klass->idepth > MONO_DEFAULT_SUPERTABLE_SIZE) {
1684 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU2_MEMBASE, idepth_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, idepth));
1685 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, idepth_reg, klass->idepth);
1686 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBLT_UN, false_target);
1688 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, stypes_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, supertypes));
1689 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, stype, stypes_reg, ((klass->idepth - 1) * SIZEOF_VOID_P));
1691 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, stype, klass_ins->dreg);
1692 } else if (cfg->compile_aot) {
1693 int const_reg = alloc_preg (cfg);
1694 MONO_EMIT_NEW_CLASSCONST (cfg, const_reg, klass);
1695 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, stype, const_reg);
1697 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, stype, klass);
1699 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, true_target);
1703 mini_emit_isninst_cast (MonoCompile *cfg, int klass_reg, MonoClass *klass, MonoBasicBlock *false_target, MonoBasicBlock *true_target)
1705 mini_emit_isninst_cast_inst (cfg, klass_reg, klass, NULL, false_target, true_target);
1709 mini_emit_iface_cast (MonoCompile *cfg, int vtable_reg, MonoClass *klass, MonoBasicBlock *false_target, MonoBasicBlock *true_target)
1711 int intf_reg = alloc_preg (cfg);
1713 mini_emit_max_iid_check_vtable (cfg, vtable_reg, klass, false_target);
1714 mini_emit_load_intf_bit_reg_vtable (cfg, intf_reg, vtable_reg, klass);
1715 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, intf_reg, 0);
1717 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, true_target);
1719 MONO_EMIT_NEW_COND_EXC (cfg, EQ, "InvalidCastException");
1723 * Variant of the above that takes a register to the class, not the vtable.
1726 mini_emit_iface_class_cast (MonoCompile *cfg, int klass_reg, MonoClass *klass, MonoBasicBlock *false_target, MonoBasicBlock *true_target)
1728 int intf_bit_reg = alloc_preg (cfg);
1730 mini_emit_max_iid_check_class (cfg, klass_reg, klass, false_target);
1731 mini_emit_load_intf_bit_reg_class (cfg, intf_bit_reg, klass_reg, klass);
1732 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, intf_bit_reg, 0);
1734 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, true_target);
1736 MONO_EMIT_NEW_COND_EXC (cfg, EQ, "InvalidCastException");
1740 mini_emit_class_check_inst (MonoCompile *cfg, int klass_reg, MonoClass *klass, MonoInst *klass_inst)
1743 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, klass_reg, klass_inst->dreg);
1744 } else if (cfg->compile_aot) {
1745 int const_reg = alloc_preg (cfg);
1746 MONO_EMIT_NEW_CLASSCONST (cfg, const_reg, klass);
1747 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, klass_reg, const_reg);
1749 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, klass_reg, klass);
1751 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException");
1755 mini_emit_class_check (MonoCompile *cfg, int klass_reg, MonoClass *klass)
1757 mini_emit_class_check_inst (cfg, klass_reg, klass, NULL);
1761 mini_emit_class_check_branch (MonoCompile *cfg, int klass_reg, MonoClass *klass, int branch_op, MonoBasicBlock *target)
1763 if (cfg->compile_aot) {
1764 int const_reg = alloc_preg (cfg);
1765 MONO_EMIT_NEW_CLASSCONST (cfg, const_reg, klass);
1766 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, klass_reg, const_reg);
1768 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, klass_reg, klass);
1770 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, branch_op, target);
1774 mini_emit_castclass (MonoCompile *cfg, int obj_reg, int klass_reg, MonoClass *klass, MonoBasicBlock *object_is_null);
1777 mini_emit_castclass_inst (MonoCompile *cfg, int obj_reg, int klass_reg, MonoClass *klass, MonoInst *klass_inst, MonoBasicBlock *object_is_null)
1780 int rank_reg = alloc_preg (cfg);
1781 int eclass_reg = alloc_preg (cfg);
1783 g_assert (!klass_inst);
1784 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU1_MEMBASE, rank_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, rank));
1785 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, rank_reg, klass->rank);
1786 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException");
1787 // MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
1788 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, eclass_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, cast_class));
1789 if (klass->cast_class == mono_defaults.object_class) {
1790 int parent_reg = alloc_preg (cfg);
1791 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, parent_reg, eclass_reg, MONO_STRUCT_OFFSET (MonoClass, parent));
1792 mini_emit_class_check_branch (cfg, parent_reg, mono_defaults.enum_class->parent, OP_PBNE_UN, object_is_null);
1793 mini_emit_class_check (cfg, eclass_reg, mono_defaults.enum_class);
1794 } else if (klass->cast_class == mono_defaults.enum_class->parent) {
1795 mini_emit_class_check_branch (cfg, eclass_reg, mono_defaults.enum_class->parent, OP_PBEQ, object_is_null);
1796 mini_emit_class_check (cfg, eclass_reg, mono_defaults.enum_class);
1797 } else if (klass->cast_class == mono_defaults.enum_class) {
1798 mini_emit_class_check (cfg, eclass_reg, mono_defaults.enum_class);
1799 } else if (klass->cast_class->flags & TYPE_ATTRIBUTE_INTERFACE) {
1800 mini_emit_iface_class_cast (cfg, eclass_reg, klass->cast_class, NULL, NULL);
1802 // Pass -1 as obj_reg to skip the check below for arrays of arrays
1803 mini_emit_castclass (cfg, -1, eclass_reg, klass->cast_class, object_is_null);
1806 if ((klass->rank == 1) && (klass->byval_arg.type == MONO_TYPE_SZARRAY) && (obj_reg != -1)) {
1807 /* Check that the object is a vector too */
1808 int bounds_reg = alloc_preg (cfg);
1809 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, bounds_reg, obj_reg, MONO_STRUCT_OFFSET (MonoArray, bounds));
1810 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, bounds_reg, 0);
1811 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException");
1814 int idepth_reg = alloc_preg (cfg);
1815 int stypes_reg = alloc_preg (cfg);
1816 int stype = alloc_preg (cfg);
1818 mono_class_setup_supertypes (klass);
1820 if (klass->idepth > MONO_DEFAULT_SUPERTABLE_SIZE) {
1821 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU2_MEMBASE, idepth_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, idepth));
1822 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, idepth_reg, klass->idepth);
1823 MONO_EMIT_NEW_COND_EXC (cfg, LT_UN, "InvalidCastException");
1825 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, stypes_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, supertypes));
1826 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, stype, stypes_reg, ((klass->idepth - 1) * SIZEOF_VOID_P));
1827 mini_emit_class_check_inst (cfg, stype, klass, klass_inst);
1832 mini_emit_castclass (MonoCompile *cfg, int obj_reg, int klass_reg, MonoClass *klass, MonoBasicBlock *object_is_null)
1834 mini_emit_castclass_inst (cfg, obj_reg, klass_reg, klass, NULL, object_is_null);
1838 mini_emit_memset (MonoCompile *cfg, int destreg, int offset, int size, int val, int align)
1842 g_assert (val == 0);
1847 if ((size <= SIZEOF_REGISTER) && (size <= align)) {
1850 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREI1_MEMBASE_IMM, destreg, offset, val);
1853 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREI2_MEMBASE_IMM, destreg, offset, val);
1856 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREI4_MEMBASE_IMM, destreg, offset, val);
1858 #if SIZEOF_REGISTER == 8
1860 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREI8_MEMBASE_IMM, destreg, offset, val);
1866 val_reg = alloc_preg (cfg);
1868 if (SIZEOF_REGISTER == 8)
1869 MONO_EMIT_NEW_I8CONST (cfg, val_reg, val);
1871 MONO_EMIT_NEW_ICONST (cfg, val_reg, val);
1874 /* This could be optimized further if neccesary */
1876 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI1_MEMBASE_REG, destreg, offset, val_reg);
1883 #if !NO_UNALIGNED_ACCESS
1884 if (SIZEOF_REGISTER == 8) {
1886 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI4_MEMBASE_REG, destreg, offset, val_reg);
1891 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI8_MEMBASE_REG, destreg, offset, val_reg);
1899 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI4_MEMBASE_REG, destreg, offset, val_reg);
1904 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI2_MEMBASE_REG, destreg, offset, val_reg);
1909 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI1_MEMBASE_REG, destreg, offset, val_reg);
1916 mini_emit_memcpy (MonoCompile *cfg, int destreg, int doffset, int srcreg, int soffset, int size, int align)
1923 /*FIXME arbitrary hack to avoid unbound code expansion.*/
1924 g_assert (size < 10000);
1927 /* This could be optimized further if neccesary */
1929 cur_reg = alloc_preg (cfg);
1930 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI1_MEMBASE, cur_reg, srcreg, soffset);
1931 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI1_MEMBASE_REG, destreg, doffset, cur_reg);
1938 #if !NO_UNALIGNED_ACCESS
1939 if (SIZEOF_REGISTER == 8) {
1941 cur_reg = alloc_preg (cfg);
1942 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI8_MEMBASE, cur_reg, srcreg, soffset);
1943 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI8_MEMBASE_REG, destreg, doffset, cur_reg);
1952 cur_reg = alloc_preg (cfg);
1953 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI4_MEMBASE, cur_reg, srcreg, soffset);
1954 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI4_MEMBASE_REG, destreg, doffset, cur_reg);
1960 cur_reg = alloc_preg (cfg);
1961 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI2_MEMBASE, cur_reg, srcreg, soffset);
1962 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI2_MEMBASE_REG, destreg, doffset, cur_reg);
1968 cur_reg = alloc_preg (cfg);
1969 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI1_MEMBASE, cur_reg, srcreg, soffset);
1970 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI1_MEMBASE_REG, destreg, doffset, cur_reg);
1978 emit_tls_set (MonoCompile *cfg, int sreg1, int tls_key)
1982 if (cfg->compile_aot) {
1983 EMIT_NEW_TLS_OFFSETCONST (cfg, c, tls_key);
1984 MONO_INST_NEW (cfg, ins, OP_TLS_SET_REG);
1986 ins->sreg2 = c->dreg;
1987 MONO_ADD_INS (cfg->cbb, ins);
1989 MONO_INST_NEW (cfg, ins, OP_TLS_SET);
1991 ins->inst_offset = mini_get_tls_offset (tls_key);
1992 MONO_ADD_INS (cfg->cbb, ins);
1999 * Emit IR to push the current LMF onto the LMF stack.
2002 emit_push_lmf (MonoCompile *cfg)
2005 * Emit IR to push the LMF:
2006 * lmf_addr = <lmf_addr from tls>
2007 * lmf->lmf_addr = lmf_addr
2008 * lmf->prev_lmf = *lmf_addr
2011 int lmf_reg, prev_lmf_reg;
2012 MonoInst *ins, *lmf_ins;
2017 if (cfg->lmf_ir_mono_lmf && mini_tls_get_supported (cfg, TLS_KEY_LMF)) {
2018 /* Load current lmf */
2019 lmf_ins = mono_get_lmf_intrinsic (cfg);
2021 MONO_ADD_INS (cfg->cbb, lmf_ins);
2022 EMIT_NEW_VARLOADA (cfg, ins, cfg->lmf_var, NULL);
2023 lmf_reg = ins->dreg;
2024 /* Save previous_lmf */
2025 EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, lmf_reg, MONO_STRUCT_OFFSET (MonoLMF, previous_lmf), lmf_ins->dreg);
2027 emit_tls_set (cfg, lmf_reg, TLS_KEY_LMF);
2030 * Store lmf_addr in a variable, so it can be allocated to a global register.
2032 if (!cfg->lmf_addr_var)
2033 cfg->lmf_addr_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
2036 ins = mono_get_jit_tls_intrinsic (cfg);
2038 int jit_tls_dreg = ins->dreg;
2040 MONO_ADD_INS (cfg->cbb, ins);
2041 lmf_reg = alloc_preg (cfg);
2042 EMIT_NEW_BIALU_IMM (cfg, lmf_ins, OP_PADD_IMM, lmf_reg, jit_tls_dreg, MONO_STRUCT_OFFSET (MonoJitTlsData, lmf));
2044 lmf_ins = mono_emit_jit_icall (cfg, mono_get_lmf_addr, NULL);
2047 lmf_ins = mono_get_lmf_addr_intrinsic (cfg);
2049 MONO_ADD_INS (cfg->cbb, lmf_ins);
2052 MonoInst *args [16], *jit_tls_ins, *ins;
2054 /* Inline mono_get_lmf_addr () */
2055 /* jit_tls = pthread_getspecific (mono_jit_tls_id); lmf_addr = &jit_tls->lmf; */
2057 /* Load mono_jit_tls_id */
2058 EMIT_NEW_AOTCONST (cfg, args [0], MONO_PATCH_INFO_JIT_TLS_ID, NULL);
2059 /* call pthread_getspecific () */
2060 jit_tls_ins = mono_emit_jit_icall (cfg, pthread_getspecific, args);
2061 /* lmf_addr = &jit_tls->lmf */
2062 EMIT_NEW_BIALU_IMM (cfg, ins, OP_PADD_IMM, cfg->lmf_addr_var->dreg, jit_tls_ins->dreg, MONO_STRUCT_OFFSET (MonoJitTlsData, lmf));
2065 lmf_ins = mono_emit_jit_icall (cfg, mono_get_lmf_addr, NULL);
2069 lmf_ins->dreg = cfg->lmf_addr_var->dreg;
2071 EMIT_NEW_VARLOADA (cfg, ins, cfg->lmf_var, NULL);
2072 lmf_reg = ins->dreg;
2074 prev_lmf_reg = alloc_preg (cfg);
2075 /* Save previous_lmf */
2076 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, prev_lmf_reg, cfg->lmf_addr_var->dreg, 0);
2077 EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, lmf_reg, MONO_STRUCT_OFFSET (MonoLMF, previous_lmf), prev_lmf_reg);
2079 EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, cfg->lmf_addr_var->dreg, 0, lmf_reg);
2086 * Emit IR to pop the current LMF from the LMF stack.
2089 emit_pop_lmf (MonoCompile *cfg)
2091 int lmf_reg, lmf_addr_reg, prev_lmf_reg;
2097 EMIT_NEW_VARLOADA (cfg, ins, cfg->lmf_var, NULL);
2098 lmf_reg = ins->dreg;
2100 if (cfg->lmf_ir_mono_lmf && mini_tls_get_supported (cfg, TLS_KEY_LMF)) {
2101 /* Load previous_lmf */
2102 prev_lmf_reg = alloc_preg (cfg);
2103 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, prev_lmf_reg, lmf_reg, MONO_STRUCT_OFFSET (MonoLMF, previous_lmf));
2105 emit_tls_set (cfg, prev_lmf_reg, TLS_KEY_LMF);
2108 * Emit IR to pop the LMF:
2109 * *(lmf->lmf_addr) = lmf->prev_lmf
2111 /* This could be called before emit_push_lmf () */
2112 if (!cfg->lmf_addr_var)
2113 cfg->lmf_addr_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
2114 lmf_addr_reg = cfg->lmf_addr_var->dreg;
2116 prev_lmf_reg = alloc_preg (cfg);
2117 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, prev_lmf_reg, lmf_reg, MONO_STRUCT_OFFSET (MonoLMF, previous_lmf));
2118 EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, lmf_addr_reg, 0, prev_lmf_reg);
2123 emit_instrumentation_call (MonoCompile *cfg, void *func)
2125 MonoInst *iargs [1];
2128 * Avoid instrumenting inlined methods since it can
2129 * distort profiling results.
2131 if (cfg->method != cfg->current_method)
2134 if (cfg->prof_options & MONO_PROFILE_ENTER_LEAVE) {
2135 EMIT_NEW_METHODCONST (cfg, iargs [0], cfg->method);
2136 mono_emit_jit_icall (cfg, func, iargs);
2141 ret_type_to_call_opcode (MonoCompile *cfg, MonoType *type, int calli, int virt, MonoGenericSharingContext *gsctx)
2144 return calli? OP_CALL_REG: virt? OP_CALL_MEMBASE: OP_CALL;
2147 type = mini_get_basic_type_from_generic (gsctx, type);
2148 type = mini_replace_type (type);
2149 switch (type->type) {
2150 case MONO_TYPE_VOID:
2151 return calli? OP_VOIDCALL_REG: virt? OP_VOIDCALL_MEMBASE: OP_VOIDCALL;
2154 case MONO_TYPE_BOOLEAN:
2157 case MONO_TYPE_CHAR:
2160 return calli? OP_CALL_REG: virt? OP_CALL_MEMBASE: OP_CALL;
2164 case MONO_TYPE_FNPTR:
2165 return calli? OP_CALL_REG: virt? OP_CALL_MEMBASE: OP_CALL;
2166 case MONO_TYPE_CLASS:
2167 case MONO_TYPE_STRING:
2168 case MONO_TYPE_OBJECT:
2169 case MONO_TYPE_SZARRAY:
2170 case MONO_TYPE_ARRAY:
2171 return calli? OP_CALL_REG: virt? OP_CALL_MEMBASE: OP_CALL;
2174 return calli? OP_LCALL_REG: virt? OP_LCALL_MEMBASE: OP_LCALL;
2177 return calli? OP_RCALL_REG: virt? OP_RCALL_MEMBASE: OP_RCALL;
2179 return calli? OP_FCALL_REG: virt? OP_FCALL_MEMBASE: OP_FCALL;
2181 return calli? OP_FCALL_REG: virt? OP_FCALL_MEMBASE: OP_FCALL;
2182 case MONO_TYPE_VALUETYPE:
2183 if (type->data.klass->enumtype) {
2184 type = mono_class_enum_basetype (type->data.klass);
2187 return calli? OP_VCALL_REG: virt? OP_VCALL_MEMBASE: OP_VCALL;
2188 case MONO_TYPE_TYPEDBYREF:
2189 return calli? OP_VCALL_REG: virt? OP_VCALL_MEMBASE: OP_VCALL;
2190 case MONO_TYPE_GENERICINST:
2191 type = &type->data.generic_class->container_class->byval_arg;
2194 case MONO_TYPE_MVAR:
2196 return calli? OP_VCALL_REG: virt? OP_VCALL_MEMBASE: OP_VCALL;
2198 g_error ("unknown type 0x%02x in ret_type_to_call_opcode", type->type);
2204 * target_type_is_incompatible:
2205 * @cfg: MonoCompile context
2207 * Check that the item @arg on the evaluation stack can be stored
2208 * in the target type (can be a local, or field, etc).
2209 * The cfg arg can be used to check if we need verification or just
2212 * Returns: non-0 value if arg can't be stored on a target.
2215 target_type_is_incompatible (MonoCompile *cfg, MonoType *target, MonoInst *arg)
2217 MonoType *simple_type;
2220 target = mini_replace_type (target);
2221 if (target->byref) {
2222 /* FIXME: check that the pointed to types match */
2223 if (arg->type == STACK_MP)
2224 return arg->klass != mono_class_from_mono_type (target);
2225 if (arg->type == STACK_PTR)
2230 simple_type = mono_type_get_underlying_type (target);
2231 switch (simple_type->type) {
2232 case MONO_TYPE_VOID:
2236 case MONO_TYPE_BOOLEAN:
2239 case MONO_TYPE_CHAR:
2242 if (arg->type != STACK_I4 && arg->type != STACK_PTR)
2246 /* STACK_MP is needed when setting pinned locals */
2247 if (arg->type != STACK_I4 && arg->type != STACK_PTR && arg->type != STACK_MP)
2252 case MONO_TYPE_FNPTR:
2254 * Some opcodes like ldloca returns 'transient pointers' which can be stored in
2255 * in native int. (#688008).
2257 if (arg->type != STACK_I4 && arg->type != STACK_PTR && arg->type != STACK_MP)
2260 case MONO_TYPE_CLASS:
2261 case MONO_TYPE_STRING:
2262 case MONO_TYPE_OBJECT:
2263 case MONO_TYPE_SZARRAY:
2264 case MONO_TYPE_ARRAY:
2265 if (arg->type != STACK_OBJ)
2267 /* FIXME: check type compatibility */
2271 if (arg->type != STACK_I8)
2275 if (arg->type != cfg->r4_stack_type)
2279 if (arg->type != STACK_R8)
2282 case MONO_TYPE_VALUETYPE:
2283 if (arg->type != STACK_VTYPE)
2285 klass = mono_class_from_mono_type (simple_type);
2286 if (klass != arg->klass)
2289 case MONO_TYPE_TYPEDBYREF:
2290 if (arg->type != STACK_VTYPE)
2292 klass = mono_class_from_mono_type (simple_type);
2293 if (klass != arg->klass)
2296 case MONO_TYPE_GENERICINST:
2297 if (mono_type_generic_inst_is_valuetype (simple_type)) {
2298 if (arg->type != STACK_VTYPE)
2300 klass = mono_class_from_mono_type (simple_type);
2301 if (klass != arg->klass)
2305 if (arg->type != STACK_OBJ)
2307 /* FIXME: check type compatibility */
2311 case MONO_TYPE_MVAR:
2312 g_assert (cfg->generic_sharing_context);
2313 if (mini_type_var_is_vt (cfg, simple_type)) {
2314 if (arg->type != STACK_VTYPE)
2317 if (arg->type != STACK_OBJ)
2322 g_error ("unknown type 0x%02x in target_type_is_incompatible", simple_type->type);
2328 * Prepare arguments for passing to a function call.
2329 * Return a non-zero value if the arguments can't be passed to the given
2331 * The type checks are not yet complete and some conversions may need
2332 * casts on 32 or 64 bit architectures.
2334 * FIXME: implement this using target_type_is_incompatible ()
2337 check_call_signature (MonoCompile *cfg, MonoMethodSignature *sig, MonoInst **args)
2339 MonoType *simple_type;
2343 if (args [0]->type != STACK_OBJ && args [0]->type != STACK_MP && args [0]->type != STACK_PTR)
2347 for (i = 0; i < sig->param_count; ++i) {
2348 if (sig->params [i]->byref) {
2349 if (args [i]->type != STACK_MP && args [i]->type != STACK_PTR)
2353 simple_type = sig->params [i];
2354 simple_type = mini_get_basic_type_from_generic (cfg->generic_sharing_context, simple_type);
2356 switch (simple_type->type) {
2357 case MONO_TYPE_VOID:
2362 case MONO_TYPE_BOOLEAN:
2365 case MONO_TYPE_CHAR:
2368 if (args [i]->type != STACK_I4 && args [i]->type != STACK_PTR)
2374 case MONO_TYPE_FNPTR:
2375 if (args [i]->type != STACK_I4 && args [i]->type != STACK_PTR && args [i]->type != STACK_MP && args [i]->type != STACK_OBJ)
2378 case MONO_TYPE_CLASS:
2379 case MONO_TYPE_STRING:
2380 case MONO_TYPE_OBJECT:
2381 case MONO_TYPE_SZARRAY:
2382 case MONO_TYPE_ARRAY:
2383 if (args [i]->type != STACK_OBJ)
2388 if (args [i]->type != STACK_I8)
2392 if (args [i]->type != cfg->r4_stack_type)
2396 if (args [i]->type != STACK_R8)
2399 case MONO_TYPE_VALUETYPE:
2400 if (simple_type->data.klass->enumtype) {
2401 simple_type = mono_class_enum_basetype (simple_type->data.klass);
2404 if (args [i]->type != STACK_VTYPE)
2407 case MONO_TYPE_TYPEDBYREF:
2408 if (args [i]->type != STACK_VTYPE)
2411 case MONO_TYPE_GENERICINST:
2412 simple_type = &simple_type->data.generic_class->container_class->byval_arg;
2415 case MONO_TYPE_MVAR:
2417 if (args [i]->type != STACK_VTYPE)
2421 g_error ("unknown type 0x%02x in check_call_signature",
2429 callvirt_to_call (int opcode)
2432 case OP_CALL_MEMBASE:
2434 case OP_VOIDCALL_MEMBASE:
2436 case OP_FCALL_MEMBASE:
2438 case OP_RCALL_MEMBASE:
2440 case OP_VCALL_MEMBASE:
2442 case OP_LCALL_MEMBASE:
2445 g_assert_not_reached ();
2451 /* Either METHOD or IMT_ARG needs to be set */
2453 emit_imt_argument (MonoCompile *cfg, MonoCallInst *call, MonoMethod *method, MonoInst *imt_arg)
2457 if (COMPILE_LLVM (cfg)) {
2458 method_reg = alloc_preg (cfg);
2461 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, method_reg, imt_arg->dreg);
2462 } else if (cfg->compile_aot) {
2463 MONO_EMIT_NEW_AOTCONST (cfg, method_reg, method, MONO_PATCH_INFO_METHODCONST);
2466 MONO_INST_NEW (cfg, ins, OP_PCONST);
2467 ins->inst_p0 = method;
2468 ins->dreg = method_reg;
2469 MONO_ADD_INS (cfg->cbb, ins);
2473 call->imt_arg_reg = method_reg;
2475 #ifdef MONO_ARCH_IMT_REG
2476 mono_call_inst_add_outarg_reg (cfg, call, method_reg, MONO_ARCH_IMT_REG, FALSE);
2478 /* Need this to keep the IMT arg alive */
2479 mono_call_inst_add_outarg_reg (cfg, call, method_reg, 0, FALSE);
2484 #ifdef MONO_ARCH_IMT_REG
2485 method_reg = alloc_preg (cfg);
2488 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, method_reg, imt_arg->dreg);
2489 } else if (cfg->compile_aot) {
2490 MONO_EMIT_NEW_AOTCONST (cfg, method_reg, method, MONO_PATCH_INFO_METHODCONST);
2493 MONO_INST_NEW (cfg, ins, OP_PCONST);
2494 ins->inst_p0 = method;
2495 ins->dreg = method_reg;
2496 MONO_ADD_INS (cfg->cbb, ins);
2499 mono_call_inst_add_outarg_reg (cfg, call, method_reg, MONO_ARCH_IMT_REG, FALSE);
2501 mono_arch_emit_imt_argument (cfg, call, imt_arg);
2505 static MonoJumpInfo *
2506 mono_patch_info_new (MonoMemPool *mp, int ip, MonoJumpInfoType type, gconstpointer target)
2508 MonoJumpInfo *ji = mono_mempool_alloc (mp, sizeof (MonoJumpInfo));
2512 ji->data.target = target;
2518 mini_class_check_context_used (MonoCompile *cfg, MonoClass *klass)
2520 if (cfg->generic_sharing_context)
2521 return mono_class_check_context_used (klass);
2527 mini_method_check_context_used (MonoCompile *cfg, MonoMethod *method)
2529 if (cfg->generic_sharing_context)
2530 return mono_method_check_context_used (method);
2536 * check_method_sharing:
2538 * Check whenever the vtable or an mrgctx needs to be passed when calling CMETHOD.
2541 check_method_sharing (MonoCompile *cfg, MonoMethod *cmethod, gboolean *out_pass_vtable, gboolean *out_pass_mrgctx)
2543 gboolean pass_vtable = FALSE;
2544 gboolean pass_mrgctx = FALSE;
2546 if (((cmethod->flags & METHOD_ATTRIBUTE_STATIC) || cmethod->klass->valuetype) &&
2547 (cmethod->klass->generic_class || cmethod->klass->generic_container)) {
2548 gboolean sharable = FALSE;
2550 if (mono_method_is_generic_sharable (cmethod, TRUE)) {
2553 gboolean sharing_enabled = mono_class_generic_sharing_enabled (cmethod->klass);
2554 MonoGenericContext *context = mini_class_get_context (cmethod->klass);
2555 gboolean context_sharable = mono_generic_context_is_sharable (context, TRUE);
2557 sharable = sharing_enabled && context_sharable;
2561 * Pass vtable iff target method might
2562 * be shared, which means that sharing
2563 * is enabled for its class and its
2564 * context is sharable (and it's not a
2567 if (sharable && !(mini_method_get_context (cmethod) && mini_method_get_context (cmethod)->method_inst))
2571 if (mini_method_get_context (cmethod) &&
2572 mini_method_get_context (cmethod)->method_inst) {
2573 g_assert (!pass_vtable);
2575 if (mono_method_is_generic_sharable (cmethod, TRUE)) {
2578 gboolean sharing_enabled = mono_class_generic_sharing_enabled (cmethod->klass);
2579 MonoGenericContext *context = mini_method_get_context (cmethod);
2580 gboolean context_sharable = mono_generic_context_is_sharable (context, TRUE);
2582 if (sharing_enabled && context_sharable)
2584 if (cfg->gsharedvt && mini_is_gsharedvt_signature (cfg, mono_method_signature (cmethod)))
2589 if (out_pass_vtable)
2590 *out_pass_vtable = pass_vtable;
2591 if (out_pass_mrgctx)
2592 *out_pass_mrgctx = pass_mrgctx;
2595 inline static MonoCallInst *
2596 mono_emit_call_args (MonoCompile *cfg, MonoMethodSignature *sig,
2597 MonoInst **args, int calli, int virtual, int tail, int rgctx, int unbox_trampoline)
2601 #ifdef MONO_ARCH_SOFT_FLOAT_FALLBACK
2606 emit_instrumentation_call (cfg, mono_profiler_method_leave);
2608 MONO_INST_NEW_CALL (cfg, call, OP_TAILCALL);
2610 MONO_INST_NEW_CALL (cfg, call, ret_type_to_call_opcode (cfg, sig->ret, calli, virtual, cfg->generic_sharing_context));
2613 call->signature = sig;
2614 call->rgctx_reg = rgctx;
2615 sig_ret = mini_replace_type (sig->ret);
2617 type_to_eval_stack_type ((cfg), sig_ret, &call->inst);
2620 if (mini_type_is_vtype (cfg, sig_ret)) {
2621 call->vret_var = cfg->vret_addr;
2622 //g_assert_not_reached ();
2624 } else if (mini_type_is_vtype (cfg, sig_ret)) {
2625 MonoInst *temp = mono_compile_create_var (cfg, sig_ret, OP_LOCAL);
2628 temp->backend.is_pinvoke = sig->pinvoke;
2631 * We use a new opcode OP_OUTARG_VTRETADDR instead of LDADDR for emitting the
2632 * address of return value to increase optimization opportunities.
2633 * Before vtype decomposition, the dreg of the call ins itself represents the
2634 * fact the call modifies the return value. After decomposition, the call will
2635 * be transformed into one of the OP_VOIDCALL opcodes, and the VTRETADDR opcode
2636 * will be transformed into an LDADDR.
2638 MONO_INST_NEW (cfg, loada, OP_OUTARG_VTRETADDR);
2639 loada->dreg = alloc_preg (cfg);
2640 loada->inst_p0 = temp;
2641 /* We reference the call too since call->dreg could change during optimization */
2642 loada->inst_p1 = call;
2643 MONO_ADD_INS (cfg->cbb, loada);
2645 call->inst.dreg = temp->dreg;
2647 call->vret_var = loada;
2648 } else if (!MONO_TYPE_IS_VOID (sig_ret))
2649 call->inst.dreg = alloc_dreg (cfg, call->inst.type);
2651 #ifdef MONO_ARCH_SOFT_FLOAT_FALLBACK
2652 if (COMPILE_SOFT_FLOAT (cfg)) {
2654 * If the call has a float argument, we would need to do an r8->r4 conversion using
2655 * an icall, but that cannot be done during the call sequence since it would clobber
2656 * the call registers + the stack. So we do it before emitting the call.
2658 for (i = 0; i < sig->param_count + sig->hasthis; ++i) {
2660 MonoInst *in = call->args [i];
2662 if (i >= sig->hasthis)
2663 t = sig->params [i - sig->hasthis];
2665 t = &mono_defaults.int_class->byval_arg;
2666 t = mono_type_get_underlying_type (t);
2668 if (!t->byref && t->type == MONO_TYPE_R4) {
2669 MonoInst *iargs [1];
2673 conv = mono_emit_jit_icall (cfg, mono_fload_r4_arg, iargs);
2675 /* The result will be in an int vreg */
2676 call->args [i] = conv;
2682 call->need_unbox_trampoline = unbox_trampoline;
2685 if (COMPILE_LLVM (cfg))
2686 mono_llvm_emit_call (cfg, call);
2688 mono_arch_emit_call (cfg, call);
2690 mono_arch_emit_call (cfg, call);
2693 cfg->param_area = MAX (cfg->param_area, call->stack_usage);
2694 cfg->flags |= MONO_CFG_HAS_CALLS;
2700 set_rgctx_arg (MonoCompile *cfg, MonoCallInst *call, int rgctx_reg, MonoInst *rgctx_arg)
2702 #ifdef MONO_ARCH_RGCTX_REG
2703 mono_call_inst_add_outarg_reg (cfg, call, rgctx_reg, MONO_ARCH_RGCTX_REG, FALSE);
2704 cfg->uses_rgctx_reg = TRUE;
2705 call->rgctx_reg = TRUE;
2707 call->rgctx_arg_reg = rgctx_reg;
2714 inline static MonoInst*
2715 mono_emit_calli (MonoCompile *cfg, MonoMethodSignature *sig, MonoInst **args, MonoInst *addr, MonoInst *imt_arg, MonoInst *rgctx_arg)
2720 gboolean check_sp = FALSE;
2722 if (cfg->check_pinvoke_callconv && cfg->method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE) {
2723 WrapperInfo *info = mono_marshal_get_wrapper_info (cfg->method);
2725 if (info && info->subtype == WRAPPER_SUBTYPE_PINVOKE)
2730 rgctx_reg = mono_alloc_preg (cfg);
2731 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, rgctx_reg, rgctx_arg->dreg);
2735 if (!cfg->stack_inbalance_var)
2736 cfg->stack_inbalance_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
2738 MONO_INST_NEW (cfg, ins, OP_GET_SP);
2739 ins->dreg = cfg->stack_inbalance_var->dreg;
2740 MONO_ADD_INS (cfg->cbb, ins);
2743 call = mono_emit_call_args (cfg, sig, args, TRUE, FALSE, FALSE, rgctx_arg ? TRUE : FALSE, FALSE);
2745 call->inst.sreg1 = addr->dreg;
2748 emit_imt_argument (cfg, call, NULL, imt_arg);
2750 MONO_ADD_INS (cfg->cbb, (MonoInst*)call);
2755 sp_reg = mono_alloc_preg (cfg);
2757 MONO_INST_NEW (cfg, ins, OP_GET_SP);
2759 MONO_ADD_INS (cfg->cbb, ins);
2761 /* Restore the stack so we don't crash when throwing the exception */
2762 MONO_INST_NEW (cfg, ins, OP_SET_SP);
2763 ins->sreg1 = cfg->stack_inbalance_var->dreg;
2764 MONO_ADD_INS (cfg->cbb, ins);
2766 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, cfg->stack_inbalance_var->dreg, sp_reg);
2767 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "ExecutionEngineException");
2771 set_rgctx_arg (cfg, call, rgctx_reg, rgctx_arg);
2773 return (MonoInst*)call;
2777 emit_get_gsharedvt_info_klass (MonoCompile *cfg, MonoClass *klass, MonoRgctxInfoType rgctx_type);
2780 emit_get_rgctx_method (MonoCompile *cfg, int context_used, MonoMethod *cmethod, MonoRgctxInfoType rgctx_type);
2782 emit_get_rgctx_klass (MonoCompile *cfg, int context_used, MonoClass *klass, MonoRgctxInfoType rgctx_type);
2785 mono_emit_method_call_full (MonoCompile *cfg, MonoMethod *method, MonoMethodSignature *sig, gboolean tail,
2786 MonoInst **args, MonoInst *this, MonoInst *imt_arg, MonoInst *rgctx_arg)
2788 #ifndef DISABLE_REMOTING
2789 gboolean might_be_remote = FALSE;
2791 gboolean virtual = this != NULL;
2792 gboolean enable_for_aot = TRUE;
2796 gboolean need_unbox_trampoline;
2799 sig = mono_method_signature (method);
2802 rgctx_reg = mono_alloc_preg (cfg);
2803 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, rgctx_reg, rgctx_arg->dreg);
2806 if (method->string_ctor) {
2807 /* Create the real signature */
2808 /* FIXME: Cache these */
2809 MonoMethodSignature *ctor_sig = mono_metadata_signature_dup_mempool (cfg->mempool, sig);
2810 ctor_sig->ret = &mono_defaults.string_class->byval_arg;
2815 context_used = mini_method_check_context_used (cfg, method);
2817 #ifndef DISABLE_REMOTING
2818 might_be_remote = this && sig->hasthis &&
2819 (mono_class_is_marshalbyref (method->klass) || method->klass == mono_defaults.object_class) &&
2820 !(method->flags & METHOD_ATTRIBUTE_VIRTUAL) && (!MONO_CHECK_THIS (this) || context_used);
2822 if (might_be_remote && context_used) {
2825 g_assert (cfg->generic_sharing_context);
2827 addr = emit_get_rgctx_method (cfg, context_used, method, MONO_RGCTX_INFO_REMOTING_INVOKE_WITH_CHECK);
2829 return mono_emit_calli (cfg, sig, args, addr, NULL, NULL);
2833 need_unbox_trampoline = method->klass == mono_defaults.object_class || (method->klass->flags & TYPE_ATTRIBUTE_INTERFACE);
2835 call = mono_emit_call_args (cfg, sig, args, FALSE, virtual, tail, rgctx_arg ? TRUE : FALSE, need_unbox_trampoline);
2837 #ifndef DISABLE_REMOTING
2838 if (might_be_remote)
2839 call->method = mono_marshal_get_remoting_invoke_with_check (method);
2842 call->method = method;
2843 call->inst.flags |= MONO_INST_HAS_METHOD;
2844 call->inst.inst_left = this;
2845 call->tail_call = tail;
2848 int vtable_reg, slot_reg, this_reg;
2851 this_reg = this->dreg;
2853 if (ARCH_HAVE_DELEGATE_TRAMPOLINES && (method->klass->parent == mono_defaults.multicastdelegate_class) && !strcmp (method->name, "Invoke")) {
2854 MonoInst *dummy_use;
2856 MONO_EMIT_NULL_CHECK (cfg, this_reg);
2858 /* Make a call to delegate->invoke_impl */
2859 call->inst.inst_basereg = this_reg;
2860 call->inst.inst_offset = MONO_STRUCT_OFFSET (MonoDelegate, invoke_impl);
2861 MONO_ADD_INS (cfg->cbb, (MonoInst*)call);
2863 /* We must emit a dummy use here because the delegate trampoline will
2864 replace the 'this' argument with the delegate target making this activation
2865 no longer a root for the delegate.
2866 This is an issue for delegates that target collectible code such as dynamic
2867 methods of GC'able assemblies.
2869 For a test case look into #667921.
2871 FIXME: a dummy use is not the best way to do it as the local register allocator
2872 will put it on a caller save register and spil it around the call.
2873 Ideally, we would either put it on a callee save register or only do the store part.
2875 EMIT_NEW_DUMMY_USE (cfg, dummy_use, args [0]);
2877 return (MonoInst*)call;
2880 if ((!cfg->compile_aot || enable_for_aot) &&
2881 (!(method->flags & METHOD_ATTRIBUTE_VIRTUAL) ||
2882 (MONO_METHOD_IS_FINAL (method) &&
2883 method->wrapper_type != MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK)) &&
2884 !(mono_class_is_marshalbyref (method->klass) && context_used)) {
2886 * the method is not virtual, we just need to ensure this is not null
2887 * and then we can call the method directly.
2889 #ifndef DISABLE_REMOTING
2890 if (mono_class_is_marshalbyref (method->klass) || method->klass == mono_defaults.object_class) {
2892 * The check above ensures method is not gshared, this is needed since
2893 * gshared methods can't have wrappers.
2895 method = call->method = mono_marshal_get_remoting_invoke_with_check (method);
2899 if (!method->string_ctor)
2900 MONO_EMIT_NEW_CHECK_THIS (cfg, this_reg);
2902 call->inst.opcode = callvirt_to_call (call->inst.opcode);
2903 } else if ((method->flags & METHOD_ATTRIBUTE_VIRTUAL) && MONO_METHOD_IS_FINAL (method)) {
2905 * the method is virtual, but we can statically dispatch since either
2906 * it's class or the method itself are sealed.
2907 * But first we need to ensure it's not a null reference.
2909 MONO_EMIT_NEW_CHECK_THIS (cfg, this_reg);
2911 call->inst.opcode = callvirt_to_call (call->inst.opcode);
2913 vtable_reg = alloc_preg (cfg);
2914 MONO_EMIT_NEW_LOAD_MEMBASE_FAULT (cfg, vtable_reg, this_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
2915 if (method->klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
2918 guint32 imt_slot = mono_method_get_imt_slot (method);
2919 emit_imt_argument (cfg, call, call->method, imt_arg);
2920 slot_reg = vtable_reg;
2921 offset = ((gint32)imt_slot - MONO_IMT_SIZE) * SIZEOF_VOID_P;
2923 if (slot_reg == -1) {
2924 slot_reg = alloc_preg (cfg);
2925 mini_emit_load_intf_reg_vtable (cfg, slot_reg, vtable_reg, method->klass);
2926 offset = mono_method_get_vtable_index (method) * SIZEOF_VOID_P;
2929 slot_reg = vtable_reg;
2930 offset = MONO_STRUCT_OFFSET (MonoVTable, vtable) +
2931 ((mono_method_get_vtable_index (method)) * (SIZEOF_VOID_P));
2933 g_assert (mono_method_signature (method)->generic_param_count);
2934 emit_imt_argument (cfg, call, call->method, imt_arg);
2938 call->inst.sreg1 = slot_reg;
2939 call->inst.inst_offset = offset;
2940 call->virtual = TRUE;
2944 MONO_ADD_INS (cfg->cbb, (MonoInst*)call);
2947 set_rgctx_arg (cfg, call, rgctx_reg, rgctx_arg);
2949 return (MonoInst*)call;
2953 mono_emit_method_call (MonoCompile *cfg, MonoMethod *method, MonoInst **args, MonoInst *this)
2955 return mono_emit_method_call_full (cfg, method, mono_method_signature (method), FALSE, args, this, NULL, NULL);
2959 mono_emit_native_call (MonoCompile *cfg, gconstpointer func, MonoMethodSignature *sig,
2966 call = mono_emit_call_args (cfg, sig, args, FALSE, FALSE, FALSE, FALSE, FALSE);
2969 MONO_ADD_INS (cfg->cbb, (MonoInst*)call);
2971 return (MonoInst*)call;
2975 mono_emit_jit_icall (MonoCompile *cfg, gconstpointer func, MonoInst **args)
2977 MonoJitICallInfo *info = mono_find_jit_icall_by_addr (func);
2981 return mono_emit_native_call (cfg, mono_icall_get_wrapper (info), info->sig, args);
2985 * mono_emit_abs_call:
2987 * Emit a call to the runtime function described by PATCH_TYPE and DATA.
2989 inline static MonoInst*
2990 mono_emit_abs_call (MonoCompile *cfg, MonoJumpInfoType patch_type, gconstpointer data,
2991 MonoMethodSignature *sig, MonoInst **args)
2993 MonoJumpInfo *ji = mono_patch_info_new (cfg->mempool, 0, patch_type, data);
2997 * We pass ji as the call address, the PATCH_INFO_ABS resolving code will
3000 if (cfg->abs_patches == NULL)
3001 cfg->abs_patches = g_hash_table_new (NULL, NULL);
3002 g_hash_table_insert (cfg->abs_patches, ji, ji);
3003 ins = mono_emit_native_call (cfg, ji, sig, args);
3004 ((MonoCallInst*)ins)->fptr_is_patch = TRUE;
3009 mono_emit_widen_call_res (MonoCompile *cfg, MonoInst *ins, MonoMethodSignature *fsig)
3011 if (!MONO_TYPE_IS_VOID (fsig->ret)) {
3012 if ((fsig->pinvoke || LLVM_ENABLED) && !fsig->ret->byref) {
3016 * Native code might return non register sized integers
3017 * without initializing the upper bits.
3019 switch (mono_type_to_load_membase (cfg, fsig->ret)) {
3020 case OP_LOADI1_MEMBASE:
3021 widen_op = OP_ICONV_TO_I1;
3023 case OP_LOADU1_MEMBASE:
3024 widen_op = OP_ICONV_TO_U1;
3026 case OP_LOADI2_MEMBASE:
3027 widen_op = OP_ICONV_TO_I2;
3029 case OP_LOADU2_MEMBASE:
3030 widen_op = OP_ICONV_TO_U2;
3036 if (widen_op != -1) {
3037 int dreg = alloc_preg (cfg);
3040 EMIT_NEW_UNALU (cfg, widen, widen_op, dreg, ins->dreg);
3041 widen->type = ins->type;
3051 get_memcpy_method (void)
3053 static MonoMethod *memcpy_method = NULL;
3054 if (!memcpy_method) {
3055 memcpy_method = mono_class_get_method_from_name (mono_defaults.string_class, "memcpy", 3);
3057 g_error ("Old corlib found. Install a new one");
3059 return memcpy_method;
3063 create_write_barrier_bitmap (MonoCompile *cfg, MonoClass *klass, unsigned *wb_bitmap, int offset)
3065 MonoClassField *field;
3066 gpointer iter = NULL;
3068 while ((field = mono_class_get_fields (klass, &iter))) {
3071 if (field->type->attrs & FIELD_ATTRIBUTE_STATIC)
3073 foffset = klass->valuetype ? field->offset - sizeof (MonoObject): field->offset;
3074 if (mini_type_is_reference (cfg, mono_field_get_type (field))) {
3075 g_assert ((foffset % SIZEOF_VOID_P) == 0);
3076 *wb_bitmap |= 1 << ((offset + foffset) / SIZEOF_VOID_P);
3078 MonoClass *field_class = mono_class_from_mono_type (field->type);
3079 if (field_class->has_references)
3080 create_write_barrier_bitmap (cfg, field_class, wb_bitmap, offset + foffset);
3086 emit_write_barrier (MonoCompile *cfg, MonoInst *ptr, MonoInst *value)
3088 int card_table_shift_bits;
3089 gpointer card_table_mask;
3091 MonoInst *dummy_use;
3092 int nursery_shift_bits;
3093 size_t nursery_size;
3094 gboolean has_card_table_wb = FALSE;
3096 if (!cfg->gen_write_barriers)
3099 card_table = mono_gc_get_card_table (&card_table_shift_bits, &card_table_mask);
3101 mono_gc_get_nursery (&nursery_shift_bits, &nursery_size);
3103 #ifdef MONO_ARCH_HAVE_CARD_TABLE_WBARRIER
3104 has_card_table_wb = TRUE;
3107 if (has_card_table_wb && !cfg->compile_aot && card_table && nursery_shift_bits > 0 && !COMPILE_LLVM (cfg)) {
3110 MONO_INST_NEW (cfg, wbarrier, OP_CARD_TABLE_WBARRIER);
3111 wbarrier->sreg1 = ptr->dreg;
3112 wbarrier->sreg2 = value->dreg;
3113 MONO_ADD_INS (cfg->cbb, wbarrier);
3114 } else if (card_table) {
3115 int offset_reg = alloc_preg (cfg);
3116 int card_reg = alloc_preg (cfg);
3119 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SHR_UN_IMM, offset_reg, ptr->dreg, card_table_shift_bits);
3120 if (card_table_mask)
3121 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_PAND_IMM, offset_reg, offset_reg, card_table_mask);
3123 /*We can't use PADD_IMM since the cardtable might end up in high addresses and amd64 doesn't support
3124 * IMM's larger than 32bits.
3126 if (cfg->compile_aot) {
3127 MONO_EMIT_NEW_AOTCONST (cfg, card_reg, NULL, MONO_PATCH_INFO_GC_CARD_TABLE_ADDR);
3129 MONO_INST_NEW (cfg, ins, OP_PCONST);
3130 ins->inst_p0 = card_table;
3131 ins->dreg = card_reg;
3132 MONO_ADD_INS (cfg->cbb, ins);
3135 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, offset_reg, offset_reg, card_reg);
3136 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREI1_MEMBASE_IMM, offset_reg, 0, 1);
3138 MonoMethod *write_barrier = mono_gc_get_write_barrier ();
3139 mono_emit_method_call (cfg, write_barrier, &ptr, NULL);
3142 EMIT_NEW_DUMMY_USE (cfg, dummy_use, value);
3146 mono_emit_wb_aware_memcpy (MonoCompile *cfg, MonoClass *klass, MonoInst *iargs[4], int size, int align)
3148 int dest_ptr_reg, tmp_reg, destreg, srcreg, offset;
3149 unsigned need_wb = 0;
3154 /*types with references can't have alignment smaller than sizeof(void*) */
3155 if (align < SIZEOF_VOID_P)
3158 /*This value cannot be bigger than 32 due to the way we calculate the required wb bitmap.*/
3159 if (size > 32 * SIZEOF_VOID_P)
3162 create_write_barrier_bitmap (cfg, klass, &need_wb, 0);
3164 /* We don't unroll more than 5 stores to avoid code bloat. */
3165 if (size > 5 * SIZEOF_VOID_P) {
3166 /*This is harmless and simplify mono_gc_wbarrier_value_copy_bitmap */
3167 size += (SIZEOF_VOID_P - 1);
3168 size &= ~(SIZEOF_VOID_P - 1);
3170 EMIT_NEW_ICONST (cfg, iargs [2], size);
3171 EMIT_NEW_ICONST (cfg, iargs [3], need_wb);
3172 mono_emit_jit_icall (cfg, mono_gc_wbarrier_value_copy_bitmap, iargs);
3176 destreg = iargs [0]->dreg;
3177 srcreg = iargs [1]->dreg;
3180 dest_ptr_reg = alloc_preg (cfg);
3181 tmp_reg = alloc_preg (cfg);
3184 EMIT_NEW_UNALU (cfg, iargs [0], OP_MOVE, dest_ptr_reg, destreg);
3186 while (size >= SIZEOF_VOID_P) {
3187 MonoInst *load_inst;
3188 MONO_INST_NEW (cfg, load_inst, OP_LOAD_MEMBASE);
3189 load_inst->dreg = tmp_reg;
3190 load_inst->inst_basereg = srcreg;
3191 load_inst->inst_offset = offset;
3192 MONO_ADD_INS (cfg->cbb, load_inst);
3194 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREP_MEMBASE_REG, dest_ptr_reg, 0, tmp_reg);
3197 emit_write_barrier (cfg, iargs [0], load_inst);
3199 offset += SIZEOF_VOID_P;
3200 size -= SIZEOF_VOID_P;
3203 /*tmp += sizeof (void*)*/
3204 if (size >= SIZEOF_VOID_P) {
3205 NEW_BIALU_IMM (cfg, iargs [0], OP_PADD_IMM, dest_ptr_reg, dest_ptr_reg, SIZEOF_VOID_P);
3206 MONO_ADD_INS (cfg->cbb, iargs [0]);
3210 /* Those cannot be references since size < sizeof (void*) */
3212 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI4_MEMBASE, tmp_reg, srcreg, offset);
3213 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI4_MEMBASE_REG, destreg, offset, tmp_reg);
3219 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI2_MEMBASE, tmp_reg, srcreg, offset);
3220 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI2_MEMBASE_REG, destreg, offset, tmp_reg);
3226 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI1_MEMBASE, tmp_reg, srcreg, offset);
3227 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI1_MEMBASE_REG, destreg, offset, tmp_reg);
3236 * Emit code to copy a valuetype of type @klass whose address is stored in
3237 * @src->dreg to memory whose address is stored at @dest->dreg.
3240 mini_emit_stobj (MonoCompile *cfg, MonoInst *dest, MonoInst *src, MonoClass *klass, gboolean native)
3242 MonoInst *iargs [4];
3243 int context_used, n;
3245 MonoMethod *memcpy_method;
3246 MonoInst *size_ins = NULL;
3247 MonoInst *memcpy_ins = NULL;
3251 * This check breaks with spilled vars... need to handle it during verification anyway.
3252 * g_assert (klass && klass == src->klass && klass == dest->klass);
3255 if (mini_is_gsharedvt_klass (cfg, klass)) {
3257 context_used = mini_class_check_context_used (cfg, klass);
3258 size_ins = emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_VALUE_SIZE);
3259 memcpy_ins = emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_MEMCPY);
3263 n = mono_class_native_size (klass, &align);
3265 n = mono_class_value_size (klass, &align);
3267 /* if native is true there should be no references in the struct */
3268 if (cfg->gen_write_barriers && (klass->has_references || size_ins) && !native) {
3269 /* Avoid barriers when storing to the stack */
3270 if (!((dest->opcode == OP_ADD_IMM && dest->sreg1 == cfg->frame_reg) ||
3271 (dest->opcode == OP_LDADDR))) {
3277 context_used = mini_class_check_context_used (cfg, klass);
3279 /* It's ok to intrinsify under gsharing since shared code types are layout stable. */
3280 if (!size_ins && (cfg->opt & MONO_OPT_INTRINS) && mono_emit_wb_aware_memcpy (cfg, klass, iargs, n, align)) {
3282 } else if (context_used) {
3283 iargs [2] = emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_KLASS);
3285 if (cfg->compile_aot) {
3286 EMIT_NEW_CLASSCONST (cfg, iargs [2], klass);
3288 EMIT_NEW_PCONST (cfg, iargs [2], klass);
3289 mono_class_compute_gc_descriptor (klass);
3294 mono_emit_jit_icall (cfg, mono_gsharedvt_value_copy, iargs);
3296 mono_emit_jit_icall (cfg, mono_value_copy, iargs);
3301 if (!size_ins && (cfg->opt & MONO_OPT_INTRINS) && n <= sizeof (gpointer) * 8) {
3302 /* FIXME: Optimize the case when src/dest is OP_LDADDR */
3303 mini_emit_memcpy (cfg, dest->dreg, 0, src->dreg, 0, n, align);
3308 iargs [2] = size_ins;
3310 EMIT_NEW_ICONST (cfg, iargs [2], n);
3312 memcpy_method = get_memcpy_method ();
3314 mono_emit_calli (cfg, mono_method_signature (memcpy_method), iargs, memcpy_ins, NULL, NULL);
3316 mono_emit_method_call (cfg, memcpy_method, iargs, NULL);
3321 get_memset_method (void)
3323 static MonoMethod *memset_method = NULL;
3324 if (!memset_method) {
3325 memset_method = mono_class_get_method_from_name (mono_defaults.string_class, "memset", 3);
3327 g_error ("Old corlib found. Install a new one");
3329 return memset_method;
3333 mini_emit_initobj (MonoCompile *cfg, MonoInst *dest, const guchar *ip, MonoClass *klass)
3335 MonoInst *iargs [3];
3336 int n, context_used;
3338 MonoMethod *memset_method;
3339 MonoInst *size_ins = NULL;
3340 MonoInst *bzero_ins = NULL;
3341 static MonoMethod *bzero_method;
3343 /* FIXME: Optimize this for the case when dest is an LDADDR */
3345 mono_class_init (klass);
3346 if (mini_is_gsharedvt_klass (cfg, klass)) {
3347 context_used = mini_class_check_context_used (cfg, klass);
3348 size_ins = emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_VALUE_SIZE);
3349 bzero_ins = emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_BZERO);
3351 bzero_method = mono_class_get_method_from_name (mono_defaults.string_class, "bzero_aligned_1", 2);
3352 g_assert (bzero_method);
3354 iargs [1] = size_ins;
3355 mono_emit_calli (cfg, mono_method_signature (bzero_method), iargs, bzero_ins, NULL, NULL);
3359 n = mono_class_value_size (klass, &align);
3361 if (n <= sizeof (gpointer) * 8) {
3362 mini_emit_memset (cfg, dest->dreg, 0, n, 0, align);
3365 memset_method = get_memset_method ();
3367 EMIT_NEW_ICONST (cfg, iargs [1], 0);
3368 EMIT_NEW_ICONST (cfg, iargs [2], n);
3369 mono_emit_method_call (cfg, memset_method, iargs, NULL);
3374 emit_get_rgctx (MonoCompile *cfg, MonoMethod *method, int context_used)
3376 MonoInst *this = NULL;
3378 g_assert (cfg->generic_sharing_context);
3380 if (!(method->flags & METHOD_ATTRIBUTE_STATIC) &&
3381 !(context_used & MONO_GENERIC_CONTEXT_USED_METHOD) &&
3382 !method->klass->valuetype)
3383 EMIT_NEW_ARGLOAD (cfg, this, 0);
3385 if (context_used & MONO_GENERIC_CONTEXT_USED_METHOD) {
3386 MonoInst *mrgctx_loc, *mrgctx_var;
3389 g_assert (method->is_inflated && mono_method_get_context (method)->method_inst);
3391 mrgctx_loc = mono_get_vtable_var (cfg);
3392 EMIT_NEW_TEMPLOAD (cfg, mrgctx_var, mrgctx_loc->inst_c0);
3395 } else if (method->flags & METHOD_ATTRIBUTE_STATIC || method->klass->valuetype) {
3396 MonoInst *vtable_loc, *vtable_var;
3400 vtable_loc = mono_get_vtable_var (cfg);
3401 EMIT_NEW_TEMPLOAD (cfg, vtable_var, vtable_loc->inst_c0);
3403 if (method->is_inflated && mono_method_get_context (method)->method_inst) {
3404 MonoInst *mrgctx_var = vtable_var;
3407 vtable_reg = alloc_preg (cfg);
3408 EMIT_NEW_LOAD_MEMBASE (cfg, vtable_var, OP_LOAD_MEMBASE, vtable_reg, mrgctx_var->dreg, MONO_STRUCT_OFFSET (MonoMethodRuntimeGenericContext, class_vtable));
3409 vtable_var->type = STACK_PTR;
3417 vtable_reg = alloc_preg (cfg);
3418 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, vtable_reg, this->dreg, MONO_STRUCT_OFFSET (MonoObject, vtable));
3423 static MonoJumpInfoRgctxEntry *
3424 mono_patch_info_rgctx_entry_new (MonoMemPool *mp, MonoMethod *method, gboolean in_mrgctx, MonoJumpInfoType patch_type, gconstpointer patch_data, MonoRgctxInfoType info_type)
3426 MonoJumpInfoRgctxEntry *res = mono_mempool_alloc0 (mp, sizeof (MonoJumpInfoRgctxEntry));
3427 res->method = method;
3428 res->in_mrgctx = in_mrgctx;
3429 res->data = mono_mempool_alloc0 (mp, sizeof (MonoJumpInfo));
3430 res->data->type = patch_type;
3431 res->data->data.target = patch_data;
3432 res->info_type = info_type;
3437 static inline MonoInst*
3438 emit_rgctx_fetch (MonoCompile *cfg, MonoInst *rgctx, MonoJumpInfoRgctxEntry *entry)
3440 return mono_emit_abs_call (cfg, MONO_PATCH_INFO_RGCTX_FETCH, entry, helper_sig_rgctx_lazy_fetch_trampoline, &rgctx);
3444 emit_get_rgctx_klass (MonoCompile *cfg, int context_used,
3445 MonoClass *klass, MonoRgctxInfoType rgctx_type)
3447 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);
3448 MonoInst *rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
3450 return emit_rgctx_fetch (cfg, rgctx, entry);
3454 emit_get_rgctx_sig (MonoCompile *cfg, int context_used,
3455 MonoMethodSignature *sig, MonoRgctxInfoType rgctx_type)
3457 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);
3458 MonoInst *rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
3460 return emit_rgctx_fetch (cfg, rgctx, entry);
3464 emit_get_rgctx_gsharedvt_call (MonoCompile *cfg, int context_used,
3465 MonoMethodSignature *sig, MonoMethod *cmethod, MonoRgctxInfoType rgctx_type)
3467 MonoJumpInfoGSharedVtCall *call_info;
3468 MonoJumpInfoRgctxEntry *entry;
3471 call_info = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoJumpInfoGSharedVtCall));
3472 call_info->sig = sig;
3473 call_info->method = cmethod;
3475 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);
3476 rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
3478 return emit_rgctx_fetch (cfg, rgctx, entry);
3483 emit_get_rgctx_gsharedvt_method (MonoCompile *cfg, int context_used,
3484 MonoMethod *cmethod, MonoGSharedVtMethodInfo *info)
3486 MonoJumpInfoRgctxEntry *entry;
3489 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);
3490 rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
3492 return emit_rgctx_fetch (cfg, rgctx, entry);
3496 * emit_get_rgctx_method:
3498 * Emit IR to load the property RGCTX_TYPE of CMETHOD. If context_used is 0, emit
3499 * normal constants, else emit a load from the rgctx.
3502 emit_get_rgctx_method (MonoCompile *cfg, int context_used,
3503 MonoMethod *cmethod, MonoRgctxInfoType rgctx_type)
3505 if (!context_used) {
3508 switch (rgctx_type) {
3509 case MONO_RGCTX_INFO_METHOD:
3510 EMIT_NEW_METHODCONST (cfg, ins, cmethod);
3512 case MONO_RGCTX_INFO_METHOD_RGCTX:
3513 EMIT_NEW_METHOD_RGCTX_CONST (cfg, ins, cmethod);
3516 g_assert_not_reached ();
3519 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);
3520 MonoInst *rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
3522 return emit_rgctx_fetch (cfg, rgctx, entry);
3527 emit_get_rgctx_field (MonoCompile *cfg, int context_used,
3528 MonoClassField *field, MonoRgctxInfoType rgctx_type)
3530 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);
3531 MonoInst *rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
3533 return emit_rgctx_fetch (cfg, rgctx, entry);
3537 get_gsharedvt_info_slot (MonoCompile *cfg, gpointer data, MonoRgctxInfoType rgctx_type)
3539 MonoGSharedVtMethodInfo *info = cfg->gsharedvt_info;
3540 MonoRuntimeGenericContextInfoTemplate *template;
3545 for (i = 0; i < info->num_entries; ++i) {
3546 MonoRuntimeGenericContextInfoTemplate *otemplate = &info->entries [i];
3548 if (otemplate->info_type == rgctx_type && otemplate->data == data && rgctx_type != MONO_RGCTX_INFO_LOCAL_OFFSET)
3552 if (info->num_entries == info->count_entries) {
3553 MonoRuntimeGenericContextInfoTemplate *new_entries;
3554 int new_count_entries = info->count_entries ? info->count_entries * 2 : 16;
3556 new_entries = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoRuntimeGenericContextInfoTemplate) * new_count_entries);
3558 memcpy (new_entries, info->entries, sizeof (MonoRuntimeGenericContextInfoTemplate) * info->count_entries);
3559 info->entries = new_entries;
3560 info->count_entries = new_count_entries;
3563 idx = info->num_entries;
3564 template = &info->entries [idx];
3565 template->info_type = rgctx_type;
3566 template->data = data;
3568 info->num_entries ++;
3574 * emit_get_gsharedvt_info:
3576 * This is similar to emit_get_rgctx_.., but loads the data from the gsharedvt info var instead of calling an rgctx fetch trampoline.
3579 emit_get_gsharedvt_info (MonoCompile *cfg, gpointer data, MonoRgctxInfoType rgctx_type)
3584 idx = get_gsharedvt_info_slot (cfg, data, rgctx_type);
3585 /* Load info->entries [idx] */
3586 dreg = alloc_preg (cfg);
3587 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, dreg, cfg->gsharedvt_info_var->dreg, MONO_STRUCT_OFFSET (MonoGSharedVtMethodRuntimeInfo, entries) + (idx * sizeof (gpointer)));
3593 emit_get_gsharedvt_info_klass (MonoCompile *cfg, MonoClass *klass, MonoRgctxInfoType rgctx_type)
3595 return emit_get_gsharedvt_info (cfg, &klass->byval_arg, rgctx_type);
3599 * On return the caller must check @klass for load errors.
3602 emit_generic_class_init (MonoCompile *cfg, MonoClass *klass)
3604 MonoInst *vtable_arg;
3608 context_used = mini_class_check_context_used (cfg, klass);
3611 vtable_arg = emit_get_rgctx_klass (cfg, context_used,
3612 klass, MONO_RGCTX_INFO_VTABLE);
3614 MonoVTable *vtable = mono_class_vtable (cfg->domain, klass);
3618 EMIT_NEW_VTABLECONST (cfg, vtable_arg, vtable);
3621 if (COMPILE_LLVM (cfg))
3622 call = (MonoCallInst*)mono_emit_abs_call (cfg, MONO_PATCH_INFO_GENERIC_CLASS_INIT, NULL, helper_sig_generic_class_init_trampoline_llvm, &vtable_arg);
3624 call = (MonoCallInst*)mono_emit_abs_call (cfg, MONO_PATCH_INFO_GENERIC_CLASS_INIT, NULL, helper_sig_generic_class_init_trampoline, &vtable_arg);
3625 #ifdef MONO_ARCH_VTABLE_REG
3626 mono_call_inst_add_outarg_reg (cfg, call, vtable_arg->dreg, MONO_ARCH_VTABLE_REG, FALSE);
3627 cfg->uses_vtable_reg = TRUE;
3634 emit_seq_point (MonoCompile *cfg, MonoMethod *method, guint8* ip, gboolean intr_loc, gboolean nonempty_stack)
3638 if (cfg->gen_seq_points && cfg->method == method) {
3639 NEW_SEQ_POINT (cfg, ins, ip - cfg->header->code, intr_loc);
3641 ins->flags |= MONO_INST_NONEMPTY_STACK;
3642 MONO_ADD_INS (cfg->cbb, ins);
3647 save_cast_details (MonoCompile *cfg, MonoClass *klass, int obj_reg, gboolean null_check, MonoBasicBlock **out_bblock)
3649 if (mini_get_debug_options ()->better_cast_details) {
3650 int vtable_reg = alloc_preg (cfg);
3651 int klass_reg = alloc_preg (cfg);
3652 MonoBasicBlock *is_null_bb = NULL;
3654 int to_klass_reg, context_used;
3657 NEW_BBLOCK (cfg, is_null_bb);
3659 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, obj_reg, 0);
3660 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, is_null_bb);
3663 tls_get = mono_get_jit_tls_intrinsic (cfg);
3665 fprintf (stderr, "error: --debug=casts not supported on this platform.\n.");
3669 MONO_ADD_INS (cfg->cbb, tls_get);
3670 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, vtable_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
3671 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
3673 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, tls_get->dreg, MONO_STRUCT_OFFSET (MonoJitTlsData, class_cast_from), klass_reg);
3675 context_used = mini_class_check_context_used (cfg, klass);
3677 MonoInst *class_ins;
3679 class_ins = emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_KLASS);
3680 to_klass_reg = class_ins->dreg;
3682 to_klass_reg = alloc_preg (cfg);
3683 MONO_EMIT_NEW_CLASSCONST (cfg, to_klass_reg, klass);
3685 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, tls_get->dreg, MONO_STRUCT_OFFSET (MonoJitTlsData, class_cast_to), to_klass_reg);
3688 MONO_START_BB (cfg, is_null_bb);
3690 *out_bblock = cfg->cbb;
3696 reset_cast_details (MonoCompile *cfg)
3698 /* Reset the variables holding the cast details */
3699 if (mini_get_debug_options ()->better_cast_details) {
3700 MonoInst *tls_get = mono_get_jit_tls_intrinsic (cfg);
3702 MONO_ADD_INS (cfg->cbb, tls_get);
3703 /* It is enough to reset the from field */
3704 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STORE_MEMBASE_IMM, tls_get->dreg, MONO_STRUCT_OFFSET (MonoJitTlsData, class_cast_from), 0);
3709 * On return the caller must check @array_class for load errors
3712 mini_emit_check_array_type (MonoCompile *cfg, MonoInst *obj, MonoClass *array_class)
3714 int vtable_reg = alloc_preg (cfg);
3717 context_used = mini_class_check_context_used (cfg, array_class);
3719 save_cast_details (cfg, array_class, obj->dreg, FALSE, NULL);
3721 MONO_EMIT_NEW_LOAD_MEMBASE_FAULT (cfg, vtable_reg, obj->dreg, MONO_STRUCT_OFFSET (MonoObject, vtable));
3723 if (cfg->opt & MONO_OPT_SHARED) {
3724 int class_reg = alloc_preg (cfg);
3725 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, class_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
3726 if (cfg->compile_aot) {
3727 int klass_reg = alloc_preg (cfg);
3728 MONO_EMIT_NEW_CLASSCONST (cfg, klass_reg, array_class);
3729 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, class_reg, klass_reg);
3731 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, class_reg, array_class);
3733 } else if (context_used) {
3734 MonoInst *vtable_ins;
3736 vtable_ins = emit_get_rgctx_klass (cfg, context_used, array_class, MONO_RGCTX_INFO_VTABLE);
3737 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, vtable_reg, vtable_ins->dreg);
3739 if (cfg->compile_aot) {
3743 if (!(vtable = mono_class_vtable (cfg->domain, array_class)))
3745 vt_reg = alloc_preg (cfg);
3746 MONO_EMIT_NEW_VTABLECONST (cfg, vt_reg, vtable);
3747 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, vtable_reg, vt_reg);
3750 if (!(vtable = mono_class_vtable (cfg->domain, array_class)))
3752 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, vtable_reg, vtable);
3756 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "ArrayTypeMismatchException");
3758 reset_cast_details (cfg);
3762 * Handles unbox of a Nullable<T>. If context_used is non zero, then shared
3763 * generic code is generated.
3766 handle_unbox_nullable (MonoCompile* cfg, MonoInst* val, MonoClass* klass, int context_used)
3768 MonoMethod* method = mono_class_get_method_from_name (klass, "Unbox", 1);
3771 MonoInst *rgctx, *addr;
3773 /* FIXME: What if the class is shared? We might not
3774 have to get the address of the method from the
3776 addr = emit_get_rgctx_method (cfg, context_used, method,
3777 MONO_RGCTX_INFO_GENERIC_METHOD_CODE);
3779 rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
3781 return mono_emit_calli (cfg, mono_method_signature (method), &val, addr, NULL, rgctx);
3783 gboolean pass_vtable, pass_mrgctx;
3784 MonoInst *rgctx_arg = NULL;
3786 check_method_sharing (cfg, method, &pass_vtable, &pass_mrgctx);
3787 g_assert (!pass_mrgctx);
3790 MonoVTable *vtable = mono_class_vtable (cfg->domain, method->klass);
3793 EMIT_NEW_VTABLECONST (cfg, rgctx_arg, vtable);
3796 return mono_emit_method_call_full (cfg, method, NULL, FALSE, &val, NULL, NULL, rgctx_arg);
3801 handle_unbox (MonoCompile *cfg, MonoClass *klass, MonoInst **sp, int context_used)
3805 int vtable_reg = alloc_dreg (cfg ,STACK_PTR);
3806 int klass_reg = alloc_dreg (cfg ,STACK_PTR);
3807 int eclass_reg = alloc_dreg (cfg ,STACK_PTR);
3808 int rank_reg = alloc_dreg (cfg ,STACK_I4);
3810 obj_reg = sp [0]->dreg;
3811 MONO_EMIT_NEW_LOAD_MEMBASE_FAULT (cfg, vtable_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
3812 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU1_MEMBASE, rank_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, rank));
3814 /* FIXME: generics */
3815 g_assert (klass->rank == 0);
3818 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, rank_reg, 0);
3819 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException");
3821 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
3822 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, eclass_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, element_class));
3825 MonoInst *element_class;
3827 /* This assertion is from the unboxcast insn */
3828 g_assert (klass->rank == 0);
3830 element_class = emit_get_rgctx_klass (cfg, context_used,
3831 klass->element_class, MONO_RGCTX_INFO_KLASS);
3833 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, eclass_reg, element_class->dreg);
3834 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException");
3836 save_cast_details (cfg, klass->element_class, obj_reg, FALSE, NULL);
3837 mini_emit_class_check (cfg, eclass_reg, klass->element_class);
3838 reset_cast_details (cfg);
3841 NEW_BIALU_IMM (cfg, add, OP_ADD_IMM, alloc_dreg (cfg, STACK_MP), obj_reg, sizeof (MonoObject));
3842 MONO_ADD_INS (cfg->cbb, add);
3843 add->type = STACK_MP;
3850 handle_unbox_gsharedvt (MonoCompile *cfg, MonoClass *klass, MonoInst *obj, MonoBasicBlock **out_cbb)
3852 MonoInst *addr, *klass_inst, *is_ref, *args[16];
3853 MonoBasicBlock *is_ref_bb, *is_nullable_bb, *end_bb;
3857 klass_inst = emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_KLASS);
3863 args [1] = klass_inst;
3866 obj = mono_emit_jit_icall (cfg, mono_object_castclass_unbox, args);
3868 NEW_BBLOCK (cfg, is_ref_bb);
3869 NEW_BBLOCK (cfg, is_nullable_bb);
3870 NEW_BBLOCK (cfg, end_bb);
3871 is_ref = emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_CLASS_BOX_TYPE);
3872 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, is_ref->dreg, 1);
3873 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, is_ref_bb);
3875 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, is_ref->dreg, 2);
3876 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, is_nullable_bb);
3878 /* This will contain either the address of the unboxed vtype, or an address of the temporary where the ref is stored */
3879 addr_reg = alloc_dreg (cfg, STACK_MP);
3883 NEW_BIALU_IMM (cfg, addr, OP_ADD_IMM, addr_reg, obj->dreg, sizeof (MonoObject));
3884 MONO_ADD_INS (cfg->cbb, addr);
3886 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
3889 MONO_START_BB (cfg, is_ref_bb);
3891 /* Save the ref to a temporary */
3892 dreg = alloc_ireg (cfg);
3893 EMIT_NEW_VARLOADA_VREG (cfg, addr, dreg, &klass->byval_arg);
3894 addr->dreg = addr_reg;
3895 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, addr->dreg, 0, obj->dreg);
3896 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
3899 MONO_START_BB (cfg, is_nullable_bb);
3902 MonoInst *addr = emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_NULLABLE_CLASS_UNBOX);
3903 MonoInst *unbox_call;
3904 MonoMethodSignature *unbox_sig;
3907 var = mono_compile_create_var (cfg, &klass->byval_arg, OP_LOCAL);
3909 unbox_sig = mono_mempool_alloc0 (cfg->mempool, MONO_SIZEOF_METHOD_SIGNATURE + (1 * sizeof (MonoType *)));
3910 unbox_sig->ret = &klass->byval_arg;
3911 unbox_sig->param_count = 1;
3912 unbox_sig->params [0] = &mono_defaults.object_class->byval_arg;
3913 unbox_call = mono_emit_calli (cfg, unbox_sig, &obj, addr, NULL, NULL);
3915 EMIT_NEW_VARLOADA_VREG (cfg, addr, unbox_call->dreg, &klass->byval_arg);
3916 addr->dreg = addr_reg;
3919 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
3922 MONO_START_BB (cfg, end_bb);
3925 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, addr_reg, 0);
3927 *out_cbb = cfg->cbb;
3933 * Returns NULL and set the cfg exception on error.
3936 handle_alloc (MonoCompile *cfg, MonoClass *klass, gboolean for_box, int context_used)
3938 MonoInst *iargs [2];
3944 MonoInst *iargs [2];
3946 MonoMethod *managed_alloc = mono_gc_get_managed_allocator (klass, for_box);
3948 if (cfg->opt & MONO_OPT_SHARED)
3949 rgctx_info = MONO_RGCTX_INFO_KLASS;
3951 rgctx_info = MONO_RGCTX_INFO_VTABLE;
3952 data = emit_get_rgctx_klass (cfg, context_used, klass, rgctx_info);
3954 if (cfg->opt & MONO_OPT_SHARED) {
3955 EMIT_NEW_DOMAINCONST (cfg, iargs [0]);
3957 alloc_ftn = mono_object_new;
3960 alloc_ftn = mono_object_new_specific;
3963 if (managed_alloc && !(cfg->opt & MONO_OPT_SHARED))
3964 return mono_emit_method_call (cfg, managed_alloc, iargs, NULL);
3966 return mono_emit_jit_icall (cfg, alloc_ftn, iargs);
3969 if (cfg->opt & MONO_OPT_SHARED) {
3970 EMIT_NEW_DOMAINCONST (cfg, iargs [0]);
3971 EMIT_NEW_CLASSCONST (cfg, iargs [1], klass);
3973 alloc_ftn = mono_object_new;
3974 } else if (cfg->compile_aot && cfg->cbb->out_of_line && klass->type_token && klass->image == mono_defaults.corlib && !klass->generic_class) {
3975 /* This happens often in argument checking code, eg. throw new FooException... */
3976 /* Avoid relocations and save some space by calling a helper function specialized to mscorlib */
3977 EMIT_NEW_ICONST (cfg, iargs [0], mono_metadata_token_index (klass->type_token));
3978 return mono_emit_jit_icall (cfg, mono_helper_newobj_mscorlib, iargs);
3980 MonoVTable *vtable = mono_class_vtable (cfg->domain, klass);
3981 MonoMethod *managed_alloc = NULL;
3985 mono_cfg_set_exception (cfg, MONO_EXCEPTION_TYPE_LOAD);
3986 cfg->exception_ptr = klass;
3990 #ifndef MONO_CROSS_COMPILE
3991 managed_alloc = mono_gc_get_managed_allocator (klass, for_box);
3994 if (managed_alloc) {
3995 EMIT_NEW_VTABLECONST (cfg, iargs [0], vtable);
3996 return mono_emit_method_call (cfg, managed_alloc, iargs, NULL);
3998 alloc_ftn = mono_class_get_allocation_ftn (vtable, for_box, &pass_lw);
4000 guint32 lw = vtable->klass->instance_size;
4001 lw = ((lw + (sizeof (gpointer) - 1)) & ~(sizeof (gpointer) - 1)) / sizeof (gpointer);
4002 EMIT_NEW_ICONST (cfg, iargs [0], lw);
4003 EMIT_NEW_VTABLECONST (cfg, iargs [1], vtable);
4006 EMIT_NEW_VTABLECONST (cfg, iargs [0], vtable);
4010 return mono_emit_jit_icall (cfg, alloc_ftn, iargs);
4014 * Returns NULL and set the cfg exception on error.
4017 handle_box (MonoCompile *cfg, MonoInst *val, MonoClass *klass, int context_used, MonoBasicBlock **out_cbb)
4019 MonoInst *alloc, *ins;
4021 *out_cbb = cfg->cbb;
4023 if (mono_class_is_nullable (klass)) {
4024 MonoMethod* method = mono_class_get_method_from_name (klass, "Box", 1);
4027 /* FIXME: What if the class is shared? We might not
4028 have to get the method address from the RGCTX. */
4029 MonoInst *addr = emit_get_rgctx_method (cfg, context_used, method,
4030 MONO_RGCTX_INFO_GENERIC_METHOD_CODE);
4031 MonoInst *rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
4033 return mono_emit_calli (cfg, mono_method_signature (method), &val, addr, NULL, rgctx);
4035 gboolean pass_vtable, pass_mrgctx;
4036 MonoInst *rgctx_arg = NULL;
4038 check_method_sharing (cfg, method, &pass_vtable, &pass_mrgctx);
4039 g_assert (!pass_mrgctx);
4042 MonoVTable *vtable = mono_class_vtable (cfg->domain, method->klass);
4045 EMIT_NEW_VTABLECONST (cfg, rgctx_arg, vtable);
4048 return mono_emit_method_call_full (cfg, method, NULL, FALSE, &val, NULL, NULL, rgctx_arg);
4052 if (mini_is_gsharedvt_klass (cfg, klass)) {
4053 MonoBasicBlock *is_ref_bb, *is_nullable_bb, *end_bb;
4054 MonoInst *res, *is_ref, *src_var, *addr;
4057 dreg = alloc_ireg (cfg);
4059 NEW_BBLOCK (cfg, is_ref_bb);
4060 NEW_BBLOCK (cfg, is_nullable_bb);
4061 NEW_BBLOCK (cfg, end_bb);
4062 is_ref = emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_CLASS_BOX_TYPE);
4063 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, is_ref->dreg, 1);
4064 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, is_ref_bb);
4066 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, is_ref->dreg, 2);
4067 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, is_nullable_bb);
4070 alloc = handle_alloc (cfg, klass, TRUE, context_used);
4073 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, alloc->dreg, sizeof (MonoObject), val->dreg);
4074 ins->opcode = OP_STOREV_MEMBASE;
4076 EMIT_NEW_UNALU (cfg, res, OP_MOVE, dreg, alloc->dreg);
4077 res->type = STACK_OBJ;
4079 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4082 MONO_START_BB (cfg, is_ref_bb);
4083 addr_reg = alloc_ireg (cfg);
4085 /* val is a vtype, so has to load the value manually */
4086 src_var = get_vreg_to_inst (cfg, val->dreg);
4088 src_var = mono_compile_create_var_for_vreg (cfg, &klass->byval_arg, OP_LOCAL, val->dreg);
4089 EMIT_NEW_VARLOADA (cfg, addr, src_var, src_var->inst_vtype);
4090 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, dreg, addr->dreg, 0);
4091 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4094 MONO_START_BB (cfg, is_nullable_bb);
4097 MonoInst *addr = emit_get_gsharedvt_info_klass (cfg, klass,
4098 MONO_RGCTX_INFO_NULLABLE_CLASS_BOX);
4100 MonoMethodSignature *box_sig;
4103 * klass is Nullable<T>, need to call Nullable<T>.Box () using a gsharedvt signature, but we cannot
4104 * construct that method at JIT time, so have to do things by hand.
4106 box_sig = mono_mempool_alloc0 (cfg->mempool, MONO_SIZEOF_METHOD_SIGNATURE + (1 * sizeof (MonoType *)));
4107 box_sig->ret = &mono_defaults.object_class->byval_arg;
4108 box_sig->param_count = 1;
4109 box_sig->params [0] = &klass->byval_arg;
4110 box_call = mono_emit_calli (cfg, box_sig, &val, addr, NULL, NULL);
4111 EMIT_NEW_UNALU (cfg, res, OP_MOVE, dreg, box_call->dreg);
4112 res->type = STACK_OBJ;
4116 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4118 MONO_START_BB (cfg, end_bb);
4120 *out_cbb = cfg->cbb;
4124 alloc = handle_alloc (cfg, klass, TRUE, context_used);
4128 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, alloc->dreg, sizeof (MonoObject), val->dreg);
4135 mini_class_has_reference_variant_generic_argument (MonoCompile *cfg, MonoClass *klass, int context_used)
4138 MonoGenericContainer *container;
4139 MonoGenericInst *ginst;
4141 if (klass->generic_class) {
4142 container = klass->generic_class->container_class->generic_container;
4143 ginst = klass->generic_class->context.class_inst;
4144 } else if (klass->generic_container && context_used) {
4145 container = klass->generic_container;
4146 ginst = container->context.class_inst;
4151 for (i = 0; i < container->type_argc; ++i) {
4153 if (!(mono_generic_container_get_param_info (container, i)->flags & (MONO_GEN_PARAM_VARIANT|MONO_GEN_PARAM_COVARIANT)))
4155 type = ginst->type_argv [i];
4156 if (mini_type_is_reference (cfg, type))
4162 #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)
4165 emit_castclass_with_cache (MonoCompile *cfg, MonoClass *klass, MonoInst **args, MonoBasicBlock **out_bblock)
4167 MonoMethod *mono_castclass;
4170 mono_castclass = mono_marshal_get_castclass_with_cache ();
4172 save_cast_details (cfg, klass, args [0]->dreg, TRUE, out_bblock);
4173 res = mono_emit_method_call (cfg, mono_castclass, args, NULL);
4174 reset_cast_details (cfg);
4175 *out_bblock = cfg->cbb;
4181 emit_castclass_with_cache_nonshared (MonoCompile *cfg, MonoInst *obj, MonoClass *klass, MonoBasicBlock **out_bblock)
4190 EMIT_NEW_CLASSCONST (cfg, args [1], klass);
4193 if (cfg->compile_aot) {
4194 /* Each CASTCLASS_CACHE patch needs a unique index which identifies the call site */
4195 cfg->castclass_cache_index ++;
4196 idx = (cfg->method_index << 16) | cfg->castclass_cache_index;
4197 EMIT_NEW_AOTCONST (cfg, args [2], MONO_PATCH_INFO_CASTCLASS_CACHE, GINT_TO_POINTER (idx));
4199 EMIT_NEW_PCONST (cfg, args [2], mono_domain_alloc0 (cfg->domain, sizeof (gpointer)));
4202 /*The wrapper doesn't inline well so the bloat of inlining doesn't pay off.*/
4204 return emit_castclass_with_cache (cfg, klass, args, out_bblock);
4208 * Returns NULL and set the cfg exception on error.
4211 handle_castclass (MonoCompile *cfg, MonoClass *klass, MonoInst *src, guint8 *ip, MonoBasicBlock **out_bb, int *inline_costs)
4213 MonoBasicBlock *is_null_bb;
4214 int obj_reg = src->dreg;
4215 int vtable_reg = alloc_preg (cfg);
4217 MonoInst *klass_inst = NULL, *res;
4218 MonoBasicBlock *bblock;
4222 context_used = mini_class_check_context_used (cfg, klass);
4224 if (!context_used && mini_class_has_reference_variant_generic_argument (cfg, klass, context_used)) {
4225 res = emit_castclass_with_cache_nonshared (cfg, src, klass, &bblock);
4226 (*inline_costs) += 2;
4229 } else if (!context_used && (mono_class_is_marshalbyref (klass) || klass->flags & TYPE_ATTRIBUTE_INTERFACE)) {
4230 MonoMethod *mono_castclass;
4231 MonoInst *iargs [1];
4234 mono_castclass = mono_marshal_get_castclass (klass);
4237 save_cast_details (cfg, klass, src->dreg, TRUE, &bblock);
4238 costs = inline_method (cfg, mono_castclass, mono_method_signature (mono_castclass),
4239 iargs, ip, cfg->real_offset, TRUE, &bblock);
4240 reset_cast_details (cfg);
4241 CHECK_CFG_EXCEPTION;
4242 g_assert (costs > 0);
4244 cfg->real_offset += 5;
4246 (*inline_costs) += costs;
4255 if(mini_class_has_reference_variant_generic_argument (cfg, klass, context_used) || is_complex_isinst (klass)) {
4256 MonoInst *cache_ins;
4258 cache_ins = emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_CAST_CACHE);
4263 /* klass - it's the second element of the cache entry*/
4264 EMIT_NEW_LOAD_MEMBASE (cfg, args [1], OP_LOAD_MEMBASE, alloc_preg (cfg), cache_ins->dreg, sizeof (gpointer));
4267 args [2] = cache_ins;
4269 return emit_castclass_with_cache (cfg, klass, args, out_bb);
4272 klass_inst = emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_KLASS);
4275 NEW_BBLOCK (cfg, is_null_bb);
4277 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, obj_reg, 0);
4278 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, is_null_bb);
4280 save_cast_details (cfg, klass, obj_reg, FALSE, NULL);
4282 if (klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
4283 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, vtable_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
4284 mini_emit_iface_cast (cfg, vtable_reg, klass, NULL, NULL);
4286 int klass_reg = alloc_preg (cfg);
4288 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, vtable_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
4290 if (!klass->rank && !cfg->compile_aot && !(cfg->opt & MONO_OPT_SHARED) && (klass->flags & TYPE_ATTRIBUTE_SEALED)) {
4291 /* the remoting code is broken, access the class for now */
4292 if (0) { /*FIXME what exactly is broken? This change refers to r39380 from 2005 and mention some remoting fixes were due.*/
4293 MonoVTable *vt = mono_class_vtable (cfg->domain, klass);
4295 mono_cfg_set_exception (cfg, MONO_EXCEPTION_TYPE_LOAD);
4296 cfg->exception_ptr = klass;
4299 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, vtable_reg, vt);
4301 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4302 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, klass_reg, klass);
4304 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException");
4306 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4307 mini_emit_castclass_inst (cfg, obj_reg, klass_reg, klass, klass_inst, is_null_bb);
4311 MONO_START_BB (cfg, is_null_bb);
4313 reset_cast_details (cfg);
4324 * Returns NULL and set the cfg exception on error.
4327 handle_isinst (MonoCompile *cfg, MonoClass *klass, MonoInst *src, int context_used)
4330 MonoBasicBlock *is_null_bb, *false_bb, *end_bb;
4331 int obj_reg = src->dreg;
4332 int vtable_reg = alloc_preg (cfg);
4333 int res_reg = alloc_ireg_ref (cfg);
4334 MonoInst *klass_inst = NULL;
4339 if(mini_class_has_reference_variant_generic_argument (cfg, klass, context_used) || is_complex_isinst (klass)) {
4340 MonoMethod *mono_isinst = mono_marshal_get_isinst_with_cache ();
4341 MonoInst *cache_ins;
4343 cache_ins = emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_CAST_CACHE);
4348 /* klass - it's the second element of the cache entry*/
4349 EMIT_NEW_LOAD_MEMBASE (cfg, args [1], OP_LOAD_MEMBASE, alloc_preg (cfg), cache_ins->dreg, sizeof (gpointer));
4352 args [2] = cache_ins;
4354 return mono_emit_method_call (cfg, mono_isinst, args, NULL);
4357 klass_inst = emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_KLASS);
4360 NEW_BBLOCK (cfg, is_null_bb);
4361 NEW_BBLOCK (cfg, false_bb);
4362 NEW_BBLOCK (cfg, end_bb);
4364 /* Do the assignment at the beginning, so the other assignment can be if converted */
4365 EMIT_NEW_UNALU (cfg, ins, OP_MOVE, res_reg, obj_reg);
4366 ins->type = STACK_OBJ;
4369 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, obj_reg, 0);
4370 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, is_null_bb);
4372 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, vtable_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
4374 if (klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
4375 g_assert (!context_used);
4376 /* the is_null_bb target simply copies the input register to the output */
4377 mini_emit_iface_cast (cfg, vtable_reg, klass, false_bb, is_null_bb);
4379 int klass_reg = alloc_preg (cfg);
4382 int rank_reg = alloc_preg (cfg);
4383 int eclass_reg = alloc_preg (cfg);
4385 g_assert (!context_used);
4386 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU1_MEMBASE, rank_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, rank));
4387 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, rank_reg, klass->rank);
4388 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, false_bb);
4389 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4390 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, eclass_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, cast_class));
4391 if (klass->cast_class == mono_defaults.object_class) {
4392 int parent_reg = alloc_preg (cfg);
4393 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, parent_reg, eclass_reg, MONO_STRUCT_OFFSET (MonoClass, parent));
4394 mini_emit_class_check_branch (cfg, parent_reg, mono_defaults.enum_class->parent, OP_PBNE_UN, is_null_bb);
4395 mini_emit_class_check_branch (cfg, eclass_reg, mono_defaults.enum_class, OP_PBEQ, is_null_bb);
4396 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, false_bb);
4397 } else if (klass->cast_class == mono_defaults.enum_class->parent) {
4398 mini_emit_class_check_branch (cfg, eclass_reg, mono_defaults.enum_class->parent, OP_PBEQ, is_null_bb);
4399 mini_emit_class_check_branch (cfg, eclass_reg, mono_defaults.enum_class, OP_PBEQ, is_null_bb);
4400 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, false_bb);
4401 } else if (klass->cast_class == mono_defaults.enum_class) {
4402 mini_emit_class_check_branch (cfg, eclass_reg, mono_defaults.enum_class, OP_PBEQ, is_null_bb);
4403 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, false_bb);
4404 } else if (klass->cast_class->flags & TYPE_ATTRIBUTE_INTERFACE) {
4405 mini_emit_iface_class_cast (cfg, eclass_reg, klass->cast_class, false_bb, is_null_bb);
4407 if ((klass->rank == 1) && (klass->byval_arg.type == MONO_TYPE_SZARRAY)) {
4408 /* Check that the object is a vector too */
4409 int bounds_reg = alloc_preg (cfg);
4410 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, bounds_reg, obj_reg, MONO_STRUCT_OFFSET (MonoArray, bounds));
4411 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, bounds_reg, 0);
4412 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, false_bb);
4415 /* the is_null_bb target simply copies the input register to the output */
4416 mini_emit_isninst_cast (cfg, eclass_reg, klass->cast_class, false_bb, is_null_bb);
4418 } else if (mono_class_is_nullable (klass)) {
4419 g_assert (!context_used);
4420 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4421 /* the is_null_bb target simply copies the input register to the output */
4422 mini_emit_isninst_cast (cfg, klass_reg, klass->cast_class, false_bb, is_null_bb);
4424 if (!cfg->compile_aot && !(cfg->opt & MONO_OPT_SHARED) && (klass->flags & TYPE_ATTRIBUTE_SEALED)) {
4425 g_assert (!context_used);
4426 /* the remoting code is broken, access the class for now */
4427 if (0) {/*FIXME what exactly is broken? This change refers to r39380 from 2005 and mention some remoting fixes were due.*/
4428 MonoVTable *vt = mono_class_vtable (cfg->domain, klass);
4430 mono_cfg_set_exception (cfg, MONO_EXCEPTION_TYPE_LOAD);
4431 cfg->exception_ptr = klass;
4434 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, vtable_reg, vt);
4436 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4437 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, klass_reg, klass);
4439 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, false_bb);
4440 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, is_null_bb);
4442 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4443 /* the is_null_bb target simply copies the input register to the output */
4444 mini_emit_isninst_cast_inst (cfg, klass_reg, klass, klass_inst, false_bb, is_null_bb);
4449 MONO_START_BB (cfg, false_bb);
4451 MONO_EMIT_NEW_PCONST (cfg, res_reg, 0);
4452 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4454 MONO_START_BB (cfg, is_null_bb);
4456 MONO_START_BB (cfg, end_bb);
4462 handle_cisinst (MonoCompile *cfg, MonoClass *klass, MonoInst *src)
4464 /* This opcode takes as input an object reference and a class, and returns:
4465 0) if the object is an instance of the class,
4466 1) if the object is not instance of the class,
4467 2) if the object is a proxy whose type cannot be determined */
4470 #ifndef DISABLE_REMOTING
4471 MonoBasicBlock *true_bb, *false_bb, *false2_bb, *end_bb, *no_proxy_bb, *interface_fail_bb;
4473 MonoBasicBlock *true_bb, *false_bb, *end_bb;
4475 int obj_reg = src->dreg;
4476 int dreg = alloc_ireg (cfg);
4478 #ifndef DISABLE_REMOTING
4479 int klass_reg = alloc_preg (cfg);
4482 NEW_BBLOCK (cfg, true_bb);
4483 NEW_BBLOCK (cfg, false_bb);
4484 NEW_BBLOCK (cfg, end_bb);
4485 #ifndef DISABLE_REMOTING
4486 NEW_BBLOCK (cfg, false2_bb);
4487 NEW_BBLOCK (cfg, no_proxy_bb);
4490 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, obj_reg, 0);
4491 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, false_bb);
4493 if (klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
4494 #ifndef DISABLE_REMOTING
4495 NEW_BBLOCK (cfg, interface_fail_bb);
4498 tmp_reg = alloc_preg (cfg);
4499 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
4500 #ifndef DISABLE_REMOTING
4501 mini_emit_iface_cast (cfg, tmp_reg, klass, interface_fail_bb, true_bb);
4502 MONO_START_BB (cfg, interface_fail_bb);
4503 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, tmp_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4505 mini_emit_class_check_branch (cfg, klass_reg, mono_defaults.transparent_proxy_class, OP_PBNE_UN, false_bb);
4507 tmp_reg = alloc_preg (cfg);
4508 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoTransparentProxy, custom_type_info));
4509 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, tmp_reg, 0);
4510 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, false2_bb);
4512 mini_emit_iface_cast (cfg, tmp_reg, klass, false_bb, true_bb);
4515 #ifndef DISABLE_REMOTING
4516 tmp_reg = alloc_preg (cfg);
4517 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
4518 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, tmp_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4520 mini_emit_class_check_branch (cfg, klass_reg, mono_defaults.transparent_proxy_class, OP_PBNE_UN, no_proxy_bb);
4521 tmp_reg = alloc_preg (cfg);
4522 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoTransparentProxy, remote_class));
4523 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, tmp_reg, MONO_STRUCT_OFFSET (MonoRemoteClass, proxy_class));
4525 tmp_reg = alloc_preg (cfg);
4526 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoTransparentProxy, custom_type_info));
4527 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, tmp_reg, 0);
4528 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, no_proxy_bb);
4530 mini_emit_isninst_cast (cfg, klass_reg, klass, false2_bb, true_bb);
4531 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, false2_bb);
4533 MONO_START_BB (cfg, no_proxy_bb);
4535 mini_emit_isninst_cast (cfg, klass_reg, klass, false_bb, true_bb);
4537 g_error ("transparent proxy support is disabled while trying to JIT code that uses it");
4541 MONO_START_BB (cfg, false_bb);
4543 MONO_EMIT_NEW_ICONST (cfg, dreg, 1);
4544 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4546 #ifndef DISABLE_REMOTING
4547 MONO_START_BB (cfg, false2_bb);
4549 MONO_EMIT_NEW_ICONST (cfg, dreg, 2);
4550 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4553 MONO_START_BB (cfg, true_bb);
4555 MONO_EMIT_NEW_ICONST (cfg, dreg, 0);
4557 MONO_START_BB (cfg, end_bb);
4560 MONO_INST_NEW (cfg, ins, OP_ICONST);
4562 ins->type = STACK_I4;
4568 handle_ccastclass (MonoCompile *cfg, MonoClass *klass, MonoInst *src)
4570 /* This opcode takes as input an object reference and a class, and returns:
4571 0) if the object is an instance of the class,
4572 1) if the object is a proxy whose type cannot be determined
4573 an InvalidCastException exception is thrown otherwhise*/
4576 #ifndef DISABLE_REMOTING
4577 MonoBasicBlock *end_bb, *ok_result_bb, *no_proxy_bb, *interface_fail_bb, *fail_1_bb;
4579 MonoBasicBlock *ok_result_bb;
4581 int obj_reg = src->dreg;
4582 int dreg = alloc_ireg (cfg);
4583 int tmp_reg = alloc_preg (cfg);
4585 #ifndef DISABLE_REMOTING
4586 int klass_reg = alloc_preg (cfg);
4587 NEW_BBLOCK (cfg, end_bb);
4590 NEW_BBLOCK (cfg, ok_result_bb);
4592 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, obj_reg, 0);
4593 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, ok_result_bb);
4595 save_cast_details (cfg, klass, obj_reg, FALSE, NULL);
4597 if (klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
4598 #ifndef DISABLE_REMOTING
4599 NEW_BBLOCK (cfg, interface_fail_bb);
4601 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
4602 mini_emit_iface_cast (cfg, tmp_reg, klass, interface_fail_bb, ok_result_bb);
4603 MONO_START_BB (cfg, interface_fail_bb);
4604 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, tmp_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4606 mini_emit_class_check (cfg, klass_reg, mono_defaults.transparent_proxy_class);
4608 tmp_reg = alloc_preg (cfg);
4609 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoTransparentProxy, custom_type_info));
4610 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, tmp_reg, 0);
4611 MONO_EMIT_NEW_COND_EXC (cfg, EQ, "InvalidCastException");
4613 MONO_EMIT_NEW_ICONST (cfg, dreg, 1);
4614 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4616 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
4617 mini_emit_iface_cast (cfg, tmp_reg, klass, NULL, NULL);
4618 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, ok_result_bb);
4621 #ifndef DISABLE_REMOTING
4622 NEW_BBLOCK (cfg, no_proxy_bb);
4624 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
4625 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, tmp_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4626 mini_emit_class_check_branch (cfg, klass_reg, mono_defaults.transparent_proxy_class, OP_PBNE_UN, no_proxy_bb);
4628 tmp_reg = alloc_preg (cfg);
4629 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoTransparentProxy, remote_class));
4630 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, tmp_reg, MONO_STRUCT_OFFSET (MonoRemoteClass, proxy_class));
4632 tmp_reg = alloc_preg (cfg);
4633 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoTransparentProxy, custom_type_info));
4634 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, tmp_reg, 0);
4635 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, no_proxy_bb);
4637 NEW_BBLOCK (cfg, fail_1_bb);
4639 mini_emit_isninst_cast (cfg, klass_reg, klass, fail_1_bb, ok_result_bb);
4641 MONO_START_BB (cfg, fail_1_bb);
4643 MONO_EMIT_NEW_ICONST (cfg, dreg, 1);
4644 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4646 MONO_START_BB (cfg, no_proxy_bb);
4648 mini_emit_castclass (cfg, obj_reg, klass_reg, klass, ok_result_bb);
4650 g_error ("Transparent proxy support is disabled while trying to JIT code that uses it");
4654 MONO_START_BB (cfg, ok_result_bb);
4656 MONO_EMIT_NEW_ICONST (cfg, dreg, 0);
4658 #ifndef DISABLE_REMOTING
4659 MONO_START_BB (cfg, end_bb);
4663 MONO_INST_NEW (cfg, ins, OP_ICONST);
4665 ins->type = STACK_I4;
4670 static G_GNUC_UNUSED MonoInst*
4671 handle_enum_has_flag (MonoCompile *cfg, MonoClass *klass, MonoInst *enum_this, MonoInst *enum_flag)
4673 MonoType *enum_type = mono_type_get_underlying_type (&klass->byval_arg);
4674 guint32 load_opc = mono_type_to_load_membase (cfg, enum_type);
4675 gboolean is_i4 = TRUE;
4677 switch (enum_type->type) {
4680 #if SIZEOF_REGISTER == 8
4689 MonoInst *load, *and, *cmp, *ceq;
4690 int enum_reg = is_i4 ? alloc_ireg (cfg) : alloc_lreg (cfg);
4691 int and_reg = is_i4 ? alloc_ireg (cfg) : alloc_lreg (cfg);
4692 int dest_reg = alloc_ireg (cfg);
4694 EMIT_NEW_LOAD_MEMBASE (cfg, load, load_opc, enum_reg, enum_this->dreg, 0);
4695 EMIT_NEW_BIALU (cfg, and, is_i4 ? OP_IAND : OP_LAND, and_reg, enum_reg, enum_flag->dreg);
4696 EMIT_NEW_BIALU (cfg, cmp, is_i4 ? OP_ICOMPARE : OP_LCOMPARE, -1, and_reg, enum_flag->dreg);
4697 EMIT_NEW_UNALU (cfg, ceq, is_i4 ? OP_ICEQ : OP_LCEQ, dest_reg, -1);
4699 ceq->type = STACK_I4;
4702 load = mono_decompose_opcode (cfg, load);
4703 and = mono_decompose_opcode (cfg, and);
4704 cmp = mono_decompose_opcode (cfg, cmp);
4705 ceq = mono_decompose_opcode (cfg, ceq);
4713 * Returns NULL and set the cfg exception on error.
4715 static G_GNUC_UNUSED MonoInst*
4716 handle_delegate_ctor (MonoCompile *cfg, MonoClass *klass, MonoInst *target, MonoMethod *method, int context_used, gboolean virtual)
4720 gpointer trampoline;
4721 MonoInst *obj, *method_ins, *tramp_ins;
4725 // FIXME reenable optimisation for virtual case
4730 MonoMethod *invoke = mono_get_delegate_invoke (klass);
4733 if (!mono_get_delegate_virtual_invoke_impl (mono_method_signature (invoke), context_used ? NULL : method))
4737 obj = handle_alloc (cfg, klass, FALSE, 0);
4741 /* Inline the contents of mono_delegate_ctor */
4743 /* Set target field */
4744 /* Optimize away setting of NULL target */
4745 if (!(target->opcode == OP_PCONST && target->inst_p0 == 0)) {
4746 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, target), target->dreg);
4747 if (cfg->gen_write_barriers) {
4748 dreg = alloc_preg (cfg);
4749 EMIT_NEW_BIALU_IMM (cfg, ptr, OP_PADD_IMM, dreg, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, target));
4750 emit_write_barrier (cfg, ptr, target);
4754 /* Set method field */
4755 method_ins = emit_get_rgctx_method (cfg, context_used, method, MONO_RGCTX_INFO_METHOD);
4756 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, method), method_ins->dreg);
4759 * To avoid looking up the compiled code belonging to the target method
4760 * in mono_delegate_trampoline (), we allocate a per-domain memory slot to
4761 * store it, and we fill it after the method has been compiled.
4763 if (!method->dynamic && !(cfg->opt & MONO_OPT_SHARED)) {
4764 MonoInst *code_slot_ins;
4767 code_slot_ins = emit_get_rgctx_method (cfg, context_used, method, MONO_RGCTX_INFO_METHOD_DELEGATE_CODE);
4769 domain = mono_domain_get ();
4770 mono_domain_lock (domain);
4771 if (!domain_jit_info (domain)->method_code_hash)
4772 domain_jit_info (domain)->method_code_hash = g_hash_table_new (NULL, NULL);
4773 code_slot = g_hash_table_lookup (domain_jit_info (domain)->method_code_hash, method);
4775 code_slot = mono_domain_alloc0 (domain, sizeof (gpointer));
4776 g_hash_table_insert (domain_jit_info (domain)->method_code_hash, method, code_slot);
4778 mono_domain_unlock (domain);
4780 if (cfg->compile_aot)
4781 EMIT_NEW_AOTCONST (cfg, code_slot_ins, MONO_PATCH_INFO_METHOD_CODE_SLOT, method);
4783 EMIT_NEW_PCONST (cfg, code_slot_ins, code_slot);
4785 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, method_code), code_slot_ins->dreg);
4788 if (cfg->compile_aot) {
4789 MonoDelegateClassMethodPair *del_tramp;
4791 del_tramp = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoDelegateClassMethodPair));
4792 del_tramp->klass = klass;
4793 del_tramp->method = context_used ? NULL : method;
4794 del_tramp->virtual = virtual;
4795 EMIT_NEW_AOTCONST (cfg, tramp_ins, MONO_PATCH_INFO_DELEGATE_TRAMPOLINE, del_tramp);
4798 trampoline = mono_create_delegate_virtual_trampoline (cfg->domain, klass, context_used ? NULL : method);
4800 trampoline = mono_create_delegate_trampoline_info (cfg->domain, klass, context_used ? NULL : method);
4801 EMIT_NEW_PCONST (cfg, tramp_ins, trampoline);
4804 /* Set invoke_impl field */
4806 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, invoke_impl), tramp_ins->dreg);
4808 dreg = alloc_preg (cfg);
4809 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, dreg, tramp_ins->dreg, MONO_STRUCT_OFFSET (MonoDelegateTrampInfo, invoke_impl));
4810 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, invoke_impl), dreg);
4812 dreg = alloc_preg (cfg);
4813 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, dreg, tramp_ins->dreg, MONO_STRUCT_OFFSET (MonoDelegateTrampInfo, method_ptr));
4814 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, method_ptr), dreg);
4817 /* All the checks which are in mono_delegate_ctor () are done by the delegate trampoline */
4823 handle_array_new (MonoCompile *cfg, int rank, MonoInst **sp, unsigned char *ip)
4825 MonoJitICallInfo *info;
4827 /* Need to register the icall so it gets an icall wrapper */
4828 info = mono_get_array_new_va_icall (rank);
4830 cfg->flags |= MONO_CFG_HAS_VARARGS;
4832 /* mono_array_new_va () needs a vararg calling convention */
4833 cfg->disable_llvm = TRUE;
4835 /* FIXME: This uses info->sig, but it should use the signature of the wrapper */
4836 return mono_emit_native_call (cfg, mono_icall_get_wrapper (info), info->sig, sp);
4840 * handle_constrained_gsharedvt_call:
4842 * Handle constrained calls where the receiver is a gsharedvt type.
4843 * Return the instruction representing the call. Set the cfg exception on failure.
4846 handle_constrained_gsharedvt_call (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **sp, MonoClass *constrained_call,
4847 gboolean *ref_emit_widen, MonoBasicBlock **ref_bblock)
4849 MonoInst *ins = NULL;
4850 MonoBasicBlock *bblock = *ref_bblock;
4851 gboolean emit_widen = *ref_emit_widen;
4854 * Constrained calls need to behave differently at runtime dependending on whenever the receiver is instantiated as ref type or as a vtype.
4855 * 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
4856 * pack the arguments into an array, and do the rest of the work in in an icall.
4858 if (((cmethod->klass == mono_defaults.object_class) || (cmethod->klass->flags & TYPE_ATTRIBUTE_INTERFACE) || (!cmethod->klass->valuetype && cmethod->klass->image != mono_defaults.corlib)) &&
4859 (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 (cfg, fsig->ret)) &&
4860 (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 (cfg, fsig->params [0]))))) {
4861 MonoInst *args [16];
4864 * This case handles calls to
4865 * - object:ToString()/Equals()/GetHashCode(),
4866 * - System.IComparable<T>:CompareTo()
4867 * - System.IEquatable<T>:Equals ()
4868 * plus some simple interface calls enough to support AsyncTaskMethodBuilder.
4872 if (mono_method_check_context_used (cmethod))
4873 args [1] = emit_get_rgctx_method (cfg, mono_method_check_context_used (cmethod), cmethod, MONO_RGCTX_INFO_METHOD);
4875 EMIT_NEW_METHODCONST (cfg, args [1], cmethod);
4876 args [2] = emit_get_rgctx_klass (cfg, mono_class_check_context_used (constrained_call), constrained_call, MONO_RGCTX_INFO_KLASS);
4878 /* !fsig->hasthis is for the wrapper for the Object.GetType () icall */
4879 if (fsig->hasthis && fsig->param_count) {
4880 /* Pass the arguments using a localloc-ed array using the format expected by runtime_invoke () */
4881 MONO_INST_NEW (cfg, ins, OP_LOCALLOC_IMM);
4882 ins->dreg = alloc_preg (cfg);
4883 ins->inst_imm = fsig->param_count * sizeof (mgreg_t);
4884 MONO_ADD_INS (cfg->cbb, ins);
4887 if (mini_is_gsharedvt_type (cfg, fsig->params [0])) {
4890 args [3] = emit_get_gsharedvt_info_klass (cfg, mono_class_from_mono_type (fsig->params [0]), MONO_RGCTX_INFO_CLASS_BOX_TYPE);
4892 EMIT_NEW_VARLOADA_VREG (cfg, ins, sp [1]->dreg, fsig->params [0]);
4893 addr_reg = ins->dreg;
4894 EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, args [4]->dreg, 0, addr_reg);
4896 EMIT_NEW_ICONST (cfg, args [3], 0);
4897 EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, args [4]->dreg, 0, sp [1]->dreg);
4900 EMIT_NEW_ICONST (cfg, args [3], 0);
4901 EMIT_NEW_ICONST (cfg, args [4], 0);
4903 ins = mono_emit_jit_icall (cfg, mono_gsharedvt_constrained_call, args);
4906 if (mini_is_gsharedvt_type (cfg, fsig->ret)) {
4907 ins = handle_unbox_gsharedvt (cfg, mono_class_from_mono_type (fsig->ret), ins, &bblock);
4908 } else if (MONO_TYPE_IS_PRIMITIVE (fsig->ret) || MONO_TYPE_ISSTRUCT (fsig->ret)) {
4912 NEW_BIALU_IMM (cfg, add, OP_ADD_IMM, alloc_dreg (cfg, STACK_MP), ins->dreg, sizeof (MonoObject));
4913 MONO_ADD_INS (cfg->cbb, add);
4915 NEW_LOAD_MEMBASE_TYPE (cfg, ins, fsig->ret, add->dreg, 0);
4916 MONO_ADD_INS (cfg->cbb, ins);
4917 /* ins represents the call result */
4920 GSHAREDVT_FAILURE (CEE_CALLVIRT);
4923 *ref_emit_widen = emit_widen;
4924 *ref_bblock = bblock;
4933 mono_emit_load_got_addr (MonoCompile *cfg)
4935 MonoInst *getaddr, *dummy_use;
4937 if (!cfg->got_var || cfg->got_var_allocated)
4940 MONO_INST_NEW (cfg, getaddr, OP_LOAD_GOTADDR);
4941 getaddr->cil_code = cfg->header->code;
4942 getaddr->dreg = cfg->got_var->dreg;
4944 /* Add it to the start of the first bblock */
4945 if (cfg->bb_entry->code) {
4946 getaddr->next = cfg->bb_entry->code;
4947 cfg->bb_entry->code = getaddr;
4950 MONO_ADD_INS (cfg->bb_entry, getaddr);
4952 cfg->got_var_allocated = TRUE;
4955 * Add a dummy use to keep the got_var alive, since real uses might
4956 * only be generated by the back ends.
4957 * Add it to end_bblock, so the variable's lifetime covers the whole
4959 * It would be better to make the usage of the got var explicit in all
4960 * cases when the backend needs it (i.e. calls, throw etc.), so this
4961 * wouldn't be needed.
4963 NEW_DUMMY_USE (cfg, dummy_use, cfg->got_var);
4964 MONO_ADD_INS (cfg->bb_exit, dummy_use);
4967 static int inline_limit;
4968 static gboolean inline_limit_inited;
4971 mono_method_check_inlining (MonoCompile *cfg, MonoMethod *method)
4973 MonoMethodHeaderSummary header;
4975 #ifdef MONO_ARCH_SOFT_FLOAT_FALLBACK
4976 MonoMethodSignature *sig = mono_method_signature (method);
4980 if (cfg->disable_inline)
4982 if (cfg->generic_sharing_context)
4985 if (cfg->inline_depth > 10)
4988 #ifdef MONO_ARCH_HAVE_LMF_OPS
4989 if (((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
4990 (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)) &&
4991 !MONO_TYPE_ISSTRUCT (signature->ret) && !mini_class_is_system_array (method->klass))
4996 if (!mono_method_get_header_summary (method, &header))
4999 /*runtime, icall and pinvoke are checked by summary call*/
5000 if ((method->iflags & METHOD_IMPL_ATTRIBUTE_NOINLINING) ||
5001 (method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED) ||
5002 (mono_class_is_marshalbyref (method->klass)) ||
5006 /* also consider num_locals? */
5007 /* Do the size check early to avoid creating vtables */
5008 if (!inline_limit_inited) {
5009 if (g_getenv ("MONO_INLINELIMIT"))
5010 inline_limit = atoi (g_getenv ("MONO_INLINELIMIT"));
5012 inline_limit = INLINE_LENGTH_LIMIT;
5013 inline_limit_inited = TRUE;
5015 if (header.code_size >= inline_limit && !(method->iflags & METHOD_IMPL_ATTRIBUTE_AGGRESSIVE_INLINING))
5019 * if we can initialize the class of the method right away, we do,
5020 * otherwise we don't allow inlining if the class needs initialization,
5021 * since it would mean inserting a call to mono_runtime_class_init()
5022 * inside the inlined code
5024 if (!(cfg->opt & MONO_OPT_SHARED)) {
5025 /* The AggressiveInlining hint is a good excuse to force that cctor to run. */
5026 if (method->iflags & METHOD_IMPL_ATTRIBUTE_AGGRESSIVE_INLINING) {
5027 vtable = mono_class_vtable (cfg->domain, method->klass);
5030 if (!cfg->compile_aot)
5031 mono_runtime_class_init (vtable);
5032 } else if (method->klass->flags & TYPE_ATTRIBUTE_BEFORE_FIELD_INIT) {
5033 if (cfg->run_cctors && method->klass->has_cctor) {
5034 /*FIXME it would easier and lazier to just use mono_class_try_get_vtable */
5035 if (!method->klass->runtime_info)
5036 /* No vtable created yet */
5038 vtable = mono_class_vtable (cfg->domain, method->klass);
5041 /* This makes so that inline cannot trigger */
5042 /* .cctors: too many apps depend on them */
5043 /* running with a specific order... */
5044 if (! vtable->initialized)
5046 mono_runtime_class_init (vtable);
5048 } else if (mono_class_needs_cctor_run (method->klass, NULL)) {
5049 if (!method->klass->runtime_info)
5050 /* No vtable created yet */
5052 vtable = mono_class_vtable (cfg->domain, method->klass);
5055 if (!vtable->initialized)
5060 * If we're compiling for shared code
5061 * the cctor will need to be run at aot method load time, for example,
5062 * or at the end of the compilation of the inlining method.
5064 if (mono_class_needs_cctor_run (method->klass, NULL) && !((method->klass->flags & TYPE_ATTRIBUTE_BEFORE_FIELD_INIT)))
5069 * CAS - do not inline methods with declarative security
5070 * Note: this has to be before any possible return TRUE;
5072 if (mono_security_method_has_declsec (method))
5075 #ifdef MONO_ARCH_SOFT_FLOAT_FALLBACK
5076 if (mono_arch_is_soft_float ()) {
5078 if (sig->ret && sig->ret->type == MONO_TYPE_R4)
5080 for (i = 0; i < sig->param_count; ++i)
5081 if (!sig->params [i]->byref && sig->params [i]->type == MONO_TYPE_R4)
5086 if (g_list_find (cfg->dont_inline, method))
5093 mini_field_access_needs_cctor_run (MonoCompile *cfg, MonoMethod *method, MonoClass *klass, MonoVTable *vtable)
5095 if (!cfg->compile_aot) {
5097 if (vtable->initialized)
5101 if (klass->flags & TYPE_ATTRIBUTE_BEFORE_FIELD_INIT) {
5102 if (cfg->method == method)
5106 if (!mono_class_needs_cctor_run (klass, method))
5109 if (! (method->flags & METHOD_ATTRIBUTE_STATIC) && (klass == method->klass))
5110 /* The initialization is already done before the method is called */
5117 mini_emit_ldelema_1_ins (MonoCompile *cfg, MonoClass *klass, MonoInst *arr, MonoInst *index, gboolean bcheck)
5121 int mult_reg, add_reg, array_reg, index_reg, index2_reg;
5124 if (mini_is_gsharedvt_variable_klass (cfg, klass)) {
5127 mono_class_init (klass);
5128 size = mono_class_array_element_size (klass);
5131 mult_reg = alloc_preg (cfg);
5132 array_reg = arr->dreg;
5133 index_reg = index->dreg;
5135 #if SIZEOF_REGISTER == 8
5136 /* The array reg is 64 bits but the index reg is only 32 */
5137 if (COMPILE_LLVM (cfg)) {
5139 index2_reg = index_reg;
5141 index2_reg = alloc_preg (cfg);
5142 MONO_EMIT_NEW_UNALU (cfg, OP_SEXT_I4, index2_reg, index_reg);
5145 if (index->type == STACK_I8) {
5146 index2_reg = alloc_preg (cfg);
5147 MONO_EMIT_NEW_UNALU (cfg, OP_LCONV_TO_I4, index2_reg, index_reg);
5149 index2_reg = index_reg;
5154 MONO_EMIT_BOUNDS_CHECK (cfg, array_reg, MonoArray, max_length, index2_reg);
5156 #if defined(TARGET_X86) || defined(TARGET_AMD64)
5157 if (size == 1 || size == 2 || size == 4 || size == 8) {
5158 static const int fast_log2 [] = { 1, 0, 1, -1, 2, -1, -1, -1, 3 };
5160 EMIT_NEW_X86_LEA (cfg, ins, array_reg, index2_reg, fast_log2 [size], MONO_STRUCT_OFFSET (MonoArray, vector));
5161 ins->klass = mono_class_get_element_class (klass);
5162 ins->type = STACK_MP;
5168 add_reg = alloc_ireg_mp (cfg);
5171 MonoInst *rgctx_ins;
5174 g_assert (cfg->generic_sharing_context);
5175 context_used = mini_class_check_context_used (cfg, klass);
5176 g_assert (context_used);
5177 rgctx_ins = emit_get_gsharedvt_info (cfg, &klass->byval_arg, MONO_RGCTX_INFO_ARRAY_ELEMENT_SIZE);
5178 MONO_EMIT_NEW_BIALU (cfg, OP_IMUL, mult_reg, index2_reg, rgctx_ins->dreg);
5180 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_MUL_IMM, mult_reg, index2_reg, size);
5182 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, add_reg, array_reg, mult_reg);
5183 NEW_BIALU_IMM (cfg, ins, OP_PADD_IMM, add_reg, add_reg, MONO_STRUCT_OFFSET (MonoArray, vector));
5184 ins->klass = mono_class_get_element_class (klass);
5185 ins->type = STACK_MP;
5186 MONO_ADD_INS (cfg->cbb, ins);
5191 #ifndef MONO_ARCH_EMULATE_MUL_DIV
5193 mini_emit_ldelema_2_ins (MonoCompile *cfg, MonoClass *klass, MonoInst *arr, MonoInst *index_ins1, MonoInst *index_ins2)
5195 int bounds_reg = alloc_preg (cfg);
5196 int add_reg = alloc_ireg_mp (cfg);
5197 int mult_reg = alloc_preg (cfg);
5198 int mult2_reg = alloc_preg (cfg);
5199 int low1_reg = alloc_preg (cfg);
5200 int low2_reg = alloc_preg (cfg);
5201 int high1_reg = alloc_preg (cfg);
5202 int high2_reg = alloc_preg (cfg);
5203 int realidx1_reg = alloc_preg (cfg);
5204 int realidx2_reg = alloc_preg (cfg);
5205 int sum_reg = alloc_preg (cfg);
5206 int index1, index2, tmpreg;
5210 mono_class_init (klass);
5211 size = mono_class_array_element_size (klass);
5213 index1 = index_ins1->dreg;
5214 index2 = index_ins2->dreg;
5216 #if SIZEOF_REGISTER == 8
5217 /* The array reg is 64 bits but the index reg is only 32 */
5218 if (COMPILE_LLVM (cfg)) {
5221 tmpreg = alloc_preg (cfg);
5222 MONO_EMIT_NEW_UNALU (cfg, OP_SEXT_I4, tmpreg, index1);
5224 tmpreg = alloc_preg (cfg);
5225 MONO_EMIT_NEW_UNALU (cfg, OP_SEXT_I4, tmpreg, index2);
5229 // FIXME: Do we need to do something here for i8 indexes, like in ldelema_1_ins ?
5233 /* range checking */
5234 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, bounds_reg,
5235 arr->dreg, MONO_STRUCT_OFFSET (MonoArray, bounds));
5237 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI4_MEMBASE, low1_reg,
5238 bounds_reg, MONO_STRUCT_OFFSET (MonoArrayBounds, lower_bound));
5239 MONO_EMIT_NEW_BIALU (cfg, OP_PSUB, realidx1_reg, index1, low1_reg);
5240 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI4_MEMBASE, high1_reg,
5241 bounds_reg, MONO_STRUCT_OFFSET (MonoArrayBounds, length));
5242 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, high1_reg, realidx1_reg);
5243 MONO_EMIT_NEW_COND_EXC (cfg, LE_UN, "IndexOutOfRangeException");
5245 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI4_MEMBASE, low2_reg,
5246 bounds_reg, sizeof (MonoArrayBounds) + MONO_STRUCT_OFFSET (MonoArrayBounds, lower_bound));
5247 MONO_EMIT_NEW_BIALU (cfg, OP_PSUB, realidx2_reg, index2, low2_reg);
5248 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI4_MEMBASE, high2_reg,
5249 bounds_reg, sizeof (MonoArrayBounds) + MONO_STRUCT_OFFSET (MonoArrayBounds, length));
5250 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, high2_reg, realidx2_reg);
5251 MONO_EMIT_NEW_COND_EXC (cfg, LE_UN, "IndexOutOfRangeException");
5253 MONO_EMIT_NEW_BIALU (cfg, OP_PMUL, mult_reg, high2_reg, realidx1_reg);
5254 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, sum_reg, mult_reg, realidx2_reg);
5255 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_PMUL_IMM, mult2_reg, sum_reg, size);
5256 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, add_reg, mult2_reg, arr->dreg);
5257 NEW_BIALU_IMM (cfg, ins, OP_PADD_IMM, add_reg, add_reg, MONO_STRUCT_OFFSET (MonoArray, vector));
5259 ins->type = STACK_MP;
5261 MONO_ADD_INS (cfg->cbb, ins);
5268 mini_emit_ldelema_ins (MonoCompile *cfg, MonoMethod *cmethod, MonoInst **sp, unsigned char *ip, gboolean is_set)
5272 MonoMethod *addr_method;
5275 rank = mono_method_signature (cmethod)->param_count - (is_set? 1: 0);
5278 return mini_emit_ldelema_1_ins (cfg, cmethod->klass->element_class, sp [0], sp [1], TRUE);
5280 #ifndef MONO_ARCH_EMULATE_MUL_DIV
5281 /* emit_ldelema_2 depends on OP_LMUL */
5282 if (rank == 2 && (cfg->opt & MONO_OPT_INTRINS)) {
5283 return mini_emit_ldelema_2_ins (cfg, cmethod->klass->element_class, sp [0], sp [1], sp [2]);
5287 element_size = mono_class_array_element_size (cmethod->klass->element_class);
5288 addr_method = mono_marshal_get_array_address (rank, element_size);
5289 addr = mono_emit_method_call (cfg, addr_method, sp, NULL);
5294 static MonoBreakPolicy
5295 always_insert_breakpoint (MonoMethod *method)
5297 return MONO_BREAK_POLICY_ALWAYS;
5300 static MonoBreakPolicyFunc break_policy_func = always_insert_breakpoint;
5303 * mono_set_break_policy:
5304 * policy_callback: the new callback function
5306 * Allow embedders to decide wherther to actually obey breakpoint instructions
5307 * (both break IL instructions and Debugger.Break () method calls), for example
5308 * to not allow an app to be aborted by a perfectly valid IL opcode when executing
5309 * untrusted or semi-trusted code.
5311 * @policy_callback will be called every time a break point instruction needs to
5312 * be inserted with the method argument being the method that calls Debugger.Break()
5313 * or has the IL break instruction. The callback should return #MONO_BREAK_POLICY_NEVER
5314 * if it wants the breakpoint to not be effective in the given method.
5315 * #MONO_BREAK_POLICY_ALWAYS is the default.
5318 mono_set_break_policy (MonoBreakPolicyFunc policy_callback)
5320 if (policy_callback)
5321 break_policy_func = policy_callback;
5323 break_policy_func = always_insert_breakpoint;
5327 should_insert_brekpoint (MonoMethod *method) {
5328 switch (break_policy_func (method)) {
5329 case MONO_BREAK_POLICY_ALWAYS:
5331 case MONO_BREAK_POLICY_NEVER:
5333 case MONO_BREAK_POLICY_ON_DBG:
5334 g_warning ("mdb no longer supported");
5337 g_warning ("Incorrect value returned from break policy callback");
5342 /* optimize the simple GetGenericValueImpl/SetGenericValueImpl generic icalls */
5344 emit_array_generic_access (MonoCompile *cfg, MonoMethodSignature *fsig, MonoInst **args, int is_set)
5346 MonoInst *addr, *store, *load;
5347 MonoClass *eklass = mono_class_from_mono_type (fsig->params [2]);
5349 /* the bounds check is already done by the callers */
5350 addr = mini_emit_ldelema_1_ins (cfg, eklass, args [0], args [1], FALSE);
5352 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, load, &eklass->byval_arg, args [2]->dreg, 0);
5353 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, store, &eklass->byval_arg, addr->dreg, 0, load->dreg);
5354 if (mini_type_is_reference (cfg, fsig->params [2]))
5355 emit_write_barrier (cfg, addr, load);
5357 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, load, &eklass->byval_arg, addr->dreg, 0);
5358 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, store, &eklass->byval_arg, args [2]->dreg, 0, load->dreg);
5365 generic_class_is_reference_type (MonoCompile *cfg, MonoClass *klass)
5367 return mini_type_is_reference (cfg, &klass->byval_arg);
5371 emit_array_store (MonoCompile *cfg, MonoClass *klass, MonoInst **sp, gboolean safety_checks)
5373 if (safety_checks && generic_class_is_reference_type (cfg, klass) &&
5374 !(sp [2]->opcode == OP_PCONST && sp [2]->inst_p0 == NULL)) {
5375 MonoClass *obj_array = mono_array_class_get_cached (mono_defaults.object_class, 1);
5376 MonoMethod *helper = mono_marshal_get_virtual_stelemref (obj_array);
5377 MonoInst *iargs [3];
5380 mono_class_setup_vtable (obj_array);
5381 g_assert (helper->slot);
5383 if (sp [0]->type != STACK_OBJ)
5385 if (sp [2]->type != STACK_OBJ)
5392 return mono_emit_method_call (cfg, helper, iargs, sp [0]);
5396 if (mini_is_gsharedvt_variable_klass (cfg, klass)) {
5399 // FIXME-VT: OP_ICONST optimization
5400 addr = mini_emit_ldelema_1_ins (cfg, klass, sp [0], sp [1], TRUE);
5401 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, addr->dreg, 0, sp [2]->dreg);
5402 ins->opcode = OP_STOREV_MEMBASE;
5403 } else if (sp [1]->opcode == OP_ICONST) {
5404 int array_reg = sp [0]->dreg;
5405 int index_reg = sp [1]->dreg;
5406 int offset = (mono_class_array_element_size (klass) * sp [1]->inst_c0) + MONO_STRUCT_OFFSET (MonoArray, vector);
5409 MONO_EMIT_BOUNDS_CHECK (cfg, array_reg, MonoArray, max_length, index_reg);
5410 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, array_reg, offset, sp [2]->dreg);
5412 MonoInst *addr = mini_emit_ldelema_1_ins (cfg, klass, sp [0], sp [1], safety_checks);
5413 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, addr->dreg, 0, sp [2]->dreg);
5414 if (generic_class_is_reference_type (cfg, klass))
5415 emit_write_barrier (cfg, addr, sp [2]);
5422 emit_array_unsafe_access (MonoCompile *cfg, MonoMethodSignature *fsig, MonoInst **args, int is_set)
5427 eklass = mono_class_from_mono_type (fsig->params [2]);
5429 eklass = mono_class_from_mono_type (fsig->ret);
5432 return emit_array_store (cfg, eklass, args, FALSE);
5434 MonoInst *ins, *addr = mini_emit_ldelema_1_ins (cfg, eklass, args [0], args [1], FALSE);
5435 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &eklass->byval_arg, addr->dreg, 0);
5441 is_unsafe_mov_compatible (MonoClass *param_klass, MonoClass *return_klass)
5445 //Only allow for valuetypes
5446 if (!param_klass->valuetype || !return_klass->valuetype)
5450 if (param_klass->has_references || return_klass->has_references)
5453 /* Avoid mixing structs and primitive types/enums, they need to be handled differently in the JIT */
5454 if ((MONO_TYPE_ISSTRUCT (¶m_klass->byval_arg) && !MONO_TYPE_ISSTRUCT (&return_klass->byval_arg)) ||
5455 (!MONO_TYPE_ISSTRUCT (¶m_klass->byval_arg) && MONO_TYPE_ISSTRUCT (&return_klass->byval_arg)))
5458 if (param_klass->byval_arg.type == MONO_TYPE_R4 || param_klass->byval_arg.type == MONO_TYPE_R8 ||
5459 return_klass->byval_arg.type == MONO_TYPE_R4 || return_klass->byval_arg.type == MONO_TYPE_R8)
5462 //And have the same size
5463 if (mono_class_value_size (param_klass, &align) != mono_class_value_size (return_klass, &align))
5469 emit_array_unsafe_mov (MonoCompile *cfg, MonoMethodSignature *fsig, MonoInst **args)
5471 MonoClass *param_klass = mono_class_from_mono_type (fsig->params [0]);
5472 MonoClass *return_klass = mono_class_from_mono_type (fsig->ret);
5474 //Valuetypes that are semantically equivalent
5475 if (is_unsafe_mov_compatible (param_klass, return_klass))
5478 //Arrays of valuetypes that are semantically equivalent
5479 if (param_klass->rank == 1 && return_klass->rank == 1 && is_unsafe_mov_compatible (param_klass->element_class, return_klass->element_class))
5486 mini_emit_inst_for_ctor (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
5488 #ifdef MONO_ARCH_SIMD_INTRINSICS
5489 MonoInst *ins = NULL;
5491 if (cfg->opt & MONO_OPT_SIMD) {
5492 ins = mono_emit_simd_intrinsics (cfg, cmethod, fsig, args);
5498 return mono_emit_native_types_intrinsics (cfg, cmethod, fsig, args);
5502 emit_memory_barrier (MonoCompile *cfg, int kind)
5504 MonoInst *ins = NULL;
5505 MONO_INST_NEW (cfg, ins, OP_MEMORY_BARRIER);
5506 MONO_ADD_INS (cfg->cbb, ins);
5507 ins->backend.memory_barrier_kind = kind;
5513 llvm_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
5515 MonoInst *ins = NULL;
5518 /* The LLVM backend supports these intrinsics */
5519 if (cmethod->klass == mono_defaults.math_class) {
5520 if (strcmp (cmethod->name, "Sin") == 0) {
5522 } else if (strcmp (cmethod->name, "Cos") == 0) {
5524 } else if (strcmp (cmethod->name, "Sqrt") == 0) {
5526 } else if (strcmp (cmethod->name, "Abs") == 0 && fsig->params [0]->type == MONO_TYPE_R8) {
5530 if (opcode && fsig->param_count == 1) {
5531 MONO_INST_NEW (cfg, ins, opcode);
5532 ins->type = STACK_R8;
5533 ins->dreg = mono_alloc_freg (cfg);
5534 ins->sreg1 = args [0]->dreg;
5535 MONO_ADD_INS (cfg->cbb, ins);
5539 if (cfg->opt & MONO_OPT_CMOV) {
5540 if (strcmp (cmethod->name, "Min") == 0) {
5541 if (fsig->params [0]->type == MONO_TYPE_I4)
5543 if (fsig->params [0]->type == MONO_TYPE_U4)
5544 opcode = OP_IMIN_UN;
5545 else if (fsig->params [0]->type == MONO_TYPE_I8)
5547 else if (fsig->params [0]->type == MONO_TYPE_U8)
5548 opcode = OP_LMIN_UN;
5549 } else if (strcmp (cmethod->name, "Max") == 0) {
5550 if (fsig->params [0]->type == MONO_TYPE_I4)
5552 if (fsig->params [0]->type == MONO_TYPE_U4)
5553 opcode = OP_IMAX_UN;
5554 else if (fsig->params [0]->type == MONO_TYPE_I8)
5556 else if (fsig->params [0]->type == MONO_TYPE_U8)
5557 opcode = OP_LMAX_UN;
5561 if (opcode && fsig->param_count == 2) {
5562 MONO_INST_NEW (cfg, ins, opcode);
5563 ins->type = fsig->params [0]->type == MONO_TYPE_I4 ? STACK_I4 : STACK_I8;
5564 ins->dreg = mono_alloc_ireg (cfg);
5565 ins->sreg1 = args [0]->dreg;
5566 ins->sreg2 = args [1]->dreg;
5567 MONO_ADD_INS (cfg->cbb, ins);
5575 mini_emit_inst_for_sharable_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
5577 if (cmethod->klass == mono_defaults.array_class) {
5578 if (strcmp (cmethod->name, "UnsafeStore") == 0)
5579 return emit_array_unsafe_access (cfg, fsig, args, TRUE);
5580 else if (strcmp (cmethod->name, "UnsafeLoad") == 0)
5581 return emit_array_unsafe_access (cfg, fsig, args, FALSE);
5582 else if (strcmp (cmethod->name, "UnsafeMov") == 0)
5583 return emit_array_unsafe_mov (cfg, fsig, args);
5590 mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
5592 MonoInst *ins = NULL;
5594 static MonoClass *runtime_helpers_class = NULL;
5595 if (! runtime_helpers_class)
5596 runtime_helpers_class = mono_class_from_name (mono_defaults.corlib,
5597 "System.Runtime.CompilerServices", "RuntimeHelpers");
5599 if (cmethod->klass == mono_defaults.string_class) {
5600 if (strcmp (cmethod->name, "get_Chars") == 0 && fsig->param_count == 2) {
5601 int dreg = alloc_ireg (cfg);
5602 int index_reg = alloc_preg (cfg);
5603 int mult_reg = alloc_preg (cfg);
5604 int add_reg = alloc_preg (cfg);
5606 #if SIZEOF_REGISTER == 8
5607 /* The array reg is 64 bits but the index reg is only 32 */
5608 MONO_EMIT_NEW_UNALU (cfg, OP_SEXT_I4, index_reg, args [1]->dreg);
5610 index_reg = args [1]->dreg;
5612 MONO_EMIT_BOUNDS_CHECK (cfg, args [0]->dreg, MonoString, length, index_reg);
5614 #if defined(TARGET_X86) || defined(TARGET_AMD64)
5615 EMIT_NEW_X86_LEA (cfg, ins, args [0]->dreg, index_reg, 1, MONO_STRUCT_OFFSET (MonoString, chars));
5616 add_reg = ins->dreg;
5617 /* Avoid a warning */
5619 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADU2_MEMBASE, dreg,
5622 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SHL_IMM, mult_reg, index_reg, 1);
5623 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, add_reg, mult_reg, args [0]->dreg);
5624 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADU2_MEMBASE, dreg,
5625 add_reg, MONO_STRUCT_OFFSET (MonoString, chars));
5627 type_from_op (cfg, ins, NULL, NULL);
5629 } else if (strcmp (cmethod->name, "get_Length") == 0 && fsig->param_count == 1) {
5630 int dreg = alloc_ireg (cfg);
5631 /* Decompose later to allow more optimizations */
5632 EMIT_NEW_UNALU (cfg, ins, OP_STRLEN, dreg, args [0]->dreg);
5633 ins->type = STACK_I4;
5634 ins->flags |= MONO_INST_FAULT;
5635 cfg->cbb->has_array_access = TRUE;
5636 cfg->flags |= MONO_CFG_HAS_ARRAY_ACCESS;
5639 } else if (strcmp (cmethod->name, "InternalSetChar") == 0 && fsig->param_count == 3) {
5640 int mult_reg = alloc_preg (cfg);
5641 int add_reg = alloc_preg (cfg);
5643 /* The corlib functions check for oob already. */
5644 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SHL_IMM, mult_reg, args [1]->dreg, 1);
5645 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, add_reg, mult_reg, args [0]->dreg);
5646 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI2_MEMBASE_REG, add_reg, MONO_STRUCT_OFFSET (MonoString, chars), args [2]->dreg);
5647 return cfg->cbb->last_ins;
5650 } else if (cmethod->klass == mono_defaults.object_class) {
5652 if (strcmp (cmethod->name, "GetType") == 0 && fsig->param_count == 1) {
5653 int dreg = alloc_ireg_ref (cfg);
5654 int vt_reg = alloc_preg (cfg);
5655 MONO_EMIT_NEW_LOAD_MEMBASE_FAULT (cfg, vt_reg, args [0]->dreg, MONO_STRUCT_OFFSET (MonoObject, vtable));
5656 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, dreg, vt_reg, MONO_STRUCT_OFFSET (MonoVTable, type));
5657 type_from_op (cfg, ins, NULL, NULL);
5660 #if !defined(MONO_ARCH_EMULATE_MUL_DIV)
5661 } else if (strcmp (cmethod->name, "InternalGetHashCode") == 0 && fsig->param_count == 1 && !mono_gc_is_moving ()) {
5662 int dreg = alloc_ireg (cfg);
5663 int t1 = alloc_ireg (cfg);
5665 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SHL_IMM, t1, args [0]->dreg, 3);
5666 EMIT_NEW_BIALU_IMM (cfg, ins, OP_MUL_IMM, dreg, t1, 2654435761u);
5667 ins->type = STACK_I4;
5671 } else if (strcmp (cmethod->name, ".ctor") == 0 && fsig->param_count == 0) {
5672 MONO_INST_NEW (cfg, ins, OP_NOP);
5673 MONO_ADD_INS (cfg->cbb, ins);
5677 } else if (cmethod->klass == mono_defaults.array_class) {
5678 if (strcmp (cmethod->name, "GetGenericValueImpl") == 0 && fsig->param_count == 3 && !cfg->gsharedvt)
5679 return emit_array_generic_access (cfg, fsig, args, FALSE);
5680 else if (strcmp (cmethod->name, "SetGenericValueImpl") == 0 && fsig->param_count == 3 && !cfg->gsharedvt)
5681 return emit_array_generic_access (cfg, fsig, args, TRUE);
5683 #ifndef MONO_BIG_ARRAYS
5685 * This is an inline version of GetLength/GetLowerBound(0) used frequently in
5688 else if (((strcmp (cmethod->name, "GetLength") == 0 && fsig->param_count == 2) ||
5689 (strcmp (cmethod->name, "GetLowerBound") == 0 && fsig->param_count == 2)) &&
5690 args [1]->opcode == OP_ICONST && args [1]->inst_c0 == 0) {
5691 int dreg = alloc_ireg (cfg);
5692 int bounds_reg = alloc_ireg_mp (cfg);
5693 MonoBasicBlock *end_bb, *szarray_bb;
5694 gboolean get_length = strcmp (cmethod->name, "GetLength") == 0;
5696 NEW_BBLOCK (cfg, end_bb);
5697 NEW_BBLOCK (cfg, szarray_bb);
5699 EMIT_NEW_LOAD_MEMBASE_FAULT (cfg, ins, OP_LOAD_MEMBASE, bounds_reg,
5700 args [0]->dreg, MONO_STRUCT_OFFSET (MonoArray, bounds));
5701 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, bounds_reg, 0);
5702 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, szarray_bb);
5703 /* Non-szarray case */
5705 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADI4_MEMBASE, dreg,
5706 bounds_reg, MONO_STRUCT_OFFSET (MonoArrayBounds, length));
5708 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADI4_MEMBASE, dreg,
5709 bounds_reg, MONO_STRUCT_OFFSET (MonoArrayBounds, lower_bound));
5710 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
5711 MONO_START_BB (cfg, szarray_bb);
5714 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADI4_MEMBASE, dreg,
5715 args [0]->dreg, MONO_STRUCT_OFFSET (MonoArray, max_length));
5717 MONO_EMIT_NEW_ICONST (cfg, dreg, 0);
5718 MONO_START_BB (cfg, end_bb);
5720 EMIT_NEW_UNALU (cfg, ins, OP_MOVE, dreg, dreg);
5721 ins->type = STACK_I4;
5727 if (cmethod->name [0] != 'g')
5730 if (strcmp (cmethod->name, "get_Rank") == 0 && fsig->param_count == 1) {
5731 int dreg = alloc_ireg (cfg);
5732 int vtable_reg = alloc_preg (cfg);
5733 MONO_EMIT_NEW_LOAD_MEMBASE_OP_FAULT (cfg, OP_LOAD_MEMBASE, vtable_reg,
5734 args [0]->dreg, MONO_STRUCT_OFFSET (MonoObject, vtable));
5735 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADU1_MEMBASE, dreg,
5736 vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, rank));
5737 type_from_op (cfg, ins, NULL, NULL);
5740 } else if (strcmp (cmethod->name, "get_Length") == 0 && fsig->param_count == 1) {
5741 int dreg = alloc_ireg (cfg);
5743 EMIT_NEW_LOAD_MEMBASE_FAULT (cfg, ins, OP_LOADI4_MEMBASE, dreg,
5744 args [0]->dreg, MONO_STRUCT_OFFSET (MonoArray, max_length));
5745 type_from_op (cfg, ins, NULL, NULL);
5750 } else if (cmethod->klass == runtime_helpers_class) {
5752 if (strcmp (cmethod->name, "get_OffsetToStringData") == 0 && fsig->param_count == 0) {
5753 EMIT_NEW_ICONST (cfg, ins, MONO_STRUCT_OFFSET (MonoString, chars));
5757 } else if (cmethod->klass == mono_defaults.thread_class) {
5758 if (strcmp (cmethod->name, "SpinWait_nop") == 0 && fsig->param_count == 0) {
5759 MONO_INST_NEW (cfg, ins, OP_RELAXED_NOP);
5760 MONO_ADD_INS (cfg->cbb, ins);
5762 } else if (strcmp (cmethod->name, "MemoryBarrier") == 0 && fsig->param_count == 0) {
5763 return emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_SEQ);
5764 } else if (!strcmp (cmethod->name, "VolatileRead") && fsig->param_count == 1) {
5766 gboolean is_ref = mini_type_is_reference (cfg, fsig->params [0]);
5768 if (fsig->params [0]->type == MONO_TYPE_I1)
5769 opcode = OP_LOADI1_MEMBASE;
5770 else if (fsig->params [0]->type == MONO_TYPE_U1)
5771 opcode = OP_LOADU1_MEMBASE;
5772 else if (fsig->params [0]->type == MONO_TYPE_I2)
5773 opcode = OP_LOADI2_MEMBASE;
5774 else if (fsig->params [0]->type == MONO_TYPE_U2)
5775 opcode = OP_LOADU2_MEMBASE;
5776 else if (fsig->params [0]->type == MONO_TYPE_I4)
5777 opcode = OP_LOADI4_MEMBASE;
5778 else if (fsig->params [0]->type == MONO_TYPE_U4)
5779 opcode = OP_LOADU4_MEMBASE;
5780 else if (fsig->params [0]->type == MONO_TYPE_I8 || fsig->params [0]->type == MONO_TYPE_U8)
5781 opcode = OP_LOADI8_MEMBASE;
5782 else if (fsig->params [0]->type == MONO_TYPE_R4)
5783 opcode = OP_LOADR4_MEMBASE;
5784 else if (fsig->params [0]->type == MONO_TYPE_R8)
5785 opcode = OP_LOADR8_MEMBASE;
5786 else if (is_ref || fsig->params [0]->type == MONO_TYPE_I || fsig->params [0]->type == MONO_TYPE_U)
5787 opcode = OP_LOAD_MEMBASE;
5790 MONO_INST_NEW (cfg, ins, opcode);
5791 ins->inst_basereg = args [0]->dreg;
5792 ins->inst_offset = 0;
5793 MONO_ADD_INS (cfg->cbb, ins);
5795 switch (fsig->params [0]->type) {
5802 ins->dreg = mono_alloc_ireg (cfg);
5803 ins->type = STACK_I4;
5807 ins->dreg = mono_alloc_lreg (cfg);
5808 ins->type = STACK_I8;
5812 ins->dreg = mono_alloc_ireg (cfg);
5813 #if SIZEOF_REGISTER == 8
5814 ins->type = STACK_I8;
5816 ins->type = STACK_I4;
5821 ins->dreg = mono_alloc_freg (cfg);
5822 ins->type = STACK_R8;
5825 g_assert (mini_type_is_reference (cfg, fsig->params [0]));
5826 ins->dreg = mono_alloc_ireg_ref (cfg);
5827 ins->type = STACK_OBJ;
5831 if (opcode == OP_LOADI8_MEMBASE)
5832 ins = mono_decompose_opcode (cfg, ins);
5834 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_ACQ);
5838 } else if (!strcmp (cmethod->name, "VolatileWrite") && fsig->param_count == 2) {
5840 gboolean is_ref = mini_type_is_reference (cfg, fsig->params [0]);
5842 if (fsig->params [0]->type == MONO_TYPE_I1 || fsig->params [0]->type == MONO_TYPE_U1)
5843 opcode = OP_STOREI1_MEMBASE_REG;
5844 else if (fsig->params [0]->type == MONO_TYPE_I2 || fsig->params [0]->type == MONO_TYPE_U2)
5845 opcode = OP_STOREI2_MEMBASE_REG;
5846 else if (fsig->params [0]->type == MONO_TYPE_I4 || fsig->params [0]->type == MONO_TYPE_U4)
5847 opcode = OP_STOREI4_MEMBASE_REG;
5848 else if (fsig->params [0]->type == MONO_TYPE_I8 || fsig->params [0]->type == MONO_TYPE_U8)
5849 opcode = OP_STOREI8_MEMBASE_REG;
5850 else if (fsig->params [0]->type == MONO_TYPE_R4)
5851 opcode = OP_STORER4_MEMBASE_REG;
5852 else if (fsig->params [0]->type == MONO_TYPE_R8)
5853 opcode = OP_STORER8_MEMBASE_REG;
5854 else if (is_ref || fsig->params [0]->type == MONO_TYPE_I || fsig->params [0]->type == MONO_TYPE_U)
5855 opcode = OP_STORE_MEMBASE_REG;
5858 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_REL);
5860 MONO_INST_NEW (cfg, ins, opcode);
5861 ins->sreg1 = args [1]->dreg;
5862 ins->inst_destbasereg = args [0]->dreg;
5863 ins->inst_offset = 0;
5864 MONO_ADD_INS (cfg->cbb, ins);
5866 if (opcode == OP_STOREI8_MEMBASE_REG)
5867 ins = mono_decompose_opcode (cfg, ins);
5872 } else if (cmethod->klass == mono_defaults.monitor_class) {
5873 #if defined(MONO_ARCH_MONITOR_OBJECT_REG)
5874 if (strcmp (cmethod->name, "Enter") == 0 && fsig->param_count == 1) {
5877 if (COMPILE_LLVM (cfg)) {
5879 * Pass the argument normally, the LLVM backend will handle the
5880 * calling convention problems.
5882 call = (MonoCallInst*)mono_emit_abs_call (cfg, MONO_PATCH_INFO_MONITOR_ENTER, NULL, helper_sig_monitor_enter_exit_trampoline_llvm, args);
5884 call = (MonoCallInst*)mono_emit_abs_call (cfg, MONO_PATCH_INFO_MONITOR_ENTER,
5885 NULL, helper_sig_monitor_enter_exit_trampoline, NULL);
5886 mono_call_inst_add_outarg_reg (cfg, call, args [0]->dreg,
5887 MONO_ARCH_MONITOR_OBJECT_REG, FALSE);
5890 return (MonoInst*)call;
5891 #if defined(MONO_ARCH_MONITOR_LOCK_TAKEN_REG)
5892 } else if (strcmp (cmethod->name, "Enter") == 0 && fsig->param_count == 2) {
5895 if (COMPILE_LLVM (cfg)) {
5897 * Pass the argument normally, the LLVM backend will handle the
5898 * calling convention problems.
5900 call = (MonoCallInst*)mono_emit_abs_call (cfg, MONO_PATCH_INFO_MONITOR_ENTER_V4, NULL, helper_sig_monitor_enter_v4_trampoline_llvm, args);
5902 call = (MonoCallInst*)mono_emit_abs_call (cfg, MONO_PATCH_INFO_MONITOR_ENTER_V4,
5903 NULL, helper_sig_monitor_enter_exit_trampoline, NULL);
5904 mono_call_inst_add_outarg_reg (cfg, call, args [0]->dreg, MONO_ARCH_MONITOR_OBJECT_REG, FALSE);
5905 mono_call_inst_add_outarg_reg (cfg, call, args [1]->dreg, MONO_ARCH_MONITOR_LOCK_TAKEN_REG, FALSE);
5908 return (MonoInst*)call;
5910 } else if (strcmp (cmethod->name, "Exit") == 0 && fsig->param_count == 1) {
5913 if (COMPILE_LLVM (cfg)) {
5914 call = (MonoCallInst*)mono_emit_abs_call (cfg, MONO_PATCH_INFO_MONITOR_EXIT, NULL, helper_sig_monitor_enter_exit_trampoline_llvm, args);
5916 call = (MonoCallInst*)mono_emit_abs_call (cfg, MONO_PATCH_INFO_MONITOR_EXIT,
5917 NULL, helper_sig_monitor_enter_exit_trampoline, NULL);
5918 mono_call_inst_add_outarg_reg (cfg, call, args [0]->dreg,
5919 MONO_ARCH_MONITOR_OBJECT_REG, FALSE);
5922 return (MonoInst*)call;
5925 } else if (cmethod->klass->image == mono_defaults.corlib &&
5926 (strcmp (cmethod->klass->name_space, "System.Threading") == 0) &&
5927 (strcmp (cmethod->klass->name, "Interlocked") == 0)) {
5930 #if SIZEOF_REGISTER == 8
5931 if (strcmp (cmethod->name, "Read") == 0 && fsig->param_count == 1 && (fsig->params [0]->type == MONO_TYPE_I8)) {
5932 if (mono_arch_opcode_supported (OP_ATOMIC_LOAD_I8)) {
5933 MONO_INST_NEW (cfg, ins, OP_ATOMIC_LOAD_I8);
5934 ins->dreg = mono_alloc_preg (cfg);
5935 ins->sreg1 = args [0]->dreg;
5936 ins->type = STACK_I8;
5937 ins->backend.memory_barrier_kind = MONO_MEMORY_BARRIER_SEQ;
5938 MONO_ADD_INS (cfg->cbb, ins);
5942 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_SEQ);
5944 /* 64 bit reads are already atomic */
5945 MONO_INST_NEW (cfg, load_ins, OP_LOADI8_MEMBASE);
5946 load_ins->dreg = mono_alloc_preg (cfg);
5947 load_ins->inst_basereg = args [0]->dreg;
5948 load_ins->inst_offset = 0;
5949 load_ins->type = STACK_I8;
5950 MONO_ADD_INS (cfg->cbb, load_ins);
5952 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_SEQ);
5959 if (strcmp (cmethod->name, "Increment") == 0 && fsig->param_count == 1) {
5960 MonoInst *ins_iconst;
5963 if (fsig->params [0]->type == MONO_TYPE_I4) {
5964 opcode = OP_ATOMIC_ADD_I4;
5965 cfg->has_atomic_add_i4 = TRUE;
5967 #if SIZEOF_REGISTER == 8
5968 else if (fsig->params [0]->type == MONO_TYPE_I8)
5969 opcode = OP_ATOMIC_ADD_I8;
5972 if (!mono_arch_opcode_supported (opcode))
5974 MONO_INST_NEW (cfg, ins_iconst, OP_ICONST);
5975 ins_iconst->inst_c0 = 1;
5976 ins_iconst->dreg = mono_alloc_ireg (cfg);
5977 MONO_ADD_INS (cfg->cbb, ins_iconst);
5979 MONO_INST_NEW (cfg, ins, opcode);
5980 ins->dreg = mono_alloc_ireg (cfg);
5981 ins->inst_basereg = args [0]->dreg;
5982 ins->inst_offset = 0;
5983 ins->sreg2 = ins_iconst->dreg;
5984 ins->type = (opcode == OP_ATOMIC_ADD_I4) ? STACK_I4 : STACK_I8;
5985 MONO_ADD_INS (cfg->cbb, ins);
5987 } else if (strcmp (cmethod->name, "Decrement") == 0 && fsig->param_count == 1) {
5988 MonoInst *ins_iconst;
5991 if (fsig->params [0]->type == MONO_TYPE_I4) {
5992 opcode = OP_ATOMIC_ADD_I4;
5993 cfg->has_atomic_add_i4 = TRUE;
5995 #if SIZEOF_REGISTER == 8
5996 else if (fsig->params [0]->type == MONO_TYPE_I8)
5997 opcode = OP_ATOMIC_ADD_I8;
6000 if (!mono_arch_opcode_supported (opcode))
6002 MONO_INST_NEW (cfg, ins_iconst, OP_ICONST);
6003 ins_iconst->inst_c0 = -1;
6004 ins_iconst->dreg = mono_alloc_ireg (cfg);
6005 MONO_ADD_INS (cfg->cbb, ins_iconst);
6007 MONO_INST_NEW (cfg, ins, opcode);
6008 ins->dreg = mono_alloc_ireg (cfg);
6009 ins->inst_basereg = args [0]->dreg;
6010 ins->inst_offset = 0;
6011 ins->sreg2 = ins_iconst->dreg;
6012 ins->type = (opcode == OP_ATOMIC_ADD_I4) ? STACK_I4 : STACK_I8;
6013 MONO_ADD_INS (cfg->cbb, ins);
6015 } else if (strcmp (cmethod->name, "Add") == 0 && fsig->param_count == 2) {
6018 if (fsig->params [0]->type == MONO_TYPE_I4) {
6019 opcode = OP_ATOMIC_ADD_I4;
6020 cfg->has_atomic_add_i4 = TRUE;
6022 #if SIZEOF_REGISTER == 8
6023 else if (fsig->params [0]->type == MONO_TYPE_I8)
6024 opcode = OP_ATOMIC_ADD_I8;
6027 if (!mono_arch_opcode_supported (opcode))
6029 MONO_INST_NEW (cfg, ins, opcode);
6030 ins->dreg = mono_alloc_ireg (cfg);
6031 ins->inst_basereg = args [0]->dreg;
6032 ins->inst_offset = 0;
6033 ins->sreg2 = args [1]->dreg;
6034 ins->type = (opcode == OP_ATOMIC_ADD_I4) ? STACK_I4 : STACK_I8;
6035 MONO_ADD_INS (cfg->cbb, ins);
6038 else if (strcmp (cmethod->name, "Exchange") == 0 && fsig->param_count == 2) {
6039 MonoInst *f2i = NULL, *i2f;
6040 guint32 opcode, f2i_opcode, i2f_opcode;
6041 gboolean is_ref = mini_type_is_reference (cfg, fsig->params [0]);
6042 gboolean is_float = fsig->params [0]->type == MONO_TYPE_R4 || fsig->params [0]->type == MONO_TYPE_R8;
6044 if (fsig->params [0]->type == MONO_TYPE_I4 ||
6045 fsig->params [0]->type == MONO_TYPE_R4) {
6046 opcode = OP_ATOMIC_EXCHANGE_I4;
6047 f2i_opcode = OP_MOVE_F_TO_I4;
6048 i2f_opcode = OP_MOVE_I4_TO_F;
6049 cfg->has_atomic_exchange_i4 = TRUE;
6051 #if SIZEOF_REGISTER == 8
6053 fsig->params [0]->type == MONO_TYPE_I8 ||
6054 fsig->params [0]->type == MONO_TYPE_R8 ||
6055 fsig->params [0]->type == MONO_TYPE_I) {
6056 opcode = OP_ATOMIC_EXCHANGE_I8;
6057 f2i_opcode = OP_MOVE_F_TO_I8;
6058 i2f_opcode = OP_MOVE_I8_TO_F;
6061 else if (is_ref || fsig->params [0]->type == MONO_TYPE_I) {
6062 opcode = OP_ATOMIC_EXCHANGE_I4;
6063 cfg->has_atomic_exchange_i4 = TRUE;
6069 if (!mono_arch_opcode_supported (opcode))
6073 /* TODO: Decompose these opcodes instead of bailing here. */
6074 if (COMPILE_SOFT_FLOAT (cfg))
6077 MONO_INST_NEW (cfg, f2i, f2i_opcode);
6078 f2i->dreg = mono_alloc_ireg (cfg);
6079 f2i->sreg1 = args [1]->dreg;
6080 if (f2i_opcode == OP_MOVE_F_TO_I4)
6081 f2i->backend.spill_var = mini_get_int_to_float_spill_area (cfg);
6082 MONO_ADD_INS (cfg->cbb, f2i);
6085 MONO_INST_NEW (cfg, ins, opcode);
6086 ins->dreg = is_ref ? mono_alloc_ireg_ref (cfg) : mono_alloc_ireg (cfg);
6087 ins->inst_basereg = args [0]->dreg;
6088 ins->inst_offset = 0;
6089 ins->sreg2 = is_float ? f2i->dreg : args [1]->dreg;
6090 MONO_ADD_INS (cfg->cbb, ins);
6092 switch (fsig->params [0]->type) {
6094 ins->type = STACK_I4;
6097 ins->type = STACK_I8;
6100 #if SIZEOF_REGISTER == 8
6101 ins->type = STACK_I8;
6103 ins->type = STACK_I4;
6108 ins->type = STACK_R8;
6111 g_assert (mini_type_is_reference (cfg, fsig->params [0]));
6112 ins->type = STACK_OBJ;
6117 MONO_INST_NEW (cfg, i2f, i2f_opcode);
6118 i2f->dreg = mono_alloc_freg (cfg);
6119 i2f->sreg1 = ins->dreg;
6120 i2f->type = STACK_R8;
6121 if (i2f_opcode == OP_MOVE_I4_TO_F)
6122 i2f->backend.spill_var = mini_get_int_to_float_spill_area (cfg);
6123 MONO_ADD_INS (cfg->cbb, i2f);
6128 if (cfg->gen_write_barriers && is_ref)
6129 emit_write_barrier (cfg, args [0], args [1]);
6131 else if ((strcmp (cmethod->name, "CompareExchange") == 0) && fsig->param_count == 3) {
6132 MonoInst *f2i_new = NULL, *f2i_cmp = NULL, *i2f;
6133 guint32 opcode, f2i_opcode, i2f_opcode;
6134 gboolean is_ref = mini_type_is_reference (cfg, fsig->params [1]);
6135 gboolean is_float = fsig->params [1]->type == MONO_TYPE_R4 || fsig->params [1]->type == MONO_TYPE_R8;
6137 if (fsig->params [1]->type == MONO_TYPE_I4 ||
6138 fsig->params [1]->type == MONO_TYPE_R4) {
6139 opcode = OP_ATOMIC_CAS_I4;
6140 f2i_opcode = OP_MOVE_F_TO_I4;
6141 i2f_opcode = OP_MOVE_I4_TO_F;
6142 cfg->has_atomic_cas_i4 = TRUE;
6144 #if SIZEOF_REGISTER == 8
6146 fsig->params [1]->type == MONO_TYPE_I8 ||
6147 fsig->params [1]->type == MONO_TYPE_R8 ||
6148 fsig->params [1]->type == MONO_TYPE_I) {
6149 opcode = OP_ATOMIC_CAS_I8;
6150 f2i_opcode = OP_MOVE_F_TO_I8;
6151 i2f_opcode = OP_MOVE_I8_TO_F;
6154 else if (is_ref || fsig->params [1]->type == MONO_TYPE_I) {
6155 opcode = OP_ATOMIC_CAS_I4;
6156 cfg->has_atomic_cas_i4 = TRUE;
6162 if (!mono_arch_opcode_supported (opcode))
6166 /* TODO: Decompose these opcodes instead of bailing here. */
6167 if (COMPILE_SOFT_FLOAT (cfg))
6170 MONO_INST_NEW (cfg, f2i_new, f2i_opcode);
6171 f2i_new->dreg = mono_alloc_ireg (cfg);
6172 f2i_new->sreg1 = args [1]->dreg;
6173 if (f2i_opcode == OP_MOVE_F_TO_I4)
6174 f2i_new->backend.spill_var = mini_get_int_to_float_spill_area (cfg);
6175 MONO_ADD_INS (cfg->cbb, f2i_new);
6177 MONO_INST_NEW (cfg, f2i_cmp, f2i_opcode);
6178 f2i_cmp->dreg = mono_alloc_ireg (cfg);
6179 f2i_cmp->sreg1 = args [2]->dreg;
6180 if (f2i_opcode == OP_MOVE_F_TO_I4)
6181 f2i_cmp->backend.spill_var = mini_get_int_to_float_spill_area (cfg);
6182 MONO_ADD_INS (cfg->cbb, f2i_cmp);
6185 MONO_INST_NEW (cfg, ins, opcode);
6186 ins->dreg = is_ref ? alloc_ireg_ref (cfg) : alloc_ireg (cfg);
6187 ins->sreg1 = args [0]->dreg;
6188 ins->sreg2 = is_float ? f2i_new->dreg : args [1]->dreg;
6189 ins->sreg3 = is_float ? f2i_cmp->dreg : args [2]->dreg;
6190 MONO_ADD_INS (cfg->cbb, ins);
6192 switch (fsig->params [0]->type) {
6194 ins->type = STACK_I4;
6197 ins->type = STACK_I8;
6200 #if SIZEOF_REGISTER == 8
6201 ins->type = STACK_I8;
6203 ins->type = STACK_I4;
6208 ins->type = STACK_R8;
6211 g_assert (mini_type_is_reference (cfg, fsig->params [0]));
6212 ins->type = STACK_OBJ;
6217 MONO_INST_NEW (cfg, i2f, i2f_opcode);
6218 i2f->dreg = mono_alloc_freg (cfg);
6219 i2f->sreg1 = ins->dreg;
6220 i2f->type = STACK_R8;
6221 if (i2f_opcode == OP_MOVE_I4_TO_F)
6222 i2f->backend.spill_var = mini_get_int_to_float_spill_area (cfg);
6223 MONO_ADD_INS (cfg->cbb, i2f);
6228 if (cfg->gen_write_barriers && is_ref)
6229 emit_write_barrier (cfg, args [0], args [1]);
6231 else if ((strcmp (cmethod->name, "CompareExchange") == 0) && fsig->param_count == 4 &&
6232 fsig->params [1]->type == MONO_TYPE_I4) {
6233 MonoInst *cmp, *ceq;
6235 if (!mono_arch_opcode_supported (OP_ATOMIC_CAS_I4))
6238 /* int32 r = CAS (location, value, comparand); */
6239 MONO_INST_NEW (cfg, ins, OP_ATOMIC_CAS_I4);
6240 ins->dreg = alloc_ireg (cfg);
6241 ins->sreg1 = args [0]->dreg;
6242 ins->sreg2 = args [1]->dreg;
6243 ins->sreg3 = args [2]->dreg;
6244 ins->type = STACK_I4;
6245 MONO_ADD_INS (cfg->cbb, ins);
6247 /* bool result = r == comparand; */
6248 MONO_INST_NEW (cfg, cmp, OP_ICOMPARE);
6249 cmp->sreg1 = ins->dreg;
6250 cmp->sreg2 = args [2]->dreg;
6251 cmp->type = STACK_I4;
6252 MONO_ADD_INS (cfg->cbb, cmp);
6254 MONO_INST_NEW (cfg, ceq, OP_ICEQ);
6255 ceq->dreg = alloc_ireg (cfg);
6256 ceq->type = STACK_I4;
6257 MONO_ADD_INS (cfg->cbb, ceq);
6259 /* *success = result; */
6260 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI1_MEMBASE_REG, args [3]->dreg, 0, ceq->dreg);
6262 cfg->has_atomic_cas_i4 = TRUE;
6264 else if (strcmp (cmethod->name, "MemoryBarrier") == 0 && fsig->param_count == 0)
6265 ins = emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_SEQ);
6269 } else if (cmethod->klass->image == mono_defaults.corlib &&
6270 (strcmp (cmethod->klass->name_space, "System.Threading") == 0) &&
6271 (strcmp (cmethod->klass->name, "Volatile") == 0)) {
6274 if (!strcmp (cmethod->name, "Read") && fsig->param_count == 1) {
6276 gboolean is_ref = mini_type_is_reference (cfg, fsig->params [0]);
6277 gboolean is_float = fsig->params [0]->type == MONO_TYPE_R4 || fsig->params [0]->type == MONO_TYPE_R8;
6279 if (fsig->params [0]->type == MONO_TYPE_I1)
6280 opcode = OP_ATOMIC_LOAD_I1;
6281 else if (fsig->params [0]->type == MONO_TYPE_U1 || fsig->params [0]->type == MONO_TYPE_BOOLEAN)
6282 opcode = OP_ATOMIC_LOAD_U1;
6283 else if (fsig->params [0]->type == MONO_TYPE_I2)
6284 opcode = OP_ATOMIC_LOAD_I2;
6285 else if (fsig->params [0]->type == MONO_TYPE_U2)
6286 opcode = OP_ATOMIC_LOAD_U2;
6287 else if (fsig->params [0]->type == MONO_TYPE_I4)
6288 opcode = OP_ATOMIC_LOAD_I4;
6289 else if (fsig->params [0]->type == MONO_TYPE_U4)
6290 opcode = OP_ATOMIC_LOAD_U4;
6291 else if (fsig->params [0]->type == MONO_TYPE_R4)
6292 opcode = OP_ATOMIC_LOAD_R4;
6293 else if (fsig->params [0]->type == MONO_TYPE_R8)
6294 opcode = OP_ATOMIC_LOAD_R8;
6295 #if SIZEOF_REGISTER == 8
6296 else if (fsig->params [0]->type == MONO_TYPE_I8 || fsig->params [0]->type == MONO_TYPE_I)
6297 opcode = OP_ATOMIC_LOAD_I8;
6298 else if (is_ref || fsig->params [0]->type == MONO_TYPE_U8 || fsig->params [0]->type == MONO_TYPE_U)
6299 opcode = OP_ATOMIC_LOAD_U8;
6301 else if (fsig->params [0]->type == MONO_TYPE_I)
6302 opcode = OP_ATOMIC_LOAD_I4;
6303 else if (is_ref || fsig->params [0]->type == MONO_TYPE_U)
6304 opcode = OP_ATOMIC_LOAD_U4;
6308 if (!mono_arch_opcode_supported (opcode))
6311 MONO_INST_NEW (cfg, ins, opcode);
6312 ins->dreg = is_ref ? mono_alloc_ireg_ref (cfg) : (is_float ? mono_alloc_freg (cfg) : mono_alloc_ireg (cfg));
6313 ins->sreg1 = args [0]->dreg;
6314 ins->backend.memory_barrier_kind = MONO_MEMORY_BARRIER_ACQ;
6315 MONO_ADD_INS (cfg->cbb, ins);
6317 switch (fsig->params [0]->type) {
6318 case MONO_TYPE_BOOLEAN:
6325 ins->type = STACK_I4;
6329 ins->type = STACK_I8;
6333 #if SIZEOF_REGISTER == 8
6334 ins->type = STACK_I8;
6336 ins->type = STACK_I4;
6341 ins->type = STACK_R8;
6344 g_assert (mini_type_is_reference (cfg, fsig->params [0]));
6345 ins->type = STACK_OBJ;
6351 if (!strcmp (cmethod->name, "Write") && fsig->param_count == 2) {
6353 gboolean is_ref = mini_type_is_reference (cfg, fsig->params [0]);
6355 if (fsig->params [0]->type == MONO_TYPE_I1)
6356 opcode = OP_ATOMIC_STORE_I1;
6357 else if (fsig->params [0]->type == MONO_TYPE_U1 || fsig->params [0]->type == MONO_TYPE_BOOLEAN)
6358 opcode = OP_ATOMIC_STORE_U1;
6359 else if (fsig->params [0]->type == MONO_TYPE_I2)
6360 opcode = OP_ATOMIC_STORE_I2;
6361 else if (fsig->params [0]->type == MONO_TYPE_U2)
6362 opcode = OP_ATOMIC_STORE_U2;
6363 else if (fsig->params [0]->type == MONO_TYPE_I4)
6364 opcode = OP_ATOMIC_STORE_I4;
6365 else if (fsig->params [0]->type == MONO_TYPE_U4)
6366 opcode = OP_ATOMIC_STORE_U4;
6367 else if (fsig->params [0]->type == MONO_TYPE_R4)
6368 opcode = OP_ATOMIC_STORE_R4;
6369 else if (fsig->params [0]->type == MONO_TYPE_R8)
6370 opcode = OP_ATOMIC_STORE_R8;
6371 #if SIZEOF_REGISTER == 8
6372 else if (fsig->params [0]->type == MONO_TYPE_I8 || fsig->params [0]->type == MONO_TYPE_I)
6373 opcode = OP_ATOMIC_STORE_I8;
6374 else if (is_ref || fsig->params [0]->type == MONO_TYPE_U8 || fsig->params [0]->type == MONO_TYPE_U)
6375 opcode = OP_ATOMIC_STORE_U8;
6377 else if (fsig->params [0]->type == MONO_TYPE_I)
6378 opcode = OP_ATOMIC_STORE_I4;
6379 else if (is_ref || fsig->params [0]->type == MONO_TYPE_U)
6380 opcode = OP_ATOMIC_STORE_U4;
6384 if (!mono_arch_opcode_supported (opcode))
6387 MONO_INST_NEW (cfg, ins, opcode);
6388 ins->dreg = args [0]->dreg;
6389 ins->sreg1 = args [1]->dreg;
6390 ins->backend.memory_barrier_kind = MONO_MEMORY_BARRIER_REL;
6391 MONO_ADD_INS (cfg->cbb, ins);
6393 if (cfg->gen_write_barriers && is_ref)
6394 emit_write_barrier (cfg, args [0], args [1]);
6400 } else if (cmethod->klass->image == mono_defaults.corlib &&
6401 (strcmp (cmethod->klass->name_space, "System.Diagnostics") == 0) &&
6402 (strcmp (cmethod->klass->name, "Debugger") == 0)) {
6403 if (!strcmp (cmethod->name, "Break") && fsig->param_count == 0) {
6404 if (should_insert_brekpoint (cfg->method)) {
6405 ins = mono_emit_jit_icall (cfg, mono_debugger_agent_user_break, NULL);
6407 MONO_INST_NEW (cfg, ins, OP_NOP);
6408 MONO_ADD_INS (cfg->cbb, ins);
6412 } else if (cmethod->klass->image == mono_defaults.corlib &&
6413 (strcmp (cmethod->klass->name_space, "System") == 0) &&
6414 (strcmp (cmethod->klass->name, "Environment") == 0)) {
6415 if (!strcmp (cmethod->name, "get_IsRunningOnWindows") && fsig->param_count == 0) {
6417 EMIT_NEW_ICONST (cfg, ins, 1);
6419 EMIT_NEW_ICONST (cfg, ins, 0);
6422 } else if (cmethod->klass == mono_defaults.math_class) {
6424 * There is general branchless code for Min/Max, but it does not work for
6426 * http://everything2.com/?node_id=1051618
6428 } else if ((!strcmp (cmethod->klass->image->assembly->aname.name, "MonoMac") ||
6429 !strcmp (cmethod->klass->image->assembly->aname.name, "monotouch")) &&
6430 !strcmp (cmethod->klass->name_space, "XamCore.ObjCRuntime") &&
6431 !strcmp (cmethod->klass->name, "Selector")) {
6432 #ifdef MONO_ARCH_HAVE_OBJC_GET_SELECTOR
6433 if (!strcmp (cmethod->klass->name, "GetHandle") && fsig->param_count == 1 &&
6434 (args [0]->opcode == OP_GOT_ENTRY || args [0]->opcode == OP_AOTCONST) &&
6437 MonoJumpInfoToken *ji;
6440 cfg->disable_llvm = TRUE;
6442 if (args [0]->opcode == OP_GOT_ENTRY) {
6443 pi = args [0]->inst_p1;
6444 g_assert (pi->opcode == OP_PATCH_INFO);
6445 g_assert (GPOINTER_TO_INT (pi->inst_p1) == MONO_PATCH_INFO_LDSTR);
6448 g_assert (GPOINTER_TO_INT (args [0]->inst_p1) == MONO_PATCH_INFO_LDSTR);
6449 ji = args [0]->inst_p0;
6452 NULLIFY_INS (args [0]);
6455 s = mono_ldstr (cfg->domain, ji->image, mono_metadata_token_index (ji->token));
6456 MONO_INST_NEW (cfg, ins, OP_OBJC_GET_SELECTOR);
6457 ins->dreg = mono_alloc_ireg (cfg);
6459 ins->inst_p0 = mono_string_to_utf8 (s);
6460 MONO_ADD_INS (cfg->cbb, ins);
6466 #ifdef MONO_ARCH_SIMD_INTRINSICS
6467 if (cfg->opt & MONO_OPT_SIMD) {
6468 ins = mono_emit_simd_intrinsics (cfg, cmethod, fsig, args);
6474 ins = mono_emit_native_types_intrinsics (cfg, cmethod, fsig, args);
6478 if (COMPILE_LLVM (cfg)) {
6479 ins = llvm_emit_inst_for_method (cfg, cmethod, fsig, args);
6484 return mono_arch_emit_inst_for_method (cfg, cmethod, fsig, args);
6488 * This entry point could be used later for arbitrary method
6491 inline static MonoInst*
6492 mini_redirect_call (MonoCompile *cfg, MonoMethod *method,
6493 MonoMethodSignature *signature, MonoInst **args, MonoInst *this)
6495 if (method->klass == mono_defaults.string_class) {
6496 /* managed string allocation support */
6497 if (strcmp (method->name, "InternalAllocateStr") == 0 && !(mono_profiler_events & MONO_PROFILE_ALLOCATIONS) && !(cfg->opt & MONO_OPT_SHARED)) {
6498 MonoInst *iargs [2];
6499 MonoVTable *vtable = mono_class_vtable (cfg->domain, method->klass);
6500 MonoMethod *managed_alloc = NULL;
6502 g_assert (vtable); /*Should not fail since it System.String*/
6503 #ifndef MONO_CROSS_COMPILE
6504 managed_alloc = mono_gc_get_managed_allocator (method->klass, FALSE);
6508 EMIT_NEW_VTABLECONST (cfg, iargs [0], vtable);
6509 iargs [1] = args [0];
6510 return mono_emit_method_call (cfg, managed_alloc, iargs, this);
6517 mono_save_args (MonoCompile *cfg, MonoMethodSignature *sig, MonoInst **sp)
6519 MonoInst *store, *temp;
6522 for (i = 0; i < sig->param_count + sig->hasthis; ++i) {
6523 MonoType *argtype = (sig->hasthis && (i == 0)) ? type_from_stack_type (*sp) : sig->params [i - sig->hasthis];
6526 * FIXME: We should use *args++ = sp [0], but that would mean the arg
6527 * would be different than the MonoInst's used to represent arguments, and
6528 * the ldelema implementation can't deal with that.
6529 * Solution: When ldelema is used on an inline argument, create a var for
6530 * it, emit ldelema on that var, and emit the saving code below in
6531 * inline_method () if needed.
6533 temp = mono_compile_create_var (cfg, argtype, OP_LOCAL);
6534 cfg->args [i] = temp;
6535 /* This uses cfg->args [i] which is set by the preceeding line */
6536 EMIT_NEW_ARGSTORE (cfg, store, i, *sp);
6537 store->cil_code = sp [0]->cil_code;
6542 #define MONO_INLINE_CALLED_LIMITED_METHODS 1
6543 #define MONO_INLINE_CALLER_LIMITED_METHODS 1
6545 #if (MONO_INLINE_CALLED_LIMITED_METHODS)
6547 check_inline_called_method_name_limit (MonoMethod *called_method)
6550 static const char *limit = NULL;
6552 if (limit == NULL) {
6553 const char *limit_string = g_getenv ("MONO_INLINE_CALLED_METHOD_NAME_LIMIT");
6555 if (limit_string != NULL)
6556 limit = limit_string;
6561 if (limit [0] != '\0') {
6562 char *called_method_name = mono_method_full_name (called_method, TRUE);
6564 strncmp_result = strncmp (called_method_name, limit, strlen (limit));
6565 g_free (called_method_name);
6567 //return (strncmp_result <= 0);
6568 return (strncmp_result == 0);
6575 #if (MONO_INLINE_CALLER_LIMITED_METHODS)
6577 check_inline_caller_method_name_limit (MonoMethod *caller_method)
6580 static const char *limit = NULL;
6582 if (limit == NULL) {
6583 const char *limit_string = g_getenv ("MONO_INLINE_CALLER_METHOD_NAME_LIMIT");
6584 if (limit_string != NULL) {
6585 limit = limit_string;
6591 if (limit [0] != '\0') {
6592 char *caller_method_name = mono_method_full_name (caller_method, TRUE);
6594 strncmp_result = strncmp (caller_method_name, limit, strlen (limit));
6595 g_free (caller_method_name);
6597 //return (strncmp_result <= 0);
6598 return (strncmp_result == 0);
6606 emit_init_rvar (MonoCompile *cfg, int dreg, MonoType *rtype)
6608 static double r8_0 = 0.0;
6609 static float r4_0 = 0.0;
6613 rtype = mini_replace_type (rtype);
6617 MONO_EMIT_NEW_PCONST (cfg, dreg, NULL);
6618 } else if (t >= MONO_TYPE_BOOLEAN && t <= MONO_TYPE_U4) {
6619 MONO_EMIT_NEW_ICONST (cfg, dreg, 0);
6620 } else if (t == MONO_TYPE_I8 || t == MONO_TYPE_U8) {
6621 MONO_EMIT_NEW_I8CONST (cfg, dreg, 0);
6622 } else if (cfg->r4fp && t == MONO_TYPE_R4) {
6623 MONO_INST_NEW (cfg, ins, OP_R4CONST);
6624 ins->type = STACK_R4;
6625 ins->inst_p0 = (void*)&r4_0;
6627 MONO_ADD_INS (cfg->cbb, ins);
6628 } else if (t == MONO_TYPE_R4 || t == MONO_TYPE_R8) {
6629 MONO_INST_NEW (cfg, ins, OP_R8CONST);
6630 ins->type = STACK_R8;
6631 ins->inst_p0 = (void*)&r8_0;
6633 MONO_ADD_INS (cfg->cbb, ins);
6634 } else if ((t == MONO_TYPE_VALUETYPE) || (t == MONO_TYPE_TYPEDBYREF) ||
6635 ((t == MONO_TYPE_GENERICINST) && mono_type_generic_inst_is_valuetype (rtype))) {
6636 MONO_EMIT_NEW_VZERO (cfg, dreg, mono_class_from_mono_type (rtype));
6637 } else if (((t == MONO_TYPE_VAR) || (t == MONO_TYPE_MVAR)) && mini_type_var_is_vt (cfg, rtype)) {
6638 MONO_EMIT_NEW_VZERO (cfg, dreg, mono_class_from_mono_type (rtype));
6640 MONO_EMIT_NEW_PCONST (cfg, dreg, NULL);
6645 emit_dummy_init_rvar (MonoCompile *cfg, int dreg, MonoType *rtype)
6649 rtype = mini_replace_type (rtype);
6653 MONO_EMIT_NEW_DUMMY_INIT (cfg, dreg, OP_DUMMY_PCONST);
6654 } else if (t >= MONO_TYPE_BOOLEAN && t <= MONO_TYPE_U4) {
6655 MONO_EMIT_NEW_DUMMY_INIT (cfg, dreg, OP_DUMMY_ICONST);
6656 } else if (t == MONO_TYPE_I8 || t == MONO_TYPE_U8) {
6657 MONO_EMIT_NEW_DUMMY_INIT (cfg, dreg, OP_DUMMY_I8CONST);
6658 } else if (cfg->r4fp && t == MONO_TYPE_R4) {
6659 MONO_EMIT_NEW_DUMMY_INIT (cfg, dreg, OP_DUMMY_R4CONST);
6660 } else if (t == MONO_TYPE_R4 || t == MONO_TYPE_R8) {
6661 MONO_EMIT_NEW_DUMMY_INIT (cfg, dreg, OP_DUMMY_R8CONST);
6662 } else if ((t == MONO_TYPE_VALUETYPE) || (t == MONO_TYPE_TYPEDBYREF) ||
6663 ((t == MONO_TYPE_GENERICINST) && mono_type_generic_inst_is_valuetype (rtype))) {
6664 MONO_EMIT_NEW_DUMMY_INIT (cfg, dreg, OP_DUMMY_VZERO);
6665 } else if (((t == MONO_TYPE_VAR) || (t == MONO_TYPE_MVAR)) && mini_type_var_is_vt (cfg, rtype)) {
6666 MONO_EMIT_NEW_DUMMY_INIT (cfg, dreg, OP_DUMMY_VZERO);
6668 emit_init_rvar (cfg, dreg, rtype);
6672 /* If INIT is FALSE, emit dummy initialization statements to keep the IR valid */
6674 emit_init_local (MonoCompile *cfg, int local, MonoType *type, gboolean init)
6676 MonoInst *var = cfg->locals [local];
6677 if (COMPILE_SOFT_FLOAT (cfg)) {
6679 int reg = alloc_dreg (cfg, var->type);
6680 emit_init_rvar (cfg, reg, type);
6681 EMIT_NEW_LOCSTORE (cfg, store, local, cfg->cbb->last_ins);
6684 emit_init_rvar (cfg, var->dreg, type);
6686 emit_dummy_init_rvar (cfg, var->dreg, type);
6693 * Return the cost of inlining CMETHOD.
6696 inline_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **sp,
6697 guchar *ip, guint real_offset, gboolean inline_always, MonoBasicBlock **out_cbb)
6699 MonoInst *ins, *rvar = NULL;
6700 MonoMethodHeader *cheader;
6701 MonoBasicBlock *ebblock, *sbblock;
6703 MonoMethod *prev_inlined_method;
6704 MonoInst **prev_locals, **prev_args;
6705 MonoType **prev_arg_types;
6706 guint prev_real_offset;
6707 GHashTable *prev_cbb_hash;
6708 MonoBasicBlock **prev_cil_offset_to_bb;
6709 MonoBasicBlock *prev_cbb;
6710 unsigned char* prev_cil_start;
6711 guint32 prev_cil_offset_to_bb_len;
6712 MonoMethod *prev_current_method;
6713 MonoGenericContext *prev_generic_context;
6714 gboolean ret_var_set, prev_ret_var_set, prev_disable_inline, virtual = FALSE;
6716 g_assert (cfg->exception_type == MONO_EXCEPTION_NONE);
6718 #if (MONO_INLINE_CALLED_LIMITED_METHODS)
6719 if ((! inline_always) && ! check_inline_called_method_name_limit (cmethod))
6722 #if (MONO_INLINE_CALLER_LIMITED_METHODS)
6723 if ((! inline_always) && ! check_inline_caller_method_name_limit (cfg->method))
6727 if (cfg->verbose_level > 2)
6728 printf ("INLINE START %p %s -> %s\n", cmethod, mono_method_full_name (cfg->method, TRUE), mono_method_full_name (cmethod, TRUE));
6730 if (!cmethod->inline_info) {
6731 cfg->stat_inlineable_methods++;
6732 cmethod->inline_info = 1;
6735 /* allocate local variables */
6736 cheader = mono_method_get_header (cmethod);
6738 if (cheader == NULL || mono_loader_get_last_error ()) {
6739 MonoLoaderError *error = mono_loader_get_last_error ();
6742 mono_metadata_free_mh (cheader);
6743 if (inline_always && error)
6744 mono_cfg_set_exception (cfg, error->exception_type);
6746 mono_loader_clear_error ();
6750 /*Must verify before creating locals as it can cause the JIT to assert.*/
6751 if (mono_compile_is_broken (cfg, cmethod, FALSE)) {
6752 mono_metadata_free_mh (cheader);
6756 /* allocate space to store the return value */
6757 if (!MONO_TYPE_IS_VOID (fsig->ret)) {
6758 rvar = mono_compile_create_var (cfg, fsig->ret, OP_LOCAL);
6761 prev_locals = cfg->locals;
6762 cfg->locals = mono_mempool_alloc0 (cfg->mempool, cheader->num_locals * sizeof (MonoInst*));
6763 for (i = 0; i < cheader->num_locals; ++i)
6764 cfg->locals [i] = mono_compile_create_var (cfg, cheader->locals [i], OP_LOCAL);
6766 /* allocate start and end blocks */
6767 /* This is needed so if the inline is aborted, we can clean up */
6768 NEW_BBLOCK (cfg, sbblock);
6769 sbblock->real_offset = real_offset;
6771 NEW_BBLOCK (cfg, ebblock);
6772 ebblock->block_num = cfg->num_bblocks++;
6773 ebblock->real_offset = real_offset;
6775 prev_args = cfg->args;
6776 prev_arg_types = cfg->arg_types;
6777 prev_inlined_method = cfg->inlined_method;
6778 cfg->inlined_method = cmethod;
6779 cfg->ret_var_set = FALSE;
6780 cfg->inline_depth ++;
6781 prev_real_offset = cfg->real_offset;
6782 prev_cbb_hash = cfg->cbb_hash;
6783 prev_cil_offset_to_bb = cfg->cil_offset_to_bb;
6784 prev_cil_offset_to_bb_len = cfg->cil_offset_to_bb_len;
6785 prev_cil_start = cfg->cil_start;
6786 prev_cbb = cfg->cbb;
6787 prev_current_method = cfg->current_method;
6788 prev_generic_context = cfg->generic_context;
6789 prev_ret_var_set = cfg->ret_var_set;
6790 prev_disable_inline = cfg->disable_inline;
6792 if (*ip == CEE_CALLVIRT && !(cmethod->flags & METHOD_ATTRIBUTE_STATIC))
6795 costs = mono_method_to_ir (cfg, cmethod, sbblock, ebblock, rvar, sp, real_offset, virtual);
6797 ret_var_set = cfg->ret_var_set;
6799 cfg->inlined_method = prev_inlined_method;
6800 cfg->real_offset = prev_real_offset;
6801 cfg->cbb_hash = prev_cbb_hash;
6802 cfg->cil_offset_to_bb = prev_cil_offset_to_bb;
6803 cfg->cil_offset_to_bb_len = prev_cil_offset_to_bb_len;
6804 cfg->cil_start = prev_cil_start;
6805 cfg->locals = prev_locals;
6806 cfg->args = prev_args;
6807 cfg->arg_types = prev_arg_types;
6808 cfg->current_method = prev_current_method;
6809 cfg->generic_context = prev_generic_context;
6810 cfg->ret_var_set = prev_ret_var_set;
6811 cfg->disable_inline = prev_disable_inline;
6812 cfg->inline_depth --;
6814 if ((costs >= 0 && costs < 60) || inline_always) {
6815 if (cfg->verbose_level > 2)
6816 printf ("INLINE END %s -> %s\n", mono_method_full_name (cfg->method, TRUE), mono_method_full_name (cmethod, TRUE));
6818 cfg->stat_inlined_methods++;
6820 /* always add some code to avoid block split failures */
6821 MONO_INST_NEW (cfg, ins, OP_NOP);
6822 MONO_ADD_INS (prev_cbb, ins);
6824 prev_cbb->next_bb = sbblock;
6825 link_bblock (cfg, prev_cbb, sbblock);
6828 * Get rid of the begin and end bblocks if possible to aid local
6831 mono_merge_basic_blocks (cfg, prev_cbb, sbblock);
6833 if ((prev_cbb->out_count == 1) && (prev_cbb->out_bb [0]->in_count == 1) && (prev_cbb->out_bb [0] != ebblock))
6834 mono_merge_basic_blocks (cfg, prev_cbb, prev_cbb->out_bb [0]);
6836 if ((ebblock->in_count == 1) && ebblock->in_bb [0]->out_count == 1) {
6837 MonoBasicBlock *prev = ebblock->in_bb [0];
6838 mono_merge_basic_blocks (cfg, prev, ebblock);
6840 if ((prev_cbb->out_count == 1) && (prev_cbb->out_bb [0]->in_count == 1) && (prev_cbb->out_bb [0] == prev)) {
6841 mono_merge_basic_blocks (cfg, prev_cbb, prev);
6842 cfg->cbb = prev_cbb;
6846 * Its possible that the rvar is set in some prev bblock, but not in others.
6852 for (i = 0; i < ebblock->in_count; ++i) {
6853 bb = ebblock->in_bb [i];
6855 if (bb->last_ins && bb->last_ins->opcode == OP_NOT_REACHED) {
6858 emit_init_rvar (cfg, rvar->dreg, fsig->ret);
6866 *out_cbb = cfg->cbb;
6870 * If the inlined method contains only a throw, then the ret var is not
6871 * set, so set it to a dummy value.
6874 emit_init_rvar (cfg, rvar->dreg, fsig->ret);
6876 EMIT_NEW_TEMPLOAD (cfg, ins, rvar->inst_c0);
6879 cfg->headers_to_free = g_slist_prepend_mempool (cfg->mempool, cfg->headers_to_free, cheader);
6882 if (cfg->verbose_level > 2)
6883 printf ("INLINE ABORTED %s (cost %d)\n", mono_method_full_name (cmethod, TRUE), costs);
6884 cfg->exception_type = MONO_EXCEPTION_NONE;
6885 mono_loader_clear_error ();
6887 /* This gets rid of the newly added bblocks */
6888 cfg->cbb = prev_cbb;
6890 cfg->headers_to_free = g_slist_prepend_mempool (cfg->mempool, cfg->headers_to_free, cheader);
6895 * Some of these comments may well be out-of-date.
6896 * Design decisions: we do a single pass over the IL code (and we do bblock
6897 * splitting/merging in the few cases when it's required: a back jump to an IL
6898 * address that was not already seen as bblock starting point).
6899 * Code is validated as we go (full verification is still better left to metadata/verify.c).
6900 * Complex operations are decomposed in simpler ones right away. We need to let the
6901 * arch-specific code peek and poke inside this process somehow (except when the
6902 * optimizations can take advantage of the full semantic info of coarse opcodes).
6903 * All the opcodes of the form opcode.s are 'normalized' to opcode.
6904 * MonoInst->opcode initially is the IL opcode or some simplification of that
6905 * (OP_LOAD, OP_STORE). The arch-specific code may rearrange it to an arch-specific
6906 * opcode with value bigger than OP_LAST.
6907 * At this point the IR can be handed over to an interpreter, a dumb code generator
6908 * or to the optimizing code generator that will translate it to SSA form.
6910 * Profiling directed optimizations.
6911 * We may compile by default with few or no optimizations and instrument the code
6912 * or the user may indicate what methods to optimize the most either in a config file
6913 * or through repeated runs where the compiler applies offline the optimizations to
6914 * each method and then decides if it was worth it.
6917 #define CHECK_TYPE(ins) if (!(ins)->type) UNVERIFIED
6918 #define CHECK_STACK(num) if ((sp - stack_start) < (num)) UNVERIFIED
6919 #define CHECK_STACK_OVF(num) if (((sp - stack_start) + (num)) > header->max_stack) UNVERIFIED
6920 #define CHECK_ARG(num) if ((unsigned)(num) >= (unsigned)num_args) UNVERIFIED
6921 #define CHECK_LOCAL(num) if ((unsigned)(num) >= (unsigned)header->num_locals) UNVERIFIED
6922 #define CHECK_OPSIZE(size) if (ip + size > end) UNVERIFIED
6923 #define CHECK_UNVERIFIABLE(cfg) if (cfg->unverifiable) UNVERIFIED
6924 #define CHECK_TYPELOAD(klass) if (!(klass) || (klass)->exception_type) TYPE_LOAD_ERROR ((klass))
6926 /* offset from br.s -> br like opcodes */
6927 #define BIG_BRANCH_OFFSET 13
6930 ip_in_bb (MonoCompile *cfg, MonoBasicBlock *bb, const guint8* ip)
6932 MonoBasicBlock *b = cfg->cil_offset_to_bb [ip - cfg->cil_start];
6934 return b == NULL || b == bb;
6938 get_basic_blocks (MonoCompile *cfg, MonoMethodHeader* header, guint real_offset, unsigned char *start, unsigned char *end, unsigned char **pos)
6940 unsigned char *ip = start;
6941 unsigned char *target;
6944 MonoBasicBlock *bblock;
6945 const MonoOpcode *opcode;
6948 cli_addr = ip - start;
6949 i = mono_opcode_value ((const guint8 **)&ip, end);
6952 opcode = &mono_opcodes [i];
6953 switch (opcode->argument) {
6954 case MonoInlineNone:
6957 case MonoInlineString:
6958 case MonoInlineType:
6959 case MonoInlineField:
6960 case MonoInlineMethod:
6963 case MonoShortInlineR:
6970 case MonoShortInlineVar:
6971 case MonoShortInlineI:
6974 case MonoShortInlineBrTarget:
6975 target = start + cli_addr + 2 + (signed char)ip [1];
6976 GET_BBLOCK (cfg, bblock, target);
6979 GET_BBLOCK (cfg, bblock, ip);
6981 case MonoInlineBrTarget:
6982 target = start + cli_addr + 5 + (gint32)read32 (ip + 1);
6983 GET_BBLOCK (cfg, bblock, target);
6986 GET_BBLOCK (cfg, bblock, ip);
6988 case MonoInlineSwitch: {
6989 guint32 n = read32 (ip + 1);
6992 cli_addr += 5 + 4 * n;
6993 target = start + cli_addr;
6994 GET_BBLOCK (cfg, bblock, target);
6996 for (j = 0; j < n; ++j) {
6997 target = start + cli_addr + (gint32)read32 (ip);
6998 GET_BBLOCK (cfg, bblock, target);
7008 g_assert_not_reached ();
7011 if (i == CEE_THROW) {
7012 unsigned char *bb_start = ip - 1;
7014 /* Find the start of the bblock containing the throw */
7016 while ((bb_start >= start) && !bblock) {
7017 bblock = cfg->cil_offset_to_bb [(bb_start) - start];
7021 bblock->out_of_line = 1;
7031 static inline MonoMethod *
7032 mini_get_method_allow_open (MonoMethod *m, guint32 token, MonoClass *klass, MonoGenericContext *context)
7036 if (m->wrapper_type != MONO_WRAPPER_NONE) {
7037 method = mono_method_get_wrapper_data (m, token);
7040 method = mono_class_inflate_generic_method_checked (method, context, &error);
7041 g_assert (mono_error_ok (&error)); /* FIXME don't swallow the error */
7044 method = mono_get_method_full (m->klass->image, token, klass, context);
7050 static inline MonoMethod *
7051 mini_get_method (MonoCompile *cfg, MonoMethod *m, guint32 token, MonoClass *klass, MonoGenericContext *context)
7053 MonoMethod *method = mini_get_method_allow_open (m, token, klass, context);
7055 if (method && cfg && !cfg->generic_sharing_context && mono_class_is_open_constructed_type (&method->klass->byval_arg))
7061 static inline MonoClass*
7062 mini_get_class (MonoMethod *method, guint32 token, MonoGenericContext *context)
7067 if (method->wrapper_type != MONO_WRAPPER_NONE) {
7068 klass = mono_method_get_wrapper_data (method, token);
7070 klass = mono_class_inflate_generic_class (klass, context);
7072 klass = mono_class_get_and_inflate_typespec_checked (method->klass->image, token, context, &error);
7073 mono_error_cleanup (&error); /* FIXME don't swallow the error */
7076 mono_class_init (klass);
7080 static inline MonoMethodSignature*
7081 mini_get_signature (MonoMethod *method, guint32 token, MonoGenericContext *context)
7083 MonoMethodSignature *fsig;
7085 if (method->wrapper_type != MONO_WRAPPER_NONE) {
7088 fsig = (MonoMethodSignature *)mono_method_get_wrapper_data (method, token);
7090 fsig = mono_inflate_generic_signature (fsig, context, &error);
7092 g_assert (mono_error_ok (&error));
7095 fsig = mono_metadata_parse_signature (method->klass->image, token);
7101 * Returns TRUE if the JIT should abort inlining because "callee"
7102 * is influenced by security attributes.
7105 gboolean check_linkdemand (MonoCompile *cfg, MonoMethod *caller, MonoMethod *callee)
7109 if ((cfg->method != caller) && mono_security_method_has_declsec (callee)) {
7113 result = mono_declsec_linkdemand (cfg->domain, caller, callee);
7114 if (result == MONO_JIT_SECURITY_OK)
7117 if (result == MONO_JIT_LINKDEMAND_ECMA) {
7118 /* Generate code to throw a SecurityException before the actual call/link */
7119 MonoSecurityManager *secman = mono_security_manager_get_methods ();
7122 NEW_ICONST (cfg, args [0], 4);
7123 NEW_METHODCONST (cfg, args [1], caller);
7124 mono_emit_method_call (cfg, secman->linkdemandsecurityexception, args, NULL);
7125 } else if (cfg->exception_type == MONO_EXCEPTION_NONE) {
7126 /* don't hide previous results */
7127 mono_cfg_set_exception (cfg, MONO_EXCEPTION_SECURITY_LINKDEMAND);
7128 cfg->exception_data = result;
7136 throw_exception (void)
7138 static MonoMethod *method = NULL;
7141 MonoSecurityManager *secman = mono_security_manager_get_methods ();
7142 method = mono_class_get_method_from_name (secman->securitymanager, "ThrowException", 1);
7149 emit_throw_exception (MonoCompile *cfg, MonoException *ex)
7151 MonoMethod *thrower = throw_exception ();
7154 EMIT_NEW_PCONST (cfg, args [0], ex);
7155 mono_emit_method_call (cfg, thrower, args, NULL);
7159 * Return the original method is a wrapper is specified. We can only access
7160 * the custom attributes from the original method.
7163 get_original_method (MonoMethod *method)
7165 if (method->wrapper_type == MONO_WRAPPER_NONE)
7168 /* native code (which is like Critical) can call any managed method XXX FIXME XXX to validate all usages */
7169 if (method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED)
7172 /* in other cases we need to find the original method */
7173 return mono_marshal_method_from_wrapper (method);
7177 ensure_method_is_allowed_to_access_field (MonoCompile *cfg, MonoMethod *caller, MonoClassField *field,
7178 MonoBasicBlock *bblock, unsigned char *ip)
7180 /* we can't get the coreclr security level on wrappers since they don't have the attributes */
7181 MonoException *ex = mono_security_core_clr_is_field_access_allowed (get_original_method (caller), field);
7183 emit_throw_exception (cfg, ex);
7187 ensure_method_is_allowed_to_call_method (MonoCompile *cfg, MonoMethod *caller, MonoMethod *callee,
7188 MonoBasicBlock *bblock, unsigned char *ip)
7190 /* we can't get the coreclr security level on wrappers since they don't have the attributes */
7191 MonoException *ex = mono_security_core_clr_is_call_allowed (get_original_method (caller), callee);
7193 emit_throw_exception (cfg, ex);
7197 * Check that the IL instructions at ip are the array initialization
7198 * sequence and return the pointer to the data and the size.
7201 initialize_array_data (MonoMethod *method, gboolean aot, unsigned char *ip, MonoClass *klass, guint32 len, int *out_size, guint32 *out_field_token)
7204 * newarr[System.Int32]
7206 * ldtoken field valuetype ...
7207 * call void class [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(class [mscorlib]System.Array, valuetype [mscorlib]System.RuntimeFieldHandle)
7209 if (ip [0] == CEE_DUP && ip [1] == CEE_LDTOKEN && ip [5] == 0x4 && ip [6] == CEE_CALL) {
7211 guint32 token = read32 (ip + 7);
7212 guint32 field_token = read32 (ip + 2);
7213 guint32 field_index = field_token & 0xffffff;
7215 const char *data_ptr;
7217 MonoMethod *cmethod;
7218 MonoClass *dummy_class;
7219 MonoClassField *field = mono_field_from_token_checked (method->klass->image, field_token, &dummy_class, NULL, &error);
7223 mono_error_cleanup (&error); /* FIXME don't swallow the error */
7227 *out_field_token = field_token;
7229 cmethod = mini_get_method (NULL, method, token, NULL, NULL);
7232 if (strcmp (cmethod->name, "InitializeArray") || strcmp (cmethod->klass->name, "RuntimeHelpers") || cmethod->klass->image != mono_defaults.corlib)
7234 switch (mono_type_get_underlying_type (&klass->byval_arg)->type) {
7235 case MONO_TYPE_BOOLEAN:
7239 /* we need to swap on big endian, so punt. Should we handle R4 and R8 as well? */
7240 #if TARGET_BYTE_ORDER == G_LITTLE_ENDIAN
7241 case MONO_TYPE_CHAR:
7258 if (size > mono_type_size (field->type, &dummy_align))
7261 /*g_print ("optimized in %s: size: %d, numelems: %d\n", method->name, size, newarr->inst_newa_len->inst_c0);*/
7262 if (!image_is_dynamic (method->klass->image)) {
7263 field_index = read32 (ip + 2) & 0xffffff;
7264 mono_metadata_field_info (method->klass->image, field_index - 1, NULL, &rva, NULL);
7265 data_ptr = mono_image_rva_map (method->klass->image, rva);
7266 /*g_print ("field: 0x%08x, rva: %d, rva_ptr: %p\n", read32 (ip + 2), rva, data_ptr);*/
7267 /* for aot code we do the lookup on load */
7268 if (aot && data_ptr)
7269 return GUINT_TO_POINTER (rva);
7271 /*FIXME is it possible to AOT a SRE assembly not meant to be saved? */
7273 data_ptr = mono_field_get_data (field);
7281 set_exception_type_from_invalid_il (MonoCompile *cfg, MonoMethod *method, unsigned char *ip)
7283 char *method_fname = mono_method_full_name (method, TRUE);
7285 MonoMethodHeader *header = mono_method_get_header (method);
7287 if (header->code_size == 0)
7288 method_code = g_strdup ("method body is empty.");
7290 method_code = mono_disasm_code_one (NULL, method, ip, NULL);
7291 mono_cfg_set_exception (cfg, MONO_EXCEPTION_INVALID_PROGRAM);
7292 cfg->exception_message = g_strdup_printf ("Invalid IL code in %s: %s\n", method_fname, method_code);
7293 g_free (method_fname);
7294 g_free (method_code);
7295 cfg->headers_to_free = g_slist_prepend_mempool (cfg->mempool, cfg->headers_to_free, header);
7299 set_exception_object (MonoCompile *cfg, MonoException *exception)
7301 mono_cfg_set_exception (cfg, MONO_EXCEPTION_OBJECT_SUPPLIED);
7302 MONO_GC_REGISTER_ROOT_SINGLE (cfg->exception_ptr);
7303 cfg->exception_ptr = exception;
7307 emit_stloc_ir (MonoCompile *cfg, MonoInst **sp, MonoMethodHeader *header, int n)
7310 guint32 opcode = mono_type_to_regmove (cfg, header->locals [n]);
7311 if ((opcode == OP_MOVE) && cfg->cbb->last_ins == sp [0] &&
7312 ((sp [0]->opcode == OP_ICONST) || (sp [0]->opcode == OP_I8CONST))) {
7313 /* Optimize reg-reg moves away */
7315 * Can't optimize other opcodes, since sp[0] might point to
7316 * the last ins of a decomposed opcode.
7318 sp [0]->dreg = (cfg)->locals [n]->dreg;
7320 EMIT_NEW_LOCSTORE (cfg, ins, n, *sp);
7325 * ldloca inhibits many optimizations so try to get rid of it in common
7328 static inline unsigned char *
7329 emit_optimized_ldloca_ir (MonoCompile *cfg, unsigned char *ip, unsigned char *end, int size)
7339 local = read16 (ip + 2);
7343 if (ip + 6 < end && (ip [0] == CEE_PREFIX1) && (ip [1] == CEE_INITOBJ) && ip_in_bb (cfg, cfg->cbb, ip + 1)) {
7344 /* From the INITOBJ case */
7345 token = read32 (ip + 2);
7346 klass = mini_get_class (cfg->current_method, token, cfg->generic_context);
7347 CHECK_TYPELOAD (klass);
7348 type = mini_replace_type (&klass->byval_arg);
7349 emit_init_local (cfg, local, type, TRUE);
7357 is_exception_class (MonoClass *class)
7360 if (class == mono_defaults.exception_class)
7362 class = class->parent;
7368 * is_jit_optimizer_disabled:
7370 * Determine whenever M's assembly has a DebuggableAttribute with the
7371 * IsJITOptimizerDisabled flag set.
7374 is_jit_optimizer_disabled (MonoMethod *m)
7376 MonoAssembly *ass = m->klass->image->assembly;
7377 MonoCustomAttrInfo* attrs;
7378 static MonoClass *klass;
7380 gboolean val = FALSE;
7383 if (ass->jit_optimizer_disabled_inited)
7384 return ass->jit_optimizer_disabled;
7387 klass = mono_class_from_name (mono_defaults.corlib, "System.Diagnostics", "DebuggableAttribute");
7390 ass->jit_optimizer_disabled = FALSE;
7391 mono_memory_barrier ();
7392 ass->jit_optimizer_disabled_inited = TRUE;
7396 attrs = mono_custom_attrs_from_assembly (ass);
7398 for (i = 0; i < attrs->num_attrs; ++i) {
7399 MonoCustomAttrEntry *attr = &attrs->attrs [i];
7402 MonoMethodSignature *sig;
7404 if (!attr->ctor || attr->ctor->klass != klass)
7406 /* Decode the attribute. See reflection.c */
7407 len = attr->data_size;
7408 p = (const char*)attr->data;
7409 g_assert (read16 (p) == 0x0001);
7412 // FIXME: Support named parameters
7413 sig = mono_method_signature (attr->ctor);
7414 if (sig->param_count != 2 || sig->params [0]->type != MONO_TYPE_BOOLEAN || sig->params [1]->type != MONO_TYPE_BOOLEAN)
7416 /* Two boolean arguments */
7420 mono_custom_attrs_free (attrs);
7423 ass->jit_optimizer_disabled = val;
7424 mono_memory_barrier ();
7425 ass->jit_optimizer_disabled_inited = TRUE;
7431 is_supported_tail_call (MonoCompile *cfg, MonoMethod *method, MonoMethod *cmethod, MonoMethodSignature *fsig, int call_opcode)
7433 gboolean supported_tail_call;
7436 #ifdef MONO_ARCH_HAVE_OP_TAIL_CALL
7437 supported_tail_call = mono_arch_tail_call_supported (cfg, mono_method_signature (method), mono_method_signature (cmethod));
7439 supported_tail_call = mono_metadata_signature_equal (mono_method_signature (method), mono_method_signature (cmethod)) && !MONO_TYPE_ISSTRUCT (mono_method_signature (cmethod)->ret);
7442 for (i = 0; i < fsig->param_count; ++i) {
7443 if (fsig->params [i]->byref || fsig->params [i]->type == MONO_TYPE_PTR || fsig->params [i]->type == MONO_TYPE_FNPTR)
7444 /* These can point to the current method's stack */
7445 supported_tail_call = FALSE;
7447 if (fsig->hasthis && cmethod->klass->valuetype)
7448 /* this might point to the current method's stack */
7449 supported_tail_call = FALSE;
7450 if (cmethod->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)
7451 supported_tail_call = FALSE;
7452 if (cfg->method->save_lmf)
7453 supported_tail_call = FALSE;
7454 if (cmethod->wrapper_type && cmethod->wrapper_type != MONO_WRAPPER_DYNAMIC_METHOD)
7455 supported_tail_call = FALSE;
7456 if (call_opcode != CEE_CALL)
7457 supported_tail_call = FALSE;
7459 /* Debugging support */
7461 if (supported_tail_call) {
7462 if (!mono_debug_count ())
7463 supported_tail_call = FALSE;
7467 return supported_tail_call;
7470 /* the JIT intercepts ldflda instructions to the tlsdata field in ThreadLocal<T> and redirects
7471 * it to the thread local value based on the tls_offset field. Every other kind of access to
7472 * the field causes an assert.
7475 is_magic_tls_access (MonoClassField *field)
7477 if (strcmp (field->name, "tlsdata"))
7479 if (strcmp (field->parent->name, "ThreadLocal`1"))
7481 return field->parent->image == mono_defaults.corlib;
7484 /* emits the code needed to access a managed tls var (like ThreadStatic)
7485 * with the value of the tls offset in offset_reg. thread_ins represents the MonoInternalThread
7486 * pointer for the current thread.
7487 * Returns the MonoInst* representing the address of the tls var.
7490 emit_managed_static_data_access (MonoCompile *cfg, MonoInst *thread_ins, int offset_reg)
7493 int static_data_reg, array_reg, dreg;
7494 int offset2_reg, idx_reg;
7495 // inlined access to the tls data
7496 // idx = (offset >> 24) - 1;
7497 // return ((char*) thread->static_data [idx]) + (offset & 0xffffff);
7498 static_data_reg = alloc_ireg (cfg);
7499 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, static_data_reg, thread_ins->dreg, MONO_STRUCT_OFFSET (MonoInternalThread, static_data));
7500 idx_reg = alloc_ireg (cfg);
7501 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ISHR_IMM, idx_reg, offset_reg, 24);
7502 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ISUB_IMM, idx_reg, idx_reg, 1);
7503 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ISHL_IMM, idx_reg, idx_reg, sizeof (gpointer) == 8 ? 3 : 2);
7504 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, static_data_reg, static_data_reg, idx_reg);
7505 array_reg = alloc_ireg (cfg);
7506 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, array_reg, static_data_reg, 0);
7507 offset2_reg = alloc_ireg (cfg);
7508 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_IAND_IMM, offset2_reg, offset_reg, 0xffffff);
7509 dreg = alloc_ireg (cfg);
7510 EMIT_NEW_BIALU (cfg, addr, OP_PADD, dreg, array_reg, offset2_reg);
7515 * redirect access to the tlsdata field to the tls var given by the tls_offset field.
7516 * this address is cached per-method in cached_tls_addr.
7519 create_magic_tls_access (MonoCompile *cfg, MonoClassField *tls_field, MonoInst **cached_tls_addr, MonoInst *thread_local)
7521 MonoInst *load, *addr, *temp, *store, *thread_ins;
7522 MonoClassField *offset_field;
7524 if (*cached_tls_addr) {
7525 EMIT_NEW_TEMPLOAD (cfg, addr, (*cached_tls_addr)->inst_c0);
7528 thread_ins = mono_get_thread_intrinsic (cfg);
7529 offset_field = mono_class_get_field_from_name (tls_field->parent, "tls_offset");
7531 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, load, offset_field->type, thread_local->dreg, offset_field->offset);
7533 MONO_ADD_INS (cfg->cbb, thread_ins);
7535 MonoMethod *thread_method;
7536 thread_method = mono_class_get_method_from_name (mono_get_thread_class(), "CurrentInternalThread_internal", 0);
7537 thread_ins = mono_emit_method_call (cfg, thread_method, NULL, NULL);
7539 addr = emit_managed_static_data_access (cfg, thread_ins, load->dreg);
7540 addr->klass = mono_class_from_mono_type (tls_field->type);
7541 addr->type = STACK_MP;
7542 *cached_tls_addr = temp = mono_compile_create_var (cfg, type_from_stack_type (addr), OP_LOCAL);
7543 EMIT_NEW_TEMPSTORE (cfg, store, temp->inst_c0, addr);
7545 EMIT_NEW_TEMPLOAD (cfg, addr, temp->inst_c0);
7552 * Handle calls made to ctors from NEWOBJ opcodes.
7554 * REF_BBLOCK will point to the current bblock after the call.
7557 handle_ctor_call (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, int context_used,
7558 MonoInst **sp, guint8 *ip, MonoBasicBlock **ref_bblock, int *inline_costs)
7560 MonoInst *vtable_arg = NULL, *callvirt_this_arg = NULL, *ins;
7561 MonoBasicBlock *bblock = *ref_bblock;
7563 if (cmethod->klass->valuetype && mono_class_generic_sharing_enabled (cmethod->klass) &&
7564 mono_method_is_generic_sharable (cmethod, TRUE)) {
7565 if (cmethod->is_inflated && mono_method_get_context (cmethod)->method_inst) {
7566 mono_class_vtable (cfg->domain, cmethod->klass);
7567 CHECK_TYPELOAD (cmethod->klass);
7569 vtable_arg = emit_get_rgctx_method (cfg, context_used,
7570 cmethod, MONO_RGCTX_INFO_METHOD_RGCTX);
7573 vtable_arg = emit_get_rgctx_klass (cfg, context_used,
7574 cmethod->klass, MONO_RGCTX_INFO_VTABLE);
7576 MonoVTable *vtable = mono_class_vtable (cfg->domain, cmethod->klass);
7578 CHECK_TYPELOAD (cmethod->klass);
7579 EMIT_NEW_VTABLECONST (cfg, vtable_arg, vtable);
7584 /* Avoid virtual calls to ctors if possible */
7585 if (mono_class_is_marshalbyref (cmethod->klass))
7586 callvirt_this_arg = sp [0];
7588 if (cmethod && (cfg->opt & MONO_OPT_INTRINS) && (ins = mini_emit_inst_for_ctor (cfg, cmethod, fsig, sp))) {
7589 g_assert (MONO_TYPE_IS_VOID (fsig->ret));
7590 CHECK_CFG_EXCEPTION;
7591 } else if ((cfg->opt & MONO_OPT_INLINE) && cmethod && !context_used && !vtable_arg &&
7592 mono_method_check_inlining (cfg, cmethod) &&
7593 !mono_class_is_subclass_of (cmethod->klass, mono_defaults.exception_class, FALSE)) {
7596 if ((costs = inline_method (cfg, cmethod, fsig, sp, ip, cfg->real_offset, FALSE, &bblock))) {
7597 cfg->real_offset += 5;
7599 *inline_costs += costs - 5;
7600 *ref_bblock = bblock;
7602 INLINE_FAILURE ("inline failure");
7603 // FIXME-VT: Clean this up
7604 if (cfg->gsharedvt && mini_is_gsharedvt_signature (cfg, fsig))
7605 GSHAREDVT_FAILURE(*ip);
7606 mono_emit_method_call_full (cfg, cmethod, fsig, FALSE, sp, callvirt_this_arg, NULL, NULL);
7608 } else if (cfg->gsharedvt && mini_is_gsharedvt_signature (cfg, fsig)) {
7611 addr = emit_get_rgctx_gsharedvt_call (cfg, context_used, fsig, cmethod, MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE);
7612 mono_emit_calli (cfg, fsig, sp, addr, NULL, vtable_arg);
7613 } else if (context_used &&
7614 ((!mono_method_is_generic_sharable_full (cmethod, TRUE, FALSE, FALSE) ||
7615 !mono_class_generic_sharing_enabled (cmethod->klass)) || cfg->gsharedvt)) {
7616 MonoInst *cmethod_addr;
7618 /* Generic calls made out of gsharedvt methods cannot be patched, so use an indirect call */
7620 cmethod_addr = emit_get_rgctx_method (cfg, context_used,
7621 cmethod, MONO_RGCTX_INFO_GENERIC_METHOD_CODE);
7623 mono_emit_calli (cfg, fsig, sp, cmethod_addr, NULL, vtable_arg);
7625 INLINE_FAILURE ("ctor call");
7626 ins = mono_emit_method_call_full (cfg, cmethod, fsig, FALSE, sp,
7627 callvirt_this_arg, NULL, vtable_arg);
7634 * mono_method_to_ir:
7636 * Translate the .net IL into linear IR.
7639 mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_bblock, MonoBasicBlock *end_bblock,
7640 MonoInst *return_var, MonoInst **inline_args,
7641 guint inline_offset, gboolean is_virtual_call)
7644 MonoInst *ins, **sp, **stack_start;
7645 MonoBasicBlock *bblock, *tblock = NULL, *init_localsbb = NULL;
7646 MonoSimpleBasicBlock *bb = NULL, *original_bb = NULL;
7647 MonoMethod *cmethod, *method_definition;
7648 MonoInst **arg_array;
7649 MonoMethodHeader *header;
7651 guint32 token, ins_flag;
7653 MonoClass *constrained_call = NULL;
7654 unsigned char *ip, *end, *target, *err_pos;
7655 MonoMethodSignature *sig;
7656 MonoGenericContext *generic_context = NULL;
7657 MonoGenericContainer *generic_container = NULL;
7658 MonoType **param_types;
7659 int i, n, start_new_bblock, dreg;
7660 int num_calls = 0, inline_costs = 0;
7661 int breakpoint_id = 0;
7663 MonoBoolean security, pinvoke;
7664 MonoSecurityManager* secman = NULL;
7665 MonoDeclSecurityActions actions;
7666 GSList *class_inits = NULL;
7667 gboolean dont_verify, dont_verify_stloc, readonly = FALSE;
7669 gboolean init_locals, seq_points, skip_dead_blocks;
7670 gboolean sym_seq_points = FALSE;
7671 MonoInst *cached_tls_addr = NULL;
7672 MonoDebugMethodInfo *minfo;
7673 MonoBitSet *seq_point_locs = NULL;
7674 MonoBitSet *seq_point_set_locs = NULL;
7676 cfg->disable_inline = is_jit_optimizer_disabled (method);
7678 /* serialization and xdomain stuff may need access to private fields and methods */
7679 dont_verify = method->klass->image->assembly->corlib_internal? TRUE: FALSE;
7680 dont_verify |= method->wrapper_type == MONO_WRAPPER_XDOMAIN_INVOKE;
7681 dont_verify |= method->wrapper_type == MONO_WRAPPER_XDOMAIN_DISPATCH;
7682 dont_verify |= method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE; /* bug #77896 */
7683 dont_verify |= method->wrapper_type == MONO_WRAPPER_COMINTEROP;
7684 dont_verify |= method->wrapper_type == MONO_WRAPPER_COMINTEROP_INVOKE;
7686 dont_verify |= mono_security_smcs_hack_enabled ();
7688 /* still some type unsafety issues in marshal wrappers... (unknown is PtrToStructure) */
7689 dont_verify_stloc = method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE;
7690 dont_verify_stloc |= method->wrapper_type == MONO_WRAPPER_UNKNOWN;
7691 dont_verify_stloc |= method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED;
7692 dont_verify_stloc |= method->wrapper_type == MONO_WRAPPER_STELEMREF;
7694 image = method->klass->image;
7695 header = mono_method_get_header (method);
7697 MonoLoaderError *error;
7699 if ((error = mono_loader_get_last_error ())) {
7700 mono_cfg_set_exception (cfg, error->exception_type);
7702 mono_cfg_set_exception (cfg, MONO_EXCEPTION_INVALID_PROGRAM);
7703 cfg->exception_message = g_strdup_printf ("Missing or incorrect header for method %s", cfg->method->name);
7705 goto exception_exit;
7707 generic_container = mono_method_get_generic_container (method);
7708 sig = mono_method_signature (method);
7709 num_args = sig->hasthis + sig->param_count;
7710 ip = (unsigned char*)header->code;
7711 cfg->cil_start = ip;
7712 end = ip + header->code_size;
7713 cfg->stat_cil_code_size += header->code_size;
7715 seq_points = cfg->gen_seq_points && cfg->method == method;
7716 #ifdef PLATFORM_ANDROID
7717 seq_points &= cfg->method->wrapper_type == MONO_WRAPPER_NONE;
7720 if (method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED) {
7721 /* We could hit a seq point before attaching to the JIT (#8338) */
7725 if (cfg->gen_seq_points_debug_data && cfg->method == method) {
7726 minfo = mono_debug_lookup_method (method);
7728 int i, n_il_offsets;
7732 mono_debug_symfile_get_line_numbers_full (minfo, NULL, NULL, &n_il_offsets, &il_offsets, &line_numbers, NULL, NULL, NULL, NULL);
7733 seq_point_locs = mono_bitset_mem_new (mono_mempool_alloc0 (cfg->mempool, mono_bitset_alloc_size (header->code_size, 0)), header->code_size, 0);
7734 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);
7735 sym_seq_points = TRUE;
7736 for (i = 0; i < n_il_offsets; ++i) {
7737 if (il_offsets [i] < header->code_size)
7738 mono_bitset_set_fast (seq_point_locs, il_offsets [i]);
7740 g_free (il_offsets);
7741 g_free (line_numbers);
7742 } else if (!method->wrapper_type && !method->dynamic && mono_debug_image_has_debug_info (method->klass->image)) {
7743 /* Methods without line number info like auto-generated property accessors */
7744 seq_point_locs = mono_bitset_mem_new (mono_mempool_alloc0 (cfg->mempool, mono_bitset_alloc_size (header->code_size, 0)), header->code_size, 0);
7745 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);
7746 sym_seq_points = TRUE;
7751 * Methods without init_locals set could cause asserts in various passes
7752 * (#497220). To work around this, we emit dummy initialization opcodes
7753 * (OP_DUMMY_ICONST etc.) which generate no code. These are only supported
7754 * on some platforms.
7756 if ((cfg->opt & MONO_OPT_UNSAFE) && ARCH_HAVE_DUMMY_INIT)
7757 init_locals = header->init_locals;
7761 method_definition = method;
7762 while (method_definition->is_inflated) {
7763 MonoMethodInflated *imethod = (MonoMethodInflated *) method_definition;
7764 method_definition = imethod->declaring;
7767 /* SkipVerification is not allowed if core-clr is enabled */
7768 if (!dont_verify && mini_assembly_can_skip_verification (cfg->domain, method)) {
7770 dont_verify_stloc = TRUE;
7773 if (sig->is_inflated)
7774 generic_context = mono_method_get_context (method);
7775 else if (generic_container)
7776 generic_context = &generic_container->context;
7777 cfg->generic_context = generic_context;
7779 if (!cfg->generic_sharing_context)
7780 g_assert (!sig->has_type_parameters);
7782 if (sig->generic_param_count && method->wrapper_type == MONO_WRAPPER_NONE) {
7783 g_assert (method->is_inflated);
7784 g_assert (mono_method_get_context (method)->method_inst);
7786 if (method->is_inflated && mono_method_get_context (method)->method_inst)
7787 g_assert (sig->generic_param_count);
7789 if (cfg->method == method) {
7790 cfg->real_offset = 0;
7792 cfg->real_offset = inline_offset;
7795 cfg->cil_offset_to_bb = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoBasicBlock*) * header->code_size);
7796 cfg->cil_offset_to_bb_len = header->code_size;
7798 cfg->current_method = method;
7800 if (cfg->verbose_level > 2)
7801 printf ("method to IR %s\n", mono_method_full_name (method, TRUE));
7803 param_types = mono_mempool_alloc (cfg->mempool, sizeof (MonoType*) * num_args);
7805 param_types [0] = method->klass->valuetype?&method->klass->this_arg:&method->klass->byval_arg;
7806 for (n = 0; n < sig->param_count; ++n)
7807 param_types [n + sig->hasthis] = sig->params [n];
7808 cfg->arg_types = param_types;
7810 cfg->dont_inline = g_list_prepend (cfg->dont_inline, method);
7811 if (cfg->method == method) {
7813 if (cfg->prof_options & MONO_PROFILE_INS_COVERAGE)
7814 cfg->coverage_info = mono_profiler_coverage_alloc (cfg->method, header->code_size);
7817 NEW_BBLOCK (cfg, start_bblock);
7818 cfg->bb_entry = start_bblock;
7819 start_bblock->cil_code = NULL;
7820 start_bblock->cil_length = 0;
7821 #if defined(__native_client_codegen__)
7822 MONO_INST_NEW (cfg, ins, OP_NACL_GC_SAFE_POINT);
7823 ins->dreg = alloc_dreg (cfg, STACK_I4);
7824 MONO_ADD_INS (start_bblock, ins);
7828 NEW_BBLOCK (cfg, end_bblock);
7829 cfg->bb_exit = end_bblock;
7830 end_bblock->cil_code = NULL;
7831 end_bblock->cil_length = 0;
7832 end_bblock->flags |= BB_INDIRECT_JUMP_TARGET;
7833 g_assert (cfg->num_bblocks == 2);
7835 arg_array = cfg->args;
7837 if (header->num_clauses) {
7838 cfg->spvars = g_hash_table_new (NULL, NULL);
7839 cfg->exvars = g_hash_table_new (NULL, NULL);
7841 /* handle exception clauses */
7842 for (i = 0; i < header->num_clauses; ++i) {
7843 MonoBasicBlock *try_bb;
7844 MonoExceptionClause *clause = &header->clauses [i];
7845 GET_BBLOCK (cfg, try_bb, ip + clause->try_offset);
7846 try_bb->real_offset = clause->try_offset;
7847 try_bb->try_start = TRUE;
7848 try_bb->region = ((i + 1) << 8) | clause->flags;
7849 GET_BBLOCK (cfg, tblock, ip + clause->handler_offset);
7850 tblock->real_offset = clause->handler_offset;
7851 tblock->flags |= BB_EXCEPTION_HANDLER;
7854 * Linking the try block with the EH block hinders inlining as we won't be able to
7855 * merge the bblocks from inlining and produce an artificial hole for no good reason.
7857 if (COMPILE_LLVM (cfg))
7858 link_bblock (cfg, try_bb, tblock);
7860 if (*(ip + clause->handler_offset) == CEE_POP)
7861 tblock->flags |= BB_EXCEPTION_DEAD_OBJ;
7863 if (clause->flags == MONO_EXCEPTION_CLAUSE_FINALLY ||
7864 clause->flags == MONO_EXCEPTION_CLAUSE_FILTER ||
7865 clause->flags == MONO_EXCEPTION_CLAUSE_FAULT) {
7866 MONO_INST_NEW (cfg, ins, OP_START_HANDLER);
7867 MONO_ADD_INS (tblock, ins);
7869 if (seq_points && clause->flags != MONO_EXCEPTION_CLAUSE_FINALLY) {
7870 /* finally clauses already have a seq point */
7871 NEW_SEQ_POINT (cfg, ins, clause->handler_offset, TRUE);
7872 MONO_ADD_INS (tblock, ins);
7875 /* todo: is a fault block unsafe to optimize? */
7876 if (clause->flags == MONO_EXCEPTION_CLAUSE_FAULT)
7877 tblock->flags |= BB_EXCEPTION_UNSAFE;
7881 /*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);
7883 printf ("%s", mono_disasm_code_one (NULL, method, p, &p));
7885 /* catch and filter blocks get the exception object on the stack */
7886 if (clause->flags == MONO_EXCEPTION_CLAUSE_NONE ||
7887 clause->flags == MONO_EXCEPTION_CLAUSE_FILTER) {
7888 MonoInst *dummy_use;
7890 /* mostly like handle_stack_args (), but just sets the input args */
7891 /* printf ("handling clause at IL_%04x\n", clause->handler_offset); */
7892 tblock->in_scount = 1;
7893 tblock->in_stack = mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*));
7894 tblock->in_stack [0] = mono_create_exvar_for_offset (cfg, clause->handler_offset);
7897 * Add a dummy use for the exvar so its liveness info will be
7901 EMIT_NEW_DUMMY_USE (cfg, dummy_use, tblock->in_stack [0]);
7903 if (clause->flags == MONO_EXCEPTION_CLAUSE_FILTER) {
7904 GET_BBLOCK (cfg, tblock, ip + clause->data.filter_offset);
7905 tblock->flags |= BB_EXCEPTION_HANDLER;
7906 tblock->real_offset = clause->data.filter_offset;
7907 tblock->in_scount = 1;
7908 tblock->in_stack = mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*));
7909 /* The filter block shares the exvar with the handler block */
7910 tblock->in_stack [0] = mono_create_exvar_for_offset (cfg, clause->handler_offset);
7911 MONO_INST_NEW (cfg, ins, OP_START_HANDLER);
7912 MONO_ADD_INS (tblock, ins);
7916 if (clause->flags != MONO_EXCEPTION_CLAUSE_FILTER &&
7917 clause->data.catch_class &&
7918 cfg->generic_sharing_context &&
7919 mono_class_check_context_used (clause->data.catch_class)) {
7921 * In shared generic code with catch
7922 * clauses containing type variables
7923 * the exception handling code has to
7924 * be able to get to the rgctx.
7925 * Therefore we have to make sure that
7926 * the vtable/mrgctx argument (for
7927 * static or generic methods) or the
7928 * "this" argument (for non-static
7929 * methods) are live.
7931 if ((method->flags & METHOD_ATTRIBUTE_STATIC) ||
7932 mini_method_get_context (method)->method_inst ||
7933 method->klass->valuetype) {
7934 mono_get_vtable_var (cfg);
7936 MonoInst *dummy_use;
7938 EMIT_NEW_DUMMY_USE (cfg, dummy_use, arg_array [0]);
7943 arg_array = (MonoInst **) alloca (sizeof (MonoInst *) * num_args);
7944 cfg->cbb = start_bblock;
7945 cfg->args = arg_array;
7946 mono_save_args (cfg, sig, inline_args);
7949 /* FIRST CODE BLOCK */
7950 NEW_BBLOCK (cfg, bblock);
7951 bblock->cil_code = ip;
7955 ADD_BBLOCK (cfg, bblock);
7957 if (cfg->method == method) {
7958 breakpoint_id = mono_debugger_method_has_breakpoint (method);
7959 if (breakpoint_id) {
7960 MONO_INST_NEW (cfg, ins, OP_BREAK);
7961 MONO_ADD_INS (bblock, ins);
7965 if (mono_security_cas_enabled ())
7966 secman = mono_security_manager_get_methods ();
7968 security = (secman && mono_security_method_has_declsec (method));
7969 /* at this point having security doesn't mean we have any code to generate */
7970 if (security && (cfg->method == method)) {
7971 /* Only Demand, NonCasDemand and DemandChoice requires code generation.
7972 * And we do not want to enter the next section (with allocation) if we
7973 * have nothing to generate */
7974 security = mono_declsec_get_demands (method, &actions);
7977 /* we must Demand SecurityPermission.Unmanaged before P/Invoking */
7978 pinvoke = (secman && (method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE));
7980 MonoMethod *wrapped = mono_marshal_method_from_wrapper (method);
7981 if (wrapped && (wrapped->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)) {
7982 MonoCustomAttrInfo* custom = mono_custom_attrs_from_method (wrapped);
7984 /* unless the method or it's class has the [SuppressUnmanagedCodeSecurity] attribute */
7985 if (custom && mono_custom_attrs_has_attr (custom, secman->suppressunmanagedcodesecurity)) {
7989 mono_custom_attrs_free (custom);
7992 custom = mono_custom_attrs_from_class (wrapped->klass);
7993 if (custom && mono_custom_attrs_has_attr (custom, secman->suppressunmanagedcodesecurity)) {
7997 mono_custom_attrs_free (custom);
8000 /* not a P/Invoke after all */
8005 /* we use a separate basic block for the initialization code */
8006 NEW_BBLOCK (cfg, init_localsbb);
8007 cfg->bb_init = init_localsbb;
8008 init_localsbb->real_offset = cfg->real_offset;
8009 start_bblock->next_bb = init_localsbb;
8010 init_localsbb->next_bb = bblock;
8011 link_bblock (cfg, start_bblock, init_localsbb);
8012 link_bblock (cfg, init_localsbb, bblock);
8014 cfg->cbb = init_localsbb;
8016 if (cfg->gsharedvt && cfg->method == method) {
8017 MonoGSharedVtMethodInfo *info;
8018 MonoInst *var, *locals_var;
8021 info = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoGSharedVtMethodInfo));
8022 info->method = cfg->method;
8023 info->count_entries = 16;
8024 info->entries = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoRuntimeGenericContextInfoTemplate) * info->count_entries);
8025 cfg->gsharedvt_info = info;
8027 var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
8028 /* prevent it from being register allocated */
8029 //var->flags |= MONO_INST_VOLATILE;
8030 cfg->gsharedvt_info_var = var;
8032 ins = emit_get_rgctx_gsharedvt_method (cfg, mini_method_check_context_used (cfg, method), method, info);
8033 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, var->dreg, ins->dreg);
8035 /* Allocate locals */
8036 locals_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
8037 /* prevent it from being register allocated */
8038 //locals_var->flags |= MONO_INST_VOLATILE;
8039 cfg->gsharedvt_locals_var = locals_var;
8041 dreg = alloc_ireg (cfg);
8042 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI4_MEMBASE, dreg, var->dreg, MONO_STRUCT_OFFSET (MonoGSharedVtMethodRuntimeInfo, locals_size));
8044 MONO_INST_NEW (cfg, ins, OP_LOCALLOC);
8045 ins->dreg = locals_var->dreg;
8047 MONO_ADD_INS (cfg->cbb, ins);
8048 cfg->gsharedvt_locals_var_ins = ins;
8050 cfg->flags |= MONO_CFG_HAS_ALLOCA;
8053 ins->flags |= MONO_INST_INIT;
8057 /* at this point we know, if security is TRUE, that some code needs to be generated */
8058 if (security && (cfg->method == method)) {
8061 cfg->stat_cas_demand_generation++;
8063 if (actions.demand.blob) {
8064 /* Add code for SecurityAction.Demand */
8065 EMIT_NEW_DECLSECCONST (cfg, args[0], image, actions.demand);
8066 EMIT_NEW_ICONST (cfg, args [1], actions.demand.size);
8067 /* Calls static void SecurityManager.InternalDemand (byte* permissions, int size); */
8068 mono_emit_method_call (cfg, secman->demand, args, NULL);
8070 if (actions.noncasdemand.blob) {
8071 /* CLR 1.x uses a .noncasdemand (but 2.x doesn't) */
8072 /* For Mono we re-route non-CAS Demand to Demand (as the managed code must deal with it anyway) */
8073 EMIT_NEW_DECLSECCONST (cfg, args[0], image, actions.noncasdemand);
8074 EMIT_NEW_ICONST (cfg, args [1], actions.noncasdemand.size);
8075 /* Calls static void SecurityManager.InternalDemand (byte* permissions, int size); */
8076 mono_emit_method_call (cfg, secman->demand, args, NULL);
8078 if (actions.demandchoice.blob) {
8079 /* New in 2.0, Demand must succeed for one of the permissions (i.e. not all) */
8080 EMIT_NEW_DECLSECCONST (cfg, args[0], image, actions.demandchoice);
8081 EMIT_NEW_ICONST (cfg, args [1], actions.demandchoice.size);
8082 /* Calls static void SecurityManager.InternalDemandChoice (byte* permissions, int size); */
8083 mono_emit_method_call (cfg, secman->demandchoice, args, NULL);
8087 /* we must Demand SecurityPermission.Unmanaged before p/invoking */
8089 mono_emit_method_call (cfg, secman->demandunmanaged, NULL, NULL);
8092 if (mono_security_core_clr_enabled ()) {
8093 /* check if this is native code, e.g. an icall or a p/invoke */
8094 if (method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE) {
8095 MonoMethod *wrapped = mono_marshal_method_from_wrapper (method);
8097 gboolean pinvk = (wrapped->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL);
8098 gboolean icall = (wrapped->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL);
8100 /* if this ia a native call then it can only be JITted from platform code */
8101 if ((icall || pinvk) && method->klass && method->klass->image) {
8102 if (!mono_security_core_clr_is_platform_image (method->klass->image)) {
8103 MonoException *ex = icall ? mono_get_exception_security () :
8104 mono_get_exception_method_access ();
8105 emit_throw_exception (cfg, ex);
8112 CHECK_CFG_EXCEPTION;
8114 if (header->code_size == 0)
8117 if (get_basic_blocks (cfg, header, cfg->real_offset, ip, end, &err_pos)) {
8122 if (cfg->method == method)
8123 mono_debug_init_method (cfg, bblock, breakpoint_id);
8125 for (n = 0; n < header->num_locals; ++n) {
8126 if (header->locals [n]->type == MONO_TYPE_VOID && !header->locals [n]->byref)
8131 /* We force the vtable variable here for all shared methods
8132 for the possibility that they might show up in a stack
8133 trace where their exact instantiation is needed. */
8134 if (cfg->generic_sharing_context && method == cfg->method) {
8135 if ((method->flags & METHOD_ATTRIBUTE_STATIC) ||
8136 mini_method_get_context (method)->method_inst ||
8137 method->klass->valuetype) {
8138 mono_get_vtable_var (cfg);
8140 /* FIXME: Is there a better way to do this?
8141 We need the variable live for the duration
8142 of the whole method. */
8143 cfg->args [0]->flags |= MONO_INST_VOLATILE;
8147 /* add a check for this != NULL to inlined methods */
8148 if (is_virtual_call) {
8151 NEW_ARGLOAD (cfg, arg_ins, 0);
8152 MONO_ADD_INS (cfg->cbb, arg_ins);
8153 MONO_EMIT_NEW_CHECK_THIS (cfg, arg_ins->dreg);
8156 skip_dead_blocks = !dont_verify;
8157 if (skip_dead_blocks) {
8158 original_bb = bb = mono_basic_block_split (method, &cfg->error);
8163 /* we use a spare stack slot in SWITCH and NEWOBJ and others */
8164 stack_start = sp = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoInst*) * (header->max_stack + 1));
8167 start_new_bblock = 0;
8170 if (cfg->method == method)
8171 cfg->real_offset = ip - header->code;
8173 cfg->real_offset = inline_offset;
8178 if (start_new_bblock) {
8179 bblock->cil_length = ip - bblock->cil_code;
8180 if (start_new_bblock == 2) {
8181 g_assert (ip == tblock->cil_code);
8183 GET_BBLOCK (cfg, tblock, ip);
8185 bblock->next_bb = tblock;
8188 start_new_bblock = 0;
8189 for (i = 0; i < bblock->in_scount; ++i) {
8190 if (cfg->verbose_level > 3)
8191 printf ("loading %d from temp %d\n", i, (int)bblock->in_stack [i]->inst_c0);
8192 EMIT_NEW_TEMPLOAD (cfg, ins, bblock->in_stack [i]->inst_c0);
8196 g_slist_free (class_inits);
8199 if ((tblock = cfg->cil_offset_to_bb [ip - cfg->cil_start]) && (tblock != bblock)) {
8200 link_bblock (cfg, bblock, tblock);
8201 if (sp != stack_start) {
8202 handle_stack_args (cfg, stack_start, sp - stack_start);
8204 CHECK_UNVERIFIABLE (cfg);
8206 bblock->next_bb = tblock;
8209 for (i = 0; i < bblock->in_scount; ++i) {
8210 if (cfg->verbose_level > 3)
8211 printf ("loading %d from temp %d\n", i, (int)bblock->in_stack [i]->inst_c0);
8212 EMIT_NEW_TEMPLOAD (cfg, ins, bblock->in_stack [i]->inst_c0);
8215 g_slist_free (class_inits);
8220 if (skip_dead_blocks) {
8221 int ip_offset = ip - header->code;
8223 if (ip_offset == bb->end)
8227 int op_size = mono_opcode_size (ip, end);
8228 g_assert (op_size > 0); /*The BB formation pass must catch all bad ops*/
8230 if (cfg->verbose_level > 3) printf ("SKIPPING DEAD OP at %x\n", ip_offset);
8232 if (ip_offset + op_size == bb->end) {
8233 MONO_INST_NEW (cfg, ins, OP_NOP);
8234 MONO_ADD_INS (bblock, ins);
8235 start_new_bblock = 1;
8243 * Sequence points are points where the debugger can place a breakpoint.
8244 * Currently, we generate these automatically at points where the IL
8247 if (seq_points && ((!sym_seq_points && (sp == stack_start)) || (sym_seq_points && mono_bitset_test_fast (seq_point_locs, ip - header->code)))) {
8249 * Make methods interruptable at the beginning, and at the targets of
8250 * backward branches.
8251 * Also, do this at the start of every bblock in methods with clauses too,
8252 * to be able to handle instructions with inprecise control flow like
8254 * Backward branches are handled at the end of method-to-ir ().
8256 gboolean intr_loc = ip == header->code || (!cfg->cbb->last_ins && cfg->header->num_clauses);
8258 /* Avoid sequence points on empty IL like .volatile */
8259 // FIXME: Enable this
8260 //if (!(cfg->cbb->last_ins && cfg->cbb->last_ins->opcode == OP_SEQ_POINT)) {
8261 NEW_SEQ_POINT (cfg, ins, ip - header->code, intr_loc);
8262 if (sp != stack_start)
8263 ins->flags |= MONO_INST_NONEMPTY_STACK;
8264 MONO_ADD_INS (cfg->cbb, ins);
8267 mono_bitset_set_fast (seq_point_set_locs, ip - header->code);
8270 bblock->real_offset = cfg->real_offset;
8272 if ((cfg->method == method) && cfg->coverage_info) {
8273 guint32 cil_offset = ip - header->code;
8274 cfg->coverage_info->data [cil_offset].cil_code = ip;
8276 /* TODO: Use an increment here */
8277 #if defined(TARGET_X86)
8278 MONO_INST_NEW (cfg, ins, OP_STORE_MEM_IMM);
8279 ins->inst_p0 = &(cfg->coverage_info->data [cil_offset].count);
8281 MONO_ADD_INS (cfg->cbb, ins);
8283 EMIT_NEW_PCONST (cfg, ins, &(cfg->coverage_info->data [cil_offset].count));
8284 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STORE_MEMBASE_IMM, ins->dreg, 0, 1);
8288 if (cfg->verbose_level > 3)
8289 printf ("converting (in B%d: stack: %d) %s", bblock->block_num, (int)(sp - stack_start), mono_disasm_code_one (NULL, method, ip, NULL));
8293 if (seq_points && !sym_seq_points && sp != stack_start) {
8295 * The C# compiler uses these nops to notify the JIT that it should
8296 * insert seq points.
8298 NEW_SEQ_POINT (cfg, ins, ip - header->code, FALSE);
8299 MONO_ADD_INS (cfg->cbb, ins);
8301 if (cfg->keep_cil_nops)
8302 MONO_INST_NEW (cfg, ins, OP_HARD_NOP);
8304 MONO_INST_NEW (cfg, ins, OP_NOP);
8306 MONO_ADD_INS (bblock, ins);
8309 if (should_insert_brekpoint (cfg->method)) {
8310 ins = mono_emit_jit_icall (cfg, mono_debugger_agent_user_break, NULL);
8312 MONO_INST_NEW (cfg, ins, OP_NOP);
8315 MONO_ADD_INS (bblock, ins);
8321 CHECK_STACK_OVF (1);
8322 n = (*ip)-CEE_LDARG_0;
8324 EMIT_NEW_ARGLOAD (cfg, ins, n);
8332 CHECK_STACK_OVF (1);
8333 n = (*ip)-CEE_LDLOC_0;
8335 EMIT_NEW_LOCLOAD (cfg, ins, n);
8344 n = (*ip)-CEE_STLOC_0;
8347 if (!dont_verify_stloc && target_type_is_incompatible (cfg, header->locals [n], *sp))
8349 emit_stloc_ir (cfg, sp, header, n);
8356 CHECK_STACK_OVF (1);
8359 EMIT_NEW_ARGLOAD (cfg, ins, n);
8365 CHECK_STACK_OVF (1);
8368 NEW_ARGLOADA (cfg, ins, n);
8369 MONO_ADD_INS (cfg->cbb, ins);
8379 if (!dont_verify_stloc && target_type_is_incompatible (cfg, param_types [ip [1]], *sp))
8381 EMIT_NEW_ARGSTORE (cfg, ins, n, *sp);
8386 CHECK_STACK_OVF (1);
8389 EMIT_NEW_LOCLOAD (cfg, ins, n);
8393 case CEE_LDLOCA_S: {
8394 unsigned char *tmp_ip;
8396 CHECK_STACK_OVF (1);
8397 CHECK_LOCAL (ip [1]);
8399 if ((tmp_ip = emit_optimized_ldloca_ir (cfg, ip, end, 1))) {
8405 EMIT_NEW_LOCLOADA (cfg, ins, ip [1]);
8414 CHECK_LOCAL (ip [1]);
8415 if (!dont_verify_stloc && target_type_is_incompatible (cfg, header->locals [ip [1]], *sp))
8417 emit_stloc_ir (cfg, sp, header, ip [1]);
8422 CHECK_STACK_OVF (1);
8423 EMIT_NEW_PCONST (cfg, ins, NULL);
8424 ins->type = STACK_OBJ;
8429 CHECK_STACK_OVF (1);
8430 EMIT_NEW_ICONST (cfg, ins, -1);
8443 CHECK_STACK_OVF (1);
8444 EMIT_NEW_ICONST (cfg, ins, (*ip) - CEE_LDC_I4_0);
8450 CHECK_STACK_OVF (1);
8452 EMIT_NEW_ICONST (cfg, ins, *((signed char*)ip));
8458 CHECK_STACK_OVF (1);
8459 EMIT_NEW_ICONST (cfg, ins, (gint32)read32 (ip + 1));
8465 CHECK_STACK_OVF (1);
8466 MONO_INST_NEW (cfg, ins, OP_I8CONST);
8467 ins->type = STACK_I8;
8468 ins->dreg = alloc_dreg (cfg, STACK_I8);
8470 ins->inst_l = (gint64)read64 (ip);
8471 MONO_ADD_INS (bblock, ins);
8477 gboolean use_aotconst = FALSE;
8479 #ifdef TARGET_POWERPC
8480 /* FIXME: Clean this up */
8481 if (cfg->compile_aot)
8482 use_aotconst = TRUE;
8485 /* FIXME: we should really allocate this only late in the compilation process */
8486 f = mono_domain_alloc (cfg->domain, sizeof (float));
8488 CHECK_STACK_OVF (1);
8494 EMIT_NEW_AOTCONST (cfg, cons, MONO_PATCH_INFO_R4, f);
8496 dreg = alloc_freg (cfg);
8497 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADR4_MEMBASE, dreg, cons->dreg, 0);
8498 ins->type = cfg->r4_stack_type;
8500 MONO_INST_NEW (cfg, ins, OP_R4CONST);
8501 ins->type = cfg->r4_stack_type;
8502 ins->dreg = alloc_dreg (cfg, STACK_R8);
8504 MONO_ADD_INS (bblock, ins);
8514 gboolean use_aotconst = FALSE;
8516 #ifdef TARGET_POWERPC
8517 /* FIXME: Clean this up */
8518 if (cfg->compile_aot)
8519 use_aotconst = TRUE;
8522 /* FIXME: we should really allocate this only late in the compilation process */
8523 d = mono_domain_alloc (cfg->domain, sizeof (double));
8525 CHECK_STACK_OVF (1);
8531 EMIT_NEW_AOTCONST (cfg, cons, MONO_PATCH_INFO_R8, d);
8533 dreg = alloc_freg (cfg);
8534 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADR8_MEMBASE, dreg, cons->dreg, 0);
8535 ins->type = STACK_R8;
8537 MONO_INST_NEW (cfg, ins, OP_R8CONST);
8538 ins->type = STACK_R8;
8539 ins->dreg = alloc_dreg (cfg, STACK_R8);
8541 MONO_ADD_INS (bblock, ins);
8550 MonoInst *temp, *store;
8552 CHECK_STACK_OVF (1);
8556 temp = mono_compile_create_var (cfg, type_from_stack_type (ins), OP_LOCAL);
8557 EMIT_NEW_TEMPSTORE (cfg, store, temp->inst_c0, ins);
8559 EMIT_NEW_TEMPLOAD (cfg, ins, temp->inst_c0);
8562 EMIT_NEW_TEMPLOAD (cfg, ins, temp->inst_c0);
8575 if (sp [0]->type == STACK_R8)
8576 /* we need to pop the value from the x86 FP stack */
8577 MONO_EMIT_NEW_UNALU (cfg, OP_X86_FPOP, -1, sp [0]->dreg);
8583 INLINE_FAILURE ("jmp");
8584 GSHAREDVT_FAILURE (*ip);
8587 if (stack_start != sp)
8589 token = read32 (ip + 1);
8590 /* FIXME: check the signature matches */
8591 cmethod = mini_get_method (cfg, method, token, NULL, generic_context);
8593 if (!cmethod || mono_loader_get_last_error ())
8596 if (cfg->generic_sharing_context && mono_method_check_context_used (cmethod))
8597 GENERIC_SHARING_FAILURE (CEE_JMP);
8599 if (mono_security_cas_enabled ())
8600 CHECK_CFG_EXCEPTION;
8602 emit_instrumentation_call (cfg, mono_profiler_method_leave);
8604 if (ARCH_HAVE_OP_TAIL_CALL) {
8605 MonoMethodSignature *fsig = mono_method_signature (cmethod);
8608 /* Handle tail calls similarly to calls */
8609 n = fsig->param_count + fsig->hasthis;
8613 MONO_INST_NEW_CALL (cfg, call, OP_TAILCALL);
8614 call->method = cmethod;
8615 call->tail_call = TRUE;
8616 call->signature = mono_method_signature (cmethod);
8617 call->args = mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*) * n);
8618 call->inst.inst_p0 = cmethod;
8619 for (i = 0; i < n; ++i)
8620 EMIT_NEW_ARGLOAD (cfg, call->args [i], i);
8622 mono_arch_emit_call (cfg, call);
8623 cfg->param_area = MAX(cfg->param_area, call->stack_usage);
8624 MONO_ADD_INS (bblock, (MonoInst*)call);
8626 for (i = 0; i < num_args; ++i)
8627 /* Prevent arguments from being optimized away */
8628 arg_array [i]->flags |= MONO_INST_VOLATILE;
8630 MONO_INST_NEW_CALL (cfg, call, OP_JMP);
8631 ins = (MonoInst*)call;
8632 ins->inst_p0 = cmethod;
8633 MONO_ADD_INS (bblock, ins);
8637 start_new_bblock = 1;
8642 case CEE_CALLVIRT: {
8643 MonoInst *addr = NULL;
8644 MonoMethodSignature *fsig = NULL;
8646 int virtual = *ip == CEE_CALLVIRT;
8647 int calli = *ip == CEE_CALLI;
8648 gboolean pass_imt_from_rgctx = FALSE;
8649 MonoInst *imt_arg = NULL;
8650 MonoInst *keep_this_alive = NULL;
8651 gboolean pass_vtable = FALSE;
8652 gboolean pass_mrgctx = FALSE;
8653 MonoInst *vtable_arg = NULL;
8654 gboolean check_this = FALSE;
8655 gboolean supported_tail_call = FALSE;
8656 gboolean tail_call = FALSE;
8657 gboolean need_seq_point = FALSE;
8658 guint32 call_opcode = *ip;
8659 gboolean emit_widen = TRUE;
8660 gboolean push_res = TRUE;
8661 gboolean skip_ret = FALSE;
8662 gboolean delegate_invoke = FALSE;
8665 token = read32 (ip + 1);
8670 //GSHAREDVT_FAILURE (*ip);
8675 fsig = mini_get_signature (method, token, generic_context);
8676 n = fsig->param_count + fsig->hasthis;
8678 if (method->dynamic && fsig->pinvoke) {
8682 * This is a call through a function pointer using a pinvoke
8683 * signature. Have to create a wrapper and call that instead.
8684 * FIXME: This is very slow, need to create a wrapper at JIT time
8685 * instead based on the signature.
8687 EMIT_NEW_IMAGECONST (cfg, args [0], method->klass->image);
8688 EMIT_NEW_PCONST (cfg, args [1], fsig);
8690 addr = mono_emit_jit_icall (cfg, mono_get_native_calli_wrapper, args);
8693 MonoMethod *cil_method;
8695 cmethod = mini_get_method (cfg, method, token, NULL, generic_context);
8696 cil_method = cmethod;
8698 if (constrained_call) {
8699 if (method->wrapper_type != MONO_WRAPPER_NONE) {
8700 if (cfg->verbose_level > 2)
8701 printf ("DM Constrained call to %s\n", mono_type_get_full_name (constrained_call));
8702 if (!((constrained_call->byval_arg.type == MONO_TYPE_VAR ||
8703 constrained_call->byval_arg.type == MONO_TYPE_MVAR) &&
8704 cfg->generic_sharing_context)) {
8705 cmethod = mono_get_method_constrained_with_method (image, cil_method, constrained_call, generic_context, &cfg->error);
8709 if (cfg->verbose_level > 2)
8710 printf ("Constrained call to %s\n", mono_type_get_full_name (constrained_call));
8712 if ((constrained_call->byval_arg.type == MONO_TYPE_VAR || constrained_call->byval_arg.type == MONO_TYPE_MVAR) && cfg->generic_sharing_context) {
8714 * This is needed since get_method_constrained can't find
8715 * the method in klass representing a type var.
8716 * The type var is guaranteed to be a reference type in this
8719 if (!mini_is_gsharedvt_klass (cfg, constrained_call))
8720 g_assert (!cmethod->klass->valuetype);
8722 cmethod = mono_get_method_constrained_checked (image, token, constrained_call, generic_context, &cil_method, &cfg->error);
8728 if (!cmethod || mono_loader_get_last_error ())
8730 if (!dont_verify && !cfg->skip_visibility) {
8731 MonoMethod *target_method = cil_method;
8732 if (method->is_inflated) {
8733 target_method = mini_get_method_allow_open (method, token, NULL, &(mono_method_get_generic_container (method_definition)->context));
8735 if (!mono_method_can_access_method (method_definition, target_method) &&
8736 !mono_method_can_access_method (method, cil_method))
8737 METHOD_ACCESS_FAILURE (method, cil_method);
8740 if (mono_security_core_clr_enabled ())
8741 ensure_method_is_allowed_to_call_method (cfg, method, cil_method, bblock, ip);
8743 if (!virtual && (cmethod->flags & METHOD_ATTRIBUTE_ABSTRACT))
8744 /* MS.NET seems to silently convert this to a callvirt */
8749 * MS.NET accepts non virtual calls to virtual final methods of transparent proxy classes and
8750 * converts to a callvirt.
8752 * tests/bug-515884.il is an example of this behavior
8754 const int test_flags = METHOD_ATTRIBUTE_VIRTUAL | METHOD_ATTRIBUTE_FINAL | METHOD_ATTRIBUTE_STATIC;
8755 const int expected_flags = METHOD_ATTRIBUTE_VIRTUAL | METHOD_ATTRIBUTE_FINAL;
8756 if (!virtual && mono_class_is_marshalbyref (cmethod->klass) && (cmethod->flags & test_flags) == expected_flags && cfg->method->wrapper_type == MONO_WRAPPER_NONE)
8760 if (!cmethod->klass->inited)
8761 if (!mono_class_init (cmethod->klass))
8762 TYPE_LOAD_ERROR (cmethod->klass);
8764 if (cmethod->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL &&
8765 mini_class_is_system_array (cmethod->klass)) {
8766 array_rank = cmethod->klass->rank;
8767 fsig = mono_method_signature (cmethod);
8769 fsig = mono_method_signature (cmethod);
8774 if (fsig->pinvoke) {
8775 MonoMethod *wrapper = mono_marshal_get_native_wrapper (cmethod,
8776 check_for_pending_exc, cfg->compile_aot);
8777 fsig = mono_method_signature (wrapper);
8778 } else if (constrained_call) {
8779 fsig = mono_method_signature (cmethod);
8781 fsig = mono_method_get_signature_checked (cmethod, image, token, generic_context, &cfg->error);
8786 mono_save_token_info (cfg, image, token, cil_method);
8788 if (!(seq_point_locs && mono_bitset_test_fast (seq_point_locs, ip + 5 - header->code)))
8789 need_seq_point = TRUE;
8791 n = fsig->param_count + fsig->hasthis;
8793 /* Don't support calls made using type arguments for now */
8795 if (cfg->gsharedvt) {
8796 if (mini_is_gsharedvt_signature (cfg, fsig))
8797 GSHAREDVT_FAILURE (*ip);
8801 if (mono_security_cas_enabled ()) {
8802 if (check_linkdemand (cfg, method, cmethod))
8803 INLINE_FAILURE ("linkdemand");
8804 CHECK_CFG_EXCEPTION;
8807 if (cmethod->string_ctor && method->wrapper_type != MONO_WRAPPER_RUNTIME_INVOKE)
8808 g_assert_not_reached ();
8811 if (!cfg->generic_sharing_context && cmethod && cmethod->klass->generic_container)
8814 if (!cfg->generic_sharing_context && cmethod)
8815 g_assert (!mono_method_check_context_used (cmethod));
8819 //g_assert (!virtual || fsig->hasthis);
8823 if (constrained_call) {
8824 if (mini_is_gsharedvt_klass (cfg, constrained_call)) {
8825 if ((cmethod->klass != mono_defaults.object_class) && constrained_call->valuetype && cmethod->klass->valuetype) {
8826 /* The 'Own method' case below */
8827 } else if (cmethod->klass->image != mono_defaults.corlib && !(cmethod->klass->flags & TYPE_ATTRIBUTE_INTERFACE) && !cmethod->klass->valuetype) {
8828 /* 'The type parameter is instantiated as a reference type' case below. */
8830 ins = handle_constrained_gsharedvt_call (cfg, cmethod, fsig, sp, constrained_call, &emit_widen, &bblock);
8831 CHECK_CFG_EXCEPTION;
8838 * We have the `constrained.' prefix opcode.
8840 if (constrained_call->valuetype && (cmethod->klass == mono_defaults.object_class || cmethod->klass == mono_defaults.enum_class->parent || cmethod->klass == mono_defaults.enum_class)) {
8842 * The type parameter is instantiated as a valuetype,
8843 * but that type doesn't override the method we're
8844 * calling, so we need to box `this'.
8846 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &constrained_call->byval_arg, sp [0]->dreg, 0);
8847 ins->klass = constrained_call;
8848 sp [0] = handle_box (cfg, ins, constrained_call, mono_class_check_context_used (constrained_call), &bblock);
8849 CHECK_CFG_EXCEPTION;
8850 } else if (!constrained_call->valuetype) {
8851 int dreg = alloc_ireg_ref (cfg);
8854 * The type parameter is instantiated as a reference
8855 * type. We have a managed pointer on the stack, so
8856 * we need to dereference it here.
8858 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, dreg, sp [0]->dreg, 0);
8859 ins->type = STACK_OBJ;
8862 if (cmethod->klass->valuetype) {
8865 /* Interface method */
8868 mono_class_setup_vtable (constrained_call);
8869 CHECK_TYPELOAD (constrained_call);
8870 ioffset = mono_class_interface_offset (constrained_call, cmethod->klass);
8872 TYPE_LOAD_ERROR (constrained_call);
8873 slot = mono_method_get_vtable_slot (cmethod);
8875 TYPE_LOAD_ERROR (cmethod->klass);
8876 cmethod = constrained_call->vtable [ioffset + slot];
8878 if (cmethod->klass == mono_defaults.enum_class) {
8879 /* Enum implements some interfaces, so treat this as the first case */
8880 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &constrained_call->byval_arg, sp [0]->dreg, 0);
8881 ins->klass = constrained_call;
8882 sp [0] = handle_box (cfg, ins, constrained_call, mono_class_check_context_used (constrained_call), &bblock);
8883 CHECK_CFG_EXCEPTION;
8888 constrained_call = NULL;
8891 if (!calli && check_call_signature (cfg, fsig, sp))
8894 #ifdef MONO_ARCH_HAVE_CREATE_DELEGATE_TRAMPOLINE
8895 if (cmethod && (cmethod->klass->parent == mono_defaults.multicastdelegate_class) && !strcmp (cmethod->name, "Invoke"))
8896 delegate_invoke = TRUE;
8899 if (cmethod && (cfg->opt & MONO_OPT_INTRINS) && (ins = mini_emit_inst_for_sharable_method (cfg, cmethod, fsig, sp))) {
8901 if (!MONO_TYPE_IS_VOID (fsig->ret)) {
8902 type_to_eval_stack_type ((cfg), fsig->ret, ins);
8910 * If the callee is a shared method, then its static cctor
8911 * might not get called after the call was patched.
8913 if (cfg->generic_sharing_context && cmethod && cmethod->klass != method->klass && cmethod->klass->generic_class && mono_method_is_generic_sharable (cmethod, TRUE) && mono_class_needs_cctor_run (cmethod->klass, method)) {
8914 emit_generic_class_init (cfg, cmethod->klass);
8915 CHECK_TYPELOAD (cmethod->klass);
8919 check_method_sharing (cfg, cmethod, &pass_vtable, &pass_mrgctx);
8921 if (cfg->generic_sharing_context && cmethod) {
8922 MonoGenericContext *cmethod_context = mono_method_get_context (cmethod);
8924 context_used = mini_method_check_context_used (cfg, cmethod);
8926 if (context_used && (cmethod->klass->flags & TYPE_ATTRIBUTE_INTERFACE)) {
8927 /* Generic method interface
8928 calls are resolved via a
8929 helper function and don't
8931 if (!cmethod_context || !cmethod_context->method_inst)
8932 pass_imt_from_rgctx = TRUE;
8936 * If a shared method calls another
8937 * shared method then the caller must
8938 * have a generic sharing context
8939 * because the magic trampoline
8940 * requires it. FIXME: We shouldn't
8941 * have to force the vtable/mrgctx
8942 * variable here. Instead there
8943 * should be a flag in the cfg to
8944 * request a generic sharing context.
8947 ((method->flags & METHOD_ATTRIBUTE_STATIC) || method->klass->valuetype))
8948 mono_get_vtable_var (cfg);
8953 vtable_arg = emit_get_rgctx_klass (cfg, context_used, cmethod->klass, MONO_RGCTX_INFO_VTABLE);
8955 MonoVTable *vtable = mono_class_vtable (cfg->domain, cmethod->klass);
8957 CHECK_TYPELOAD (cmethod->klass);
8958 EMIT_NEW_VTABLECONST (cfg, vtable_arg, vtable);
8963 g_assert (!vtable_arg);
8965 if (!cfg->compile_aot) {
8967 * emit_get_rgctx_method () calls mono_class_vtable () so check
8968 * for type load errors before.
8970 mono_class_setup_vtable (cmethod->klass);
8971 CHECK_TYPELOAD (cmethod->klass);
8974 vtable_arg = emit_get_rgctx_method (cfg, context_used, cmethod, MONO_RGCTX_INFO_METHOD_RGCTX);
8976 /* !marshalbyref is needed to properly handle generic methods + remoting */
8977 if ((!(cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL) ||
8978 MONO_METHOD_IS_FINAL (cmethod)) &&
8979 !mono_class_is_marshalbyref (cmethod->klass)) {
8986 if (pass_imt_from_rgctx) {
8987 g_assert (!pass_vtable);
8990 imt_arg = emit_get_rgctx_method (cfg, context_used,
8991 cmethod, MONO_RGCTX_INFO_METHOD);
8995 MONO_EMIT_NEW_CHECK_THIS (cfg, sp [0]->dreg);
8997 /* Calling virtual generic methods */
8998 if (cmethod && virtual &&
8999 (cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL) &&
9000 !(MONO_METHOD_IS_FINAL (cmethod) &&
9001 cmethod->wrapper_type != MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK) &&
9002 fsig->generic_param_count &&
9003 !(cfg->gsharedvt && mini_is_gsharedvt_signature (cfg, fsig))) {
9004 MonoInst *this_temp, *this_arg_temp, *store;
9005 MonoInst *iargs [4];
9006 gboolean use_imt = FALSE;
9008 g_assert (fsig->is_inflated);
9010 /* Prevent inlining of methods that contain indirect calls */
9011 INLINE_FAILURE ("virtual generic call");
9013 if (cfg->gsharedvt && mini_is_gsharedvt_signature (cfg, fsig))
9014 GSHAREDVT_FAILURE (*ip);
9016 #if MONO_ARCH_HAVE_GENERALIZED_IMT_THUNK && defined(MONO_ARCH_GSHARED_SUPPORTED)
9017 if (cmethod->wrapper_type == MONO_WRAPPER_NONE && mono_use_imt)
9022 g_assert (!imt_arg);
9024 g_assert (cmethod->is_inflated);
9025 imt_arg = emit_get_rgctx_method (cfg, context_used,
9026 cmethod, MONO_RGCTX_INFO_METHOD);
9027 ins = mono_emit_method_call_full (cfg, cmethod, fsig, FALSE, sp, sp [0], imt_arg, NULL);
9029 this_temp = mono_compile_create_var (cfg, type_from_stack_type (sp [0]), OP_LOCAL);
9030 NEW_TEMPSTORE (cfg, store, this_temp->inst_c0, sp [0]);
9031 MONO_ADD_INS (bblock, store);
9033 /* FIXME: This should be a managed pointer */
9034 this_arg_temp = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
9036 EMIT_NEW_TEMPLOAD (cfg, iargs [0], this_temp->inst_c0);
9037 iargs [1] = emit_get_rgctx_method (cfg, context_used,
9038 cmethod, MONO_RGCTX_INFO_METHOD);
9039 EMIT_NEW_TEMPLOADA (cfg, iargs [2], this_arg_temp->inst_c0);
9040 addr = mono_emit_jit_icall (cfg,
9041 mono_helper_compile_generic_method, iargs);
9043 EMIT_NEW_TEMPLOAD (cfg, sp [0], this_arg_temp->inst_c0);
9045 ins = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr, NULL, NULL);
9052 * Implement a workaround for the inherent races involved in locking:
9058 * If a thread abort happens between the call to Monitor.Enter () and the start of the
9059 * try block, the Exit () won't be executed, see:
9060 * http://www.bluebytesoftware.com/blog/2007/01/30/MonitorEnterThreadAbortsAndOrphanedLocks.aspx
9061 * To work around this, we extend such try blocks to include the last x bytes
9062 * of the Monitor.Enter () call.
9064 if (cmethod && cmethod->klass == mono_defaults.monitor_class && !strcmp (cmethod->name, "Enter") && mono_method_signature (cmethod)->param_count == 1) {
9065 MonoBasicBlock *tbb;
9067 GET_BBLOCK (cfg, tbb, ip + 5);
9069 * Only extend try blocks with a finally, to avoid catching exceptions thrown
9070 * from Monitor.Enter like ArgumentNullException.
9072 if (tbb->try_start && MONO_REGION_FLAGS(tbb->region) == MONO_EXCEPTION_CLAUSE_FINALLY) {
9073 /* Mark this bblock as needing to be extended */
9074 tbb->extend_try_block = TRUE;
9078 /* Conversion to a JIT intrinsic */
9079 if (cmethod && (cfg->opt & MONO_OPT_INTRINS) && (ins = mini_emit_inst_for_method (cfg, cmethod, fsig, sp))) {
9081 if (!MONO_TYPE_IS_VOID (fsig->ret)) {
9082 type_to_eval_stack_type ((cfg), fsig->ret, ins);
9089 if (cmethod && (cfg->opt & MONO_OPT_INLINE) &&
9090 (!virtual || !(cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL) || MONO_METHOD_IS_FINAL (cmethod)) &&
9091 mono_method_check_inlining (cfg, cmethod)) {
9093 gboolean always = FALSE;
9095 if ((cmethod->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
9096 (cmethod->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)) {
9097 /* Prevent inlining of methods that call wrappers */
9098 INLINE_FAILURE ("wrapper call");
9099 cmethod = mono_marshal_get_native_wrapper (cmethod, check_for_pending_exc, FALSE);
9103 costs = inline_method (cfg, cmethod, fsig, sp, ip, cfg->real_offset, always, &bblock);
9105 cfg->real_offset += 5;
9107 if (!MONO_TYPE_IS_VOID (fsig->ret)) {
9108 /* *sp is already set by inline_method */
9113 inline_costs += costs;
9119 /* Tail recursion elimination */
9120 if ((cfg->opt & MONO_OPT_TAILC) && call_opcode == CEE_CALL && cmethod == method && ip [5] == CEE_RET && !vtable_arg) {
9121 gboolean has_vtargs = FALSE;
9124 /* Prevent inlining of methods with tail calls (the call stack would be altered) */
9125 INLINE_FAILURE ("tail call");
9127 /* keep it simple */
9128 for (i = fsig->param_count - 1; i >= 0; i--) {
9129 if (MONO_TYPE_ISSTRUCT (mono_method_signature (cmethod)->params [i]))
9134 for (i = 0; i < n; ++i)
9135 EMIT_NEW_ARGSTORE (cfg, ins, i, sp [i]);
9136 MONO_INST_NEW (cfg, ins, OP_BR);
9137 MONO_ADD_INS (bblock, ins);
9138 tblock = start_bblock->out_bb [0];
9139 link_bblock (cfg, bblock, tblock);
9140 ins->inst_target_bb = tblock;
9141 start_new_bblock = 1;
9143 /* skip the CEE_RET, too */
9144 if (ip_in_bb (cfg, bblock, ip + 5))
9151 inline_costs += 10 * num_calls++;
9154 * Making generic calls out of gsharedvt methods.
9155 * This needs to be used for all generic calls, not just ones with a gsharedvt signature, to avoid
9156 * patching gshared method addresses into a gsharedvt method.
9158 if (cmethod && cfg->gsharedvt && (mini_is_gsharedvt_signature (cfg, fsig) || cmethod->is_inflated || cmethod->klass->generic_class)) {
9159 MonoRgctxInfoType info_type;
9162 //if (cmethod->klass->flags & TYPE_ATTRIBUTE_INTERFACE)
9163 //GSHAREDVT_FAILURE (*ip);
9164 // disable for possible remoting calls
9165 if (fsig->hasthis && (mono_class_is_marshalbyref (method->klass) || method->klass == mono_defaults.object_class))
9166 GSHAREDVT_FAILURE (*ip);
9167 if (fsig->generic_param_count) {
9168 /* virtual generic call */
9169 g_assert (mono_use_imt);
9170 g_assert (!imt_arg);
9171 /* Same as the virtual generic case above */
9172 imt_arg = emit_get_rgctx_method (cfg, context_used,
9173 cmethod, MONO_RGCTX_INFO_METHOD);
9174 /* This is not needed, as the trampoline code will pass one, and it might be passed in the same reg as the imt arg */
9176 } else if ((cmethod->klass->flags & TYPE_ATTRIBUTE_INTERFACE) && !imt_arg) {
9177 /* This can happen when we call a fully instantiated iface method */
9178 imt_arg = emit_get_rgctx_method (cfg, context_used,
9179 cmethod, MONO_RGCTX_INFO_METHOD);
9184 if (cmethod->klass->rank && cmethod->klass->byval_arg.type != MONO_TYPE_SZARRAY)
9185 /* test_0_multi_dim_arrays () in gshared.cs */
9186 GSHAREDVT_FAILURE (*ip);
9188 if ((cmethod->klass->parent == mono_defaults.multicastdelegate_class) && (!strcmp (cmethod->name, "Invoke")))
9189 keep_this_alive = sp [0];
9191 if (virtual && (cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL))
9192 info_type = MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE_VIRT;
9194 info_type = MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE;
9195 addr = emit_get_rgctx_gsharedvt_call (cfg, context_used, fsig, cmethod, info_type);
9197 ins = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr, imt_arg, vtable_arg);
9199 } else if (calli && cfg->gsharedvt && mini_is_gsharedvt_signature (cfg, fsig)) {
9201 * We pass the address to the gsharedvt trampoline in the rgctx reg
9203 MonoInst *callee = addr;
9205 if (method->wrapper_type != MONO_WRAPPER_DELEGATE_INVOKE)
9207 GSHAREDVT_FAILURE (*ip);
9209 addr = emit_get_rgctx_sig (cfg, context_used,
9210 fsig, MONO_RGCTX_INFO_SIG_GSHAREDVT_OUT_TRAMPOLINE_CALLI);
9211 ins = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr, NULL, callee);
9215 /* Generic sharing */
9218 * Use this if the callee is gsharedvt sharable too, since
9219 * at runtime we might find an instantiation so the call cannot
9220 * be patched (the 'no_patch' code path in mini-trampolines.c).
9222 if (context_used && !imt_arg && !array_rank && !delegate_invoke &&
9223 (!mono_method_is_generic_sharable_full (cmethod, TRUE, FALSE, FALSE) ||
9224 !mono_class_generic_sharing_enabled (cmethod->klass)) &&
9225 (!virtual || MONO_METHOD_IS_FINAL (cmethod) ||
9226 !(cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL))) {
9227 INLINE_FAILURE ("gshared");
9229 g_assert (cfg->generic_sharing_context && cmethod);
9233 * We are compiling a call to a
9234 * generic method from shared code,
9235 * which means that we have to look up
9236 * the method in the rgctx and do an
9240 MONO_EMIT_NEW_CHECK_THIS (cfg, sp [0]->dreg);
9242 addr = emit_get_rgctx_method (cfg, context_used, cmethod, MONO_RGCTX_INFO_GENERIC_METHOD_CODE);
9243 ins = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr, imt_arg, vtable_arg);
9247 /* Indirect calls */
9249 if (call_opcode == CEE_CALL)
9250 g_assert (context_used);
9251 else if (call_opcode == CEE_CALLI)
9252 g_assert (!vtable_arg);
9254 /* FIXME: what the hell is this??? */
9255 g_assert (cmethod->flags & METHOD_ATTRIBUTE_FINAL ||
9256 !(cmethod->flags & METHOD_ATTRIBUTE_FINAL));
9258 /* Prevent inlining of methods with indirect calls */
9259 INLINE_FAILURE ("indirect call");
9261 if (addr->opcode == OP_PCONST || addr->opcode == OP_AOTCONST || addr->opcode == OP_GOT_ENTRY) {
9266 * Instead of emitting an indirect call, emit a direct call
9267 * with the contents of the aotconst as the patch info.
9269 if (addr->opcode == OP_PCONST || addr->opcode == OP_AOTCONST) {
9270 info_type = addr->inst_c1;
9271 info_data = addr->inst_p0;
9273 info_type = addr->inst_right->inst_c1;
9274 info_data = addr->inst_right->inst_left;
9277 if (info_type == MONO_PATCH_INFO_ICALL_ADDR || info_type == MONO_PATCH_INFO_JIT_ICALL_ADDR) {
9278 ins = (MonoInst*)mono_emit_abs_call (cfg, info_type, info_data, fsig, sp);
9283 ins = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr, imt_arg, vtable_arg);
9291 if (strcmp (cmethod->name, "Set") == 0) { /* array Set */
9292 MonoInst *val = sp [fsig->param_count];
9294 if (val->type == STACK_OBJ) {
9295 MonoInst *iargs [2];
9300 mono_emit_jit_icall (cfg, mono_helper_stelem_ref_check, iargs);
9303 addr = mini_emit_ldelema_ins (cfg, cmethod, sp, ip, TRUE);
9304 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, fsig->params [fsig->param_count - 1], addr->dreg, 0, val->dreg);
9305 if (cfg->gen_write_barriers && val->type == STACK_OBJ && !(val->opcode == OP_PCONST && val->inst_c0 == 0))
9306 emit_write_barrier (cfg, addr, val);
9307 } else if (strcmp (cmethod->name, "Get") == 0) { /* array Get */
9308 addr = mini_emit_ldelema_ins (cfg, cmethod, sp, ip, FALSE);
9310 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, fsig->ret, addr->dreg, 0);
9311 } else if (strcmp (cmethod->name, "Address") == 0) { /* array Address */
9312 if (!cmethod->klass->element_class->valuetype && !readonly)
9313 mini_emit_check_array_type (cfg, sp [0], cmethod->klass);
9314 CHECK_TYPELOAD (cmethod->klass);
9317 addr = mini_emit_ldelema_ins (cfg, cmethod, sp, ip, FALSE);
9320 g_assert_not_reached ();
9327 ins = mini_redirect_call (cfg, cmethod, fsig, sp, virtual ? sp [0] : NULL);
9331 /* Tail prefix / tail call optimization */
9333 /* FIXME: Enabling TAILC breaks some inlining/stack trace/etc tests */
9334 /* FIXME: runtime generic context pointer for jumps? */
9335 /* FIXME: handle this for generic sharing eventually */
9336 if (cmethod && (ins_flag & MONO_INST_TAILCALL) &&
9337 !vtable_arg && !cfg->generic_sharing_context && is_supported_tail_call (cfg, method, cmethod, fsig, call_opcode))
9338 supported_tail_call = TRUE;
9340 if (supported_tail_call) {
9343 /* Prevent inlining of methods with tail calls (the call stack would be altered) */
9344 INLINE_FAILURE ("tail call");
9346 //printf ("HIT: %s -> %s\n", mono_method_full_name (cfg->method, TRUE), mono_method_full_name (cmethod, TRUE));
9348 if (ARCH_HAVE_OP_TAIL_CALL) {
9349 /* Handle tail calls similarly to normal calls */
9352 emit_instrumentation_call (cfg, mono_profiler_method_leave);
9354 MONO_INST_NEW_CALL (cfg, call, OP_JMP);
9355 call->tail_call = TRUE;
9356 call->method = cmethod;
9357 call->signature = mono_method_signature (cmethod);
9360 * We implement tail calls by storing the actual arguments into the
9361 * argument variables, then emitting a CEE_JMP.
9363 for (i = 0; i < n; ++i) {
9364 /* Prevent argument from being register allocated */
9365 arg_array [i]->flags |= MONO_INST_VOLATILE;
9366 EMIT_NEW_ARGSTORE (cfg, ins, i, sp [i]);
9368 ins = (MonoInst*)call;
9369 ins->inst_p0 = cmethod;
9370 ins->inst_p1 = arg_array [0];
9371 MONO_ADD_INS (bblock, ins);
9372 link_bblock (cfg, bblock, end_bblock);
9373 start_new_bblock = 1;
9375 // FIXME: Eliminate unreachable epilogs
9378 * OP_TAILCALL has no return value, so skip the CEE_RET if it is
9379 * only reachable from this call.
9381 GET_BBLOCK (cfg, tblock, ip + 5);
9382 if (tblock == bblock || tblock->in_count == 0)
9391 * Synchronized wrappers.
9392 * Its hard to determine where to replace a method with its synchronized
9393 * wrapper without causing an infinite recursion. The current solution is
9394 * to add the synchronized wrapper in the trampolines, and to
9395 * change the called method to a dummy wrapper, and resolve that wrapper
9396 * to the real method in mono_jit_compile_method ().
9398 if (cfg->method->wrapper_type == MONO_WRAPPER_SYNCHRONIZED) {
9399 MonoMethod *orig = mono_marshal_method_from_wrapper (cfg->method);
9400 if (cmethod == orig || (cmethod->is_inflated && mono_method_get_declaring_generic_method (cmethod) == orig))
9401 cmethod = mono_marshal_get_synchronized_inner_wrapper (cmethod);
9405 INLINE_FAILURE ("call");
9406 ins = mono_emit_method_call_full (cfg, cmethod, fsig, tail_call, sp, virtual ? sp [0] : NULL,
9407 imt_arg, vtable_arg);
9410 link_bblock (cfg, bblock, end_bblock);
9411 start_new_bblock = 1;
9413 // FIXME: Eliminate unreachable epilogs
9416 * OP_TAILCALL has no return value, so skip the CEE_RET if it is
9417 * only reachable from this call.
9419 GET_BBLOCK (cfg, tblock, ip + 5);
9420 if (tblock == bblock || tblock->in_count == 0)
9427 /* End of call, INS should contain the result of the call, if any */
9429 if (push_res && !MONO_TYPE_IS_VOID (fsig->ret)) {
9432 *sp++ = mono_emit_widen_call_res (cfg, ins, fsig);
9437 if (keep_this_alive) {
9438 MonoInst *dummy_use;
9440 /* See mono_emit_method_call_full () */
9441 EMIT_NEW_DUMMY_USE (cfg, dummy_use, keep_this_alive);
9444 CHECK_CFG_EXCEPTION;
9448 g_assert (*ip == CEE_RET);
9452 constrained_call = NULL;
9454 emit_seq_point (cfg, method, ip, FALSE, TRUE);
9458 if (cfg->method != method) {
9459 /* return from inlined method */
9461 * If in_count == 0, that means the ret is unreachable due to
9462 * being preceeded by a throw. In that case, inline_method () will
9463 * handle setting the return value
9464 * (test case: test_0_inline_throw ()).
9466 if (return_var && cfg->cbb->in_count) {
9467 MonoType *ret_type = mono_method_signature (method)->ret;
9473 if ((method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD || method->wrapper_type == MONO_WRAPPER_NONE) && target_type_is_incompatible (cfg, ret_type, *sp))
9476 //g_assert (returnvar != -1);
9477 EMIT_NEW_TEMPSTORE (cfg, store, return_var->inst_c0, *sp);
9478 cfg->ret_var_set = TRUE;
9481 emit_instrumentation_call (cfg, mono_profiler_method_leave);
9483 if (cfg->lmf_var && cfg->cbb->in_count)
9487 MonoType *ret_type = mini_replace_type (mono_method_signature (method)->ret);
9489 if (seq_points && !sym_seq_points) {
9491 * Place a seq point here too even through the IL stack is not
9492 * empty, so a step over on
9495 * will work correctly.
9497 NEW_SEQ_POINT (cfg, ins, ip - header->code, TRUE);
9498 MONO_ADD_INS (cfg->cbb, ins);
9501 g_assert (!return_var);
9505 if ((method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD || method->wrapper_type == MONO_WRAPPER_NONE) && target_type_is_incompatible (cfg, ret_type, *sp))
9508 if (mini_type_to_stind (cfg, ret_type) == CEE_STOBJ) {
9511 if (!cfg->vret_addr) {
9514 EMIT_NEW_VARSTORE (cfg, ins, cfg->ret, ret_type, (*sp));
9516 EMIT_NEW_RETLOADA (cfg, ret_addr);
9518 EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STOREV_MEMBASE, ret_addr->dreg, 0, (*sp)->dreg);
9519 ins->klass = mono_class_from_mono_type (ret_type);
9522 #ifdef MONO_ARCH_SOFT_FLOAT_FALLBACK
9523 if (COMPILE_SOFT_FLOAT (cfg) && !ret_type->byref && ret_type->type == MONO_TYPE_R4) {
9524 MonoInst *iargs [1];
9528 conv = mono_emit_jit_icall (cfg, mono_fload_r4_arg, iargs);
9529 mono_arch_emit_setret (cfg, method, conv);
9531 mono_arch_emit_setret (cfg, method, *sp);
9534 mono_arch_emit_setret (cfg, method, *sp);
9539 if (sp != stack_start)
9541 MONO_INST_NEW (cfg, ins, OP_BR);
9543 ins->inst_target_bb = end_bblock;
9544 MONO_ADD_INS (bblock, ins);
9545 link_bblock (cfg, bblock, end_bblock);
9546 start_new_bblock = 1;
9550 MONO_INST_NEW (cfg, ins, OP_BR);
9552 target = ip + 1 + (signed char)(*ip);
9554 GET_BBLOCK (cfg, tblock, target);
9555 link_bblock (cfg, bblock, tblock);
9556 ins->inst_target_bb = tblock;
9557 if (sp != stack_start) {
9558 handle_stack_args (cfg, stack_start, sp - stack_start);
9560 CHECK_UNVERIFIABLE (cfg);
9562 MONO_ADD_INS (bblock, ins);
9563 start_new_bblock = 1;
9564 inline_costs += BRANCH_COST;
9578 MONO_INST_NEW (cfg, ins, *ip + BIG_BRANCH_OFFSET);
9580 target = ip + 1 + *(signed char*)ip;
9586 inline_costs += BRANCH_COST;
9590 MONO_INST_NEW (cfg, ins, OP_BR);
9593 target = ip + 4 + (gint32)read32(ip);
9595 GET_BBLOCK (cfg, tblock, target);
9596 link_bblock (cfg, bblock, tblock);
9597 ins->inst_target_bb = tblock;
9598 if (sp != stack_start) {
9599 handle_stack_args (cfg, stack_start, sp - stack_start);
9601 CHECK_UNVERIFIABLE (cfg);
9604 MONO_ADD_INS (bblock, ins);
9606 start_new_bblock = 1;
9607 inline_costs += BRANCH_COST;
9614 gboolean is_short = ((*ip) == CEE_BRFALSE_S) || ((*ip) == CEE_BRTRUE_S);
9615 gboolean is_true = ((*ip) == CEE_BRTRUE_S) || ((*ip) == CEE_BRTRUE);
9616 guint32 opsize = is_short ? 1 : 4;
9618 CHECK_OPSIZE (opsize);
9620 if (sp [-1]->type == STACK_VTYPE || sp [-1]->type == STACK_R8)
9623 target = ip + opsize + (is_short ? *(signed char*)ip : (gint32)read32(ip));
9628 GET_BBLOCK (cfg, tblock, target);
9629 link_bblock (cfg, bblock, tblock);
9630 GET_BBLOCK (cfg, tblock, ip);
9631 link_bblock (cfg, bblock, tblock);
9633 if (sp != stack_start) {
9634 handle_stack_args (cfg, stack_start, sp - stack_start);
9635 CHECK_UNVERIFIABLE (cfg);
9638 MONO_INST_NEW(cfg, cmp, OP_ICOMPARE_IMM);
9639 cmp->sreg1 = sp [0]->dreg;
9640 type_from_op (cfg, cmp, sp [0], NULL);
9643 #if SIZEOF_REGISTER == 4
9644 if (cmp->opcode == OP_LCOMPARE_IMM) {
9645 /* Convert it to OP_LCOMPARE */
9646 MONO_INST_NEW (cfg, ins, OP_I8CONST);
9647 ins->type = STACK_I8;
9648 ins->dreg = alloc_dreg (cfg, STACK_I8);
9650 MONO_ADD_INS (bblock, ins);
9651 cmp->opcode = OP_LCOMPARE;
9652 cmp->sreg2 = ins->dreg;
9655 MONO_ADD_INS (bblock, cmp);
9657 MONO_INST_NEW (cfg, ins, is_true ? CEE_BNE_UN : CEE_BEQ);
9658 type_from_op (cfg, ins, sp [0], NULL);
9659 MONO_ADD_INS (bblock, ins);
9660 ins->inst_many_bb = mono_mempool_alloc (cfg->mempool, sizeof(gpointer)*2);
9661 GET_BBLOCK (cfg, tblock, target);
9662 ins->inst_true_bb = tblock;
9663 GET_BBLOCK (cfg, tblock, ip);
9664 ins->inst_false_bb = tblock;
9665 start_new_bblock = 2;
9668 inline_costs += BRANCH_COST;
9683 MONO_INST_NEW (cfg, ins, *ip);
9685 target = ip + 4 + (gint32)read32(ip);
9691 inline_costs += BRANCH_COST;
9695 MonoBasicBlock **targets;
9696 MonoBasicBlock *default_bblock;
9697 MonoJumpInfoBBTable *table;
9698 int offset_reg = alloc_preg (cfg);
9699 int target_reg = alloc_preg (cfg);
9700 int table_reg = alloc_preg (cfg);
9701 int sum_reg = alloc_preg (cfg);
9702 gboolean use_op_switch;
9706 n = read32 (ip + 1);
9709 if ((src1->type != STACK_I4) && (src1->type != STACK_PTR))
9713 CHECK_OPSIZE (n * sizeof (guint32));
9714 target = ip + n * sizeof (guint32);
9716 GET_BBLOCK (cfg, default_bblock, target);
9717 default_bblock->flags |= BB_INDIRECT_JUMP_TARGET;
9719 targets = mono_mempool_alloc (cfg->mempool, sizeof (MonoBasicBlock*) * n);
9720 for (i = 0; i < n; ++i) {
9721 GET_BBLOCK (cfg, tblock, target + (gint32)read32(ip));
9722 targets [i] = tblock;
9723 targets [i]->flags |= BB_INDIRECT_JUMP_TARGET;
9727 if (sp != stack_start) {
9729 * Link the current bb with the targets as well, so handle_stack_args
9730 * will set their in_stack correctly.
9732 link_bblock (cfg, bblock, default_bblock);
9733 for (i = 0; i < n; ++i)
9734 link_bblock (cfg, bblock, targets [i]);
9736 handle_stack_args (cfg, stack_start, sp - stack_start);
9738 CHECK_UNVERIFIABLE (cfg);
9741 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ICOMPARE_IMM, -1, src1->dreg, n);
9742 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBGE_UN, default_bblock);
9745 for (i = 0; i < n; ++i)
9746 link_bblock (cfg, bblock, targets [i]);
9748 table = mono_mempool_alloc (cfg->mempool, sizeof (MonoJumpInfoBBTable));
9749 table->table = targets;
9750 table->table_size = n;
9752 use_op_switch = FALSE;
9754 /* ARM implements SWITCH statements differently */
9755 /* FIXME: Make it use the generic implementation */
9756 if (!cfg->compile_aot)
9757 use_op_switch = TRUE;
9760 if (COMPILE_LLVM (cfg))
9761 use_op_switch = TRUE;
9763 cfg->cbb->has_jump_table = 1;
9765 if (use_op_switch) {
9766 MONO_INST_NEW (cfg, ins, OP_SWITCH);
9767 ins->sreg1 = src1->dreg;
9768 ins->inst_p0 = table;
9769 ins->inst_many_bb = targets;
9770 ins->klass = GUINT_TO_POINTER (n);
9771 MONO_ADD_INS (cfg->cbb, ins);
9773 if (sizeof (gpointer) == 8)
9774 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SHL_IMM, offset_reg, src1->dreg, 3);
9776 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SHL_IMM, offset_reg, src1->dreg, 2);
9778 #if SIZEOF_REGISTER == 8
9779 /* The upper word might not be zero, and we add it to a 64 bit address later */
9780 MONO_EMIT_NEW_UNALU (cfg, OP_ZEXT_I4, offset_reg, offset_reg);
9783 if (cfg->compile_aot) {
9784 MONO_EMIT_NEW_AOTCONST (cfg, table_reg, table, MONO_PATCH_INFO_SWITCH);
9786 MONO_INST_NEW (cfg, ins, OP_JUMP_TABLE);
9787 ins->inst_c1 = MONO_PATCH_INFO_SWITCH;
9788 ins->inst_p0 = table;
9789 ins->dreg = table_reg;
9790 MONO_ADD_INS (cfg->cbb, ins);
9793 /* FIXME: Use load_memindex */
9794 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, sum_reg, table_reg, offset_reg);
9795 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, target_reg, sum_reg, 0);
9796 MONO_EMIT_NEW_UNALU (cfg, OP_BR_REG, -1, target_reg);
9798 start_new_bblock = 1;
9799 inline_costs += (BRANCH_COST * 2);
9819 dreg = alloc_freg (cfg);
9822 dreg = alloc_lreg (cfg);
9825 dreg = alloc_ireg_ref (cfg);
9828 dreg = alloc_preg (cfg);
9831 NEW_LOAD_MEMBASE (cfg, ins, ldind_to_load_membase (*ip), dreg, sp [0]->dreg, 0);
9832 ins->type = ldind_type [*ip - CEE_LDIND_I1];
9833 if (*ip == CEE_LDIND_R4)
9834 ins->type = cfg->r4_stack_type;
9835 ins->flags |= ins_flag;
9836 MONO_ADD_INS (bblock, ins);
9838 if (ins_flag & MONO_INST_VOLATILE) {
9839 /* Volatile loads have acquire semantics, see 12.6.7 in Ecma 335 */
9840 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_ACQ);
9856 if (ins_flag & MONO_INST_VOLATILE) {
9857 /* Volatile stores have release semantics, see 12.6.7 in Ecma 335 */
9858 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_REL);
9861 NEW_STORE_MEMBASE (cfg, ins, stind_to_store_membase (*ip), sp [0]->dreg, 0, sp [1]->dreg);
9862 ins->flags |= ins_flag;
9865 MONO_ADD_INS (bblock, ins);
9867 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)))
9868 emit_write_barrier (cfg, sp [0], sp [1]);
9877 MONO_INST_NEW (cfg, ins, (*ip));
9879 ins->sreg1 = sp [0]->dreg;
9880 ins->sreg2 = sp [1]->dreg;
9881 type_from_op (cfg, ins, sp [0], sp [1]);
9883 ins->dreg = alloc_dreg ((cfg), (ins)->type);
9885 /* Use the immediate opcodes if possible */
9886 if ((sp [1]->opcode == OP_ICONST) && mono_arch_is_inst_imm (sp [1]->inst_c0)) {
9887 int imm_opcode = mono_op_to_op_imm_noemul (ins->opcode);
9888 if (imm_opcode != -1) {
9889 ins->opcode = imm_opcode;
9890 ins->inst_p1 = (gpointer)(gssize)(sp [1]->inst_c0);
9893 NULLIFY_INS (sp [1]);
9897 MONO_ADD_INS ((cfg)->cbb, (ins));
9899 *sp++ = mono_decompose_opcode (cfg, ins);
9916 MONO_INST_NEW (cfg, ins, (*ip));
9918 ins->sreg1 = sp [0]->dreg;
9919 ins->sreg2 = sp [1]->dreg;
9920 type_from_op (cfg, ins, sp [0], sp [1]);
9922 add_widen_op (cfg, ins, &sp [0], &sp [1]);
9923 ins->dreg = alloc_dreg ((cfg), (ins)->type);
9925 /* FIXME: Pass opcode to is_inst_imm */
9927 /* Use the immediate opcodes if possible */
9928 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)) {
9931 imm_opcode = mono_op_to_op_imm_noemul (ins->opcode);
9932 #if defined(MONO_ARCH_EMULATE_MUL_DIV) || defined(MONO_ARCH_EMULATE_DIV)
9933 /* Keep emulated opcodes which are optimized away later */
9934 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) {
9935 imm_opcode = mono_op_to_op_imm (ins->opcode);
9938 if (imm_opcode != -1) {
9939 ins->opcode = imm_opcode;
9940 if (sp [1]->opcode == OP_I8CONST) {
9941 #if SIZEOF_REGISTER == 8
9942 ins->inst_imm = sp [1]->inst_l;
9944 ins->inst_ls_word = sp [1]->inst_ls_word;
9945 ins->inst_ms_word = sp [1]->inst_ms_word;
9949 ins->inst_imm = (gssize)(sp [1]->inst_c0);
9952 /* Might be followed by an instruction added by add_widen_op */
9953 if (sp [1]->next == NULL)
9954 NULLIFY_INS (sp [1]);
9957 MONO_ADD_INS ((cfg)->cbb, (ins));
9959 *sp++ = mono_decompose_opcode (cfg, ins);
9972 case CEE_CONV_OVF_I8:
9973 case CEE_CONV_OVF_U8:
9977 /* Special case this earlier so we have long constants in the IR */
9978 if ((((*ip) == CEE_CONV_I8) || ((*ip) == CEE_CONV_U8)) && (sp [-1]->opcode == OP_ICONST)) {
9979 int data = sp [-1]->inst_c0;
9980 sp [-1]->opcode = OP_I8CONST;
9981 sp [-1]->type = STACK_I8;
9982 #if SIZEOF_REGISTER == 8
9983 if ((*ip) == CEE_CONV_U8)
9984 sp [-1]->inst_c0 = (guint32)data;
9986 sp [-1]->inst_c0 = data;
9988 sp [-1]->inst_ls_word = data;
9989 if ((*ip) == CEE_CONV_U8)
9990 sp [-1]->inst_ms_word = 0;
9992 sp [-1]->inst_ms_word = (data < 0) ? -1 : 0;
9994 sp [-1]->dreg = alloc_dreg (cfg, STACK_I8);
10001 case CEE_CONV_OVF_I4:
10002 case CEE_CONV_OVF_I1:
10003 case CEE_CONV_OVF_I2:
10004 case CEE_CONV_OVF_I:
10005 case CEE_CONV_OVF_U:
10008 if (sp [-1]->type == STACK_R8 || sp [-1]->type == STACK_R4) {
10009 ADD_UNOP (CEE_CONV_OVF_I8);
10016 case CEE_CONV_OVF_U1:
10017 case CEE_CONV_OVF_U2:
10018 case CEE_CONV_OVF_U4:
10021 if (sp [-1]->type == STACK_R8 || sp [-1]->type == STACK_R4) {
10022 ADD_UNOP (CEE_CONV_OVF_U8);
10029 case CEE_CONV_OVF_I1_UN:
10030 case CEE_CONV_OVF_I2_UN:
10031 case CEE_CONV_OVF_I4_UN:
10032 case CEE_CONV_OVF_I8_UN:
10033 case CEE_CONV_OVF_U1_UN:
10034 case CEE_CONV_OVF_U2_UN:
10035 case CEE_CONV_OVF_U4_UN:
10036 case CEE_CONV_OVF_U8_UN:
10037 case CEE_CONV_OVF_I_UN:
10038 case CEE_CONV_OVF_U_UN:
10045 CHECK_CFG_EXCEPTION;
10049 case CEE_ADD_OVF_UN:
10051 case CEE_MUL_OVF_UN:
10053 case CEE_SUB_OVF_UN:
10059 GSHAREDVT_FAILURE (*ip);
10062 token = read32 (ip + 1);
10063 klass = mini_get_class (method, token, generic_context);
10064 CHECK_TYPELOAD (klass);
10066 if (generic_class_is_reference_type (cfg, klass)) {
10067 MonoInst *store, *load;
10068 int dreg = alloc_ireg_ref (cfg);
10070 NEW_LOAD_MEMBASE (cfg, load, OP_LOAD_MEMBASE, dreg, sp [1]->dreg, 0);
10071 load->flags |= ins_flag;
10072 MONO_ADD_INS (cfg->cbb, load);
10074 NEW_STORE_MEMBASE (cfg, store, OP_STORE_MEMBASE_REG, sp [0]->dreg, 0, dreg);
10075 store->flags |= ins_flag;
10076 MONO_ADD_INS (cfg->cbb, store);
10078 if (cfg->gen_write_barriers && cfg->method->wrapper_type != MONO_WRAPPER_WRITE_BARRIER)
10079 emit_write_barrier (cfg, sp [0], sp [1]);
10081 mini_emit_stobj (cfg, sp [0], sp [1], klass, FALSE);
10087 int loc_index = -1;
10093 token = read32 (ip + 1);
10094 klass = mini_get_class (method, token, generic_context);
10095 CHECK_TYPELOAD (klass);
10097 /* Optimize the common ldobj+stloc combination */
10100 loc_index = ip [6];
10107 loc_index = ip [5] - CEE_STLOC_0;
10114 if ((loc_index != -1) && ip_in_bb (cfg, bblock, ip + 5)) {
10115 CHECK_LOCAL (loc_index);
10117 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, sp [0]->dreg, 0);
10118 ins->dreg = cfg->locals [loc_index]->dreg;
10119 ins->flags |= ins_flag;
10122 if (ins_flag & MONO_INST_VOLATILE) {
10123 /* Volatile loads have acquire semantics, see 12.6.7 in Ecma 335 */
10124 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_ACQ);
10130 /* Optimize the ldobj+stobj combination */
10131 /* The reference case ends up being a load+store anyway */
10132 /* Skip this if the operation is volatile. */
10133 if (((ip [5] == CEE_STOBJ) && ip_in_bb (cfg, bblock, ip + 5) && read32 (ip + 6) == token) && !generic_class_is_reference_type (cfg, klass) && !(ins_flag & MONO_INST_VOLATILE)) {
10138 mini_emit_stobj (cfg, sp [0], sp [1], klass, FALSE);
10145 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, sp [0]->dreg, 0);
10146 ins->flags |= ins_flag;
10149 if (ins_flag & MONO_INST_VOLATILE) {
10150 /* Volatile loads have acquire semantics, see 12.6.7 in Ecma 335 */
10151 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_ACQ);
10160 CHECK_STACK_OVF (1);
10162 n = read32 (ip + 1);
10164 if (method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD) {
10165 EMIT_NEW_PCONST (cfg, ins, mono_method_get_wrapper_data (method, n));
10166 ins->type = STACK_OBJ;
10169 else if (method->wrapper_type != MONO_WRAPPER_NONE) {
10170 MonoInst *iargs [1];
10171 char *str = mono_method_get_wrapper_data (method, n);
10173 if (cfg->compile_aot)
10174 EMIT_NEW_LDSTRLITCONST (cfg, iargs [0], str);
10176 EMIT_NEW_PCONST (cfg, iargs [0], str);
10177 *sp = mono_emit_jit_icall (cfg, mono_string_new_wrapper, iargs);
10179 if (cfg->opt & MONO_OPT_SHARED) {
10180 MonoInst *iargs [3];
10182 if (cfg->compile_aot) {
10183 cfg->ldstr_list = g_list_prepend (cfg->ldstr_list, GINT_TO_POINTER (n));
10185 EMIT_NEW_DOMAINCONST (cfg, iargs [0]);
10186 EMIT_NEW_IMAGECONST (cfg, iargs [1], image);
10187 EMIT_NEW_ICONST (cfg, iargs [2], mono_metadata_token_index (n));
10188 *sp = mono_emit_jit_icall (cfg, mono_ldstr, iargs);
10189 mono_ldstr (cfg->domain, image, mono_metadata_token_index (n));
10191 if (bblock->out_of_line) {
10192 MonoInst *iargs [2];
10194 if (image == mono_defaults.corlib) {
10196 * Avoid relocations in AOT and save some space by using a
10197 * version of helper_ldstr specialized to mscorlib.
10199 EMIT_NEW_ICONST (cfg, iargs [0], mono_metadata_token_index (n));
10200 *sp = mono_emit_jit_icall (cfg, mono_helper_ldstr_mscorlib, iargs);
10202 /* Avoid creating the string object */
10203 EMIT_NEW_IMAGECONST (cfg, iargs [0], image);
10204 EMIT_NEW_ICONST (cfg, iargs [1], mono_metadata_token_index (n));
10205 *sp = mono_emit_jit_icall (cfg, mono_helper_ldstr, iargs);
10209 if (cfg->compile_aot) {
10210 NEW_LDSTRCONST (cfg, ins, image, n);
10212 MONO_ADD_INS (bblock, ins);
10215 NEW_PCONST (cfg, ins, NULL);
10216 ins->type = STACK_OBJ;
10217 ins->inst_p0 = mono_ldstr (cfg->domain, image, mono_metadata_token_index (n));
10219 OUT_OF_MEMORY_FAILURE;
10222 MONO_ADD_INS (bblock, ins);
10231 MonoInst *iargs [2];
10232 MonoMethodSignature *fsig;
10235 MonoInst *vtable_arg = NULL;
10238 token = read32 (ip + 1);
10239 cmethod = mini_get_method (cfg, method, token, NULL, generic_context);
10240 if (!cmethod || mono_loader_get_last_error ())
10242 fsig = mono_method_get_signature_checked (cmethod, image, token, generic_context, &cfg->error);
10245 mono_save_token_info (cfg, image, token, cmethod);
10247 if (!mono_class_init (cmethod->klass))
10248 TYPE_LOAD_ERROR (cmethod->klass);
10250 context_used = mini_method_check_context_used (cfg, cmethod);
10252 if (mono_security_cas_enabled ()) {
10253 if (check_linkdemand (cfg, method, cmethod))
10254 INLINE_FAILURE ("linkdemand");
10255 CHECK_CFG_EXCEPTION;
10256 } else if (mono_security_core_clr_enabled ()) {
10257 ensure_method_is_allowed_to_call_method (cfg, method, cmethod, bblock, ip);
10260 if (cfg->generic_sharing_context && cmethod && cmethod->klass != method->klass && cmethod->klass->generic_class && mono_method_is_generic_sharable (cmethod, TRUE) && mono_class_needs_cctor_run (cmethod->klass, method)) {
10261 emit_generic_class_init (cfg, cmethod->klass);
10262 CHECK_TYPELOAD (cmethod->klass);
10266 if (cfg->gsharedvt) {
10267 if (mini_is_gsharedvt_variable_signature (sig))
10268 GSHAREDVT_FAILURE (*ip);
10272 n = fsig->param_count;
10276 * Generate smaller code for the common newobj <exception> instruction in
10277 * argument checking code.
10279 if (bblock->out_of_line && cmethod->klass->image == mono_defaults.corlib &&
10280 is_exception_class (cmethod->klass) && n <= 2 &&
10281 ((n < 1) || (!fsig->params [0]->byref && fsig->params [0]->type == MONO_TYPE_STRING)) &&
10282 ((n < 2) || (!fsig->params [1]->byref && fsig->params [1]->type == MONO_TYPE_STRING))) {
10283 MonoInst *iargs [3];
10287 EMIT_NEW_ICONST (cfg, iargs [0], cmethod->klass->type_token);
10290 *sp ++ = mono_emit_jit_icall (cfg, mono_create_corlib_exception_0, iargs);
10293 iargs [1] = sp [0];
10294 *sp ++ = mono_emit_jit_icall (cfg, mono_create_corlib_exception_1, iargs);
10297 iargs [1] = sp [0];
10298 iargs [2] = sp [1];
10299 *sp ++ = mono_emit_jit_icall (cfg, mono_create_corlib_exception_2, iargs);
10302 g_assert_not_reached ();
10310 /* move the args to allow room for 'this' in the first position */
10316 /* check_call_signature () requires sp[0] to be set */
10317 this_ins.type = STACK_OBJ;
10318 sp [0] = &this_ins;
10319 if (check_call_signature (cfg, fsig, sp))
10324 if (mini_class_is_system_array (cmethod->klass)) {
10325 *sp = emit_get_rgctx_method (cfg, context_used,
10326 cmethod, MONO_RGCTX_INFO_METHOD);
10328 /* Avoid varargs in the common case */
10329 if (fsig->param_count == 1)
10330 alloc = mono_emit_jit_icall (cfg, mono_array_new_1, sp);
10331 else if (fsig->param_count == 2)
10332 alloc = mono_emit_jit_icall (cfg, mono_array_new_2, sp);
10333 else if (fsig->param_count == 3)
10334 alloc = mono_emit_jit_icall (cfg, mono_array_new_3, sp);
10335 else if (fsig->param_count == 4)
10336 alloc = mono_emit_jit_icall (cfg, mono_array_new_4, sp);
10338 alloc = handle_array_new (cfg, fsig->param_count, sp, ip);
10339 } else if (cmethod->string_ctor) {
10340 g_assert (!context_used);
10341 g_assert (!vtable_arg);
10342 /* we simply pass a null pointer */
10343 EMIT_NEW_PCONST (cfg, *sp, NULL);
10344 /* now call the string ctor */
10345 alloc = mono_emit_method_call_full (cfg, cmethod, fsig, FALSE, sp, NULL, NULL, NULL);
10347 if (cmethod->klass->valuetype) {
10348 iargs [0] = mono_compile_create_var (cfg, &cmethod->klass->byval_arg, OP_LOCAL);
10349 emit_init_rvar (cfg, iargs [0]->dreg, &cmethod->klass->byval_arg);
10350 EMIT_NEW_TEMPLOADA (cfg, *sp, iargs [0]->inst_c0);
10355 * The code generated by mini_emit_virtual_call () expects
10356 * iargs [0] to be a boxed instance, but luckily the vcall
10357 * will be transformed into a normal call there.
10359 } else if (context_used) {
10360 alloc = handle_alloc (cfg, cmethod->klass, FALSE, context_used);
10363 MonoVTable *vtable = NULL;
10365 if (!cfg->compile_aot)
10366 vtable = mono_class_vtable (cfg->domain, cmethod->klass);
10367 CHECK_TYPELOAD (cmethod->klass);
10370 * TypeInitializationExceptions thrown from the mono_runtime_class_init
10371 * call in mono_jit_runtime_invoke () can abort the finalizer thread.
10372 * As a workaround, we call class cctors before allocating objects.
10374 if (mini_field_access_needs_cctor_run (cfg, method, cmethod->klass, vtable) && !(g_slist_find (class_inits, cmethod->klass))) {
10375 mono_emit_abs_call (cfg, MONO_PATCH_INFO_CLASS_INIT, cmethod->klass, helper_sig_class_init_trampoline, NULL);
10376 if (cfg->verbose_level > 2)
10377 printf ("class %s.%s needs init call for ctor\n", cmethod->klass->name_space, cmethod->klass->name);
10378 class_inits = g_slist_prepend (class_inits, cmethod->klass);
10381 alloc = handle_alloc (cfg, cmethod->klass, FALSE, 0);
10384 CHECK_CFG_EXCEPTION; /*for handle_alloc*/
10387 MONO_EMIT_NEW_UNALU (cfg, OP_NOT_NULL, -1, alloc->dreg);
10389 /* Now call the actual ctor */
10390 handle_ctor_call (cfg, cmethod, fsig, context_used, sp, ip, &bblock, &inline_costs);
10391 CHECK_CFG_EXCEPTION;
10394 if (alloc == NULL) {
10396 EMIT_NEW_TEMPLOAD (cfg, ins, iargs [0]->inst_c0);
10397 type_to_eval_stack_type (cfg, &ins->klass->byval_arg, ins);
10405 if (!(seq_point_locs && mono_bitset_test_fast (seq_point_locs, ip - header->code)))
10406 emit_seq_point (cfg, method, ip, FALSE, TRUE);
10409 case CEE_CASTCLASS:
10413 token = read32 (ip + 1);
10414 klass = mini_get_class (method, token, generic_context);
10415 CHECK_TYPELOAD (klass);
10416 if (sp [0]->type != STACK_OBJ)
10419 ins = handle_castclass (cfg, klass, *sp, ip, &bblock, &inline_costs);
10420 CHECK_CFG_EXCEPTION;
10429 token = read32 (ip + 1);
10430 klass = mini_get_class (method, token, generic_context);
10431 CHECK_TYPELOAD (klass);
10432 if (sp [0]->type != STACK_OBJ)
10435 context_used = mini_class_check_context_used (cfg, klass);
10437 if (!context_used && mini_class_has_reference_variant_generic_argument (cfg, klass, context_used)) {
10438 MonoMethod *mono_isinst = mono_marshal_get_isinst_with_cache ();
10439 MonoInst *args [3];
10445 EMIT_NEW_CLASSCONST (cfg, args [1], klass);
10448 if (cfg->compile_aot)
10449 EMIT_NEW_AOTCONST (cfg, args [2], MONO_PATCH_INFO_CASTCLASS_CACHE, NULL);
10451 EMIT_NEW_PCONST (cfg, args [2], mono_domain_alloc0 (cfg->domain, sizeof (gpointer)));
10453 *sp++ = mono_emit_method_call (cfg, mono_isinst, args, NULL);
10456 } else if (!context_used && (mono_class_is_marshalbyref (klass) || klass->flags & TYPE_ATTRIBUTE_INTERFACE)) {
10457 MonoMethod *mono_isinst;
10458 MonoInst *iargs [1];
10461 mono_isinst = mono_marshal_get_isinst (klass);
10462 iargs [0] = sp [0];
10464 costs = inline_method (cfg, mono_isinst, mono_method_signature (mono_isinst),
10465 iargs, ip, cfg->real_offset, TRUE, &bblock);
10466 CHECK_CFG_EXCEPTION;
10467 g_assert (costs > 0);
10470 cfg->real_offset += 5;
10474 inline_costs += costs;
10477 ins = handle_isinst (cfg, klass, *sp, context_used);
10478 CHECK_CFG_EXCEPTION;
10485 case CEE_UNBOX_ANY: {
10486 MonoInst *res, *addr;
10491 token = read32 (ip + 1);
10492 klass = mini_get_class (method, token, generic_context);
10493 CHECK_TYPELOAD (klass);
10495 mono_save_token_info (cfg, image, token, klass);
10497 context_used = mini_class_check_context_used (cfg, klass);
10499 if (mini_is_gsharedvt_klass (cfg, klass)) {
10500 res = handle_unbox_gsharedvt (cfg, klass, *sp, &bblock);
10502 } else if (generic_class_is_reference_type (cfg, klass)) {
10503 res = handle_castclass (cfg, klass, *sp, ip, &bblock, &inline_costs);
10504 CHECK_CFG_EXCEPTION;
10505 } else if (mono_class_is_nullable (klass)) {
10506 res = handle_unbox_nullable (cfg, *sp, klass, context_used);
10508 addr = handle_unbox (cfg, klass, sp, context_used);
10510 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, addr->dreg, 0);
10521 MonoClass *enum_class;
10522 MonoMethod *has_flag;
10528 token = read32 (ip + 1);
10529 klass = mini_get_class (method, token, generic_context);
10530 CHECK_TYPELOAD (klass);
10532 mono_save_token_info (cfg, image, token, klass);
10534 context_used = mini_class_check_context_used (cfg, klass);
10536 if (generic_class_is_reference_type (cfg, klass)) {
10542 if (klass == mono_defaults.void_class)
10544 if (target_type_is_incompatible (cfg, &klass->byval_arg, *sp))
10546 /* frequent check in generic code: box (struct), brtrue */
10551 * <push int/long ptr>
10554 * constrained. MyFlags
10555 * callvirt instace bool class [mscorlib] System.Enum::HasFlag (class [mscorlib] System.Enum)
10557 * If we find this sequence and the operand types on box and constrained
10558 * are equal, we can emit a specialized instruction sequence instead of
10559 * the very slow HasFlag () call.
10561 if ((cfg->opt & MONO_OPT_INTRINS) &&
10562 /* Cheap checks first. */
10563 ip + 5 + 6 + 5 < end &&
10564 ip [5] == CEE_PREFIX1 &&
10565 ip [6] == CEE_CONSTRAINED_ &&
10566 ip [11] == CEE_CALLVIRT &&
10567 ip_in_bb (cfg, bblock, ip + 5 + 6 + 5) &&
10568 mono_class_is_enum (klass) &&
10569 (enum_class = mini_get_class (method, read32 (ip + 7), generic_context)) &&
10570 (has_flag = mini_get_method (cfg, method, read32 (ip + 12), NULL, generic_context)) &&
10571 has_flag->klass == mono_defaults.enum_class &&
10572 !strcmp (has_flag->name, "HasFlag") &&
10573 has_flag->signature->hasthis &&
10574 has_flag->signature->param_count == 1) {
10575 CHECK_TYPELOAD (enum_class);
10577 if (enum_class == klass) {
10578 MonoInst *enum_this, *enum_flag;
10583 enum_this = sp [0];
10584 enum_flag = sp [1];
10586 *sp++ = handle_enum_has_flag (cfg, klass, enum_this, enum_flag);
10591 // FIXME: LLVM can't handle the inconsistent bb linking
10592 if (!mono_class_is_nullable (klass) &&
10593 ip + 5 < end && ip_in_bb (cfg, bblock, ip + 5) &&
10594 (ip [5] == CEE_BRTRUE ||
10595 ip [5] == CEE_BRTRUE_S ||
10596 ip [5] == CEE_BRFALSE ||
10597 ip [5] == CEE_BRFALSE_S)) {
10598 gboolean is_true = ip [5] == CEE_BRTRUE || ip [5] == CEE_BRTRUE_S;
10600 MonoBasicBlock *true_bb, *false_bb;
10604 if (cfg->verbose_level > 3) {
10605 printf ("converting (in B%d: stack: %d) %s", bblock->block_num, (int)(sp - stack_start), mono_disasm_code_one (NULL, method, ip, NULL));
10606 printf ("<box+brtrue opt>\n");
10611 case CEE_BRFALSE_S:
10614 target = ip + 1 + (signed char)(*ip);
10621 target = ip + 4 + (gint)(read32 (ip));
10625 g_assert_not_reached ();
10629 * We need to link both bblocks, since it is needed for handling stack
10630 * arguments correctly (See test_0_box_brtrue_opt_regress_81102).
10631 * Branching to only one of them would lead to inconsistencies, so
10632 * generate an ICONST+BRTRUE, the branch opts will get rid of them.
10634 GET_BBLOCK (cfg, true_bb, target);
10635 GET_BBLOCK (cfg, false_bb, ip);
10637 mono_link_bblock (cfg, cfg->cbb, true_bb);
10638 mono_link_bblock (cfg, cfg->cbb, false_bb);
10640 if (sp != stack_start) {
10641 handle_stack_args (cfg, stack_start, sp - stack_start);
10643 CHECK_UNVERIFIABLE (cfg);
10646 if (COMPILE_LLVM (cfg)) {
10647 dreg = alloc_ireg (cfg);
10648 MONO_EMIT_NEW_ICONST (cfg, dreg, 0);
10649 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, dreg, is_true ? 0 : 1);
10651 MONO_EMIT_NEW_BRANCH_BLOCK2 (cfg, OP_IBEQ, true_bb, false_bb);
10653 /* The JIT can't eliminate the iconst+compare */
10654 MONO_INST_NEW (cfg, ins, OP_BR);
10655 ins->inst_target_bb = is_true ? true_bb : false_bb;
10656 MONO_ADD_INS (cfg->cbb, ins);
10659 start_new_bblock = 1;
10663 *sp++ = handle_box (cfg, val, klass, context_used, &bblock);
10665 CHECK_CFG_EXCEPTION;
10674 token = read32 (ip + 1);
10675 klass = mini_get_class (method, token, generic_context);
10676 CHECK_TYPELOAD (klass);
10678 mono_save_token_info (cfg, image, token, klass);
10680 context_used = mini_class_check_context_used (cfg, klass);
10682 if (mono_class_is_nullable (klass)) {
10685 val = handle_unbox_nullable (cfg, *sp, klass, context_used);
10686 EMIT_NEW_VARLOADA (cfg, ins, get_vreg_to_inst (cfg, val->dreg), &val->klass->byval_arg);
10690 ins = handle_unbox (cfg, klass, sp, context_used);
10703 MonoClassField *field;
10704 #ifndef DISABLE_REMOTING
10708 gboolean is_instance;
10710 gpointer addr = NULL;
10711 gboolean is_special_static;
10713 MonoInst *store_val = NULL;
10714 MonoInst *thread_ins;
10717 is_instance = (op == CEE_LDFLD || op == CEE_LDFLDA || op == CEE_STFLD);
10719 if (op == CEE_STFLD) {
10722 store_val = sp [1];
10727 if (sp [0]->type == STACK_I4 || sp [0]->type == STACK_I8 || sp [0]->type == STACK_R8)
10729 if (*ip != CEE_LDFLD && sp [0]->type == STACK_VTYPE)
10732 if (op == CEE_STSFLD) {
10735 store_val = sp [0];
10740 token = read32 (ip + 1);
10741 if (method->wrapper_type != MONO_WRAPPER_NONE) {
10742 field = mono_method_get_wrapper_data (method, token);
10743 klass = field->parent;
10746 field = mono_field_from_token_checked (image, token, &klass, generic_context, &cfg->error);
10749 if (!dont_verify && !cfg->skip_visibility && !mono_method_can_access_field (method, field))
10750 FIELD_ACCESS_FAILURE (method, field);
10751 mono_class_init (klass);
10753 if (is_instance && *ip != CEE_LDFLDA && is_magic_tls_access (field))
10756 /* if the class is Critical then transparent code cannot access it's fields */
10757 if (!is_instance && mono_security_core_clr_enabled ())
10758 ensure_method_is_allowed_to_access_field (cfg, method, field, bblock, ip);
10760 /* XXX this is technically required but, so far (SL2), no [SecurityCritical] types (not many exists) have
10761 any visible *instance* field (in fact there's a single case for a static field in Marshal) XXX
10762 if (mono_security_core_clr_enabled ())
10763 ensure_method_is_allowed_to_access_field (cfg, method, field, bblock, ip);
10767 * LDFLD etc. is usable on static fields as well, so convert those cases to
10770 if (is_instance && field->type->attrs & FIELD_ATTRIBUTE_STATIC) {
10782 g_assert_not_reached ();
10784 is_instance = FALSE;
10787 context_used = mini_class_check_context_used (cfg, klass);
10789 /* INSTANCE CASE */
10791 foffset = klass->valuetype? field->offset - sizeof (MonoObject): field->offset;
10792 if (op == CEE_STFLD) {
10793 if (target_type_is_incompatible (cfg, field->type, sp [1]))
10795 #ifndef DISABLE_REMOTING
10796 if ((mono_class_is_marshalbyref (klass) && !MONO_CHECK_THIS (sp [0])) || mono_class_is_contextbound (klass) || klass == mono_defaults.marshalbyrefobject_class) {
10797 MonoMethod *stfld_wrapper = mono_marshal_get_stfld_wrapper (field->type);
10798 MonoInst *iargs [5];
10800 GSHAREDVT_FAILURE (op);
10802 iargs [0] = sp [0];
10803 EMIT_NEW_CLASSCONST (cfg, iargs [1], klass);
10804 EMIT_NEW_FIELDCONST (cfg, iargs [2], field);
10805 EMIT_NEW_ICONST (cfg, iargs [3], klass->valuetype ? field->offset - sizeof (MonoObject) :
10807 iargs [4] = sp [1];
10809 if (cfg->opt & MONO_OPT_INLINE || cfg->compile_aot) {
10810 costs = inline_method (cfg, stfld_wrapper, mono_method_signature (stfld_wrapper),
10811 iargs, ip, cfg->real_offset, TRUE, &bblock);
10812 CHECK_CFG_EXCEPTION;
10813 g_assert (costs > 0);
10815 cfg->real_offset += 5;
10817 inline_costs += costs;
10819 mono_emit_method_call (cfg, stfld_wrapper, iargs, NULL);
10826 MONO_EMIT_NULL_CHECK (cfg, sp [0]->dreg);
10828 if (mini_is_gsharedvt_klass (cfg, klass)) {
10829 MonoInst *offset_ins;
10831 context_used = mini_class_check_context_used (cfg, klass);
10833 offset_ins = emit_get_gsharedvt_info (cfg, field, MONO_RGCTX_INFO_FIELD_OFFSET);
10834 dreg = alloc_ireg_mp (cfg);
10835 EMIT_NEW_BIALU (cfg, ins, OP_PADD, dreg, sp [0]->dreg, offset_ins->dreg);
10836 /* The decomposition will call mini_emit_stobj () which will emit a wbarrier if needed */
10837 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, store, field->type, dreg, 0, sp [1]->dreg);
10839 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, store, field->type, sp [0]->dreg, foffset, sp [1]->dreg);
10841 if (sp [0]->opcode != OP_LDADDR)
10842 store->flags |= MONO_INST_FAULT;
10844 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)) {
10845 /* insert call to write barrier */
10849 dreg = alloc_ireg_mp (cfg);
10850 EMIT_NEW_BIALU_IMM (cfg, ptr, OP_PADD_IMM, dreg, sp [0]->dreg, foffset);
10851 emit_write_barrier (cfg, ptr, sp [1]);
10854 store->flags |= ins_flag;
10861 #ifndef DISABLE_REMOTING
10862 if (is_instance && ((mono_class_is_marshalbyref (klass) && !MONO_CHECK_THIS (sp [0])) || mono_class_is_contextbound (klass) || klass == mono_defaults.marshalbyrefobject_class)) {
10863 MonoMethod *wrapper = (op == CEE_LDFLDA) ? mono_marshal_get_ldflda_wrapper (field->type) : mono_marshal_get_ldfld_wrapper (field->type);
10864 MonoInst *iargs [4];
10866 GSHAREDVT_FAILURE (op);
10868 iargs [0] = sp [0];
10869 EMIT_NEW_CLASSCONST (cfg, iargs [1], klass);
10870 EMIT_NEW_FIELDCONST (cfg, iargs [2], field);
10871 EMIT_NEW_ICONST (cfg, iargs [3], klass->valuetype ? field->offset - sizeof (MonoObject) : field->offset);
10872 if (cfg->opt & MONO_OPT_INLINE || cfg->compile_aot) {
10873 costs = inline_method (cfg, wrapper, mono_method_signature (wrapper),
10874 iargs, ip, cfg->real_offset, TRUE, &bblock);
10875 CHECK_CFG_EXCEPTION;
10876 g_assert (costs > 0);
10878 cfg->real_offset += 5;
10882 inline_costs += costs;
10884 ins = mono_emit_method_call (cfg, wrapper, iargs, NULL);
10890 if (sp [0]->type == STACK_VTYPE) {
10893 /* Have to compute the address of the variable */
10895 var = get_vreg_to_inst (cfg, sp [0]->dreg);
10897 var = mono_compile_create_var_for_vreg (cfg, &klass->byval_arg, OP_LOCAL, sp [0]->dreg);
10899 g_assert (var->klass == klass);
10901 EMIT_NEW_VARLOADA (cfg, ins, var, &var->klass->byval_arg);
10905 if (op == CEE_LDFLDA) {
10906 if (is_magic_tls_access (field)) {
10907 GSHAREDVT_FAILURE (*ip);
10909 *sp++ = create_magic_tls_access (cfg, field, &cached_tls_addr, ins);
10911 if (sp [0]->type == STACK_OBJ) {
10912 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, sp [0]->dreg, 0);
10913 MONO_EMIT_NEW_COND_EXC (cfg, EQ, "NullReferenceException");
10916 dreg = alloc_ireg_mp (cfg);
10918 if (mini_is_gsharedvt_klass (cfg, klass)) {
10919 MonoInst *offset_ins;
10921 offset_ins = emit_get_gsharedvt_info (cfg, field, MONO_RGCTX_INFO_FIELD_OFFSET);
10922 EMIT_NEW_BIALU (cfg, ins, OP_PADD, dreg, sp [0]->dreg, offset_ins->dreg);
10924 EMIT_NEW_BIALU_IMM (cfg, ins, OP_PADD_IMM, dreg, sp [0]->dreg, foffset);
10926 ins->klass = mono_class_from_mono_type (field->type);
10927 ins->type = STACK_MP;
10933 MONO_EMIT_NULL_CHECK (cfg, sp [0]->dreg);
10935 if (mini_is_gsharedvt_klass (cfg, klass)) {
10936 MonoInst *offset_ins;
10938 offset_ins = emit_get_gsharedvt_info (cfg, field, MONO_RGCTX_INFO_FIELD_OFFSET);
10939 dreg = alloc_ireg_mp (cfg);
10940 EMIT_NEW_BIALU (cfg, ins, OP_PADD, dreg, sp [0]->dreg, offset_ins->dreg);
10941 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, load, field->type, dreg, 0);
10943 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, load, field->type, sp [0]->dreg, foffset);
10945 load->flags |= ins_flag;
10946 if (sp [0]->opcode != OP_LDADDR)
10947 load->flags |= MONO_INST_FAULT;
10961 * We can only support shared generic static
10962 * field access on architectures where the
10963 * trampoline code has been extended to handle
10964 * the generic class init.
10966 #ifndef MONO_ARCH_VTABLE_REG
10967 GENERIC_SHARING_FAILURE (op);
10970 context_used = mini_class_check_context_used (cfg, klass);
10972 ftype = mono_field_get_type (field);
10974 if (ftype->attrs & FIELD_ATTRIBUTE_LITERAL)
10977 /* The special_static_fields field is init'd in mono_class_vtable, so it needs
10978 * to be called here.
10980 if (!context_used && !(cfg->opt & MONO_OPT_SHARED)) {
10981 mono_class_vtable (cfg->domain, klass);
10982 CHECK_TYPELOAD (klass);
10984 mono_domain_lock (cfg->domain);
10985 if (cfg->domain->special_static_fields)
10986 addr = g_hash_table_lookup (cfg->domain->special_static_fields, field);
10987 mono_domain_unlock (cfg->domain);
10989 is_special_static = mono_class_field_is_special_static (field);
10991 if (is_special_static && ((gsize)addr & 0x80000000) == 0)
10992 thread_ins = mono_get_thread_intrinsic (cfg);
10996 /* Generate IR to compute the field address */
10997 if (is_special_static && ((gsize)addr & 0x80000000) == 0 && thread_ins && !(cfg->opt & MONO_OPT_SHARED) && !context_used) {
10999 * Fast access to TLS data
11000 * Inline version of get_thread_static_data () in
11004 int idx, static_data_reg, array_reg, dreg;
11006 GSHAREDVT_FAILURE (op);
11008 // offset &= 0x7fffffff;
11009 // idx = (offset >> 24) - 1;
11010 // return ((char*) thread->static_data [idx]) + (offset & 0xffffff);
11011 MONO_ADD_INS (cfg->cbb, thread_ins);
11012 static_data_reg = alloc_ireg (cfg);
11013 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, static_data_reg, thread_ins->dreg, MONO_STRUCT_OFFSET (MonoInternalThread, static_data));
11015 if (cfg->compile_aot) {
11016 int offset_reg, offset2_reg, idx_reg;
11018 /* For TLS variables, this will return the TLS offset */
11019 EMIT_NEW_SFLDACONST (cfg, ins, field);
11020 offset_reg = ins->dreg;
11021 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_IAND_IMM, offset_reg, offset_reg, 0x7fffffff);
11022 idx_reg = alloc_ireg (cfg);
11023 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ISHR_IMM, idx_reg, offset_reg, 24);
11024 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ISUB_IMM, idx_reg, idx_reg, 1);
11025 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ISHL_IMM, idx_reg, idx_reg, sizeof (gpointer) == 8 ? 3 : 2);
11026 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, static_data_reg, static_data_reg, idx_reg);
11027 array_reg = alloc_ireg (cfg);
11028 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, array_reg, static_data_reg, 0);
11029 offset2_reg = alloc_ireg (cfg);
11030 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_IAND_IMM, offset2_reg, offset_reg, 0xffffff);
11031 dreg = alloc_ireg (cfg);
11032 EMIT_NEW_BIALU (cfg, ins, OP_PADD, dreg, array_reg, offset2_reg);
11034 offset = (gsize)addr & 0x7fffffff;
11035 idx = (offset >> 24) - 1;
11037 array_reg = alloc_ireg (cfg);
11038 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, array_reg, static_data_reg, idx * sizeof (gpointer));
11039 dreg = alloc_ireg (cfg);
11040 EMIT_NEW_BIALU_IMM (cfg, ins, OP_ADD_IMM, dreg, array_reg, (offset & 0xffffff));
11042 } else if ((cfg->opt & MONO_OPT_SHARED) ||
11043 (cfg->compile_aot && is_special_static) ||
11044 (context_used && is_special_static)) {
11045 MonoInst *iargs [2];
11047 g_assert (field->parent);
11048 EMIT_NEW_DOMAINCONST (cfg, iargs [0]);
11049 if (context_used) {
11050 iargs [1] = emit_get_rgctx_field (cfg, context_used,
11051 field, MONO_RGCTX_INFO_CLASS_FIELD);
11053 EMIT_NEW_FIELDCONST (cfg, iargs [1], field);
11055 ins = mono_emit_jit_icall (cfg, mono_class_static_field_address, iargs);
11056 } else if (context_used) {
11057 MonoInst *static_data;
11060 g_print ("sharing static field access in %s.%s.%s - depth %d offset %d\n",
11061 method->klass->name_space, method->klass->name, method->name,
11062 depth, field->offset);
11065 if (mono_class_needs_cctor_run (klass, method))
11066 emit_generic_class_init (cfg, klass);
11069 * The pointer we're computing here is
11071 * super_info.static_data + field->offset
11073 static_data = emit_get_rgctx_klass (cfg, context_used,
11074 klass, MONO_RGCTX_INFO_STATIC_DATA);
11076 if (mini_is_gsharedvt_klass (cfg, klass)) {
11077 MonoInst *offset_ins;
11079 offset_ins = emit_get_rgctx_field (cfg, context_used, field, MONO_RGCTX_INFO_FIELD_OFFSET);
11080 dreg = alloc_ireg_mp (cfg);
11081 EMIT_NEW_BIALU (cfg, ins, OP_PADD, dreg, static_data->dreg, offset_ins->dreg);
11082 } else if (field->offset == 0) {
11085 int addr_reg = mono_alloc_preg (cfg);
11086 EMIT_NEW_BIALU_IMM (cfg, ins, OP_PADD_IMM, addr_reg, static_data->dreg, field->offset);
11088 } else if ((cfg->opt & MONO_OPT_SHARED) || (cfg->compile_aot && addr)) {
11089 MonoInst *iargs [2];
11091 g_assert (field->parent);
11092 EMIT_NEW_DOMAINCONST (cfg, iargs [0]);
11093 EMIT_NEW_FIELDCONST (cfg, iargs [1], field);
11094 ins = mono_emit_jit_icall (cfg, mono_class_static_field_address, iargs);
11096 MonoVTable *vtable = NULL;
11098 if (!cfg->compile_aot)
11099 vtable = mono_class_vtable (cfg->domain, klass);
11100 CHECK_TYPELOAD (klass);
11103 if (mini_field_access_needs_cctor_run (cfg, method, klass, vtable)) {
11104 if (!(g_slist_find (class_inits, klass))) {
11105 mono_emit_abs_call (cfg, MONO_PATCH_INFO_CLASS_INIT, klass, helper_sig_class_init_trampoline, NULL);
11106 if (cfg->verbose_level > 2)
11107 printf ("class %s.%s needs init call for %s\n", klass->name_space, klass->name, mono_field_get_name (field));
11108 class_inits = g_slist_prepend (class_inits, klass);
11111 if (cfg->run_cctors) {
11113 /* This makes so that inline cannot trigger */
11114 /* .cctors: too many apps depend on them */
11115 /* running with a specific order... */
11117 if (! vtable->initialized)
11118 INLINE_FAILURE ("class init");
11119 ex = mono_runtime_class_init_full (vtable, FALSE);
11121 set_exception_object (cfg, ex);
11122 goto exception_exit;
11126 if (cfg->compile_aot)
11127 EMIT_NEW_SFLDACONST (cfg, ins, field);
11130 addr = (char*)mono_vtable_get_static_field_data (vtable) + field->offset;
11132 EMIT_NEW_PCONST (cfg, ins, addr);
11135 MonoInst *iargs [1];
11136 EMIT_NEW_ICONST (cfg, iargs [0], GPOINTER_TO_UINT (addr));
11137 ins = mono_emit_jit_icall (cfg, mono_get_special_static_data, iargs);
11141 /* Generate IR to do the actual load/store operation */
11143 if ((op == CEE_STFLD || op == CEE_STSFLD) && (ins_flag & MONO_INST_VOLATILE)) {
11144 /* Volatile stores have release semantics, see 12.6.7 in Ecma 335 */
11145 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_REL);
11148 if (op == CEE_LDSFLDA) {
11149 ins->klass = mono_class_from_mono_type (ftype);
11150 ins->type = STACK_PTR;
11152 } else if (op == CEE_STSFLD) {
11155 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, store, ftype, ins->dreg, 0, store_val->dreg);
11156 store->flags |= ins_flag;
11158 gboolean is_const = FALSE;
11159 MonoVTable *vtable = NULL;
11160 gpointer addr = NULL;
11162 if (!context_used) {
11163 vtable = mono_class_vtable (cfg->domain, klass);
11164 CHECK_TYPELOAD (klass);
11166 if ((ftype->attrs & FIELD_ATTRIBUTE_INIT_ONLY) && (((addr = mono_aot_readonly_field_override (field)) != NULL) ||
11167 (!context_used && !((cfg->opt & MONO_OPT_SHARED) || cfg->compile_aot) && vtable->initialized))) {
11168 int ro_type = ftype->type;
11170 addr = (char*)mono_vtable_get_static_field_data (vtable) + field->offset;
11171 if (ro_type == MONO_TYPE_VALUETYPE && ftype->data.klass->enumtype) {
11172 ro_type = mono_class_enum_basetype (ftype->data.klass)->type;
11175 GSHAREDVT_FAILURE (op);
11177 /* printf ("RO-FIELD %s.%s:%s\n", klass->name_space, klass->name, mono_field_get_name (field));*/
11180 case MONO_TYPE_BOOLEAN:
11182 EMIT_NEW_ICONST (cfg, *sp, *((guint8 *)addr));
11186 EMIT_NEW_ICONST (cfg, *sp, *((gint8 *)addr));
11189 case MONO_TYPE_CHAR:
11191 EMIT_NEW_ICONST (cfg, *sp, *((guint16 *)addr));
11195 EMIT_NEW_ICONST (cfg, *sp, *((gint16 *)addr));
11200 EMIT_NEW_ICONST (cfg, *sp, *((gint32 *)addr));
11204 EMIT_NEW_ICONST (cfg, *sp, *((guint32 *)addr));
11209 case MONO_TYPE_PTR:
11210 case MONO_TYPE_FNPTR:
11211 EMIT_NEW_PCONST (cfg, *sp, *((gpointer *)addr));
11212 type_to_eval_stack_type ((cfg), field->type, *sp);
11215 case MONO_TYPE_STRING:
11216 case MONO_TYPE_OBJECT:
11217 case MONO_TYPE_CLASS:
11218 case MONO_TYPE_SZARRAY:
11219 case MONO_TYPE_ARRAY:
11220 if (!mono_gc_is_moving ()) {
11221 EMIT_NEW_PCONST (cfg, *sp, *((gpointer *)addr));
11222 type_to_eval_stack_type ((cfg), field->type, *sp);
11230 EMIT_NEW_I8CONST (cfg, *sp, *((gint64 *)addr));
11235 case MONO_TYPE_VALUETYPE:
11245 CHECK_STACK_OVF (1);
11247 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, load, field->type, ins->dreg, 0);
11248 load->flags |= ins_flag;
11254 if ((op == CEE_LDFLD || op == CEE_LDSFLD) && (ins_flag & MONO_INST_VOLATILE)) {
11255 /* Volatile loads have acquire semantics, see 12.6.7 in Ecma 335 */
11256 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_ACQ);
11267 token = read32 (ip + 1);
11268 klass = mini_get_class (method, token, generic_context);
11269 CHECK_TYPELOAD (klass);
11270 if (ins_flag & MONO_INST_VOLATILE) {
11271 /* Volatile stores have release semantics, see 12.6.7 in Ecma 335 */
11272 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_REL);
11274 /* FIXME: should check item at sp [1] is compatible with the type of the store. */
11275 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, sp [0]->dreg, 0, sp [1]->dreg);
11276 ins->flags |= ins_flag;
11277 if (cfg->gen_write_barriers && cfg->method->wrapper_type != MONO_WRAPPER_WRITE_BARRIER &&
11278 generic_class_is_reference_type (cfg, klass)) {
11279 /* insert call to write barrier */
11280 emit_write_barrier (cfg, sp [0], sp [1]);
11292 const char *data_ptr;
11294 guint32 field_token;
11300 token = read32 (ip + 1);
11302 klass = mini_get_class (method, token, generic_context);
11303 CHECK_TYPELOAD (klass);
11305 context_used = mini_class_check_context_used (cfg, klass);
11307 if (sp [0]->type == STACK_I8 || (SIZEOF_VOID_P == 8 && sp [0]->type == STACK_PTR)) {
11308 MONO_INST_NEW (cfg, ins, OP_LCONV_TO_OVF_U4);
11309 ins->sreg1 = sp [0]->dreg;
11310 ins->type = STACK_I4;
11311 ins->dreg = alloc_ireg (cfg);
11312 MONO_ADD_INS (cfg->cbb, ins);
11313 *sp = mono_decompose_opcode (cfg, ins);
11316 if (context_used) {
11317 MonoInst *args [3];
11318 MonoClass *array_class = mono_array_class_get (klass, 1);
11319 MonoMethod *managed_alloc = mono_gc_get_managed_array_allocator (array_class);
11321 /* FIXME: Use OP_NEWARR and decompose later to help abcrem */
11324 args [0] = emit_get_rgctx_klass (cfg, context_used,
11325 array_class, MONO_RGCTX_INFO_VTABLE);
11330 ins = mono_emit_method_call (cfg, managed_alloc, args, NULL);
11332 ins = mono_emit_jit_icall (cfg, mono_array_new_specific, args);
11334 if (cfg->opt & MONO_OPT_SHARED) {
11335 /* Decompose now to avoid problems with references to the domainvar */
11336 MonoInst *iargs [3];
11338 EMIT_NEW_DOMAINCONST (cfg, iargs [0]);
11339 EMIT_NEW_CLASSCONST (cfg, iargs [1], klass);
11340 iargs [2] = sp [0];
11342 ins = mono_emit_jit_icall (cfg, mono_array_new, iargs);
11344 /* Decompose later since it is needed by abcrem */
11345 MonoClass *array_type = mono_array_class_get (klass, 1);
11346 mono_class_vtable (cfg->domain, array_type);
11347 CHECK_TYPELOAD (array_type);
11349 MONO_INST_NEW (cfg, ins, OP_NEWARR);
11350 ins->dreg = alloc_ireg_ref (cfg);
11351 ins->sreg1 = sp [0]->dreg;
11352 ins->inst_newa_class = klass;
11353 ins->type = STACK_OBJ;
11354 ins->klass = array_type;
11355 MONO_ADD_INS (cfg->cbb, ins);
11356 cfg->flags |= MONO_CFG_HAS_ARRAY_ACCESS;
11357 cfg->cbb->has_array_access = TRUE;
11359 /* Needed so mono_emit_load_get_addr () gets called */
11360 mono_get_got_var (cfg);
11370 * we inline/optimize the initialization sequence if possible.
11371 * we should also allocate the array as not cleared, since we spend as much time clearing to 0 as initializing
11372 * for small sizes open code the memcpy
11373 * ensure the rva field is big enough
11375 if ((cfg->opt & MONO_OPT_INTRINS) && ip + 6 < end && ip_in_bb (cfg, bblock, 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))) {
11376 MonoMethod *memcpy_method = get_memcpy_method ();
11377 MonoInst *iargs [3];
11378 int add_reg = alloc_ireg_mp (cfg);
11380 EMIT_NEW_BIALU_IMM (cfg, iargs [0], OP_PADD_IMM, add_reg, ins->dreg, MONO_STRUCT_OFFSET (MonoArray, vector));
11381 if (cfg->compile_aot) {
11382 EMIT_NEW_AOTCONST_TOKEN (cfg, iargs [1], MONO_PATCH_INFO_RVA, method->klass->image, GPOINTER_TO_UINT(field_token), STACK_PTR, NULL);
11384 EMIT_NEW_PCONST (cfg, iargs [1], (char*)data_ptr);
11386 EMIT_NEW_ICONST (cfg, iargs [2], data_size);
11387 mono_emit_method_call (cfg, memcpy_method, iargs, NULL);
11396 if (sp [0]->type != STACK_OBJ)
11399 MONO_INST_NEW (cfg, ins, OP_LDLEN);
11400 ins->dreg = alloc_preg (cfg);
11401 ins->sreg1 = sp [0]->dreg;
11402 ins->type = STACK_I4;
11403 /* This flag will be inherited by the decomposition */
11404 ins->flags |= MONO_INST_FAULT;
11405 MONO_ADD_INS (cfg->cbb, ins);
11406 cfg->flags |= MONO_CFG_HAS_ARRAY_ACCESS;
11407 cfg->cbb->has_array_access = TRUE;
11415 if (sp [0]->type != STACK_OBJ)
11418 cfg->flags |= MONO_CFG_HAS_LDELEMA;
11420 klass = mini_get_class (method, read32 (ip + 1), generic_context);
11421 CHECK_TYPELOAD (klass);
11422 /* we need to make sure that this array is exactly the type it needs
11423 * to be for correctness. the wrappers are lax with their usage
11424 * so we need to ignore them here
11426 if (!klass->valuetype && method->wrapper_type == MONO_WRAPPER_NONE && !readonly) {
11427 MonoClass *array_class = mono_array_class_get (klass, 1);
11428 mini_emit_check_array_type (cfg, sp [0], array_class);
11429 CHECK_TYPELOAD (array_class);
11433 ins = mini_emit_ldelema_1_ins (cfg, klass, sp [0], sp [1], TRUE);
11438 case CEE_LDELEM_I1:
11439 case CEE_LDELEM_U1:
11440 case CEE_LDELEM_I2:
11441 case CEE_LDELEM_U2:
11442 case CEE_LDELEM_I4:
11443 case CEE_LDELEM_U4:
11444 case CEE_LDELEM_I8:
11446 case CEE_LDELEM_R4:
11447 case CEE_LDELEM_R8:
11448 case CEE_LDELEM_REF: {
11454 if (*ip == CEE_LDELEM) {
11456 token = read32 (ip + 1);
11457 klass = mini_get_class (method, token, generic_context);
11458 CHECK_TYPELOAD (klass);
11459 mono_class_init (klass);
11462 klass = array_access_to_klass (*ip);
11464 if (sp [0]->type != STACK_OBJ)
11467 cfg->flags |= MONO_CFG_HAS_LDELEMA;
11469 if (mini_is_gsharedvt_variable_klass (cfg, klass)) {
11470 // FIXME-VT: OP_ICONST optimization
11471 addr = mini_emit_ldelema_1_ins (cfg, klass, sp [0], sp [1], TRUE);
11472 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, addr->dreg, 0);
11473 ins->opcode = OP_LOADV_MEMBASE;
11474 } else if (sp [1]->opcode == OP_ICONST) {
11475 int array_reg = sp [0]->dreg;
11476 int index_reg = sp [1]->dreg;
11477 int offset = (mono_class_array_element_size (klass) * sp [1]->inst_c0) + MONO_STRUCT_OFFSET (MonoArray, vector);
11479 MONO_EMIT_BOUNDS_CHECK (cfg, array_reg, MonoArray, max_length, index_reg);
11480 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, array_reg, offset);
11482 addr = mini_emit_ldelema_1_ins (cfg, klass, sp [0], sp [1], TRUE);
11483 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, addr->dreg, 0);
11486 if (*ip == CEE_LDELEM)
11493 case CEE_STELEM_I1:
11494 case CEE_STELEM_I2:
11495 case CEE_STELEM_I4:
11496 case CEE_STELEM_I8:
11497 case CEE_STELEM_R4:
11498 case CEE_STELEM_R8:
11499 case CEE_STELEM_REF:
11504 cfg->flags |= MONO_CFG_HAS_LDELEMA;
11506 if (*ip == CEE_STELEM) {
11508 token = read32 (ip + 1);
11509 klass = mini_get_class (method, token, generic_context);
11510 CHECK_TYPELOAD (klass);
11511 mono_class_init (klass);
11514 klass = array_access_to_klass (*ip);
11516 if (sp [0]->type != STACK_OBJ)
11519 emit_array_store (cfg, klass, sp, TRUE);
11521 if (*ip == CEE_STELEM)
11528 case CEE_CKFINITE: {
11532 MONO_INST_NEW (cfg, ins, OP_CKFINITE);
11533 ins->sreg1 = sp [0]->dreg;
11534 ins->dreg = alloc_freg (cfg);
11535 ins->type = STACK_R8;
11536 MONO_ADD_INS (bblock, ins);
11538 *sp++ = mono_decompose_opcode (cfg, ins);
11543 case CEE_REFANYVAL: {
11544 MonoInst *src_var, *src;
11546 int klass_reg = alloc_preg (cfg);
11547 int dreg = alloc_preg (cfg);
11549 GSHAREDVT_FAILURE (*ip);
11552 MONO_INST_NEW (cfg, ins, *ip);
11555 klass = mini_get_class (method, read32 (ip + 1), generic_context);
11556 CHECK_TYPELOAD (klass);
11558 context_used = mini_class_check_context_used (cfg, klass);
11561 src_var = get_vreg_to_inst (cfg, sp [0]->dreg);
11563 src_var = mono_compile_create_var_for_vreg (cfg, &mono_defaults.typed_reference_class->byval_arg, OP_LOCAL, sp [0]->dreg);
11564 EMIT_NEW_VARLOADA (cfg, src, src_var, src_var->inst_vtype);
11565 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, src->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, klass));
11567 if (context_used) {
11568 MonoInst *klass_ins;
11570 klass_ins = emit_get_rgctx_klass (cfg, context_used,
11571 klass, MONO_RGCTX_INFO_KLASS);
11574 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, klass_reg, klass_ins->dreg);
11575 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException");
11577 mini_emit_class_check (cfg, klass_reg, klass);
11579 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, dreg, src->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, value));
11580 ins->type = STACK_MP;
11585 case CEE_MKREFANY: {
11586 MonoInst *loc, *addr;
11588 GSHAREDVT_FAILURE (*ip);
11591 MONO_INST_NEW (cfg, ins, *ip);
11594 klass = mini_get_class (method, read32 (ip + 1), generic_context);
11595 CHECK_TYPELOAD (klass);
11597 context_used = mini_class_check_context_used (cfg, klass);
11599 loc = mono_compile_create_var (cfg, &mono_defaults.typed_reference_class->byval_arg, OP_LOCAL);
11600 EMIT_NEW_TEMPLOADA (cfg, addr, loc->inst_c0);
11602 if (context_used) {
11603 MonoInst *const_ins;
11604 int type_reg = alloc_preg (cfg);
11606 const_ins = emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_KLASS);
11607 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREP_MEMBASE_REG, addr->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, klass), const_ins->dreg);
11608 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ADD_IMM, type_reg, const_ins->dreg, MONO_STRUCT_OFFSET (MonoClass, byval_arg));
11609 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREP_MEMBASE_REG, addr->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, type), type_reg);
11610 } else if (cfg->compile_aot) {
11611 int const_reg = alloc_preg (cfg);
11612 int type_reg = alloc_preg (cfg);
11614 MONO_EMIT_NEW_CLASSCONST (cfg, const_reg, klass);
11615 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREP_MEMBASE_REG, addr->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, klass), const_reg);
11616 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ADD_IMM, type_reg, const_reg, MONO_STRUCT_OFFSET (MonoClass, byval_arg));
11617 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREP_MEMBASE_REG, addr->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, type), type_reg);
11619 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREP_MEMBASE_IMM, addr->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, type), &klass->byval_arg);
11620 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREP_MEMBASE_IMM, addr->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, klass), klass);
11622 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREP_MEMBASE_REG, addr->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, value), sp [0]->dreg);
11624 EMIT_NEW_TEMPLOAD (cfg, ins, loc->inst_c0);
11625 ins->type = STACK_VTYPE;
11626 ins->klass = mono_defaults.typed_reference_class;
11631 case CEE_LDTOKEN: {
11633 MonoClass *handle_class;
11635 CHECK_STACK_OVF (1);
11638 n = read32 (ip + 1);
11640 if (method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD ||
11641 method->wrapper_type == MONO_WRAPPER_SYNCHRONIZED) {
11642 handle = mono_method_get_wrapper_data (method, n);
11643 handle_class = mono_method_get_wrapper_data (method, n + 1);
11644 if (handle_class == mono_defaults.typehandle_class)
11645 handle = &((MonoClass*)handle)->byval_arg;
11648 handle = mono_ldtoken_checked (image, n, &handle_class, generic_context, &cfg->error);
11653 mono_class_init (handle_class);
11654 if (cfg->generic_sharing_context) {
11655 if (mono_metadata_token_table (n) == MONO_TABLE_TYPEDEF ||
11656 mono_metadata_token_table (n) == MONO_TABLE_TYPEREF) {
11657 /* This case handles ldtoken
11658 of an open type, like for
11661 } else if (handle_class == mono_defaults.typehandle_class) {
11662 context_used = mini_class_check_context_used (cfg, mono_class_from_mono_type (handle));
11663 } else if (handle_class == mono_defaults.fieldhandle_class)
11664 context_used = mini_class_check_context_used (cfg, ((MonoClassField*)handle)->parent);
11665 else if (handle_class == mono_defaults.methodhandle_class)
11666 context_used = mini_method_check_context_used (cfg, handle);
11668 g_assert_not_reached ();
11671 if ((cfg->opt & MONO_OPT_SHARED) &&
11672 method->wrapper_type != MONO_WRAPPER_DYNAMIC_METHOD &&
11673 method->wrapper_type != MONO_WRAPPER_SYNCHRONIZED) {
11674 MonoInst *addr, *vtvar, *iargs [3];
11675 int method_context_used;
11677 method_context_used = mini_method_check_context_used (cfg, method);
11679 vtvar = mono_compile_create_var (cfg, &handle_class->byval_arg, OP_LOCAL);
11681 EMIT_NEW_IMAGECONST (cfg, iargs [0], image);
11682 EMIT_NEW_ICONST (cfg, iargs [1], n);
11683 if (method_context_used) {
11684 iargs [2] = emit_get_rgctx_method (cfg, method_context_used,
11685 method, MONO_RGCTX_INFO_METHOD);
11686 ins = mono_emit_jit_icall (cfg, mono_ldtoken_wrapper_generic_shared, iargs);
11688 EMIT_NEW_PCONST (cfg, iargs [2], generic_context);
11689 ins = mono_emit_jit_icall (cfg, mono_ldtoken_wrapper, iargs);
11691 EMIT_NEW_TEMPLOADA (cfg, addr, vtvar->inst_c0);
11693 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, addr->dreg, 0, ins->dreg);
11695 EMIT_NEW_TEMPLOAD (cfg, ins, vtvar->inst_c0);
11697 if ((ip + 5 < end) && ip_in_bb (cfg, bblock, ip + 5) &&
11698 ((ip [5] == CEE_CALL) || (ip [5] == CEE_CALLVIRT)) &&
11699 (cmethod = mini_get_method (cfg, method, read32 (ip + 6), NULL, generic_context)) &&
11700 (cmethod->klass == mono_defaults.systemtype_class) &&
11701 (strcmp (cmethod->name, "GetTypeFromHandle") == 0)) {
11702 MonoClass *tclass = mono_class_from_mono_type (handle);
11704 mono_class_init (tclass);
11705 if (context_used) {
11706 ins = emit_get_rgctx_klass (cfg, context_used,
11707 tclass, MONO_RGCTX_INFO_REFLECTION_TYPE);
11708 } else if (cfg->compile_aot) {
11709 if (method->wrapper_type) {
11710 mono_error_init (&error); //got to do it since there are multiple conditionals below
11711 if (mono_class_get_checked (tclass->image, tclass->type_token, &error) == tclass && !generic_context) {
11712 /* Special case for static synchronized wrappers */
11713 EMIT_NEW_TYPE_FROM_HANDLE_CONST (cfg, ins, tclass->image, tclass->type_token, generic_context);
11715 mono_error_cleanup (&error); /* FIXME don't swallow the error */
11716 /* FIXME: n is not a normal token */
11718 EMIT_NEW_PCONST (cfg, ins, NULL);
11721 EMIT_NEW_TYPE_FROM_HANDLE_CONST (cfg, ins, image, n, generic_context);
11724 EMIT_NEW_PCONST (cfg, ins, mono_type_get_object (cfg->domain, handle));
11726 ins->type = STACK_OBJ;
11727 ins->klass = cmethod->klass;
11730 MonoInst *addr, *vtvar;
11732 vtvar = mono_compile_create_var (cfg, &handle_class->byval_arg, OP_LOCAL);
11734 if (context_used) {
11735 if (handle_class == mono_defaults.typehandle_class) {
11736 ins = emit_get_rgctx_klass (cfg, context_used,
11737 mono_class_from_mono_type (handle),
11738 MONO_RGCTX_INFO_TYPE);
11739 } else if (handle_class == mono_defaults.methodhandle_class) {
11740 ins = emit_get_rgctx_method (cfg, context_used,
11741 handle, MONO_RGCTX_INFO_METHOD);
11742 } else if (handle_class == mono_defaults.fieldhandle_class) {
11743 ins = emit_get_rgctx_field (cfg, context_used,
11744 handle, MONO_RGCTX_INFO_CLASS_FIELD);
11746 g_assert_not_reached ();
11748 } else if (cfg->compile_aot) {
11749 EMIT_NEW_LDTOKENCONST (cfg, ins, image, n, generic_context);
11751 EMIT_NEW_PCONST (cfg, ins, handle);
11753 EMIT_NEW_TEMPLOADA (cfg, addr, vtvar->inst_c0);
11754 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, addr->dreg, 0, ins->dreg);
11755 EMIT_NEW_TEMPLOAD (cfg, ins, vtvar->inst_c0);
11765 MONO_INST_NEW (cfg, ins, OP_THROW);
11767 ins->sreg1 = sp [0]->dreg;
11769 bblock->out_of_line = TRUE;
11770 MONO_ADD_INS (bblock, ins);
11771 MONO_INST_NEW (cfg, ins, OP_NOT_REACHED);
11772 MONO_ADD_INS (bblock, ins);
11775 link_bblock (cfg, bblock, end_bblock);
11776 start_new_bblock = 1;
11778 case CEE_ENDFINALLY:
11779 /* mono_save_seq_point_info () depends on this */
11780 if (sp != stack_start)
11781 emit_seq_point (cfg, method, ip, FALSE, FALSE);
11782 MONO_INST_NEW (cfg, ins, OP_ENDFINALLY);
11783 MONO_ADD_INS (bblock, ins);
11785 start_new_bblock = 1;
11788 * Control will leave the method so empty the stack, otherwise
11789 * the next basic block will start with a nonempty stack.
11791 while (sp != stack_start) {
11796 case CEE_LEAVE_S: {
11799 if (*ip == CEE_LEAVE) {
11801 target = ip + 5 + (gint32)read32(ip + 1);
11804 target = ip + 2 + (signed char)(ip [1]);
11807 /* empty the stack */
11808 while (sp != stack_start) {
11813 * If this leave statement is in a catch block, check for a
11814 * pending exception, and rethrow it if necessary.
11815 * We avoid doing this in runtime invoke wrappers, since those are called
11816 * by native code which excepts the wrapper to catch all exceptions.
11818 for (i = 0; i < header->num_clauses; ++i) {
11819 MonoExceptionClause *clause = &header->clauses [i];
11822 * Use <= in the final comparison to handle clauses with multiple
11823 * leave statements, like in bug #78024.
11824 * The ordering of the exception clauses guarantees that we find the
11825 * innermost clause.
11827 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) {
11829 MonoBasicBlock *dont_throw;
11834 NEW_TEMPLOAD (cfg, load, mono_find_exvar_for_offset (cfg, clause->handler_offset)->inst_c0);
11837 exc_ins = mono_emit_jit_icall (cfg, mono_thread_get_undeniable_exception, NULL);
11839 NEW_BBLOCK (cfg, dont_throw);
11842 * Currently, we always rethrow the abort exception, despite the
11843 * fact that this is not correct. See thread6.cs for an example.
11844 * But propagating the abort exception is more important than
11845 * getting the sematics right.
11847 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, exc_ins->dreg, 0);
11848 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, dont_throw);
11849 MONO_EMIT_NEW_UNALU (cfg, OP_THROW, -1, exc_ins->dreg);
11851 MONO_START_BB (cfg, dont_throw);
11856 if ((handlers = mono_find_final_block (cfg, ip, target, MONO_EXCEPTION_CLAUSE_FINALLY))) {
11858 MonoExceptionClause *clause;
11860 for (tmp = handlers; tmp; tmp = tmp->next) {
11861 clause = tmp->data;
11862 tblock = cfg->cil_offset_to_bb [clause->handler_offset];
11864 link_bblock (cfg, bblock, tblock);
11865 MONO_INST_NEW (cfg, ins, OP_CALL_HANDLER);
11866 ins->inst_target_bb = tblock;
11867 ins->inst_eh_block = clause;
11868 MONO_ADD_INS (bblock, ins);
11869 bblock->has_call_handler = 1;
11870 if (COMPILE_LLVM (cfg)) {
11871 MonoBasicBlock *target_bb;
11874 * Link the finally bblock with the target, since it will
11875 * conceptually branch there.
11876 * FIXME: Have to link the bblock containing the endfinally.
11878 GET_BBLOCK (cfg, target_bb, target);
11879 link_bblock (cfg, tblock, target_bb);
11882 g_list_free (handlers);
11885 MONO_INST_NEW (cfg, ins, OP_BR);
11886 MONO_ADD_INS (bblock, ins);
11887 GET_BBLOCK (cfg, tblock, target);
11888 link_bblock (cfg, bblock, tblock);
11889 ins->inst_target_bb = tblock;
11890 start_new_bblock = 1;
11892 if (*ip == CEE_LEAVE)
11901 * Mono specific opcodes
11903 case MONO_CUSTOM_PREFIX: {
11905 g_assert (method->wrapper_type != MONO_WRAPPER_NONE);
11909 case CEE_MONO_ICALL: {
11911 MonoJitICallInfo *info;
11913 token = read32 (ip + 2);
11914 func = mono_method_get_wrapper_data (method, token);
11915 info = mono_find_jit_icall_by_addr (func);
11917 g_error ("Could not find icall address in wrapper %s", mono_method_full_name (method, 1));
11920 CHECK_STACK (info->sig->param_count);
11921 sp -= info->sig->param_count;
11923 ins = mono_emit_jit_icall (cfg, info->func, sp);
11924 if (!MONO_TYPE_IS_VOID (info->sig->ret))
11928 inline_costs += 10 * num_calls++;
11932 case CEE_MONO_LDPTR: {
11935 CHECK_STACK_OVF (1);
11937 token = read32 (ip + 2);
11939 ptr = mono_method_get_wrapper_data (method, token);
11940 /* FIXME: Generalize this */
11941 if (cfg->compile_aot && ptr == mono_thread_interruption_request_flag ()) {
11942 EMIT_NEW_AOTCONST (cfg, ins, MONO_PATCH_INFO_INTERRUPTION_REQUEST_FLAG, NULL);
11947 EMIT_NEW_PCONST (cfg, ins, ptr);
11950 inline_costs += 10 * num_calls++;
11951 /* Can't embed random pointers into AOT code */
11955 case CEE_MONO_JIT_ICALL_ADDR: {
11956 MonoJitICallInfo *callinfo;
11959 CHECK_STACK_OVF (1);
11961 token = read32 (ip + 2);
11963 ptr = mono_method_get_wrapper_data (method, token);
11964 callinfo = mono_find_jit_icall_by_addr (ptr);
11965 g_assert (callinfo);
11966 EMIT_NEW_JIT_ICALL_ADDRCONST (cfg, ins, (char*)callinfo->name);
11969 inline_costs += 10 * num_calls++;
11972 case CEE_MONO_ICALL_ADDR: {
11973 MonoMethod *cmethod;
11976 CHECK_STACK_OVF (1);
11978 token = read32 (ip + 2);
11980 cmethod = mono_method_get_wrapper_data (method, token);
11982 if (cfg->compile_aot) {
11983 EMIT_NEW_AOTCONST (cfg, ins, MONO_PATCH_INFO_ICALL_ADDR, cmethod);
11985 ptr = mono_lookup_internal_call (cmethod);
11987 EMIT_NEW_PCONST (cfg, ins, ptr);
11993 case CEE_MONO_VTADDR: {
11994 MonoInst *src_var, *src;
12000 src_var = get_vreg_to_inst (cfg, sp [0]->dreg);
12001 EMIT_NEW_VARLOADA ((cfg), (src), src_var, src_var->inst_vtype);
12006 case CEE_MONO_NEWOBJ: {
12007 MonoInst *iargs [2];
12009 CHECK_STACK_OVF (1);
12011 token = read32 (ip + 2);
12012 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
12013 mono_class_init (klass);
12014 NEW_DOMAINCONST (cfg, iargs [0]);
12015 MONO_ADD_INS (cfg->cbb, iargs [0]);
12016 NEW_CLASSCONST (cfg, iargs [1], klass);
12017 MONO_ADD_INS (cfg->cbb, iargs [1]);
12018 *sp++ = mono_emit_jit_icall (cfg, mono_object_new, iargs);
12020 inline_costs += 10 * num_calls++;
12023 case CEE_MONO_OBJADDR:
12026 MONO_INST_NEW (cfg, ins, OP_MOVE);
12027 ins->dreg = alloc_ireg_mp (cfg);
12028 ins->sreg1 = sp [0]->dreg;
12029 ins->type = STACK_MP;
12030 MONO_ADD_INS (cfg->cbb, ins);
12034 case CEE_MONO_LDNATIVEOBJ:
12036 * Similar to LDOBJ, but instead load the unmanaged
12037 * representation of the vtype to the stack.
12042 token = read32 (ip + 2);
12043 klass = mono_method_get_wrapper_data (method, token);
12044 g_assert (klass->valuetype);
12045 mono_class_init (klass);
12048 MonoInst *src, *dest, *temp;
12051 temp = mono_compile_create_var (cfg, &klass->byval_arg, OP_LOCAL);
12052 temp->backend.is_pinvoke = 1;
12053 EMIT_NEW_TEMPLOADA (cfg, dest, temp->inst_c0);
12054 mini_emit_stobj (cfg, dest, src, klass, TRUE);
12056 EMIT_NEW_TEMPLOAD (cfg, dest, temp->inst_c0);
12057 dest->type = STACK_VTYPE;
12058 dest->klass = klass;
12064 case CEE_MONO_RETOBJ: {
12066 * Same as RET, but return the native representation of a vtype
12069 g_assert (cfg->ret);
12070 g_assert (mono_method_signature (method)->pinvoke);
12075 token = read32 (ip + 2);
12076 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
12078 if (!cfg->vret_addr) {
12079 g_assert (cfg->ret_var_is_local);
12081 EMIT_NEW_VARLOADA (cfg, ins, cfg->ret, cfg->ret->inst_vtype);
12083 EMIT_NEW_RETLOADA (cfg, ins);
12085 mini_emit_stobj (cfg, ins, sp [0], klass, TRUE);
12087 if (sp != stack_start)
12090 MONO_INST_NEW (cfg, ins, OP_BR);
12091 ins->inst_target_bb = end_bblock;
12092 MONO_ADD_INS (bblock, ins);
12093 link_bblock (cfg, bblock, end_bblock);
12094 start_new_bblock = 1;
12098 case CEE_MONO_CISINST:
12099 case CEE_MONO_CCASTCLASS: {
12104 token = read32 (ip + 2);
12105 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
12106 if (ip [1] == CEE_MONO_CISINST)
12107 ins = handle_cisinst (cfg, klass, sp [0]);
12109 ins = handle_ccastclass (cfg, klass, sp [0]);
12115 case CEE_MONO_SAVE_LMF:
12116 case CEE_MONO_RESTORE_LMF:
12117 #ifdef MONO_ARCH_HAVE_LMF_OPS
12118 MONO_INST_NEW (cfg, ins, (ip [1] == CEE_MONO_SAVE_LMF) ? OP_SAVE_LMF : OP_RESTORE_LMF);
12119 MONO_ADD_INS (bblock, ins);
12120 cfg->need_lmf_area = TRUE;
12124 case CEE_MONO_CLASSCONST:
12125 CHECK_STACK_OVF (1);
12127 token = read32 (ip + 2);
12128 EMIT_NEW_CLASSCONST (cfg, ins, mono_method_get_wrapper_data (method, token));
12131 inline_costs += 10 * num_calls++;
12133 case CEE_MONO_NOT_TAKEN:
12134 bblock->out_of_line = TRUE;
12137 case CEE_MONO_TLS: {
12140 CHECK_STACK_OVF (1);
12142 key = (gint32)read32 (ip + 2);
12143 g_assert (key < TLS_KEY_NUM);
12145 ins = mono_create_tls_get (cfg, key);
12147 if (cfg->compile_aot) {
12149 MONO_INST_NEW (cfg, ins, OP_TLS_GET);
12150 ins->dreg = alloc_preg (cfg);
12151 ins->type = STACK_PTR;
12153 g_assert_not_reached ();
12156 ins->type = STACK_PTR;
12157 MONO_ADD_INS (bblock, ins);
12162 case CEE_MONO_DYN_CALL: {
12163 MonoCallInst *call;
12165 /* It would be easier to call a trampoline, but that would put an
12166 * extra frame on the stack, confusing exception handling. So
12167 * implement it inline using an opcode for now.
12170 if (!cfg->dyn_call_var) {
12171 cfg->dyn_call_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
12172 /* prevent it from being register allocated */
12173 cfg->dyn_call_var->flags |= MONO_INST_VOLATILE;
12176 /* Has to use a call inst since it local regalloc expects it */
12177 MONO_INST_NEW_CALL (cfg, call, OP_DYN_CALL);
12178 ins = (MonoInst*)call;
12180 ins->sreg1 = sp [0]->dreg;
12181 ins->sreg2 = sp [1]->dreg;
12182 MONO_ADD_INS (bblock, ins);
12184 cfg->param_area = MAX (cfg->param_area, MONO_ARCH_DYN_CALL_PARAM_AREA);
12187 inline_costs += 10 * num_calls++;
12191 case CEE_MONO_MEMORY_BARRIER: {
12193 emit_memory_barrier (cfg, (int)read32 (ip + 2));
12197 case CEE_MONO_JIT_ATTACH: {
12198 MonoInst *args [16], *domain_ins;
12199 MonoInst *ad_ins, *jit_tls_ins;
12200 MonoBasicBlock *next_bb = NULL, *call_bb = NULL;
12202 cfg->orig_domain_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
12204 EMIT_NEW_PCONST (cfg, ins, NULL);
12205 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, cfg->orig_domain_var->dreg, ins->dreg);
12207 ad_ins = mono_get_domain_intrinsic (cfg);
12208 jit_tls_ins = mono_get_jit_tls_intrinsic (cfg);
12210 if (MONO_ARCH_HAVE_TLS_GET && ad_ins && jit_tls_ins) {
12211 NEW_BBLOCK (cfg, next_bb);
12212 NEW_BBLOCK (cfg, call_bb);
12214 if (cfg->compile_aot) {
12215 /* AOT code is only used in the root domain */
12216 EMIT_NEW_PCONST (cfg, domain_ins, NULL);
12218 EMIT_NEW_PCONST (cfg, domain_ins, cfg->domain);
12220 MONO_ADD_INS (cfg->cbb, ad_ins);
12221 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, ad_ins->dreg, domain_ins->dreg);
12222 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, call_bb);
12224 MONO_ADD_INS (cfg->cbb, jit_tls_ins);
12225 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, jit_tls_ins->dreg, 0);
12226 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, call_bb);
12228 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, next_bb);
12229 MONO_START_BB (cfg, call_bb);
12232 if (cfg->compile_aot) {
12233 /* AOT code is only used in the root domain */
12234 EMIT_NEW_PCONST (cfg, args [0], NULL);
12236 EMIT_NEW_PCONST (cfg, args [0], cfg->domain);
12238 ins = mono_emit_jit_icall (cfg, mono_jit_thread_attach, args);
12239 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, cfg->orig_domain_var->dreg, ins->dreg);
12242 MONO_START_BB (cfg, next_bb);
12248 case CEE_MONO_JIT_DETACH: {
12249 MonoInst *args [16];
12251 /* Restore the original domain */
12252 dreg = alloc_ireg (cfg);
12253 EMIT_NEW_UNALU (cfg, args [0], OP_MOVE, dreg, cfg->orig_domain_var->dreg);
12254 mono_emit_jit_icall (cfg, mono_jit_set_domain, args);
12259 g_error ("opcode 0x%02x 0x%02x not handled", MONO_CUSTOM_PREFIX, ip [1]);
12265 case CEE_PREFIX1: {
12268 case CEE_ARGLIST: {
12269 /* somewhat similar to LDTOKEN */
12270 MonoInst *addr, *vtvar;
12271 CHECK_STACK_OVF (1);
12272 vtvar = mono_compile_create_var (cfg, &mono_defaults.argumenthandle_class->byval_arg, OP_LOCAL);
12274 EMIT_NEW_TEMPLOADA (cfg, addr, vtvar->inst_c0);
12275 EMIT_NEW_UNALU (cfg, ins, OP_ARGLIST, -1, addr->dreg);
12277 EMIT_NEW_TEMPLOAD (cfg, ins, vtvar->inst_c0);
12278 ins->type = STACK_VTYPE;
12279 ins->klass = mono_defaults.argumenthandle_class;
12289 MonoInst *cmp, *arg1, *arg2;
12297 * The following transforms:
12298 * CEE_CEQ into OP_CEQ
12299 * CEE_CGT into OP_CGT
12300 * CEE_CGT_UN into OP_CGT_UN
12301 * CEE_CLT into OP_CLT
12302 * CEE_CLT_UN into OP_CLT_UN
12304 MONO_INST_NEW (cfg, cmp, (OP_CEQ - CEE_CEQ) + ip [1]);
12306 MONO_INST_NEW (cfg, ins, cmp->opcode);
12307 cmp->sreg1 = arg1->dreg;
12308 cmp->sreg2 = arg2->dreg;
12309 type_from_op (cfg, cmp, arg1, arg2);
12311 add_widen_op (cfg, cmp, &arg1, &arg2);
12312 if ((arg1->type == STACK_I8) || ((SIZEOF_VOID_P == 8) && ((arg1->type == STACK_PTR) || (arg1->type == STACK_OBJ) || (arg1->type == STACK_MP))))
12313 cmp->opcode = OP_LCOMPARE;
12314 else if (arg1->type == STACK_R4)
12315 cmp->opcode = OP_RCOMPARE;
12316 else if (arg1->type == STACK_R8)
12317 cmp->opcode = OP_FCOMPARE;
12319 cmp->opcode = OP_ICOMPARE;
12320 MONO_ADD_INS (bblock, cmp);
12321 ins->type = STACK_I4;
12322 ins->dreg = alloc_dreg (cfg, ins->type);
12323 type_from_op (cfg, ins, arg1, arg2);
12325 if (cmp->opcode == OP_FCOMPARE || cmp->opcode == OP_RCOMPARE) {
12327 * The backends expect the fceq opcodes to do the
12330 ins->sreg1 = cmp->sreg1;
12331 ins->sreg2 = cmp->sreg2;
12334 MONO_ADD_INS (bblock, ins);
12340 MonoInst *argconst;
12341 MonoMethod *cil_method;
12343 CHECK_STACK_OVF (1);
12345 n = read32 (ip + 2);
12346 cmethod = mini_get_method (cfg, method, n, NULL, generic_context);
12347 if (!cmethod || mono_loader_get_last_error ())
12349 mono_class_init (cmethod->klass);
12351 mono_save_token_info (cfg, image, n, cmethod);
12353 context_used = mini_method_check_context_used (cfg, cmethod);
12355 cil_method = cmethod;
12356 if (!dont_verify && !cfg->skip_visibility && !mono_method_can_access_method (method, cmethod))
12357 METHOD_ACCESS_FAILURE (method, cil_method);
12359 if (mono_security_cas_enabled ()) {
12360 if (check_linkdemand (cfg, method, cmethod))
12361 INLINE_FAILURE ("linkdemand");
12362 CHECK_CFG_EXCEPTION;
12363 } else if (mono_security_core_clr_enabled ()) {
12364 ensure_method_is_allowed_to_call_method (cfg, method, cmethod, bblock, ip);
12368 * Optimize the common case of ldftn+delegate creation
12370 if ((sp > stack_start) && (ip + 6 + 5 < end) && ip_in_bb (cfg, bblock, ip + 6) && (ip [6] == CEE_NEWOBJ)) {
12371 MonoMethod *ctor_method = mini_get_method (cfg, method, read32 (ip + 7), NULL, generic_context);
12372 if (ctor_method && (ctor_method->klass->parent == mono_defaults.multicastdelegate_class)) {
12373 MonoInst *target_ins, *handle_ins;
12374 MonoMethod *invoke;
12375 int invoke_context_used;
12377 invoke = mono_get_delegate_invoke (ctor_method->klass);
12378 if (!invoke || !mono_method_signature (invoke))
12381 invoke_context_used = mini_method_check_context_used (cfg, invoke);
12383 target_ins = sp [-1];
12385 if (mono_security_core_clr_enabled ())
12386 ensure_method_is_allowed_to_call_method (cfg, method, ctor_method, bblock, ip);
12388 if (!(cmethod->flags & METHOD_ATTRIBUTE_STATIC)) {
12389 /*LAME IMPL: We must not add a null check for virtual invoke delegates.*/
12390 if (mono_method_signature (invoke)->param_count == mono_method_signature (cmethod)->param_count) {
12391 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, target_ins->dreg, 0);
12392 MONO_EMIT_NEW_COND_EXC (cfg, EQ, "ArgumentException");
12396 #if defined(MONO_ARCH_HAVE_CREATE_DELEGATE_TRAMPOLINE)
12397 /* FIXME: SGEN support */
12398 if (invoke_context_used == 0) {
12400 if (cfg->verbose_level > 3)
12401 g_print ("converting (in B%d: stack: %d) %s", bblock->block_num, (int)(sp - stack_start), mono_disasm_code_one (NULL, method, ip, NULL));
12402 if ((handle_ins = handle_delegate_ctor (cfg, ctor_method->klass, target_ins, cmethod, context_used, FALSE))) {
12405 CHECK_CFG_EXCEPTION;
12416 argconst = emit_get_rgctx_method (cfg, context_used, cmethod, MONO_RGCTX_INFO_METHOD);
12417 ins = mono_emit_jit_icall (cfg, mono_ldftn, &argconst);
12421 inline_costs += 10 * num_calls++;
12424 case CEE_LDVIRTFTN: {
12425 MonoInst *args [2];
12429 n = read32 (ip + 2);
12430 cmethod = mini_get_method (cfg, method, n, NULL, generic_context);
12431 if (!cmethod || mono_loader_get_last_error ())
12433 mono_class_init (cmethod->klass);
12435 context_used = mini_method_check_context_used (cfg, cmethod);
12437 if (mono_security_cas_enabled ()) {
12438 if (check_linkdemand (cfg, method, cmethod))
12439 INLINE_FAILURE ("linkdemand");
12440 CHECK_CFG_EXCEPTION;
12441 } else if (mono_security_core_clr_enabled ()) {
12442 ensure_method_is_allowed_to_call_method (cfg, method, cmethod, bblock, ip);
12446 * Optimize the common case of ldvirtftn+delegate creation
12448 if ((sp > stack_start) && (ip + 6 + 5 < end) && ip_in_bb (cfg, bblock, ip + 6) && (ip [6] == CEE_NEWOBJ) && (ip > header->code && ip [-1] == CEE_DUP)) {
12449 MonoMethod *ctor_method = mini_get_method (cfg, method, read32 (ip + 7), NULL, generic_context);
12450 if (ctor_method && (ctor_method->klass->parent == mono_defaults.multicastdelegate_class)) {
12451 MonoInst *target_ins, *handle_ins;
12452 MonoMethod *invoke;
12453 int invoke_context_used;
12455 invoke = mono_get_delegate_invoke (ctor_method->klass);
12456 if (!invoke || !mono_method_signature (invoke))
12459 invoke_context_used = mini_method_check_context_used (cfg, invoke);
12461 target_ins = sp [-1];
12463 if (mono_security_core_clr_enabled ())
12464 ensure_method_is_allowed_to_call_method (cfg, method, ctor_method, bblock, ip);
12466 #if defined(MONO_ARCH_HAVE_CREATE_DELEGATE_TRAMPOLINE)
12467 /* FIXME: SGEN support */
12468 if (invoke_context_used == 0) {
12470 if (cfg->verbose_level > 3)
12471 g_print ("converting (in B%d: stack: %d) %s", bblock->block_num, (int)(sp - stack_start), mono_disasm_code_one (NULL, method, ip, NULL));
12472 if ((handle_ins = handle_delegate_ctor (cfg, ctor_method->klass, target_ins, cmethod, context_used, TRUE))) {
12475 CHECK_CFG_EXCEPTION;
12489 args [1] = emit_get_rgctx_method (cfg, context_used,
12490 cmethod, MONO_RGCTX_INFO_METHOD);
12493 *sp++ = mono_emit_jit_icall (cfg, mono_ldvirtfn_gshared, args);
12495 *sp++ = mono_emit_jit_icall (cfg, mono_ldvirtfn, args);
12498 inline_costs += 10 * num_calls++;
12502 CHECK_STACK_OVF (1);
12504 n = read16 (ip + 2);
12506 EMIT_NEW_ARGLOAD (cfg, ins, n);
12511 CHECK_STACK_OVF (1);
12513 n = read16 (ip + 2);
12515 NEW_ARGLOADA (cfg, ins, n);
12516 MONO_ADD_INS (cfg->cbb, ins);
12524 n = read16 (ip + 2);
12526 if (!dont_verify_stloc && target_type_is_incompatible (cfg, param_types [n], *sp))
12528 EMIT_NEW_ARGSTORE (cfg, ins, n, *sp);
12532 CHECK_STACK_OVF (1);
12534 n = read16 (ip + 2);
12536 EMIT_NEW_LOCLOAD (cfg, ins, n);
12541 unsigned char *tmp_ip;
12542 CHECK_STACK_OVF (1);
12544 n = read16 (ip + 2);
12547 if ((tmp_ip = emit_optimized_ldloca_ir (cfg, ip, end, 2))) {
12553 EMIT_NEW_LOCLOADA (cfg, ins, n);
12562 n = read16 (ip + 2);
12564 if (!dont_verify_stloc && target_type_is_incompatible (cfg, header->locals [n], *sp))
12566 emit_stloc_ir (cfg, sp, header, n);
12573 if (sp != stack_start)
12575 if (cfg->method != method)
12577 * Inlining this into a loop in a parent could lead to
12578 * stack overflows which is different behavior than the
12579 * non-inlined case, thus disable inlining in this case.
12581 INLINE_FAILURE("localloc");
12583 MONO_INST_NEW (cfg, ins, OP_LOCALLOC);
12584 ins->dreg = alloc_preg (cfg);
12585 ins->sreg1 = sp [0]->dreg;
12586 ins->type = STACK_PTR;
12587 MONO_ADD_INS (cfg->cbb, ins);
12589 cfg->flags |= MONO_CFG_HAS_ALLOCA;
12591 ins->flags |= MONO_INST_INIT;
12596 case CEE_ENDFILTER: {
12597 MonoExceptionClause *clause, *nearest;
12598 int cc, nearest_num;
12602 if ((sp != stack_start) || (sp [0]->type != STACK_I4))
12604 MONO_INST_NEW (cfg, ins, OP_ENDFILTER);
12605 ins->sreg1 = (*sp)->dreg;
12606 MONO_ADD_INS (bblock, ins);
12607 start_new_bblock = 1;
12612 for (cc = 0; cc < header->num_clauses; ++cc) {
12613 clause = &header->clauses [cc];
12614 if ((clause->flags & MONO_EXCEPTION_CLAUSE_FILTER) &&
12615 ((ip - header->code) > clause->data.filter_offset && (ip - header->code) <= clause->handler_offset) &&
12616 (!nearest || (clause->data.filter_offset < nearest->data.filter_offset))) {
12621 g_assert (nearest);
12622 if ((ip - header->code) != nearest->handler_offset)
12627 case CEE_UNALIGNED_:
12628 ins_flag |= MONO_INST_UNALIGNED;
12629 /* FIXME: record alignment? we can assume 1 for now */
12633 case CEE_VOLATILE_:
12634 ins_flag |= MONO_INST_VOLATILE;
12638 ins_flag |= MONO_INST_TAILCALL;
12639 cfg->flags |= MONO_CFG_HAS_TAIL;
12640 /* Can't inline tail calls at this time */
12641 inline_costs += 100000;
12648 token = read32 (ip + 2);
12649 klass = mini_get_class (method, token, generic_context);
12650 CHECK_TYPELOAD (klass);
12651 if (generic_class_is_reference_type (cfg, klass))
12652 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STORE_MEMBASE_IMM, sp [0]->dreg, 0, 0);
12654 mini_emit_initobj (cfg, *sp, NULL, klass);
12658 case CEE_CONSTRAINED_:
12660 token = read32 (ip + 2);
12661 constrained_call = mini_get_class (method, token, generic_context);
12662 CHECK_TYPELOAD (constrained_call);
12666 case CEE_INITBLK: {
12667 MonoInst *iargs [3];
12671 /* Skip optimized paths for volatile operations. */
12672 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)) {
12673 mini_emit_memcpy (cfg, sp [0]->dreg, 0, sp [1]->dreg, 0, sp [2]->inst_c0, 0);
12674 } 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)) {
12675 /* emit_memset only works when val == 0 */
12676 mini_emit_memset (cfg, sp [0]->dreg, 0, sp [2]->inst_c0, sp [1]->inst_c0, 0);
12679 iargs [0] = sp [0];
12680 iargs [1] = sp [1];
12681 iargs [2] = sp [2];
12682 if (ip [1] == CEE_CPBLK) {
12684 * FIXME: It's unclear whether we should be emitting both the acquire
12685 * and release barriers for cpblk. It is technically both a load and
12686 * store operation, so it seems like that's the sensible thing to do.
12688 * FIXME: We emit full barriers on both sides of the operation for
12689 * simplicity. We should have a separate atomic memcpy method instead.
12691 MonoMethod *memcpy_method = get_memcpy_method ();
12693 if (ins_flag & MONO_INST_VOLATILE)
12694 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_SEQ);
12696 call = mono_emit_method_call (cfg, memcpy_method, iargs, NULL);
12697 call->flags |= ins_flag;
12699 if (ins_flag & MONO_INST_VOLATILE)
12700 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_SEQ);
12702 MonoMethod *memset_method = get_memset_method ();
12703 if (ins_flag & MONO_INST_VOLATILE) {
12704 /* Volatile stores have release semantics, see 12.6.7 in Ecma 335 */
12705 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_REL);
12707 call = mono_emit_method_call (cfg, memset_method, iargs, NULL);
12708 call->flags |= ins_flag;
12719 ins_flag |= MONO_INST_NOTYPECHECK;
12721 ins_flag |= MONO_INST_NORANGECHECK;
12722 /* we ignore the no-nullcheck for now since we
12723 * really do it explicitly only when doing callvirt->call
12727 case CEE_RETHROW: {
12729 int handler_offset = -1;
12731 for (i = 0; i < header->num_clauses; ++i) {
12732 MonoExceptionClause *clause = &header->clauses [i];
12733 if (MONO_OFFSET_IN_HANDLER (clause, ip - header->code) && !(clause->flags & MONO_EXCEPTION_CLAUSE_FINALLY)) {
12734 handler_offset = clause->handler_offset;
12739 bblock->flags |= BB_EXCEPTION_UNSAFE;
12741 if (handler_offset == -1)
12744 EMIT_NEW_TEMPLOAD (cfg, load, mono_find_exvar_for_offset (cfg, handler_offset)->inst_c0);
12745 MONO_INST_NEW (cfg, ins, OP_RETHROW);
12746 ins->sreg1 = load->dreg;
12747 MONO_ADD_INS (bblock, ins);
12749 MONO_INST_NEW (cfg, ins, OP_NOT_REACHED);
12750 MONO_ADD_INS (bblock, ins);
12753 link_bblock (cfg, bblock, end_bblock);
12754 start_new_bblock = 1;
12762 CHECK_STACK_OVF (1);
12764 token = read32 (ip + 2);
12765 if (mono_metadata_token_table (token) == MONO_TABLE_TYPESPEC && !image_is_dynamic (method->klass->image) && !generic_context) {
12766 MonoType *type = mono_type_create_from_typespec_checked (image, token, &cfg->error);
12769 val = mono_type_size (type, &ialign);
12771 MonoClass *klass = mini_get_class (method, token, generic_context);
12772 CHECK_TYPELOAD (klass);
12774 val = mono_type_size (&klass->byval_arg, &ialign);
12776 if (mini_is_gsharedvt_klass (cfg, klass))
12777 GSHAREDVT_FAILURE (*ip);
12779 EMIT_NEW_ICONST (cfg, ins, val);
12784 case CEE_REFANYTYPE: {
12785 MonoInst *src_var, *src;
12787 GSHAREDVT_FAILURE (*ip);
12793 src_var = get_vreg_to_inst (cfg, sp [0]->dreg);
12795 src_var = mono_compile_create_var_for_vreg (cfg, &mono_defaults.typed_reference_class->byval_arg, OP_LOCAL, sp [0]->dreg);
12796 EMIT_NEW_VARLOADA (cfg, src, src_var, src_var->inst_vtype);
12797 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &mono_defaults.typehandle_class->byval_arg, src->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, type));
12802 case CEE_READONLY_:
12815 g_warning ("opcode 0xfe 0x%02x not handled", ip [1]);
12825 g_warning ("opcode 0x%02x not handled", *ip);
12829 if (start_new_bblock != 1)
12832 bblock->cil_length = ip - bblock->cil_code;
12833 if (bblock->next_bb) {
12834 /* This could already be set because of inlining, #693905 */
12835 MonoBasicBlock *bb = bblock;
12837 while (bb->next_bb)
12839 bb->next_bb = end_bblock;
12841 bblock->next_bb = end_bblock;
12844 if (cfg->method == method && cfg->domainvar) {
12846 MonoInst *get_domain;
12848 cfg->cbb = init_localsbb;
12850 if ((get_domain = mono_get_domain_intrinsic (cfg))) {
12851 MONO_ADD_INS (cfg->cbb, get_domain);
12853 get_domain = mono_emit_jit_icall (cfg, mono_domain_get, NULL);
12855 NEW_TEMPSTORE (cfg, store, cfg->domainvar->inst_c0, get_domain);
12856 MONO_ADD_INS (cfg->cbb, store);
12859 #if defined(TARGET_POWERPC) || defined(TARGET_X86)
12860 if (cfg->compile_aot)
12861 /* FIXME: The plt slots require a GOT var even if the method doesn't use it */
12862 mono_get_got_var (cfg);
12865 if (cfg->method == method && cfg->got_var)
12866 mono_emit_load_got_addr (cfg);
12868 if (init_localsbb) {
12869 cfg->cbb = init_localsbb;
12871 for (i = 0; i < header->num_locals; ++i) {
12872 emit_init_local (cfg, i, header->locals [i], init_locals);
12876 if (cfg->init_ref_vars && cfg->method == method) {
12877 /* Emit initialization for ref vars */
12878 // FIXME: Avoid duplication initialization for IL locals.
12879 for (i = 0; i < cfg->num_varinfo; ++i) {
12880 MonoInst *ins = cfg->varinfo [i];
12882 if (ins->opcode == OP_LOCAL && ins->type == STACK_OBJ)
12883 MONO_EMIT_NEW_PCONST (cfg, ins->dreg, NULL);
12887 if (cfg->lmf_var && cfg->method == method) {
12888 cfg->cbb = init_localsbb;
12889 emit_push_lmf (cfg);
12892 cfg->cbb = init_localsbb;
12893 emit_instrumentation_call (cfg, mono_profiler_method_enter);
12896 MonoBasicBlock *bb;
12899 * Make seq points at backward branch targets interruptable.
12901 for (bb = cfg->bb_entry; bb; bb = bb->next_bb)
12902 if (bb->code && bb->in_count > 1 && bb->code->opcode == OP_SEQ_POINT)
12903 bb->code->flags |= MONO_INST_SINGLE_STEP_LOC;
12906 /* Add a sequence point for method entry/exit events */
12907 if (cfg->gen_seq_points_debug_data) {
12908 NEW_SEQ_POINT (cfg, ins, METHOD_ENTRY_IL_OFFSET, FALSE);
12909 MONO_ADD_INS (init_localsbb, ins);
12910 NEW_SEQ_POINT (cfg, ins, METHOD_EXIT_IL_OFFSET, FALSE);
12911 MONO_ADD_INS (cfg->bb_exit, ins);
12915 * Add seq points for IL offsets which have line number info, but wasn't generated a seq point during JITting because
12916 * the code they refer to was dead (#11880).
12918 if (sym_seq_points) {
12919 for (i = 0; i < header->code_size; ++i) {
12920 if (mono_bitset_test_fast (seq_point_locs, i) && !mono_bitset_test_fast (seq_point_set_locs, i)) {
12923 NEW_SEQ_POINT (cfg, ins, i, FALSE);
12924 mono_add_seq_point (cfg, NULL, ins, SEQ_POINT_NATIVE_OFFSET_DEAD_CODE);
12931 if (cfg->method == method) {
12932 MonoBasicBlock *bb;
12933 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
12934 bb->region = mono_find_block_region (cfg, bb->real_offset);
12936 mono_create_spvar_for_region (cfg, bb->region);
12937 if (cfg->verbose_level > 2)
12938 printf ("REGION BB%d IL_%04x ID_%08X\n", bb->block_num, bb->real_offset, bb->region);
12942 if (inline_costs < 0) {
12945 /* Method is too large */
12946 mname = mono_method_full_name (method, TRUE);
12947 mono_cfg_set_exception (cfg, MONO_EXCEPTION_INVALID_PROGRAM);
12948 cfg->exception_message = g_strdup_printf ("Method %s is too complex.", mname);
12952 if ((cfg->verbose_level > 2) && (cfg->method == method))
12953 mono_print_code (cfg, "AFTER METHOD-TO-IR");
12958 g_assert (!mono_error_ok (&cfg->error));
12962 g_assert (cfg->exception_type != MONO_EXCEPTION_NONE);
12966 set_exception_type_from_invalid_il (cfg, method, ip);
12970 g_slist_free (class_inits);
12971 mono_basic_block_free (original_bb);
12972 cfg->dont_inline = g_list_remove (cfg->dont_inline, method);
12973 cfg->headers_to_free = g_slist_prepend_mempool (cfg->mempool, cfg->headers_to_free, header);
12974 if (cfg->exception_type)
12977 return inline_costs;
12981 store_membase_reg_to_store_membase_imm (int opcode)
12984 case OP_STORE_MEMBASE_REG:
12985 return OP_STORE_MEMBASE_IMM;
12986 case OP_STOREI1_MEMBASE_REG:
12987 return OP_STOREI1_MEMBASE_IMM;
12988 case OP_STOREI2_MEMBASE_REG:
12989 return OP_STOREI2_MEMBASE_IMM;
12990 case OP_STOREI4_MEMBASE_REG:
12991 return OP_STOREI4_MEMBASE_IMM;
12992 case OP_STOREI8_MEMBASE_REG:
12993 return OP_STOREI8_MEMBASE_IMM;
12995 g_assert_not_reached ();
13002 mono_op_to_op_imm (int opcode)
13006 return OP_IADD_IMM;
13008 return OP_ISUB_IMM;
13010 return OP_IDIV_IMM;
13012 return OP_IDIV_UN_IMM;
13014 return OP_IREM_IMM;
13016 return OP_IREM_UN_IMM;
13018 return OP_IMUL_IMM;
13020 return OP_IAND_IMM;
13024 return OP_IXOR_IMM;
13026 return OP_ISHL_IMM;
13028 return OP_ISHR_IMM;
13030 return OP_ISHR_UN_IMM;
13033 return OP_LADD_IMM;
13035 return OP_LSUB_IMM;
13037 return OP_LAND_IMM;
13041 return OP_LXOR_IMM;
13043 return OP_LSHL_IMM;
13045 return OP_LSHR_IMM;
13047 return OP_LSHR_UN_IMM;
13048 #if SIZEOF_REGISTER == 8
13050 return OP_LREM_IMM;
13054 return OP_COMPARE_IMM;
13056 return OP_ICOMPARE_IMM;
13058 return OP_LCOMPARE_IMM;
13060 case OP_STORE_MEMBASE_REG:
13061 return OP_STORE_MEMBASE_IMM;
13062 case OP_STOREI1_MEMBASE_REG:
13063 return OP_STOREI1_MEMBASE_IMM;
13064 case OP_STOREI2_MEMBASE_REG:
13065 return OP_STOREI2_MEMBASE_IMM;
13066 case OP_STOREI4_MEMBASE_REG:
13067 return OP_STOREI4_MEMBASE_IMM;
13069 #if defined(TARGET_X86) || defined (TARGET_AMD64)
13071 return OP_X86_PUSH_IMM;
13072 case OP_X86_COMPARE_MEMBASE_REG:
13073 return OP_X86_COMPARE_MEMBASE_IMM;
13075 #if defined(TARGET_AMD64)
13076 case OP_AMD64_ICOMPARE_MEMBASE_REG:
13077 return OP_AMD64_ICOMPARE_MEMBASE_IMM;
13079 case OP_VOIDCALL_REG:
13080 return OP_VOIDCALL;
13088 return OP_LOCALLOC_IMM;
13095 ldind_to_load_membase (int opcode)
13099 return OP_LOADI1_MEMBASE;
13101 return OP_LOADU1_MEMBASE;
13103 return OP_LOADI2_MEMBASE;
13105 return OP_LOADU2_MEMBASE;
13107 return OP_LOADI4_MEMBASE;
13109 return OP_LOADU4_MEMBASE;
13111 return OP_LOAD_MEMBASE;
13112 case CEE_LDIND_REF:
13113 return OP_LOAD_MEMBASE;
13115 return OP_LOADI8_MEMBASE;
13117 return OP_LOADR4_MEMBASE;
13119 return OP_LOADR8_MEMBASE;
13121 g_assert_not_reached ();
13128 stind_to_store_membase (int opcode)
13132 return OP_STOREI1_MEMBASE_REG;
13134 return OP_STOREI2_MEMBASE_REG;
13136 return OP_STOREI4_MEMBASE_REG;
13138 case CEE_STIND_REF:
13139 return OP_STORE_MEMBASE_REG;
13141 return OP_STOREI8_MEMBASE_REG;
13143 return OP_STORER4_MEMBASE_REG;
13145 return OP_STORER8_MEMBASE_REG;
13147 g_assert_not_reached ();
13154 mono_load_membase_to_load_mem (int opcode)
13156 // FIXME: Add a MONO_ARCH_HAVE_LOAD_MEM macro
13157 #if defined(TARGET_X86) || defined(TARGET_AMD64)
13159 case OP_LOAD_MEMBASE:
13160 return OP_LOAD_MEM;
13161 case OP_LOADU1_MEMBASE:
13162 return OP_LOADU1_MEM;
13163 case OP_LOADU2_MEMBASE:
13164 return OP_LOADU2_MEM;
13165 case OP_LOADI4_MEMBASE:
13166 return OP_LOADI4_MEM;
13167 case OP_LOADU4_MEMBASE:
13168 return OP_LOADU4_MEM;
13169 #if SIZEOF_REGISTER == 8
13170 case OP_LOADI8_MEMBASE:
13171 return OP_LOADI8_MEM;
13180 op_to_op_dest_membase (int store_opcode, int opcode)
13182 #if defined(TARGET_X86)
13183 if (!((store_opcode == OP_STORE_MEMBASE_REG) || (store_opcode == OP_STOREI4_MEMBASE_REG)))
13188 return OP_X86_ADD_MEMBASE_REG;
13190 return OP_X86_SUB_MEMBASE_REG;
13192 return OP_X86_AND_MEMBASE_REG;
13194 return OP_X86_OR_MEMBASE_REG;
13196 return OP_X86_XOR_MEMBASE_REG;
13199 return OP_X86_ADD_MEMBASE_IMM;
13202 return OP_X86_SUB_MEMBASE_IMM;
13205 return OP_X86_AND_MEMBASE_IMM;
13208 return OP_X86_OR_MEMBASE_IMM;
13211 return OP_X86_XOR_MEMBASE_IMM;
13217 #if defined(TARGET_AMD64)
13218 if (!((store_opcode == OP_STORE_MEMBASE_REG) || (store_opcode == OP_STOREI4_MEMBASE_REG) || (store_opcode == OP_STOREI8_MEMBASE_REG)))
13223 return OP_X86_ADD_MEMBASE_REG;
13225 return OP_X86_SUB_MEMBASE_REG;
13227 return OP_X86_AND_MEMBASE_REG;
13229 return OP_X86_OR_MEMBASE_REG;
13231 return OP_X86_XOR_MEMBASE_REG;
13233 return OP_X86_ADD_MEMBASE_IMM;
13235 return OP_X86_SUB_MEMBASE_IMM;
13237 return OP_X86_AND_MEMBASE_IMM;
13239 return OP_X86_OR_MEMBASE_IMM;
13241 return OP_X86_XOR_MEMBASE_IMM;
13243 return OP_AMD64_ADD_MEMBASE_REG;
13245 return OP_AMD64_SUB_MEMBASE_REG;
13247 return OP_AMD64_AND_MEMBASE_REG;
13249 return OP_AMD64_OR_MEMBASE_REG;
13251 return OP_AMD64_XOR_MEMBASE_REG;
13254 return OP_AMD64_ADD_MEMBASE_IMM;
13257 return OP_AMD64_SUB_MEMBASE_IMM;
13260 return OP_AMD64_AND_MEMBASE_IMM;
13263 return OP_AMD64_OR_MEMBASE_IMM;
13266 return OP_AMD64_XOR_MEMBASE_IMM;
13276 op_to_op_store_membase (int store_opcode, int opcode)
13278 #if defined(TARGET_X86) || defined(TARGET_AMD64)
13281 if (store_opcode == OP_STOREI1_MEMBASE_REG)
13282 return OP_X86_SETEQ_MEMBASE;
13284 if (store_opcode == OP_STOREI1_MEMBASE_REG)
13285 return OP_X86_SETNE_MEMBASE;
13293 op_to_op_src1_membase (int load_opcode, int opcode)
13296 /* FIXME: This has sign extension issues */
13298 if ((opcode == OP_ICOMPARE_IMM) && (load_opcode == OP_LOADU1_MEMBASE))
13299 return OP_X86_COMPARE_MEMBASE8_IMM;
13302 if (!((load_opcode == OP_LOAD_MEMBASE) || (load_opcode == OP_LOADI4_MEMBASE) || (load_opcode == OP_LOADU4_MEMBASE)))
13307 return OP_X86_PUSH_MEMBASE;
13308 case OP_COMPARE_IMM:
13309 case OP_ICOMPARE_IMM:
13310 return OP_X86_COMPARE_MEMBASE_IMM;
13313 return OP_X86_COMPARE_MEMBASE_REG;
13317 #ifdef TARGET_AMD64
13318 /* FIXME: This has sign extension issues */
13320 if ((opcode == OP_ICOMPARE_IMM) && (load_opcode == OP_LOADU1_MEMBASE))
13321 return OP_X86_COMPARE_MEMBASE8_IMM;
13326 #ifdef __mono_ilp32__
13327 if (load_opcode == OP_LOADI8_MEMBASE)
13329 if ((load_opcode == OP_LOAD_MEMBASE) || (load_opcode == OP_LOADI8_MEMBASE))
13331 return OP_X86_PUSH_MEMBASE;
13333 /* FIXME: This only works for 32 bit immediates
13334 case OP_COMPARE_IMM:
13335 case OP_LCOMPARE_IMM:
13336 if ((load_opcode == OP_LOAD_MEMBASE) || (load_opcode == OP_LOADI8_MEMBASE))
13337 return OP_AMD64_COMPARE_MEMBASE_IMM;
13339 case OP_ICOMPARE_IMM:
13340 if ((load_opcode == OP_LOADI4_MEMBASE) || (load_opcode == OP_LOADU4_MEMBASE))
13341 return OP_AMD64_ICOMPARE_MEMBASE_IMM;
13345 #ifdef __mono_ilp32__
13346 if (load_opcode == OP_LOAD_MEMBASE)
13347 return OP_AMD64_ICOMPARE_MEMBASE_REG;
13348 if (load_opcode == OP_LOADI8_MEMBASE)
13350 if ((load_opcode == OP_LOAD_MEMBASE) || (load_opcode == OP_LOADI8_MEMBASE))
13352 return OP_AMD64_COMPARE_MEMBASE_REG;
13355 if ((load_opcode == OP_LOADI4_MEMBASE) || (load_opcode == OP_LOADU4_MEMBASE))
13356 return OP_AMD64_ICOMPARE_MEMBASE_REG;
13365 op_to_op_src2_membase (int load_opcode, int opcode)
13368 if (!((load_opcode == OP_LOAD_MEMBASE) || (load_opcode == OP_LOADI4_MEMBASE) || (load_opcode == OP_LOADU4_MEMBASE)))
13374 return OP_X86_COMPARE_REG_MEMBASE;
13376 return OP_X86_ADD_REG_MEMBASE;
13378 return OP_X86_SUB_REG_MEMBASE;
13380 return OP_X86_AND_REG_MEMBASE;
13382 return OP_X86_OR_REG_MEMBASE;
13384 return OP_X86_XOR_REG_MEMBASE;
13388 #ifdef TARGET_AMD64
13389 #ifdef __mono_ilp32__
13390 if ((load_opcode == OP_LOADI4_MEMBASE) || (load_opcode == OP_LOADU4_MEMBASE) || (load_opcode == OP_LOAD_MEMBASE) ) {
13392 if ((load_opcode == OP_LOADI4_MEMBASE) || (load_opcode == OP_LOADU4_MEMBASE)) {
13396 return OP_AMD64_ICOMPARE_REG_MEMBASE;
13398 return OP_X86_ADD_REG_MEMBASE;
13400 return OP_X86_SUB_REG_MEMBASE;
13402 return OP_X86_AND_REG_MEMBASE;
13404 return OP_X86_OR_REG_MEMBASE;
13406 return OP_X86_XOR_REG_MEMBASE;
13408 #ifdef __mono_ilp32__
13409 } else if (load_opcode == OP_LOADI8_MEMBASE) {
13411 } else if ((load_opcode == OP_LOADI8_MEMBASE) || (load_opcode == OP_LOAD_MEMBASE)) {
13416 return OP_AMD64_COMPARE_REG_MEMBASE;
13418 return OP_AMD64_ADD_REG_MEMBASE;
13420 return OP_AMD64_SUB_REG_MEMBASE;
13422 return OP_AMD64_AND_REG_MEMBASE;
13424 return OP_AMD64_OR_REG_MEMBASE;
13426 return OP_AMD64_XOR_REG_MEMBASE;
13435 mono_op_to_op_imm_noemul (int opcode)
13438 #if SIZEOF_REGISTER == 4 && !defined(MONO_ARCH_NO_EMULATE_LONG_SHIFT_OPS)
13444 #if defined(MONO_ARCH_EMULATE_MUL_DIV) || defined(MONO_ARCH_EMULATE_DIV)
13451 #if defined(MONO_ARCH_EMULATE_MUL_DIV)
13456 return mono_op_to_op_imm (opcode);
13461 * mono_handle_global_vregs:
13463 * Make vregs used in more than one bblock 'global', i.e. allocate a variable
13467 mono_handle_global_vregs (MonoCompile *cfg)
13469 gint32 *vreg_to_bb;
13470 MonoBasicBlock *bb;
13473 vreg_to_bb = mono_mempool_alloc0 (cfg->mempool, sizeof (gint32*) * cfg->next_vreg + 1);
13475 #ifdef MONO_ARCH_SIMD_INTRINSICS
13476 if (cfg->uses_simd_intrinsics)
13477 mono_simd_simplify_indirection (cfg);
13480 /* Find local vregs used in more than one bb */
13481 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
13482 MonoInst *ins = bb->code;
13483 int block_num = bb->block_num;
13485 if (cfg->verbose_level > 2)
13486 printf ("\nHANDLE-GLOBAL-VREGS BLOCK %d:\n", bb->block_num);
13489 for (; ins; ins = ins->next) {
13490 const char *spec = INS_INFO (ins->opcode);
13491 int regtype = 0, regindex;
13494 if (G_UNLIKELY (cfg->verbose_level > 2))
13495 mono_print_ins (ins);
13497 g_assert (ins->opcode >= MONO_CEE_LAST);
13499 for (regindex = 0; regindex < 4; regindex ++) {
13502 if (regindex == 0) {
13503 regtype = spec [MONO_INST_DEST];
13504 if (regtype == ' ')
13507 } else if (regindex == 1) {
13508 regtype = spec [MONO_INST_SRC1];
13509 if (regtype == ' ')
13512 } else if (regindex == 2) {
13513 regtype = spec [MONO_INST_SRC2];
13514 if (regtype == ' ')
13517 } else if (regindex == 3) {
13518 regtype = spec [MONO_INST_SRC3];
13519 if (regtype == ' ')
13524 #if SIZEOF_REGISTER == 4
13525 /* In the LLVM case, the long opcodes are not decomposed */
13526 if (regtype == 'l' && !COMPILE_LLVM (cfg)) {
13528 * Since some instructions reference the original long vreg,
13529 * and some reference the two component vregs, it is quite hard
13530 * to determine when it needs to be global. So be conservative.
13532 if (!get_vreg_to_inst (cfg, vreg)) {
13533 mono_compile_create_var_for_vreg (cfg, &mono_defaults.int64_class->byval_arg, OP_LOCAL, vreg);
13535 if (cfg->verbose_level > 2)
13536 printf ("LONG VREG R%d made global.\n", vreg);
13540 * Make the component vregs volatile since the optimizations can
13541 * get confused otherwise.
13543 get_vreg_to_inst (cfg, vreg + 1)->flags |= MONO_INST_VOLATILE;
13544 get_vreg_to_inst (cfg, vreg + 2)->flags |= MONO_INST_VOLATILE;
13548 g_assert (vreg != -1);
13550 prev_bb = vreg_to_bb [vreg];
13551 if (prev_bb == 0) {
13552 /* 0 is a valid block num */
13553 vreg_to_bb [vreg] = block_num + 1;
13554 } else if ((prev_bb != block_num + 1) && (prev_bb != -1)) {
13555 if (((regtype == 'i' && (vreg < MONO_MAX_IREGS))) || (regtype == 'f' && (vreg < MONO_MAX_FREGS)))
13558 if (!get_vreg_to_inst (cfg, vreg)) {
13559 if (G_UNLIKELY (cfg->verbose_level > 2))
13560 printf ("VREG R%d used in BB%d and BB%d made global.\n", vreg, vreg_to_bb [vreg], block_num);
13564 if (vreg_is_ref (cfg, vreg))
13565 mono_compile_create_var_for_vreg (cfg, &mono_defaults.object_class->byval_arg, OP_LOCAL, vreg);
13567 mono_compile_create_var_for_vreg (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL, vreg);
13570 mono_compile_create_var_for_vreg (cfg, &mono_defaults.int64_class->byval_arg, OP_LOCAL, vreg);
13573 mono_compile_create_var_for_vreg (cfg, &mono_defaults.double_class->byval_arg, OP_LOCAL, vreg);
13576 mono_compile_create_var_for_vreg (cfg, &ins->klass->byval_arg, OP_LOCAL, vreg);
13579 g_assert_not_reached ();
13583 /* Flag as having been used in more than one bb */
13584 vreg_to_bb [vreg] = -1;
13590 /* If a variable is used in only one bblock, convert it into a local vreg */
13591 for (i = 0; i < cfg->num_varinfo; i++) {
13592 MonoInst *var = cfg->varinfo [i];
13593 MonoMethodVar *vmv = MONO_VARINFO (cfg, i);
13595 switch (var->type) {
13601 #if SIZEOF_REGISTER == 8
13604 #if !defined(TARGET_X86)
13605 /* Enabling this screws up the fp stack on x86 */
13608 if (mono_arch_is_soft_float ())
13611 /* Arguments are implicitly global */
13612 /* Putting R4 vars into registers doesn't work currently */
13613 /* The gsharedvt vars are implicitly referenced by ldaddr opcodes, but those opcodes are only generated later */
13614 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) {
13616 * Make that the variable's liveness interval doesn't contain a call, since
13617 * that would cause the lvreg to be spilled, making the whole optimization
13620 /* This is too slow for JIT compilation */
13622 if (cfg->compile_aot && vreg_to_bb [var->dreg]) {
13624 int def_index, call_index, ins_index;
13625 gboolean spilled = FALSE;
13630 for (ins = vreg_to_bb [var->dreg]->code; ins; ins = ins->next) {
13631 const char *spec = INS_INFO (ins->opcode);
13633 if ((spec [MONO_INST_DEST] != ' ') && (ins->dreg == var->dreg))
13634 def_index = ins_index;
13636 if (((spec [MONO_INST_SRC1] != ' ') && (ins->sreg1 == var->dreg)) ||
13637 ((spec [MONO_INST_SRC1] != ' ') && (ins->sreg1 == var->dreg))) {
13638 if (call_index > def_index) {
13644 if (MONO_IS_CALL (ins))
13645 call_index = ins_index;
13655 if (G_UNLIKELY (cfg->verbose_level > 2))
13656 printf ("CONVERTED R%d(%d) TO VREG.\n", var->dreg, vmv->idx);
13657 var->flags |= MONO_INST_IS_DEAD;
13658 cfg->vreg_to_inst [var->dreg] = NULL;
13665 * Compress the varinfo and vars tables so the liveness computation is faster and
13666 * takes up less space.
13669 for (i = 0; i < cfg->num_varinfo; ++i) {
13670 MonoInst *var = cfg->varinfo [i];
13671 if (pos < i && cfg->locals_start == i)
13672 cfg->locals_start = pos;
13673 if (!(var->flags & MONO_INST_IS_DEAD)) {
13675 cfg->varinfo [pos] = cfg->varinfo [i];
13676 cfg->varinfo [pos]->inst_c0 = pos;
13677 memcpy (&cfg->vars [pos], &cfg->vars [i], sizeof (MonoMethodVar));
13678 cfg->vars [pos].idx = pos;
13679 #if SIZEOF_REGISTER == 4
13680 if (cfg->varinfo [pos]->type == STACK_I8) {
13681 /* Modify the two component vars too */
13684 var1 = get_vreg_to_inst (cfg, cfg->varinfo [pos]->dreg + 1);
13685 var1->inst_c0 = pos;
13686 var1 = get_vreg_to_inst (cfg, cfg->varinfo [pos]->dreg + 2);
13687 var1->inst_c0 = pos;
13694 cfg->num_varinfo = pos;
13695 if (cfg->locals_start > cfg->num_varinfo)
13696 cfg->locals_start = cfg->num_varinfo;
13700 * mono_spill_global_vars:
13702 * Generate spill code for variables which are not allocated to registers,
13703 * and replace vregs with their allocated hregs. *need_local_opts is set to TRUE if
13704 * code is generated which could be optimized by the local optimization passes.
13707 mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local_opts)
13709 MonoBasicBlock *bb;
13711 int orig_next_vreg;
13712 guint32 *vreg_to_lvreg;
13714 guint32 i, lvregs_len;
13715 gboolean dest_has_lvreg = FALSE;
13716 guint32 stacktypes [128];
13717 MonoInst **live_range_start, **live_range_end;
13718 MonoBasicBlock **live_range_start_bb, **live_range_end_bb;
13719 int *gsharedvt_vreg_to_idx = NULL;
13721 *need_local_opts = FALSE;
13723 memset (spec2, 0, sizeof (spec2));
13725 /* FIXME: Move this function to mini.c */
13726 stacktypes ['i'] = STACK_PTR;
13727 stacktypes ['l'] = STACK_I8;
13728 stacktypes ['f'] = STACK_R8;
13729 #ifdef MONO_ARCH_SIMD_INTRINSICS
13730 stacktypes ['x'] = STACK_VTYPE;
13733 #if SIZEOF_REGISTER == 4
13734 /* Create MonoInsts for longs */
13735 for (i = 0; i < cfg->num_varinfo; i++) {
13736 MonoInst *ins = cfg->varinfo [i];
13738 if ((ins->opcode != OP_REGVAR) && !(ins->flags & MONO_INST_IS_DEAD)) {
13739 switch (ins->type) {
13744 if (ins->type == STACK_R8 && !COMPILE_SOFT_FLOAT (cfg))
13747 g_assert (ins->opcode == OP_REGOFFSET);
13749 tree = get_vreg_to_inst (cfg, ins->dreg + 1);
13751 tree->opcode = OP_REGOFFSET;
13752 tree->inst_basereg = ins->inst_basereg;
13753 tree->inst_offset = ins->inst_offset + MINI_LS_WORD_OFFSET;
13755 tree = get_vreg_to_inst (cfg, ins->dreg + 2);
13757 tree->opcode = OP_REGOFFSET;
13758 tree->inst_basereg = ins->inst_basereg;
13759 tree->inst_offset = ins->inst_offset + MINI_MS_WORD_OFFSET;
13769 if (cfg->compute_gc_maps) {
13770 /* registers need liveness info even for !non refs */
13771 for (i = 0; i < cfg->num_varinfo; i++) {
13772 MonoInst *ins = cfg->varinfo [i];
13774 if (ins->opcode == OP_REGVAR)
13775 ins->flags |= MONO_INST_GC_TRACK;
13779 if (cfg->gsharedvt) {
13780 gsharedvt_vreg_to_idx = mono_mempool_alloc0 (cfg->mempool, sizeof (int) * cfg->next_vreg);
13782 for (i = 0; i < cfg->num_varinfo; ++i) {
13783 MonoInst *ins = cfg->varinfo [i];
13786 if (mini_is_gsharedvt_variable_type (cfg, ins->inst_vtype)) {
13787 if (i >= cfg->locals_start) {
13789 idx = get_gsharedvt_info_slot (cfg, ins->inst_vtype, MONO_RGCTX_INFO_LOCAL_OFFSET);
13790 gsharedvt_vreg_to_idx [ins->dreg] = idx + 1;
13791 ins->opcode = OP_GSHAREDVT_LOCAL;
13792 ins->inst_imm = idx;
13795 gsharedvt_vreg_to_idx [ins->dreg] = -1;
13796 ins->opcode = OP_GSHAREDVT_ARG_REGOFFSET;
13802 /* FIXME: widening and truncation */
13805 * As an optimization, when a variable allocated to the stack is first loaded into
13806 * an lvreg, we will remember the lvreg and use it the next time instead of loading
13807 * the variable again.
13809 orig_next_vreg = cfg->next_vreg;
13810 vreg_to_lvreg = mono_mempool_alloc0 (cfg->mempool, sizeof (guint32) * cfg->next_vreg);
13811 lvregs = mono_mempool_alloc (cfg->mempool, sizeof (guint32) * 1024);
13815 * These arrays contain the first and last instructions accessing a given
13817 * Since we emit bblocks in the same order we process them here, and we
13818 * don't split live ranges, these will precisely describe the live range of
13819 * the variable, i.e. the instruction range where a valid value can be found
13820 * in the variables location.
13821 * The live range is computed using the liveness info computed by the liveness pass.
13822 * We can't use vmv->range, since that is an abstract live range, and we need
13823 * one which is instruction precise.
13824 * FIXME: Variables used in out-of-line bblocks have a hole in their live range.
13826 /* FIXME: Only do this if debugging info is requested */
13827 live_range_start = g_new0 (MonoInst*, cfg->next_vreg);
13828 live_range_end = g_new0 (MonoInst*, cfg->next_vreg);
13829 live_range_start_bb = g_new (MonoBasicBlock*, cfg->next_vreg);
13830 live_range_end_bb = g_new (MonoBasicBlock*, cfg->next_vreg);
13832 /* Add spill loads/stores */
13833 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
13836 if (cfg->verbose_level > 2)
13837 printf ("\nSPILL BLOCK %d:\n", bb->block_num);
13839 /* Clear vreg_to_lvreg array */
13840 for (i = 0; i < lvregs_len; i++)
13841 vreg_to_lvreg [lvregs [i]] = 0;
13845 MONO_BB_FOR_EACH_INS (bb, ins) {
13846 const char *spec = INS_INFO (ins->opcode);
13847 int regtype, srcindex, sreg, tmp_reg, prev_dreg, num_sregs;
13848 gboolean store, no_lvreg;
13849 int sregs [MONO_MAX_SRC_REGS];
13851 if (G_UNLIKELY (cfg->verbose_level > 2))
13852 mono_print_ins (ins);
13854 if (ins->opcode == OP_NOP)
13858 * We handle LDADDR here as well, since it can only be decomposed
13859 * when variable addresses are known.
13861 if (ins->opcode == OP_LDADDR) {
13862 MonoInst *var = ins->inst_p0;
13864 if (var->opcode == OP_VTARG_ADDR) {
13865 /* Happens on SPARC/S390 where vtypes are passed by reference */
13866 MonoInst *vtaddr = var->inst_left;
13867 if (vtaddr->opcode == OP_REGVAR) {
13868 ins->opcode = OP_MOVE;
13869 ins->sreg1 = vtaddr->dreg;
13871 else if (var->inst_left->opcode == OP_REGOFFSET) {
13872 ins->opcode = OP_LOAD_MEMBASE;
13873 ins->inst_basereg = vtaddr->inst_basereg;
13874 ins->inst_offset = vtaddr->inst_offset;
13877 } else if (cfg->gsharedvt && gsharedvt_vreg_to_idx [var->dreg] < 0) {
13878 /* gsharedvt arg passed by ref */
13879 g_assert (var->opcode == OP_GSHAREDVT_ARG_REGOFFSET);
13881 ins->opcode = OP_LOAD_MEMBASE;
13882 ins->inst_basereg = var->inst_basereg;
13883 ins->inst_offset = var->inst_offset;
13884 } else if (cfg->gsharedvt && gsharedvt_vreg_to_idx [var->dreg]) {
13885 MonoInst *load, *load2, *load3;
13886 int idx = gsharedvt_vreg_to_idx [var->dreg] - 1;
13887 int reg1, reg2, reg3;
13888 MonoInst *info_var = cfg->gsharedvt_info_var;
13889 MonoInst *locals_var = cfg->gsharedvt_locals_var;
13893 * Compute the address of the local as gsharedvt_locals_var + gsharedvt_info_var->locals_offsets [idx].
13896 g_assert (var->opcode == OP_GSHAREDVT_LOCAL);
13898 g_assert (info_var);
13899 g_assert (locals_var);
13901 /* Mark the instruction used to compute the locals var as used */
13902 cfg->gsharedvt_locals_var_ins = NULL;
13904 /* Load the offset */
13905 if (info_var->opcode == OP_REGOFFSET) {
13906 reg1 = alloc_ireg (cfg);
13907 NEW_LOAD_MEMBASE (cfg, load, OP_LOAD_MEMBASE, reg1, info_var->inst_basereg, info_var->inst_offset);
13908 } else if (info_var->opcode == OP_REGVAR) {
13910 reg1 = info_var->dreg;
13912 g_assert_not_reached ();
13914 reg2 = alloc_ireg (cfg);
13915 NEW_LOAD_MEMBASE (cfg, load2, OP_LOADI4_MEMBASE, reg2, reg1, MONO_STRUCT_OFFSET (MonoGSharedVtMethodRuntimeInfo, entries) + (idx * sizeof (gpointer)));
13916 /* Load the locals area address */
13917 reg3 = alloc_ireg (cfg);
13918 if (locals_var->opcode == OP_REGOFFSET) {
13919 NEW_LOAD_MEMBASE (cfg, load3, OP_LOAD_MEMBASE, reg3, locals_var->inst_basereg, locals_var->inst_offset);
13920 } else if (locals_var->opcode == OP_REGVAR) {
13921 NEW_UNALU (cfg, load3, OP_MOVE, reg3, locals_var->dreg);
13923 g_assert_not_reached ();
13925 /* Compute the address */
13926 ins->opcode = OP_PADD;
13930 mono_bblock_insert_before_ins (bb, ins, load3);
13931 mono_bblock_insert_before_ins (bb, load3, load2);
13933 mono_bblock_insert_before_ins (bb, load2, load);
13935 g_assert (var->opcode == OP_REGOFFSET);
13937 ins->opcode = OP_ADD_IMM;
13938 ins->sreg1 = var->inst_basereg;
13939 ins->inst_imm = var->inst_offset;
13942 *need_local_opts = TRUE;
13943 spec = INS_INFO (ins->opcode);
13946 if (ins->opcode < MONO_CEE_LAST) {
13947 mono_print_ins (ins);
13948 g_assert_not_reached ();
13952 * Store opcodes have destbasereg in the dreg, but in reality, it is an
13956 if (MONO_IS_STORE_MEMBASE (ins)) {
13957 tmp_reg = ins->dreg;
13958 ins->dreg = ins->sreg2;
13959 ins->sreg2 = tmp_reg;
13962 spec2 [MONO_INST_DEST] = ' ';
13963 spec2 [MONO_INST_SRC1] = spec [MONO_INST_SRC1];
13964 spec2 [MONO_INST_SRC2] = spec [MONO_INST_DEST];
13965 spec2 [MONO_INST_SRC3] = ' ';
13967 } else if (MONO_IS_STORE_MEMINDEX (ins))
13968 g_assert_not_reached ();
13973 if (G_UNLIKELY (cfg->verbose_level > 2)) {
13974 printf ("\t %.3s %d", spec, ins->dreg);
13975 num_sregs = mono_inst_get_src_registers (ins, sregs);
13976 for (srcindex = 0; srcindex < num_sregs; ++srcindex)
13977 printf (" %d", sregs [srcindex]);
13984 regtype = spec [MONO_INST_DEST];
13985 g_assert (((ins->dreg == -1) && (regtype == ' ')) || ((ins->dreg != -1) && (regtype != ' ')));
13988 if ((ins->dreg != -1) && get_vreg_to_inst (cfg, ins->dreg)) {
13989 MonoInst *var = get_vreg_to_inst (cfg, ins->dreg);
13990 MonoInst *store_ins;
13992 MonoInst *def_ins = ins;
13993 int dreg = ins->dreg; /* The original vreg */
13995 store_opcode = mono_type_to_store_membase (cfg, var->inst_vtype);
13997 if (var->opcode == OP_REGVAR) {
13998 ins->dreg = var->dreg;
13999 } 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)) {
14001 * Instead of emitting a load+store, use a _membase opcode.
14003 g_assert (var->opcode == OP_REGOFFSET);
14004 if (ins->opcode == OP_MOVE) {
14008 ins->opcode = op_to_op_dest_membase (store_opcode, ins->opcode);
14009 ins->inst_basereg = var->inst_basereg;
14010 ins->inst_offset = var->inst_offset;
14013 spec = INS_INFO (ins->opcode);
14017 g_assert (var->opcode == OP_REGOFFSET);
14019 prev_dreg = ins->dreg;
14021 /* Invalidate any previous lvreg for this vreg */
14022 vreg_to_lvreg [ins->dreg] = 0;
14026 if (COMPILE_SOFT_FLOAT (cfg) && store_opcode == OP_STORER8_MEMBASE_REG) {
14028 store_opcode = OP_STOREI8_MEMBASE_REG;
14031 ins->dreg = alloc_dreg (cfg, stacktypes [regtype]);
14033 #if SIZEOF_REGISTER != 8
14034 if (regtype == 'l') {
14035 NEW_STORE_MEMBASE (cfg, store_ins, OP_STOREI4_MEMBASE_REG, var->inst_basereg, var->inst_offset + MINI_LS_WORD_OFFSET, ins->dreg + 1);
14036 mono_bblock_insert_after_ins (bb, ins, store_ins);
14037 NEW_STORE_MEMBASE (cfg, store_ins, OP_STOREI4_MEMBASE_REG, var->inst_basereg, var->inst_offset + MINI_MS_WORD_OFFSET, ins->dreg + 2);
14038 mono_bblock_insert_after_ins (bb, ins, store_ins);
14039 def_ins = store_ins;
14044 g_assert (store_opcode != OP_STOREV_MEMBASE);
14046 /* Try to fuse the store into the instruction itself */
14047 /* FIXME: Add more instructions */
14048 if (!lvreg && ((ins->opcode == OP_ICONST) || ((ins->opcode == OP_I8CONST) && (ins->inst_c0 == 0)))) {
14049 ins->opcode = store_membase_reg_to_store_membase_imm (store_opcode);
14050 ins->inst_imm = ins->inst_c0;
14051 ins->inst_destbasereg = var->inst_basereg;
14052 ins->inst_offset = var->inst_offset;
14053 spec = INS_INFO (ins->opcode);
14054 } else if (!lvreg && ((ins->opcode == OP_MOVE) || (ins->opcode == OP_FMOVE) || (ins->opcode == OP_LMOVE) || (ins->opcode == OP_RMOVE))) {
14055 ins->opcode = store_opcode;
14056 ins->inst_destbasereg = var->inst_basereg;
14057 ins->inst_offset = var->inst_offset;
14061 tmp_reg = ins->dreg;
14062 ins->dreg = ins->sreg2;
14063 ins->sreg2 = tmp_reg;
14066 spec2 [MONO_INST_DEST] = ' ';
14067 spec2 [MONO_INST_SRC1] = spec [MONO_INST_SRC1];
14068 spec2 [MONO_INST_SRC2] = spec [MONO_INST_DEST];
14069 spec2 [MONO_INST_SRC3] = ' ';
14071 } else if (!lvreg && (op_to_op_store_membase (store_opcode, ins->opcode) != -1)) {
14072 // FIXME: The backends expect the base reg to be in inst_basereg
14073 ins->opcode = op_to_op_store_membase (store_opcode, ins->opcode);
14075 ins->inst_basereg = var->inst_basereg;
14076 ins->inst_offset = var->inst_offset;
14077 spec = INS_INFO (ins->opcode);
14079 /* printf ("INS: "); mono_print_ins (ins); */
14080 /* Create a store instruction */
14081 NEW_STORE_MEMBASE (cfg, store_ins, store_opcode, var->inst_basereg, var->inst_offset, ins->dreg);
14083 /* Insert it after the instruction */
14084 mono_bblock_insert_after_ins (bb, ins, store_ins);
14086 def_ins = store_ins;
14089 * We can't assign ins->dreg to var->dreg here, since the
14090 * sregs could use it. So set a flag, and do it after
14093 if ((!MONO_ARCH_USE_FPSTACK || ((store_opcode != OP_STORER8_MEMBASE_REG) && (store_opcode != OP_STORER4_MEMBASE_REG))) && !((var)->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT)))
14094 dest_has_lvreg = TRUE;
14099 if (def_ins && !live_range_start [dreg]) {
14100 live_range_start [dreg] = def_ins;
14101 live_range_start_bb [dreg] = bb;
14104 if (cfg->compute_gc_maps && def_ins && (var->flags & MONO_INST_GC_TRACK)) {
14107 MONO_INST_NEW (cfg, tmp, OP_GC_LIVENESS_DEF);
14108 tmp->inst_c1 = dreg;
14109 mono_bblock_insert_after_ins (bb, def_ins, tmp);
14116 num_sregs = mono_inst_get_src_registers (ins, sregs);
14117 for (srcindex = 0; srcindex < 3; ++srcindex) {
14118 regtype = spec [MONO_INST_SRC1 + srcindex];
14119 sreg = sregs [srcindex];
14121 g_assert (((sreg == -1) && (regtype == ' ')) || ((sreg != -1) && (regtype != ' ')));
14122 if ((sreg != -1) && get_vreg_to_inst (cfg, sreg)) {
14123 MonoInst *var = get_vreg_to_inst (cfg, sreg);
14124 MonoInst *use_ins = ins;
14125 MonoInst *load_ins;
14126 guint32 load_opcode;
14128 if (var->opcode == OP_REGVAR) {
14129 sregs [srcindex] = var->dreg;
14130 //mono_inst_set_src_registers (ins, sregs);
14131 live_range_end [sreg] = use_ins;
14132 live_range_end_bb [sreg] = bb;
14134 if (cfg->compute_gc_maps && var->dreg < orig_next_vreg && (var->flags & MONO_INST_GC_TRACK)) {
14137 MONO_INST_NEW (cfg, tmp, OP_GC_LIVENESS_USE);
14138 /* var->dreg is a hreg */
14139 tmp->inst_c1 = sreg;
14140 mono_bblock_insert_after_ins (bb, ins, tmp);
14146 g_assert (var->opcode == OP_REGOFFSET);
14148 load_opcode = mono_type_to_load_membase (cfg, var->inst_vtype);
14150 g_assert (load_opcode != OP_LOADV_MEMBASE);
14152 if (vreg_to_lvreg [sreg]) {
14153 g_assert (vreg_to_lvreg [sreg] != -1);
14155 /* The variable is already loaded to an lvreg */
14156 if (G_UNLIKELY (cfg->verbose_level > 2))
14157 printf ("\t\tUse lvreg R%d for R%d.\n", vreg_to_lvreg [sreg], sreg);
14158 sregs [srcindex] = vreg_to_lvreg [sreg];
14159 //mono_inst_set_src_registers (ins, sregs);
14163 /* Try to fuse the load into the instruction */
14164 if ((srcindex == 0) && (op_to_op_src1_membase (load_opcode, ins->opcode) != -1)) {
14165 ins->opcode = op_to_op_src1_membase (load_opcode, ins->opcode);
14166 sregs [0] = var->inst_basereg;
14167 //mono_inst_set_src_registers (ins, sregs);
14168 ins->inst_offset = var->inst_offset;
14169 } else if ((srcindex == 1) && (op_to_op_src2_membase (load_opcode, ins->opcode) != -1)) {
14170 ins->opcode = op_to_op_src2_membase (load_opcode, ins->opcode);
14171 sregs [1] = var->inst_basereg;
14172 //mono_inst_set_src_registers (ins, sregs);
14173 ins->inst_offset = var->inst_offset;
14175 if (MONO_IS_REAL_MOVE (ins)) {
14176 ins->opcode = OP_NOP;
14179 //printf ("%d ", srcindex); mono_print_ins (ins);
14181 sreg = alloc_dreg (cfg, stacktypes [regtype]);
14183 if ((!MONO_ARCH_USE_FPSTACK || ((load_opcode != OP_LOADR8_MEMBASE) && (load_opcode != OP_LOADR4_MEMBASE))) && !((var)->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT)) && !no_lvreg) {
14184 if (var->dreg == prev_dreg) {
14186 * sreg refers to the value loaded by the load
14187 * emitted below, but we need to use ins->dreg
14188 * since it refers to the store emitted earlier.
14192 g_assert (sreg != -1);
14193 vreg_to_lvreg [var->dreg] = sreg;
14194 g_assert (lvregs_len < 1024);
14195 lvregs [lvregs_len ++] = var->dreg;
14199 sregs [srcindex] = sreg;
14200 //mono_inst_set_src_registers (ins, sregs);
14202 #if SIZEOF_REGISTER != 8
14203 if (regtype == 'l') {
14204 NEW_LOAD_MEMBASE (cfg, load_ins, OP_LOADI4_MEMBASE, sreg + 2, var->inst_basereg, var->inst_offset + MINI_MS_WORD_OFFSET);
14205 mono_bblock_insert_before_ins (bb, ins, load_ins);
14206 NEW_LOAD_MEMBASE (cfg, load_ins, OP_LOADI4_MEMBASE, sreg + 1, var->inst_basereg, var->inst_offset + MINI_LS_WORD_OFFSET);
14207 mono_bblock_insert_before_ins (bb, ins, load_ins);
14208 use_ins = load_ins;
14213 #if SIZEOF_REGISTER == 4
14214 g_assert (load_opcode != OP_LOADI8_MEMBASE);
14216 NEW_LOAD_MEMBASE (cfg, load_ins, load_opcode, sreg, var->inst_basereg, var->inst_offset);
14217 mono_bblock_insert_before_ins (bb, ins, load_ins);
14218 use_ins = load_ins;
14222 if (var->dreg < orig_next_vreg) {
14223 live_range_end [var->dreg] = use_ins;
14224 live_range_end_bb [var->dreg] = bb;
14227 if (cfg->compute_gc_maps && var->dreg < orig_next_vreg && (var->flags & MONO_INST_GC_TRACK)) {
14230 MONO_INST_NEW (cfg, tmp, OP_GC_LIVENESS_USE);
14231 tmp->inst_c1 = var->dreg;
14232 mono_bblock_insert_after_ins (bb, ins, tmp);
14236 mono_inst_set_src_registers (ins, sregs);
14238 if (dest_has_lvreg) {
14239 g_assert (ins->dreg != -1);
14240 vreg_to_lvreg [prev_dreg] = ins->dreg;
14241 g_assert (lvregs_len < 1024);
14242 lvregs [lvregs_len ++] = prev_dreg;
14243 dest_has_lvreg = FALSE;
14247 tmp_reg = ins->dreg;
14248 ins->dreg = ins->sreg2;
14249 ins->sreg2 = tmp_reg;
14252 if (MONO_IS_CALL (ins)) {
14253 /* Clear vreg_to_lvreg array */
14254 for (i = 0; i < lvregs_len; i++)
14255 vreg_to_lvreg [lvregs [i]] = 0;
14257 } else if (ins->opcode == OP_NOP) {
14259 MONO_INST_NULLIFY_SREGS (ins);
14262 if (cfg->verbose_level > 2)
14263 mono_print_ins_index (1, ins);
14266 /* Extend the live range based on the liveness info */
14267 if (cfg->compute_precise_live_ranges && bb->live_out_set && bb->code) {
14268 for (i = 0; i < cfg->num_varinfo; i ++) {
14269 MonoMethodVar *vi = MONO_VARINFO (cfg, i);
14271 if (vreg_is_volatile (cfg, vi->vreg))
14272 /* The liveness info is incomplete */
14275 if (mono_bitset_test_fast (bb->live_in_set, i) && !live_range_start [vi->vreg]) {
14276 /* Live from at least the first ins of this bb */
14277 live_range_start [vi->vreg] = bb->code;
14278 live_range_start_bb [vi->vreg] = bb;
14281 if (mono_bitset_test_fast (bb->live_out_set, i)) {
14282 /* Live at least until the last ins of this bb */
14283 live_range_end [vi->vreg] = bb->last_ins;
14284 live_range_end_bb [vi->vreg] = bb;
14290 #ifdef MONO_ARCH_HAVE_LIVERANGE_OPS
14292 * Emit LIVERANGE_START/LIVERANGE_END opcodes, the backend will implement them
14293 * by storing the current native offset into MonoMethodVar->live_range_start/end.
14295 if (cfg->compute_precise_live_ranges && cfg->comp_done & MONO_COMP_LIVENESS) {
14296 for (i = 0; i < cfg->num_varinfo; ++i) {
14297 int vreg = MONO_VARINFO (cfg, i)->vreg;
14300 if (live_range_start [vreg]) {
14301 MONO_INST_NEW (cfg, ins, OP_LIVERANGE_START);
14303 ins->inst_c1 = vreg;
14304 mono_bblock_insert_after_ins (live_range_start_bb [vreg], live_range_start [vreg], ins);
14306 if (live_range_end [vreg]) {
14307 MONO_INST_NEW (cfg, ins, OP_LIVERANGE_END);
14309 ins->inst_c1 = vreg;
14310 if (live_range_end [vreg] == live_range_end_bb [vreg]->last_ins)
14311 mono_add_ins_to_end (live_range_end_bb [vreg], ins);
14313 mono_bblock_insert_after_ins (live_range_end_bb [vreg], live_range_end [vreg], ins);
14319 if (cfg->gsharedvt_locals_var_ins) {
14320 /* Nullify if unused */
14321 cfg->gsharedvt_locals_var_ins->opcode = OP_PCONST;
14322 cfg->gsharedvt_locals_var_ins->inst_imm = 0;
14325 g_free (live_range_start);
14326 g_free (live_range_end);
14327 g_free (live_range_start_bb);
14328 g_free (live_range_end_bb);
14333 * - use 'iadd' instead of 'int_add'
14334 * - handling ovf opcodes: decompose in method_to_ir.
14335 * - unify iregs/fregs
14336 * -> partly done, the missing parts are:
14337 * - a more complete unification would involve unifying the hregs as well, so
14338 * code wouldn't need if (fp) all over the place. but that would mean the hregs
14339 * would no longer map to the machine hregs, so the code generators would need to
14340 * be modified. Also, on ia64 for example, niregs + nfregs > 256 -> bitmasks
14341 * wouldn't work any more. Duplicating the code in mono_local_regalloc () into
14342 * fp/non-fp branches speeds it up by about 15%.
14343 * - use sext/zext opcodes instead of shifts
14345 * - get rid of TEMPLOADs if possible and use vregs instead
14346 * - clean up usage of OP_P/OP_ opcodes
14347 * - cleanup usage of DUMMY_USE
14348 * - cleanup the setting of ins->type for MonoInst's which are pushed on the
14350 * - set the stack type and allocate a dreg in the EMIT_NEW macros
14351 * - get rid of all the <foo>2 stuff when the new JIT is ready.
14352 * - make sure handle_stack_args () is called before the branch is emitted
14353 * - when the new IR is done, get rid of all unused stuff
14354 * - COMPARE/BEQ as separate instructions or unify them ?
14355 * - keeping them separate allows specialized compare instructions like
14356 * compare_imm, compare_membase
14357 * - most back ends unify fp compare+branch, fp compare+ceq
14358 * - integrate mono_save_args into inline_method
14359 * - get rid of the empty bblocks created by MONO_EMIT_NEW_BRACH_BLOCK2
14360 * - handle long shift opts on 32 bit platforms somehow: they require
14361 * 3 sregs (2 for arg1 and 1 for arg2)
14362 * - make byref a 'normal' type.
14363 * - use vregs for bb->out_stacks if possible, handle_global_vreg will make them a
14364 * variable if needed.
14365 * - do not start a new IL level bblock when cfg->cbb is changed by a function call
14366 * like inline_method.
14367 * - remove inlining restrictions
14368 * - fix LNEG and enable cfold of INEG
14369 * - generalize x86 optimizations like ldelema as a peephole optimization
14370 * - add store_mem_imm for amd64
14371 * - optimize the loading of the interruption flag in the managed->native wrappers
14372 * - avoid special handling of OP_NOP in passes
14373 * - move code inserting instructions into one function/macro.
14374 * - try a coalescing phase after liveness analysis
14375 * - add float -> vreg conversion + local optimizations on !x86
14376 * - figure out how to handle decomposed branches during optimizations, ie.
14377 * compare+branch, op_jump_table+op_br etc.
14378 * - promote RuntimeXHandles to vregs
14379 * - vtype cleanups:
14380 * - add a NEW_VARLOADA_VREG macro
14381 * - the vtype optimizations are blocked by the LDADDR opcodes generated for
14382 * accessing vtype fields.
14383 * - get rid of I8CONST on 64 bit platforms
14384 * - dealing with the increase in code size due to branches created during opcode
14386 * - use extended basic blocks
14387 * - all parts of the JIT
14388 * - handle_global_vregs () && local regalloc
14389 * - avoid introducing global vregs during decomposition, like 'vtable' in isinst
14390 * - sources of increase in code size:
14393 * - isinst and castclass
14394 * - lvregs not allocated to global registers even if used multiple times
14395 * - call cctors outside the JIT, to make -v output more readable and JIT timings more
14397 * - check for fp stack leakage in other opcodes too. (-> 'exceptions' optimization)
14398 * - add all micro optimizations from the old JIT
14399 * - put tree optimizations into the deadce pass
14400 * - decompose op_start_handler/op_endfilter/op_endfinally earlier using an arch
14401 * specific function.
14402 * - unify the float comparison opcodes with the other comparison opcodes, i.e.
14403 * fcompare + branchCC.
14404 * - create a helper function for allocating a stack slot, taking into account
14405 * MONO_CFG_HAS_SPILLUP.
14407 * - merge the ia64 switch changes.
14408 * - optimize mono_regstate2_alloc_int/float.
14409 * - fix the pessimistic handling of variables accessed in exception handler blocks.
14410 * - need to write a tree optimization pass, but the creation of trees is difficult, i.e.
14411 * parts of the tree could be separated by other instructions, killing the tree
14412 * arguments, or stores killing loads etc. Also, should we fold loads into other
14413 * instructions if the result of the load is used multiple times ?
14414 * - make the REM_IMM optimization in mini-x86.c arch-independent.
14415 * - LAST MERGE: 108395.
14416 * - when returning vtypes in registers, generate IR and append it to the end of the
14417 * last bb instead of doing it in the epilog.
14418 * - change the store opcodes so they use sreg1 instead of dreg to store the base register.
14426 - When to decompose opcodes:
14427 - earlier: this makes some optimizations hard to implement, since the low level IR
14428 no longer contains the neccessary information. But it is easier to do.
14429 - later: harder to implement, enables more optimizations.
14430 - Branches inside bblocks:
14431 - created when decomposing complex opcodes.
14432 - branches to another bblock: harmless, but not tracked by the branch
14433 optimizations, so need to branch to a label at the start of the bblock.
14434 - branches to inside the same bblock: very problematic, trips up the local
14435 reg allocator. Can be fixed by spitting the current bblock, but that is a
14436 complex operation, since some local vregs can become global vregs etc.
14437 - Local/global vregs:
14438 - local vregs: temporary vregs used inside one bblock. Assigned to hregs by the
14439 local register allocator.
14440 - global vregs: used in more than one bblock. Have an associated MonoMethodVar
14441 structure, created by mono_create_var (). Assigned to hregs or the stack by
14442 the global register allocator.
14443 - When to do optimizations like alu->alu_imm:
14444 - earlier -> saves work later on since the IR will be smaller/simpler
14445 - later -> can work on more instructions
14446 - Handling of valuetypes:
14447 - When a vtype is pushed on the stack, a new temporary is created, an
14448 instruction computing its address (LDADDR) is emitted and pushed on
14449 the stack. Need to optimize cases when the vtype is used immediately as in
14450 argument passing, stloc etc.
14451 - Instead of the to_end stuff in the old JIT, simply call the function handling
14452 the values on the stack before emitting the last instruction of the bb.
14455 #endif /* DISABLE_JIT */