2 * method-to-ir.c: Convert CIL to the JIT internal representation
5 * Paolo Molaro (lupus@ximian.com)
6 * Dietmar Maurer (dietmar@ximian.com)
8 * (C) 2002 Ximian, Inc.
9 * Copyright 2003-2010 Novell, Inc (http://www.novell.com)
10 * Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
27 #ifdef HAVE_SYS_TIME_H
35 #include <mono/utils/memcheck.h>
37 #include <mono/metadata/abi-details.h>
38 #include <mono/metadata/assembly.h>
39 #include <mono/metadata/attrdefs.h>
40 #include <mono/metadata/loader.h>
41 #include <mono/metadata/tabledefs.h>
42 #include <mono/metadata/class.h>
43 #include <mono/metadata/object.h>
44 #include <mono/metadata/exception.h>
45 #include <mono/metadata/opcodes.h>
46 #include <mono/metadata/mono-endian.h>
47 #include <mono/metadata/tokentype.h>
48 #include <mono/metadata/tabledefs.h>
49 #include <mono/metadata/marshal.h>
50 #include <mono/metadata/debug-helpers.h>
51 #include <mono/metadata/mono-debug.h>
52 #include <mono/metadata/gc-internal.h>
53 #include <mono/metadata/security-manager.h>
54 #include <mono/metadata/threads-types.h>
55 #include <mono/metadata/security-core-clr.h>
56 #include <mono/metadata/monitor.h>
57 #include <mono/metadata/profiler-private.h>
58 #include <mono/metadata/profiler.h>
59 #include <mono/metadata/debug-mono-symfile.h>
60 #include <mono/utils/mono-compiler.h>
61 #include <mono/utils/mono-memory-model.h>
62 #include <mono/metadata/mono-basic-block.h>
68 #include "jit-icalls.h"
70 #include "debugger-agent.h"
71 #include "seq-points.h"
73 #define BRANCH_COST 10
74 #define INLINE_LENGTH_LIMIT 20
76 /* These have 'cfg' as an implicit argument */
77 #define INLINE_FAILURE(msg) do { \
78 if ((cfg->method != cfg->current_method) && (cfg->current_method->wrapper_type == MONO_WRAPPER_NONE)) { \
79 inline_failure (cfg, msg); \
80 goto exception_exit; \
83 #define CHECK_CFG_EXCEPTION do {\
84 if (cfg->exception_type != MONO_EXCEPTION_NONE) \
85 goto exception_exit; \
87 #define METHOD_ACCESS_FAILURE(method, cmethod) do { \
88 method_access_failure ((cfg), (method), (cmethod)); \
89 goto exception_exit; \
91 #define FIELD_ACCESS_FAILURE(method, field) do { \
92 field_access_failure ((cfg), (method), (field)); \
93 goto exception_exit; \
95 #define GENERIC_SHARING_FAILURE(opcode) do { \
97 gshared_failure (cfg, opcode, __FILE__, __LINE__); \
98 goto exception_exit; \
101 #define GSHAREDVT_FAILURE(opcode) do { \
102 if (cfg->gsharedvt) { \
103 gsharedvt_failure (cfg, opcode, __FILE__, __LINE__); \
104 goto exception_exit; \
107 #define OUT_OF_MEMORY_FAILURE do { \
108 mono_cfg_set_exception (cfg, MONO_EXCEPTION_OUT_OF_MEMORY); \
109 goto exception_exit; \
111 #define DISABLE_AOT(cfg) do { \
112 if ((cfg)->verbose_level >= 2) \
113 printf ("AOT disabled: %s:%d\n", __FILE__, __LINE__); \
114 (cfg)->disable_aot = TRUE; \
116 #define LOAD_ERROR do { \
117 break_on_unverified (); \
118 mono_cfg_set_exception (cfg, MONO_EXCEPTION_TYPE_LOAD); \
119 goto exception_exit; \
122 #define TYPE_LOAD_ERROR(klass) do { \
123 cfg->exception_ptr = klass; \
127 #define CHECK_CFG_ERROR do {\
128 if (!mono_error_ok (&cfg->error)) { \
129 mono_cfg_set_exception (cfg, MONO_EXCEPTION_MONO_ERROR); \
130 goto mono_error_exit; \
134 /* Determine whenever 'ins' represents a load of the 'this' argument */
135 #define MONO_CHECK_THIS(ins) (mono_method_signature (cfg->method)->hasthis && ((ins)->opcode == OP_MOVE) && ((ins)->sreg1 == cfg->args [0]->dreg))
137 static int ldind_to_load_membase (int opcode);
138 static int stind_to_store_membase (int opcode);
140 int mono_op_to_op_imm (int opcode);
141 int mono_op_to_op_imm_noemul (int opcode);
143 MONO_API MonoInst* mono_emit_native_call (MonoCompile *cfg, gconstpointer func, MonoMethodSignature *sig, MonoInst **args);
145 static int inline_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **sp,
146 guchar *ip, guint real_offset, gboolean inline_always, MonoBasicBlock **out_cbb);
148 /* helper methods signatures */
149 static MonoMethodSignature *helper_sig_class_init_trampoline;
150 static MonoMethodSignature *helper_sig_domain_get;
151 static MonoMethodSignature *helper_sig_generic_class_init_trampoline;
152 static MonoMethodSignature *helper_sig_generic_class_init_trampoline_llvm;
153 static MonoMethodSignature *helper_sig_rgctx_lazy_fetch_trampoline;
154 static MonoMethodSignature *helper_sig_monitor_enter_exit_trampoline;
155 static MonoMethodSignature *helper_sig_monitor_enter_exit_trampoline_llvm;
156 static MonoMethodSignature *helper_sig_monitor_enter_v4_trampoline_llvm;
159 * Instruction metadata
167 #define MINI_OP(a,b,dest,src1,src2) dest, src1, src2, ' ',
168 #define MINI_OP3(a,b,dest,src1,src2,src3) dest, src1, src2, src3,
174 #if SIZEOF_REGISTER == 8 && SIZEOF_REGISTER == SIZEOF_VOID_P
179 /* keep in sync with the enum in mini.h */
182 #include "mini-ops.h"
187 #define MINI_OP(a,b,dest,src1,src2) ((src2) != NONE ? 2 : ((src1) != NONE ? 1 : 0)),
188 #define MINI_OP3(a,b,dest,src1,src2,src3) ((src3) != NONE ? 3 : ((src2) != NONE ? 2 : ((src1) != NONE ? 1 : 0))),
190 * This should contain the index of the last sreg + 1. This is not the same
191 * as the number of sregs for opcodes like IA64_CMP_EQ_IMM.
193 const gint8 ins_sreg_counts[] = {
194 #include "mini-ops.h"
199 #define MONO_INIT_VARINFO(vi,id) do { \
200 (vi)->range.first_use.pos.bid = 0xffff; \
206 mono_alloc_ireg (MonoCompile *cfg)
208 return alloc_ireg (cfg);
212 mono_alloc_lreg (MonoCompile *cfg)
214 return alloc_lreg (cfg);
218 mono_alloc_freg (MonoCompile *cfg)
220 return alloc_freg (cfg);
224 mono_alloc_preg (MonoCompile *cfg)
226 return alloc_preg (cfg);
230 mono_alloc_dreg (MonoCompile *cfg, MonoStackType stack_type)
232 return alloc_dreg (cfg, stack_type);
236 * mono_alloc_ireg_ref:
238 * Allocate an IREG, and mark it as holding a GC ref.
241 mono_alloc_ireg_ref (MonoCompile *cfg)
243 return alloc_ireg_ref (cfg);
247 * mono_alloc_ireg_mp:
249 * Allocate an IREG, and mark it as holding a managed pointer.
252 mono_alloc_ireg_mp (MonoCompile *cfg)
254 return alloc_ireg_mp (cfg);
258 * mono_alloc_ireg_copy:
260 * Allocate an IREG with the same GC type as VREG.
263 mono_alloc_ireg_copy (MonoCompile *cfg, guint32 vreg)
265 if (vreg_is_ref (cfg, vreg))
266 return alloc_ireg_ref (cfg);
267 else if (vreg_is_mp (cfg, vreg))
268 return alloc_ireg_mp (cfg);
270 return alloc_ireg (cfg);
274 mono_type_to_regmove (MonoCompile *cfg, MonoType *type)
279 type = mini_replace_type (type);
281 switch (type->type) {
284 case MONO_TYPE_BOOLEAN:
296 case MONO_TYPE_FNPTR:
298 case MONO_TYPE_CLASS:
299 case MONO_TYPE_STRING:
300 case MONO_TYPE_OBJECT:
301 case MONO_TYPE_SZARRAY:
302 case MONO_TYPE_ARRAY:
306 #if SIZEOF_REGISTER == 8
312 return cfg->r4fp ? OP_RMOVE : OP_FMOVE;
315 case MONO_TYPE_VALUETYPE:
316 if (type->data.klass->enumtype) {
317 type = mono_class_enum_basetype (type->data.klass);
320 if (MONO_CLASS_IS_SIMD (cfg, mono_class_from_mono_type (type)))
323 case MONO_TYPE_TYPEDBYREF:
325 case MONO_TYPE_GENERICINST:
326 type = &type->data.generic_class->container_class->byval_arg;
330 g_assert (cfg->generic_sharing_context);
331 if (mini_type_var_is_vt (cfg, type))
336 g_error ("unknown type 0x%02x in type_to_regstore", type->type);
342 mono_print_bb (MonoBasicBlock *bb, const char *msg)
347 printf ("\n%s %d: [IN: ", msg, bb->block_num);
348 for (i = 0; i < bb->in_count; ++i)
349 printf (" BB%d(%d)", bb->in_bb [i]->block_num, bb->in_bb [i]->dfn);
351 for (i = 0; i < bb->out_count; ++i)
352 printf (" BB%d(%d)", bb->out_bb [i]->block_num, bb->out_bb [i]->dfn);
354 for (tree = bb->code; tree; tree = tree->next)
355 mono_print_ins_index (-1, tree);
359 mono_create_helper_signatures (void)
361 helper_sig_domain_get = mono_create_icall_signature ("ptr");
362 helper_sig_class_init_trampoline = mono_create_icall_signature ("void");
363 helper_sig_generic_class_init_trampoline = mono_create_icall_signature ("void");
364 helper_sig_generic_class_init_trampoline_llvm = mono_create_icall_signature ("void ptr");
365 helper_sig_rgctx_lazy_fetch_trampoline = mono_create_icall_signature ("ptr ptr");
366 helper_sig_monitor_enter_exit_trampoline = mono_create_icall_signature ("void");
367 helper_sig_monitor_enter_exit_trampoline_llvm = mono_create_icall_signature ("void object");
368 helper_sig_monitor_enter_v4_trampoline_llvm = mono_create_icall_signature ("void object ptr");
371 static MONO_NEVER_INLINE void
372 break_on_unverified (void)
374 if (mini_get_debug_options ()->break_on_unverified)
378 static MONO_NEVER_INLINE void
379 method_access_failure (MonoCompile *cfg, MonoMethod *method, MonoMethod *cil_method)
381 char *method_fname = mono_method_full_name (method, TRUE);
382 char *cil_method_fname = mono_method_full_name (cil_method, TRUE);
383 mono_cfg_set_exception (cfg, MONO_EXCEPTION_METHOD_ACCESS);
384 cfg->exception_message = g_strdup_printf ("Method `%s' is inaccessible from method `%s'\n", cil_method_fname, method_fname);
385 g_free (method_fname);
386 g_free (cil_method_fname);
389 static MONO_NEVER_INLINE void
390 field_access_failure (MonoCompile *cfg, MonoMethod *method, MonoClassField *field)
392 char *method_fname = mono_method_full_name (method, TRUE);
393 char *field_fname = mono_field_full_name (field);
394 mono_cfg_set_exception (cfg, MONO_EXCEPTION_FIELD_ACCESS);
395 cfg->exception_message = g_strdup_printf ("Field `%s' is inaccessible from method `%s'\n", field_fname, method_fname);
396 g_free (method_fname);
397 g_free (field_fname);
400 static MONO_NEVER_INLINE void
401 inline_failure (MonoCompile *cfg, const char *msg)
403 if (cfg->verbose_level >= 2)
404 printf ("inline failed: %s\n", msg);
405 mono_cfg_set_exception (cfg, MONO_EXCEPTION_INLINE_FAILED);
408 static MONO_NEVER_INLINE void
409 gshared_failure (MonoCompile *cfg, int opcode, const char *file, int line)
411 if (cfg->verbose_level > 2) \
412 printf ("sharing failed for method %s.%s.%s/%d opcode %s line %d\n", cfg->current_method->klass->name_space, cfg->current_method->klass->name, cfg->current_method->name, cfg->current_method->signature->param_count, mono_opcode_name ((opcode)), __LINE__);
413 mono_cfg_set_exception (cfg, MONO_EXCEPTION_GENERIC_SHARING_FAILED);
416 static MONO_NEVER_INLINE void
417 gsharedvt_failure (MonoCompile *cfg, int opcode, const char *file, int line)
419 cfg->exception_message = g_strdup_printf ("gsharedvt failed for method %s.%s.%s/%d opcode %s %s:%d", cfg->current_method->klass->name_space, cfg->current_method->klass->name, cfg->current_method->name, cfg->current_method->signature->param_count, mono_opcode_name ((opcode)), file, line);
420 if (cfg->verbose_level >= 2)
421 printf ("%s\n", cfg->exception_message);
422 mono_cfg_set_exception (cfg, MONO_EXCEPTION_GENERIC_SHARING_FAILED);
426 * When using gsharedvt, some instatiations might be verifiable, and some might be not. i.e.
427 * foo<T> (int i) { ldarg.0; box T; }
429 #define UNVERIFIED do { \
430 if (cfg->gsharedvt) { \
431 if (cfg->verbose_level > 2) \
432 printf ("gsharedvt method failed to verify, falling back to instantiation.\n"); \
433 mono_cfg_set_exception (cfg, MONO_EXCEPTION_GENERIC_SHARING_FAILED); \
434 goto exception_exit; \
436 break_on_unverified (); \
440 #define GET_BBLOCK(cfg,tblock,ip) do { \
441 (tblock) = cfg->cil_offset_to_bb [(ip) - cfg->cil_start]; \
443 if ((ip) >= end || (ip) < header->code) UNVERIFIED; \
444 NEW_BBLOCK (cfg, (tblock)); \
445 (tblock)->cil_code = (ip); \
446 ADD_BBLOCK (cfg, (tblock)); \
450 #if defined(TARGET_X86) || defined(TARGET_AMD64)
451 #define EMIT_NEW_X86_LEA(cfg,dest,sr1,sr2,shift,imm) do { \
452 MONO_INST_NEW (cfg, dest, OP_X86_LEA); \
453 (dest)->dreg = alloc_ireg_mp ((cfg)); \
454 (dest)->sreg1 = (sr1); \
455 (dest)->sreg2 = (sr2); \
456 (dest)->inst_imm = (imm); \
457 (dest)->backend.shift_amount = (shift); \
458 MONO_ADD_INS ((cfg)->cbb, (dest)); \
462 /* Emit conversions so both operands of a binary opcode are of the same type */
464 add_widen_op (MonoCompile *cfg, MonoInst *ins, MonoInst **arg1_ref, MonoInst **arg2_ref)
466 MonoInst *arg1 = *arg1_ref;
467 MonoInst *arg2 = *arg2_ref;
470 ((arg1->type == STACK_R4 && arg2->type == STACK_R8) ||
471 (arg1->type == STACK_R8 && arg2->type == STACK_R4))) {
474 /* Mixing r4/r8 is allowed by the spec */
475 if (arg1->type == STACK_R4) {
476 int dreg = alloc_freg (cfg);
478 EMIT_NEW_UNALU (cfg, conv, OP_RCONV_TO_R8, dreg, arg1->dreg);
479 conv->type = STACK_R8;
483 if (arg2->type == STACK_R4) {
484 int dreg = alloc_freg (cfg);
486 EMIT_NEW_UNALU (cfg, conv, OP_RCONV_TO_R8, dreg, arg2->dreg);
487 conv->type = STACK_R8;
493 #if SIZEOF_REGISTER == 8
494 /* FIXME: Need to add many more cases */
495 if ((arg1)->type == STACK_PTR && (arg2)->type == STACK_I4) {
498 int dr = alloc_preg (cfg);
499 EMIT_NEW_UNALU (cfg, widen, OP_SEXT_I4, dr, (arg2)->dreg);
500 (ins)->sreg2 = widen->dreg;
505 #define ADD_BINOP(op) do { \
506 MONO_INST_NEW (cfg, ins, (op)); \
508 ins->sreg1 = sp [0]->dreg; \
509 ins->sreg2 = sp [1]->dreg; \
510 type_from_op (cfg, ins, sp [0], sp [1]); \
512 /* Have to insert a widening op */ \
513 add_widen_op (cfg, ins, &sp [0], &sp [1]); \
514 ins->dreg = alloc_dreg ((cfg), (ins)->type); \
515 MONO_ADD_INS ((cfg)->cbb, (ins)); \
516 *sp++ = mono_decompose_opcode ((cfg), (ins)); \
519 #define ADD_UNOP(op) do { \
520 MONO_INST_NEW (cfg, ins, (op)); \
522 ins->sreg1 = sp [0]->dreg; \
523 type_from_op (cfg, ins, sp [0], NULL); \
525 (ins)->dreg = alloc_dreg ((cfg), (ins)->type); \
526 MONO_ADD_INS ((cfg)->cbb, (ins)); \
527 *sp++ = mono_decompose_opcode (cfg, ins); \
530 #define ADD_BINCOND(next_block) do { \
533 MONO_INST_NEW(cfg, cmp, OP_COMPARE); \
534 cmp->sreg1 = sp [0]->dreg; \
535 cmp->sreg2 = sp [1]->dreg; \
536 type_from_op (cfg, cmp, sp [0], sp [1]); \
538 add_widen_op (cfg, cmp, &sp [0], &sp [1]); \
539 type_from_op (cfg, ins, sp [0], sp [1]); \
540 ins->inst_many_bb = mono_mempool_alloc (cfg->mempool, sizeof(gpointer)*2); \
541 GET_BBLOCK (cfg, tblock, target); \
542 link_bblock (cfg, bblock, tblock); \
543 ins->inst_true_bb = tblock; \
544 if ((next_block)) { \
545 link_bblock (cfg, bblock, (next_block)); \
546 ins->inst_false_bb = (next_block); \
547 start_new_bblock = 1; \
549 GET_BBLOCK (cfg, tblock, ip); \
550 link_bblock (cfg, bblock, tblock); \
551 ins->inst_false_bb = tblock; \
552 start_new_bblock = 2; \
554 if (sp != stack_start) { \
555 handle_stack_args (cfg, stack_start, sp - stack_start); \
556 CHECK_UNVERIFIABLE (cfg); \
558 MONO_ADD_INS (bblock, cmp); \
559 MONO_ADD_INS (bblock, ins); \
563 * link_bblock: Links two basic blocks
565 * links two basic blocks in the control flow graph, the 'from'
566 * argument is the starting block and the 'to' argument is the block
567 * the control flow ends to after 'from'.
570 link_bblock (MonoCompile *cfg, MonoBasicBlock *from, MonoBasicBlock* to)
572 MonoBasicBlock **newa;
576 if (from->cil_code) {
578 printf ("edge from IL%04x to IL_%04x\n", from->cil_code - cfg->cil_code, to->cil_code - cfg->cil_code);
580 printf ("edge from IL%04x to exit\n", from->cil_code - cfg->cil_code);
583 printf ("edge from entry to IL_%04x\n", to->cil_code - cfg->cil_code);
585 printf ("edge from entry to exit\n");
590 for (i = 0; i < from->out_count; ++i) {
591 if (to == from->out_bb [i]) {
597 newa = mono_mempool_alloc (cfg->mempool, sizeof (gpointer) * (from->out_count + 1));
598 for (i = 0; i < from->out_count; ++i) {
599 newa [i] = from->out_bb [i];
607 for (i = 0; i < to->in_count; ++i) {
608 if (from == to->in_bb [i]) {
614 newa = mono_mempool_alloc (cfg->mempool, sizeof (gpointer) * (to->in_count + 1));
615 for (i = 0; i < to->in_count; ++i) {
616 newa [i] = to->in_bb [i];
625 mono_link_bblock (MonoCompile *cfg, MonoBasicBlock *from, MonoBasicBlock* to)
627 link_bblock (cfg, from, to);
631 * mono_find_block_region:
633 * We mark each basic block with a region ID. We use that to avoid BB
634 * optimizations when blocks are in different regions.
637 * A region token that encodes where this region is, and information
638 * about the clause owner for this block.
640 * The region encodes the try/catch/filter clause that owns this block
641 * as well as the type. -1 is a special value that represents a block
642 * that is in none of try/catch/filter.
645 mono_find_block_region (MonoCompile *cfg, int offset)
647 MonoMethodHeader *header = cfg->header;
648 MonoExceptionClause *clause;
651 for (i = 0; i < header->num_clauses; ++i) {
652 clause = &header->clauses [i];
653 if ((clause->flags == MONO_EXCEPTION_CLAUSE_FILTER) && (offset >= clause->data.filter_offset) &&
654 (offset < (clause->handler_offset)))
655 return ((i + 1) << 8) | MONO_REGION_FILTER | clause->flags;
657 if (MONO_OFFSET_IN_HANDLER (clause, offset)) {
658 if (clause->flags == MONO_EXCEPTION_CLAUSE_FINALLY)
659 return ((i + 1) << 8) | MONO_REGION_FINALLY | clause->flags;
660 else if (clause->flags == MONO_EXCEPTION_CLAUSE_FAULT)
661 return ((i + 1) << 8) | MONO_REGION_FAULT | clause->flags;
663 return ((i + 1) << 8) | MONO_REGION_CATCH | clause->flags;
666 if (MONO_OFFSET_IN_CLAUSE (clause, offset))
667 return ((i + 1) << 8) | clause->flags;
674 mono_find_final_block (MonoCompile *cfg, unsigned char *ip, unsigned char *target, int type)
676 MonoMethodHeader *header = cfg->header;
677 MonoExceptionClause *clause;
681 for (i = 0; i < header->num_clauses; ++i) {
682 clause = &header->clauses [i];
683 if (MONO_OFFSET_IN_CLAUSE (clause, (ip - header->code)) &&
684 (!MONO_OFFSET_IN_CLAUSE (clause, (target - header->code)))) {
685 if (clause->flags == type)
686 res = g_list_append (res, clause);
693 mono_create_spvar_for_region (MonoCompile *cfg, int region)
697 var = g_hash_table_lookup (cfg->spvars, GINT_TO_POINTER (region));
701 var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
702 /* prevent it from being register allocated */
703 var->flags |= MONO_INST_VOLATILE;
705 g_hash_table_insert (cfg->spvars, GINT_TO_POINTER (region), var);
709 mono_find_exvar_for_offset (MonoCompile *cfg, int offset)
711 return g_hash_table_lookup (cfg->exvars, GINT_TO_POINTER (offset));
715 mono_create_exvar_for_offset (MonoCompile *cfg, int offset)
719 var = g_hash_table_lookup (cfg->exvars, GINT_TO_POINTER (offset));
723 var = mono_compile_create_var (cfg, &mono_defaults.object_class->byval_arg, OP_LOCAL);
724 /* prevent it from being register allocated */
725 var->flags |= MONO_INST_VOLATILE;
727 g_hash_table_insert (cfg->exvars, GINT_TO_POINTER (offset), var);
733 * Returns the type used in the eval stack when @type is loaded.
734 * FIXME: return a MonoType/MonoClass for the byref and VALUETYPE cases.
737 type_to_eval_stack_type (MonoCompile *cfg, MonoType *type, MonoInst *inst)
741 type = mini_replace_type (type);
742 inst->klass = klass = mono_class_from_mono_type (type);
744 inst->type = STACK_MP;
749 switch (type->type) {
751 inst->type = STACK_INV;
755 case MONO_TYPE_BOOLEAN:
761 inst->type = STACK_I4;
766 case MONO_TYPE_FNPTR:
767 inst->type = STACK_PTR;
769 case MONO_TYPE_CLASS:
770 case MONO_TYPE_STRING:
771 case MONO_TYPE_OBJECT:
772 case MONO_TYPE_SZARRAY:
773 case MONO_TYPE_ARRAY:
774 inst->type = STACK_OBJ;
778 inst->type = STACK_I8;
781 inst->type = cfg->r4_stack_type;
784 inst->type = STACK_R8;
786 case MONO_TYPE_VALUETYPE:
787 if (type->data.klass->enumtype) {
788 type = mono_class_enum_basetype (type->data.klass);
792 inst->type = STACK_VTYPE;
795 case MONO_TYPE_TYPEDBYREF:
796 inst->klass = mono_defaults.typed_reference_class;
797 inst->type = STACK_VTYPE;
799 case MONO_TYPE_GENERICINST:
800 type = &type->data.generic_class->container_class->byval_arg;
804 g_assert (cfg->generic_sharing_context);
805 if (mini_is_gsharedvt_type (cfg, type)) {
806 g_assert (cfg->gsharedvt);
807 inst->type = STACK_VTYPE;
809 inst->type = STACK_OBJ;
813 g_error ("unknown type 0x%02x in eval stack type", type->type);
818 * The following tables are used to quickly validate the IL code in type_from_op ().
821 bin_num_table [STACK_MAX] [STACK_MAX] = {
822 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
823 {STACK_INV, STACK_I4, STACK_INV, STACK_PTR, STACK_INV, STACK_MP, STACK_INV, STACK_INV},
824 {STACK_INV, STACK_INV, STACK_I8, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
825 {STACK_INV, STACK_PTR, STACK_INV, STACK_PTR, STACK_INV, STACK_MP, STACK_INV, STACK_INV},
826 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_R8, STACK_INV, STACK_INV, STACK_INV, STACK_R8},
827 {STACK_INV, STACK_MP, STACK_INV, STACK_MP, STACK_INV, STACK_PTR, STACK_INV, STACK_INV},
828 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
829 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
830 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_R8, STACK_INV, STACK_INV, STACK_INV, STACK_R4}
835 STACK_INV, STACK_I4, STACK_I8, STACK_PTR, STACK_R8, STACK_INV, STACK_INV, STACK_INV, STACK_R4
838 /* reduce the size of this table */
840 bin_int_table [STACK_MAX] [STACK_MAX] = {
841 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
842 {STACK_INV, STACK_I4, STACK_INV, STACK_PTR, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
843 {STACK_INV, STACK_INV, STACK_I8, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
844 {STACK_INV, STACK_PTR, STACK_INV, STACK_PTR, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
845 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
846 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
847 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
848 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV}
852 bin_comp_table [STACK_MAX] [STACK_MAX] = {
853 /* Inv i L p F & O vt r4 */
855 {0, 1, 0, 1, 0, 0, 0, 0}, /* i, int32 */
856 {0, 0, 1, 0, 0, 0, 0, 0}, /* L, int64 */
857 {0, 1, 0, 1, 0, 2, 4, 0}, /* p, ptr */
858 {0, 0, 0, 0, 1, 0, 0, 0, 1}, /* F, R8 */
859 {0, 0, 0, 2, 0, 1, 0, 0}, /* &, managed pointer */
860 {0, 0, 0, 4, 0, 0, 3, 0}, /* O, reference */
861 {0, 0, 0, 0, 0, 0, 0, 0}, /* vt value type */
862 {0, 0, 0, 0, 1, 0, 0, 0, 1}, /* r, r4 */
865 /* reduce the size of this table */
867 shift_table [STACK_MAX] [STACK_MAX] = {
868 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
869 {STACK_INV, STACK_I4, STACK_INV, STACK_I4, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
870 {STACK_INV, STACK_I8, STACK_INV, STACK_I8, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
871 {STACK_INV, STACK_PTR, STACK_INV, STACK_PTR, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
872 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
873 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
874 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
875 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV}
879 * Tables to map from the non-specific opcode to the matching
880 * type-specific opcode.
882 /* handles from CEE_ADD to CEE_SHR_UN (CEE_REM_UN for floats) */
884 binops_op_map [STACK_MAX] = {
885 0, OP_IADD-CEE_ADD, OP_LADD-CEE_ADD, OP_PADD-CEE_ADD, OP_FADD-CEE_ADD, OP_PADD-CEE_ADD, 0, 0, OP_RADD-CEE_ADD
888 /* handles from CEE_NEG to CEE_CONV_U8 */
890 unops_op_map [STACK_MAX] = {
891 0, OP_INEG-CEE_NEG, OP_LNEG-CEE_NEG, OP_PNEG-CEE_NEG, OP_FNEG-CEE_NEG, OP_PNEG-CEE_NEG, 0, 0, OP_RNEG-CEE_NEG
894 /* handles from CEE_CONV_U2 to CEE_SUB_OVF_UN */
896 ovfops_op_map [STACK_MAX] = {
897 0, OP_ICONV_TO_U2-CEE_CONV_U2, OP_LCONV_TO_U2-CEE_CONV_U2, OP_PCONV_TO_U2-CEE_CONV_U2, OP_FCONV_TO_U2-CEE_CONV_U2, OP_PCONV_TO_U2-CEE_CONV_U2, OP_PCONV_TO_U2-CEE_CONV_U2, 0, OP_RCONV_TO_U2-CEE_CONV_U2
900 /* handles from CEE_CONV_OVF_I1_UN to CEE_CONV_OVF_U_UN */
902 ovf2ops_op_map [STACK_MAX] = {
903 0, OP_ICONV_TO_OVF_I1_UN-CEE_CONV_OVF_I1_UN, OP_LCONV_TO_OVF_I1_UN-CEE_CONV_OVF_I1_UN, OP_PCONV_TO_OVF_I1_UN-CEE_CONV_OVF_I1_UN, OP_FCONV_TO_OVF_I1_UN-CEE_CONV_OVF_I1_UN, OP_PCONV_TO_OVF_I1_UN-CEE_CONV_OVF_I1_UN, 0, 0, OP_RCONV_TO_OVF_I1_UN-CEE_CONV_OVF_I1_UN
906 /* handles from CEE_CONV_OVF_I1 to CEE_CONV_OVF_U8 */
908 ovf3ops_op_map [STACK_MAX] = {
909 0, OP_ICONV_TO_OVF_I1-CEE_CONV_OVF_I1, OP_LCONV_TO_OVF_I1-CEE_CONV_OVF_I1, OP_PCONV_TO_OVF_I1-CEE_CONV_OVF_I1, OP_FCONV_TO_OVF_I1-CEE_CONV_OVF_I1, OP_PCONV_TO_OVF_I1-CEE_CONV_OVF_I1, 0, 0, OP_RCONV_TO_OVF_I1-CEE_CONV_OVF_I1
912 /* handles from CEE_BEQ to CEE_BLT_UN */
914 beqops_op_map [STACK_MAX] = {
915 0, OP_IBEQ-CEE_BEQ, OP_LBEQ-CEE_BEQ, OP_PBEQ-CEE_BEQ, OP_FBEQ-CEE_BEQ, OP_PBEQ-CEE_BEQ, OP_PBEQ-CEE_BEQ, 0, OP_FBEQ-CEE_BEQ
918 /* handles from CEE_CEQ to CEE_CLT_UN */
920 ceqops_op_map [STACK_MAX] = {
921 0, OP_ICEQ-OP_CEQ, OP_LCEQ-OP_CEQ, OP_PCEQ-OP_CEQ, OP_FCEQ-OP_CEQ, OP_PCEQ-OP_CEQ, OP_PCEQ-OP_CEQ, 0, OP_RCEQ-OP_CEQ
925 * Sets ins->type (the type on the eval stack) according to the
926 * type of the opcode and the arguments to it.
927 * Invalid IL code is marked by setting ins->type to the invalid value STACK_INV.
929 * FIXME: this function sets ins->type unconditionally in some cases, but
930 * it should set it to invalid for some types (a conv.x on an object)
933 type_from_op (MonoCompile *cfg, MonoInst *ins, MonoInst *src1, MonoInst *src2)
935 switch (ins->opcode) {
942 /* FIXME: check unverifiable args for STACK_MP */
943 ins->type = bin_num_table [src1->type] [src2->type];
944 ins->opcode += binops_op_map [ins->type];
951 ins->type = bin_int_table [src1->type] [src2->type];
952 ins->opcode += binops_op_map [ins->type];
957 ins->type = shift_table [src1->type] [src2->type];
958 ins->opcode += binops_op_map [ins->type];
963 ins->type = bin_comp_table [src1->type] [src2->type] ? STACK_I4: STACK_INV;
964 if ((src1->type == STACK_I8) || ((SIZEOF_VOID_P == 8) && ((src1->type == STACK_PTR) || (src1->type == STACK_OBJ) || (src1->type == STACK_MP))))
965 ins->opcode = OP_LCOMPARE;
966 else if (src1->type == STACK_R4)
967 ins->opcode = OP_RCOMPARE;
968 else if (src1->type == STACK_R8)
969 ins->opcode = OP_FCOMPARE;
971 ins->opcode = OP_ICOMPARE;
973 case OP_ICOMPARE_IMM:
974 ins->type = bin_comp_table [src1->type] [src1->type] ? STACK_I4 : STACK_INV;
975 if ((src1->type == STACK_I8) || ((SIZEOF_VOID_P == 8) && ((src1->type == STACK_PTR) || (src1->type == STACK_OBJ) || (src1->type == STACK_MP))))
976 ins->opcode = OP_LCOMPARE_IMM;
988 ins->opcode += beqops_op_map [src1->type];
991 ins->type = bin_comp_table [src1->type] [src2->type] ? STACK_I4: STACK_INV;
992 ins->opcode += ceqops_op_map [src1->type];
998 ins->type = (bin_comp_table [src1->type] [src2->type] & 1) ? STACK_I4: STACK_INV;
999 ins->opcode += ceqops_op_map [src1->type];
1003 ins->type = neg_table [src1->type];
1004 ins->opcode += unops_op_map [ins->type];
1007 if (src1->type >= STACK_I4 && src1->type <= STACK_PTR)
1008 ins->type = src1->type;
1010 ins->type = STACK_INV;
1011 ins->opcode += unops_op_map [ins->type];
1017 ins->type = STACK_I4;
1018 ins->opcode += unops_op_map [src1->type];
1021 ins->type = STACK_R8;
1022 switch (src1->type) {
1025 ins->opcode = OP_ICONV_TO_R_UN;
1028 ins->opcode = OP_LCONV_TO_R_UN;
1032 case CEE_CONV_OVF_I1:
1033 case CEE_CONV_OVF_U1:
1034 case CEE_CONV_OVF_I2:
1035 case CEE_CONV_OVF_U2:
1036 case CEE_CONV_OVF_I4:
1037 case CEE_CONV_OVF_U4:
1038 ins->type = STACK_I4;
1039 ins->opcode += ovf3ops_op_map [src1->type];
1041 case CEE_CONV_OVF_I_UN:
1042 case CEE_CONV_OVF_U_UN:
1043 ins->type = STACK_PTR;
1044 ins->opcode += ovf2ops_op_map [src1->type];
1046 case CEE_CONV_OVF_I1_UN:
1047 case CEE_CONV_OVF_I2_UN:
1048 case CEE_CONV_OVF_I4_UN:
1049 case CEE_CONV_OVF_U1_UN:
1050 case CEE_CONV_OVF_U2_UN:
1051 case CEE_CONV_OVF_U4_UN:
1052 ins->type = STACK_I4;
1053 ins->opcode += ovf2ops_op_map [src1->type];
1056 ins->type = STACK_PTR;
1057 switch (src1->type) {
1059 ins->opcode = OP_ICONV_TO_U;
1063 #if SIZEOF_VOID_P == 8
1064 ins->opcode = OP_LCONV_TO_U;
1066 ins->opcode = OP_MOVE;
1070 ins->opcode = OP_LCONV_TO_U;
1073 ins->opcode = OP_FCONV_TO_U;
1079 ins->type = STACK_I8;
1080 ins->opcode += unops_op_map [src1->type];
1082 case CEE_CONV_OVF_I8:
1083 case CEE_CONV_OVF_U8:
1084 ins->type = STACK_I8;
1085 ins->opcode += ovf3ops_op_map [src1->type];
1087 case CEE_CONV_OVF_U8_UN:
1088 case CEE_CONV_OVF_I8_UN:
1089 ins->type = STACK_I8;
1090 ins->opcode += ovf2ops_op_map [src1->type];
1093 ins->type = cfg->r4_stack_type;
1094 ins->opcode += unops_op_map [src1->type];
1097 ins->type = STACK_R8;
1098 ins->opcode += unops_op_map [src1->type];
1101 ins->type = STACK_R8;
1105 ins->type = STACK_I4;
1106 ins->opcode += ovfops_op_map [src1->type];
1109 case CEE_CONV_OVF_I:
1110 case CEE_CONV_OVF_U:
1111 ins->type = STACK_PTR;
1112 ins->opcode += ovfops_op_map [src1->type];
1115 case CEE_ADD_OVF_UN:
1117 case CEE_MUL_OVF_UN:
1119 case CEE_SUB_OVF_UN:
1120 ins->type = bin_num_table [src1->type] [src2->type];
1121 ins->opcode += ovfops_op_map [src1->type];
1122 if (ins->type == STACK_R8)
1123 ins->type = STACK_INV;
1125 case OP_LOAD_MEMBASE:
1126 ins->type = STACK_PTR;
1128 case OP_LOADI1_MEMBASE:
1129 case OP_LOADU1_MEMBASE:
1130 case OP_LOADI2_MEMBASE:
1131 case OP_LOADU2_MEMBASE:
1132 case OP_LOADI4_MEMBASE:
1133 case OP_LOADU4_MEMBASE:
1134 ins->type = STACK_PTR;
1136 case OP_LOADI8_MEMBASE:
1137 ins->type = STACK_I8;
1139 case OP_LOADR4_MEMBASE:
1140 ins->type = cfg->r4_stack_type;
1142 case OP_LOADR8_MEMBASE:
1143 ins->type = STACK_R8;
1146 g_error ("opcode 0x%04x not handled in type from op", ins->opcode);
1150 if (ins->type == STACK_MP)
1151 ins->klass = mono_defaults.object_class;
1156 STACK_I4, STACK_I4, STACK_I4, STACK_I4, STACK_I4, STACK_I4, STACK_I8, STACK_PTR, STACK_R8, STACK_R8, STACK_OBJ
1162 param_table [STACK_MAX] [STACK_MAX] = {
1167 check_values_to_signature (MonoInst *args, MonoType *this, MonoMethodSignature *sig) {
1171 switch (args->type) {
1181 for (i = 0; i < sig->param_count; ++i) {
1182 switch (args [i].type) {
1186 if (!sig->params [i]->byref)
1190 if (sig->params [i]->byref)
1192 switch (sig->params [i]->type) {
1193 case MONO_TYPE_CLASS:
1194 case MONO_TYPE_STRING:
1195 case MONO_TYPE_OBJECT:
1196 case MONO_TYPE_SZARRAY:
1197 case MONO_TYPE_ARRAY:
1204 if (sig->params [i]->byref)
1206 if (sig->params [i]->type != MONO_TYPE_R4 && sig->params [i]->type != MONO_TYPE_R8)
1215 /*if (!param_table [args [i].type] [sig->params [i]->type])
1223 * When we need a pointer to the current domain many times in a method, we
1224 * call mono_domain_get() once and we store the result in a local variable.
1225 * This function returns the variable that represents the MonoDomain*.
1227 inline static MonoInst *
1228 mono_get_domainvar (MonoCompile *cfg)
1230 if (!cfg->domainvar)
1231 cfg->domainvar = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
1232 return cfg->domainvar;
1236 * The got_var contains the address of the Global Offset Table when AOT
1240 mono_get_got_var (MonoCompile *cfg)
1242 #ifdef MONO_ARCH_NEED_GOT_VAR
1243 if (!cfg->compile_aot)
1245 if (!cfg->got_var) {
1246 cfg->got_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
1248 return cfg->got_var;
1255 mono_get_vtable_var (MonoCompile *cfg)
1257 g_assert (cfg->generic_sharing_context);
1259 if (!cfg->rgctx_var) {
1260 cfg->rgctx_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
1261 /* force the var to be stack allocated */
1262 cfg->rgctx_var->flags |= MONO_INST_VOLATILE;
1265 return cfg->rgctx_var;
1269 type_from_stack_type (MonoInst *ins) {
1270 switch (ins->type) {
1271 case STACK_I4: return &mono_defaults.int32_class->byval_arg;
1272 case STACK_I8: return &mono_defaults.int64_class->byval_arg;
1273 case STACK_PTR: return &mono_defaults.int_class->byval_arg;
1274 case STACK_R4: return &mono_defaults.single_class->byval_arg;
1275 case STACK_R8: return &mono_defaults.double_class->byval_arg;
1277 return &ins->klass->this_arg;
1278 case STACK_OBJ: return &mono_defaults.object_class->byval_arg;
1279 case STACK_VTYPE: return &ins->klass->byval_arg;
1281 g_error ("stack type %d to monotype not handled\n", ins->type);
1286 static G_GNUC_UNUSED int
1287 type_to_stack_type (MonoCompile *cfg, MonoType *t)
1289 t = mono_type_get_underlying_type (t);
1293 case MONO_TYPE_BOOLEAN:
1296 case MONO_TYPE_CHAR:
1303 case MONO_TYPE_FNPTR:
1305 case MONO_TYPE_CLASS:
1306 case MONO_TYPE_STRING:
1307 case MONO_TYPE_OBJECT:
1308 case MONO_TYPE_SZARRAY:
1309 case MONO_TYPE_ARRAY:
1315 return cfg->r4_stack_type;
1318 case MONO_TYPE_VALUETYPE:
1319 case MONO_TYPE_TYPEDBYREF:
1321 case MONO_TYPE_GENERICINST:
1322 if (mono_type_generic_inst_is_valuetype (t))
1328 g_assert_not_reached ();
1335 array_access_to_klass (int opcode)
1339 return mono_defaults.byte_class;
1341 return mono_defaults.uint16_class;
1344 return mono_defaults.int_class;
1347 return mono_defaults.sbyte_class;
1350 return mono_defaults.int16_class;
1353 return mono_defaults.int32_class;
1355 return mono_defaults.uint32_class;
1358 return mono_defaults.int64_class;
1361 return mono_defaults.single_class;
1364 return mono_defaults.double_class;
1365 case CEE_LDELEM_REF:
1366 case CEE_STELEM_REF:
1367 return mono_defaults.object_class;
1369 g_assert_not_reached ();
1375 * We try to share variables when possible
1378 mono_compile_get_interface_var (MonoCompile *cfg, int slot, MonoInst *ins)
1383 /* inlining can result in deeper stacks */
1384 if (slot >= cfg->header->max_stack)
1385 return mono_compile_create_var (cfg, type_from_stack_type (ins), OP_LOCAL);
1387 pos = ins->type - 1 + slot * STACK_MAX;
1389 switch (ins->type) {
1396 if ((vnum = cfg->intvars [pos]))
1397 return cfg->varinfo [vnum];
1398 res = mono_compile_create_var (cfg, type_from_stack_type (ins), OP_LOCAL);
1399 cfg->intvars [pos] = res->inst_c0;
1402 res = mono_compile_create_var (cfg, type_from_stack_type (ins), OP_LOCAL);
1408 mono_save_token_info (MonoCompile *cfg, MonoImage *image, guint32 token, gpointer key)
1411 * Don't use this if a generic_context is set, since that means AOT can't
1412 * look up the method using just the image+token.
1413 * table == 0 means this is a reference made from a wrapper.
1415 if (cfg->compile_aot && !cfg->generic_context && (mono_metadata_token_table (token) > 0)) {
1416 MonoJumpInfoToken *jump_info_token = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoJumpInfoToken));
1417 jump_info_token->image = image;
1418 jump_info_token->token = token;
1419 g_hash_table_insert (cfg->token_info_hash, key, jump_info_token);
1424 * This function is called to handle items that are left on the evaluation stack
1425 * at basic block boundaries. What happens is that we save the values to local variables
1426 * and we reload them later when first entering the target basic block (with the
1427 * handle_loaded_temps () function).
1428 * A single joint point will use the same variables (stored in the array bb->out_stack or
1429 * bb->in_stack, if the basic block is before or after the joint point).
1431 * This function needs to be called _before_ emitting the last instruction of
1432 * the bb (i.e. before emitting a branch).
1433 * If the stack merge fails at a join point, cfg->unverifiable is set.
1436 handle_stack_args (MonoCompile *cfg, MonoInst **sp, int count)
1439 MonoBasicBlock *bb = cfg->cbb;
1440 MonoBasicBlock *outb;
1441 MonoInst *inst, **locals;
1446 if (cfg->verbose_level > 3)
1447 printf ("%d item(s) on exit from B%d\n", count, bb->block_num);
1448 if (!bb->out_scount) {
1449 bb->out_scount = count;
1450 //printf ("bblock %d has out:", bb->block_num);
1452 for (i = 0; i < bb->out_count; ++i) {
1453 outb = bb->out_bb [i];
1454 /* exception handlers are linked, but they should not be considered for stack args */
1455 if (outb->flags & BB_EXCEPTION_HANDLER)
1457 //printf (" %d", outb->block_num);
1458 if (outb->in_stack) {
1460 bb->out_stack = outb->in_stack;
1466 bb->out_stack = mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*) * count);
1467 for (i = 0; i < count; ++i) {
1469 * try to reuse temps already allocated for this purpouse, if they occupy the same
1470 * stack slot and if they are of the same type.
1471 * This won't cause conflicts since if 'local' is used to
1472 * store one of the values in the in_stack of a bblock, then
1473 * the same variable will be used for the same outgoing stack
1475 * This doesn't work when inlining methods, since the bblocks
1476 * in the inlined methods do not inherit their in_stack from
1477 * the bblock they are inlined to. See bug #58863 for an
1480 if (cfg->inlined_method)
1481 bb->out_stack [i] = mono_compile_create_var (cfg, type_from_stack_type (sp [i]), OP_LOCAL);
1483 bb->out_stack [i] = mono_compile_get_interface_var (cfg, i, sp [i]);
1488 for (i = 0; i < bb->out_count; ++i) {
1489 outb = bb->out_bb [i];
1490 /* exception handlers are linked, but they should not be considered for stack args */
1491 if (outb->flags & BB_EXCEPTION_HANDLER)
1493 if (outb->in_scount) {
1494 if (outb->in_scount != bb->out_scount) {
1495 cfg->unverifiable = TRUE;
1498 continue; /* check they are the same locals */
1500 outb->in_scount = count;
1501 outb->in_stack = bb->out_stack;
1504 locals = bb->out_stack;
1506 for (i = 0; i < count; ++i) {
1507 EMIT_NEW_TEMPSTORE (cfg, inst, locals [i]->inst_c0, sp [i]);
1508 inst->cil_code = sp [i]->cil_code;
1509 sp [i] = locals [i];
1510 if (cfg->verbose_level > 3)
1511 printf ("storing %d to temp %d\n", i, (int)locals [i]->inst_c0);
1515 * It is possible that the out bblocks already have in_stack assigned, and
1516 * the in_stacks differ. In this case, we will store to all the different
1523 /* Find a bblock which has a different in_stack */
1525 while (bindex < bb->out_count) {
1526 outb = bb->out_bb [bindex];
1527 /* exception handlers are linked, but they should not be considered for stack args */
1528 if (outb->flags & BB_EXCEPTION_HANDLER) {
1532 if (outb->in_stack != locals) {
1533 for (i = 0; i < count; ++i) {
1534 EMIT_NEW_TEMPSTORE (cfg, inst, outb->in_stack [i]->inst_c0, sp [i]);
1535 inst->cil_code = sp [i]->cil_code;
1536 sp [i] = locals [i];
1537 if (cfg->verbose_level > 3)
1538 printf ("storing %d to temp %d\n", i, (int)outb->in_stack [i]->inst_c0);
1540 locals = outb->in_stack;
1549 /* Emit code which loads interface_offsets [klass->interface_id]
1550 * The array is stored in memory before vtable.
1553 mini_emit_load_intf_reg_vtable (MonoCompile *cfg, int intf_reg, int vtable_reg, MonoClass *klass)
1555 if (cfg->compile_aot) {
1556 int ioffset_reg = alloc_preg (cfg);
1557 int iid_reg = alloc_preg (cfg);
1559 MONO_EMIT_NEW_AOTCONST (cfg, iid_reg, klass, MONO_PATCH_INFO_ADJUSTED_IID);
1560 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, ioffset_reg, iid_reg, vtable_reg);
1561 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, intf_reg, ioffset_reg, 0);
1564 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, intf_reg, vtable_reg, -((klass->interface_id + 1) * SIZEOF_VOID_P));
1569 mini_emit_interface_bitmap_check (MonoCompile *cfg, int intf_bit_reg, int base_reg, int offset, MonoClass *klass)
1571 int ibitmap_reg = alloc_preg (cfg);
1572 #ifdef COMPRESSED_INTERFACE_BITMAP
1574 MonoInst *res, *ins;
1575 NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, ibitmap_reg, base_reg, offset);
1576 MONO_ADD_INS (cfg->cbb, ins);
1578 if (cfg->compile_aot)
1579 EMIT_NEW_AOTCONST (cfg, args [1], MONO_PATCH_INFO_IID, klass);
1581 EMIT_NEW_ICONST (cfg, args [1], klass->interface_id);
1582 res = mono_emit_jit_icall (cfg, mono_class_interface_match, args);
1583 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, intf_bit_reg, res->dreg);
1585 int ibitmap_byte_reg = alloc_preg (cfg);
1587 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, ibitmap_reg, base_reg, offset);
1589 if (cfg->compile_aot) {
1590 int iid_reg = alloc_preg (cfg);
1591 int shifted_iid_reg = alloc_preg (cfg);
1592 int ibitmap_byte_address_reg = alloc_preg (cfg);
1593 int masked_iid_reg = alloc_preg (cfg);
1594 int iid_one_bit_reg = alloc_preg (cfg);
1595 int iid_bit_reg = alloc_preg (cfg);
1596 MONO_EMIT_NEW_AOTCONST (cfg, iid_reg, klass, MONO_PATCH_INFO_IID);
1597 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SHR_IMM, shifted_iid_reg, iid_reg, 3);
1598 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, ibitmap_byte_address_reg, ibitmap_reg, shifted_iid_reg);
1599 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU1_MEMBASE, ibitmap_byte_reg, ibitmap_byte_address_reg, 0);
1600 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_IAND_IMM, masked_iid_reg, iid_reg, 7);
1601 MONO_EMIT_NEW_ICONST (cfg, iid_one_bit_reg, 1);
1602 MONO_EMIT_NEW_BIALU (cfg, OP_ISHL, iid_bit_reg, iid_one_bit_reg, masked_iid_reg);
1603 MONO_EMIT_NEW_BIALU (cfg, OP_IAND, intf_bit_reg, ibitmap_byte_reg, iid_bit_reg);
1605 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI1_MEMBASE, ibitmap_byte_reg, ibitmap_reg, klass->interface_id >> 3);
1606 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_AND_IMM, intf_bit_reg, ibitmap_byte_reg, 1 << (klass->interface_id & 7));
1612 * Emit code which loads into "intf_bit_reg" a nonzero value if the MonoClass
1613 * stored in "klass_reg" implements the interface "klass".
1616 mini_emit_load_intf_bit_reg_class (MonoCompile *cfg, int intf_bit_reg, int klass_reg, MonoClass *klass)
1618 mini_emit_interface_bitmap_check (cfg, intf_bit_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, interface_bitmap), klass);
1622 * Emit code which loads into "intf_bit_reg" a nonzero value if the MonoVTable
1623 * stored in "vtable_reg" implements the interface "klass".
1626 mini_emit_load_intf_bit_reg_vtable (MonoCompile *cfg, int intf_bit_reg, int vtable_reg, MonoClass *klass)
1628 mini_emit_interface_bitmap_check (cfg, intf_bit_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, interface_bitmap), klass);
1632 * Emit code which checks whenever the interface id of @klass is smaller than
1633 * than the value given by max_iid_reg.
1636 mini_emit_max_iid_check (MonoCompile *cfg, int max_iid_reg, MonoClass *klass,
1637 MonoBasicBlock *false_target)
1639 if (cfg->compile_aot) {
1640 int iid_reg = alloc_preg (cfg);
1641 MONO_EMIT_NEW_AOTCONST (cfg, iid_reg, klass, MONO_PATCH_INFO_IID);
1642 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, max_iid_reg, iid_reg);
1645 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, max_iid_reg, klass->interface_id);
1647 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBLT_UN, false_target);
1649 MONO_EMIT_NEW_COND_EXC (cfg, LT_UN, "InvalidCastException");
1652 /* Same as above, but obtains max_iid from a vtable */
1654 mini_emit_max_iid_check_vtable (MonoCompile *cfg, int vtable_reg, MonoClass *klass,
1655 MonoBasicBlock *false_target)
1657 int max_iid_reg = alloc_preg (cfg);
1659 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU2_MEMBASE, max_iid_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, max_interface_id));
1660 mini_emit_max_iid_check (cfg, max_iid_reg, klass, false_target);
1663 /* Same as above, but obtains max_iid from a klass */
1665 mini_emit_max_iid_check_class (MonoCompile *cfg, int klass_reg, MonoClass *klass,
1666 MonoBasicBlock *false_target)
1668 int max_iid_reg = alloc_preg (cfg);
1670 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU2_MEMBASE, max_iid_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, max_interface_id));
1671 mini_emit_max_iid_check (cfg, max_iid_reg, klass, false_target);
1675 mini_emit_isninst_cast_inst (MonoCompile *cfg, int klass_reg, MonoClass *klass, MonoInst *klass_ins, MonoBasicBlock *false_target, MonoBasicBlock *true_target)
1677 int idepth_reg = alloc_preg (cfg);
1678 int stypes_reg = alloc_preg (cfg);
1679 int stype = alloc_preg (cfg);
1681 mono_class_setup_supertypes (klass);
1683 if (klass->idepth > MONO_DEFAULT_SUPERTABLE_SIZE) {
1684 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU2_MEMBASE, idepth_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, idepth));
1685 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, idepth_reg, klass->idepth);
1686 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBLT_UN, false_target);
1688 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, stypes_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, supertypes));
1689 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, stype, stypes_reg, ((klass->idepth - 1) * SIZEOF_VOID_P));
1691 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, stype, klass_ins->dreg);
1692 } else if (cfg->compile_aot) {
1693 int const_reg = alloc_preg (cfg);
1694 MONO_EMIT_NEW_CLASSCONST (cfg, const_reg, klass);
1695 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, stype, const_reg);
1697 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, stype, klass);
1699 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, true_target);
1703 mini_emit_isninst_cast (MonoCompile *cfg, int klass_reg, MonoClass *klass, MonoBasicBlock *false_target, MonoBasicBlock *true_target)
1705 mini_emit_isninst_cast_inst (cfg, klass_reg, klass, NULL, false_target, true_target);
1709 mini_emit_iface_cast (MonoCompile *cfg, int vtable_reg, MonoClass *klass, MonoBasicBlock *false_target, MonoBasicBlock *true_target)
1711 int intf_reg = alloc_preg (cfg);
1713 mini_emit_max_iid_check_vtable (cfg, vtable_reg, klass, false_target);
1714 mini_emit_load_intf_bit_reg_vtable (cfg, intf_reg, vtable_reg, klass);
1715 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, intf_reg, 0);
1717 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, true_target);
1719 MONO_EMIT_NEW_COND_EXC (cfg, EQ, "InvalidCastException");
1723 * Variant of the above that takes a register to the class, not the vtable.
1726 mini_emit_iface_class_cast (MonoCompile *cfg, int klass_reg, MonoClass *klass, MonoBasicBlock *false_target, MonoBasicBlock *true_target)
1728 int intf_bit_reg = alloc_preg (cfg);
1730 mini_emit_max_iid_check_class (cfg, klass_reg, klass, false_target);
1731 mini_emit_load_intf_bit_reg_class (cfg, intf_bit_reg, klass_reg, klass);
1732 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, intf_bit_reg, 0);
1734 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, true_target);
1736 MONO_EMIT_NEW_COND_EXC (cfg, EQ, "InvalidCastException");
1740 mini_emit_class_check_inst (MonoCompile *cfg, int klass_reg, MonoClass *klass, MonoInst *klass_inst)
1743 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, klass_reg, klass_inst->dreg);
1744 } else if (cfg->compile_aot) {
1745 int const_reg = alloc_preg (cfg);
1746 MONO_EMIT_NEW_CLASSCONST (cfg, const_reg, klass);
1747 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, klass_reg, const_reg);
1749 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, klass_reg, klass);
1751 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException");
1755 mini_emit_class_check (MonoCompile *cfg, int klass_reg, MonoClass *klass)
1757 mini_emit_class_check_inst (cfg, klass_reg, klass, NULL);
1761 mini_emit_class_check_branch (MonoCompile *cfg, int klass_reg, MonoClass *klass, int branch_op, MonoBasicBlock *target)
1763 if (cfg->compile_aot) {
1764 int const_reg = alloc_preg (cfg);
1765 MONO_EMIT_NEW_CLASSCONST (cfg, const_reg, klass);
1766 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, klass_reg, const_reg);
1768 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, klass_reg, klass);
1770 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, branch_op, target);
1774 mini_emit_castclass (MonoCompile *cfg, int obj_reg, int klass_reg, MonoClass *klass, MonoBasicBlock *object_is_null);
1777 mini_emit_castclass_inst (MonoCompile *cfg, int obj_reg, int klass_reg, MonoClass *klass, MonoInst *klass_inst, MonoBasicBlock *object_is_null)
1780 int rank_reg = alloc_preg (cfg);
1781 int eclass_reg = alloc_preg (cfg);
1783 g_assert (!klass_inst);
1784 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU1_MEMBASE, rank_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, rank));
1785 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, rank_reg, klass->rank);
1786 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException");
1787 // MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
1788 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, eclass_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, cast_class));
1789 if (klass->cast_class == mono_defaults.object_class) {
1790 int parent_reg = alloc_preg (cfg);
1791 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, parent_reg, eclass_reg, MONO_STRUCT_OFFSET (MonoClass, parent));
1792 mini_emit_class_check_branch (cfg, parent_reg, mono_defaults.enum_class->parent, OP_PBNE_UN, object_is_null);
1793 mini_emit_class_check (cfg, eclass_reg, mono_defaults.enum_class);
1794 } else if (klass->cast_class == mono_defaults.enum_class->parent) {
1795 mini_emit_class_check_branch (cfg, eclass_reg, mono_defaults.enum_class->parent, OP_PBEQ, object_is_null);
1796 mini_emit_class_check (cfg, eclass_reg, mono_defaults.enum_class);
1797 } else if (klass->cast_class == mono_defaults.enum_class) {
1798 mini_emit_class_check (cfg, eclass_reg, mono_defaults.enum_class);
1799 } else if (klass->cast_class->flags & TYPE_ATTRIBUTE_INTERFACE) {
1800 mini_emit_iface_class_cast (cfg, eclass_reg, klass->cast_class, NULL, NULL);
1802 // Pass -1 as obj_reg to skip the check below for arrays of arrays
1803 mini_emit_castclass (cfg, -1, eclass_reg, klass->cast_class, object_is_null);
1806 if ((klass->rank == 1) && (klass->byval_arg.type == MONO_TYPE_SZARRAY) && (obj_reg != -1)) {
1807 /* Check that the object is a vector too */
1808 int bounds_reg = alloc_preg (cfg);
1809 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, bounds_reg, obj_reg, MONO_STRUCT_OFFSET (MonoArray, bounds));
1810 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, bounds_reg, 0);
1811 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException");
1814 int idepth_reg = alloc_preg (cfg);
1815 int stypes_reg = alloc_preg (cfg);
1816 int stype = alloc_preg (cfg);
1818 mono_class_setup_supertypes (klass);
1820 if (klass->idepth > MONO_DEFAULT_SUPERTABLE_SIZE) {
1821 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU2_MEMBASE, idepth_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, idepth));
1822 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, idepth_reg, klass->idepth);
1823 MONO_EMIT_NEW_COND_EXC (cfg, LT_UN, "InvalidCastException");
1825 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, stypes_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, supertypes));
1826 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, stype, stypes_reg, ((klass->idepth - 1) * SIZEOF_VOID_P));
1827 mini_emit_class_check_inst (cfg, stype, klass, klass_inst);
1832 mini_emit_castclass (MonoCompile *cfg, int obj_reg, int klass_reg, MonoClass *klass, MonoBasicBlock *object_is_null)
1834 mini_emit_castclass_inst (cfg, obj_reg, klass_reg, klass, NULL, object_is_null);
1838 mini_emit_memset (MonoCompile *cfg, int destreg, int offset, int size, int val, int align)
1842 g_assert (val == 0);
1847 if ((size <= SIZEOF_REGISTER) && (size <= align)) {
1850 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREI1_MEMBASE_IMM, destreg, offset, val);
1853 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREI2_MEMBASE_IMM, destreg, offset, val);
1856 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREI4_MEMBASE_IMM, destreg, offset, val);
1858 #if SIZEOF_REGISTER == 8
1860 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREI8_MEMBASE_IMM, destreg, offset, val);
1866 val_reg = alloc_preg (cfg);
1868 if (SIZEOF_REGISTER == 8)
1869 MONO_EMIT_NEW_I8CONST (cfg, val_reg, val);
1871 MONO_EMIT_NEW_ICONST (cfg, val_reg, val);
1874 /* This could be optimized further if neccesary */
1876 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI1_MEMBASE_REG, destreg, offset, val_reg);
1883 #if !NO_UNALIGNED_ACCESS
1884 if (SIZEOF_REGISTER == 8) {
1886 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI4_MEMBASE_REG, destreg, offset, val_reg);
1891 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI8_MEMBASE_REG, destreg, offset, val_reg);
1899 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI4_MEMBASE_REG, destreg, offset, val_reg);
1904 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI2_MEMBASE_REG, destreg, offset, val_reg);
1909 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI1_MEMBASE_REG, destreg, offset, val_reg);
1916 mini_emit_memcpy (MonoCompile *cfg, int destreg, int doffset, int srcreg, int soffset, int size, int align)
1923 /*FIXME arbitrary hack to avoid unbound code expansion.*/
1924 g_assert (size < 10000);
1927 /* This could be optimized further if neccesary */
1929 cur_reg = alloc_preg (cfg);
1930 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI1_MEMBASE, cur_reg, srcreg, soffset);
1931 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI1_MEMBASE_REG, destreg, doffset, cur_reg);
1938 #if !NO_UNALIGNED_ACCESS
1939 if (SIZEOF_REGISTER == 8) {
1941 cur_reg = alloc_preg (cfg);
1942 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI8_MEMBASE, cur_reg, srcreg, soffset);
1943 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI8_MEMBASE_REG, destreg, doffset, cur_reg);
1952 cur_reg = alloc_preg (cfg);
1953 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI4_MEMBASE, cur_reg, srcreg, soffset);
1954 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI4_MEMBASE_REG, destreg, doffset, cur_reg);
1960 cur_reg = alloc_preg (cfg);
1961 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI2_MEMBASE, cur_reg, srcreg, soffset);
1962 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI2_MEMBASE_REG, destreg, doffset, cur_reg);
1968 cur_reg = alloc_preg (cfg);
1969 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI1_MEMBASE, cur_reg, srcreg, soffset);
1970 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI1_MEMBASE_REG, destreg, doffset, cur_reg);
1978 emit_tls_set (MonoCompile *cfg, int sreg1, int tls_key)
1982 if (cfg->compile_aot) {
1983 EMIT_NEW_TLS_OFFSETCONST (cfg, c, tls_key);
1984 MONO_INST_NEW (cfg, ins, OP_TLS_SET_REG);
1986 ins->sreg2 = c->dreg;
1987 MONO_ADD_INS (cfg->cbb, ins);
1989 MONO_INST_NEW (cfg, ins, OP_TLS_SET);
1991 ins->inst_offset = mini_get_tls_offset (tls_key);
1992 MONO_ADD_INS (cfg->cbb, ins);
1999 * Emit IR to push the current LMF onto the LMF stack.
2002 emit_push_lmf (MonoCompile *cfg)
2005 * Emit IR to push the LMF:
2006 * lmf_addr = <lmf_addr from tls>
2007 * lmf->lmf_addr = lmf_addr
2008 * lmf->prev_lmf = *lmf_addr
2011 int lmf_reg, prev_lmf_reg;
2012 MonoInst *ins, *lmf_ins;
2017 if (cfg->lmf_ir_mono_lmf && mini_tls_get_supported (cfg, TLS_KEY_LMF)) {
2018 /* Load current lmf */
2019 lmf_ins = mono_get_lmf_intrinsic (cfg);
2021 MONO_ADD_INS (cfg->cbb, lmf_ins);
2022 EMIT_NEW_VARLOADA (cfg, ins, cfg->lmf_var, NULL);
2023 lmf_reg = ins->dreg;
2024 /* Save previous_lmf */
2025 EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, lmf_reg, MONO_STRUCT_OFFSET (MonoLMF, previous_lmf), lmf_ins->dreg);
2027 emit_tls_set (cfg, lmf_reg, TLS_KEY_LMF);
2030 * Store lmf_addr in a variable, so it can be allocated to a global register.
2032 if (!cfg->lmf_addr_var)
2033 cfg->lmf_addr_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
2036 ins = mono_get_jit_tls_intrinsic (cfg);
2038 int jit_tls_dreg = ins->dreg;
2040 MONO_ADD_INS (cfg->cbb, ins);
2041 lmf_reg = alloc_preg (cfg);
2042 EMIT_NEW_BIALU_IMM (cfg, lmf_ins, OP_PADD_IMM, lmf_reg, jit_tls_dreg, MONO_STRUCT_OFFSET (MonoJitTlsData, lmf));
2044 lmf_ins = mono_emit_jit_icall (cfg, mono_get_lmf_addr, NULL);
2047 lmf_ins = mono_get_lmf_addr_intrinsic (cfg);
2049 MONO_ADD_INS (cfg->cbb, lmf_ins);
2052 MonoInst *args [16], *jit_tls_ins, *ins;
2054 /* Inline mono_get_lmf_addr () */
2055 /* jit_tls = pthread_getspecific (mono_jit_tls_id); lmf_addr = &jit_tls->lmf; */
2057 /* Load mono_jit_tls_id */
2058 EMIT_NEW_AOTCONST (cfg, args [0], MONO_PATCH_INFO_JIT_TLS_ID, NULL);
2059 /* call pthread_getspecific () */
2060 jit_tls_ins = mono_emit_jit_icall (cfg, pthread_getspecific, args);
2061 /* lmf_addr = &jit_tls->lmf */
2062 EMIT_NEW_BIALU_IMM (cfg, ins, OP_PADD_IMM, cfg->lmf_addr_var->dreg, jit_tls_ins->dreg, MONO_STRUCT_OFFSET (MonoJitTlsData, lmf));
2065 lmf_ins = mono_emit_jit_icall (cfg, mono_get_lmf_addr, NULL);
2069 lmf_ins->dreg = cfg->lmf_addr_var->dreg;
2071 EMIT_NEW_VARLOADA (cfg, ins, cfg->lmf_var, NULL);
2072 lmf_reg = ins->dreg;
2074 prev_lmf_reg = alloc_preg (cfg);
2075 /* Save previous_lmf */
2076 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, prev_lmf_reg, cfg->lmf_addr_var->dreg, 0);
2077 EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, lmf_reg, MONO_STRUCT_OFFSET (MonoLMF, previous_lmf), prev_lmf_reg);
2079 EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, cfg->lmf_addr_var->dreg, 0, lmf_reg);
2086 * Emit IR to pop the current LMF from the LMF stack.
2089 emit_pop_lmf (MonoCompile *cfg)
2091 int lmf_reg, lmf_addr_reg, prev_lmf_reg;
2097 EMIT_NEW_VARLOADA (cfg, ins, cfg->lmf_var, NULL);
2098 lmf_reg = ins->dreg;
2100 if (cfg->lmf_ir_mono_lmf && mini_tls_get_supported (cfg, TLS_KEY_LMF)) {
2101 /* Load previous_lmf */
2102 prev_lmf_reg = alloc_preg (cfg);
2103 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, prev_lmf_reg, lmf_reg, MONO_STRUCT_OFFSET (MonoLMF, previous_lmf));
2105 emit_tls_set (cfg, prev_lmf_reg, TLS_KEY_LMF);
2108 * Emit IR to pop the LMF:
2109 * *(lmf->lmf_addr) = lmf->prev_lmf
2111 /* This could be called before emit_push_lmf () */
2112 if (!cfg->lmf_addr_var)
2113 cfg->lmf_addr_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
2114 lmf_addr_reg = cfg->lmf_addr_var->dreg;
2116 prev_lmf_reg = alloc_preg (cfg);
2117 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, prev_lmf_reg, lmf_reg, MONO_STRUCT_OFFSET (MonoLMF, previous_lmf));
2118 EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, lmf_addr_reg, 0, prev_lmf_reg);
2123 emit_instrumentation_call (MonoCompile *cfg, void *func)
2125 MonoInst *iargs [1];
2128 * Avoid instrumenting inlined methods since it can
2129 * distort profiling results.
2131 if (cfg->method != cfg->current_method)
2134 if (cfg->prof_options & MONO_PROFILE_ENTER_LEAVE) {
2135 EMIT_NEW_METHODCONST (cfg, iargs [0], cfg->method);
2136 mono_emit_jit_icall (cfg, func, iargs);
2141 ret_type_to_call_opcode (MonoCompile *cfg, MonoType *type, int calli, int virt, MonoGenericSharingContext *gsctx)
2144 return calli? OP_CALL_REG: virt? OP_CALL_MEMBASE: OP_CALL;
2147 type = mini_get_basic_type_from_generic (gsctx, type);
2148 type = mini_replace_type (type);
2149 switch (type->type) {
2150 case MONO_TYPE_VOID:
2151 return calli? OP_VOIDCALL_REG: virt? OP_VOIDCALL_MEMBASE: OP_VOIDCALL;
2154 case MONO_TYPE_BOOLEAN:
2157 case MONO_TYPE_CHAR:
2160 return calli? OP_CALL_REG: virt? OP_CALL_MEMBASE: OP_CALL;
2164 case MONO_TYPE_FNPTR:
2165 return calli? OP_CALL_REG: virt? OP_CALL_MEMBASE: OP_CALL;
2166 case MONO_TYPE_CLASS:
2167 case MONO_TYPE_STRING:
2168 case MONO_TYPE_OBJECT:
2169 case MONO_TYPE_SZARRAY:
2170 case MONO_TYPE_ARRAY:
2171 return calli? OP_CALL_REG: virt? OP_CALL_MEMBASE: OP_CALL;
2174 return calli? OP_LCALL_REG: virt? OP_LCALL_MEMBASE: OP_LCALL;
2177 return calli? OP_RCALL_REG: virt? OP_RCALL_MEMBASE: OP_RCALL;
2179 return calli? OP_FCALL_REG: virt? OP_FCALL_MEMBASE: OP_FCALL;
2181 return calli? OP_FCALL_REG: virt? OP_FCALL_MEMBASE: OP_FCALL;
2182 case MONO_TYPE_VALUETYPE:
2183 if (type->data.klass->enumtype) {
2184 type = mono_class_enum_basetype (type->data.klass);
2187 return calli? OP_VCALL_REG: virt? OP_VCALL_MEMBASE: OP_VCALL;
2188 case MONO_TYPE_TYPEDBYREF:
2189 return calli? OP_VCALL_REG: virt? OP_VCALL_MEMBASE: OP_VCALL;
2190 case MONO_TYPE_GENERICINST:
2191 type = &type->data.generic_class->container_class->byval_arg;
2194 case MONO_TYPE_MVAR:
2196 return calli? OP_VCALL_REG: virt? OP_VCALL_MEMBASE: OP_VCALL;
2198 g_error ("unknown type 0x%02x in ret_type_to_call_opcode", type->type);
2204 * target_type_is_incompatible:
2205 * @cfg: MonoCompile context
2207 * Check that the item @arg on the evaluation stack can be stored
2208 * in the target type (can be a local, or field, etc).
2209 * The cfg arg can be used to check if we need verification or just
2212 * Returns: non-0 value if arg can't be stored on a target.
2215 target_type_is_incompatible (MonoCompile *cfg, MonoType *target, MonoInst *arg)
2217 MonoType *simple_type;
2220 target = mini_replace_type (target);
2221 if (target->byref) {
2222 /* FIXME: check that the pointed to types match */
2223 if (arg->type == STACK_MP)
2224 return arg->klass != mono_class_from_mono_type (target);
2225 if (arg->type == STACK_PTR)
2230 simple_type = mono_type_get_underlying_type (target);
2231 switch (simple_type->type) {
2232 case MONO_TYPE_VOID:
2236 case MONO_TYPE_BOOLEAN:
2239 case MONO_TYPE_CHAR:
2242 if (arg->type != STACK_I4 && arg->type != STACK_PTR)
2246 /* STACK_MP is needed when setting pinned locals */
2247 if (arg->type != STACK_I4 && arg->type != STACK_PTR && arg->type != STACK_MP)
2252 case MONO_TYPE_FNPTR:
2254 * Some opcodes like ldloca returns 'transient pointers' which can be stored in
2255 * in native int. (#688008).
2257 if (arg->type != STACK_I4 && arg->type != STACK_PTR && arg->type != STACK_MP)
2260 case MONO_TYPE_CLASS:
2261 case MONO_TYPE_STRING:
2262 case MONO_TYPE_OBJECT:
2263 case MONO_TYPE_SZARRAY:
2264 case MONO_TYPE_ARRAY:
2265 if (arg->type != STACK_OBJ)
2267 /* FIXME: check type compatibility */
2271 if (arg->type != STACK_I8)
2275 if (arg->type != cfg->r4_stack_type)
2279 if (arg->type != STACK_R8)
2282 case MONO_TYPE_VALUETYPE:
2283 if (arg->type != STACK_VTYPE)
2285 klass = mono_class_from_mono_type (simple_type);
2286 if (klass != arg->klass)
2289 case MONO_TYPE_TYPEDBYREF:
2290 if (arg->type != STACK_VTYPE)
2292 klass = mono_class_from_mono_type (simple_type);
2293 if (klass != arg->klass)
2296 case MONO_TYPE_GENERICINST:
2297 if (mono_type_generic_inst_is_valuetype (simple_type)) {
2298 if (arg->type != STACK_VTYPE)
2300 klass = mono_class_from_mono_type (simple_type);
2301 if (klass != arg->klass)
2305 if (arg->type != STACK_OBJ)
2307 /* FIXME: check type compatibility */
2311 case MONO_TYPE_MVAR:
2312 g_assert (cfg->generic_sharing_context);
2313 if (mini_type_var_is_vt (cfg, simple_type)) {
2314 if (arg->type != STACK_VTYPE)
2317 if (arg->type != STACK_OBJ)
2322 g_error ("unknown type 0x%02x in target_type_is_incompatible", simple_type->type);
2328 * Prepare arguments for passing to a function call.
2329 * Return a non-zero value if the arguments can't be passed to the given
2331 * The type checks are not yet complete and some conversions may need
2332 * casts on 32 or 64 bit architectures.
2334 * FIXME: implement this using target_type_is_incompatible ()
2337 check_call_signature (MonoCompile *cfg, MonoMethodSignature *sig, MonoInst **args)
2339 MonoType *simple_type;
2343 if (args [0]->type != STACK_OBJ && args [0]->type != STACK_MP && args [0]->type != STACK_PTR)
2347 for (i = 0; i < sig->param_count; ++i) {
2348 if (sig->params [i]->byref) {
2349 if (args [i]->type != STACK_MP && args [i]->type != STACK_PTR)
2353 simple_type = sig->params [i];
2354 simple_type = mini_get_basic_type_from_generic (cfg->generic_sharing_context, simple_type);
2356 switch (simple_type->type) {
2357 case MONO_TYPE_VOID:
2362 case MONO_TYPE_BOOLEAN:
2365 case MONO_TYPE_CHAR:
2368 if (args [i]->type != STACK_I4 && args [i]->type != STACK_PTR)
2374 case MONO_TYPE_FNPTR:
2375 if (args [i]->type != STACK_I4 && args [i]->type != STACK_PTR && args [i]->type != STACK_MP && args [i]->type != STACK_OBJ)
2378 case MONO_TYPE_CLASS:
2379 case MONO_TYPE_STRING:
2380 case MONO_TYPE_OBJECT:
2381 case MONO_TYPE_SZARRAY:
2382 case MONO_TYPE_ARRAY:
2383 if (args [i]->type != STACK_OBJ)
2388 if (args [i]->type != STACK_I8)
2392 if (args [i]->type != cfg->r4_stack_type)
2396 if (args [i]->type != STACK_R8)
2399 case MONO_TYPE_VALUETYPE:
2400 if (simple_type->data.klass->enumtype) {
2401 simple_type = mono_class_enum_basetype (simple_type->data.klass);
2404 if (args [i]->type != STACK_VTYPE)
2407 case MONO_TYPE_TYPEDBYREF:
2408 if (args [i]->type != STACK_VTYPE)
2411 case MONO_TYPE_GENERICINST:
2412 simple_type = &simple_type->data.generic_class->container_class->byval_arg;
2415 case MONO_TYPE_MVAR:
2417 if (args [i]->type != STACK_VTYPE)
2421 g_error ("unknown type 0x%02x in check_call_signature",
2429 callvirt_to_call (int opcode)
2432 case OP_CALL_MEMBASE:
2434 case OP_VOIDCALL_MEMBASE:
2436 case OP_FCALL_MEMBASE:
2438 case OP_RCALL_MEMBASE:
2440 case OP_VCALL_MEMBASE:
2442 case OP_LCALL_MEMBASE:
2445 g_assert_not_reached ();
2451 /* Either METHOD or IMT_ARG needs to be set */
2453 emit_imt_argument (MonoCompile *cfg, MonoCallInst *call, MonoMethod *method, MonoInst *imt_arg)
2457 if (COMPILE_LLVM (cfg)) {
2458 method_reg = alloc_preg (cfg);
2461 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, method_reg, imt_arg->dreg);
2462 } else if (cfg->compile_aot) {
2463 MONO_EMIT_NEW_AOTCONST (cfg, method_reg, method, MONO_PATCH_INFO_METHODCONST);
2466 MONO_INST_NEW (cfg, ins, OP_PCONST);
2467 ins->inst_p0 = method;
2468 ins->dreg = method_reg;
2469 MONO_ADD_INS (cfg->cbb, ins);
2473 call->imt_arg_reg = method_reg;
2475 #ifdef MONO_ARCH_IMT_REG
2476 mono_call_inst_add_outarg_reg (cfg, call, method_reg, MONO_ARCH_IMT_REG, FALSE);
2478 /* Need this to keep the IMT arg alive */
2479 mono_call_inst_add_outarg_reg (cfg, call, method_reg, 0, FALSE);
2484 #ifdef MONO_ARCH_IMT_REG
2485 method_reg = alloc_preg (cfg);
2488 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, method_reg, imt_arg->dreg);
2489 } else if (cfg->compile_aot) {
2490 MONO_EMIT_NEW_AOTCONST (cfg, method_reg, method, MONO_PATCH_INFO_METHODCONST);
2493 MONO_INST_NEW (cfg, ins, OP_PCONST);
2494 ins->inst_p0 = method;
2495 ins->dreg = method_reg;
2496 MONO_ADD_INS (cfg->cbb, ins);
2499 mono_call_inst_add_outarg_reg (cfg, call, method_reg, MONO_ARCH_IMT_REG, FALSE);
2501 mono_arch_emit_imt_argument (cfg, call, imt_arg);
2505 static MonoJumpInfo *
2506 mono_patch_info_new (MonoMemPool *mp, int ip, MonoJumpInfoType type, gconstpointer target)
2508 MonoJumpInfo *ji = mono_mempool_alloc (mp, sizeof (MonoJumpInfo));
2512 ji->data.target = target;
2518 mini_class_check_context_used (MonoCompile *cfg, MonoClass *klass)
2520 if (cfg->generic_sharing_context)
2521 return mono_class_check_context_used (klass);
2527 mini_method_check_context_used (MonoCompile *cfg, MonoMethod *method)
2529 if (cfg->generic_sharing_context)
2530 return mono_method_check_context_used (method);
2536 * check_method_sharing:
2538 * Check whenever the vtable or an mrgctx needs to be passed when calling CMETHOD.
2541 check_method_sharing (MonoCompile *cfg, MonoMethod *cmethod, gboolean *out_pass_vtable, gboolean *out_pass_mrgctx)
2543 gboolean pass_vtable = FALSE;
2544 gboolean pass_mrgctx = FALSE;
2546 if (((cmethod->flags & METHOD_ATTRIBUTE_STATIC) || cmethod->klass->valuetype) &&
2547 (cmethod->klass->generic_class || cmethod->klass->generic_container)) {
2548 gboolean sharable = FALSE;
2550 if (mono_method_is_generic_sharable (cmethod, TRUE)) {
2553 gboolean sharing_enabled = mono_class_generic_sharing_enabled (cmethod->klass);
2554 MonoGenericContext *context = mini_class_get_context (cmethod->klass);
2555 gboolean context_sharable = mono_generic_context_is_sharable (context, TRUE);
2557 sharable = sharing_enabled && context_sharable;
2561 * Pass vtable iff target method might
2562 * be shared, which means that sharing
2563 * is enabled for its class and its
2564 * context is sharable (and it's not a
2567 if (sharable && !(mini_method_get_context (cmethod) && mini_method_get_context (cmethod)->method_inst))
2571 if (mini_method_get_context (cmethod) &&
2572 mini_method_get_context (cmethod)->method_inst) {
2573 g_assert (!pass_vtable);
2575 if (mono_method_is_generic_sharable (cmethod, TRUE)) {
2578 gboolean sharing_enabled = mono_class_generic_sharing_enabled (cmethod->klass);
2579 MonoGenericContext *context = mini_method_get_context (cmethod);
2580 gboolean context_sharable = mono_generic_context_is_sharable (context, TRUE);
2582 if (sharing_enabled && context_sharable)
2584 if (cfg->gsharedvt && mini_is_gsharedvt_signature (cfg, mono_method_signature (cmethod)))
2589 if (out_pass_vtable)
2590 *out_pass_vtable = pass_vtable;
2591 if (out_pass_mrgctx)
2592 *out_pass_mrgctx = pass_mrgctx;
2595 inline static MonoCallInst *
2596 mono_emit_call_args (MonoCompile *cfg, MonoMethodSignature *sig,
2597 MonoInst **args, int calli, int virtual, int tail, int rgctx, int unbox_trampoline)
2601 #ifdef MONO_ARCH_SOFT_FLOAT_FALLBACK
2606 emit_instrumentation_call (cfg, mono_profiler_method_leave);
2608 MONO_INST_NEW_CALL (cfg, call, OP_TAILCALL);
2610 MONO_INST_NEW_CALL (cfg, call, ret_type_to_call_opcode (cfg, sig->ret, calli, virtual, cfg->generic_sharing_context));
2613 call->signature = sig;
2614 call->rgctx_reg = rgctx;
2615 sig_ret = mini_replace_type (sig->ret);
2617 type_to_eval_stack_type ((cfg), sig_ret, &call->inst);
2620 if (mini_type_is_vtype (cfg, sig_ret)) {
2621 call->vret_var = cfg->vret_addr;
2622 //g_assert_not_reached ();
2624 } else if (mini_type_is_vtype (cfg, sig_ret)) {
2625 MonoInst *temp = mono_compile_create_var (cfg, sig_ret, OP_LOCAL);
2628 temp->backend.is_pinvoke = sig->pinvoke;
2631 * We use a new opcode OP_OUTARG_VTRETADDR instead of LDADDR for emitting the
2632 * address of return value to increase optimization opportunities.
2633 * Before vtype decomposition, the dreg of the call ins itself represents the
2634 * fact the call modifies the return value. After decomposition, the call will
2635 * be transformed into one of the OP_VOIDCALL opcodes, and the VTRETADDR opcode
2636 * will be transformed into an LDADDR.
2638 MONO_INST_NEW (cfg, loada, OP_OUTARG_VTRETADDR);
2639 loada->dreg = alloc_preg (cfg);
2640 loada->inst_p0 = temp;
2641 /* We reference the call too since call->dreg could change during optimization */
2642 loada->inst_p1 = call;
2643 MONO_ADD_INS (cfg->cbb, loada);
2645 call->inst.dreg = temp->dreg;
2647 call->vret_var = loada;
2648 } else if (!MONO_TYPE_IS_VOID (sig_ret))
2649 call->inst.dreg = alloc_dreg (cfg, call->inst.type);
2651 #ifdef MONO_ARCH_SOFT_FLOAT_FALLBACK
2652 if (COMPILE_SOFT_FLOAT (cfg)) {
2654 * If the call has a float argument, we would need to do an r8->r4 conversion using
2655 * an icall, but that cannot be done during the call sequence since it would clobber
2656 * the call registers + the stack. So we do it before emitting the call.
2658 for (i = 0; i < sig->param_count + sig->hasthis; ++i) {
2660 MonoInst *in = call->args [i];
2662 if (i >= sig->hasthis)
2663 t = sig->params [i - sig->hasthis];
2665 t = &mono_defaults.int_class->byval_arg;
2666 t = mono_type_get_underlying_type (t);
2668 if (!t->byref && t->type == MONO_TYPE_R4) {
2669 MonoInst *iargs [1];
2673 conv = mono_emit_jit_icall (cfg, mono_fload_r4_arg, iargs);
2675 /* The result will be in an int vreg */
2676 call->args [i] = conv;
2682 call->need_unbox_trampoline = unbox_trampoline;
2685 if (COMPILE_LLVM (cfg))
2686 mono_llvm_emit_call (cfg, call);
2688 mono_arch_emit_call (cfg, call);
2690 mono_arch_emit_call (cfg, call);
2693 cfg->param_area = MAX (cfg->param_area, call->stack_usage);
2694 cfg->flags |= MONO_CFG_HAS_CALLS;
2700 set_rgctx_arg (MonoCompile *cfg, MonoCallInst *call, int rgctx_reg, MonoInst *rgctx_arg)
2702 #ifdef MONO_ARCH_RGCTX_REG
2703 mono_call_inst_add_outarg_reg (cfg, call, rgctx_reg, MONO_ARCH_RGCTX_REG, FALSE);
2704 cfg->uses_rgctx_reg = TRUE;
2705 call->rgctx_reg = TRUE;
2707 call->rgctx_arg_reg = rgctx_reg;
2714 inline static MonoInst*
2715 mono_emit_calli (MonoCompile *cfg, MonoMethodSignature *sig, MonoInst **args, MonoInst *addr, MonoInst *imt_arg, MonoInst *rgctx_arg)
2720 gboolean check_sp = FALSE;
2722 if (cfg->check_pinvoke_callconv && cfg->method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE) {
2723 WrapperInfo *info = mono_marshal_get_wrapper_info (cfg->method);
2725 if (info && info->subtype == WRAPPER_SUBTYPE_PINVOKE)
2730 rgctx_reg = mono_alloc_preg (cfg);
2731 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, rgctx_reg, rgctx_arg->dreg);
2735 if (!cfg->stack_inbalance_var)
2736 cfg->stack_inbalance_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
2738 MONO_INST_NEW (cfg, ins, OP_GET_SP);
2739 ins->dreg = cfg->stack_inbalance_var->dreg;
2740 MONO_ADD_INS (cfg->cbb, ins);
2743 call = mono_emit_call_args (cfg, sig, args, TRUE, FALSE, FALSE, rgctx_arg ? TRUE : FALSE, FALSE);
2745 call->inst.sreg1 = addr->dreg;
2748 emit_imt_argument (cfg, call, NULL, imt_arg);
2750 MONO_ADD_INS (cfg->cbb, (MonoInst*)call);
2755 sp_reg = mono_alloc_preg (cfg);
2757 MONO_INST_NEW (cfg, ins, OP_GET_SP);
2759 MONO_ADD_INS (cfg->cbb, ins);
2761 /* Restore the stack so we don't crash when throwing the exception */
2762 MONO_INST_NEW (cfg, ins, OP_SET_SP);
2763 ins->sreg1 = cfg->stack_inbalance_var->dreg;
2764 MONO_ADD_INS (cfg->cbb, ins);
2766 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, cfg->stack_inbalance_var->dreg, sp_reg);
2767 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "ExecutionEngineException");
2771 set_rgctx_arg (cfg, call, rgctx_reg, rgctx_arg);
2773 return (MonoInst*)call;
2777 emit_get_gsharedvt_info_klass (MonoCompile *cfg, MonoClass *klass, MonoRgctxInfoType rgctx_type);
2780 emit_get_rgctx_method (MonoCompile *cfg, int context_used, MonoMethod *cmethod, MonoRgctxInfoType rgctx_type);
2782 emit_get_rgctx_klass (MonoCompile *cfg, int context_used, MonoClass *klass, MonoRgctxInfoType rgctx_type);
2785 mono_emit_method_call_full (MonoCompile *cfg, MonoMethod *method, MonoMethodSignature *sig, gboolean tail,
2786 MonoInst **args, MonoInst *this, MonoInst *imt_arg, MonoInst *rgctx_arg)
2788 #ifndef DISABLE_REMOTING
2789 gboolean might_be_remote = FALSE;
2791 gboolean virtual = this != NULL;
2792 gboolean enable_for_aot = TRUE;
2796 gboolean need_unbox_trampoline;
2799 sig = mono_method_signature (method);
2802 rgctx_reg = mono_alloc_preg (cfg);
2803 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, rgctx_reg, rgctx_arg->dreg);
2806 if (method->string_ctor) {
2807 /* Create the real signature */
2808 /* FIXME: Cache these */
2809 MonoMethodSignature *ctor_sig = mono_metadata_signature_dup_mempool (cfg->mempool, sig);
2810 ctor_sig->ret = &mono_defaults.string_class->byval_arg;
2815 context_used = mini_method_check_context_used (cfg, method);
2817 #ifndef DISABLE_REMOTING
2818 might_be_remote = this && sig->hasthis &&
2819 (mono_class_is_marshalbyref (method->klass) || method->klass == mono_defaults.object_class) &&
2820 !(method->flags & METHOD_ATTRIBUTE_VIRTUAL) && (!MONO_CHECK_THIS (this) || context_used);
2822 if (might_be_remote && context_used) {
2825 g_assert (cfg->generic_sharing_context);
2827 addr = emit_get_rgctx_method (cfg, context_used, method, MONO_RGCTX_INFO_REMOTING_INVOKE_WITH_CHECK);
2829 return mono_emit_calli (cfg, sig, args, addr, NULL, NULL);
2833 need_unbox_trampoline = method->klass == mono_defaults.object_class || (method->klass->flags & TYPE_ATTRIBUTE_INTERFACE);
2835 call = mono_emit_call_args (cfg, sig, args, FALSE, virtual, tail, rgctx_arg ? TRUE : FALSE, need_unbox_trampoline);
2837 #ifndef DISABLE_REMOTING
2838 if (might_be_remote)
2839 call->method = mono_marshal_get_remoting_invoke_with_check (method);
2842 call->method = method;
2843 call->inst.flags |= MONO_INST_HAS_METHOD;
2844 call->inst.inst_left = this;
2845 call->tail_call = tail;
2848 int vtable_reg, slot_reg, this_reg;
2851 this_reg = this->dreg;
2853 if (ARCH_HAVE_DELEGATE_TRAMPOLINES && (method->klass->parent == mono_defaults.multicastdelegate_class) && !strcmp (method->name, "Invoke")) {
2854 MonoInst *dummy_use;
2856 MONO_EMIT_NULL_CHECK (cfg, this_reg);
2858 /* Make a call to delegate->invoke_impl */
2859 call->inst.inst_basereg = this_reg;
2860 call->inst.inst_offset = MONO_STRUCT_OFFSET (MonoDelegate, invoke_impl);
2861 MONO_ADD_INS (cfg->cbb, (MonoInst*)call);
2863 /* We must emit a dummy use here because the delegate trampoline will
2864 replace the 'this' argument with the delegate target making this activation
2865 no longer a root for the delegate.
2866 This is an issue for delegates that target collectible code such as dynamic
2867 methods of GC'able assemblies.
2869 For a test case look into #667921.
2871 FIXME: a dummy use is not the best way to do it as the local register allocator
2872 will put it on a caller save register and spil it around the call.
2873 Ideally, we would either put it on a callee save register or only do the store part.
2875 EMIT_NEW_DUMMY_USE (cfg, dummy_use, args [0]);
2877 return (MonoInst*)call;
2880 if ((!cfg->compile_aot || enable_for_aot) &&
2881 (!(method->flags & METHOD_ATTRIBUTE_VIRTUAL) ||
2882 (MONO_METHOD_IS_FINAL (method) &&
2883 method->wrapper_type != MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK)) &&
2884 !(mono_class_is_marshalbyref (method->klass) && context_used)) {
2886 * the method is not virtual, we just need to ensure this is not null
2887 * and then we can call the method directly.
2889 #ifndef DISABLE_REMOTING
2890 if (mono_class_is_marshalbyref (method->klass) || method->klass == mono_defaults.object_class) {
2892 * The check above ensures method is not gshared, this is needed since
2893 * gshared methods can't have wrappers.
2895 method = call->method = mono_marshal_get_remoting_invoke_with_check (method);
2899 if (!method->string_ctor)
2900 MONO_EMIT_NEW_CHECK_THIS (cfg, this_reg);
2902 call->inst.opcode = callvirt_to_call (call->inst.opcode);
2903 } else if ((method->flags & METHOD_ATTRIBUTE_VIRTUAL) && MONO_METHOD_IS_FINAL (method)) {
2905 * the method is virtual, but we can statically dispatch since either
2906 * it's class or the method itself are sealed.
2907 * But first we need to ensure it's not a null reference.
2909 MONO_EMIT_NEW_CHECK_THIS (cfg, this_reg);
2911 call->inst.opcode = callvirt_to_call (call->inst.opcode);
2913 vtable_reg = alloc_preg (cfg);
2914 MONO_EMIT_NEW_LOAD_MEMBASE_FAULT (cfg, vtable_reg, this_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
2915 if (method->klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
2918 guint32 imt_slot = mono_method_get_imt_slot (method);
2919 emit_imt_argument (cfg, call, call->method, imt_arg);
2920 slot_reg = vtable_reg;
2921 offset = ((gint32)imt_slot - MONO_IMT_SIZE) * SIZEOF_VOID_P;
2923 if (slot_reg == -1) {
2924 slot_reg = alloc_preg (cfg);
2925 mini_emit_load_intf_reg_vtable (cfg, slot_reg, vtable_reg, method->klass);
2926 offset = mono_method_get_vtable_index (method) * SIZEOF_VOID_P;
2929 slot_reg = vtable_reg;
2930 offset = MONO_STRUCT_OFFSET (MonoVTable, vtable) +
2931 ((mono_method_get_vtable_index (method)) * (SIZEOF_VOID_P));
2933 g_assert (mono_method_signature (method)->generic_param_count);
2934 emit_imt_argument (cfg, call, call->method, imt_arg);
2938 call->inst.sreg1 = slot_reg;
2939 call->inst.inst_offset = offset;
2940 call->virtual = TRUE;
2944 MONO_ADD_INS (cfg->cbb, (MonoInst*)call);
2947 set_rgctx_arg (cfg, call, rgctx_reg, rgctx_arg);
2949 return (MonoInst*)call;
2953 mono_emit_method_call (MonoCompile *cfg, MonoMethod *method, MonoInst **args, MonoInst *this)
2955 return mono_emit_method_call_full (cfg, method, mono_method_signature (method), FALSE, args, this, NULL, NULL);
2959 mono_emit_native_call (MonoCompile *cfg, gconstpointer func, MonoMethodSignature *sig,
2966 call = mono_emit_call_args (cfg, sig, args, FALSE, FALSE, FALSE, FALSE, FALSE);
2969 MONO_ADD_INS (cfg->cbb, (MonoInst*)call);
2971 return (MonoInst*)call;
2975 mono_emit_jit_icall (MonoCompile *cfg, gconstpointer func, MonoInst **args)
2977 MonoJitICallInfo *info = mono_find_jit_icall_by_addr (func);
2981 return mono_emit_native_call (cfg, mono_icall_get_wrapper (info), info->sig, args);
2985 * mono_emit_abs_call:
2987 * Emit a call to the runtime function described by PATCH_TYPE and DATA.
2989 inline static MonoInst*
2990 mono_emit_abs_call (MonoCompile *cfg, MonoJumpInfoType patch_type, gconstpointer data,
2991 MonoMethodSignature *sig, MonoInst **args)
2993 MonoJumpInfo *ji = mono_patch_info_new (cfg->mempool, 0, patch_type, data);
2997 * We pass ji as the call address, the PATCH_INFO_ABS resolving code will
3000 if (cfg->abs_patches == NULL)
3001 cfg->abs_patches = g_hash_table_new (NULL, NULL);
3002 g_hash_table_insert (cfg->abs_patches, ji, ji);
3003 ins = mono_emit_native_call (cfg, ji, sig, args);
3004 ((MonoCallInst*)ins)->fptr_is_patch = TRUE;
3009 mono_emit_widen_call_res (MonoCompile *cfg, MonoInst *ins, MonoMethodSignature *fsig)
3011 if (!MONO_TYPE_IS_VOID (fsig->ret)) {
3012 if ((fsig->pinvoke || LLVM_ENABLED) && !fsig->ret->byref) {
3016 * Native code might return non register sized integers
3017 * without initializing the upper bits.
3019 switch (mono_type_to_load_membase (cfg, fsig->ret)) {
3020 case OP_LOADI1_MEMBASE:
3021 widen_op = OP_ICONV_TO_I1;
3023 case OP_LOADU1_MEMBASE:
3024 widen_op = OP_ICONV_TO_U1;
3026 case OP_LOADI2_MEMBASE:
3027 widen_op = OP_ICONV_TO_I2;
3029 case OP_LOADU2_MEMBASE:
3030 widen_op = OP_ICONV_TO_U2;
3036 if (widen_op != -1) {
3037 int dreg = alloc_preg (cfg);
3040 EMIT_NEW_UNALU (cfg, widen, widen_op, dreg, ins->dreg);
3041 widen->type = ins->type;
3051 get_memcpy_method (void)
3053 static MonoMethod *memcpy_method = NULL;
3054 if (!memcpy_method) {
3055 memcpy_method = mono_class_get_method_from_name (mono_defaults.string_class, "memcpy", 3);
3057 g_error ("Old corlib found. Install a new one");
3059 return memcpy_method;
3063 create_write_barrier_bitmap (MonoCompile *cfg, MonoClass *klass, unsigned *wb_bitmap, int offset)
3065 MonoClassField *field;
3066 gpointer iter = NULL;
3068 while ((field = mono_class_get_fields (klass, &iter))) {
3071 if (field->type->attrs & FIELD_ATTRIBUTE_STATIC)
3073 foffset = klass->valuetype ? field->offset - sizeof (MonoObject): field->offset;
3074 if (mini_type_is_reference (cfg, mono_field_get_type (field))) {
3075 g_assert ((foffset % SIZEOF_VOID_P) == 0);
3076 *wb_bitmap |= 1 << ((offset + foffset) / SIZEOF_VOID_P);
3078 MonoClass *field_class = mono_class_from_mono_type (field->type);
3079 if (field_class->has_references)
3080 create_write_barrier_bitmap (cfg, field_class, wb_bitmap, offset + foffset);
3086 emit_write_barrier (MonoCompile *cfg, MonoInst *ptr, MonoInst *value)
3088 int card_table_shift_bits;
3089 gpointer card_table_mask;
3091 MonoInst *dummy_use;
3092 int nursery_shift_bits;
3093 size_t nursery_size;
3094 gboolean has_card_table_wb = FALSE;
3096 if (!cfg->gen_write_barriers)
3099 card_table = mono_gc_get_card_table (&card_table_shift_bits, &card_table_mask);
3101 mono_gc_get_nursery (&nursery_shift_bits, &nursery_size);
3103 #ifdef MONO_ARCH_HAVE_CARD_TABLE_WBARRIER
3104 has_card_table_wb = TRUE;
3107 if (has_card_table_wb && !cfg->compile_aot && card_table && nursery_shift_bits > 0 && !COMPILE_LLVM (cfg)) {
3110 MONO_INST_NEW (cfg, wbarrier, OP_CARD_TABLE_WBARRIER);
3111 wbarrier->sreg1 = ptr->dreg;
3112 wbarrier->sreg2 = value->dreg;
3113 MONO_ADD_INS (cfg->cbb, wbarrier);
3114 } else if (card_table) {
3115 int offset_reg = alloc_preg (cfg);
3116 int card_reg = alloc_preg (cfg);
3119 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SHR_UN_IMM, offset_reg, ptr->dreg, card_table_shift_bits);
3120 if (card_table_mask)
3121 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_PAND_IMM, offset_reg, offset_reg, card_table_mask);
3123 /*We can't use PADD_IMM since the cardtable might end up in high addresses and amd64 doesn't support
3124 * IMM's larger than 32bits.
3126 if (cfg->compile_aot) {
3127 MONO_EMIT_NEW_AOTCONST (cfg, card_reg, NULL, MONO_PATCH_INFO_GC_CARD_TABLE_ADDR);
3129 MONO_INST_NEW (cfg, ins, OP_PCONST);
3130 ins->inst_p0 = card_table;
3131 ins->dreg = card_reg;
3132 MONO_ADD_INS (cfg->cbb, ins);
3135 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, offset_reg, offset_reg, card_reg);
3136 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREI1_MEMBASE_IMM, offset_reg, 0, 1);
3138 MonoMethod *write_barrier = mono_gc_get_write_barrier ();
3139 mono_emit_method_call (cfg, write_barrier, &ptr, NULL);
3142 EMIT_NEW_DUMMY_USE (cfg, dummy_use, value);
3146 mono_emit_wb_aware_memcpy (MonoCompile *cfg, MonoClass *klass, MonoInst *iargs[4], int size, int align)
3148 int dest_ptr_reg, tmp_reg, destreg, srcreg, offset;
3149 unsigned need_wb = 0;
3154 /*types with references can't have alignment smaller than sizeof(void*) */
3155 if (align < SIZEOF_VOID_P)
3158 /*This value cannot be bigger than 32 due to the way we calculate the required wb bitmap.*/
3159 if (size > 32 * SIZEOF_VOID_P)
3162 create_write_barrier_bitmap (cfg, klass, &need_wb, 0);
3164 /* We don't unroll more than 5 stores to avoid code bloat. */
3165 if (size > 5 * SIZEOF_VOID_P) {
3166 /*This is harmless and simplify mono_gc_wbarrier_value_copy_bitmap */
3167 size += (SIZEOF_VOID_P - 1);
3168 size &= ~(SIZEOF_VOID_P - 1);
3170 EMIT_NEW_ICONST (cfg, iargs [2], size);
3171 EMIT_NEW_ICONST (cfg, iargs [3], need_wb);
3172 mono_emit_jit_icall (cfg, mono_gc_wbarrier_value_copy_bitmap, iargs);
3176 destreg = iargs [0]->dreg;
3177 srcreg = iargs [1]->dreg;
3180 dest_ptr_reg = alloc_preg (cfg);
3181 tmp_reg = alloc_preg (cfg);
3184 EMIT_NEW_UNALU (cfg, iargs [0], OP_MOVE, dest_ptr_reg, destreg);
3186 while (size >= SIZEOF_VOID_P) {
3187 MonoInst *load_inst;
3188 MONO_INST_NEW (cfg, load_inst, OP_LOAD_MEMBASE);
3189 load_inst->dreg = tmp_reg;
3190 load_inst->inst_basereg = srcreg;
3191 load_inst->inst_offset = offset;
3192 MONO_ADD_INS (cfg->cbb, load_inst);
3194 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREP_MEMBASE_REG, dest_ptr_reg, 0, tmp_reg);
3197 emit_write_barrier (cfg, iargs [0], load_inst);
3199 offset += SIZEOF_VOID_P;
3200 size -= SIZEOF_VOID_P;
3203 /*tmp += sizeof (void*)*/
3204 if (size >= SIZEOF_VOID_P) {
3205 NEW_BIALU_IMM (cfg, iargs [0], OP_PADD_IMM, dest_ptr_reg, dest_ptr_reg, SIZEOF_VOID_P);
3206 MONO_ADD_INS (cfg->cbb, iargs [0]);
3210 /* Those cannot be references since size < sizeof (void*) */
3212 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI4_MEMBASE, tmp_reg, srcreg, offset);
3213 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI4_MEMBASE_REG, destreg, offset, tmp_reg);
3219 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI2_MEMBASE, tmp_reg, srcreg, offset);
3220 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI2_MEMBASE_REG, destreg, offset, tmp_reg);
3226 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI1_MEMBASE, tmp_reg, srcreg, offset);
3227 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI1_MEMBASE_REG, destreg, offset, tmp_reg);
3236 * Emit code to copy a valuetype of type @klass whose address is stored in
3237 * @src->dreg to memory whose address is stored at @dest->dreg.
3240 mini_emit_stobj (MonoCompile *cfg, MonoInst *dest, MonoInst *src, MonoClass *klass, gboolean native)
3242 MonoInst *iargs [4];
3243 int context_used, n;
3245 MonoMethod *memcpy_method;
3246 MonoInst *size_ins = NULL;
3247 MonoInst *memcpy_ins = NULL;
3251 * This check breaks with spilled vars... need to handle it during verification anyway.
3252 * g_assert (klass && klass == src->klass && klass == dest->klass);
3255 if (mini_is_gsharedvt_klass (cfg, klass)) {
3257 context_used = mini_class_check_context_used (cfg, klass);
3258 size_ins = emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_VALUE_SIZE);
3259 memcpy_ins = emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_MEMCPY);
3263 n = mono_class_native_size (klass, &align);
3265 n = mono_class_value_size (klass, &align);
3267 /* if native is true there should be no references in the struct */
3268 if (cfg->gen_write_barriers && (klass->has_references || size_ins) && !native) {
3269 /* Avoid barriers when storing to the stack */
3270 if (!((dest->opcode == OP_ADD_IMM && dest->sreg1 == cfg->frame_reg) ||
3271 (dest->opcode == OP_LDADDR))) {
3277 context_used = mini_class_check_context_used (cfg, klass);
3279 /* It's ok to intrinsify under gsharing since shared code types are layout stable. */
3280 if (!size_ins && (cfg->opt & MONO_OPT_INTRINS) && mono_emit_wb_aware_memcpy (cfg, klass, iargs, n, align)) {
3282 } else if (context_used) {
3283 iargs [2] = emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_KLASS);
3285 if (cfg->compile_aot) {
3286 EMIT_NEW_CLASSCONST (cfg, iargs [2], klass);
3288 EMIT_NEW_PCONST (cfg, iargs [2], klass);
3289 mono_class_compute_gc_descriptor (klass);
3294 mono_emit_jit_icall (cfg, mono_gsharedvt_value_copy, iargs);
3296 mono_emit_jit_icall (cfg, mono_value_copy, iargs);
3301 if (!size_ins && (cfg->opt & MONO_OPT_INTRINS) && n <= sizeof (gpointer) * 8) {
3302 /* FIXME: Optimize the case when src/dest is OP_LDADDR */
3303 mini_emit_memcpy (cfg, dest->dreg, 0, src->dreg, 0, n, align);
3308 iargs [2] = size_ins;
3310 EMIT_NEW_ICONST (cfg, iargs [2], n);
3312 memcpy_method = get_memcpy_method ();
3314 mono_emit_calli (cfg, mono_method_signature (memcpy_method), iargs, memcpy_ins, NULL, NULL);
3316 mono_emit_method_call (cfg, memcpy_method, iargs, NULL);
3321 get_memset_method (void)
3323 static MonoMethod *memset_method = NULL;
3324 if (!memset_method) {
3325 memset_method = mono_class_get_method_from_name (mono_defaults.string_class, "memset", 3);
3327 g_error ("Old corlib found. Install a new one");
3329 return memset_method;
3333 mini_emit_initobj (MonoCompile *cfg, MonoInst *dest, const guchar *ip, MonoClass *klass)
3335 MonoInst *iargs [3];
3336 int n, context_used;
3338 MonoMethod *memset_method;
3339 MonoInst *size_ins = NULL;
3340 MonoInst *bzero_ins = NULL;
3341 static MonoMethod *bzero_method;
3343 /* FIXME: Optimize this for the case when dest is an LDADDR */
3345 mono_class_init (klass);
3346 if (mini_is_gsharedvt_klass (cfg, klass)) {
3347 context_used = mini_class_check_context_used (cfg, klass);
3348 size_ins = emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_VALUE_SIZE);
3349 bzero_ins = emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_BZERO);
3351 bzero_method = mono_class_get_method_from_name (mono_defaults.string_class, "bzero_aligned_1", 2);
3352 g_assert (bzero_method);
3354 iargs [1] = size_ins;
3355 mono_emit_calli (cfg, mono_method_signature (bzero_method), iargs, bzero_ins, NULL, NULL);
3359 n = mono_class_value_size (klass, &align);
3361 if (n <= sizeof (gpointer) * 8) {
3362 mini_emit_memset (cfg, dest->dreg, 0, n, 0, align);
3365 memset_method = get_memset_method ();
3367 EMIT_NEW_ICONST (cfg, iargs [1], 0);
3368 EMIT_NEW_ICONST (cfg, iargs [2], n);
3369 mono_emit_method_call (cfg, memset_method, iargs, NULL);
3374 emit_get_rgctx (MonoCompile *cfg, MonoMethod *method, int context_used)
3376 MonoInst *this = NULL;
3378 g_assert (cfg->generic_sharing_context);
3380 if (!(method->flags & METHOD_ATTRIBUTE_STATIC) &&
3381 !(context_used & MONO_GENERIC_CONTEXT_USED_METHOD) &&
3382 !method->klass->valuetype)
3383 EMIT_NEW_ARGLOAD (cfg, this, 0);
3385 if (context_used & MONO_GENERIC_CONTEXT_USED_METHOD) {
3386 MonoInst *mrgctx_loc, *mrgctx_var;
3389 g_assert (method->is_inflated && mono_method_get_context (method)->method_inst);
3391 mrgctx_loc = mono_get_vtable_var (cfg);
3392 EMIT_NEW_TEMPLOAD (cfg, mrgctx_var, mrgctx_loc->inst_c0);
3395 } else if (method->flags & METHOD_ATTRIBUTE_STATIC || method->klass->valuetype) {
3396 MonoInst *vtable_loc, *vtable_var;
3400 vtable_loc = mono_get_vtable_var (cfg);
3401 EMIT_NEW_TEMPLOAD (cfg, vtable_var, vtable_loc->inst_c0);
3403 if (method->is_inflated && mono_method_get_context (method)->method_inst) {
3404 MonoInst *mrgctx_var = vtable_var;
3407 vtable_reg = alloc_preg (cfg);
3408 EMIT_NEW_LOAD_MEMBASE (cfg, vtable_var, OP_LOAD_MEMBASE, vtable_reg, mrgctx_var->dreg, MONO_STRUCT_OFFSET (MonoMethodRuntimeGenericContext, class_vtable));
3409 vtable_var->type = STACK_PTR;
3417 vtable_reg = alloc_preg (cfg);
3418 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, vtable_reg, this->dreg, MONO_STRUCT_OFFSET (MonoObject, vtable));
3423 static MonoJumpInfoRgctxEntry *
3424 mono_patch_info_rgctx_entry_new (MonoMemPool *mp, MonoMethod *method, gboolean in_mrgctx, MonoJumpInfoType patch_type, gconstpointer patch_data, MonoRgctxInfoType info_type)
3426 MonoJumpInfoRgctxEntry *res = mono_mempool_alloc0 (mp, sizeof (MonoJumpInfoRgctxEntry));
3427 res->method = method;
3428 res->in_mrgctx = in_mrgctx;
3429 res->data = mono_mempool_alloc0 (mp, sizeof (MonoJumpInfo));
3430 res->data->type = patch_type;
3431 res->data->data.target = patch_data;
3432 res->info_type = info_type;
3437 static inline MonoInst*
3438 emit_rgctx_fetch (MonoCompile *cfg, MonoInst *rgctx, MonoJumpInfoRgctxEntry *entry)
3440 return mono_emit_abs_call (cfg, MONO_PATCH_INFO_RGCTX_FETCH, entry, helper_sig_rgctx_lazy_fetch_trampoline, &rgctx);
3444 emit_get_rgctx_klass (MonoCompile *cfg, int context_used,
3445 MonoClass *klass, MonoRgctxInfoType rgctx_type)
3447 MonoJumpInfoRgctxEntry *entry = mono_patch_info_rgctx_entry_new (cfg->mempool, cfg->current_method, context_used & MONO_GENERIC_CONTEXT_USED_METHOD, MONO_PATCH_INFO_CLASS, klass, rgctx_type);
3448 MonoInst *rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
3450 return emit_rgctx_fetch (cfg, rgctx, entry);
3454 emit_get_rgctx_sig (MonoCompile *cfg, int context_used,
3455 MonoMethodSignature *sig, MonoRgctxInfoType rgctx_type)
3457 MonoJumpInfoRgctxEntry *entry = mono_patch_info_rgctx_entry_new (cfg->mempool, cfg->current_method, context_used & MONO_GENERIC_CONTEXT_USED_METHOD, MONO_PATCH_INFO_SIGNATURE, sig, rgctx_type);
3458 MonoInst *rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
3460 return emit_rgctx_fetch (cfg, rgctx, entry);
3464 emit_get_rgctx_gsharedvt_call (MonoCompile *cfg, int context_used,
3465 MonoMethodSignature *sig, MonoMethod *cmethod, MonoRgctxInfoType rgctx_type)
3467 MonoJumpInfoGSharedVtCall *call_info;
3468 MonoJumpInfoRgctxEntry *entry;
3471 call_info = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoJumpInfoGSharedVtCall));
3472 call_info->sig = sig;
3473 call_info->method = cmethod;
3475 entry = mono_patch_info_rgctx_entry_new (cfg->mempool, cfg->current_method, context_used & MONO_GENERIC_CONTEXT_USED_METHOD, MONO_PATCH_INFO_GSHAREDVT_CALL, call_info, rgctx_type);
3476 rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
3478 return emit_rgctx_fetch (cfg, rgctx, entry);
3483 emit_get_rgctx_gsharedvt_method (MonoCompile *cfg, int context_used,
3484 MonoMethod *cmethod, MonoGSharedVtMethodInfo *info)
3486 MonoJumpInfoRgctxEntry *entry;
3489 entry = mono_patch_info_rgctx_entry_new (cfg->mempool, cfg->current_method, context_used & MONO_GENERIC_CONTEXT_USED_METHOD, MONO_PATCH_INFO_GSHAREDVT_METHOD, info, MONO_RGCTX_INFO_METHOD_GSHAREDVT_INFO);
3490 rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
3492 return emit_rgctx_fetch (cfg, rgctx, entry);
3496 * emit_get_rgctx_method:
3498 * Emit IR to load the property RGCTX_TYPE of CMETHOD. If context_used is 0, emit
3499 * normal constants, else emit a load from the rgctx.
3502 emit_get_rgctx_method (MonoCompile *cfg, int context_used,
3503 MonoMethod *cmethod, MonoRgctxInfoType rgctx_type)
3505 if (!context_used) {
3508 switch (rgctx_type) {
3509 case MONO_RGCTX_INFO_METHOD:
3510 EMIT_NEW_METHODCONST (cfg, ins, cmethod);
3512 case MONO_RGCTX_INFO_METHOD_RGCTX:
3513 EMIT_NEW_METHOD_RGCTX_CONST (cfg, ins, cmethod);
3516 g_assert_not_reached ();
3519 MonoJumpInfoRgctxEntry *entry = mono_patch_info_rgctx_entry_new (cfg->mempool, cfg->current_method, context_used & MONO_GENERIC_CONTEXT_USED_METHOD, MONO_PATCH_INFO_METHODCONST, cmethod, rgctx_type);
3520 MonoInst *rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
3522 return emit_rgctx_fetch (cfg, rgctx, entry);
3527 emit_get_rgctx_field (MonoCompile *cfg, int context_used,
3528 MonoClassField *field, MonoRgctxInfoType rgctx_type)
3530 MonoJumpInfoRgctxEntry *entry = mono_patch_info_rgctx_entry_new (cfg->mempool, cfg->current_method, context_used & MONO_GENERIC_CONTEXT_USED_METHOD, MONO_PATCH_INFO_FIELD, field, rgctx_type);
3531 MonoInst *rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
3533 return emit_rgctx_fetch (cfg, rgctx, entry);
3537 get_gsharedvt_info_slot (MonoCompile *cfg, gpointer data, MonoRgctxInfoType rgctx_type)
3539 MonoGSharedVtMethodInfo *info = cfg->gsharedvt_info;
3540 MonoRuntimeGenericContextInfoTemplate *template;
3545 for (i = 0; i < info->num_entries; ++i) {
3546 MonoRuntimeGenericContextInfoTemplate *otemplate = &info->entries [i];
3548 if (otemplate->info_type == rgctx_type && otemplate->data == data && rgctx_type != MONO_RGCTX_INFO_LOCAL_OFFSET)
3552 if (info->num_entries == info->count_entries) {
3553 MonoRuntimeGenericContextInfoTemplate *new_entries;
3554 int new_count_entries = info->count_entries ? info->count_entries * 2 : 16;
3556 new_entries = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoRuntimeGenericContextInfoTemplate) * new_count_entries);
3558 memcpy (new_entries, info->entries, sizeof (MonoRuntimeGenericContextInfoTemplate) * info->count_entries);
3559 info->entries = new_entries;
3560 info->count_entries = new_count_entries;
3563 idx = info->num_entries;
3564 template = &info->entries [idx];
3565 template->info_type = rgctx_type;
3566 template->data = data;
3568 info->num_entries ++;
3574 * emit_get_gsharedvt_info:
3576 * This is similar to emit_get_rgctx_.., but loads the data from the gsharedvt info var instead of calling an rgctx fetch trampoline.
3579 emit_get_gsharedvt_info (MonoCompile *cfg, gpointer data, MonoRgctxInfoType rgctx_type)
3584 idx = get_gsharedvt_info_slot (cfg, data, rgctx_type);
3585 /* Load info->entries [idx] */
3586 dreg = alloc_preg (cfg);
3587 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, dreg, cfg->gsharedvt_info_var->dreg, MONO_STRUCT_OFFSET (MonoGSharedVtMethodRuntimeInfo, entries) + (idx * sizeof (gpointer)));
3593 emit_get_gsharedvt_info_klass (MonoCompile *cfg, MonoClass *klass, MonoRgctxInfoType rgctx_type)
3595 return emit_get_gsharedvt_info (cfg, &klass->byval_arg, rgctx_type);
3599 * On return the caller must check @klass for load errors.
3602 emit_generic_class_init (MonoCompile *cfg, MonoClass *klass)
3604 MonoInst *vtable_arg;
3608 context_used = mini_class_check_context_used (cfg, klass);
3611 vtable_arg = emit_get_rgctx_klass (cfg, context_used,
3612 klass, MONO_RGCTX_INFO_VTABLE);
3614 MonoVTable *vtable = mono_class_vtable (cfg->domain, klass);
3618 EMIT_NEW_VTABLECONST (cfg, vtable_arg, vtable);
3621 if (COMPILE_LLVM (cfg))
3622 call = (MonoCallInst*)mono_emit_abs_call (cfg, MONO_PATCH_INFO_GENERIC_CLASS_INIT, NULL, helper_sig_generic_class_init_trampoline_llvm, &vtable_arg);
3624 call = (MonoCallInst*)mono_emit_abs_call (cfg, MONO_PATCH_INFO_GENERIC_CLASS_INIT, NULL, helper_sig_generic_class_init_trampoline, &vtable_arg);
3625 #ifdef MONO_ARCH_VTABLE_REG
3626 mono_call_inst_add_outarg_reg (cfg, call, vtable_arg->dreg, MONO_ARCH_VTABLE_REG, FALSE);
3627 cfg->uses_vtable_reg = TRUE;
3634 emit_seq_point (MonoCompile *cfg, MonoMethod *method, guint8* ip, gboolean intr_loc, gboolean nonempty_stack)
3638 if (cfg->gen_seq_points && cfg->method == method) {
3639 NEW_SEQ_POINT (cfg, ins, ip - cfg->header->code, intr_loc);
3641 ins->flags |= MONO_INST_NONEMPTY_STACK;
3642 MONO_ADD_INS (cfg->cbb, ins);
3647 save_cast_details (MonoCompile *cfg, MonoClass *klass, int obj_reg, gboolean null_check, MonoBasicBlock **out_bblock)
3649 if (mini_get_debug_options ()->better_cast_details) {
3650 int vtable_reg = alloc_preg (cfg);
3651 int klass_reg = alloc_preg (cfg);
3652 MonoBasicBlock *is_null_bb = NULL;
3654 int to_klass_reg, context_used;
3657 NEW_BBLOCK (cfg, is_null_bb);
3659 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, obj_reg, 0);
3660 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, is_null_bb);
3663 tls_get = mono_get_jit_tls_intrinsic (cfg);
3665 fprintf (stderr, "error: --debug=casts not supported on this platform.\n.");
3669 MONO_ADD_INS (cfg->cbb, tls_get);
3670 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, vtable_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
3671 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
3673 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, tls_get->dreg, MONO_STRUCT_OFFSET (MonoJitTlsData, class_cast_from), klass_reg);
3675 context_used = mini_class_check_context_used (cfg, klass);
3677 MonoInst *class_ins;
3679 class_ins = emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_KLASS);
3680 to_klass_reg = class_ins->dreg;
3682 to_klass_reg = alloc_preg (cfg);
3683 MONO_EMIT_NEW_CLASSCONST (cfg, to_klass_reg, klass);
3685 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, tls_get->dreg, MONO_STRUCT_OFFSET (MonoJitTlsData, class_cast_to), to_klass_reg);
3688 MONO_START_BB (cfg, is_null_bb);
3690 *out_bblock = cfg->cbb;
3696 reset_cast_details (MonoCompile *cfg)
3698 /* Reset the variables holding the cast details */
3699 if (mini_get_debug_options ()->better_cast_details) {
3700 MonoInst *tls_get = mono_get_jit_tls_intrinsic (cfg);
3702 MONO_ADD_INS (cfg->cbb, tls_get);
3703 /* It is enough to reset the from field */
3704 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STORE_MEMBASE_IMM, tls_get->dreg, MONO_STRUCT_OFFSET (MonoJitTlsData, class_cast_from), 0);
3709 * On return the caller must check @array_class for load errors
3712 mini_emit_check_array_type (MonoCompile *cfg, MonoInst *obj, MonoClass *array_class)
3714 int vtable_reg = alloc_preg (cfg);
3717 context_used = mini_class_check_context_used (cfg, array_class);
3719 save_cast_details (cfg, array_class, obj->dreg, FALSE, NULL);
3721 MONO_EMIT_NEW_LOAD_MEMBASE_FAULT (cfg, vtable_reg, obj->dreg, MONO_STRUCT_OFFSET (MonoObject, vtable));
3723 if (cfg->opt & MONO_OPT_SHARED) {
3724 int class_reg = alloc_preg (cfg);
3725 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, class_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
3726 if (cfg->compile_aot) {
3727 int klass_reg = alloc_preg (cfg);
3728 MONO_EMIT_NEW_CLASSCONST (cfg, klass_reg, array_class);
3729 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, class_reg, klass_reg);
3731 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, class_reg, array_class);
3733 } else if (context_used) {
3734 MonoInst *vtable_ins;
3736 vtable_ins = emit_get_rgctx_klass (cfg, context_used, array_class, MONO_RGCTX_INFO_VTABLE);
3737 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, vtable_reg, vtable_ins->dreg);
3739 if (cfg->compile_aot) {
3743 if (!(vtable = mono_class_vtable (cfg->domain, array_class)))
3745 vt_reg = alloc_preg (cfg);
3746 MONO_EMIT_NEW_VTABLECONST (cfg, vt_reg, vtable);
3747 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, vtable_reg, vt_reg);
3750 if (!(vtable = mono_class_vtable (cfg->domain, array_class)))
3752 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, vtable_reg, vtable);
3756 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "ArrayTypeMismatchException");
3758 reset_cast_details (cfg);
3762 * Handles unbox of a Nullable<T>. If context_used is non zero, then shared
3763 * generic code is generated.
3766 handle_unbox_nullable (MonoCompile* cfg, MonoInst* val, MonoClass* klass, int context_used)
3768 MonoMethod* method = mono_class_get_method_from_name (klass, "Unbox", 1);
3771 MonoInst *rgctx, *addr;
3773 /* FIXME: What if the class is shared? We might not
3774 have to get the address of the method from the
3776 addr = emit_get_rgctx_method (cfg, context_used, method,
3777 MONO_RGCTX_INFO_GENERIC_METHOD_CODE);
3779 rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
3781 return mono_emit_calli (cfg, mono_method_signature (method), &val, addr, NULL, rgctx);
3783 gboolean pass_vtable, pass_mrgctx;
3784 MonoInst *rgctx_arg = NULL;
3786 check_method_sharing (cfg, method, &pass_vtable, &pass_mrgctx);
3787 g_assert (!pass_mrgctx);
3790 MonoVTable *vtable = mono_class_vtable (cfg->domain, method->klass);
3793 EMIT_NEW_VTABLECONST (cfg, rgctx_arg, vtable);
3796 return mono_emit_method_call_full (cfg, method, NULL, FALSE, &val, NULL, NULL, rgctx_arg);
3801 handle_unbox (MonoCompile *cfg, MonoClass *klass, MonoInst **sp, int context_used)
3805 int vtable_reg = alloc_dreg (cfg ,STACK_PTR);
3806 int klass_reg = alloc_dreg (cfg ,STACK_PTR);
3807 int eclass_reg = alloc_dreg (cfg ,STACK_PTR);
3808 int rank_reg = alloc_dreg (cfg ,STACK_I4);
3810 obj_reg = sp [0]->dreg;
3811 MONO_EMIT_NEW_LOAD_MEMBASE_FAULT (cfg, vtable_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
3812 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU1_MEMBASE, rank_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, rank));
3814 /* FIXME: generics */
3815 g_assert (klass->rank == 0);
3818 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, rank_reg, 0);
3819 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException");
3821 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
3822 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, eclass_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, element_class));
3825 MonoInst *element_class;
3827 /* This assertion is from the unboxcast insn */
3828 g_assert (klass->rank == 0);
3830 element_class = emit_get_rgctx_klass (cfg, context_used,
3831 klass->element_class, MONO_RGCTX_INFO_KLASS);
3833 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, eclass_reg, element_class->dreg);
3834 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException");
3836 save_cast_details (cfg, klass->element_class, obj_reg, FALSE, NULL);
3837 mini_emit_class_check (cfg, eclass_reg, klass->element_class);
3838 reset_cast_details (cfg);
3841 NEW_BIALU_IMM (cfg, add, OP_ADD_IMM, alloc_dreg (cfg, STACK_MP), obj_reg, sizeof (MonoObject));
3842 MONO_ADD_INS (cfg->cbb, add);
3843 add->type = STACK_MP;
3850 handle_unbox_gsharedvt (MonoCompile *cfg, MonoClass *klass, MonoInst *obj, MonoBasicBlock **out_cbb)
3852 MonoInst *addr, *klass_inst, *is_ref, *args[16];
3853 MonoBasicBlock *is_ref_bb, *is_nullable_bb, *end_bb;
3857 klass_inst = emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_KLASS);
3863 args [1] = klass_inst;
3866 obj = mono_emit_jit_icall (cfg, mono_object_castclass_unbox, args);
3868 NEW_BBLOCK (cfg, is_ref_bb);
3869 NEW_BBLOCK (cfg, is_nullable_bb);
3870 NEW_BBLOCK (cfg, end_bb);
3871 is_ref = emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_CLASS_BOX_TYPE);
3872 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, is_ref->dreg, 1);
3873 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, is_ref_bb);
3875 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, is_ref->dreg, 2);
3876 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, is_nullable_bb);
3878 /* This will contain either the address of the unboxed vtype, or an address of the temporary where the ref is stored */
3879 addr_reg = alloc_dreg (cfg, STACK_MP);
3883 NEW_BIALU_IMM (cfg, addr, OP_ADD_IMM, addr_reg, obj->dreg, sizeof (MonoObject));
3884 MONO_ADD_INS (cfg->cbb, addr);
3886 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
3889 MONO_START_BB (cfg, is_ref_bb);
3891 /* Save the ref to a temporary */
3892 dreg = alloc_ireg (cfg);
3893 EMIT_NEW_VARLOADA_VREG (cfg, addr, dreg, &klass->byval_arg);
3894 addr->dreg = addr_reg;
3895 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, addr->dreg, 0, obj->dreg);
3896 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
3899 MONO_START_BB (cfg, is_nullable_bb);
3902 MonoInst *addr = emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_NULLABLE_CLASS_UNBOX);
3903 MonoInst *unbox_call;
3904 MonoMethodSignature *unbox_sig;
3907 var = mono_compile_create_var (cfg, &klass->byval_arg, OP_LOCAL);
3909 unbox_sig = mono_mempool_alloc0 (cfg->mempool, MONO_SIZEOF_METHOD_SIGNATURE + (1 * sizeof (MonoType *)));
3910 unbox_sig->ret = &klass->byval_arg;
3911 unbox_sig->param_count = 1;
3912 unbox_sig->params [0] = &mono_defaults.object_class->byval_arg;
3913 unbox_call = mono_emit_calli (cfg, unbox_sig, &obj, addr, NULL, NULL);
3915 EMIT_NEW_VARLOADA_VREG (cfg, addr, unbox_call->dreg, &klass->byval_arg);
3916 addr->dreg = addr_reg;
3919 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
3922 MONO_START_BB (cfg, end_bb);
3925 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, addr_reg, 0);
3927 *out_cbb = cfg->cbb;
3933 * Returns NULL and set the cfg exception on error.
3936 handle_alloc (MonoCompile *cfg, MonoClass *klass, gboolean for_box, int context_used)
3938 MonoInst *iargs [2];
3944 MonoInst *iargs [2];
3946 MonoMethod *managed_alloc = mono_gc_get_managed_allocator (klass, for_box);
3948 if (cfg->opt & MONO_OPT_SHARED)
3949 rgctx_info = MONO_RGCTX_INFO_KLASS;
3951 rgctx_info = MONO_RGCTX_INFO_VTABLE;
3952 data = emit_get_rgctx_klass (cfg, context_used, klass, rgctx_info);
3954 if (cfg->opt & MONO_OPT_SHARED) {
3955 EMIT_NEW_DOMAINCONST (cfg, iargs [0]);
3957 alloc_ftn = mono_object_new;
3960 alloc_ftn = mono_object_new_specific;
3963 if (managed_alloc && !(cfg->opt & MONO_OPT_SHARED))
3964 return mono_emit_method_call (cfg, managed_alloc, iargs, NULL);
3966 return mono_emit_jit_icall (cfg, alloc_ftn, iargs);
3969 if (cfg->opt & MONO_OPT_SHARED) {
3970 EMIT_NEW_DOMAINCONST (cfg, iargs [0]);
3971 EMIT_NEW_CLASSCONST (cfg, iargs [1], klass);
3973 alloc_ftn = mono_object_new;
3974 } else if (cfg->compile_aot && cfg->cbb->out_of_line && klass->type_token && klass->image == mono_defaults.corlib && !klass->generic_class) {
3975 /* This happens often in argument checking code, eg. throw new FooException... */
3976 /* Avoid relocations and save some space by calling a helper function specialized to mscorlib */
3977 EMIT_NEW_ICONST (cfg, iargs [0], mono_metadata_token_index (klass->type_token));
3978 return mono_emit_jit_icall (cfg, mono_helper_newobj_mscorlib, iargs);
3980 MonoVTable *vtable = mono_class_vtable (cfg->domain, klass);
3981 MonoMethod *managed_alloc = NULL;
3985 mono_cfg_set_exception (cfg, MONO_EXCEPTION_TYPE_LOAD);
3986 cfg->exception_ptr = klass;
3990 #ifndef MONO_CROSS_COMPILE
3991 managed_alloc = mono_gc_get_managed_allocator (klass, for_box);
3994 if (managed_alloc) {
3995 EMIT_NEW_VTABLECONST (cfg, iargs [0], vtable);
3996 return mono_emit_method_call (cfg, managed_alloc, iargs, NULL);
3998 alloc_ftn = mono_class_get_allocation_ftn (vtable, for_box, &pass_lw);
4000 guint32 lw = vtable->klass->instance_size;
4001 lw = ((lw + (sizeof (gpointer) - 1)) & ~(sizeof (gpointer) - 1)) / sizeof (gpointer);
4002 EMIT_NEW_ICONST (cfg, iargs [0], lw);
4003 EMIT_NEW_VTABLECONST (cfg, iargs [1], vtable);
4006 EMIT_NEW_VTABLECONST (cfg, iargs [0], vtable);
4010 return mono_emit_jit_icall (cfg, alloc_ftn, iargs);
4014 * Returns NULL and set the cfg exception on error.
4017 handle_box (MonoCompile *cfg, MonoInst *val, MonoClass *klass, int context_used, MonoBasicBlock **out_cbb)
4019 MonoInst *alloc, *ins;
4021 *out_cbb = cfg->cbb;
4023 if (mono_class_is_nullable (klass)) {
4024 MonoMethod* method = mono_class_get_method_from_name (klass, "Box", 1);
4027 /* FIXME: What if the class is shared? We might not
4028 have to get the method address from the RGCTX. */
4029 MonoInst *addr = emit_get_rgctx_method (cfg, context_used, method,
4030 MONO_RGCTX_INFO_GENERIC_METHOD_CODE);
4031 MonoInst *rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
4033 return mono_emit_calli (cfg, mono_method_signature (method), &val, addr, NULL, rgctx);
4035 gboolean pass_vtable, pass_mrgctx;
4036 MonoInst *rgctx_arg = NULL;
4038 check_method_sharing (cfg, method, &pass_vtable, &pass_mrgctx);
4039 g_assert (!pass_mrgctx);
4042 MonoVTable *vtable = mono_class_vtable (cfg->domain, method->klass);
4045 EMIT_NEW_VTABLECONST (cfg, rgctx_arg, vtable);
4048 return mono_emit_method_call_full (cfg, method, NULL, FALSE, &val, NULL, NULL, rgctx_arg);
4052 if (mini_is_gsharedvt_klass (cfg, klass)) {
4053 MonoBasicBlock *is_ref_bb, *is_nullable_bb, *end_bb;
4054 MonoInst *res, *is_ref, *src_var, *addr;
4057 dreg = alloc_ireg (cfg);
4059 NEW_BBLOCK (cfg, is_ref_bb);
4060 NEW_BBLOCK (cfg, is_nullable_bb);
4061 NEW_BBLOCK (cfg, end_bb);
4062 is_ref = emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_CLASS_BOX_TYPE);
4063 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, is_ref->dreg, 1);
4064 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, is_ref_bb);
4066 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, is_ref->dreg, 2);
4067 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, is_nullable_bb);
4070 alloc = handle_alloc (cfg, klass, TRUE, context_used);
4073 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, alloc->dreg, sizeof (MonoObject), val->dreg);
4074 ins->opcode = OP_STOREV_MEMBASE;
4076 EMIT_NEW_UNALU (cfg, res, OP_MOVE, dreg, alloc->dreg);
4077 res->type = STACK_OBJ;
4079 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4082 MONO_START_BB (cfg, is_ref_bb);
4083 addr_reg = alloc_ireg (cfg);
4085 /* val is a vtype, so has to load the value manually */
4086 src_var = get_vreg_to_inst (cfg, val->dreg);
4088 src_var = mono_compile_create_var_for_vreg (cfg, &klass->byval_arg, OP_LOCAL, val->dreg);
4089 EMIT_NEW_VARLOADA (cfg, addr, src_var, src_var->inst_vtype);
4090 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, dreg, addr->dreg, 0);
4091 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4094 MONO_START_BB (cfg, is_nullable_bb);
4097 MonoInst *addr = emit_get_gsharedvt_info_klass (cfg, klass,
4098 MONO_RGCTX_INFO_NULLABLE_CLASS_BOX);
4100 MonoMethodSignature *box_sig;
4103 * klass is Nullable<T>, need to call Nullable<T>.Box () using a gsharedvt signature, but we cannot
4104 * construct that method at JIT time, so have to do things by hand.
4106 box_sig = mono_mempool_alloc0 (cfg->mempool, MONO_SIZEOF_METHOD_SIGNATURE + (1 * sizeof (MonoType *)));
4107 box_sig->ret = &mono_defaults.object_class->byval_arg;
4108 box_sig->param_count = 1;
4109 box_sig->params [0] = &klass->byval_arg;
4110 box_call = mono_emit_calli (cfg, box_sig, &val, addr, NULL, NULL);
4111 EMIT_NEW_UNALU (cfg, res, OP_MOVE, dreg, box_call->dreg);
4112 res->type = STACK_OBJ;
4116 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4118 MONO_START_BB (cfg, end_bb);
4120 *out_cbb = cfg->cbb;
4124 alloc = handle_alloc (cfg, klass, TRUE, context_used);
4128 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, alloc->dreg, sizeof (MonoObject), val->dreg);
4135 mini_class_has_reference_variant_generic_argument (MonoCompile *cfg, MonoClass *klass, int context_used)
4138 MonoGenericContainer *container;
4139 MonoGenericInst *ginst;
4141 if (klass->generic_class) {
4142 container = klass->generic_class->container_class->generic_container;
4143 ginst = klass->generic_class->context.class_inst;
4144 } else if (klass->generic_container && context_used) {
4145 container = klass->generic_container;
4146 ginst = container->context.class_inst;
4151 for (i = 0; i < container->type_argc; ++i) {
4153 if (!(mono_generic_container_get_param_info (container, i)->flags & (MONO_GEN_PARAM_VARIANT|MONO_GEN_PARAM_COVARIANT)))
4155 type = ginst->type_argv [i];
4156 if (mini_type_is_reference (cfg, type))
4162 #define is_complex_isinst(klass) ((klass->flags & TYPE_ATTRIBUTE_INTERFACE) || klass->rank || mono_class_is_nullable (klass) || mono_class_is_marshalbyref (klass) || (klass->flags & TYPE_ATTRIBUTE_SEALED) || klass->byval_arg.type == MONO_TYPE_VAR || klass->byval_arg.type == MONO_TYPE_MVAR)
4165 emit_castclass_with_cache (MonoCompile *cfg, MonoClass *klass, MonoInst **args, MonoBasicBlock **out_bblock)
4167 MonoMethod *mono_castclass;
4170 mono_castclass = mono_marshal_get_castclass_with_cache ();
4172 save_cast_details (cfg, klass, args [0]->dreg, TRUE, out_bblock);
4173 res = mono_emit_method_call (cfg, mono_castclass, args, NULL);
4174 reset_cast_details (cfg);
4175 *out_bblock = cfg->cbb;
4181 emit_castclass_with_cache_nonshared (MonoCompile *cfg, MonoInst *obj, MonoClass *klass, MonoBasicBlock **out_bblock)
4190 EMIT_NEW_CLASSCONST (cfg, args [1], klass);
4193 if (cfg->compile_aot) {
4194 /* Each CASTCLASS_CACHE patch needs a unique index which identifies the call site */
4195 cfg->castclass_cache_index ++;
4196 idx = (cfg->method_index << 16) | cfg->castclass_cache_index;
4197 EMIT_NEW_AOTCONST (cfg, args [2], MONO_PATCH_INFO_CASTCLASS_CACHE, GINT_TO_POINTER (idx));
4199 EMIT_NEW_PCONST (cfg, args [2], mono_domain_alloc0 (cfg->domain, sizeof (gpointer)));
4202 /*The wrapper doesn't inline well so the bloat of inlining doesn't pay off.*/
4204 return emit_castclass_with_cache (cfg, klass, args, out_bblock);
4208 * Returns NULL and set the cfg exception on error.
4211 handle_castclass (MonoCompile *cfg, MonoClass *klass, MonoInst *src, guint8 *ip, MonoBasicBlock **out_bb, int *inline_costs)
4213 MonoBasicBlock *is_null_bb;
4214 int obj_reg = src->dreg;
4215 int vtable_reg = alloc_preg (cfg);
4217 MonoInst *klass_inst = NULL, *res;
4218 MonoBasicBlock *bblock;
4222 context_used = mini_class_check_context_used (cfg, klass);
4224 if (!context_used && mini_class_has_reference_variant_generic_argument (cfg, klass, context_used)) {
4225 res = emit_castclass_with_cache_nonshared (cfg, src, klass, &bblock);
4226 (*inline_costs) += 2;
4229 } else if (!context_used && (mono_class_is_marshalbyref (klass) || klass->flags & TYPE_ATTRIBUTE_INTERFACE)) {
4230 MonoMethod *mono_castclass;
4231 MonoInst *iargs [1];
4234 mono_castclass = mono_marshal_get_castclass (klass);
4237 save_cast_details (cfg, klass, src->dreg, TRUE, &bblock);
4238 costs = inline_method (cfg, mono_castclass, mono_method_signature (mono_castclass),
4239 iargs, ip, cfg->real_offset, TRUE, &bblock);
4240 reset_cast_details (cfg);
4241 CHECK_CFG_EXCEPTION;
4242 g_assert (costs > 0);
4244 cfg->real_offset += 5;
4246 (*inline_costs) += costs;
4255 if(mini_class_has_reference_variant_generic_argument (cfg, klass, context_used) || is_complex_isinst (klass)) {
4256 MonoInst *cache_ins;
4258 cache_ins = emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_CAST_CACHE);
4263 /* klass - it's the second element of the cache entry*/
4264 EMIT_NEW_LOAD_MEMBASE (cfg, args [1], OP_LOAD_MEMBASE, alloc_preg (cfg), cache_ins->dreg, sizeof (gpointer));
4267 args [2] = cache_ins;
4269 return emit_castclass_with_cache (cfg, klass, args, out_bb);
4272 klass_inst = emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_KLASS);
4275 NEW_BBLOCK (cfg, is_null_bb);
4277 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, obj_reg, 0);
4278 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, is_null_bb);
4280 save_cast_details (cfg, klass, obj_reg, FALSE, NULL);
4282 if (klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
4283 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, vtable_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
4284 mini_emit_iface_cast (cfg, vtable_reg, klass, NULL, NULL);
4286 int klass_reg = alloc_preg (cfg);
4288 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, vtable_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
4290 if (!klass->rank && !cfg->compile_aot && !(cfg->opt & MONO_OPT_SHARED) && (klass->flags & TYPE_ATTRIBUTE_SEALED)) {
4291 /* the remoting code is broken, access the class for now */
4292 if (0) { /*FIXME what exactly is broken? This change refers to r39380 from 2005 and mention some remoting fixes were due.*/
4293 MonoVTable *vt = mono_class_vtable (cfg->domain, klass);
4295 mono_cfg_set_exception (cfg, MONO_EXCEPTION_TYPE_LOAD);
4296 cfg->exception_ptr = klass;
4299 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, vtable_reg, vt);
4301 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4302 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, klass_reg, klass);
4304 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException");
4306 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4307 mini_emit_castclass_inst (cfg, obj_reg, klass_reg, klass, klass_inst, is_null_bb);
4311 MONO_START_BB (cfg, is_null_bb);
4313 reset_cast_details (cfg);
4324 * Returns NULL and set the cfg exception on error.
4327 handle_isinst (MonoCompile *cfg, MonoClass *klass, MonoInst *src, int context_used)
4330 MonoBasicBlock *is_null_bb, *false_bb, *end_bb;
4331 int obj_reg = src->dreg;
4332 int vtable_reg = alloc_preg (cfg);
4333 int res_reg = alloc_ireg_ref (cfg);
4334 MonoInst *klass_inst = NULL;
4339 if(mini_class_has_reference_variant_generic_argument (cfg, klass, context_used) || is_complex_isinst (klass)) {
4340 MonoMethod *mono_isinst = mono_marshal_get_isinst_with_cache ();
4341 MonoInst *cache_ins;
4343 cache_ins = emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_CAST_CACHE);
4348 /* klass - it's the second element of the cache entry*/
4349 EMIT_NEW_LOAD_MEMBASE (cfg, args [1], OP_LOAD_MEMBASE, alloc_preg (cfg), cache_ins->dreg, sizeof (gpointer));
4352 args [2] = cache_ins;
4354 return mono_emit_method_call (cfg, mono_isinst, args, NULL);
4357 klass_inst = emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_KLASS);
4360 NEW_BBLOCK (cfg, is_null_bb);
4361 NEW_BBLOCK (cfg, false_bb);
4362 NEW_BBLOCK (cfg, end_bb);
4364 /* Do the assignment at the beginning, so the other assignment can be if converted */
4365 EMIT_NEW_UNALU (cfg, ins, OP_MOVE, res_reg, obj_reg);
4366 ins->type = STACK_OBJ;
4369 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, obj_reg, 0);
4370 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, is_null_bb);
4372 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, vtable_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
4374 if (klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
4375 g_assert (!context_used);
4376 /* the is_null_bb target simply copies the input register to the output */
4377 mini_emit_iface_cast (cfg, vtable_reg, klass, false_bb, is_null_bb);
4379 int klass_reg = alloc_preg (cfg);
4382 int rank_reg = alloc_preg (cfg);
4383 int eclass_reg = alloc_preg (cfg);
4385 g_assert (!context_used);
4386 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU1_MEMBASE, rank_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, rank));
4387 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, rank_reg, klass->rank);
4388 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, false_bb);
4389 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4390 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, eclass_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, cast_class));
4391 if (klass->cast_class == mono_defaults.object_class) {
4392 int parent_reg = alloc_preg (cfg);
4393 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, parent_reg, eclass_reg, MONO_STRUCT_OFFSET (MonoClass, parent));
4394 mini_emit_class_check_branch (cfg, parent_reg, mono_defaults.enum_class->parent, OP_PBNE_UN, is_null_bb);
4395 mini_emit_class_check_branch (cfg, eclass_reg, mono_defaults.enum_class, OP_PBEQ, is_null_bb);
4396 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, false_bb);
4397 } else if (klass->cast_class == mono_defaults.enum_class->parent) {
4398 mini_emit_class_check_branch (cfg, eclass_reg, mono_defaults.enum_class->parent, OP_PBEQ, is_null_bb);
4399 mini_emit_class_check_branch (cfg, eclass_reg, mono_defaults.enum_class, OP_PBEQ, is_null_bb);
4400 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, false_bb);
4401 } else if (klass->cast_class == mono_defaults.enum_class) {
4402 mini_emit_class_check_branch (cfg, eclass_reg, mono_defaults.enum_class, OP_PBEQ, is_null_bb);
4403 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, false_bb);
4404 } else if (klass->cast_class->flags & TYPE_ATTRIBUTE_INTERFACE) {
4405 mini_emit_iface_class_cast (cfg, eclass_reg, klass->cast_class, false_bb, is_null_bb);
4407 if ((klass->rank == 1) && (klass->byval_arg.type == MONO_TYPE_SZARRAY)) {
4408 /* Check that the object is a vector too */
4409 int bounds_reg = alloc_preg (cfg);
4410 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, bounds_reg, obj_reg, MONO_STRUCT_OFFSET (MonoArray, bounds));
4411 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, bounds_reg, 0);
4412 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, false_bb);
4415 /* the is_null_bb target simply copies the input register to the output */
4416 mini_emit_isninst_cast (cfg, eclass_reg, klass->cast_class, false_bb, is_null_bb);
4418 } else if (mono_class_is_nullable (klass)) {
4419 g_assert (!context_used);
4420 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4421 /* the is_null_bb target simply copies the input register to the output */
4422 mini_emit_isninst_cast (cfg, klass_reg, klass->cast_class, false_bb, is_null_bb);
4424 if (!cfg->compile_aot && !(cfg->opt & MONO_OPT_SHARED) && (klass->flags & TYPE_ATTRIBUTE_SEALED)) {
4425 g_assert (!context_used);
4426 /* the remoting code is broken, access the class for now */
4427 if (0) {/*FIXME what exactly is broken? This change refers to r39380 from 2005 and mention some remoting fixes were due.*/
4428 MonoVTable *vt = mono_class_vtable (cfg->domain, klass);
4430 mono_cfg_set_exception (cfg, MONO_EXCEPTION_TYPE_LOAD);
4431 cfg->exception_ptr = klass;
4434 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, vtable_reg, vt);
4436 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4437 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, klass_reg, klass);
4439 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, false_bb);
4440 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, is_null_bb);
4442 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4443 /* the is_null_bb target simply copies the input register to the output */
4444 mini_emit_isninst_cast_inst (cfg, klass_reg, klass, klass_inst, false_bb, is_null_bb);
4449 MONO_START_BB (cfg, false_bb);
4451 MONO_EMIT_NEW_PCONST (cfg, res_reg, 0);
4452 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4454 MONO_START_BB (cfg, is_null_bb);
4456 MONO_START_BB (cfg, end_bb);
4462 handle_cisinst (MonoCompile *cfg, MonoClass *klass, MonoInst *src)
4464 /* This opcode takes as input an object reference and a class, and returns:
4465 0) if the object is an instance of the class,
4466 1) if the object is not instance of the class,
4467 2) if the object is a proxy whose type cannot be determined */
4470 #ifndef DISABLE_REMOTING
4471 MonoBasicBlock *true_bb, *false_bb, *false2_bb, *end_bb, *no_proxy_bb, *interface_fail_bb;
4473 MonoBasicBlock *true_bb, *false_bb, *end_bb;
4475 int obj_reg = src->dreg;
4476 int dreg = alloc_ireg (cfg);
4478 #ifndef DISABLE_REMOTING
4479 int klass_reg = alloc_preg (cfg);
4482 NEW_BBLOCK (cfg, true_bb);
4483 NEW_BBLOCK (cfg, false_bb);
4484 NEW_BBLOCK (cfg, end_bb);
4485 #ifndef DISABLE_REMOTING
4486 NEW_BBLOCK (cfg, false2_bb);
4487 NEW_BBLOCK (cfg, no_proxy_bb);
4490 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, obj_reg, 0);
4491 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, false_bb);
4493 if (klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
4494 #ifndef DISABLE_REMOTING
4495 NEW_BBLOCK (cfg, interface_fail_bb);
4498 tmp_reg = alloc_preg (cfg);
4499 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
4500 #ifndef DISABLE_REMOTING
4501 mini_emit_iface_cast (cfg, tmp_reg, klass, interface_fail_bb, true_bb);
4502 MONO_START_BB (cfg, interface_fail_bb);
4503 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, tmp_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4505 mini_emit_class_check_branch (cfg, klass_reg, mono_defaults.transparent_proxy_class, OP_PBNE_UN, false_bb);
4507 tmp_reg = alloc_preg (cfg);
4508 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoTransparentProxy, custom_type_info));
4509 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, tmp_reg, 0);
4510 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, false2_bb);
4512 mini_emit_iface_cast (cfg, tmp_reg, klass, false_bb, true_bb);
4515 #ifndef DISABLE_REMOTING
4516 tmp_reg = alloc_preg (cfg);
4517 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
4518 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, tmp_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4520 mini_emit_class_check_branch (cfg, klass_reg, mono_defaults.transparent_proxy_class, OP_PBNE_UN, no_proxy_bb);
4521 tmp_reg = alloc_preg (cfg);
4522 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoTransparentProxy, remote_class));
4523 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, tmp_reg, MONO_STRUCT_OFFSET (MonoRemoteClass, proxy_class));
4525 tmp_reg = alloc_preg (cfg);
4526 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoTransparentProxy, custom_type_info));
4527 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, tmp_reg, 0);
4528 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, no_proxy_bb);
4530 mini_emit_isninst_cast (cfg, klass_reg, klass, false2_bb, true_bb);
4531 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, false2_bb);
4533 MONO_START_BB (cfg, no_proxy_bb);
4535 mini_emit_isninst_cast (cfg, klass_reg, klass, false_bb, true_bb);
4537 g_error ("transparent proxy support is disabled while trying to JIT code that uses it");
4541 MONO_START_BB (cfg, false_bb);
4543 MONO_EMIT_NEW_ICONST (cfg, dreg, 1);
4544 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4546 #ifndef DISABLE_REMOTING
4547 MONO_START_BB (cfg, false2_bb);
4549 MONO_EMIT_NEW_ICONST (cfg, dreg, 2);
4550 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4553 MONO_START_BB (cfg, true_bb);
4555 MONO_EMIT_NEW_ICONST (cfg, dreg, 0);
4557 MONO_START_BB (cfg, end_bb);
4560 MONO_INST_NEW (cfg, ins, OP_ICONST);
4562 ins->type = STACK_I4;
4568 handle_ccastclass (MonoCompile *cfg, MonoClass *klass, MonoInst *src)
4570 /* This opcode takes as input an object reference and a class, and returns:
4571 0) if the object is an instance of the class,
4572 1) if the object is a proxy whose type cannot be determined
4573 an InvalidCastException exception is thrown otherwhise*/
4576 #ifndef DISABLE_REMOTING
4577 MonoBasicBlock *end_bb, *ok_result_bb, *no_proxy_bb, *interface_fail_bb, *fail_1_bb;
4579 MonoBasicBlock *ok_result_bb;
4581 int obj_reg = src->dreg;
4582 int dreg = alloc_ireg (cfg);
4583 int tmp_reg = alloc_preg (cfg);
4585 #ifndef DISABLE_REMOTING
4586 int klass_reg = alloc_preg (cfg);
4587 NEW_BBLOCK (cfg, end_bb);
4590 NEW_BBLOCK (cfg, ok_result_bb);
4592 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, obj_reg, 0);
4593 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, ok_result_bb);
4595 save_cast_details (cfg, klass, obj_reg, FALSE, NULL);
4597 if (klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
4598 #ifndef DISABLE_REMOTING
4599 NEW_BBLOCK (cfg, interface_fail_bb);
4601 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
4602 mini_emit_iface_cast (cfg, tmp_reg, klass, interface_fail_bb, ok_result_bb);
4603 MONO_START_BB (cfg, interface_fail_bb);
4604 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, tmp_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4606 mini_emit_class_check (cfg, klass_reg, mono_defaults.transparent_proxy_class);
4608 tmp_reg = alloc_preg (cfg);
4609 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoTransparentProxy, custom_type_info));
4610 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, tmp_reg, 0);
4611 MONO_EMIT_NEW_COND_EXC (cfg, EQ, "InvalidCastException");
4613 MONO_EMIT_NEW_ICONST (cfg, dreg, 1);
4614 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4616 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
4617 mini_emit_iface_cast (cfg, tmp_reg, klass, NULL, NULL);
4618 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, ok_result_bb);
4621 #ifndef DISABLE_REMOTING
4622 NEW_BBLOCK (cfg, no_proxy_bb);
4624 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
4625 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, tmp_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
4626 mini_emit_class_check_branch (cfg, klass_reg, mono_defaults.transparent_proxy_class, OP_PBNE_UN, no_proxy_bb);
4628 tmp_reg = alloc_preg (cfg);
4629 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoTransparentProxy, remote_class));
4630 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, tmp_reg, MONO_STRUCT_OFFSET (MonoRemoteClass, proxy_class));
4632 tmp_reg = alloc_preg (cfg);
4633 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, tmp_reg, obj_reg, MONO_STRUCT_OFFSET (MonoTransparentProxy, custom_type_info));
4634 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, tmp_reg, 0);
4635 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, no_proxy_bb);
4637 NEW_BBLOCK (cfg, fail_1_bb);
4639 mini_emit_isninst_cast (cfg, klass_reg, klass, fail_1_bb, ok_result_bb);
4641 MONO_START_BB (cfg, fail_1_bb);
4643 MONO_EMIT_NEW_ICONST (cfg, dreg, 1);
4644 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4646 MONO_START_BB (cfg, no_proxy_bb);
4648 mini_emit_castclass (cfg, obj_reg, klass_reg, klass, ok_result_bb);
4650 g_error ("Transparent proxy support is disabled while trying to JIT code that uses it");
4654 MONO_START_BB (cfg, ok_result_bb);
4656 MONO_EMIT_NEW_ICONST (cfg, dreg, 0);
4658 #ifndef DISABLE_REMOTING
4659 MONO_START_BB (cfg, end_bb);
4663 MONO_INST_NEW (cfg, ins, OP_ICONST);
4665 ins->type = STACK_I4;
4670 static G_GNUC_UNUSED MonoInst*
4671 handle_enum_has_flag (MonoCompile *cfg, MonoClass *klass, MonoInst *enum_this, MonoInst *enum_flag)
4673 MonoType *enum_type = mono_type_get_underlying_type (&klass->byval_arg);
4674 guint32 load_opc = mono_type_to_load_membase (cfg, enum_type);
4675 gboolean is_i4 = TRUE;
4677 switch (enum_type->type) {
4680 #if SIZEOF_REGISTER == 8
4689 MonoInst *load, *and, *cmp, *ceq;
4690 int enum_reg = is_i4 ? alloc_ireg (cfg) : alloc_lreg (cfg);
4691 int and_reg = is_i4 ? alloc_ireg (cfg) : alloc_lreg (cfg);
4692 int dest_reg = alloc_ireg (cfg);
4694 EMIT_NEW_LOAD_MEMBASE (cfg, load, load_opc, enum_reg, enum_this->dreg, 0);
4695 EMIT_NEW_BIALU (cfg, and, is_i4 ? OP_IAND : OP_LAND, and_reg, enum_reg, enum_flag->dreg);
4696 EMIT_NEW_BIALU (cfg, cmp, is_i4 ? OP_ICOMPARE : OP_LCOMPARE, -1, and_reg, enum_flag->dreg);
4697 EMIT_NEW_UNALU (cfg, ceq, is_i4 ? OP_ICEQ : OP_LCEQ, dest_reg, -1);
4699 ceq->type = STACK_I4;
4702 load = mono_decompose_opcode (cfg, load);
4703 and = mono_decompose_opcode (cfg, and);
4704 cmp = mono_decompose_opcode (cfg, cmp);
4705 ceq = mono_decompose_opcode (cfg, ceq);
4713 * Returns NULL and set the cfg exception on error.
4715 static G_GNUC_UNUSED MonoInst*
4716 handle_delegate_ctor (MonoCompile *cfg, MonoClass *klass, MonoInst *target, MonoMethod *method, int context_used, gboolean virtual)
4720 gpointer trampoline;
4721 MonoInst *obj, *method_ins, *tramp_ins;
4725 // FIXME reenable optimisation for virtual case
4730 MonoMethod *invoke = mono_get_delegate_invoke (klass);
4733 if (!mono_get_delegate_virtual_invoke_impl (mono_method_signature (invoke), context_used ? NULL : method))
4737 obj = handle_alloc (cfg, klass, FALSE, 0);
4741 /* Inline the contents of mono_delegate_ctor */
4743 /* Set target field */
4744 /* Optimize away setting of NULL target */
4745 if (!(target->opcode == OP_PCONST && target->inst_p0 == 0)) {
4746 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, target), target->dreg);
4747 if (cfg->gen_write_barriers) {
4748 dreg = alloc_preg (cfg);
4749 EMIT_NEW_BIALU_IMM (cfg, ptr, OP_PADD_IMM, dreg, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, target));
4750 emit_write_barrier (cfg, ptr, target);
4754 /* Set method field */
4755 method_ins = emit_get_rgctx_method (cfg, context_used, method, MONO_RGCTX_INFO_METHOD);
4756 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, method), method_ins->dreg);
4759 * To avoid looking up the compiled code belonging to the target method
4760 * in mono_delegate_trampoline (), we allocate a per-domain memory slot to
4761 * store it, and we fill it after the method has been compiled.
4763 if (!method->dynamic && !(cfg->opt & MONO_OPT_SHARED)) {
4764 MonoInst *code_slot_ins;
4767 code_slot_ins = emit_get_rgctx_method (cfg, context_used, method, MONO_RGCTX_INFO_METHOD_DELEGATE_CODE);
4769 domain = mono_domain_get ();
4770 mono_domain_lock (domain);
4771 if (!domain_jit_info (domain)->method_code_hash)
4772 domain_jit_info (domain)->method_code_hash = g_hash_table_new (NULL, NULL);
4773 code_slot = g_hash_table_lookup (domain_jit_info (domain)->method_code_hash, method);
4775 code_slot = mono_domain_alloc0 (domain, sizeof (gpointer));
4776 g_hash_table_insert (domain_jit_info (domain)->method_code_hash, method, code_slot);
4778 mono_domain_unlock (domain);
4780 if (cfg->compile_aot)
4781 EMIT_NEW_AOTCONST (cfg, code_slot_ins, MONO_PATCH_INFO_METHOD_CODE_SLOT, method);
4783 EMIT_NEW_PCONST (cfg, code_slot_ins, code_slot);
4785 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, method_code), code_slot_ins->dreg);
4788 if (cfg->compile_aot) {
4789 MonoDelegateClassMethodPair *del_tramp;
4791 del_tramp = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoDelegateClassMethodPair));
4792 del_tramp->klass = klass;
4793 del_tramp->method = context_used ? NULL : method;
4794 del_tramp->virtual = virtual;
4795 EMIT_NEW_AOTCONST (cfg, tramp_ins, MONO_PATCH_INFO_DELEGATE_TRAMPOLINE, del_tramp);
4798 trampoline = mono_create_delegate_virtual_trampoline (cfg->domain, klass, context_used ? NULL : method);
4800 trampoline = mono_create_delegate_trampoline_info (cfg->domain, klass, context_used ? NULL : method);
4801 EMIT_NEW_PCONST (cfg, tramp_ins, trampoline);
4804 /* Set invoke_impl field */
4806 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, invoke_impl), tramp_ins->dreg);
4808 dreg = alloc_preg (cfg);
4809 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, dreg, tramp_ins->dreg, MONO_STRUCT_OFFSET (MonoDelegateTrampInfo, invoke_impl));
4810 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, invoke_impl), dreg);
4812 dreg = alloc_preg (cfg);
4813 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, dreg, tramp_ins->dreg, MONO_STRUCT_OFFSET (MonoDelegateTrampInfo, method_ptr));
4814 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, method_ptr), dreg);
4817 /* All the checks which are in mono_delegate_ctor () are done by the delegate trampoline */
4823 handle_array_new (MonoCompile *cfg, int rank, MonoInst **sp, unsigned char *ip)
4825 MonoJitICallInfo *info;
4827 /* Need to register the icall so it gets an icall wrapper */
4828 info = mono_get_array_new_va_icall (rank);
4830 cfg->flags |= MONO_CFG_HAS_VARARGS;
4832 /* mono_array_new_va () needs a vararg calling convention */
4833 cfg->disable_llvm = TRUE;
4835 /* FIXME: This uses info->sig, but it should use the signature of the wrapper */
4836 return mono_emit_native_call (cfg, mono_icall_get_wrapper (info), info->sig, sp);
4840 mono_emit_load_got_addr (MonoCompile *cfg)
4842 MonoInst *getaddr, *dummy_use;
4844 if (!cfg->got_var || cfg->got_var_allocated)
4847 MONO_INST_NEW (cfg, getaddr, OP_LOAD_GOTADDR);
4848 getaddr->cil_code = cfg->header->code;
4849 getaddr->dreg = cfg->got_var->dreg;
4851 /* Add it to the start of the first bblock */
4852 if (cfg->bb_entry->code) {
4853 getaddr->next = cfg->bb_entry->code;
4854 cfg->bb_entry->code = getaddr;
4857 MONO_ADD_INS (cfg->bb_entry, getaddr);
4859 cfg->got_var_allocated = TRUE;
4862 * Add a dummy use to keep the got_var alive, since real uses might
4863 * only be generated by the back ends.
4864 * Add it to end_bblock, so the variable's lifetime covers the whole
4866 * It would be better to make the usage of the got var explicit in all
4867 * cases when the backend needs it (i.e. calls, throw etc.), so this
4868 * wouldn't be needed.
4870 NEW_DUMMY_USE (cfg, dummy_use, cfg->got_var);
4871 MONO_ADD_INS (cfg->bb_exit, dummy_use);
4874 static int inline_limit;
4875 static gboolean inline_limit_inited;
4878 mono_method_check_inlining (MonoCompile *cfg, MonoMethod *method)
4880 MonoMethodHeaderSummary header;
4882 #ifdef MONO_ARCH_SOFT_FLOAT_FALLBACK
4883 MonoMethodSignature *sig = mono_method_signature (method);
4887 if (cfg->disable_inline)
4889 if (cfg->generic_sharing_context)
4892 if (cfg->inline_depth > 10)
4895 #ifdef MONO_ARCH_HAVE_LMF_OPS
4896 if (((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
4897 (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)) &&
4898 !MONO_TYPE_ISSTRUCT (signature->ret) && !mini_class_is_system_array (method->klass))
4903 if (!mono_method_get_header_summary (method, &header))
4906 /*runtime, icall and pinvoke are checked by summary call*/
4907 if ((method->iflags & METHOD_IMPL_ATTRIBUTE_NOINLINING) ||
4908 (method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED) ||
4909 (mono_class_is_marshalbyref (method->klass)) ||
4913 /* also consider num_locals? */
4914 /* Do the size check early to avoid creating vtables */
4915 if (!inline_limit_inited) {
4916 if (g_getenv ("MONO_INLINELIMIT"))
4917 inline_limit = atoi (g_getenv ("MONO_INLINELIMIT"));
4919 inline_limit = INLINE_LENGTH_LIMIT;
4920 inline_limit_inited = TRUE;
4922 if (header.code_size >= inline_limit && !(method->iflags & METHOD_IMPL_ATTRIBUTE_AGGRESSIVE_INLINING))
4926 * if we can initialize the class of the method right away, we do,
4927 * otherwise we don't allow inlining if the class needs initialization,
4928 * since it would mean inserting a call to mono_runtime_class_init()
4929 * inside the inlined code
4931 if (!(cfg->opt & MONO_OPT_SHARED)) {
4932 /* The AggressiveInlining hint is a good excuse to force that cctor to run. */
4933 if (method->iflags & METHOD_IMPL_ATTRIBUTE_AGGRESSIVE_INLINING) {
4934 vtable = mono_class_vtable (cfg->domain, method->klass);
4937 if (!cfg->compile_aot)
4938 mono_runtime_class_init (vtable);
4939 } else if (method->klass->flags & TYPE_ATTRIBUTE_BEFORE_FIELD_INIT) {
4940 if (cfg->run_cctors && method->klass->has_cctor) {
4941 /*FIXME it would easier and lazier to just use mono_class_try_get_vtable */
4942 if (!method->klass->runtime_info)
4943 /* No vtable created yet */
4945 vtable = mono_class_vtable (cfg->domain, method->klass);
4948 /* This makes so that inline cannot trigger */
4949 /* .cctors: too many apps depend on them */
4950 /* running with a specific order... */
4951 if (! vtable->initialized)
4953 mono_runtime_class_init (vtable);
4955 } else if (mono_class_needs_cctor_run (method->klass, NULL)) {
4956 if (!method->klass->runtime_info)
4957 /* No vtable created yet */
4959 vtable = mono_class_vtable (cfg->domain, method->klass);
4962 if (!vtable->initialized)
4967 * If we're compiling for shared code
4968 * the cctor will need to be run at aot method load time, for example,
4969 * or at the end of the compilation of the inlining method.
4971 if (mono_class_needs_cctor_run (method->klass, NULL) && !((method->klass->flags & TYPE_ATTRIBUTE_BEFORE_FIELD_INIT)))
4976 * CAS - do not inline methods with declarative security
4977 * Note: this has to be before any possible return TRUE;
4979 if (mono_security_method_has_declsec (method))
4982 #ifdef MONO_ARCH_SOFT_FLOAT_FALLBACK
4983 if (mono_arch_is_soft_float ()) {
4985 if (sig->ret && sig->ret->type == MONO_TYPE_R4)
4987 for (i = 0; i < sig->param_count; ++i)
4988 if (!sig->params [i]->byref && sig->params [i]->type == MONO_TYPE_R4)
4993 if (g_list_find (cfg->dont_inline, method))
5000 mini_field_access_needs_cctor_run (MonoCompile *cfg, MonoMethod *method, MonoClass *klass, MonoVTable *vtable)
5002 if (!cfg->compile_aot) {
5004 if (vtable->initialized)
5008 if (klass->flags & TYPE_ATTRIBUTE_BEFORE_FIELD_INIT) {
5009 if (cfg->method == method)
5013 if (!mono_class_needs_cctor_run (klass, method))
5016 if (! (method->flags & METHOD_ATTRIBUTE_STATIC) && (klass == method->klass))
5017 /* The initialization is already done before the method is called */
5024 mini_emit_ldelema_1_ins (MonoCompile *cfg, MonoClass *klass, MonoInst *arr, MonoInst *index, gboolean bcheck)
5028 int mult_reg, add_reg, array_reg, index_reg, index2_reg;
5031 if (mini_is_gsharedvt_variable_klass (cfg, klass)) {
5034 mono_class_init (klass);
5035 size = mono_class_array_element_size (klass);
5038 mult_reg = alloc_preg (cfg);
5039 array_reg = arr->dreg;
5040 index_reg = index->dreg;
5042 #if SIZEOF_REGISTER == 8
5043 /* The array reg is 64 bits but the index reg is only 32 */
5044 if (COMPILE_LLVM (cfg)) {
5046 index2_reg = index_reg;
5048 index2_reg = alloc_preg (cfg);
5049 MONO_EMIT_NEW_UNALU (cfg, OP_SEXT_I4, index2_reg, index_reg);
5052 if (index->type == STACK_I8) {
5053 index2_reg = alloc_preg (cfg);
5054 MONO_EMIT_NEW_UNALU (cfg, OP_LCONV_TO_I4, index2_reg, index_reg);
5056 index2_reg = index_reg;
5061 MONO_EMIT_BOUNDS_CHECK (cfg, array_reg, MonoArray, max_length, index2_reg);
5063 #if defined(TARGET_X86) || defined(TARGET_AMD64)
5064 if (size == 1 || size == 2 || size == 4 || size == 8) {
5065 static const int fast_log2 [] = { 1, 0, 1, -1, 2, -1, -1, -1, 3 };
5067 EMIT_NEW_X86_LEA (cfg, ins, array_reg, index2_reg, fast_log2 [size], MONO_STRUCT_OFFSET (MonoArray, vector));
5068 ins->klass = mono_class_get_element_class (klass);
5069 ins->type = STACK_MP;
5075 add_reg = alloc_ireg_mp (cfg);
5078 MonoInst *rgctx_ins;
5081 g_assert (cfg->generic_sharing_context);
5082 context_used = mini_class_check_context_used (cfg, klass);
5083 g_assert (context_used);
5084 rgctx_ins = emit_get_gsharedvt_info (cfg, &klass->byval_arg, MONO_RGCTX_INFO_ARRAY_ELEMENT_SIZE);
5085 MONO_EMIT_NEW_BIALU (cfg, OP_IMUL, mult_reg, index2_reg, rgctx_ins->dreg);
5087 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_MUL_IMM, mult_reg, index2_reg, size);
5089 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, add_reg, array_reg, mult_reg);
5090 NEW_BIALU_IMM (cfg, ins, OP_PADD_IMM, add_reg, add_reg, MONO_STRUCT_OFFSET (MonoArray, vector));
5091 ins->klass = mono_class_get_element_class (klass);
5092 ins->type = STACK_MP;
5093 MONO_ADD_INS (cfg->cbb, ins);
5098 #ifndef MONO_ARCH_EMULATE_MUL_DIV
5100 mini_emit_ldelema_2_ins (MonoCompile *cfg, MonoClass *klass, MonoInst *arr, MonoInst *index_ins1, MonoInst *index_ins2)
5102 int bounds_reg = alloc_preg (cfg);
5103 int add_reg = alloc_ireg_mp (cfg);
5104 int mult_reg = alloc_preg (cfg);
5105 int mult2_reg = alloc_preg (cfg);
5106 int low1_reg = alloc_preg (cfg);
5107 int low2_reg = alloc_preg (cfg);
5108 int high1_reg = alloc_preg (cfg);
5109 int high2_reg = alloc_preg (cfg);
5110 int realidx1_reg = alloc_preg (cfg);
5111 int realidx2_reg = alloc_preg (cfg);
5112 int sum_reg = alloc_preg (cfg);
5113 int index1, index2, tmpreg;
5117 mono_class_init (klass);
5118 size = mono_class_array_element_size (klass);
5120 index1 = index_ins1->dreg;
5121 index2 = index_ins2->dreg;
5123 #if SIZEOF_REGISTER == 8
5124 /* The array reg is 64 bits but the index reg is only 32 */
5125 if (COMPILE_LLVM (cfg)) {
5128 tmpreg = alloc_preg (cfg);
5129 MONO_EMIT_NEW_UNALU (cfg, OP_SEXT_I4, tmpreg, index1);
5131 tmpreg = alloc_preg (cfg);
5132 MONO_EMIT_NEW_UNALU (cfg, OP_SEXT_I4, tmpreg, index2);
5136 // FIXME: Do we need to do something here for i8 indexes, like in ldelema_1_ins ?
5140 /* range checking */
5141 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, bounds_reg,
5142 arr->dreg, MONO_STRUCT_OFFSET (MonoArray, bounds));
5144 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI4_MEMBASE, low1_reg,
5145 bounds_reg, MONO_STRUCT_OFFSET (MonoArrayBounds, lower_bound));
5146 MONO_EMIT_NEW_BIALU (cfg, OP_PSUB, realidx1_reg, index1, low1_reg);
5147 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI4_MEMBASE, high1_reg,
5148 bounds_reg, MONO_STRUCT_OFFSET (MonoArrayBounds, length));
5149 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, high1_reg, realidx1_reg);
5150 MONO_EMIT_NEW_COND_EXC (cfg, LE_UN, "IndexOutOfRangeException");
5152 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI4_MEMBASE, low2_reg,
5153 bounds_reg, sizeof (MonoArrayBounds) + MONO_STRUCT_OFFSET (MonoArrayBounds, lower_bound));
5154 MONO_EMIT_NEW_BIALU (cfg, OP_PSUB, realidx2_reg, index2, low2_reg);
5155 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI4_MEMBASE, high2_reg,
5156 bounds_reg, sizeof (MonoArrayBounds) + MONO_STRUCT_OFFSET (MonoArrayBounds, length));
5157 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, high2_reg, realidx2_reg);
5158 MONO_EMIT_NEW_COND_EXC (cfg, LE_UN, "IndexOutOfRangeException");
5160 MONO_EMIT_NEW_BIALU (cfg, OP_PMUL, mult_reg, high2_reg, realidx1_reg);
5161 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, sum_reg, mult_reg, realidx2_reg);
5162 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_PMUL_IMM, mult2_reg, sum_reg, size);
5163 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, add_reg, mult2_reg, arr->dreg);
5164 NEW_BIALU_IMM (cfg, ins, OP_PADD_IMM, add_reg, add_reg, MONO_STRUCT_OFFSET (MonoArray, vector));
5166 ins->type = STACK_MP;
5168 MONO_ADD_INS (cfg->cbb, ins);
5175 mini_emit_ldelema_ins (MonoCompile *cfg, MonoMethod *cmethod, MonoInst **sp, unsigned char *ip, gboolean is_set)
5179 MonoMethod *addr_method;
5182 rank = mono_method_signature (cmethod)->param_count - (is_set? 1: 0);
5185 return mini_emit_ldelema_1_ins (cfg, cmethod->klass->element_class, sp [0], sp [1], TRUE);
5187 #ifndef MONO_ARCH_EMULATE_MUL_DIV
5188 /* emit_ldelema_2 depends on OP_LMUL */
5189 if (rank == 2 && (cfg->opt & MONO_OPT_INTRINS)) {
5190 return mini_emit_ldelema_2_ins (cfg, cmethod->klass->element_class, sp [0], sp [1], sp [2]);
5194 element_size = mono_class_array_element_size (cmethod->klass->element_class);
5195 addr_method = mono_marshal_get_array_address (rank, element_size);
5196 addr = mono_emit_method_call (cfg, addr_method, sp, NULL);
5201 static MonoBreakPolicy
5202 always_insert_breakpoint (MonoMethod *method)
5204 return MONO_BREAK_POLICY_ALWAYS;
5207 static MonoBreakPolicyFunc break_policy_func = always_insert_breakpoint;
5210 * mono_set_break_policy:
5211 * policy_callback: the new callback function
5213 * Allow embedders to decide wherther to actually obey breakpoint instructions
5214 * (both break IL instructions and Debugger.Break () method calls), for example
5215 * to not allow an app to be aborted by a perfectly valid IL opcode when executing
5216 * untrusted or semi-trusted code.
5218 * @policy_callback will be called every time a break point instruction needs to
5219 * be inserted with the method argument being the method that calls Debugger.Break()
5220 * or has the IL break instruction. The callback should return #MONO_BREAK_POLICY_NEVER
5221 * if it wants the breakpoint to not be effective in the given method.
5222 * #MONO_BREAK_POLICY_ALWAYS is the default.
5225 mono_set_break_policy (MonoBreakPolicyFunc policy_callback)
5227 if (policy_callback)
5228 break_policy_func = policy_callback;
5230 break_policy_func = always_insert_breakpoint;
5234 should_insert_brekpoint (MonoMethod *method) {
5235 switch (break_policy_func (method)) {
5236 case MONO_BREAK_POLICY_ALWAYS:
5238 case MONO_BREAK_POLICY_NEVER:
5240 case MONO_BREAK_POLICY_ON_DBG:
5241 g_warning ("mdb no longer supported");
5244 g_warning ("Incorrect value returned from break policy callback");
5249 /* optimize the simple GetGenericValueImpl/SetGenericValueImpl generic icalls */
5251 emit_array_generic_access (MonoCompile *cfg, MonoMethodSignature *fsig, MonoInst **args, int is_set)
5253 MonoInst *addr, *store, *load;
5254 MonoClass *eklass = mono_class_from_mono_type (fsig->params [2]);
5256 /* the bounds check is already done by the callers */
5257 addr = mini_emit_ldelema_1_ins (cfg, eklass, args [0], args [1], FALSE);
5259 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, load, &eklass->byval_arg, args [2]->dreg, 0);
5260 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, store, &eklass->byval_arg, addr->dreg, 0, load->dreg);
5261 if (mini_type_is_reference (cfg, fsig->params [2]))
5262 emit_write_barrier (cfg, addr, load);
5264 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, load, &eklass->byval_arg, addr->dreg, 0);
5265 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, store, &eklass->byval_arg, args [2]->dreg, 0, load->dreg);
5272 generic_class_is_reference_type (MonoCompile *cfg, MonoClass *klass)
5274 return mini_type_is_reference (cfg, &klass->byval_arg);
5278 emit_array_store (MonoCompile *cfg, MonoClass *klass, MonoInst **sp, gboolean safety_checks)
5280 if (safety_checks && generic_class_is_reference_type (cfg, klass) &&
5281 !(sp [2]->opcode == OP_PCONST && sp [2]->inst_p0 == NULL)) {
5282 MonoClass *obj_array = mono_array_class_get_cached (mono_defaults.object_class, 1);
5283 MonoMethod *helper = mono_marshal_get_virtual_stelemref (obj_array);
5284 MonoInst *iargs [3];
5287 mono_class_setup_vtable (obj_array);
5288 g_assert (helper->slot);
5290 if (sp [0]->type != STACK_OBJ)
5292 if (sp [2]->type != STACK_OBJ)
5299 return mono_emit_method_call (cfg, helper, iargs, sp [0]);
5303 if (mini_is_gsharedvt_variable_klass (cfg, klass)) {
5306 // FIXME-VT: OP_ICONST optimization
5307 addr = mini_emit_ldelema_1_ins (cfg, klass, sp [0], sp [1], TRUE);
5308 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, addr->dreg, 0, sp [2]->dreg);
5309 ins->opcode = OP_STOREV_MEMBASE;
5310 } else if (sp [1]->opcode == OP_ICONST) {
5311 int array_reg = sp [0]->dreg;
5312 int index_reg = sp [1]->dreg;
5313 int offset = (mono_class_array_element_size (klass) * sp [1]->inst_c0) + MONO_STRUCT_OFFSET (MonoArray, vector);
5316 MONO_EMIT_BOUNDS_CHECK (cfg, array_reg, MonoArray, max_length, index_reg);
5317 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, array_reg, offset, sp [2]->dreg);
5319 MonoInst *addr = mini_emit_ldelema_1_ins (cfg, klass, sp [0], sp [1], safety_checks);
5320 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, addr->dreg, 0, sp [2]->dreg);
5321 if (generic_class_is_reference_type (cfg, klass))
5322 emit_write_barrier (cfg, addr, sp [2]);
5329 emit_array_unsafe_access (MonoCompile *cfg, MonoMethodSignature *fsig, MonoInst **args, int is_set)
5334 eklass = mono_class_from_mono_type (fsig->params [2]);
5336 eklass = mono_class_from_mono_type (fsig->ret);
5339 return emit_array_store (cfg, eklass, args, FALSE);
5341 MonoInst *ins, *addr = mini_emit_ldelema_1_ins (cfg, eklass, args [0], args [1], FALSE);
5342 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &eklass->byval_arg, addr->dreg, 0);
5348 is_unsafe_mov_compatible (MonoClass *param_klass, MonoClass *return_klass)
5352 //Only allow for valuetypes
5353 if (!param_klass->valuetype || !return_klass->valuetype)
5357 if (param_klass->has_references || return_klass->has_references)
5360 /* Avoid mixing structs and primitive types/enums, they need to be handled differently in the JIT */
5361 if ((MONO_TYPE_ISSTRUCT (¶m_klass->byval_arg) && !MONO_TYPE_ISSTRUCT (&return_klass->byval_arg)) ||
5362 (!MONO_TYPE_ISSTRUCT (¶m_klass->byval_arg) && MONO_TYPE_ISSTRUCT (&return_klass->byval_arg)))
5365 if (param_klass->byval_arg.type == MONO_TYPE_R4 || param_klass->byval_arg.type == MONO_TYPE_R8 ||
5366 return_klass->byval_arg.type == MONO_TYPE_R4 || return_klass->byval_arg.type == MONO_TYPE_R8)
5369 //And have the same size
5370 if (mono_class_value_size (param_klass, &align) != mono_class_value_size (return_klass, &align))
5376 emit_array_unsafe_mov (MonoCompile *cfg, MonoMethodSignature *fsig, MonoInst **args)
5378 MonoClass *param_klass = mono_class_from_mono_type (fsig->params [0]);
5379 MonoClass *return_klass = mono_class_from_mono_type (fsig->ret);
5381 //Valuetypes that are semantically equivalent
5382 if (is_unsafe_mov_compatible (param_klass, return_klass))
5385 //Arrays of valuetypes that are semantically equivalent
5386 if (param_klass->rank == 1 && return_klass->rank == 1 && is_unsafe_mov_compatible (param_klass->element_class, return_klass->element_class))
5393 mini_emit_inst_for_ctor (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
5395 #ifdef MONO_ARCH_SIMD_INTRINSICS
5396 MonoInst *ins = NULL;
5398 if (cfg->opt & MONO_OPT_SIMD) {
5399 ins = mono_emit_simd_intrinsics (cfg, cmethod, fsig, args);
5405 return mono_emit_native_types_intrinsics (cfg, cmethod, fsig, args);
5409 emit_memory_barrier (MonoCompile *cfg, int kind)
5411 MonoInst *ins = NULL;
5412 MONO_INST_NEW (cfg, ins, OP_MEMORY_BARRIER);
5413 MONO_ADD_INS (cfg->cbb, ins);
5414 ins->backend.memory_barrier_kind = kind;
5420 llvm_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
5422 MonoInst *ins = NULL;
5425 /* The LLVM backend supports these intrinsics */
5426 if (cmethod->klass == mono_defaults.math_class) {
5427 if (strcmp (cmethod->name, "Sin") == 0) {
5429 } else if (strcmp (cmethod->name, "Cos") == 0) {
5431 } else if (strcmp (cmethod->name, "Sqrt") == 0) {
5433 } else if (strcmp (cmethod->name, "Abs") == 0 && fsig->params [0]->type == MONO_TYPE_R8) {
5437 if (opcode && fsig->param_count == 1) {
5438 MONO_INST_NEW (cfg, ins, opcode);
5439 ins->type = STACK_R8;
5440 ins->dreg = mono_alloc_freg (cfg);
5441 ins->sreg1 = args [0]->dreg;
5442 MONO_ADD_INS (cfg->cbb, ins);
5446 if (cfg->opt & MONO_OPT_CMOV) {
5447 if (strcmp (cmethod->name, "Min") == 0) {
5448 if (fsig->params [0]->type == MONO_TYPE_I4)
5450 if (fsig->params [0]->type == MONO_TYPE_U4)
5451 opcode = OP_IMIN_UN;
5452 else if (fsig->params [0]->type == MONO_TYPE_I8)
5454 else if (fsig->params [0]->type == MONO_TYPE_U8)
5455 opcode = OP_LMIN_UN;
5456 } else if (strcmp (cmethod->name, "Max") == 0) {
5457 if (fsig->params [0]->type == MONO_TYPE_I4)
5459 if (fsig->params [0]->type == MONO_TYPE_U4)
5460 opcode = OP_IMAX_UN;
5461 else if (fsig->params [0]->type == MONO_TYPE_I8)
5463 else if (fsig->params [0]->type == MONO_TYPE_U8)
5464 opcode = OP_LMAX_UN;
5468 if (opcode && fsig->param_count == 2) {
5469 MONO_INST_NEW (cfg, ins, opcode);
5470 ins->type = fsig->params [0]->type == MONO_TYPE_I4 ? STACK_I4 : STACK_I8;
5471 ins->dreg = mono_alloc_ireg (cfg);
5472 ins->sreg1 = args [0]->dreg;
5473 ins->sreg2 = args [1]->dreg;
5474 MONO_ADD_INS (cfg->cbb, ins);
5482 mini_emit_inst_for_sharable_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
5484 if (cmethod->klass == mono_defaults.array_class) {
5485 if (strcmp (cmethod->name, "UnsafeStore") == 0)
5486 return emit_array_unsafe_access (cfg, fsig, args, TRUE);
5487 else if (strcmp (cmethod->name, "UnsafeLoad") == 0)
5488 return emit_array_unsafe_access (cfg, fsig, args, FALSE);
5489 else if (strcmp (cmethod->name, "UnsafeMov") == 0)
5490 return emit_array_unsafe_mov (cfg, fsig, args);
5497 mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
5499 MonoInst *ins = NULL;
5501 static MonoClass *runtime_helpers_class = NULL;
5502 if (! runtime_helpers_class)
5503 runtime_helpers_class = mono_class_from_name (mono_defaults.corlib,
5504 "System.Runtime.CompilerServices", "RuntimeHelpers");
5506 if (cmethod->klass == mono_defaults.string_class) {
5507 if (strcmp (cmethod->name, "get_Chars") == 0 && fsig->param_count == 2) {
5508 int dreg = alloc_ireg (cfg);
5509 int index_reg = alloc_preg (cfg);
5510 int mult_reg = alloc_preg (cfg);
5511 int add_reg = alloc_preg (cfg);
5513 #if SIZEOF_REGISTER == 8
5514 /* The array reg is 64 bits but the index reg is only 32 */
5515 MONO_EMIT_NEW_UNALU (cfg, OP_SEXT_I4, index_reg, args [1]->dreg);
5517 index_reg = args [1]->dreg;
5519 MONO_EMIT_BOUNDS_CHECK (cfg, args [0]->dreg, MonoString, length, index_reg);
5521 #if defined(TARGET_X86) || defined(TARGET_AMD64)
5522 EMIT_NEW_X86_LEA (cfg, ins, args [0]->dreg, index_reg, 1, MONO_STRUCT_OFFSET (MonoString, chars));
5523 add_reg = ins->dreg;
5524 /* Avoid a warning */
5526 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADU2_MEMBASE, dreg,
5529 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SHL_IMM, mult_reg, index_reg, 1);
5530 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, add_reg, mult_reg, args [0]->dreg);
5531 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADU2_MEMBASE, dreg,
5532 add_reg, MONO_STRUCT_OFFSET (MonoString, chars));
5534 type_from_op (cfg, ins, NULL, NULL);
5536 } else if (strcmp (cmethod->name, "get_Length") == 0 && fsig->param_count == 1) {
5537 int dreg = alloc_ireg (cfg);
5538 /* Decompose later to allow more optimizations */
5539 EMIT_NEW_UNALU (cfg, ins, OP_STRLEN, dreg, args [0]->dreg);
5540 ins->type = STACK_I4;
5541 ins->flags |= MONO_INST_FAULT;
5542 cfg->cbb->has_array_access = TRUE;
5543 cfg->flags |= MONO_CFG_HAS_ARRAY_ACCESS;
5546 } else if (strcmp (cmethod->name, "InternalSetChar") == 0 && fsig->param_count == 3) {
5547 int mult_reg = alloc_preg (cfg);
5548 int add_reg = alloc_preg (cfg);
5550 /* The corlib functions check for oob already. */
5551 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SHL_IMM, mult_reg, args [1]->dreg, 1);
5552 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, add_reg, mult_reg, args [0]->dreg);
5553 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI2_MEMBASE_REG, add_reg, MONO_STRUCT_OFFSET (MonoString, chars), args [2]->dreg);
5554 return cfg->cbb->last_ins;
5557 } else if (cmethod->klass == mono_defaults.object_class) {
5559 if (strcmp (cmethod->name, "GetType") == 0 && fsig->param_count == 1) {
5560 int dreg = alloc_ireg_ref (cfg);
5561 int vt_reg = alloc_preg (cfg);
5562 MONO_EMIT_NEW_LOAD_MEMBASE_FAULT (cfg, vt_reg, args [0]->dreg, MONO_STRUCT_OFFSET (MonoObject, vtable));
5563 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, dreg, vt_reg, MONO_STRUCT_OFFSET (MonoVTable, type));
5564 type_from_op (cfg, ins, NULL, NULL);
5567 #if !defined(MONO_ARCH_EMULATE_MUL_DIV)
5568 } else if (strcmp (cmethod->name, "InternalGetHashCode") == 0 && fsig->param_count == 1 && !mono_gc_is_moving ()) {
5569 int dreg = alloc_ireg (cfg);
5570 int t1 = alloc_ireg (cfg);
5572 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SHL_IMM, t1, args [0]->dreg, 3);
5573 EMIT_NEW_BIALU_IMM (cfg, ins, OP_MUL_IMM, dreg, t1, 2654435761u);
5574 ins->type = STACK_I4;
5578 } else if (strcmp (cmethod->name, ".ctor") == 0 && fsig->param_count == 0) {
5579 MONO_INST_NEW (cfg, ins, OP_NOP);
5580 MONO_ADD_INS (cfg->cbb, ins);
5584 } else if (cmethod->klass == mono_defaults.array_class) {
5585 if (strcmp (cmethod->name, "GetGenericValueImpl") == 0 && fsig->param_count == 3 && !cfg->gsharedvt)
5586 return emit_array_generic_access (cfg, fsig, args, FALSE);
5587 else if (strcmp (cmethod->name, "SetGenericValueImpl") == 0 && fsig->param_count == 3 && !cfg->gsharedvt)
5588 return emit_array_generic_access (cfg, fsig, args, TRUE);
5590 #ifndef MONO_BIG_ARRAYS
5592 * This is an inline version of GetLength/GetLowerBound(0) used frequently in
5595 else if (((strcmp (cmethod->name, "GetLength") == 0 && fsig->param_count == 2) ||
5596 (strcmp (cmethod->name, "GetLowerBound") == 0 && fsig->param_count == 2)) &&
5597 args [1]->opcode == OP_ICONST && args [1]->inst_c0 == 0) {
5598 int dreg = alloc_ireg (cfg);
5599 int bounds_reg = alloc_ireg_mp (cfg);
5600 MonoBasicBlock *end_bb, *szarray_bb;
5601 gboolean get_length = strcmp (cmethod->name, "GetLength") == 0;
5603 NEW_BBLOCK (cfg, end_bb);
5604 NEW_BBLOCK (cfg, szarray_bb);
5606 EMIT_NEW_LOAD_MEMBASE_FAULT (cfg, ins, OP_LOAD_MEMBASE, bounds_reg,
5607 args [0]->dreg, MONO_STRUCT_OFFSET (MonoArray, bounds));
5608 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, bounds_reg, 0);
5609 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, szarray_bb);
5610 /* Non-szarray case */
5612 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADI4_MEMBASE, dreg,
5613 bounds_reg, MONO_STRUCT_OFFSET (MonoArrayBounds, length));
5615 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADI4_MEMBASE, dreg,
5616 bounds_reg, MONO_STRUCT_OFFSET (MonoArrayBounds, lower_bound));
5617 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
5618 MONO_START_BB (cfg, szarray_bb);
5621 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADI4_MEMBASE, dreg,
5622 args [0]->dreg, MONO_STRUCT_OFFSET (MonoArray, max_length));
5624 MONO_EMIT_NEW_ICONST (cfg, dreg, 0);
5625 MONO_START_BB (cfg, end_bb);
5627 EMIT_NEW_UNALU (cfg, ins, OP_MOVE, dreg, dreg);
5628 ins->type = STACK_I4;
5634 if (cmethod->name [0] != 'g')
5637 if (strcmp (cmethod->name, "get_Rank") == 0 && fsig->param_count == 1) {
5638 int dreg = alloc_ireg (cfg);
5639 int vtable_reg = alloc_preg (cfg);
5640 MONO_EMIT_NEW_LOAD_MEMBASE_OP_FAULT (cfg, OP_LOAD_MEMBASE, vtable_reg,
5641 args [0]->dreg, MONO_STRUCT_OFFSET (MonoObject, vtable));
5642 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADU1_MEMBASE, dreg,
5643 vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, rank));
5644 type_from_op (cfg, ins, NULL, NULL);
5647 } else if (strcmp (cmethod->name, "get_Length") == 0 && fsig->param_count == 1) {
5648 int dreg = alloc_ireg (cfg);
5650 EMIT_NEW_LOAD_MEMBASE_FAULT (cfg, ins, OP_LOADI4_MEMBASE, dreg,
5651 args [0]->dreg, MONO_STRUCT_OFFSET (MonoArray, max_length));
5652 type_from_op (cfg, ins, NULL, NULL);
5657 } else if (cmethod->klass == runtime_helpers_class) {
5659 if (strcmp (cmethod->name, "get_OffsetToStringData") == 0 && fsig->param_count == 0) {
5660 EMIT_NEW_ICONST (cfg, ins, MONO_STRUCT_OFFSET (MonoString, chars));
5664 } else if (cmethod->klass == mono_defaults.thread_class) {
5665 if (strcmp (cmethod->name, "SpinWait_nop") == 0 && fsig->param_count == 0) {
5666 MONO_INST_NEW (cfg, ins, OP_RELAXED_NOP);
5667 MONO_ADD_INS (cfg->cbb, ins);
5669 } else if (strcmp (cmethod->name, "MemoryBarrier") == 0 && fsig->param_count == 0) {
5670 return emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_SEQ);
5671 } else if (!strcmp (cmethod->name, "VolatileRead") && fsig->param_count == 1) {
5673 gboolean is_ref = mini_type_is_reference (cfg, fsig->params [0]);
5675 if (fsig->params [0]->type == MONO_TYPE_I1)
5676 opcode = OP_LOADI1_MEMBASE;
5677 else if (fsig->params [0]->type == MONO_TYPE_U1)
5678 opcode = OP_LOADU1_MEMBASE;
5679 else if (fsig->params [0]->type == MONO_TYPE_I2)
5680 opcode = OP_LOADI2_MEMBASE;
5681 else if (fsig->params [0]->type == MONO_TYPE_U2)
5682 opcode = OP_LOADU2_MEMBASE;
5683 else if (fsig->params [0]->type == MONO_TYPE_I4)
5684 opcode = OP_LOADI4_MEMBASE;
5685 else if (fsig->params [0]->type == MONO_TYPE_U4)
5686 opcode = OP_LOADU4_MEMBASE;
5687 else if (fsig->params [0]->type == MONO_TYPE_I8 || fsig->params [0]->type == MONO_TYPE_U8)
5688 opcode = OP_LOADI8_MEMBASE;
5689 else if (fsig->params [0]->type == MONO_TYPE_R4)
5690 opcode = OP_LOADR4_MEMBASE;
5691 else if (fsig->params [0]->type == MONO_TYPE_R8)
5692 opcode = OP_LOADR8_MEMBASE;
5693 else if (is_ref || fsig->params [0]->type == MONO_TYPE_I || fsig->params [0]->type == MONO_TYPE_U)
5694 opcode = OP_LOAD_MEMBASE;
5697 MONO_INST_NEW (cfg, ins, opcode);
5698 ins->inst_basereg = args [0]->dreg;
5699 ins->inst_offset = 0;
5700 MONO_ADD_INS (cfg->cbb, ins);
5702 switch (fsig->params [0]->type) {
5709 ins->dreg = mono_alloc_ireg (cfg);
5710 ins->type = STACK_I4;
5714 ins->dreg = mono_alloc_lreg (cfg);
5715 ins->type = STACK_I8;
5719 ins->dreg = mono_alloc_ireg (cfg);
5720 #if SIZEOF_REGISTER == 8
5721 ins->type = STACK_I8;
5723 ins->type = STACK_I4;
5728 ins->dreg = mono_alloc_freg (cfg);
5729 ins->type = STACK_R8;
5732 g_assert (mini_type_is_reference (cfg, fsig->params [0]));
5733 ins->dreg = mono_alloc_ireg_ref (cfg);
5734 ins->type = STACK_OBJ;
5738 if (opcode == OP_LOADI8_MEMBASE)
5739 ins = mono_decompose_opcode (cfg, ins);
5741 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_ACQ);
5745 } else if (!strcmp (cmethod->name, "VolatileWrite") && fsig->param_count == 2) {
5747 gboolean is_ref = mini_type_is_reference (cfg, fsig->params [0]);
5749 if (fsig->params [0]->type == MONO_TYPE_I1 || fsig->params [0]->type == MONO_TYPE_U1)
5750 opcode = OP_STOREI1_MEMBASE_REG;
5751 else if (fsig->params [0]->type == MONO_TYPE_I2 || fsig->params [0]->type == MONO_TYPE_U2)
5752 opcode = OP_STOREI2_MEMBASE_REG;
5753 else if (fsig->params [0]->type == MONO_TYPE_I4 || fsig->params [0]->type == MONO_TYPE_U4)
5754 opcode = OP_STOREI4_MEMBASE_REG;
5755 else if (fsig->params [0]->type == MONO_TYPE_I8 || fsig->params [0]->type == MONO_TYPE_U8)
5756 opcode = OP_STOREI8_MEMBASE_REG;
5757 else if (fsig->params [0]->type == MONO_TYPE_R4)
5758 opcode = OP_STORER4_MEMBASE_REG;
5759 else if (fsig->params [0]->type == MONO_TYPE_R8)
5760 opcode = OP_STORER8_MEMBASE_REG;
5761 else if (is_ref || fsig->params [0]->type == MONO_TYPE_I || fsig->params [0]->type == MONO_TYPE_U)
5762 opcode = OP_STORE_MEMBASE_REG;
5765 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_REL);
5767 MONO_INST_NEW (cfg, ins, opcode);
5768 ins->sreg1 = args [1]->dreg;
5769 ins->inst_destbasereg = args [0]->dreg;
5770 ins->inst_offset = 0;
5771 MONO_ADD_INS (cfg->cbb, ins);
5773 if (opcode == OP_STOREI8_MEMBASE_REG)
5774 ins = mono_decompose_opcode (cfg, ins);
5779 } else if (cmethod->klass == mono_defaults.monitor_class) {
5780 #if defined(MONO_ARCH_MONITOR_OBJECT_REG)
5781 if (strcmp (cmethod->name, "Enter") == 0 && fsig->param_count == 1) {
5784 if (COMPILE_LLVM (cfg)) {
5786 * Pass the argument normally, the LLVM backend will handle the
5787 * calling convention problems.
5789 call = (MonoCallInst*)mono_emit_abs_call (cfg, MONO_PATCH_INFO_MONITOR_ENTER, NULL, helper_sig_monitor_enter_exit_trampoline_llvm, args);
5791 call = (MonoCallInst*)mono_emit_abs_call (cfg, MONO_PATCH_INFO_MONITOR_ENTER,
5792 NULL, helper_sig_monitor_enter_exit_trampoline, NULL);
5793 mono_call_inst_add_outarg_reg (cfg, call, args [0]->dreg,
5794 MONO_ARCH_MONITOR_OBJECT_REG, FALSE);
5797 return (MonoInst*)call;
5798 #if defined(MONO_ARCH_MONITOR_LOCK_TAKEN_REG)
5799 } else if (strcmp (cmethod->name, "Enter") == 0 && fsig->param_count == 2) {
5802 if (COMPILE_LLVM (cfg)) {
5804 * Pass the argument normally, the LLVM backend will handle the
5805 * calling convention problems.
5807 call = (MonoCallInst*)mono_emit_abs_call (cfg, MONO_PATCH_INFO_MONITOR_ENTER_V4, NULL, helper_sig_monitor_enter_v4_trampoline_llvm, args);
5809 call = (MonoCallInst*)mono_emit_abs_call (cfg, MONO_PATCH_INFO_MONITOR_ENTER_V4,
5810 NULL, helper_sig_monitor_enter_exit_trampoline, NULL);
5811 mono_call_inst_add_outarg_reg (cfg, call, args [0]->dreg, MONO_ARCH_MONITOR_OBJECT_REG, FALSE);
5812 mono_call_inst_add_outarg_reg (cfg, call, args [1]->dreg, MONO_ARCH_MONITOR_LOCK_TAKEN_REG, FALSE);
5815 return (MonoInst*)call;
5817 } else if (strcmp (cmethod->name, "Exit") == 0 && fsig->param_count == 1) {
5820 if (COMPILE_LLVM (cfg)) {
5821 call = (MonoCallInst*)mono_emit_abs_call (cfg, MONO_PATCH_INFO_MONITOR_EXIT, NULL, helper_sig_monitor_enter_exit_trampoline_llvm, args);
5823 call = (MonoCallInst*)mono_emit_abs_call (cfg, MONO_PATCH_INFO_MONITOR_EXIT,
5824 NULL, helper_sig_monitor_enter_exit_trampoline, NULL);
5825 mono_call_inst_add_outarg_reg (cfg, call, args [0]->dreg,
5826 MONO_ARCH_MONITOR_OBJECT_REG, FALSE);
5829 return (MonoInst*)call;
5832 } else if (cmethod->klass->image == mono_defaults.corlib &&
5833 (strcmp (cmethod->klass->name_space, "System.Threading") == 0) &&
5834 (strcmp (cmethod->klass->name, "Interlocked") == 0)) {
5837 #if SIZEOF_REGISTER == 8
5838 if (strcmp (cmethod->name, "Read") == 0 && fsig->param_count == 1 && (fsig->params [0]->type == MONO_TYPE_I8)) {
5839 if (mono_arch_opcode_supported (OP_ATOMIC_LOAD_I8)) {
5840 MONO_INST_NEW (cfg, ins, OP_ATOMIC_LOAD_I8);
5841 ins->dreg = mono_alloc_preg (cfg);
5842 ins->sreg1 = args [0]->dreg;
5843 ins->type = STACK_I8;
5844 ins->backend.memory_barrier_kind = MONO_MEMORY_BARRIER_SEQ;
5845 MONO_ADD_INS (cfg->cbb, ins);
5849 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_SEQ);
5851 /* 64 bit reads are already atomic */
5852 MONO_INST_NEW (cfg, load_ins, OP_LOADI8_MEMBASE);
5853 load_ins->dreg = mono_alloc_preg (cfg);
5854 load_ins->inst_basereg = args [0]->dreg;
5855 load_ins->inst_offset = 0;
5856 load_ins->type = STACK_I8;
5857 MONO_ADD_INS (cfg->cbb, load_ins);
5859 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_SEQ);
5866 if (strcmp (cmethod->name, "Increment") == 0 && fsig->param_count == 1) {
5867 MonoInst *ins_iconst;
5870 if (fsig->params [0]->type == MONO_TYPE_I4) {
5871 opcode = OP_ATOMIC_ADD_I4;
5872 cfg->has_atomic_add_i4 = TRUE;
5874 #if SIZEOF_REGISTER == 8
5875 else if (fsig->params [0]->type == MONO_TYPE_I8)
5876 opcode = OP_ATOMIC_ADD_I8;
5879 if (!mono_arch_opcode_supported (opcode))
5881 MONO_INST_NEW (cfg, ins_iconst, OP_ICONST);
5882 ins_iconst->inst_c0 = 1;
5883 ins_iconst->dreg = mono_alloc_ireg (cfg);
5884 MONO_ADD_INS (cfg->cbb, ins_iconst);
5886 MONO_INST_NEW (cfg, ins, opcode);
5887 ins->dreg = mono_alloc_ireg (cfg);
5888 ins->inst_basereg = args [0]->dreg;
5889 ins->inst_offset = 0;
5890 ins->sreg2 = ins_iconst->dreg;
5891 ins->type = (opcode == OP_ATOMIC_ADD_I4) ? STACK_I4 : STACK_I8;
5892 MONO_ADD_INS (cfg->cbb, ins);
5894 } else if (strcmp (cmethod->name, "Decrement") == 0 && fsig->param_count == 1) {
5895 MonoInst *ins_iconst;
5898 if (fsig->params [0]->type == MONO_TYPE_I4) {
5899 opcode = OP_ATOMIC_ADD_I4;
5900 cfg->has_atomic_add_i4 = TRUE;
5902 #if SIZEOF_REGISTER == 8
5903 else if (fsig->params [0]->type == MONO_TYPE_I8)
5904 opcode = OP_ATOMIC_ADD_I8;
5907 if (!mono_arch_opcode_supported (opcode))
5909 MONO_INST_NEW (cfg, ins_iconst, OP_ICONST);
5910 ins_iconst->inst_c0 = -1;
5911 ins_iconst->dreg = mono_alloc_ireg (cfg);
5912 MONO_ADD_INS (cfg->cbb, ins_iconst);
5914 MONO_INST_NEW (cfg, ins, opcode);
5915 ins->dreg = mono_alloc_ireg (cfg);
5916 ins->inst_basereg = args [0]->dreg;
5917 ins->inst_offset = 0;
5918 ins->sreg2 = ins_iconst->dreg;
5919 ins->type = (opcode == OP_ATOMIC_ADD_I4) ? STACK_I4 : STACK_I8;
5920 MONO_ADD_INS (cfg->cbb, ins);
5922 } else if (strcmp (cmethod->name, "Add") == 0 && fsig->param_count == 2) {
5925 if (fsig->params [0]->type == MONO_TYPE_I4) {
5926 opcode = OP_ATOMIC_ADD_I4;
5927 cfg->has_atomic_add_i4 = TRUE;
5929 #if SIZEOF_REGISTER == 8
5930 else if (fsig->params [0]->type == MONO_TYPE_I8)
5931 opcode = OP_ATOMIC_ADD_I8;
5934 if (!mono_arch_opcode_supported (opcode))
5936 MONO_INST_NEW (cfg, ins, opcode);
5937 ins->dreg = mono_alloc_ireg (cfg);
5938 ins->inst_basereg = args [0]->dreg;
5939 ins->inst_offset = 0;
5940 ins->sreg2 = args [1]->dreg;
5941 ins->type = (opcode == OP_ATOMIC_ADD_I4) ? STACK_I4 : STACK_I8;
5942 MONO_ADD_INS (cfg->cbb, ins);
5945 else if (strcmp (cmethod->name, "Exchange") == 0 && fsig->param_count == 2) {
5946 MonoInst *f2i = NULL, *i2f;
5947 guint32 opcode, f2i_opcode, i2f_opcode;
5948 gboolean is_ref = mini_type_is_reference (cfg, fsig->params [0]);
5949 gboolean is_float = fsig->params [0]->type == MONO_TYPE_R4 || fsig->params [0]->type == MONO_TYPE_R8;
5951 if (fsig->params [0]->type == MONO_TYPE_I4 ||
5952 fsig->params [0]->type == MONO_TYPE_R4) {
5953 opcode = OP_ATOMIC_EXCHANGE_I4;
5954 f2i_opcode = OP_MOVE_F_TO_I4;
5955 i2f_opcode = OP_MOVE_I4_TO_F;
5956 cfg->has_atomic_exchange_i4 = TRUE;
5958 #if SIZEOF_REGISTER == 8
5960 fsig->params [0]->type == MONO_TYPE_I8 ||
5961 fsig->params [0]->type == MONO_TYPE_R8 ||
5962 fsig->params [0]->type == MONO_TYPE_I) {
5963 opcode = OP_ATOMIC_EXCHANGE_I8;
5964 f2i_opcode = OP_MOVE_F_TO_I8;
5965 i2f_opcode = OP_MOVE_I8_TO_F;
5968 else if (is_ref || fsig->params [0]->type == MONO_TYPE_I) {
5969 opcode = OP_ATOMIC_EXCHANGE_I4;
5970 cfg->has_atomic_exchange_i4 = TRUE;
5976 if (!mono_arch_opcode_supported (opcode))
5980 /* TODO: Decompose these opcodes instead of bailing here. */
5981 if (COMPILE_SOFT_FLOAT (cfg))
5984 MONO_INST_NEW (cfg, f2i, f2i_opcode);
5985 f2i->dreg = mono_alloc_ireg (cfg);
5986 f2i->sreg1 = args [1]->dreg;
5987 if (f2i_opcode == OP_MOVE_F_TO_I4)
5988 f2i->backend.spill_var = mini_get_int_to_float_spill_area (cfg);
5989 MONO_ADD_INS (cfg->cbb, f2i);
5992 MONO_INST_NEW (cfg, ins, opcode);
5993 ins->dreg = is_ref ? mono_alloc_ireg_ref (cfg) : mono_alloc_ireg (cfg);
5994 ins->inst_basereg = args [0]->dreg;
5995 ins->inst_offset = 0;
5996 ins->sreg2 = is_float ? f2i->dreg : args [1]->dreg;
5997 MONO_ADD_INS (cfg->cbb, ins);
5999 switch (fsig->params [0]->type) {
6001 ins->type = STACK_I4;
6004 ins->type = STACK_I8;
6007 #if SIZEOF_REGISTER == 8
6008 ins->type = STACK_I8;
6010 ins->type = STACK_I4;
6015 ins->type = STACK_R8;
6018 g_assert (mini_type_is_reference (cfg, fsig->params [0]));
6019 ins->type = STACK_OBJ;
6024 MONO_INST_NEW (cfg, i2f, i2f_opcode);
6025 i2f->dreg = mono_alloc_freg (cfg);
6026 i2f->sreg1 = ins->dreg;
6027 i2f->type = STACK_R8;
6028 if (i2f_opcode == OP_MOVE_I4_TO_F)
6029 i2f->backend.spill_var = mini_get_int_to_float_spill_area (cfg);
6030 MONO_ADD_INS (cfg->cbb, i2f);
6035 if (cfg->gen_write_barriers && is_ref)
6036 emit_write_barrier (cfg, args [0], args [1]);
6038 else if ((strcmp (cmethod->name, "CompareExchange") == 0) && fsig->param_count == 3) {
6039 MonoInst *f2i_new = NULL, *f2i_cmp = NULL, *i2f;
6040 guint32 opcode, f2i_opcode, i2f_opcode;
6041 gboolean is_ref = mini_type_is_reference (cfg, fsig->params [1]);
6042 gboolean is_float = fsig->params [1]->type == MONO_TYPE_R4 || fsig->params [1]->type == MONO_TYPE_R8;
6044 if (fsig->params [1]->type == MONO_TYPE_I4 ||
6045 fsig->params [1]->type == MONO_TYPE_R4) {
6046 opcode = OP_ATOMIC_CAS_I4;
6047 f2i_opcode = OP_MOVE_F_TO_I4;
6048 i2f_opcode = OP_MOVE_I4_TO_F;
6049 cfg->has_atomic_cas_i4 = TRUE;
6051 #if SIZEOF_REGISTER == 8
6053 fsig->params [1]->type == MONO_TYPE_I8 ||
6054 fsig->params [1]->type == MONO_TYPE_R8 ||
6055 fsig->params [1]->type == MONO_TYPE_I) {
6056 opcode = OP_ATOMIC_CAS_I8;
6057 f2i_opcode = OP_MOVE_F_TO_I8;
6058 i2f_opcode = OP_MOVE_I8_TO_F;
6061 else if (is_ref || fsig->params [1]->type == MONO_TYPE_I) {
6062 opcode = OP_ATOMIC_CAS_I4;
6063 cfg->has_atomic_cas_i4 = TRUE;
6069 if (!mono_arch_opcode_supported (opcode))
6073 /* TODO: Decompose these opcodes instead of bailing here. */
6074 if (COMPILE_SOFT_FLOAT (cfg))
6077 MONO_INST_NEW (cfg, f2i_new, f2i_opcode);
6078 f2i_new->dreg = mono_alloc_ireg (cfg);
6079 f2i_new->sreg1 = args [1]->dreg;
6080 if (f2i_opcode == OP_MOVE_F_TO_I4)
6081 f2i_new->backend.spill_var = mini_get_int_to_float_spill_area (cfg);
6082 MONO_ADD_INS (cfg->cbb, f2i_new);
6084 MONO_INST_NEW (cfg, f2i_cmp, f2i_opcode);
6085 f2i_cmp->dreg = mono_alloc_ireg (cfg);
6086 f2i_cmp->sreg1 = args [2]->dreg;
6087 if (f2i_opcode == OP_MOVE_F_TO_I4)
6088 f2i_cmp->backend.spill_var = mini_get_int_to_float_spill_area (cfg);
6089 MONO_ADD_INS (cfg->cbb, f2i_cmp);
6092 MONO_INST_NEW (cfg, ins, opcode);
6093 ins->dreg = is_ref ? alloc_ireg_ref (cfg) : alloc_ireg (cfg);
6094 ins->sreg1 = args [0]->dreg;
6095 ins->sreg2 = is_float ? f2i_new->dreg : args [1]->dreg;
6096 ins->sreg3 = is_float ? f2i_cmp->dreg : args [2]->dreg;
6097 MONO_ADD_INS (cfg->cbb, ins);
6099 switch (fsig->params [0]->type) {
6101 ins->type = STACK_I4;
6104 ins->type = STACK_I8;
6107 #if SIZEOF_REGISTER == 8
6108 ins->type = STACK_I8;
6110 ins->type = STACK_I4;
6115 ins->type = STACK_R8;
6118 g_assert (mini_type_is_reference (cfg, fsig->params [0]));
6119 ins->type = STACK_OBJ;
6124 MONO_INST_NEW (cfg, i2f, i2f_opcode);
6125 i2f->dreg = mono_alloc_freg (cfg);
6126 i2f->sreg1 = ins->dreg;
6127 i2f->type = STACK_R8;
6128 if (i2f_opcode == OP_MOVE_I4_TO_F)
6129 i2f->backend.spill_var = mini_get_int_to_float_spill_area (cfg);
6130 MONO_ADD_INS (cfg->cbb, i2f);
6135 if (cfg->gen_write_barriers && is_ref)
6136 emit_write_barrier (cfg, args [0], args [1]);
6138 else if ((strcmp (cmethod->name, "CompareExchange") == 0) && fsig->param_count == 4 &&
6139 fsig->params [1]->type == MONO_TYPE_I4) {
6140 MonoInst *cmp, *ceq;
6142 if (!mono_arch_opcode_supported (OP_ATOMIC_CAS_I4))
6145 /* int32 r = CAS (location, value, comparand); */
6146 MONO_INST_NEW (cfg, ins, OP_ATOMIC_CAS_I4);
6147 ins->dreg = alloc_ireg (cfg);
6148 ins->sreg1 = args [0]->dreg;
6149 ins->sreg2 = args [1]->dreg;
6150 ins->sreg3 = args [2]->dreg;
6151 ins->type = STACK_I4;
6152 MONO_ADD_INS (cfg->cbb, ins);
6154 /* bool result = r == comparand; */
6155 MONO_INST_NEW (cfg, cmp, OP_ICOMPARE);
6156 cmp->sreg1 = ins->dreg;
6157 cmp->sreg2 = args [2]->dreg;
6158 cmp->type = STACK_I4;
6159 MONO_ADD_INS (cfg->cbb, cmp);
6161 MONO_INST_NEW (cfg, ceq, OP_ICEQ);
6162 ceq->dreg = alloc_ireg (cfg);
6163 ceq->type = STACK_I4;
6164 MONO_ADD_INS (cfg->cbb, ceq);
6166 /* *success = result; */
6167 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI1_MEMBASE_REG, args [3]->dreg, 0, ceq->dreg);
6169 cfg->has_atomic_cas_i4 = TRUE;
6171 else if (strcmp (cmethod->name, "MemoryBarrier") == 0 && fsig->param_count == 0)
6172 ins = emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_SEQ);
6176 } else if (cmethod->klass->image == mono_defaults.corlib &&
6177 (strcmp (cmethod->klass->name_space, "System.Threading") == 0) &&
6178 (strcmp (cmethod->klass->name, "Volatile") == 0)) {
6181 if (!strcmp (cmethod->name, "Read") && fsig->param_count == 1) {
6183 gboolean is_ref = mini_type_is_reference (cfg, fsig->params [0]);
6184 gboolean is_float = fsig->params [0]->type == MONO_TYPE_R4 || fsig->params [0]->type == MONO_TYPE_R8;
6186 if (fsig->params [0]->type == MONO_TYPE_I1)
6187 opcode = OP_ATOMIC_LOAD_I1;
6188 else if (fsig->params [0]->type == MONO_TYPE_U1 || fsig->params [0]->type == MONO_TYPE_BOOLEAN)
6189 opcode = OP_ATOMIC_LOAD_U1;
6190 else if (fsig->params [0]->type == MONO_TYPE_I2)
6191 opcode = OP_ATOMIC_LOAD_I2;
6192 else if (fsig->params [0]->type == MONO_TYPE_U2)
6193 opcode = OP_ATOMIC_LOAD_U2;
6194 else if (fsig->params [0]->type == MONO_TYPE_I4)
6195 opcode = OP_ATOMIC_LOAD_I4;
6196 else if (fsig->params [0]->type == MONO_TYPE_U4)
6197 opcode = OP_ATOMIC_LOAD_U4;
6198 else if (fsig->params [0]->type == MONO_TYPE_R4)
6199 opcode = OP_ATOMIC_LOAD_R4;
6200 else if (fsig->params [0]->type == MONO_TYPE_R8)
6201 opcode = OP_ATOMIC_LOAD_R8;
6202 #if SIZEOF_REGISTER == 8
6203 else if (fsig->params [0]->type == MONO_TYPE_I8 || fsig->params [0]->type == MONO_TYPE_I)
6204 opcode = OP_ATOMIC_LOAD_I8;
6205 else if (is_ref || fsig->params [0]->type == MONO_TYPE_U8 || fsig->params [0]->type == MONO_TYPE_U)
6206 opcode = OP_ATOMIC_LOAD_U8;
6208 else if (fsig->params [0]->type == MONO_TYPE_I)
6209 opcode = OP_ATOMIC_LOAD_I4;
6210 else if (is_ref || fsig->params [0]->type == MONO_TYPE_U)
6211 opcode = OP_ATOMIC_LOAD_U4;
6215 if (!mono_arch_opcode_supported (opcode))
6218 MONO_INST_NEW (cfg, ins, opcode);
6219 ins->dreg = is_ref ? mono_alloc_ireg_ref (cfg) : (is_float ? mono_alloc_freg (cfg) : mono_alloc_ireg (cfg));
6220 ins->sreg1 = args [0]->dreg;
6221 ins->backend.memory_barrier_kind = MONO_MEMORY_BARRIER_ACQ;
6222 MONO_ADD_INS (cfg->cbb, ins);
6224 switch (fsig->params [0]->type) {
6225 case MONO_TYPE_BOOLEAN:
6232 ins->type = STACK_I4;
6236 ins->type = STACK_I8;
6240 #if SIZEOF_REGISTER == 8
6241 ins->type = STACK_I8;
6243 ins->type = STACK_I4;
6248 ins->type = STACK_R8;
6251 g_assert (mini_type_is_reference (cfg, fsig->params [0]));
6252 ins->type = STACK_OBJ;
6258 if (!strcmp (cmethod->name, "Write") && fsig->param_count == 2) {
6260 gboolean is_ref = mini_type_is_reference (cfg, fsig->params [0]);
6262 if (fsig->params [0]->type == MONO_TYPE_I1)
6263 opcode = OP_ATOMIC_STORE_I1;
6264 else if (fsig->params [0]->type == MONO_TYPE_U1 || fsig->params [0]->type == MONO_TYPE_BOOLEAN)
6265 opcode = OP_ATOMIC_STORE_U1;
6266 else if (fsig->params [0]->type == MONO_TYPE_I2)
6267 opcode = OP_ATOMIC_STORE_I2;
6268 else if (fsig->params [0]->type == MONO_TYPE_U2)
6269 opcode = OP_ATOMIC_STORE_U2;
6270 else if (fsig->params [0]->type == MONO_TYPE_I4)
6271 opcode = OP_ATOMIC_STORE_I4;
6272 else if (fsig->params [0]->type == MONO_TYPE_U4)
6273 opcode = OP_ATOMIC_STORE_U4;
6274 else if (fsig->params [0]->type == MONO_TYPE_R4)
6275 opcode = OP_ATOMIC_STORE_R4;
6276 else if (fsig->params [0]->type == MONO_TYPE_R8)
6277 opcode = OP_ATOMIC_STORE_R8;
6278 #if SIZEOF_REGISTER == 8
6279 else if (fsig->params [0]->type == MONO_TYPE_I8 || fsig->params [0]->type == MONO_TYPE_I)
6280 opcode = OP_ATOMIC_STORE_I8;
6281 else if (is_ref || fsig->params [0]->type == MONO_TYPE_U8 || fsig->params [0]->type == MONO_TYPE_U)
6282 opcode = OP_ATOMIC_STORE_U8;
6284 else if (fsig->params [0]->type == MONO_TYPE_I)
6285 opcode = OP_ATOMIC_STORE_I4;
6286 else if (is_ref || fsig->params [0]->type == MONO_TYPE_U)
6287 opcode = OP_ATOMIC_STORE_U4;
6291 if (!mono_arch_opcode_supported (opcode))
6294 MONO_INST_NEW (cfg, ins, opcode);
6295 ins->dreg = args [0]->dreg;
6296 ins->sreg1 = args [1]->dreg;
6297 ins->backend.memory_barrier_kind = MONO_MEMORY_BARRIER_REL;
6298 MONO_ADD_INS (cfg->cbb, ins);
6300 if (cfg->gen_write_barriers && is_ref)
6301 emit_write_barrier (cfg, args [0], args [1]);
6307 } else if (cmethod->klass->image == mono_defaults.corlib &&
6308 (strcmp (cmethod->klass->name_space, "System.Diagnostics") == 0) &&
6309 (strcmp (cmethod->klass->name, "Debugger") == 0)) {
6310 if (!strcmp (cmethod->name, "Break") && fsig->param_count == 0) {
6311 if (should_insert_brekpoint (cfg->method)) {
6312 ins = mono_emit_jit_icall (cfg, mono_debugger_agent_user_break, NULL);
6314 MONO_INST_NEW (cfg, ins, OP_NOP);
6315 MONO_ADD_INS (cfg->cbb, ins);
6319 } else if (cmethod->klass->image == mono_defaults.corlib &&
6320 (strcmp (cmethod->klass->name_space, "System") == 0) &&
6321 (strcmp (cmethod->klass->name, "Environment") == 0)) {
6322 if (!strcmp (cmethod->name, "get_IsRunningOnWindows") && fsig->param_count == 0) {
6324 EMIT_NEW_ICONST (cfg, ins, 1);
6326 EMIT_NEW_ICONST (cfg, ins, 0);
6329 } else if (cmethod->klass == mono_defaults.math_class) {
6331 * There is general branchless code for Min/Max, but it does not work for
6333 * http://everything2.com/?node_id=1051618
6335 } else if ((!strcmp (cmethod->klass->image->assembly->aname.name, "MonoMac") ||
6336 !strcmp (cmethod->klass->image->assembly->aname.name, "monotouch")) &&
6337 !strcmp (cmethod->klass->name_space, "XamCore.ObjCRuntime") &&
6338 !strcmp (cmethod->klass->name, "Selector")) {
6339 #ifdef MONO_ARCH_HAVE_OBJC_GET_SELECTOR
6340 if (!strcmp (cmethod->klass->name, "GetHandle") && fsig->param_count == 1 &&
6341 (args [0]->opcode == OP_GOT_ENTRY || args [0]->opcode == OP_AOTCONST) &&
6344 MonoJumpInfoToken *ji;
6347 cfg->disable_llvm = TRUE;
6349 if (args [0]->opcode == OP_GOT_ENTRY) {
6350 pi = args [0]->inst_p1;
6351 g_assert (pi->opcode == OP_PATCH_INFO);
6352 g_assert (GPOINTER_TO_INT (pi->inst_p1) == MONO_PATCH_INFO_LDSTR);
6355 g_assert (GPOINTER_TO_INT (args [0]->inst_p1) == MONO_PATCH_INFO_LDSTR);
6356 ji = args [0]->inst_p0;
6359 NULLIFY_INS (args [0]);
6362 s = mono_ldstr (cfg->domain, ji->image, mono_metadata_token_index (ji->token));
6363 MONO_INST_NEW (cfg, ins, OP_OBJC_GET_SELECTOR);
6364 ins->dreg = mono_alloc_ireg (cfg);
6366 ins->inst_p0 = mono_string_to_utf8 (s);
6367 MONO_ADD_INS (cfg->cbb, ins);
6373 #ifdef MONO_ARCH_SIMD_INTRINSICS
6374 if (cfg->opt & MONO_OPT_SIMD) {
6375 ins = mono_emit_simd_intrinsics (cfg, cmethod, fsig, args);
6381 ins = mono_emit_native_types_intrinsics (cfg, cmethod, fsig, args);
6385 if (COMPILE_LLVM (cfg)) {
6386 ins = llvm_emit_inst_for_method (cfg, cmethod, fsig, args);
6391 return mono_arch_emit_inst_for_method (cfg, cmethod, fsig, args);
6395 * This entry point could be used later for arbitrary method
6398 inline static MonoInst*
6399 mini_redirect_call (MonoCompile *cfg, MonoMethod *method,
6400 MonoMethodSignature *signature, MonoInst **args, MonoInst *this)
6402 if (method->klass == mono_defaults.string_class) {
6403 /* managed string allocation support */
6404 if (strcmp (method->name, "InternalAllocateStr") == 0 && !(mono_profiler_events & MONO_PROFILE_ALLOCATIONS) && !(cfg->opt & MONO_OPT_SHARED)) {
6405 MonoInst *iargs [2];
6406 MonoVTable *vtable = mono_class_vtable (cfg->domain, method->klass);
6407 MonoMethod *managed_alloc = NULL;
6409 g_assert (vtable); /*Should not fail since it System.String*/
6410 #ifndef MONO_CROSS_COMPILE
6411 managed_alloc = mono_gc_get_managed_allocator (method->klass, FALSE);
6415 EMIT_NEW_VTABLECONST (cfg, iargs [0], vtable);
6416 iargs [1] = args [0];
6417 return mono_emit_method_call (cfg, managed_alloc, iargs, this);
6424 mono_save_args (MonoCompile *cfg, MonoMethodSignature *sig, MonoInst **sp)
6426 MonoInst *store, *temp;
6429 for (i = 0; i < sig->param_count + sig->hasthis; ++i) {
6430 MonoType *argtype = (sig->hasthis && (i == 0)) ? type_from_stack_type (*sp) : sig->params [i - sig->hasthis];
6433 * FIXME: We should use *args++ = sp [0], but that would mean the arg
6434 * would be different than the MonoInst's used to represent arguments, and
6435 * the ldelema implementation can't deal with that.
6436 * Solution: When ldelema is used on an inline argument, create a var for
6437 * it, emit ldelema on that var, and emit the saving code below in
6438 * inline_method () if needed.
6440 temp = mono_compile_create_var (cfg, argtype, OP_LOCAL);
6441 cfg->args [i] = temp;
6442 /* This uses cfg->args [i] which is set by the preceeding line */
6443 EMIT_NEW_ARGSTORE (cfg, store, i, *sp);
6444 store->cil_code = sp [0]->cil_code;
6449 #define MONO_INLINE_CALLED_LIMITED_METHODS 1
6450 #define MONO_INLINE_CALLER_LIMITED_METHODS 1
6452 #if (MONO_INLINE_CALLED_LIMITED_METHODS)
6454 check_inline_called_method_name_limit (MonoMethod *called_method)
6457 static const char *limit = NULL;
6459 if (limit == NULL) {
6460 const char *limit_string = g_getenv ("MONO_INLINE_CALLED_METHOD_NAME_LIMIT");
6462 if (limit_string != NULL)
6463 limit = limit_string;
6468 if (limit [0] != '\0') {
6469 char *called_method_name = mono_method_full_name (called_method, TRUE);
6471 strncmp_result = strncmp (called_method_name, limit, strlen (limit));
6472 g_free (called_method_name);
6474 //return (strncmp_result <= 0);
6475 return (strncmp_result == 0);
6482 #if (MONO_INLINE_CALLER_LIMITED_METHODS)
6484 check_inline_caller_method_name_limit (MonoMethod *caller_method)
6487 static const char *limit = NULL;
6489 if (limit == NULL) {
6490 const char *limit_string = g_getenv ("MONO_INLINE_CALLER_METHOD_NAME_LIMIT");
6491 if (limit_string != NULL) {
6492 limit = limit_string;
6498 if (limit [0] != '\0') {
6499 char *caller_method_name = mono_method_full_name (caller_method, TRUE);
6501 strncmp_result = strncmp (caller_method_name, limit, strlen (limit));
6502 g_free (caller_method_name);
6504 //return (strncmp_result <= 0);
6505 return (strncmp_result == 0);
6513 emit_init_rvar (MonoCompile *cfg, int dreg, MonoType *rtype)
6515 static double r8_0 = 0.0;
6516 static float r4_0 = 0.0;
6520 rtype = mini_replace_type (rtype);
6524 MONO_EMIT_NEW_PCONST (cfg, dreg, NULL);
6525 } else if (t >= MONO_TYPE_BOOLEAN && t <= MONO_TYPE_U4) {
6526 MONO_EMIT_NEW_ICONST (cfg, dreg, 0);
6527 } else if (t == MONO_TYPE_I8 || t == MONO_TYPE_U8) {
6528 MONO_EMIT_NEW_I8CONST (cfg, dreg, 0);
6529 } else if (cfg->r4fp && t == MONO_TYPE_R4) {
6530 MONO_INST_NEW (cfg, ins, OP_R4CONST);
6531 ins->type = STACK_R4;
6532 ins->inst_p0 = (void*)&r4_0;
6534 MONO_ADD_INS (cfg->cbb, ins);
6535 } else if (t == MONO_TYPE_R4 || t == MONO_TYPE_R8) {
6536 MONO_INST_NEW (cfg, ins, OP_R8CONST);
6537 ins->type = STACK_R8;
6538 ins->inst_p0 = (void*)&r8_0;
6540 MONO_ADD_INS (cfg->cbb, ins);
6541 } else if ((t == MONO_TYPE_VALUETYPE) || (t == MONO_TYPE_TYPEDBYREF) ||
6542 ((t == MONO_TYPE_GENERICINST) && mono_type_generic_inst_is_valuetype (rtype))) {
6543 MONO_EMIT_NEW_VZERO (cfg, dreg, mono_class_from_mono_type (rtype));
6544 } else if (((t == MONO_TYPE_VAR) || (t == MONO_TYPE_MVAR)) && mini_type_var_is_vt (cfg, rtype)) {
6545 MONO_EMIT_NEW_VZERO (cfg, dreg, mono_class_from_mono_type (rtype));
6547 MONO_EMIT_NEW_PCONST (cfg, dreg, NULL);
6552 emit_dummy_init_rvar (MonoCompile *cfg, int dreg, MonoType *rtype)
6556 rtype = mini_replace_type (rtype);
6560 MONO_EMIT_NEW_DUMMY_INIT (cfg, dreg, OP_DUMMY_PCONST);
6561 } else if (t >= MONO_TYPE_BOOLEAN && t <= MONO_TYPE_U4) {
6562 MONO_EMIT_NEW_DUMMY_INIT (cfg, dreg, OP_DUMMY_ICONST);
6563 } else if (t == MONO_TYPE_I8 || t == MONO_TYPE_U8) {
6564 MONO_EMIT_NEW_DUMMY_INIT (cfg, dreg, OP_DUMMY_I8CONST);
6565 } else if (cfg->r4fp && t == MONO_TYPE_R4) {
6566 MONO_EMIT_NEW_DUMMY_INIT (cfg, dreg, OP_DUMMY_R4CONST);
6567 } else if (t == MONO_TYPE_R4 || t == MONO_TYPE_R8) {
6568 MONO_EMIT_NEW_DUMMY_INIT (cfg, dreg, OP_DUMMY_R8CONST);
6569 } else if ((t == MONO_TYPE_VALUETYPE) || (t == MONO_TYPE_TYPEDBYREF) ||
6570 ((t == MONO_TYPE_GENERICINST) && mono_type_generic_inst_is_valuetype (rtype))) {
6571 MONO_EMIT_NEW_DUMMY_INIT (cfg, dreg, OP_DUMMY_VZERO);
6572 } else if (((t == MONO_TYPE_VAR) || (t == MONO_TYPE_MVAR)) && mini_type_var_is_vt (cfg, rtype)) {
6573 MONO_EMIT_NEW_DUMMY_INIT (cfg, dreg, OP_DUMMY_VZERO);
6575 emit_init_rvar (cfg, dreg, rtype);
6579 /* If INIT is FALSE, emit dummy initialization statements to keep the IR valid */
6581 emit_init_local (MonoCompile *cfg, int local, MonoType *type, gboolean init)
6583 MonoInst *var = cfg->locals [local];
6584 if (COMPILE_SOFT_FLOAT (cfg)) {
6586 int reg = alloc_dreg (cfg, var->type);
6587 emit_init_rvar (cfg, reg, type);
6588 EMIT_NEW_LOCSTORE (cfg, store, local, cfg->cbb->last_ins);
6591 emit_init_rvar (cfg, var->dreg, type);
6593 emit_dummy_init_rvar (cfg, var->dreg, type);
6600 * Return the cost of inlining CMETHOD.
6603 inline_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **sp,
6604 guchar *ip, guint real_offset, gboolean inline_always, MonoBasicBlock **out_cbb)
6606 MonoInst *ins, *rvar = NULL;
6607 MonoMethodHeader *cheader;
6608 MonoBasicBlock *ebblock, *sbblock;
6610 MonoMethod *prev_inlined_method;
6611 MonoInst **prev_locals, **prev_args;
6612 MonoType **prev_arg_types;
6613 guint prev_real_offset;
6614 GHashTable *prev_cbb_hash;
6615 MonoBasicBlock **prev_cil_offset_to_bb;
6616 MonoBasicBlock *prev_cbb;
6617 unsigned char* prev_cil_start;
6618 guint32 prev_cil_offset_to_bb_len;
6619 MonoMethod *prev_current_method;
6620 MonoGenericContext *prev_generic_context;
6621 gboolean ret_var_set, prev_ret_var_set, prev_disable_inline, virtual = FALSE;
6623 g_assert (cfg->exception_type == MONO_EXCEPTION_NONE);
6625 #if (MONO_INLINE_CALLED_LIMITED_METHODS)
6626 if ((! inline_always) && ! check_inline_called_method_name_limit (cmethod))
6629 #if (MONO_INLINE_CALLER_LIMITED_METHODS)
6630 if ((! inline_always) && ! check_inline_caller_method_name_limit (cfg->method))
6634 if (cfg->verbose_level > 2)
6635 printf ("INLINE START %p %s -> %s\n", cmethod, mono_method_full_name (cfg->method, TRUE), mono_method_full_name (cmethod, TRUE));
6637 if (!cmethod->inline_info) {
6638 cfg->stat_inlineable_methods++;
6639 cmethod->inline_info = 1;
6642 /* allocate local variables */
6643 cheader = mono_method_get_header (cmethod);
6645 if (cheader == NULL || mono_loader_get_last_error ()) {
6646 MonoLoaderError *error = mono_loader_get_last_error ();
6649 mono_metadata_free_mh (cheader);
6650 if (inline_always && error)
6651 mono_cfg_set_exception (cfg, error->exception_type);
6653 mono_loader_clear_error ();
6657 /*Must verify before creating locals as it can cause the JIT to assert.*/
6658 if (mono_compile_is_broken (cfg, cmethod, FALSE)) {
6659 mono_metadata_free_mh (cheader);
6663 /* allocate space to store the return value */
6664 if (!MONO_TYPE_IS_VOID (fsig->ret)) {
6665 rvar = mono_compile_create_var (cfg, fsig->ret, OP_LOCAL);
6668 prev_locals = cfg->locals;
6669 cfg->locals = mono_mempool_alloc0 (cfg->mempool, cheader->num_locals * sizeof (MonoInst*));
6670 for (i = 0; i < cheader->num_locals; ++i)
6671 cfg->locals [i] = mono_compile_create_var (cfg, cheader->locals [i], OP_LOCAL);
6673 /* allocate start and end blocks */
6674 /* This is needed so if the inline is aborted, we can clean up */
6675 NEW_BBLOCK (cfg, sbblock);
6676 sbblock->real_offset = real_offset;
6678 NEW_BBLOCK (cfg, ebblock);
6679 ebblock->block_num = cfg->num_bblocks++;
6680 ebblock->real_offset = real_offset;
6682 prev_args = cfg->args;
6683 prev_arg_types = cfg->arg_types;
6684 prev_inlined_method = cfg->inlined_method;
6685 cfg->inlined_method = cmethod;
6686 cfg->ret_var_set = FALSE;
6687 cfg->inline_depth ++;
6688 prev_real_offset = cfg->real_offset;
6689 prev_cbb_hash = cfg->cbb_hash;
6690 prev_cil_offset_to_bb = cfg->cil_offset_to_bb;
6691 prev_cil_offset_to_bb_len = cfg->cil_offset_to_bb_len;
6692 prev_cil_start = cfg->cil_start;
6693 prev_cbb = cfg->cbb;
6694 prev_current_method = cfg->current_method;
6695 prev_generic_context = cfg->generic_context;
6696 prev_ret_var_set = cfg->ret_var_set;
6697 prev_disable_inline = cfg->disable_inline;
6699 if (*ip == CEE_CALLVIRT && !(cmethod->flags & METHOD_ATTRIBUTE_STATIC))
6702 costs = mono_method_to_ir (cfg, cmethod, sbblock, ebblock, rvar, sp, real_offset, virtual);
6704 ret_var_set = cfg->ret_var_set;
6706 cfg->inlined_method = prev_inlined_method;
6707 cfg->real_offset = prev_real_offset;
6708 cfg->cbb_hash = prev_cbb_hash;
6709 cfg->cil_offset_to_bb = prev_cil_offset_to_bb;
6710 cfg->cil_offset_to_bb_len = prev_cil_offset_to_bb_len;
6711 cfg->cil_start = prev_cil_start;
6712 cfg->locals = prev_locals;
6713 cfg->args = prev_args;
6714 cfg->arg_types = prev_arg_types;
6715 cfg->current_method = prev_current_method;
6716 cfg->generic_context = prev_generic_context;
6717 cfg->ret_var_set = prev_ret_var_set;
6718 cfg->disable_inline = prev_disable_inline;
6719 cfg->inline_depth --;
6721 if ((costs >= 0 && costs < 60) || inline_always) {
6722 if (cfg->verbose_level > 2)
6723 printf ("INLINE END %s -> %s\n", mono_method_full_name (cfg->method, TRUE), mono_method_full_name (cmethod, TRUE));
6725 cfg->stat_inlined_methods++;
6727 /* always add some code to avoid block split failures */
6728 MONO_INST_NEW (cfg, ins, OP_NOP);
6729 MONO_ADD_INS (prev_cbb, ins);
6731 prev_cbb->next_bb = sbblock;
6732 link_bblock (cfg, prev_cbb, sbblock);
6735 * Get rid of the begin and end bblocks if possible to aid local
6738 mono_merge_basic_blocks (cfg, prev_cbb, sbblock);
6740 if ((prev_cbb->out_count == 1) && (prev_cbb->out_bb [0]->in_count == 1) && (prev_cbb->out_bb [0] != ebblock))
6741 mono_merge_basic_blocks (cfg, prev_cbb, prev_cbb->out_bb [0]);
6743 if ((ebblock->in_count == 1) && ebblock->in_bb [0]->out_count == 1) {
6744 MonoBasicBlock *prev = ebblock->in_bb [0];
6745 mono_merge_basic_blocks (cfg, prev, ebblock);
6747 if ((prev_cbb->out_count == 1) && (prev_cbb->out_bb [0]->in_count == 1) && (prev_cbb->out_bb [0] == prev)) {
6748 mono_merge_basic_blocks (cfg, prev_cbb, prev);
6749 cfg->cbb = prev_cbb;
6753 * Its possible that the rvar is set in some prev bblock, but not in others.
6759 for (i = 0; i < ebblock->in_count; ++i) {
6760 bb = ebblock->in_bb [i];
6762 if (bb->last_ins && bb->last_ins->opcode == OP_NOT_REACHED) {
6765 emit_init_rvar (cfg, rvar->dreg, fsig->ret);
6773 *out_cbb = cfg->cbb;
6777 * If the inlined method contains only a throw, then the ret var is not
6778 * set, so set it to a dummy value.
6781 emit_init_rvar (cfg, rvar->dreg, fsig->ret);
6783 EMIT_NEW_TEMPLOAD (cfg, ins, rvar->inst_c0);
6786 cfg->headers_to_free = g_slist_prepend_mempool (cfg->mempool, cfg->headers_to_free, cheader);
6789 if (cfg->verbose_level > 2)
6790 printf ("INLINE ABORTED %s (cost %d)\n", mono_method_full_name (cmethod, TRUE), costs);
6791 cfg->exception_type = MONO_EXCEPTION_NONE;
6792 mono_loader_clear_error ();
6794 /* This gets rid of the newly added bblocks */
6795 cfg->cbb = prev_cbb;
6797 cfg->headers_to_free = g_slist_prepend_mempool (cfg->mempool, cfg->headers_to_free, cheader);
6802 * Some of these comments may well be out-of-date.
6803 * Design decisions: we do a single pass over the IL code (and we do bblock
6804 * splitting/merging in the few cases when it's required: a back jump to an IL
6805 * address that was not already seen as bblock starting point).
6806 * Code is validated as we go (full verification is still better left to metadata/verify.c).
6807 * Complex operations are decomposed in simpler ones right away. We need to let the
6808 * arch-specific code peek and poke inside this process somehow (except when the
6809 * optimizations can take advantage of the full semantic info of coarse opcodes).
6810 * All the opcodes of the form opcode.s are 'normalized' to opcode.
6811 * MonoInst->opcode initially is the IL opcode or some simplification of that
6812 * (OP_LOAD, OP_STORE). The arch-specific code may rearrange it to an arch-specific
6813 * opcode with value bigger than OP_LAST.
6814 * At this point the IR can be handed over to an interpreter, a dumb code generator
6815 * or to the optimizing code generator that will translate it to SSA form.
6817 * Profiling directed optimizations.
6818 * We may compile by default with few or no optimizations and instrument the code
6819 * or the user may indicate what methods to optimize the most either in a config file
6820 * or through repeated runs where the compiler applies offline the optimizations to
6821 * each method and then decides if it was worth it.
6824 #define CHECK_TYPE(ins) if (!(ins)->type) UNVERIFIED
6825 #define CHECK_STACK(num) if ((sp - stack_start) < (num)) UNVERIFIED
6826 #define CHECK_STACK_OVF(num) if (((sp - stack_start) + (num)) > header->max_stack) UNVERIFIED
6827 #define CHECK_ARG(num) if ((unsigned)(num) >= (unsigned)num_args) UNVERIFIED
6828 #define CHECK_LOCAL(num) if ((unsigned)(num) >= (unsigned)header->num_locals) UNVERIFIED
6829 #define CHECK_OPSIZE(size) if (ip + size > end) UNVERIFIED
6830 #define CHECK_UNVERIFIABLE(cfg) if (cfg->unverifiable) UNVERIFIED
6831 #define CHECK_TYPELOAD(klass) if (!(klass) || (klass)->exception_type) TYPE_LOAD_ERROR ((klass))
6833 /* offset from br.s -> br like opcodes */
6834 #define BIG_BRANCH_OFFSET 13
6837 ip_in_bb (MonoCompile *cfg, MonoBasicBlock *bb, const guint8* ip)
6839 MonoBasicBlock *b = cfg->cil_offset_to_bb [ip - cfg->cil_start];
6841 return b == NULL || b == bb;
6845 get_basic_blocks (MonoCompile *cfg, MonoMethodHeader* header, guint real_offset, unsigned char *start, unsigned char *end, unsigned char **pos)
6847 unsigned char *ip = start;
6848 unsigned char *target;
6851 MonoBasicBlock *bblock;
6852 const MonoOpcode *opcode;
6855 cli_addr = ip - start;
6856 i = mono_opcode_value ((const guint8 **)&ip, end);
6859 opcode = &mono_opcodes [i];
6860 switch (opcode->argument) {
6861 case MonoInlineNone:
6864 case MonoInlineString:
6865 case MonoInlineType:
6866 case MonoInlineField:
6867 case MonoInlineMethod:
6870 case MonoShortInlineR:
6877 case MonoShortInlineVar:
6878 case MonoShortInlineI:
6881 case MonoShortInlineBrTarget:
6882 target = start + cli_addr + 2 + (signed char)ip [1];
6883 GET_BBLOCK (cfg, bblock, target);
6886 GET_BBLOCK (cfg, bblock, ip);
6888 case MonoInlineBrTarget:
6889 target = start + cli_addr + 5 + (gint32)read32 (ip + 1);
6890 GET_BBLOCK (cfg, bblock, target);
6893 GET_BBLOCK (cfg, bblock, ip);
6895 case MonoInlineSwitch: {
6896 guint32 n = read32 (ip + 1);
6899 cli_addr += 5 + 4 * n;
6900 target = start + cli_addr;
6901 GET_BBLOCK (cfg, bblock, target);
6903 for (j = 0; j < n; ++j) {
6904 target = start + cli_addr + (gint32)read32 (ip);
6905 GET_BBLOCK (cfg, bblock, target);
6915 g_assert_not_reached ();
6918 if (i == CEE_THROW) {
6919 unsigned char *bb_start = ip - 1;
6921 /* Find the start of the bblock containing the throw */
6923 while ((bb_start >= start) && !bblock) {
6924 bblock = cfg->cil_offset_to_bb [(bb_start) - start];
6928 bblock->out_of_line = 1;
6938 static inline MonoMethod *
6939 mini_get_method_allow_open (MonoMethod *m, guint32 token, MonoClass *klass, MonoGenericContext *context)
6943 if (m->wrapper_type != MONO_WRAPPER_NONE) {
6944 method = mono_method_get_wrapper_data (m, token);
6947 method = mono_class_inflate_generic_method_checked (method, context, &error);
6948 g_assert (mono_error_ok (&error)); /* FIXME don't swallow the error */
6951 method = mono_get_method_full (m->klass->image, token, klass, context);
6957 static inline MonoMethod *
6958 mini_get_method (MonoCompile *cfg, MonoMethod *m, guint32 token, MonoClass *klass, MonoGenericContext *context)
6960 MonoMethod *method = mini_get_method_allow_open (m, token, klass, context);
6962 if (method && cfg && !cfg->generic_sharing_context && mono_class_is_open_constructed_type (&method->klass->byval_arg))
6968 static inline MonoClass*
6969 mini_get_class (MonoMethod *method, guint32 token, MonoGenericContext *context)
6974 if (method->wrapper_type != MONO_WRAPPER_NONE) {
6975 klass = mono_method_get_wrapper_data (method, token);
6977 klass = mono_class_inflate_generic_class (klass, context);
6979 klass = mono_class_get_and_inflate_typespec_checked (method->klass->image, token, context, &error);
6980 mono_error_cleanup (&error); /* FIXME don't swallow the error */
6983 mono_class_init (klass);
6987 static inline MonoMethodSignature*
6988 mini_get_signature (MonoMethod *method, guint32 token, MonoGenericContext *context)
6990 MonoMethodSignature *fsig;
6992 if (method->wrapper_type != MONO_WRAPPER_NONE) {
6995 fsig = (MonoMethodSignature *)mono_method_get_wrapper_data (method, token);
6997 fsig = mono_inflate_generic_signature (fsig, context, &error);
6999 g_assert (mono_error_ok (&error));
7002 fsig = mono_metadata_parse_signature (method->klass->image, token);
7008 * Returns TRUE if the JIT should abort inlining because "callee"
7009 * is influenced by security attributes.
7012 gboolean check_linkdemand (MonoCompile *cfg, MonoMethod *caller, MonoMethod *callee)
7016 if ((cfg->method != caller) && mono_security_method_has_declsec (callee)) {
7020 result = mono_declsec_linkdemand (cfg->domain, caller, callee);
7021 if (result == MONO_JIT_SECURITY_OK)
7024 if (result == MONO_JIT_LINKDEMAND_ECMA) {
7025 /* Generate code to throw a SecurityException before the actual call/link */
7026 MonoSecurityManager *secman = mono_security_manager_get_methods ();
7029 NEW_ICONST (cfg, args [0], 4);
7030 NEW_METHODCONST (cfg, args [1], caller);
7031 mono_emit_method_call (cfg, secman->linkdemandsecurityexception, args, NULL);
7032 } else if (cfg->exception_type == MONO_EXCEPTION_NONE) {
7033 /* don't hide previous results */
7034 mono_cfg_set_exception (cfg, MONO_EXCEPTION_SECURITY_LINKDEMAND);
7035 cfg->exception_data = result;
7043 throw_exception (void)
7045 static MonoMethod *method = NULL;
7048 MonoSecurityManager *secman = mono_security_manager_get_methods ();
7049 method = mono_class_get_method_from_name (secman->securitymanager, "ThrowException", 1);
7056 emit_throw_exception (MonoCompile *cfg, MonoException *ex)
7058 MonoMethod *thrower = throw_exception ();
7061 EMIT_NEW_PCONST (cfg, args [0], ex);
7062 mono_emit_method_call (cfg, thrower, args, NULL);
7066 * Return the original method is a wrapper is specified. We can only access
7067 * the custom attributes from the original method.
7070 get_original_method (MonoMethod *method)
7072 if (method->wrapper_type == MONO_WRAPPER_NONE)
7075 /* native code (which is like Critical) can call any managed method XXX FIXME XXX to validate all usages */
7076 if (method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED)
7079 /* in other cases we need to find the original method */
7080 return mono_marshal_method_from_wrapper (method);
7084 ensure_method_is_allowed_to_access_field (MonoCompile *cfg, MonoMethod *caller, MonoClassField *field,
7085 MonoBasicBlock *bblock, unsigned char *ip)
7087 /* we can't get the coreclr security level on wrappers since they don't have the attributes */
7088 MonoException *ex = mono_security_core_clr_is_field_access_allowed (get_original_method (caller), field);
7090 emit_throw_exception (cfg, ex);
7094 ensure_method_is_allowed_to_call_method (MonoCompile *cfg, MonoMethod *caller, MonoMethod *callee,
7095 MonoBasicBlock *bblock, unsigned char *ip)
7097 /* we can't get the coreclr security level on wrappers since they don't have the attributes */
7098 MonoException *ex = mono_security_core_clr_is_call_allowed (get_original_method (caller), callee);
7100 emit_throw_exception (cfg, ex);
7104 * Check that the IL instructions at ip are the array initialization
7105 * sequence and return the pointer to the data and the size.
7108 initialize_array_data (MonoMethod *method, gboolean aot, unsigned char *ip, MonoClass *klass, guint32 len, int *out_size, guint32 *out_field_token)
7111 * newarr[System.Int32]
7113 * ldtoken field valuetype ...
7114 * call void class [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(class [mscorlib]System.Array, valuetype [mscorlib]System.RuntimeFieldHandle)
7116 if (ip [0] == CEE_DUP && ip [1] == CEE_LDTOKEN && ip [5] == 0x4 && ip [6] == CEE_CALL) {
7118 guint32 token = read32 (ip + 7);
7119 guint32 field_token = read32 (ip + 2);
7120 guint32 field_index = field_token & 0xffffff;
7122 const char *data_ptr;
7124 MonoMethod *cmethod;
7125 MonoClass *dummy_class;
7126 MonoClassField *field = mono_field_from_token_checked (method->klass->image, field_token, &dummy_class, NULL, &error);
7130 mono_error_cleanup (&error); /* FIXME don't swallow the error */
7134 *out_field_token = field_token;
7136 cmethod = mini_get_method (NULL, method, token, NULL, NULL);
7139 if (strcmp (cmethod->name, "InitializeArray") || strcmp (cmethod->klass->name, "RuntimeHelpers") || cmethod->klass->image != mono_defaults.corlib)
7141 switch (mono_type_get_underlying_type (&klass->byval_arg)->type) {
7142 case MONO_TYPE_BOOLEAN:
7146 /* we need to swap on big endian, so punt. Should we handle R4 and R8 as well? */
7147 #if TARGET_BYTE_ORDER == G_LITTLE_ENDIAN
7148 case MONO_TYPE_CHAR:
7165 if (size > mono_type_size (field->type, &dummy_align))
7168 /*g_print ("optimized in %s: size: %d, numelems: %d\n", method->name, size, newarr->inst_newa_len->inst_c0);*/
7169 if (!image_is_dynamic (method->klass->image)) {
7170 field_index = read32 (ip + 2) & 0xffffff;
7171 mono_metadata_field_info (method->klass->image, field_index - 1, NULL, &rva, NULL);
7172 data_ptr = mono_image_rva_map (method->klass->image, rva);
7173 /*g_print ("field: 0x%08x, rva: %d, rva_ptr: %p\n", read32 (ip + 2), rva, data_ptr);*/
7174 /* for aot code we do the lookup on load */
7175 if (aot && data_ptr)
7176 return GUINT_TO_POINTER (rva);
7178 /*FIXME is it possible to AOT a SRE assembly not meant to be saved? */
7180 data_ptr = mono_field_get_data (field);
7188 set_exception_type_from_invalid_il (MonoCompile *cfg, MonoMethod *method, unsigned char *ip)
7190 char *method_fname = mono_method_full_name (method, TRUE);
7192 MonoMethodHeader *header = mono_method_get_header (method);
7194 if (header->code_size == 0)
7195 method_code = g_strdup ("method body is empty.");
7197 method_code = mono_disasm_code_one (NULL, method, ip, NULL);
7198 mono_cfg_set_exception (cfg, MONO_EXCEPTION_INVALID_PROGRAM);
7199 cfg->exception_message = g_strdup_printf ("Invalid IL code in %s: %s\n", method_fname, method_code);
7200 g_free (method_fname);
7201 g_free (method_code);
7202 cfg->headers_to_free = g_slist_prepend_mempool (cfg->mempool, cfg->headers_to_free, header);
7206 set_exception_object (MonoCompile *cfg, MonoException *exception)
7208 mono_cfg_set_exception (cfg, MONO_EXCEPTION_OBJECT_SUPPLIED);
7209 MONO_GC_REGISTER_ROOT_SINGLE (cfg->exception_ptr);
7210 cfg->exception_ptr = exception;
7214 emit_stloc_ir (MonoCompile *cfg, MonoInst **sp, MonoMethodHeader *header, int n)
7217 guint32 opcode = mono_type_to_regmove (cfg, header->locals [n]);
7218 if ((opcode == OP_MOVE) && cfg->cbb->last_ins == sp [0] &&
7219 ((sp [0]->opcode == OP_ICONST) || (sp [0]->opcode == OP_I8CONST))) {
7220 /* Optimize reg-reg moves away */
7222 * Can't optimize other opcodes, since sp[0] might point to
7223 * the last ins of a decomposed opcode.
7225 sp [0]->dreg = (cfg)->locals [n]->dreg;
7227 EMIT_NEW_LOCSTORE (cfg, ins, n, *sp);
7232 * ldloca inhibits many optimizations so try to get rid of it in common
7235 static inline unsigned char *
7236 emit_optimized_ldloca_ir (MonoCompile *cfg, unsigned char *ip, unsigned char *end, int size)
7246 local = read16 (ip + 2);
7250 if (ip + 6 < end && (ip [0] == CEE_PREFIX1) && (ip [1] == CEE_INITOBJ) && ip_in_bb (cfg, cfg->cbb, ip + 1)) {
7251 /* From the INITOBJ case */
7252 token = read32 (ip + 2);
7253 klass = mini_get_class (cfg->current_method, token, cfg->generic_context);
7254 CHECK_TYPELOAD (klass);
7255 type = mini_replace_type (&klass->byval_arg);
7256 emit_init_local (cfg, local, type, TRUE);
7264 is_exception_class (MonoClass *class)
7267 if (class == mono_defaults.exception_class)
7269 class = class->parent;
7275 * is_jit_optimizer_disabled:
7277 * Determine whenever M's assembly has a DebuggableAttribute with the
7278 * IsJITOptimizerDisabled flag set.
7281 is_jit_optimizer_disabled (MonoMethod *m)
7283 MonoAssembly *ass = m->klass->image->assembly;
7284 MonoCustomAttrInfo* attrs;
7285 static MonoClass *klass;
7287 gboolean val = FALSE;
7290 if (ass->jit_optimizer_disabled_inited)
7291 return ass->jit_optimizer_disabled;
7294 klass = mono_class_from_name (mono_defaults.corlib, "System.Diagnostics", "DebuggableAttribute");
7297 ass->jit_optimizer_disabled = FALSE;
7298 mono_memory_barrier ();
7299 ass->jit_optimizer_disabled_inited = TRUE;
7303 attrs = mono_custom_attrs_from_assembly (ass);
7305 for (i = 0; i < attrs->num_attrs; ++i) {
7306 MonoCustomAttrEntry *attr = &attrs->attrs [i];
7309 MonoMethodSignature *sig;
7311 if (!attr->ctor || attr->ctor->klass != klass)
7313 /* Decode the attribute. See reflection.c */
7314 len = attr->data_size;
7315 p = (const char*)attr->data;
7316 g_assert (read16 (p) == 0x0001);
7319 // FIXME: Support named parameters
7320 sig = mono_method_signature (attr->ctor);
7321 if (sig->param_count != 2 || sig->params [0]->type != MONO_TYPE_BOOLEAN || sig->params [1]->type != MONO_TYPE_BOOLEAN)
7323 /* Two boolean arguments */
7327 mono_custom_attrs_free (attrs);
7330 ass->jit_optimizer_disabled = val;
7331 mono_memory_barrier ();
7332 ass->jit_optimizer_disabled_inited = TRUE;
7338 is_supported_tail_call (MonoCompile *cfg, MonoMethod *method, MonoMethod *cmethod, MonoMethodSignature *fsig, int call_opcode)
7340 gboolean supported_tail_call;
7343 #ifdef MONO_ARCH_HAVE_OP_TAIL_CALL
7344 supported_tail_call = mono_arch_tail_call_supported (cfg, mono_method_signature (method), mono_method_signature (cmethod));
7346 supported_tail_call = mono_metadata_signature_equal (mono_method_signature (method), mono_method_signature (cmethod)) && !MONO_TYPE_ISSTRUCT (mono_method_signature (cmethod)->ret);
7349 for (i = 0; i < fsig->param_count; ++i) {
7350 if (fsig->params [i]->byref || fsig->params [i]->type == MONO_TYPE_PTR || fsig->params [i]->type == MONO_TYPE_FNPTR)
7351 /* These can point to the current method's stack */
7352 supported_tail_call = FALSE;
7354 if (fsig->hasthis && cmethod->klass->valuetype)
7355 /* this might point to the current method's stack */
7356 supported_tail_call = FALSE;
7357 if (cmethod->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)
7358 supported_tail_call = FALSE;
7359 if (cfg->method->save_lmf)
7360 supported_tail_call = FALSE;
7361 if (cmethod->wrapper_type && cmethod->wrapper_type != MONO_WRAPPER_DYNAMIC_METHOD)
7362 supported_tail_call = FALSE;
7363 if (call_opcode != CEE_CALL)
7364 supported_tail_call = FALSE;
7366 /* Debugging support */
7368 if (supported_tail_call) {
7369 if (!mono_debug_count ())
7370 supported_tail_call = FALSE;
7374 return supported_tail_call;
7377 /* the JIT intercepts ldflda instructions to the tlsdata field in ThreadLocal<T> and redirects
7378 * it to the thread local value based on the tls_offset field. Every other kind of access to
7379 * the field causes an assert.
7382 is_magic_tls_access (MonoClassField *field)
7384 if (strcmp (field->name, "tlsdata"))
7386 if (strcmp (field->parent->name, "ThreadLocal`1"))
7388 return field->parent->image == mono_defaults.corlib;
7391 /* emits the code needed to access a managed tls var (like ThreadStatic)
7392 * with the value of the tls offset in offset_reg. thread_ins represents the MonoInternalThread
7393 * pointer for the current thread.
7394 * Returns the MonoInst* representing the address of the tls var.
7397 emit_managed_static_data_access (MonoCompile *cfg, MonoInst *thread_ins, int offset_reg)
7400 int static_data_reg, array_reg, dreg;
7401 int offset2_reg, idx_reg;
7402 // inlined access to the tls data
7403 // idx = (offset >> 24) - 1;
7404 // return ((char*) thread->static_data [idx]) + (offset & 0xffffff);
7405 static_data_reg = alloc_ireg (cfg);
7406 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, static_data_reg, thread_ins->dreg, MONO_STRUCT_OFFSET (MonoInternalThread, static_data));
7407 idx_reg = alloc_ireg (cfg);
7408 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ISHR_IMM, idx_reg, offset_reg, 24);
7409 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ISUB_IMM, idx_reg, idx_reg, 1);
7410 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ISHL_IMM, idx_reg, idx_reg, sizeof (gpointer) == 8 ? 3 : 2);
7411 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, static_data_reg, static_data_reg, idx_reg);
7412 array_reg = alloc_ireg (cfg);
7413 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, array_reg, static_data_reg, 0);
7414 offset2_reg = alloc_ireg (cfg);
7415 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_IAND_IMM, offset2_reg, offset_reg, 0xffffff);
7416 dreg = alloc_ireg (cfg);
7417 EMIT_NEW_BIALU (cfg, addr, OP_PADD, dreg, array_reg, offset2_reg);
7422 * redirect access to the tlsdata field to the tls var given by the tls_offset field.
7423 * this address is cached per-method in cached_tls_addr.
7426 create_magic_tls_access (MonoCompile *cfg, MonoClassField *tls_field, MonoInst **cached_tls_addr, MonoInst *thread_local)
7428 MonoInst *load, *addr, *temp, *store, *thread_ins;
7429 MonoClassField *offset_field;
7431 if (*cached_tls_addr) {
7432 EMIT_NEW_TEMPLOAD (cfg, addr, (*cached_tls_addr)->inst_c0);
7435 thread_ins = mono_get_thread_intrinsic (cfg);
7436 offset_field = mono_class_get_field_from_name (tls_field->parent, "tls_offset");
7438 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, load, offset_field->type, thread_local->dreg, offset_field->offset);
7440 MONO_ADD_INS (cfg->cbb, thread_ins);
7442 MonoMethod *thread_method;
7443 thread_method = mono_class_get_method_from_name (mono_get_thread_class(), "CurrentInternalThread_internal", 0);
7444 thread_ins = mono_emit_method_call (cfg, thread_method, NULL, NULL);
7446 addr = emit_managed_static_data_access (cfg, thread_ins, load->dreg);
7447 addr->klass = mono_class_from_mono_type (tls_field->type);
7448 addr->type = STACK_MP;
7449 *cached_tls_addr = temp = mono_compile_create_var (cfg, type_from_stack_type (addr), OP_LOCAL);
7450 EMIT_NEW_TEMPSTORE (cfg, store, temp->inst_c0, addr);
7452 EMIT_NEW_TEMPLOAD (cfg, addr, temp->inst_c0);
7459 * Handle calls made to ctors from NEWOBJ opcodes.
7461 * REF_BBLOCK will point to the current bblock after the call.
7464 handle_ctor_call (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, int context_used,
7465 MonoInst **sp, guint8 *ip, MonoBasicBlock **ref_bblock, int *inline_costs)
7467 MonoInst *vtable_arg = NULL, *callvirt_this_arg = NULL, *ins;
7468 MonoBasicBlock *bblock = *ref_bblock;
7470 if (cmethod->klass->valuetype && mono_class_generic_sharing_enabled (cmethod->klass) &&
7471 mono_method_is_generic_sharable (cmethod, TRUE)) {
7472 if (cmethod->is_inflated && mono_method_get_context (cmethod)->method_inst) {
7473 mono_class_vtable (cfg->domain, cmethod->klass);
7474 CHECK_TYPELOAD (cmethod->klass);
7476 vtable_arg = emit_get_rgctx_method (cfg, context_used,
7477 cmethod, MONO_RGCTX_INFO_METHOD_RGCTX);
7480 vtable_arg = emit_get_rgctx_klass (cfg, context_used,
7481 cmethod->klass, MONO_RGCTX_INFO_VTABLE);
7483 MonoVTable *vtable = mono_class_vtable (cfg->domain, cmethod->klass);
7485 CHECK_TYPELOAD (cmethod->klass);
7486 EMIT_NEW_VTABLECONST (cfg, vtable_arg, vtable);
7491 /* Avoid virtual calls to ctors if possible */
7492 if (mono_class_is_marshalbyref (cmethod->klass))
7493 callvirt_this_arg = sp [0];
7495 if (cmethod && (cfg->opt & MONO_OPT_INTRINS) && (ins = mini_emit_inst_for_ctor (cfg, cmethod, fsig, sp))) {
7496 g_assert (MONO_TYPE_IS_VOID (fsig->ret));
7497 CHECK_CFG_EXCEPTION;
7498 } else if ((cfg->opt & MONO_OPT_INLINE) && cmethod && !context_used && !vtable_arg &&
7499 mono_method_check_inlining (cfg, cmethod) &&
7500 !mono_class_is_subclass_of (cmethod->klass, mono_defaults.exception_class, FALSE)) {
7503 if ((costs = inline_method (cfg, cmethod, fsig, sp, ip, cfg->real_offset, FALSE, &bblock))) {
7504 cfg->real_offset += 5;
7506 *inline_costs += costs - 5;
7507 *ref_bblock = bblock;
7509 INLINE_FAILURE ("inline failure");
7510 // FIXME-VT: Clean this up
7511 if (cfg->gsharedvt && mini_is_gsharedvt_signature (cfg, fsig))
7512 GSHAREDVT_FAILURE(*ip);
7513 mono_emit_method_call_full (cfg, cmethod, fsig, FALSE, sp, callvirt_this_arg, NULL, NULL);
7515 } else if (cfg->gsharedvt && mini_is_gsharedvt_signature (cfg, fsig)) {
7518 addr = emit_get_rgctx_gsharedvt_call (cfg, context_used, fsig, cmethod, MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE);
7519 mono_emit_calli (cfg, fsig, sp, addr, NULL, vtable_arg);
7520 } else if (context_used &&
7521 ((!mono_method_is_generic_sharable_full (cmethod, TRUE, FALSE, FALSE) ||
7522 !mono_class_generic_sharing_enabled (cmethod->klass)) || cfg->gsharedvt)) {
7523 MonoInst *cmethod_addr;
7525 /* Generic calls made out of gsharedvt methods cannot be patched, so use an indirect call */
7527 cmethod_addr = emit_get_rgctx_method (cfg, context_used,
7528 cmethod, MONO_RGCTX_INFO_GENERIC_METHOD_CODE);
7530 mono_emit_calli (cfg, fsig, sp, cmethod_addr, NULL, vtable_arg);
7532 INLINE_FAILURE ("ctor call");
7533 ins = mono_emit_method_call_full (cfg, cmethod, fsig, FALSE, sp,
7534 callvirt_this_arg, NULL, vtable_arg);
7541 * mono_method_to_ir:
7543 * Translate the .net IL into linear IR.
7546 mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_bblock, MonoBasicBlock *end_bblock,
7547 MonoInst *return_var, MonoInst **inline_args,
7548 guint inline_offset, gboolean is_virtual_call)
7551 MonoInst *ins, **sp, **stack_start;
7552 MonoBasicBlock *bblock, *tblock = NULL, *init_localsbb = NULL;
7553 MonoSimpleBasicBlock *bb = NULL, *original_bb = NULL;
7554 MonoMethod *cmethod, *method_definition;
7555 MonoInst **arg_array;
7556 MonoMethodHeader *header;
7558 guint32 token, ins_flag;
7560 MonoClass *constrained_call = NULL;
7561 unsigned char *ip, *end, *target, *err_pos;
7562 MonoMethodSignature *sig;
7563 MonoGenericContext *generic_context = NULL;
7564 MonoGenericContainer *generic_container = NULL;
7565 MonoType **param_types;
7566 int i, n, start_new_bblock, dreg;
7567 int num_calls = 0, inline_costs = 0;
7568 int breakpoint_id = 0;
7570 MonoBoolean security, pinvoke;
7571 MonoSecurityManager* secman = NULL;
7572 MonoDeclSecurityActions actions;
7573 GSList *class_inits = NULL;
7574 gboolean dont_verify, dont_verify_stloc, readonly = FALSE;
7576 gboolean init_locals, seq_points, skip_dead_blocks;
7577 gboolean sym_seq_points = FALSE;
7578 MonoInst *cached_tls_addr = NULL;
7579 MonoDebugMethodInfo *minfo;
7580 MonoBitSet *seq_point_locs = NULL;
7581 MonoBitSet *seq_point_set_locs = NULL;
7583 cfg->disable_inline = is_jit_optimizer_disabled (method);
7585 /* serialization and xdomain stuff may need access to private fields and methods */
7586 dont_verify = method->klass->image->assembly->corlib_internal? TRUE: FALSE;
7587 dont_verify |= method->wrapper_type == MONO_WRAPPER_XDOMAIN_INVOKE;
7588 dont_verify |= method->wrapper_type == MONO_WRAPPER_XDOMAIN_DISPATCH;
7589 dont_verify |= method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE; /* bug #77896 */
7590 dont_verify |= method->wrapper_type == MONO_WRAPPER_COMINTEROP;
7591 dont_verify |= method->wrapper_type == MONO_WRAPPER_COMINTEROP_INVOKE;
7593 dont_verify |= mono_security_smcs_hack_enabled ();
7595 /* still some type unsafety issues in marshal wrappers... (unknown is PtrToStructure) */
7596 dont_verify_stloc = method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE;
7597 dont_verify_stloc |= method->wrapper_type == MONO_WRAPPER_UNKNOWN;
7598 dont_verify_stloc |= method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED;
7599 dont_verify_stloc |= method->wrapper_type == MONO_WRAPPER_STELEMREF;
7601 image = method->klass->image;
7602 header = mono_method_get_header (method);
7604 MonoLoaderError *error;
7606 if ((error = mono_loader_get_last_error ())) {
7607 mono_cfg_set_exception (cfg, error->exception_type);
7609 mono_cfg_set_exception (cfg, MONO_EXCEPTION_INVALID_PROGRAM);
7610 cfg->exception_message = g_strdup_printf ("Missing or incorrect header for method %s", cfg->method->name);
7612 goto exception_exit;
7614 generic_container = mono_method_get_generic_container (method);
7615 sig = mono_method_signature (method);
7616 num_args = sig->hasthis + sig->param_count;
7617 ip = (unsigned char*)header->code;
7618 cfg->cil_start = ip;
7619 end = ip + header->code_size;
7620 cfg->stat_cil_code_size += header->code_size;
7622 seq_points = cfg->gen_seq_points && cfg->method == method;
7623 #ifdef PLATFORM_ANDROID
7624 seq_points &= cfg->method->wrapper_type == MONO_WRAPPER_NONE;
7627 if (method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED) {
7628 /* We could hit a seq point before attaching to the JIT (#8338) */
7632 if (cfg->gen_seq_points_debug_data && cfg->method == method) {
7633 minfo = mono_debug_lookup_method (method);
7635 int i, n_il_offsets;
7639 mono_debug_symfile_get_line_numbers_full (minfo, NULL, NULL, &n_il_offsets, &il_offsets, &line_numbers, NULL, NULL, NULL, NULL);
7640 seq_point_locs = mono_bitset_mem_new (mono_mempool_alloc0 (cfg->mempool, mono_bitset_alloc_size (header->code_size, 0)), header->code_size, 0);
7641 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);
7642 sym_seq_points = TRUE;
7643 for (i = 0; i < n_il_offsets; ++i) {
7644 if (il_offsets [i] < header->code_size)
7645 mono_bitset_set_fast (seq_point_locs, il_offsets [i]);
7647 g_free (il_offsets);
7648 g_free (line_numbers);
7649 } else if (!method->wrapper_type && !method->dynamic && mono_debug_image_has_debug_info (method->klass->image)) {
7650 /* Methods without line number info like auto-generated property accessors */
7651 seq_point_locs = mono_bitset_mem_new (mono_mempool_alloc0 (cfg->mempool, mono_bitset_alloc_size (header->code_size, 0)), header->code_size, 0);
7652 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);
7653 sym_seq_points = TRUE;
7658 * Methods without init_locals set could cause asserts in various passes
7659 * (#497220). To work around this, we emit dummy initialization opcodes
7660 * (OP_DUMMY_ICONST etc.) which generate no code. These are only supported
7661 * on some platforms.
7663 if ((cfg->opt & MONO_OPT_UNSAFE) && ARCH_HAVE_DUMMY_INIT)
7664 init_locals = header->init_locals;
7668 method_definition = method;
7669 while (method_definition->is_inflated) {
7670 MonoMethodInflated *imethod = (MonoMethodInflated *) method_definition;
7671 method_definition = imethod->declaring;
7674 /* SkipVerification is not allowed if core-clr is enabled */
7675 if (!dont_verify && mini_assembly_can_skip_verification (cfg->domain, method)) {
7677 dont_verify_stloc = TRUE;
7680 if (sig->is_inflated)
7681 generic_context = mono_method_get_context (method);
7682 else if (generic_container)
7683 generic_context = &generic_container->context;
7684 cfg->generic_context = generic_context;
7686 if (!cfg->generic_sharing_context)
7687 g_assert (!sig->has_type_parameters);
7689 if (sig->generic_param_count && method->wrapper_type == MONO_WRAPPER_NONE) {
7690 g_assert (method->is_inflated);
7691 g_assert (mono_method_get_context (method)->method_inst);
7693 if (method->is_inflated && mono_method_get_context (method)->method_inst)
7694 g_assert (sig->generic_param_count);
7696 if (cfg->method == method) {
7697 cfg->real_offset = 0;
7699 cfg->real_offset = inline_offset;
7702 cfg->cil_offset_to_bb = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoBasicBlock*) * header->code_size);
7703 cfg->cil_offset_to_bb_len = header->code_size;
7705 cfg->current_method = method;
7707 if (cfg->verbose_level > 2)
7708 printf ("method to IR %s\n", mono_method_full_name (method, TRUE));
7710 param_types = mono_mempool_alloc (cfg->mempool, sizeof (MonoType*) * num_args);
7712 param_types [0] = method->klass->valuetype?&method->klass->this_arg:&method->klass->byval_arg;
7713 for (n = 0; n < sig->param_count; ++n)
7714 param_types [n + sig->hasthis] = sig->params [n];
7715 cfg->arg_types = param_types;
7717 cfg->dont_inline = g_list_prepend (cfg->dont_inline, method);
7718 if (cfg->method == method) {
7720 if (cfg->prof_options & MONO_PROFILE_INS_COVERAGE)
7721 cfg->coverage_info = mono_profiler_coverage_alloc (cfg->method, header->code_size);
7724 NEW_BBLOCK (cfg, start_bblock);
7725 cfg->bb_entry = start_bblock;
7726 start_bblock->cil_code = NULL;
7727 start_bblock->cil_length = 0;
7728 #if defined(__native_client_codegen__)
7729 MONO_INST_NEW (cfg, ins, OP_NACL_GC_SAFE_POINT);
7730 ins->dreg = alloc_dreg (cfg, STACK_I4);
7731 MONO_ADD_INS (start_bblock, ins);
7735 NEW_BBLOCK (cfg, end_bblock);
7736 cfg->bb_exit = end_bblock;
7737 end_bblock->cil_code = NULL;
7738 end_bblock->cil_length = 0;
7739 end_bblock->flags |= BB_INDIRECT_JUMP_TARGET;
7740 g_assert (cfg->num_bblocks == 2);
7742 arg_array = cfg->args;
7744 if (header->num_clauses) {
7745 cfg->spvars = g_hash_table_new (NULL, NULL);
7746 cfg->exvars = g_hash_table_new (NULL, NULL);
7748 /* handle exception clauses */
7749 for (i = 0; i < header->num_clauses; ++i) {
7750 MonoBasicBlock *try_bb;
7751 MonoExceptionClause *clause = &header->clauses [i];
7752 GET_BBLOCK (cfg, try_bb, ip + clause->try_offset);
7753 try_bb->real_offset = clause->try_offset;
7754 try_bb->try_start = TRUE;
7755 try_bb->region = ((i + 1) << 8) | clause->flags;
7756 GET_BBLOCK (cfg, tblock, ip + clause->handler_offset);
7757 tblock->real_offset = clause->handler_offset;
7758 tblock->flags |= BB_EXCEPTION_HANDLER;
7761 * Linking the try block with the EH block hinders inlining as we won't be able to
7762 * merge the bblocks from inlining and produce an artificial hole for no good reason.
7764 if (COMPILE_LLVM (cfg))
7765 link_bblock (cfg, try_bb, tblock);
7767 if (*(ip + clause->handler_offset) == CEE_POP)
7768 tblock->flags |= BB_EXCEPTION_DEAD_OBJ;
7770 if (clause->flags == MONO_EXCEPTION_CLAUSE_FINALLY ||
7771 clause->flags == MONO_EXCEPTION_CLAUSE_FILTER ||
7772 clause->flags == MONO_EXCEPTION_CLAUSE_FAULT) {
7773 MONO_INST_NEW (cfg, ins, OP_START_HANDLER);
7774 MONO_ADD_INS (tblock, ins);
7776 if (seq_points && clause->flags != MONO_EXCEPTION_CLAUSE_FINALLY) {
7777 /* finally clauses already have a seq point */
7778 NEW_SEQ_POINT (cfg, ins, clause->handler_offset, TRUE);
7779 MONO_ADD_INS (tblock, ins);
7782 /* todo: is a fault block unsafe to optimize? */
7783 if (clause->flags == MONO_EXCEPTION_CLAUSE_FAULT)
7784 tblock->flags |= BB_EXCEPTION_UNSAFE;
7788 /*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);
7790 printf ("%s", mono_disasm_code_one (NULL, method, p, &p));
7792 /* catch and filter blocks get the exception object on the stack */
7793 if (clause->flags == MONO_EXCEPTION_CLAUSE_NONE ||
7794 clause->flags == MONO_EXCEPTION_CLAUSE_FILTER) {
7795 MonoInst *dummy_use;
7797 /* mostly like handle_stack_args (), but just sets the input args */
7798 /* printf ("handling clause at IL_%04x\n", clause->handler_offset); */
7799 tblock->in_scount = 1;
7800 tblock->in_stack = mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*));
7801 tblock->in_stack [0] = mono_create_exvar_for_offset (cfg, clause->handler_offset);
7804 * Add a dummy use for the exvar so its liveness info will be
7808 EMIT_NEW_DUMMY_USE (cfg, dummy_use, tblock->in_stack [0]);
7810 if (clause->flags == MONO_EXCEPTION_CLAUSE_FILTER) {
7811 GET_BBLOCK (cfg, tblock, ip + clause->data.filter_offset);
7812 tblock->flags |= BB_EXCEPTION_HANDLER;
7813 tblock->real_offset = clause->data.filter_offset;
7814 tblock->in_scount = 1;
7815 tblock->in_stack = mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*));
7816 /* The filter block shares the exvar with the handler block */
7817 tblock->in_stack [0] = mono_create_exvar_for_offset (cfg, clause->handler_offset);
7818 MONO_INST_NEW (cfg, ins, OP_START_HANDLER);
7819 MONO_ADD_INS (tblock, ins);
7823 if (clause->flags != MONO_EXCEPTION_CLAUSE_FILTER &&
7824 clause->data.catch_class &&
7825 cfg->generic_sharing_context &&
7826 mono_class_check_context_used (clause->data.catch_class)) {
7828 * In shared generic code with catch
7829 * clauses containing type variables
7830 * the exception handling code has to
7831 * be able to get to the rgctx.
7832 * Therefore we have to make sure that
7833 * the vtable/mrgctx argument (for
7834 * static or generic methods) or the
7835 * "this" argument (for non-static
7836 * methods) are live.
7838 if ((method->flags & METHOD_ATTRIBUTE_STATIC) ||
7839 mini_method_get_context (method)->method_inst ||
7840 method->klass->valuetype) {
7841 mono_get_vtable_var (cfg);
7843 MonoInst *dummy_use;
7845 EMIT_NEW_DUMMY_USE (cfg, dummy_use, arg_array [0]);
7850 arg_array = (MonoInst **) alloca (sizeof (MonoInst *) * num_args);
7851 cfg->cbb = start_bblock;
7852 cfg->args = arg_array;
7853 mono_save_args (cfg, sig, inline_args);
7856 /* FIRST CODE BLOCK */
7857 NEW_BBLOCK (cfg, bblock);
7858 bblock->cil_code = ip;
7862 ADD_BBLOCK (cfg, bblock);
7864 if (cfg->method == method) {
7865 breakpoint_id = mono_debugger_method_has_breakpoint (method);
7866 if (breakpoint_id) {
7867 MONO_INST_NEW (cfg, ins, OP_BREAK);
7868 MONO_ADD_INS (bblock, ins);
7872 if (mono_security_cas_enabled ())
7873 secman = mono_security_manager_get_methods ();
7875 security = (secman && mono_security_method_has_declsec (method));
7876 /* at this point having security doesn't mean we have any code to generate */
7877 if (security && (cfg->method == method)) {
7878 /* Only Demand, NonCasDemand and DemandChoice requires code generation.
7879 * And we do not want to enter the next section (with allocation) if we
7880 * have nothing to generate */
7881 security = mono_declsec_get_demands (method, &actions);
7884 /* we must Demand SecurityPermission.Unmanaged before P/Invoking */
7885 pinvoke = (secman && (method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE));
7887 MonoMethod *wrapped = mono_marshal_method_from_wrapper (method);
7888 if (wrapped && (wrapped->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)) {
7889 MonoCustomAttrInfo* custom = mono_custom_attrs_from_method (wrapped);
7891 /* unless the method or it's class has the [SuppressUnmanagedCodeSecurity] attribute */
7892 if (custom && mono_custom_attrs_has_attr (custom, secman->suppressunmanagedcodesecurity)) {
7896 mono_custom_attrs_free (custom);
7899 custom = mono_custom_attrs_from_class (wrapped->klass);
7900 if (custom && mono_custom_attrs_has_attr (custom, secman->suppressunmanagedcodesecurity)) {
7904 mono_custom_attrs_free (custom);
7907 /* not a P/Invoke after all */
7912 /* we use a separate basic block for the initialization code */
7913 NEW_BBLOCK (cfg, init_localsbb);
7914 cfg->bb_init = init_localsbb;
7915 init_localsbb->real_offset = cfg->real_offset;
7916 start_bblock->next_bb = init_localsbb;
7917 init_localsbb->next_bb = bblock;
7918 link_bblock (cfg, start_bblock, init_localsbb);
7919 link_bblock (cfg, init_localsbb, bblock);
7921 cfg->cbb = init_localsbb;
7923 if (cfg->gsharedvt && cfg->method == method) {
7924 MonoGSharedVtMethodInfo *info;
7925 MonoInst *var, *locals_var;
7928 info = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoGSharedVtMethodInfo));
7929 info->method = cfg->method;
7930 info->count_entries = 16;
7931 info->entries = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoRuntimeGenericContextInfoTemplate) * info->count_entries);
7932 cfg->gsharedvt_info = info;
7934 var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
7935 /* prevent it from being register allocated */
7936 //var->flags |= MONO_INST_VOLATILE;
7937 cfg->gsharedvt_info_var = var;
7939 ins = emit_get_rgctx_gsharedvt_method (cfg, mini_method_check_context_used (cfg, method), method, info);
7940 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, var->dreg, ins->dreg);
7942 /* Allocate locals */
7943 locals_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
7944 /* prevent it from being register allocated */
7945 //locals_var->flags |= MONO_INST_VOLATILE;
7946 cfg->gsharedvt_locals_var = locals_var;
7948 dreg = alloc_ireg (cfg);
7949 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI4_MEMBASE, dreg, var->dreg, MONO_STRUCT_OFFSET (MonoGSharedVtMethodRuntimeInfo, locals_size));
7951 MONO_INST_NEW (cfg, ins, OP_LOCALLOC);
7952 ins->dreg = locals_var->dreg;
7954 MONO_ADD_INS (cfg->cbb, ins);
7955 cfg->gsharedvt_locals_var_ins = ins;
7957 cfg->flags |= MONO_CFG_HAS_ALLOCA;
7960 ins->flags |= MONO_INST_INIT;
7964 /* at this point we know, if security is TRUE, that some code needs to be generated */
7965 if (security && (cfg->method == method)) {
7968 cfg->stat_cas_demand_generation++;
7970 if (actions.demand.blob) {
7971 /* Add code for SecurityAction.Demand */
7972 EMIT_NEW_DECLSECCONST (cfg, args[0], image, actions.demand);
7973 EMIT_NEW_ICONST (cfg, args [1], actions.demand.size);
7974 /* Calls static void SecurityManager.InternalDemand (byte* permissions, int size); */
7975 mono_emit_method_call (cfg, secman->demand, args, NULL);
7977 if (actions.noncasdemand.blob) {
7978 /* CLR 1.x uses a .noncasdemand (but 2.x doesn't) */
7979 /* For Mono we re-route non-CAS Demand to Demand (as the managed code must deal with it anyway) */
7980 EMIT_NEW_DECLSECCONST (cfg, args[0], image, actions.noncasdemand);
7981 EMIT_NEW_ICONST (cfg, args [1], actions.noncasdemand.size);
7982 /* Calls static void SecurityManager.InternalDemand (byte* permissions, int size); */
7983 mono_emit_method_call (cfg, secman->demand, args, NULL);
7985 if (actions.demandchoice.blob) {
7986 /* New in 2.0, Demand must succeed for one of the permissions (i.e. not all) */
7987 EMIT_NEW_DECLSECCONST (cfg, args[0], image, actions.demandchoice);
7988 EMIT_NEW_ICONST (cfg, args [1], actions.demandchoice.size);
7989 /* Calls static void SecurityManager.InternalDemandChoice (byte* permissions, int size); */
7990 mono_emit_method_call (cfg, secman->demandchoice, args, NULL);
7994 /* we must Demand SecurityPermission.Unmanaged before p/invoking */
7996 mono_emit_method_call (cfg, secman->demandunmanaged, NULL, NULL);
7999 if (mono_security_core_clr_enabled ()) {
8000 /* check if this is native code, e.g. an icall or a p/invoke */
8001 if (method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE) {
8002 MonoMethod *wrapped = mono_marshal_method_from_wrapper (method);
8004 gboolean pinvk = (wrapped->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL);
8005 gboolean icall = (wrapped->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL);
8007 /* if this ia a native call then it can only be JITted from platform code */
8008 if ((icall || pinvk) && method->klass && method->klass->image) {
8009 if (!mono_security_core_clr_is_platform_image (method->klass->image)) {
8010 MonoException *ex = icall ? mono_get_exception_security () :
8011 mono_get_exception_method_access ();
8012 emit_throw_exception (cfg, ex);
8019 CHECK_CFG_EXCEPTION;
8021 if (header->code_size == 0)
8024 if (get_basic_blocks (cfg, header, cfg->real_offset, ip, end, &err_pos)) {
8029 if (cfg->method == method)
8030 mono_debug_init_method (cfg, bblock, breakpoint_id);
8032 for (n = 0; n < header->num_locals; ++n) {
8033 if (header->locals [n]->type == MONO_TYPE_VOID && !header->locals [n]->byref)
8038 /* We force the vtable variable here for all shared methods
8039 for the possibility that they might show up in a stack
8040 trace where their exact instantiation is needed. */
8041 if (cfg->generic_sharing_context && method == cfg->method) {
8042 if ((method->flags & METHOD_ATTRIBUTE_STATIC) ||
8043 mini_method_get_context (method)->method_inst ||
8044 method->klass->valuetype) {
8045 mono_get_vtable_var (cfg);
8047 /* FIXME: Is there a better way to do this?
8048 We need the variable live for the duration
8049 of the whole method. */
8050 cfg->args [0]->flags |= MONO_INST_VOLATILE;
8054 /* add a check for this != NULL to inlined methods */
8055 if (is_virtual_call) {
8058 NEW_ARGLOAD (cfg, arg_ins, 0);
8059 MONO_ADD_INS (cfg->cbb, arg_ins);
8060 MONO_EMIT_NEW_CHECK_THIS (cfg, arg_ins->dreg);
8063 skip_dead_blocks = !dont_verify;
8064 if (skip_dead_blocks) {
8065 original_bb = bb = mono_basic_block_split (method, &cfg->error);
8070 /* we use a spare stack slot in SWITCH and NEWOBJ and others */
8071 stack_start = sp = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoInst*) * (header->max_stack + 1));
8074 start_new_bblock = 0;
8077 if (cfg->method == method)
8078 cfg->real_offset = ip - header->code;
8080 cfg->real_offset = inline_offset;
8085 if (start_new_bblock) {
8086 bblock->cil_length = ip - bblock->cil_code;
8087 if (start_new_bblock == 2) {
8088 g_assert (ip == tblock->cil_code);
8090 GET_BBLOCK (cfg, tblock, ip);
8092 bblock->next_bb = tblock;
8095 start_new_bblock = 0;
8096 for (i = 0; i < bblock->in_scount; ++i) {
8097 if (cfg->verbose_level > 3)
8098 printf ("loading %d from temp %d\n", i, (int)bblock->in_stack [i]->inst_c0);
8099 EMIT_NEW_TEMPLOAD (cfg, ins, bblock->in_stack [i]->inst_c0);
8103 g_slist_free (class_inits);
8106 if ((tblock = cfg->cil_offset_to_bb [ip - cfg->cil_start]) && (tblock != bblock)) {
8107 link_bblock (cfg, bblock, tblock);
8108 if (sp != stack_start) {
8109 handle_stack_args (cfg, stack_start, sp - stack_start);
8111 CHECK_UNVERIFIABLE (cfg);
8113 bblock->next_bb = tblock;
8116 for (i = 0; i < bblock->in_scount; ++i) {
8117 if (cfg->verbose_level > 3)
8118 printf ("loading %d from temp %d\n", i, (int)bblock->in_stack [i]->inst_c0);
8119 EMIT_NEW_TEMPLOAD (cfg, ins, bblock->in_stack [i]->inst_c0);
8122 g_slist_free (class_inits);
8127 if (skip_dead_blocks) {
8128 int ip_offset = ip - header->code;
8130 if (ip_offset == bb->end)
8134 int op_size = mono_opcode_size (ip, end);
8135 g_assert (op_size > 0); /*The BB formation pass must catch all bad ops*/
8137 if (cfg->verbose_level > 3) printf ("SKIPPING DEAD OP at %x\n", ip_offset);
8139 if (ip_offset + op_size == bb->end) {
8140 MONO_INST_NEW (cfg, ins, OP_NOP);
8141 MONO_ADD_INS (bblock, ins);
8142 start_new_bblock = 1;
8150 * Sequence points are points where the debugger can place a breakpoint.
8151 * Currently, we generate these automatically at points where the IL
8154 if (seq_points && ((!sym_seq_points && (sp == stack_start)) || (sym_seq_points && mono_bitset_test_fast (seq_point_locs, ip - header->code)))) {
8156 * Make methods interruptable at the beginning, and at the targets of
8157 * backward branches.
8158 * Also, do this at the start of every bblock in methods with clauses too,
8159 * to be able to handle instructions with inprecise control flow like
8161 * Backward branches are handled at the end of method-to-ir ().
8163 gboolean intr_loc = ip == header->code || (!cfg->cbb->last_ins && cfg->header->num_clauses);
8165 /* Avoid sequence points on empty IL like .volatile */
8166 // FIXME: Enable this
8167 //if (!(cfg->cbb->last_ins && cfg->cbb->last_ins->opcode == OP_SEQ_POINT)) {
8168 NEW_SEQ_POINT (cfg, ins, ip - header->code, intr_loc);
8169 if (sp != stack_start)
8170 ins->flags |= MONO_INST_NONEMPTY_STACK;
8171 MONO_ADD_INS (cfg->cbb, ins);
8174 mono_bitset_set_fast (seq_point_set_locs, ip - header->code);
8177 bblock->real_offset = cfg->real_offset;
8179 if ((cfg->method == method) && cfg->coverage_info) {
8180 guint32 cil_offset = ip - header->code;
8181 cfg->coverage_info->data [cil_offset].cil_code = ip;
8183 /* TODO: Use an increment here */
8184 #if defined(TARGET_X86)
8185 MONO_INST_NEW (cfg, ins, OP_STORE_MEM_IMM);
8186 ins->inst_p0 = &(cfg->coverage_info->data [cil_offset].count);
8188 MONO_ADD_INS (cfg->cbb, ins);
8190 EMIT_NEW_PCONST (cfg, ins, &(cfg->coverage_info->data [cil_offset].count));
8191 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STORE_MEMBASE_IMM, ins->dreg, 0, 1);
8195 if (cfg->verbose_level > 3)
8196 printf ("converting (in B%d: stack: %d) %s", bblock->block_num, (int)(sp - stack_start), mono_disasm_code_one (NULL, method, ip, NULL));
8200 if (seq_points && !sym_seq_points && sp != stack_start) {
8202 * The C# compiler uses these nops to notify the JIT that it should
8203 * insert seq points.
8205 NEW_SEQ_POINT (cfg, ins, ip - header->code, FALSE);
8206 MONO_ADD_INS (cfg->cbb, ins);
8208 if (cfg->keep_cil_nops)
8209 MONO_INST_NEW (cfg, ins, OP_HARD_NOP);
8211 MONO_INST_NEW (cfg, ins, OP_NOP);
8213 MONO_ADD_INS (bblock, ins);
8216 if (should_insert_brekpoint (cfg->method)) {
8217 ins = mono_emit_jit_icall (cfg, mono_debugger_agent_user_break, NULL);
8219 MONO_INST_NEW (cfg, ins, OP_NOP);
8222 MONO_ADD_INS (bblock, ins);
8228 CHECK_STACK_OVF (1);
8229 n = (*ip)-CEE_LDARG_0;
8231 EMIT_NEW_ARGLOAD (cfg, ins, n);
8239 CHECK_STACK_OVF (1);
8240 n = (*ip)-CEE_LDLOC_0;
8242 EMIT_NEW_LOCLOAD (cfg, ins, n);
8251 n = (*ip)-CEE_STLOC_0;
8254 if (!dont_verify_stloc && target_type_is_incompatible (cfg, header->locals [n], *sp))
8256 emit_stloc_ir (cfg, sp, header, n);
8263 CHECK_STACK_OVF (1);
8266 EMIT_NEW_ARGLOAD (cfg, ins, n);
8272 CHECK_STACK_OVF (1);
8275 NEW_ARGLOADA (cfg, ins, n);
8276 MONO_ADD_INS (cfg->cbb, ins);
8286 if (!dont_verify_stloc && target_type_is_incompatible (cfg, param_types [ip [1]], *sp))
8288 EMIT_NEW_ARGSTORE (cfg, ins, n, *sp);
8293 CHECK_STACK_OVF (1);
8296 EMIT_NEW_LOCLOAD (cfg, ins, n);
8300 case CEE_LDLOCA_S: {
8301 unsigned char *tmp_ip;
8303 CHECK_STACK_OVF (1);
8304 CHECK_LOCAL (ip [1]);
8306 if ((tmp_ip = emit_optimized_ldloca_ir (cfg, ip, end, 1))) {
8312 EMIT_NEW_LOCLOADA (cfg, ins, ip [1]);
8321 CHECK_LOCAL (ip [1]);
8322 if (!dont_verify_stloc && target_type_is_incompatible (cfg, header->locals [ip [1]], *sp))
8324 emit_stloc_ir (cfg, sp, header, ip [1]);
8329 CHECK_STACK_OVF (1);
8330 EMIT_NEW_PCONST (cfg, ins, NULL);
8331 ins->type = STACK_OBJ;
8336 CHECK_STACK_OVF (1);
8337 EMIT_NEW_ICONST (cfg, ins, -1);
8350 CHECK_STACK_OVF (1);
8351 EMIT_NEW_ICONST (cfg, ins, (*ip) - CEE_LDC_I4_0);
8357 CHECK_STACK_OVF (1);
8359 EMIT_NEW_ICONST (cfg, ins, *((signed char*)ip));
8365 CHECK_STACK_OVF (1);
8366 EMIT_NEW_ICONST (cfg, ins, (gint32)read32 (ip + 1));
8372 CHECK_STACK_OVF (1);
8373 MONO_INST_NEW (cfg, ins, OP_I8CONST);
8374 ins->type = STACK_I8;
8375 ins->dreg = alloc_dreg (cfg, STACK_I8);
8377 ins->inst_l = (gint64)read64 (ip);
8378 MONO_ADD_INS (bblock, ins);
8384 gboolean use_aotconst = FALSE;
8386 #ifdef TARGET_POWERPC
8387 /* FIXME: Clean this up */
8388 if (cfg->compile_aot)
8389 use_aotconst = TRUE;
8392 /* FIXME: we should really allocate this only late in the compilation process */
8393 f = mono_domain_alloc (cfg->domain, sizeof (float));
8395 CHECK_STACK_OVF (1);
8401 EMIT_NEW_AOTCONST (cfg, cons, MONO_PATCH_INFO_R4, f);
8403 dreg = alloc_freg (cfg);
8404 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADR4_MEMBASE, dreg, cons->dreg, 0);
8405 ins->type = cfg->r4_stack_type;
8407 MONO_INST_NEW (cfg, ins, OP_R4CONST);
8408 ins->type = cfg->r4_stack_type;
8409 ins->dreg = alloc_dreg (cfg, STACK_R8);
8411 MONO_ADD_INS (bblock, ins);
8421 gboolean use_aotconst = FALSE;
8423 #ifdef TARGET_POWERPC
8424 /* FIXME: Clean this up */
8425 if (cfg->compile_aot)
8426 use_aotconst = TRUE;
8429 /* FIXME: we should really allocate this only late in the compilation process */
8430 d = mono_domain_alloc (cfg->domain, sizeof (double));
8432 CHECK_STACK_OVF (1);
8438 EMIT_NEW_AOTCONST (cfg, cons, MONO_PATCH_INFO_R8, d);
8440 dreg = alloc_freg (cfg);
8441 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADR8_MEMBASE, dreg, cons->dreg, 0);
8442 ins->type = STACK_R8;
8444 MONO_INST_NEW (cfg, ins, OP_R8CONST);
8445 ins->type = STACK_R8;
8446 ins->dreg = alloc_dreg (cfg, STACK_R8);
8448 MONO_ADD_INS (bblock, ins);
8457 MonoInst *temp, *store;
8459 CHECK_STACK_OVF (1);
8463 temp = mono_compile_create_var (cfg, type_from_stack_type (ins), OP_LOCAL);
8464 EMIT_NEW_TEMPSTORE (cfg, store, temp->inst_c0, ins);
8466 EMIT_NEW_TEMPLOAD (cfg, ins, temp->inst_c0);
8469 EMIT_NEW_TEMPLOAD (cfg, ins, temp->inst_c0);
8482 if (sp [0]->type == STACK_R8)
8483 /* we need to pop the value from the x86 FP stack */
8484 MONO_EMIT_NEW_UNALU (cfg, OP_X86_FPOP, -1, sp [0]->dreg);
8490 INLINE_FAILURE ("jmp");
8491 GSHAREDVT_FAILURE (*ip);
8494 if (stack_start != sp)
8496 token = read32 (ip + 1);
8497 /* FIXME: check the signature matches */
8498 cmethod = mini_get_method (cfg, method, token, NULL, generic_context);
8500 if (!cmethod || mono_loader_get_last_error ())
8503 if (cfg->generic_sharing_context && mono_method_check_context_used (cmethod))
8504 GENERIC_SHARING_FAILURE (CEE_JMP);
8506 if (mono_security_cas_enabled ())
8507 CHECK_CFG_EXCEPTION;
8509 emit_instrumentation_call (cfg, mono_profiler_method_leave);
8511 if (ARCH_HAVE_OP_TAIL_CALL) {
8512 MonoMethodSignature *fsig = mono_method_signature (cmethod);
8515 /* Handle tail calls similarly to calls */
8516 n = fsig->param_count + fsig->hasthis;
8520 MONO_INST_NEW_CALL (cfg, call, OP_TAILCALL);
8521 call->method = cmethod;
8522 call->tail_call = TRUE;
8523 call->signature = mono_method_signature (cmethod);
8524 call->args = mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*) * n);
8525 call->inst.inst_p0 = cmethod;
8526 for (i = 0; i < n; ++i)
8527 EMIT_NEW_ARGLOAD (cfg, call->args [i], i);
8529 mono_arch_emit_call (cfg, call);
8530 cfg->param_area = MAX(cfg->param_area, call->stack_usage);
8531 MONO_ADD_INS (bblock, (MonoInst*)call);
8533 for (i = 0; i < num_args; ++i)
8534 /* Prevent arguments from being optimized away */
8535 arg_array [i]->flags |= MONO_INST_VOLATILE;
8537 MONO_INST_NEW_CALL (cfg, call, OP_JMP);
8538 ins = (MonoInst*)call;
8539 ins->inst_p0 = cmethod;
8540 MONO_ADD_INS (bblock, ins);
8544 start_new_bblock = 1;
8549 case CEE_CALLVIRT: {
8550 MonoInst *addr = NULL;
8551 MonoMethodSignature *fsig = NULL;
8553 int virtual = *ip == CEE_CALLVIRT;
8554 int calli = *ip == CEE_CALLI;
8555 gboolean pass_imt_from_rgctx = FALSE;
8556 MonoInst *imt_arg = NULL;
8557 MonoInst *keep_this_alive = NULL;
8558 gboolean pass_vtable = FALSE;
8559 gboolean pass_mrgctx = FALSE;
8560 MonoInst *vtable_arg = NULL;
8561 gboolean check_this = FALSE;
8562 gboolean supported_tail_call = FALSE;
8563 gboolean tail_call = FALSE;
8564 gboolean need_seq_point = FALSE;
8565 guint32 call_opcode = *ip;
8566 gboolean emit_widen = TRUE;
8567 gboolean push_res = TRUE;
8568 gboolean skip_ret = FALSE;
8569 gboolean delegate_invoke = FALSE;
8572 token = read32 (ip + 1);
8577 //GSHAREDVT_FAILURE (*ip);
8582 fsig = mini_get_signature (method, token, generic_context);
8583 n = fsig->param_count + fsig->hasthis;
8585 if (method->dynamic && fsig->pinvoke) {
8589 * This is a call through a function pointer using a pinvoke
8590 * signature. Have to create a wrapper and call that instead.
8591 * FIXME: This is very slow, need to create a wrapper at JIT time
8592 * instead based on the signature.
8594 EMIT_NEW_IMAGECONST (cfg, args [0], method->klass->image);
8595 EMIT_NEW_PCONST (cfg, args [1], fsig);
8597 addr = mono_emit_jit_icall (cfg, mono_get_native_calli_wrapper, args);
8600 MonoMethod *cil_method;
8602 cmethod = mini_get_method (cfg, method, token, NULL, generic_context);
8603 cil_method = cmethod;
8605 if (constrained_call) {
8606 if (method->wrapper_type != MONO_WRAPPER_NONE) {
8607 if (cfg->verbose_level > 2)
8608 printf ("DM Constrained call to %s\n", mono_type_get_full_name (constrained_call));
8609 if (!((constrained_call->byval_arg.type == MONO_TYPE_VAR ||
8610 constrained_call->byval_arg.type == MONO_TYPE_MVAR) &&
8611 cfg->generic_sharing_context)) {
8612 cmethod = mono_get_method_constrained_with_method (image, cil_method, constrained_call, generic_context, &cfg->error);
8616 if (cfg->verbose_level > 2)
8617 printf ("Constrained call to %s\n", mono_type_get_full_name (constrained_call));
8619 if ((constrained_call->byval_arg.type == MONO_TYPE_VAR || constrained_call->byval_arg.type == MONO_TYPE_MVAR) && cfg->generic_sharing_context) {
8621 * This is needed since get_method_constrained can't find
8622 * the method in klass representing a type var.
8623 * The type var is guaranteed to be a reference type in this
8626 if (!mini_is_gsharedvt_klass (cfg, constrained_call))
8627 g_assert (!cmethod->klass->valuetype);
8629 cmethod = mono_get_method_constrained_checked (image, token, constrained_call, generic_context, &cil_method, &cfg->error);
8635 if (!cmethod || mono_loader_get_last_error ())
8637 if (!dont_verify && !cfg->skip_visibility) {
8638 MonoMethod *target_method = cil_method;
8639 if (method->is_inflated) {
8640 target_method = mini_get_method_allow_open (method, token, NULL, &(mono_method_get_generic_container (method_definition)->context));
8642 if (!mono_method_can_access_method (method_definition, target_method) &&
8643 !mono_method_can_access_method (method, cil_method))
8644 METHOD_ACCESS_FAILURE (method, cil_method);
8647 if (mono_security_core_clr_enabled ())
8648 ensure_method_is_allowed_to_call_method (cfg, method, cil_method, bblock, ip);
8650 if (!virtual && (cmethod->flags & METHOD_ATTRIBUTE_ABSTRACT))
8651 /* MS.NET seems to silently convert this to a callvirt */
8656 * MS.NET accepts non virtual calls to virtual final methods of transparent proxy classes and
8657 * converts to a callvirt.
8659 * tests/bug-515884.il is an example of this behavior
8661 const int test_flags = METHOD_ATTRIBUTE_VIRTUAL | METHOD_ATTRIBUTE_FINAL | METHOD_ATTRIBUTE_STATIC;
8662 const int expected_flags = METHOD_ATTRIBUTE_VIRTUAL | METHOD_ATTRIBUTE_FINAL;
8663 if (!virtual && mono_class_is_marshalbyref (cmethod->klass) && (cmethod->flags & test_flags) == expected_flags && cfg->method->wrapper_type == MONO_WRAPPER_NONE)
8667 if (!cmethod->klass->inited)
8668 if (!mono_class_init (cmethod->klass))
8669 TYPE_LOAD_ERROR (cmethod->klass);
8671 if (cmethod->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL &&
8672 mini_class_is_system_array (cmethod->klass)) {
8673 array_rank = cmethod->klass->rank;
8674 fsig = mono_method_signature (cmethod);
8676 fsig = mono_method_signature (cmethod);
8681 if (fsig->pinvoke) {
8682 MonoMethod *wrapper = mono_marshal_get_native_wrapper (cmethod,
8683 check_for_pending_exc, cfg->compile_aot);
8684 fsig = mono_method_signature (wrapper);
8685 } else if (constrained_call) {
8686 fsig = mono_method_signature (cmethod);
8688 fsig = mono_method_get_signature_checked (cmethod, image, token, generic_context, &cfg->error);
8693 mono_save_token_info (cfg, image, token, cil_method);
8695 if (!(seq_point_locs && mono_bitset_test_fast (seq_point_locs, ip + 5 - header->code)))
8696 need_seq_point = TRUE;
8698 n = fsig->param_count + fsig->hasthis;
8700 /* Don't support calls made using type arguments for now */
8702 if (cfg->gsharedvt) {
8703 if (mini_is_gsharedvt_signature (cfg, fsig))
8704 GSHAREDVT_FAILURE (*ip);
8708 if (mono_security_cas_enabled ()) {
8709 if (check_linkdemand (cfg, method, cmethod))
8710 INLINE_FAILURE ("linkdemand");
8711 CHECK_CFG_EXCEPTION;
8714 if (cmethod->string_ctor && method->wrapper_type != MONO_WRAPPER_RUNTIME_INVOKE)
8715 g_assert_not_reached ();
8718 if (!cfg->generic_sharing_context && cmethod && cmethod->klass->generic_container)
8721 if (!cfg->generic_sharing_context && cmethod)
8722 g_assert (!mono_method_check_context_used (cmethod));
8726 //g_assert (!virtual || fsig->hasthis);
8730 if (constrained_call) {
8731 if (mini_is_gsharedvt_klass (cfg, constrained_call)) {
8733 * Constrained calls need to behave differently at runtime dependending on whenever the receiver is instantiated as ref type or as a vtype.
8735 if ((cmethod->klass != mono_defaults.object_class) && constrained_call->valuetype && cmethod->klass->valuetype) {
8736 /* The 'Own method' case below */
8737 } else if (cmethod->klass->image != mono_defaults.corlib && !(cmethod->klass->flags & TYPE_ATTRIBUTE_INTERFACE) && !cmethod->klass->valuetype) {
8738 /* 'The type parameter is instantiated as a reference type' case below. */
8739 } else if (((cmethod->klass == mono_defaults.object_class) || (cmethod->klass->flags & TYPE_ATTRIBUTE_INTERFACE) || (!cmethod->klass->valuetype && cmethod->klass->image != mono_defaults.corlib)) &&
8740 (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)) &&
8741 (fsig->param_count == 0 || (!fsig->hasthis && fsig->param_count == 1) || (fsig->param_count == 1 && (MONO_TYPE_IS_REFERENCE (fsig->params [0]) || mini_is_gsharedvt_type (cfg, fsig->params [0]))))) {
8742 MonoInst *args [16];
8745 * This case handles calls to
8746 * - object:ToString()/Equals()/GetHashCode(),
8747 * - System.IComparable<T>:CompareTo()
8748 * - System.IEquatable<T>:Equals ()
8749 * plus some simple interface calls enough to support AsyncTaskMethodBuilder.
8753 if (mono_method_check_context_used (cmethod))
8754 args [1] = emit_get_rgctx_method (cfg, mono_method_check_context_used (cmethod), cmethod, MONO_RGCTX_INFO_METHOD);
8756 EMIT_NEW_METHODCONST (cfg, args [1], cmethod);
8757 args [2] = emit_get_rgctx_klass (cfg, mono_class_check_context_used (constrained_call), constrained_call, MONO_RGCTX_INFO_KLASS);
8759 /* !fsig->hasthis is for the wrapper for the Object.GetType () icall */
8760 if (fsig->hasthis && fsig->param_count) {
8761 /* Pass the arguments using a localloc-ed array using the format expected by runtime_invoke () */
8762 MONO_INST_NEW (cfg, ins, OP_LOCALLOC_IMM);
8763 ins->dreg = alloc_preg (cfg);
8764 ins->inst_imm = fsig->param_count * sizeof (mgreg_t);
8765 MONO_ADD_INS (cfg->cbb, ins);
8768 if (mini_is_gsharedvt_type (cfg, fsig->params [0])) {
8771 args [3] = emit_get_gsharedvt_info_klass (cfg, mono_class_from_mono_type (fsig->params [0]), MONO_RGCTX_INFO_CLASS_BOX_TYPE);
8773 EMIT_NEW_VARLOADA_VREG (cfg, ins, sp [1]->dreg, fsig->params [0]);
8774 addr_reg = ins->dreg;
8775 EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, args [4]->dreg, 0, addr_reg);
8777 EMIT_NEW_ICONST (cfg, args [3], 0);
8778 EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, args [4]->dreg, 0, sp [1]->dreg);
8781 EMIT_NEW_ICONST (cfg, args [3], 0);
8782 EMIT_NEW_ICONST (cfg, args [4], 0);
8784 ins = mono_emit_jit_icall (cfg, mono_gsharedvt_constrained_call, args);
8787 if (mini_is_gsharedvt_type (cfg, fsig->ret)) {
8788 ins = handle_unbox_gsharedvt (cfg, mono_class_from_mono_type (fsig->ret), ins, &bblock);
8789 } else if (MONO_TYPE_IS_PRIMITIVE (fsig->ret) || MONO_TYPE_ISSTRUCT (fsig->ret)) {
8793 NEW_BIALU_IMM (cfg, add, OP_ADD_IMM, alloc_dreg (cfg, STACK_MP), ins->dreg, sizeof (MonoObject));
8794 MONO_ADD_INS (cfg->cbb, add);
8796 NEW_LOAD_MEMBASE_TYPE (cfg, ins, fsig->ret, add->dreg, 0);
8797 MONO_ADD_INS (cfg->cbb, ins);
8798 /* ins represents the call result */
8803 GSHAREDVT_FAILURE (*ip);
8807 * We have the `constrained.' prefix opcode.
8809 if (constrained_call->valuetype && (cmethod->klass == mono_defaults.object_class || cmethod->klass == mono_defaults.enum_class->parent || cmethod->klass == mono_defaults.enum_class)) {
8811 * The type parameter is instantiated as a valuetype,
8812 * but that type doesn't override the method we're
8813 * calling, so we need to box `this'.
8815 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &constrained_call->byval_arg, sp [0]->dreg, 0);
8816 ins->klass = constrained_call;
8817 sp [0] = handle_box (cfg, ins, constrained_call, mono_class_check_context_used (constrained_call), &bblock);
8818 CHECK_CFG_EXCEPTION;
8819 } else if (!constrained_call->valuetype) {
8820 int dreg = alloc_ireg_ref (cfg);
8823 * The type parameter is instantiated as a reference
8824 * type. We have a managed pointer on the stack, so
8825 * we need to dereference it here.
8827 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, dreg, sp [0]->dreg, 0);
8828 ins->type = STACK_OBJ;
8831 if (cmethod->klass->valuetype) {
8834 /* Interface method */
8837 mono_class_setup_vtable (constrained_call);
8838 CHECK_TYPELOAD (constrained_call);
8839 ioffset = mono_class_interface_offset (constrained_call, cmethod->klass);
8841 TYPE_LOAD_ERROR (constrained_call);
8842 slot = mono_method_get_vtable_slot (cmethod);
8844 TYPE_LOAD_ERROR (cmethod->klass);
8845 cmethod = constrained_call->vtable [ioffset + slot];
8847 if (cmethod->klass == mono_defaults.enum_class) {
8848 /* Enum implements some interfaces, so treat this as the first case */
8849 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &constrained_call->byval_arg, sp [0]->dreg, 0);
8850 ins->klass = constrained_call;
8851 sp [0] = handle_box (cfg, ins, constrained_call, mono_class_check_context_used (constrained_call), &bblock);
8852 CHECK_CFG_EXCEPTION;
8857 constrained_call = NULL;
8860 if (!calli && check_call_signature (cfg, fsig, sp))
8863 #ifdef MONO_ARCH_HAVE_CREATE_DELEGATE_TRAMPOLINE
8864 if (cmethod && (cmethod->klass->parent == mono_defaults.multicastdelegate_class) && !strcmp (cmethod->name, "Invoke"))
8865 delegate_invoke = TRUE;
8868 if (cmethod && (cfg->opt & MONO_OPT_INTRINS) && (ins = mini_emit_inst_for_sharable_method (cfg, cmethod, fsig, sp))) {
8870 if (!MONO_TYPE_IS_VOID (fsig->ret)) {
8871 type_to_eval_stack_type ((cfg), fsig->ret, ins);
8879 * If the callee is a shared method, then its static cctor
8880 * might not get called after the call was patched.
8882 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)) {
8883 emit_generic_class_init (cfg, cmethod->klass);
8884 CHECK_TYPELOAD (cmethod->klass);
8888 check_method_sharing (cfg, cmethod, &pass_vtable, &pass_mrgctx);
8890 if (cfg->generic_sharing_context && cmethod) {
8891 MonoGenericContext *cmethod_context = mono_method_get_context (cmethod);
8893 context_used = mini_method_check_context_used (cfg, cmethod);
8895 if (context_used && (cmethod->klass->flags & TYPE_ATTRIBUTE_INTERFACE)) {
8896 /* Generic method interface
8897 calls are resolved via a
8898 helper function and don't
8900 if (!cmethod_context || !cmethod_context->method_inst)
8901 pass_imt_from_rgctx = TRUE;
8905 * If a shared method calls another
8906 * shared method then the caller must
8907 * have a generic sharing context
8908 * because the magic trampoline
8909 * requires it. FIXME: We shouldn't
8910 * have to force the vtable/mrgctx
8911 * variable here. Instead there
8912 * should be a flag in the cfg to
8913 * request a generic sharing context.
8916 ((method->flags & METHOD_ATTRIBUTE_STATIC) || method->klass->valuetype))
8917 mono_get_vtable_var (cfg);
8922 vtable_arg = emit_get_rgctx_klass (cfg, context_used, cmethod->klass, MONO_RGCTX_INFO_VTABLE);
8924 MonoVTable *vtable = mono_class_vtable (cfg->domain, cmethod->klass);
8926 CHECK_TYPELOAD (cmethod->klass);
8927 EMIT_NEW_VTABLECONST (cfg, vtable_arg, vtable);
8932 g_assert (!vtable_arg);
8934 if (!cfg->compile_aot) {
8936 * emit_get_rgctx_method () calls mono_class_vtable () so check
8937 * for type load errors before.
8939 mono_class_setup_vtable (cmethod->klass);
8940 CHECK_TYPELOAD (cmethod->klass);
8943 vtable_arg = emit_get_rgctx_method (cfg, context_used, cmethod, MONO_RGCTX_INFO_METHOD_RGCTX);
8945 /* !marshalbyref is needed to properly handle generic methods + remoting */
8946 if ((!(cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL) ||
8947 MONO_METHOD_IS_FINAL (cmethod)) &&
8948 !mono_class_is_marshalbyref (cmethod->klass)) {
8955 if (pass_imt_from_rgctx) {
8956 g_assert (!pass_vtable);
8959 imt_arg = emit_get_rgctx_method (cfg, context_used,
8960 cmethod, MONO_RGCTX_INFO_METHOD);
8964 MONO_EMIT_NEW_CHECK_THIS (cfg, sp [0]->dreg);
8966 /* Calling virtual generic methods */
8967 if (cmethod && virtual &&
8968 (cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL) &&
8969 !(MONO_METHOD_IS_FINAL (cmethod) &&
8970 cmethod->wrapper_type != MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK) &&
8971 fsig->generic_param_count &&
8972 !(cfg->gsharedvt && mini_is_gsharedvt_signature (cfg, fsig))) {
8973 MonoInst *this_temp, *this_arg_temp, *store;
8974 MonoInst *iargs [4];
8975 gboolean use_imt = FALSE;
8977 g_assert (fsig->is_inflated);
8979 /* Prevent inlining of methods that contain indirect calls */
8980 INLINE_FAILURE ("virtual generic call");
8982 if (cfg->gsharedvt && mini_is_gsharedvt_signature (cfg, fsig))
8983 GSHAREDVT_FAILURE (*ip);
8985 #if MONO_ARCH_HAVE_GENERALIZED_IMT_THUNK && defined(MONO_ARCH_GSHARED_SUPPORTED)
8986 if (cmethod->wrapper_type == MONO_WRAPPER_NONE && mono_use_imt)
8991 g_assert (!imt_arg);
8993 g_assert (cmethod->is_inflated);
8994 imt_arg = emit_get_rgctx_method (cfg, context_used,
8995 cmethod, MONO_RGCTX_INFO_METHOD);
8996 ins = mono_emit_method_call_full (cfg, cmethod, fsig, FALSE, sp, sp [0], imt_arg, NULL);
8998 this_temp = mono_compile_create_var (cfg, type_from_stack_type (sp [0]), OP_LOCAL);
8999 NEW_TEMPSTORE (cfg, store, this_temp->inst_c0, sp [0]);
9000 MONO_ADD_INS (bblock, store);
9002 /* FIXME: This should be a managed pointer */
9003 this_arg_temp = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
9005 EMIT_NEW_TEMPLOAD (cfg, iargs [0], this_temp->inst_c0);
9006 iargs [1] = emit_get_rgctx_method (cfg, context_used,
9007 cmethod, MONO_RGCTX_INFO_METHOD);
9008 EMIT_NEW_TEMPLOADA (cfg, iargs [2], this_arg_temp->inst_c0);
9009 addr = mono_emit_jit_icall (cfg,
9010 mono_helper_compile_generic_method, iargs);
9012 EMIT_NEW_TEMPLOAD (cfg, sp [0], this_arg_temp->inst_c0);
9014 ins = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr, NULL, NULL);
9021 * Implement a workaround for the inherent races involved in locking:
9027 * If a thread abort happens between the call to Monitor.Enter () and the start of the
9028 * try block, the Exit () won't be executed, see:
9029 * http://www.bluebytesoftware.com/blog/2007/01/30/MonitorEnterThreadAbortsAndOrphanedLocks.aspx
9030 * To work around this, we extend such try blocks to include the last x bytes
9031 * of the Monitor.Enter () call.
9033 if (cmethod && cmethod->klass == mono_defaults.monitor_class && !strcmp (cmethod->name, "Enter") && mono_method_signature (cmethod)->param_count == 1) {
9034 MonoBasicBlock *tbb;
9036 GET_BBLOCK (cfg, tbb, ip + 5);
9038 * Only extend try blocks with a finally, to avoid catching exceptions thrown
9039 * from Monitor.Enter like ArgumentNullException.
9041 if (tbb->try_start && MONO_REGION_FLAGS(tbb->region) == MONO_EXCEPTION_CLAUSE_FINALLY) {
9042 /* Mark this bblock as needing to be extended */
9043 tbb->extend_try_block = TRUE;
9047 /* Conversion to a JIT intrinsic */
9048 if (cmethod && (cfg->opt & MONO_OPT_INTRINS) && (ins = mini_emit_inst_for_method (cfg, cmethod, fsig, sp))) {
9050 if (!MONO_TYPE_IS_VOID (fsig->ret)) {
9051 type_to_eval_stack_type ((cfg), fsig->ret, ins);
9058 if (cmethod && (cfg->opt & MONO_OPT_INLINE) &&
9059 (!virtual || !(cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL) || MONO_METHOD_IS_FINAL (cmethod)) &&
9060 mono_method_check_inlining (cfg, cmethod)) {
9062 gboolean always = FALSE;
9064 if ((cmethod->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
9065 (cmethod->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)) {
9066 /* Prevent inlining of methods that call wrappers */
9067 INLINE_FAILURE ("wrapper call");
9068 cmethod = mono_marshal_get_native_wrapper (cmethod, check_for_pending_exc, FALSE);
9072 costs = inline_method (cfg, cmethod, fsig, sp, ip, cfg->real_offset, always, &bblock);
9074 cfg->real_offset += 5;
9076 if (!MONO_TYPE_IS_VOID (fsig->ret)) {
9077 /* *sp is already set by inline_method */
9082 inline_costs += costs;
9088 /* Tail recursion elimination */
9089 if ((cfg->opt & MONO_OPT_TAILC) && call_opcode == CEE_CALL && cmethod == method && ip [5] == CEE_RET && !vtable_arg) {
9090 gboolean has_vtargs = FALSE;
9093 /* Prevent inlining of methods with tail calls (the call stack would be altered) */
9094 INLINE_FAILURE ("tail call");
9096 /* keep it simple */
9097 for (i = fsig->param_count - 1; i >= 0; i--) {
9098 if (MONO_TYPE_ISSTRUCT (mono_method_signature (cmethod)->params [i]))
9103 for (i = 0; i < n; ++i)
9104 EMIT_NEW_ARGSTORE (cfg, ins, i, sp [i]);
9105 MONO_INST_NEW (cfg, ins, OP_BR);
9106 MONO_ADD_INS (bblock, ins);
9107 tblock = start_bblock->out_bb [0];
9108 link_bblock (cfg, bblock, tblock);
9109 ins->inst_target_bb = tblock;
9110 start_new_bblock = 1;
9112 /* skip the CEE_RET, too */
9113 if (ip_in_bb (cfg, bblock, ip + 5))
9120 inline_costs += 10 * num_calls++;
9123 * Making generic calls out of gsharedvt methods.
9124 * This needs to be used for all generic calls, not just ones with a gsharedvt signature, to avoid
9125 * patching gshared method addresses into a gsharedvt method.
9127 if (cmethod && cfg->gsharedvt && (mini_is_gsharedvt_signature (cfg, fsig) || cmethod->is_inflated || cmethod->klass->generic_class)) {
9128 MonoRgctxInfoType info_type;
9131 //if (cmethod->klass->flags & TYPE_ATTRIBUTE_INTERFACE)
9132 //GSHAREDVT_FAILURE (*ip);
9133 // disable for possible remoting calls
9134 if (fsig->hasthis && (mono_class_is_marshalbyref (method->klass) || method->klass == mono_defaults.object_class))
9135 GSHAREDVT_FAILURE (*ip);
9136 if (fsig->generic_param_count) {
9137 /* virtual generic call */
9138 g_assert (mono_use_imt);
9139 g_assert (!imt_arg);
9140 /* Same as the virtual generic case above */
9141 imt_arg = emit_get_rgctx_method (cfg, context_used,
9142 cmethod, MONO_RGCTX_INFO_METHOD);
9143 /* This is not needed, as the trampoline code will pass one, and it might be passed in the same reg as the imt arg */
9145 } else if ((cmethod->klass->flags & TYPE_ATTRIBUTE_INTERFACE) && !imt_arg) {
9146 /* This can happen when we call a fully instantiated iface method */
9147 imt_arg = emit_get_rgctx_method (cfg, context_used,
9148 cmethod, MONO_RGCTX_INFO_METHOD);
9153 if (cmethod->klass->rank && cmethod->klass->byval_arg.type != MONO_TYPE_SZARRAY)
9154 /* test_0_multi_dim_arrays () in gshared.cs */
9155 GSHAREDVT_FAILURE (*ip);
9157 if ((cmethod->klass->parent == mono_defaults.multicastdelegate_class) && (!strcmp (cmethod->name, "Invoke")))
9158 keep_this_alive = sp [0];
9160 if (virtual && (cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL))
9161 info_type = MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE_VIRT;
9163 info_type = MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE;
9164 addr = emit_get_rgctx_gsharedvt_call (cfg, context_used, fsig, cmethod, info_type);
9166 ins = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr, imt_arg, vtable_arg);
9168 } else if (calli && cfg->gsharedvt && mini_is_gsharedvt_signature (cfg, fsig)) {
9170 * We pass the address to the gsharedvt trampoline in the rgctx reg
9172 MonoInst *callee = addr;
9174 if (method->wrapper_type != MONO_WRAPPER_DELEGATE_INVOKE)
9176 GSHAREDVT_FAILURE (*ip);
9178 addr = emit_get_rgctx_sig (cfg, context_used,
9179 fsig, MONO_RGCTX_INFO_SIG_GSHAREDVT_OUT_TRAMPOLINE_CALLI);
9180 ins = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr, NULL, callee);
9184 /* Generic sharing */
9187 * Use this if the callee is gsharedvt sharable too, since
9188 * at runtime we might find an instantiation so the call cannot
9189 * be patched (the 'no_patch' code path in mini-trampolines.c).
9191 if (context_used && !imt_arg && !array_rank && !delegate_invoke &&
9192 (!mono_method_is_generic_sharable_full (cmethod, TRUE, FALSE, FALSE) ||
9193 !mono_class_generic_sharing_enabled (cmethod->klass)) &&
9194 (!virtual || MONO_METHOD_IS_FINAL (cmethod) ||
9195 !(cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL))) {
9196 INLINE_FAILURE ("gshared");
9198 g_assert (cfg->generic_sharing_context && cmethod);
9202 * We are compiling a call to a
9203 * generic method from shared code,
9204 * which means that we have to look up
9205 * the method in the rgctx and do an
9209 MONO_EMIT_NEW_CHECK_THIS (cfg, sp [0]->dreg);
9211 addr = emit_get_rgctx_method (cfg, context_used, cmethod, MONO_RGCTX_INFO_GENERIC_METHOD_CODE);
9212 ins = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr, imt_arg, vtable_arg);
9216 /* Indirect calls */
9218 if (call_opcode == CEE_CALL)
9219 g_assert (context_used);
9220 else if (call_opcode == CEE_CALLI)
9221 g_assert (!vtable_arg);
9223 /* FIXME: what the hell is this??? */
9224 g_assert (cmethod->flags & METHOD_ATTRIBUTE_FINAL ||
9225 !(cmethod->flags & METHOD_ATTRIBUTE_FINAL));
9227 /* Prevent inlining of methods with indirect calls */
9228 INLINE_FAILURE ("indirect call");
9230 if (addr->opcode == OP_PCONST || addr->opcode == OP_AOTCONST || addr->opcode == OP_GOT_ENTRY) {
9235 * Instead of emitting an indirect call, emit a direct call
9236 * with the contents of the aotconst as the patch info.
9238 if (addr->opcode == OP_PCONST || addr->opcode == OP_AOTCONST) {
9239 info_type = addr->inst_c1;
9240 info_data = addr->inst_p0;
9242 info_type = addr->inst_right->inst_c1;
9243 info_data = addr->inst_right->inst_left;
9246 if (info_type == MONO_PATCH_INFO_ICALL_ADDR || info_type == MONO_PATCH_INFO_JIT_ICALL_ADDR) {
9247 ins = (MonoInst*)mono_emit_abs_call (cfg, info_type, info_data, fsig, sp);
9252 ins = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr, imt_arg, vtable_arg);
9260 if (strcmp (cmethod->name, "Set") == 0) { /* array Set */
9261 MonoInst *val = sp [fsig->param_count];
9263 if (val->type == STACK_OBJ) {
9264 MonoInst *iargs [2];
9269 mono_emit_jit_icall (cfg, mono_helper_stelem_ref_check, iargs);
9272 addr = mini_emit_ldelema_ins (cfg, cmethod, sp, ip, TRUE);
9273 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, fsig->params [fsig->param_count - 1], addr->dreg, 0, val->dreg);
9274 if (cfg->gen_write_barriers && val->type == STACK_OBJ && !(val->opcode == OP_PCONST && val->inst_c0 == 0))
9275 emit_write_barrier (cfg, addr, val);
9276 } else if (strcmp (cmethod->name, "Get") == 0) { /* array Get */
9277 addr = mini_emit_ldelema_ins (cfg, cmethod, sp, ip, FALSE);
9279 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, fsig->ret, addr->dreg, 0);
9280 } else if (strcmp (cmethod->name, "Address") == 0) { /* array Address */
9281 if (!cmethod->klass->element_class->valuetype && !readonly)
9282 mini_emit_check_array_type (cfg, sp [0], cmethod->klass);
9283 CHECK_TYPELOAD (cmethod->klass);
9286 addr = mini_emit_ldelema_ins (cfg, cmethod, sp, ip, FALSE);
9289 g_assert_not_reached ();
9296 ins = mini_redirect_call (cfg, cmethod, fsig, sp, virtual ? sp [0] : NULL);
9300 /* Tail prefix / tail call optimization */
9302 /* FIXME: Enabling TAILC breaks some inlining/stack trace/etc tests */
9303 /* FIXME: runtime generic context pointer for jumps? */
9304 /* FIXME: handle this for generic sharing eventually */
9305 if (cmethod && (ins_flag & MONO_INST_TAILCALL) &&
9306 !vtable_arg && !cfg->generic_sharing_context && is_supported_tail_call (cfg, method, cmethod, fsig, call_opcode))
9307 supported_tail_call = TRUE;
9309 if (supported_tail_call) {
9312 /* Prevent inlining of methods with tail calls (the call stack would be altered) */
9313 INLINE_FAILURE ("tail call");
9315 //printf ("HIT: %s -> %s\n", mono_method_full_name (cfg->method, TRUE), mono_method_full_name (cmethod, TRUE));
9317 if (ARCH_HAVE_OP_TAIL_CALL) {
9318 /* Handle tail calls similarly to normal calls */
9321 emit_instrumentation_call (cfg, mono_profiler_method_leave);
9323 MONO_INST_NEW_CALL (cfg, call, OP_JMP);
9324 call->tail_call = TRUE;
9325 call->method = cmethod;
9326 call->signature = mono_method_signature (cmethod);
9329 * We implement tail calls by storing the actual arguments into the
9330 * argument variables, then emitting a CEE_JMP.
9332 for (i = 0; i < n; ++i) {
9333 /* Prevent argument from being register allocated */
9334 arg_array [i]->flags |= MONO_INST_VOLATILE;
9335 EMIT_NEW_ARGSTORE (cfg, ins, i, sp [i]);
9337 ins = (MonoInst*)call;
9338 ins->inst_p0 = cmethod;
9339 ins->inst_p1 = arg_array [0];
9340 MONO_ADD_INS (bblock, ins);
9341 link_bblock (cfg, bblock, end_bblock);
9342 start_new_bblock = 1;
9344 // FIXME: Eliminate unreachable epilogs
9347 * OP_TAILCALL has no return value, so skip the CEE_RET if it is
9348 * only reachable from this call.
9350 GET_BBLOCK (cfg, tblock, ip + 5);
9351 if (tblock == bblock || tblock->in_count == 0)
9360 * Synchronized wrappers.
9361 * Its hard to determine where to replace a method with its synchronized
9362 * wrapper without causing an infinite recursion. The current solution is
9363 * to add the synchronized wrapper in the trampolines, and to
9364 * change the called method to a dummy wrapper, and resolve that wrapper
9365 * to the real method in mono_jit_compile_method ().
9367 if (cfg->method->wrapper_type == MONO_WRAPPER_SYNCHRONIZED) {
9368 MonoMethod *orig = mono_marshal_method_from_wrapper (cfg->method);
9369 if (cmethod == orig || (cmethod->is_inflated && mono_method_get_declaring_generic_method (cmethod) == orig))
9370 cmethod = mono_marshal_get_synchronized_inner_wrapper (cmethod);
9374 INLINE_FAILURE ("call");
9375 ins = mono_emit_method_call_full (cfg, cmethod, fsig, tail_call, sp, virtual ? sp [0] : NULL,
9376 imt_arg, vtable_arg);
9379 link_bblock (cfg, bblock, end_bblock);
9380 start_new_bblock = 1;
9382 // FIXME: Eliminate unreachable epilogs
9385 * OP_TAILCALL has no return value, so skip the CEE_RET if it is
9386 * only reachable from this call.
9388 GET_BBLOCK (cfg, tblock, ip + 5);
9389 if (tblock == bblock || tblock->in_count == 0)
9396 /* End of call, INS should contain the result of the call, if any */
9398 if (push_res && !MONO_TYPE_IS_VOID (fsig->ret)) {
9401 *sp++ = mono_emit_widen_call_res (cfg, ins, fsig);
9406 if (keep_this_alive) {
9407 MonoInst *dummy_use;
9409 /* See mono_emit_method_call_full () */
9410 EMIT_NEW_DUMMY_USE (cfg, dummy_use, keep_this_alive);
9413 CHECK_CFG_EXCEPTION;
9417 g_assert (*ip == CEE_RET);
9421 constrained_call = NULL;
9423 emit_seq_point (cfg, method, ip, FALSE, TRUE);
9427 if (cfg->method != method) {
9428 /* return from inlined method */
9430 * If in_count == 0, that means the ret is unreachable due to
9431 * being preceeded by a throw. In that case, inline_method () will
9432 * handle setting the return value
9433 * (test case: test_0_inline_throw ()).
9435 if (return_var && cfg->cbb->in_count) {
9436 MonoType *ret_type = mono_method_signature (method)->ret;
9442 if ((method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD || method->wrapper_type == MONO_WRAPPER_NONE) && target_type_is_incompatible (cfg, ret_type, *sp))
9445 //g_assert (returnvar != -1);
9446 EMIT_NEW_TEMPSTORE (cfg, store, return_var->inst_c0, *sp);
9447 cfg->ret_var_set = TRUE;
9450 emit_instrumentation_call (cfg, mono_profiler_method_leave);
9452 if (cfg->lmf_var && cfg->cbb->in_count)
9456 MonoType *ret_type = mini_replace_type (mono_method_signature (method)->ret);
9458 if (seq_points && !sym_seq_points) {
9460 * Place a seq point here too even through the IL stack is not
9461 * empty, so a step over on
9464 * will work correctly.
9466 NEW_SEQ_POINT (cfg, ins, ip - header->code, TRUE);
9467 MONO_ADD_INS (cfg->cbb, ins);
9470 g_assert (!return_var);
9474 if ((method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD || method->wrapper_type == MONO_WRAPPER_NONE) && target_type_is_incompatible (cfg, ret_type, *sp))
9477 if (mini_type_to_stind (cfg, ret_type) == CEE_STOBJ) {
9480 if (!cfg->vret_addr) {
9483 EMIT_NEW_VARSTORE (cfg, ins, cfg->ret, ret_type, (*sp));
9485 EMIT_NEW_RETLOADA (cfg, ret_addr);
9487 EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STOREV_MEMBASE, ret_addr->dreg, 0, (*sp)->dreg);
9488 ins->klass = mono_class_from_mono_type (ret_type);
9491 #ifdef MONO_ARCH_SOFT_FLOAT_FALLBACK
9492 if (COMPILE_SOFT_FLOAT (cfg) && !ret_type->byref && ret_type->type == MONO_TYPE_R4) {
9493 MonoInst *iargs [1];
9497 conv = mono_emit_jit_icall (cfg, mono_fload_r4_arg, iargs);
9498 mono_arch_emit_setret (cfg, method, conv);
9500 mono_arch_emit_setret (cfg, method, *sp);
9503 mono_arch_emit_setret (cfg, method, *sp);
9508 if (sp != stack_start)
9510 MONO_INST_NEW (cfg, ins, OP_BR);
9512 ins->inst_target_bb = end_bblock;
9513 MONO_ADD_INS (bblock, ins);
9514 link_bblock (cfg, bblock, end_bblock);
9515 start_new_bblock = 1;
9519 MONO_INST_NEW (cfg, ins, OP_BR);
9521 target = ip + 1 + (signed char)(*ip);
9523 GET_BBLOCK (cfg, tblock, target);
9524 link_bblock (cfg, bblock, tblock);
9525 ins->inst_target_bb = tblock;
9526 if (sp != stack_start) {
9527 handle_stack_args (cfg, stack_start, sp - stack_start);
9529 CHECK_UNVERIFIABLE (cfg);
9531 MONO_ADD_INS (bblock, ins);
9532 start_new_bblock = 1;
9533 inline_costs += BRANCH_COST;
9547 MONO_INST_NEW (cfg, ins, *ip + BIG_BRANCH_OFFSET);
9549 target = ip + 1 + *(signed char*)ip;
9555 inline_costs += BRANCH_COST;
9559 MONO_INST_NEW (cfg, ins, OP_BR);
9562 target = ip + 4 + (gint32)read32(ip);
9564 GET_BBLOCK (cfg, tblock, target);
9565 link_bblock (cfg, bblock, tblock);
9566 ins->inst_target_bb = tblock;
9567 if (sp != stack_start) {
9568 handle_stack_args (cfg, stack_start, sp - stack_start);
9570 CHECK_UNVERIFIABLE (cfg);
9573 MONO_ADD_INS (bblock, ins);
9575 start_new_bblock = 1;
9576 inline_costs += BRANCH_COST;
9583 gboolean is_short = ((*ip) == CEE_BRFALSE_S) || ((*ip) == CEE_BRTRUE_S);
9584 gboolean is_true = ((*ip) == CEE_BRTRUE_S) || ((*ip) == CEE_BRTRUE);
9585 guint32 opsize = is_short ? 1 : 4;
9587 CHECK_OPSIZE (opsize);
9589 if (sp [-1]->type == STACK_VTYPE || sp [-1]->type == STACK_R8)
9592 target = ip + opsize + (is_short ? *(signed char*)ip : (gint32)read32(ip));
9597 GET_BBLOCK (cfg, tblock, target);
9598 link_bblock (cfg, bblock, tblock);
9599 GET_BBLOCK (cfg, tblock, ip);
9600 link_bblock (cfg, bblock, tblock);
9602 if (sp != stack_start) {
9603 handle_stack_args (cfg, stack_start, sp - stack_start);
9604 CHECK_UNVERIFIABLE (cfg);
9607 MONO_INST_NEW(cfg, cmp, OP_ICOMPARE_IMM);
9608 cmp->sreg1 = sp [0]->dreg;
9609 type_from_op (cfg, cmp, sp [0], NULL);
9612 #if SIZEOF_REGISTER == 4
9613 if (cmp->opcode == OP_LCOMPARE_IMM) {
9614 /* Convert it to OP_LCOMPARE */
9615 MONO_INST_NEW (cfg, ins, OP_I8CONST);
9616 ins->type = STACK_I8;
9617 ins->dreg = alloc_dreg (cfg, STACK_I8);
9619 MONO_ADD_INS (bblock, ins);
9620 cmp->opcode = OP_LCOMPARE;
9621 cmp->sreg2 = ins->dreg;
9624 MONO_ADD_INS (bblock, cmp);
9626 MONO_INST_NEW (cfg, ins, is_true ? CEE_BNE_UN : CEE_BEQ);
9627 type_from_op (cfg, ins, sp [0], NULL);
9628 MONO_ADD_INS (bblock, ins);
9629 ins->inst_many_bb = mono_mempool_alloc (cfg->mempool, sizeof(gpointer)*2);
9630 GET_BBLOCK (cfg, tblock, target);
9631 ins->inst_true_bb = tblock;
9632 GET_BBLOCK (cfg, tblock, ip);
9633 ins->inst_false_bb = tblock;
9634 start_new_bblock = 2;
9637 inline_costs += BRANCH_COST;
9652 MONO_INST_NEW (cfg, ins, *ip);
9654 target = ip + 4 + (gint32)read32(ip);
9660 inline_costs += BRANCH_COST;
9664 MonoBasicBlock **targets;
9665 MonoBasicBlock *default_bblock;
9666 MonoJumpInfoBBTable *table;
9667 int offset_reg = alloc_preg (cfg);
9668 int target_reg = alloc_preg (cfg);
9669 int table_reg = alloc_preg (cfg);
9670 int sum_reg = alloc_preg (cfg);
9671 gboolean use_op_switch;
9675 n = read32 (ip + 1);
9678 if ((src1->type != STACK_I4) && (src1->type != STACK_PTR))
9682 CHECK_OPSIZE (n * sizeof (guint32));
9683 target = ip + n * sizeof (guint32);
9685 GET_BBLOCK (cfg, default_bblock, target);
9686 default_bblock->flags |= BB_INDIRECT_JUMP_TARGET;
9688 targets = mono_mempool_alloc (cfg->mempool, sizeof (MonoBasicBlock*) * n);
9689 for (i = 0; i < n; ++i) {
9690 GET_BBLOCK (cfg, tblock, target + (gint32)read32(ip));
9691 targets [i] = tblock;
9692 targets [i]->flags |= BB_INDIRECT_JUMP_TARGET;
9696 if (sp != stack_start) {
9698 * Link the current bb with the targets as well, so handle_stack_args
9699 * will set their in_stack correctly.
9701 link_bblock (cfg, bblock, default_bblock);
9702 for (i = 0; i < n; ++i)
9703 link_bblock (cfg, bblock, targets [i]);
9705 handle_stack_args (cfg, stack_start, sp - stack_start);
9707 CHECK_UNVERIFIABLE (cfg);
9710 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ICOMPARE_IMM, -1, src1->dreg, n);
9711 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBGE_UN, default_bblock);
9714 for (i = 0; i < n; ++i)
9715 link_bblock (cfg, bblock, targets [i]);
9717 table = mono_mempool_alloc (cfg->mempool, sizeof (MonoJumpInfoBBTable));
9718 table->table = targets;
9719 table->table_size = n;
9721 use_op_switch = FALSE;
9723 /* ARM implements SWITCH statements differently */
9724 /* FIXME: Make it use the generic implementation */
9725 if (!cfg->compile_aot)
9726 use_op_switch = TRUE;
9729 if (COMPILE_LLVM (cfg))
9730 use_op_switch = TRUE;
9732 cfg->cbb->has_jump_table = 1;
9734 if (use_op_switch) {
9735 MONO_INST_NEW (cfg, ins, OP_SWITCH);
9736 ins->sreg1 = src1->dreg;
9737 ins->inst_p0 = table;
9738 ins->inst_many_bb = targets;
9739 ins->klass = GUINT_TO_POINTER (n);
9740 MONO_ADD_INS (cfg->cbb, ins);
9742 if (sizeof (gpointer) == 8)
9743 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SHL_IMM, offset_reg, src1->dreg, 3);
9745 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SHL_IMM, offset_reg, src1->dreg, 2);
9747 #if SIZEOF_REGISTER == 8
9748 /* The upper word might not be zero, and we add it to a 64 bit address later */
9749 MONO_EMIT_NEW_UNALU (cfg, OP_ZEXT_I4, offset_reg, offset_reg);
9752 if (cfg->compile_aot) {
9753 MONO_EMIT_NEW_AOTCONST (cfg, table_reg, table, MONO_PATCH_INFO_SWITCH);
9755 MONO_INST_NEW (cfg, ins, OP_JUMP_TABLE);
9756 ins->inst_c1 = MONO_PATCH_INFO_SWITCH;
9757 ins->inst_p0 = table;
9758 ins->dreg = table_reg;
9759 MONO_ADD_INS (cfg->cbb, ins);
9762 /* FIXME: Use load_memindex */
9763 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, sum_reg, table_reg, offset_reg);
9764 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, target_reg, sum_reg, 0);
9765 MONO_EMIT_NEW_UNALU (cfg, OP_BR_REG, -1, target_reg);
9767 start_new_bblock = 1;
9768 inline_costs += (BRANCH_COST * 2);
9788 dreg = alloc_freg (cfg);
9791 dreg = alloc_lreg (cfg);
9794 dreg = alloc_ireg_ref (cfg);
9797 dreg = alloc_preg (cfg);
9800 NEW_LOAD_MEMBASE (cfg, ins, ldind_to_load_membase (*ip), dreg, sp [0]->dreg, 0);
9801 ins->type = ldind_type [*ip - CEE_LDIND_I1];
9802 if (*ip == CEE_LDIND_R4)
9803 ins->type = cfg->r4_stack_type;
9804 ins->flags |= ins_flag;
9805 MONO_ADD_INS (bblock, ins);
9807 if (ins_flag & MONO_INST_VOLATILE) {
9808 /* Volatile loads have acquire semantics, see 12.6.7 in Ecma 335 */
9809 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_ACQ);
9825 if (ins_flag & MONO_INST_VOLATILE) {
9826 /* Volatile stores have release semantics, see 12.6.7 in Ecma 335 */
9827 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_REL);
9830 NEW_STORE_MEMBASE (cfg, ins, stind_to_store_membase (*ip), sp [0]->dreg, 0, sp [1]->dreg);
9831 ins->flags |= ins_flag;
9834 MONO_ADD_INS (bblock, ins);
9836 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)))
9837 emit_write_barrier (cfg, sp [0], sp [1]);
9846 MONO_INST_NEW (cfg, ins, (*ip));
9848 ins->sreg1 = sp [0]->dreg;
9849 ins->sreg2 = sp [1]->dreg;
9850 type_from_op (cfg, ins, sp [0], sp [1]);
9852 ins->dreg = alloc_dreg ((cfg), (ins)->type);
9854 /* Use the immediate opcodes if possible */
9855 if ((sp [1]->opcode == OP_ICONST) && mono_arch_is_inst_imm (sp [1]->inst_c0)) {
9856 int imm_opcode = mono_op_to_op_imm_noemul (ins->opcode);
9857 if (imm_opcode != -1) {
9858 ins->opcode = imm_opcode;
9859 ins->inst_p1 = (gpointer)(gssize)(sp [1]->inst_c0);
9862 NULLIFY_INS (sp [1]);
9866 MONO_ADD_INS ((cfg)->cbb, (ins));
9868 *sp++ = mono_decompose_opcode (cfg, ins);
9885 MONO_INST_NEW (cfg, ins, (*ip));
9887 ins->sreg1 = sp [0]->dreg;
9888 ins->sreg2 = sp [1]->dreg;
9889 type_from_op (cfg, ins, sp [0], sp [1]);
9891 add_widen_op (cfg, ins, &sp [0], &sp [1]);
9892 ins->dreg = alloc_dreg ((cfg), (ins)->type);
9894 /* FIXME: Pass opcode to is_inst_imm */
9896 /* Use the immediate opcodes if possible */
9897 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)) {
9900 imm_opcode = mono_op_to_op_imm_noemul (ins->opcode);
9901 #if defined(MONO_ARCH_EMULATE_MUL_DIV) || defined(MONO_ARCH_EMULATE_DIV)
9902 /* Keep emulated opcodes which are optimized away later */
9903 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) {
9904 imm_opcode = mono_op_to_op_imm (ins->opcode);
9907 if (imm_opcode != -1) {
9908 ins->opcode = imm_opcode;
9909 if (sp [1]->opcode == OP_I8CONST) {
9910 #if SIZEOF_REGISTER == 8
9911 ins->inst_imm = sp [1]->inst_l;
9913 ins->inst_ls_word = sp [1]->inst_ls_word;
9914 ins->inst_ms_word = sp [1]->inst_ms_word;
9918 ins->inst_imm = (gssize)(sp [1]->inst_c0);
9921 /* Might be followed by an instruction added by add_widen_op */
9922 if (sp [1]->next == NULL)
9923 NULLIFY_INS (sp [1]);
9926 MONO_ADD_INS ((cfg)->cbb, (ins));
9928 *sp++ = mono_decompose_opcode (cfg, ins);
9941 case CEE_CONV_OVF_I8:
9942 case CEE_CONV_OVF_U8:
9946 /* Special case this earlier so we have long constants in the IR */
9947 if ((((*ip) == CEE_CONV_I8) || ((*ip) == CEE_CONV_U8)) && (sp [-1]->opcode == OP_ICONST)) {
9948 int data = sp [-1]->inst_c0;
9949 sp [-1]->opcode = OP_I8CONST;
9950 sp [-1]->type = STACK_I8;
9951 #if SIZEOF_REGISTER == 8
9952 if ((*ip) == CEE_CONV_U8)
9953 sp [-1]->inst_c0 = (guint32)data;
9955 sp [-1]->inst_c0 = data;
9957 sp [-1]->inst_ls_word = data;
9958 if ((*ip) == CEE_CONV_U8)
9959 sp [-1]->inst_ms_word = 0;
9961 sp [-1]->inst_ms_word = (data < 0) ? -1 : 0;
9963 sp [-1]->dreg = alloc_dreg (cfg, STACK_I8);
9970 case CEE_CONV_OVF_I4:
9971 case CEE_CONV_OVF_I1:
9972 case CEE_CONV_OVF_I2:
9973 case CEE_CONV_OVF_I:
9974 case CEE_CONV_OVF_U:
9977 if (sp [-1]->type == STACK_R8 || sp [-1]->type == STACK_R4) {
9978 ADD_UNOP (CEE_CONV_OVF_I8);
9985 case CEE_CONV_OVF_U1:
9986 case CEE_CONV_OVF_U2:
9987 case CEE_CONV_OVF_U4:
9990 if (sp [-1]->type == STACK_R8 || sp [-1]->type == STACK_R4) {
9991 ADD_UNOP (CEE_CONV_OVF_U8);
9998 case CEE_CONV_OVF_I1_UN:
9999 case CEE_CONV_OVF_I2_UN:
10000 case CEE_CONV_OVF_I4_UN:
10001 case CEE_CONV_OVF_I8_UN:
10002 case CEE_CONV_OVF_U1_UN:
10003 case CEE_CONV_OVF_U2_UN:
10004 case CEE_CONV_OVF_U4_UN:
10005 case CEE_CONV_OVF_U8_UN:
10006 case CEE_CONV_OVF_I_UN:
10007 case CEE_CONV_OVF_U_UN:
10014 CHECK_CFG_EXCEPTION;
10018 case CEE_ADD_OVF_UN:
10020 case CEE_MUL_OVF_UN:
10022 case CEE_SUB_OVF_UN:
10028 GSHAREDVT_FAILURE (*ip);
10031 token = read32 (ip + 1);
10032 klass = mini_get_class (method, token, generic_context);
10033 CHECK_TYPELOAD (klass);
10035 if (generic_class_is_reference_type (cfg, klass)) {
10036 MonoInst *store, *load;
10037 int dreg = alloc_ireg_ref (cfg);
10039 NEW_LOAD_MEMBASE (cfg, load, OP_LOAD_MEMBASE, dreg, sp [1]->dreg, 0);
10040 load->flags |= ins_flag;
10041 MONO_ADD_INS (cfg->cbb, load);
10043 NEW_STORE_MEMBASE (cfg, store, OP_STORE_MEMBASE_REG, sp [0]->dreg, 0, dreg);
10044 store->flags |= ins_flag;
10045 MONO_ADD_INS (cfg->cbb, store);
10047 if (cfg->gen_write_barriers && cfg->method->wrapper_type != MONO_WRAPPER_WRITE_BARRIER)
10048 emit_write_barrier (cfg, sp [0], sp [1]);
10050 mini_emit_stobj (cfg, sp [0], sp [1], klass, FALSE);
10056 int loc_index = -1;
10062 token = read32 (ip + 1);
10063 klass = mini_get_class (method, token, generic_context);
10064 CHECK_TYPELOAD (klass);
10066 /* Optimize the common ldobj+stloc combination */
10069 loc_index = ip [6];
10076 loc_index = ip [5] - CEE_STLOC_0;
10083 if ((loc_index != -1) && ip_in_bb (cfg, bblock, ip + 5)) {
10084 CHECK_LOCAL (loc_index);
10086 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, sp [0]->dreg, 0);
10087 ins->dreg = cfg->locals [loc_index]->dreg;
10088 ins->flags |= ins_flag;
10091 if (ins_flag & MONO_INST_VOLATILE) {
10092 /* Volatile loads have acquire semantics, see 12.6.7 in Ecma 335 */
10093 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_ACQ);
10099 /* Optimize the ldobj+stobj combination */
10100 /* The reference case ends up being a load+store anyway */
10101 /* Skip this if the operation is volatile. */
10102 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)) {
10107 mini_emit_stobj (cfg, sp [0], sp [1], klass, FALSE);
10114 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, sp [0]->dreg, 0);
10115 ins->flags |= ins_flag;
10118 if (ins_flag & MONO_INST_VOLATILE) {
10119 /* Volatile loads have acquire semantics, see 12.6.7 in Ecma 335 */
10120 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_ACQ);
10129 CHECK_STACK_OVF (1);
10131 n = read32 (ip + 1);
10133 if (method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD) {
10134 EMIT_NEW_PCONST (cfg, ins, mono_method_get_wrapper_data (method, n));
10135 ins->type = STACK_OBJ;
10138 else if (method->wrapper_type != MONO_WRAPPER_NONE) {
10139 MonoInst *iargs [1];
10140 char *str = mono_method_get_wrapper_data (method, n);
10142 if (cfg->compile_aot)
10143 EMIT_NEW_LDSTRLITCONST (cfg, iargs [0], str);
10145 EMIT_NEW_PCONST (cfg, iargs [0], str);
10146 *sp = mono_emit_jit_icall (cfg, mono_string_new_wrapper, iargs);
10148 if (cfg->opt & MONO_OPT_SHARED) {
10149 MonoInst *iargs [3];
10151 if (cfg->compile_aot) {
10152 cfg->ldstr_list = g_list_prepend (cfg->ldstr_list, GINT_TO_POINTER (n));
10154 EMIT_NEW_DOMAINCONST (cfg, iargs [0]);
10155 EMIT_NEW_IMAGECONST (cfg, iargs [1], image);
10156 EMIT_NEW_ICONST (cfg, iargs [2], mono_metadata_token_index (n));
10157 *sp = mono_emit_jit_icall (cfg, mono_ldstr, iargs);
10158 mono_ldstr (cfg->domain, image, mono_metadata_token_index (n));
10160 if (bblock->out_of_line) {
10161 MonoInst *iargs [2];
10163 if (image == mono_defaults.corlib) {
10165 * Avoid relocations in AOT and save some space by using a
10166 * version of helper_ldstr specialized to mscorlib.
10168 EMIT_NEW_ICONST (cfg, iargs [0], mono_metadata_token_index (n));
10169 *sp = mono_emit_jit_icall (cfg, mono_helper_ldstr_mscorlib, iargs);
10171 /* Avoid creating the string object */
10172 EMIT_NEW_IMAGECONST (cfg, iargs [0], image);
10173 EMIT_NEW_ICONST (cfg, iargs [1], mono_metadata_token_index (n));
10174 *sp = mono_emit_jit_icall (cfg, mono_helper_ldstr, iargs);
10178 if (cfg->compile_aot) {
10179 NEW_LDSTRCONST (cfg, ins, image, n);
10181 MONO_ADD_INS (bblock, ins);
10184 NEW_PCONST (cfg, ins, NULL);
10185 ins->type = STACK_OBJ;
10186 ins->inst_p0 = mono_ldstr (cfg->domain, image, mono_metadata_token_index (n));
10188 OUT_OF_MEMORY_FAILURE;
10191 MONO_ADD_INS (bblock, ins);
10200 MonoInst *iargs [2];
10201 MonoMethodSignature *fsig;
10204 MonoInst *vtable_arg = NULL;
10207 token = read32 (ip + 1);
10208 cmethod = mini_get_method (cfg, method, token, NULL, generic_context);
10209 if (!cmethod || mono_loader_get_last_error ())
10211 fsig = mono_method_get_signature_checked (cmethod, image, token, generic_context, &cfg->error);
10214 mono_save_token_info (cfg, image, token, cmethod);
10216 if (!mono_class_init (cmethod->klass))
10217 TYPE_LOAD_ERROR (cmethod->klass);
10219 context_used = mini_method_check_context_used (cfg, cmethod);
10221 if (mono_security_cas_enabled ()) {
10222 if (check_linkdemand (cfg, method, cmethod))
10223 INLINE_FAILURE ("linkdemand");
10224 CHECK_CFG_EXCEPTION;
10225 } else if (mono_security_core_clr_enabled ()) {
10226 ensure_method_is_allowed_to_call_method (cfg, method, cmethod, bblock, ip);
10229 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)) {
10230 emit_generic_class_init (cfg, cmethod->klass);
10231 CHECK_TYPELOAD (cmethod->klass);
10235 if (cfg->gsharedvt) {
10236 if (mini_is_gsharedvt_variable_signature (sig))
10237 GSHAREDVT_FAILURE (*ip);
10241 n = fsig->param_count;
10245 * Generate smaller code for the common newobj <exception> instruction in
10246 * argument checking code.
10248 if (bblock->out_of_line && cmethod->klass->image == mono_defaults.corlib &&
10249 is_exception_class (cmethod->klass) && n <= 2 &&
10250 ((n < 1) || (!fsig->params [0]->byref && fsig->params [0]->type == MONO_TYPE_STRING)) &&
10251 ((n < 2) || (!fsig->params [1]->byref && fsig->params [1]->type == MONO_TYPE_STRING))) {
10252 MonoInst *iargs [3];
10256 EMIT_NEW_ICONST (cfg, iargs [0], cmethod->klass->type_token);
10259 *sp ++ = mono_emit_jit_icall (cfg, mono_create_corlib_exception_0, iargs);
10262 iargs [1] = sp [0];
10263 *sp ++ = mono_emit_jit_icall (cfg, mono_create_corlib_exception_1, iargs);
10266 iargs [1] = sp [0];
10267 iargs [2] = sp [1];
10268 *sp ++ = mono_emit_jit_icall (cfg, mono_create_corlib_exception_2, iargs);
10271 g_assert_not_reached ();
10279 /* move the args to allow room for 'this' in the first position */
10285 /* check_call_signature () requires sp[0] to be set */
10286 this_ins.type = STACK_OBJ;
10287 sp [0] = &this_ins;
10288 if (check_call_signature (cfg, fsig, sp))
10293 if (mini_class_is_system_array (cmethod->klass)) {
10294 *sp = emit_get_rgctx_method (cfg, context_used,
10295 cmethod, MONO_RGCTX_INFO_METHOD);
10297 /* Avoid varargs in the common case */
10298 if (fsig->param_count == 1)
10299 alloc = mono_emit_jit_icall (cfg, mono_array_new_1, sp);
10300 else if (fsig->param_count == 2)
10301 alloc = mono_emit_jit_icall (cfg, mono_array_new_2, sp);
10302 else if (fsig->param_count == 3)
10303 alloc = mono_emit_jit_icall (cfg, mono_array_new_3, sp);
10304 else if (fsig->param_count == 4)
10305 alloc = mono_emit_jit_icall (cfg, mono_array_new_4, sp);
10307 alloc = handle_array_new (cfg, fsig->param_count, sp, ip);
10308 } else if (cmethod->string_ctor) {
10309 g_assert (!context_used);
10310 g_assert (!vtable_arg);
10311 /* we simply pass a null pointer */
10312 EMIT_NEW_PCONST (cfg, *sp, NULL);
10313 /* now call the string ctor */
10314 alloc = mono_emit_method_call_full (cfg, cmethod, fsig, FALSE, sp, NULL, NULL, NULL);
10316 if (cmethod->klass->valuetype) {
10317 iargs [0] = mono_compile_create_var (cfg, &cmethod->klass->byval_arg, OP_LOCAL);
10318 emit_init_rvar (cfg, iargs [0]->dreg, &cmethod->klass->byval_arg);
10319 EMIT_NEW_TEMPLOADA (cfg, *sp, iargs [0]->inst_c0);
10324 * The code generated by mini_emit_virtual_call () expects
10325 * iargs [0] to be a boxed instance, but luckily the vcall
10326 * will be transformed into a normal call there.
10328 } else if (context_used) {
10329 alloc = handle_alloc (cfg, cmethod->klass, FALSE, context_used);
10332 MonoVTable *vtable = NULL;
10334 if (!cfg->compile_aot)
10335 vtable = mono_class_vtable (cfg->domain, cmethod->klass);
10336 CHECK_TYPELOAD (cmethod->klass);
10339 * TypeInitializationExceptions thrown from the mono_runtime_class_init
10340 * call in mono_jit_runtime_invoke () can abort the finalizer thread.
10341 * As a workaround, we call class cctors before allocating objects.
10343 if (mini_field_access_needs_cctor_run (cfg, method, cmethod->klass, vtable) && !(g_slist_find (class_inits, cmethod->klass))) {
10344 mono_emit_abs_call (cfg, MONO_PATCH_INFO_CLASS_INIT, cmethod->klass, helper_sig_class_init_trampoline, NULL);
10345 if (cfg->verbose_level > 2)
10346 printf ("class %s.%s needs init call for ctor\n", cmethod->klass->name_space, cmethod->klass->name);
10347 class_inits = g_slist_prepend (class_inits, cmethod->klass);
10350 alloc = handle_alloc (cfg, cmethod->klass, FALSE, 0);
10353 CHECK_CFG_EXCEPTION; /*for handle_alloc*/
10356 MONO_EMIT_NEW_UNALU (cfg, OP_NOT_NULL, -1, alloc->dreg);
10358 /* Now call the actual ctor */
10359 handle_ctor_call (cfg, cmethod, fsig, context_used, sp, ip, &bblock, &inline_costs);
10360 CHECK_CFG_EXCEPTION;
10363 if (alloc == NULL) {
10365 EMIT_NEW_TEMPLOAD (cfg, ins, iargs [0]->inst_c0);
10366 type_to_eval_stack_type (cfg, &ins->klass->byval_arg, ins);
10374 if (!(seq_point_locs && mono_bitset_test_fast (seq_point_locs, ip - header->code)))
10375 emit_seq_point (cfg, method, ip, FALSE, TRUE);
10378 case CEE_CASTCLASS:
10382 token = read32 (ip + 1);
10383 klass = mini_get_class (method, token, generic_context);
10384 CHECK_TYPELOAD (klass);
10385 if (sp [0]->type != STACK_OBJ)
10388 ins = handle_castclass (cfg, klass, *sp, ip, &bblock, &inline_costs);
10389 CHECK_CFG_EXCEPTION;
10398 token = read32 (ip + 1);
10399 klass = mini_get_class (method, token, generic_context);
10400 CHECK_TYPELOAD (klass);
10401 if (sp [0]->type != STACK_OBJ)
10404 context_used = mini_class_check_context_used (cfg, klass);
10406 if (!context_used && mini_class_has_reference_variant_generic_argument (cfg, klass, context_used)) {
10407 MonoMethod *mono_isinst = mono_marshal_get_isinst_with_cache ();
10408 MonoInst *args [3];
10414 EMIT_NEW_CLASSCONST (cfg, args [1], klass);
10417 if (cfg->compile_aot)
10418 EMIT_NEW_AOTCONST (cfg, args [2], MONO_PATCH_INFO_CASTCLASS_CACHE, NULL);
10420 EMIT_NEW_PCONST (cfg, args [2], mono_domain_alloc0 (cfg->domain, sizeof (gpointer)));
10422 *sp++ = mono_emit_method_call (cfg, mono_isinst, args, NULL);
10425 } else if (!context_used && (mono_class_is_marshalbyref (klass) || klass->flags & TYPE_ATTRIBUTE_INTERFACE)) {
10426 MonoMethod *mono_isinst;
10427 MonoInst *iargs [1];
10430 mono_isinst = mono_marshal_get_isinst (klass);
10431 iargs [0] = sp [0];
10433 costs = inline_method (cfg, mono_isinst, mono_method_signature (mono_isinst),
10434 iargs, ip, cfg->real_offset, TRUE, &bblock);
10435 CHECK_CFG_EXCEPTION;
10436 g_assert (costs > 0);
10439 cfg->real_offset += 5;
10443 inline_costs += costs;
10446 ins = handle_isinst (cfg, klass, *sp, context_used);
10447 CHECK_CFG_EXCEPTION;
10454 case CEE_UNBOX_ANY: {
10455 MonoInst *res, *addr;
10460 token = read32 (ip + 1);
10461 klass = mini_get_class (method, token, generic_context);
10462 CHECK_TYPELOAD (klass);
10464 mono_save_token_info (cfg, image, token, klass);
10466 context_used = mini_class_check_context_used (cfg, klass);
10468 if (mini_is_gsharedvt_klass (cfg, klass)) {
10469 res = handle_unbox_gsharedvt (cfg, klass, *sp, &bblock);
10471 } else if (generic_class_is_reference_type (cfg, klass)) {
10472 res = handle_castclass (cfg, klass, *sp, ip, &bblock, &inline_costs);
10473 CHECK_CFG_EXCEPTION;
10474 } else if (mono_class_is_nullable (klass)) {
10475 res = handle_unbox_nullable (cfg, *sp, klass, context_used);
10477 addr = handle_unbox (cfg, klass, sp, context_used);
10479 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, addr->dreg, 0);
10490 MonoClass *enum_class;
10491 MonoMethod *has_flag;
10497 token = read32 (ip + 1);
10498 klass = mini_get_class (method, token, generic_context);
10499 CHECK_TYPELOAD (klass);
10501 mono_save_token_info (cfg, image, token, klass);
10503 context_used = mini_class_check_context_used (cfg, klass);
10505 if (generic_class_is_reference_type (cfg, klass)) {
10511 if (klass == mono_defaults.void_class)
10513 if (target_type_is_incompatible (cfg, &klass->byval_arg, *sp))
10515 /* frequent check in generic code: box (struct), brtrue */
10520 * <push int/long ptr>
10523 * constrained. MyFlags
10524 * callvirt instace bool class [mscorlib] System.Enum::HasFlag (class [mscorlib] System.Enum)
10526 * If we find this sequence and the operand types on box and constrained
10527 * are equal, we can emit a specialized instruction sequence instead of
10528 * the very slow HasFlag () call.
10530 if ((cfg->opt & MONO_OPT_INTRINS) &&
10531 /* Cheap checks first. */
10532 ip + 5 + 6 + 5 < end &&
10533 ip [5] == CEE_PREFIX1 &&
10534 ip [6] == CEE_CONSTRAINED_ &&
10535 ip [11] == CEE_CALLVIRT &&
10536 ip_in_bb (cfg, bblock, ip + 5 + 6 + 5) &&
10537 mono_class_is_enum (klass) &&
10538 (enum_class = mini_get_class (method, read32 (ip + 7), generic_context)) &&
10539 (has_flag = mini_get_method (cfg, method, read32 (ip + 12), NULL, generic_context)) &&
10540 has_flag->klass == mono_defaults.enum_class &&
10541 !strcmp (has_flag->name, "HasFlag") &&
10542 has_flag->signature->hasthis &&
10543 has_flag->signature->param_count == 1) {
10544 CHECK_TYPELOAD (enum_class);
10546 if (enum_class == klass) {
10547 MonoInst *enum_this, *enum_flag;
10552 enum_this = sp [0];
10553 enum_flag = sp [1];
10555 *sp++ = handle_enum_has_flag (cfg, klass, enum_this, enum_flag);
10560 // FIXME: LLVM can't handle the inconsistent bb linking
10561 if (!mono_class_is_nullable (klass) &&
10562 ip + 5 < end && ip_in_bb (cfg, bblock, ip + 5) &&
10563 (ip [5] == CEE_BRTRUE ||
10564 ip [5] == CEE_BRTRUE_S ||
10565 ip [5] == CEE_BRFALSE ||
10566 ip [5] == CEE_BRFALSE_S)) {
10567 gboolean is_true = ip [5] == CEE_BRTRUE || ip [5] == CEE_BRTRUE_S;
10569 MonoBasicBlock *true_bb, *false_bb;
10573 if (cfg->verbose_level > 3) {
10574 printf ("converting (in B%d: stack: %d) %s", bblock->block_num, (int)(sp - stack_start), mono_disasm_code_one (NULL, method, ip, NULL));
10575 printf ("<box+brtrue opt>\n");
10580 case CEE_BRFALSE_S:
10583 target = ip + 1 + (signed char)(*ip);
10590 target = ip + 4 + (gint)(read32 (ip));
10594 g_assert_not_reached ();
10598 * We need to link both bblocks, since it is needed for handling stack
10599 * arguments correctly (See test_0_box_brtrue_opt_regress_81102).
10600 * Branching to only one of them would lead to inconsistencies, so
10601 * generate an ICONST+BRTRUE, the branch opts will get rid of them.
10603 GET_BBLOCK (cfg, true_bb, target);
10604 GET_BBLOCK (cfg, false_bb, ip);
10606 mono_link_bblock (cfg, cfg->cbb, true_bb);
10607 mono_link_bblock (cfg, cfg->cbb, false_bb);
10609 if (sp != stack_start) {
10610 handle_stack_args (cfg, stack_start, sp - stack_start);
10612 CHECK_UNVERIFIABLE (cfg);
10615 if (COMPILE_LLVM (cfg)) {
10616 dreg = alloc_ireg (cfg);
10617 MONO_EMIT_NEW_ICONST (cfg, dreg, 0);
10618 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, dreg, is_true ? 0 : 1);
10620 MONO_EMIT_NEW_BRANCH_BLOCK2 (cfg, OP_IBEQ, true_bb, false_bb);
10622 /* The JIT can't eliminate the iconst+compare */
10623 MONO_INST_NEW (cfg, ins, OP_BR);
10624 ins->inst_target_bb = is_true ? true_bb : false_bb;
10625 MONO_ADD_INS (cfg->cbb, ins);
10628 start_new_bblock = 1;
10632 *sp++ = handle_box (cfg, val, klass, context_used, &bblock);
10634 CHECK_CFG_EXCEPTION;
10643 token = read32 (ip + 1);
10644 klass = mini_get_class (method, token, generic_context);
10645 CHECK_TYPELOAD (klass);
10647 mono_save_token_info (cfg, image, token, klass);
10649 context_used = mini_class_check_context_used (cfg, klass);
10651 if (mono_class_is_nullable (klass)) {
10654 val = handle_unbox_nullable (cfg, *sp, klass, context_used);
10655 EMIT_NEW_VARLOADA (cfg, ins, get_vreg_to_inst (cfg, val->dreg), &val->klass->byval_arg);
10659 ins = handle_unbox (cfg, klass, sp, context_used);
10672 MonoClassField *field;
10673 #ifndef DISABLE_REMOTING
10677 gboolean is_instance;
10679 gpointer addr = NULL;
10680 gboolean is_special_static;
10682 MonoInst *store_val = NULL;
10683 MonoInst *thread_ins;
10686 is_instance = (op == CEE_LDFLD || op == CEE_LDFLDA || op == CEE_STFLD);
10688 if (op == CEE_STFLD) {
10691 store_val = sp [1];
10696 if (sp [0]->type == STACK_I4 || sp [0]->type == STACK_I8 || sp [0]->type == STACK_R8)
10698 if (*ip != CEE_LDFLD && sp [0]->type == STACK_VTYPE)
10701 if (op == CEE_STSFLD) {
10704 store_val = sp [0];
10709 token = read32 (ip + 1);
10710 if (method->wrapper_type != MONO_WRAPPER_NONE) {
10711 field = mono_method_get_wrapper_data (method, token);
10712 klass = field->parent;
10715 field = mono_field_from_token_checked (image, token, &klass, generic_context, &cfg->error);
10718 if (!dont_verify && !cfg->skip_visibility && !mono_method_can_access_field (method, field))
10719 FIELD_ACCESS_FAILURE (method, field);
10720 mono_class_init (klass);
10722 if (is_instance && *ip != CEE_LDFLDA && is_magic_tls_access (field))
10725 /* if the class is Critical then transparent code cannot access it's fields */
10726 if (!is_instance && mono_security_core_clr_enabled ())
10727 ensure_method_is_allowed_to_access_field (cfg, method, field, bblock, ip);
10729 /* XXX this is technically required but, so far (SL2), no [SecurityCritical] types (not many exists) have
10730 any visible *instance* field (in fact there's a single case for a static field in Marshal) XXX
10731 if (mono_security_core_clr_enabled ())
10732 ensure_method_is_allowed_to_access_field (cfg, method, field, bblock, ip);
10736 * LDFLD etc. is usable on static fields as well, so convert those cases to
10739 if (is_instance && field->type->attrs & FIELD_ATTRIBUTE_STATIC) {
10751 g_assert_not_reached ();
10753 is_instance = FALSE;
10756 context_used = mini_class_check_context_used (cfg, klass);
10758 /* INSTANCE CASE */
10760 foffset = klass->valuetype? field->offset - sizeof (MonoObject): field->offset;
10761 if (op == CEE_STFLD) {
10762 if (target_type_is_incompatible (cfg, field->type, sp [1]))
10764 #ifndef DISABLE_REMOTING
10765 if ((mono_class_is_marshalbyref (klass) && !MONO_CHECK_THIS (sp [0])) || mono_class_is_contextbound (klass) || klass == mono_defaults.marshalbyrefobject_class) {
10766 MonoMethod *stfld_wrapper = mono_marshal_get_stfld_wrapper (field->type);
10767 MonoInst *iargs [5];
10769 GSHAREDVT_FAILURE (op);
10771 iargs [0] = sp [0];
10772 EMIT_NEW_CLASSCONST (cfg, iargs [1], klass);
10773 EMIT_NEW_FIELDCONST (cfg, iargs [2], field);
10774 EMIT_NEW_ICONST (cfg, iargs [3], klass->valuetype ? field->offset - sizeof (MonoObject) :
10776 iargs [4] = sp [1];
10778 if (cfg->opt & MONO_OPT_INLINE || cfg->compile_aot) {
10779 costs = inline_method (cfg, stfld_wrapper, mono_method_signature (stfld_wrapper),
10780 iargs, ip, cfg->real_offset, TRUE, &bblock);
10781 CHECK_CFG_EXCEPTION;
10782 g_assert (costs > 0);
10784 cfg->real_offset += 5;
10786 inline_costs += costs;
10788 mono_emit_method_call (cfg, stfld_wrapper, iargs, NULL);
10795 MONO_EMIT_NULL_CHECK (cfg, sp [0]->dreg);
10797 if (mini_is_gsharedvt_klass (cfg, klass)) {
10798 MonoInst *offset_ins;
10800 context_used = mini_class_check_context_used (cfg, klass);
10802 offset_ins = emit_get_gsharedvt_info (cfg, field, MONO_RGCTX_INFO_FIELD_OFFSET);
10803 dreg = alloc_ireg_mp (cfg);
10804 EMIT_NEW_BIALU (cfg, ins, OP_PADD, dreg, sp [0]->dreg, offset_ins->dreg);
10805 /* The decomposition will call mini_emit_stobj () which will emit a wbarrier if needed */
10806 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, store, field->type, dreg, 0, sp [1]->dreg);
10808 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, store, field->type, sp [0]->dreg, foffset, sp [1]->dreg);
10810 if (sp [0]->opcode != OP_LDADDR)
10811 store->flags |= MONO_INST_FAULT;
10813 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)) {
10814 /* insert call to write barrier */
10818 dreg = alloc_ireg_mp (cfg);
10819 EMIT_NEW_BIALU_IMM (cfg, ptr, OP_PADD_IMM, dreg, sp [0]->dreg, foffset);
10820 emit_write_barrier (cfg, ptr, sp [1]);
10823 store->flags |= ins_flag;
10830 #ifndef DISABLE_REMOTING
10831 if (is_instance && ((mono_class_is_marshalbyref (klass) && !MONO_CHECK_THIS (sp [0])) || mono_class_is_contextbound (klass) || klass == mono_defaults.marshalbyrefobject_class)) {
10832 MonoMethod *wrapper = (op == CEE_LDFLDA) ? mono_marshal_get_ldflda_wrapper (field->type) : mono_marshal_get_ldfld_wrapper (field->type);
10833 MonoInst *iargs [4];
10835 GSHAREDVT_FAILURE (op);
10837 iargs [0] = sp [0];
10838 EMIT_NEW_CLASSCONST (cfg, iargs [1], klass);
10839 EMIT_NEW_FIELDCONST (cfg, iargs [2], field);
10840 EMIT_NEW_ICONST (cfg, iargs [3], klass->valuetype ? field->offset - sizeof (MonoObject) : field->offset);
10841 if (cfg->opt & MONO_OPT_INLINE || cfg->compile_aot) {
10842 costs = inline_method (cfg, wrapper, mono_method_signature (wrapper),
10843 iargs, ip, cfg->real_offset, TRUE, &bblock);
10844 CHECK_CFG_EXCEPTION;
10845 g_assert (costs > 0);
10847 cfg->real_offset += 5;
10851 inline_costs += costs;
10853 ins = mono_emit_method_call (cfg, wrapper, iargs, NULL);
10859 if (sp [0]->type == STACK_VTYPE) {
10862 /* Have to compute the address of the variable */
10864 var = get_vreg_to_inst (cfg, sp [0]->dreg);
10866 var = mono_compile_create_var_for_vreg (cfg, &klass->byval_arg, OP_LOCAL, sp [0]->dreg);
10868 g_assert (var->klass == klass);
10870 EMIT_NEW_VARLOADA (cfg, ins, var, &var->klass->byval_arg);
10874 if (op == CEE_LDFLDA) {
10875 if (is_magic_tls_access (field)) {
10876 GSHAREDVT_FAILURE (*ip);
10878 *sp++ = create_magic_tls_access (cfg, field, &cached_tls_addr, ins);
10880 if (sp [0]->type == STACK_OBJ) {
10881 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, sp [0]->dreg, 0);
10882 MONO_EMIT_NEW_COND_EXC (cfg, EQ, "NullReferenceException");
10885 dreg = alloc_ireg_mp (cfg);
10887 if (mini_is_gsharedvt_klass (cfg, klass)) {
10888 MonoInst *offset_ins;
10890 offset_ins = emit_get_gsharedvt_info (cfg, field, MONO_RGCTX_INFO_FIELD_OFFSET);
10891 EMIT_NEW_BIALU (cfg, ins, OP_PADD, dreg, sp [0]->dreg, offset_ins->dreg);
10893 EMIT_NEW_BIALU_IMM (cfg, ins, OP_PADD_IMM, dreg, sp [0]->dreg, foffset);
10895 ins->klass = mono_class_from_mono_type (field->type);
10896 ins->type = STACK_MP;
10902 MONO_EMIT_NULL_CHECK (cfg, sp [0]->dreg);
10904 if (mini_is_gsharedvt_klass (cfg, klass)) {
10905 MonoInst *offset_ins;
10907 offset_ins = emit_get_gsharedvt_info (cfg, field, MONO_RGCTX_INFO_FIELD_OFFSET);
10908 dreg = alloc_ireg_mp (cfg);
10909 EMIT_NEW_BIALU (cfg, ins, OP_PADD, dreg, sp [0]->dreg, offset_ins->dreg);
10910 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, load, field->type, dreg, 0);
10912 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, load, field->type, sp [0]->dreg, foffset);
10914 load->flags |= ins_flag;
10915 if (sp [0]->opcode != OP_LDADDR)
10916 load->flags |= MONO_INST_FAULT;
10930 * We can only support shared generic static
10931 * field access on architectures where the
10932 * trampoline code has been extended to handle
10933 * the generic class init.
10935 #ifndef MONO_ARCH_VTABLE_REG
10936 GENERIC_SHARING_FAILURE (op);
10939 context_used = mini_class_check_context_used (cfg, klass);
10941 ftype = mono_field_get_type (field);
10943 if (ftype->attrs & FIELD_ATTRIBUTE_LITERAL)
10946 /* The special_static_fields field is init'd in mono_class_vtable, so it needs
10947 * to be called here.
10949 if (!context_used && !(cfg->opt & MONO_OPT_SHARED)) {
10950 mono_class_vtable (cfg->domain, klass);
10951 CHECK_TYPELOAD (klass);
10953 mono_domain_lock (cfg->domain);
10954 if (cfg->domain->special_static_fields)
10955 addr = g_hash_table_lookup (cfg->domain->special_static_fields, field);
10956 mono_domain_unlock (cfg->domain);
10958 is_special_static = mono_class_field_is_special_static (field);
10960 if (is_special_static && ((gsize)addr & 0x80000000) == 0)
10961 thread_ins = mono_get_thread_intrinsic (cfg);
10965 /* Generate IR to compute the field address */
10966 if (is_special_static && ((gsize)addr & 0x80000000) == 0 && thread_ins && !(cfg->opt & MONO_OPT_SHARED) && !context_used) {
10968 * Fast access to TLS data
10969 * Inline version of get_thread_static_data () in
10973 int idx, static_data_reg, array_reg, dreg;
10975 GSHAREDVT_FAILURE (op);
10977 // offset &= 0x7fffffff;
10978 // idx = (offset >> 24) - 1;
10979 // return ((char*) thread->static_data [idx]) + (offset & 0xffffff);
10980 MONO_ADD_INS (cfg->cbb, thread_ins);
10981 static_data_reg = alloc_ireg (cfg);
10982 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, static_data_reg, thread_ins->dreg, MONO_STRUCT_OFFSET (MonoInternalThread, static_data));
10984 if (cfg->compile_aot) {
10985 int offset_reg, offset2_reg, idx_reg;
10987 /* For TLS variables, this will return the TLS offset */
10988 EMIT_NEW_SFLDACONST (cfg, ins, field);
10989 offset_reg = ins->dreg;
10990 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_IAND_IMM, offset_reg, offset_reg, 0x7fffffff);
10991 idx_reg = alloc_ireg (cfg);
10992 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ISHR_IMM, idx_reg, offset_reg, 24);
10993 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ISUB_IMM, idx_reg, idx_reg, 1);
10994 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ISHL_IMM, idx_reg, idx_reg, sizeof (gpointer) == 8 ? 3 : 2);
10995 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, static_data_reg, static_data_reg, idx_reg);
10996 array_reg = alloc_ireg (cfg);
10997 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, array_reg, static_data_reg, 0);
10998 offset2_reg = alloc_ireg (cfg);
10999 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_IAND_IMM, offset2_reg, offset_reg, 0xffffff);
11000 dreg = alloc_ireg (cfg);
11001 EMIT_NEW_BIALU (cfg, ins, OP_PADD, dreg, array_reg, offset2_reg);
11003 offset = (gsize)addr & 0x7fffffff;
11004 idx = (offset >> 24) - 1;
11006 array_reg = alloc_ireg (cfg);
11007 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, array_reg, static_data_reg, idx * sizeof (gpointer));
11008 dreg = alloc_ireg (cfg);
11009 EMIT_NEW_BIALU_IMM (cfg, ins, OP_ADD_IMM, dreg, array_reg, (offset & 0xffffff));
11011 } else if ((cfg->opt & MONO_OPT_SHARED) ||
11012 (cfg->compile_aot && is_special_static) ||
11013 (context_used && is_special_static)) {
11014 MonoInst *iargs [2];
11016 g_assert (field->parent);
11017 EMIT_NEW_DOMAINCONST (cfg, iargs [0]);
11018 if (context_used) {
11019 iargs [1] = emit_get_rgctx_field (cfg, context_used,
11020 field, MONO_RGCTX_INFO_CLASS_FIELD);
11022 EMIT_NEW_FIELDCONST (cfg, iargs [1], field);
11024 ins = mono_emit_jit_icall (cfg, mono_class_static_field_address, iargs);
11025 } else if (context_used) {
11026 MonoInst *static_data;
11029 g_print ("sharing static field access in %s.%s.%s - depth %d offset %d\n",
11030 method->klass->name_space, method->klass->name, method->name,
11031 depth, field->offset);
11034 if (mono_class_needs_cctor_run (klass, method))
11035 emit_generic_class_init (cfg, klass);
11038 * The pointer we're computing here is
11040 * super_info.static_data + field->offset
11042 static_data = emit_get_rgctx_klass (cfg, context_used,
11043 klass, MONO_RGCTX_INFO_STATIC_DATA);
11045 if (mini_is_gsharedvt_klass (cfg, klass)) {
11046 MonoInst *offset_ins;
11048 offset_ins = emit_get_rgctx_field (cfg, context_used, field, MONO_RGCTX_INFO_FIELD_OFFSET);
11049 dreg = alloc_ireg_mp (cfg);
11050 EMIT_NEW_BIALU (cfg, ins, OP_PADD, dreg, static_data->dreg, offset_ins->dreg);
11051 } else if (field->offset == 0) {
11054 int addr_reg = mono_alloc_preg (cfg);
11055 EMIT_NEW_BIALU_IMM (cfg, ins, OP_PADD_IMM, addr_reg, static_data->dreg, field->offset);
11057 } else if ((cfg->opt & MONO_OPT_SHARED) || (cfg->compile_aot && addr)) {
11058 MonoInst *iargs [2];
11060 g_assert (field->parent);
11061 EMIT_NEW_DOMAINCONST (cfg, iargs [0]);
11062 EMIT_NEW_FIELDCONST (cfg, iargs [1], field);
11063 ins = mono_emit_jit_icall (cfg, mono_class_static_field_address, iargs);
11065 MonoVTable *vtable = NULL;
11067 if (!cfg->compile_aot)
11068 vtable = mono_class_vtable (cfg->domain, klass);
11069 CHECK_TYPELOAD (klass);
11072 if (mini_field_access_needs_cctor_run (cfg, method, klass, vtable)) {
11073 if (!(g_slist_find (class_inits, klass))) {
11074 mono_emit_abs_call (cfg, MONO_PATCH_INFO_CLASS_INIT, klass, helper_sig_class_init_trampoline, NULL);
11075 if (cfg->verbose_level > 2)
11076 printf ("class %s.%s needs init call for %s\n", klass->name_space, klass->name, mono_field_get_name (field));
11077 class_inits = g_slist_prepend (class_inits, klass);
11080 if (cfg->run_cctors) {
11082 /* This makes so that inline cannot trigger */
11083 /* .cctors: too many apps depend on them */
11084 /* running with a specific order... */
11086 if (! vtable->initialized)
11087 INLINE_FAILURE ("class init");
11088 ex = mono_runtime_class_init_full (vtable, FALSE);
11090 set_exception_object (cfg, ex);
11091 goto exception_exit;
11095 if (cfg->compile_aot)
11096 EMIT_NEW_SFLDACONST (cfg, ins, field);
11099 addr = (char*)mono_vtable_get_static_field_data (vtable) + field->offset;
11101 EMIT_NEW_PCONST (cfg, ins, addr);
11104 MonoInst *iargs [1];
11105 EMIT_NEW_ICONST (cfg, iargs [0], GPOINTER_TO_UINT (addr));
11106 ins = mono_emit_jit_icall (cfg, mono_get_special_static_data, iargs);
11110 /* Generate IR to do the actual load/store operation */
11112 if ((op == CEE_STFLD || op == CEE_STSFLD) && (ins_flag & MONO_INST_VOLATILE)) {
11113 /* Volatile stores have release semantics, see 12.6.7 in Ecma 335 */
11114 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_REL);
11117 if (op == CEE_LDSFLDA) {
11118 ins->klass = mono_class_from_mono_type (ftype);
11119 ins->type = STACK_PTR;
11121 } else if (op == CEE_STSFLD) {
11124 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, store, ftype, ins->dreg, 0, store_val->dreg);
11125 store->flags |= ins_flag;
11127 gboolean is_const = FALSE;
11128 MonoVTable *vtable = NULL;
11129 gpointer addr = NULL;
11131 if (!context_used) {
11132 vtable = mono_class_vtable (cfg->domain, klass);
11133 CHECK_TYPELOAD (klass);
11135 if ((ftype->attrs & FIELD_ATTRIBUTE_INIT_ONLY) && (((addr = mono_aot_readonly_field_override (field)) != NULL) ||
11136 (!context_used && !((cfg->opt & MONO_OPT_SHARED) || cfg->compile_aot) && vtable->initialized))) {
11137 int ro_type = ftype->type;
11139 addr = (char*)mono_vtable_get_static_field_data (vtable) + field->offset;
11140 if (ro_type == MONO_TYPE_VALUETYPE && ftype->data.klass->enumtype) {
11141 ro_type = mono_class_enum_basetype (ftype->data.klass)->type;
11144 GSHAREDVT_FAILURE (op);
11146 /* printf ("RO-FIELD %s.%s:%s\n", klass->name_space, klass->name, mono_field_get_name (field));*/
11149 case MONO_TYPE_BOOLEAN:
11151 EMIT_NEW_ICONST (cfg, *sp, *((guint8 *)addr));
11155 EMIT_NEW_ICONST (cfg, *sp, *((gint8 *)addr));
11158 case MONO_TYPE_CHAR:
11160 EMIT_NEW_ICONST (cfg, *sp, *((guint16 *)addr));
11164 EMIT_NEW_ICONST (cfg, *sp, *((gint16 *)addr));
11169 EMIT_NEW_ICONST (cfg, *sp, *((gint32 *)addr));
11173 EMIT_NEW_ICONST (cfg, *sp, *((guint32 *)addr));
11178 case MONO_TYPE_PTR:
11179 case MONO_TYPE_FNPTR:
11180 EMIT_NEW_PCONST (cfg, *sp, *((gpointer *)addr));
11181 type_to_eval_stack_type ((cfg), field->type, *sp);
11184 case MONO_TYPE_STRING:
11185 case MONO_TYPE_OBJECT:
11186 case MONO_TYPE_CLASS:
11187 case MONO_TYPE_SZARRAY:
11188 case MONO_TYPE_ARRAY:
11189 if (!mono_gc_is_moving ()) {
11190 EMIT_NEW_PCONST (cfg, *sp, *((gpointer *)addr));
11191 type_to_eval_stack_type ((cfg), field->type, *sp);
11199 EMIT_NEW_I8CONST (cfg, *sp, *((gint64 *)addr));
11204 case MONO_TYPE_VALUETYPE:
11214 CHECK_STACK_OVF (1);
11216 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, load, field->type, ins->dreg, 0);
11217 load->flags |= ins_flag;
11223 if ((op == CEE_LDFLD || op == CEE_LDSFLD) && (ins_flag & MONO_INST_VOLATILE)) {
11224 /* Volatile loads have acquire semantics, see 12.6.7 in Ecma 335 */
11225 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_ACQ);
11236 token = read32 (ip + 1);
11237 klass = mini_get_class (method, token, generic_context);
11238 CHECK_TYPELOAD (klass);
11239 if (ins_flag & MONO_INST_VOLATILE) {
11240 /* Volatile stores have release semantics, see 12.6.7 in Ecma 335 */
11241 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_REL);
11243 /* FIXME: should check item at sp [1] is compatible with the type of the store. */
11244 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, sp [0]->dreg, 0, sp [1]->dreg);
11245 ins->flags |= ins_flag;
11246 if (cfg->gen_write_barriers && cfg->method->wrapper_type != MONO_WRAPPER_WRITE_BARRIER &&
11247 generic_class_is_reference_type (cfg, klass)) {
11248 /* insert call to write barrier */
11249 emit_write_barrier (cfg, sp [0], sp [1]);
11261 const char *data_ptr;
11263 guint32 field_token;
11269 token = read32 (ip + 1);
11271 klass = mini_get_class (method, token, generic_context);
11272 CHECK_TYPELOAD (klass);
11274 context_used = mini_class_check_context_used (cfg, klass);
11276 if (sp [0]->type == STACK_I8 || (SIZEOF_VOID_P == 8 && sp [0]->type == STACK_PTR)) {
11277 MONO_INST_NEW (cfg, ins, OP_LCONV_TO_OVF_U4);
11278 ins->sreg1 = sp [0]->dreg;
11279 ins->type = STACK_I4;
11280 ins->dreg = alloc_ireg (cfg);
11281 MONO_ADD_INS (cfg->cbb, ins);
11282 *sp = mono_decompose_opcode (cfg, ins);
11285 if (context_used) {
11286 MonoInst *args [3];
11287 MonoClass *array_class = mono_array_class_get (klass, 1);
11288 MonoMethod *managed_alloc = mono_gc_get_managed_array_allocator (array_class);
11290 /* FIXME: Use OP_NEWARR and decompose later to help abcrem */
11293 args [0] = emit_get_rgctx_klass (cfg, context_used,
11294 array_class, MONO_RGCTX_INFO_VTABLE);
11299 ins = mono_emit_method_call (cfg, managed_alloc, args, NULL);
11301 ins = mono_emit_jit_icall (cfg, mono_array_new_specific, args);
11303 if (cfg->opt & MONO_OPT_SHARED) {
11304 /* Decompose now to avoid problems with references to the domainvar */
11305 MonoInst *iargs [3];
11307 EMIT_NEW_DOMAINCONST (cfg, iargs [0]);
11308 EMIT_NEW_CLASSCONST (cfg, iargs [1], klass);
11309 iargs [2] = sp [0];
11311 ins = mono_emit_jit_icall (cfg, mono_array_new, iargs);
11313 /* Decompose later since it is needed by abcrem */
11314 MonoClass *array_type = mono_array_class_get (klass, 1);
11315 mono_class_vtable (cfg->domain, array_type);
11316 CHECK_TYPELOAD (array_type);
11318 MONO_INST_NEW (cfg, ins, OP_NEWARR);
11319 ins->dreg = alloc_ireg_ref (cfg);
11320 ins->sreg1 = sp [0]->dreg;
11321 ins->inst_newa_class = klass;
11322 ins->type = STACK_OBJ;
11323 ins->klass = array_type;
11324 MONO_ADD_INS (cfg->cbb, ins);
11325 cfg->flags |= MONO_CFG_HAS_ARRAY_ACCESS;
11326 cfg->cbb->has_array_access = TRUE;
11328 /* Needed so mono_emit_load_get_addr () gets called */
11329 mono_get_got_var (cfg);
11339 * we inline/optimize the initialization sequence if possible.
11340 * we should also allocate the array as not cleared, since we spend as much time clearing to 0 as initializing
11341 * for small sizes open code the memcpy
11342 * ensure the rva field is big enough
11344 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))) {
11345 MonoMethod *memcpy_method = get_memcpy_method ();
11346 MonoInst *iargs [3];
11347 int add_reg = alloc_ireg_mp (cfg);
11349 EMIT_NEW_BIALU_IMM (cfg, iargs [0], OP_PADD_IMM, add_reg, ins->dreg, MONO_STRUCT_OFFSET (MonoArray, vector));
11350 if (cfg->compile_aot) {
11351 EMIT_NEW_AOTCONST_TOKEN (cfg, iargs [1], MONO_PATCH_INFO_RVA, method->klass->image, GPOINTER_TO_UINT(field_token), STACK_PTR, NULL);
11353 EMIT_NEW_PCONST (cfg, iargs [1], (char*)data_ptr);
11355 EMIT_NEW_ICONST (cfg, iargs [2], data_size);
11356 mono_emit_method_call (cfg, memcpy_method, iargs, NULL);
11365 if (sp [0]->type != STACK_OBJ)
11368 MONO_INST_NEW (cfg, ins, OP_LDLEN);
11369 ins->dreg = alloc_preg (cfg);
11370 ins->sreg1 = sp [0]->dreg;
11371 ins->type = STACK_I4;
11372 /* This flag will be inherited by the decomposition */
11373 ins->flags |= MONO_INST_FAULT;
11374 MONO_ADD_INS (cfg->cbb, ins);
11375 cfg->flags |= MONO_CFG_HAS_ARRAY_ACCESS;
11376 cfg->cbb->has_array_access = TRUE;
11384 if (sp [0]->type != STACK_OBJ)
11387 cfg->flags |= MONO_CFG_HAS_LDELEMA;
11389 klass = mini_get_class (method, read32 (ip + 1), generic_context);
11390 CHECK_TYPELOAD (klass);
11391 /* we need to make sure that this array is exactly the type it needs
11392 * to be for correctness. the wrappers are lax with their usage
11393 * so we need to ignore them here
11395 if (!klass->valuetype && method->wrapper_type == MONO_WRAPPER_NONE && !readonly) {
11396 MonoClass *array_class = mono_array_class_get (klass, 1);
11397 mini_emit_check_array_type (cfg, sp [0], array_class);
11398 CHECK_TYPELOAD (array_class);
11402 ins = mini_emit_ldelema_1_ins (cfg, klass, sp [0], sp [1], TRUE);
11407 case CEE_LDELEM_I1:
11408 case CEE_LDELEM_U1:
11409 case CEE_LDELEM_I2:
11410 case CEE_LDELEM_U2:
11411 case CEE_LDELEM_I4:
11412 case CEE_LDELEM_U4:
11413 case CEE_LDELEM_I8:
11415 case CEE_LDELEM_R4:
11416 case CEE_LDELEM_R8:
11417 case CEE_LDELEM_REF: {
11423 if (*ip == CEE_LDELEM) {
11425 token = read32 (ip + 1);
11426 klass = mini_get_class (method, token, generic_context);
11427 CHECK_TYPELOAD (klass);
11428 mono_class_init (klass);
11431 klass = array_access_to_klass (*ip);
11433 if (sp [0]->type != STACK_OBJ)
11436 cfg->flags |= MONO_CFG_HAS_LDELEMA;
11438 if (mini_is_gsharedvt_variable_klass (cfg, klass)) {
11439 // FIXME-VT: OP_ICONST optimization
11440 addr = mini_emit_ldelema_1_ins (cfg, klass, sp [0], sp [1], TRUE);
11441 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, addr->dreg, 0);
11442 ins->opcode = OP_LOADV_MEMBASE;
11443 } else if (sp [1]->opcode == OP_ICONST) {
11444 int array_reg = sp [0]->dreg;
11445 int index_reg = sp [1]->dreg;
11446 int offset = (mono_class_array_element_size (klass) * sp [1]->inst_c0) + MONO_STRUCT_OFFSET (MonoArray, vector);
11448 MONO_EMIT_BOUNDS_CHECK (cfg, array_reg, MonoArray, max_length, index_reg);
11449 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, array_reg, offset);
11451 addr = mini_emit_ldelema_1_ins (cfg, klass, sp [0], sp [1], TRUE);
11452 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, addr->dreg, 0);
11455 if (*ip == CEE_LDELEM)
11462 case CEE_STELEM_I1:
11463 case CEE_STELEM_I2:
11464 case CEE_STELEM_I4:
11465 case CEE_STELEM_I8:
11466 case CEE_STELEM_R4:
11467 case CEE_STELEM_R8:
11468 case CEE_STELEM_REF:
11473 cfg->flags |= MONO_CFG_HAS_LDELEMA;
11475 if (*ip == CEE_STELEM) {
11477 token = read32 (ip + 1);
11478 klass = mini_get_class (method, token, generic_context);
11479 CHECK_TYPELOAD (klass);
11480 mono_class_init (klass);
11483 klass = array_access_to_klass (*ip);
11485 if (sp [0]->type != STACK_OBJ)
11488 emit_array_store (cfg, klass, sp, TRUE);
11490 if (*ip == CEE_STELEM)
11497 case CEE_CKFINITE: {
11501 MONO_INST_NEW (cfg, ins, OP_CKFINITE);
11502 ins->sreg1 = sp [0]->dreg;
11503 ins->dreg = alloc_freg (cfg);
11504 ins->type = STACK_R8;
11505 MONO_ADD_INS (bblock, ins);
11507 *sp++ = mono_decompose_opcode (cfg, ins);
11512 case CEE_REFANYVAL: {
11513 MonoInst *src_var, *src;
11515 int klass_reg = alloc_preg (cfg);
11516 int dreg = alloc_preg (cfg);
11518 GSHAREDVT_FAILURE (*ip);
11521 MONO_INST_NEW (cfg, ins, *ip);
11524 klass = mini_get_class (method, read32 (ip + 1), generic_context);
11525 CHECK_TYPELOAD (klass);
11527 context_used = mini_class_check_context_used (cfg, klass);
11530 src_var = get_vreg_to_inst (cfg, sp [0]->dreg);
11532 src_var = mono_compile_create_var_for_vreg (cfg, &mono_defaults.typed_reference_class->byval_arg, OP_LOCAL, sp [0]->dreg);
11533 EMIT_NEW_VARLOADA (cfg, src, src_var, src_var->inst_vtype);
11534 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, src->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, klass));
11536 if (context_used) {
11537 MonoInst *klass_ins;
11539 klass_ins = emit_get_rgctx_klass (cfg, context_used,
11540 klass, MONO_RGCTX_INFO_KLASS);
11543 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, klass_reg, klass_ins->dreg);
11544 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException");
11546 mini_emit_class_check (cfg, klass_reg, klass);
11548 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, dreg, src->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, value));
11549 ins->type = STACK_MP;
11554 case CEE_MKREFANY: {
11555 MonoInst *loc, *addr;
11557 GSHAREDVT_FAILURE (*ip);
11560 MONO_INST_NEW (cfg, ins, *ip);
11563 klass = mini_get_class (method, read32 (ip + 1), generic_context);
11564 CHECK_TYPELOAD (klass);
11566 context_used = mini_class_check_context_used (cfg, klass);
11568 loc = mono_compile_create_var (cfg, &mono_defaults.typed_reference_class->byval_arg, OP_LOCAL);
11569 EMIT_NEW_TEMPLOADA (cfg, addr, loc->inst_c0);
11571 if (context_used) {
11572 MonoInst *const_ins;
11573 int type_reg = alloc_preg (cfg);
11575 const_ins = emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_KLASS);
11576 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREP_MEMBASE_REG, addr->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, klass), const_ins->dreg);
11577 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ADD_IMM, type_reg, const_ins->dreg, MONO_STRUCT_OFFSET (MonoClass, byval_arg));
11578 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREP_MEMBASE_REG, addr->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, type), type_reg);
11579 } else if (cfg->compile_aot) {
11580 int const_reg = alloc_preg (cfg);
11581 int type_reg = alloc_preg (cfg);
11583 MONO_EMIT_NEW_CLASSCONST (cfg, const_reg, klass);
11584 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREP_MEMBASE_REG, addr->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, klass), const_reg);
11585 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ADD_IMM, type_reg, const_reg, MONO_STRUCT_OFFSET (MonoClass, byval_arg));
11586 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREP_MEMBASE_REG, addr->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, type), type_reg);
11588 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREP_MEMBASE_IMM, addr->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, type), &klass->byval_arg);
11589 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREP_MEMBASE_IMM, addr->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, klass), klass);
11591 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREP_MEMBASE_REG, addr->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, value), sp [0]->dreg);
11593 EMIT_NEW_TEMPLOAD (cfg, ins, loc->inst_c0);
11594 ins->type = STACK_VTYPE;
11595 ins->klass = mono_defaults.typed_reference_class;
11600 case CEE_LDTOKEN: {
11602 MonoClass *handle_class;
11604 CHECK_STACK_OVF (1);
11607 n = read32 (ip + 1);
11609 if (method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD ||
11610 method->wrapper_type == MONO_WRAPPER_SYNCHRONIZED) {
11611 handle = mono_method_get_wrapper_data (method, n);
11612 handle_class = mono_method_get_wrapper_data (method, n + 1);
11613 if (handle_class == mono_defaults.typehandle_class)
11614 handle = &((MonoClass*)handle)->byval_arg;
11617 handle = mono_ldtoken_checked (image, n, &handle_class, generic_context, &cfg->error);
11622 mono_class_init (handle_class);
11623 if (cfg->generic_sharing_context) {
11624 if (mono_metadata_token_table (n) == MONO_TABLE_TYPEDEF ||
11625 mono_metadata_token_table (n) == MONO_TABLE_TYPEREF) {
11626 /* This case handles ldtoken
11627 of an open type, like for
11630 } else if (handle_class == mono_defaults.typehandle_class) {
11631 context_used = mini_class_check_context_used (cfg, mono_class_from_mono_type (handle));
11632 } else if (handle_class == mono_defaults.fieldhandle_class)
11633 context_used = mini_class_check_context_used (cfg, ((MonoClassField*)handle)->parent);
11634 else if (handle_class == mono_defaults.methodhandle_class)
11635 context_used = mini_method_check_context_used (cfg, handle);
11637 g_assert_not_reached ();
11640 if ((cfg->opt & MONO_OPT_SHARED) &&
11641 method->wrapper_type != MONO_WRAPPER_DYNAMIC_METHOD &&
11642 method->wrapper_type != MONO_WRAPPER_SYNCHRONIZED) {
11643 MonoInst *addr, *vtvar, *iargs [3];
11644 int method_context_used;
11646 method_context_used = mini_method_check_context_used (cfg, method);
11648 vtvar = mono_compile_create_var (cfg, &handle_class->byval_arg, OP_LOCAL);
11650 EMIT_NEW_IMAGECONST (cfg, iargs [0], image);
11651 EMIT_NEW_ICONST (cfg, iargs [1], n);
11652 if (method_context_used) {
11653 iargs [2] = emit_get_rgctx_method (cfg, method_context_used,
11654 method, MONO_RGCTX_INFO_METHOD);
11655 ins = mono_emit_jit_icall (cfg, mono_ldtoken_wrapper_generic_shared, iargs);
11657 EMIT_NEW_PCONST (cfg, iargs [2], generic_context);
11658 ins = mono_emit_jit_icall (cfg, mono_ldtoken_wrapper, iargs);
11660 EMIT_NEW_TEMPLOADA (cfg, addr, vtvar->inst_c0);
11662 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, addr->dreg, 0, ins->dreg);
11664 EMIT_NEW_TEMPLOAD (cfg, ins, vtvar->inst_c0);
11666 if ((ip + 5 < end) && ip_in_bb (cfg, bblock, ip + 5) &&
11667 ((ip [5] == CEE_CALL) || (ip [5] == CEE_CALLVIRT)) &&
11668 (cmethod = mini_get_method (cfg, method, read32 (ip + 6), NULL, generic_context)) &&
11669 (cmethod->klass == mono_defaults.systemtype_class) &&
11670 (strcmp (cmethod->name, "GetTypeFromHandle") == 0)) {
11671 MonoClass *tclass = mono_class_from_mono_type (handle);
11673 mono_class_init (tclass);
11674 if (context_used) {
11675 ins = emit_get_rgctx_klass (cfg, context_used,
11676 tclass, MONO_RGCTX_INFO_REFLECTION_TYPE);
11677 } else if (cfg->compile_aot) {
11678 if (method->wrapper_type) {
11679 mono_error_init (&error); //got to do it since there are multiple conditionals below
11680 if (mono_class_get_checked (tclass->image, tclass->type_token, &error) == tclass && !generic_context) {
11681 /* Special case for static synchronized wrappers */
11682 EMIT_NEW_TYPE_FROM_HANDLE_CONST (cfg, ins, tclass->image, tclass->type_token, generic_context);
11684 mono_error_cleanup (&error); /* FIXME don't swallow the error */
11685 /* FIXME: n is not a normal token */
11687 EMIT_NEW_PCONST (cfg, ins, NULL);
11690 EMIT_NEW_TYPE_FROM_HANDLE_CONST (cfg, ins, image, n, generic_context);
11693 EMIT_NEW_PCONST (cfg, ins, mono_type_get_object (cfg->domain, handle));
11695 ins->type = STACK_OBJ;
11696 ins->klass = cmethod->klass;
11699 MonoInst *addr, *vtvar;
11701 vtvar = mono_compile_create_var (cfg, &handle_class->byval_arg, OP_LOCAL);
11703 if (context_used) {
11704 if (handle_class == mono_defaults.typehandle_class) {
11705 ins = emit_get_rgctx_klass (cfg, context_used,
11706 mono_class_from_mono_type (handle),
11707 MONO_RGCTX_INFO_TYPE);
11708 } else if (handle_class == mono_defaults.methodhandle_class) {
11709 ins = emit_get_rgctx_method (cfg, context_used,
11710 handle, MONO_RGCTX_INFO_METHOD);
11711 } else if (handle_class == mono_defaults.fieldhandle_class) {
11712 ins = emit_get_rgctx_field (cfg, context_used,
11713 handle, MONO_RGCTX_INFO_CLASS_FIELD);
11715 g_assert_not_reached ();
11717 } else if (cfg->compile_aot) {
11718 EMIT_NEW_LDTOKENCONST (cfg, ins, image, n, generic_context);
11720 EMIT_NEW_PCONST (cfg, ins, handle);
11722 EMIT_NEW_TEMPLOADA (cfg, addr, vtvar->inst_c0);
11723 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, addr->dreg, 0, ins->dreg);
11724 EMIT_NEW_TEMPLOAD (cfg, ins, vtvar->inst_c0);
11734 MONO_INST_NEW (cfg, ins, OP_THROW);
11736 ins->sreg1 = sp [0]->dreg;
11738 bblock->out_of_line = TRUE;
11739 MONO_ADD_INS (bblock, ins);
11740 MONO_INST_NEW (cfg, ins, OP_NOT_REACHED);
11741 MONO_ADD_INS (bblock, ins);
11744 link_bblock (cfg, bblock, end_bblock);
11745 start_new_bblock = 1;
11747 case CEE_ENDFINALLY:
11748 /* mono_save_seq_point_info () depends on this */
11749 if (sp != stack_start)
11750 emit_seq_point (cfg, method, ip, FALSE, FALSE);
11751 MONO_INST_NEW (cfg, ins, OP_ENDFINALLY);
11752 MONO_ADD_INS (bblock, ins);
11754 start_new_bblock = 1;
11757 * Control will leave the method so empty the stack, otherwise
11758 * the next basic block will start with a nonempty stack.
11760 while (sp != stack_start) {
11765 case CEE_LEAVE_S: {
11768 if (*ip == CEE_LEAVE) {
11770 target = ip + 5 + (gint32)read32(ip + 1);
11773 target = ip + 2 + (signed char)(ip [1]);
11776 /* empty the stack */
11777 while (sp != stack_start) {
11782 * If this leave statement is in a catch block, check for a
11783 * pending exception, and rethrow it if necessary.
11784 * We avoid doing this in runtime invoke wrappers, since those are called
11785 * by native code which excepts the wrapper to catch all exceptions.
11787 for (i = 0; i < header->num_clauses; ++i) {
11788 MonoExceptionClause *clause = &header->clauses [i];
11791 * Use <= in the final comparison to handle clauses with multiple
11792 * leave statements, like in bug #78024.
11793 * The ordering of the exception clauses guarantees that we find the
11794 * innermost clause.
11796 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) {
11798 MonoBasicBlock *dont_throw;
11803 NEW_TEMPLOAD (cfg, load, mono_find_exvar_for_offset (cfg, clause->handler_offset)->inst_c0);
11806 exc_ins = mono_emit_jit_icall (cfg, mono_thread_get_undeniable_exception, NULL);
11808 NEW_BBLOCK (cfg, dont_throw);
11811 * Currently, we always rethrow the abort exception, despite the
11812 * fact that this is not correct. See thread6.cs for an example.
11813 * But propagating the abort exception is more important than
11814 * getting the sematics right.
11816 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, exc_ins->dreg, 0);
11817 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, dont_throw);
11818 MONO_EMIT_NEW_UNALU (cfg, OP_THROW, -1, exc_ins->dreg);
11820 MONO_START_BB (cfg, dont_throw);
11825 if ((handlers = mono_find_final_block (cfg, ip, target, MONO_EXCEPTION_CLAUSE_FINALLY))) {
11827 MonoExceptionClause *clause;
11829 for (tmp = handlers; tmp; tmp = tmp->next) {
11830 clause = tmp->data;
11831 tblock = cfg->cil_offset_to_bb [clause->handler_offset];
11833 link_bblock (cfg, bblock, tblock);
11834 MONO_INST_NEW (cfg, ins, OP_CALL_HANDLER);
11835 ins->inst_target_bb = tblock;
11836 ins->inst_eh_block = clause;
11837 MONO_ADD_INS (bblock, ins);
11838 bblock->has_call_handler = 1;
11839 if (COMPILE_LLVM (cfg)) {
11840 MonoBasicBlock *target_bb;
11843 * Link the finally bblock with the target, since it will
11844 * conceptually branch there.
11845 * FIXME: Have to link the bblock containing the endfinally.
11847 GET_BBLOCK (cfg, target_bb, target);
11848 link_bblock (cfg, tblock, target_bb);
11851 g_list_free (handlers);
11854 MONO_INST_NEW (cfg, ins, OP_BR);
11855 MONO_ADD_INS (bblock, ins);
11856 GET_BBLOCK (cfg, tblock, target);
11857 link_bblock (cfg, bblock, tblock);
11858 ins->inst_target_bb = tblock;
11859 start_new_bblock = 1;
11861 if (*ip == CEE_LEAVE)
11870 * Mono specific opcodes
11872 case MONO_CUSTOM_PREFIX: {
11874 g_assert (method->wrapper_type != MONO_WRAPPER_NONE);
11878 case CEE_MONO_ICALL: {
11880 MonoJitICallInfo *info;
11882 token = read32 (ip + 2);
11883 func = mono_method_get_wrapper_data (method, token);
11884 info = mono_find_jit_icall_by_addr (func);
11886 g_error ("Could not find icall address in wrapper %s", mono_method_full_name (method, 1));
11889 CHECK_STACK (info->sig->param_count);
11890 sp -= info->sig->param_count;
11892 ins = mono_emit_jit_icall (cfg, info->func, sp);
11893 if (!MONO_TYPE_IS_VOID (info->sig->ret))
11897 inline_costs += 10 * num_calls++;
11901 case CEE_MONO_LDPTR: {
11904 CHECK_STACK_OVF (1);
11906 token = read32 (ip + 2);
11908 ptr = mono_method_get_wrapper_data (method, token);
11909 /* FIXME: Generalize this */
11910 if (cfg->compile_aot && ptr == mono_thread_interruption_request_flag ()) {
11911 EMIT_NEW_AOTCONST (cfg, ins, MONO_PATCH_INFO_INTERRUPTION_REQUEST_FLAG, NULL);
11916 EMIT_NEW_PCONST (cfg, ins, ptr);
11919 inline_costs += 10 * num_calls++;
11920 /* Can't embed random pointers into AOT code */
11924 case CEE_MONO_JIT_ICALL_ADDR: {
11925 MonoJitICallInfo *callinfo;
11928 CHECK_STACK_OVF (1);
11930 token = read32 (ip + 2);
11932 ptr = mono_method_get_wrapper_data (method, token);
11933 callinfo = mono_find_jit_icall_by_addr (ptr);
11934 g_assert (callinfo);
11935 EMIT_NEW_JIT_ICALL_ADDRCONST (cfg, ins, (char*)callinfo->name);
11938 inline_costs += 10 * num_calls++;
11941 case CEE_MONO_ICALL_ADDR: {
11942 MonoMethod *cmethod;
11945 CHECK_STACK_OVF (1);
11947 token = read32 (ip + 2);
11949 cmethod = mono_method_get_wrapper_data (method, token);
11951 if (cfg->compile_aot) {
11952 EMIT_NEW_AOTCONST (cfg, ins, MONO_PATCH_INFO_ICALL_ADDR, cmethod);
11954 ptr = mono_lookup_internal_call (cmethod);
11956 EMIT_NEW_PCONST (cfg, ins, ptr);
11962 case CEE_MONO_VTADDR: {
11963 MonoInst *src_var, *src;
11969 src_var = get_vreg_to_inst (cfg, sp [0]->dreg);
11970 EMIT_NEW_VARLOADA ((cfg), (src), src_var, src_var->inst_vtype);
11975 case CEE_MONO_NEWOBJ: {
11976 MonoInst *iargs [2];
11978 CHECK_STACK_OVF (1);
11980 token = read32 (ip + 2);
11981 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
11982 mono_class_init (klass);
11983 NEW_DOMAINCONST (cfg, iargs [0]);
11984 MONO_ADD_INS (cfg->cbb, iargs [0]);
11985 NEW_CLASSCONST (cfg, iargs [1], klass);
11986 MONO_ADD_INS (cfg->cbb, iargs [1]);
11987 *sp++ = mono_emit_jit_icall (cfg, mono_object_new, iargs);
11989 inline_costs += 10 * num_calls++;
11992 case CEE_MONO_OBJADDR:
11995 MONO_INST_NEW (cfg, ins, OP_MOVE);
11996 ins->dreg = alloc_ireg_mp (cfg);
11997 ins->sreg1 = sp [0]->dreg;
11998 ins->type = STACK_MP;
11999 MONO_ADD_INS (cfg->cbb, ins);
12003 case CEE_MONO_LDNATIVEOBJ:
12005 * Similar to LDOBJ, but instead load the unmanaged
12006 * representation of the vtype to the stack.
12011 token = read32 (ip + 2);
12012 klass = mono_method_get_wrapper_data (method, token);
12013 g_assert (klass->valuetype);
12014 mono_class_init (klass);
12017 MonoInst *src, *dest, *temp;
12020 temp = mono_compile_create_var (cfg, &klass->byval_arg, OP_LOCAL);
12021 temp->backend.is_pinvoke = 1;
12022 EMIT_NEW_TEMPLOADA (cfg, dest, temp->inst_c0);
12023 mini_emit_stobj (cfg, dest, src, klass, TRUE);
12025 EMIT_NEW_TEMPLOAD (cfg, dest, temp->inst_c0);
12026 dest->type = STACK_VTYPE;
12027 dest->klass = klass;
12033 case CEE_MONO_RETOBJ: {
12035 * Same as RET, but return the native representation of a vtype
12038 g_assert (cfg->ret);
12039 g_assert (mono_method_signature (method)->pinvoke);
12044 token = read32 (ip + 2);
12045 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
12047 if (!cfg->vret_addr) {
12048 g_assert (cfg->ret_var_is_local);
12050 EMIT_NEW_VARLOADA (cfg, ins, cfg->ret, cfg->ret->inst_vtype);
12052 EMIT_NEW_RETLOADA (cfg, ins);
12054 mini_emit_stobj (cfg, ins, sp [0], klass, TRUE);
12056 if (sp != stack_start)
12059 MONO_INST_NEW (cfg, ins, OP_BR);
12060 ins->inst_target_bb = end_bblock;
12061 MONO_ADD_INS (bblock, ins);
12062 link_bblock (cfg, bblock, end_bblock);
12063 start_new_bblock = 1;
12067 case CEE_MONO_CISINST:
12068 case CEE_MONO_CCASTCLASS: {
12073 token = read32 (ip + 2);
12074 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
12075 if (ip [1] == CEE_MONO_CISINST)
12076 ins = handle_cisinst (cfg, klass, sp [0]);
12078 ins = handle_ccastclass (cfg, klass, sp [0]);
12084 case CEE_MONO_SAVE_LMF:
12085 case CEE_MONO_RESTORE_LMF:
12086 #ifdef MONO_ARCH_HAVE_LMF_OPS
12087 MONO_INST_NEW (cfg, ins, (ip [1] == CEE_MONO_SAVE_LMF) ? OP_SAVE_LMF : OP_RESTORE_LMF);
12088 MONO_ADD_INS (bblock, ins);
12089 cfg->need_lmf_area = TRUE;
12093 case CEE_MONO_CLASSCONST:
12094 CHECK_STACK_OVF (1);
12096 token = read32 (ip + 2);
12097 EMIT_NEW_CLASSCONST (cfg, ins, mono_method_get_wrapper_data (method, token));
12100 inline_costs += 10 * num_calls++;
12102 case CEE_MONO_NOT_TAKEN:
12103 bblock->out_of_line = TRUE;
12106 case CEE_MONO_TLS: {
12109 CHECK_STACK_OVF (1);
12111 key = (gint32)read32 (ip + 2);
12112 g_assert (key < TLS_KEY_NUM);
12114 ins = mono_create_tls_get (cfg, key);
12116 if (cfg->compile_aot) {
12118 MONO_INST_NEW (cfg, ins, OP_TLS_GET);
12119 ins->dreg = alloc_preg (cfg);
12120 ins->type = STACK_PTR;
12122 g_assert_not_reached ();
12125 ins->type = STACK_PTR;
12126 MONO_ADD_INS (bblock, ins);
12131 case CEE_MONO_DYN_CALL: {
12132 MonoCallInst *call;
12134 /* It would be easier to call a trampoline, but that would put an
12135 * extra frame on the stack, confusing exception handling. So
12136 * implement it inline using an opcode for now.
12139 if (!cfg->dyn_call_var) {
12140 cfg->dyn_call_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
12141 /* prevent it from being register allocated */
12142 cfg->dyn_call_var->flags |= MONO_INST_VOLATILE;
12145 /* Has to use a call inst since it local regalloc expects it */
12146 MONO_INST_NEW_CALL (cfg, call, OP_DYN_CALL);
12147 ins = (MonoInst*)call;
12149 ins->sreg1 = sp [0]->dreg;
12150 ins->sreg2 = sp [1]->dreg;
12151 MONO_ADD_INS (bblock, ins);
12153 cfg->param_area = MAX (cfg->param_area, MONO_ARCH_DYN_CALL_PARAM_AREA);
12156 inline_costs += 10 * num_calls++;
12160 case CEE_MONO_MEMORY_BARRIER: {
12162 emit_memory_barrier (cfg, (int)read32 (ip + 2));
12166 case CEE_MONO_JIT_ATTACH: {
12167 MonoInst *args [16], *domain_ins;
12168 MonoInst *ad_ins, *jit_tls_ins;
12169 MonoBasicBlock *next_bb = NULL, *call_bb = NULL;
12171 cfg->orig_domain_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
12173 EMIT_NEW_PCONST (cfg, ins, NULL);
12174 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, cfg->orig_domain_var->dreg, ins->dreg);
12176 ad_ins = mono_get_domain_intrinsic (cfg);
12177 jit_tls_ins = mono_get_jit_tls_intrinsic (cfg);
12179 if (MONO_ARCH_HAVE_TLS_GET && ad_ins && jit_tls_ins) {
12180 NEW_BBLOCK (cfg, next_bb);
12181 NEW_BBLOCK (cfg, call_bb);
12183 if (cfg->compile_aot) {
12184 /* AOT code is only used in the root domain */
12185 EMIT_NEW_PCONST (cfg, domain_ins, NULL);
12187 EMIT_NEW_PCONST (cfg, domain_ins, cfg->domain);
12189 MONO_ADD_INS (cfg->cbb, ad_ins);
12190 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, ad_ins->dreg, domain_ins->dreg);
12191 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, call_bb);
12193 MONO_ADD_INS (cfg->cbb, jit_tls_ins);
12194 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, jit_tls_ins->dreg, 0);
12195 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, call_bb);
12197 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, next_bb);
12198 MONO_START_BB (cfg, call_bb);
12201 if (cfg->compile_aot) {
12202 /* AOT code is only used in the root domain */
12203 EMIT_NEW_PCONST (cfg, args [0], NULL);
12205 EMIT_NEW_PCONST (cfg, args [0], cfg->domain);
12207 ins = mono_emit_jit_icall (cfg, mono_jit_thread_attach, args);
12208 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, cfg->orig_domain_var->dreg, ins->dreg);
12211 MONO_START_BB (cfg, next_bb);
12217 case CEE_MONO_JIT_DETACH: {
12218 MonoInst *args [16];
12220 /* Restore the original domain */
12221 dreg = alloc_ireg (cfg);
12222 EMIT_NEW_UNALU (cfg, args [0], OP_MOVE, dreg, cfg->orig_domain_var->dreg);
12223 mono_emit_jit_icall (cfg, mono_jit_set_domain, args);
12228 g_error ("opcode 0x%02x 0x%02x not handled", MONO_CUSTOM_PREFIX, ip [1]);
12234 case CEE_PREFIX1: {
12237 case CEE_ARGLIST: {
12238 /* somewhat similar to LDTOKEN */
12239 MonoInst *addr, *vtvar;
12240 CHECK_STACK_OVF (1);
12241 vtvar = mono_compile_create_var (cfg, &mono_defaults.argumenthandle_class->byval_arg, OP_LOCAL);
12243 EMIT_NEW_TEMPLOADA (cfg, addr, vtvar->inst_c0);
12244 EMIT_NEW_UNALU (cfg, ins, OP_ARGLIST, -1, addr->dreg);
12246 EMIT_NEW_TEMPLOAD (cfg, ins, vtvar->inst_c0);
12247 ins->type = STACK_VTYPE;
12248 ins->klass = mono_defaults.argumenthandle_class;
12258 MonoInst *cmp, *arg1, *arg2;
12266 * The following transforms:
12267 * CEE_CEQ into OP_CEQ
12268 * CEE_CGT into OP_CGT
12269 * CEE_CGT_UN into OP_CGT_UN
12270 * CEE_CLT into OP_CLT
12271 * CEE_CLT_UN into OP_CLT_UN
12273 MONO_INST_NEW (cfg, cmp, (OP_CEQ - CEE_CEQ) + ip [1]);
12275 MONO_INST_NEW (cfg, ins, cmp->opcode);
12276 cmp->sreg1 = arg1->dreg;
12277 cmp->sreg2 = arg2->dreg;
12278 type_from_op (cfg, cmp, arg1, arg2);
12280 add_widen_op (cfg, cmp, &arg1, &arg2);
12281 if ((arg1->type == STACK_I8) || ((SIZEOF_VOID_P == 8) && ((arg1->type == STACK_PTR) || (arg1->type == STACK_OBJ) || (arg1->type == STACK_MP))))
12282 cmp->opcode = OP_LCOMPARE;
12283 else if (arg1->type == STACK_R4)
12284 cmp->opcode = OP_RCOMPARE;
12285 else if (arg1->type == STACK_R8)
12286 cmp->opcode = OP_FCOMPARE;
12288 cmp->opcode = OP_ICOMPARE;
12289 MONO_ADD_INS (bblock, cmp);
12290 ins->type = STACK_I4;
12291 ins->dreg = alloc_dreg (cfg, ins->type);
12292 type_from_op (cfg, ins, arg1, arg2);
12294 if (cmp->opcode == OP_FCOMPARE || cmp->opcode == OP_RCOMPARE) {
12296 * The backends expect the fceq opcodes to do the
12299 ins->sreg1 = cmp->sreg1;
12300 ins->sreg2 = cmp->sreg2;
12303 MONO_ADD_INS (bblock, ins);
12309 MonoInst *argconst;
12310 MonoMethod *cil_method;
12312 CHECK_STACK_OVF (1);
12314 n = read32 (ip + 2);
12315 cmethod = mini_get_method (cfg, method, n, NULL, generic_context);
12316 if (!cmethod || mono_loader_get_last_error ())
12318 mono_class_init (cmethod->klass);
12320 mono_save_token_info (cfg, image, n, cmethod);
12322 context_used = mini_method_check_context_used (cfg, cmethod);
12324 cil_method = cmethod;
12325 if (!dont_verify && !cfg->skip_visibility && !mono_method_can_access_method (method, cmethod))
12326 METHOD_ACCESS_FAILURE (method, cil_method);
12328 if (mono_security_cas_enabled ()) {
12329 if (check_linkdemand (cfg, method, cmethod))
12330 INLINE_FAILURE ("linkdemand");
12331 CHECK_CFG_EXCEPTION;
12332 } else if (mono_security_core_clr_enabled ()) {
12333 ensure_method_is_allowed_to_call_method (cfg, method, cmethod, bblock, ip);
12337 * Optimize the common case of ldftn+delegate creation
12339 if ((sp > stack_start) && (ip + 6 + 5 < end) && ip_in_bb (cfg, bblock, ip + 6) && (ip [6] == CEE_NEWOBJ)) {
12340 MonoMethod *ctor_method = mini_get_method (cfg, method, read32 (ip + 7), NULL, generic_context);
12341 if (ctor_method && (ctor_method->klass->parent == mono_defaults.multicastdelegate_class)) {
12342 MonoInst *target_ins, *handle_ins;
12343 MonoMethod *invoke;
12344 int invoke_context_used;
12346 invoke = mono_get_delegate_invoke (ctor_method->klass);
12347 if (!invoke || !mono_method_signature (invoke))
12350 invoke_context_used = mini_method_check_context_used (cfg, invoke);
12352 target_ins = sp [-1];
12354 if (mono_security_core_clr_enabled ())
12355 ensure_method_is_allowed_to_call_method (cfg, method, ctor_method, bblock, ip);
12357 if (!(cmethod->flags & METHOD_ATTRIBUTE_STATIC)) {
12358 /*LAME IMPL: We must not add a null check for virtual invoke delegates.*/
12359 if (mono_method_signature (invoke)->param_count == mono_method_signature (cmethod)->param_count) {
12360 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, target_ins->dreg, 0);
12361 MONO_EMIT_NEW_COND_EXC (cfg, EQ, "ArgumentException");
12365 #if defined(MONO_ARCH_HAVE_CREATE_DELEGATE_TRAMPOLINE)
12366 /* FIXME: SGEN support */
12367 if (invoke_context_used == 0) {
12369 if (cfg->verbose_level > 3)
12370 g_print ("converting (in B%d: stack: %d) %s", bblock->block_num, (int)(sp - stack_start), mono_disasm_code_one (NULL, method, ip, NULL));
12371 if ((handle_ins = handle_delegate_ctor (cfg, ctor_method->klass, target_ins, cmethod, context_used, FALSE))) {
12374 CHECK_CFG_EXCEPTION;
12385 argconst = emit_get_rgctx_method (cfg, context_used, cmethod, MONO_RGCTX_INFO_METHOD);
12386 ins = mono_emit_jit_icall (cfg, mono_ldftn, &argconst);
12390 inline_costs += 10 * num_calls++;
12393 case CEE_LDVIRTFTN: {
12394 MonoInst *args [2];
12398 n = read32 (ip + 2);
12399 cmethod = mini_get_method (cfg, method, n, NULL, generic_context);
12400 if (!cmethod || mono_loader_get_last_error ())
12402 mono_class_init (cmethod->klass);
12404 context_used = mini_method_check_context_used (cfg, cmethod);
12406 if (mono_security_cas_enabled ()) {
12407 if (check_linkdemand (cfg, method, cmethod))
12408 INLINE_FAILURE ("linkdemand");
12409 CHECK_CFG_EXCEPTION;
12410 } else if (mono_security_core_clr_enabled ()) {
12411 ensure_method_is_allowed_to_call_method (cfg, method, cmethod, bblock, ip);
12415 * Optimize the common case of ldvirtftn+delegate creation
12417 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)) {
12418 MonoMethod *ctor_method = mini_get_method (cfg, method, read32 (ip + 7), NULL, generic_context);
12419 if (ctor_method && (ctor_method->klass->parent == mono_defaults.multicastdelegate_class)) {
12420 MonoInst *target_ins, *handle_ins;
12421 MonoMethod *invoke;
12422 int invoke_context_used;
12424 invoke = mono_get_delegate_invoke (ctor_method->klass);
12425 if (!invoke || !mono_method_signature (invoke))
12428 invoke_context_used = mini_method_check_context_used (cfg, invoke);
12430 target_ins = sp [-1];
12432 if (mono_security_core_clr_enabled ())
12433 ensure_method_is_allowed_to_call_method (cfg, method, ctor_method, bblock, ip);
12435 #if defined(MONO_ARCH_HAVE_CREATE_DELEGATE_TRAMPOLINE)
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, TRUE))) {
12444 CHECK_CFG_EXCEPTION;
12458 args [1] = emit_get_rgctx_method (cfg, context_used,
12459 cmethod, MONO_RGCTX_INFO_METHOD);
12462 *sp++ = mono_emit_jit_icall (cfg, mono_ldvirtfn_gshared, args);
12464 *sp++ = mono_emit_jit_icall (cfg, mono_ldvirtfn, args);
12467 inline_costs += 10 * num_calls++;
12471 CHECK_STACK_OVF (1);
12473 n = read16 (ip + 2);
12475 EMIT_NEW_ARGLOAD (cfg, ins, n);
12480 CHECK_STACK_OVF (1);
12482 n = read16 (ip + 2);
12484 NEW_ARGLOADA (cfg, ins, n);
12485 MONO_ADD_INS (cfg->cbb, ins);
12493 n = read16 (ip + 2);
12495 if (!dont_verify_stloc && target_type_is_incompatible (cfg, param_types [n], *sp))
12497 EMIT_NEW_ARGSTORE (cfg, ins, n, *sp);
12501 CHECK_STACK_OVF (1);
12503 n = read16 (ip + 2);
12505 EMIT_NEW_LOCLOAD (cfg, ins, n);
12510 unsigned char *tmp_ip;
12511 CHECK_STACK_OVF (1);
12513 n = read16 (ip + 2);
12516 if ((tmp_ip = emit_optimized_ldloca_ir (cfg, ip, end, 2))) {
12522 EMIT_NEW_LOCLOADA (cfg, ins, n);
12531 n = read16 (ip + 2);
12533 if (!dont_verify_stloc && target_type_is_incompatible (cfg, header->locals [n], *sp))
12535 emit_stloc_ir (cfg, sp, header, n);
12542 if (sp != stack_start)
12544 if (cfg->method != method)
12546 * Inlining this into a loop in a parent could lead to
12547 * stack overflows which is different behavior than the
12548 * non-inlined case, thus disable inlining in this case.
12550 INLINE_FAILURE("localloc");
12552 MONO_INST_NEW (cfg, ins, OP_LOCALLOC);
12553 ins->dreg = alloc_preg (cfg);
12554 ins->sreg1 = sp [0]->dreg;
12555 ins->type = STACK_PTR;
12556 MONO_ADD_INS (cfg->cbb, ins);
12558 cfg->flags |= MONO_CFG_HAS_ALLOCA;
12560 ins->flags |= MONO_INST_INIT;
12565 case CEE_ENDFILTER: {
12566 MonoExceptionClause *clause, *nearest;
12567 int cc, nearest_num;
12571 if ((sp != stack_start) || (sp [0]->type != STACK_I4))
12573 MONO_INST_NEW (cfg, ins, OP_ENDFILTER);
12574 ins->sreg1 = (*sp)->dreg;
12575 MONO_ADD_INS (bblock, ins);
12576 start_new_bblock = 1;
12581 for (cc = 0; cc < header->num_clauses; ++cc) {
12582 clause = &header->clauses [cc];
12583 if ((clause->flags & MONO_EXCEPTION_CLAUSE_FILTER) &&
12584 ((ip - header->code) > clause->data.filter_offset && (ip - header->code) <= clause->handler_offset) &&
12585 (!nearest || (clause->data.filter_offset < nearest->data.filter_offset))) {
12590 g_assert (nearest);
12591 if ((ip - header->code) != nearest->handler_offset)
12596 case CEE_UNALIGNED_:
12597 ins_flag |= MONO_INST_UNALIGNED;
12598 /* FIXME: record alignment? we can assume 1 for now */
12602 case CEE_VOLATILE_:
12603 ins_flag |= MONO_INST_VOLATILE;
12607 ins_flag |= MONO_INST_TAILCALL;
12608 cfg->flags |= MONO_CFG_HAS_TAIL;
12609 /* Can't inline tail calls at this time */
12610 inline_costs += 100000;
12617 token = read32 (ip + 2);
12618 klass = mini_get_class (method, token, generic_context);
12619 CHECK_TYPELOAD (klass);
12620 if (generic_class_is_reference_type (cfg, klass))
12621 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STORE_MEMBASE_IMM, sp [0]->dreg, 0, 0);
12623 mini_emit_initobj (cfg, *sp, NULL, klass);
12627 case CEE_CONSTRAINED_:
12629 token = read32 (ip + 2);
12630 constrained_call = mini_get_class (method, token, generic_context);
12631 CHECK_TYPELOAD (constrained_call);
12635 case CEE_INITBLK: {
12636 MonoInst *iargs [3];
12640 /* Skip optimized paths for volatile operations. */
12641 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)) {
12642 mini_emit_memcpy (cfg, sp [0]->dreg, 0, sp [1]->dreg, 0, sp [2]->inst_c0, 0);
12643 } 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)) {
12644 /* emit_memset only works when val == 0 */
12645 mini_emit_memset (cfg, sp [0]->dreg, 0, sp [2]->inst_c0, sp [1]->inst_c0, 0);
12648 iargs [0] = sp [0];
12649 iargs [1] = sp [1];
12650 iargs [2] = sp [2];
12651 if (ip [1] == CEE_CPBLK) {
12653 * FIXME: It's unclear whether we should be emitting both the acquire
12654 * and release barriers for cpblk. It is technically both a load and
12655 * store operation, so it seems like that's the sensible thing to do.
12657 * FIXME: We emit full barriers on both sides of the operation for
12658 * simplicity. We should have a separate atomic memcpy method instead.
12660 MonoMethod *memcpy_method = get_memcpy_method ();
12662 if (ins_flag & MONO_INST_VOLATILE)
12663 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_SEQ);
12665 call = mono_emit_method_call (cfg, memcpy_method, iargs, NULL);
12666 call->flags |= ins_flag;
12668 if (ins_flag & MONO_INST_VOLATILE)
12669 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_SEQ);
12671 MonoMethod *memset_method = get_memset_method ();
12672 if (ins_flag & MONO_INST_VOLATILE) {
12673 /* Volatile stores have release semantics, see 12.6.7 in Ecma 335 */
12674 emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_REL);
12676 call = mono_emit_method_call (cfg, memset_method, iargs, NULL);
12677 call->flags |= ins_flag;
12688 ins_flag |= MONO_INST_NOTYPECHECK;
12690 ins_flag |= MONO_INST_NORANGECHECK;
12691 /* we ignore the no-nullcheck for now since we
12692 * really do it explicitly only when doing callvirt->call
12696 case CEE_RETHROW: {
12698 int handler_offset = -1;
12700 for (i = 0; i < header->num_clauses; ++i) {
12701 MonoExceptionClause *clause = &header->clauses [i];
12702 if (MONO_OFFSET_IN_HANDLER (clause, ip - header->code) && !(clause->flags & MONO_EXCEPTION_CLAUSE_FINALLY)) {
12703 handler_offset = clause->handler_offset;
12708 bblock->flags |= BB_EXCEPTION_UNSAFE;
12710 if (handler_offset == -1)
12713 EMIT_NEW_TEMPLOAD (cfg, load, mono_find_exvar_for_offset (cfg, handler_offset)->inst_c0);
12714 MONO_INST_NEW (cfg, ins, OP_RETHROW);
12715 ins->sreg1 = load->dreg;
12716 MONO_ADD_INS (bblock, ins);
12718 MONO_INST_NEW (cfg, ins, OP_NOT_REACHED);
12719 MONO_ADD_INS (bblock, ins);
12722 link_bblock (cfg, bblock, end_bblock);
12723 start_new_bblock = 1;
12731 CHECK_STACK_OVF (1);
12733 token = read32 (ip + 2);
12734 if (mono_metadata_token_table (token) == MONO_TABLE_TYPESPEC && !image_is_dynamic (method->klass->image) && !generic_context) {
12735 MonoType *type = mono_type_create_from_typespec_checked (image, token, &cfg->error);
12738 val = mono_type_size (type, &ialign);
12740 MonoClass *klass = mini_get_class (method, token, generic_context);
12741 CHECK_TYPELOAD (klass);
12743 val = mono_type_size (&klass->byval_arg, &ialign);
12745 if (mini_is_gsharedvt_klass (cfg, klass))
12746 GSHAREDVT_FAILURE (*ip);
12748 EMIT_NEW_ICONST (cfg, ins, val);
12753 case CEE_REFANYTYPE: {
12754 MonoInst *src_var, *src;
12756 GSHAREDVT_FAILURE (*ip);
12762 src_var = get_vreg_to_inst (cfg, sp [0]->dreg);
12764 src_var = mono_compile_create_var_for_vreg (cfg, &mono_defaults.typed_reference_class->byval_arg, OP_LOCAL, sp [0]->dreg);
12765 EMIT_NEW_VARLOADA (cfg, src, src_var, src_var->inst_vtype);
12766 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &mono_defaults.typehandle_class->byval_arg, src->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, type));
12771 case CEE_READONLY_:
12784 g_warning ("opcode 0xfe 0x%02x not handled", ip [1]);
12794 g_warning ("opcode 0x%02x not handled", *ip);
12798 if (start_new_bblock != 1)
12801 bblock->cil_length = ip - bblock->cil_code;
12802 if (bblock->next_bb) {
12803 /* This could already be set because of inlining, #693905 */
12804 MonoBasicBlock *bb = bblock;
12806 while (bb->next_bb)
12808 bb->next_bb = end_bblock;
12810 bblock->next_bb = end_bblock;
12813 if (cfg->method == method && cfg->domainvar) {
12815 MonoInst *get_domain;
12817 cfg->cbb = init_localsbb;
12819 if ((get_domain = mono_get_domain_intrinsic (cfg))) {
12820 MONO_ADD_INS (cfg->cbb, get_domain);
12822 get_domain = mono_emit_jit_icall (cfg, mono_domain_get, NULL);
12824 NEW_TEMPSTORE (cfg, store, cfg->domainvar->inst_c0, get_domain);
12825 MONO_ADD_INS (cfg->cbb, store);
12828 #if defined(TARGET_POWERPC) || defined(TARGET_X86)
12829 if (cfg->compile_aot)
12830 /* FIXME: The plt slots require a GOT var even if the method doesn't use it */
12831 mono_get_got_var (cfg);
12834 if (cfg->method == method && cfg->got_var)
12835 mono_emit_load_got_addr (cfg);
12837 if (init_localsbb) {
12838 cfg->cbb = init_localsbb;
12840 for (i = 0; i < header->num_locals; ++i) {
12841 emit_init_local (cfg, i, header->locals [i], init_locals);
12845 if (cfg->init_ref_vars && cfg->method == method) {
12846 /* Emit initialization for ref vars */
12847 // FIXME: Avoid duplication initialization for IL locals.
12848 for (i = 0; i < cfg->num_varinfo; ++i) {
12849 MonoInst *ins = cfg->varinfo [i];
12851 if (ins->opcode == OP_LOCAL && ins->type == STACK_OBJ)
12852 MONO_EMIT_NEW_PCONST (cfg, ins->dreg, NULL);
12856 if (cfg->lmf_var && cfg->method == method) {
12857 cfg->cbb = init_localsbb;
12858 emit_push_lmf (cfg);
12861 cfg->cbb = init_localsbb;
12862 emit_instrumentation_call (cfg, mono_profiler_method_enter);
12865 MonoBasicBlock *bb;
12868 * Make seq points at backward branch targets interruptable.
12870 for (bb = cfg->bb_entry; bb; bb = bb->next_bb)
12871 if (bb->code && bb->in_count > 1 && bb->code->opcode == OP_SEQ_POINT)
12872 bb->code->flags |= MONO_INST_SINGLE_STEP_LOC;
12875 /* Add a sequence point for method entry/exit events */
12876 if (cfg->gen_seq_points_debug_data) {
12877 NEW_SEQ_POINT (cfg, ins, METHOD_ENTRY_IL_OFFSET, FALSE);
12878 MONO_ADD_INS (init_localsbb, ins);
12879 NEW_SEQ_POINT (cfg, ins, METHOD_EXIT_IL_OFFSET, FALSE);
12880 MONO_ADD_INS (cfg->bb_exit, ins);
12884 * Add seq points for IL offsets which have line number info, but wasn't generated a seq point during JITting because
12885 * the code they refer to was dead (#11880).
12887 if (sym_seq_points) {
12888 for (i = 0; i < header->code_size; ++i) {
12889 if (mono_bitset_test_fast (seq_point_locs, i) && !mono_bitset_test_fast (seq_point_set_locs, i)) {
12892 NEW_SEQ_POINT (cfg, ins, i, FALSE);
12893 mono_add_seq_point (cfg, NULL, ins, SEQ_POINT_NATIVE_OFFSET_DEAD_CODE);
12900 if (cfg->method == method) {
12901 MonoBasicBlock *bb;
12902 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
12903 bb->region = mono_find_block_region (cfg, bb->real_offset);
12905 mono_create_spvar_for_region (cfg, bb->region);
12906 if (cfg->verbose_level > 2)
12907 printf ("REGION BB%d IL_%04x ID_%08X\n", bb->block_num, bb->real_offset, bb->region);
12911 if (inline_costs < 0) {
12914 /* Method is too large */
12915 mname = mono_method_full_name (method, TRUE);
12916 mono_cfg_set_exception (cfg, MONO_EXCEPTION_INVALID_PROGRAM);
12917 cfg->exception_message = g_strdup_printf ("Method %s is too complex.", mname);
12921 if ((cfg->verbose_level > 2) && (cfg->method == method))
12922 mono_print_code (cfg, "AFTER METHOD-TO-IR");
12927 g_assert (!mono_error_ok (&cfg->error));
12931 g_assert (cfg->exception_type != MONO_EXCEPTION_NONE);
12935 set_exception_type_from_invalid_il (cfg, method, ip);
12939 g_slist_free (class_inits);
12940 mono_basic_block_free (original_bb);
12941 cfg->dont_inline = g_list_remove (cfg->dont_inline, method);
12942 cfg->headers_to_free = g_slist_prepend_mempool (cfg->mempool, cfg->headers_to_free, header);
12943 if (cfg->exception_type)
12946 return inline_costs;
12950 store_membase_reg_to_store_membase_imm (int opcode)
12953 case OP_STORE_MEMBASE_REG:
12954 return OP_STORE_MEMBASE_IMM;
12955 case OP_STOREI1_MEMBASE_REG:
12956 return OP_STOREI1_MEMBASE_IMM;
12957 case OP_STOREI2_MEMBASE_REG:
12958 return OP_STOREI2_MEMBASE_IMM;
12959 case OP_STOREI4_MEMBASE_REG:
12960 return OP_STOREI4_MEMBASE_IMM;
12961 case OP_STOREI8_MEMBASE_REG:
12962 return OP_STOREI8_MEMBASE_IMM;
12964 g_assert_not_reached ();
12971 mono_op_to_op_imm (int opcode)
12975 return OP_IADD_IMM;
12977 return OP_ISUB_IMM;
12979 return OP_IDIV_IMM;
12981 return OP_IDIV_UN_IMM;
12983 return OP_IREM_IMM;
12985 return OP_IREM_UN_IMM;
12987 return OP_IMUL_IMM;
12989 return OP_IAND_IMM;
12993 return OP_IXOR_IMM;
12995 return OP_ISHL_IMM;
12997 return OP_ISHR_IMM;
12999 return OP_ISHR_UN_IMM;
13002 return OP_LADD_IMM;
13004 return OP_LSUB_IMM;
13006 return OP_LAND_IMM;
13010 return OP_LXOR_IMM;
13012 return OP_LSHL_IMM;
13014 return OP_LSHR_IMM;
13016 return OP_LSHR_UN_IMM;
13017 #if SIZEOF_REGISTER == 8
13019 return OP_LREM_IMM;
13023 return OP_COMPARE_IMM;
13025 return OP_ICOMPARE_IMM;
13027 return OP_LCOMPARE_IMM;
13029 case OP_STORE_MEMBASE_REG:
13030 return OP_STORE_MEMBASE_IMM;
13031 case OP_STOREI1_MEMBASE_REG:
13032 return OP_STOREI1_MEMBASE_IMM;
13033 case OP_STOREI2_MEMBASE_REG:
13034 return OP_STOREI2_MEMBASE_IMM;
13035 case OP_STOREI4_MEMBASE_REG:
13036 return OP_STOREI4_MEMBASE_IMM;
13038 #if defined(TARGET_X86) || defined (TARGET_AMD64)
13040 return OP_X86_PUSH_IMM;
13041 case OP_X86_COMPARE_MEMBASE_REG:
13042 return OP_X86_COMPARE_MEMBASE_IMM;
13044 #if defined(TARGET_AMD64)
13045 case OP_AMD64_ICOMPARE_MEMBASE_REG:
13046 return OP_AMD64_ICOMPARE_MEMBASE_IMM;
13048 case OP_VOIDCALL_REG:
13049 return OP_VOIDCALL;
13057 return OP_LOCALLOC_IMM;
13064 ldind_to_load_membase (int opcode)
13068 return OP_LOADI1_MEMBASE;
13070 return OP_LOADU1_MEMBASE;
13072 return OP_LOADI2_MEMBASE;
13074 return OP_LOADU2_MEMBASE;
13076 return OP_LOADI4_MEMBASE;
13078 return OP_LOADU4_MEMBASE;
13080 return OP_LOAD_MEMBASE;
13081 case CEE_LDIND_REF:
13082 return OP_LOAD_MEMBASE;
13084 return OP_LOADI8_MEMBASE;
13086 return OP_LOADR4_MEMBASE;
13088 return OP_LOADR8_MEMBASE;
13090 g_assert_not_reached ();
13097 stind_to_store_membase (int opcode)
13101 return OP_STOREI1_MEMBASE_REG;
13103 return OP_STOREI2_MEMBASE_REG;
13105 return OP_STOREI4_MEMBASE_REG;
13107 case CEE_STIND_REF:
13108 return OP_STORE_MEMBASE_REG;
13110 return OP_STOREI8_MEMBASE_REG;
13112 return OP_STORER4_MEMBASE_REG;
13114 return OP_STORER8_MEMBASE_REG;
13116 g_assert_not_reached ();
13123 mono_load_membase_to_load_mem (int opcode)
13125 // FIXME: Add a MONO_ARCH_HAVE_LOAD_MEM macro
13126 #if defined(TARGET_X86) || defined(TARGET_AMD64)
13128 case OP_LOAD_MEMBASE:
13129 return OP_LOAD_MEM;
13130 case OP_LOADU1_MEMBASE:
13131 return OP_LOADU1_MEM;
13132 case OP_LOADU2_MEMBASE:
13133 return OP_LOADU2_MEM;
13134 case OP_LOADI4_MEMBASE:
13135 return OP_LOADI4_MEM;
13136 case OP_LOADU4_MEMBASE:
13137 return OP_LOADU4_MEM;
13138 #if SIZEOF_REGISTER == 8
13139 case OP_LOADI8_MEMBASE:
13140 return OP_LOADI8_MEM;
13149 op_to_op_dest_membase (int store_opcode, int opcode)
13151 #if defined(TARGET_X86)
13152 if (!((store_opcode == OP_STORE_MEMBASE_REG) || (store_opcode == OP_STOREI4_MEMBASE_REG)))
13157 return OP_X86_ADD_MEMBASE_REG;
13159 return OP_X86_SUB_MEMBASE_REG;
13161 return OP_X86_AND_MEMBASE_REG;
13163 return OP_X86_OR_MEMBASE_REG;
13165 return OP_X86_XOR_MEMBASE_REG;
13168 return OP_X86_ADD_MEMBASE_IMM;
13171 return OP_X86_SUB_MEMBASE_IMM;
13174 return OP_X86_AND_MEMBASE_IMM;
13177 return OP_X86_OR_MEMBASE_IMM;
13180 return OP_X86_XOR_MEMBASE_IMM;
13186 #if defined(TARGET_AMD64)
13187 if (!((store_opcode == OP_STORE_MEMBASE_REG) || (store_opcode == OP_STOREI4_MEMBASE_REG) || (store_opcode == OP_STOREI8_MEMBASE_REG)))
13192 return OP_X86_ADD_MEMBASE_REG;
13194 return OP_X86_SUB_MEMBASE_REG;
13196 return OP_X86_AND_MEMBASE_REG;
13198 return OP_X86_OR_MEMBASE_REG;
13200 return OP_X86_XOR_MEMBASE_REG;
13202 return OP_X86_ADD_MEMBASE_IMM;
13204 return OP_X86_SUB_MEMBASE_IMM;
13206 return OP_X86_AND_MEMBASE_IMM;
13208 return OP_X86_OR_MEMBASE_IMM;
13210 return OP_X86_XOR_MEMBASE_IMM;
13212 return OP_AMD64_ADD_MEMBASE_REG;
13214 return OP_AMD64_SUB_MEMBASE_REG;
13216 return OP_AMD64_AND_MEMBASE_REG;
13218 return OP_AMD64_OR_MEMBASE_REG;
13220 return OP_AMD64_XOR_MEMBASE_REG;
13223 return OP_AMD64_ADD_MEMBASE_IMM;
13226 return OP_AMD64_SUB_MEMBASE_IMM;
13229 return OP_AMD64_AND_MEMBASE_IMM;
13232 return OP_AMD64_OR_MEMBASE_IMM;
13235 return OP_AMD64_XOR_MEMBASE_IMM;
13245 op_to_op_store_membase (int store_opcode, int opcode)
13247 #if defined(TARGET_X86) || defined(TARGET_AMD64)
13250 if (store_opcode == OP_STOREI1_MEMBASE_REG)
13251 return OP_X86_SETEQ_MEMBASE;
13253 if (store_opcode == OP_STOREI1_MEMBASE_REG)
13254 return OP_X86_SETNE_MEMBASE;
13262 op_to_op_src1_membase (int load_opcode, int opcode)
13265 /* FIXME: This has sign extension issues */
13267 if ((opcode == OP_ICOMPARE_IMM) && (load_opcode == OP_LOADU1_MEMBASE))
13268 return OP_X86_COMPARE_MEMBASE8_IMM;
13271 if (!((load_opcode == OP_LOAD_MEMBASE) || (load_opcode == OP_LOADI4_MEMBASE) || (load_opcode == OP_LOADU4_MEMBASE)))
13276 return OP_X86_PUSH_MEMBASE;
13277 case OP_COMPARE_IMM:
13278 case OP_ICOMPARE_IMM:
13279 return OP_X86_COMPARE_MEMBASE_IMM;
13282 return OP_X86_COMPARE_MEMBASE_REG;
13286 #ifdef TARGET_AMD64
13287 /* FIXME: This has sign extension issues */
13289 if ((opcode == OP_ICOMPARE_IMM) && (load_opcode == OP_LOADU1_MEMBASE))
13290 return OP_X86_COMPARE_MEMBASE8_IMM;
13295 #ifdef __mono_ilp32__
13296 if (load_opcode == OP_LOADI8_MEMBASE)
13298 if ((load_opcode == OP_LOAD_MEMBASE) || (load_opcode == OP_LOADI8_MEMBASE))
13300 return OP_X86_PUSH_MEMBASE;
13302 /* FIXME: This only works for 32 bit immediates
13303 case OP_COMPARE_IMM:
13304 case OP_LCOMPARE_IMM:
13305 if ((load_opcode == OP_LOAD_MEMBASE) || (load_opcode == OP_LOADI8_MEMBASE))
13306 return OP_AMD64_COMPARE_MEMBASE_IMM;
13308 case OP_ICOMPARE_IMM:
13309 if ((load_opcode == OP_LOADI4_MEMBASE) || (load_opcode == OP_LOADU4_MEMBASE))
13310 return OP_AMD64_ICOMPARE_MEMBASE_IMM;
13314 #ifdef __mono_ilp32__
13315 if (load_opcode == OP_LOAD_MEMBASE)
13316 return OP_AMD64_ICOMPARE_MEMBASE_REG;
13317 if (load_opcode == OP_LOADI8_MEMBASE)
13319 if ((load_opcode == OP_LOAD_MEMBASE) || (load_opcode == OP_LOADI8_MEMBASE))
13321 return OP_AMD64_COMPARE_MEMBASE_REG;
13324 if ((load_opcode == OP_LOADI4_MEMBASE) || (load_opcode == OP_LOADU4_MEMBASE))
13325 return OP_AMD64_ICOMPARE_MEMBASE_REG;
13334 op_to_op_src2_membase (int load_opcode, int opcode)
13337 if (!((load_opcode == OP_LOAD_MEMBASE) || (load_opcode == OP_LOADI4_MEMBASE) || (load_opcode == OP_LOADU4_MEMBASE)))
13343 return OP_X86_COMPARE_REG_MEMBASE;
13345 return OP_X86_ADD_REG_MEMBASE;
13347 return OP_X86_SUB_REG_MEMBASE;
13349 return OP_X86_AND_REG_MEMBASE;
13351 return OP_X86_OR_REG_MEMBASE;
13353 return OP_X86_XOR_REG_MEMBASE;
13357 #ifdef TARGET_AMD64
13358 #ifdef __mono_ilp32__
13359 if ((load_opcode == OP_LOADI4_MEMBASE) || (load_opcode == OP_LOADU4_MEMBASE) || (load_opcode == OP_LOAD_MEMBASE) ) {
13361 if ((load_opcode == OP_LOADI4_MEMBASE) || (load_opcode == OP_LOADU4_MEMBASE)) {
13365 return OP_AMD64_ICOMPARE_REG_MEMBASE;
13367 return OP_X86_ADD_REG_MEMBASE;
13369 return OP_X86_SUB_REG_MEMBASE;
13371 return OP_X86_AND_REG_MEMBASE;
13373 return OP_X86_OR_REG_MEMBASE;
13375 return OP_X86_XOR_REG_MEMBASE;
13377 #ifdef __mono_ilp32__
13378 } else if (load_opcode == OP_LOADI8_MEMBASE) {
13380 } else if ((load_opcode == OP_LOADI8_MEMBASE) || (load_opcode == OP_LOAD_MEMBASE)) {
13385 return OP_AMD64_COMPARE_REG_MEMBASE;
13387 return OP_AMD64_ADD_REG_MEMBASE;
13389 return OP_AMD64_SUB_REG_MEMBASE;
13391 return OP_AMD64_AND_REG_MEMBASE;
13393 return OP_AMD64_OR_REG_MEMBASE;
13395 return OP_AMD64_XOR_REG_MEMBASE;
13404 mono_op_to_op_imm_noemul (int opcode)
13407 #if SIZEOF_REGISTER == 4 && !defined(MONO_ARCH_NO_EMULATE_LONG_SHIFT_OPS)
13413 #if defined(MONO_ARCH_EMULATE_MUL_DIV) || defined(MONO_ARCH_EMULATE_DIV)
13420 #if defined(MONO_ARCH_EMULATE_MUL_DIV)
13425 return mono_op_to_op_imm (opcode);
13430 * mono_handle_global_vregs:
13432 * Make vregs used in more than one bblock 'global', i.e. allocate a variable
13436 mono_handle_global_vregs (MonoCompile *cfg)
13438 gint32 *vreg_to_bb;
13439 MonoBasicBlock *bb;
13442 vreg_to_bb = mono_mempool_alloc0 (cfg->mempool, sizeof (gint32*) * cfg->next_vreg + 1);
13444 #ifdef MONO_ARCH_SIMD_INTRINSICS
13445 if (cfg->uses_simd_intrinsics)
13446 mono_simd_simplify_indirection (cfg);
13449 /* Find local vregs used in more than one bb */
13450 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
13451 MonoInst *ins = bb->code;
13452 int block_num = bb->block_num;
13454 if (cfg->verbose_level > 2)
13455 printf ("\nHANDLE-GLOBAL-VREGS BLOCK %d:\n", bb->block_num);
13458 for (; ins; ins = ins->next) {
13459 const char *spec = INS_INFO (ins->opcode);
13460 int regtype = 0, regindex;
13463 if (G_UNLIKELY (cfg->verbose_level > 2))
13464 mono_print_ins (ins);
13466 g_assert (ins->opcode >= MONO_CEE_LAST);
13468 for (regindex = 0; regindex < 4; regindex ++) {
13471 if (regindex == 0) {
13472 regtype = spec [MONO_INST_DEST];
13473 if (regtype == ' ')
13476 } else if (regindex == 1) {
13477 regtype = spec [MONO_INST_SRC1];
13478 if (regtype == ' ')
13481 } else if (regindex == 2) {
13482 regtype = spec [MONO_INST_SRC2];
13483 if (regtype == ' ')
13486 } else if (regindex == 3) {
13487 regtype = spec [MONO_INST_SRC3];
13488 if (regtype == ' ')
13493 #if SIZEOF_REGISTER == 4
13494 /* In the LLVM case, the long opcodes are not decomposed */
13495 if (regtype == 'l' && !COMPILE_LLVM (cfg)) {
13497 * Since some instructions reference the original long vreg,
13498 * and some reference the two component vregs, it is quite hard
13499 * to determine when it needs to be global. So be conservative.
13501 if (!get_vreg_to_inst (cfg, vreg)) {
13502 mono_compile_create_var_for_vreg (cfg, &mono_defaults.int64_class->byval_arg, OP_LOCAL, vreg);
13504 if (cfg->verbose_level > 2)
13505 printf ("LONG VREG R%d made global.\n", vreg);
13509 * Make the component vregs volatile since the optimizations can
13510 * get confused otherwise.
13512 get_vreg_to_inst (cfg, vreg + 1)->flags |= MONO_INST_VOLATILE;
13513 get_vreg_to_inst (cfg, vreg + 2)->flags |= MONO_INST_VOLATILE;
13517 g_assert (vreg != -1);
13519 prev_bb = vreg_to_bb [vreg];
13520 if (prev_bb == 0) {
13521 /* 0 is a valid block num */
13522 vreg_to_bb [vreg] = block_num + 1;
13523 } else if ((prev_bb != block_num + 1) && (prev_bb != -1)) {
13524 if (((regtype == 'i' && (vreg < MONO_MAX_IREGS))) || (regtype == 'f' && (vreg < MONO_MAX_FREGS)))
13527 if (!get_vreg_to_inst (cfg, vreg)) {
13528 if (G_UNLIKELY (cfg->verbose_level > 2))
13529 printf ("VREG R%d used in BB%d and BB%d made global.\n", vreg, vreg_to_bb [vreg], block_num);
13533 if (vreg_is_ref (cfg, vreg))
13534 mono_compile_create_var_for_vreg (cfg, &mono_defaults.object_class->byval_arg, OP_LOCAL, vreg);
13536 mono_compile_create_var_for_vreg (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL, vreg);
13539 mono_compile_create_var_for_vreg (cfg, &mono_defaults.int64_class->byval_arg, OP_LOCAL, vreg);
13542 mono_compile_create_var_for_vreg (cfg, &mono_defaults.double_class->byval_arg, OP_LOCAL, vreg);
13545 mono_compile_create_var_for_vreg (cfg, &ins->klass->byval_arg, OP_LOCAL, vreg);
13548 g_assert_not_reached ();
13552 /* Flag as having been used in more than one bb */
13553 vreg_to_bb [vreg] = -1;
13559 /* If a variable is used in only one bblock, convert it into a local vreg */
13560 for (i = 0; i < cfg->num_varinfo; i++) {
13561 MonoInst *var = cfg->varinfo [i];
13562 MonoMethodVar *vmv = MONO_VARINFO (cfg, i);
13564 switch (var->type) {
13570 #if SIZEOF_REGISTER == 8
13573 #if !defined(TARGET_X86)
13574 /* Enabling this screws up the fp stack on x86 */
13577 if (mono_arch_is_soft_float ())
13580 /* Arguments are implicitly global */
13581 /* Putting R4 vars into registers doesn't work currently */
13582 /* The gsharedvt vars are implicitly referenced by ldaddr opcodes, but those opcodes are only generated later */
13583 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) {
13585 * Make that the variable's liveness interval doesn't contain a call, since
13586 * that would cause the lvreg to be spilled, making the whole optimization
13589 /* This is too slow for JIT compilation */
13591 if (cfg->compile_aot && vreg_to_bb [var->dreg]) {
13593 int def_index, call_index, ins_index;
13594 gboolean spilled = FALSE;
13599 for (ins = vreg_to_bb [var->dreg]->code; ins; ins = ins->next) {
13600 const char *spec = INS_INFO (ins->opcode);
13602 if ((spec [MONO_INST_DEST] != ' ') && (ins->dreg == var->dreg))
13603 def_index = ins_index;
13605 if (((spec [MONO_INST_SRC1] != ' ') && (ins->sreg1 == var->dreg)) ||
13606 ((spec [MONO_INST_SRC1] != ' ') && (ins->sreg1 == var->dreg))) {
13607 if (call_index > def_index) {
13613 if (MONO_IS_CALL (ins))
13614 call_index = ins_index;
13624 if (G_UNLIKELY (cfg->verbose_level > 2))
13625 printf ("CONVERTED R%d(%d) TO VREG.\n", var->dreg, vmv->idx);
13626 var->flags |= MONO_INST_IS_DEAD;
13627 cfg->vreg_to_inst [var->dreg] = NULL;
13634 * Compress the varinfo and vars tables so the liveness computation is faster and
13635 * takes up less space.
13638 for (i = 0; i < cfg->num_varinfo; ++i) {
13639 MonoInst *var = cfg->varinfo [i];
13640 if (pos < i && cfg->locals_start == i)
13641 cfg->locals_start = pos;
13642 if (!(var->flags & MONO_INST_IS_DEAD)) {
13644 cfg->varinfo [pos] = cfg->varinfo [i];
13645 cfg->varinfo [pos]->inst_c0 = pos;
13646 memcpy (&cfg->vars [pos], &cfg->vars [i], sizeof (MonoMethodVar));
13647 cfg->vars [pos].idx = pos;
13648 #if SIZEOF_REGISTER == 4
13649 if (cfg->varinfo [pos]->type == STACK_I8) {
13650 /* Modify the two component vars too */
13653 var1 = get_vreg_to_inst (cfg, cfg->varinfo [pos]->dreg + 1);
13654 var1->inst_c0 = pos;
13655 var1 = get_vreg_to_inst (cfg, cfg->varinfo [pos]->dreg + 2);
13656 var1->inst_c0 = pos;
13663 cfg->num_varinfo = pos;
13664 if (cfg->locals_start > cfg->num_varinfo)
13665 cfg->locals_start = cfg->num_varinfo;
13669 * mono_spill_global_vars:
13671 * Generate spill code for variables which are not allocated to registers,
13672 * and replace vregs with their allocated hregs. *need_local_opts is set to TRUE if
13673 * code is generated which could be optimized by the local optimization passes.
13676 mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local_opts)
13678 MonoBasicBlock *bb;
13680 int orig_next_vreg;
13681 guint32 *vreg_to_lvreg;
13683 guint32 i, lvregs_len;
13684 gboolean dest_has_lvreg = FALSE;
13685 guint32 stacktypes [128];
13686 MonoInst **live_range_start, **live_range_end;
13687 MonoBasicBlock **live_range_start_bb, **live_range_end_bb;
13688 int *gsharedvt_vreg_to_idx = NULL;
13690 *need_local_opts = FALSE;
13692 memset (spec2, 0, sizeof (spec2));
13694 /* FIXME: Move this function to mini.c */
13695 stacktypes ['i'] = STACK_PTR;
13696 stacktypes ['l'] = STACK_I8;
13697 stacktypes ['f'] = STACK_R8;
13698 #ifdef MONO_ARCH_SIMD_INTRINSICS
13699 stacktypes ['x'] = STACK_VTYPE;
13702 #if SIZEOF_REGISTER == 4
13703 /* Create MonoInsts for longs */
13704 for (i = 0; i < cfg->num_varinfo; i++) {
13705 MonoInst *ins = cfg->varinfo [i];
13707 if ((ins->opcode != OP_REGVAR) && !(ins->flags & MONO_INST_IS_DEAD)) {
13708 switch (ins->type) {
13713 if (ins->type == STACK_R8 && !COMPILE_SOFT_FLOAT (cfg))
13716 g_assert (ins->opcode == OP_REGOFFSET);
13718 tree = get_vreg_to_inst (cfg, ins->dreg + 1);
13720 tree->opcode = OP_REGOFFSET;
13721 tree->inst_basereg = ins->inst_basereg;
13722 tree->inst_offset = ins->inst_offset + MINI_LS_WORD_OFFSET;
13724 tree = get_vreg_to_inst (cfg, ins->dreg + 2);
13726 tree->opcode = OP_REGOFFSET;
13727 tree->inst_basereg = ins->inst_basereg;
13728 tree->inst_offset = ins->inst_offset + MINI_MS_WORD_OFFSET;
13738 if (cfg->compute_gc_maps) {
13739 /* registers need liveness info even for !non refs */
13740 for (i = 0; i < cfg->num_varinfo; i++) {
13741 MonoInst *ins = cfg->varinfo [i];
13743 if (ins->opcode == OP_REGVAR)
13744 ins->flags |= MONO_INST_GC_TRACK;
13748 if (cfg->gsharedvt) {
13749 gsharedvt_vreg_to_idx = mono_mempool_alloc0 (cfg->mempool, sizeof (int) * cfg->next_vreg);
13751 for (i = 0; i < cfg->num_varinfo; ++i) {
13752 MonoInst *ins = cfg->varinfo [i];
13755 if (mini_is_gsharedvt_variable_type (cfg, ins->inst_vtype)) {
13756 if (i >= cfg->locals_start) {
13758 idx = get_gsharedvt_info_slot (cfg, ins->inst_vtype, MONO_RGCTX_INFO_LOCAL_OFFSET);
13759 gsharedvt_vreg_to_idx [ins->dreg] = idx + 1;
13760 ins->opcode = OP_GSHAREDVT_LOCAL;
13761 ins->inst_imm = idx;
13764 gsharedvt_vreg_to_idx [ins->dreg] = -1;
13765 ins->opcode = OP_GSHAREDVT_ARG_REGOFFSET;
13771 /* FIXME: widening and truncation */
13774 * As an optimization, when a variable allocated to the stack is first loaded into
13775 * an lvreg, we will remember the lvreg and use it the next time instead of loading
13776 * the variable again.
13778 orig_next_vreg = cfg->next_vreg;
13779 vreg_to_lvreg = mono_mempool_alloc0 (cfg->mempool, sizeof (guint32) * cfg->next_vreg);
13780 lvregs = mono_mempool_alloc (cfg->mempool, sizeof (guint32) * 1024);
13784 * These arrays contain the first and last instructions accessing a given
13786 * Since we emit bblocks in the same order we process them here, and we
13787 * don't split live ranges, these will precisely describe the live range of
13788 * the variable, i.e. the instruction range where a valid value can be found
13789 * in the variables location.
13790 * The live range is computed using the liveness info computed by the liveness pass.
13791 * We can't use vmv->range, since that is an abstract live range, and we need
13792 * one which is instruction precise.
13793 * FIXME: Variables used in out-of-line bblocks have a hole in their live range.
13795 /* FIXME: Only do this if debugging info is requested */
13796 live_range_start = g_new0 (MonoInst*, cfg->next_vreg);
13797 live_range_end = g_new0 (MonoInst*, cfg->next_vreg);
13798 live_range_start_bb = g_new (MonoBasicBlock*, cfg->next_vreg);
13799 live_range_end_bb = g_new (MonoBasicBlock*, cfg->next_vreg);
13801 /* Add spill loads/stores */
13802 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
13805 if (cfg->verbose_level > 2)
13806 printf ("\nSPILL BLOCK %d:\n", bb->block_num);
13808 /* Clear vreg_to_lvreg array */
13809 for (i = 0; i < lvregs_len; i++)
13810 vreg_to_lvreg [lvregs [i]] = 0;
13814 MONO_BB_FOR_EACH_INS (bb, ins) {
13815 const char *spec = INS_INFO (ins->opcode);
13816 int regtype, srcindex, sreg, tmp_reg, prev_dreg, num_sregs;
13817 gboolean store, no_lvreg;
13818 int sregs [MONO_MAX_SRC_REGS];
13820 if (G_UNLIKELY (cfg->verbose_level > 2))
13821 mono_print_ins (ins);
13823 if (ins->opcode == OP_NOP)
13827 * We handle LDADDR here as well, since it can only be decomposed
13828 * when variable addresses are known.
13830 if (ins->opcode == OP_LDADDR) {
13831 MonoInst *var = ins->inst_p0;
13833 if (var->opcode == OP_VTARG_ADDR) {
13834 /* Happens on SPARC/S390 where vtypes are passed by reference */
13835 MonoInst *vtaddr = var->inst_left;
13836 if (vtaddr->opcode == OP_REGVAR) {
13837 ins->opcode = OP_MOVE;
13838 ins->sreg1 = vtaddr->dreg;
13840 else if (var->inst_left->opcode == OP_REGOFFSET) {
13841 ins->opcode = OP_LOAD_MEMBASE;
13842 ins->inst_basereg = vtaddr->inst_basereg;
13843 ins->inst_offset = vtaddr->inst_offset;
13846 } else if (cfg->gsharedvt && gsharedvt_vreg_to_idx [var->dreg] < 0) {
13847 /* gsharedvt arg passed by ref */
13848 g_assert (var->opcode == OP_GSHAREDVT_ARG_REGOFFSET);
13850 ins->opcode = OP_LOAD_MEMBASE;
13851 ins->inst_basereg = var->inst_basereg;
13852 ins->inst_offset = var->inst_offset;
13853 } else if (cfg->gsharedvt && gsharedvt_vreg_to_idx [var->dreg]) {
13854 MonoInst *load, *load2, *load3;
13855 int idx = gsharedvt_vreg_to_idx [var->dreg] - 1;
13856 int reg1, reg2, reg3;
13857 MonoInst *info_var = cfg->gsharedvt_info_var;
13858 MonoInst *locals_var = cfg->gsharedvt_locals_var;
13862 * Compute the address of the local as gsharedvt_locals_var + gsharedvt_info_var->locals_offsets [idx].
13865 g_assert (var->opcode == OP_GSHAREDVT_LOCAL);
13867 g_assert (info_var);
13868 g_assert (locals_var);
13870 /* Mark the instruction used to compute the locals var as used */
13871 cfg->gsharedvt_locals_var_ins = NULL;
13873 /* Load the offset */
13874 if (info_var->opcode == OP_REGOFFSET) {
13875 reg1 = alloc_ireg (cfg);
13876 NEW_LOAD_MEMBASE (cfg, load, OP_LOAD_MEMBASE, reg1, info_var->inst_basereg, info_var->inst_offset);
13877 } else if (info_var->opcode == OP_REGVAR) {
13879 reg1 = info_var->dreg;
13881 g_assert_not_reached ();
13883 reg2 = alloc_ireg (cfg);
13884 NEW_LOAD_MEMBASE (cfg, load2, OP_LOADI4_MEMBASE, reg2, reg1, MONO_STRUCT_OFFSET (MonoGSharedVtMethodRuntimeInfo, entries) + (idx * sizeof (gpointer)));
13885 /* Load the locals area address */
13886 reg3 = alloc_ireg (cfg);
13887 if (locals_var->opcode == OP_REGOFFSET) {
13888 NEW_LOAD_MEMBASE (cfg, load3, OP_LOAD_MEMBASE, reg3, locals_var->inst_basereg, locals_var->inst_offset);
13889 } else if (locals_var->opcode == OP_REGVAR) {
13890 NEW_UNALU (cfg, load3, OP_MOVE, reg3, locals_var->dreg);
13892 g_assert_not_reached ();
13894 /* Compute the address */
13895 ins->opcode = OP_PADD;
13899 mono_bblock_insert_before_ins (bb, ins, load3);
13900 mono_bblock_insert_before_ins (bb, load3, load2);
13902 mono_bblock_insert_before_ins (bb, load2, load);
13904 g_assert (var->opcode == OP_REGOFFSET);
13906 ins->opcode = OP_ADD_IMM;
13907 ins->sreg1 = var->inst_basereg;
13908 ins->inst_imm = var->inst_offset;
13911 *need_local_opts = TRUE;
13912 spec = INS_INFO (ins->opcode);
13915 if (ins->opcode < MONO_CEE_LAST) {
13916 mono_print_ins (ins);
13917 g_assert_not_reached ();
13921 * Store opcodes have destbasereg in the dreg, but in reality, it is an
13925 if (MONO_IS_STORE_MEMBASE (ins)) {
13926 tmp_reg = ins->dreg;
13927 ins->dreg = ins->sreg2;
13928 ins->sreg2 = tmp_reg;
13931 spec2 [MONO_INST_DEST] = ' ';
13932 spec2 [MONO_INST_SRC1] = spec [MONO_INST_SRC1];
13933 spec2 [MONO_INST_SRC2] = spec [MONO_INST_DEST];
13934 spec2 [MONO_INST_SRC3] = ' ';
13936 } else if (MONO_IS_STORE_MEMINDEX (ins))
13937 g_assert_not_reached ();
13942 if (G_UNLIKELY (cfg->verbose_level > 2)) {
13943 printf ("\t %.3s %d", spec, ins->dreg);
13944 num_sregs = mono_inst_get_src_registers (ins, sregs);
13945 for (srcindex = 0; srcindex < num_sregs; ++srcindex)
13946 printf (" %d", sregs [srcindex]);
13953 regtype = spec [MONO_INST_DEST];
13954 g_assert (((ins->dreg == -1) && (regtype == ' ')) || ((ins->dreg != -1) && (regtype != ' ')));
13957 if ((ins->dreg != -1) && get_vreg_to_inst (cfg, ins->dreg)) {
13958 MonoInst *var = get_vreg_to_inst (cfg, ins->dreg);
13959 MonoInst *store_ins;
13961 MonoInst *def_ins = ins;
13962 int dreg = ins->dreg; /* The original vreg */
13964 store_opcode = mono_type_to_store_membase (cfg, var->inst_vtype);
13966 if (var->opcode == OP_REGVAR) {
13967 ins->dreg = var->dreg;
13968 } 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)) {
13970 * Instead of emitting a load+store, use a _membase opcode.
13972 g_assert (var->opcode == OP_REGOFFSET);
13973 if (ins->opcode == OP_MOVE) {
13977 ins->opcode = op_to_op_dest_membase (store_opcode, ins->opcode);
13978 ins->inst_basereg = var->inst_basereg;
13979 ins->inst_offset = var->inst_offset;
13982 spec = INS_INFO (ins->opcode);
13986 g_assert (var->opcode == OP_REGOFFSET);
13988 prev_dreg = ins->dreg;
13990 /* Invalidate any previous lvreg for this vreg */
13991 vreg_to_lvreg [ins->dreg] = 0;
13995 if (COMPILE_SOFT_FLOAT (cfg) && store_opcode == OP_STORER8_MEMBASE_REG) {
13997 store_opcode = OP_STOREI8_MEMBASE_REG;
14000 ins->dreg = alloc_dreg (cfg, stacktypes [regtype]);
14002 #if SIZEOF_REGISTER != 8
14003 if (regtype == 'l') {
14004 NEW_STORE_MEMBASE (cfg, store_ins, OP_STOREI4_MEMBASE_REG, var->inst_basereg, var->inst_offset + MINI_LS_WORD_OFFSET, ins->dreg + 1);
14005 mono_bblock_insert_after_ins (bb, ins, store_ins);
14006 NEW_STORE_MEMBASE (cfg, store_ins, OP_STOREI4_MEMBASE_REG, var->inst_basereg, var->inst_offset + MINI_MS_WORD_OFFSET, ins->dreg + 2);
14007 mono_bblock_insert_after_ins (bb, ins, store_ins);
14008 def_ins = store_ins;
14013 g_assert (store_opcode != OP_STOREV_MEMBASE);
14015 /* Try to fuse the store into the instruction itself */
14016 /* FIXME: Add more instructions */
14017 if (!lvreg && ((ins->opcode == OP_ICONST) || ((ins->opcode == OP_I8CONST) && (ins->inst_c0 == 0)))) {
14018 ins->opcode = store_membase_reg_to_store_membase_imm (store_opcode);
14019 ins->inst_imm = ins->inst_c0;
14020 ins->inst_destbasereg = var->inst_basereg;
14021 ins->inst_offset = var->inst_offset;
14022 spec = INS_INFO (ins->opcode);
14023 } else if (!lvreg && ((ins->opcode == OP_MOVE) || (ins->opcode == OP_FMOVE) || (ins->opcode == OP_LMOVE) || (ins->opcode == OP_RMOVE))) {
14024 ins->opcode = store_opcode;
14025 ins->inst_destbasereg = var->inst_basereg;
14026 ins->inst_offset = var->inst_offset;
14030 tmp_reg = ins->dreg;
14031 ins->dreg = ins->sreg2;
14032 ins->sreg2 = tmp_reg;
14035 spec2 [MONO_INST_DEST] = ' ';
14036 spec2 [MONO_INST_SRC1] = spec [MONO_INST_SRC1];
14037 spec2 [MONO_INST_SRC2] = spec [MONO_INST_DEST];
14038 spec2 [MONO_INST_SRC3] = ' ';
14040 } else if (!lvreg && (op_to_op_store_membase (store_opcode, ins->opcode) != -1)) {
14041 // FIXME: The backends expect the base reg to be in inst_basereg
14042 ins->opcode = op_to_op_store_membase (store_opcode, ins->opcode);
14044 ins->inst_basereg = var->inst_basereg;
14045 ins->inst_offset = var->inst_offset;
14046 spec = INS_INFO (ins->opcode);
14048 /* printf ("INS: "); mono_print_ins (ins); */
14049 /* Create a store instruction */
14050 NEW_STORE_MEMBASE (cfg, store_ins, store_opcode, var->inst_basereg, var->inst_offset, ins->dreg);
14052 /* Insert it after the instruction */
14053 mono_bblock_insert_after_ins (bb, ins, store_ins);
14055 def_ins = store_ins;
14058 * We can't assign ins->dreg to var->dreg here, since the
14059 * sregs could use it. So set a flag, and do it after
14062 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)))
14063 dest_has_lvreg = TRUE;
14068 if (def_ins && !live_range_start [dreg]) {
14069 live_range_start [dreg] = def_ins;
14070 live_range_start_bb [dreg] = bb;
14073 if (cfg->compute_gc_maps && def_ins && (var->flags & MONO_INST_GC_TRACK)) {
14076 MONO_INST_NEW (cfg, tmp, OP_GC_LIVENESS_DEF);
14077 tmp->inst_c1 = dreg;
14078 mono_bblock_insert_after_ins (bb, def_ins, tmp);
14085 num_sregs = mono_inst_get_src_registers (ins, sregs);
14086 for (srcindex = 0; srcindex < 3; ++srcindex) {
14087 regtype = spec [MONO_INST_SRC1 + srcindex];
14088 sreg = sregs [srcindex];
14090 g_assert (((sreg == -1) && (regtype == ' ')) || ((sreg != -1) && (regtype != ' ')));
14091 if ((sreg != -1) && get_vreg_to_inst (cfg, sreg)) {
14092 MonoInst *var = get_vreg_to_inst (cfg, sreg);
14093 MonoInst *use_ins = ins;
14094 MonoInst *load_ins;
14095 guint32 load_opcode;
14097 if (var->opcode == OP_REGVAR) {
14098 sregs [srcindex] = var->dreg;
14099 //mono_inst_set_src_registers (ins, sregs);
14100 live_range_end [sreg] = use_ins;
14101 live_range_end_bb [sreg] = bb;
14103 if (cfg->compute_gc_maps && var->dreg < orig_next_vreg && (var->flags & MONO_INST_GC_TRACK)) {
14106 MONO_INST_NEW (cfg, tmp, OP_GC_LIVENESS_USE);
14107 /* var->dreg is a hreg */
14108 tmp->inst_c1 = sreg;
14109 mono_bblock_insert_after_ins (bb, ins, tmp);
14115 g_assert (var->opcode == OP_REGOFFSET);
14117 load_opcode = mono_type_to_load_membase (cfg, var->inst_vtype);
14119 g_assert (load_opcode != OP_LOADV_MEMBASE);
14121 if (vreg_to_lvreg [sreg]) {
14122 g_assert (vreg_to_lvreg [sreg] != -1);
14124 /* The variable is already loaded to an lvreg */
14125 if (G_UNLIKELY (cfg->verbose_level > 2))
14126 printf ("\t\tUse lvreg R%d for R%d.\n", vreg_to_lvreg [sreg], sreg);
14127 sregs [srcindex] = vreg_to_lvreg [sreg];
14128 //mono_inst_set_src_registers (ins, sregs);
14132 /* Try to fuse the load into the instruction */
14133 if ((srcindex == 0) && (op_to_op_src1_membase (load_opcode, ins->opcode) != -1)) {
14134 ins->opcode = op_to_op_src1_membase (load_opcode, ins->opcode);
14135 sregs [0] = var->inst_basereg;
14136 //mono_inst_set_src_registers (ins, sregs);
14137 ins->inst_offset = var->inst_offset;
14138 } else if ((srcindex == 1) && (op_to_op_src2_membase (load_opcode, ins->opcode) != -1)) {
14139 ins->opcode = op_to_op_src2_membase (load_opcode, ins->opcode);
14140 sregs [1] = var->inst_basereg;
14141 //mono_inst_set_src_registers (ins, sregs);
14142 ins->inst_offset = var->inst_offset;
14144 if (MONO_IS_REAL_MOVE (ins)) {
14145 ins->opcode = OP_NOP;
14148 //printf ("%d ", srcindex); mono_print_ins (ins);
14150 sreg = alloc_dreg (cfg, stacktypes [regtype]);
14152 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) {
14153 if (var->dreg == prev_dreg) {
14155 * sreg refers to the value loaded by the load
14156 * emitted below, but we need to use ins->dreg
14157 * since it refers to the store emitted earlier.
14161 g_assert (sreg != -1);
14162 vreg_to_lvreg [var->dreg] = sreg;
14163 g_assert (lvregs_len < 1024);
14164 lvregs [lvregs_len ++] = var->dreg;
14168 sregs [srcindex] = sreg;
14169 //mono_inst_set_src_registers (ins, sregs);
14171 #if SIZEOF_REGISTER != 8
14172 if (regtype == 'l') {
14173 NEW_LOAD_MEMBASE (cfg, load_ins, OP_LOADI4_MEMBASE, sreg + 2, var->inst_basereg, var->inst_offset + MINI_MS_WORD_OFFSET);
14174 mono_bblock_insert_before_ins (bb, ins, load_ins);
14175 NEW_LOAD_MEMBASE (cfg, load_ins, OP_LOADI4_MEMBASE, sreg + 1, var->inst_basereg, var->inst_offset + MINI_LS_WORD_OFFSET);
14176 mono_bblock_insert_before_ins (bb, ins, load_ins);
14177 use_ins = load_ins;
14182 #if SIZEOF_REGISTER == 4
14183 g_assert (load_opcode != OP_LOADI8_MEMBASE);
14185 NEW_LOAD_MEMBASE (cfg, load_ins, load_opcode, sreg, var->inst_basereg, var->inst_offset);
14186 mono_bblock_insert_before_ins (bb, ins, load_ins);
14187 use_ins = load_ins;
14191 if (var->dreg < orig_next_vreg) {
14192 live_range_end [var->dreg] = use_ins;
14193 live_range_end_bb [var->dreg] = bb;
14196 if (cfg->compute_gc_maps && var->dreg < orig_next_vreg && (var->flags & MONO_INST_GC_TRACK)) {
14199 MONO_INST_NEW (cfg, tmp, OP_GC_LIVENESS_USE);
14200 tmp->inst_c1 = var->dreg;
14201 mono_bblock_insert_after_ins (bb, ins, tmp);
14205 mono_inst_set_src_registers (ins, sregs);
14207 if (dest_has_lvreg) {
14208 g_assert (ins->dreg != -1);
14209 vreg_to_lvreg [prev_dreg] = ins->dreg;
14210 g_assert (lvregs_len < 1024);
14211 lvregs [lvregs_len ++] = prev_dreg;
14212 dest_has_lvreg = FALSE;
14216 tmp_reg = ins->dreg;
14217 ins->dreg = ins->sreg2;
14218 ins->sreg2 = tmp_reg;
14221 if (MONO_IS_CALL (ins)) {
14222 /* Clear vreg_to_lvreg array */
14223 for (i = 0; i < lvregs_len; i++)
14224 vreg_to_lvreg [lvregs [i]] = 0;
14226 } else if (ins->opcode == OP_NOP) {
14228 MONO_INST_NULLIFY_SREGS (ins);
14231 if (cfg->verbose_level > 2)
14232 mono_print_ins_index (1, ins);
14235 /* Extend the live range based on the liveness info */
14236 if (cfg->compute_precise_live_ranges && bb->live_out_set && bb->code) {
14237 for (i = 0; i < cfg->num_varinfo; i ++) {
14238 MonoMethodVar *vi = MONO_VARINFO (cfg, i);
14240 if (vreg_is_volatile (cfg, vi->vreg))
14241 /* The liveness info is incomplete */
14244 if (mono_bitset_test_fast (bb->live_in_set, i) && !live_range_start [vi->vreg]) {
14245 /* Live from at least the first ins of this bb */
14246 live_range_start [vi->vreg] = bb->code;
14247 live_range_start_bb [vi->vreg] = bb;
14250 if (mono_bitset_test_fast (bb->live_out_set, i)) {
14251 /* Live at least until the last ins of this bb */
14252 live_range_end [vi->vreg] = bb->last_ins;
14253 live_range_end_bb [vi->vreg] = bb;
14259 #ifdef MONO_ARCH_HAVE_LIVERANGE_OPS
14261 * Emit LIVERANGE_START/LIVERANGE_END opcodes, the backend will implement them
14262 * by storing the current native offset into MonoMethodVar->live_range_start/end.
14264 if (cfg->compute_precise_live_ranges && cfg->comp_done & MONO_COMP_LIVENESS) {
14265 for (i = 0; i < cfg->num_varinfo; ++i) {
14266 int vreg = MONO_VARINFO (cfg, i)->vreg;
14269 if (live_range_start [vreg]) {
14270 MONO_INST_NEW (cfg, ins, OP_LIVERANGE_START);
14272 ins->inst_c1 = vreg;
14273 mono_bblock_insert_after_ins (live_range_start_bb [vreg], live_range_start [vreg], ins);
14275 if (live_range_end [vreg]) {
14276 MONO_INST_NEW (cfg, ins, OP_LIVERANGE_END);
14278 ins->inst_c1 = vreg;
14279 if (live_range_end [vreg] == live_range_end_bb [vreg]->last_ins)
14280 mono_add_ins_to_end (live_range_end_bb [vreg], ins);
14282 mono_bblock_insert_after_ins (live_range_end_bb [vreg], live_range_end [vreg], ins);
14288 if (cfg->gsharedvt_locals_var_ins) {
14289 /* Nullify if unused */
14290 cfg->gsharedvt_locals_var_ins->opcode = OP_PCONST;
14291 cfg->gsharedvt_locals_var_ins->inst_imm = 0;
14294 g_free (live_range_start);
14295 g_free (live_range_end);
14296 g_free (live_range_start_bb);
14297 g_free (live_range_end_bb);
14302 * - use 'iadd' instead of 'int_add'
14303 * - handling ovf opcodes: decompose in method_to_ir.
14304 * - unify iregs/fregs
14305 * -> partly done, the missing parts are:
14306 * - a more complete unification would involve unifying the hregs as well, so
14307 * code wouldn't need if (fp) all over the place. but that would mean the hregs
14308 * would no longer map to the machine hregs, so the code generators would need to
14309 * be modified. Also, on ia64 for example, niregs + nfregs > 256 -> bitmasks
14310 * wouldn't work any more. Duplicating the code in mono_local_regalloc () into
14311 * fp/non-fp branches speeds it up by about 15%.
14312 * - use sext/zext opcodes instead of shifts
14314 * - get rid of TEMPLOADs if possible and use vregs instead
14315 * - clean up usage of OP_P/OP_ opcodes
14316 * - cleanup usage of DUMMY_USE
14317 * - cleanup the setting of ins->type for MonoInst's which are pushed on the
14319 * - set the stack type and allocate a dreg in the EMIT_NEW macros
14320 * - get rid of all the <foo>2 stuff when the new JIT is ready.
14321 * - make sure handle_stack_args () is called before the branch is emitted
14322 * - when the new IR is done, get rid of all unused stuff
14323 * - COMPARE/BEQ as separate instructions or unify them ?
14324 * - keeping them separate allows specialized compare instructions like
14325 * compare_imm, compare_membase
14326 * - most back ends unify fp compare+branch, fp compare+ceq
14327 * - integrate mono_save_args into inline_method
14328 * - get rid of the empty bblocks created by MONO_EMIT_NEW_BRACH_BLOCK2
14329 * - handle long shift opts on 32 bit platforms somehow: they require
14330 * 3 sregs (2 for arg1 and 1 for arg2)
14331 * - make byref a 'normal' type.
14332 * - use vregs for bb->out_stacks if possible, handle_global_vreg will make them a
14333 * variable if needed.
14334 * - do not start a new IL level bblock when cfg->cbb is changed by a function call
14335 * like inline_method.
14336 * - remove inlining restrictions
14337 * - fix LNEG and enable cfold of INEG
14338 * - generalize x86 optimizations like ldelema as a peephole optimization
14339 * - add store_mem_imm for amd64
14340 * - optimize the loading of the interruption flag in the managed->native wrappers
14341 * - avoid special handling of OP_NOP in passes
14342 * - move code inserting instructions into one function/macro.
14343 * - try a coalescing phase after liveness analysis
14344 * - add float -> vreg conversion + local optimizations on !x86
14345 * - figure out how to handle decomposed branches during optimizations, ie.
14346 * compare+branch, op_jump_table+op_br etc.
14347 * - promote RuntimeXHandles to vregs
14348 * - vtype cleanups:
14349 * - add a NEW_VARLOADA_VREG macro
14350 * - the vtype optimizations are blocked by the LDADDR opcodes generated for
14351 * accessing vtype fields.
14352 * - get rid of I8CONST on 64 bit platforms
14353 * - dealing with the increase in code size due to branches created during opcode
14355 * - use extended basic blocks
14356 * - all parts of the JIT
14357 * - handle_global_vregs () && local regalloc
14358 * - avoid introducing global vregs during decomposition, like 'vtable' in isinst
14359 * - sources of increase in code size:
14362 * - isinst and castclass
14363 * - lvregs not allocated to global registers even if used multiple times
14364 * - call cctors outside the JIT, to make -v output more readable and JIT timings more
14366 * - check for fp stack leakage in other opcodes too. (-> 'exceptions' optimization)
14367 * - add all micro optimizations from the old JIT
14368 * - put tree optimizations into the deadce pass
14369 * - decompose op_start_handler/op_endfilter/op_endfinally earlier using an arch
14370 * specific function.
14371 * - unify the float comparison opcodes with the other comparison opcodes, i.e.
14372 * fcompare + branchCC.
14373 * - create a helper function for allocating a stack slot, taking into account
14374 * MONO_CFG_HAS_SPILLUP.
14376 * - merge the ia64 switch changes.
14377 * - optimize mono_regstate2_alloc_int/float.
14378 * - fix the pessimistic handling of variables accessed in exception handler blocks.
14379 * - need to write a tree optimization pass, but the creation of trees is difficult, i.e.
14380 * parts of the tree could be separated by other instructions, killing the tree
14381 * arguments, or stores killing loads etc. Also, should we fold loads into other
14382 * instructions if the result of the load is used multiple times ?
14383 * - make the REM_IMM optimization in mini-x86.c arch-independent.
14384 * - LAST MERGE: 108395.
14385 * - when returning vtypes in registers, generate IR and append it to the end of the
14386 * last bb instead of doing it in the epilog.
14387 * - change the store opcodes so they use sreg1 instead of dreg to store the base register.
14395 - When to decompose opcodes:
14396 - earlier: this makes some optimizations hard to implement, since the low level IR
14397 no longer contains the neccessary information. But it is easier to do.
14398 - later: harder to implement, enables more optimizations.
14399 - Branches inside bblocks:
14400 - created when decomposing complex opcodes.
14401 - branches to another bblock: harmless, but not tracked by the branch
14402 optimizations, so need to branch to a label at the start of the bblock.
14403 - branches to inside the same bblock: very problematic, trips up the local
14404 reg allocator. Can be fixed by spitting the current bblock, but that is a
14405 complex operation, since some local vregs can become global vregs etc.
14406 - Local/global vregs:
14407 - local vregs: temporary vregs used inside one bblock. Assigned to hregs by the
14408 local register allocator.
14409 - global vregs: used in more than one bblock. Have an associated MonoMethodVar
14410 structure, created by mono_create_var (). Assigned to hregs or the stack by
14411 the global register allocator.
14412 - When to do optimizations like alu->alu_imm:
14413 - earlier -> saves work later on since the IR will be smaller/simpler
14414 - later -> can work on more instructions
14415 - Handling of valuetypes:
14416 - When a vtype is pushed on the stack, a new temporary is created, an
14417 instruction computing its address (LDADDR) is emitted and pushed on
14418 the stack. Need to optimize cases when the vtype is used immediately as in
14419 argument passing, stloc etc.
14420 - Instead of the to_end stuff in the old JIT, simply call the function handling
14421 the values on the stack before emitting the last instruction of the bb.
14424 #endif /* DISABLE_JIT */