2 * method-to-ir.c: Convert CIL to the JIT internal representation
5 * Paolo Molaro (lupus@ximian.com)
6 * Dietmar Maurer (dietmar@ximian.com)
8 * (C) 2002 Ximian, Inc.
9 * Copyright 2003-2010 Novell, Inc (http://www.novell.com)
10 * Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
27 #ifdef HAVE_SYS_TIME_H
35 #include <mono/utils/memcheck.h>
37 #include <mono/metadata/abi-details.h>
38 #include <mono/metadata/assembly.h>
39 #include <mono/metadata/attrdefs.h>
40 #include <mono/metadata/loader.h>
41 #include <mono/metadata/tabledefs.h>
42 #include <mono/metadata/class.h>
43 #include <mono/metadata/object.h>
44 #include <mono/metadata/exception.h>
45 #include <mono/metadata/opcodes.h>
46 #include <mono/metadata/mono-endian.h>
47 #include <mono/metadata/tokentype.h>
48 #include <mono/metadata/tabledefs.h>
49 #include <mono/metadata/marshal.h>
50 #include <mono/metadata/debug-helpers.h>
51 #include <mono/metadata/mono-debug.h>
52 #include <mono/metadata/mono-debug-debugger.h>
53 #include <mono/metadata/gc-internal.h>
54 #include <mono/metadata/security-manager.h>
55 #include <mono/metadata/threads-types.h>
56 #include <mono/metadata/security-core-clr.h>
57 #include <mono/metadata/monitor.h>
58 #include <mono/metadata/profiler-private.h>
59 #include <mono/metadata/profiler.h>
60 #include <mono/metadata/debug-mono-symfile.h>
61 #include <mono/utils/mono-compiler.h>
62 #include <mono/utils/mono-memory-model.h>
63 #include <mono/metadata/mono-basic-block.h>
69 #include "jit-icalls.h"
71 #include "debugger-agent.h"
72 #include "seq-points.h"
74 #define BRANCH_COST 10
75 #define INLINE_LENGTH_LIMIT 20
77 /* These have 'cfg' as an implicit argument */
78 #define INLINE_FAILURE(msg) do { \
79 if ((cfg->method != cfg->current_method) && (cfg->current_method->wrapper_type == MONO_WRAPPER_NONE)) { \
80 inline_failure (cfg, msg); \
81 goto exception_exit; \
84 #define CHECK_CFG_EXCEPTION do {\
85 if (cfg->exception_type != MONO_EXCEPTION_NONE) \
86 goto exception_exit; \
88 #define METHOD_ACCESS_FAILURE(method, cmethod) do { \
89 method_access_failure ((cfg), (method), (cmethod)); \
90 goto exception_exit; \
92 #define FIELD_ACCESS_FAILURE(method, field) do { \
93 field_access_failure ((cfg), (method), (field)); \
94 goto exception_exit; \
96 #define GENERIC_SHARING_FAILURE(opcode) do { \
98 gshared_failure (cfg, opcode, __FILE__, __LINE__); \
99 goto exception_exit; \
102 #define GSHAREDVT_FAILURE(opcode) do { \
103 if (cfg->gsharedvt) { \
104 gsharedvt_failure (cfg, opcode, __FILE__, __LINE__); \
105 goto exception_exit; \
108 #define OUT_OF_MEMORY_FAILURE do { \
109 mono_cfg_set_exception (cfg, MONO_EXCEPTION_OUT_OF_MEMORY); \
110 goto exception_exit; \
112 #define DISABLE_AOT(cfg) do { \
113 if ((cfg)->verbose_level >= 2) \
114 printf ("AOT disabled: %s:%d\n", __FILE__, __LINE__); \
115 (cfg)->disable_aot = TRUE; \
117 #define LOAD_ERROR do { \
118 break_on_unverified (); \
119 mono_cfg_set_exception (cfg, MONO_EXCEPTION_TYPE_LOAD); \
120 goto exception_exit; \
123 #define TYPE_LOAD_ERROR(klass) do { \
124 cfg->exception_ptr = klass; \
128 #define CHECK_CFG_ERROR do {\
129 if (!mono_error_ok (&cfg->error)) { \
130 mono_cfg_set_exception (cfg, MONO_EXCEPTION_MONO_ERROR); \
131 goto mono_error_exit; \
135 /* Determine whenever 'ins' represents a load of the 'this' argument */
136 #define MONO_CHECK_THIS(ins) (mono_method_signature (cfg->method)->hasthis && ((ins)->opcode == OP_MOVE) && ((ins)->sreg1 == cfg->args [0]->dreg))
138 static int ldind_to_load_membase (int opcode);
139 static int stind_to_store_membase (int opcode);
141 int mono_op_to_op_imm (int opcode);
142 int mono_op_to_op_imm_noemul (int opcode);
144 MONO_API MonoInst* mono_emit_native_call (MonoCompile *cfg, gconstpointer func, MonoMethodSignature *sig, MonoInst **args);
146 static int inline_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **sp,
147 guchar *ip, guint real_offset, gboolean inline_always, MonoBasicBlock **out_cbb);
149 /* helper methods signatures */
150 static MonoMethodSignature *helper_sig_class_init_trampoline;
151 static MonoMethodSignature *helper_sig_domain_get;
152 static MonoMethodSignature *helper_sig_rgctx_lazy_fetch_trampoline;
153 static MonoMethodSignature *helper_sig_monitor_enter_exit_trampoline;
154 static MonoMethodSignature *helper_sig_monitor_enter_exit_trampoline_llvm;
155 static MonoMethodSignature *helper_sig_monitor_enter_v4_trampoline_llvm;
158 * Instruction metadata
166 #define MINI_OP(a,b,dest,src1,src2) dest, src1, src2, ' ',
167 #define MINI_OP3(a,b,dest,src1,src2,src3) dest, src1, src2, src3,
173 #if SIZEOF_REGISTER == 8 && SIZEOF_REGISTER == SIZEOF_VOID_P
178 /* keep in sync with the enum in mini.h */
181 #include "mini-ops.h"
186 #define MINI_OP(a,b,dest,src1,src2) ((src2) != NONE ? 2 : ((src1) != NONE ? 1 : 0)),
187 #define MINI_OP3(a,b,dest,src1,src2,src3) ((src3) != NONE ? 3 : ((src2) != NONE ? 2 : ((src1) != NONE ? 1 : 0))),
189 * This should contain the index of the last sreg + 1. This is not the same
190 * as the number of sregs for opcodes like IA64_CMP_EQ_IMM.
192 const gint8 ins_sreg_counts[] = {
193 #include "mini-ops.h"
198 #define MONO_INIT_VARINFO(vi,id) do { \
199 (vi)->range.first_use.pos.bid = 0xffff; \
205 mono_alloc_ireg (MonoCompile *cfg)
207 return alloc_ireg (cfg);
211 mono_alloc_lreg (MonoCompile *cfg)
213 return alloc_lreg (cfg);
217 mono_alloc_freg (MonoCompile *cfg)
219 return alloc_freg (cfg);
223 mono_alloc_preg (MonoCompile *cfg)
225 return alloc_preg (cfg);
229 mono_alloc_dreg (MonoCompile *cfg, MonoStackType stack_type)
231 return alloc_dreg (cfg, stack_type);
235 * mono_alloc_ireg_ref:
237 * Allocate an IREG, and mark it as holding a GC ref.
240 mono_alloc_ireg_ref (MonoCompile *cfg)
242 return alloc_ireg_ref (cfg);
246 * mono_alloc_ireg_mp:
248 * Allocate an IREG, and mark it as holding a managed pointer.
251 mono_alloc_ireg_mp (MonoCompile *cfg)
253 return alloc_ireg_mp (cfg);
257 * mono_alloc_ireg_copy:
259 * Allocate an IREG with the same GC type as VREG.
262 mono_alloc_ireg_copy (MonoCompile *cfg, guint32 vreg)
264 if (vreg_is_ref (cfg, vreg))
265 return alloc_ireg_ref (cfg);
266 else if (vreg_is_mp (cfg, vreg))
267 return alloc_ireg_mp (cfg);
269 return alloc_ireg (cfg);
273 mono_type_to_regmove (MonoCompile *cfg, MonoType *type)
278 type = mini_get_underlying_type (cfg, type);
280 switch (type->type) {
293 case MONO_TYPE_FNPTR:
295 case MONO_TYPE_CLASS:
296 case MONO_TYPE_STRING:
297 case MONO_TYPE_OBJECT:
298 case MONO_TYPE_SZARRAY:
299 case MONO_TYPE_ARRAY:
303 #if SIZEOF_REGISTER == 8
309 return cfg->r4fp ? OP_RMOVE : OP_FMOVE;
312 case MONO_TYPE_VALUETYPE:
313 if (type->data.klass->enumtype) {
314 type = mono_class_enum_basetype (type->data.klass);
317 if (MONO_CLASS_IS_SIMD (cfg, mono_class_from_mono_type (type)))
320 case MONO_TYPE_TYPEDBYREF:
322 case MONO_TYPE_GENERICINST:
323 type = &type->data.generic_class->container_class->byval_arg;
327 g_assert (cfg->generic_sharing_context);
328 if (mini_type_var_is_vt (cfg, type))
331 return mono_type_to_regmove (cfg, mini_get_underlying_type (cfg, type));
333 g_error ("unknown type 0x%02x in type_to_regstore", type->type);
339 mono_print_bb (MonoBasicBlock *bb, const char *msg)
344 printf ("\n%s %d: [IN: ", msg, bb->block_num);
345 for (i = 0; i < bb->in_count; ++i)
346 printf (" BB%d(%d)", bb->in_bb [i]->block_num, bb->in_bb [i]->dfn);
348 for (i = 0; i < bb->out_count; ++i)
349 printf (" BB%d(%d)", bb->out_bb [i]->block_num, bb->out_bb [i]->dfn);
351 for (tree = bb->code; tree; tree = tree->next)
352 mono_print_ins_index (-1, tree);
356 mono_create_helper_signatures (void)
358 helper_sig_domain_get = mono_create_icall_signature ("ptr");
359 helper_sig_class_init_trampoline = mono_create_icall_signature ("void");
360 helper_sig_rgctx_lazy_fetch_trampoline = mono_create_icall_signature ("ptr ptr");
361 helper_sig_monitor_enter_exit_trampoline = mono_create_icall_signature ("void");
362 helper_sig_monitor_enter_exit_trampoline_llvm = mono_create_icall_signature ("void object");
363 helper_sig_monitor_enter_v4_trampoline_llvm = mono_create_icall_signature ("void object ptr");
366 static MONO_NEVER_INLINE void
367 break_on_unverified (void)
369 if (mini_get_debug_options ()->break_on_unverified)
373 static MONO_NEVER_INLINE void
374 method_access_failure (MonoCompile *cfg, MonoMethod *method, MonoMethod *cil_method)
376 char *method_fname = mono_method_full_name (method, TRUE);
377 char *cil_method_fname = mono_method_full_name (cil_method, TRUE);
378 mono_cfg_set_exception (cfg, MONO_EXCEPTION_METHOD_ACCESS);
379 cfg->exception_message = g_strdup_printf ("Method `%s' is inaccessible from method `%s'\n", cil_method_fname, method_fname);
380 g_free (method_fname);
381 g_free (cil_method_fname);
384 static MONO_NEVER_INLINE void
385 field_access_failure (MonoCompile *cfg, MonoMethod *method, MonoClassField *field)
387 char *method_fname = mono_method_full_name (method, TRUE);
388 char *field_fname = mono_field_full_name (field);
389 mono_cfg_set_exception (cfg, MONO_EXCEPTION_FIELD_ACCESS);
390 cfg->exception_message = g_strdup_printf ("Field `%s' is inaccessible from method `%s'\n", field_fname, method_fname);
391 g_free (method_fname);
392 g_free (field_fname);
395 static MONO_NEVER_INLINE void
396 inline_failure (MonoCompile *cfg, const char *msg)
398 if (cfg->verbose_level >= 2)
399 printf ("inline failed: %s\n", msg);
400 mono_cfg_set_exception (cfg, MONO_EXCEPTION_INLINE_FAILED);
403 static MONO_NEVER_INLINE void
404 gshared_failure (MonoCompile *cfg, int opcode, const char *file, int line)
406 if (cfg->verbose_level > 2) \
407 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);
408 mono_cfg_set_exception (cfg, MONO_EXCEPTION_GENERIC_SHARING_FAILED);
411 static MONO_NEVER_INLINE void
412 gsharedvt_failure (MonoCompile *cfg, int opcode, const char *file, int line)
414 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);
415 if (cfg->verbose_level >= 2)
416 printf ("%s\n", cfg->exception_message);
417 mono_cfg_set_exception (cfg, MONO_EXCEPTION_GENERIC_SHARING_FAILED);
421 * When using gsharedvt, some instatiations might be verifiable, and some might be not. i.e.
422 * foo<T> (int i) { ldarg.0; box T; }
424 #define UNVERIFIED do { \
425 if (cfg->gsharedvt) { \
426 if (cfg->verbose_level > 2) \
427 printf ("gsharedvt method failed to verify, falling back to instantiation.\n"); \
428 mono_cfg_set_exception (cfg, MONO_EXCEPTION_GENERIC_SHARING_FAILED); \
429 goto exception_exit; \
431 break_on_unverified (); \
435 #define GET_BBLOCK(cfg,tblock,ip) do { \
436 (tblock) = cfg->cil_offset_to_bb [(ip) - cfg->cil_start]; \
438 if ((ip) >= end || (ip) < header->code) UNVERIFIED; \
439 NEW_BBLOCK (cfg, (tblock)); \
440 (tblock)->cil_code = (ip); \
441 ADD_BBLOCK (cfg, (tblock)); \
445 #if defined(TARGET_X86) || defined(TARGET_AMD64)
446 #define EMIT_NEW_X86_LEA(cfg,dest,sr1,sr2,shift,imm) do { \
447 MONO_INST_NEW (cfg, dest, OP_X86_LEA); \
448 (dest)->dreg = alloc_ireg_mp ((cfg)); \
449 (dest)->sreg1 = (sr1); \
450 (dest)->sreg2 = (sr2); \
451 (dest)->inst_imm = (imm); \
452 (dest)->backend.shift_amount = (shift); \
453 MONO_ADD_INS ((cfg)->cbb, (dest)); \
457 /* Emit conversions so both operands of a binary opcode are of the same type */
459 add_widen_op (MonoCompile *cfg, MonoInst *ins, MonoInst **arg1_ref, MonoInst **arg2_ref)
461 MonoInst *arg1 = *arg1_ref;
462 MonoInst *arg2 = *arg2_ref;
465 ((arg1->type == STACK_R4 && arg2->type == STACK_R8) ||
466 (arg1->type == STACK_R8 && arg2->type == STACK_R4))) {
469 /* Mixing r4/r8 is allowed by the spec */
470 if (arg1->type == STACK_R4) {
471 int dreg = alloc_freg (cfg);
473 EMIT_NEW_UNALU (cfg, conv, OP_RCONV_TO_R8, dreg, arg1->dreg);
474 conv->type = STACK_R8;
478 if (arg2->type == STACK_R4) {
479 int dreg = alloc_freg (cfg);
481 EMIT_NEW_UNALU (cfg, conv, OP_RCONV_TO_R8, dreg, arg2->dreg);
482 conv->type = STACK_R8;
488 #if SIZEOF_REGISTER == 8
489 /* FIXME: Need to add many more cases */
490 if ((arg1)->type == STACK_PTR && (arg2)->type == STACK_I4) {
493 int dr = alloc_preg (cfg);
494 EMIT_NEW_UNALU (cfg, widen, OP_SEXT_I4, dr, (arg2)->dreg);
495 (ins)->sreg2 = widen->dreg;
500 #define ADD_BINOP(op) do { \
501 MONO_INST_NEW (cfg, ins, (op)); \
503 ins->sreg1 = sp [0]->dreg; \
504 ins->sreg2 = sp [1]->dreg; \
505 type_from_op (cfg, ins, sp [0], sp [1]); \
507 /* Have to insert a widening op */ \
508 add_widen_op (cfg, ins, &sp [0], &sp [1]); \
509 ins->dreg = alloc_dreg ((cfg), (ins)->type); \
510 MONO_ADD_INS ((cfg)->cbb, (ins)); \
511 *sp++ = mono_decompose_opcode ((cfg), (ins), &bblock); \
514 #define ADD_UNOP(op) do { \
515 MONO_INST_NEW (cfg, ins, (op)); \
517 ins->sreg1 = sp [0]->dreg; \
518 type_from_op (cfg, ins, sp [0], NULL); \
520 (ins)->dreg = alloc_dreg ((cfg), (ins)->type); \
521 MONO_ADD_INS ((cfg)->cbb, (ins)); \
522 *sp++ = mono_decompose_opcode (cfg, ins, &bblock); \
525 #define ADD_BINCOND(next_block) do { \
528 MONO_INST_NEW(cfg, cmp, OP_COMPARE); \
529 cmp->sreg1 = sp [0]->dreg; \
530 cmp->sreg2 = sp [1]->dreg; \
531 type_from_op (cfg, cmp, sp [0], sp [1]); \
533 add_widen_op (cfg, cmp, &sp [0], &sp [1]); \
534 type_from_op (cfg, ins, sp [0], sp [1]); \
535 ins->inst_many_bb = mono_mempool_alloc (cfg->mempool, sizeof(gpointer)*2); \
536 GET_BBLOCK (cfg, tblock, target); \
537 link_bblock (cfg, bblock, tblock); \
538 ins->inst_true_bb = tblock; \
539 if ((next_block)) { \
540 link_bblock (cfg, bblock, (next_block)); \
541 ins->inst_false_bb = (next_block); \
542 start_new_bblock = 1; \
544 GET_BBLOCK (cfg, tblock, ip); \
545 link_bblock (cfg, bblock, tblock); \
546 ins->inst_false_bb = tblock; \
547 start_new_bblock = 2; \
549 if (sp != stack_start) { \
550 handle_stack_args (cfg, stack_start, sp - stack_start); \
551 CHECK_UNVERIFIABLE (cfg); \
553 MONO_ADD_INS (bblock, cmp); \
554 MONO_ADD_INS (bblock, ins); \
558 * link_bblock: Links two basic blocks
560 * links two basic blocks in the control flow graph, the 'from'
561 * argument is the starting block and the 'to' argument is the block
562 * the control flow ends to after 'from'.
565 link_bblock (MonoCompile *cfg, MonoBasicBlock *from, MonoBasicBlock* to)
567 MonoBasicBlock **newa;
571 if (from->cil_code) {
573 printf ("edge from IL%04x to IL_%04x\n", from->cil_code - cfg->cil_code, to->cil_code - cfg->cil_code);
575 printf ("edge from IL%04x to exit\n", from->cil_code - cfg->cil_code);
578 printf ("edge from entry to IL_%04x\n", to->cil_code - cfg->cil_code);
580 printf ("edge from entry to exit\n");
585 for (i = 0; i < from->out_count; ++i) {
586 if (to == from->out_bb [i]) {
592 newa = mono_mempool_alloc (cfg->mempool, sizeof (gpointer) * (from->out_count + 1));
593 for (i = 0; i < from->out_count; ++i) {
594 newa [i] = from->out_bb [i];
602 for (i = 0; i < to->in_count; ++i) {
603 if (from == to->in_bb [i]) {
609 newa = mono_mempool_alloc (cfg->mempool, sizeof (gpointer) * (to->in_count + 1));
610 for (i = 0; i < to->in_count; ++i) {
611 newa [i] = to->in_bb [i];
620 mono_link_bblock (MonoCompile *cfg, MonoBasicBlock *from, MonoBasicBlock* to)
622 link_bblock (cfg, from, to);
626 * mono_find_block_region:
628 * We mark each basic block with a region ID. We use that to avoid BB
629 * optimizations when blocks are in different regions.
632 * A region token that encodes where this region is, and information
633 * about the clause owner for this block.
635 * The region encodes the try/catch/filter clause that owns this block
636 * as well as the type. -1 is a special value that represents a block
637 * that is in none of try/catch/filter.
640 mono_find_block_region (MonoCompile *cfg, int offset)
642 MonoMethodHeader *header = cfg->header;
643 MonoExceptionClause *clause;
646 for (i = 0; i < header->num_clauses; ++i) {
647 clause = &header->clauses [i];
648 if ((clause->flags == MONO_EXCEPTION_CLAUSE_FILTER) && (offset >= clause->data.filter_offset) &&
649 (offset < (clause->handler_offset)))
650 return ((i + 1) << 8) | MONO_REGION_FILTER | clause->flags;
652 if (MONO_OFFSET_IN_HANDLER (clause, offset)) {
653 if (clause->flags == MONO_EXCEPTION_CLAUSE_FINALLY)
654 return ((i + 1) << 8) | MONO_REGION_FINALLY | clause->flags;
655 else if (clause->flags == MONO_EXCEPTION_CLAUSE_FAULT)
656 return ((i + 1) << 8) | MONO_REGION_FAULT | clause->flags;
658 return ((i + 1) << 8) | MONO_REGION_CATCH | clause->flags;
661 for (i = 0; i < header->num_clauses; ++i) {
662 clause = &header->clauses [i];
664 if (MONO_OFFSET_IN_CLAUSE (clause, offset))
665 return ((i + 1) << 8) | clause->flags;
672 mono_find_final_block (MonoCompile *cfg, unsigned char *ip, unsigned char *target, int type)
674 MonoMethodHeader *header = cfg->header;
675 MonoExceptionClause *clause;
679 for (i = 0; i < header->num_clauses; ++i) {
680 clause = &header->clauses [i];
681 if (MONO_OFFSET_IN_CLAUSE (clause, (ip - header->code)) &&
682 (!MONO_OFFSET_IN_CLAUSE (clause, (target - header->code)))) {
683 if (clause->flags == type)
684 res = g_list_append (res, clause);
691 mono_create_spvar_for_region (MonoCompile *cfg, int region)
695 var = g_hash_table_lookup (cfg->spvars, GINT_TO_POINTER (region));
699 var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
700 /* prevent it from being register allocated */
701 var->flags |= MONO_INST_VOLATILE;
703 g_hash_table_insert (cfg->spvars, GINT_TO_POINTER (region), var);
707 mono_find_exvar_for_offset (MonoCompile *cfg, int offset)
709 return g_hash_table_lookup (cfg->exvars, GINT_TO_POINTER (offset));
713 mono_create_exvar_for_offset (MonoCompile *cfg, int offset)
717 var = g_hash_table_lookup (cfg->exvars, GINT_TO_POINTER (offset));
721 var = mono_compile_create_var (cfg, &mono_defaults.object_class->byval_arg, OP_LOCAL);
722 /* prevent it from being register allocated */
723 var->flags |= MONO_INST_VOLATILE;
725 g_hash_table_insert (cfg->exvars, GINT_TO_POINTER (offset), var);
731 * Returns the type used in the eval stack when @type is loaded.
732 * FIXME: return a MonoType/MonoClass for the byref and VALUETYPE cases.
735 type_to_eval_stack_type (MonoCompile *cfg, MonoType *type, MonoInst *inst)
739 type = mini_get_underlying_type (cfg, type);
740 inst->klass = klass = mono_class_from_mono_type (type);
742 inst->type = STACK_MP;
747 switch (type->type) {
749 inst->type = STACK_INV;
757 inst->type = STACK_I4;
762 case MONO_TYPE_FNPTR:
763 inst->type = STACK_PTR;
765 case MONO_TYPE_CLASS:
766 case MONO_TYPE_STRING:
767 case MONO_TYPE_OBJECT:
768 case MONO_TYPE_SZARRAY:
769 case MONO_TYPE_ARRAY:
770 inst->type = STACK_OBJ;
774 inst->type = STACK_I8;
777 inst->type = cfg->r4_stack_type;
780 inst->type = STACK_R8;
782 case MONO_TYPE_VALUETYPE:
783 if (type->data.klass->enumtype) {
784 type = mono_class_enum_basetype (type->data.klass);
788 inst->type = STACK_VTYPE;
791 case MONO_TYPE_TYPEDBYREF:
792 inst->klass = mono_defaults.typed_reference_class;
793 inst->type = STACK_VTYPE;
795 case MONO_TYPE_GENERICINST:
796 type = &type->data.generic_class->container_class->byval_arg;
800 g_assert (cfg->generic_sharing_context);
801 if (mini_is_gsharedvt_type (cfg, type)) {
802 g_assert (cfg->gsharedvt);
803 inst->type = STACK_VTYPE;
805 type_to_eval_stack_type (cfg, mini_get_underlying_type (cfg, type), inst);
809 g_error ("unknown type 0x%02x in eval stack type", type->type);
814 * The following tables are used to quickly validate the IL code in type_from_op ().
817 bin_num_table [STACK_MAX] [STACK_MAX] = {
818 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
819 {STACK_INV, STACK_I4, STACK_INV, STACK_PTR, STACK_INV, STACK_MP, STACK_INV, STACK_INV},
820 {STACK_INV, STACK_INV, STACK_I8, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
821 {STACK_INV, STACK_PTR, STACK_INV, STACK_PTR, STACK_INV, STACK_MP, STACK_INV, STACK_INV},
822 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_R8, STACK_INV, STACK_INV, STACK_INV, STACK_R8},
823 {STACK_INV, STACK_MP, STACK_INV, STACK_MP, STACK_INV, STACK_PTR, STACK_INV, STACK_INV},
824 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
825 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
826 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_R8, STACK_INV, STACK_INV, STACK_INV, STACK_R4}
831 STACK_INV, STACK_I4, STACK_I8, STACK_PTR, STACK_R8, STACK_INV, STACK_INV, STACK_INV, STACK_R4
834 /* reduce the size of this table */
836 bin_int_table [STACK_MAX] [STACK_MAX] = {
837 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
838 {STACK_INV, STACK_I4, STACK_INV, STACK_PTR, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
839 {STACK_INV, STACK_INV, STACK_I8, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
840 {STACK_INV, STACK_PTR, STACK_INV, STACK_PTR, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
841 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
842 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
843 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
844 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV}
848 bin_comp_table [STACK_MAX] [STACK_MAX] = {
849 /* Inv i L p F & O vt r4 */
851 {0, 1, 0, 1, 0, 0, 0, 0}, /* i, int32 */
852 {0, 0, 1, 0, 0, 0, 0, 0}, /* L, int64 */
853 {0, 1, 0, 1, 0, 2, 4, 0}, /* p, ptr */
854 {0, 0, 0, 0, 1, 0, 0, 0, 1}, /* F, R8 */
855 {0, 0, 0, 2, 0, 1, 0, 0}, /* &, managed pointer */
856 {0, 0, 0, 4, 0, 0, 3, 0}, /* O, reference */
857 {0, 0, 0, 0, 0, 0, 0, 0}, /* vt value type */
858 {0, 0, 0, 0, 1, 0, 0, 0, 1}, /* r, r4 */
861 /* reduce the size of this table */
863 shift_table [STACK_MAX] [STACK_MAX] = {
864 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
865 {STACK_INV, STACK_I4, STACK_INV, STACK_I4, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
866 {STACK_INV, STACK_I8, STACK_INV, STACK_I8, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
867 {STACK_INV, STACK_PTR, STACK_INV, STACK_PTR, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
868 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
869 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
870 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
871 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV}
875 * Tables to map from the non-specific opcode to the matching
876 * type-specific opcode.
878 /* handles from CEE_ADD to CEE_SHR_UN (CEE_REM_UN for floats) */
880 binops_op_map [STACK_MAX] = {
881 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
884 /* handles from CEE_NEG to CEE_CONV_U8 */
886 unops_op_map [STACK_MAX] = {
887 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
890 /* handles from CEE_CONV_U2 to CEE_SUB_OVF_UN */
892 ovfops_op_map [STACK_MAX] = {
893 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
896 /* handles from CEE_CONV_OVF_I1_UN to CEE_CONV_OVF_U_UN */
898 ovf2ops_op_map [STACK_MAX] = {
899 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
902 /* handles from CEE_CONV_OVF_I1 to CEE_CONV_OVF_U8 */
904 ovf3ops_op_map [STACK_MAX] = {
905 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
908 /* handles from CEE_BEQ to CEE_BLT_UN */
910 beqops_op_map [STACK_MAX] = {
911 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
914 /* handles from CEE_CEQ to CEE_CLT_UN */
916 ceqops_op_map [STACK_MAX] = {
917 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
921 * Sets ins->type (the type on the eval stack) according to the
922 * type of the opcode and the arguments to it.
923 * Invalid IL code is marked by setting ins->type to the invalid value STACK_INV.
925 * FIXME: this function sets ins->type unconditionally in some cases, but
926 * it should set it to invalid for some types (a conv.x on an object)
929 type_from_op (MonoCompile *cfg, MonoInst *ins, MonoInst *src1, MonoInst *src2)
931 switch (ins->opcode) {
938 /* FIXME: check unverifiable args for STACK_MP */
939 ins->type = bin_num_table [src1->type] [src2->type];
940 ins->opcode += binops_op_map [ins->type];
947 ins->type = bin_int_table [src1->type] [src2->type];
948 ins->opcode += binops_op_map [ins->type];
953 ins->type = shift_table [src1->type] [src2->type];
954 ins->opcode += binops_op_map [ins->type];
959 ins->type = bin_comp_table [src1->type] [src2->type] ? STACK_I4: STACK_INV;
960 if ((src1->type == STACK_I8) || ((SIZEOF_VOID_P == 8) && ((src1->type == STACK_PTR) || (src1->type == STACK_OBJ) || (src1->type == STACK_MP))))
961 ins->opcode = OP_LCOMPARE;
962 else if (src1->type == STACK_R4)
963 ins->opcode = OP_RCOMPARE;
964 else if (src1->type == STACK_R8)
965 ins->opcode = OP_FCOMPARE;
967 ins->opcode = OP_ICOMPARE;
969 case OP_ICOMPARE_IMM:
970 ins->type = bin_comp_table [src1->type] [src1->type] ? STACK_I4 : STACK_INV;
971 if ((src1->type == STACK_I8) || ((SIZEOF_VOID_P == 8) && ((src1->type == STACK_PTR) || (src1->type == STACK_OBJ) || (src1->type == STACK_MP))))
972 ins->opcode = OP_LCOMPARE_IMM;
984 ins->opcode += beqops_op_map [src1->type];
987 ins->type = bin_comp_table [src1->type] [src2->type] ? STACK_I4: STACK_INV;
988 ins->opcode += ceqops_op_map [src1->type];
994 ins->type = (bin_comp_table [src1->type] [src2->type] & 1) ? STACK_I4: STACK_INV;
995 ins->opcode += ceqops_op_map [src1->type];
999 ins->type = neg_table [src1->type];
1000 ins->opcode += unops_op_map [ins->type];
1003 if (src1->type >= STACK_I4 && src1->type <= STACK_PTR)
1004 ins->type = src1->type;
1006 ins->type = STACK_INV;
1007 ins->opcode += unops_op_map [ins->type];
1013 ins->type = STACK_I4;
1014 ins->opcode += unops_op_map [src1->type];
1017 ins->type = STACK_R8;
1018 switch (src1->type) {
1021 ins->opcode = OP_ICONV_TO_R_UN;
1024 ins->opcode = OP_LCONV_TO_R_UN;
1028 case CEE_CONV_OVF_I1:
1029 case CEE_CONV_OVF_U1:
1030 case CEE_CONV_OVF_I2:
1031 case CEE_CONV_OVF_U2:
1032 case CEE_CONV_OVF_I4:
1033 case CEE_CONV_OVF_U4:
1034 ins->type = STACK_I4;
1035 ins->opcode += ovf3ops_op_map [src1->type];
1037 case CEE_CONV_OVF_I_UN:
1038 case CEE_CONV_OVF_U_UN:
1039 ins->type = STACK_PTR;
1040 ins->opcode += ovf2ops_op_map [src1->type];
1042 case CEE_CONV_OVF_I1_UN:
1043 case CEE_CONV_OVF_I2_UN:
1044 case CEE_CONV_OVF_I4_UN:
1045 case CEE_CONV_OVF_U1_UN:
1046 case CEE_CONV_OVF_U2_UN:
1047 case CEE_CONV_OVF_U4_UN:
1048 ins->type = STACK_I4;
1049 ins->opcode += ovf2ops_op_map [src1->type];
1052 ins->type = STACK_PTR;
1053 switch (src1->type) {
1055 ins->opcode = OP_ICONV_TO_U;
1059 #if SIZEOF_VOID_P == 8
1060 ins->opcode = OP_LCONV_TO_U;
1062 ins->opcode = OP_MOVE;
1066 ins->opcode = OP_LCONV_TO_U;
1069 ins->opcode = OP_FCONV_TO_U;
1075 ins->type = STACK_I8;
1076 ins->opcode += unops_op_map [src1->type];
1078 case CEE_CONV_OVF_I8:
1079 case CEE_CONV_OVF_U8:
1080 ins->type = STACK_I8;
1081 ins->opcode += ovf3ops_op_map [src1->type];
1083 case CEE_CONV_OVF_U8_UN:
1084 case CEE_CONV_OVF_I8_UN:
1085 ins->type = STACK_I8;
1086 ins->opcode += ovf2ops_op_map [src1->type];
1089 ins->type = cfg->r4_stack_type;
1090 ins->opcode += unops_op_map [src1->type];
1093 ins->type = STACK_R8;
1094 ins->opcode += unops_op_map [src1->type];
1097 ins->type = STACK_R8;
1101 ins->type = STACK_I4;
1102 ins->opcode += ovfops_op_map [src1->type];
1105 case CEE_CONV_OVF_I:
1106 case CEE_CONV_OVF_U:
1107 ins->type = STACK_PTR;
1108 ins->opcode += ovfops_op_map [src1->type];
1111 case CEE_ADD_OVF_UN:
1113 case CEE_MUL_OVF_UN:
1115 case CEE_SUB_OVF_UN:
1116 ins->type = bin_num_table [src1->type] [src2->type];
1117 ins->opcode += ovfops_op_map [src1->type];
1118 if (ins->type == STACK_R8)
1119 ins->type = STACK_INV;
1121 case OP_LOAD_MEMBASE:
1122 ins->type = STACK_PTR;
1124 case OP_LOADI1_MEMBASE:
1125 case OP_LOADU1_MEMBASE:
1126 case OP_LOADI2_MEMBASE:
1127 case OP_LOADU2_MEMBASE:
1128 case OP_LOADI4_MEMBASE:
1129 case OP_LOADU4_MEMBASE:
1130 ins->type = STACK_PTR;
1132 case OP_LOADI8_MEMBASE:
1133 ins->type = STACK_I8;
1135 case OP_LOADR4_MEMBASE:
1136 ins->type = cfg->r4_stack_type;
1138 case OP_LOADR8_MEMBASE:
1139 ins->type = STACK_R8;
1142 g_error ("opcode 0x%04x not handled in type from op", ins->opcode);
1146 if (ins->type == STACK_MP)
1147 ins->klass = mono_defaults.object_class;
1152 STACK_I4, STACK_I4, STACK_I4, STACK_I4, STACK_I4, STACK_I4, STACK_I8, STACK_PTR, STACK_R8, STACK_R8, STACK_OBJ
1158 param_table [STACK_MAX] [STACK_MAX] = {
1163 check_values_to_signature (MonoInst *args, MonoType *this, MonoMethodSignature *sig) {
1167 switch (args->type) {
1177 for (i = 0; i < sig->param_count; ++i) {
1178 switch (args [i].type) {
1182 if (!sig->params [i]->byref)
1186 if (sig->params [i]->byref)
1188 switch (sig->params [i]->type) {
1189 case MONO_TYPE_CLASS:
1190 case MONO_TYPE_STRING:
1191 case MONO_TYPE_OBJECT:
1192 case MONO_TYPE_SZARRAY:
1193 case MONO_TYPE_ARRAY:
1200 if (sig->params [i]->byref)
1202 if (sig->params [i]->type != MONO_TYPE_R4 && sig->params [i]->type != MONO_TYPE_R8)
1211 /*if (!param_table [args [i].type] [sig->params [i]->type])
1219 * When we need a pointer to the current domain many times in a method, we
1220 * call mono_domain_get() once and we store the result in a local variable.
1221 * This function returns the variable that represents the MonoDomain*.
1223 inline static MonoInst *
1224 mono_get_domainvar (MonoCompile *cfg)
1226 if (!cfg->domainvar)
1227 cfg->domainvar = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
1228 return cfg->domainvar;
1232 * The got_var contains the address of the Global Offset Table when AOT
1236 mono_get_got_var (MonoCompile *cfg)
1238 #ifdef MONO_ARCH_NEED_GOT_VAR
1239 if (!cfg->compile_aot)
1241 if (!cfg->got_var) {
1242 cfg->got_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
1244 return cfg->got_var;
1251 mono_get_vtable_var (MonoCompile *cfg)
1253 g_assert (cfg->generic_sharing_context);
1255 if (!cfg->rgctx_var) {
1256 cfg->rgctx_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
1257 /* force the var to be stack allocated */
1258 cfg->rgctx_var->flags |= MONO_INST_VOLATILE;
1261 return cfg->rgctx_var;
1265 type_from_stack_type (MonoInst *ins) {
1266 switch (ins->type) {
1267 case STACK_I4: return &mono_defaults.int32_class->byval_arg;
1268 case STACK_I8: return &mono_defaults.int64_class->byval_arg;
1269 case STACK_PTR: return &mono_defaults.int_class->byval_arg;
1270 case STACK_R4: return &mono_defaults.single_class->byval_arg;
1271 case STACK_R8: return &mono_defaults.double_class->byval_arg;
1273 return &ins->klass->this_arg;
1274 case STACK_OBJ: return &mono_defaults.object_class->byval_arg;
1275 case STACK_VTYPE: return &ins->klass->byval_arg;
1277 g_error ("stack type %d to monotype not handled\n", ins->type);
1282 static G_GNUC_UNUSED int
1283 type_to_stack_type (MonoCompile *cfg, MonoType *t)
1285 t = mono_type_get_underlying_type (t);
1297 case MONO_TYPE_FNPTR:
1299 case MONO_TYPE_CLASS:
1300 case MONO_TYPE_STRING:
1301 case MONO_TYPE_OBJECT:
1302 case MONO_TYPE_SZARRAY:
1303 case MONO_TYPE_ARRAY:
1309 return cfg->r4_stack_type;
1312 case MONO_TYPE_VALUETYPE:
1313 case MONO_TYPE_TYPEDBYREF:
1315 case MONO_TYPE_GENERICINST:
1316 if (mono_type_generic_inst_is_valuetype (t))
1322 g_assert_not_reached ();
1329 array_access_to_klass (int opcode)
1333 return mono_defaults.byte_class;
1335 return mono_defaults.uint16_class;
1338 return mono_defaults.int_class;
1341 return mono_defaults.sbyte_class;
1344 return mono_defaults.int16_class;
1347 return mono_defaults.int32_class;
1349 return mono_defaults.uint32_class;
1352 return mono_defaults.int64_class;
1355 return mono_defaults.single_class;
1358 return mono_defaults.double_class;
1359 case CEE_LDELEM_REF:
1360 case CEE_STELEM_REF:
1361 return mono_defaults.object_class;
1363 g_assert_not_reached ();
1369 * We try to share variables when possible
1372 mono_compile_get_interface_var (MonoCompile *cfg, int slot, MonoInst *ins)
1377 /* inlining can result in deeper stacks */
1378 if (slot >= cfg->header->max_stack)
1379 return mono_compile_create_var (cfg, type_from_stack_type (ins), OP_LOCAL);
1381 pos = ins->type - 1 + slot * STACK_MAX;
1383 switch (ins->type) {
1390 if ((vnum = cfg->intvars [pos]))
1391 return cfg->varinfo [vnum];
1392 res = mono_compile_create_var (cfg, type_from_stack_type (ins), OP_LOCAL);
1393 cfg->intvars [pos] = res->inst_c0;
1396 res = mono_compile_create_var (cfg, type_from_stack_type (ins), OP_LOCAL);
1402 mono_save_token_info (MonoCompile *cfg, MonoImage *image, guint32 token, gpointer key)
1405 * Don't use this if a generic_context is set, since that means AOT can't
1406 * look up the method using just the image+token.
1407 * table == 0 means this is a reference made from a wrapper.
1409 if (cfg->compile_aot && !cfg->generic_context && (mono_metadata_token_table (token) > 0)) {
1410 MonoJumpInfoToken *jump_info_token = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoJumpInfoToken));
1411 jump_info_token->image = image;
1412 jump_info_token->token = token;
1413 g_hash_table_insert (cfg->token_info_hash, key, jump_info_token);
1418 * This function is called to handle items that are left on the evaluation stack
1419 * at basic block boundaries. What happens is that we save the values to local variables
1420 * and we reload them later when first entering the target basic block (with the
1421 * handle_loaded_temps () function).
1422 * A single joint point will use the same variables (stored in the array bb->out_stack or
1423 * bb->in_stack, if the basic block is before or after the joint point).
1425 * This function needs to be called _before_ emitting the last instruction of
1426 * the bb (i.e. before emitting a branch).
1427 * If the stack merge fails at a join point, cfg->unverifiable is set.
1430 handle_stack_args (MonoCompile *cfg, MonoInst **sp, int count)
1433 MonoBasicBlock *bb = cfg->cbb;
1434 MonoBasicBlock *outb;
1435 MonoInst *inst, **locals;
1440 if (cfg->verbose_level > 3)
1441 printf ("%d item(s) on exit from B%d\n", count, bb->block_num);
1442 if (!bb->out_scount) {
1443 bb->out_scount = count;
1444 //printf ("bblock %d has out:", bb->block_num);
1446 for (i = 0; i < bb->out_count; ++i) {
1447 outb = bb->out_bb [i];
1448 /* exception handlers are linked, but they should not be considered for stack args */
1449 if (outb->flags & BB_EXCEPTION_HANDLER)
1451 //printf (" %d", outb->block_num);
1452 if (outb->in_stack) {
1454 bb->out_stack = outb->in_stack;
1460 bb->out_stack = mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*) * count);
1461 for (i = 0; i < count; ++i) {
1463 * try to reuse temps already allocated for this purpouse, if they occupy the same
1464 * stack slot and if they are of the same type.
1465 * This won't cause conflicts since if 'local' is used to
1466 * store one of the values in the in_stack of a bblock, then
1467 * the same variable will be used for the same outgoing stack
1469 * This doesn't work when inlining methods, since the bblocks
1470 * in the inlined methods do not inherit their in_stack from
1471 * the bblock they are inlined to. See bug #58863 for an
1474 if (cfg->inlined_method)
1475 bb->out_stack [i] = mono_compile_create_var (cfg, type_from_stack_type (sp [i]), OP_LOCAL);
1477 bb->out_stack [i] = mono_compile_get_interface_var (cfg, i, sp [i]);
1482 for (i = 0; i < bb->out_count; ++i) {
1483 outb = bb->out_bb [i];
1484 /* exception handlers are linked, but they should not be considered for stack args */
1485 if (outb->flags & BB_EXCEPTION_HANDLER)
1487 if (outb->in_scount) {
1488 if (outb->in_scount != bb->out_scount) {
1489 cfg->unverifiable = TRUE;
1492 continue; /* check they are the same locals */
1494 outb->in_scount = count;
1495 outb->in_stack = bb->out_stack;
1498 locals = bb->out_stack;
1500 for (i = 0; i < count; ++i) {
1501 EMIT_NEW_TEMPSTORE (cfg, inst, locals [i]->inst_c0, sp [i]);
1502 inst->cil_code = sp [i]->cil_code;
1503 sp [i] = locals [i];
1504 if (cfg->verbose_level > 3)
1505 printf ("storing %d to temp %d\n", i, (int)locals [i]->inst_c0);
1509 * It is possible that the out bblocks already have in_stack assigned, and
1510 * the in_stacks differ. In this case, we will store to all the different
1517 /* Find a bblock which has a different in_stack */
1519 while (bindex < bb->out_count) {
1520 outb = bb->out_bb [bindex];
1521 /* exception handlers are linked, but they should not be considered for stack args */
1522 if (outb->flags & BB_EXCEPTION_HANDLER) {
1526 if (outb->in_stack != locals) {
1527 for (i = 0; i < count; ++i) {
1528 EMIT_NEW_TEMPSTORE (cfg, inst, outb->in_stack [i]->inst_c0, sp [i]);
1529 inst->cil_code = sp [i]->cil_code;
1530 sp [i] = locals [i];
1531 if (cfg->verbose_level > 3)
1532 printf ("storing %d to temp %d\n", i, (int)outb->in_stack [i]->inst_c0);
1534 locals = outb->in_stack;
1544 mini_emit_interface_bitmap_check (MonoCompile *cfg, int intf_bit_reg, int base_reg, int offset, MonoClass *klass)
1546 int ibitmap_reg = alloc_preg (cfg);
1547 #ifdef COMPRESSED_INTERFACE_BITMAP
1549 MonoInst *res, *ins;
1550 NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, ibitmap_reg, base_reg, offset);
1551 MONO_ADD_INS (cfg->cbb, ins);
1553 if (cfg->compile_aot)
1554 EMIT_NEW_AOTCONST (cfg, args [1], MONO_PATCH_INFO_IID, klass);
1556 EMIT_NEW_ICONST (cfg, args [1], klass->interface_id);
1557 res = mono_emit_jit_icall (cfg, mono_class_interface_match, args);
1558 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, intf_bit_reg, res->dreg);
1560 int ibitmap_byte_reg = alloc_preg (cfg);
1562 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, ibitmap_reg, base_reg, offset);
1564 if (cfg->compile_aot) {
1565 int iid_reg = alloc_preg (cfg);
1566 int shifted_iid_reg = alloc_preg (cfg);
1567 int ibitmap_byte_address_reg = alloc_preg (cfg);
1568 int masked_iid_reg = alloc_preg (cfg);
1569 int iid_one_bit_reg = alloc_preg (cfg);
1570 int iid_bit_reg = alloc_preg (cfg);
1571 MONO_EMIT_NEW_AOTCONST (cfg, iid_reg, klass, MONO_PATCH_INFO_IID);
1572 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SHR_IMM, shifted_iid_reg, iid_reg, 3);
1573 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, ibitmap_byte_address_reg, ibitmap_reg, shifted_iid_reg);
1574 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU1_MEMBASE, ibitmap_byte_reg, ibitmap_byte_address_reg, 0);
1575 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_IAND_IMM, masked_iid_reg, iid_reg, 7);
1576 MONO_EMIT_NEW_ICONST (cfg, iid_one_bit_reg, 1);
1577 MONO_EMIT_NEW_BIALU (cfg, OP_ISHL, iid_bit_reg, iid_one_bit_reg, masked_iid_reg);
1578 MONO_EMIT_NEW_BIALU (cfg, OP_IAND, intf_bit_reg, ibitmap_byte_reg, iid_bit_reg);
1580 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI1_MEMBASE, ibitmap_byte_reg, ibitmap_reg, klass->interface_id >> 3);
1581 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_AND_IMM, intf_bit_reg, ibitmap_byte_reg, 1 << (klass->interface_id & 7));
1587 * Emit code which loads into "intf_bit_reg" a nonzero value if the MonoClass
1588 * stored in "klass_reg" implements the interface "klass".
1591 mini_emit_load_intf_bit_reg_class (MonoCompile *cfg, int intf_bit_reg, int klass_reg, MonoClass *klass)
1593 mini_emit_interface_bitmap_check (cfg, intf_bit_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, interface_bitmap), klass);
1597 * Emit code which loads into "intf_bit_reg" a nonzero value if the MonoVTable
1598 * stored in "vtable_reg" implements the interface "klass".
1601 mini_emit_load_intf_bit_reg_vtable (MonoCompile *cfg, int intf_bit_reg, int vtable_reg, MonoClass *klass)
1603 mini_emit_interface_bitmap_check (cfg, intf_bit_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, interface_bitmap), klass);
1607 * Emit code which checks whenever the interface id of @klass is smaller than
1608 * than the value given by max_iid_reg.
1611 mini_emit_max_iid_check (MonoCompile *cfg, int max_iid_reg, MonoClass *klass,
1612 MonoBasicBlock *false_target)
1614 if (cfg->compile_aot) {
1615 int iid_reg = alloc_preg (cfg);
1616 MONO_EMIT_NEW_AOTCONST (cfg, iid_reg, klass, MONO_PATCH_INFO_IID);
1617 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, max_iid_reg, iid_reg);
1620 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, max_iid_reg, klass->interface_id);
1622 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBLT_UN, false_target);
1624 MONO_EMIT_NEW_COND_EXC (cfg, LT_UN, "InvalidCastException");
1627 /* Same as above, but obtains max_iid from a vtable */
1629 mini_emit_max_iid_check_vtable (MonoCompile *cfg, int vtable_reg, MonoClass *klass,
1630 MonoBasicBlock *false_target)
1632 int max_iid_reg = alloc_preg (cfg);
1634 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU2_MEMBASE, max_iid_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, max_interface_id));
1635 mini_emit_max_iid_check (cfg, max_iid_reg, klass, false_target);
1638 /* Same as above, but obtains max_iid from a klass */
1640 mini_emit_max_iid_check_class (MonoCompile *cfg, int klass_reg, MonoClass *klass,
1641 MonoBasicBlock *false_target)
1643 int max_iid_reg = alloc_preg (cfg);
1645 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU2_MEMBASE, max_iid_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, max_interface_id));
1646 mini_emit_max_iid_check (cfg, max_iid_reg, klass, false_target);
1650 mini_emit_isninst_cast_inst (MonoCompile *cfg, int klass_reg, MonoClass *klass, MonoInst *klass_ins, MonoBasicBlock *false_target, MonoBasicBlock *true_target)
1652 int idepth_reg = alloc_preg (cfg);
1653 int stypes_reg = alloc_preg (cfg);
1654 int stype = alloc_preg (cfg);
1656 mono_class_setup_supertypes (klass);
1658 if (klass->idepth > MONO_DEFAULT_SUPERTABLE_SIZE) {
1659 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU2_MEMBASE, idepth_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, idepth));
1660 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, idepth_reg, klass->idepth);
1661 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBLT_UN, false_target);
1663 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, stypes_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, supertypes));
1664 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, stype, stypes_reg, ((klass->idepth - 1) * SIZEOF_VOID_P));
1666 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, stype, klass_ins->dreg);
1667 } else if (cfg->compile_aot) {
1668 int const_reg = alloc_preg (cfg);
1669 MONO_EMIT_NEW_CLASSCONST (cfg, const_reg, klass);
1670 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, stype, const_reg);
1672 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, stype, klass);
1674 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, true_target);
1678 mini_emit_isninst_cast (MonoCompile *cfg, int klass_reg, MonoClass *klass, MonoBasicBlock *false_target, MonoBasicBlock *true_target)
1680 mini_emit_isninst_cast_inst (cfg, klass_reg, klass, NULL, false_target, true_target);
1684 mini_emit_iface_cast (MonoCompile *cfg, int vtable_reg, MonoClass *klass, MonoBasicBlock *false_target, MonoBasicBlock *true_target)
1686 int intf_reg = alloc_preg (cfg);
1688 mini_emit_max_iid_check_vtable (cfg, vtable_reg, klass, false_target);
1689 mini_emit_load_intf_bit_reg_vtable (cfg, intf_reg, vtable_reg, klass);
1690 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, intf_reg, 0);
1692 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, true_target);
1694 MONO_EMIT_NEW_COND_EXC (cfg, EQ, "InvalidCastException");
1698 * Variant of the above that takes a register to the class, not the vtable.
1701 mini_emit_iface_class_cast (MonoCompile *cfg, int klass_reg, MonoClass *klass, MonoBasicBlock *false_target, MonoBasicBlock *true_target)
1703 int intf_bit_reg = alloc_preg (cfg);
1705 mini_emit_max_iid_check_class (cfg, klass_reg, klass, false_target);
1706 mini_emit_load_intf_bit_reg_class (cfg, intf_bit_reg, klass_reg, klass);
1707 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, intf_bit_reg, 0);
1709 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, true_target);
1711 MONO_EMIT_NEW_COND_EXC (cfg, EQ, "InvalidCastException");
1715 mini_emit_class_check_inst (MonoCompile *cfg, int klass_reg, MonoClass *klass, MonoInst *klass_inst)
1718 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, klass_reg, klass_inst->dreg);
1719 } else if (cfg->compile_aot) {
1720 int const_reg = alloc_preg (cfg);
1721 MONO_EMIT_NEW_CLASSCONST (cfg, const_reg, klass);
1722 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, klass_reg, const_reg);
1724 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, klass_reg, klass);
1726 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException");
1730 mini_emit_class_check (MonoCompile *cfg, int klass_reg, MonoClass *klass)
1732 mini_emit_class_check_inst (cfg, klass_reg, klass, NULL);
1736 mini_emit_class_check_branch (MonoCompile *cfg, int klass_reg, MonoClass *klass, int branch_op, MonoBasicBlock *target)
1738 if (cfg->compile_aot) {
1739 int const_reg = alloc_preg (cfg);
1740 MONO_EMIT_NEW_CLASSCONST (cfg, const_reg, klass);
1741 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, klass_reg, const_reg);
1743 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, klass_reg, klass);
1745 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, branch_op, target);
1749 mini_emit_castclass (MonoCompile *cfg, int obj_reg, int klass_reg, MonoClass *klass, MonoBasicBlock *object_is_null);
1752 mini_emit_castclass_inst (MonoCompile *cfg, int obj_reg, int klass_reg, MonoClass *klass, MonoInst *klass_inst, MonoBasicBlock *object_is_null)
1755 int rank_reg = alloc_preg (cfg);
1756 int eclass_reg = alloc_preg (cfg);
1758 g_assert (!klass_inst);
1759 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU1_MEMBASE, rank_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, rank));
1760 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, rank_reg, klass->rank);
1761 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException");
1762 // MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
1763 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, eclass_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, cast_class));
1764 if (klass->cast_class == mono_defaults.object_class) {
1765 int parent_reg = alloc_preg (cfg);
1766 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, parent_reg, eclass_reg, MONO_STRUCT_OFFSET (MonoClass, parent));
1767 mini_emit_class_check_branch (cfg, parent_reg, mono_defaults.enum_class->parent, OP_PBNE_UN, object_is_null);
1768 mini_emit_class_check (cfg, eclass_reg, mono_defaults.enum_class);
1769 } else if (klass->cast_class == mono_defaults.enum_class->parent) {
1770 mini_emit_class_check_branch (cfg, eclass_reg, mono_defaults.enum_class->parent, OP_PBEQ, object_is_null);
1771 mini_emit_class_check (cfg, eclass_reg, mono_defaults.enum_class);
1772 } else if (klass->cast_class == mono_defaults.enum_class) {
1773 mini_emit_class_check (cfg, eclass_reg, mono_defaults.enum_class);
1774 } else if (klass->cast_class->flags & TYPE_ATTRIBUTE_INTERFACE) {
1775 mini_emit_iface_class_cast (cfg, eclass_reg, klass->cast_class, NULL, NULL);
1777 // Pass -1 as obj_reg to skip the check below for arrays of arrays
1778 mini_emit_castclass (cfg, -1, eclass_reg, klass->cast_class, object_is_null);
1781 if ((klass->rank == 1) && (klass->byval_arg.type == MONO_TYPE_SZARRAY) && (obj_reg != -1)) {
1782 /* Check that the object is a vector too */
1783 int bounds_reg = alloc_preg (cfg);
1784 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, bounds_reg, obj_reg, MONO_STRUCT_OFFSET (MonoArray, bounds));
1785 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, bounds_reg, 0);
1786 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException");
1789 int idepth_reg = alloc_preg (cfg);
1790 int stypes_reg = alloc_preg (cfg);
1791 int stype = alloc_preg (cfg);
1793 mono_class_setup_supertypes (klass);
1795 if (klass->idepth > MONO_DEFAULT_SUPERTABLE_SIZE) {
1796 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU2_MEMBASE, idepth_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, idepth));
1797 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, idepth_reg, klass->idepth);
1798 MONO_EMIT_NEW_COND_EXC (cfg, LT_UN, "InvalidCastException");
1800 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, stypes_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, supertypes));
1801 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, stype, stypes_reg, ((klass->idepth - 1) * SIZEOF_VOID_P));
1802 mini_emit_class_check_inst (cfg, stype, klass, klass_inst);
1807 mini_emit_castclass (MonoCompile *cfg, int obj_reg, int klass_reg, MonoClass *klass, MonoBasicBlock *object_is_null)
1809 mini_emit_castclass_inst (cfg, obj_reg, klass_reg, klass, NULL, object_is_null);
1813 mini_emit_memset (MonoCompile *cfg, int destreg, int offset, int size, int val, int align)
1817 g_assert (val == 0);
1822 if ((size <= SIZEOF_REGISTER) && (size <= align)) {
1825 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREI1_MEMBASE_IMM, destreg, offset, val);
1828 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREI2_MEMBASE_IMM, destreg, offset, val);
1831 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREI4_MEMBASE_IMM, destreg, offset, val);
1833 #if SIZEOF_REGISTER == 8
1835 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREI8_MEMBASE_IMM, destreg, offset, val);
1841 val_reg = alloc_preg (cfg);
1843 if (SIZEOF_REGISTER == 8)
1844 MONO_EMIT_NEW_I8CONST (cfg, val_reg, val);
1846 MONO_EMIT_NEW_ICONST (cfg, val_reg, val);
1849 /* This could be optimized further if neccesary */
1851 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI1_MEMBASE_REG, destreg, offset, val_reg);
1858 #if !NO_UNALIGNED_ACCESS
1859 if (SIZEOF_REGISTER == 8) {
1861 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI4_MEMBASE_REG, destreg, offset, val_reg);
1866 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI8_MEMBASE_REG, destreg, offset, val_reg);
1874 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI4_MEMBASE_REG, destreg, offset, val_reg);
1879 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI2_MEMBASE_REG, destreg, offset, val_reg);
1884 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI1_MEMBASE_REG, destreg, offset, val_reg);
1891 mini_emit_memcpy (MonoCompile *cfg, int destreg, int doffset, int srcreg, int soffset, int size, int align)
1898 /*FIXME arbitrary hack to avoid unbound code expansion.*/
1899 g_assert (size < 10000);
1902 /* This could be optimized further if neccesary */
1904 cur_reg = alloc_preg (cfg);
1905 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI1_MEMBASE, cur_reg, srcreg, soffset);
1906 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI1_MEMBASE_REG, destreg, doffset, cur_reg);
1913 #if !NO_UNALIGNED_ACCESS
1914 if (SIZEOF_REGISTER == 8) {
1916 cur_reg = alloc_preg (cfg);
1917 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI8_MEMBASE, cur_reg, srcreg, soffset);
1918 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI8_MEMBASE_REG, destreg, doffset, cur_reg);
1927 cur_reg = alloc_preg (cfg);
1928 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI4_MEMBASE, cur_reg, srcreg, soffset);
1929 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI4_MEMBASE_REG, destreg, doffset, cur_reg);
1935 cur_reg = alloc_preg (cfg);
1936 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI2_MEMBASE, cur_reg, srcreg, soffset);
1937 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI2_MEMBASE_REG, destreg, doffset, cur_reg);
1943 cur_reg = alloc_preg (cfg);
1944 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI1_MEMBASE, cur_reg, srcreg, soffset);
1945 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI1_MEMBASE_REG, destreg, doffset, cur_reg);
1953 emit_tls_set (MonoCompile *cfg, int sreg1, int tls_key)
1957 if (cfg->compile_aot) {
1958 EMIT_NEW_TLS_OFFSETCONST (cfg, c, tls_key);
1959 MONO_INST_NEW (cfg, ins, OP_TLS_SET_REG);
1961 ins->sreg2 = c->dreg;
1962 MONO_ADD_INS (cfg->cbb, ins);
1964 MONO_INST_NEW (cfg, ins, OP_TLS_SET);
1966 ins->inst_offset = mini_get_tls_offset (tls_key);
1967 MONO_ADD_INS (cfg->cbb, ins);
1974 * Emit IR to push the current LMF onto the LMF stack.
1977 emit_push_lmf (MonoCompile *cfg)
1980 * Emit IR to push the LMF:
1981 * lmf_addr = <lmf_addr from tls>
1982 * lmf->lmf_addr = lmf_addr
1983 * lmf->prev_lmf = *lmf_addr
1986 int lmf_reg, prev_lmf_reg;
1987 MonoInst *ins, *lmf_ins;
1992 if (cfg->lmf_ir_mono_lmf && mini_tls_get_supported (cfg, TLS_KEY_LMF)) {
1993 /* Load current lmf */
1994 lmf_ins = mono_get_lmf_intrinsic (cfg);
1996 MONO_ADD_INS (cfg->cbb, lmf_ins);
1997 EMIT_NEW_VARLOADA (cfg, ins, cfg->lmf_var, NULL);
1998 lmf_reg = ins->dreg;
1999 /* Save previous_lmf */
2000 EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, lmf_reg, MONO_STRUCT_OFFSET (MonoLMF, previous_lmf), lmf_ins->dreg);
2002 emit_tls_set (cfg, lmf_reg, TLS_KEY_LMF);
2005 * Store lmf_addr in a variable, so it can be allocated to a global register.
2007 if (!cfg->lmf_addr_var)
2008 cfg->lmf_addr_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
2011 ins = mono_get_jit_tls_intrinsic (cfg);
2013 int jit_tls_dreg = ins->dreg;
2015 MONO_ADD_INS (cfg->cbb, ins);
2016 lmf_reg = alloc_preg (cfg);
2017 EMIT_NEW_BIALU_IMM (cfg, lmf_ins, OP_PADD_IMM, lmf_reg, jit_tls_dreg, MONO_STRUCT_OFFSET (MonoJitTlsData, lmf));
2019 lmf_ins = mono_emit_jit_icall (cfg, mono_get_lmf_addr, NULL);
2022 lmf_ins = mono_get_lmf_addr_intrinsic (cfg);
2024 MONO_ADD_INS (cfg->cbb, lmf_ins);
2027 MonoInst *args [16], *jit_tls_ins, *ins;
2029 /* Inline mono_get_lmf_addr () */
2030 /* jit_tls = pthread_getspecific (mono_jit_tls_id); lmf_addr = &jit_tls->lmf; */
2032 /* Load mono_jit_tls_id */
2033 EMIT_NEW_AOTCONST (cfg, args [0], MONO_PATCH_INFO_JIT_TLS_ID, NULL);
2034 /* call pthread_getspecific () */
2035 jit_tls_ins = mono_emit_jit_icall (cfg, pthread_getspecific, args);
2036 /* lmf_addr = &jit_tls->lmf */
2037 EMIT_NEW_BIALU_IMM (cfg, ins, OP_PADD_IMM, cfg->lmf_addr_var->dreg, jit_tls_ins->dreg, MONO_STRUCT_OFFSET (MonoJitTlsData, lmf));
2040 lmf_ins = mono_emit_jit_icall (cfg, mono_get_lmf_addr, NULL);
2044 lmf_ins->dreg = cfg->lmf_addr_var->dreg;
2046 EMIT_NEW_VARLOADA (cfg, ins, cfg->lmf_var, NULL);
2047 lmf_reg = ins->dreg;
2049 prev_lmf_reg = alloc_preg (cfg);
2050 /* Save previous_lmf */
2051 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, prev_lmf_reg, cfg->lmf_addr_var->dreg, 0);
2052 EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, lmf_reg, MONO_STRUCT_OFFSET (MonoLMF, previous_lmf), prev_lmf_reg);
2054 EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, cfg->lmf_addr_var->dreg, 0, lmf_reg);
2061 * Emit IR to pop the current LMF from the LMF stack.
2064 emit_pop_lmf (MonoCompile *cfg)
2066 int lmf_reg, lmf_addr_reg, prev_lmf_reg;
2072 EMIT_NEW_VARLOADA (cfg, ins, cfg->lmf_var, NULL);
2073 lmf_reg = ins->dreg;
2075 if (cfg->lmf_ir_mono_lmf && mini_tls_get_supported (cfg, TLS_KEY_LMF)) {
2076 /* Load previous_lmf */
2077 prev_lmf_reg = alloc_preg (cfg);
2078 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, prev_lmf_reg, lmf_reg, MONO_STRUCT_OFFSET (MonoLMF, previous_lmf));
2080 emit_tls_set (cfg, prev_lmf_reg, TLS_KEY_LMF);
2083 * Emit IR to pop the LMF:
2084 * *(lmf->lmf_addr) = lmf->prev_lmf
2086 /* This could be called before emit_push_lmf () */
2087 if (!cfg->lmf_addr_var)
2088 cfg->lmf_addr_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
2089 lmf_addr_reg = cfg->lmf_addr_var->dreg;
2091 prev_lmf_reg = alloc_preg (cfg);
2092 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, prev_lmf_reg, lmf_reg, MONO_STRUCT_OFFSET (MonoLMF, previous_lmf));
2093 EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, lmf_addr_reg, 0, prev_lmf_reg);
2098 emit_instrumentation_call (MonoCompile *cfg, void *func)
2100 MonoInst *iargs [1];
2103 * Avoid instrumenting inlined methods since it can
2104 * distort profiling results.
2106 if (cfg->method != cfg->current_method)
2109 if (cfg->prof_options & MONO_PROFILE_ENTER_LEAVE) {
2110 EMIT_NEW_METHODCONST (cfg, iargs [0], cfg->method);
2111 mono_emit_jit_icall (cfg, func, iargs);
2116 ret_type_to_call_opcode (MonoCompile *cfg, MonoType *type, int calli, int virt, MonoGenericSharingContext *gsctx)
2119 type = mini_get_underlying_type (cfg, type);
2120 switch (type->type) {
2121 case MONO_TYPE_VOID:
2122 return calli? OP_VOIDCALL_REG: virt? OP_VOIDCALL_MEMBASE: OP_VOIDCALL;
2129 return calli? OP_CALL_REG: virt? OP_CALL_MEMBASE: OP_CALL;
2133 case MONO_TYPE_FNPTR:
2134 return calli? OP_CALL_REG: virt? OP_CALL_MEMBASE: OP_CALL;
2135 case MONO_TYPE_CLASS:
2136 case MONO_TYPE_STRING:
2137 case MONO_TYPE_OBJECT:
2138 case MONO_TYPE_SZARRAY:
2139 case MONO_TYPE_ARRAY:
2140 return calli? OP_CALL_REG: virt? OP_CALL_MEMBASE: OP_CALL;
2143 return calli? OP_LCALL_REG: virt? OP_LCALL_MEMBASE: OP_LCALL;
2146 return calli? OP_RCALL_REG: virt? OP_RCALL_MEMBASE: OP_RCALL;
2148 return calli? OP_FCALL_REG: virt? OP_FCALL_MEMBASE: OP_FCALL;
2150 return calli? OP_FCALL_REG: virt? OP_FCALL_MEMBASE: OP_FCALL;
2151 case MONO_TYPE_VALUETYPE:
2152 if (type->data.klass->enumtype) {
2153 type = mono_class_enum_basetype (type->data.klass);
2156 return calli? OP_VCALL_REG: virt? OP_VCALL_MEMBASE: OP_VCALL;
2157 case MONO_TYPE_TYPEDBYREF:
2158 return calli? OP_VCALL_REG: virt? OP_VCALL_MEMBASE: OP_VCALL;
2159 case MONO_TYPE_GENERICINST:
2160 type = &type->data.generic_class->container_class->byval_arg;
2163 case MONO_TYPE_MVAR:
2165 return calli? OP_VCALL_REG: virt? OP_VCALL_MEMBASE: OP_VCALL;
2167 g_error ("unknown type 0x%02x in ret_type_to_call_opcode", type->type);
2173 * target_type_is_incompatible:
2174 * @cfg: MonoCompile context
2176 * Check that the item @arg on the evaluation stack can be stored
2177 * in the target type (can be a local, or field, etc).
2178 * The cfg arg can be used to check if we need verification or just
2181 * Returns: non-0 value if arg can't be stored on a target.
2184 target_type_is_incompatible (MonoCompile *cfg, MonoType *target, MonoInst *arg)
2186 MonoType *simple_type;
2189 if (target->byref) {
2190 /* FIXME: check that the pointed to types match */
2191 if (arg->type == STACK_MP)
2192 return arg->klass != mono_class_from_mono_type (target);
2193 if (arg->type == STACK_PTR)
2198 simple_type = mini_get_underlying_type (cfg, target);
2199 switch (simple_type->type) {
2200 case MONO_TYPE_VOID:
2208 if (arg->type != STACK_I4 && arg->type != STACK_PTR)
2212 /* STACK_MP is needed when setting pinned locals */
2213 if (arg->type != STACK_I4 && arg->type != STACK_PTR && arg->type != STACK_MP)
2218 case MONO_TYPE_FNPTR:
2220 * Some opcodes like ldloca returns 'transient pointers' which can be stored in
2221 * in native int. (#688008).
2223 if (arg->type != STACK_I4 && arg->type != STACK_PTR && arg->type != STACK_MP)
2226 case MONO_TYPE_CLASS:
2227 case MONO_TYPE_STRING:
2228 case MONO_TYPE_OBJECT:
2229 case MONO_TYPE_SZARRAY:
2230 case MONO_TYPE_ARRAY:
2231 if (arg->type != STACK_OBJ)
2233 /* FIXME: check type compatibility */
2237 if (arg->type != STACK_I8)
2241 if (arg->type != cfg->r4_stack_type)
2245 if (arg->type != STACK_R8)
2248 case MONO_TYPE_VALUETYPE:
2249 if (arg->type != STACK_VTYPE)
2251 klass = mono_class_from_mono_type (simple_type);
2252 if (klass != arg->klass)
2255 case MONO_TYPE_TYPEDBYREF:
2256 if (arg->type != STACK_VTYPE)
2258 klass = mono_class_from_mono_type (simple_type);
2259 if (klass != arg->klass)
2262 case MONO_TYPE_GENERICINST:
2263 if (mono_type_generic_inst_is_valuetype (simple_type)) {
2264 if (arg->type != STACK_VTYPE)
2266 klass = mono_class_from_mono_type (simple_type);
2267 /* The second cases is needed when doing partial sharing */
2268 if (klass != arg->klass && mono_class_from_mono_type (target) != arg->klass)
2272 if (arg->type != STACK_OBJ)
2274 /* FIXME: check type compatibility */
2278 case MONO_TYPE_MVAR:
2279 g_assert (cfg->generic_sharing_context);
2280 if (mini_type_var_is_vt (cfg, simple_type)) {
2281 if (arg->type != STACK_VTYPE)
2284 if (arg->type != STACK_OBJ)
2289 g_error ("unknown type 0x%02x in target_type_is_incompatible", simple_type->type);
2295 * Prepare arguments for passing to a function call.
2296 * Return a non-zero value if the arguments can't be passed to the given
2298 * The type checks are not yet complete and some conversions may need
2299 * casts on 32 or 64 bit architectures.
2301 * FIXME: implement this using target_type_is_incompatible ()
2304 check_call_signature (MonoCompile *cfg, MonoMethodSignature *sig, MonoInst **args)
2306 MonoType *simple_type;
2310 if (args [0]->type != STACK_OBJ && args [0]->type != STACK_MP && args [0]->type != STACK_PTR)
2314 for (i = 0; i < sig->param_count; ++i) {
2315 if (sig->params [i]->byref) {
2316 if (args [i]->type != STACK_MP && args [i]->type != STACK_PTR)
2320 simple_type = mini_get_underlying_type (cfg, sig->params [i]);
2322 switch (simple_type->type) {
2323 case MONO_TYPE_VOID:
2332 if (args [i]->type != STACK_I4 && args [i]->type != STACK_PTR)
2338 case MONO_TYPE_FNPTR:
2339 if (args [i]->type != STACK_I4 && args [i]->type != STACK_PTR && args [i]->type != STACK_MP && args [i]->type != STACK_OBJ)
2342 case MONO_TYPE_CLASS:
2343 case MONO_TYPE_STRING:
2344 case MONO_TYPE_OBJECT:
2345 case MONO_TYPE_SZARRAY:
2346 case MONO_TYPE_ARRAY:
2347 if (args [i]->type != STACK_OBJ)
2352 if (args [i]->type != STACK_I8)
2356 if (args [i]->type != cfg->r4_stack_type)
2360 if (args [i]->type != STACK_R8)
2363 case MONO_TYPE_VALUETYPE:
2364 if (simple_type->data.klass->enumtype) {
2365 simple_type = mono_class_enum_basetype (simple_type->data.klass);
2368 if (args [i]->type != STACK_VTYPE)
2371 case MONO_TYPE_TYPEDBYREF:
2372 if (args [i]->type != STACK_VTYPE)
2375 case MONO_TYPE_GENERICINST:
2376 simple_type = &simple_type->data.generic_class->container_class->byval_arg;
2379 case MONO_TYPE_MVAR:
2381 if (args [i]->type != STACK_VTYPE)
2385 g_error ("unknown type 0x%02x in check_call_signature",
2393 callvirt_to_call (int opcode)
2396 case OP_CALL_MEMBASE:
2398 case OP_VOIDCALL_MEMBASE:
2400 case OP_FCALL_MEMBASE:
2402 case OP_RCALL_MEMBASE:
2404 case OP_VCALL_MEMBASE:
2406 case OP_LCALL_MEMBASE:
2409 g_assert_not_reached ();
2415 /* Either METHOD or IMT_ARG needs to be set */
2417 emit_imt_argument (MonoCompile *cfg, MonoCallInst *call, MonoMethod *method, MonoInst *imt_arg)
2421 if (COMPILE_LLVM (cfg)) {
2422 method_reg = alloc_preg (cfg);
2425 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, method_reg, imt_arg->dreg);
2426 } else if (cfg->compile_aot) {
2427 MONO_EMIT_NEW_AOTCONST (cfg, method_reg, method, MONO_PATCH_INFO_METHODCONST);
2430 MONO_INST_NEW (cfg, ins, OP_PCONST);
2431 ins->inst_p0 = method;
2432 ins->dreg = method_reg;
2433 MONO_ADD_INS (cfg->cbb, ins);
2437 call->imt_arg_reg = method_reg;
2439 mono_call_inst_add_outarg_reg (cfg, call, method_reg, MONO_ARCH_IMT_REG, FALSE);
2443 method_reg = alloc_preg (cfg);
2446 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, method_reg, imt_arg->dreg);
2447 } else if (cfg->compile_aot) {
2448 MONO_EMIT_NEW_AOTCONST (cfg, method_reg, method, MONO_PATCH_INFO_METHODCONST);
2451 MONO_INST_NEW (cfg, ins, OP_PCONST);
2452 ins->inst_p0 = method;
2453 ins->dreg = method_reg;
2454 MONO_ADD_INS (cfg->cbb, ins);
2457 mono_call_inst_add_outarg_reg (cfg, call, method_reg, MONO_ARCH_IMT_REG, FALSE);
2460 static MonoJumpInfo *
2461 mono_patch_info_new (MonoMemPool *mp, int ip, MonoJumpInfoType type, gconstpointer target)
2463 MonoJumpInfo *ji = mono_mempool_alloc (mp, sizeof (MonoJumpInfo));
2467 ji->data.target = target;
2473 mini_class_check_context_used (MonoCompile *cfg, MonoClass *klass)
2475 if (cfg->generic_sharing_context)
2476 return mono_class_check_context_used (klass);
2482 mini_method_check_context_used (MonoCompile *cfg, MonoMethod *method)
2484 if (cfg->generic_sharing_context)
2485 return mono_method_check_context_used (method);
2491 * check_method_sharing:
2493 * Check whenever the vtable or an mrgctx needs to be passed when calling CMETHOD.
2496 check_method_sharing (MonoCompile *cfg, MonoMethod *cmethod, gboolean *out_pass_vtable, gboolean *out_pass_mrgctx)
2498 gboolean pass_vtable = FALSE;
2499 gboolean pass_mrgctx = FALSE;
2501 if (((cmethod->flags & METHOD_ATTRIBUTE_STATIC) || cmethod->klass->valuetype) &&
2502 (cmethod->klass->generic_class || cmethod->klass->generic_container)) {
2503 gboolean sharable = FALSE;
2505 if (mono_method_is_generic_sharable (cmethod, TRUE)) {
2508 gboolean sharing_enabled = mono_class_generic_sharing_enabled (cmethod->klass);
2509 MonoGenericContext *context = mini_class_get_context (cmethod->klass);
2510 gboolean context_sharable = mono_generic_context_is_sharable (context, TRUE);
2512 sharable = sharing_enabled && context_sharable;
2516 * Pass vtable iff target method might
2517 * be shared, which means that sharing
2518 * is enabled for its class and its
2519 * context is sharable (and it's not a
2522 if (sharable && !(mini_method_get_context (cmethod) && mini_method_get_context (cmethod)->method_inst))
2526 if (mini_method_get_context (cmethod) &&
2527 mini_method_get_context (cmethod)->method_inst) {
2528 g_assert (!pass_vtable);
2530 if (mono_method_is_generic_sharable (cmethod, TRUE)) {
2533 gboolean sharing_enabled = mono_class_generic_sharing_enabled (cmethod->klass);
2534 MonoGenericContext *context = mini_method_get_context (cmethod);
2535 gboolean context_sharable = mono_generic_context_is_sharable (context, TRUE);
2537 if (sharing_enabled && context_sharable)
2539 if (cfg->gsharedvt && mini_is_gsharedvt_signature (cfg, mono_method_signature (cmethod)))
2544 if (out_pass_vtable)
2545 *out_pass_vtable = pass_vtable;
2546 if (out_pass_mrgctx)
2547 *out_pass_mrgctx = pass_mrgctx;
2550 inline static MonoCallInst *
2551 mono_emit_call_args (MonoCompile *cfg, MonoMethodSignature *sig,
2552 MonoInst **args, int calli, int virtual, int tail, int rgctx, int unbox_trampoline)
2556 #ifdef MONO_ARCH_SOFT_FLOAT_FALLBACK
2561 emit_instrumentation_call (cfg, mono_profiler_method_leave);
2563 MONO_INST_NEW_CALL (cfg, call, OP_TAILCALL);
2565 MONO_INST_NEW_CALL (cfg, call, ret_type_to_call_opcode (cfg, sig->ret, calli, virtual, cfg->generic_sharing_context));
2568 call->signature = sig;
2569 call->rgctx_reg = rgctx;
2570 sig_ret = mini_get_underlying_type (cfg, sig->ret);
2572 type_to_eval_stack_type ((cfg), sig_ret, &call->inst);
2575 if (mini_type_is_vtype (cfg, sig_ret)) {
2576 call->vret_var = cfg->vret_addr;
2577 //g_assert_not_reached ();
2579 } else if (mini_type_is_vtype (cfg, sig_ret)) {
2580 MonoInst *temp = mono_compile_create_var (cfg, sig_ret, OP_LOCAL);
2583 temp->backend.is_pinvoke = sig->pinvoke;
2586 * We use a new opcode OP_OUTARG_VTRETADDR instead of LDADDR for emitting the
2587 * address of return value to increase optimization opportunities.
2588 * Before vtype decomposition, the dreg of the call ins itself represents the
2589 * fact the call modifies the return value. After decomposition, the call will
2590 * be transformed into one of the OP_VOIDCALL opcodes, and the VTRETADDR opcode
2591 * will be transformed into an LDADDR.
2593 MONO_INST_NEW (cfg, loada, OP_OUTARG_VTRETADDR);
2594 loada->dreg = alloc_preg (cfg);
2595 loada->inst_p0 = temp;
2596 /* We reference the call too since call->dreg could change during optimization */
2597 loada->inst_p1 = call;
2598 MONO_ADD_INS (cfg->cbb, loada);
2600 call->inst.dreg = temp->dreg;
2602 call->vret_var = loada;
2603 } else if (!MONO_TYPE_IS_VOID (sig_ret))
2604 call->inst.dreg = alloc_dreg (cfg, call->inst.type);
2606 #ifdef MONO_ARCH_SOFT_FLOAT_FALLBACK
2607 if (COMPILE_SOFT_FLOAT (cfg)) {
2609 * If the call has a float argument, we would need to do an r8->r4 conversion using
2610 * an icall, but that cannot be done during the call sequence since it would clobber
2611 * the call registers + the stack. So we do it before emitting the call.
2613 for (i = 0; i < sig->param_count + sig->hasthis; ++i) {
2615 MonoInst *in = call->args [i];
2617 if (i >= sig->hasthis)
2618 t = sig->params [i - sig->hasthis];
2620 t = &mono_defaults.int_class->byval_arg;
2621 t = mono_type_get_underlying_type (t);
2623 if (!t->byref && t->type == MONO_TYPE_R4) {
2624 MonoInst *iargs [1];
2628 conv = mono_emit_jit_icall (cfg, mono_fload_r4_arg, iargs);
2630 /* The result will be in an int vreg */
2631 call->args [i] = conv;
2637 call->need_unbox_trampoline = unbox_trampoline;
2640 if (COMPILE_LLVM (cfg))
2641 mono_llvm_emit_call (cfg, call);
2643 mono_arch_emit_call (cfg, call);
2645 mono_arch_emit_call (cfg, call);
2648 cfg->param_area = MAX (cfg->param_area, call->stack_usage);
2649 cfg->flags |= MONO_CFG_HAS_CALLS;
2655 set_rgctx_arg (MonoCompile *cfg, MonoCallInst *call, int rgctx_reg, MonoInst *rgctx_arg)
2657 #ifdef MONO_ARCH_RGCTX_REG
2658 mono_call_inst_add_outarg_reg (cfg, call, rgctx_reg, MONO_ARCH_RGCTX_REG, FALSE);
2659 cfg->uses_rgctx_reg = TRUE;
2660 call->rgctx_reg = TRUE;
2662 call->rgctx_arg_reg = rgctx_reg;
2669 inline static MonoInst*
2670 mono_emit_calli (MonoCompile *cfg, MonoMethodSignature *sig, MonoInst **args, MonoInst *addr, MonoInst *imt_arg, MonoInst *rgctx_arg)
2675 gboolean check_sp = FALSE;
2677 if (cfg->check_pinvoke_callconv && cfg->method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE) {
2678 WrapperInfo *info = mono_marshal_get_wrapper_info (cfg->method);
2680 if (info && info->subtype == WRAPPER_SUBTYPE_PINVOKE)
2685 rgctx_reg = mono_alloc_preg (cfg);
2686 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, rgctx_reg, rgctx_arg->dreg);
2690 if (!cfg->stack_inbalance_var)
2691 cfg->stack_inbalance_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
2693 MONO_INST_NEW (cfg, ins, OP_GET_SP);
2694 ins->dreg = cfg->stack_inbalance_var->dreg;
2695 MONO_ADD_INS (cfg->cbb, ins);
2698 call = mono_emit_call_args (cfg, sig, args, TRUE, FALSE, FALSE, rgctx_arg ? TRUE : FALSE, FALSE);
2700 call->inst.sreg1 = addr->dreg;
2703 emit_imt_argument (cfg, call, NULL, imt_arg);
2705 MONO_ADD_INS (cfg->cbb, (MonoInst*)call);
2710 sp_reg = mono_alloc_preg (cfg);
2712 MONO_INST_NEW (cfg, ins, OP_GET_SP);
2714 MONO_ADD_INS (cfg->cbb, ins);
2716 /* Restore the stack so we don't crash when throwing the exception */
2717 MONO_INST_NEW (cfg, ins, OP_SET_SP);
2718 ins->sreg1 = cfg->stack_inbalance_var->dreg;
2719 MONO_ADD_INS (cfg->cbb, ins);
2721 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, cfg->stack_inbalance_var->dreg, sp_reg);
2722 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "ExecutionEngineException");
2726 set_rgctx_arg (cfg, call, rgctx_reg, rgctx_arg);
2728 return (MonoInst*)call;
2732 emit_get_gsharedvt_info_klass (MonoCompile *cfg, MonoClass *klass, MonoRgctxInfoType rgctx_type);
2735 emit_get_rgctx_method (MonoCompile *cfg, int context_used, MonoMethod *cmethod, MonoRgctxInfoType rgctx_type);
2737 emit_get_rgctx_klass (MonoCompile *cfg, int context_used, MonoClass *klass, MonoRgctxInfoType rgctx_type);
2740 mono_emit_method_call_full (MonoCompile *cfg, MonoMethod *method, MonoMethodSignature *sig, gboolean tail,
2741 MonoInst **args, MonoInst *this, MonoInst *imt_arg, MonoInst *rgctx_arg)
2743 #ifndef DISABLE_REMOTING
2744 gboolean might_be_remote = FALSE;
2746 gboolean virtual = this != NULL;
2747 gboolean enable_for_aot = TRUE;
2751 gboolean need_unbox_trampoline;
2754 sig = mono_method_signature (method);
2757 rgctx_reg = mono_alloc_preg (cfg);
2758 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, rgctx_reg, rgctx_arg->dreg);
2761 if (method->string_ctor) {
2762 /* Create the real signature */
2763 /* FIXME: Cache these */
2764 MonoMethodSignature *ctor_sig = mono_metadata_signature_dup_mempool (cfg->mempool, sig);
2765 ctor_sig->ret = &mono_defaults.string_class->byval_arg;
2770 context_used = mini_method_check_context_used (cfg, method);
2772 #ifndef DISABLE_REMOTING
2773 might_be_remote = this && sig->hasthis &&
2774 (mono_class_is_marshalbyref (method->klass) || method->klass == mono_defaults.object_class) &&
2775 !(method->flags & METHOD_ATTRIBUTE_VIRTUAL) && (!MONO_CHECK_THIS (this) || context_used);
2777 if (might_be_remote && context_used) {
2780 g_assert (cfg->generic_sharing_context);
2782 addr = emit_get_rgctx_method (cfg, context_used, method, MONO_RGCTX_INFO_REMOTING_INVOKE_WITH_CHECK);
2784 return mono_emit_calli (cfg, sig, args, addr, NULL, NULL);
2788 need_unbox_trampoline = method->klass == mono_defaults.object_class || (method->klass->flags & TYPE_ATTRIBUTE_INTERFACE);
2790 call = mono_emit_call_args (cfg, sig, args, FALSE, virtual, tail, rgctx_arg ? TRUE : FALSE, need_unbox_trampoline);
2792 #ifndef DISABLE_REMOTING
2793 if (might_be_remote)
2794 call->method = mono_marshal_get_remoting_invoke_with_check (method);
2797 call->method = method;
2798 call->inst.flags |= MONO_INST_HAS_METHOD;
2799 call->inst.inst_left = this;
2800 call->tail_call = tail;
2803 int vtable_reg, slot_reg, this_reg;
2806 this_reg = this->dreg;
2808 if ((method->klass->parent == mono_defaults.multicastdelegate_class) && !strcmp (method->name, "Invoke")) {
2809 MonoInst *dummy_use;
2811 MONO_EMIT_NULL_CHECK (cfg, this_reg);
2813 /* Make a call to delegate->invoke_impl */
2814 call->inst.inst_basereg = this_reg;
2815 call->inst.inst_offset = MONO_STRUCT_OFFSET (MonoDelegate, invoke_impl);
2816 MONO_ADD_INS (cfg->cbb, (MonoInst*)call);
2818 /* We must emit a dummy use here because the delegate trampoline will
2819 replace the 'this' argument with the delegate target making this activation
2820 no longer a root for the delegate.
2821 This is an issue for delegates that target collectible code such as dynamic
2822 methods of GC'able assemblies.
2824 For a test case look into #667921.
2826 FIXME: a dummy use is not the best way to do it as the local register allocator
2827 will put it on a caller save register and spil it around the call.
2828 Ideally, we would either put it on a callee save register or only do the store part.
2830 EMIT_NEW_DUMMY_USE (cfg, dummy_use, args [0]);
2832 return (MonoInst*)call;
2835 if ((!cfg->compile_aot || enable_for_aot) &&
2836 (!(method->flags & METHOD_ATTRIBUTE_VIRTUAL) ||
2837 (MONO_METHOD_IS_FINAL (method) &&
2838 method->wrapper_type != MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK)) &&
2839 !(mono_class_is_marshalbyref (method->klass) && context_used)) {
2841 * the method is not virtual, we just need to ensure this is not null
2842 * and then we can call the method directly.
2844 #ifndef DISABLE_REMOTING
2845 if (mono_class_is_marshalbyref (method->klass) || method->klass == mono_defaults.object_class) {
2847 * The check above ensures method is not gshared, this is needed since
2848 * gshared methods can't have wrappers.
2850 method = call->method = mono_marshal_get_remoting_invoke_with_check (method);
2854 if (!method->string_ctor)
2855 MONO_EMIT_NEW_CHECK_THIS (cfg, this_reg);
2857 call->inst.opcode = callvirt_to_call (call->inst.opcode);
2858 } else if ((method->flags & METHOD_ATTRIBUTE_VIRTUAL) && MONO_METHOD_IS_FINAL (method)) {
2860 * the method is virtual, but we can statically dispatch since either
2861 * it's class or the method itself are sealed.
2862 * But first we need to ensure it's not a null reference.
2864 MONO_EMIT_NEW_CHECK_THIS (cfg, this_reg);
2866 call->inst.opcode = callvirt_to_call (call->inst.opcode);
2868 vtable_reg = alloc_preg (cfg);
2869 MONO_EMIT_NEW_LOAD_MEMBASE_FAULT (cfg, vtable_reg, this_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
2870 if (method->klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
2871 guint32 imt_slot = mono_method_get_imt_slot (method);
2872 emit_imt_argument (cfg, call, call->method, imt_arg);
2873 slot_reg = vtable_reg;
2874 offset = ((gint32)imt_slot - MONO_IMT_SIZE) * SIZEOF_VOID_P;
2876 slot_reg = vtable_reg;
2877 offset = MONO_STRUCT_OFFSET (MonoVTable, vtable) +
2878 ((mono_method_get_vtable_index (method)) * (SIZEOF_VOID_P));
2880 g_assert (mono_method_signature (method)->generic_param_count);
2881 emit_imt_argument (cfg, call, call->method, imt_arg);
2885 call->inst.sreg1 = slot_reg;
2886 call->inst.inst_offset = offset;
2887 call->virtual = TRUE;
2891 MONO_ADD_INS (cfg->cbb, (MonoInst*)call);
2894 set_rgctx_arg (cfg, call, rgctx_reg, rgctx_arg);
2896 return (MonoInst*)call;
2900 mono_emit_method_call (MonoCompile *cfg, MonoMethod *method, MonoInst **args, MonoInst *this)
2902 return mono_emit_method_call_full (cfg, method, mono_method_signature (method), FALSE, args, this, NULL, NULL);
2906 mono_emit_native_call (MonoCompile *cfg, gconstpointer func, MonoMethodSignature *sig,
2913 call = mono_emit_call_args (cfg, sig, args, FALSE, FALSE, FALSE, FALSE, FALSE);
2916 MONO_ADD_INS (cfg->cbb, (MonoInst*)call);
2918 return (MonoInst*)call;
2922 mono_emit_jit_icall (MonoCompile *cfg, gconstpointer func, MonoInst **args)
2924 MonoJitICallInfo *info = mono_find_jit_icall_by_addr (func);
2928 return mono_emit_native_call (cfg, mono_icall_get_wrapper (info), info->sig, args);
2932 * mono_emit_abs_call:
2934 * Emit a call to the runtime function described by PATCH_TYPE and DATA.
2936 inline static MonoInst*
2937 mono_emit_abs_call (MonoCompile *cfg, MonoJumpInfoType patch_type, gconstpointer data,
2938 MonoMethodSignature *sig, MonoInst **args)
2940 MonoJumpInfo *ji = mono_patch_info_new (cfg->mempool, 0, patch_type, data);
2944 * We pass ji as the call address, the PATCH_INFO_ABS resolving code will
2947 if (cfg->abs_patches == NULL)
2948 cfg->abs_patches = g_hash_table_new (NULL, NULL);
2949 g_hash_table_insert (cfg->abs_patches, ji, ji);
2950 ins = mono_emit_native_call (cfg, ji, sig, args);
2951 ((MonoCallInst*)ins)->fptr_is_patch = TRUE;
2956 direct_icalls_enabled (MonoCompile *cfg)
2958 /* LLVM on amd64 can't handle calls to non-32 bit addresses */
2960 if (cfg->compile_llvm)
2963 if (cfg->gen_sdb_seq_points || cfg->disable_direct_icalls)
2969 mono_emit_jit_icall_by_info (MonoCompile *cfg, MonoJitICallInfo *info, MonoInst **args, MonoBasicBlock **out_cbb)
2972 * Call the jit icall without a wrapper if possible.
2973 * The wrapper is needed for the following reasons:
2974 * - to handle exceptions thrown using mono_raise_exceptions () from the
2975 * icall function. The EH code needs the lmf frame pushed by the
2976 * wrapper to be able to unwind back to managed code.
2977 * - to be able to do stack walks for asynchronously suspended
2978 * threads when debugging.
2980 if (info->no_raise && direct_icalls_enabled (cfg)) {
2984 if (!info->wrapper_method) {
2985 name = g_strdup_printf ("__icall_wrapper_%s", info->name);
2986 info->wrapper_method = mono_marshal_get_icall_wrapper (info->sig, name, info->func, TRUE);
2988 mono_memory_barrier ();
2992 * Inline the wrapper method, which is basically a call to the C icall, and
2993 * an exception check.
2995 costs = inline_method (cfg, info->wrapper_method, NULL,
2996 args, NULL, cfg->real_offset, TRUE, out_cbb);
2997 g_assert (costs > 0);
2998 g_assert (!MONO_TYPE_IS_VOID (info->sig->ret));
3002 return mono_emit_native_call (cfg, mono_icall_get_wrapper (info), info->sig, args);
3007 mono_emit_widen_call_res (MonoCompile *cfg, MonoInst *ins, MonoMethodSignature *fsig)
3009 if (!MONO_TYPE_IS_VOID (fsig->ret)) {
3010 if ((fsig->pinvoke || LLVM_ENABLED) && !fsig->ret->byref) {
3014 * Native code might return non register sized integers
3015 * without initializing the upper bits.
3017 switch (mono_type_to_load_membase (cfg, fsig->ret)) {
3018 case OP_LOADI1_MEMBASE:
3019 widen_op = OP_ICONV_TO_I1;
3021 case OP_LOADU1_MEMBASE:
3022 widen_op = OP_ICONV_TO_U1;
3024 case OP_LOADI2_MEMBASE:
3025 widen_op = OP_ICONV_TO_I2;
3027 case OP_LOADU2_MEMBASE:
3028 widen_op = OP_ICONV_TO_U2;
3034 if (widen_op != -1) {
3035 int dreg = alloc_preg (cfg);
3038 EMIT_NEW_UNALU (cfg, widen, widen_op, dreg, ins->dreg);
3039 widen->type = ins->type;
3049 get_memcpy_method (void)
3051 static MonoMethod *memcpy_method = NULL;
3052 if (!memcpy_method) {
3053 memcpy_method = mono_class_get_method_from_name (mono_defaults.string_class, "memcpy", 3);
3055 g_error ("Old corlib found. Install a new one");
3057 return memcpy_method;
3061 create_write_barrier_bitmap (MonoCompile *cfg, MonoClass *klass, unsigned *wb_bitmap, int offset)
3063 MonoClassField *field;
3064 gpointer iter = NULL;
3066 while ((field = mono_class_get_fields (klass, &iter))) {
3069 if (field->type->attrs & FIELD_ATTRIBUTE_STATIC)
3071 foffset = klass->valuetype ? field->offset - sizeof (MonoObject): field->offset;
3072 if (mini_type_is_reference (cfg, mono_field_get_type (field))) {
3073 g_assert ((foffset % SIZEOF_VOID_P) == 0);
3074 *wb_bitmap |= 1 << ((offset + foffset) / SIZEOF_VOID_P);
3076 MonoClass *field_class = mono_class_from_mono_type (field->type);
3077 if (field_class->has_references)
3078 create_write_barrier_bitmap (cfg, field_class, wb_bitmap, offset + foffset);
3084 emit_write_barrier (MonoCompile *cfg, MonoInst *ptr, MonoInst *value)
3086 int card_table_shift_bits;
3087 gpointer card_table_mask;
3089 MonoInst *dummy_use;
3090 int nursery_shift_bits;
3091 size_t nursery_size;
3092 gboolean has_card_table_wb = FALSE;
3094 if (!cfg->gen_write_barriers)
3097 card_table = mono_gc_get_card_table (&card_table_shift_bits, &card_table_mask);
3099 mono_gc_get_nursery (&nursery_shift_bits, &nursery_size);
3101 #ifdef MONO_ARCH_HAVE_CARD_TABLE_WBARRIER
3102 has_card_table_wb = TRUE;
3105 if (has_card_table_wb && !cfg->compile_aot && card_table && nursery_shift_bits > 0 && !COMPILE_LLVM (cfg)) {
3108 MONO_INST_NEW (cfg, wbarrier, OP_CARD_TABLE_WBARRIER);
3109 wbarrier->sreg1 = ptr->dreg;
3110 wbarrier->sreg2 = value->dreg;
3111 MONO_ADD_INS (cfg->cbb, wbarrier);
3112 } else if (card_table && !cfg->compile_aot && !mono_gc_card_table_nursery_check ()) {
3113 int offset_reg = alloc_preg (cfg);
3114 int card_reg = alloc_preg (cfg);
3117 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SHR_UN_IMM, offset_reg, ptr->dreg, card_table_shift_bits);
3118 if (card_table_mask)
3119 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_PAND_IMM, offset_reg, offset_reg, card_table_mask);
3121 /*We can't use PADD_IMM since the cardtable might end up in high addresses and amd64 doesn't support
3122 * IMM's larger than 32bits.
3124 if (cfg->compile_aot) {
3125 MONO_EMIT_NEW_AOTCONST (cfg, card_reg, NULL, MONO_PATCH_INFO_GC_CARD_TABLE_ADDR);
3127 MONO_INST_NEW (cfg, ins, OP_PCONST);
3128 ins->inst_p0 = card_table;
3129 ins->dreg = card_reg;
3130 MONO_ADD_INS (cfg->cbb, ins);
3133 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, offset_reg, offset_reg, card_reg);
3134 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREI1_MEMBASE_IMM, offset_reg, 0, 1);
3136 MonoMethod *write_barrier = mono_gc_get_write_barrier ();
3137 mono_emit_method_call (cfg, write_barrier, &ptr, NULL);
3140 EMIT_NEW_DUMMY_USE (cfg, dummy_use, value);
3144 mono_emit_wb_aware_memcpy (MonoCompile *cfg, MonoClass *klass, MonoInst *iargs[4], int size, int align)
3146 int dest_ptr_reg, tmp_reg, destreg, srcreg, offset;
3147 unsigned need_wb = 0;
3152 /*types with references can't have alignment smaller than sizeof(void*) */
3153 if (align < SIZEOF_VOID_P)
3156 /*This value cannot be bigger than 32 due to the way we calculate the required wb bitmap.*/
3157 if (size > 32 * SIZEOF_VOID_P)
3160 create_write_barrier_bitmap (cfg, klass, &need_wb, 0);
3162 /* We don't unroll more than 5 stores to avoid code bloat. */
3163 if (size > 5 * SIZEOF_VOID_P) {
3164 /*This is harmless and simplify mono_gc_wbarrier_value_copy_bitmap */
3165 size += (SIZEOF_VOID_P - 1);
3166 size &= ~(SIZEOF_VOID_P - 1);
3168 EMIT_NEW_ICONST (cfg, iargs [2], size);
3169 EMIT_NEW_ICONST (cfg, iargs [3], need_wb);
3170 mono_emit_jit_icall (cfg, mono_gc_wbarrier_value_copy_bitmap, iargs);
3174 destreg = iargs [0]->dreg;
3175 srcreg = iargs [1]->dreg;
3178 dest_ptr_reg = alloc_preg (cfg);
3179 tmp_reg = alloc_preg (cfg);
3182 EMIT_NEW_UNALU (cfg, iargs [0], OP_MOVE, dest_ptr_reg, destreg);
3184 while (size >= SIZEOF_VOID_P) {
3185 MonoInst *load_inst;
3186 MONO_INST_NEW (cfg, load_inst, OP_LOAD_MEMBASE);
3187 load_inst->dreg = tmp_reg;
3188 load_inst->inst_basereg = srcreg;
3189 load_inst->inst_offset = offset;
3190 MONO_ADD_INS (cfg->cbb, load_inst);
3192 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREP_MEMBASE_REG, dest_ptr_reg, 0, tmp_reg);
3195 emit_write_barrier (cfg, iargs [0], load_inst);
3197 offset += SIZEOF_VOID_P;
3198 size -= SIZEOF_VOID_P;
3201 /*tmp += sizeof (void*)*/
3202 if (size >= SIZEOF_VOID_P) {
3203 NEW_BIALU_IMM (cfg, iargs [0], OP_PADD_IMM, dest_ptr_reg, dest_ptr_reg, SIZEOF_VOID_P);
3204 MONO_ADD_INS (cfg->cbb, iargs [0]);
3208 /* Those cannot be references since size < sizeof (void*) */
3210 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI4_MEMBASE, tmp_reg, srcreg, offset);
3211 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI4_MEMBASE_REG, destreg, offset, tmp_reg);
3217 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI2_MEMBASE, tmp_reg, srcreg, offset);
3218 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI2_MEMBASE_REG, destreg, offset, tmp_reg);
3224 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI1_MEMBASE, tmp_reg, srcreg, offset);
3225 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI1_MEMBASE_REG, destreg, offset, tmp_reg);
3234 * Emit code to copy a valuetype of type @klass whose address is stored in
3235 * @src->dreg to memory whose address is stored at @dest->dreg.
3238 mini_emit_stobj (MonoCompile *cfg, MonoInst *dest, MonoInst *src, MonoClass *klass, gboolean native)
3240 MonoInst *iargs [4];
3243 MonoMethod *memcpy_method;
3244 MonoInst *size_ins = NULL;
3245 MonoInst *memcpy_ins = NULL;
3248 if (cfg->generic_sharing_context)
3249 klass = mono_class_from_mono_type (mini_get_underlying_type (cfg, &klass->byval_arg));
3252 * This check breaks with spilled vars... need to handle it during verification anyway.
3253 * g_assert (klass && klass == src->klass && klass == dest->klass);
3256 if (mini_is_gsharedvt_klass (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];
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 */
3344 mono_class_init (klass);
3345 if (mini_is_gsharedvt_klass (cfg, klass)) {
3346 size_ins = emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_VALUE_SIZE);
3347 bzero_ins = emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_BZERO);
3349 bzero_method = mono_class_get_method_from_name (mono_defaults.string_class, "bzero_aligned_1", 2);
3350 g_assert (bzero_method);
3352 iargs [1] = size_ins;
3353 mono_emit_calli (cfg, mono_method_signature (bzero_method), iargs, bzero_ins, NULL, NULL);
3357 n = mono_class_value_size (klass, &align);
3359 if (n <= sizeof (gpointer) * 8) {
3360 mini_emit_memset (cfg, dest->dreg, 0, n, 0, align);
3363 memset_method = get_memset_method ();
3365 EMIT_NEW_ICONST (cfg, iargs [1], 0);
3366 EMIT_NEW_ICONST (cfg, iargs [2], n);
3367 mono_emit_method_call (cfg, memset_method, iargs, NULL);
3372 emit_get_rgctx (MonoCompile *cfg, MonoMethod *method, int context_used)
3374 MonoInst *this = NULL;
3376 g_assert (cfg->generic_sharing_context);
3378 if (!(method->flags & METHOD_ATTRIBUTE_STATIC) &&
3379 !(context_used & MONO_GENERIC_CONTEXT_USED_METHOD) &&
3380 !method->klass->valuetype)
3381 EMIT_NEW_ARGLOAD (cfg, this, 0);
3383 if (context_used & MONO_GENERIC_CONTEXT_USED_METHOD) {
3384 MonoInst *mrgctx_loc, *mrgctx_var;
3387 g_assert (method->is_inflated && mono_method_get_context (method)->method_inst);
3389 mrgctx_loc = mono_get_vtable_var (cfg);
3390 EMIT_NEW_TEMPLOAD (cfg, mrgctx_var, mrgctx_loc->inst_c0);
3393 } else if (method->flags & METHOD_ATTRIBUTE_STATIC || method->klass->valuetype) {
3394 MonoInst *vtable_loc, *vtable_var;
3398 vtable_loc = mono_get_vtable_var (cfg);
3399 EMIT_NEW_TEMPLOAD (cfg, vtable_var, vtable_loc->inst_c0);
3401 if (method->is_inflated && mono_method_get_context (method)->method_inst) {
3402 MonoInst *mrgctx_var = vtable_var;
3405 vtable_reg = alloc_preg (cfg);
3406 EMIT_NEW_LOAD_MEMBASE (cfg, vtable_var, OP_LOAD_MEMBASE, vtable_reg, mrgctx_var->dreg, MONO_STRUCT_OFFSET (MonoMethodRuntimeGenericContext, class_vtable));
3407 vtable_var->type = STACK_PTR;
3415 vtable_reg = alloc_preg (cfg);
3416 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, vtable_reg, this->dreg, MONO_STRUCT_OFFSET (MonoObject, vtable));
3421 static MonoJumpInfoRgctxEntry *
3422 mono_patch_info_rgctx_entry_new (MonoMemPool *mp, MonoMethod *method, gboolean in_mrgctx, MonoJumpInfoType patch_type, gconstpointer patch_data, MonoRgctxInfoType info_type)
3424 MonoJumpInfoRgctxEntry *res = mono_mempool_alloc0 (mp, sizeof (MonoJumpInfoRgctxEntry));
3425 res->method = method;
3426 res->in_mrgctx = in_mrgctx;
3427 res->data = mono_mempool_alloc0 (mp, sizeof (MonoJumpInfo));
3428 res->data->type = patch_type;
3429 res->data->data.target = patch_data;
3430 res->info_type = info_type;
3435 static inline MonoInst*
3436 emit_rgctx_fetch (MonoCompile *cfg, MonoInst *rgctx, MonoJumpInfoRgctxEntry *entry)
3438 return mono_emit_abs_call (cfg, MONO_PATCH_INFO_RGCTX_FETCH, entry, helper_sig_rgctx_lazy_fetch_trampoline, &rgctx);
3442 emit_get_rgctx_klass (MonoCompile *cfg, int context_used,
3443 MonoClass *klass, MonoRgctxInfoType rgctx_type)
3445 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);
3446 MonoInst *rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
3448 return emit_rgctx_fetch (cfg, rgctx, entry);
3452 emit_get_rgctx_sig (MonoCompile *cfg, int context_used,
3453 MonoMethodSignature *sig, MonoRgctxInfoType rgctx_type)
3455 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);
3456 MonoInst *rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
3458 return emit_rgctx_fetch (cfg, rgctx, entry);
3462 emit_get_rgctx_gsharedvt_call (MonoCompile *cfg, int context_used,
3463 MonoMethodSignature *sig, MonoMethod *cmethod, MonoRgctxInfoType rgctx_type)
3465 MonoJumpInfoGSharedVtCall *call_info;
3466 MonoJumpInfoRgctxEntry *entry;
3469 call_info = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoJumpInfoGSharedVtCall));
3470 call_info->sig = sig;
3471 call_info->method = cmethod;
3473 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);
3474 rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
3476 return emit_rgctx_fetch (cfg, rgctx, entry);
3480 * emit_get_rgctx_virt_method:
3482 * Return data for method VIRT_METHOD for a receiver of type KLASS.
3485 emit_get_rgctx_virt_method (MonoCompile *cfg, int context_used,
3486 MonoClass *klass, MonoMethod *virt_method, MonoRgctxInfoType rgctx_type)
3488 MonoJumpInfoVirtMethod *info;
3489 MonoJumpInfoRgctxEntry *entry;
3492 info = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoJumpInfoVirtMethod));
3493 info->klass = klass;
3494 info->method = virt_method;
3496 entry = mono_patch_info_rgctx_entry_new (cfg->mempool, cfg->current_method, context_used & MONO_GENERIC_CONTEXT_USED_METHOD, MONO_PATCH_INFO_VIRT_METHOD, info, rgctx_type);
3497 rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
3499 return emit_rgctx_fetch (cfg, rgctx, entry);
3503 emit_get_rgctx_gsharedvt_method (MonoCompile *cfg, int context_used,
3504 MonoMethod *cmethod, MonoGSharedVtMethodInfo *info)
3506 MonoJumpInfoRgctxEntry *entry;
3509 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);
3510 rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
3512 return emit_rgctx_fetch (cfg, rgctx, entry);
3516 * emit_get_rgctx_method:
3518 * Emit IR to load the property RGCTX_TYPE of CMETHOD. If context_used is 0, emit
3519 * normal constants, else emit a load from the rgctx.
3522 emit_get_rgctx_method (MonoCompile *cfg, int context_used,
3523 MonoMethod *cmethod, MonoRgctxInfoType rgctx_type)
3525 if (!context_used) {
3528 switch (rgctx_type) {
3529 case MONO_RGCTX_INFO_METHOD:
3530 EMIT_NEW_METHODCONST (cfg, ins, cmethod);
3532 case MONO_RGCTX_INFO_METHOD_RGCTX:
3533 EMIT_NEW_METHOD_RGCTX_CONST (cfg, ins, cmethod);
3536 g_assert_not_reached ();
3539 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);
3540 MonoInst *rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
3542 return emit_rgctx_fetch (cfg, rgctx, entry);
3547 emit_get_rgctx_field (MonoCompile *cfg, int context_used,
3548 MonoClassField *field, MonoRgctxInfoType rgctx_type)
3550 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);
3551 MonoInst *rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
3553 return emit_rgctx_fetch (cfg, rgctx, entry);
3557 get_gsharedvt_info_slot (MonoCompile *cfg, gpointer data, MonoRgctxInfoType rgctx_type)
3559 MonoGSharedVtMethodInfo *info = cfg->gsharedvt_info;
3560 MonoRuntimeGenericContextInfoTemplate *template;
3565 for (i = 0; i < info->num_entries; ++i) {
3566 MonoRuntimeGenericContextInfoTemplate *otemplate = &info->entries [i];
3568 if (otemplate->info_type == rgctx_type && otemplate->data == data && rgctx_type != MONO_RGCTX_INFO_LOCAL_OFFSET)
3572 if (info->num_entries == info->count_entries) {
3573 MonoRuntimeGenericContextInfoTemplate *new_entries;
3574 int new_count_entries = info->count_entries ? info->count_entries * 2 : 16;
3576 new_entries = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoRuntimeGenericContextInfoTemplate) * new_count_entries);
3578 memcpy (new_entries, info->entries, sizeof (MonoRuntimeGenericContextInfoTemplate) * info->count_entries);
3579 info->entries = new_entries;
3580 info->count_entries = new_count_entries;
3583 idx = info->num_entries;
3584 template = &info->entries [idx];
3585 template->info_type = rgctx_type;
3586 template->data = data;
3588 info->num_entries ++;
3594 * emit_get_gsharedvt_info:
3596 * This is similar to emit_get_rgctx_.., but loads the data from the gsharedvt info var instead of calling an rgctx fetch trampoline.
3599 emit_get_gsharedvt_info (MonoCompile *cfg, gpointer data, MonoRgctxInfoType rgctx_type)
3604 idx = get_gsharedvt_info_slot (cfg, data, rgctx_type);
3605 /* Load info->entries [idx] */
3606 dreg = alloc_preg (cfg);
3607 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, dreg, cfg->gsharedvt_info_var->dreg, MONO_STRUCT_OFFSET (MonoGSharedVtMethodRuntimeInfo, entries) + (idx * sizeof (gpointer)));
3613 emit_get_gsharedvt_info_klass (MonoCompile *cfg, MonoClass *klass, MonoRgctxInfoType rgctx_type)
3615 return emit_get_gsharedvt_info (cfg, &klass->byval_arg, rgctx_type);
3619 * On return the caller must check @klass for load errors.
3622 emit_generic_class_init (MonoCompile *cfg, MonoClass *klass, MonoBasicBlock **out_bblock)
3624 MonoInst *vtable_arg;
3626 gboolean use_op_generic_class_init = FALSE;
3628 *out_bblock = cfg->cbb;
3630 context_used = mini_class_check_context_used (cfg, klass);
3633 vtable_arg = emit_get_rgctx_klass (cfg, context_used,
3634 klass, MONO_RGCTX_INFO_VTABLE);
3636 MonoVTable *vtable = mono_class_vtable (cfg->domain, klass);
3640 EMIT_NEW_VTABLECONST (cfg, vtable_arg, vtable);
3643 #ifdef MONO_ARCH_HAVE_OP_GENERIC_CLASS_INIT
3644 if (!COMPILE_LLVM (cfg))
3645 use_op_generic_class_init = TRUE;
3648 if (use_op_generic_class_init) {
3652 * Using an opcode instead of emitting IR here allows the hiding of the call inside the opcode,
3653 * so this doesn't have to clobber any regs and it doesn't break basic blocks.
3655 MONO_INST_NEW (cfg, ins, OP_GENERIC_CLASS_INIT);
3656 ins->sreg1 = vtable_arg->dreg;
3657 MONO_ADD_INS (cfg->cbb, ins);
3659 static int byte_offset = -1;
3660 static guint8 bitmask;
3661 int bits_reg, inited_reg;
3662 MonoBasicBlock *inited_bb;
3663 MonoInst *args [16];
3665 if (byte_offset < 0)
3666 mono_marshal_find_bitfield_offset (MonoVTable, initialized, &byte_offset, &bitmask);
3668 bits_reg = alloc_ireg (cfg);
3669 inited_reg = alloc_ireg (cfg);
3671 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU1_MEMBASE, bits_reg, vtable_arg->dreg, byte_offset);
3672 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_IAND_IMM, inited_reg, bits_reg, bitmask);
3674 NEW_BBLOCK (cfg, inited_bb);
3676 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, inited_reg, 0);
3677 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBNE_UN, inited_bb);
3679 args [0] = vtable_arg;
3680 mono_emit_jit_icall (cfg, mono_generic_class_init, args);
3682 MONO_START_BB (cfg, inited_bb);
3683 *out_bblock = inited_bb;
3689 emit_class_init (MonoCompile *cfg, MonoClass *klass, MonoBasicBlock **out_bblock)
3691 /* This could be used as a fallback if needed */
3692 //emit_generic_class_init (cfg, klass, out_bblock);
3694 *out_bblock = cfg->cbb;
3696 mono_emit_abs_call (cfg, MONO_PATCH_INFO_CLASS_INIT, klass, helper_sig_class_init_trampoline, NULL);
3700 emit_seq_point (MonoCompile *cfg, MonoMethod *method, guint8* ip, gboolean intr_loc, gboolean nonempty_stack)
3704 if (cfg->gen_seq_points && cfg->method == method) {
3705 NEW_SEQ_POINT (cfg, ins, ip - cfg->header->code, intr_loc);
3707 ins->flags |= MONO_INST_NONEMPTY_STACK;
3708 MONO_ADD_INS (cfg->cbb, ins);
3713 save_cast_details (MonoCompile *cfg, MonoClass *klass, int obj_reg, gboolean null_check, MonoBasicBlock **out_bblock)
3715 if (mini_get_debug_options ()->better_cast_details) {
3716 int vtable_reg = alloc_preg (cfg);
3717 int klass_reg = alloc_preg (cfg);
3718 MonoBasicBlock *is_null_bb = NULL;
3720 int to_klass_reg, context_used;
3723 NEW_BBLOCK (cfg, is_null_bb);
3725 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, obj_reg, 0);
3726 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, is_null_bb);
3729 tls_get = mono_get_jit_tls_intrinsic (cfg);
3731 fprintf (stderr, "error: --debug=casts not supported on this platform.\n.");
3735 MONO_ADD_INS (cfg->cbb, tls_get);
3736 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, vtable_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
3737 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
3739 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, tls_get->dreg, MONO_STRUCT_OFFSET (MonoJitTlsData, class_cast_from), klass_reg);
3741 context_used = mini_class_check_context_used (cfg, klass);
3743 MonoInst *class_ins;
3745 class_ins = emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_KLASS);
3746 to_klass_reg = class_ins->dreg;
3748 to_klass_reg = alloc_preg (cfg);
3749 MONO_EMIT_NEW_CLASSCONST (cfg, to_klass_reg, klass);
3751 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, tls_get->dreg, MONO_STRUCT_OFFSET (MonoJitTlsData, class_cast_to), to_klass_reg);
3754 MONO_START_BB (cfg, is_null_bb);
3756 *out_bblock = cfg->cbb;
3762 reset_cast_details (MonoCompile *cfg)
3764 /* Reset the variables holding the cast details */
3765 if (mini_get_debug_options ()->better_cast_details) {
3766 MonoInst *tls_get = mono_get_jit_tls_intrinsic (cfg);
3768 MONO_ADD_INS (cfg->cbb, tls_get);
3769 /* It is enough to reset the from field */
3770 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STORE_MEMBASE_IMM, tls_get->dreg, MONO_STRUCT_OFFSET (MonoJitTlsData, class_cast_from), 0);
3775 * On return the caller must check @array_class for load errors
3778 mini_emit_check_array_type (MonoCompile *cfg, MonoInst *obj, MonoClass *array_class)
3780 int vtable_reg = alloc_preg (cfg);
3783 context_used = mini_class_check_context_used (cfg, array_class);
3785 save_cast_details (cfg, array_class, obj->dreg, FALSE, NULL);
3787 MONO_EMIT_NEW_LOAD_MEMBASE_FAULT (cfg, vtable_reg, obj->dreg, MONO_STRUCT_OFFSET (MonoObject, vtable));
3789 if (cfg->opt & MONO_OPT_SHARED) {
3790 int class_reg = alloc_preg (cfg);
3791 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, class_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
3792 if (cfg->compile_aot) {
3793 int klass_reg = alloc_preg (cfg);
3794 MONO_EMIT_NEW_CLASSCONST (cfg, klass_reg, array_class);
3795 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, class_reg, klass_reg);
3797 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, class_reg, array_class);
3799 } else if (context_used) {
3800 MonoInst *vtable_ins;
3802 vtable_ins = emit_get_rgctx_klass (cfg, context_used, array_class, MONO_RGCTX_INFO_VTABLE);
3803 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, vtable_reg, vtable_ins->dreg);
3805 if (cfg->compile_aot) {
3809 if (!(vtable = mono_class_vtable (cfg->domain, array_class)))
3811 vt_reg = alloc_preg (cfg);
3812 MONO_EMIT_NEW_VTABLECONST (cfg, vt_reg, vtable);
3813 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, vtable_reg, vt_reg);
3816 if (!(vtable = mono_class_vtable (cfg->domain, array_class)))
3818 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, vtable_reg, vtable);
3822 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "ArrayTypeMismatchException");
3824 reset_cast_details (cfg);
3828 * Handles unbox of a Nullable<T>. If context_used is non zero, then shared
3829 * generic code is generated.
3832 handle_unbox_nullable (MonoCompile* cfg, MonoInst* val, MonoClass* klass, int context_used)
3834 MonoMethod* method = mono_class_get_method_from_name (klass, "Unbox", 1);
3837 MonoInst *rgctx, *addr;
3839 /* FIXME: What if the class is shared? We might not
3840 have to get the address of the method from the
3842 addr = emit_get_rgctx_method (cfg, context_used, method,
3843 MONO_RGCTX_INFO_GENERIC_METHOD_CODE);
3845 rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
3847 return mono_emit_calli (cfg, mono_method_signature (method), &val, addr, NULL, rgctx);
3849 gboolean pass_vtable, pass_mrgctx;
3850 MonoInst *rgctx_arg = NULL;
3852 check_method_sharing (cfg, method, &pass_vtable, &pass_mrgctx);
3853 g_assert (!pass_mrgctx);
3856 MonoVTable *vtable = mono_class_vtable (cfg->domain, method->klass);
3859 EMIT_NEW_VTABLECONST (cfg, rgctx_arg, vtable);
3862 return mono_emit_method_call_full (cfg, method, NULL, FALSE, &val, NULL, NULL, rgctx_arg);
3867 handle_unbox (MonoCompile *cfg, MonoClass *klass, MonoInst **sp, int context_used)
3871 int vtable_reg = alloc_dreg (cfg ,STACK_PTR);
3872 int klass_reg = alloc_dreg (cfg ,STACK_PTR);
3873 int eclass_reg = alloc_dreg (cfg ,STACK_PTR);
3874 int rank_reg = alloc_dreg (cfg ,STACK_I4);
3876 obj_reg = sp [0]->dreg;
3877 MONO_EMIT_NEW_LOAD_MEMBASE_FAULT (cfg, vtable_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
3878 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU1_MEMBASE, rank_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, rank));
3880 /* FIXME: generics */
3881 g_assert (klass->rank == 0);
3884 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, rank_reg, 0);
3885 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException");
3887 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
3888 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, eclass_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, element_class));
3891 MonoInst *element_class;
3893 /* This assertion is from the unboxcast insn */
3894 g_assert (klass->rank == 0);
3896 element_class = emit_get_rgctx_klass (cfg, context_used,
3897 klass, MONO_RGCTX_INFO_ELEMENT_KLASS);
3899 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, eclass_reg, element_class->dreg);
3900 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException");
3902 save_cast_details (cfg, klass->element_class, obj_reg, FALSE, NULL);
3903 mini_emit_class_check (cfg, eclass_reg, klass->element_class);
3904 reset_cast_details (cfg);
3907 NEW_BIALU_IMM (cfg, add, OP_ADD_IMM, alloc_dreg (cfg, STACK_MP), obj_reg, sizeof (MonoObject));
3908 MONO_ADD_INS (cfg->cbb, add);
3909 add->type = STACK_MP;
3916 handle_unbox_gsharedvt (MonoCompile *cfg, MonoClass *klass, MonoInst *obj, MonoBasicBlock **out_cbb)
3918 MonoInst *addr, *klass_inst, *is_ref, *args[16];
3919 MonoBasicBlock *is_ref_bb, *is_nullable_bb, *end_bb;
3923 klass_inst = emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_KLASS);
3929 args [1] = klass_inst;
3932 obj = mono_emit_jit_icall (cfg, mono_object_castclass_unbox, args);
3934 NEW_BBLOCK (cfg, is_ref_bb);
3935 NEW_BBLOCK (cfg, is_nullable_bb);
3936 NEW_BBLOCK (cfg, end_bb);
3937 is_ref = emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_CLASS_BOX_TYPE);
3938 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, is_ref->dreg, 1);
3939 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, is_ref_bb);
3941 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, is_ref->dreg, 2);
3942 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, is_nullable_bb);
3944 /* This will contain either the address of the unboxed vtype, or an address of the temporary where the ref is stored */
3945 addr_reg = alloc_dreg (cfg, STACK_MP);
3949 NEW_BIALU_IMM (cfg, addr, OP_ADD_IMM, addr_reg, obj->dreg, sizeof (MonoObject));
3950 MONO_ADD_INS (cfg->cbb, addr);
3952 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
3955 MONO_START_BB (cfg, is_ref_bb);
3957 /* Save the ref to a temporary */
3958 dreg = alloc_ireg (cfg);
3959 EMIT_NEW_VARLOADA_VREG (cfg, addr, dreg, &klass->byval_arg);
3960 addr->dreg = addr_reg;
3961 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, addr->dreg, 0, obj->dreg);
3962 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
3965 MONO_START_BB (cfg, is_nullable_bb);
3968 MonoInst *addr = emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_NULLABLE_CLASS_UNBOX);
3969 MonoInst *unbox_call;
3970 MonoMethodSignature *unbox_sig;
3972 unbox_sig = mono_mempool_alloc0 (cfg->mempool, MONO_SIZEOF_METHOD_SIGNATURE + (1 * sizeof (MonoType *)));
3973 unbox_sig->ret = &klass->byval_arg;
3974 unbox_sig->param_count = 1;
3975 unbox_sig->params [0] = &mono_defaults.object_class->byval_arg;
3976 unbox_call = mono_emit_calli (cfg, unbox_sig, &obj, addr, NULL, NULL);
3978 EMIT_NEW_VARLOADA_VREG (cfg, addr, unbox_call->dreg, &klass->byval_arg);
3979 addr->dreg = addr_reg;
3982 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
3985 MONO_START_BB (cfg, end_bb);
3988 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, addr_reg, 0);
3990 *out_cbb = cfg->cbb;
3996 * Returns NULL and set the cfg exception on error.
3999 handle_alloc (MonoCompile *cfg, MonoClass *klass, gboolean for_box, int context_used)
4001 MonoInst *iargs [2];
4007 MonoInst *iargs [2];
4008 gboolean known_instance_size = !mini_is_gsharedvt_klass (cfg, klass);
4010 MonoMethod *managed_alloc = mono_gc_get_managed_allocator (klass, for_box, known_instance_size);
4012 if (cfg->opt & MONO_OPT_SHARED)
4013 rgctx_info = MONO_RGCTX_INFO_KLASS;
4015 rgctx_info = MONO_RGCTX_INFO_VTABLE;
4016 data = emit_get_rgctx_klass (cfg, context_used, klass, rgctx_info);
4018 if (cfg->opt & MONO_OPT_SHARED) {
4019 EMIT_NEW_DOMAINCONST (cfg, iargs [0]);
4021 alloc_ftn = mono_object_new;
4024 alloc_ftn = mono_object_new_specific;
4027 if (managed_alloc && !(cfg->opt & MONO_OPT_SHARED)) {
4028 if (known_instance_size) {
4029 int size = mono_class_instance_size (klass);
4030 if (size < sizeof (MonoObject))
4031 g_error ("Invalid size %d for class %s", size, mono_type_get_full_name (klass));
4033 EMIT_NEW_ICONST (cfg, iargs [1], mono_gc_get_aligned_size_for_allocator (size));
4035 return mono_emit_method_call (cfg, managed_alloc, iargs, NULL);
4038 return mono_emit_jit_icall (cfg, alloc_ftn, iargs);
4041 if (cfg->opt & MONO_OPT_SHARED) {
4042 EMIT_NEW_DOMAINCONST (cfg, iargs [0]);
4043 EMIT_NEW_CLASSCONST (cfg, iargs [1], klass);
4045 alloc_ftn = mono_object_new;
4046 } else if (cfg->compile_aot && cfg->cbb->out_of_line && klass->type_token && klass->image == mono_defaults.corlib && !klass->generic_class) {
4047 /* This happens often in argument checking code, eg. throw new FooException... */
4048 /* Avoid relocations and save some space by calling a helper function specialized to mscorlib */
4049 EMIT_NEW_ICONST (cfg, iargs [0], mono_metadata_token_index (klass->type_token));
4050 return mono_emit_jit_icall (cfg, mono_helper_newobj_mscorlib, iargs);
4052 MonoVTable *vtable = mono_class_vtable (cfg->domain, klass);
4053 MonoMethod *managed_alloc = NULL;
4057 mono_cfg_set_exception (cfg, MONO_EXCEPTION_TYPE_LOAD);
4058 cfg->exception_ptr = klass;
4062 #ifndef MONO_CROSS_COMPILE
4063 managed_alloc = mono_gc_get_managed_allocator (klass, for_box, TRUE);
4066 if (managed_alloc) {
4067 int size = mono_class_instance_size (klass);
4068 if (size < sizeof (MonoObject))
4069 g_error ("Invalid size %d for class %s", size, mono_type_get_full_name (klass));
4071 EMIT_NEW_VTABLECONST (cfg, iargs [0], vtable);
4072 EMIT_NEW_ICONST (cfg, iargs [1], mono_gc_get_aligned_size_for_allocator (size));
4073 return mono_emit_method_call (cfg, managed_alloc, iargs, NULL);
4075 alloc_ftn = mono_class_get_allocation_ftn (vtable, for_box, &pass_lw);
4077 guint32 lw = vtable->klass->instance_size;
4078 lw = ((lw + (sizeof (gpointer) - 1)) & ~(sizeof (gpointer) - 1)) / sizeof (gpointer);
4079 EMIT_NEW_ICONST (cfg, iargs [0], lw);
4080 EMIT_NEW_VTABLECONST (cfg, iargs [1], vtable);
4083 EMIT_NEW_VTABLECONST (cfg, iargs [0], vtable);
4087 return mono_emit_jit_icall (cfg, alloc_ftn, iargs);
4091 * Returns NULL and set the cfg exception on error.
4094 handle_box (MonoCompile *cfg, MonoInst *val, MonoClass *klass, int context_used, MonoBasicBlock **out_cbb)
4096 MonoInst *alloc, *ins;
4098 *out_cbb = cfg->cbb;
4100 if (mono_class_is_nullable (klass)) {
4101 MonoMethod* method = mono_class_get_method_from_name (klass, "Box", 1);
4104 /* FIXME: What if the class is shared? We might not
4105 have to get the method address from the RGCTX. */
4106 MonoInst *addr = emit_get_rgctx_method (cfg, context_used, method,
4107 MONO_RGCTX_INFO_GENERIC_METHOD_CODE);
4108 MonoInst *rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
4110 return mono_emit_calli (cfg, mono_method_signature (method), &val, addr, NULL, rgctx);
4112 gboolean pass_vtable, pass_mrgctx;
4113 MonoInst *rgctx_arg = NULL;
4115 check_method_sharing (cfg, method, &pass_vtable, &pass_mrgctx);
4116 g_assert (!pass_mrgctx);
4119 MonoVTable *vtable = mono_class_vtable (cfg->domain, method->klass);
4122 EMIT_NEW_VTABLECONST (cfg, rgctx_arg, vtable);
4125 return mono_emit_method_call_full (cfg, method, NULL, FALSE, &val, NULL, NULL, rgctx_arg);
4129 if (mini_is_gsharedvt_klass (cfg, klass)) {
4130 MonoBasicBlock *is_ref_bb, *is_nullable_bb, *end_bb;
4131 MonoInst *res, *is_ref, *src_var, *addr;
4134 dreg = alloc_ireg (cfg);
4136 NEW_BBLOCK (cfg, is_ref_bb);
4137 NEW_BBLOCK (cfg, is_nullable_bb);
4138 NEW_BBLOCK (cfg, end_bb);
4139 is_ref = emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_CLASS_BOX_TYPE);
4140 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, is_ref->dreg, 1);
4141 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, is_ref_bb);
4143 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, is_ref->dreg, 2);
4144 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, is_nullable_bb);
4147 alloc = handle_alloc (cfg, klass, TRUE, context_used);
4150 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, alloc->dreg, sizeof (MonoObject), val->dreg);
4151 ins->opcode = OP_STOREV_MEMBASE;
4153 EMIT_NEW_UNALU (cfg, res, OP_MOVE, dreg, alloc->dreg);
4154 res->type = STACK_OBJ;
4156 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4159 MONO_START_BB (cfg, is_ref_bb);
4161 /* val is a vtype, so has to load the value manually */
4162 src_var = get_vreg_to_inst (cfg, val->dreg);
4164 src_var = mono_compile_create_var_for_vreg (cfg, &klass->byval_arg, OP_LOCAL, val->dreg);
4165 EMIT_NEW_VARLOADA (cfg, addr, src_var, src_var->inst_vtype);
4166 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, dreg, addr->dreg, 0);
4167 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4170 MONO_START_BB (cfg, is_nullable_bb);
4173 MonoInst *addr = emit_get_gsharedvt_info_klass (cfg, klass,
4174 MONO_RGCTX_INFO_NULLABLE_CLASS_BOX);
4176 MonoMethodSignature *box_sig;
4179 * klass is Nullable<T>, need to call Nullable<T>.Box () using a gsharedvt signature, but we cannot
4180 * construct that method at JIT time, so have to do things by hand.
4182 box_sig = mono_mempool_alloc0 (cfg->mempool, MONO_SIZEOF_METHOD_SIGNATURE + (1 * sizeof (MonoType *)));
4183 box_sig->ret = &mono_defaults.object_class->byval_arg;
4184 box_sig->param_count = 1;
4185 box_sig->params [0] = &klass->byval_arg;
4186 box_call = mono_emit_calli (cfg, box_sig, &val, addr, NULL, NULL);
4187 EMIT_NEW_UNALU (cfg, res, OP_MOVE, dreg, box_call->dreg);
4188 res->type = STACK_OBJ;
4192 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4194 MONO_START_BB (cfg, end_bb);
4196 *out_cbb = cfg->cbb;
4200 alloc = handle_alloc (cfg, klass, TRUE, context_used);
4204 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, alloc->dreg, sizeof (MonoObject), val->dreg);
4210 mini_class_has_reference_variant_generic_argument (MonoCompile *cfg, MonoClass *klass, int context_used)
4213 MonoGenericContainer *container;
4214 MonoGenericInst *ginst;
4216 if (klass->generic_class) {
4217 container = klass->generic_class->container_class->generic_container;
4218 ginst = klass->generic_class->context.class_inst;
4219 } else if (klass->generic_container && context_used) {
4220 container = klass->generic_container;
4221 ginst = container->context.class_inst;
4226 for (i = 0; i < container->type_argc; ++i) {
4228 if (!(mono_generic_container_get_param_info (container, i)->flags & (MONO_GEN_PARAM_VARIANT|MONO_GEN_PARAM_COVARIANT)))
4230 type = ginst->type_argv [i];
4231 if (mini_type_is_reference (cfg, type))
4237 static GHashTable* direct_icall_type_hash;
4240 icall_is_direct_callable (MonoCompile *cfg, MonoMethod *cmethod)
4242 /* LLVM on amd64 can't handle calls to non-32 bit addresses */
4243 if (!direct_icalls_enabled (cfg))
4247 * An icall is directly callable if it doesn't directly or indirectly call mono_raise_exception ().
4248 * Whitelist a few icalls for now.
4250 if (!direct_icall_type_hash) {
4251 GHashTable *h = g_hash_table_new (g_str_hash, g_str_equal);
4253 g_hash_table_insert (h, (char*)"Decimal", GUINT_TO_POINTER (1));
4254 g_hash_table_insert (h, (char*)"Number", GUINT_TO_POINTER (1));
4255 g_hash_table_insert (h, (char*)"Buffer", GUINT_TO_POINTER (1));
4256 mono_memory_barrier ();
4257 direct_icall_type_hash = h;
4260 if (cmethod->klass == mono_defaults.math_class)
4262 /* No locking needed */
4263 if (cmethod->klass->image == mono_defaults.corlib && g_hash_table_lookup (direct_icall_type_hash, cmethod->klass->name))
4268 #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)
4271 emit_castclass_with_cache (MonoCompile *cfg, MonoClass *klass, MonoInst **args, MonoBasicBlock **out_bblock)
4273 MonoMethod *mono_castclass;
4276 mono_castclass = mono_marshal_get_castclass_with_cache ();
4278 save_cast_details (cfg, klass, args [0]->dreg, TRUE, out_bblock);
4279 res = mono_emit_method_call (cfg, mono_castclass, args, NULL);
4280 reset_cast_details (cfg);
4281 *out_bblock = cfg->cbb;
4287 get_castclass_cache_idx (MonoCompile *cfg)
4289 /* Each CASTCLASS_CACHE patch needs a unique index which identifies the call site */
4290 cfg->castclass_cache_index ++;
4291 return (cfg->method_index << 16) | cfg->castclass_cache_index;
4295 emit_castclass_with_cache_nonshared (MonoCompile *cfg, MonoInst *obj, MonoClass *klass, MonoBasicBlock **out_bblock)
4304 EMIT_NEW_CLASSCONST (cfg, args [1], klass);
4307 if (cfg->compile_aot) {
4308 idx = get_castclass_cache_idx (cfg);
4309 EMIT_NEW_AOTCONST (cfg, args [2], MONO_PATCH_INFO_CASTCLASS_CACHE, GINT_TO_POINTER (idx));
4311 EMIT_NEW_PCONST (cfg, args [2], mono_domain_alloc0 (cfg->domain, sizeof (gpointer)));
4314 /*The wrapper doesn't inline well so the bloat of inlining doesn't pay off.*/
4316 return emit_castclass_with_cache (cfg, klass, args, out_bblock);
4320 * Returns NULL and set the cfg exception on error.
4323 handle_castclass (MonoCompile *cfg, MonoClass *klass, MonoInst *src, guint8 *ip, MonoBasicBlock **out_bb, int *inline_costs)
4325 MonoBasicBlock *is_null_bb;
4326 int obj_reg = src->dreg;
4327 int vtable_reg = alloc_preg (cfg);
4329 MonoInst *klass_inst = NULL, *res;
4330 MonoBasicBlock *bblock;
4334 context_used = mini_class_check_context_used (cfg, klass);
4336 if (!context_used && mini_class_has_reference_variant_generic_argument (cfg, klass, context_used)) {
4337 res = emit_castclass_with_cache_nonshared (cfg, src, klass, &bblock);
4338 (*inline_costs) += 2;
4341 } else if (!context_used && (mono_class_is_marshalbyref (klass) || klass->flags & TYPE_ATTRIBUTE_INTERFACE)) {
4342 MonoMethod *mono_castclass;
4343 MonoInst *iargs [1];
4346 mono_castclass = mono_marshal_get_castclass (klass);
4349 save_cast_details (cfg, klass, src->dreg, TRUE, &bblock);
4350 costs = inline_method (cfg, mono_castclass, mono_method_signature (mono_castclass),
4351 iargs, ip, cfg->real_offset, TRUE, &bblock);
4352 reset_cast_details (cfg);
4353 CHECK_CFG_EXCEPTION;
4354 g_assert (costs > 0);
4356 cfg->real_offset += 5;
4358 (*inline_costs) += costs;
4367 if(mini_class_has_reference_variant_generic_argument (cfg, klass, context_used) || is_complex_isinst (klass)) {
4368 MonoInst *cache_ins;
4370 cache_ins = emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_CAST_CACHE);
4375 /* klass - it's the second element of the cache entry*/
4376 EMIT_NEW_LOAD_MEMBASE (cfg, args [1], OP_LOAD_MEMBASE, alloc_preg (cfg), cache_ins->dreg, sizeof (gpointer));
4379 args [2] = cache_ins;
4381 return emit_castclass_with_cache (cfg, klass, args, out_bb);
4384 klass_inst = emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_KLASS);
4387 NEW_BBLOCK (cfg, is_null_bb);
4389 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, obj_reg, 0);
4390 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, is_null_bb);
4392 save_cast_details (cfg, klass, obj_reg, FALSE, NULL);
4394 if (klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
4395 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, vtable_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
4396 mini_emit_iface_cast (cfg, vtable_reg, klass, NULL, NULL);
4398 int klass_reg = alloc_preg (cfg);
4400 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, vtable_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
4402 if (!klass->rank && !cfg->compile_aot && !(cfg->opt & MONO_OPT_SHARED) && (klass->flags & TYPE_ATTRIBUTE_SEALED)) {
4403 /* the remoting code is broken, access the class for now */
4404 if (0) { /*FIXME what exactly is broken? This change refers to r39380 from 2005 and mention some remoting fixes were due.*/
4405 MonoVTable *vt = mono_class_vtable (cfg->domain, klass);
4407 mono_cfg_set_exception (cfg, MONO_EXCEPTION_TYPE_LOAD);
4408 cfg->exception_ptr = klass;
4411 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, vtable_reg, vt);
4413 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4414 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, klass_reg, klass);
4416 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException");
4418 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4419 mini_emit_castclass_inst (cfg, obj_reg, klass_reg, klass, klass_inst, is_null_bb);
4423 MONO_START_BB (cfg, is_null_bb);
4425 reset_cast_details (cfg);
4436 * Returns NULL and set the cfg exception on error.
4439 handle_isinst (MonoCompile *cfg, MonoClass *klass, MonoInst *src, int context_used)
4442 MonoBasicBlock *is_null_bb, *false_bb, *end_bb;
4443 int obj_reg = src->dreg;
4444 int vtable_reg = alloc_preg (cfg);
4445 int res_reg = alloc_ireg_ref (cfg);
4446 MonoInst *klass_inst = NULL;
4451 if(mini_class_has_reference_variant_generic_argument (cfg, klass, context_used) || is_complex_isinst (klass)) {
4452 MonoMethod *mono_isinst = mono_marshal_get_isinst_with_cache ();
4453 MonoInst *cache_ins;
4455 cache_ins = emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_CAST_CACHE);
4460 /* klass - it's the second element of the cache entry*/
4461 EMIT_NEW_LOAD_MEMBASE (cfg, args [1], OP_LOAD_MEMBASE, alloc_preg (cfg), cache_ins->dreg, sizeof (gpointer));
4464 args [2] = cache_ins;
4466 return mono_emit_method_call (cfg, mono_isinst, args, NULL);
4469 klass_inst = emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_KLASS);
4472 NEW_BBLOCK (cfg, is_null_bb);
4473 NEW_BBLOCK (cfg, false_bb);
4474 NEW_BBLOCK (cfg, end_bb);
4476 /* Do the assignment at the beginning, so the other assignment can be if converted */
4477 EMIT_NEW_UNALU (cfg, ins, OP_MOVE, res_reg, obj_reg);
4478 ins->type = STACK_OBJ;
4481 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, obj_reg, 0);
4482 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, is_null_bb);
4484 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, vtable_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
4486 if (klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
4487 g_assert (!context_used);
4488 /* the is_null_bb target simply copies the input register to the output */
4489 mini_emit_iface_cast (cfg, vtable_reg, klass, false_bb, is_null_bb);
4491 int klass_reg = alloc_preg (cfg);
4494 int rank_reg = alloc_preg (cfg);
4495 int eclass_reg = alloc_preg (cfg);
4497 g_assert (!context_used);
4498 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU1_MEMBASE, rank_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, rank));
4499 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, rank_reg, klass->rank);
4500 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, false_bb);
4501 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4502 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, eclass_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, cast_class));
4503 if (klass->cast_class == mono_defaults.object_class) {
4504 int parent_reg = alloc_preg (cfg);
4505 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, parent_reg, eclass_reg, MONO_STRUCT_OFFSET (MonoClass, parent));
4506 mini_emit_class_check_branch (cfg, parent_reg, mono_defaults.enum_class->parent, OP_PBNE_UN, is_null_bb);
4507 mini_emit_class_check_branch (cfg, eclass_reg, mono_defaults.enum_class, OP_PBEQ, is_null_bb);
4508 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, false_bb);
4509 } else if (klass->cast_class == mono_defaults.enum_class->parent) {
4510 mini_emit_class_check_branch (cfg, eclass_reg, mono_defaults.enum_class->parent, OP_PBEQ, is_null_bb);
4511 mini_emit_class_check_branch (cfg, eclass_reg, mono_defaults.enum_class, OP_PBEQ, is_null_bb);
4512 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, false_bb);
4513 } else if (klass->cast_class == mono_defaults.enum_class) {
4514 mini_emit_class_check_branch (cfg, eclass_reg, mono_defaults.enum_class, OP_PBEQ, is_null_bb);
4515 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, false_bb);
4516 } else if (klass->cast_class->flags & TYPE_ATTRIBUTE_INTERFACE) {
4517 mini_emit_iface_class_cast (cfg, eclass_reg, klass->cast_class, false_bb, is_null_bb);
4519 if ((klass->rank == 1) && (klass->byval_arg.type == MONO_TYPE_SZARRAY)) {
4520 /* Check that the object is a vector too */
4521 int bounds_reg = alloc_preg (cfg);
4522 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, bounds_reg, obj_reg, MONO_STRUCT_OFFSET (MonoArray, bounds));
4523 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, bounds_reg, 0);
4524 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, false_bb);
4527 /* the is_null_bb target simply copies the input register to the output */
4528 mini_emit_isninst_cast (cfg, eclass_reg, klass->cast_class, false_bb, is_null_bb);
4530 } else if (mono_class_is_nullable (klass)) {
4531 g_assert (!context_used);
4532 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4533 /* the is_null_bb target simply copies the input register to the output */
4534 mini_emit_isninst_cast (cfg, klass_reg, klass->cast_class, false_bb, is_null_bb);
4536 if (!cfg->compile_aot && !(cfg->opt & MONO_OPT_SHARED) && (klass->flags & TYPE_ATTRIBUTE_SEALED)) {
4537 g_assert (!context_used);
4538 /* the remoting code is broken, access the class for now */
4539 if (0) {/*FIXME what exactly is broken? This change refers to r39380 from 2005 and mention some remoting fixes were due.*/
4540 MonoVTable *vt = mono_class_vtable (cfg->domain, klass);
4542 mono_cfg_set_exception (cfg, MONO_EXCEPTION_TYPE_LOAD);
4543 cfg->exception_ptr = klass;
4546 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, vtable_reg, vt);
4548 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4549 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, klass_reg, klass);
4551 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, false_bb);
4552 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, is_null_bb);
4554 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4555 /* the is_null_bb target simply copies the input register to the output */
4556 mini_emit_isninst_cast_inst (cfg, klass_reg, klass, klass_inst, false_bb, is_null_bb);
4561 MONO_START_BB (cfg, false_bb);
4563 MONO_EMIT_NEW_PCONST (cfg, res_reg, 0);
4564 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4566 MONO_START_BB (cfg, is_null_bb);
4568 MONO_START_BB (cfg, end_bb);
4574 handle_cisinst (MonoCompile *cfg, MonoClass *klass, MonoInst *src)
4576 /* This opcode takes as input an object reference and a class, and returns:
4577 0) if the object is an instance of the class,
4578 1) if the object is not instance of the class,
4579 2) if the object is a proxy whose type cannot be determined */
4582 #ifndef DISABLE_REMOTING
4583 MonoBasicBlock *true_bb, *false_bb, *false2_bb, *end_bb, *no_proxy_bb, *interface_fail_bb;
4585 MonoBasicBlock *true_bb, *false_bb, *end_bb;
4587 int obj_reg = src->dreg;
4588 int dreg = alloc_ireg (cfg);
4590 #ifndef DISABLE_REMOTING
4591 int klass_reg = alloc_preg (cfg);
4594 NEW_BBLOCK (cfg, true_bb);
4595 NEW_BBLOCK (cfg, false_bb);
4596 NEW_BBLOCK (cfg, end_bb);
4597 #ifndef DISABLE_REMOTING
4598 NEW_BBLOCK (cfg, false2_bb);
4599 NEW_BBLOCK (cfg, no_proxy_bb);
4602 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, obj_reg, 0);
4603 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, false_bb);
4605 if (klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
4606 #ifndef DISABLE_REMOTING
4607 NEW_BBLOCK (cfg, interface_fail_bb);
4610 tmp_reg = alloc_preg (cfg);
4611 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
4612 #ifndef DISABLE_REMOTING
4613 mini_emit_iface_cast (cfg, tmp_reg, klass, interface_fail_bb, true_bb);
4614 MONO_START_BB (cfg, interface_fail_bb);
4615 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, tmp_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4617 mini_emit_class_check_branch (cfg, klass_reg, mono_defaults.transparent_proxy_class, OP_PBNE_UN, false_bb);
4619 tmp_reg = alloc_preg (cfg);
4620 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoTransparentProxy, custom_type_info));
4621 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, tmp_reg, 0);
4622 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, false2_bb);
4624 mini_emit_iface_cast (cfg, tmp_reg, klass, false_bb, true_bb);
4627 #ifndef DISABLE_REMOTING
4628 tmp_reg = alloc_preg (cfg);
4629 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
4630 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, tmp_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4632 mini_emit_class_check_branch (cfg, klass_reg, mono_defaults.transparent_proxy_class, OP_PBNE_UN, no_proxy_bb);
4633 tmp_reg = alloc_preg (cfg);
4634 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoTransparentProxy, remote_class));
4635 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, tmp_reg, MONO_STRUCT_OFFSET (MonoRemoteClass, proxy_class));
4637 tmp_reg = alloc_preg (cfg);
4638 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoTransparentProxy, custom_type_info));
4639 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, tmp_reg, 0);
4640 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, no_proxy_bb);
4642 mini_emit_isninst_cast (cfg, klass_reg, klass, false2_bb, true_bb);
4643 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, false2_bb);
4645 MONO_START_BB (cfg, no_proxy_bb);
4647 mini_emit_isninst_cast (cfg, klass_reg, klass, false_bb, true_bb);
4649 g_error ("transparent proxy support is disabled while trying to JIT code that uses it");
4653 MONO_START_BB (cfg, false_bb);
4655 MONO_EMIT_NEW_ICONST (cfg, dreg, 1);
4656 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4658 #ifndef DISABLE_REMOTING
4659 MONO_START_BB (cfg, false2_bb);
4661 MONO_EMIT_NEW_ICONST (cfg, dreg, 2);
4662 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4665 MONO_START_BB (cfg, true_bb);
4667 MONO_EMIT_NEW_ICONST (cfg, dreg, 0);
4669 MONO_START_BB (cfg, end_bb);
4672 MONO_INST_NEW (cfg, ins, OP_ICONST);
4674 ins->type = STACK_I4;
4680 handle_ccastclass (MonoCompile *cfg, MonoClass *klass, MonoInst *src)
4682 /* This opcode takes as input an object reference and a class, and returns:
4683 0) if the object is an instance of the class,
4684 1) if the object is a proxy whose type cannot be determined
4685 an InvalidCastException exception is thrown otherwhise*/
4688 #ifndef DISABLE_REMOTING
4689 MonoBasicBlock *end_bb, *ok_result_bb, *no_proxy_bb, *interface_fail_bb, *fail_1_bb;
4691 MonoBasicBlock *ok_result_bb;
4693 int obj_reg = src->dreg;
4694 int dreg = alloc_ireg (cfg);
4695 int tmp_reg = alloc_preg (cfg);
4697 #ifndef DISABLE_REMOTING
4698 int klass_reg = alloc_preg (cfg);
4699 NEW_BBLOCK (cfg, end_bb);
4702 NEW_BBLOCK (cfg, ok_result_bb);
4704 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, obj_reg, 0);
4705 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, ok_result_bb);
4707 save_cast_details (cfg, klass, obj_reg, FALSE, NULL);
4709 if (klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
4710 #ifndef DISABLE_REMOTING
4711 NEW_BBLOCK (cfg, interface_fail_bb);
4713 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
4714 mini_emit_iface_cast (cfg, tmp_reg, klass, interface_fail_bb, ok_result_bb);
4715 MONO_START_BB (cfg, interface_fail_bb);
4716 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, tmp_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4718 mini_emit_class_check (cfg, klass_reg, mono_defaults.transparent_proxy_class);
4720 tmp_reg = alloc_preg (cfg);
4721 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoTransparentProxy, custom_type_info));
4722 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, tmp_reg, 0);
4723 MONO_EMIT_NEW_COND_EXC (cfg, EQ, "InvalidCastException");
4725 MONO_EMIT_NEW_ICONST (cfg, dreg, 1);
4726 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4728 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
4729 mini_emit_iface_cast (cfg, tmp_reg, klass, NULL, NULL);
4730 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, ok_result_bb);
4733 #ifndef DISABLE_REMOTING
4734 NEW_BBLOCK (cfg, no_proxy_bb);
4736 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
4737 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, tmp_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4738 mini_emit_class_check_branch (cfg, klass_reg, mono_defaults.transparent_proxy_class, OP_PBNE_UN, no_proxy_bb);
4740 tmp_reg = alloc_preg (cfg);
4741 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoTransparentProxy, remote_class));
4742 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, tmp_reg, MONO_STRUCT_OFFSET (MonoRemoteClass, proxy_class));
4744 tmp_reg = alloc_preg (cfg);
4745 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoTransparentProxy, custom_type_info));
4746 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, tmp_reg, 0);
4747 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, no_proxy_bb);
4749 NEW_BBLOCK (cfg, fail_1_bb);
4751 mini_emit_isninst_cast (cfg, klass_reg, klass, fail_1_bb, ok_result_bb);
4753 MONO_START_BB (cfg, fail_1_bb);
4755 MONO_EMIT_NEW_ICONST (cfg, dreg, 1);
4756 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4758 MONO_START_BB (cfg, no_proxy_bb);
4760 mini_emit_castclass (cfg, obj_reg, klass_reg, klass, ok_result_bb);
4762 g_error ("Transparent proxy support is disabled while trying to JIT code that uses it");
4766 MONO_START_BB (cfg, ok_result_bb);
4768 MONO_EMIT_NEW_ICONST (cfg, dreg, 0);
4770 #ifndef DISABLE_REMOTING
4771 MONO_START_BB (cfg, end_bb);
4775 MONO_INST_NEW (cfg, ins, OP_ICONST);
4777 ins->type = STACK_I4;
4782 static G_GNUC_UNUSED MonoInst*
4783 handle_enum_has_flag (MonoCompile *cfg, MonoClass *klass, MonoInst *enum_this, MonoInst *enum_flag)
4785 MonoType *enum_type = mono_type_get_underlying_type (&klass->byval_arg);
4786 guint32 load_opc = mono_type_to_load_membase (cfg, enum_type);
4789 switch (enum_type->type) {
4792 #if SIZEOF_REGISTER == 8
4804 MonoInst *load, *and, *cmp, *ceq;
4805 int enum_reg = is_i4 ? alloc_ireg (cfg) : alloc_lreg (cfg);
4806 int and_reg = is_i4 ? alloc_ireg (cfg) : alloc_lreg (cfg);
4807 int dest_reg = alloc_ireg (cfg);
4809 EMIT_NEW_LOAD_MEMBASE (cfg, load, load_opc, enum_reg, enum_this->dreg, 0);
4810 EMIT_NEW_BIALU (cfg, and, is_i4 ? OP_IAND : OP_LAND, and_reg, enum_reg, enum_flag->dreg);
4811 EMIT_NEW_BIALU (cfg, cmp, is_i4 ? OP_ICOMPARE : OP_LCOMPARE, -1, and_reg, enum_flag->dreg);
4812 EMIT_NEW_UNALU (cfg, ceq, is_i4 ? OP_ICEQ : OP_LCEQ, dest_reg, -1);
4814 ceq->type = STACK_I4;
4817 load = mono_decompose_opcode (cfg, load, NULL);
4818 and = mono_decompose_opcode (cfg, and, NULL);
4819 cmp = mono_decompose_opcode (cfg, cmp, NULL);
4820 ceq = mono_decompose_opcode (cfg, ceq, NULL);
4828 * Returns NULL and set the cfg exception on error.
4830 static G_GNUC_UNUSED MonoInst*
4831 handle_delegate_ctor (MonoCompile *cfg, MonoClass *klass, MonoInst *target, MonoMethod *method, int context_used, gboolean virtual)
4835 gpointer trampoline;
4836 MonoInst *obj, *method_ins, *tramp_ins;
4841 MonoMethod *invoke = mono_get_delegate_invoke (klass);
4844 if (!mono_get_delegate_virtual_invoke_impl (mono_method_signature (invoke), context_used ? NULL : method))
4848 obj = handle_alloc (cfg, klass, FALSE, 0);
4852 /* Inline the contents of mono_delegate_ctor */
4854 /* Set target field */
4855 /* Optimize away setting of NULL target */
4856 if (!(target->opcode == OP_PCONST && target->inst_p0 == 0)) {
4857 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, target), target->dreg);
4858 if (cfg->gen_write_barriers) {
4859 dreg = alloc_preg (cfg);
4860 EMIT_NEW_BIALU_IMM (cfg, ptr, OP_PADD_IMM, dreg, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, target));
4861 emit_write_barrier (cfg, ptr, target);
4865 /* Set method field */
4866 method_ins = emit_get_rgctx_method (cfg, context_used, method, MONO_RGCTX_INFO_METHOD);
4867 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, method), method_ins->dreg);
4870 * To avoid looking up the compiled code belonging to the target method
4871 * in mono_delegate_trampoline (), we allocate a per-domain memory slot to
4872 * store it, and we fill it after the method has been compiled.
4874 if (!method->dynamic && !(cfg->opt & MONO_OPT_SHARED)) {
4875 MonoInst *code_slot_ins;
4878 code_slot_ins = emit_get_rgctx_method (cfg, context_used, method, MONO_RGCTX_INFO_METHOD_DELEGATE_CODE);
4880 domain = mono_domain_get ();
4881 mono_domain_lock (domain);
4882 if (!domain_jit_info (domain)->method_code_hash)
4883 domain_jit_info (domain)->method_code_hash = g_hash_table_new (NULL, NULL);
4884 code_slot = g_hash_table_lookup (domain_jit_info (domain)->method_code_hash, method);
4886 code_slot = mono_domain_alloc0 (domain, sizeof (gpointer));
4887 g_hash_table_insert (domain_jit_info (domain)->method_code_hash, method, code_slot);
4889 mono_domain_unlock (domain);
4891 if (cfg->compile_aot)
4892 EMIT_NEW_AOTCONST (cfg, code_slot_ins, MONO_PATCH_INFO_METHOD_CODE_SLOT, method);
4894 EMIT_NEW_PCONST (cfg, code_slot_ins, code_slot);
4896 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, method_code), code_slot_ins->dreg);
4899 if (cfg->compile_aot) {
4900 MonoDelegateClassMethodPair *del_tramp;
4902 del_tramp = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoDelegateClassMethodPair));
4903 del_tramp->klass = klass;
4904 del_tramp->method = context_used ? NULL : method;
4905 del_tramp->virtual = virtual;
4906 EMIT_NEW_AOTCONST (cfg, tramp_ins, MONO_PATCH_INFO_DELEGATE_TRAMPOLINE, del_tramp);
4909 trampoline = mono_create_delegate_virtual_trampoline (cfg->domain, klass, context_used ? NULL : method);
4911 trampoline = mono_create_delegate_trampoline_info (cfg->domain, klass, context_used ? NULL : method);
4912 EMIT_NEW_PCONST (cfg, tramp_ins, trampoline);
4915 /* Set invoke_impl field */
4917 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, invoke_impl), tramp_ins->dreg);
4919 dreg = alloc_preg (cfg);
4920 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, dreg, tramp_ins->dreg, MONO_STRUCT_OFFSET (MonoDelegateTrampInfo, invoke_impl));
4921 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, invoke_impl), dreg);
4923 dreg = alloc_preg (cfg);
4924 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, dreg, tramp_ins->dreg, MONO_STRUCT_OFFSET (MonoDelegateTrampInfo, method_ptr));
4925 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, method_ptr), dreg);
4928 /* All the checks which are in mono_delegate_ctor () are done by the delegate trampoline */
4934 handle_array_new (MonoCompile *cfg, int rank, MonoInst **sp, unsigned char *ip)
4936 MonoJitICallInfo *info;
4938 /* Need to register the icall so it gets an icall wrapper */
4939 info = mono_get_array_new_va_icall (rank);
4941 cfg->flags |= MONO_CFG_HAS_VARARGS;
4943 /* mono_array_new_va () needs a vararg calling convention */
4944 cfg->disable_llvm = TRUE;
4946 /* FIXME: This uses info->sig, but it should use the signature of the wrapper */
4947 return mono_emit_native_call (cfg, mono_icall_get_wrapper (info), info->sig, sp);
4951 * handle_constrained_gsharedvt_call:
4953 * Handle constrained calls where the receiver is a gsharedvt type.
4954 * Return the instruction representing the call. Set the cfg exception on failure.
4957 handle_constrained_gsharedvt_call (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **sp, MonoClass *constrained_class,
4958 gboolean *ref_emit_widen, MonoBasicBlock **ref_bblock)
4960 MonoInst *ins = NULL;
4961 MonoBasicBlock *bblock = *ref_bblock;
4962 gboolean emit_widen = *ref_emit_widen;
4965 * Constrained calls need to behave differently at runtime dependending on whenever the receiver is instantiated as ref type or as a vtype.
4966 * 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
4967 * pack the arguments into an array, and do the rest of the work in in an icall.
4969 if (((cmethod->klass == mono_defaults.object_class) || (cmethod->klass->flags & TYPE_ATTRIBUTE_INTERFACE) || (!cmethod->klass->valuetype && cmethod->klass->image != mono_defaults.corlib)) &&
4970 (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)) &&
4971 (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]))))) {
4972 MonoInst *args [16];
4975 * This case handles calls to
4976 * - object:ToString()/Equals()/GetHashCode(),
4977 * - System.IComparable<T>:CompareTo()
4978 * - System.IEquatable<T>:Equals ()
4979 * plus some simple interface calls enough to support AsyncTaskMethodBuilder.
4983 if (mono_method_check_context_used (cmethod))
4984 args [1] = emit_get_rgctx_method (cfg, mono_method_check_context_used (cmethod), cmethod, MONO_RGCTX_INFO_METHOD);
4986 EMIT_NEW_METHODCONST (cfg, args [1], cmethod);
4987 args [2] = emit_get_rgctx_klass (cfg, mono_class_check_context_used (constrained_class), constrained_class, MONO_RGCTX_INFO_KLASS);
4989 /* !fsig->hasthis is for the wrapper for the Object.GetType () icall */
4990 if (fsig->hasthis && fsig->param_count) {
4991 /* Pass the arguments using a localloc-ed array using the format expected by runtime_invoke () */
4992 MONO_INST_NEW (cfg, ins, OP_LOCALLOC_IMM);
4993 ins->dreg = alloc_preg (cfg);
4994 ins->inst_imm = fsig->param_count * sizeof (mgreg_t);
4995 MONO_ADD_INS (cfg->cbb, ins);
4998 if (mini_is_gsharedvt_type (cfg, fsig->params [0])) {
5001 args [3] = emit_get_gsharedvt_info_klass (cfg, mono_class_from_mono_type (fsig->params [0]), MONO_RGCTX_INFO_CLASS_BOX_TYPE);
5003 EMIT_NEW_VARLOADA_VREG (cfg, ins, sp [1]->dreg, fsig->params [0]);
5004 addr_reg = ins->dreg;
5005 EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, args [4]->dreg, 0, addr_reg);
5007 EMIT_NEW_ICONST (cfg, args [3], 0);
5008 EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, args [4]->dreg, 0, sp [1]->dreg);
5011 EMIT_NEW_ICONST (cfg, args [3], 0);
5012 EMIT_NEW_ICONST (cfg, args [4], 0);
5014 ins = mono_emit_jit_icall (cfg, mono_gsharedvt_constrained_call, args);
5017 if (mini_is_gsharedvt_type (cfg, fsig->ret)) {
5018 ins = handle_unbox_gsharedvt (cfg, mono_class_from_mono_type (fsig->ret), ins, &bblock);
5019 } else if (MONO_TYPE_IS_PRIMITIVE (fsig->ret) || MONO_TYPE_ISSTRUCT (fsig->ret)) {
5023 NEW_BIALU_IMM (cfg, add, OP_ADD_IMM, alloc_dreg (cfg, STACK_MP), ins->dreg, sizeof (MonoObject));
5024 MONO_ADD_INS (cfg->cbb, add);
5026 NEW_LOAD_MEMBASE_TYPE (cfg, ins, fsig->ret, add->dreg, 0);
5027 MONO_ADD_INS (cfg->cbb, ins);
5028 /* ins represents the call result */
5031 GSHAREDVT_FAILURE (CEE_CALLVIRT);
5034 *ref_emit_widen = emit_widen;
5035 *ref_bblock = bblock;
5044 mono_emit_load_got_addr (MonoCompile *cfg)
5046 MonoInst *getaddr, *dummy_use;
5048 if (!cfg->got_var || cfg->got_var_allocated)
5051 MONO_INST_NEW (cfg, getaddr, OP_LOAD_GOTADDR);
5052 getaddr->cil_code = cfg->header->code;
5053 getaddr->dreg = cfg->got_var->dreg;
5055 /* Add it to the start of the first bblock */
5056 if (cfg->bb_entry->code) {
5057 getaddr->next = cfg->bb_entry->code;
5058 cfg->bb_entry->code = getaddr;
5061 MONO_ADD_INS (cfg->bb_entry, getaddr);
5063 cfg->got_var_allocated = TRUE;
5066 * Add a dummy use to keep the got_var alive, since real uses might
5067 * only be generated by the back ends.
5068 * Add it to end_bblock, so the variable's lifetime covers the whole
5070 * It would be better to make the usage of the got var explicit in all
5071 * cases when the backend needs it (i.e. calls, throw etc.), so this
5072 * wouldn't be needed.
5074 NEW_DUMMY_USE (cfg, dummy_use, cfg->got_var);
5075 MONO_ADD_INS (cfg->bb_exit, dummy_use);
5078 static int inline_limit;
5079 static gboolean inline_limit_inited;
5082 mono_method_check_inlining (MonoCompile *cfg, MonoMethod *method)
5084 MonoMethodHeaderSummary header;
5086 #ifdef MONO_ARCH_SOFT_FLOAT_FALLBACK
5087 MonoMethodSignature *sig = mono_method_signature (method);
5091 if (cfg->disable_inline)
5093 if (cfg->generic_sharing_context)
5096 if (cfg->inline_depth > 10)
5099 #ifdef MONO_ARCH_HAVE_LMF_OPS
5100 if (((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
5101 (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)) &&
5102 !MONO_TYPE_ISSTRUCT (signature->ret) && !mini_class_is_system_array (method->klass))
5107 if (!mono_method_get_header_summary (method, &header))
5110 /*runtime, icall and pinvoke are checked by summary call*/
5111 if ((method->iflags & METHOD_IMPL_ATTRIBUTE_NOINLINING) ||
5112 (method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED) ||
5113 (mono_class_is_marshalbyref (method->klass)) ||
5117 /* also consider num_locals? */
5118 /* Do the size check early to avoid creating vtables */
5119 if (!inline_limit_inited) {
5120 if (g_getenv ("MONO_INLINELIMIT"))
5121 inline_limit = atoi (g_getenv ("MONO_INLINELIMIT"));
5123 inline_limit = INLINE_LENGTH_LIMIT;
5124 inline_limit_inited = TRUE;
5126 if (header.code_size >= inline_limit && !(method->iflags & METHOD_IMPL_ATTRIBUTE_AGGRESSIVE_INLINING))
5130 * if we can initialize the class of the method right away, we do,
5131 * otherwise we don't allow inlining if the class needs initialization,
5132 * since it would mean inserting a call to mono_runtime_class_init()
5133 * inside the inlined code
5135 if (!(cfg->opt & MONO_OPT_SHARED)) {
5136 /* The AggressiveInlining hint is a good excuse to force that cctor to run. */
5137 if (method->iflags & METHOD_IMPL_ATTRIBUTE_AGGRESSIVE_INLINING) {
5138 vtable = mono_class_vtable (cfg->domain, method->klass);
5141 if (!cfg->compile_aot)
5142 mono_runtime_class_init (vtable);
5143 } else if (method->klass->flags & TYPE_ATTRIBUTE_BEFORE_FIELD_INIT) {
5144 if (cfg->run_cctors && method->klass->has_cctor) {
5145 /*FIXME it would easier and lazier to just use mono_class_try_get_vtable */
5146 if (!method->klass->runtime_info)
5147 /* No vtable created yet */
5149 vtable = mono_class_vtable (cfg->domain, method->klass);
5152 /* This makes so that inline cannot trigger */
5153 /* .cctors: too many apps depend on them */
5154 /* running with a specific order... */
5155 if (! vtable->initialized)
5157 mono_runtime_class_init (vtable);
5159 } else if (mono_class_needs_cctor_run (method->klass, NULL)) {
5160 if (!method->klass->runtime_info)
5161 /* No vtable created yet */
5163 vtable = mono_class_vtable (cfg->domain, method->klass);
5166 if (!vtable->initialized)
5171 * If we're compiling for shared code
5172 * the cctor will need to be run at aot method load time, for example,
5173 * or at the end of the compilation of the inlining method.
5175 if (mono_class_needs_cctor_run (method->klass, NULL) && !((method->klass->flags & TYPE_ATTRIBUTE_BEFORE_FIELD_INIT)))
5179 #ifdef MONO_ARCH_SOFT_FLOAT_FALLBACK
5180 if (mono_arch_is_soft_float ()) {
5182 if (sig->ret && sig->ret->type == MONO_TYPE_R4)
5184 for (i = 0; i < sig->param_count; ++i)
5185 if (!sig->params [i]->byref && sig->params [i]->type == MONO_TYPE_R4)
5190 if (g_list_find (cfg->dont_inline, method))
5197 mini_field_access_needs_cctor_run (MonoCompile *cfg, MonoMethod *method, MonoClass *klass, MonoVTable *vtable)
5199 if (!cfg->compile_aot) {
5201 if (vtable->initialized)
5205 if (klass->flags & TYPE_ATTRIBUTE_BEFORE_FIELD_INIT) {
5206 if (cfg->method == method)
5210 if (!mono_class_needs_cctor_run (klass, method))
5213 if (! (method->flags & METHOD_ATTRIBUTE_STATIC) && (klass == method->klass))
5214 /* The initialization is already done before the method is called */
5221 mini_emit_ldelema_1_ins (MonoCompile *cfg, MonoClass *klass, MonoInst *arr, MonoInst *index, gboolean bcheck)
5225 int mult_reg, add_reg, array_reg, index_reg, index2_reg;
5228 if (mini_is_gsharedvt_variable_klass (cfg, klass)) {
5231 mono_class_init (klass);
5232 size = mono_class_array_element_size (klass);
5235 mult_reg = alloc_preg (cfg);
5236 array_reg = arr->dreg;
5237 index_reg = index->dreg;
5239 #if SIZEOF_REGISTER == 8
5240 /* The array reg is 64 bits but the index reg is only 32 */
5241 if (COMPILE_LLVM (cfg)) {
5243 index2_reg = index_reg;
5245 index2_reg = alloc_preg (cfg);
5246 MONO_EMIT_NEW_UNALU (cfg, OP_SEXT_I4, index2_reg, index_reg);
5249 if (index->type == STACK_I8) {
5250 index2_reg = alloc_preg (cfg);
5251 MONO_EMIT_NEW_UNALU (cfg, OP_LCONV_TO_I4, index2_reg, index_reg);
5253 index2_reg = index_reg;
5258 MONO_EMIT_BOUNDS_CHECK (cfg, array_reg, MonoArray, max_length, index2_reg);
5260 #if defined(TARGET_X86) || defined(TARGET_AMD64)
5261 if (size == 1 || size == 2 || size == 4 || size == 8) {
5262 static const int fast_log2 [] = { 1, 0, 1, -1, 2, -1, -1, -1, 3 };
5264 EMIT_NEW_X86_LEA (cfg, ins, array_reg, index2_reg, fast_log2 [size], MONO_STRUCT_OFFSET (MonoArray, vector));
5265 ins->klass = mono_class_get_element_class (klass);
5266 ins->type = STACK_MP;
5272 add_reg = alloc_ireg_mp (cfg);
5275 MonoInst *rgctx_ins;
5278 g_assert (cfg->generic_sharing_context);
5279 context_used = mini_class_check_context_used (cfg, klass);
5280 g_assert (context_used);
5281 rgctx_ins = emit_get_gsharedvt_info (cfg, &klass->byval_arg, MONO_RGCTX_INFO_ARRAY_ELEMENT_SIZE);
5282 MONO_EMIT_NEW_BIALU (cfg, OP_IMUL, mult_reg, index2_reg, rgctx_ins->dreg);
5284 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_MUL_IMM, mult_reg, index2_reg, size);
5286 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, add_reg, array_reg, mult_reg);
5287 NEW_BIALU_IMM (cfg, ins, OP_PADD_IMM, add_reg, add_reg, MONO_STRUCT_OFFSET (MonoArray, vector));
5288 ins->klass = mono_class_get_element_class (klass);
5289 ins->type = STACK_MP;
5290 MONO_ADD_INS (cfg->cbb, ins);
5295 #ifndef MONO_ARCH_EMULATE_MUL_DIV
5297 mini_emit_ldelema_2_ins (MonoCompile *cfg, MonoClass *klass, MonoInst *arr, MonoInst *index_ins1, MonoInst *index_ins2)
5299 int bounds_reg = alloc_preg (cfg);
5300 int add_reg = alloc_ireg_mp (cfg);
5301 int mult_reg = alloc_preg (cfg);
5302 int mult2_reg = alloc_preg (cfg);
5303 int low1_reg = alloc_preg (cfg);
5304 int low2_reg = alloc_preg (cfg);
5305 int high1_reg = alloc_preg (cfg);
5306 int high2_reg = alloc_preg (cfg);
5307 int realidx1_reg = alloc_preg (cfg);
5308 int realidx2_reg = alloc_preg (cfg);
5309 int sum_reg = alloc_preg (cfg);
5310 int index1, index2, tmpreg;
5314 mono_class_init (klass);
5315 size = mono_class_array_element_size (klass);
5317 index1 = index_ins1->dreg;
5318 index2 = index_ins2->dreg;
5320 #if SIZEOF_REGISTER == 8
5321 /* The array reg is 64 bits but the index reg is only 32 */
5322 if (COMPILE_LLVM (cfg)) {
5325 tmpreg = alloc_preg (cfg);
5326 MONO_EMIT_NEW_UNALU (cfg, OP_SEXT_I4, tmpreg, index1);
5328 tmpreg = alloc_preg (cfg);
5329 MONO_EMIT_NEW_UNALU (cfg, OP_SEXT_I4, tmpreg, index2);
5333 // FIXME: Do we need to do something here for i8 indexes, like in ldelema_1_ins ?
5337 /* range checking */
5338 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, bounds_reg,
5339 arr->dreg, MONO_STRUCT_OFFSET (MonoArray, bounds));
5341 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI4_MEMBASE, low1_reg,
5342 bounds_reg, MONO_STRUCT_OFFSET (MonoArrayBounds, lower_bound));
5343 MONO_EMIT_NEW_BIALU (cfg, OP_PSUB, realidx1_reg, index1, low1_reg);
5344 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI4_MEMBASE, high1_reg,
5345 bounds_reg, MONO_STRUCT_OFFSET (MonoArrayBounds, length));
5346 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, high1_reg, realidx1_reg);
5347 MONO_EMIT_NEW_COND_EXC (cfg, LE_UN, "IndexOutOfRangeException");
5349 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI4_MEMBASE, low2_reg,
5350 bounds_reg, sizeof (MonoArrayBounds) + MONO_STRUCT_OFFSET (MonoArrayBounds, lower_bound));
5351 MONO_EMIT_NEW_BIALU (cfg, OP_PSUB, realidx2_reg, index2, low2_reg);
5352 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI4_MEMBASE, high2_reg,
5353 bounds_reg, sizeof (MonoArrayBounds) + MONO_STRUCT_OFFSET (MonoArrayBounds, length));
5354 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, high2_reg, realidx2_reg);
5355 MONO_EMIT_NEW_COND_EXC (cfg, LE_UN, "IndexOutOfRangeException");
5357 MONO_EMIT_NEW_BIALU (cfg, OP_PMUL, mult_reg, high2_reg, realidx1_reg);
5358 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, sum_reg, mult_reg, realidx2_reg);
5359 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_PMUL_IMM, mult2_reg, sum_reg, size);
5360 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, add_reg, mult2_reg, arr->dreg);
5361 NEW_BIALU_IMM (cfg, ins, OP_PADD_IMM, add_reg, add_reg, MONO_STRUCT_OFFSET (MonoArray, vector));
5363 ins->type = STACK_MP;
5365 MONO_ADD_INS (cfg->cbb, ins);
5372 mini_emit_ldelema_ins (MonoCompile *cfg, MonoMethod *cmethod, MonoInst **sp, unsigned char *ip, gboolean is_set)
5376 MonoMethod *addr_method;
5378 MonoClass *eclass = cmethod->klass->element_class;
5380 rank = mono_method_signature (cmethod)->param_count - (is_set? 1: 0);
5383 return mini_emit_ldelema_1_ins (cfg, eclass, sp [0], sp [1], TRUE);
5385 #ifndef MONO_ARCH_EMULATE_MUL_DIV
5386 /* emit_ldelema_2 depends on OP_LMUL */
5387 if (rank == 2 && (cfg->opt & MONO_OPT_INTRINS) && !mini_is_gsharedvt_variable_klass (cfg, eclass)) {
5388 return mini_emit_ldelema_2_ins (cfg, eclass, sp [0], sp [1], sp [2]);
5392 if (mini_is_gsharedvt_variable_klass (cfg, eclass))
5395 element_size = mono_class_array_element_size (eclass);
5396 addr_method = mono_marshal_get_array_address (rank, element_size);
5397 addr = mono_emit_method_call (cfg, addr_method, sp, NULL);
5402 static MonoBreakPolicy
5403 always_insert_breakpoint (MonoMethod *method)
5405 return MONO_BREAK_POLICY_ALWAYS;
5408 static MonoBreakPolicyFunc break_policy_func = always_insert_breakpoint;
5411 * mono_set_break_policy:
5412 * policy_callback: the new callback function
5414 * Allow embedders to decide wherther to actually obey breakpoint instructions
5415 * (both break IL instructions and Debugger.Break () method calls), for example
5416 * to not allow an app to be aborted by a perfectly valid IL opcode when executing
5417 * untrusted or semi-trusted code.
5419 * @policy_callback will be called every time a break point instruction needs to
5420 * be inserted with the method argument being the method that calls Debugger.Break()
5421 * or has the IL break instruction. The callback should return #MONO_BREAK_POLICY_NEVER
5422 * if it wants the breakpoint to not be effective in the given method.
5423 * #MONO_BREAK_POLICY_ALWAYS is the default.
5426 mono_set_break_policy (MonoBreakPolicyFunc policy_callback)
5428 if (policy_callback)
5429 break_policy_func = policy_callback;
5431 break_policy_func = always_insert_breakpoint;
5435 should_insert_brekpoint (MonoMethod *method) {
5436 switch (break_policy_func (method)) {
5437 case MONO_BREAK_POLICY_ALWAYS:
5439 case MONO_BREAK_POLICY_NEVER:
5441 case MONO_BREAK_POLICY_ON_DBG:
5442 g_warning ("mdb no longer supported");
5445 g_warning ("Incorrect value returned from break policy callback");
5450 /* optimize the simple GetGenericValueImpl/SetGenericValueImpl generic icalls */
5452 emit_array_generic_access (MonoCompile *cfg, MonoMethodSignature *fsig, MonoInst **args, int is_set)
5454 MonoInst *addr, *store, *load;
5455 MonoClass *eklass = mono_class_from_mono_type (fsig->params [2]);
5457 /* the bounds check is already done by the callers */
5458 addr = mini_emit_ldelema_1_ins (cfg, eklass, args [0], args [1], FALSE);
5460 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, load, &eklass->byval_arg, args [2]->dreg, 0);
5461 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, store, &eklass->byval_arg, addr->dreg, 0, load->dreg);
5462 if (mini_type_is_reference (cfg, fsig->params [2]))
5463 emit_write_barrier (cfg, addr, load);
5465 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, load, &eklass->byval_arg, addr->dreg, 0);
5466 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, store, &eklass->byval_arg, args [2]->dreg, 0, load->dreg);
5473 generic_class_is_reference_type (MonoCompile *cfg, MonoClass *klass)
5475 return mini_type_is_reference (cfg, &klass->byval_arg);
5479 emit_array_store (MonoCompile *cfg, MonoClass *klass, MonoInst **sp, gboolean safety_checks)
5481 if (safety_checks && generic_class_is_reference_type (cfg, klass) &&
5482 !(sp [2]->opcode == OP_PCONST && sp [2]->inst_p0 == NULL)) {
5483 MonoClass *obj_array = mono_array_class_get_cached (mono_defaults.object_class, 1);
5484 MonoMethod *helper = mono_marshal_get_virtual_stelemref (obj_array);
5485 MonoInst *iargs [3];
5488 mono_class_setup_vtable (obj_array);
5489 g_assert (helper->slot);
5491 if (sp [0]->type != STACK_OBJ)
5493 if (sp [2]->type != STACK_OBJ)
5500 return mono_emit_method_call (cfg, helper, iargs, sp [0]);
5504 if (mini_is_gsharedvt_variable_klass (cfg, klass)) {
5507 // FIXME-VT: OP_ICONST optimization
5508 addr = mini_emit_ldelema_1_ins (cfg, klass, sp [0], sp [1], TRUE);
5509 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, addr->dreg, 0, sp [2]->dreg);
5510 ins->opcode = OP_STOREV_MEMBASE;
5511 } else if (sp [1]->opcode == OP_ICONST) {
5512 int array_reg = sp [0]->dreg;
5513 int index_reg = sp [1]->dreg;
5514 int offset = (mono_class_array_element_size (klass) * sp [1]->inst_c0) + MONO_STRUCT_OFFSET (MonoArray, vector);
5517 MONO_EMIT_BOUNDS_CHECK (cfg, array_reg, MonoArray, max_length, index_reg);
5518 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, array_reg, offset, sp [2]->dreg);
5520 MonoInst *addr = mini_emit_ldelema_1_ins (cfg, klass, sp [0], sp [1], safety_checks);
5521 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, addr->dreg, 0, sp [2]->dreg);
5522 if (generic_class_is_reference_type (cfg, klass))
5523 emit_write_barrier (cfg, addr, sp [2]);
5530 emit_array_unsafe_access (MonoCompile *cfg, MonoMethodSignature *fsig, MonoInst **args, int is_set)
5535 eklass = mono_class_from_mono_type (fsig->params [2]);
5537 eklass = mono_class_from_mono_type (fsig->ret);
5540 return emit_array_store (cfg, eklass, args, FALSE);
5542 MonoInst *ins, *addr = mini_emit_ldelema_1_ins (cfg, eklass, args [0], args [1], FALSE);
5543 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &eklass->byval_arg, addr->dreg, 0);
5549 is_unsafe_mov_compatible (MonoClass *param_klass, MonoClass *return_klass)
5553 //Only allow for valuetypes
5554 if (!param_klass->valuetype || !return_klass->valuetype)
5558 if (param_klass->has_references || return_klass->has_references)
5561 /* Avoid mixing structs and primitive types/enums, they need to be handled differently in the JIT */
5562 if ((MONO_TYPE_ISSTRUCT (¶m_klass->byval_arg) && !MONO_TYPE_ISSTRUCT (&return_klass->byval_arg)) ||
5563 (!MONO_TYPE_ISSTRUCT (¶m_klass->byval_arg) && MONO_TYPE_ISSTRUCT (&return_klass->byval_arg)))
5566 if (param_klass->byval_arg.type == MONO_TYPE_R4 || param_klass->byval_arg.type == MONO_TYPE_R8 ||
5567 return_klass->byval_arg.type == MONO_TYPE_R4 || return_klass->byval_arg.type == MONO_TYPE_R8)
5570 //And have the same size
5571 if (mono_class_value_size (param_klass, &align) != mono_class_value_size (return_klass, &align))
5577 emit_array_unsafe_mov (MonoCompile *cfg, MonoMethodSignature *fsig, MonoInst **args)
5579 MonoClass *param_klass = mono_class_from_mono_type (fsig->params [0]);
5580 MonoClass *return_klass = mono_class_from_mono_type (fsig->ret);
5582 //Valuetypes that are semantically equivalent
5583 if (is_unsafe_mov_compatible (param_klass, return_klass))
5586 //Arrays of valuetypes that are semantically equivalent
5587 if (param_klass->rank == 1 && return_klass->rank == 1 && is_unsafe_mov_compatible (param_klass->element_class, return_klass->element_class))
5594 mini_emit_inst_for_ctor (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
5596 #ifdef MONO_ARCH_SIMD_INTRINSICS
5597 MonoInst *ins = NULL;
5599 if (cfg->opt & MONO_OPT_SIMD) {
5600 ins = mono_emit_simd_intrinsics (cfg, cmethod, fsig, args);
5606 return mono_emit_native_types_intrinsics (cfg, cmethod, fsig, args);
5610 emit_memory_barrier (MonoCompile *cfg, int kind)
5612 MonoInst *ins = NULL;
5613 MONO_INST_NEW (cfg, ins, OP_MEMORY_BARRIER);
5614 MONO_ADD_INS (cfg->cbb, ins);
5615 ins->backend.memory_barrier_kind = kind;
5621 llvm_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
5623 MonoInst *ins = NULL;
5626 /* The LLVM backend supports these intrinsics */
5627 if (cmethod->klass == mono_defaults.math_class) {
5628 if (strcmp (cmethod->name, "Sin") == 0) {
5630 } else if (strcmp (cmethod->name, "Cos") == 0) {
5632 } else if (strcmp (cmethod->name, "Sqrt") == 0) {
5634 } else if (strcmp (cmethod->name, "Abs") == 0 && fsig->params [0]->type == MONO_TYPE_R8) {
5638 if (opcode && fsig->param_count == 1) {
5639 MONO_INST_NEW (cfg, ins, opcode);
5640 ins->type = STACK_R8;
5641 ins->dreg = mono_alloc_freg (cfg);
5642 ins->sreg1 = args [0]->dreg;
5643 MONO_ADD_INS (cfg->cbb, ins);
5647 if (cfg->opt & MONO_OPT_CMOV) {
5648 if (strcmp (cmethod->name, "Min") == 0) {
5649 if (fsig->params [0]->type == MONO_TYPE_I4)
5651 if (fsig->params [0]->type == MONO_TYPE_U4)
5652 opcode = OP_IMIN_UN;
5653 else if (fsig->params [0]->type == MONO_TYPE_I8)
5655 else if (fsig->params [0]->type == MONO_TYPE_U8)
5656 opcode = OP_LMIN_UN;
5657 } else if (strcmp (cmethod->name, "Max") == 0) {
5658 if (fsig->params [0]->type == MONO_TYPE_I4)
5660 if (fsig->params [0]->type == MONO_TYPE_U4)
5661 opcode = OP_IMAX_UN;
5662 else if (fsig->params [0]->type == MONO_TYPE_I8)
5664 else if (fsig->params [0]->type == MONO_TYPE_U8)
5665 opcode = OP_LMAX_UN;
5669 if (opcode && fsig->param_count == 2) {
5670 MONO_INST_NEW (cfg, ins, opcode);
5671 ins->type = fsig->params [0]->type == MONO_TYPE_I4 ? STACK_I4 : STACK_I8;
5672 ins->dreg = mono_alloc_ireg (cfg);
5673 ins->sreg1 = args [0]->dreg;
5674 ins->sreg2 = args [1]->dreg;
5675 MONO_ADD_INS (cfg->cbb, ins);
5683 mini_emit_inst_for_sharable_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
5685 if (cmethod->klass == mono_defaults.array_class) {
5686 if (strcmp (cmethod->name, "UnsafeStore") == 0)
5687 return emit_array_unsafe_access (cfg, fsig, args, TRUE);
5688 else if (strcmp (cmethod->name, "UnsafeLoad") == 0)
5689 return emit_array_unsafe_access (cfg, fsig, args, FALSE);
5690 else if (strcmp (cmethod->name, "UnsafeMov") == 0)
5691 return emit_array_unsafe_mov (cfg, fsig, args);
5698 mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
5700 MonoInst *ins = NULL;
5702 static MonoClass *runtime_helpers_class = NULL;
5703 if (! runtime_helpers_class)
5704 runtime_helpers_class = mono_class_from_name (mono_defaults.corlib,
5705 "System.Runtime.CompilerServices", "RuntimeHelpers");
5707 if (cmethod->klass == mono_defaults.string_class) {
5708 if (strcmp (cmethod->name, "get_Chars") == 0 && fsig->param_count + fsig->hasthis == 2) {
5709 int dreg = alloc_ireg (cfg);
5710 int index_reg = alloc_preg (cfg);
5711 int add_reg = alloc_preg (cfg);
5713 #if SIZEOF_REGISTER == 8
5714 /* The array reg is 64 bits but the index reg is only 32 */
5715 MONO_EMIT_NEW_UNALU (cfg, OP_SEXT_I4, index_reg, args [1]->dreg);
5717 index_reg = args [1]->dreg;
5719 MONO_EMIT_BOUNDS_CHECK (cfg, args [0]->dreg, MonoString, length, index_reg);
5721 #if defined(TARGET_X86) || defined(TARGET_AMD64)
5722 EMIT_NEW_X86_LEA (cfg, ins, args [0]->dreg, index_reg, 1, MONO_STRUCT_OFFSET (MonoString, chars));
5723 add_reg = ins->dreg;
5724 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADU2_MEMBASE, dreg,
5727 int mult_reg = alloc_preg (cfg);
5728 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SHL_IMM, mult_reg, index_reg, 1);
5729 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, add_reg, mult_reg, args [0]->dreg);
5730 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADU2_MEMBASE, dreg,
5731 add_reg, MONO_STRUCT_OFFSET (MonoString, chars));
5733 type_from_op (cfg, ins, NULL, NULL);
5735 } else if (strcmp (cmethod->name, "get_Length") == 0 && fsig->param_count + fsig->hasthis == 1) {
5736 int dreg = alloc_ireg (cfg);
5737 /* Decompose later to allow more optimizations */
5738 EMIT_NEW_UNALU (cfg, ins, OP_STRLEN, dreg, args [0]->dreg);
5739 ins->type = STACK_I4;
5740 ins->flags |= MONO_INST_FAULT;
5741 cfg->cbb->has_array_access = TRUE;
5742 cfg->flags |= MONO_CFG_HAS_ARRAY_ACCESS;
5747 } else if (cmethod->klass == mono_defaults.object_class) {
5749 if (strcmp (cmethod->name, "GetType") == 0 && fsig->param_count + fsig->hasthis == 1) {
5750 int dreg = alloc_ireg_ref (cfg);
5751 int vt_reg = alloc_preg (cfg);
5752 MONO_EMIT_NEW_LOAD_MEMBASE_FAULT (cfg, vt_reg, args [0]->dreg, MONO_STRUCT_OFFSET (MonoObject, vtable));
5753 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, dreg, vt_reg, MONO_STRUCT_OFFSET (MonoVTable, type));
5754 type_from_op (cfg, ins, NULL, NULL);
5757 #if !defined(MONO_ARCH_EMULATE_MUL_DIV)
5758 } else if (strcmp (cmethod->name, "InternalGetHashCode") == 0 && fsig->param_count == 1 && !mono_gc_is_moving ()) {
5759 int dreg = alloc_ireg (cfg);
5760 int t1 = alloc_ireg (cfg);
5762 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SHL_IMM, t1, args [0]->dreg, 3);
5763 EMIT_NEW_BIALU_IMM (cfg, ins, OP_MUL_IMM, dreg, t1, 2654435761u);
5764 ins->type = STACK_I4;
5768 } else if (strcmp (cmethod->name, ".ctor") == 0 && fsig->param_count == 0) {
5769 MONO_INST_NEW (cfg, ins, OP_NOP);
5770 MONO_ADD_INS (cfg->cbb, ins);
5774 } else if (cmethod->klass == mono_defaults.array_class) {
5775 if (strcmp (cmethod->name, "GetGenericValueImpl") == 0 && fsig->param_count + fsig->hasthis == 3 && !cfg->gsharedvt)
5776 return emit_array_generic_access (cfg, fsig, args, FALSE);
5777 else if (strcmp (cmethod->name, "SetGenericValueImpl") == 0 && fsig->param_count + fsig->hasthis == 3 && !cfg->gsharedvt)
5778 return emit_array_generic_access (cfg, fsig, args, TRUE);
5780 #ifndef MONO_BIG_ARRAYS
5782 * This is an inline version of GetLength/GetLowerBound(0) used frequently in
5785 else if (((strcmp (cmethod->name, "GetLength") == 0 && fsig->param_count + fsig->hasthis == 2) ||
5786 (strcmp (cmethod->name, "GetLowerBound") == 0 && fsig->param_count + fsig->hasthis == 2)) &&
5787 args [1]->opcode == OP_ICONST && args [1]->inst_c0 == 0) {
5788 int dreg = alloc_ireg (cfg);
5789 int bounds_reg = alloc_ireg_mp (cfg);
5790 MonoBasicBlock *end_bb, *szarray_bb;
5791 gboolean get_length = strcmp (cmethod->name, "GetLength") == 0;
5793 NEW_BBLOCK (cfg, end_bb);
5794 NEW_BBLOCK (cfg, szarray_bb);
5796 EMIT_NEW_LOAD_MEMBASE_FAULT (cfg, ins, OP_LOAD_MEMBASE, bounds_reg,
5797 args [0]->dreg, MONO_STRUCT_OFFSET (MonoArray, bounds));
5798 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, bounds_reg, 0);
5799 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, szarray_bb);
5800 /* Non-szarray case */
5802 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADI4_MEMBASE, dreg,
5803 bounds_reg, MONO_STRUCT_OFFSET (MonoArrayBounds, length));
5805 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADI4_MEMBASE, dreg,
5806 bounds_reg, MONO_STRUCT_OFFSET (MonoArrayBounds, lower_bound));
5807 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
5808 MONO_START_BB (cfg, szarray_bb);
5811 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADI4_MEMBASE, dreg,
5812 args [0]->dreg, MONO_STRUCT_OFFSET (MonoArray, max_length));
5814 MONO_EMIT_NEW_ICONST (cfg, dreg, 0);
5815 MONO_START_BB (cfg, end_bb);
5817 EMIT_NEW_UNALU (cfg, ins, OP_MOVE, dreg, dreg);
5818 ins->type = STACK_I4;
5824 if (cmethod->name [0] != 'g')
5827 if (strcmp (cmethod->name, "get_Rank") == 0 && fsig->param_count + fsig->hasthis == 1) {
5828 int dreg = alloc_ireg (cfg);
5829 int vtable_reg = alloc_preg (cfg);
5830 MONO_EMIT_NEW_LOAD_MEMBASE_OP_FAULT (cfg, OP_LOAD_MEMBASE, vtable_reg,
5831 args [0]->dreg, MONO_STRUCT_OFFSET (MonoObject, vtable));
5832 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADU1_MEMBASE, dreg,
5833 vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, rank));
5834 type_from_op (cfg, ins, NULL, NULL);
5837 } else if (strcmp (cmethod->name, "get_Length") == 0 && fsig->param_count + fsig->hasthis == 1) {
5838 int dreg = alloc_ireg (cfg);
5840 EMIT_NEW_LOAD_MEMBASE_FAULT (cfg, ins, OP_LOADI4_MEMBASE, dreg,
5841 args [0]->dreg, MONO_STRUCT_OFFSET (MonoArray, max_length));
5842 type_from_op (cfg, ins, NULL, NULL);
5847 } else if (cmethod->klass == runtime_helpers_class) {
5849 if (strcmp (cmethod->name, "get_OffsetToStringData") == 0 && fsig->param_count == 0) {
5850 EMIT_NEW_ICONST (cfg, ins, MONO_STRUCT_OFFSET (MonoString, chars));
5854 } else if (cmethod->klass == mono_defaults.thread_class) {
5855 if (strcmp (cmethod->name, "SpinWait_nop") == 0 && fsig->param_count == 0) {
5856 MONO_INST_NEW (cfg, ins, OP_RELAXED_NOP);
5857 MONO_ADD_INS (cfg->cbb, ins);
5859 } else if (strcmp (cmethod->name, "MemoryBarrier") == 0 && fsig->param_count == 0) {
5860 return emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_SEQ);
5861 } else if (!strcmp (cmethod->name, "VolatileRead") && fsig->param_count == 1) {
5863 gboolean is_ref = mini_type_is_reference (cfg, fsig->params [0]);
5865 if (fsig->params [0]->type == MONO_TYPE_I1)
5866 opcode = OP_LOADI1_MEMBASE;
5867 else if (fsig->params [0]->type == MONO_TYPE_U1)
5868 opcode = OP_LOADU1_MEMBASE;
5869 else if (fsig->params [0]->type == MONO_TYPE_I2)
5870 opcode = OP_LOADI2_MEMBASE;
5871 else if (fsig->params [0]->type == MONO_TYPE_U2)
5872 opcode = OP_LOADU2_MEMBASE;
5873 else if (fsig->params [0]->type == MONO_TYPE_I4)
5874 opcode = OP_LOADI4_MEMBASE;
5875 else if (fsig->params [0]->type == MONO_TYPE_U4)
5876 opcode = OP_LOADU4_MEMBASE;
5877 else if (fsig->params [0]->type == MONO_TYPE_I8 || fsig->params [0]->type == MONO_TYPE_U8)
5878 opcode = OP_LOADI8_MEMBASE;
5879 else if (fsig->params [0]->type == MONO_TYPE_R4)
5880 opcode = OP_LOADR4_MEMBASE;
5881 else if (fsig->params [0]->type == MONO_TYPE_R8)
5882 opcode = OP_LOADR8_MEMBASE;
5883 else if (is_ref || fsig->params [0]->type == MONO_TYPE_I || fsig->params [0]->type == MONO_TYPE_U)
5884 opcode = OP_LOAD_MEMBASE;
5887 MONO_INST_NEW (cfg, ins, opcode);
5888 ins->inst_basereg = args [0]->dreg;
5889 ins->inst_offset = 0;
5890 MONO_ADD_INS (cfg->cbb, ins);
5892 switch (fsig->params [0]->type) {
5899 ins->dreg = mono_alloc_ireg (cfg);
5900 ins->type = STACK_I4;
5904 ins->dreg = mono_alloc_lreg (cfg);
5905 ins->type = STACK_I8;
5909 ins->dreg = mono_alloc_ireg (cfg);
5910 #if SIZEOF_REGISTER == 8
5911 ins->type = STACK_I8;
5913 ins->type = STACK_I4;
5918 ins->dreg = mono_alloc_freg (cfg);
5919 ins->type = STACK_R8;
5922 g_assert (mini_type_is_reference (cfg, fsig->params [0]));
5923 ins->dreg = mono_alloc_ireg_ref (cfg);
5924 ins->type = STACK_OBJ;
5928 if (opcode == OP_LOADI8_MEMBASE)
5929 ins = mono_decompose_opcode (cfg, ins, NULL);
5931 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_ACQ);
5935 } else if (!strcmp (cmethod->name, "VolatileWrite") && fsig->param_count == 2) {
5937 gboolean is_ref = mini_type_is_reference (cfg, fsig->params [0]);
5939 if (fsig->params [0]->type == MONO_TYPE_I1 || fsig->params [0]->type == MONO_TYPE_U1)
5940 opcode = OP_STOREI1_MEMBASE_REG;
5941 else if (fsig->params [0]->type == MONO_TYPE_I2 || fsig->params [0]->type == MONO_TYPE_U2)
5942 opcode = OP_STOREI2_MEMBASE_REG;
5943 else if (fsig->params [0]->type == MONO_TYPE_I4 || fsig->params [0]->type == MONO_TYPE_U4)
5944 opcode = OP_STOREI4_MEMBASE_REG;
5945 else if (fsig->params [0]->type == MONO_TYPE_I8 || fsig->params [0]->type == MONO_TYPE_U8)
5946 opcode = OP_STOREI8_MEMBASE_REG;
5947 else if (fsig->params [0]->type == MONO_TYPE_R4)
5948 opcode = OP_STORER4_MEMBASE_REG;
5949 else if (fsig->params [0]->type == MONO_TYPE_R8)
5950 opcode = OP_STORER8_MEMBASE_REG;
5951 else if (is_ref || fsig->params [0]->type == MONO_TYPE_I || fsig->params [0]->type == MONO_TYPE_U)
5952 opcode = OP_STORE_MEMBASE_REG;
5955 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_REL);
5957 MONO_INST_NEW (cfg, ins, opcode);
5958 ins->sreg1 = args [1]->dreg;
5959 ins->inst_destbasereg = args [0]->dreg;
5960 ins->inst_offset = 0;
5961 MONO_ADD_INS (cfg->cbb, ins);
5963 if (opcode == OP_STOREI8_MEMBASE_REG)
5964 ins = mono_decompose_opcode (cfg, ins, NULL);
5969 } else if (cmethod->klass == mono_defaults.monitor_class) {
5970 #if defined(MONO_ARCH_MONITOR_OBJECT_REG)
5971 if (strcmp (cmethod->name, "Enter") == 0 && fsig->param_count == 1) {
5974 if (COMPILE_LLVM (cfg)) {
5976 * Pass the argument normally, the LLVM backend will handle the
5977 * calling convention problems.
5979 call = (MonoCallInst*)mono_emit_abs_call (cfg, MONO_PATCH_INFO_MONITOR_ENTER, NULL, helper_sig_monitor_enter_exit_trampoline_llvm, args);
5981 call = (MonoCallInst*)mono_emit_abs_call (cfg, MONO_PATCH_INFO_MONITOR_ENTER,
5982 NULL, helper_sig_monitor_enter_exit_trampoline, NULL);
5983 mono_call_inst_add_outarg_reg (cfg, call, args [0]->dreg,
5984 MONO_ARCH_MONITOR_OBJECT_REG, FALSE);
5987 return (MonoInst*)call;
5988 #if defined(MONO_ARCH_MONITOR_LOCK_TAKEN_REG)
5989 } else if (strcmp (cmethod->name, "Enter") == 0 && fsig->param_count == 2) {
5992 if (COMPILE_LLVM (cfg)) {
5994 * Pass the argument normally, the LLVM backend will handle the
5995 * calling convention problems.
5997 call = (MonoCallInst*)mono_emit_abs_call (cfg, MONO_PATCH_INFO_MONITOR_ENTER_V4, NULL, helper_sig_monitor_enter_v4_trampoline_llvm, args);
5999 call = (MonoCallInst*)mono_emit_abs_call (cfg, MONO_PATCH_INFO_MONITOR_ENTER_V4,
6000 NULL, helper_sig_monitor_enter_exit_trampoline, NULL);
6001 mono_call_inst_add_outarg_reg (cfg, call, args [0]->dreg, MONO_ARCH_MONITOR_OBJECT_REG, FALSE);
6002 mono_call_inst_add_outarg_reg (cfg, call, args [1]->dreg, MONO_ARCH_MONITOR_LOCK_TAKEN_REG, FALSE);
6005 return (MonoInst*)call;
6007 } else if (strcmp (cmethod->name, "Exit") == 0 && fsig->param_count == 1) {
6010 if (COMPILE_LLVM (cfg)) {
6011 call = (MonoCallInst*)mono_emit_abs_call (cfg, MONO_PATCH_INFO_MONITOR_EXIT, NULL, helper_sig_monitor_enter_exit_trampoline_llvm, args);
6013 call = (MonoCallInst*)mono_emit_abs_call (cfg, MONO_PATCH_INFO_MONITOR_EXIT,
6014 NULL, helper_sig_monitor_enter_exit_trampoline, NULL);
6015 mono_call_inst_add_outarg_reg (cfg, call, args [0]->dreg,
6016 MONO_ARCH_MONITOR_OBJECT_REG, FALSE);
6019 return (MonoInst*)call;
6022 } else if (cmethod->klass->image == mono_defaults.corlib &&
6023 (strcmp (cmethod->klass->name_space, "System.Threading") == 0) &&
6024 (strcmp (cmethod->klass->name, "Interlocked") == 0)) {
6027 #if SIZEOF_REGISTER == 8
6028 if (strcmp (cmethod->name, "Read") == 0 && fsig->param_count == 1 && (fsig->params [0]->type == MONO_TYPE_I8)) {
6029 if (mono_arch_opcode_supported (OP_ATOMIC_LOAD_I8)) {
6030 MONO_INST_NEW (cfg, ins, OP_ATOMIC_LOAD_I8);
6031 ins->dreg = mono_alloc_preg (cfg);
6032 ins->sreg1 = args [0]->dreg;
6033 ins->type = STACK_I8;
6034 ins->backend.memory_barrier_kind = MONO_MEMORY_BARRIER_SEQ;
6035 MONO_ADD_INS (cfg->cbb, ins);
6039 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_SEQ);
6041 /* 64 bit reads are already atomic */
6042 MONO_INST_NEW (cfg, load_ins, OP_LOADI8_MEMBASE);
6043 load_ins->dreg = mono_alloc_preg (cfg);
6044 load_ins->inst_basereg = args [0]->dreg;
6045 load_ins->inst_offset = 0;
6046 load_ins->type = STACK_I8;
6047 MONO_ADD_INS (cfg->cbb, load_ins);
6049 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_SEQ);
6056 if (strcmp (cmethod->name, "Increment") == 0 && fsig->param_count == 1) {
6057 MonoInst *ins_iconst;
6060 if (fsig->params [0]->type == MONO_TYPE_I4) {
6061 opcode = OP_ATOMIC_ADD_I4;
6062 cfg->has_atomic_add_i4 = TRUE;
6064 #if SIZEOF_REGISTER == 8
6065 else if (fsig->params [0]->type == MONO_TYPE_I8)
6066 opcode = OP_ATOMIC_ADD_I8;
6069 if (!mono_arch_opcode_supported (opcode))
6071 MONO_INST_NEW (cfg, ins_iconst, OP_ICONST);
6072 ins_iconst->inst_c0 = 1;
6073 ins_iconst->dreg = mono_alloc_ireg (cfg);
6074 MONO_ADD_INS (cfg->cbb, ins_iconst);
6076 MONO_INST_NEW (cfg, ins, opcode);
6077 ins->dreg = mono_alloc_ireg (cfg);
6078 ins->inst_basereg = args [0]->dreg;
6079 ins->inst_offset = 0;
6080 ins->sreg2 = ins_iconst->dreg;
6081 ins->type = (opcode == OP_ATOMIC_ADD_I4) ? STACK_I4 : STACK_I8;
6082 MONO_ADD_INS (cfg->cbb, ins);
6084 } else if (strcmp (cmethod->name, "Decrement") == 0 && fsig->param_count == 1) {
6085 MonoInst *ins_iconst;
6088 if (fsig->params [0]->type == MONO_TYPE_I4) {
6089 opcode = OP_ATOMIC_ADD_I4;
6090 cfg->has_atomic_add_i4 = TRUE;
6092 #if SIZEOF_REGISTER == 8
6093 else if (fsig->params [0]->type == MONO_TYPE_I8)
6094 opcode = OP_ATOMIC_ADD_I8;
6097 if (!mono_arch_opcode_supported (opcode))
6099 MONO_INST_NEW (cfg, ins_iconst, OP_ICONST);
6100 ins_iconst->inst_c0 = -1;
6101 ins_iconst->dreg = mono_alloc_ireg (cfg);
6102 MONO_ADD_INS (cfg->cbb, ins_iconst);
6104 MONO_INST_NEW (cfg, ins, opcode);
6105 ins->dreg = mono_alloc_ireg (cfg);
6106 ins->inst_basereg = args [0]->dreg;
6107 ins->inst_offset = 0;
6108 ins->sreg2 = ins_iconst->dreg;
6109 ins->type = (opcode == OP_ATOMIC_ADD_I4) ? STACK_I4 : STACK_I8;
6110 MONO_ADD_INS (cfg->cbb, ins);
6112 } else if (strcmp (cmethod->name, "Add") == 0 && fsig->param_count == 2) {
6115 if (fsig->params [0]->type == MONO_TYPE_I4) {
6116 opcode = OP_ATOMIC_ADD_I4;
6117 cfg->has_atomic_add_i4 = TRUE;
6119 #if SIZEOF_REGISTER == 8
6120 else if (fsig->params [0]->type == MONO_TYPE_I8)
6121 opcode = OP_ATOMIC_ADD_I8;
6124 if (!mono_arch_opcode_supported (opcode))
6126 MONO_INST_NEW (cfg, ins, opcode);
6127 ins->dreg = mono_alloc_ireg (cfg);
6128 ins->inst_basereg = args [0]->dreg;
6129 ins->inst_offset = 0;
6130 ins->sreg2 = args [1]->dreg;
6131 ins->type = (opcode == OP_ATOMIC_ADD_I4) ? STACK_I4 : STACK_I8;
6132 MONO_ADD_INS (cfg->cbb, ins);
6135 else if (strcmp (cmethod->name, "Exchange") == 0 && fsig->param_count == 2) {
6136 MonoInst *f2i = NULL, *i2f;
6137 guint32 opcode, f2i_opcode, i2f_opcode;
6138 gboolean is_ref = mini_type_is_reference (cfg, fsig->params [0]);
6139 gboolean is_float = fsig->params [0]->type == MONO_TYPE_R4 || fsig->params [0]->type == MONO_TYPE_R8;
6141 if (fsig->params [0]->type == MONO_TYPE_I4 ||
6142 fsig->params [0]->type == MONO_TYPE_R4) {
6143 opcode = OP_ATOMIC_EXCHANGE_I4;
6144 f2i_opcode = OP_MOVE_F_TO_I4;
6145 i2f_opcode = OP_MOVE_I4_TO_F;
6146 cfg->has_atomic_exchange_i4 = TRUE;
6148 #if SIZEOF_REGISTER == 8
6150 fsig->params [0]->type == MONO_TYPE_I8 ||
6151 fsig->params [0]->type == MONO_TYPE_R8 ||
6152 fsig->params [0]->type == MONO_TYPE_I) {
6153 opcode = OP_ATOMIC_EXCHANGE_I8;
6154 f2i_opcode = OP_MOVE_F_TO_I8;
6155 i2f_opcode = OP_MOVE_I8_TO_F;
6158 else if (is_ref || fsig->params [0]->type == MONO_TYPE_I) {
6159 opcode = OP_ATOMIC_EXCHANGE_I4;
6160 cfg->has_atomic_exchange_i4 = TRUE;
6166 if (!mono_arch_opcode_supported (opcode))
6170 /* TODO: Decompose these opcodes instead of bailing here. */
6171 if (COMPILE_SOFT_FLOAT (cfg))
6174 MONO_INST_NEW (cfg, f2i, f2i_opcode);
6175 f2i->dreg = mono_alloc_ireg (cfg);
6176 f2i->sreg1 = args [1]->dreg;
6177 if (f2i_opcode == OP_MOVE_F_TO_I4)
6178 f2i->backend.spill_var = mini_get_int_to_float_spill_area (cfg);
6179 MONO_ADD_INS (cfg->cbb, f2i);
6182 MONO_INST_NEW (cfg, ins, opcode);
6183 ins->dreg = is_ref ? mono_alloc_ireg_ref (cfg) : mono_alloc_ireg (cfg);
6184 ins->inst_basereg = args [0]->dreg;
6185 ins->inst_offset = 0;
6186 ins->sreg2 = is_float ? f2i->dreg : args [1]->dreg;
6187 MONO_ADD_INS (cfg->cbb, ins);
6189 switch (fsig->params [0]->type) {
6191 ins->type = STACK_I4;
6194 ins->type = STACK_I8;
6197 #if SIZEOF_REGISTER == 8
6198 ins->type = STACK_I8;
6200 ins->type = STACK_I4;
6205 ins->type = STACK_R8;
6208 g_assert (mini_type_is_reference (cfg, fsig->params [0]));
6209 ins->type = STACK_OBJ;
6214 MONO_INST_NEW (cfg, i2f, i2f_opcode);
6215 i2f->dreg = mono_alloc_freg (cfg);
6216 i2f->sreg1 = ins->dreg;
6217 i2f->type = STACK_R8;
6218 if (i2f_opcode == OP_MOVE_I4_TO_F)
6219 i2f->backend.spill_var = mini_get_int_to_float_spill_area (cfg);
6220 MONO_ADD_INS (cfg->cbb, i2f);
6225 if (cfg->gen_write_barriers && is_ref)
6226 emit_write_barrier (cfg, args [0], args [1]);
6228 else if ((strcmp (cmethod->name, "CompareExchange") == 0) && fsig->param_count == 3) {
6229 MonoInst *f2i_new = NULL, *f2i_cmp = NULL, *i2f;
6230 guint32 opcode, f2i_opcode, i2f_opcode;
6231 gboolean is_ref = mini_type_is_reference (cfg, fsig->params [1]);
6232 gboolean is_float = fsig->params [1]->type == MONO_TYPE_R4 || fsig->params [1]->type == MONO_TYPE_R8;
6234 if (fsig->params [1]->type == MONO_TYPE_I4 ||
6235 fsig->params [1]->type == MONO_TYPE_R4) {
6236 opcode = OP_ATOMIC_CAS_I4;
6237 f2i_opcode = OP_MOVE_F_TO_I4;
6238 i2f_opcode = OP_MOVE_I4_TO_F;
6239 cfg->has_atomic_cas_i4 = TRUE;
6241 #if SIZEOF_REGISTER == 8
6243 fsig->params [1]->type == MONO_TYPE_I8 ||
6244 fsig->params [1]->type == MONO_TYPE_R8 ||
6245 fsig->params [1]->type == MONO_TYPE_I) {
6246 opcode = OP_ATOMIC_CAS_I8;
6247 f2i_opcode = OP_MOVE_F_TO_I8;
6248 i2f_opcode = OP_MOVE_I8_TO_F;
6251 else if (is_ref || fsig->params [1]->type == MONO_TYPE_I) {
6252 opcode = OP_ATOMIC_CAS_I4;
6253 cfg->has_atomic_cas_i4 = TRUE;
6259 if (!mono_arch_opcode_supported (opcode))
6263 /* TODO: Decompose these opcodes instead of bailing here. */
6264 if (COMPILE_SOFT_FLOAT (cfg))
6267 MONO_INST_NEW (cfg, f2i_new, f2i_opcode);
6268 f2i_new->dreg = mono_alloc_ireg (cfg);
6269 f2i_new->sreg1 = args [1]->dreg;
6270 if (f2i_opcode == OP_MOVE_F_TO_I4)
6271 f2i_new->backend.spill_var = mini_get_int_to_float_spill_area (cfg);
6272 MONO_ADD_INS (cfg->cbb, f2i_new);
6274 MONO_INST_NEW (cfg, f2i_cmp, f2i_opcode);
6275 f2i_cmp->dreg = mono_alloc_ireg (cfg);
6276 f2i_cmp->sreg1 = args [2]->dreg;
6277 if (f2i_opcode == OP_MOVE_F_TO_I4)
6278 f2i_cmp->backend.spill_var = mini_get_int_to_float_spill_area (cfg);
6279 MONO_ADD_INS (cfg->cbb, f2i_cmp);
6282 MONO_INST_NEW (cfg, ins, opcode);
6283 ins->dreg = is_ref ? alloc_ireg_ref (cfg) : alloc_ireg (cfg);
6284 ins->sreg1 = args [0]->dreg;
6285 ins->sreg2 = is_float ? f2i_new->dreg : args [1]->dreg;
6286 ins->sreg3 = is_float ? f2i_cmp->dreg : args [2]->dreg;
6287 MONO_ADD_INS (cfg->cbb, ins);
6289 switch (fsig->params [1]->type) {
6291 ins->type = STACK_I4;
6294 ins->type = STACK_I8;
6297 #if SIZEOF_REGISTER == 8
6298 ins->type = STACK_I8;
6300 ins->type = STACK_I4;
6305 ins->type = STACK_R8;
6308 g_assert (mini_type_is_reference (cfg, fsig->params [1]));
6309 ins->type = STACK_OBJ;
6314 MONO_INST_NEW (cfg, i2f, i2f_opcode);
6315 i2f->dreg = mono_alloc_freg (cfg);
6316 i2f->sreg1 = ins->dreg;
6317 i2f->type = STACK_R8;
6318 if (i2f_opcode == OP_MOVE_I4_TO_F)
6319 i2f->backend.spill_var = mini_get_int_to_float_spill_area (cfg);
6320 MONO_ADD_INS (cfg->cbb, i2f);
6325 if (cfg->gen_write_barriers && is_ref)
6326 emit_write_barrier (cfg, args [0], args [1]);
6328 else if ((strcmp (cmethod->name, "CompareExchange") == 0) && fsig->param_count == 4 &&
6329 fsig->params [1]->type == MONO_TYPE_I4) {
6330 MonoInst *cmp, *ceq;
6332 if (!mono_arch_opcode_supported (OP_ATOMIC_CAS_I4))
6335 /* int32 r = CAS (location, value, comparand); */
6336 MONO_INST_NEW (cfg, ins, OP_ATOMIC_CAS_I4);
6337 ins->dreg = alloc_ireg (cfg);
6338 ins->sreg1 = args [0]->dreg;
6339 ins->sreg2 = args [1]->dreg;
6340 ins->sreg3 = args [2]->dreg;
6341 ins->type = STACK_I4;
6342 MONO_ADD_INS (cfg->cbb, ins);
6344 /* bool result = r == comparand; */
6345 MONO_INST_NEW (cfg, cmp, OP_ICOMPARE);
6346 cmp->sreg1 = ins->dreg;
6347 cmp->sreg2 = args [2]->dreg;
6348 cmp->type = STACK_I4;
6349 MONO_ADD_INS (cfg->cbb, cmp);
6351 MONO_INST_NEW (cfg, ceq, OP_ICEQ);
6352 ceq->dreg = alloc_ireg (cfg);
6353 ceq->type = STACK_I4;
6354 MONO_ADD_INS (cfg->cbb, ceq);
6356 /* *success = result; */
6357 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI1_MEMBASE_REG, args [3]->dreg, 0, ceq->dreg);
6359 cfg->has_atomic_cas_i4 = TRUE;
6361 else if (strcmp (cmethod->name, "MemoryBarrier") == 0 && fsig->param_count == 0)
6362 ins = emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_SEQ);
6366 } else if (cmethod->klass->image == mono_defaults.corlib &&
6367 (strcmp (cmethod->klass->name_space, "System.Threading") == 0) &&
6368 (strcmp (cmethod->klass->name, "Volatile") == 0)) {
6371 if (!strcmp (cmethod->name, "Read") && fsig->param_count == 1) {
6373 gboolean is_ref = mini_type_is_reference (cfg, fsig->params [0]);
6374 gboolean is_float = fsig->params [0]->type == MONO_TYPE_R4 || fsig->params [0]->type == MONO_TYPE_R8;
6376 if (fsig->params [0]->type == MONO_TYPE_I1)
6377 opcode = OP_ATOMIC_LOAD_I1;
6378 else if (fsig->params [0]->type == MONO_TYPE_U1 || fsig->params [0]->type == MONO_TYPE_BOOLEAN)
6379 opcode = OP_ATOMIC_LOAD_U1;
6380 else if (fsig->params [0]->type == MONO_TYPE_I2)
6381 opcode = OP_ATOMIC_LOAD_I2;
6382 else if (fsig->params [0]->type == MONO_TYPE_U2)
6383 opcode = OP_ATOMIC_LOAD_U2;
6384 else if (fsig->params [0]->type == MONO_TYPE_I4)
6385 opcode = OP_ATOMIC_LOAD_I4;
6386 else if (fsig->params [0]->type == MONO_TYPE_U4)
6387 opcode = OP_ATOMIC_LOAD_U4;
6388 else if (fsig->params [0]->type == MONO_TYPE_R4)
6389 opcode = OP_ATOMIC_LOAD_R4;
6390 else if (fsig->params [0]->type == MONO_TYPE_R8)
6391 opcode = OP_ATOMIC_LOAD_R8;
6392 #if SIZEOF_REGISTER == 8
6393 else if (fsig->params [0]->type == MONO_TYPE_I8 || fsig->params [0]->type == MONO_TYPE_I)
6394 opcode = OP_ATOMIC_LOAD_I8;
6395 else if (is_ref || fsig->params [0]->type == MONO_TYPE_U8 || fsig->params [0]->type == MONO_TYPE_U)
6396 opcode = OP_ATOMIC_LOAD_U8;
6398 else if (fsig->params [0]->type == MONO_TYPE_I)
6399 opcode = OP_ATOMIC_LOAD_I4;
6400 else if (is_ref || fsig->params [0]->type == MONO_TYPE_U)
6401 opcode = OP_ATOMIC_LOAD_U4;
6405 if (!mono_arch_opcode_supported (opcode))
6408 MONO_INST_NEW (cfg, ins, opcode);
6409 ins->dreg = is_ref ? mono_alloc_ireg_ref (cfg) : (is_float ? mono_alloc_freg (cfg) : mono_alloc_ireg (cfg));
6410 ins->sreg1 = args [0]->dreg;
6411 ins->backend.memory_barrier_kind = MONO_MEMORY_BARRIER_ACQ;
6412 MONO_ADD_INS (cfg->cbb, ins);
6414 switch (fsig->params [0]->type) {
6415 case MONO_TYPE_BOOLEAN:
6422 ins->type = STACK_I4;
6426 ins->type = STACK_I8;
6430 #if SIZEOF_REGISTER == 8
6431 ins->type = STACK_I8;
6433 ins->type = STACK_I4;
6438 ins->type = STACK_R8;
6441 g_assert (mini_type_is_reference (cfg, fsig->params [0]));
6442 ins->type = STACK_OBJ;
6448 if (!strcmp (cmethod->name, "Write") && fsig->param_count == 2) {
6450 gboolean is_ref = mini_type_is_reference (cfg, fsig->params [0]);
6452 if (fsig->params [0]->type == MONO_TYPE_I1)
6453 opcode = OP_ATOMIC_STORE_I1;
6454 else if (fsig->params [0]->type == MONO_TYPE_U1 || fsig->params [0]->type == MONO_TYPE_BOOLEAN)
6455 opcode = OP_ATOMIC_STORE_U1;
6456 else if (fsig->params [0]->type == MONO_TYPE_I2)
6457 opcode = OP_ATOMIC_STORE_I2;
6458 else if (fsig->params [0]->type == MONO_TYPE_U2)
6459 opcode = OP_ATOMIC_STORE_U2;
6460 else if (fsig->params [0]->type == MONO_TYPE_I4)
6461 opcode = OP_ATOMIC_STORE_I4;
6462 else if (fsig->params [0]->type == MONO_TYPE_U4)
6463 opcode = OP_ATOMIC_STORE_U4;
6464 else if (fsig->params [0]->type == MONO_TYPE_R4)
6465 opcode = OP_ATOMIC_STORE_R4;
6466 else if (fsig->params [0]->type == MONO_TYPE_R8)
6467 opcode = OP_ATOMIC_STORE_R8;
6468 #if SIZEOF_REGISTER == 8
6469 else if (fsig->params [0]->type == MONO_TYPE_I8 || fsig->params [0]->type == MONO_TYPE_I)
6470 opcode = OP_ATOMIC_STORE_I8;
6471 else if (is_ref || fsig->params [0]->type == MONO_TYPE_U8 || fsig->params [0]->type == MONO_TYPE_U)
6472 opcode = OP_ATOMIC_STORE_U8;
6474 else if (fsig->params [0]->type == MONO_TYPE_I)
6475 opcode = OP_ATOMIC_STORE_I4;
6476 else if (is_ref || fsig->params [0]->type == MONO_TYPE_U)
6477 opcode = OP_ATOMIC_STORE_U4;
6481 if (!mono_arch_opcode_supported (opcode))
6484 MONO_INST_NEW (cfg, ins, opcode);
6485 ins->dreg = args [0]->dreg;
6486 ins->sreg1 = args [1]->dreg;
6487 ins->backend.memory_barrier_kind = MONO_MEMORY_BARRIER_REL;
6488 MONO_ADD_INS (cfg->cbb, ins);
6490 if (cfg->gen_write_barriers && is_ref)
6491 emit_write_barrier (cfg, args [0], args [1]);
6497 } else if (cmethod->klass->image == mono_defaults.corlib &&
6498 (strcmp (cmethod->klass->name_space, "System.Diagnostics") == 0) &&
6499 (strcmp (cmethod->klass->name, "Debugger") == 0)) {
6500 if (!strcmp (cmethod->name, "Break") && fsig->param_count == 0) {
6501 if (should_insert_brekpoint (cfg->method)) {
6502 ins = mono_emit_jit_icall (cfg, mono_debugger_agent_user_break, NULL);
6504 MONO_INST_NEW (cfg, ins, OP_NOP);
6505 MONO_ADD_INS (cfg->cbb, ins);
6509 } else if (cmethod->klass->image == mono_defaults.corlib &&
6510 (strcmp (cmethod->klass->name_space, "System") == 0) &&
6511 (strcmp (cmethod->klass->name, "Environment") == 0)) {
6512 if (!strcmp (cmethod->name, "get_IsRunningOnWindows") && fsig->param_count == 0) {
6514 EMIT_NEW_ICONST (cfg, ins, 1);
6516 EMIT_NEW_ICONST (cfg, ins, 0);
6519 } else if (cmethod->klass == mono_defaults.math_class) {
6521 * There is general branchless code for Min/Max, but it does not work for
6523 * http://everything2.com/?node_id=1051618
6525 } else if (((!strcmp (cmethod->klass->image->assembly->aname.name, "MonoMac") ||
6526 !strcmp (cmethod->klass->image->assembly->aname.name, "monotouch")) &&
6527 !strcmp (cmethod->klass->name_space, "XamCore.ObjCRuntime") &&
6528 !strcmp (cmethod->klass->name, "Selector")) ||
6529 (!strcmp (cmethod->klass->image->assembly->aname.name, "Xamarin.iOS") &&
6530 !strcmp (cmethod->klass->name_space, "ObjCRuntime") &&
6531 !strcmp (cmethod->klass->name, "Selector"))
6533 #ifdef MONO_ARCH_HAVE_OBJC_GET_SELECTOR
6534 if (!strcmp (cmethod->name, "GetHandle") && fsig->param_count == 1 &&
6535 (args [0]->opcode == OP_GOT_ENTRY || args [0]->opcode == OP_AOTCONST) &&
6538 MonoJumpInfoToken *ji;
6541 cfg->disable_llvm = TRUE;
6543 if (args [0]->opcode == OP_GOT_ENTRY) {
6544 pi = args [0]->inst_p1;
6545 g_assert (pi->opcode == OP_PATCH_INFO);
6546 g_assert (GPOINTER_TO_INT (pi->inst_p1) == MONO_PATCH_INFO_LDSTR);
6549 g_assert (GPOINTER_TO_INT (args [0]->inst_p1) == MONO_PATCH_INFO_LDSTR);
6550 ji = args [0]->inst_p0;
6553 NULLIFY_INS (args [0]);
6556 s = mono_ldstr (cfg->domain, ji->image, mono_metadata_token_index (ji->token));
6557 MONO_INST_NEW (cfg, ins, OP_OBJC_GET_SELECTOR);
6558 ins->dreg = mono_alloc_ireg (cfg);
6560 ins->inst_p0 = mono_string_to_utf8 (s);
6561 MONO_ADD_INS (cfg->cbb, ins);
6567 #ifdef MONO_ARCH_SIMD_INTRINSICS
6568 if (cfg->opt & MONO_OPT_SIMD) {
6569 ins = mono_emit_simd_intrinsics (cfg, cmethod, fsig, args);
6575 ins = mono_emit_native_types_intrinsics (cfg, cmethod, fsig, args);
6579 if (COMPILE_LLVM (cfg)) {
6580 ins = llvm_emit_inst_for_method (cfg, cmethod, fsig, args);
6585 return mono_arch_emit_inst_for_method (cfg, cmethod, fsig, args);
6589 * This entry point could be used later for arbitrary method
6592 inline static MonoInst*
6593 mini_redirect_call (MonoCompile *cfg, MonoMethod *method,
6594 MonoMethodSignature *signature, MonoInst **args, MonoInst *this)
6596 if (method->klass == mono_defaults.string_class) {
6597 /* managed string allocation support */
6598 if (strcmp (method->name, "InternalAllocateStr") == 0 && !(mono_profiler_events & MONO_PROFILE_ALLOCATIONS) && !(cfg->opt & MONO_OPT_SHARED)) {
6599 MonoInst *iargs [2];
6600 MonoVTable *vtable = mono_class_vtable (cfg->domain, method->klass);
6601 MonoMethod *managed_alloc = NULL;
6603 g_assert (vtable); /*Should not fail since it System.String*/
6604 #ifndef MONO_CROSS_COMPILE
6605 managed_alloc = mono_gc_get_managed_allocator (method->klass, FALSE, FALSE);
6609 EMIT_NEW_VTABLECONST (cfg, iargs [0], vtable);
6610 iargs [1] = args [0];
6611 return mono_emit_method_call (cfg, managed_alloc, iargs, this);
6618 mono_save_args (MonoCompile *cfg, MonoMethodSignature *sig, MonoInst **sp)
6620 MonoInst *store, *temp;
6623 for (i = 0; i < sig->param_count + sig->hasthis; ++i) {
6624 MonoType *argtype = (sig->hasthis && (i == 0)) ? type_from_stack_type (*sp) : sig->params [i - sig->hasthis];
6627 * FIXME: We should use *args++ = sp [0], but that would mean the arg
6628 * would be different than the MonoInst's used to represent arguments, and
6629 * the ldelema implementation can't deal with that.
6630 * Solution: When ldelema is used on an inline argument, create a var for
6631 * it, emit ldelema on that var, and emit the saving code below in
6632 * inline_method () if needed.
6634 temp = mono_compile_create_var (cfg, argtype, OP_LOCAL);
6635 cfg->args [i] = temp;
6636 /* This uses cfg->args [i] which is set by the preceeding line */
6637 EMIT_NEW_ARGSTORE (cfg, store, i, *sp);
6638 store->cil_code = sp [0]->cil_code;
6643 #define MONO_INLINE_CALLED_LIMITED_METHODS 1
6644 #define MONO_INLINE_CALLER_LIMITED_METHODS 1
6646 #if (MONO_INLINE_CALLED_LIMITED_METHODS)
6648 check_inline_called_method_name_limit (MonoMethod *called_method)
6651 static const char *limit = NULL;
6653 if (limit == NULL) {
6654 const char *limit_string = g_getenv ("MONO_INLINE_CALLED_METHOD_NAME_LIMIT");
6656 if (limit_string != NULL)
6657 limit = limit_string;
6662 if (limit [0] != '\0') {
6663 char *called_method_name = mono_method_full_name (called_method, TRUE);
6665 strncmp_result = strncmp (called_method_name, limit, strlen (limit));
6666 g_free (called_method_name);
6668 //return (strncmp_result <= 0);
6669 return (strncmp_result == 0);
6676 #if (MONO_INLINE_CALLER_LIMITED_METHODS)
6678 check_inline_caller_method_name_limit (MonoMethod *caller_method)
6681 static const char *limit = NULL;
6683 if (limit == NULL) {
6684 const char *limit_string = g_getenv ("MONO_INLINE_CALLER_METHOD_NAME_LIMIT");
6685 if (limit_string != NULL) {
6686 limit = limit_string;
6692 if (limit [0] != '\0') {
6693 char *caller_method_name = mono_method_full_name (caller_method, TRUE);
6695 strncmp_result = strncmp (caller_method_name, limit, strlen (limit));
6696 g_free (caller_method_name);
6698 //return (strncmp_result <= 0);
6699 return (strncmp_result == 0);
6707 emit_init_rvar (MonoCompile *cfg, int dreg, MonoType *rtype)
6709 static double r8_0 = 0.0;
6710 static float r4_0 = 0.0;
6714 rtype = mini_get_underlying_type (cfg, rtype);
6718 MONO_EMIT_NEW_PCONST (cfg, dreg, NULL);
6719 } else if (t >= MONO_TYPE_BOOLEAN && t <= MONO_TYPE_U4) {
6720 MONO_EMIT_NEW_ICONST (cfg, dreg, 0);
6721 } else if (t == MONO_TYPE_I8 || t == MONO_TYPE_U8) {
6722 MONO_EMIT_NEW_I8CONST (cfg, dreg, 0);
6723 } else if (cfg->r4fp && t == MONO_TYPE_R4) {
6724 MONO_INST_NEW (cfg, ins, OP_R4CONST);
6725 ins->type = STACK_R4;
6726 ins->inst_p0 = (void*)&r4_0;
6728 MONO_ADD_INS (cfg->cbb, ins);
6729 } else if (t == MONO_TYPE_R4 || t == MONO_TYPE_R8) {
6730 MONO_INST_NEW (cfg, ins, OP_R8CONST);
6731 ins->type = STACK_R8;
6732 ins->inst_p0 = (void*)&r8_0;
6734 MONO_ADD_INS (cfg->cbb, ins);
6735 } else if ((t == MONO_TYPE_VALUETYPE) || (t == MONO_TYPE_TYPEDBYREF) ||
6736 ((t == MONO_TYPE_GENERICINST) && mono_type_generic_inst_is_valuetype (rtype))) {
6737 MONO_EMIT_NEW_VZERO (cfg, dreg, mono_class_from_mono_type (rtype));
6738 } else if (((t == MONO_TYPE_VAR) || (t == MONO_TYPE_MVAR)) && mini_type_var_is_vt (cfg, rtype)) {
6739 MONO_EMIT_NEW_VZERO (cfg, dreg, mono_class_from_mono_type (rtype));
6741 MONO_EMIT_NEW_PCONST (cfg, dreg, NULL);
6746 emit_dummy_init_rvar (MonoCompile *cfg, int dreg, MonoType *rtype)
6750 rtype = mini_get_underlying_type (cfg, rtype);
6754 MONO_EMIT_NEW_DUMMY_INIT (cfg, dreg, OP_DUMMY_PCONST);
6755 } else if (t >= MONO_TYPE_BOOLEAN && t <= MONO_TYPE_U4) {
6756 MONO_EMIT_NEW_DUMMY_INIT (cfg, dreg, OP_DUMMY_ICONST);
6757 } else if (t == MONO_TYPE_I8 || t == MONO_TYPE_U8) {
6758 MONO_EMIT_NEW_DUMMY_INIT (cfg, dreg, OP_DUMMY_I8CONST);
6759 } else if (cfg->r4fp && t == MONO_TYPE_R4) {
6760 MONO_EMIT_NEW_DUMMY_INIT (cfg, dreg, OP_DUMMY_R4CONST);
6761 } else if (t == MONO_TYPE_R4 || t == MONO_TYPE_R8) {
6762 MONO_EMIT_NEW_DUMMY_INIT (cfg, dreg, OP_DUMMY_R8CONST);
6763 } else if ((t == MONO_TYPE_VALUETYPE) || (t == MONO_TYPE_TYPEDBYREF) ||
6764 ((t == MONO_TYPE_GENERICINST) && mono_type_generic_inst_is_valuetype (rtype))) {
6765 MONO_EMIT_NEW_DUMMY_INIT (cfg, dreg, OP_DUMMY_VZERO);
6766 } else if (((t == MONO_TYPE_VAR) || (t == MONO_TYPE_MVAR)) && mini_type_var_is_vt (cfg, rtype)) {
6767 MONO_EMIT_NEW_DUMMY_INIT (cfg, dreg, OP_DUMMY_VZERO);
6769 emit_init_rvar (cfg, dreg, rtype);
6773 /* If INIT is FALSE, emit dummy initialization statements to keep the IR valid */
6775 emit_init_local (MonoCompile *cfg, int local, MonoType *type, gboolean init)
6777 MonoInst *var = cfg->locals [local];
6778 if (COMPILE_SOFT_FLOAT (cfg)) {
6780 int reg = alloc_dreg (cfg, var->type);
6781 emit_init_rvar (cfg, reg, type);
6782 EMIT_NEW_LOCSTORE (cfg, store, local, cfg->cbb->last_ins);
6785 emit_init_rvar (cfg, var->dreg, type);
6787 emit_dummy_init_rvar (cfg, var->dreg, type);
6794 * Return the cost of inlining CMETHOD.
6797 inline_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **sp,
6798 guchar *ip, guint real_offset, gboolean inline_always, MonoBasicBlock **out_cbb)
6800 MonoInst *ins, *rvar = NULL;
6801 MonoMethodHeader *cheader;
6802 MonoBasicBlock *ebblock, *sbblock;
6804 MonoMethod *prev_inlined_method;
6805 MonoInst **prev_locals, **prev_args;
6806 MonoType **prev_arg_types;
6807 guint prev_real_offset;
6808 GHashTable *prev_cbb_hash;
6809 MonoBasicBlock **prev_cil_offset_to_bb;
6810 MonoBasicBlock *prev_cbb;
6811 unsigned char* prev_cil_start;
6812 guint32 prev_cil_offset_to_bb_len;
6813 MonoMethod *prev_current_method;
6814 MonoGenericContext *prev_generic_context;
6815 gboolean ret_var_set, prev_ret_var_set, prev_disable_inline, virtual = FALSE;
6817 g_assert (cfg->exception_type == MONO_EXCEPTION_NONE);
6819 #if (MONO_INLINE_CALLED_LIMITED_METHODS)
6820 if ((! inline_always) && ! check_inline_called_method_name_limit (cmethod))
6823 #if (MONO_INLINE_CALLER_LIMITED_METHODS)
6824 if ((! inline_always) && ! check_inline_caller_method_name_limit (cfg->method))
6829 fsig = mono_method_signature (cmethod);
6831 if (cfg->verbose_level > 2)
6832 printf ("INLINE START %p %s -> %s\n", cmethod, mono_method_full_name (cfg->method, TRUE), mono_method_full_name (cmethod, TRUE));
6834 if (!cmethod->inline_info) {
6835 cfg->stat_inlineable_methods++;
6836 cmethod->inline_info = 1;
6839 /* allocate local variables */
6840 cheader = mono_method_get_header (cmethod);
6842 if (cheader == NULL || mono_loader_get_last_error ()) {
6843 MonoLoaderError *error = mono_loader_get_last_error ();
6846 mono_metadata_free_mh (cheader);
6847 if (inline_always && error)
6848 mono_cfg_set_exception (cfg, error->exception_type);
6850 mono_loader_clear_error ();
6854 /*Must verify before creating locals as it can cause the JIT to assert.*/
6855 if (mono_compile_is_broken (cfg, cmethod, FALSE)) {
6856 mono_metadata_free_mh (cheader);
6860 /* allocate space to store the return value */
6861 if (!MONO_TYPE_IS_VOID (fsig->ret)) {
6862 rvar = mono_compile_create_var (cfg, fsig->ret, OP_LOCAL);
6865 prev_locals = cfg->locals;
6866 cfg->locals = mono_mempool_alloc0 (cfg->mempool, cheader->num_locals * sizeof (MonoInst*));
6867 for (i = 0; i < cheader->num_locals; ++i)
6868 cfg->locals [i] = mono_compile_create_var (cfg, cheader->locals [i], OP_LOCAL);
6870 /* allocate start and end blocks */
6871 /* This is needed so if the inline is aborted, we can clean up */
6872 NEW_BBLOCK (cfg, sbblock);
6873 sbblock->real_offset = real_offset;
6875 NEW_BBLOCK (cfg, ebblock);
6876 ebblock->block_num = cfg->num_bblocks++;
6877 ebblock->real_offset = real_offset;
6879 prev_args = cfg->args;
6880 prev_arg_types = cfg->arg_types;
6881 prev_inlined_method = cfg->inlined_method;
6882 cfg->inlined_method = cmethod;
6883 cfg->ret_var_set = FALSE;
6884 cfg->inline_depth ++;
6885 prev_real_offset = cfg->real_offset;
6886 prev_cbb_hash = cfg->cbb_hash;
6887 prev_cil_offset_to_bb = cfg->cil_offset_to_bb;
6888 prev_cil_offset_to_bb_len = cfg->cil_offset_to_bb_len;
6889 prev_cil_start = cfg->cil_start;
6890 prev_cbb = cfg->cbb;
6891 prev_current_method = cfg->current_method;
6892 prev_generic_context = cfg->generic_context;
6893 prev_ret_var_set = cfg->ret_var_set;
6894 prev_disable_inline = cfg->disable_inline;
6896 if (ip && *ip == CEE_CALLVIRT && !(cmethod->flags & METHOD_ATTRIBUTE_STATIC))
6899 costs = mono_method_to_ir (cfg, cmethod, sbblock, ebblock, rvar, sp, real_offset, virtual);
6901 ret_var_set = cfg->ret_var_set;
6903 cfg->inlined_method = prev_inlined_method;
6904 cfg->real_offset = prev_real_offset;
6905 cfg->cbb_hash = prev_cbb_hash;
6906 cfg->cil_offset_to_bb = prev_cil_offset_to_bb;
6907 cfg->cil_offset_to_bb_len = prev_cil_offset_to_bb_len;
6908 cfg->cil_start = prev_cil_start;
6909 cfg->locals = prev_locals;
6910 cfg->args = prev_args;
6911 cfg->arg_types = prev_arg_types;
6912 cfg->current_method = prev_current_method;
6913 cfg->generic_context = prev_generic_context;
6914 cfg->ret_var_set = prev_ret_var_set;
6915 cfg->disable_inline = prev_disable_inline;
6916 cfg->inline_depth --;
6918 if ((costs >= 0 && costs < 60) || inline_always) {
6919 if (cfg->verbose_level > 2)
6920 printf ("INLINE END %s -> %s\n", mono_method_full_name (cfg->method, TRUE), mono_method_full_name (cmethod, TRUE));
6922 cfg->stat_inlined_methods++;
6924 /* always add some code to avoid block split failures */
6925 MONO_INST_NEW (cfg, ins, OP_NOP);
6926 MONO_ADD_INS (prev_cbb, ins);
6928 prev_cbb->next_bb = sbblock;
6929 link_bblock (cfg, prev_cbb, sbblock);
6932 * Get rid of the begin and end bblocks if possible to aid local
6935 mono_merge_basic_blocks (cfg, prev_cbb, sbblock);
6937 if ((prev_cbb->out_count == 1) && (prev_cbb->out_bb [0]->in_count == 1) && (prev_cbb->out_bb [0] != ebblock))
6938 mono_merge_basic_blocks (cfg, prev_cbb, prev_cbb->out_bb [0]);
6940 if ((ebblock->in_count == 1) && ebblock->in_bb [0]->out_count == 1) {
6941 MonoBasicBlock *prev = ebblock->in_bb [0];
6942 mono_merge_basic_blocks (cfg, prev, ebblock);
6944 if ((prev_cbb->out_count == 1) && (prev_cbb->out_bb [0]->in_count == 1) && (prev_cbb->out_bb [0] == prev)) {
6945 mono_merge_basic_blocks (cfg, prev_cbb, prev);
6946 cfg->cbb = prev_cbb;
6950 * Its possible that the rvar is set in some prev bblock, but not in others.
6956 for (i = 0; i < ebblock->in_count; ++i) {
6957 bb = ebblock->in_bb [i];
6959 if (bb->last_ins && bb->last_ins->opcode == OP_NOT_REACHED) {
6962 emit_init_rvar (cfg, rvar->dreg, fsig->ret);
6971 *out_cbb = cfg->cbb;
6975 * If the inlined method contains only a throw, then the ret var is not
6976 * set, so set it to a dummy value.
6979 emit_init_rvar (cfg, rvar->dreg, fsig->ret);
6981 EMIT_NEW_TEMPLOAD (cfg, ins, rvar->inst_c0);
6984 cfg->headers_to_free = g_slist_prepend_mempool (cfg->mempool, cfg->headers_to_free, cheader);
6987 if (cfg->verbose_level > 2)
6988 printf ("INLINE ABORTED %s (cost %d)\n", mono_method_full_name (cmethod, TRUE), costs);
6989 cfg->exception_type = MONO_EXCEPTION_NONE;
6990 mono_loader_clear_error ();
6992 /* This gets rid of the newly added bblocks */
6993 cfg->cbb = prev_cbb;
6995 cfg->headers_to_free = g_slist_prepend_mempool (cfg->mempool, cfg->headers_to_free, cheader);
7000 * Some of these comments may well be out-of-date.
7001 * Design decisions: we do a single pass over the IL code (and we do bblock
7002 * splitting/merging in the few cases when it's required: a back jump to an IL
7003 * address that was not already seen as bblock starting point).
7004 * Code is validated as we go (full verification is still better left to metadata/verify.c).
7005 * Complex operations are decomposed in simpler ones right away. We need to let the
7006 * arch-specific code peek and poke inside this process somehow (except when the
7007 * optimizations can take advantage of the full semantic info of coarse opcodes).
7008 * All the opcodes of the form opcode.s are 'normalized' to opcode.
7009 * MonoInst->opcode initially is the IL opcode or some simplification of that
7010 * (OP_LOAD, OP_STORE). The arch-specific code may rearrange it to an arch-specific
7011 * opcode with value bigger than OP_LAST.
7012 * At this point the IR can be handed over to an interpreter, a dumb code generator
7013 * or to the optimizing code generator that will translate it to SSA form.
7015 * Profiling directed optimizations.
7016 * We may compile by default with few or no optimizations and instrument the code
7017 * or the user may indicate what methods to optimize the most either in a config file
7018 * or through repeated runs where the compiler applies offline the optimizations to
7019 * each method and then decides if it was worth it.
7022 #define CHECK_TYPE(ins) if (!(ins)->type) UNVERIFIED
7023 #define CHECK_STACK(num) if ((sp - stack_start) < (num)) UNVERIFIED
7024 #define CHECK_STACK_OVF(num) if (((sp - stack_start) + (num)) > header->max_stack) UNVERIFIED
7025 #define CHECK_ARG(num) if ((unsigned)(num) >= (unsigned)num_args) UNVERIFIED
7026 #define CHECK_LOCAL(num) if ((unsigned)(num) >= (unsigned)header->num_locals) UNVERIFIED
7027 #define CHECK_OPSIZE(size) if (ip + size > end) UNVERIFIED
7028 #define CHECK_UNVERIFIABLE(cfg) if (cfg->unverifiable) UNVERIFIED
7029 #define CHECK_TYPELOAD(klass) if (!(klass) || (klass)->exception_type) TYPE_LOAD_ERROR ((klass))
7031 /* offset from br.s -> br like opcodes */
7032 #define BIG_BRANCH_OFFSET 13
7035 ip_in_bb (MonoCompile *cfg, MonoBasicBlock *bb, const guint8* ip)
7037 MonoBasicBlock *b = cfg->cil_offset_to_bb [ip - cfg->cil_start];
7039 return b == NULL || b == bb;
7043 get_basic_blocks (MonoCompile *cfg, MonoMethodHeader* header, guint real_offset, unsigned char *start, unsigned char *end, unsigned char **pos)
7045 unsigned char *ip = start;
7046 unsigned char *target;
7049 MonoBasicBlock *bblock;
7050 const MonoOpcode *opcode;
7053 cli_addr = ip - start;
7054 i = mono_opcode_value ((const guint8 **)&ip, end);
7057 opcode = &mono_opcodes [i];
7058 switch (opcode->argument) {
7059 case MonoInlineNone:
7062 case MonoInlineString:
7063 case MonoInlineType:
7064 case MonoInlineField:
7065 case MonoInlineMethod:
7068 case MonoShortInlineR:
7075 case MonoShortInlineVar:
7076 case MonoShortInlineI:
7079 case MonoShortInlineBrTarget:
7080 target = start + cli_addr + 2 + (signed char)ip [1];
7081 GET_BBLOCK (cfg, bblock, target);
7084 GET_BBLOCK (cfg, bblock, ip);
7086 case MonoInlineBrTarget:
7087 target = start + cli_addr + 5 + (gint32)read32 (ip + 1);
7088 GET_BBLOCK (cfg, bblock, target);
7091 GET_BBLOCK (cfg, bblock, ip);
7093 case MonoInlineSwitch: {
7094 guint32 n = read32 (ip + 1);
7097 cli_addr += 5 + 4 * n;
7098 target = start + cli_addr;
7099 GET_BBLOCK (cfg, bblock, target);
7101 for (j = 0; j < n; ++j) {
7102 target = start + cli_addr + (gint32)read32 (ip);
7103 GET_BBLOCK (cfg, bblock, target);
7113 g_assert_not_reached ();
7116 if (i == CEE_THROW) {
7117 unsigned char *bb_start = ip - 1;
7119 /* Find the start of the bblock containing the throw */
7121 while ((bb_start >= start) && !bblock) {
7122 bblock = cfg->cil_offset_to_bb [(bb_start) - start];
7126 bblock->out_of_line = 1;
7136 static inline MonoMethod *
7137 mini_get_method_allow_open (MonoMethod *m, guint32 token, MonoClass *klass, MonoGenericContext *context)
7141 if (m->wrapper_type != MONO_WRAPPER_NONE) {
7142 method = mono_method_get_wrapper_data (m, token);
7145 method = mono_class_inflate_generic_method_checked (method, context, &error);
7146 g_assert (mono_error_ok (&error)); /* FIXME don't swallow the error */
7149 method = mono_get_method_full (m->klass->image, token, klass, context);
7155 static inline MonoMethod *
7156 mini_get_method (MonoCompile *cfg, MonoMethod *m, guint32 token, MonoClass *klass, MonoGenericContext *context)
7158 MonoMethod *method = mini_get_method_allow_open (m, token, klass, context);
7160 if (method && cfg && !cfg->generic_sharing_context && mono_class_is_open_constructed_type (&method->klass->byval_arg))
7166 static inline MonoClass*
7167 mini_get_class (MonoMethod *method, guint32 token, MonoGenericContext *context)
7172 if (method->wrapper_type != MONO_WRAPPER_NONE) {
7173 klass = mono_method_get_wrapper_data (method, token);
7175 klass = mono_class_inflate_generic_class (klass, context);
7177 klass = mono_class_get_and_inflate_typespec_checked (method->klass->image, token, context, &error);
7178 mono_error_cleanup (&error); /* FIXME don't swallow the error */
7181 mono_class_init (klass);
7185 static inline MonoMethodSignature*
7186 mini_get_signature (MonoMethod *method, guint32 token, MonoGenericContext *context)
7188 MonoMethodSignature *fsig;
7190 if (method->wrapper_type != MONO_WRAPPER_NONE) {
7193 fsig = (MonoMethodSignature *)mono_method_get_wrapper_data (method, token);
7195 fsig = mono_inflate_generic_signature (fsig, context, &error);
7197 g_assert (mono_error_ok (&error));
7200 fsig = mono_metadata_parse_signature (method->klass->image, token);
7206 throw_exception (void)
7208 static MonoMethod *method = NULL;
7211 MonoSecurityManager *secman = mono_security_manager_get_methods ();
7212 method = mono_class_get_method_from_name (secman->securitymanager, "ThrowException", 1);
7219 emit_throw_exception (MonoCompile *cfg, MonoException *ex)
7221 MonoMethod *thrower = throw_exception ();
7224 EMIT_NEW_PCONST (cfg, args [0], ex);
7225 mono_emit_method_call (cfg, thrower, args, NULL);
7229 * Return the original method is a wrapper is specified. We can only access
7230 * the custom attributes from the original method.
7233 get_original_method (MonoMethod *method)
7235 if (method->wrapper_type == MONO_WRAPPER_NONE)
7238 /* native code (which is like Critical) can call any managed method XXX FIXME XXX to validate all usages */
7239 if (method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED)
7242 /* in other cases we need to find the original method */
7243 return mono_marshal_method_from_wrapper (method);
7247 ensure_method_is_allowed_to_access_field (MonoCompile *cfg, MonoMethod *caller, MonoClassField *field,
7248 MonoBasicBlock *bblock, unsigned char *ip)
7250 /* we can't get the coreclr security level on wrappers since they don't have the attributes */
7251 MonoException *ex = mono_security_core_clr_is_field_access_allowed (get_original_method (caller), field);
7253 emit_throw_exception (cfg, ex);
7257 ensure_method_is_allowed_to_call_method (MonoCompile *cfg, MonoMethod *caller, MonoMethod *callee,
7258 MonoBasicBlock *bblock, unsigned char *ip)
7260 /* we can't get the coreclr security level on wrappers since they don't have the attributes */
7261 MonoException *ex = mono_security_core_clr_is_call_allowed (get_original_method (caller), callee);
7263 emit_throw_exception (cfg, ex);
7267 * Check that the IL instructions at ip are the array initialization
7268 * sequence and return the pointer to the data and the size.
7271 initialize_array_data (MonoMethod *method, gboolean aot, unsigned char *ip, MonoClass *klass, guint32 len, int *out_size, guint32 *out_field_token)
7274 * newarr[System.Int32]
7276 * ldtoken field valuetype ...
7277 * call void class [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(class [mscorlib]System.Array, valuetype [mscorlib]System.RuntimeFieldHandle)
7279 if (ip [0] == CEE_DUP && ip [1] == CEE_LDTOKEN && ip [5] == 0x4 && ip [6] == CEE_CALL) {
7281 guint32 token = read32 (ip + 7);
7282 guint32 field_token = read32 (ip + 2);
7283 guint32 field_index = field_token & 0xffffff;
7285 const char *data_ptr;
7287 MonoMethod *cmethod;
7288 MonoClass *dummy_class;
7289 MonoClassField *field = mono_field_from_token_checked (method->klass->image, field_token, &dummy_class, NULL, &error);
7293 mono_error_cleanup (&error); /* FIXME don't swallow the error */
7297 *out_field_token = field_token;
7299 cmethod = mini_get_method (NULL, method, token, NULL, NULL);
7302 if (strcmp (cmethod->name, "InitializeArray") || strcmp (cmethod->klass->name, "RuntimeHelpers") || cmethod->klass->image != mono_defaults.corlib)
7304 switch (mono_type_get_underlying_type (&klass->byval_arg)->type) {
7305 case MONO_TYPE_BOOLEAN:
7309 /* we need to swap on big endian, so punt. Should we handle R4 and R8 as well? */
7310 #if TARGET_BYTE_ORDER == G_LITTLE_ENDIAN
7311 case MONO_TYPE_CHAR:
7328 if (size > mono_type_size (field->type, &dummy_align))
7331 /*g_print ("optimized in %s: size: %d, numelems: %d\n", method->name, size, newarr->inst_newa_len->inst_c0);*/
7332 if (!image_is_dynamic (method->klass->image)) {
7333 field_index = read32 (ip + 2) & 0xffffff;
7334 mono_metadata_field_info (method->klass->image, field_index - 1, NULL, &rva, NULL);
7335 data_ptr = mono_image_rva_map (method->klass->image, rva);
7336 /*g_print ("field: 0x%08x, rva: %d, rva_ptr: %p\n", read32 (ip + 2), rva, data_ptr);*/
7337 /* for aot code we do the lookup on load */
7338 if (aot && data_ptr)
7339 return GUINT_TO_POINTER (rva);
7341 /*FIXME is it possible to AOT a SRE assembly not meant to be saved? */
7343 data_ptr = mono_field_get_data (field);
7351 set_exception_type_from_invalid_il (MonoCompile *cfg, MonoMethod *method, unsigned char *ip)
7353 char *method_fname = mono_method_full_name (method, TRUE);
7355 MonoMethodHeader *header = mono_method_get_header (method);
7357 if (header->code_size == 0)
7358 method_code = g_strdup ("method body is empty.");
7360 method_code = mono_disasm_code_one (NULL, method, ip, NULL);
7361 mono_cfg_set_exception (cfg, MONO_EXCEPTION_INVALID_PROGRAM);
7362 cfg->exception_message = g_strdup_printf ("Invalid IL code in %s: %s\n", method_fname, method_code);
7363 g_free (method_fname);
7364 g_free (method_code);
7365 cfg->headers_to_free = g_slist_prepend_mempool (cfg->mempool, cfg->headers_to_free, header);
7369 set_exception_object (MonoCompile *cfg, MonoException *exception)
7371 mono_cfg_set_exception (cfg, MONO_EXCEPTION_OBJECT_SUPPLIED);
7372 MONO_GC_REGISTER_ROOT_SINGLE (cfg->exception_ptr);
7373 cfg->exception_ptr = exception;
7377 emit_stloc_ir (MonoCompile *cfg, MonoInst **sp, MonoMethodHeader *header, int n)
7380 guint32 opcode = mono_type_to_regmove (cfg, header->locals [n]);
7381 if ((opcode == OP_MOVE) && cfg->cbb->last_ins == sp [0] &&
7382 ((sp [0]->opcode == OP_ICONST) || (sp [0]->opcode == OP_I8CONST))) {
7383 /* Optimize reg-reg moves away */
7385 * Can't optimize other opcodes, since sp[0] might point to
7386 * the last ins of a decomposed opcode.
7388 sp [0]->dreg = (cfg)->locals [n]->dreg;
7390 EMIT_NEW_LOCSTORE (cfg, ins, n, *sp);
7395 * ldloca inhibits many optimizations so try to get rid of it in common
7398 static inline unsigned char *
7399 emit_optimized_ldloca_ir (MonoCompile *cfg, unsigned char *ip, unsigned char *end, int size)
7409 local = read16 (ip + 2);
7413 if (ip + 6 < end && (ip [0] == CEE_PREFIX1) && (ip [1] == CEE_INITOBJ) && ip_in_bb (cfg, cfg->cbb, ip + 1)) {
7414 /* From the INITOBJ case */
7415 token = read32 (ip + 2);
7416 klass = mini_get_class (cfg->current_method, token, cfg->generic_context);
7417 CHECK_TYPELOAD (klass);
7418 type = mini_get_underlying_type (cfg, &klass->byval_arg);
7419 emit_init_local (cfg, local, type, TRUE);
7427 is_exception_class (MonoClass *class)
7430 if (class == mono_defaults.exception_class)
7432 class = class->parent;
7438 * is_jit_optimizer_disabled:
7440 * Determine whenever M's assembly has a DebuggableAttribute with the
7441 * IsJITOptimizerDisabled flag set.
7444 is_jit_optimizer_disabled (MonoMethod *m)
7446 MonoAssembly *ass = m->klass->image->assembly;
7447 MonoCustomAttrInfo* attrs;
7448 static MonoClass *klass;
7450 gboolean val = FALSE;
7453 if (ass->jit_optimizer_disabled_inited)
7454 return ass->jit_optimizer_disabled;
7457 klass = mono_class_from_name (mono_defaults.corlib, "System.Diagnostics", "DebuggableAttribute");
7460 ass->jit_optimizer_disabled = FALSE;
7461 mono_memory_barrier ();
7462 ass->jit_optimizer_disabled_inited = TRUE;
7466 attrs = mono_custom_attrs_from_assembly (ass);
7468 for (i = 0; i < attrs->num_attrs; ++i) {
7469 MonoCustomAttrEntry *attr = &attrs->attrs [i];
7471 MonoMethodSignature *sig;
7473 if (!attr->ctor || attr->ctor->klass != klass)
7475 /* Decode the attribute. See reflection.c */
7476 p = (const char*)attr->data;
7477 g_assert (read16 (p) == 0x0001);
7480 // FIXME: Support named parameters
7481 sig = mono_method_signature (attr->ctor);
7482 if (sig->param_count != 2 || sig->params [0]->type != MONO_TYPE_BOOLEAN || sig->params [1]->type != MONO_TYPE_BOOLEAN)
7484 /* Two boolean arguments */
7488 mono_custom_attrs_free (attrs);
7491 ass->jit_optimizer_disabled = val;
7492 mono_memory_barrier ();
7493 ass->jit_optimizer_disabled_inited = TRUE;
7499 is_supported_tail_call (MonoCompile *cfg, MonoMethod *method, MonoMethod *cmethod, MonoMethodSignature *fsig, int call_opcode)
7501 gboolean supported_tail_call;
7504 #ifdef MONO_ARCH_HAVE_OP_TAIL_CALL
7505 supported_tail_call = mono_arch_tail_call_supported (cfg, mono_method_signature (method), mono_method_signature (cmethod));
7507 supported_tail_call = mono_metadata_signature_equal (mono_method_signature (method), mono_method_signature (cmethod)) && !MONO_TYPE_ISSTRUCT (mono_method_signature (cmethod)->ret);
7510 for (i = 0; i < fsig->param_count; ++i) {
7511 if (fsig->params [i]->byref || fsig->params [i]->type == MONO_TYPE_PTR || fsig->params [i]->type == MONO_TYPE_FNPTR)
7512 /* These can point to the current method's stack */
7513 supported_tail_call = FALSE;
7515 if (fsig->hasthis && cmethod->klass->valuetype)
7516 /* this might point to the current method's stack */
7517 supported_tail_call = FALSE;
7518 if (cmethod->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)
7519 supported_tail_call = FALSE;
7520 if (cfg->method->save_lmf)
7521 supported_tail_call = FALSE;
7522 if (cmethod->wrapper_type && cmethod->wrapper_type != MONO_WRAPPER_DYNAMIC_METHOD)
7523 supported_tail_call = FALSE;
7524 if (call_opcode != CEE_CALL)
7525 supported_tail_call = FALSE;
7527 /* Debugging support */
7529 if (supported_tail_call) {
7530 if (!mono_debug_count ())
7531 supported_tail_call = FALSE;
7535 return supported_tail_call;
7538 /* emits the code needed to access a managed tls var (like ThreadStatic)
7539 * with the value of the tls offset in offset_reg. thread_ins represents the MonoInternalThread
7540 * pointer for the current thread.
7541 * Returns the MonoInst* representing the address of the tls var.
7544 emit_managed_static_data_access (MonoCompile *cfg, MonoInst *thread_ins, int offset_reg)
7547 int static_data_reg, array_reg, dreg;
7548 int offset2_reg, idx_reg;
7549 // inlined access to the tls data (see threads.c)
7550 static_data_reg = alloc_ireg (cfg);
7551 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, static_data_reg, thread_ins->dreg, MONO_STRUCT_OFFSET (MonoInternalThread, static_data));
7552 idx_reg = alloc_ireg (cfg);
7553 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_IAND_IMM, idx_reg, offset_reg, 0x3f);
7554 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ISHL_IMM, idx_reg, idx_reg, sizeof (gpointer) == 8 ? 3 : 2);
7555 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, static_data_reg, static_data_reg, idx_reg);
7556 array_reg = alloc_ireg (cfg);
7557 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, array_reg, static_data_reg, 0);
7558 offset2_reg = alloc_ireg (cfg);
7559 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ISHR_UN_IMM, offset2_reg, offset_reg, 6);
7560 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_IAND_IMM, offset2_reg, offset2_reg, 0x1ffffff);
7561 dreg = alloc_ireg (cfg);
7562 EMIT_NEW_BIALU (cfg, addr, OP_PADD, dreg, array_reg, offset2_reg);
7569 * Handle calls made to ctors from NEWOBJ opcodes.
7571 * REF_BBLOCK will point to the current bblock after the call.
7574 handle_ctor_call (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, int context_used,
7575 MonoInst **sp, guint8 *ip, MonoBasicBlock **ref_bblock, int *inline_costs)
7577 MonoInst *vtable_arg = NULL, *callvirt_this_arg = NULL, *ins;
7578 MonoBasicBlock *bblock = *ref_bblock;
7580 if (cmethod->klass->valuetype && mono_class_generic_sharing_enabled (cmethod->klass) &&
7581 mono_method_is_generic_sharable (cmethod, TRUE)) {
7582 if (cmethod->is_inflated && mono_method_get_context (cmethod)->method_inst) {
7583 mono_class_vtable (cfg->domain, cmethod->klass);
7584 CHECK_TYPELOAD (cmethod->klass);
7586 vtable_arg = emit_get_rgctx_method (cfg, context_used,
7587 cmethod, MONO_RGCTX_INFO_METHOD_RGCTX);
7590 vtable_arg = emit_get_rgctx_klass (cfg, context_used,
7591 cmethod->klass, MONO_RGCTX_INFO_VTABLE);
7593 MonoVTable *vtable = mono_class_vtable (cfg->domain, cmethod->klass);
7595 CHECK_TYPELOAD (cmethod->klass);
7596 EMIT_NEW_VTABLECONST (cfg, vtable_arg, vtable);
7601 /* Avoid virtual calls to ctors if possible */
7602 if (mono_class_is_marshalbyref (cmethod->klass))
7603 callvirt_this_arg = sp [0];
7605 if (cmethod && (cfg->opt & MONO_OPT_INTRINS) && (ins = mini_emit_inst_for_ctor (cfg, cmethod, fsig, sp))) {
7606 g_assert (MONO_TYPE_IS_VOID (fsig->ret));
7607 CHECK_CFG_EXCEPTION;
7608 } else if ((cfg->opt & MONO_OPT_INLINE) && cmethod && !context_used && !vtable_arg &&
7609 mono_method_check_inlining (cfg, cmethod) &&
7610 !mono_class_is_subclass_of (cmethod->klass, mono_defaults.exception_class, FALSE)) {
7613 if ((costs = inline_method (cfg, cmethod, fsig, sp, ip, cfg->real_offset, FALSE, &bblock))) {
7614 cfg->real_offset += 5;
7616 *inline_costs += costs - 5;
7617 *ref_bblock = bblock;
7619 INLINE_FAILURE ("inline failure");
7620 // FIXME-VT: Clean this up
7621 if (cfg->gsharedvt && mini_is_gsharedvt_signature (cfg, fsig))
7622 GSHAREDVT_FAILURE(*ip);
7623 mono_emit_method_call_full (cfg, cmethod, fsig, FALSE, sp, callvirt_this_arg, NULL, NULL);
7625 } else if (cfg->gsharedvt && mini_is_gsharedvt_signature (cfg, fsig)) {
7628 addr = emit_get_rgctx_gsharedvt_call (cfg, context_used, fsig, cmethod, MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE);
7629 mono_emit_calli (cfg, fsig, sp, addr, NULL, vtable_arg);
7630 } else if (context_used &&
7631 ((!mono_method_is_generic_sharable_full (cmethod, TRUE, FALSE, FALSE) ||
7632 !mono_class_generic_sharing_enabled (cmethod->klass)) || cfg->gsharedvt)) {
7633 MonoInst *cmethod_addr;
7635 /* Generic calls made out of gsharedvt methods cannot be patched, so use an indirect call */
7637 cmethod_addr = emit_get_rgctx_method (cfg, context_used,
7638 cmethod, MONO_RGCTX_INFO_GENERIC_METHOD_CODE);
7640 mono_emit_calli (cfg, fsig, sp, cmethod_addr, NULL, vtable_arg);
7642 INLINE_FAILURE ("ctor call");
7643 ins = mono_emit_method_call_full (cfg, cmethod, fsig, FALSE, sp,
7644 callvirt_this_arg, NULL, vtable_arg);
7651 * mono_method_to_ir:
7653 * Translate the .net IL into linear IR.
7656 mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_bblock, MonoBasicBlock *end_bblock,
7657 MonoInst *return_var, MonoInst **inline_args,
7658 guint inline_offset, gboolean is_virtual_call)
7661 MonoInst *ins, **sp, **stack_start;
7662 MonoBasicBlock *bblock, *tblock = NULL, *init_localsbb = NULL;
7663 MonoSimpleBasicBlock *bb = NULL, *original_bb = NULL;
7664 MonoMethod *cmethod, *method_definition;
7665 MonoInst **arg_array;
7666 MonoMethodHeader *header;
7668 guint32 token, ins_flag;
7670 MonoClass *constrained_class = NULL;
7671 unsigned char *ip, *end, *target, *err_pos;
7672 MonoMethodSignature *sig;
7673 MonoGenericContext *generic_context = NULL;
7674 MonoGenericContainer *generic_container = NULL;
7675 MonoType **param_types;
7676 int i, n, start_new_bblock, dreg;
7677 int num_calls = 0, inline_costs = 0;
7678 int breakpoint_id = 0;
7680 GSList *class_inits = NULL;
7681 gboolean dont_verify, dont_verify_stloc, readonly = FALSE;
7683 gboolean init_locals, seq_points, skip_dead_blocks;
7684 gboolean sym_seq_points = FALSE;
7685 MonoDebugMethodInfo *minfo;
7686 MonoBitSet *seq_point_locs = NULL;
7687 MonoBitSet *seq_point_set_locs = NULL;
7689 cfg->disable_inline = is_jit_optimizer_disabled (method);
7691 /* serialization and xdomain stuff may need access to private fields and methods */
7692 dont_verify = method->klass->image->assembly->corlib_internal? TRUE: FALSE;
7693 dont_verify |= method->wrapper_type == MONO_WRAPPER_XDOMAIN_INVOKE;
7694 dont_verify |= method->wrapper_type == MONO_WRAPPER_XDOMAIN_DISPATCH;
7695 dont_verify |= method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE; /* bug #77896 */
7696 dont_verify |= method->wrapper_type == MONO_WRAPPER_COMINTEROP;
7697 dont_verify |= method->wrapper_type == MONO_WRAPPER_COMINTEROP_INVOKE;
7699 /* still some type unsafety issues in marshal wrappers... (unknown is PtrToStructure) */
7700 dont_verify_stloc = method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE;
7701 dont_verify_stloc |= method->wrapper_type == MONO_WRAPPER_UNKNOWN;
7702 dont_verify_stloc |= method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED;
7703 dont_verify_stloc |= method->wrapper_type == MONO_WRAPPER_STELEMREF;
7705 image = method->klass->image;
7706 header = mono_method_get_header (method);
7708 MonoLoaderError *error;
7710 if ((error = mono_loader_get_last_error ())) {
7711 mono_cfg_set_exception (cfg, error->exception_type);
7713 mono_cfg_set_exception (cfg, MONO_EXCEPTION_INVALID_PROGRAM);
7714 cfg->exception_message = g_strdup_printf ("Missing or incorrect header for method %s", cfg->method->name);
7716 goto exception_exit;
7718 generic_container = mono_method_get_generic_container (method);
7719 sig = mono_method_signature (method);
7720 num_args = sig->hasthis + sig->param_count;
7721 ip = (unsigned char*)header->code;
7722 cfg->cil_start = ip;
7723 end = ip + header->code_size;
7724 cfg->stat_cil_code_size += header->code_size;
7726 seq_points = cfg->gen_seq_points && cfg->method == method;
7728 if (method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED) {
7729 /* We could hit a seq point before attaching to the JIT (#8338) */
7733 if (cfg->gen_sdb_seq_points && cfg->method == method) {
7734 minfo = mono_debug_lookup_method (method);
7736 MonoSymSeqPoint *sps;
7737 int i, n_il_offsets;
7739 mono_debug_get_seq_points (minfo, NULL, NULL, NULL, &sps, &n_il_offsets);
7740 seq_point_locs = mono_bitset_mem_new (mono_mempool_alloc0 (cfg->mempool, mono_bitset_alloc_size (header->code_size, 0)), header->code_size, 0);
7741 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);
7742 sym_seq_points = TRUE;
7743 for (i = 0; i < n_il_offsets; ++i) {
7744 if (sps [i].il_offset < header->code_size)
7745 mono_bitset_set_fast (seq_point_locs, sps [i].il_offset);
7748 } else if (!method->wrapper_type && !method->dynamic && mono_debug_image_has_debug_info (method->klass->image)) {
7749 /* Methods without line number info like auto-generated property accessors */
7750 seq_point_locs = mono_bitset_mem_new (mono_mempool_alloc0 (cfg->mempool, mono_bitset_alloc_size (header->code_size, 0)), header->code_size, 0);
7751 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);
7752 sym_seq_points = TRUE;
7757 * Methods without init_locals set could cause asserts in various passes
7758 * (#497220). To work around this, we emit dummy initialization opcodes
7759 * (OP_DUMMY_ICONST etc.) which generate no code. These are only supported
7760 * on some platforms.
7762 if ((cfg->opt & MONO_OPT_UNSAFE) && ARCH_HAVE_DUMMY_INIT)
7763 init_locals = header->init_locals;
7767 method_definition = method;
7768 while (method_definition->is_inflated) {
7769 MonoMethodInflated *imethod = (MonoMethodInflated *) method_definition;
7770 method_definition = imethod->declaring;
7773 /* SkipVerification is not allowed if core-clr is enabled */
7774 if (!dont_verify && mini_assembly_can_skip_verification (cfg->domain, method)) {
7776 dont_verify_stloc = TRUE;
7779 if (sig->is_inflated)
7780 generic_context = mono_method_get_context (method);
7781 else if (generic_container)
7782 generic_context = &generic_container->context;
7783 cfg->generic_context = generic_context;
7785 if (!cfg->generic_sharing_context)
7786 g_assert (!sig->has_type_parameters);
7788 if (sig->generic_param_count && method->wrapper_type == MONO_WRAPPER_NONE) {
7789 g_assert (method->is_inflated);
7790 g_assert (mono_method_get_context (method)->method_inst);
7792 if (method->is_inflated && mono_method_get_context (method)->method_inst)
7793 g_assert (sig->generic_param_count);
7795 if (cfg->method == method) {
7796 cfg->real_offset = 0;
7798 cfg->real_offset = inline_offset;
7801 cfg->cil_offset_to_bb = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoBasicBlock*) * header->code_size);
7802 cfg->cil_offset_to_bb_len = header->code_size;
7804 cfg->current_method = method;
7806 if (cfg->verbose_level > 2)
7807 printf ("method to IR %s\n", mono_method_full_name (method, TRUE));
7809 param_types = mono_mempool_alloc (cfg->mempool, sizeof (MonoType*) * num_args);
7811 param_types [0] = method->klass->valuetype?&method->klass->this_arg:&method->klass->byval_arg;
7812 for (n = 0; n < sig->param_count; ++n)
7813 param_types [n + sig->hasthis] = sig->params [n];
7814 cfg->arg_types = param_types;
7816 cfg->dont_inline = g_list_prepend (cfg->dont_inline, method);
7817 if (cfg->method == method) {
7819 if (cfg->prof_options & MONO_PROFILE_INS_COVERAGE)
7820 cfg->coverage_info = mono_profiler_coverage_alloc (cfg->method, header->code_size);
7823 NEW_BBLOCK (cfg, start_bblock);
7824 cfg->bb_entry = start_bblock;
7825 start_bblock->cil_code = NULL;
7826 start_bblock->cil_length = 0;
7829 NEW_BBLOCK (cfg, end_bblock);
7830 cfg->bb_exit = end_bblock;
7831 end_bblock->cil_code = NULL;
7832 end_bblock->cil_length = 0;
7833 end_bblock->flags |= BB_INDIRECT_JUMP_TARGET;
7834 g_assert (cfg->num_bblocks == 2);
7836 arg_array = cfg->args;
7838 if (header->num_clauses) {
7839 cfg->spvars = g_hash_table_new (NULL, NULL);
7840 cfg->exvars = g_hash_table_new (NULL, NULL);
7842 /* handle exception clauses */
7843 for (i = 0; i < header->num_clauses; ++i) {
7844 MonoBasicBlock *try_bb;
7845 MonoExceptionClause *clause = &header->clauses [i];
7846 GET_BBLOCK (cfg, try_bb, ip + clause->try_offset);
7847 try_bb->real_offset = clause->try_offset;
7848 try_bb->try_start = TRUE;
7849 try_bb->region = ((i + 1) << 8) | clause->flags;
7850 GET_BBLOCK (cfg, tblock, ip + clause->handler_offset);
7851 tblock->real_offset = clause->handler_offset;
7852 tblock->flags |= BB_EXCEPTION_HANDLER;
7855 * Linking the try block with the EH block hinders inlining as we won't be able to
7856 * merge the bblocks from inlining and produce an artificial hole for no good reason.
7858 if (COMPILE_LLVM (cfg))
7859 link_bblock (cfg, try_bb, tblock);
7861 if (*(ip + clause->handler_offset) == CEE_POP)
7862 tblock->flags |= BB_EXCEPTION_DEAD_OBJ;
7864 if (clause->flags == MONO_EXCEPTION_CLAUSE_FINALLY ||
7865 clause->flags == MONO_EXCEPTION_CLAUSE_FILTER ||
7866 clause->flags == MONO_EXCEPTION_CLAUSE_FAULT) {
7867 MONO_INST_NEW (cfg, ins, OP_START_HANDLER);
7868 MONO_ADD_INS (tblock, ins);
7870 if (seq_points && clause->flags != MONO_EXCEPTION_CLAUSE_FINALLY) {
7871 /* finally clauses already have a seq point */
7872 NEW_SEQ_POINT (cfg, ins, clause->handler_offset, TRUE);
7873 MONO_ADD_INS (tblock, ins);
7876 /* todo: is a fault block unsafe to optimize? */
7877 if (clause->flags == MONO_EXCEPTION_CLAUSE_FAULT)
7878 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) {
7889 /* mostly like handle_stack_args (), but just sets the input args */
7890 /* printf ("handling clause at IL_%04x\n", clause->handler_offset); */
7891 tblock->in_scount = 1;
7892 tblock->in_stack = mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*));
7893 tblock->in_stack [0] = mono_create_exvar_for_offset (cfg, clause->handler_offset);
7897 #ifdef MONO_CONTEXT_SET_LLVM_EXC_REG
7898 /* The EH code passes in the exception in a register to both JITted and LLVM compiled code */
7899 if (!cfg->compile_llvm) {
7900 MONO_INST_NEW (cfg, ins, OP_GET_EX_OBJ);
7901 ins->dreg = tblock->in_stack [0]->dreg;
7902 MONO_ADD_INS (tblock, ins);
7905 MonoInst *dummy_use;
7908 * Add a dummy use for the exvar so its liveness info will be
7911 EMIT_NEW_DUMMY_USE (cfg, dummy_use, tblock->in_stack [0]);
7914 if (clause->flags == MONO_EXCEPTION_CLAUSE_FILTER) {
7915 GET_BBLOCK (cfg, tblock, ip + clause->data.filter_offset);
7916 tblock->flags |= BB_EXCEPTION_HANDLER;
7917 tblock->real_offset = clause->data.filter_offset;
7918 tblock->in_scount = 1;
7919 tblock->in_stack = mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*));
7920 /* The filter block shares the exvar with the handler block */
7921 tblock->in_stack [0] = mono_create_exvar_for_offset (cfg, clause->handler_offset);
7922 MONO_INST_NEW (cfg, ins, OP_START_HANDLER);
7923 MONO_ADD_INS (tblock, ins);
7927 if (clause->flags != MONO_EXCEPTION_CLAUSE_FILTER &&
7928 clause->data.catch_class &&
7929 cfg->generic_sharing_context &&
7930 mono_class_check_context_used (clause->data.catch_class)) {
7932 * In shared generic code with catch
7933 * clauses containing type variables
7934 * the exception handling code has to
7935 * be able to get to the rgctx.
7936 * Therefore we have to make sure that
7937 * the vtable/mrgctx argument (for
7938 * static or generic methods) or the
7939 * "this" argument (for non-static
7940 * methods) are live.
7942 if ((method->flags & METHOD_ATTRIBUTE_STATIC) ||
7943 mini_method_get_context (method)->method_inst ||
7944 method->klass->valuetype) {
7945 mono_get_vtable_var (cfg);
7947 MonoInst *dummy_use;
7949 EMIT_NEW_DUMMY_USE (cfg, dummy_use, arg_array [0]);
7954 arg_array = (MonoInst **) alloca (sizeof (MonoInst *) * num_args);
7955 cfg->cbb = start_bblock;
7956 cfg->args = arg_array;
7957 mono_save_args (cfg, sig, inline_args);
7960 /* FIRST CODE BLOCK */
7961 NEW_BBLOCK (cfg, bblock);
7962 bblock->cil_code = ip;
7966 ADD_BBLOCK (cfg, bblock);
7968 if (cfg->method == method) {
7969 breakpoint_id = mono_debugger_method_has_breakpoint (method);
7970 if (breakpoint_id) {
7971 MONO_INST_NEW (cfg, ins, OP_BREAK);
7972 MONO_ADD_INS (bblock, ins);
7976 /* we use a separate basic block for the initialization code */
7977 NEW_BBLOCK (cfg, init_localsbb);
7978 cfg->bb_init = init_localsbb;
7979 init_localsbb->real_offset = cfg->real_offset;
7980 start_bblock->next_bb = init_localsbb;
7981 init_localsbb->next_bb = bblock;
7982 link_bblock (cfg, start_bblock, init_localsbb);
7983 link_bblock (cfg, init_localsbb, bblock);
7985 cfg->cbb = init_localsbb;
7987 if (cfg->gsharedvt && cfg->method == method) {
7988 MonoGSharedVtMethodInfo *info;
7989 MonoInst *var, *locals_var;
7992 info = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoGSharedVtMethodInfo));
7993 info->method = cfg->method;
7994 info->count_entries = 16;
7995 info->entries = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoRuntimeGenericContextInfoTemplate) * info->count_entries);
7996 cfg->gsharedvt_info = info;
7998 var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
7999 /* prevent it from being register allocated */
8000 //var->flags |= MONO_INST_VOLATILE;
8001 cfg->gsharedvt_info_var = var;
8003 ins = emit_get_rgctx_gsharedvt_method (cfg, mini_method_check_context_used (cfg, method), method, info);
8004 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, var->dreg, ins->dreg);
8006 /* Allocate locals */
8007 locals_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
8008 /* prevent it from being register allocated */
8009 //locals_var->flags |= MONO_INST_VOLATILE;
8010 cfg->gsharedvt_locals_var = locals_var;
8012 dreg = alloc_ireg (cfg);
8013 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI4_MEMBASE, dreg, var->dreg, MONO_STRUCT_OFFSET (MonoGSharedVtMethodRuntimeInfo, locals_size));
8015 MONO_INST_NEW (cfg, ins, OP_LOCALLOC);
8016 ins->dreg = locals_var->dreg;
8018 MONO_ADD_INS (cfg->cbb, ins);
8019 cfg->gsharedvt_locals_var_ins = ins;
8021 cfg->flags |= MONO_CFG_HAS_ALLOCA;
8024 ins->flags |= MONO_INST_INIT;
8028 if (mono_security_core_clr_enabled ()) {
8029 /* check if this is native code, e.g. an icall or a p/invoke */
8030 if (method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE) {
8031 MonoMethod *wrapped = mono_marshal_method_from_wrapper (method);
8033 gboolean pinvk = (wrapped->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL);
8034 gboolean icall = (wrapped->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL);
8036 /* if this ia a native call then it can only be JITted from platform code */
8037 if ((icall || pinvk) && method->klass && method->klass->image) {
8038 if (!mono_security_core_clr_is_platform_image (method->klass->image)) {
8039 MonoException *ex = icall ? mono_get_exception_security () :
8040 mono_get_exception_method_access ();
8041 emit_throw_exception (cfg, ex);
8048 CHECK_CFG_EXCEPTION;
8050 if (header->code_size == 0)
8053 if (get_basic_blocks (cfg, header, cfg->real_offset, ip, end, &err_pos)) {
8058 if (cfg->method == method)
8059 mono_debug_init_method (cfg, bblock, breakpoint_id);
8061 for (n = 0; n < header->num_locals; ++n) {
8062 if (header->locals [n]->type == MONO_TYPE_VOID && !header->locals [n]->byref)
8067 /* We force the vtable variable here for all shared methods
8068 for the possibility that they might show up in a stack
8069 trace where their exact instantiation is needed. */
8070 if (cfg->generic_sharing_context && method == cfg->method) {
8071 if ((method->flags & METHOD_ATTRIBUTE_STATIC) ||
8072 mini_method_get_context (method)->method_inst ||
8073 method->klass->valuetype) {
8074 mono_get_vtable_var (cfg);
8076 /* FIXME: Is there a better way to do this?
8077 We need the variable live for the duration
8078 of the whole method. */
8079 cfg->args [0]->flags |= MONO_INST_VOLATILE;
8083 /* add a check for this != NULL to inlined methods */
8084 if (is_virtual_call) {
8087 NEW_ARGLOAD (cfg, arg_ins, 0);
8088 MONO_ADD_INS (cfg->cbb, arg_ins);
8089 MONO_EMIT_NEW_CHECK_THIS (cfg, arg_ins->dreg);
8092 skip_dead_blocks = !dont_verify;
8093 if (skip_dead_blocks) {
8094 original_bb = bb = mono_basic_block_split (method, &cfg->error);
8099 /* we use a spare stack slot in SWITCH and NEWOBJ and others */
8100 stack_start = sp = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoInst*) * (header->max_stack + 1));
8103 start_new_bblock = 0;
8106 if (cfg->method == method)
8107 cfg->real_offset = ip - header->code;
8109 cfg->real_offset = inline_offset;
8114 if (start_new_bblock) {
8115 bblock->cil_length = ip - bblock->cil_code;
8116 if (start_new_bblock == 2) {
8117 g_assert (ip == tblock->cil_code);
8119 GET_BBLOCK (cfg, tblock, ip);
8121 bblock->next_bb = tblock;
8124 start_new_bblock = 0;
8125 for (i = 0; i < bblock->in_scount; ++i) {
8126 if (cfg->verbose_level > 3)
8127 printf ("loading %d from temp %d\n", i, (int)bblock->in_stack [i]->inst_c0);
8128 EMIT_NEW_TEMPLOAD (cfg, ins, bblock->in_stack [i]->inst_c0);
8132 g_slist_free (class_inits);
8135 if ((tblock = cfg->cil_offset_to_bb [ip - cfg->cil_start]) && (tblock != bblock)) {
8136 link_bblock (cfg, bblock, tblock);
8137 if (sp != stack_start) {
8138 handle_stack_args (cfg, stack_start, sp - stack_start);
8140 CHECK_UNVERIFIABLE (cfg);
8142 bblock->next_bb = tblock;
8145 for (i = 0; i < bblock->in_scount; ++i) {
8146 if (cfg->verbose_level > 3)
8147 printf ("loading %d from temp %d\n", i, (int)bblock->in_stack [i]->inst_c0);
8148 EMIT_NEW_TEMPLOAD (cfg, ins, bblock->in_stack [i]->inst_c0);
8151 g_slist_free (class_inits);
8156 if (skip_dead_blocks) {
8157 int ip_offset = ip - header->code;
8159 if (ip_offset == bb->end)
8163 int op_size = mono_opcode_size (ip, end);
8164 g_assert (op_size > 0); /*The BB formation pass must catch all bad ops*/
8166 if (cfg->verbose_level > 3) printf ("SKIPPING DEAD OP at %x\n", ip_offset);
8168 if (ip_offset + op_size == bb->end) {
8169 MONO_INST_NEW (cfg, ins, OP_NOP);
8170 MONO_ADD_INS (bblock, ins);
8171 start_new_bblock = 1;
8179 * Sequence points are points where the debugger can place a breakpoint.
8180 * Currently, we generate these automatically at points where the IL
8183 if (seq_points && ((!sym_seq_points && (sp == stack_start)) || (sym_seq_points && mono_bitset_test_fast (seq_point_locs, ip - header->code)))) {
8185 * Make methods interruptable at the beginning, and at the targets of
8186 * backward branches.
8187 * Also, do this at the start of every bblock in methods with clauses too,
8188 * to be able to handle instructions with inprecise control flow like
8190 * Backward branches are handled at the end of method-to-ir ().
8192 gboolean intr_loc = ip == header->code || (!cfg->cbb->last_ins && cfg->header->num_clauses);
8194 /* Avoid sequence points on empty IL like .volatile */
8195 // FIXME: Enable this
8196 //if (!(cfg->cbb->last_ins && cfg->cbb->last_ins->opcode == OP_SEQ_POINT)) {
8197 NEW_SEQ_POINT (cfg, ins, ip - header->code, intr_loc);
8198 if (sp != stack_start)
8199 ins->flags |= MONO_INST_NONEMPTY_STACK;
8200 MONO_ADD_INS (cfg->cbb, ins);
8203 mono_bitset_set_fast (seq_point_set_locs, ip - header->code);
8206 bblock->real_offset = cfg->real_offset;
8208 if ((cfg->method == method) && cfg->coverage_info) {
8209 guint32 cil_offset = ip - header->code;
8210 cfg->coverage_info->data [cil_offset].cil_code = ip;
8212 /* TODO: Use an increment here */
8213 #if defined(TARGET_X86)
8214 MONO_INST_NEW (cfg, ins, OP_STORE_MEM_IMM);
8215 ins->inst_p0 = &(cfg->coverage_info->data [cil_offset].count);
8217 MONO_ADD_INS (cfg->cbb, ins);
8219 EMIT_NEW_PCONST (cfg, ins, &(cfg->coverage_info->data [cil_offset].count));
8220 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STORE_MEMBASE_IMM, ins->dreg, 0, 1);
8224 if (cfg->verbose_level > 3)
8225 printf ("converting (in B%d: stack: %d) %s", bblock->block_num, (int)(sp - stack_start), mono_disasm_code_one (NULL, method, ip, NULL));
8229 if (seq_points && !sym_seq_points && sp != stack_start) {
8231 * The C# compiler uses these nops to notify the JIT that it should
8232 * insert seq points.
8234 NEW_SEQ_POINT (cfg, ins, ip - header->code, FALSE);
8235 MONO_ADD_INS (cfg->cbb, ins);
8237 if (cfg->keep_cil_nops)
8238 MONO_INST_NEW (cfg, ins, OP_HARD_NOP);
8240 MONO_INST_NEW (cfg, ins, OP_NOP);
8242 MONO_ADD_INS (bblock, ins);
8245 if (should_insert_brekpoint (cfg->method)) {
8246 ins = mono_emit_jit_icall (cfg, mono_debugger_agent_user_break, NULL);
8248 MONO_INST_NEW (cfg, ins, OP_NOP);
8251 MONO_ADD_INS (bblock, ins);
8257 CHECK_STACK_OVF (1);
8258 n = (*ip)-CEE_LDARG_0;
8260 EMIT_NEW_ARGLOAD (cfg, ins, n);
8268 CHECK_STACK_OVF (1);
8269 n = (*ip)-CEE_LDLOC_0;
8271 EMIT_NEW_LOCLOAD (cfg, ins, n);
8280 n = (*ip)-CEE_STLOC_0;
8283 if (!dont_verify_stloc && target_type_is_incompatible (cfg, header->locals [n], *sp))
8285 emit_stloc_ir (cfg, sp, header, n);
8292 CHECK_STACK_OVF (1);
8295 EMIT_NEW_ARGLOAD (cfg, ins, n);
8301 CHECK_STACK_OVF (1);
8304 NEW_ARGLOADA (cfg, ins, n);
8305 MONO_ADD_INS (cfg->cbb, ins);
8315 if (!dont_verify_stloc && target_type_is_incompatible (cfg, param_types [ip [1]], *sp))
8317 EMIT_NEW_ARGSTORE (cfg, ins, n, *sp);
8322 CHECK_STACK_OVF (1);
8325 EMIT_NEW_LOCLOAD (cfg, ins, n);
8329 case CEE_LDLOCA_S: {
8330 unsigned char *tmp_ip;
8332 CHECK_STACK_OVF (1);
8333 CHECK_LOCAL (ip [1]);
8335 if ((tmp_ip = emit_optimized_ldloca_ir (cfg, ip, end, 1))) {
8341 EMIT_NEW_LOCLOADA (cfg, ins, ip [1]);
8350 CHECK_LOCAL (ip [1]);
8351 if (!dont_verify_stloc && target_type_is_incompatible (cfg, header->locals [ip [1]], *sp))
8353 emit_stloc_ir (cfg, sp, header, ip [1]);
8358 CHECK_STACK_OVF (1);
8359 EMIT_NEW_PCONST (cfg, ins, NULL);
8360 ins->type = STACK_OBJ;
8365 CHECK_STACK_OVF (1);
8366 EMIT_NEW_ICONST (cfg, ins, -1);
8379 CHECK_STACK_OVF (1);
8380 EMIT_NEW_ICONST (cfg, ins, (*ip) - CEE_LDC_I4_0);
8386 CHECK_STACK_OVF (1);
8388 EMIT_NEW_ICONST (cfg, ins, *((signed char*)ip));
8394 CHECK_STACK_OVF (1);
8395 EMIT_NEW_ICONST (cfg, ins, (gint32)read32 (ip + 1));
8401 CHECK_STACK_OVF (1);
8402 MONO_INST_NEW (cfg, ins, OP_I8CONST);
8403 ins->type = STACK_I8;
8404 ins->dreg = alloc_dreg (cfg, STACK_I8);
8406 ins->inst_l = (gint64)read64 (ip);
8407 MONO_ADD_INS (bblock, ins);
8413 gboolean use_aotconst = FALSE;
8415 #ifdef TARGET_POWERPC
8416 /* FIXME: Clean this up */
8417 if (cfg->compile_aot)
8418 use_aotconst = TRUE;
8421 /* FIXME: we should really allocate this only late in the compilation process */
8422 f = mono_domain_alloc (cfg->domain, sizeof (float));
8424 CHECK_STACK_OVF (1);
8430 EMIT_NEW_AOTCONST (cfg, cons, MONO_PATCH_INFO_R4, f);
8432 dreg = alloc_freg (cfg);
8433 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADR4_MEMBASE, dreg, cons->dreg, 0);
8434 ins->type = cfg->r4_stack_type;
8436 MONO_INST_NEW (cfg, ins, OP_R4CONST);
8437 ins->type = cfg->r4_stack_type;
8438 ins->dreg = alloc_dreg (cfg, STACK_R8);
8440 MONO_ADD_INS (bblock, ins);
8450 gboolean use_aotconst = FALSE;
8452 #ifdef TARGET_POWERPC
8453 /* FIXME: Clean this up */
8454 if (cfg->compile_aot)
8455 use_aotconst = TRUE;
8458 /* FIXME: we should really allocate this only late in the compilation process */
8459 d = mono_domain_alloc (cfg->domain, sizeof (double));
8461 CHECK_STACK_OVF (1);
8467 EMIT_NEW_AOTCONST (cfg, cons, MONO_PATCH_INFO_R8, d);
8469 dreg = alloc_freg (cfg);
8470 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADR8_MEMBASE, dreg, cons->dreg, 0);
8471 ins->type = STACK_R8;
8473 MONO_INST_NEW (cfg, ins, OP_R8CONST);
8474 ins->type = STACK_R8;
8475 ins->dreg = alloc_dreg (cfg, STACK_R8);
8477 MONO_ADD_INS (bblock, ins);
8486 MonoInst *temp, *store;
8488 CHECK_STACK_OVF (1);
8492 temp = mono_compile_create_var (cfg, type_from_stack_type (ins), OP_LOCAL);
8493 EMIT_NEW_TEMPSTORE (cfg, store, temp->inst_c0, ins);
8495 EMIT_NEW_TEMPLOAD (cfg, ins, temp->inst_c0);
8498 EMIT_NEW_TEMPLOAD (cfg, ins, temp->inst_c0);
8511 if (sp [0]->type == STACK_R8)
8512 /* we need to pop the value from the x86 FP stack */
8513 MONO_EMIT_NEW_UNALU (cfg, OP_X86_FPOP, -1, sp [0]->dreg);
8519 INLINE_FAILURE ("jmp");
8520 GSHAREDVT_FAILURE (*ip);
8523 if (stack_start != sp)
8525 token = read32 (ip + 1);
8526 /* FIXME: check the signature matches */
8527 cmethod = mini_get_method (cfg, method, token, NULL, generic_context);
8529 if (!cmethod || mono_loader_get_last_error ())
8532 if (cfg->generic_sharing_context && mono_method_check_context_used (cmethod))
8533 GENERIC_SHARING_FAILURE (CEE_JMP);
8535 emit_instrumentation_call (cfg, mono_profiler_method_leave);
8537 if (ARCH_HAVE_OP_TAIL_CALL) {
8538 MonoMethodSignature *fsig = mono_method_signature (cmethod);
8541 /* Handle tail calls similarly to calls */
8542 n = fsig->param_count + fsig->hasthis;
8546 MONO_INST_NEW_CALL (cfg, call, OP_TAILCALL);
8547 call->method = cmethod;
8548 call->tail_call = TRUE;
8549 call->signature = mono_method_signature (cmethod);
8550 call->args = mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*) * n);
8551 call->inst.inst_p0 = cmethod;
8552 for (i = 0; i < n; ++i)
8553 EMIT_NEW_ARGLOAD (cfg, call->args [i], i);
8555 mono_arch_emit_call (cfg, call);
8556 cfg->param_area = MAX(cfg->param_area, call->stack_usage);
8557 MONO_ADD_INS (bblock, (MonoInst*)call);
8559 for (i = 0; i < num_args; ++i)
8560 /* Prevent arguments from being optimized away */
8561 arg_array [i]->flags |= MONO_INST_VOLATILE;
8563 MONO_INST_NEW_CALL (cfg, call, OP_JMP);
8564 ins = (MonoInst*)call;
8565 ins->inst_p0 = cmethod;
8566 MONO_ADD_INS (bblock, ins);
8570 start_new_bblock = 1;
8575 MonoMethodSignature *fsig;
8578 token = read32 (ip + 1);
8582 //GSHAREDVT_FAILURE (*ip);
8587 fsig = mini_get_signature (method, token, generic_context);
8589 if (method->dynamic && fsig->pinvoke) {
8593 * This is a call through a function pointer using a pinvoke
8594 * signature. Have to create a wrapper and call that instead.
8595 * FIXME: This is very slow, need to create a wrapper at JIT time
8596 * instead based on the signature.
8598 EMIT_NEW_IMAGECONST (cfg, args [0], method->klass->image);
8599 EMIT_NEW_PCONST (cfg, args [1], fsig);
8601 addr = mono_emit_jit_icall (cfg, mono_get_native_calli_wrapper, args);
8604 n = fsig->param_count + fsig->hasthis;
8608 //g_assert (!virtual || fsig->hasthis);
8612 inline_costs += 10 * num_calls++;
8615 * Making generic calls out of gsharedvt methods.
8616 * This needs to be used for all generic calls, not just ones with a gsharedvt signature, to avoid
8617 * patching gshared method addresses into a gsharedvt method.
8619 if (cfg->gsharedvt && mini_is_gsharedvt_signature (cfg, fsig)) {
8621 * We pass the address to the gsharedvt trampoline in the rgctx reg
8623 MonoInst *callee = addr;
8625 if (method->wrapper_type != MONO_WRAPPER_DELEGATE_INVOKE)
8627 GSHAREDVT_FAILURE (*ip);
8629 addr = emit_get_rgctx_sig (cfg, context_used,
8630 fsig, MONO_RGCTX_INFO_SIG_GSHAREDVT_OUT_TRAMPOLINE_CALLI);
8631 ins = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr, NULL, callee);
8635 /* Prevent inlining of methods with indirect calls */
8636 INLINE_FAILURE ("indirect call");
8638 if (addr->opcode == OP_PCONST || addr->opcode == OP_AOTCONST || addr->opcode == OP_GOT_ENTRY) {
8643 * Instead of emitting an indirect call, emit a direct call
8644 * with the contents of the aotconst as the patch info.
8646 if (addr->opcode == OP_PCONST || addr->opcode == OP_AOTCONST) {
8647 info_type = addr->inst_c1;
8648 info_data = addr->inst_p0;
8650 info_type = addr->inst_right->inst_c1;
8651 info_data = addr->inst_right->inst_left;
8654 if (info_type == MONO_PATCH_INFO_ICALL_ADDR || info_type == MONO_PATCH_INFO_JIT_ICALL_ADDR) {
8655 ins = (MonoInst*)mono_emit_abs_call (cfg, info_type, info_data, fsig, sp);
8660 ins = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr, NULL, NULL);
8664 /* End of call, INS should contain the result of the call, if any */
8666 if (!MONO_TYPE_IS_VOID (fsig->ret)) {
8668 *sp++ = mono_emit_widen_call_res (cfg, ins, fsig);
8671 CHECK_CFG_EXCEPTION;
8675 constrained_class = NULL;
8679 case CEE_CALLVIRT: {
8680 MonoInst *addr = NULL;
8681 MonoMethodSignature *fsig = NULL;
8683 int virtual = *ip == CEE_CALLVIRT;
8684 gboolean pass_imt_from_rgctx = FALSE;
8685 MonoInst *imt_arg = NULL;
8686 MonoInst *keep_this_alive = NULL;
8687 gboolean pass_vtable = FALSE;
8688 gboolean pass_mrgctx = FALSE;
8689 MonoInst *vtable_arg = NULL;
8690 gboolean check_this = FALSE;
8691 gboolean supported_tail_call = FALSE;
8692 gboolean tail_call = FALSE;
8693 gboolean need_seq_point = FALSE;
8694 guint32 call_opcode = *ip;
8695 gboolean emit_widen = TRUE;
8696 gboolean push_res = TRUE;
8697 gboolean skip_ret = FALSE;
8698 gboolean delegate_invoke = FALSE;
8699 gboolean direct_icall = FALSE;
8700 gboolean constrained_partial_call = FALSE;
8701 MonoMethod *cil_method;
8704 token = read32 (ip + 1);
8708 cmethod = mini_get_method (cfg, method, token, NULL, generic_context);
8709 cil_method = cmethod;
8711 if (constrained_class) {
8712 if ((constrained_class->byval_arg.type == MONO_TYPE_VAR || constrained_class->byval_arg.type == MONO_TYPE_MVAR) && cfg->generic_sharing_context) {
8713 if (!mini_is_gsharedvt_klass (cfg, constrained_class)) {
8714 g_assert (!cmethod->klass->valuetype);
8715 if (!mini_type_is_reference (cfg, &constrained_class->byval_arg))
8716 constrained_partial_call = TRUE;
8720 if (method->wrapper_type != MONO_WRAPPER_NONE) {
8721 if (cfg->verbose_level > 2)
8722 printf ("DM Constrained call to %s\n", mono_type_get_full_name (constrained_class));
8723 if (!((constrained_class->byval_arg.type == MONO_TYPE_VAR ||
8724 constrained_class->byval_arg.type == MONO_TYPE_MVAR) &&
8725 cfg->generic_sharing_context)) {
8726 cmethod = mono_get_method_constrained_with_method (image, cil_method, constrained_class, generic_context, &cfg->error);
8730 if (cfg->verbose_level > 2)
8731 printf ("Constrained call to %s\n", mono_type_get_full_name (constrained_class));
8733 if ((constrained_class->byval_arg.type == MONO_TYPE_VAR || constrained_class->byval_arg.type == MONO_TYPE_MVAR) && cfg->generic_sharing_context) {
8735 * This is needed since get_method_constrained can't find
8736 * the method in klass representing a type var.
8737 * The type var is guaranteed to be a reference type in this
8740 if (!mini_is_gsharedvt_klass (cfg, constrained_class))
8741 g_assert (!cmethod->klass->valuetype);
8743 cmethod = mono_get_method_constrained_checked (image, token, constrained_class, generic_context, &cil_method, &cfg->error);
8749 if (!cmethod || mono_loader_get_last_error ())
8751 if (!dont_verify && !cfg->skip_visibility) {
8752 MonoMethod *target_method = cil_method;
8753 if (method->is_inflated) {
8754 target_method = mini_get_method_allow_open (method, token, NULL, &(mono_method_get_generic_container (method_definition)->context));
8756 if (!mono_method_can_access_method (method_definition, target_method) &&
8757 !mono_method_can_access_method (method, cil_method))
8758 METHOD_ACCESS_FAILURE (method, cil_method);
8761 if (mono_security_core_clr_enabled ())
8762 ensure_method_is_allowed_to_call_method (cfg, method, cil_method, bblock, ip);
8764 if (!virtual && (cmethod->flags & METHOD_ATTRIBUTE_ABSTRACT))
8765 /* MS.NET seems to silently convert this to a callvirt */
8770 * MS.NET accepts non virtual calls to virtual final methods of transparent proxy classes and
8771 * converts to a callvirt.
8773 * tests/bug-515884.il is an example of this behavior
8775 const int test_flags = METHOD_ATTRIBUTE_VIRTUAL | METHOD_ATTRIBUTE_FINAL | METHOD_ATTRIBUTE_STATIC;
8776 const int expected_flags = METHOD_ATTRIBUTE_VIRTUAL | METHOD_ATTRIBUTE_FINAL;
8777 if (!virtual && mono_class_is_marshalbyref (cmethod->klass) && (cmethod->flags & test_flags) == expected_flags && cfg->method->wrapper_type == MONO_WRAPPER_NONE)
8781 if (!cmethod->klass->inited)
8782 if (!mono_class_init (cmethod->klass))
8783 TYPE_LOAD_ERROR (cmethod->klass);
8785 fsig = mono_method_signature (cmethod);
8788 if (cmethod->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL &&
8789 mini_class_is_system_array (cmethod->klass)) {
8790 array_rank = cmethod->klass->rank;
8791 } else if ((cmethod->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) && icall_is_direct_callable (cfg, cmethod)) {
8792 direct_icall = TRUE;
8793 } else if (fsig->pinvoke) {
8794 MonoMethod *wrapper = mono_marshal_get_native_wrapper (cmethod,
8795 check_for_pending_exc, cfg->compile_aot);
8796 fsig = mono_method_signature (wrapper);
8797 } else if (constrained_class) {
8799 fsig = mono_method_get_signature_checked (cmethod, image, token, generic_context, &cfg->error);
8803 mono_save_token_info (cfg, image, token, cil_method);
8805 if (!(seq_point_locs && mono_bitset_test_fast (seq_point_locs, ip + 5 - header->code)))
8806 need_seq_point = TRUE;
8808 /* Don't support calls made using type arguments for now */
8810 if (cfg->gsharedvt) {
8811 if (mini_is_gsharedvt_signature (cfg, fsig))
8812 GSHAREDVT_FAILURE (*ip);
8816 if (cmethod->string_ctor && method->wrapper_type != MONO_WRAPPER_RUNTIME_INVOKE)
8817 g_assert_not_reached ();
8819 n = fsig->param_count + fsig->hasthis;
8821 if (!cfg->generic_sharing_context && cmethod->klass->generic_container)
8824 if (!cfg->generic_sharing_context)
8825 g_assert (!mono_method_check_context_used (cmethod));
8829 //g_assert (!virtual || fsig->hasthis);
8833 if (constrained_class) {
8834 if (mini_is_gsharedvt_klass (cfg, constrained_class)) {
8835 if ((cmethod->klass != mono_defaults.object_class) && constrained_class->valuetype && cmethod->klass->valuetype) {
8836 /* The 'Own method' case below */
8837 } else if (cmethod->klass->image != mono_defaults.corlib && !(cmethod->klass->flags & TYPE_ATTRIBUTE_INTERFACE) && !cmethod->klass->valuetype) {
8838 /* 'The type parameter is instantiated as a reference type' case below. */
8840 ins = handle_constrained_gsharedvt_call (cfg, cmethod, fsig, sp, constrained_class, &emit_widen, &bblock);
8841 CHECK_CFG_EXCEPTION;
8848 * We have the `constrained.' prefix opcode.
8850 if (constrained_partial_call) {
8851 gboolean need_box = TRUE;
8854 * The receiver is a valuetype, but the exact type is not known at compile time. This means the
8855 * called method is not known at compile time either. The called method could end up being
8856 * one of the methods on the parent classes (object/valuetype/enum), in which case we need
8857 * to box the receiver.
8858 * A simple solution would be to box always and make a normal virtual call, but that would
8859 * be bad performance wise.
8861 if (cmethod->klass->flags & TYPE_ATTRIBUTE_INTERFACE && cmethod->klass->generic_class) {
8863 * The parent classes implement no generic interfaces, so the called method will be a vtype method, so no boxing neccessary.
8870 MonoBasicBlock *is_ref_bb, *end_bb;
8871 MonoInst *nonbox_call;
8874 * Determine at runtime whenever the called method is defined on object/valuetype/enum, and emit a boxing call
8876 * FIXME: It is possible to inline the called method in a lot of cases, i.e. for T_INT,
8877 * the no-box case goes to a method in Int32, while the box case goes to a method in Enum.
8879 addr = emit_get_rgctx_virt_method (cfg, mono_class_check_context_used (constrained_class), constrained_class, cmethod, MONO_RGCTX_INFO_VIRT_METHOD_CODE);
8881 NEW_BBLOCK (cfg, is_ref_bb);
8882 NEW_BBLOCK (cfg, end_bb);
8884 box_type = emit_get_rgctx_virt_method (cfg, mono_class_check_context_used (constrained_class), constrained_class, cmethod, MONO_RGCTX_INFO_VIRT_METHOD_BOX_TYPE);
8885 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, box_type->dreg, 1);
8886 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, is_ref_bb);
8889 nonbox_call = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr, NULL, NULL);
8891 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
8894 MONO_START_BB (cfg, is_ref_bb);
8895 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &constrained_class->byval_arg, sp [0]->dreg, 0);
8896 ins->klass = constrained_class;
8897 sp [0] = handle_box (cfg, ins, constrained_class, mono_class_check_context_used (constrained_class), &bblock);
8898 ins = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr, NULL, NULL);
8900 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
8902 MONO_START_BB (cfg, end_bb);
8905 nonbox_call->dreg = ins->dreg;
8907 g_assert (cmethod->klass->flags & TYPE_ATTRIBUTE_INTERFACE);
8908 addr = emit_get_rgctx_virt_method (cfg, mono_class_check_context_used (constrained_class), constrained_class, cmethod, MONO_RGCTX_INFO_VIRT_METHOD_CODE);
8909 ins = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr, NULL, NULL);
8912 } else if (constrained_class->valuetype && (cmethod->klass == mono_defaults.object_class || cmethod->klass == mono_defaults.enum_class->parent || cmethod->klass == mono_defaults.enum_class)) {
8914 * The type parameter is instantiated as a valuetype,
8915 * but that type doesn't override the method we're
8916 * calling, so we need to box `this'.
8918 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &constrained_class->byval_arg, sp [0]->dreg, 0);
8919 ins->klass = constrained_class;
8920 sp [0] = handle_box (cfg, ins, constrained_class, mono_class_check_context_used (constrained_class), &bblock);
8921 CHECK_CFG_EXCEPTION;
8922 } else if (!constrained_class->valuetype) {
8923 int dreg = alloc_ireg_ref (cfg);
8926 * The type parameter is instantiated as a reference
8927 * type. We have a managed pointer on the stack, so
8928 * we need to dereference it here.
8930 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, dreg, sp [0]->dreg, 0);
8931 ins->type = STACK_OBJ;
8934 if (cmethod->klass->valuetype) {
8937 /* Interface method */
8940 mono_class_setup_vtable (constrained_class);
8941 CHECK_TYPELOAD (constrained_class);
8942 ioffset = mono_class_interface_offset (constrained_class, cmethod->klass);
8944 TYPE_LOAD_ERROR (constrained_class);
8945 slot = mono_method_get_vtable_slot (cmethod);
8947 TYPE_LOAD_ERROR (cmethod->klass);
8948 cmethod = constrained_class->vtable [ioffset + slot];
8950 if (cmethod->klass == mono_defaults.enum_class) {
8951 /* Enum implements some interfaces, so treat this as the first case */
8952 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &constrained_class->byval_arg, sp [0]->dreg, 0);
8953 ins->klass = constrained_class;
8954 sp [0] = handle_box (cfg, ins, constrained_class, mono_class_check_context_used (constrained_class), &bblock);
8955 CHECK_CFG_EXCEPTION;
8960 constrained_class = NULL;
8963 if (check_call_signature (cfg, fsig, sp))
8966 if ((cmethod->klass->parent == mono_defaults.multicastdelegate_class) && !strcmp (cmethod->name, "Invoke"))
8967 delegate_invoke = TRUE;
8969 if ((cfg->opt & MONO_OPT_INTRINS) && (ins = mini_emit_inst_for_sharable_method (cfg, cmethod, fsig, sp))) {
8971 if (!MONO_TYPE_IS_VOID (fsig->ret)) {
8972 type_to_eval_stack_type ((cfg), fsig->ret, ins);
8980 * If the callee is a shared method, then its static cctor
8981 * might not get called after the call was patched.
8983 if (cfg->generic_sharing_context && cmethod->klass != method->klass && cmethod->klass->generic_class && mono_method_is_generic_sharable (cmethod, TRUE) && mono_class_needs_cctor_run (cmethod->klass, method)) {
8984 emit_generic_class_init (cfg, cmethod->klass, &bblock);
8985 CHECK_TYPELOAD (cmethod->klass);
8988 check_method_sharing (cfg, cmethod, &pass_vtable, &pass_mrgctx);
8990 if (cfg->generic_sharing_context) {
8991 MonoGenericContext *cmethod_context = mono_method_get_context (cmethod);
8993 context_used = mini_method_check_context_used (cfg, cmethod);
8995 if (context_used && (cmethod->klass->flags & TYPE_ATTRIBUTE_INTERFACE)) {
8996 /* Generic method interface
8997 calls are resolved via a
8998 helper function and don't
9000 if (!cmethod_context || !cmethod_context->method_inst)
9001 pass_imt_from_rgctx = TRUE;
9005 * If a shared method calls another
9006 * shared method then the caller must
9007 * have a generic sharing context
9008 * because the magic trampoline
9009 * requires it. FIXME: We shouldn't
9010 * have to force the vtable/mrgctx
9011 * variable here. Instead there
9012 * should be a flag in the cfg to
9013 * request a generic sharing context.
9016 ((method->flags & METHOD_ATTRIBUTE_STATIC) || method->klass->valuetype))
9017 mono_get_vtable_var (cfg);
9022 vtable_arg = emit_get_rgctx_klass (cfg, context_used, cmethod->klass, MONO_RGCTX_INFO_VTABLE);
9024 MonoVTable *vtable = mono_class_vtable (cfg->domain, cmethod->klass);
9026 CHECK_TYPELOAD (cmethod->klass);
9027 EMIT_NEW_VTABLECONST (cfg, vtable_arg, vtable);
9032 g_assert (!vtable_arg);
9034 if (!cfg->compile_aot) {
9036 * emit_get_rgctx_method () calls mono_class_vtable () so check
9037 * for type load errors before.
9039 mono_class_setup_vtable (cmethod->klass);
9040 CHECK_TYPELOAD (cmethod->klass);
9043 vtable_arg = emit_get_rgctx_method (cfg, context_used, cmethod, MONO_RGCTX_INFO_METHOD_RGCTX);
9045 /* !marshalbyref is needed to properly handle generic methods + remoting */
9046 if ((!(cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL) ||
9047 MONO_METHOD_IS_FINAL (cmethod)) &&
9048 !mono_class_is_marshalbyref (cmethod->klass)) {
9055 if (pass_imt_from_rgctx) {
9056 g_assert (!pass_vtable);
9058 imt_arg = emit_get_rgctx_method (cfg, context_used,
9059 cmethod, MONO_RGCTX_INFO_METHOD);
9063 MONO_EMIT_NEW_CHECK_THIS (cfg, sp [0]->dreg);
9065 /* Calling virtual generic methods */
9066 if (virtual && (cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL) &&
9067 !(MONO_METHOD_IS_FINAL (cmethod) &&
9068 cmethod->wrapper_type != MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK) &&
9069 fsig->generic_param_count &&
9070 !(cfg->gsharedvt && mini_is_gsharedvt_signature (cfg, fsig))) {
9071 MonoInst *this_temp, *this_arg_temp, *store;
9072 MonoInst *iargs [4];
9073 gboolean use_imt = FALSE;
9075 g_assert (fsig->is_inflated);
9077 /* Prevent inlining of methods that contain indirect calls */
9078 INLINE_FAILURE ("virtual generic call");
9080 if (cfg->gsharedvt && mini_is_gsharedvt_signature (cfg, fsig))
9081 GSHAREDVT_FAILURE (*ip);
9083 #if MONO_ARCH_HAVE_GENERALIZED_IMT_THUNK && defined(MONO_ARCH_GSHARED_SUPPORTED)
9084 if (cmethod->wrapper_type == MONO_WRAPPER_NONE)
9089 g_assert (!imt_arg);
9091 g_assert (cmethod->is_inflated);
9092 imt_arg = emit_get_rgctx_method (cfg, context_used,
9093 cmethod, MONO_RGCTX_INFO_METHOD);
9094 ins = mono_emit_method_call_full (cfg, cmethod, fsig, FALSE, sp, sp [0], imt_arg, NULL);
9096 this_temp = mono_compile_create_var (cfg, type_from_stack_type (sp [0]), OP_LOCAL);
9097 NEW_TEMPSTORE (cfg, store, this_temp->inst_c0, sp [0]);
9098 MONO_ADD_INS (bblock, store);
9100 /* FIXME: This should be a managed pointer */
9101 this_arg_temp = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
9103 EMIT_NEW_TEMPLOAD (cfg, iargs [0], this_temp->inst_c0);
9104 iargs [1] = emit_get_rgctx_method (cfg, context_used,
9105 cmethod, MONO_RGCTX_INFO_METHOD);
9106 EMIT_NEW_TEMPLOADA (cfg, iargs [2], this_arg_temp->inst_c0);
9107 addr = mono_emit_jit_icall (cfg,
9108 mono_helper_compile_generic_method, iargs);
9110 EMIT_NEW_TEMPLOAD (cfg, sp [0], this_arg_temp->inst_c0);
9112 ins = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr, NULL, NULL);
9119 * Implement a workaround for the inherent races involved in locking:
9125 * If a thread abort happens between the call to Monitor.Enter () and the start of the
9126 * try block, the Exit () won't be executed, see:
9127 * http://www.bluebytesoftware.com/blog/2007/01/30/MonitorEnterThreadAbortsAndOrphanedLocks.aspx
9128 * To work around this, we extend such try blocks to include the last x bytes
9129 * of the Monitor.Enter () call.
9131 if (cmethod->klass == mono_defaults.monitor_class && !strcmp (cmethod->name, "Enter") && mono_method_signature (cmethod)->param_count == 1) {
9132 MonoBasicBlock *tbb;
9134 GET_BBLOCK (cfg, tbb, ip + 5);
9136 * Only extend try blocks with a finally, to avoid catching exceptions thrown
9137 * from Monitor.Enter like ArgumentNullException.
9139 if (tbb->try_start && MONO_REGION_FLAGS(tbb->region) == MONO_EXCEPTION_CLAUSE_FINALLY) {
9140 /* Mark this bblock as needing to be extended */
9141 tbb->extend_try_block = TRUE;
9145 /* Conversion to a JIT intrinsic */
9146 if ((cfg->opt & MONO_OPT_INTRINS) && (ins = mini_emit_inst_for_method (cfg, cmethod, fsig, sp))) {
9148 if (!MONO_TYPE_IS_VOID (fsig->ret)) {
9149 type_to_eval_stack_type ((cfg), fsig->ret, ins);
9156 if ((cfg->opt & MONO_OPT_INLINE) &&
9157 (!virtual || !(cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL) || MONO_METHOD_IS_FINAL (cmethod)) &&
9158 mono_method_check_inlining (cfg, cmethod)) {
9160 gboolean always = FALSE;
9162 if ((cmethod->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
9163 (cmethod->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)) {
9164 /* Prevent inlining of methods that call wrappers */
9165 INLINE_FAILURE ("wrapper call");
9166 cmethod = mono_marshal_get_native_wrapper (cmethod, check_for_pending_exc, FALSE);
9170 costs = inline_method (cfg, cmethod, fsig, sp, ip, cfg->real_offset, always, &bblock);
9172 cfg->real_offset += 5;
9174 if (!MONO_TYPE_IS_VOID (fsig->ret)) {
9175 /* *sp is already set by inline_method */
9180 inline_costs += costs;
9186 /* Tail recursion elimination */
9187 if ((cfg->opt & MONO_OPT_TAILC) && call_opcode == CEE_CALL && cmethod == method && ip [5] == CEE_RET && !vtable_arg) {
9188 gboolean has_vtargs = FALSE;
9191 /* Prevent inlining of methods with tail calls (the call stack would be altered) */
9192 INLINE_FAILURE ("tail call");
9194 /* keep it simple */
9195 for (i = fsig->param_count - 1; i >= 0; i--) {
9196 if (MONO_TYPE_ISSTRUCT (mono_method_signature (cmethod)->params [i]))
9201 for (i = 0; i < n; ++i)
9202 EMIT_NEW_ARGSTORE (cfg, ins, i, sp [i]);
9203 MONO_INST_NEW (cfg, ins, OP_BR);
9204 MONO_ADD_INS (bblock, ins);
9205 tblock = start_bblock->out_bb [0];
9206 link_bblock (cfg, bblock, tblock);
9207 ins->inst_target_bb = tblock;
9208 start_new_bblock = 1;
9210 /* skip the CEE_RET, too */
9211 if (ip_in_bb (cfg, bblock, ip + 5))
9218 inline_costs += 10 * num_calls++;
9221 * Making generic calls out of gsharedvt methods.
9222 * This needs to be used for all generic calls, not just ones with a gsharedvt signature, to avoid
9223 * patching gshared method addresses into a gsharedvt method.
9225 if (cfg->gsharedvt && (mini_is_gsharedvt_signature (cfg, fsig) || cmethod->is_inflated || cmethod->klass->generic_class) &&
9226 !(cmethod->klass->rank && cmethod->klass->byval_arg.type != MONO_TYPE_SZARRAY)) {
9227 MonoRgctxInfoType info_type;
9230 //if (cmethod->klass->flags & TYPE_ATTRIBUTE_INTERFACE)
9231 //GSHAREDVT_FAILURE (*ip);
9232 // disable for possible remoting calls
9233 if (fsig->hasthis && (mono_class_is_marshalbyref (method->klass) || method->klass == mono_defaults.object_class))
9234 GSHAREDVT_FAILURE (*ip);
9235 if (fsig->generic_param_count) {
9236 /* virtual generic call */
9237 g_assert (!imt_arg);
9238 /* Same as the virtual generic case above */
9239 imt_arg = emit_get_rgctx_method (cfg, context_used,
9240 cmethod, MONO_RGCTX_INFO_METHOD);
9241 /* This is not needed, as the trampoline code will pass one, and it might be passed in the same reg as the imt arg */
9243 } else if ((cmethod->klass->flags & TYPE_ATTRIBUTE_INTERFACE) && !imt_arg) {
9244 /* This can happen when we call a fully instantiated iface method */
9245 imt_arg = emit_get_rgctx_method (cfg, context_used,
9246 cmethod, MONO_RGCTX_INFO_METHOD);
9251 if ((cmethod->klass->parent == mono_defaults.multicastdelegate_class) && (!strcmp (cmethod->name, "Invoke")))
9252 keep_this_alive = sp [0];
9254 if (virtual && (cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL))
9255 info_type = MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE_VIRT;
9257 info_type = MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE;
9258 addr = emit_get_rgctx_gsharedvt_call (cfg, context_used, fsig, cmethod, info_type);
9260 ins = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr, imt_arg, vtable_arg);
9264 /* Generic sharing */
9267 * Use this if the callee is gsharedvt sharable too, since
9268 * at runtime we might find an instantiation so the call cannot
9269 * be patched (the 'no_patch' code path in mini-trampolines.c).
9271 if (context_used && !imt_arg && !array_rank && !delegate_invoke &&
9272 (!mono_method_is_generic_sharable_full (cmethod, TRUE, FALSE, FALSE) ||
9273 !mono_class_generic_sharing_enabled (cmethod->klass)) &&
9274 (!virtual || MONO_METHOD_IS_FINAL (cmethod) ||
9275 !(cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL))) {
9276 INLINE_FAILURE ("gshared");
9278 g_assert (cfg->generic_sharing_context && cmethod);
9282 * We are compiling a call to a
9283 * generic method from shared code,
9284 * which means that we have to look up
9285 * the method in the rgctx and do an
9289 MONO_EMIT_NEW_CHECK_THIS (cfg, sp [0]->dreg);
9291 addr = emit_get_rgctx_method (cfg, context_used, cmethod, MONO_RGCTX_INFO_GENERIC_METHOD_CODE);
9292 ins = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr, imt_arg, vtable_arg);
9296 /* Direct calls to icalls */
9298 MonoMethod *wrapper;
9301 /* Inline the wrapper */
9302 wrapper = mono_marshal_get_native_wrapper (cmethod, TRUE, cfg->compile_aot);
9304 costs = inline_method (cfg, wrapper, fsig, sp, ip, cfg->real_offset, TRUE, &bblock);
9305 g_assert (costs > 0);
9306 cfg->real_offset += 5;
9308 if (!MONO_TYPE_IS_VOID (fsig->ret)) {
9309 /* *sp is already set by inline_method */
9314 inline_costs += costs;
9323 if (strcmp (cmethod->name, "Set") == 0) { /* array Set */
9324 MonoInst *val = sp [fsig->param_count];
9326 if (val->type == STACK_OBJ) {
9327 MonoInst *iargs [2];
9332 mono_emit_jit_icall (cfg, mono_helper_stelem_ref_check, iargs);
9335 addr = mini_emit_ldelema_ins (cfg, cmethod, sp, ip, TRUE);
9336 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, fsig->params [fsig->param_count - 1], addr->dreg, 0, val->dreg);
9337 if (cfg->gen_write_barriers && val->type == STACK_OBJ && !(val->opcode == OP_PCONST && val->inst_c0 == 0))
9338 emit_write_barrier (cfg, addr, val);
9339 if (cfg->gen_write_barriers && mini_is_gsharedvt_klass (cfg, cmethod->klass))
9340 GSHAREDVT_FAILURE (*ip);
9341 } else if (strcmp (cmethod->name, "Get") == 0) { /* array Get */
9342 addr = mini_emit_ldelema_ins (cfg, cmethod, sp, ip, FALSE);
9344 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, fsig->ret, addr->dreg, 0);
9345 } else if (strcmp (cmethod->name, "Address") == 0) { /* array Address */
9346 if (!cmethod->klass->element_class->valuetype && !readonly)
9347 mini_emit_check_array_type (cfg, sp [0], cmethod->klass);
9348 CHECK_TYPELOAD (cmethod->klass);
9351 addr = mini_emit_ldelema_ins (cfg, cmethod, sp, ip, FALSE);
9354 g_assert_not_reached ();
9361 ins = mini_redirect_call (cfg, cmethod, fsig, sp, virtual ? sp [0] : NULL);
9365 /* Tail prefix / tail call optimization */
9367 /* FIXME: Enabling TAILC breaks some inlining/stack trace/etc tests */
9368 /* FIXME: runtime generic context pointer for jumps? */
9369 /* FIXME: handle this for generic sharing eventually */
9370 if ((ins_flag & MONO_INST_TAILCALL) &&
9371 !vtable_arg && !cfg->generic_sharing_context && is_supported_tail_call (cfg, method, cmethod, fsig, call_opcode))
9372 supported_tail_call = TRUE;
9374 if (supported_tail_call) {
9377 /* Prevent inlining of methods with tail calls (the call stack would be altered) */
9378 INLINE_FAILURE ("tail call");
9380 //printf ("HIT: %s -> %s\n", mono_method_full_name (cfg->method, TRUE), mono_method_full_name (cmethod, TRUE));
9382 if (ARCH_HAVE_OP_TAIL_CALL) {
9383 /* Handle tail calls similarly to normal calls */
9386 emit_instrumentation_call (cfg, mono_profiler_method_leave);
9388 MONO_INST_NEW_CALL (cfg, call, OP_JMP);
9389 call->tail_call = TRUE;
9390 call->method = cmethod;
9391 call->signature = mono_method_signature (cmethod);
9394 * We implement tail calls by storing the actual arguments into the
9395 * argument variables, then emitting a CEE_JMP.
9397 for (i = 0; i < n; ++i) {
9398 /* Prevent argument from being register allocated */
9399 arg_array [i]->flags |= MONO_INST_VOLATILE;
9400 EMIT_NEW_ARGSTORE (cfg, ins, i, sp [i]);
9402 ins = (MonoInst*)call;
9403 ins->inst_p0 = cmethod;
9404 ins->inst_p1 = arg_array [0];
9405 MONO_ADD_INS (bblock, ins);
9406 link_bblock (cfg, bblock, end_bblock);
9407 start_new_bblock = 1;
9409 // FIXME: Eliminate unreachable epilogs
9412 * OP_TAILCALL has no return value, so skip the CEE_RET if it is
9413 * only reachable from this call.
9415 GET_BBLOCK (cfg, tblock, ip + 5);
9416 if (tblock == bblock || tblock->in_count == 0)
9425 * Synchronized wrappers.
9426 * Its hard to determine where to replace a method with its synchronized
9427 * wrapper without causing an infinite recursion. The current solution is
9428 * to add the synchronized wrapper in the trampolines, and to
9429 * change the called method to a dummy wrapper, and resolve that wrapper
9430 * to the real method in mono_jit_compile_method ().
9432 if (cfg->method->wrapper_type == MONO_WRAPPER_SYNCHRONIZED) {
9433 MonoMethod *orig = mono_marshal_method_from_wrapper (cfg->method);
9434 if (cmethod == orig || (cmethod->is_inflated && mono_method_get_declaring_generic_method (cmethod) == orig))
9435 cmethod = mono_marshal_get_synchronized_inner_wrapper (cmethod);
9439 INLINE_FAILURE ("call");
9440 ins = mono_emit_method_call_full (cfg, cmethod, fsig, tail_call, sp, virtual ? sp [0] : NULL,
9441 imt_arg, vtable_arg);
9444 link_bblock (cfg, bblock, end_bblock);
9445 start_new_bblock = 1;
9447 // FIXME: Eliminate unreachable epilogs
9450 * OP_TAILCALL has no return value, so skip the CEE_RET if it is
9451 * only reachable from this call.
9453 GET_BBLOCK (cfg, tblock, ip + 5);
9454 if (tblock == bblock || tblock->in_count == 0)
9461 /* End of call, INS should contain the result of the call, if any */
9463 if (push_res && !MONO_TYPE_IS_VOID (fsig->ret)) {
9466 *sp++ = mono_emit_widen_call_res (cfg, ins, fsig);
9471 if (keep_this_alive) {
9472 MonoInst *dummy_use;
9474 /* See mono_emit_method_call_full () */
9475 EMIT_NEW_DUMMY_USE (cfg, dummy_use, keep_this_alive);
9478 CHECK_CFG_EXCEPTION;
9482 g_assert (*ip == CEE_RET);
9486 constrained_class = NULL;
9488 emit_seq_point (cfg, method, ip, FALSE, TRUE);
9492 if (cfg->method != method) {
9493 /* return from inlined method */
9495 * If in_count == 0, that means the ret is unreachable due to
9496 * being preceeded by a throw. In that case, inline_method () will
9497 * handle setting the return value
9498 * (test case: test_0_inline_throw ()).
9500 if (return_var && cfg->cbb->in_count) {
9501 MonoType *ret_type = mono_method_signature (method)->ret;
9507 if ((method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD || method->wrapper_type == MONO_WRAPPER_NONE) && target_type_is_incompatible (cfg, ret_type, *sp))
9510 //g_assert (returnvar != -1);
9511 EMIT_NEW_TEMPSTORE (cfg, store, return_var->inst_c0, *sp);
9512 cfg->ret_var_set = TRUE;
9515 emit_instrumentation_call (cfg, mono_profiler_method_leave);
9517 if (cfg->lmf_var && cfg->cbb->in_count)
9521 MonoType *ret_type = mini_get_underlying_type (cfg, mono_method_signature (method)->ret);
9523 if (seq_points && !sym_seq_points) {
9525 * Place a seq point here too even through the IL stack is not
9526 * empty, so a step over on
9529 * will work correctly.
9531 NEW_SEQ_POINT (cfg, ins, ip - header->code, TRUE);
9532 MONO_ADD_INS (cfg->cbb, ins);
9535 g_assert (!return_var);
9539 if ((method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD || method->wrapper_type == MONO_WRAPPER_NONE) && target_type_is_incompatible (cfg, ret_type, *sp))
9542 if (mini_type_to_stind (cfg, ret_type) == CEE_STOBJ) {
9545 if (!cfg->vret_addr) {
9548 EMIT_NEW_VARSTORE (cfg, ins, cfg->ret, ret_type, (*sp));
9550 EMIT_NEW_RETLOADA (cfg, ret_addr);
9552 EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STOREV_MEMBASE, ret_addr->dreg, 0, (*sp)->dreg);
9553 ins->klass = mono_class_from_mono_type (ret_type);
9556 #ifdef MONO_ARCH_SOFT_FLOAT_FALLBACK
9557 if (COMPILE_SOFT_FLOAT (cfg) && !ret_type->byref && ret_type->type == MONO_TYPE_R4) {
9558 MonoInst *iargs [1];
9562 conv = mono_emit_jit_icall (cfg, mono_fload_r4_arg, iargs);
9563 mono_arch_emit_setret (cfg, method, conv);
9565 mono_arch_emit_setret (cfg, method, *sp);
9568 mono_arch_emit_setret (cfg, method, *sp);
9573 if (sp != stack_start)
9575 MONO_INST_NEW (cfg, ins, OP_BR);
9577 ins->inst_target_bb = end_bblock;
9578 MONO_ADD_INS (bblock, ins);
9579 link_bblock (cfg, bblock, end_bblock);
9580 start_new_bblock = 1;
9584 MONO_INST_NEW (cfg, ins, OP_BR);
9586 target = ip + 1 + (signed char)(*ip);
9588 GET_BBLOCK (cfg, tblock, target);
9589 link_bblock (cfg, bblock, tblock);
9590 ins->inst_target_bb = tblock;
9591 if (sp != stack_start) {
9592 handle_stack_args (cfg, stack_start, sp - stack_start);
9594 CHECK_UNVERIFIABLE (cfg);
9596 MONO_ADD_INS (bblock, ins);
9597 start_new_bblock = 1;
9598 inline_costs += BRANCH_COST;
9612 MONO_INST_NEW (cfg, ins, *ip + BIG_BRANCH_OFFSET);
9614 target = ip + 1 + *(signed char*)ip;
9620 inline_costs += BRANCH_COST;
9624 MONO_INST_NEW (cfg, ins, OP_BR);
9627 target = ip + 4 + (gint32)read32(ip);
9629 GET_BBLOCK (cfg, tblock, target);
9630 link_bblock (cfg, bblock, tblock);
9631 ins->inst_target_bb = tblock;
9632 if (sp != stack_start) {
9633 handle_stack_args (cfg, stack_start, sp - stack_start);
9635 CHECK_UNVERIFIABLE (cfg);
9638 MONO_ADD_INS (bblock, ins);
9640 start_new_bblock = 1;
9641 inline_costs += BRANCH_COST;
9648 gboolean is_short = ((*ip) == CEE_BRFALSE_S) || ((*ip) == CEE_BRTRUE_S);
9649 gboolean is_true = ((*ip) == CEE_BRTRUE_S) || ((*ip) == CEE_BRTRUE);
9650 guint32 opsize = is_short ? 1 : 4;
9652 CHECK_OPSIZE (opsize);
9654 if (sp [-1]->type == STACK_VTYPE || sp [-1]->type == STACK_R8)
9657 target = ip + opsize + (is_short ? *(signed char*)ip : (gint32)read32(ip));
9662 GET_BBLOCK (cfg, tblock, target);
9663 link_bblock (cfg, bblock, tblock);
9664 GET_BBLOCK (cfg, tblock, ip);
9665 link_bblock (cfg, bblock, tblock);
9667 if (sp != stack_start) {
9668 handle_stack_args (cfg, stack_start, sp - stack_start);
9669 CHECK_UNVERIFIABLE (cfg);
9672 MONO_INST_NEW(cfg, cmp, OP_ICOMPARE_IMM);
9673 cmp->sreg1 = sp [0]->dreg;
9674 type_from_op (cfg, cmp, sp [0], NULL);
9677 #if SIZEOF_REGISTER == 4
9678 if (cmp->opcode == OP_LCOMPARE_IMM) {
9679 /* Convert it to OP_LCOMPARE */
9680 MONO_INST_NEW (cfg, ins, OP_I8CONST);
9681 ins->type = STACK_I8;
9682 ins->dreg = alloc_dreg (cfg, STACK_I8);
9684 MONO_ADD_INS (bblock, ins);
9685 cmp->opcode = OP_LCOMPARE;
9686 cmp->sreg2 = ins->dreg;
9689 MONO_ADD_INS (bblock, cmp);
9691 MONO_INST_NEW (cfg, ins, is_true ? CEE_BNE_UN : CEE_BEQ);
9692 type_from_op (cfg, ins, sp [0], NULL);
9693 MONO_ADD_INS (bblock, ins);
9694 ins->inst_many_bb = mono_mempool_alloc (cfg->mempool, sizeof(gpointer)*2);
9695 GET_BBLOCK (cfg, tblock, target);
9696 ins->inst_true_bb = tblock;
9697 GET_BBLOCK (cfg, tblock, ip);
9698 ins->inst_false_bb = tblock;
9699 start_new_bblock = 2;
9702 inline_costs += BRANCH_COST;
9717 MONO_INST_NEW (cfg, ins, *ip);
9719 target = ip + 4 + (gint32)read32(ip);
9725 inline_costs += BRANCH_COST;
9729 MonoBasicBlock **targets;
9730 MonoBasicBlock *default_bblock;
9731 MonoJumpInfoBBTable *table;
9732 int offset_reg = alloc_preg (cfg);
9733 int target_reg = alloc_preg (cfg);
9734 int table_reg = alloc_preg (cfg);
9735 int sum_reg = alloc_preg (cfg);
9736 gboolean use_op_switch;
9740 n = read32 (ip + 1);
9743 if ((src1->type != STACK_I4) && (src1->type != STACK_PTR))
9747 CHECK_OPSIZE (n * sizeof (guint32));
9748 target = ip + n * sizeof (guint32);
9750 GET_BBLOCK (cfg, default_bblock, target);
9751 default_bblock->flags |= BB_INDIRECT_JUMP_TARGET;
9753 targets = mono_mempool_alloc (cfg->mempool, sizeof (MonoBasicBlock*) * n);
9754 for (i = 0; i < n; ++i) {
9755 GET_BBLOCK (cfg, tblock, target + (gint32)read32(ip));
9756 targets [i] = tblock;
9757 targets [i]->flags |= BB_INDIRECT_JUMP_TARGET;
9761 if (sp != stack_start) {
9763 * Link the current bb with the targets as well, so handle_stack_args
9764 * will set their in_stack correctly.
9766 link_bblock (cfg, bblock, default_bblock);
9767 for (i = 0; i < n; ++i)
9768 link_bblock (cfg, bblock, targets [i]);
9770 handle_stack_args (cfg, stack_start, sp - stack_start);
9772 CHECK_UNVERIFIABLE (cfg);
9775 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ICOMPARE_IMM, -1, src1->dreg, n);
9776 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBGE_UN, default_bblock);
9779 for (i = 0; i < n; ++i)
9780 link_bblock (cfg, bblock, targets [i]);
9782 table = mono_mempool_alloc (cfg->mempool, sizeof (MonoJumpInfoBBTable));
9783 table->table = targets;
9784 table->table_size = n;
9786 use_op_switch = FALSE;
9788 /* ARM implements SWITCH statements differently */
9789 /* FIXME: Make it use the generic implementation */
9790 if (!cfg->compile_aot)
9791 use_op_switch = TRUE;
9794 if (COMPILE_LLVM (cfg))
9795 use_op_switch = TRUE;
9797 cfg->cbb->has_jump_table = 1;
9799 if (use_op_switch) {
9800 MONO_INST_NEW (cfg, ins, OP_SWITCH);
9801 ins->sreg1 = src1->dreg;
9802 ins->inst_p0 = table;
9803 ins->inst_many_bb = targets;
9804 ins->klass = GUINT_TO_POINTER (n);
9805 MONO_ADD_INS (cfg->cbb, ins);
9807 if (sizeof (gpointer) == 8)
9808 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SHL_IMM, offset_reg, src1->dreg, 3);
9810 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SHL_IMM, offset_reg, src1->dreg, 2);
9812 #if SIZEOF_REGISTER == 8
9813 /* The upper word might not be zero, and we add it to a 64 bit address later */
9814 MONO_EMIT_NEW_UNALU (cfg, OP_ZEXT_I4, offset_reg, offset_reg);
9817 if (cfg->compile_aot) {
9818 MONO_EMIT_NEW_AOTCONST (cfg, table_reg, table, MONO_PATCH_INFO_SWITCH);
9820 MONO_INST_NEW (cfg, ins, OP_JUMP_TABLE);
9821 ins->inst_c1 = MONO_PATCH_INFO_SWITCH;
9822 ins->inst_p0 = table;
9823 ins->dreg = table_reg;
9824 MONO_ADD_INS (cfg->cbb, ins);
9827 /* FIXME: Use load_memindex */
9828 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, sum_reg, table_reg, offset_reg);
9829 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, target_reg, sum_reg, 0);
9830 MONO_EMIT_NEW_UNALU (cfg, OP_BR_REG, -1, target_reg);
9832 start_new_bblock = 1;
9833 inline_costs += (BRANCH_COST * 2);
9853 dreg = alloc_freg (cfg);
9856 dreg = alloc_lreg (cfg);
9859 dreg = alloc_ireg_ref (cfg);
9862 dreg = alloc_preg (cfg);
9865 NEW_LOAD_MEMBASE (cfg, ins, ldind_to_load_membase (*ip), dreg, sp [0]->dreg, 0);
9866 ins->type = ldind_type [*ip - CEE_LDIND_I1];
9867 if (*ip == CEE_LDIND_R4)
9868 ins->type = cfg->r4_stack_type;
9869 ins->flags |= ins_flag;
9870 MONO_ADD_INS (bblock, ins);
9872 if (ins_flag & MONO_INST_VOLATILE) {
9873 /* Volatile loads have acquire semantics, see 12.6.7 in Ecma 335 */
9874 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_ACQ);
9890 if (ins_flag & MONO_INST_VOLATILE) {
9891 /* Volatile stores have release semantics, see 12.6.7 in Ecma 335 */
9892 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_REL);
9895 NEW_STORE_MEMBASE (cfg, ins, stind_to_store_membase (*ip), sp [0]->dreg, 0, sp [1]->dreg);
9896 ins->flags |= ins_flag;
9899 MONO_ADD_INS (bblock, ins);
9901 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)))
9902 emit_write_barrier (cfg, sp [0], sp [1]);
9911 MONO_INST_NEW (cfg, ins, (*ip));
9913 ins->sreg1 = sp [0]->dreg;
9914 ins->sreg2 = sp [1]->dreg;
9915 type_from_op (cfg, ins, sp [0], sp [1]);
9917 ins->dreg = alloc_dreg ((cfg), (ins)->type);
9919 /* Use the immediate opcodes if possible */
9920 if ((sp [1]->opcode == OP_ICONST) && mono_arch_is_inst_imm (sp [1]->inst_c0)) {
9921 int imm_opcode = mono_op_to_op_imm_noemul (ins->opcode);
9922 if (imm_opcode != -1) {
9923 ins->opcode = imm_opcode;
9924 ins->inst_p1 = (gpointer)(gssize)(sp [1]->inst_c0);
9927 NULLIFY_INS (sp [1]);
9931 MONO_ADD_INS ((cfg)->cbb, (ins));
9933 *sp++ = mono_decompose_opcode (cfg, ins, &bblock);
9950 MONO_INST_NEW (cfg, ins, (*ip));
9952 ins->sreg1 = sp [0]->dreg;
9953 ins->sreg2 = sp [1]->dreg;
9954 type_from_op (cfg, ins, sp [0], sp [1]);
9956 add_widen_op (cfg, ins, &sp [0], &sp [1]);
9957 ins->dreg = alloc_dreg ((cfg), (ins)->type);
9959 /* FIXME: Pass opcode to is_inst_imm */
9961 /* Use the immediate opcodes if possible */
9962 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)) {
9965 imm_opcode = mono_op_to_op_imm_noemul (ins->opcode);
9966 #if defined(MONO_ARCH_EMULATE_MUL_DIV) || defined(MONO_ARCH_EMULATE_DIV)
9967 /* Keep emulated opcodes which are optimized away later */
9968 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) {
9969 imm_opcode = mono_op_to_op_imm (ins->opcode);
9972 if (imm_opcode != -1) {
9973 ins->opcode = imm_opcode;
9974 if (sp [1]->opcode == OP_I8CONST) {
9975 #if SIZEOF_REGISTER == 8
9976 ins->inst_imm = sp [1]->inst_l;
9978 ins->inst_ls_word = sp [1]->inst_ls_word;
9979 ins->inst_ms_word = sp [1]->inst_ms_word;
9983 ins->inst_imm = (gssize)(sp [1]->inst_c0);
9986 /* Might be followed by an instruction added by add_widen_op */
9987 if (sp [1]->next == NULL)
9988 NULLIFY_INS (sp [1]);
9991 MONO_ADD_INS ((cfg)->cbb, (ins));
9993 *sp++ = mono_decompose_opcode (cfg, ins, &bblock);
10006 case CEE_CONV_OVF_I8:
10007 case CEE_CONV_OVF_U8:
10008 case CEE_CONV_R_UN:
10011 /* Special case this earlier so we have long constants in the IR */
10012 if ((((*ip) == CEE_CONV_I8) || ((*ip) == CEE_CONV_U8)) && (sp [-1]->opcode == OP_ICONST)) {
10013 int data = sp [-1]->inst_c0;
10014 sp [-1]->opcode = OP_I8CONST;
10015 sp [-1]->type = STACK_I8;
10016 #if SIZEOF_REGISTER == 8
10017 if ((*ip) == CEE_CONV_U8)
10018 sp [-1]->inst_c0 = (guint32)data;
10020 sp [-1]->inst_c0 = data;
10022 sp [-1]->inst_ls_word = data;
10023 if ((*ip) == CEE_CONV_U8)
10024 sp [-1]->inst_ms_word = 0;
10026 sp [-1]->inst_ms_word = (data < 0) ? -1 : 0;
10028 sp [-1]->dreg = alloc_dreg (cfg, STACK_I8);
10035 case CEE_CONV_OVF_I4:
10036 case CEE_CONV_OVF_I1:
10037 case CEE_CONV_OVF_I2:
10038 case CEE_CONV_OVF_I:
10039 case CEE_CONV_OVF_U:
10042 if (sp [-1]->type == STACK_R8 || sp [-1]->type == STACK_R4) {
10043 ADD_UNOP (CEE_CONV_OVF_I8);
10050 case CEE_CONV_OVF_U1:
10051 case CEE_CONV_OVF_U2:
10052 case CEE_CONV_OVF_U4:
10055 if (sp [-1]->type == STACK_R8 || sp [-1]->type == STACK_R4) {
10056 ADD_UNOP (CEE_CONV_OVF_U8);
10063 case CEE_CONV_OVF_I1_UN:
10064 case CEE_CONV_OVF_I2_UN:
10065 case CEE_CONV_OVF_I4_UN:
10066 case CEE_CONV_OVF_I8_UN:
10067 case CEE_CONV_OVF_U1_UN:
10068 case CEE_CONV_OVF_U2_UN:
10069 case CEE_CONV_OVF_U4_UN:
10070 case CEE_CONV_OVF_U8_UN:
10071 case CEE_CONV_OVF_I_UN:
10072 case CEE_CONV_OVF_U_UN:
10079 CHECK_CFG_EXCEPTION;
10083 case CEE_ADD_OVF_UN:
10085 case CEE_MUL_OVF_UN:
10087 case CEE_SUB_OVF_UN:
10093 GSHAREDVT_FAILURE (*ip);
10096 token = read32 (ip + 1);
10097 klass = mini_get_class (method, token, generic_context);
10098 CHECK_TYPELOAD (klass);
10100 if (generic_class_is_reference_type (cfg, klass)) {
10101 MonoInst *store, *load;
10102 int dreg = alloc_ireg_ref (cfg);
10104 NEW_LOAD_MEMBASE (cfg, load, OP_LOAD_MEMBASE, dreg, sp [1]->dreg, 0);
10105 load->flags |= ins_flag;
10106 MONO_ADD_INS (cfg->cbb, load);
10108 NEW_STORE_MEMBASE (cfg, store, OP_STORE_MEMBASE_REG, sp [0]->dreg, 0, dreg);
10109 store->flags |= ins_flag;
10110 MONO_ADD_INS (cfg->cbb, store);
10112 if (cfg->gen_write_barriers && cfg->method->wrapper_type != MONO_WRAPPER_WRITE_BARRIER)
10113 emit_write_barrier (cfg, sp [0], sp [1]);
10115 mini_emit_stobj (cfg, sp [0], sp [1], klass, FALSE);
10121 int loc_index = -1;
10127 token = read32 (ip + 1);
10128 klass = mini_get_class (method, token, generic_context);
10129 CHECK_TYPELOAD (klass);
10131 /* Optimize the common ldobj+stloc combination */
10134 loc_index = ip [6];
10141 loc_index = ip [5] - CEE_STLOC_0;
10148 if ((loc_index != -1) && ip_in_bb (cfg, bblock, ip + 5)) {
10149 CHECK_LOCAL (loc_index);
10151 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, sp [0]->dreg, 0);
10152 ins->dreg = cfg->locals [loc_index]->dreg;
10153 ins->flags |= ins_flag;
10156 if (ins_flag & MONO_INST_VOLATILE) {
10157 /* Volatile loads have acquire semantics, see 12.6.7 in Ecma 335 */
10158 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_ACQ);
10164 /* Optimize the ldobj+stobj combination */
10165 /* The reference case ends up being a load+store anyway */
10166 /* Skip this if the operation is volatile. */
10167 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)) {
10172 mini_emit_stobj (cfg, sp [0], sp [1], klass, FALSE);
10179 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, sp [0]->dreg, 0);
10180 ins->flags |= ins_flag;
10183 if (ins_flag & MONO_INST_VOLATILE) {
10184 /* Volatile loads have acquire semantics, see 12.6.7 in Ecma 335 */
10185 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_ACQ);
10194 CHECK_STACK_OVF (1);
10196 n = read32 (ip + 1);
10198 if (method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD) {
10199 EMIT_NEW_PCONST (cfg, ins, mono_method_get_wrapper_data (method, n));
10200 ins->type = STACK_OBJ;
10203 else if (method->wrapper_type != MONO_WRAPPER_NONE) {
10204 MonoInst *iargs [1];
10205 char *str = mono_method_get_wrapper_data (method, n);
10207 if (cfg->compile_aot)
10208 EMIT_NEW_LDSTRLITCONST (cfg, iargs [0], str);
10210 EMIT_NEW_PCONST (cfg, iargs [0], str);
10211 *sp = mono_emit_jit_icall (cfg, mono_string_new_wrapper, iargs);
10213 if (cfg->opt & MONO_OPT_SHARED) {
10214 MonoInst *iargs [3];
10216 if (cfg->compile_aot) {
10217 cfg->ldstr_list = g_list_prepend (cfg->ldstr_list, GINT_TO_POINTER (n));
10219 EMIT_NEW_DOMAINCONST (cfg, iargs [0]);
10220 EMIT_NEW_IMAGECONST (cfg, iargs [1], image);
10221 EMIT_NEW_ICONST (cfg, iargs [2], mono_metadata_token_index (n));
10222 *sp = mono_emit_jit_icall (cfg, mono_ldstr, iargs);
10223 mono_ldstr (cfg->domain, image, mono_metadata_token_index (n));
10225 if (bblock->out_of_line) {
10226 MonoInst *iargs [2];
10228 if (image == mono_defaults.corlib) {
10230 * Avoid relocations in AOT and save some space by using a
10231 * version of helper_ldstr specialized to mscorlib.
10233 EMIT_NEW_ICONST (cfg, iargs [0], mono_metadata_token_index (n));
10234 *sp = mono_emit_jit_icall (cfg, mono_helper_ldstr_mscorlib, iargs);
10236 /* Avoid creating the string object */
10237 EMIT_NEW_IMAGECONST (cfg, iargs [0], image);
10238 EMIT_NEW_ICONST (cfg, iargs [1], mono_metadata_token_index (n));
10239 *sp = mono_emit_jit_icall (cfg, mono_helper_ldstr, iargs);
10243 if (cfg->compile_aot) {
10244 NEW_LDSTRCONST (cfg, ins, image, n);
10246 MONO_ADD_INS (bblock, ins);
10249 NEW_PCONST (cfg, ins, NULL);
10250 ins->type = STACK_OBJ;
10251 ins->inst_p0 = mono_ldstr (cfg->domain, image, mono_metadata_token_index (n));
10253 OUT_OF_MEMORY_FAILURE;
10256 MONO_ADD_INS (bblock, ins);
10265 MonoInst *iargs [2];
10266 MonoMethodSignature *fsig;
10269 MonoInst *vtable_arg = NULL;
10272 token = read32 (ip + 1);
10273 cmethod = mini_get_method (cfg, method, token, NULL, generic_context);
10274 if (!cmethod || mono_loader_get_last_error ())
10276 fsig = mono_method_get_signature_checked (cmethod, image, token, generic_context, &cfg->error);
10279 mono_save_token_info (cfg, image, token, cmethod);
10281 if (!mono_class_init (cmethod->klass))
10282 TYPE_LOAD_ERROR (cmethod->klass);
10284 context_used = mini_method_check_context_used (cfg, cmethod);
10286 if (mono_security_core_clr_enabled ())
10287 ensure_method_is_allowed_to_call_method (cfg, method, cmethod, bblock, ip);
10289 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)) {
10290 emit_generic_class_init (cfg, cmethod->klass, &bblock);
10291 CHECK_TYPELOAD (cmethod->klass);
10295 if (cfg->gsharedvt) {
10296 if (mini_is_gsharedvt_variable_signature (sig))
10297 GSHAREDVT_FAILURE (*ip);
10301 n = fsig->param_count;
10305 * Generate smaller code for the common newobj <exception> instruction in
10306 * argument checking code.
10308 if (bblock->out_of_line && cmethod->klass->image == mono_defaults.corlib &&
10309 is_exception_class (cmethod->klass) && n <= 2 &&
10310 ((n < 1) || (!fsig->params [0]->byref && fsig->params [0]->type == MONO_TYPE_STRING)) &&
10311 ((n < 2) || (!fsig->params [1]->byref && fsig->params [1]->type == MONO_TYPE_STRING))) {
10312 MonoInst *iargs [3];
10316 EMIT_NEW_ICONST (cfg, iargs [0], cmethod->klass->type_token);
10319 *sp ++ = mono_emit_jit_icall (cfg, mono_create_corlib_exception_0, iargs);
10322 iargs [1] = sp [0];
10323 *sp ++ = mono_emit_jit_icall (cfg, mono_create_corlib_exception_1, iargs);
10326 iargs [1] = sp [0];
10327 iargs [2] = sp [1];
10328 *sp ++ = mono_emit_jit_icall (cfg, mono_create_corlib_exception_2, iargs);
10331 g_assert_not_reached ();
10339 /* move the args to allow room for 'this' in the first position */
10345 /* check_call_signature () requires sp[0] to be set */
10346 this_ins.type = STACK_OBJ;
10347 sp [0] = &this_ins;
10348 if (check_call_signature (cfg, fsig, sp))
10353 if (mini_class_is_system_array (cmethod->klass)) {
10354 *sp = emit_get_rgctx_method (cfg, context_used,
10355 cmethod, MONO_RGCTX_INFO_METHOD);
10357 /* Avoid varargs in the common case */
10358 if (fsig->param_count == 1)
10359 alloc = mono_emit_jit_icall (cfg, mono_array_new_1, sp);
10360 else if (fsig->param_count == 2)
10361 alloc = mono_emit_jit_icall (cfg, mono_array_new_2, sp);
10362 else if (fsig->param_count == 3)
10363 alloc = mono_emit_jit_icall (cfg, mono_array_new_3, sp);
10364 else if (fsig->param_count == 4)
10365 alloc = mono_emit_jit_icall (cfg, mono_array_new_4, sp);
10367 alloc = handle_array_new (cfg, fsig->param_count, sp, ip);
10368 } else if (cmethod->string_ctor) {
10369 g_assert (!context_used);
10370 g_assert (!vtable_arg);
10371 /* we simply pass a null pointer */
10372 EMIT_NEW_PCONST (cfg, *sp, NULL);
10373 /* now call the string ctor */
10374 alloc = mono_emit_method_call_full (cfg, cmethod, fsig, FALSE, sp, NULL, NULL, NULL);
10376 if (cmethod->klass->valuetype) {
10377 iargs [0] = mono_compile_create_var (cfg, &cmethod->klass->byval_arg, OP_LOCAL);
10378 emit_init_rvar (cfg, iargs [0]->dreg, &cmethod->klass->byval_arg);
10379 EMIT_NEW_TEMPLOADA (cfg, *sp, iargs [0]->inst_c0);
10384 * The code generated by mini_emit_virtual_call () expects
10385 * iargs [0] to be a boxed instance, but luckily the vcall
10386 * will be transformed into a normal call there.
10388 } else if (context_used) {
10389 alloc = handle_alloc (cfg, cmethod->klass, FALSE, context_used);
10392 MonoVTable *vtable = NULL;
10394 if (!cfg->compile_aot)
10395 vtable = mono_class_vtable (cfg->domain, cmethod->klass);
10396 CHECK_TYPELOAD (cmethod->klass);
10399 * TypeInitializationExceptions thrown from the mono_runtime_class_init
10400 * call in mono_jit_runtime_invoke () can abort the finalizer thread.
10401 * As a workaround, we call class cctors before allocating objects.
10403 if (mini_field_access_needs_cctor_run (cfg, method, cmethod->klass, vtable) && !(g_slist_find (class_inits, cmethod->klass))) {
10404 emit_class_init (cfg, cmethod->klass, &bblock);
10405 if (cfg->verbose_level > 2)
10406 printf ("class %s.%s needs init call for ctor\n", cmethod->klass->name_space, cmethod->klass->name);
10407 class_inits = g_slist_prepend (class_inits, cmethod->klass);
10410 alloc = handle_alloc (cfg, cmethod->klass, FALSE, 0);
10413 CHECK_CFG_EXCEPTION; /*for handle_alloc*/
10416 MONO_EMIT_NEW_UNALU (cfg, OP_NOT_NULL, -1, alloc->dreg);
10418 /* Now call the actual ctor */
10419 handle_ctor_call (cfg, cmethod, fsig, context_used, sp, ip, &bblock, &inline_costs);
10420 CHECK_CFG_EXCEPTION;
10423 if (alloc == NULL) {
10425 EMIT_NEW_TEMPLOAD (cfg, ins, iargs [0]->inst_c0);
10426 type_to_eval_stack_type (cfg, &ins->klass->byval_arg, ins);
10434 if (!(seq_point_locs && mono_bitset_test_fast (seq_point_locs, ip - header->code)))
10435 emit_seq_point (cfg, method, ip, FALSE, TRUE);
10438 case CEE_CASTCLASS:
10442 token = read32 (ip + 1);
10443 klass = mini_get_class (method, token, generic_context);
10444 CHECK_TYPELOAD (klass);
10445 if (sp [0]->type != STACK_OBJ)
10448 ins = handle_castclass (cfg, klass, *sp, ip, &bblock, &inline_costs);
10449 CHECK_CFG_EXCEPTION;
10458 token = read32 (ip + 1);
10459 klass = mini_get_class (method, token, generic_context);
10460 CHECK_TYPELOAD (klass);
10461 if (sp [0]->type != STACK_OBJ)
10464 context_used = mini_class_check_context_used (cfg, klass);
10466 if (!context_used && mini_class_has_reference_variant_generic_argument (cfg, klass, context_used)) {
10467 MonoMethod *mono_isinst = mono_marshal_get_isinst_with_cache ();
10468 MonoInst *args [3];
10475 EMIT_NEW_CLASSCONST (cfg, args [1], klass);
10478 if (cfg->compile_aot) {
10479 idx = get_castclass_cache_idx (cfg);
10480 EMIT_NEW_AOTCONST (cfg, args [2], MONO_PATCH_INFO_CASTCLASS_CACHE, GINT_TO_POINTER (idx));
10482 EMIT_NEW_PCONST (cfg, args [2], mono_domain_alloc0 (cfg->domain, sizeof (gpointer)));
10485 *sp++ = mono_emit_method_call (cfg, mono_isinst, args, NULL);
10488 } else if (!context_used && (mono_class_is_marshalbyref (klass) || klass->flags & TYPE_ATTRIBUTE_INTERFACE)) {
10489 MonoMethod *mono_isinst;
10490 MonoInst *iargs [1];
10493 mono_isinst = mono_marshal_get_isinst (klass);
10494 iargs [0] = sp [0];
10496 costs = inline_method (cfg, mono_isinst, mono_method_signature (mono_isinst),
10497 iargs, ip, cfg->real_offset, TRUE, &bblock);
10498 CHECK_CFG_EXCEPTION;
10499 g_assert (costs > 0);
10502 cfg->real_offset += 5;
10506 inline_costs += costs;
10509 ins = handle_isinst (cfg, klass, *sp, context_used);
10510 CHECK_CFG_EXCEPTION;
10517 case CEE_UNBOX_ANY: {
10518 MonoInst *res, *addr;
10523 token = read32 (ip + 1);
10524 klass = mini_get_class (method, token, generic_context);
10525 CHECK_TYPELOAD (klass);
10527 mono_save_token_info (cfg, image, token, klass);
10529 context_used = mini_class_check_context_used (cfg, klass);
10531 if (mini_is_gsharedvt_klass (cfg, klass)) {
10532 res = handle_unbox_gsharedvt (cfg, klass, *sp, &bblock);
10534 } else if (generic_class_is_reference_type (cfg, klass)) {
10535 res = handle_castclass (cfg, klass, *sp, ip, &bblock, &inline_costs);
10536 CHECK_CFG_EXCEPTION;
10537 } else if (mono_class_is_nullable (klass)) {
10538 res = handle_unbox_nullable (cfg, *sp, klass, context_used);
10540 addr = handle_unbox (cfg, klass, sp, context_used);
10542 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, addr->dreg, 0);
10553 MonoClass *enum_class;
10554 MonoMethod *has_flag;
10560 token = read32 (ip + 1);
10561 klass = mini_get_class (method, token, generic_context);
10562 CHECK_TYPELOAD (klass);
10564 mono_save_token_info (cfg, image, token, klass);
10566 context_used = mini_class_check_context_used (cfg, klass);
10568 if (generic_class_is_reference_type (cfg, klass)) {
10574 if (klass == mono_defaults.void_class)
10576 if (target_type_is_incompatible (cfg, &klass->byval_arg, *sp))
10578 /* frequent check in generic code: box (struct), brtrue */
10583 * <push int/long ptr>
10586 * constrained. MyFlags
10587 * callvirt instace bool class [mscorlib] System.Enum::HasFlag (class [mscorlib] System.Enum)
10589 * If we find this sequence and the operand types on box and constrained
10590 * are equal, we can emit a specialized instruction sequence instead of
10591 * the very slow HasFlag () call.
10593 if ((cfg->opt & MONO_OPT_INTRINS) &&
10594 /* Cheap checks first. */
10595 ip + 5 + 6 + 5 < end &&
10596 ip [5] == CEE_PREFIX1 &&
10597 ip [6] == CEE_CONSTRAINED_ &&
10598 ip [11] == CEE_CALLVIRT &&
10599 ip_in_bb (cfg, bblock, ip + 5 + 6 + 5) &&
10600 mono_class_is_enum (klass) &&
10601 (enum_class = mini_get_class (method, read32 (ip + 7), generic_context)) &&
10602 (has_flag = mini_get_method (cfg, method, read32 (ip + 12), NULL, generic_context)) &&
10603 has_flag->klass == mono_defaults.enum_class &&
10604 !strcmp (has_flag->name, "HasFlag") &&
10605 has_flag->signature->hasthis &&
10606 has_flag->signature->param_count == 1) {
10607 CHECK_TYPELOAD (enum_class);
10609 if (enum_class == klass) {
10610 MonoInst *enum_this, *enum_flag;
10615 enum_this = sp [0];
10616 enum_flag = sp [1];
10618 *sp++ = handle_enum_has_flag (cfg, klass, enum_this, enum_flag);
10623 // FIXME: LLVM can't handle the inconsistent bb linking
10624 if (!mono_class_is_nullable (klass) &&
10625 ip + 5 < end && ip_in_bb (cfg, bblock, ip + 5) &&
10626 (ip [5] == CEE_BRTRUE ||
10627 ip [5] == CEE_BRTRUE_S ||
10628 ip [5] == CEE_BRFALSE ||
10629 ip [5] == CEE_BRFALSE_S)) {
10630 gboolean is_true = ip [5] == CEE_BRTRUE || ip [5] == CEE_BRTRUE_S;
10632 MonoBasicBlock *true_bb, *false_bb;
10636 if (cfg->verbose_level > 3) {
10637 printf ("converting (in B%d: stack: %d) %s", bblock->block_num, (int)(sp - stack_start), mono_disasm_code_one (NULL, method, ip, NULL));
10638 printf ("<box+brtrue opt>\n");
10643 case CEE_BRFALSE_S:
10646 target = ip + 1 + (signed char)(*ip);
10653 target = ip + 4 + (gint)(read32 (ip));
10657 g_assert_not_reached ();
10661 * We need to link both bblocks, since it is needed for handling stack
10662 * arguments correctly (See test_0_box_brtrue_opt_regress_81102).
10663 * Branching to only one of them would lead to inconsistencies, so
10664 * generate an ICONST+BRTRUE, the branch opts will get rid of them.
10666 GET_BBLOCK (cfg, true_bb, target);
10667 GET_BBLOCK (cfg, false_bb, ip);
10669 mono_link_bblock (cfg, cfg->cbb, true_bb);
10670 mono_link_bblock (cfg, cfg->cbb, false_bb);
10672 if (sp != stack_start) {
10673 handle_stack_args (cfg, stack_start, sp - stack_start);
10675 CHECK_UNVERIFIABLE (cfg);
10678 if (COMPILE_LLVM (cfg)) {
10679 dreg = alloc_ireg (cfg);
10680 MONO_EMIT_NEW_ICONST (cfg, dreg, 0);
10681 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, dreg, is_true ? 0 : 1);
10683 MONO_EMIT_NEW_BRANCH_BLOCK2 (cfg, OP_IBEQ, true_bb, false_bb);
10685 /* The JIT can't eliminate the iconst+compare */
10686 MONO_INST_NEW (cfg, ins, OP_BR);
10687 ins->inst_target_bb = is_true ? true_bb : false_bb;
10688 MONO_ADD_INS (cfg->cbb, ins);
10691 start_new_bblock = 1;
10695 *sp++ = handle_box (cfg, val, klass, context_used, &bblock);
10697 CHECK_CFG_EXCEPTION;
10706 token = read32 (ip + 1);
10707 klass = mini_get_class (method, token, generic_context);
10708 CHECK_TYPELOAD (klass);
10710 mono_save_token_info (cfg, image, token, klass);
10712 context_used = mini_class_check_context_used (cfg, klass);
10714 if (mono_class_is_nullable (klass)) {
10717 val = handle_unbox_nullable (cfg, *sp, klass, context_used);
10718 EMIT_NEW_VARLOADA (cfg, ins, get_vreg_to_inst (cfg, val->dreg), &val->klass->byval_arg);
10722 ins = handle_unbox (cfg, klass, sp, context_used);
10735 MonoClassField *field;
10736 #ifndef DISABLE_REMOTING
10740 gboolean is_instance;
10742 gpointer addr = NULL;
10743 gboolean is_special_static;
10745 MonoInst *store_val = NULL;
10746 MonoInst *thread_ins;
10749 is_instance = (op == CEE_LDFLD || op == CEE_LDFLDA || op == CEE_STFLD);
10751 if (op == CEE_STFLD) {
10754 store_val = sp [1];
10759 if (sp [0]->type == STACK_I4 || sp [0]->type == STACK_I8 || sp [0]->type == STACK_R8)
10761 if (*ip != CEE_LDFLD && sp [0]->type == STACK_VTYPE)
10764 if (op == CEE_STSFLD) {
10767 store_val = sp [0];
10772 token = read32 (ip + 1);
10773 if (method->wrapper_type != MONO_WRAPPER_NONE) {
10774 field = mono_method_get_wrapper_data (method, token);
10775 klass = field->parent;
10778 field = mono_field_from_token_checked (image, token, &klass, generic_context, &cfg->error);
10781 if (!dont_verify && !cfg->skip_visibility && !mono_method_can_access_field (method, field))
10782 FIELD_ACCESS_FAILURE (method, field);
10783 mono_class_init (klass);
10785 /* if the class is Critical then transparent code cannot access it's fields */
10786 if (!is_instance && mono_security_core_clr_enabled ())
10787 ensure_method_is_allowed_to_access_field (cfg, method, field, bblock, ip);
10789 /* XXX this is technically required but, so far (SL2), no [SecurityCritical] types (not many exists) have
10790 any visible *instance* field (in fact there's a single case for a static field in Marshal) XXX
10791 if (mono_security_core_clr_enabled ())
10792 ensure_method_is_allowed_to_access_field (cfg, method, field, bblock, ip);
10796 * LDFLD etc. is usable on static fields as well, so convert those cases to
10799 if (is_instance && field->type->attrs & FIELD_ATTRIBUTE_STATIC) {
10811 g_assert_not_reached ();
10813 is_instance = FALSE;
10816 context_used = mini_class_check_context_used (cfg, klass);
10818 /* INSTANCE CASE */
10820 foffset = klass->valuetype? field->offset - sizeof (MonoObject): field->offset;
10821 if (op == CEE_STFLD) {
10822 if (target_type_is_incompatible (cfg, field->type, sp [1]))
10824 #ifndef DISABLE_REMOTING
10825 if ((mono_class_is_marshalbyref (klass) && !MONO_CHECK_THIS (sp [0])) || mono_class_is_contextbound (klass) || klass == mono_defaults.marshalbyrefobject_class) {
10826 MonoMethod *stfld_wrapper = mono_marshal_get_stfld_wrapper (field->type);
10827 MonoInst *iargs [5];
10829 GSHAREDVT_FAILURE (op);
10831 iargs [0] = sp [0];
10832 EMIT_NEW_CLASSCONST (cfg, iargs [1], klass);
10833 EMIT_NEW_FIELDCONST (cfg, iargs [2], field);
10834 EMIT_NEW_ICONST (cfg, iargs [3], klass->valuetype ? field->offset - sizeof (MonoObject) :
10836 iargs [4] = sp [1];
10838 if (cfg->opt & MONO_OPT_INLINE || cfg->compile_aot) {
10839 costs = inline_method (cfg, stfld_wrapper, mono_method_signature (stfld_wrapper),
10840 iargs, ip, cfg->real_offset, TRUE, &bblock);
10841 CHECK_CFG_EXCEPTION;
10842 g_assert (costs > 0);
10844 cfg->real_offset += 5;
10846 inline_costs += costs;
10848 mono_emit_method_call (cfg, stfld_wrapper, iargs, NULL);
10855 MONO_EMIT_NULL_CHECK (cfg, sp [0]->dreg);
10857 if (mini_is_gsharedvt_klass (cfg, klass)) {
10858 MonoInst *offset_ins;
10860 context_used = mini_class_check_context_used (cfg, klass);
10862 offset_ins = emit_get_gsharedvt_info (cfg, field, MONO_RGCTX_INFO_FIELD_OFFSET);
10863 dreg = alloc_ireg_mp (cfg);
10864 EMIT_NEW_BIALU (cfg, ins, OP_PADD, dreg, sp [0]->dreg, offset_ins->dreg);
10865 /* The decomposition will call mini_emit_stobj () which will emit a wbarrier if needed */
10866 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, store, field->type, dreg, 0, sp [1]->dreg);
10868 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, store, field->type, sp [0]->dreg, foffset, sp [1]->dreg);
10870 if (sp [0]->opcode != OP_LDADDR)
10871 store->flags |= MONO_INST_FAULT;
10873 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)) {
10874 /* insert call to write barrier */
10878 dreg = alloc_ireg_mp (cfg);
10879 EMIT_NEW_BIALU_IMM (cfg, ptr, OP_PADD_IMM, dreg, sp [0]->dreg, foffset);
10880 emit_write_barrier (cfg, ptr, sp [1]);
10883 store->flags |= ins_flag;
10890 #ifndef DISABLE_REMOTING
10891 if (is_instance && ((mono_class_is_marshalbyref (klass) && !MONO_CHECK_THIS (sp [0])) || mono_class_is_contextbound (klass) || klass == mono_defaults.marshalbyrefobject_class)) {
10892 MonoMethod *wrapper = (op == CEE_LDFLDA) ? mono_marshal_get_ldflda_wrapper (field->type) : mono_marshal_get_ldfld_wrapper (field->type);
10893 MonoInst *iargs [4];
10895 GSHAREDVT_FAILURE (op);
10897 iargs [0] = sp [0];
10898 EMIT_NEW_CLASSCONST (cfg, iargs [1], klass);
10899 EMIT_NEW_FIELDCONST (cfg, iargs [2], field);
10900 EMIT_NEW_ICONST (cfg, iargs [3], klass->valuetype ? field->offset - sizeof (MonoObject) : field->offset);
10901 if (cfg->opt & MONO_OPT_INLINE || cfg->compile_aot) {
10902 costs = inline_method (cfg, wrapper, mono_method_signature (wrapper),
10903 iargs, ip, cfg->real_offset, TRUE, &bblock);
10904 CHECK_CFG_EXCEPTION;
10905 g_assert (costs > 0);
10907 cfg->real_offset += 5;
10911 inline_costs += costs;
10913 ins = mono_emit_method_call (cfg, wrapper, iargs, NULL);
10919 if (sp [0]->type == STACK_VTYPE) {
10922 /* Have to compute the address of the variable */
10924 var = get_vreg_to_inst (cfg, sp [0]->dreg);
10926 var = mono_compile_create_var_for_vreg (cfg, &klass->byval_arg, OP_LOCAL, sp [0]->dreg);
10928 g_assert (var->klass == klass);
10930 EMIT_NEW_VARLOADA (cfg, ins, var, &var->klass->byval_arg);
10934 if (op == CEE_LDFLDA) {
10935 if (sp [0]->type == STACK_OBJ) {
10936 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, sp [0]->dreg, 0);
10937 MONO_EMIT_NEW_COND_EXC (cfg, EQ, "NullReferenceException");
10940 dreg = alloc_ireg_mp (cfg);
10942 if (mini_is_gsharedvt_klass (cfg, klass)) {
10943 MonoInst *offset_ins;
10945 offset_ins = emit_get_gsharedvt_info (cfg, field, MONO_RGCTX_INFO_FIELD_OFFSET);
10946 EMIT_NEW_BIALU (cfg, ins, OP_PADD, dreg, sp [0]->dreg, offset_ins->dreg);
10948 EMIT_NEW_BIALU_IMM (cfg, ins, OP_PADD_IMM, dreg, sp [0]->dreg, foffset);
10950 ins->klass = mono_class_from_mono_type (field->type);
10951 ins->type = STACK_MP;
10956 MONO_EMIT_NULL_CHECK (cfg, sp [0]->dreg);
10958 if (mini_is_gsharedvt_klass (cfg, klass)) {
10959 MonoInst *offset_ins;
10961 offset_ins = emit_get_gsharedvt_info (cfg, field, MONO_RGCTX_INFO_FIELD_OFFSET);
10962 dreg = alloc_ireg_mp (cfg);
10963 EMIT_NEW_BIALU (cfg, ins, OP_PADD, dreg, sp [0]->dreg, offset_ins->dreg);
10964 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, load, field->type, dreg, 0);
10966 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, load, field->type, sp [0]->dreg, foffset);
10968 load->flags |= ins_flag;
10969 if (sp [0]->opcode != OP_LDADDR)
10970 load->flags |= MONO_INST_FAULT;
10982 context_used = mini_class_check_context_used (cfg, klass);
10984 ftype = mono_field_get_type (field);
10986 if (ftype->attrs & FIELD_ATTRIBUTE_LITERAL)
10989 /* The special_static_fields field is init'd in mono_class_vtable, so it needs
10990 * to be called here.
10992 if (!context_used && !(cfg->opt & MONO_OPT_SHARED)) {
10993 mono_class_vtable (cfg->domain, klass);
10994 CHECK_TYPELOAD (klass);
10996 mono_domain_lock (cfg->domain);
10997 if (cfg->domain->special_static_fields)
10998 addr = g_hash_table_lookup (cfg->domain->special_static_fields, field);
10999 mono_domain_unlock (cfg->domain);
11001 is_special_static = mono_class_field_is_special_static (field);
11003 if (is_special_static && ((gsize)addr & 0x80000000) == 0)
11004 thread_ins = mono_get_thread_intrinsic (cfg);
11008 /* Generate IR to compute the field address */
11009 if (is_special_static && ((gsize)addr & 0x80000000) == 0 && thread_ins && !(cfg->opt & MONO_OPT_SHARED) && !context_used) {
11011 * Fast access to TLS data
11012 * Inline version of get_thread_static_data () in
11016 int idx, static_data_reg, array_reg, dreg;
11018 GSHAREDVT_FAILURE (op);
11020 MONO_ADD_INS (cfg->cbb, thread_ins);
11021 static_data_reg = alloc_ireg (cfg);
11022 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, static_data_reg, thread_ins->dreg, MONO_STRUCT_OFFSET (MonoInternalThread, static_data));
11024 if (cfg->compile_aot) {
11025 int offset_reg, offset2_reg, idx_reg;
11027 /* For TLS variables, this will return the TLS offset */
11028 EMIT_NEW_SFLDACONST (cfg, ins, field);
11029 offset_reg = ins->dreg;
11030 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_IAND_IMM, offset_reg, offset_reg, 0x7fffffff);
11031 idx_reg = alloc_ireg (cfg);
11032 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_IAND_IMM, idx_reg, offset_reg, 0x3f);
11033 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ISHL_IMM, idx_reg, idx_reg, sizeof (gpointer) == 8 ? 3 : 2);
11034 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, static_data_reg, static_data_reg, idx_reg);
11035 array_reg = alloc_ireg (cfg);
11036 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, array_reg, static_data_reg, 0);
11037 offset2_reg = alloc_ireg (cfg);
11038 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ISHR_UN_IMM, offset2_reg, offset_reg, 6);
11039 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_IAND_IMM, offset2_reg, offset2_reg, 0x1ffffff);
11040 dreg = alloc_ireg (cfg);
11041 EMIT_NEW_BIALU (cfg, ins, OP_PADD, dreg, array_reg, offset2_reg);
11043 offset = (gsize)addr & 0x7fffffff;
11044 idx = offset & 0x3f;
11046 array_reg = alloc_ireg (cfg);
11047 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, array_reg, static_data_reg, idx * sizeof (gpointer));
11048 dreg = alloc_ireg (cfg);
11049 EMIT_NEW_BIALU_IMM (cfg, ins, OP_ADD_IMM, dreg, array_reg, ((offset >> 6) & 0x1ffffff));
11051 } else if ((cfg->opt & MONO_OPT_SHARED) ||
11052 (cfg->compile_aot && is_special_static) ||
11053 (context_used && is_special_static)) {
11054 MonoInst *iargs [2];
11056 g_assert (field->parent);
11057 EMIT_NEW_DOMAINCONST (cfg, iargs [0]);
11058 if (context_used) {
11059 iargs [1] = emit_get_rgctx_field (cfg, context_used,
11060 field, MONO_RGCTX_INFO_CLASS_FIELD);
11062 EMIT_NEW_FIELDCONST (cfg, iargs [1], field);
11064 ins = mono_emit_jit_icall (cfg, mono_class_static_field_address, iargs);
11065 } else if (context_used) {
11066 MonoInst *static_data;
11069 g_print ("sharing static field access in %s.%s.%s - depth %d offset %d\n",
11070 method->klass->name_space, method->klass->name, method->name,
11071 depth, field->offset);
11074 if (mono_class_needs_cctor_run (klass, method))
11075 emit_generic_class_init (cfg, klass, &bblock);
11078 * The pointer we're computing here is
11080 * super_info.static_data + field->offset
11082 static_data = emit_get_rgctx_klass (cfg, context_used,
11083 klass, MONO_RGCTX_INFO_STATIC_DATA);
11085 if (mini_is_gsharedvt_klass (cfg, klass)) {
11086 MonoInst *offset_ins;
11088 offset_ins = emit_get_rgctx_field (cfg, context_used, field, MONO_RGCTX_INFO_FIELD_OFFSET);
11089 dreg = alloc_ireg_mp (cfg);
11090 EMIT_NEW_BIALU (cfg, ins, OP_PADD, dreg, static_data->dreg, offset_ins->dreg);
11091 } else if (field->offset == 0) {
11094 int addr_reg = mono_alloc_preg (cfg);
11095 EMIT_NEW_BIALU_IMM (cfg, ins, OP_PADD_IMM, addr_reg, static_data->dreg, field->offset);
11097 } else if ((cfg->opt & MONO_OPT_SHARED) || (cfg->compile_aot && addr)) {
11098 MonoInst *iargs [2];
11100 g_assert (field->parent);
11101 EMIT_NEW_DOMAINCONST (cfg, iargs [0]);
11102 EMIT_NEW_FIELDCONST (cfg, iargs [1], field);
11103 ins = mono_emit_jit_icall (cfg, mono_class_static_field_address, iargs);
11105 MonoVTable *vtable = NULL;
11107 if (!cfg->compile_aot)
11108 vtable = mono_class_vtable (cfg->domain, klass);
11109 CHECK_TYPELOAD (klass);
11112 if (mini_field_access_needs_cctor_run (cfg, method, klass, vtable)) {
11113 if (!(g_slist_find (class_inits, klass))) {
11114 emit_class_init (cfg, klass, &bblock);
11115 if (cfg->verbose_level > 2)
11116 printf ("class %s.%s needs init call for %s\n", klass->name_space, klass->name, mono_field_get_name (field));
11117 class_inits = g_slist_prepend (class_inits, klass);
11120 if (cfg->run_cctors) {
11122 /* This makes so that inline cannot trigger */
11123 /* .cctors: too many apps depend on them */
11124 /* running with a specific order... */
11126 if (! vtable->initialized)
11127 INLINE_FAILURE ("class init");
11128 ex = mono_runtime_class_init_full (vtable, FALSE);
11130 set_exception_object (cfg, ex);
11131 goto exception_exit;
11135 if (cfg->compile_aot)
11136 EMIT_NEW_SFLDACONST (cfg, ins, field);
11139 addr = (char*)mono_vtable_get_static_field_data (vtable) + field->offset;
11141 EMIT_NEW_PCONST (cfg, ins, addr);
11144 MonoInst *iargs [1];
11145 EMIT_NEW_ICONST (cfg, iargs [0], GPOINTER_TO_UINT (addr));
11146 ins = mono_emit_jit_icall (cfg, mono_get_special_static_data, iargs);
11150 /* Generate IR to do the actual load/store operation */
11152 if ((op == CEE_STFLD || op == CEE_STSFLD) && (ins_flag & MONO_INST_VOLATILE)) {
11153 /* Volatile stores have release semantics, see 12.6.7 in Ecma 335 */
11154 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_REL);
11157 if (op == CEE_LDSFLDA) {
11158 ins->klass = mono_class_from_mono_type (ftype);
11159 ins->type = STACK_PTR;
11161 } else if (op == CEE_STSFLD) {
11164 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, store, ftype, ins->dreg, 0, store_val->dreg);
11165 store->flags |= ins_flag;
11167 gboolean is_const = FALSE;
11168 MonoVTable *vtable = NULL;
11169 gpointer addr = NULL;
11171 if (!context_used) {
11172 vtable = mono_class_vtable (cfg->domain, klass);
11173 CHECK_TYPELOAD (klass);
11175 if ((ftype->attrs & FIELD_ATTRIBUTE_INIT_ONLY) && (((addr = mono_aot_readonly_field_override (field)) != NULL) ||
11176 (!context_used && !((cfg->opt & MONO_OPT_SHARED) || cfg->compile_aot) && vtable->initialized))) {
11177 int ro_type = ftype->type;
11179 addr = (char*)mono_vtable_get_static_field_data (vtable) + field->offset;
11180 if (ro_type == MONO_TYPE_VALUETYPE && ftype->data.klass->enumtype) {
11181 ro_type = mono_class_enum_basetype (ftype->data.klass)->type;
11184 GSHAREDVT_FAILURE (op);
11186 /* printf ("RO-FIELD %s.%s:%s\n", klass->name_space, klass->name, mono_field_get_name (field));*/
11189 case MONO_TYPE_BOOLEAN:
11191 EMIT_NEW_ICONST (cfg, *sp, *((guint8 *)addr));
11195 EMIT_NEW_ICONST (cfg, *sp, *((gint8 *)addr));
11198 case MONO_TYPE_CHAR:
11200 EMIT_NEW_ICONST (cfg, *sp, *((guint16 *)addr));
11204 EMIT_NEW_ICONST (cfg, *sp, *((gint16 *)addr));
11209 EMIT_NEW_ICONST (cfg, *sp, *((gint32 *)addr));
11213 EMIT_NEW_ICONST (cfg, *sp, *((guint32 *)addr));
11218 case MONO_TYPE_PTR:
11219 case MONO_TYPE_FNPTR:
11220 EMIT_NEW_PCONST (cfg, *sp, *((gpointer *)addr));
11221 type_to_eval_stack_type ((cfg), field->type, *sp);
11224 case MONO_TYPE_STRING:
11225 case MONO_TYPE_OBJECT:
11226 case MONO_TYPE_CLASS:
11227 case MONO_TYPE_SZARRAY:
11228 case MONO_TYPE_ARRAY:
11229 if (!mono_gc_is_moving ()) {
11230 EMIT_NEW_PCONST (cfg, *sp, *((gpointer *)addr));
11231 type_to_eval_stack_type ((cfg), field->type, *sp);
11239 EMIT_NEW_I8CONST (cfg, *sp, *((gint64 *)addr));
11244 case MONO_TYPE_VALUETYPE:
11254 CHECK_STACK_OVF (1);
11256 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, load, field->type, ins->dreg, 0);
11257 load->flags |= ins_flag;
11263 if ((op == CEE_LDFLD || op == CEE_LDSFLD) && (ins_flag & MONO_INST_VOLATILE)) {
11264 /* Volatile loads have acquire semantics, see 12.6.7 in Ecma 335 */
11265 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_ACQ);
11276 token = read32 (ip + 1);
11277 klass = mini_get_class (method, token, generic_context);
11278 CHECK_TYPELOAD (klass);
11279 if (ins_flag & MONO_INST_VOLATILE) {
11280 /* Volatile stores have release semantics, see 12.6.7 in Ecma 335 */
11281 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_REL);
11283 /* FIXME: should check item at sp [1] is compatible with the type of the store. */
11284 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, sp [0]->dreg, 0, sp [1]->dreg);
11285 ins->flags |= ins_flag;
11286 if (cfg->gen_write_barriers && cfg->method->wrapper_type != MONO_WRAPPER_WRITE_BARRIER &&
11287 generic_class_is_reference_type (cfg, klass)) {
11288 /* insert call to write barrier */
11289 emit_write_barrier (cfg, sp [0], sp [1]);
11301 const char *data_ptr;
11303 guint32 field_token;
11309 token = read32 (ip + 1);
11311 klass = mini_get_class (method, token, generic_context);
11312 CHECK_TYPELOAD (klass);
11314 context_used = mini_class_check_context_used (cfg, klass);
11316 if (sp [0]->type == STACK_I8 || (SIZEOF_VOID_P == 8 && sp [0]->type == STACK_PTR)) {
11317 MONO_INST_NEW (cfg, ins, OP_LCONV_TO_OVF_U4);
11318 ins->sreg1 = sp [0]->dreg;
11319 ins->type = STACK_I4;
11320 ins->dreg = alloc_ireg (cfg);
11321 MONO_ADD_INS (cfg->cbb, ins);
11322 *sp = mono_decompose_opcode (cfg, ins, &bblock);
11325 if (context_used) {
11326 MonoInst *args [3];
11327 MonoClass *array_class = mono_array_class_get (klass, 1);
11328 MonoMethod *managed_alloc = mono_gc_get_managed_array_allocator (array_class);
11330 /* FIXME: Use OP_NEWARR and decompose later to help abcrem */
11333 args [0] = emit_get_rgctx_klass (cfg, context_used,
11334 array_class, MONO_RGCTX_INFO_VTABLE);
11339 ins = mono_emit_method_call (cfg, managed_alloc, args, NULL);
11341 ins = mono_emit_jit_icall (cfg, mono_array_new_specific, args);
11343 if (cfg->opt & MONO_OPT_SHARED) {
11344 /* Decompose now to avoid problems with references to the domainvar */
11345 MonoInst *iargs [3];
11347 EMIT_NEW_DOMAINCONST (cfg, iargs [0]);
11348 EMIT_NEW_CLASSCONST (cfg, iargs [1], klass);
11349 iargs [2] = sp [0];
11351 ins = mono_emit_jit_icall (cfg, mono_array_new, iargs);
11353 /* Decompose later since it is needed by abcrem */
11354 MonoClass *array_type = mono_array_class_get (klass, 1);
11355 mono_class_vtable (cfg->domain, array_type);
11356 CHECK_TYPELOAD (array_type);
11358 MONO_INST_NEW (cfg, ins, OP_NEWARR);
11359 ins->dreg = alloc_ireg_ref (cfg);
11360 ins->sreg1 = sp [0]->dreg;
11361 ins->inst_newa_class = klass;
11362 ins->type = STACK_OBJ;
11363 ins->klass = array_type;
11364 MONO_ADD_INS (cfg->cbb, ins);
11365 cfg->flags |= MONO_CFG_HAS_ARRAY_ACCESS;
11366 cfg->cbb->has_array_access = TRUE;
11368 /* Needed so mono_emit_load_get_addr () gets called */
11369 mono_get_got_var (cfg);
11379 * we inline/optimize the initialization sequence if possible.
11380 * we should also allocate the array as not cleared, since we spend as much time clearing to 0 as initializing
11381 * for small sizes open code the memcpy
11382 * ensure the rva field is big enough
11384 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))) {
11385 MonoMethod *memcpy_method = get_memcpy_method ();
11386 MonoInst *iargs [3];
11387 int add_reg = alloc_ireg_mp (cfg);
11389 EMIT_NEW_BIALU_IMM (cfg, iargs [0], OP_PADD_IMM, add_reg, ins->dreg, MONO_STRUCT_OFFSET (MonoArray, vector));
11390 if (cfg->compile_aot) {
11391 EMIT_NEW_AOTCONST_TOKEN (cfg, iargs [1], MONO_PATCH_INFO_RVA, method->klass->image, GPOINTER_TO_UINT(field_token), STACK_PTR, NULL);
11393 EMIT_NEW_PCONST (cfg, iargs [1], (char*)data_ptr);
11395 EMIT_NEW_ICONST (cfg, iargs [2], data_size);
11396 mono_emit_method_call (cfg, memcpy_method, iargs, NULL);
11405 if (sp [0]->type != STACK_OBJ)
11408 MONO_INST_NEW (cfg, ins, OP_LDLEN);
11409 ins->dreg = alloc_preg (cfg);
11410 ins->sreg1 = sp [0]->dreg;
11411 ins->type = STACK_I4;
11412 /* This flag will be inherited by the decomposition */
11413 ins->flags |= MONO_INST_FAULT;
11414 MONO_ADD_INS (cfg->cbb, ins);
11415 cfg->flags |= MONO_CFG_HAS_ARRAY_ACCESS;
11416 cfg->cbb->has_array_access = TRUE;
11424 if (sp [0]->type != STACK_OBJ)
11427 cfg->flags |= MONO_CFG_HAS_LDELEMA;
11429 klass = mini_get_class (method, read32 (ip + 1), generic_context);
11430 CHECK_TYPELOAD (klass);
11431 /* we need to make sure that this array is exactly the type it needs
11432 * to be for correctness. the wrappers are lax with their usage
11433 * so we need to ignore them here
11435 if (!klass->valuetype && method->wrapper_type == MONO_WRAPPER_NONE && !readonly) {
11436 MonoClass *array_class = mono_array_class_get (klass, 1);
11437 mini_emit_check_array_type (cfg, sp [0], array_class);
11438 CHECK_TYPELOAD (array_class);
11442 ins = mini_emit_ldelema_1_ins (cfg, klass, sp [0], sp [1], TRUE);
11447 case CEE_LDELEM_I1:
11448 case CEE_LDELEM_U1:
11449 case CEE_LDELEM_I2:
11450 case CEE_LDELEM_U2:
11451 case CEE_LDELEM_I4:
11452 case CEE_LDELEM_U4:
11453 case CEE_LDELEM_I8:
11455 case CEE_LDELEM_R4:
11456 case CEE_LDELEM_R8:
11457 case CEE_LDELEM_REF: {
11463 if (*ip == CEE_LDELEM) {
11465 token = read32 (ip + 1);
11466 klass = mini_get_class (method, token, generic_context);
11467 CHECK_TYPELOAD (klass);
11468 mono_class_init (klass);
11471 klass = array_access_to_klass (*ip);
11473 if (sp [0]->type != STACK_OBJ)
11476 cfg->flags |= MONO_CFG_HAS_LDELEMA;
11478 if (mini_is_gsharedvt_variable_klass (cfg, klass)) {
11479 // FIXME-VT: OP_ICONST optimization
11480 addr = mini_emit_ldelema_1_ins (cfg, klass, sp [0], sp [1], TRUE);
11481 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, addr->dreg, 0);
11482 ins->opcode = OP_LOADV_MEMBASE;
11483 } else if (sp [1]->opcode == OP_ICONST) {
11484 int array_reg = sp [0]->dreg;
11485 int index_reg = sp [1]->dreg;
11486 int offset = (mono_class_array_element_size (klass) * sp [1]->inst_c0) + MONO_STRUCT_OFFSET (MonoArray, vector);
11488 MONO_EMIT_BOUNDS_CHECK (cfg, array_reg, MonoArray, max_length, index_reg);
11489 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, array_reg, offset);
11491 addr = mini_emit_ldelema_1_ins (cfg, klass, sp [0], sp [1], TRUE);
11492 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, addr->dreg, 0);
11495 if (*ip == CEE_LDELEM)
11502 case CEE_STELEM_I1:
11503 case CEE_STELEM_I2:
11504 case CEE_STELEM_I4:
11505 case CEE_STELEM_I8:
11506 case CEE_STELEM_R4:
11507 case CEE_STELEM_R8:
11508 case CEE_STELEM_REF:
11513 cfg->flags |= MONO_CFG_HAS_LDELEMA;
11515 if (*ip == CEE_STELEM) {
11517 token = read32 (ip + 1);
11518 klass = mini_get_class (method, token, generic_context);
11519 CHECK_TYPELOAD (klass);
11520 mono_class_init (klass);
11523 klass = array_access_to_klass (*ip);
11525 if (sp [0]->type != STACK_OBJ)
11528 emit_array_store (cfg, klass, sp, TRUE);
11530 if (*ip == CEE_STELEM)
11537 case CEE_CKFINITE: {
11541 MONO_INST_NEW (cfg, ins, OP_CKFINITE);
11542 ins->sreg1 = sp [0]->dreg;
11543 ins->dreg = alloc_freg (cfg);
11544 ins->type = STACK_R8;
11545 MONO_ADD_INS (bblock, ins);
11547 *sp++ = mono_decompose_opcode (cfg, ins, &bblock);
11552 case CEE_REFANYVAL: {
11553 MonoInst *src_var, *src;
11555 int klass_reg = alloc_preg (cfg);
11556 int dreg = alloc_preg (cfg);
11558 GSHAREDVT_FAILURE (*ip);
11561 MONO_INST_NEW (cfg, ins, *ip);
11564 klass = mini_get_class (method, read32 (ip + 1), generic_context);
11565 CHECK_TYPELOAD (klass);
11567 context_used = mini_class_check_context_used (cfg, klass);
11570 src_var = get_vreg_to_inst (cfg, sp [0]->dreg);
11572 src_var = mono_compile_create_var_for_vreg (cfg, &mono_defaults.typed_reference_class->byval_arg, OP_LOCAL, sp [0]->dreg);
11573 EMIT_NEW_VARLOADA (cfg, src, src_var, src_var->inst_vtype);
11574 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, src->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, klass));
11576 if (context_used) {
11577 MonoInst *klass_ins;
11579 klass_ins = emit_get_rgctx_klass (cfg, context_used,
11580 klass, MONO_RGCTX_INFO_KLASS);
11583 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, klass_reg, klass_ins->dreg);
11584 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException");
11586 mini_emit_class_check (cfg, klass_reg, klass);
11588 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, dreg, src->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, value));
11589 ins->type = STACK_MP;
11594 case CEE_MKREFANY: {
11595 MonoInst *loc, *addr;
11597 GSHAREDVT_FAILURE (*ip);
11600 MONO_INST_NEW (cfg, ins, *ip);
11603 klass = mini_get_class (method, read32 (ip + 1), generic_context);
11604 CHECK_TYPELOAD (klass);
11606 context_used = mini_class_check_context_used (cfg, klass);
11608 loc = mono_compile_create_var (cfg, &mono_defaults.typed_reference_class->byval_arg, OP_LOCAL);
11609 EMIT_NEW_TEMPLOADA (cfg, addr, loc->inst_c0);
11611 if (context_used) {
11612 MonoInst *const_ins;
11613 int type_reg = alloc_preg (cfg);
11615 const_ins = emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_KLASS);
11616 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREP_MEMBASE_REG, addr->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, klass), const_ins->dreg);
11617 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ADD_IMM, type_reg, const_ins->dreg, MONO_STRUCT_OFFSET (MonoClass, byval_arg));
11618 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREP_MEMBASE_REG, addr->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, type), type_reg);
11619 } else if (cfg->compile_aot) {
11620 int const_reg = alloc_preg (cfg);
11621 int type_reg = alloc_preg (cfg);
11623 MONO_EMIT_NEW_CLASSCONST (cfg, const_reg, klass);
11624 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREP_MEMBASE_REG, addr->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, klass), const_reg);
11625 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ADD_IMM, type_reg, const_reg, MONO_STRUCT_OFFSET (MonoClass, byval_arg));
11626 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREP_MEMBASE_REG, addr->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, type), type_reg);
11628 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREP_MEMBASE_IMM, addr->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, type), &klass->byval_arg);
11629 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREP_MEMBASE_IMM, addr->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, klass), klass);
11631 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREP_MEMBASE_REG, addr->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, value), sp [0]->dreg);
11633 EMIT_NEW_TEMPLOAD (cfg, ins, loc->inst_c0);
11634 ins->type = STACK_VTYPE;
11635 ins->klass = mono_defaults.typed_reference_class;
11640 case CEE_LDTOKEN: {
11642 MonoClass *handle_class;
11644 CHECK_STACK_OVF (1);
11647 n = read32 (ip + 1);
11649 if (method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD ||
11650 method->wrapper_type == MONO_WRAPPER_SYNCHRONIZED) {
11651 handle = mono_method_get_wrapper_data (method, n);
11652 handle_class = mono_method_get_wrapper_data (method, n + 1);
11653 if (handle_class == mono_defaults.typehandle_class)
11654 handle = &((MonoClass*)handle)->byval_arg;
11657 handle = mono_ldtoken_checked (image, n, &handle_class, generic_context, &cfg->error);
11662 mono_class_init (handle_class);
11663 if (cfg->generic_sharing_context) {
11664 if (mono_metadata_token_table (n) == MONO_TABLE_TYPEDEF ||
11665 mono_metadata_token_table (n) == MONO_TABLE_TYPEREF) {
11666 /* This case handles ldtoken
11667 of an open type, like for
11670 } else if (handle_class == mono_defaults.typehandle_class) {
11671 context_used = mini_class_check_context_used (cfg, mono_class_from_mono_type (handle));
11672 } else if (handle_class == mono_defaults.fieldhandle_class)
11673 context_used = mini_class_check_context_used (cfg, ((MonoClassField*)handle)->parent);
11674 else if (handle_class == mono_defaults.methodhandle_class)
11675 context_used = mini_method_check_context_used (cfg, handle);
11677 g_assert_not_reached ();
11680 if ((cfg->opt & MONO_OPT_SHARED) &&
11681 method->wrapper_type != MONO_WRAPPER_DYNAMIC_METHOD &&
11682 method->wrapper_type != MONO_WRAPPER_SYNCHRONIZED) {
11683 MonoInst *addr, *vtvar, *iargs [3];
11684 int method_context_used;
11686 method_context_used = mini_method_check_context_used (cfg, method);
11688 vtvar = mono_compile_create_var (cfg, &handle_class->byval_arg, OP_LOCAL);
11690 EMIT_NEW_IMAGECONST (cfg, iargs [0], image);
11691 EMIT_NEW_ICONST (cfg, iargs [1], n);
11692 if (method_context_used) {
11693 iargs [2] = emit_get_rgctx_method (cfg, method_context_used,
11694 method, MONO_RGCTX_INFO_METHOD);
11695 ins = mono_emit_jit_icall (cfg, mono_ldtoken_wrapper_generic_shared, iargs);
11697 EMIT_NEW_PCONST (cfg, iargs [2], generic_context);
11698 ins = mono_emit_jit_icall (cfg, mono_ldtoken_wrapper, iargs);
11700 EMIT_NEW_TEMPLOADA (cfg, addr, vtvar->inst_c0);
11702 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, addr->dreg, 0, ins->dreg);
11704 EMIT_NEW_TEMPLOAD (cfg, ins, vtvar->inst_c0);
11706 if ((ip + 5 < end) && ip_in_bb (cfg, bblock, ip + 5) &&
11707 ((ip [5] == CEE_CALL) || (ip [5] == CEE_CALLVIRT)) &&
11708 (cmethod = mini_get_method (cfg, method, read32 (ip + 6), NULL, generic_context)) &&
11709 (cmethod->klass == mono_defaults.systemtype_class) &&
11710 (strcmp (cmethod->name, "GetTypeFromHandle") == 0)) {
11711 MonoClass *tclass = mono_class_from_mono_type (handle);
11713 mono_class_init (tclass);
11714 if (context_used) {
11715 ins = emit_get_rgctx_klass (cfg, context_used,
11716 tclass, MONO_RGCTX_INFO_REFLECTION_TYPE);
11717 } else if (cfg->compile_aot) {
11718 if (method->wrapper_type) {
11719 mono_error_init (&error); //got to do it since there are multiple conditionals below
11720 if (mono_class_get_checked (tclass->image, tclass->type_token, &error) == tclass && !generic_context) {
11721 /* Special case for static synchronized wrappers */
11722 EMIT_NEW_TYPE_FROM_HANDLE_CONST (cfg, ins, tclass->image, tclass->type_token, generic_context);
11724 mono_error_cleanup (&error); /* FIXME don't swallow the error */
11725 /* FIXME: n is not a normal token */
11727 EMIT_NEW_PCONST (cfg, ins, NULL);
11730 EMIT_NEW_TYPE_FROM_HANDLE_CONST (cfg, ins, image, n, generic_context);
11733 EMIT_NEW_PCONST (cfg, ins, mono_type_get_object (cfg->domain, handle));
11735 ins->type = STACK_OBJ;
11736 ins->klass = cmethod->klass;
11739 MonoInst *addr, *vtvar;
11741 vtvar = mono_compile_create_var (cfg, &handle_class->byval_arg, OP_LOCAL);
11743 if (context_used) {
11744 if (handle_class == mono_defaults.typehandle_class) {
11745 ins = emit_get_rgctx_klass (cfg, context_used,
11746 mono_class_from_mono_type (handle),
11747 MONO_RGCTX_INFO_TYPE);
11748 } else if (handle_class == mono_defaults.methodhandle_class) {
11749 ins = emit_get_rgctx_method (cfg, context_used,
11750 handle, MONO_RGCTX_INFO_METHOD);
11751 } else if (handle_class == mono_defaults.fieldhandle_class) {
11752 ins = emit_get_rgctx_field (cfg, context_used,
11753 handle, MONO_RGCTX_INFO_CLASS_FIELD);
11755 g_assert_not_reached ();
11757 } else if (cfg->compile_aot) {
11758 EMIT_NEW_LDTOKENCONST (cfg, ins, image, n, generic_context);
11760 EMIT_NEW_PCONST (cfg, ins, handle);
11762 EMIT_NEW_TEMPLOADA (cfg, addr, vtvar->inst_c0);
11763 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, addr->dreg, 0, ins->dreg);
11764 EMIT_NEW_TEMPLOAD (cfg, ins, vtvar->inst_c0);
11774 MONO_INST_NEW (cfg, ins, OP_THROW);
11776 ins->sreg1 = sp [0]->dreg;
11778 bblock->out_of_line = TRUE;
11779 MONO_ADD_INS (bblock, ins);
11780 MONO_INST_NEW (cfg, ins, OP_NOT_REACHED);
11781 MONO_ADD_INS (bblock, ins);
11784 link_bblock (cfg, bblock, end_bblock);
11785 start_new_bblock = 1;
11787 case CEE_ENDFINALLY:
11788 /* mono_save_seq_point_info () depends on this */
11789 if (sp != stack_start)
11790 emit_seq_point (cfg, method, ip, FALSE, FALSE);
11791 MONO_INST_NEW (cfg, ins, OP_ENDFINALLY);
11792 MONO_ADD_INS (bblock, ins);
11794 start_new_bblock = 1;
11797 * Control will leave the method so empty the stack, otherwise
11798 * the next basic block will start with a nonempty stack.
11800 while (sp != stack_start) {
11805 case CEE_LEAVE_S: {
11808 if (*ip == CEE_LEAVE) {
11810 target = ip + 5 + (gint32)read32(ip + 1);
11813 target = ip + 2 + (signed char)(ip [1]);
11816 /* empty the stack */
11817 while (sp != stack_start) {
11822 * If this leave statement is in a catch block, check for a
11823 * pending exception, and rethrow it if necessary.
11824 * We avoid doing this in runtime invoke wrappers, since those are called
11825 * by native code which excepts the wrapper to catch all exceptions.
11827 for (i = 0; i < header->num_clauses; ++i) {
11828 MonoExceptionClause *clause = &header->clauses [i];
11831 * Use <= in the final comparison to handle clauses with multiple
11832 * leave statements, like in bug #78024.
11833 * The ordering of the exception clauses guarantees that we find the
11834 * innermost clause.
11836 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) {
11838 MonoBasicBlock *dont_throw;
11843 NEW_TEMPLOAD (cfg, load, mono_find_exvar_for_offset (cfg, clause->handler_offset)->inst_c0);
11846 exc_ins = mono_emit_jit_icall (cfg, mono_thread_get_undeniable_exception, NULL);
11848 NEW_BBLOCK (cfg, dont_throw);
11851 * Currently, we always rethrow the abort exception, despite the
11852 * fact that this is not correct. See thread6.cs for an example.
11853 * But propagating the abort exception is more important than
11854 * getting the sematics right.
11856 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, exc_ins->dreg, 0);
11857 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, dont_throw);
11858 MONO_EMIT_NEW_UNALU (cfg, OP_THROW, -1, exc_ins->dreg);
11860 MONO_START_BB (cfg, dont_throw);
11865 if ((handlers = mono_find_final_block (cfg, ip, target, MONO_EXCEPTION_CLAUSE_FINALLY))) {
11867 MonoExceptionClause *clause;
11869 for (tmp = handlers; tmp; tmp = tmp->next) {
11870 clause = tmp->data;
11871 tblock = cfg->cil_offset_to_bb [clause->handler_offset];
11873 link_bblock (cfg, bblock, tblock);
11874 MONO_INST_NEW (cfg, ins, OP_CALL_HANDLER);
11875 ins->inst_target_bb = tblock;
11876 ins->inst_eh_block = clause;
11877 MONO_ADD_INS (bblock, ins);
11878 bblock->has_call_handler = 1;
11879 if (COMPILE_LLVM (cfg)) {
11880 MonoBasicBlock *target_bb;
11883 * Link the finally bblock with the target, since it will
11884 * conceptually branch there.
11885 * FIXME: Have to link the bblock containing the endfinally.
11887 GET_BBLOCK (cfg, target_bb, target);
11888 link_bblock (cfg, tblock, target_bb);
11891 g_list_free (handlers);
11894 MONO_INST_NEW (cfg, ins, OP_BR);
11895 MONO_ADD_INS (bblock, ins);
11896 GET_BBLOCK (cfg, tblock, target);
11897 link_bblock (cfg, bblock, tblock);
11898 ins->inst_target_bb = tblock;
11899 start_new_bblock = 1;
11901 if (*ip == CEE_LEAVE)
11910 * Mono specific opcodes
11912 case MONO_CUSTOM_PREFIX: {
11914 g_assert (method->wrapper_type != MONO_WRAPPER_NONE);
11918 case CEE_MONO_ICALL: {
11920 MonoJitICallInfo *info;
11922 token = read32 (ip + 2);
11923 func = mono_method_get_wrapper_data (method, token);
11924 info = mono_find_jit_icall_by_addr (func);
11926 g_error ("Could not find icall address in wrapper %s", mono_method_full_name (method, 1));
11929 CHECK_STACK (info->sig->param_count);
11930 sp -= info->sig->param_count;
11932 ins = mono_emit_jit_icall (cfg, info->func, sp);
11933 if (!MONO_TYPE_IS_VOID (info->sig->ret))
11937 inline_costs += 10 * num_calls++;
11941 case CEE_MONO_LDPTR_CARD_TABLE: {
11943 gpointer card_mask;
11944 CHECK_STACK_OVF (1);
11946 if (cfg->compile_aot)
11947 EMIT_NEW_AOTCONST (cfg, ins, MONO_PATCH_INFO_GC_CARD_TABLE_ADDR, NULL);
11949 EMIT_NEW_PCONST (cfg, ins, mono_gc_get_card_table (&shift_bits, &card_mask));
11953 inline_costs += 10 * num_calls++;
11956 case CEE_MONO_LDPTR_NURSERY_START: {
11959 CHECK_STACK_OVF (1);
11961 if (cfg->compile_aot)
11962 EMIT_NEW_AOTCONST (cfg, ins, MONO_PATCH_INFO_GC_NURSERY_START, NULL);
11964 EMIT_NEW_PCONST (cfg, ins, mono_gc_get_nursery (&shift_bits, &size));
11968 inline_costs += 10 * num_calls++;
11971 case CEE_MONO_LDPTR_INT_REQ_FLAG: {
11972 CHECK_STACK_OVF (1);
11974 if (cfg->compile_aot)
11975 EMIT_NEW_AOTCONST (cfg, ins, MONO_PATCH_INFO_INTERRUPTION_REQUEST_FLAG, NULL);
11977 EMIT_NEW_PCONST (cfg, ins, mono_thread_interruption_request_flag ());
11981 inline_costs += 10 * num_calls++;
11984 case CEE_MONO_LDPTR: {
11987 CHECK_STACK_OVF (1);
11989 token = read32 (ip + 2);
11991 ptr = mono_method_get_wrapper_data (method, token);
11992 EMIT_NEW_PCONST (cfg, ins, ptr);
11995 inline_costs += 10 * num_calls++;
11996 /* Can't embed random pointers into AOT code */
12000 case CEE_MONO_JIT_ICALL_ADDR: {
12001 MonoJitICallInfo *callinfo;
12004 CHECK_STACK_OVF (1);
12006 token = read32 (ip + 2);
12008 ptr = mono_method_get_wrapper_data (method, token);
12009 callinfo = mono_find_jit_icall_by_addr (ptr);
12010 g_assert (callinfo);
12011 EMIT_NEW_JIT_ICALL_ADDRCONST (cfg, ins, (char*)callinfo->name);
12014 inline_costs += 10 * num_calls++;
12017 case CEE_MONO_ICALL_ADDR: {
12018 MonoMethod *cmethod;
12021 CHECK_STACK_OVF (1);
12023 token = read32 (ip + 2);
12025 cmethod = mono_method_get_wrapper_data (method, token);
12027 if (cfg->compile_aot) {
12028 EMIT_NEW_AOTCONST (cfg, ins, MONO_PATCH_INFO_ICALL_ADDR, cmethod);
12030 ptr = mono_lookup_internal_call (cmethod);
12032 EMIT_NEW_PCONST (cfg, ins, ptr);
12038 case CEE_MONO_VTADDR: {
12039 MonoInst *src_var, *src;
12045 src_var = get_vreg_to_inst (cfg, sp [0]->dreg);
12046 EMIT_NEW_VARLOADA ((cfg), (src), src_var, src_var->inst_vtype);
12051 case CEE_MONO_NEWOBJ: {
12052 MonoInst *iargs [2];
12054 CHECK_STACK_OVF (1);
12056 token = read32 (ip + 2);
12057 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
12058 mono_class_init (klass);
12059 NEW_DOMAINCONST (cfg, iargs [0]);
12060 MONO_ADD_INS (cfg->cbb, iargs [0]);
12061 NEW_CLASSCONST (cfg, iargs [1], klass);
12062 MONO_ADD_INS (cfg->cbb, iargs [1]);
12063 *sp++ = mono_emit_jit_icall (cfg, mono_object_new, iargs);
12065 inline_costs += 10 * num_calls++;
12068 case CEE_MONO_OBJADDR:
12071 MONO_INST_NEW (cfg, ins, OP_MOVE);
12072 ins->dreg = alloc_ireg_mp (cfg);
12073 ins->sreg1 = sp [0]->dreg;
12074 ins->type = STACK_MP;
12075 MONO_ADD_INS (cfg->cbb, ins);
12079 case CEE_MONO_LDNATIVEOBJ:
12081 * Similar to LDOBJ, but instead load the unmanaged
12082 * representation of the vtype to the stack.
12087 token = read32 (ip + 2);
12088 klass = mono_method_get_wrapper_data (method, token);
12089 g_assert (klass->valuetype);
12090 mono_class_init (klass);
12093 MonoInst *src, *dest, *temp;
12096 temp = mono_compile_create_var (cfg, &klass->byval_arg, OP_LOCAL);
12097 temp->backend.is_pinvoke = 1;
12098 EMIT_NEW_TEMPLOADA (cfg, dest, temp->inst_c0);
12099 mini_emit_stobj (cfg, dest, src, klass, TRUE);
12101 EMIT_NEW_TEMPLOAD (cfg, dest, temp->inst_c0);
12102 dest->type = STACK_VTYPE;
12103 dest->klass = klass;
12109 case CEE_MONO_RETOBJ: {
12111 * Same as RET, but return the native representation of a vtype
12114 g_assert (cfg->ret);
12115 g_assert (mono_method_signature (method)->pinvoke);
12120 token = read32 (ip + 2);
12121 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
12123 if (!cfg->vret_addr) {
12124 g_assert (cfg->ret_var_is_local);
12126 EMIT_NEW_VARLOADA (cfg, ins, cfg->ret, cfg->ret->inst_vtype);
12128 EMIT_NEW_RETLOADA (cfg, ins);
12130 mini_emit_stobj (cfg, ins, sp [0], klass, TRUE);
12132 if (sp != stack_start)
12135 MONO_INST_NEW (cfg, ins, OP_BR);
12136 ins->inst_target_bb = end_bblock;
12137 MONO_ADD_INS (bblock, ins);
12138 link_bblock (cfg, bblock, end_bblock);
12139 start_new_bblock = 1;
12143 case CEE_MONO_CISINST:
12144 case CEE_MONO_CCASTCLASS: {
12149 token = read32 (ip + 2);
12150 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
12151 if (ip [1] == CEE_MONO_CISINST)
12152 ins = handle_cisinst (cfg, klass, sp [0]);
12154 ins = handle_ccastclass (cfg, klass, sp [0]);
12160 case CEE_MONO_SAVE_LMF:
12161 case CEE_MONO_RESTORE_LMF:
12162 #ifdef MONO_ARCH_HAVE_LMF_OPS
12163 MONO_INST_NEW (cfg, ins, (ip [1] == CEE_MONO_SAVE_LMF) ? OP_SAVE_LMF : OP_RESTORE_LMF);
12164 MONO_ADD_INS (bblock, ins);
12165 cfg->need_lmf_area = TRUE;
12169 case CEE_MONO_CLASSCONST:
12170 CHECK_STACK_OVF (1);
12172 token = read32 (ip + 2);
12173 EMIT_NEW_CLASSCONST (cfg, ins, mono_method_get_wrapper_data (method, token));
12176 inline_costs += 10 * num_calls++;
12178 case CEE_MONO_NOT_TAKEN:
12179 bblock->out_of_line = TRUE;
12182 case CEE_MONO_TLS: {
12185 CHECK_STACK_OVF (1);
12187 key = (gint32)read32 (ip + 2);
12188 g_assert (key < TLS_KEY_NUM);
12190 ins = mono_create_tls_get (cfg, key);
12192 if (cfg->compile_aot) {
12194 MONO_INST_NEW (cfg, ins, OP_TLS_GET);
12195 ins->dreg = alloc_preg (cfg);
12196 ins->type = STACK_PTR;
12198 g_assert_not_reached ();
12201 ins->type = STACK_PTR;
12202 MONO_ADD_INS (bblock, ins);
12207 case CEE_MONO_DYN_CALL: {
12208 MonoCallInst *call;
12210 /* It would be easier to call a trampoline, but that would put an
12211 * extra frame on the stack, confusing exception handling. So
12212 * implement it inline using an opcode for now.
12215 if (!cfg->dyn_call_var) {
12216 cfg->dyn_call_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
12217 /* prevent it from being register allocated */
12218 cfg->dyn_call_var->flags |= MONO_INST_VOLATILE;
12221 /* Has to use a call inst since it local regalloc expects it */
12222 MONO_INST_NEW_CALL (cfg, call, OP_DYN_CALL);
12223 ins = (MonoInst*)call;
12225 ins->sreg1 = sp [0]->dreg;
12226 ins->sreg2 = sp [1]->dreg;
12227 MONO_ADD_INS (bblock, ins);
12229 cfg->param_area = MAX (cfg->param_area, MONO_ARCH_DYN_CALL_PARAM_AREA);
12232 inline_costs += 10 * num_calls++;
12236 case CEE_MONO_MEMORY_BARRIER: {
12238 emit_memory_barrier (cfg, (int)read32 (ip + 2));
12242 case CEE_MONO_JIT_ATTACH: {
12243 MonoInst *args [16], *domain_ins;
12244 MonoInst *ad_ins, *jit_tls_ins;
12245 MonoBasicBlock *next_bb = NULL, *call_bb = NULL;
12247 cfg->orig_domain_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
12249 EMIT_NEW_PCONST (cfg, ins, NULL);
12250 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, cfg->orig_domain_var->dreg, ins->dreg);
12252 ad_ins = mono_get_domain_intrinsic (cfg);
12253 jit_tls_ins = mono_get_jit_tls_intrinsic (cfg);
12255 if (MONO_ARCH_HAVE_TLS_GET && ad_ins && jit_tls_ins) {
12256 NEW_BBLOCK (cfg, next_bb);
12257 NEW_BBLOCK (cfg, call_bb);
12259 if (cfg->compile_aot) {
12260 /* AOT code is only used in the root domain */
12261 EMIT_NEW_PCONST (cfg, domain_ins, NULL);
12263 EMIT_NEW_PCONST (cfg, domain_ins, cfg->domain);
12265 MONO_ADD_INS (cfg->cbb, ad_ins);
12266 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, ad_ins->dreg, domain_ins->dreg);
12267 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, call_bb);
12269 MONO_ADD_INS (cfg->cbb, jit_tls_ins);
12270 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, jit_tls_ins->dreg, 0);
12271 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, call_bb);
12273 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, next_bb);
12274 MONO_START_BB (cfg, call_bb);
12277 if (cfg->compile_aot) {
12278 /* AOT code is only used in the root domain */
12279 EMIT_NEW_PCONST (cfg, args [0], NULL);
12281 EMIT_NEW_PCONST (cfg, args [0], cfg->domain);
12283 ins = mono_emit_jit_icall (cfg, mono_jit_thread_attach, args);
12284 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, cfg->orig_domain_var->dreg, ins->dreg);
12287 MONO_START_BB (cfg, next_bb);
12293 case CEE_MONO_JIT_DETACH: {
12294 MonoInst *args [16];
12296 /* Restore the original domain */
12297 dreg = alloc_ireg (cfg);
12298 EMIT_NEW_UNALU (cfg, args [0], OP_MOVE, dreg, cfg->orig_domain_var->dreg);
12299 mono_emit_jit_icall (cfg, mono_jit_set_domain, args);
12304 g_error ("opcode 0x%02x 0x%02x not handled", MONO_CUSTOM_PREFIX, ip [1]);
12310 case CEE_PREFIX1: {
12313 case CEE_ARGLIST: {
12314 /* somewhat similar to LDTOKEN */
12315 MonoInst *addr, *vtvar;
12316 CHECK_STACK_OVF (1);
12317 vtvar = mono_compile_create_var (cfg, &mono_defaults.argumenthandle_class->byval_arg, OP_LOCAL);
12319 EMIT_NEW_TEMPLOADA (cfg, addr, vtvar->inst_c0);
12320 EMIT_NEW_UNALU (cfg, ins, OP_ARGLIST, -1, addr->dreg);
12322 EMIT_NEW_TEMPLOAD (cfg, ins, vtvar->inst_c0);
12323 ins->type = STACK_VTYPE;
12324 ins->klass = mono_defaults.argumenthandle_class;
12334 MonoInst *cmp, *arg1, *arg2;
12342 * The following transforms:
12343 * CEE_CEQ into OP_CEQ
12344 * CEE_CGT into OP_CGT
12345 * CEE_CGT_UN into OP_CGT_UN
12346 * CEE_CLT into OP_CLT
12347 * CEE_CLT_UN into OP_CLT_UN
12349 MONO_INST_NEW (cfg, cmp, (OP_CEQ - CEE_CEQ) + ip [1]);
12351 MONO_INST_NEW (cfg, ins, cmp->opcode);
12352 cmp->sreg1 = arg1->dreg;
12353 cmp->sreg2 = arg2->dreg;
12354 type_from_op (cfg, cmp, arg1, arg2);
12356 add_widen_op (cfg, cmp, &arg1, &arg2);
12357 if ((arg1->type == STACK_I8) || ((SIZEOF_VOID_P == 8) && ((arg1->type == STACK_PTR) || (arg1->type == STACK_OBJ) || (arg1->type == STACK_MP))))
12358 cmp->opcode = OP_LCOMPARE;
12359 else if (arg1->type == STACK_R4)
12360 cmp->opcode = OP_RCOMPARE;
12361 else if (arg1->type == STACK_R8)
12362 cmp->opcode = OP_FCOMPARE;
12364 cmp->opcode = OP_ICOMPARE;
12365 MONO_ADD_INS (bblock, cmp);
12366 ins->type = STACK_I4;
12367 ins->dreg = alloc_dreg (cfg, ins->type);
12368 type_from_op (cfg, ins, arg1, arg2);
12370 if (cmp->opcode == OP_FCOMPARE || cmp->opcode == OP_RCOMPARE) {
12372 * The backends expect the fceq opcodes to do the
12375 ins->sreg1 = cmp->sreg1;
12376 ins->sreg2 = cmp->sreg2;
12379 MONO_ADD_INS (bblock, ins);
12385 MonoInst *argconst;
12386 MonoMethod *cil_method;
12388 CHECK_STACK_OVF (1);
12390 n = read32 (ip + 2);
12391 cmethod = mini_get_method (cfg, method, n, NULL, generic_context);
12392 if (!cmethod || mono_loader_get_last_error ())
12394 mono_class_init (cmethod->klass);
12396 mono_save_token_info (cfg, image, n, cmethod);
12398 context_used = mini_method_check_context_used (cfg, cmethod);
12400 cil_method = cmethod;
12401 if (!dont_verify && !cfg->skip_visibility && !mono_method_can_access_method (method, cmethod))
12402 METHOD_ACCESS_FAILURE (method, cil_method);
12404 if (mono_security_core_clr_enabled ())
12405 ensure_method_is_allowed_to_call_method (cfg, method, cmethod, bblock, ip);
12408 * Optimize the common case of ldftn+delegate creation
12410 if ((sp > stack_start) && (ip + 6 + 5 < end) && ip_in_bb (cfg, bblock, ip + 6) && (ip [6] == CEE_NEWOBJ)) {
12411 MonoMethod *ctor_method = mini_get_method (cfg, method, read32 (ip + 7), NULL, generic_context);
12412 if (ctor_method && (ctor_method->klass->parent == mono_defaults.multicastdelegate_class)) {
12413 MonoInst *target_ins, *handle_ins;
12414 MonoMethod *invoke;
12415 int invoke_context_used;
12417 invoke = mono_get_delegate_invoke (ctor_method->klass);
12418 if (!invoke || !mono_method_signature (invoke))
12421 invoke_context_used = mini_method_check_context_used (cfg, invoke);
12423 target_ins = sp [-1];
12425 if (mono_security_core_clr_enabled ())
12426 ensure_method_is_allowed_to_call_method (cfg, method, ctor_method, bblock, ip);
12428 if (!(cmethod->flags & METHOD_ATTRIBUTE_STATIC)) {
12429 /*LAME IMPL: We must not add a null check for virtual invoke delegates.*/
12430 if (mono_method_signature (invoke)->param_count == mono_method_signature (cmethod)->param_count) {
12431 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, target_ins->dreg, 0);
12432 MONO_EMIT_NEW_COND_EXC (cfg, EQ, "ArgumentException");
12436 /* FIXME: SGEN support */
12437 if (invoke_context_used == 0) {
12439 if (cfg->verbose_level > 3)
12440 g_print ("converting (in B%d: stack: %d) %s", bblock->block_num, (int)(sp - stack_start), mono_disasm_code_one (NULL, method, ip, NULL));
12441 if ((handle_ins = handle_delegate_ctor (cfg, ctor_method->klass, target_ins, cmethod, context_used, FALSE))) {
12444 CHECK_CFG_EXCEPTION;
12454 argconst = emit_get_rgctx_method (cfg, context_used, cmethod, MONO_RGCTX_INFO_METHOD);
12455 ins = mono_emit_jit_icall (cfg, mono_ldftn, &argconst);
12459 inline_costs += 10 * num_calls++;
12462 case CEE_LDVIRTFTN: {
12463 MonoInst *args [2];
12467 n = read32 (ip + 2);
12468 cmethod = mini_get_method (cfg, method, n, NULL, generic_context);
12469 if (!cmethod || mono_loader_get_last_error ())
12471 mono_class_init (cmethod->klass);
12473 context_used = mini_method_check_context_used (cfg, cmethod);
12475 if (mono_security_core_clr_enabled ())
12476 ensure_method_is_allowed_to_call_method (cfg, method, cmethod, bblock, ip);
12479 * Optimize the common case of ldvirtftn+delegate creation
12481 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)) {
12482 MonoMethod *ctor_method = mini_get_method (cfg, method, read32 (ip + 7), NULL, generic_context);
12483 if (ctor_method && (ctor_method->klass->parent == mono_defaults.multicastdelegate_class)) {
12484 MonoInst *target_ins, *handle_ins;
12485 MonoMethod *invoke;
12486 int invoke_context_used;
12487 gboolean is_virtual = cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL;
12489 invoke = mono_get_delegate_invoke (ctor_method->klass);
12490 if (!invoke || !mono_method_signature (invoke))
12493 invoke_context_used = mini_method_check_context_used (cfg, invoke);
12495 target_ins = sp [-1];
12497 if (mono_security_core_clr_enabled ())
12498 ensure_method_is_allowed_to_call_method (cfg, method, ctor_method, bblock, ip);
12500 /* FIXME: SGEN support */
12501 if (invoke_context_used == 0) {
12503 if (cfg->verbose_level > 3)
12504 g_print ("converting (in B%d: stack: %d) %s", bblock->block_num, (int)(sp - stack_start), mono_disasm_code_one (NULL, method, ip, NULL));
12505 if ((handle_ins = handle_delegate_ctor (cfg, ctor_method->klass, target_ins, cmethod, context_used, is_virtual))) {
12508 CHECK_CFG_EXCEPTION;
12521 args [1] = emit_get_rgctx_method (cfg, context_used,
12522 cmethod, MONO_RGCTX_INFO_METHOD);
12525 *sp++ = mono_emit_jit_icall (cfg, mono_ldvirtfn_gshared, args);
12527 *sp++ = mono_emit_jit_icall (cfg, mono_ldvirtfn, args);
12530 inline_costs += 10 * num_calls++;
12534 CHECK_STACK_OVF (1);
12536 n = read16 (ip + 2);
12538 EMIT_NEW_ARGLOAD (cfg, ins, n);
12543 CHECK_STACK_OVF (1);
12545 n = read16 (ip + 2);
12547 NEW_ARGLOADA (cfg, ins, n);
12548 MONO_ADD_INS (cfg->cbb, ins);
12556 n = read16 (ip + 2);
12558 if (!dont_verify_stloc && target_type_is_incompatible (cfg, param_types [n], *sp))
12560 EMIT_NEW_ARGSTORE (cfg, ins, n, *sp);
12564 CHECK_STACK_OVF (1);
12566 n = read16 (ip + 2);
12568 EMIT_NEW_LOCLOAD (cfg, ins, n);
12573 unsigned char *tmp_ip;
12574 CHECK_STACK_OVF (1);
12576 n = read16 (ip + 2);
12579 if ((tmp_ip = emit_optimized_ldloca_ir (cfg, ip, end, 2))) {
12585 EMIT_NEW_LOCLOADA (cfg, ins, n);
12594 n = read16 (ip + 2);
12596 if (!dont_verify_stloc && target_type_is_incompatible (cfg, header->locals [n], *sp))
12598 emit_stloc_ir (cfg, sp, header, n);
12605 if (sp != stack_start)
12607 if (cfg->method != method)
12609 * Inlining this into a loop in a parent could lead to
12610 * stack overflows which is different behavior than the
12611 * non-inlined case, thus disable inlining in this case.
12613 INLINE_FAILURE("localloc");
12615 MONO_INST_NEW (cfg, ins, OP_LOCALLOC);
12616 ins->dreg = alloc_preg (cfg);
12617 ins->sreg1 = sp [0]->dreg;
12618 ins->type = STACK_PTR;
12619 MONO_ADD_INS (cfg->cbb, ins);
12621 cfg->flags |= MONO_CFG_HAS_ALLOCA;
12623 ins->flags |= MONO_INST_INIT;
12628 case CEE_ENDFILTER: {
12629 MonoExceptionClause *clause, *nearest;
12634 if ((sp != stack_start) || (sp [0]->type != STACK_I4))
12636 MONO_INST_NEW (cfg, ins, OP_ENDFILTER);
12637 ins->sreg1 = (*sp)->dreg;
12638 MONO_ADD_INS (bblock, ins);
12639 start_new_bblock = 1;
12643 for (cc = 0; cc < header->num_clauses; ++cc) {
12644 clause = &header->clauses [cc];
12645 if ((clause->flags & MONO_EXCEPTION_CLAUSE_FILTER) &&
12646 ((ip - header->code) > clause->data.filter_offset && (ip - header->code) <= clause->handler_offset) &&
12647 (!nearest || (clause->data.filter_offset < nearest->data.filter_offset)))
12650 g_assert (nearest);
12651 if ((ip - header->code) != nearest->handler_offset)
12656 case CEE_UNALIGNED_:
12657 ins_flag |= MONO_INST_UNALIGNED;
12658 /* FIXME: record alignment? we can assume 1 for now */
12662 case CEE_VOLATILE_:
12663 ins_flag |= MONO_INST_VOLATILE;
12667 ins_flag |= MONO_INST_TAILCALL;
12668 cfg->flags |= MONO_CFG_HAS_TAIL;
12669 /* Can't inline tail calls at this time */
12670 inline_costs += 100000;
12677 token = read32 (ip + 2);
12678 klass = mini_get_class (method, token, generic_context);
12679 CHECK_TYPELOAD (klass);
12680 if (generic_class_is_reference_type (cfg, klass))
12681 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STORE_MEMBASE_IMM, sp [0]->dreg, 0, 0);
12683 mini_emit_initobj (cfg, *sp, NULL, klass);
12687 case CEE_CONSTRAINED_:
12689 token = read32 (ip + 2);
12690 constrained_class = mini_get_class (method, token, generic_context);
12691 CHECK_TYPELOAD (constrained_class);
12695 case CEE_INITBLK: {
12696 MonoInst *iargs [3];
12700 /* Skip optimized paths for volatile operations. */
12701 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)) {
12702 mini_emit_memcpy (cfg, sp [0]->dreg, 0, sp [1]->dreg, 0, sp [2]->inst_c0, 0);
12703 } 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)) {
12704 /* emit_memset only works when val == 0 */
12705 mini_emit_memset (cfg, sp [0]->dreg, 0, sp [2]->inst_c0, sp [1]->inst_c0, 0);
12708 iargs [0] = sp [0];
12709 iargs [1] = sp [1];
12710 iargs [2] = sp [2];
12711 if (ip [1] == CEE_CPBLK) {
12713 * FIXME: It's unclear whether we should be emitting both the acquire
12714 * and release barriers for cpblk. It is technically both a load and
12715 * store operation, so it seems like that's the sensible thing to do.
12717 * FIXME: We emit full barriers on both sides of the operation for
12718 * simplicity. We should have a separate atomic memcpy method instead.
12720 MonoMethod *memcpy_method = get_memcpy_method ();
12722 if (ins_flag & MONO_INST_VOLATILE)
12723 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_SEQ);
12725 call = mono_emit_method_call (cfg, memcpy_method, iargs, NULL);
12726 call->flags |= ins_flag;
12728 if (ins_flag & MONO_INST_VOLATILE)
12729 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_SEQ);
12731 MonoMethod *memset_method = get_memset_method ();
12732 if (ins_flag & MONO_INST_VOLATILE) {
12733 /* Volatile stores have release semantics, see 12.6.7 in Ecma 335 */
12734 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_REL);
12736 call = mono_emit_method_call (cfg, memset_method, iargs, NULL);
12737 call->flags |= ins_flag;
12748 ins_flag |= MONO_INST_NOTYPECHECK;
12750 ins_flag |= MONO_INST_NORANGECHECK;
12751 /* we ignore the no-nullcheck for now since we
12752 * really do it explicitly only when doing callvirt->call
12756 case CEE_RETHROW: {
12758 int handler_offset = -1;
12760 for (i = 0; i < header->num_clauses; ++i) {
12761 MonoExceptionClause *clause = &header->clauses [i];
12762 if (MONO_OFFSET_IN_HANDLER (clause, ip - header->code) && !(clause->flags & MONO_EXCEPTION_CLAUSE_FINALLY)) {
12763 handler_offset = clause->handler_offset;
12768 bblock->flags |= BB_EXCEPTION_UNSAFE;
12770 if (handler_offset == -1)
12773 EMIT_NEW_TEMPLOAD (cfg, load, mono_find_exvar_for_offset (cfg, handler_offset)->inst_c0);
12774 MONO_INST_NEW (cfg, ins, OP_RETHROW);
12775 ins->sreg1 = load->dreg;
12776 MONO_ADD_INS (bblock, ins);
12778 MONO_INST_NEW (cfg, ins, OP_NOT_REACHED);
12779 MONO_ADD_INS (bblock, ins);
12782 link_bblock (cfg, bblock, end_bblock);
12783 start_new_bblock = 1;
12791 CHECK_STACK_OVF (1);
12793 token = read32 (ip + 2);
12794 if (mono_metadata_token_table (token) == MONO_TABLE_TYPESPEC && !image_is_dynamic (method->klass->image) && !generic_context) {
12795 MonoType *type = mono_type_create_from_typespec_checked (image, token, &cfg->error);
12798 val = mono_type_size (type, &ialign);
12800 MonoClass *klass = mini_get_class (method, token, generic_context);
12801 CHECK_TYPELOAD (klass);
12803 val = mono_type_size (&klass->byval_arg, &ialign);
12805 if (mini_is_gsharedvt_klass (cfg, klass))
12806 GSHAREDVT_FAILURE (*ip);
12808 EMIT_NEW_ICONST (cfg, ins, val);
12813 case CEE_REFANYTYPE: {
12814 MonoInst *src_var, *src;
12816 GSHAREDVT_FAILURE (*ip);
12822 src_var = get_vreg_to_inst (cfg, sp [0]->dreg);
12824 src_var = mono_compile_create_var_for_vreg (cfg, &mono_defaults.typed_reference_class->byval_arg, OP_LOCAL, sp [0]->dreg);
12825 EMIT_NEW_VARLOADA (cfg, src, src_var, src_var->inst_vtype);
12826 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &mono_defaults.typehandle_class->byval_arg, src->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, type));
12831 case CEE_READONLY_:
12844 g_warning ("opcode 0xfe 0x%02x not handled", ip [1]);
12854 g_warning ("opcode 0x%02x not handled", *ip);
12858 if (start_new_bblock != 1)
12861 bblock->cil_length = ip - bblock->cil_code;
12862 if (bblock->next_bb) {
12863 /* This could already be set because of inlining, #693905 */
12864 MonoBasicBlock *bb = bblock;
12866 while (bb->next_bb)
12868 bb->next_bb = end_bblock;
12870 bblock->next_bb = end_bblock;
12873 if (cfg->method == method && cfg->domainvar) {
12875 MonoInst *get_domain;
12877 cfg->cbb = init_localsbb;
12879 if ((get_domain = mono_get_domain_intrinsic (cfg))) {
12880 MONO_ADD_INS (cfg->cbb, get_domain);
12882 get_domain = mono_emit_jit_icall (cfg, mono_domain_get, NULL);
12884 NEW_TEMPSTORE (cfg, store, cfg->domainvar->inst_c0, get_domain);
12885 MONO_ADD_INS (cfg->cbb, store);
12888 #if defined(TARGET_POWERPC) || defined(TARGET_X86)
12889 if (cfg->compile_aot)
12890 /* FIXME: The plt slots require a GOT var even if the method doesn't use it */
12891 mono_get_got_var (cfg);
12894 if (cfg->method == method && cfg->got_var)
12895 mono_emit_load_got_addr (cfg);
12897 if (init_localsbb) {
12898 cfg->cbb = init_localsbb;
12900 for (i = 0; i < header->num_locals; ++i) {
12901 emit_init_local (cfg, i, header->locals [i], init_locals);
12905 if (cfg->init_ref_vars && cfg->method == method) {
12906 /* Emit initialization for ref vars */
12907 // FIXME: Avoid duplication initialization for IL locals.
12908 for (i = 0; i < cfg->num_varinfo; ++i) {
12909 MonoInst *ins = cfg->varinfo [i];
12911 if (ins->opcode == OP_LOCAL && ins->type == STACK_OBJ)
12912 MONO_EMIT_NEW_PCONST (cfg, ins->dreg, NULL);
12916 if (cfg->lmf_var && cfg->method == method) {
12917 cfg->cbb = init_localsbb;
12918 emit_push_lmf (cfg);
12921 cfg->cbb = init_localsbb;
12922 emit_instrumentation_call (cfg, mono_profiler_method_enter);
12925 MonoBasicBlock *bb;
12928 * Make seq points at backward branch targets interruptable.
12930 for (bb = cfg->bb_entry; bb; bb = bb->next_bb)
12931 if (bb->code && bb->in_count > 1 && bb->code->opcode == OP_SEQ_POINT)
12932 bb->code->flags |= MONO_INST_SINGLE_STEP_LOC;
12935 /* Add a sequence point for method entry/exit events */
12936 if (seq_points && cfg->gen_sdb_seq_points) {
12937 NEW_SEQ_POINT (cfg, ins, METHOD_ENTRY_IL_OFFSET, FALSE);
12938 MONO_ADD_INS (init_localsbb, ins);
12939 NEW_SEQ_POINT (cfg, ins, METHOD_EXIT_IL_OFFSET, FALSE);
12940 MONO_ADD_INS (cfg->bb_exit, ins);
12944 * Add seq points for IL offsets which have line number info, but wasn't generated a seq point during JITting because
12945 * the code they refer to was dead (#11880).
12947 if (sym_seq_points) {
12948 for (i = 0; i < header->code_size; ++i) {
12949 if (mono_bitset_test_fast (seq_point_locs, i) && !mono_bitset_test_fast (seq_point_set_locs, i)) {
12952 NEW_SEQ_POINT (cfg, ins, i, FALSE);
12953 mono_add_seq_point (cfg, NULL, ins, SEQ_POINT_NATIVE_OFFSET_DEAD_CODE);
12960 if (cfg->method == method) {
12961 MonoBasicBlock *bb;
12962 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
12963 bb->region = mono_find_block_region (cfg, bb->real_offset);
12965 mono_create_spvar_for_region (cfg, bb->region);
12966 if (cfg->verbose_level > 2)
12967 printf ("REGION BB%d IL_%04x ID_%08X\n", bb->block_num, bb->real_offset, bb->region);
12971 if (inline_costs < 0) {
12974 /* Method is too large */
12975 mname = mono_method_full_name (method, TRUE);
12976 mono_cfg_set_exception (cfg, MONO_EXCEPTION_INVALID_PROGRAM);
12977 cfg->exception_message = g_strdup_printf ("Method %s is too complex.", mname);
12981 if ((cfg->verbose_level > 2) && (cfg->method == method))
12982 mono_print_code (cfg, "AFTER METHOD-TO-IR");
12987 g_assert (!mono_error_ok (&cfg->error));
12991 g_assert (cfg->exception_type != MONO_EXCEPTION_NONE);
12995 set_exception_type_from_invalid_il (cfg, method, ip);
12999 g_slist_free (class_inits);
13000 mono_basic_block_free (original_bb);
13001 cfg->dont_inline = g_list_remove (cfg->dont_inline, method);
13002 cfg->headers_to_free = g_slist_prepend_mempool (cfg->mempool, cfg->headers_to_free, header);
13003 if (cfg->exception_type)
13006 return inline_costs;
13010 store_membase_reg_to_store_membase_imm (int opcode)
13013 case OP_STORE_MEMBASE_REG:
13014 return OP_STORE_MEMBASE_IMM;
13015 case OP_STOREI1_MEMBASE_REG:
13016 return OP_STOREI1_MEMBASE_IMM;
13017 case OP_STOREI2_MEMBASE_REG:
13018 return OP_STOREI2_MEMBASE_IMM;
13019 case OP_STOREI4_MEMBASE_REG:
13020 return OP_STOREI4_MEMBASE_IMM;
13021 case OP_STOREI8_MEMBASE_REG:
13022 return OP_STOREI8_MEMBASE_IMM;
13024 g_assert_not_reached ();
13031 mono_op_to_op_imm (int opcode)
13035 return OP_IADD_IMM;
13037 return OP_ISUB_IMM;
13039 return OP_IDIV_IMM;
13041 return OP_IDIV_UN_IMM;
13043 return OP_IREM_IMM;
13045 return OP_IREM_UN_IMM;
13047 return OP_IMUL_IMM;
13049 return OP_IAND_IMM;
13053 return OP_IXOR_IMM;
13055 return OP_ISHL_IMM;
13057 return OP_ISHR_IMM;
13059 return OP_ISHR_UN_IMM;
13062 return OP_LADD_IMM;
13064 return OP_LSUB_IMM;
13066 return OP_LAND_IMM;
13070 return OP_LXOR_IMM;
13072 return OP_LSHL_IMM;
13074 return OP_LSHR_IMM;
13076 return OP_LSHR_UN_IMM;
13077 #if SIZEOF_REGISTER == 8
13079 return OP_LREM_IMM;
13083 return OP_COMPARE_IMM;
13085 return OP_ICOMPARE_IMM;
13087 return OP_LCOMPARE_IMM;
13089 case OP_STORE_MEMBASE_REG:
13090 return OP_STORE_MEMBASE_IMM;
13091 case OP_STOREI1_MEMBASE_REG:
13092 return OP_STOREI1_MEMBASE_IMM;
13093 case OP_STOREI2_MEMBASE_REG:
13094 return OP_STOREI2_MEMBASE_IMM;
13095 case OP_STOREI4_MEMBASE_REG:
13096 return OP_STOREI4_MEMBASE_IMM;
13098 #if defined(TARGET_X86) || defined (TARGET_AMD64)
13100 return OP_X86_PUSH_IMM;
13101 case OP_X86_COMPARE_MEMBASE_REG:
13102 return OP_X86_COMPARE_MEMBASE_IMM;
13104 #if defined(TARGET_AMD64)
13105 case OP_AMD64_ICOMPARE_MEMBASE_REG:
13106 return OP_AMD64_ICOMPARE_MEMBASE_IMM;
13108 case OP_VOIDCALL_REG:
13109 return OP_VOIDCALL;
13117 return OP_LOCALLOC_IMM;
13124 ldind_to_load_membase (int opcode)
13128 return OP_LOADI1_MEMBASE;
13130 return OP_LOADU1_MEMBASE;
13132 return OP_LOADI2_MEMBASE;
13134 return OP_LOADU2_MEMBASE;
13136 return OP_LOADI4_MEMBASE;
13138 return OP_LOADU4_MEMBASE;
13140 return OP_LOAD_MEMBASE;
13141 case CEE_LDIND_REF:
13142 return OP_LOAD_MEMBASE;
13144 return OP_LOADI8_MEMBASE;
13146 return OP_LOADR4_MEMBASE;
13148 return OP_LOADR8_MEMBASE;
13150 g_assert_not_reached ();
13157 stind_to_store_membase (int opcode)
13161 return OP_STOREI1_MEMBASE_REG;
13163 return OP_STOREI2_MEMBASE_REG;
13165 return OP_STOREI4_MEMBASE_REG;
13167 case CEE_STIND_REF:
13168 return OP_STORE_MEMBASE_REG;
13170 return OP_STOREI8_MEMBASE_REG;
13172 return OP_STORER4_MEMBASE_REG;
13174 return OP_STORER8_MEMBASE_REG;
13176 g_assert_not_reached ();
13183 mono_load_membase_to_load_mem (int opcode)
13185 // FIXME: Add a MONO_ARCH_HAVE_LOAD_MEM macro
13186 #if defined(TARGET_X86) || defined(TARGET_AMD64)
13188 case OP_LOAD_MEMBASE:
13189 return OP_LOAD_MEM;
13190 case OP_LOADU1_MEMBASE:
13191 return OP_LOADU1_MEM;
13192 case OP_LOADU2_MEMBASE:
13193 return OP_LOADU2_MEM;
13194 case OP_LOADI4_MEMBASE:
13195 return OP_LOADI4_MEM;
13196 case OP_LOADU4_MEMBASE:
13197 return OP_LOADU4_MEM;
13198 #if SIZEOF_REGISTER == 8
13199 case OP_LOADI8_MEMBASE:
13200 return OP_LOADI8_MEM;
13209 op_to_op_dest_membase (int store_opcode, int opcode)
13211 #if defined(TARGET_X86)
13212 if (!((store_opcode == OP_STORE_MEMBASE_REG) || (store_opcode == OP_STOREI4_MEMBASE_REG)))
13217 return OP_X86_ADD_MEMBASE_REG;
13219 return OP_X86_SUB_MEMBASE_REG;
13221 return OP_X86_AND_MEMBASE_REG;
13223 return OP_X86_OR_MEMBASE_REG;
13225 return OP_X86_XOR_MEMBASE_REG;
13228 return OP_X86_ADD_MEMBASE_IMM;
13231 return OP_X86_SUB_MEMBASE_IMM;
13234 return OP_X86_AND_MEMBASE_IMM;
13237 return OP_X86_OR_MEMBASE_IMM;
13240 return OP_X86_XOR_MEMBASE_IMM;
13246 #if defined(TARGET_AMD64)
13247 if (!((store_opcode == OP_STORE_MEMBASE_REG) || (store_opcode == OP_STOREI4_MEMBASE_REG) || (store_opcode == OP_STOREI8_MEMBASE_REG)))
13252 return OP_X86_ADD_MEMBASE_REG;
13254 return OP_X86_SUB_MEMBASE_REG;
13256 return OP_X86_AND_MEMBASE_REG;
13258 return OP_X86_OR_MEMBASE_REG;
13260 return OP_X86_XOR_MEMBASE_REG;
13262 return OP_X86_ADD_MEMBASE_IMM;
13264 return OP_X86_SUB_MEMBASE_IMM;
13266 return OP_X86_AND_MEMBASE_IMM;
13268 return OP_X86_OR_MEMBASE_IMM;
13270 return OP_X86_XOR_MEMBASE_IMM;
13272 return OP_AMD64_ADD_MEMBASE_REG;
13274 return OP_AMD64_SUB_MEMBASE_REG;
13276 return OP_AMD64_AND_MEMBASE_REG;
13278 return OP_AMD64_OR_MEMBASE_REG;
13280 return OP_AMD64_XOR_MEMBASE_REG;
13283 return OP_AMD64_ADD_MEMBASE_IMM;
13286 return OP_AMD64_SUB_MEMBASE_IMM;
13289 return OP_AMD64_AND_MEMBASE_IMM;
13292 return OP_AMD64_OR_MEMBASE_IMM;
13295 return OP_AMD64_XOR_MEMBASE_IMM;
13305 op_to_op_store_membase (int store_opcode, int opcode)
13307 #if defined(TARGET_X86) || defined(TARGET_AMD64)
13310 if (store_opcode == OP_STOREI1_MEMBASE_REG)
13311 return OP_X86_SETEQ_MEMBASE;
13313 if (store_opcode == OP_STOREI1_MEMBASE_REG)
13314 return OP_X86_SETNE_MEMBASE;
13322 op_to_op_src1_membase (int load_opcode, int opcode)
13325 /* FIXME: This has sign extension issues */
13327 if ((opcode == OP_ICOMPARE_IMM) && (load_opcode == OP_LOADU1_MEMBASE))
13328 return OP_X86_COMPARE_MEMBASE8_IMM;
13331 if (!((load_opcode == OP_LOAD_MEMBASE) || (load_opcode == OP_LOADI4_MEMBASE) || (load_opcode == OP_LOADU4_MEMBASE)))
13336 return OP_X86_PUSH_MEMBASE;
13337 case OP_COMPARE_IMM:
13338 case OP_ICOMPARE_IMM:
13339 return OP_X86_COMPARE_MEMBASE_IMM;
13342 return OP_X86_COMPARE_MEMBASE_REG;
13346 #ifdef TARGET_AMD64
13347 /* FIXME: This has sign extension issues */
13349 if ((opcode == OP_ICOMPARE_IMM) && (load_opcode == OP_LOADU1_MEMBASE))
13350 return OP_X86_COMPARE_MEMBASE8_IMM;
13355 #ifdef __mono_ilp32__
13356 if (load_opcode == OP_LOADI8_MEMBASE)
13358 if ((load_opcode == OP_LOAD_MEMBASE) || (load_opcode == OP_LOADI8_MEMBASE))
13360 return OP_X86_PUSH_MEMBASE;
13362 /* FIXME: This only works for 32 bit immediates
13363 case OP_COMPARE_IMM:
13364 case OP_LCOMPARE_IMM:
13365 if ((load_opcode == OP_LOAD_MEMBASE) || (load_opcode == OP_LOADI8_MEMBASE))
13366 return OP_AMD64_COMPARE_MEMBASE_IMM;
13368 case OP_ICOMPARE_IMM:
13369 if ((load_opcode == OP_LOADI4_MEMBASE) || (load_opcode == OP_LOADU4_MEMBASE))
13370 return OP_AMD64_ICOMPARE_MEMBASE_IMM;
13374 #ifdef __mono_ilp32__
13375 if (load_opcode == OP_LOAD_MEMBASE)
13376 return OP_AMD64_ICOMPARE_MEMBASE_REG;
13377 if (load_opcode == OP_LOADI8_MEMBASE)
13379 if ((load_opcode == OP_LOAD_MEMBASE) || (load_opcode == OP_LOADI8_MEMBASE))
13381 return OP_AMD64_COMPARE_MEMBASE_REG;
13384 if ((load_opcode == OP_LOADI4_MEMBASE) || (load_opcode == OP_LOADU4_MEMBASE))
13385 return OP_AMD64_ICOMPARE_MEMBASE_REG;
13394 op_to_op_src2_membase (int load_opcode, int opcode)
13397 if (!((load_opcode == OP_LOAD_MEMBASE) || (load_opcode == OP_LOADI4_MEMBASE) || (load_opcode == OP_LOADU4_MEMBASE)))
13403 return OP_X86_COMPARE_REG_MEMBASE;
13405 return OP_X86_ADD_REG_MEMBASE;
13407 return OP_X86_SUB_REG_MEMBASE;
13409 return OP_X86_AND_REG_MEMBASE;
13411 return OP_X86_OR_REG_MEMBASE;
13413 return OP_X86_XOR_REG_MEMBASE;
13417 #ifdef TARGET_AMD64
13418 #ifdef __mono_ilp32__
13419 if ((load_opcode == OP_LOADI4_MEMBASE) || (load_opcode == OP_LOADU4_MEMBASE) || (load_opcode == OP_LOAD_MEMBASE) ) {
13421 if ((load_opcode == OP_LOADI4_MEMBASE) || (load_opcode == OP_LOADU4_MEMBASE)) {
13425 return OP_AMD64_ICOMPARE_REG_MEMBASE;
13427 return OP_X86_ADD_REG_MEMBASE;
13429 return OP_X86_SUB_REG_MEMBASE;
13431 return OP_X86_AND_REG_MEMBASE;
13433 return OP_X86_OR_REG_MEMBASE;
13435 return OP_X86_XOR_REG_MEMBASE;
13437 #ifdef __mono_ilp32__
13438 } else if (load_opcode == OP_LOADI8_MEMBASE) {
13440 } else if ((load_opcode == OP_LOADI8_MEMBASE) || (load_opcode == OP_LOAD_MEMBASE)) {
13445 return OP_AMD64_COMPARE_REG_MEMBASE;
13447 return OP_AMD64_ADD_REG_MEMBASE;
13449 return OP_AMD64_SUB_REG_MEMBASE;
13451 return OP_AMD64_AND_REG_MEMBASE;
13453 return OP_AMD64_OR_REG_MEMBASE;
13455 return OP_AMD64_XOR_REG_MEMBASE;
13464 mono_op_to_op_imm_noemul (int opcode)
13467 #if SIZEOF_REGISTER == 4 && !defined(MONO_ARCH_NO_EMULATE_LONG_SHIFT_OPS)
13473 #if defined(MONO_ARCH_EMULATE_MUL_DIV) || defined(MONO_ARCH_EMULATE_DIV)
13480 #if defined(MONO_ARCH_EMULATE_MUL_DIV)
13485 return mono_op_to_op_imm (opcode);
13490 * mono_handle_global_vregs:
13492 * Make vregs used in more than one bblock 'global', i.e. allocate a variable
13496 mono_handle_global_vregs (MonoCompile *cfg)
13498 gint32 *vreg_to_bb;
13499 MonoBasicBlock *bb;
13502 vreg_to_bb = mono_mempool_alloc0 (cfg->mempool, sizeof (gint32*) * cfg->next_vreg + 1);
13504 #ifdef MONO_ARCH_SIMD_INTRINSICS
13505 if (cfg->uses_simd_intrinsics)
13506 mono_simd_simplify_indirection (cfg);
13509 /* Find local vregs used in more than one bb */
13510 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
13511 MonoInst *ins = bb->code;
13512 int block_num = bb->block_num;
13514 if (cfg->verbose_level > 2)
13515 printf ("\nHANDLE-GLOBAL-VREGS BLOCK %d:\n", bb->block_num);
13518 for (; ins; ins = ins->next) {
13519 const char *spec = INS_INFO (ins->opcode);
13520 int regtype = 0, regindex;
13523 if (G_UNLIKELY (cfg->verbose_level > 2))
13524 mono_print_ins (ins);
13526 g_assert (ins->opcode >= MONO_CEE_LAST);
13528 for (regindex = 0; regindex < 4; regindex ++) {
13531 if (regindex == 0) {
13532 regtype = spec [MONO_INST_DEST];
13533 if (regtype == ' ')
13536 } else if (regindex == 1) {
13537 regtype = spec [MONO_INST_SRC1];
13538 if (regtype == ' ')
13541 } else if (regindex == 2) {
13542 regtype = spec [MONO_INST_SRC2];
13543 if (regtype == ' ')
13546 } else if (regindex == 3) {
13547 regtype = spec [MONO_INST_SRC3];
13548 if (regtype == ' ')
13553 #if SIZEOF_REGISTER == 4
13554 /* In the LLVM case, the long opcodes are not decomposed */
13555 if (regtype == 'l' && !COMPILE_LLVM (cfg)) {
13557 * Since some instructions reference the original long vreg,
13558 * and some reference the two component vregs, it is quite hard
13559 * to determine when it needs to be global. So be conservative.
13561 if (!get_vreg_to_inst (cfg, vreg)) {
13562 mono_compile_create_var_for_vreg (cfg, &mono_defaults.int64_class->byval_arg, OP_LOCAL, vreg);
13564 if (cfg->verbose_level > 2)
13565 printf ("LONG VREG R%d made global.\n", vreg);
13569 * Make the component vregs volatile since the optimizations can
13570 * get confused otherwise.
13572 get_vreg_to_inst (cfg, vreg + 1)->flags |= MONO_INST_VOLATILE;
13573 get_vreg_to_inst (cfg, vreg + 2)->flags |= MONO_INST_VOLATILE;
13577 g_assert (vreg != -1);
13579 prev_bb = vreg_to_bb [vreg];
13580 if (prev_bb == 0) {
13581 /* 0 is a valid block num */
13582 vreg_to_bb [vreg] = block_num + 1;
13583 } else if ((prev_bb != block_num + 1) && (prev_bb != -1)) {
13584 if (((regtype == 'i' && (vreg < MONO_MAX_IREGS))) || (regtype == 'f' && (vreg < MONO_MAX_FREGS)))
13587 if (!get_vreg_to_inst (cfg, vreg)) {
13588 if (G_UNLIKELY (cfg->verbose_level > 2))
13589 printf ("VREG R%d used in BB%d and BB%d made global.\n", vreg, vreg_to_bb [vreg], block_num);
13593 if (vreg_is_ref (cfg, vreg))
13594 mono_compile_create_var_for_vreg (cfg, &mono_defaults.object_class->byval_arg, OP_LOCAL, vreg);
13596 mono_compile_create_var_for_vreg (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL, vreg);
13599 mono_compile_create_var_for_vreg (cfg, &mono_defaults.int64_class->byval_arg, OP_LOCAL, vreg);
13602 mono_compile_create_var_for_vreg (cfg, &mono_defaults.double_class->byval_arg, OP_LOCAL, vreg);
13605 mono_compile_create_var_for_vreg (cfg, &ins->klass->byval_arg, OP_LOCAL, vreg);
13608 g_assert_not_reached ();
13612 /* Flag as having been used in more than one bb */
13613 vreg_to_bb [vreg] = -1;
13619 /* If a variable is used in only one bblock, convert it into a local vreg */
13620 for (i = 0; i < cfg->num_varinfo; i++) {
13621 MonoInst *var = cfg->varinfo [i];
13622 MonoMethodVar *vmv = MONO_VARINFO (cfg, i);
13624 switch (var->type) {
13630 #if SIZEOF_REGISTER == 8
13633 #if !defined(TARGET_X86)
13634 /* Enabling this screws up the fp stack on x86 */
13637 if (mono_arch_is_soft_float ())
13640 /* Arguments are implicitly global */
13641 /* Putting R4 vars into registers doesn't work currently */
13642 /* The gsharedvt vars are implicitly referenced by ldaddr opcodes, but those opcodes are only generated later */
13643 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) {
13645 * Make that the variable's liveness interval doesn't contain a call, since
13646 * that would cause the lvreg to be spilled, making the whole optimization
13649 /* This is too slow for JIT compilation */
13651 if (cfg->compile_aot && vreg_to_bb [var->dreg]) {
13653 int def_index, call_index, ins_index;
13654 gboolean spilled = FALSE;
13659 for (ins = vreg_to_bb [var->dreg]->code; ins; ins = ins->next) {
13660 const char *spec = INS_INFO (ins->opcode);
13662 if ((spec [MONO_INST_DEST] != ' ') && (ins->dreg == var->dreg))
13663 def_index = ins_index;
13665 if (((spec [MONO_INST_SRC1] != ' ') && (ins->sreg1 == var->dreg)) ||
13666 ((spec [MONO_INST_SRC1] != ' ') && (ins->sreg1 == var->dreg))) {
13667 if (call_index > def_index) {
13673 if (MONO_IS_CALL (ins))
13674 call_index = ins_index;
13684 if (G_UNLIKELY (cfg->verbose_level > 2))
13685 printf ("CONVERTED R%d(%d) TO VREG.\n", var->dreg, vmv->idx);
13686 var->flags |= MONO_INST_IS_DEAD;
13687 cfg->vreg_to_inst [var->dreg] = NULL;
13694 * Compress the varinfo and vars tables so the liveness computation is faster and
13695 * takes up less space.
13698 for (i = 0; i < cfg->num_varinfo; ++i) {
13699 MonoInst *var = cfg->varinfo [i];
13700 if (pos < i && cfg->locals_start == i)
13701 cfg->locals_start = pos;
13702 if (!(var->flags & MONO_INST_IS_DEAD)) {
13704 cfg->varinfo [pos] = cfg->varinfo [i];
13705 cfg->varinfo [pos]->inst_c0 = pos;
13706 memcpy (&cfg->vars [pos], &cfg->vars [i], sizeof (MonoMethodVar));
13707 cfg->vars [pos].idx = pos;
13708 #if SIZEOF_REGISTER == 4
13709 if (cfg->varinfo [pos]->type == STACK_I8) {
13710 /* Modify the two component vars too */
13713 var1 = get_vreg_to_inst (cfg, cfg->varinfo [pos]->dreg + 1);
13714 var1->inst_c0 = pos;
13715 var1 = get_vreg_to_inst (cfg, cfg->varinfo [pos]->dreg + 2);
13716 var1->inst_c0 = pos;
13723 cfg->num_varinfo = pos;
13724 if (cfg->locals_start > cfg->num_varinfo)
13725 cfg->locals_start = cfg->num_varinfo;
13729 * mono_spill_global_vars:
13731 * Generate spill code for variables which are not allocated to registers,
13732 * and replace vregs with their allocated hregs. *need_local_opts is set to TRUE if
13733 * code is generated which could be optimized by the local optimization passes.
13736 mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local_opts)
13738 MonoBasicBlock *bb;
13740 int orig_next_vreg;
13741 guint32 *vreg_to_lvreg;
13743 guint32 i, lvregs_len;
13744 gboolean dest_has_lvreg = FALSE;
13745 guint32 stacktypes [128];
13746 MonoInst **live_range_start, **live_range_end;
13747 MonoBasicBlock **live_range_start_bb, **live_range_end_bb;
13748 int *gsharedvt_vreg_to_idx = NULL;
13750 *need_local_opts = FALSE;
13752 memset (spec2, 0, sizeof (spec2));
13754 /* FIXME: Move this function to mini.c */
13755 stacktypes ['i'] = STACK_PTR;
13756 stacktypes ['l'] = STACK_I8;
13757 stacktypes ['f'] = STACK_R8;
13758 #ifdef MONO_ARCH_SIMD_INTRINSICS
13759 stacktypes ['x'] = STACK_VTYPE;
13762 #if SIZEOF_REGISTER == 4
13763 /* Create MonoInsts for longs */
13764 for (i = 0; i < cfg->num_varinfo; i++) {
13765 MonoInst *ins = cfg->varinfo [i];
13767 if ((ins->opcode != OP_REGVAR) && !(ins->flags & MONO_INST_IS_DEAD)) {
13768 switch (ins->type) {
13773 if (ins->type == STACK_R8 && !COMPILE_SOFT_FLOAT (cfg))
13776 g_assert (ins->opcode == OP_REGOFFSET);
13778 tree = get_vreg_to_inst (cfg, ins->dreg + 1);
13780 tree->opcode = OP_REGOFFSET;
13781 tree->inst_basereg = ins->inst_basereg;
13782 tree->inst_offset = ins->inst_offset + MINI_LS_WORD_OFFSET;
13784 tree = get_vreg_to_inst (cfg, ins->dreg + 2);
13786 tree->opcode = OP_REGOFFSET;
13787 tree->inst_basereg = ins->inst_basereg;
13788 tree->inst_offset = ins->inst_offset + MINI_MS_WORD_OFFSET;
13798 if (cfg->compute_gc_maps) {
13799 /* registers need liveness info even for !non refs */
13800 for (i = 0; i < cfg->num_varinfo; i++) {
13801 MonoInst *ins = cfg->varinfo [i];
13803 if (ins->opcode == OP_REGVAR)
13804 ins->flags |= MONO_INST_GC_TRACK;
13808 if (cfg->gsharedvt) {
13809 gsharedvt_vreg_to_idx = mono_mempool_alloc0 (cfg->mempool, sizeof (int) * cfg->next_vreg);
13811 for (i = 0; i < cfg->num_varinfo; ++i) {
13812 MonoInst *ins = cfg->varinfo [i];
13815 if (mini_is_gsharedvt_variable_type (cfg, ins->inst_vtype)) {
13816 if (i >= cfg->locals_start) {
13818 idx = get_gsharedvt_info_slot (cfg, ins->inst_vtype, MONO_RGCTX_INFO_LOCAL_OFFSET);
13819 gsharedvt_vreg_to_idx [ins->dreg] = idx + 1;
13820 ins->opcode = OP_GSHAREDVT_LOCAL;
13821 ins->inst_imm = idx;
13824 gsharedvt_vreg_to_idx [ins->dreg] = -1;
13825 ins->opcode = OP_GSHAREDVT_ARG_REGOFFSET;
13831 /* FIXME: widening and truncation */
13834 * As an optimization, when a variable allocated to the stack is first loaded into
13835 * an lvreg, we will remember the lvreg and use it the next time instead of loading
13836 * the variable again.
13838 orig_next_vreg = cfg->next_vreg;
13839 vreg_to_lvreg = mono_mempool_alloc0 (cfg->mempool, sizeof (guint32) * cfg->next_vreg);
13840 lvregs = mono_mempool_alloc (cfg->mempool, sizeof (guint32) * 1024);
13844 * These arrays contain the first and last instructions accessing a given
13846 * Since we emit bblocks in the same order we process them here, and we
13847 * don't split live ranges, these will precisely describe the live range of
13848 * the variable, i.e. the instruction range where a valid value can be found
13849 * in the variables location.
13850 * The live range is computed using the liveness info computed by the liveness pass.
13851 * We can't use vmv->range, since that is an abstract live range, and we need
13852 * one which is instruction precise.
13853 * FIXME: Variables used in out-of-line bblocks have a hole in their live range.
13855 /* FIXME: Only do this if debugging info is requested */
13856 live_range_start = g_new0 (MonoInst*, cfg->next_vreg);
13857 live_range_end = g_new0 (MonoInst*, cfg->next_vreg);
13858 live_range_start_bb = g_new (MonoBasicBlock*, cfg->next_vreg);
13859 live_range_end_bb = g_new (MonoBasicBlock*, cfg->next_vreg);
13861 /* Add spill loads/stores */
13862 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
13865 if (cfg->verbose_level > 2)
13866 printf ("\nSPILL BLOCK %d:\n", bb->block_num);
13868 /* Clear vreg_to_lvreg array */
13869 for (i = 0; i < lvregs_len; i++)
13870 vreg_to_lvreg [lvregs [i]] = 0;
13874 MONO_BB_FOR_EACH_INS (bb, ins) {
13875 const char *spec = INS_INFO (ins->opcode);
13876 int regtype, srcindex, sreg, tmp_reg, prev_dreg, num_sregs;
13877 gboolean store, no_lvreg;
13878 int sregs [MONO_MAX_SRC_REGS];
13880 if (G_UNLIKELY (cfg->verbose_level > 2))
13881 mono_print_ins (ins);
13883 if (ins->opcode == OP_NOP)
13887 * We handle LDADDR here as well, since it can only be decomposed
13888 * when variable addresses are known.
13890 if (ins->opcode == OP_LDADDR) {
13891 MonoInst *var = ins->inst_p0;
13893 if (var->opcode == OP_VTARG_ADDR) {
13894 /* Happens on SPARC/S390 where vtypes are passed by reference */
13895 MonoInst *vtaddr = var->inst_left;
13896 if (vtaddr->opcode == OP_REGVAR) {
13897 ins->opcode = OP_MOVE;
13898 ins->sreg1 = vtaddr->dreg;
13900 else if (var->inst_left->opcode == OP_REGOFFSET) {
13901 ins->opcode = OP_LOAD_MEMBASE;
13902 ins->inst_basereg = vtaddr->inst_basereg;
13903 ins->inst_offset = vtaddr->inst_offset;
13906 } else if (cfg->gsharedvt && gsharedvt_vreg_to_idx [var->dreg] < 0) {
13907 /* gsharedvt arg passed by ref */
13908 g_assert (var->opcode == OP_GSHAREDVT_ARG_REGOFFSET);
13910 ins->opcode = OP_LOAD_MEMBASE;
13911 ins->inst_basereg = var->inst_basereg;
13912 ins->inst_offset = var->inst_offset;
13913 } else if (cfg->gsharedvt && gsharedvt_vreg_to_idx [var->dreg]) {
13914 MonoInst *load, *load2, *load3;
13915 int idx = gsharedvt_vreg_to_idx [var->dreg] - 1;
13916 int reg1, reg2, reg3;
13917 MonoInst *info_var = cfg->gsharedvt_info_var;
13918 MonoInst *locals_var = cfg->gsharedvt_locals_var;
13922 * Compute the address of the local as gsharedvt_locals_var + gsharedvt_info_var->locals_offsets [idx].
13925 g_assert (var->opcode == OP_GSHAREDVT_LOCAL);
13927 g_assert (info_var);
13928 g_assert (locals_var);
13930 /* Mark the instruction used to compute the locals var as used */
13931 cfg->gsharedvt_locals_var_ins = NULL;
13933 /* Load the offset */
13934 if (info_var->opcode == OP_REGOFFSET) {
13935 reg1 = alloc_ireg (cfg);
13936 NEW_LOAD_MEMBASE (cfg, load, OP_LOAD_MEMBASE, reg1, info_var->inst_basereg, info_var->inst_offset);
13937 } else if (info_var->opcode == OP_REGVAR) {
13939 reg1 = info_var->dreg;
13941 g_assert_not_reached ();
13943 reg2 = alloc_ireg (cfg);
13944 NEW_LOAD_MEMBASE (cfg, load2, OP_LOADI4_MEMBASE, reg2, reg1, MONO_STRUCT_OFFSET (MonoGSharedVtMethodRuntimeInfo, entries) + (idx * sizeof (gpointer)));
13945 /* Load the locals area address */
13946 reg3 = alloc_ireg (cfg);
13947 if (locals_var->opcode == OP_REGOFFSET) {
13948 NEW_LOAD_MEMBASE (cfg, load3, OP_LOAD_MEMBASE, reg3, locals_var->inst_basereg, locals_var->inst_offset);
13949 } else if (locals_var->opcode == OP_REGVAR) {
13950 NEW_UNALU (cfg, load3, OP_MOVE, reg3, locals_var->dreg);
13952 g_assert_not_reached ();
13954 /* Compute the address */
13955 ins->opcode = OP_PADD;
13959 mono_bblock_insert_before_ins (bb, ins, load3);
13960 mono_bblock_insert_before_ins (bb, load3, load2);
13962 mono_bblock_insert_before_ins (bb, load2, load);
13964 g_assert (var->opcode == OP_REGOFFSET);
13966 ins->opcode = OP_ADD_IMM;
13967 ins->sreg1 = var->inst_basereg;
13968 ins->inst_imm = var->inst_offset;
13971 *need_local_opts = TRUE;
13972 spec = INS_INFO (ins->opcode);
13975 if (ins->opcode < MONO_CEE_LAST) {
13976 mono_print_ins (ins);
13977 g_assert_not_reached ();
13981 * Store opcodes have destbasereg in the dreg, but in reality, it is an
13985 if (MONO_IS_STORE_MEMBASE (ins)) {
13986 tmp_reg = ins->dreg;
13987 ins->dreg = ins->sreg2;
13988 ins->sreg2 = tmp_reg;
13991 spec2 [MONO_INST_DEST] = ' ';
13992 spec2 [MONO_INST_SRC1] = spec [MONO_INST_SRC1];
13993 spec2 [MONO_INST_SRC2] = spec [MONO_INST_DEST];
13994 spec2 [MONO_INST_SRC3] = ' ';
13996 } else if (MONO_IS_STORE_MEMINDEX (ins))
13997 g_assert_not_reached ();
14002 if (G_UNLIKELY (cfg->verbose_level > 2)) {
14003 printf ("\t %.3s %d", spec, ins->dreg);
14004 num_sregs = mono_inst_get_src_registers (ins, sregs);
14005 for (srcindex = 0; srcindex < num_sregs; ++srcindex)
14006 printf (" %d", sregs [srcindex]);
14013 regtype = spec [MONO_INST_DEST];
14014 g_assert (((ins->dreg == -1) && (regtype == ' ')) || ((ins->dreg != -1) && (regtype != ' ')));
14017 if ((ins->dreg != -1) && get_vreg_to_inst (cfg, ins->dreg)) {
14018 MonoInst *var = get_vreg_to_inst (cfg, ins->dreg);
14019 MonoInst *store_ins;
14021 MonoInst *def_ins = ins;
14022 int dreg = ins->dreg; /* The original vreg */
14024 store_opcode = mono_type_to_store_membase (cfg, var->inst_vtype);
14026 if (var->opcode == OP_REGVAR) {
14027 ins->dreg = var->dreg;
14028 } 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)) {
14030 * Instead of emitting a load+store, use a _membase opcode.
14032 g_assert (var->opcode == OP_REGOFFSET);
14033 if (ins->opcode == OP_MOVE) {
14037 ins->opcode = op_to_op_dest_membase (store_opcode, ins->opcode);
14038 ins->inst_basereg = var->inst_basereg;
14039 ins->inst_offset = var->inst_offset;
14042 spec = INS_INFO (ins->opcode);
14046 g_assert (var->opcode == OP_REGOFFSET);
14048 prev_dreg = ins->dreg;
14050 /* Invalidate any previous lvreg for this vreg */
14051 vreg_to_lvreg [ins->dreg] = 0;
14055 if (COMPILE_SOFT_FLOAT (cfg) && store_opcode == OP_STORER8_MEMBASE_REG) {
14057 store_opcode = OP_STOREI8_MEMBASE_REG;
14060 ins->dreg = alloc_dreg (cfg, stacktypes [regtype]);
14062 #if SIZEOF_REGISTER != 8
14063 if (regtype == 'l') {
14064 NEW_STORE_MEMBASE (cfg, store_ins, OP_STOREI4_MEMBASE_REG, var->inst_basereg, var->inst_offset + MINI_LS_WORD_OFFSET, ins->dreg + 1);
14065 mono_bblock_insert_after_ins (bb, ins, store_ins);
14066 NEW_STORE_MEMBASE (cfg, store_ins, OP_STOREI4_MEMBASE_REG, var->inst_basereg, var->inst_offset + MINI_MS_WORD_OFFSET, ins->dreg + 2);
14067 mono_bblock_insert_after_ins (bb, ins, store_ins);
14068 def_ins = store_ins;
14073 g_assert (store_opcode != OP_STOREV_MEMBASE);
14075 /* Try to fuse the store into the instruction itself */
14076 /* FIXME: Add more instructions */
14077 if (!lvreg && ((ins->opcode == OP_ICONST) || ((ins->opcode == OP_I8CONST) && (ins->inst_c0 == 0)))) {
14078 ins->opcode = store_membase_reg_to_store_membase_imm (store_opcode);
14079 ins->inst_imm = ins->inst_c0;
14080 ins->inst_destbasereg = var->inst_basereg;
14081 ins->inst_offset = var->inst_offset;
14082 spec = INS_INFO (ins->opcode);
14083 } else if (!lvreg && ((ins->opcode == OP_MOVE) || (ins->opcode == OP_FMOVE) || (ins->opcode == OP_LMOVE) || (ins->opcode == OP_RMOVE))) {
14084 ins->opcode = store_opcode;
14085 ins->inst_destbasereg = var->inst_basereg;
14086 ins->inst_offset = var->inst_offset;
14090 tmp_reg = ins->dreg;
14091 ins->dreg = ins->sreg2;
14092 ins->sreg2 = tmp_reg;
14095 spec2 [MONO_INST_DEST] = ' ';
14096 spec2 [MONO_INST_SRC1] = spec [MONO_INST_SRC1];
14097 spec2 [MONO_INST_SRC2] = spec [MONO_INST_DEST];
14098 spec2 [MONO_INST_SRC3] = ' ';
14100 } else if (!lvreg && (op_to_op_store_membase (store_opcode, ins->opcode) != -1)) {
14101 // FIXME: The backends expect the base reg to be in inst_basereg
14102 ins->opcode = op_to_op_store_membase (store_opcode, ins->opcode);
14104 ins->inst_basereg = var->inst_basereg;
14105 ins->inst_offset = var->inst_offset;
14106 spec = INS_INFO (ins->opcode);
14108 /* printf ("INS: "); mono_print_ins (ins); */
14109 /* Create a store instruction */
14110 NEW_STORE_MEMBASE (cfg, store_ins, store_opcode, var->inst_basereg, var->inst_offset, ins->dreg);
14112 /* Insert it after the instruction */
14113 mono_bblock_insert_after_ins (bb, ins, store_ins);
14115 def_ins = store_ins;
14118 * We can't assign ins->dreg to var->dreg here, since the
14119 * sregs could use it. So set a flag, and do it after
14122 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)))
14123 dest_has_lvreg = TRUE;
14128 if (def_ins && !live_range_start [dreg]) {
14129 live_range_start [dreg] = def_ins;
14130 live_range_start_bb [dreg] = bb;
14133 if (cfg->compute_gc_maps && def_ins && (var->flags & MONO_INST_GC_TRACK)) {
14136 MONO_INST_NEW (cfg, tmp, OP_GC_LIVENESS_DEF);
14137 tmp->inst_c1 = dreg;
14138 mono_bblock_insert_after_ins (bb, def_ins, tmp);
14145 num_sregs = mono_inst_get_src_registers (ins, sregs);
14146 for (srcindex = 0; srcindex < 3; ++srcindex) {
14147 regtype = spec [MONO_INST_SRC1 + srcindex];
14148 sreg = sregs [srcindex];
14150 g_assert (((sreg == -1) && (regtype == ' ')) || ((sreg != -1) && (regtype != ' ')));
14151 if ((sreg != -1) && get_vreg_to_inst (cfg, sreg)) {
14152 MonoInst *var = get_vreg_to_inst (cfg, sreg);
14153 MonoInst *use_ins = ins;
14154 MonoInst *load_ins;
14155 guint32 load_opcode;
14157 if (var->opcode == OP_REGVAR) {
14158 sregs [srcindex] = var->dreg;
14159 //mono_inst_set_src_registers (ins, sregs);
14160 live_range_end [sreg] = use_ins;
14161 live_range_end_bb [sreg] = bb;
14163 if (cfg->compute_gc_maps && var->dreg < orig_next_vreg && (var->flags & MONO_INST_GC_TRACK)) {
14166 MONO_INST_NEW (cfg, tmp, OP_GC_LIVENESS_USE);
14167 /* var->dreg is a hreg */
14168 tmp->inst_c1 = sreg;
14169 mono_bblock_insert_after_ins (bb, ins, tmp);
14175 g_assert (var->opcode == OP_REGOFFSET);
14177 load_opcode = mono_type_to_load_membase (cfg, var->inst_vtype);
14179 g_assert (load_opcode != OP_LOADV_MEMBASE);
14181 if (vreg_to_lvreg [sreg]) {
14182 g_assert (vreg_to_lvreg [sreg] != -1);
14184 /* The variable is already loaded to an lvreg */
14185 if (G_UNLIKELY (cfg->verbose_level > 2))
14186 printf ("\t\tUse lvreg R%d for R%d.\n", vreg_to_lvreg [sreg], sreg);
14187 sregs [srcindex] = vreg_to_lvreg [sreg];
14188 //mono_inst_set_src_registers (ins, sregs);
14192 /* Try to fuse the load into the instruction */
14193 if ((srcindex == 0) && (op_to_op_src1_membase (load_opcode, ins->opcode) != -1)) {
14194 ins->opcode = op_to_op_src1_membase (load_opcode, ins->opcode);
14195 sregs [0] = var->inst_basereg;
14196 //mono_inst_set_src_registers (ins, sregs);
14197 ins->inst_offset = var->inst_offset;
14198 } else if ((srcindex == 1) && (op_to_op_src2_membase (load_opcode, ins->opcode) != -1)) {
14199 ins->opcode = op_to_op_src2_membase (load_opcode, ins->opcode);
14200 sregs [1] = var->inst_basereg;
14201 //mono_inst_set_src_registers (ins, sregs);
14202 ins->inst_offset = var->inst_offset;
14204 if (MONO_IS_REAL_MOVE (ins)) {
14205 ins->opcode = OP_NOP;
14208 //printf ("%d ", srcindex); mono_print_ins (ins);
14210 sreg = alloc_dreg (cfg, stacktypes [regtype]);
14212 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) {
14213 if (var->dreg == prev_dreg) {
14215 * sreg refers to the value loaded by the load
14216 * emitted below, but we need to use ins->dreg
14217 * since it refers to the store emitted earlier.
14221 g_assert (sreg != -1);
14222 vreg_to_lvreg [var->dreg] = sreg;
14223 g_assert (lvregs_len < 1024);
14224 lvregs [lvregs_len ++] = var->dreg;
14228 sregs [srcindex] = sreg;
14229 //mono_inst_set_src_registers (ins, sregs);
14231 #if SIZEOF_REGISTER != 8
14232 if (regtype == 'l') {
14233 NEW_LOAD_MEMBASE (cfg, load_ins, OP_LOADI4_MEMBASE, sreg + 2, var->inst_basereg, var->inst_offset + MINI_MS_WORD_OFFSET);
14234 mono_bblock_insert_before_ins (bb, ins, load_ins);
14235 NEW_LOAD_MEMBASE (cfg, load_ins, OP_LOADI4_MEMBASE, sreg + 1, var->inst_basereg, var->inst_offset + MINI_LS_WORD_OFFSET);
14236 mono_bblock_insert_before_ins (bb, ins, load_ins);
14237 use_ins = load_ins;
14242 #if SIZEOF_REGISTER == 4
14243 g_assert (load_opcode != OP_LOADI8_MEMBASE);
14245 NEW_LOAD_MEMBASE (cfg, load_ins, load_opcode, sreg, var->inst_basereg, var->inst_offset);
14246 mono_bblock_insert_before_ins (bb, ins, load_ins);
14247 use_ins = load_ins;
14251 if (var->dreg < orig_next_vreg) {
14252 live_range_end [var->dreg] = use_ins;
14253 live_range_end_bb [var->dreg] = bb;
14256 if (cfg->compute_gc_maps && var->dreg < orig_next_vreg && (var->flags & MONO_INST_GC_TRACK)) {
14259 MONO_INST_NEW (cfg, tmp, OP_GC_LIVENESS_USE);
14260 tmp->inst_c1 = var->dreg;
14261 mono_bblock_insert_after_ins (bb, ins, tmp);
14265 mono_inst_set_src_registers (ins, sregs);
14267 if (dest_has_lvreg) {
14268 g_assert (ins->dreg != -1);
14269 vreg_to_lvreg [prev_dreg] = ins->dreg;
14270 g_assert (lvregs_len < 1024);
14271 lvregs [lvregs_len ++] = prev_dreg;
14272 dest_has_lvreg = FALSE;
14276 tmp_reg = ins->dreg;
14277 ins->dreg = ins->sreg2;
14278 ins->sreg2 = tmp_reg;
14281 if (MONO_IS_CALL (ins)) {
14282 /* Clear vreg_to_lvreg array */
14283 for (i = 0; i < lvregs_len; i++)
14284 vreg_to_lvreg [lvregs [i]] = 0;
14286 } else if (ins->opcode == OP_NOP) {
14288 MONO_INST_NULLIFY_SREGS (ins);
14291 if (cfg->verbose_level > 2)
14292 mono_print_ins_index (1, ins);
14295 /* Extend the live range based on the liveness info */
14296 if (cfg->compute_precise_live_ranges && bb->live_out_set && bb->code) {
14297 for (i = 0; i < cfg->num_varinfo; i ++) {
14298 MonoMethodVar *vi = MONO_VARINFO (cfg, i);
14300 if (vreg_is_volatile (cfg, vi->vreg))
14301 /* The liveness info is incomplete */
14304 if (mono_bitset_test_fast (bb->live_in_set, i) && !live_range_start [vi->vreg]) {
14305 /* Live from at least the first ins of this bb */
14306 live_range_start [vi->vreg] = bb->code;
14307 live_range_start_bb [vi->vreg] = bb;
14310 if (mono_bitset_test_fast (bb->live_out_set, i)) {
14311 /* Live at least until the last ins of this bb */
14312 live_range_end [vi->vreg] = bb->last_ins;
14313 live_range_end_bb [vi->vreg] = bb;
14319 #ifdef MONO_ARCH_HAVE_LIVERANGE_OPS
14321 * Emit LIVERANGE_START/LIVERANGE_END opcodes, the backend will implement them
14322 * by storing the current native offset into MonoMethodVar->live_range_start/end.
14324 if (cfg->compute_precise_live_ranges && cfg->comp_done & MONO_COMP_LIVENESS) {
14325 for (i = 0; i < cfg->num_varinfo; ++i) {
14326 int vreg = MONO_VARINFO (cfg, i)->vreg;
14329 if (live_range_start [vreg]) {
14330 MONO_INST_NEW (cfg, ins, OP_LIVERANGE_START);
14332 ins->inst_c1 = vreg;
14333 mono_bblock_insert_after_ins (live_range_start_bb [vreg], live_range_start [vreg], ins);
14335 if (live_range_end [vreg]) {
14336 MONO_INST_NEW (cfg, ins, OP_LIVERANGE_END);
14338 ins->inst_c1 = vreg;
14339 if (live_range_end [vreg] == live_range_end_bb [vreg]->last_ins)
14340 mono_add_ins_to_end (live_range_end_bb [vreg], ins);
14342 mono_bblock_insert_after_ins (live_range_end_bb [vreg], live_range_end [vreg], ins);
14348 if (cfg->gsharedvt_locals_var_ins) {
14349 /* Nullify if unused */
14350 cfg->gsharedvt_locals_var_ins->opcode = OP_PCONST;
14351 cfg->gsharedvt_locals_var_ins->inst_imm = 0;
14354 g_free (live_range_start);
14355 g_free (live_range_end);
14356 g_free (live_range_start_bb);
14357 g_free (live_range_end_bb);
14362 * - use 'iadd' instead of 'int_add'
14363 * - handling ovf opcodes: decompose in method_to_ir.
14364 * - unify iregs/fregs
14365 * -> partly done, the missing parts are:
14366 * - a more complete unification would involve unifying the hregs as well, so
14367 * code wouldn't need if (fp) all over the place. but that would mean the hregs
14368 * would no longer map to the machine hregs, so the code generators would need to
14369 * be modified. Also, on ia64 for example, niregs + nfregs > 256 -> bitmasks
14370 * wouldn't work any more. Duplicating the code in mono_local_regalloc () into
14371 * fp/non-fp branches speeds it up by about 15%.
14372 * - use sext/zext opcodes instead of shifts
14374 * - get rid of TEMPLOADs if possible and use vregs instead
14375 * - clean up usage of OP_P/OP_ opcodes
14376 * - cleanup usage of DUMMY_USE
14377 * - cleanup the setting of ins->type for MonoInst's which are pushed on the
14379 * - set the stack type and allocate a dreg in the EMIT_NEW macros
14380 * - get rid of all the <foo>2 stuff when the new JIT is ready.
14381 * - make sure handle_stack_args () is called before the branch is emitted
14382 * - when the new IR is done, get rid of all unused stuff
14383 * - COMPARE/BEQ as separate instructions or unify them ?
14384 * - keeping them separate allows specialized compare instructions like
14385 * compare_imm, compare_membase
14386 * - most back ends unify fp compare+branch, fp compare+ceq
14387 * - integrate mono_save_args into inline_method
14388 * - get rid of the empty bblocks created by MONO_EMIT_NEW_BRACH_BLOCK2
14389 * - handle long shift opts on 32 bit platforms somehow: they require
14390 * 3 sregs (2 for arg1 and 1 for arg2)
14391 * - make byref a 'normal' type.
14392 * - use vregs for bb->out_stacks if possible, handle_global_vreg will make them a
14393 * variable if needed.
14394 * - do not start a new IL level bblock when cfg->cbb is changed by a function call
14395 * like inline_method.
14396 * - remove inlining restrictions
14397 * - fix LNEG and enable cfold of INEG
14398 * - generalize x86 optimizations like ldelema as a peephole optimization
14399 * - add store_mem_imm for amd64
14400 * - optimize the loading of the interruption flag in the managed->native wrappers
14401 * - avoid special handling of OP_NOP in passes
14402 * - move code inserting instructions into one function/macro.
14403 * - try a coalescing phase after liveness analysis
14404 * - add float -> vreg conversion + local optimizations on !x86
14405 * - figure out how to handle decomposed branches during optimizations, ie.
14406 * compare+branch, op_jump_table+op_br etc.
14407 * - promote RuntimeXHandles to vregs
14408 * - vtype cleanups:
14409 * - add a NEW_VARLOADA_VREG macro
14410 * - the vtype optimizations are blocked by the LDADDR opcodes generated for
14411 * accessing vtype fields.
14412 * - get rid of I8CONST on 64 bit platforms
14413 * - dealing with the increase in code size due to branches created during opcode
14415 * - use extended basic blocks
14416 * - all parts of the JIT
14417 * - handle_global_vregs () && local regalloc
14418 * - avoid introducing global vregs during decomposition, like 'vtable' in isinst
14419 * - sources of increase in code size:
14422 * - isinst and castclass
14423 * - lvregs not allocated to global registers even if used multiple times
14424 * - call cctors outside the JIT, to make -v output more readable and JIT timings more
14426 * - check for fp stack leakage in other opcodes too. (-> 'exceptions' optimization)
14427 * - add all micro optimizations from the old JIT
14428 * - put tree optimizations into the deadce pass
14429 * - decompose op_start_handler/op_endfilter/op_endfinally earlier using an arch
14430 * specific function.
14431 * - unify the float comparison opcodes with the other comparison opcodes, i.e.
14432 * fcompare + branchCC.
14433 * - create a helper function for allocating a stack slot, taking into account
14434 * MONO_CFG_HAS_SPILLUP.
14436 * - merge the ia64 switch changes.
14437 * - optimize mono_regstate2_alloc_int/float.
14438 * - fix the pessimistic handling of variables accessed in exception handler blocks.
14439 * - need to write a tree optimization pass, but the creation of trees is difficult, i.e.
14440 * parts of the tree could be separated by other instructions, killing the tree
14441 * arguments, or stores killing loads etc. Also, should we fold loads into other
14442 * instructions if the result of the load is used multiple times ?
14443 * - make the REM_IMM optimization in mini-x86.c arch-independent.
14444 * - LAST MERGE: 108395.
14445 * - when returning vtypes in registers, generate IR and append it to the end of the
14446 * last bb instead of doing it in the epilog.
14447 * - change the store opcodes so they use sreg1 instead of dreg to store the base register.
14455 - When to decompose opcodes:
14456 - earlier: this makes some optimizations hard to implement, since the low level IR
14457 no longer contains the neccessary information. But it is easier to do.
14458 - later: harder to implement, enables more optimizations.
14459 - Branches inside bblocks:
14460 - created when decomposing complex opcodes.
14461 - branches to another bblock: harmless, but not tracked by the branch
14462 optimizations, so need to branch to a label at the start of the bblock.
14463 - branches to inside the same bblock: very problematic, trips up the local
14464 reg allocator. Can be fixed by spitting the current bblock, but that is a
14465 complex operation, since some local vregs can become global vregs etc.
14466 - Local/global vregs:
14467 - local vregs: temporary vregs used inside one bblock. Assigned to hregs by the
14468 local register allocator.
14469 - global vregs: used in more than one bblock. Have an associated MonoMethodVar
14470 structure, created by mono_create_var (). Assigned to hregs or the stack by
14471 the global register allocator.
14472 - When to do optimizations like alu->alu_imm:
14473 - earlier -> saves work later on since the IR will be smaller/simpler
14474 - later -> can work on more instructions
14475 - Handling of valuetypes:
14476 - When a vtype is pushed on the stack, a new temporary is created, an
14477 instruction computing its address (LDADDR) is emitted and pushed on
14478 the stack. Need to optimize cases when the vtype is used immediately as in
14479 argument passing, stloc etc.
14480 - Instead of the to_end stuff in the old JIT, simply call the function handling
14481 the values on the stack before emitting the last instruction of the bb.
14484 #endif /* DISABLE_JIT */