3 * Convert CIL to the JIT internal representation
6 * Paolo Molaro (lupus@ximian.com)
7 * Dietmar Maurer (dietmar@ximian.com)
9 * (C) 2002 Ximian, Inc.
10 * Copyright 2003-2010 Novell, Inc (http://www.novell.com)
11 * Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
12 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
16 #include <mono/utils/mono-compiler.h>
31 #ifdef HAVE_SYS_TIME_H
39 #include <mono/utils/memcheck.h>
40 #include <mono/metadata/abi-details.h>
41 #include <mono/metadata/assembly.h>
42 #include <mono/metadata/attrdefs.h>
43 #include <mono/metadata/loader.h>
44 #include <mono/metadata/tabledefs.h>
45 #include <mono/metadata/class.h>
46 #include <mono/metadata/object.h>
47 #include <mono/metadata/exception.h>
48 #include <mono/metadata/opcodes.h>
49 #include <mono/metadata/mono-endian.h>
50 #include <mono/metadata/tokentype.h>
51 #include <mono/metadata/tabledefs.h>
52 #include <mono/metadata/marshal.h>
53 #include <mono/metadata/debug-helpers.h>
54 #include <mono/metadata/debug-internals.h>
55 #include <mono/metadata/gc-internals.h>
56 #include <mono/metadata/security-manager.h>
57 #include <mono/metadata/threads-types.h>
58 #include <mono/metadata/security-core-clr.h>
59 #include <mono/metadata/profiler-private.h>
60 #include <mono/metadata/profiler.h>
61 #include <mono/metadata/monitor.h>
62 #include <mono/utils/mono-memory-model.h>
63 #include <mono/utils/mono-error-internals.h>
64 #include <mono/metadata/mono-basic-block.h>
65 #include <mono/metadata/reflection-internals.h>
66 #include <mono/utils/mono-threads-coop.h>
72 #include "jit-icalls.h"
74 #include "debugger-agent.h"
75 #include "seq-points.h"
76 #include "aot-compiler.h"
77 #include "mini-llvm.h"
79 #define BRANCH_COST 10
80 #define INLINE_LENGTH_LIMIT 20
82 /* These have 'cfg' as an implicit argument */
83 #define INLINE_FAILURE(msg) do { \
84 if ((cfg->method != cfg->current_method) && (cfg->current_method->wrapper_type == MONO_WRAPPER_NONE)) { \
85 inline_failure (cfg, msg); \
86 goto exception_exit; \
89 #define CHECK_CFG_EXCEPTION do {\
90 if (cfg->exception_type != MONO_EXCEPTION_NONE) \
91 goto exception_exit; \
93 #define FIELD_ACCESS_FAILURE(method, field) do { \
94 field_access_failure ((cfg), (method), (field)); \
95 goto exception_exit; \
97 #define GENERIC_SHARING_FAILURE(opcode) do { \
99 gshared_failure (cfg, opcode, __FILE__, __LINE__); \
100 goto exception_exit; \
103 #define GSHAREDVT_FAILURE(opcode) do { \
104 if (cfg->gsharedvt) { \
105 gsharedvt_failure (cfg, opcode, __FILE__, __LINE__); \
106 goto exception_exit; \
109 #define OUT_OF_MEMORY_FAILURE do { \
110 mono_cfg_set_exception (cfg, MONO_EXCEPTION_MONO_ERROR); \
111 mono_error_set_out_of_memory (&cfg->error, ""); \
112 goto exception_exit; \
114 #define DISABLE_AOT(cfg) do { \
115 if ((cfg)->verbose_level >= 2) \
116 printf ("AOT disabled: %s:%d\n", __FILE__, __LINE__); \
117 (cfg)->disable_aot = TRUE; \
119 #define LOAD_ERROR do { \
120 break_on_unverified (); \
121 mono_cfg_set_exception (cfg, MONO_EXCEPTION_TYPE_LOAD); \
122 goto exception_exit; \
125 #define TYPE_LOAD_ERROR(klass) do { \
126 cfg->exception_ptr = klass; \
130 #define CHECK_CFG_ERROR do {\
131 if (!mono_error_ok (&cfg->error)) { \
132 mono_cfg_set_exception (cfg, MONO_EXCEPTION_MONO_ERROR); \
133 goto mono_error_exit; \
137 /* Determine whenever 'ins' represents a load of the 'this' argument */
138 #define MONO_CHECK_THIS(ins) (mono_method_signature (cfg->method)->hasthis && ((ins)->opcode == OP_MOVE) && ((ins)->sreg1 == cfg->args [0]->dreg))
140 static int ldind_to_load_membase (int opcode);
141 static int stind_to_store_membase (int opcode);
143 int mono_op_to_op_imm (int opcode);
144 int mono_op_to_op_imm_noemul (int opcode);
146 static int inline_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **sp,
147 guchar *ip, guint real_offset, gboolean inline_always);
149 emit_llvmonly_virtual_call (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, int context_used, MonoInst **sp);
151 /* helper methods signatures */
152 static MonoMethodSignature *helper_sig_domain_get;
153 static MonoMethodSignature *helper_sig_rgctx_lazy_fetch_trampoline;
154 static MonoMethodSignature *helper_sig_llvmonly_imt_trampoline;
155 static MonoMethodSignature *helper_sig_jit_thread_attach;
156 static MonoMethodSignature *helper_sig_get_tls_tramp;
157 static MonoMethodSignature *helper_sig_set_tls_tramp;
159 /* type loading helpers */
160 static GENERATE_GET_CLASS_WITH_CACHE (runtime_helpers, "System.Runtime.CompilerServices", "RuntimeHelpers")
161 static GENERATE_TRY_GET_CLASS_WITH_CACHE (debuggable_attribute, "System.Diagnostics", "DebuggableAttribute")
164 * Instruction metadata
172 #define MINI_OP(a,b,dest,src1,src2) dest, src1, src2, ' ',
173 #define MINI_OP3(a,b,dest,src1,src2,src3) dest, src1, src2, src3,
179 #if SIZEOF_REGISTER == 8 && SIZEOF_REGISTER == SIZEOF_VOID_P
184 /* keep in sync with the enum in mini.h */
187 #include "mini-ops.h"
192 #define MINI_OP(a,b,dest,src1,src2) ((src2) != NONE ? 2 : ((src1) != NONE ? 1 : 0)),
193 #define MINI_OP3(a,b,dest,src1,src2,src3) ((src3) != NONE ? 3 : ((src2) != NONE ? 2 : ((src1) != NONE ? 1 : 0))),
195 * This should contain the index of the last sreg + 1. This is not the same
196 * as the number of sregs for opcodes like IA64_CMP_EQ_IMM.
198 const gint8 ins_sreg_counts[] = {
199 #include "mini-ops.h"
205 mono_alloc_ireg (MonoCompile *cfg)
207 return alloc_ireg (cfg);
211 mono_alloc_lreg (MonoCompile *cfg)
213 return alloc_lreg (cfg);
217 mono_alloc_freg (MonoCompile *cfg)
219 return alloc_freg (cfg);
223 mono_alloc_preg (MonoCompile *cfg)
225 return alloc_preg (cfg);
229 mono_alloc_dreg (MonoCompile *cfg, MonoStackType stack_type)
231 return alloc_dreg (cfg, stack_type);
235 * mono_alloc_ireg_ref:
237 * Allocate an IREG, and mark it as holding a GC ref.
240 mono_alloc_ireg_ref (MonoCompile *cfg)
242 return alloc_ireg_ref (cfg);
246 * mono_alloc_ireg_mp:
248 * Allocate an IREG, and mark it as holding a managed pointer.
251 mono_alloc_ireg_mp (MonoCompile *cfg)
253 return alloc_ireg_mp (cfg);
257 * mono_alloc_ireg_copy:
259 * Allocate an IREG with the same GC type as VREG.
262 mono_alloc_ireg_copy (MonoCompile *cfg, guint32 vreg)
264 if (vreg_is_ref (cfg, vreg))
265 return alloc_ireg_ref (cfg);
266 else if (vreg_is_mp (cfg, vreg))
267 return alloc_ireg_mp (cfg);
269 return alloc_ireg (cfg);
273 mono_type_to_regmove (MonoCompile *cfg, MonoType *type)
278 type = mini_get_underlying_type (type);
280 switch (type->type) {
293 case MONO_TYPE_FNPTR:
295 case MONO_TYPE_CLASS:
296 case MONO_TYPE_STRING:
297 case MONO_TYPE_OBJECT:
298 case MONO_TYPE_SZARRAY:
299 case MONO_TYPE_ARRAY:
303 #if SIZEOF_REGISTER == 8
309 return cfg->r4fp ? OP_RMOVE : OP_FMOVE;
312 case MONO_TYPE_VALUETYPE:
313 if (type->data.klass->enumtype) {
314 type = mono_class_enum_basetype (type->data.klass);
317 if (MONO_CLASS_IS_SIMD (cfg, mono_class_from_mono_type (type)))
320 case MONO_TYPE_TYPEDBYREF:
322 case MONO_TYPE_GENERICINST:
323 if (MONO_CLASS_IS_SIMD (cfg, mono_class_from_mono_type (type)))
325 type = &type->data.generic_class->container_class->byval_arg;
329 g_assert (cfg->gshared);
330 if (mini_type_var_is_vt (type))
333 return mono_type_to_regmove (cfg, mini_get_underlying_type (type));
335 g_error ("unknown type 0x%02x in type_to_regstore", type->type);
341 mono_print_bb (MonoBasicBlock *bb, const char *msg)
345 GString *str = g_string_new ("");
347 g_string_append_printf (str, "%s %d: [IN: ", msg, bb->block_num);
348 for (i = 0; i < bb->in_count; ++i)
349 g_string_append_printf (str, " BB%d(%d)", bb->in_bb [i]->block_num, bb->in_bb [i]->dfn);
350 g_string_append_printf (str, ", OUT: ");
351 for (i = 0; i < bb->out_count; ++i)
352 g_string_append_printf (str, " BB%d(%d)", bb->out_bb [i]->block_num, bb->out_bb [i]->dfn);
353 g_string_append_printf (str, " ]\n");
355 g_print ("%s", str->str);
356 g_string_free (str, TRUE);
358 for (tree = bb->code; tree; tree = tree->next)
359 mono_print_ins_index (-1, tree);
363 mono_create_helper_signatures (void)
365 helper_sig_domain_get = mono_create_icall_signature ("ptr");
366 helper_sig_rgctx_lazy_fetch_trampoline = mono_create_icall_signature ("ptr ptr");
367 helper_sig_llvmonly_imt_trampoline = mono_create_icall_signature ("ptr ptr ptr");
368 helper_sig_jit_thread_attach = mono_create_icall_signature ("ptr ptr");
369 helper_sig_get_tls_tramp = mono_create_icall_signature ("ptr");
370 helper_sig_set_tls_tramp = mono_create_icall_signature ("void ptr");
373 static MONO_NEVER_INLINE void
374 break_on_unverified (void)
376 if (mini_get_debug_options ()->break_on_unverified)
380 static MONO_NEVER_INLINE void
381 field_access_failure (MonoCompile *cfg, MonoMethod *method, MonoClassField *field)
383 char *method_fname = mono_method_full_name (method, TRUE);
384 char *field_fname = mono_field_full_name (field);
385 mono_cfg_set_exception (cfg, MONO_EXCEPTION_MONO_ERROR);
386 mono_error_set_generic_error (&cfg->error, "System", "FieldAccessException", "Field `%s' is inaccessible from method `%s'\n", field_fname, method_fname);
387 g_free (method_fname);
388 g_free (field_fname);
391 static MONO_NEVER_INLINE void
392 inline_failure (MonoCompile *cfg, const char *msg)
394 if (cfg->verbose_level >= 2)
395 printf ("inline failed: %s\n", msg);
396 mono_cfg_set_exception (cfg, MONO_EXCEPTION_INLINE_FAILED);
399 static MONO_NEVER_INLINE void
400 gshared_failure (MonoCompile *cfg, int opcode, const char *file, int line)
402 if (cfg->verbose_level > 2) \
403 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);
404 mono_cfg_set_exception (cfg, MONO_EXCEPTION_GENERIC_SHARING_FAILED);
407 static MONO_NEVER_INLINE void
408 gsharedvt_failure (MonoCompile *cfg, int opcode, const char *file, int line)
410 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);
411 if (cfg->verbose_level >= 2)
412 printf ("%s\n", cfg->exception_message);
413 mono_cfg_set_exception (cfg, MONO_EXCEPTION_GENERIC_SHARING_FAILED);
417 * When using gsharedvt, some instatiations might be verifiable, and some might be not. i.e.
418 * foo<T> (int i) { ldarg.0; box T; }
420 #define UNVERIFIED do { \
421 if (cfg->gsharedvt) { \
422 if (cfg->verbose_level > 2) \
423 printf ("gsharedvt method failed to verify, falling back to instantiation.\n"); \
424 mono_cfg_set_exception (cfg, MONO_EXCEPTION_GENERIC_SHARING_FAILED); \
425 goto exception_exit; \
427 break_on_unverified (); \
431 #define GET_BBLOCK(cfg,tblock,ip) do { \
432 (tblock) = cfg->cil_offset_to_bb [(ip) - cfg->cil_start]; \
434 if ((ip) >= end || (ip) < header->code) UNVERIFIED; \
435 NEW_BBLOCK (cfg, (tblock)); \
436 (tblock)->cil_code = (ip); \
437 ADD_BBLOCK (cfg, (tblock)); \
441 #if defined(TARGET_X86) || defined(TARGET_AMD64)
442 #define EMIT_NEW_X86_LEA(cfg,dest,sr1,sr2,shift,imm) do { \
443 MONO_INST_NEW (cfg, dest, OP_X86_LEA); \
444 (dest)->dreg = alloc_ireg_mp ((cfg)); \
445 (dest)->sreg1 = (sr1); \
446 (dest)->sreg2 = (sr2); \
447 (dest)->inst_imm = (imm); \
448 (dest)->backend.shift_amount = (shift); \
449 MONO_ADD_INS ((cfg)->cbb, (dest)); \
453 /* Emit conversions so both operands of a binary opcode are of the same type */
455 add_widen_op (MonoCompile *cfg, MonoInst *ins, MonoInst **arg1_ref, MonoInst **arg2_ref)
457 MonoInst *arg1 = *arg1_ref;
458 MonoInst *arg2 = *arg2_ref;
461 ((arg1->type == STACK_R4 && arg2->type == STACK_R8) ||
462 (arg1->type == STACK_R8 && arg2->type == STACK_R4))) {
465 /* Mixing r4/r8 is allowed by the spec */
466 if (arg1->type == STACK_R4) {
467 int dreg = alloc_freg (cfg);
469 EMIT_NEW_UNALU (cfg, conv, OP_RCONV_TO_R8, dreg, arg1->dreg);
470 conv->type = STACK_R8;
474 if (arg2->type == STACK_R4) {
475 int dreg = alloc_freg (cfg);
477 EMIT_NEW_UNALU (cfg, conv, OP_RCONV_TO_R8, dreg, arg2->dreg);
478 conv->type = STACK_R8;
484 #if SIZEOF_REGISTER == 8
485 /* FIXME: Need to add many more cases */
486 if ((arg1)->type == STACK_PTR && (arg2)->type == STACK_I4) {
489 int dr = alloc_preg (cfg);
490 EMIT_NEW_UNALU (cfg, widen, OP_SEXT_I4, dr, (arg2)->dreg);
491 (ins)->sreg2 = widen->dreg;
496 #define ADD_BINOP(op) do { \
497 MONO_INST_NEW (cfg, ins, (op)); \
499 ins->sreg1 = sp [0]->dreg; \
500 ins->sreg2 = sp [1]->dreg; \
501 type_from_op (cfg, ins, sp [0], sp [1]); \
503 /* Have to insert a widening op */ \
504 add_widen_op (cfg, ins, &sp [0], &sp [1]); \
505 ins->dreg = alloc_dreg ((cfg), (MonoStackType)(ins)->type); \
506 MONO_ADD_INS ((cfg)->cbb, (ins)); \
507 *sp++ = mono_decompose_opcode ((cfg), (ins)); \
510 #define ADD_UNOP(op) do { \
511 MONO_INST_NEW (cfg, ins, (op)); \
513 ins->sreg1 = sp [0]->dreg; \
514 type_from_op (cfg, ins, sp [0], NULL); \
516 (ins)->dreg = alloc_dreg ((cfg), (MonoStackType)(ins)->type); \
517 MONO_ADD_INS ((cfg)->cbb, (ins)); \
518 *sp++ = mono_decompose_opcode (cfg, ins); \
521 #define ADD_BINCOND(next_block) do { \
524 MONO_INST_NEW(cfg, cmp, OP_COMPARE); \
525 cmp->sreg1 = sp [0]->dreg; \
526 cmp->sreg2 = sp [1]->dreg; \
527 type_from_op (cfg, cmp, sp [0], sp [1]); \
529 add_widen_op (cfg, cmp, &sp [0], &sp [1]); \
530 type_from_op (cfg, ins, sp [0], sp [1]); \
531 ins->inst_many_bb = (MonoBasicBlock **)mono_mempool_alloc (cfg->mempool, sizeof(gpointer)*2); \
532 GET_BBLOCK (cfg, tblock, target); \
533 link_bblock (cfg, cfg->cbb, tblock); \
534 ins->inst_true_bb = tblock; \
535 if ((next_block)) { \
536 link_bblock (cfg, cfg->cbb, (next_block)); \
537 ins->inst_false_bb = (next_block); \
538 start_new_bblock = 1; \
540 GET_BBLOCK (cfg, tblock, ip); \
541 link_bblock (cfg, cfg->cbb, tblock); \
542 ins->inst_false_bb = tblock; \
543 start_new_bblock = 2; \
545 if (sp != stack_start) { \
546 handle_stack_args (cfg, stack_start, sp - stack_start); \
547 CHECK_UNVERIFIABLE (cfg); \
549 MONO_ADD_INS (cfg->cbb, cmp); \
550 MONO_ADD_INS (cfg->cbb, ins); \
554 * link_bblock: Links two basic blocks
556 * links two basic blocks in the control flow graph, the 'from'
557 * argument is the starting block and the 'to' argument is the block
558 * the control flow ends to after 'from'.
561 link_bblock (MonoCompile *cfg, MonoBasicBlock *from, MonoBasicBlock* to)
563 MonoBasicBlock **newa;
567 if (from->cil_code) {
569 printf ("edge from IL%04x to IL_%04x\n", from->cil_code - cfg->cil_code, to->cil_code - cfg->cil_code);
571 printf ("edge from IL%04x to exit\n", from->cil_code - cfg->cil_code);
574 printf ("edge from entry to IL_%04x\n", to->cil_code - cfg->cil_code);
576 printf ("edge from entry to exit\n");
581 for (i = 0; i < from->out_count; ++i) {
582 if (to == from->out_bb [i]) {
588 newa = (MonoBasicBlock **)mono_mempool_alloc (cfg->mempool, sizeof (gpointer) * (from->out_count + 1));
589 for (i = 0; i < from->out_count; ++i) {
590 newa [i] = from->out_bb [i];
598 for (i = 0; i < to->in_count; ++i) {
599 if (from == to->in_bb [i]) {
605 newa = (MonoBasicBlock **)mono_mempool_alloc (cfg->mempool, sizeof (gpointer) * (to->in_count + 1));
606 for (i = 0; i < to->in_count; ++i) {
607 newa [i] = to->in_bb [i];
616 mono_link_bblock (MonoCompile *cfg, MonoBasicBlock *from, MonoBasicBlock* to)
618 link_bblock (cfg, from, to);
622 * mono_find_block_region:
624 * We mark each basic block with a region ID. We use that to avoid BB
625 * optimizations when blocks are in different regions.
628 * A region token that encodes where this region is, and information
629 * about the clause owner for this block.
631 * The region encodes the try/catch/filter clause that owns this block
632 * as well as the type. -1 is a special value that represents a block
633 * that is in none of try/catch/filter.
636 mono_find_block_region (MonoCompile *cfg, int offset)
638 MonoMethodHeader *header = cfg->header;
639 MonoExceptionClause *clause;
642 for (i = 0; i < header->num_clauses; ++i) {
643 clause = &header->clauses [i];
644 if ((clause->flags == MONO_EXCEPTION_CLAUSE_FILTER) && (offset >= clause->data.filter_offset) &&
645 (offset < (clause->handler_offset)))
646 return ((i + 1) << 8) | MONO_REGION_FILTER | clause->flags;
648 if (MONO_OFFSET_IN_HANDLER (clause, offset)) {
649 if (clause->flags == MONO_EXCEPTION_CLAUSE_FINALLY)
650 return ((i + 1) << 8) | MONO_REGION_FINALLY | clause->flags;
651 else if (clause->flags == MONO_EXCEPTION_CLAUSE_FAULT)
652 return ((i + 1) << 8) | MONO_REGION_FAULT | clause->flags;
654 return ((i + 1) << 8) | MONO_REGION_CATCH | clause->flags;
657 for (i = 0; i < header->num_clauses; ++i) {
658 clause = &header->clauses [i];
660 if (MONO_OFFSET_IN_CLAUSE (clause, offset))
661 return ((i + 1) << 8) | clause->flags;
668 ip_in_finally_clause (MonoCompile *cfg, int offset)
670 MonoMethodHeader *header = cfg->header;
671 MonoExceptionClause *clause;
674 for (i = 0; i < header->num_clauses; ++i) {
675 clause = &header->clauses [i];
676 if (clause->flags != MONO_EXCEPTION_CLAUSE_FINALLY && clause->flags != MONO_EXCEPTION_CLAUSE_FAULT)
679 if (MONO_OFFSET_IN_HANDLER (clause, offset))
686 mono_find_final_block (MonoCompile *cfg, unsigned char *ip, unsigned char *target, int type)
688 MonoMethodHeader *header = cfg->header;
689 MonoExceptionClause *clause;
693 for (i = 0; i < header->num_clauses; ++i) {
694 clause = &header->clauses [i];
695 if (MONO_OFFSET_IN_CLAUSE (clause, (ip - header->code)) &&
696 (!MONO_OFFSET_IN_CLAUSE (clause, (target - header->code)))) {
697 if (clause->flags == type)
698 res = g_list_append (res, clause);
705 mono_create_spvar_for_region (MonoCompile *cfg, int region)
709 var = (MonoInst *)g_hash_table_lookup (cfg->spvars, GINT_TO_POINTER (region));
713 var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
714 /* prevent it from being register allocated */
715 var->flags |= MONO_INST_VOLATILE;
717 g_hash_table_insert (cfg->spvars, GINT_TO_POINTER (region), var);
721 mono_find_exvar_for_offset (MonoCompile *cfg, int offset)
723 return (MonoInst *)g_hash_table_lookup (cfg->exvars, GINT_TO_POINTER (offset));
727 mono_create_exvar_for_offset (MonoCompile *cfg, int offset)
731 var = (MonoInst *)g_hash_table_lookup (cfg->exvars, GINT_TO_POINTER (offset));
735 var = mono_compile_create_var (cfg, &mono_defaults.object_class->byval_arg, OP_LOCAL);
736 /* prevent it from being register allocated */
737 var->flags |= MONO_INST_VOLATILE;
739 g_hash_table_insert (cfg->exvars, GINT_TO_POINTER (offset), var);
745 * Returns the type used in the eval stack when @type is loaded.
746 * FIXME: return a MonoType/MonoClass for the byref and VALUETYPE cases.
749 type_to_eval_stack_type (MonoCompile *cfg, MonoType *type, MonoInst *inst)
753 type = mini_get_underlying_type (type);
754 inst->klass = klass = mono_class_from_mono_type (type);
756 inst->type = STACK_MP;
761 switch (type->type) {
763 inst->type = STACK_INV;
771 inst->type = STACK_I4;
776 case MONO_TYPE_FNPTR:
777 inst->type = STACK_PTR;
779 case MONO_TYPE_CLASS:
780 case MONO_TYPE_STRING:
781 case MONO_TYPE_OBJECT:
782 case MONO_TYPE_SZARRAY:
783 case MONO_TYPE_ARRAY:
784 inst->type = STACK_OBJ;
788 inst->type = STACK_I8;
791 inst->type = cfg->r4_stack_type;
794 inst->type = STACK_R8;
796 case MONO_TYPE_VALUETYPE:
797 if (type->data.klass->enumtype) {
798 type = mono_class_enum_basetype (type->data.klass);
802 inst->type = STACK_VTYPE;
805 case MONO_TYPE_TYPEDBYREF:
806 inst->klass = mono_defaults.typed_reference_class;
807 inst->type = STACK_VTYPE;
809 case MONO_TYPE_GENERICINST:
810 type = &type->data.generic_class->container_class->byval_arg;
814 g_assert (cfg->gshared);
815 if (mini_is_gsharedvt_type (type)) {
816 g_assert (cfg->gsharedvt);
817 inst->type = STACK_VTYPE;
819 type_to_eval_stack_type (cfg, mini_get_underlying_type (type), inst);
823 g_error ("unknown type 0x%02x in eval stack type", type->type);
828 * The following tables are used to quickly validate the IL code in type_from_op ().
831 bin_num_table [STACK_MAX] [STACK_MAX] = {
832 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
833 {STACK_INV, STACK_I4, STACK_INV, STACK_PTR, STACK_INV, STACK_MP, STACK_INV, STACK_INV},
834 {STACK_INV, STACK_INV, STACK_I8, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
835 {STACK_INV, STACK_PTR, STACK_INV, STACK_PTR, STACK_INV, STACK_MP, STACK_INV, STACK_INV},
836 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_R8, STACK_INV, STACK_INV, STACK_INV, STACK_R8},
837 {STACK_INV, STACK_MP, STACK_INV, STACK_MP, STACK_INV, STACK_PTR, STACK_INV, STACK_INV},
838 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
839 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
840 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_R8, STACK_INV, STACK_INV, STACK_INV, STACK_R4}
845 STACK_INV, STACK_I4, STACK_I8, STACK_PTR, STACK_R8, STACK_INV, STACK_INV, STACK_INV, STACK_R4
848 /* reduce the size of this table */
850 bin_int_table [STACK_MAX] [STACK_MAX] = {
851 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
852 {STACK_INV, STACK_I4, STACK_INV, STACK_PTR, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
853 {STACK_INV, STACK_INV, STACK_I8, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
854 {STACK_INV, STACK_PTR, STACK_INV, STACK_PTR, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
855 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
856 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
857 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
858 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV}
862 bin_comp_table [STACK_MAX] [STACK_MAX] = {
863 /* Inv i L p F & O vt r4 */
865 {0, 1, 0, 1, 0, 0, 0, 0}, /* i, int32 */
866 {0, 0, 1, 0, 0, 0, 0, 0}, /* L, int64 */
867 {0, 1, 0, 1, 0, 2, 4, 0}, /* p, ptr */
868 {0, 0, 0, 0, 1, 0, 0, 0, 1}, /* F, R8 */
869 {0, 0, 0, 2, 0, 1, 0, 0}, /* &, managed pointer */
870 {0, 0, 0, 4, 0, 0, 3, 0}, /* O, reference */
871 {0, 0, 0, 0, 0, 0, 0, 0}, /* vt value type */
872 {0, 0, 0, 0, 1, 0, 0, 0, 1}, /* r, r4 */
875 /* reduce the size of this table */
877 shift_table [STACK_MAX] [STACK_MAX] = {
878 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
879 {STACK_INV, STACK_I4, STACK_INV, STACK_I4, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
880 {STACK_INV, STACK_I8, STACK_INV, STACK_I8, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
881 {STACK_INV, STACK_PTR, STACK_INV, STACK_PTR, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
882 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
883 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
884 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV},
885 {STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV, STACK_INV}
889 * Tables to map from the non-specific opcode to the matching
890 * type-specific opcode.
892 /* handles from CEE_ADD to CEE_SHR_UN (CEE_REM_UN for floats) */
894 binops_op_map [STACK_MAX] = {
895 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
898 /* handles from CEE_NEG to CEE_CONV_U8 */
900 unops_op_map [STACK_MAX] = {
901 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
904 /* handles from CEE_CONV_U2 to CEE_SUB_OVF_UN */
906 ovfops_op_map [STACK_MAX] = {
907 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
910 /* handles from CEE_CONV_OVF_I1_UN to CEE_CONV_OVF_U_UN */
912 ovf2ops_op_map [STACK_MAX] = {
913 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
916 /* handles from CEE_CONV_OVF_I1 to CEE_CONV_OVF_U8 */
918 ovf3ops_op_map [STACK_MAX] = {
919 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
922 /* handles from CEE_BEQ to CEE_BLT_UN */
924 beqops_op_map [STACK_MAX] = {
925 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
928 /* handles from CEE_CEQ to CEE_CLT_UN */
930 ceqops_op_map [STACK_MAX] = {
931 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
935 * Sets ins->type (the type on the eval stack) according to the
936 * type of the opcode and the arguments to it.
937 * Invalid IL code is marked by setting ins->type to the invalid value STACK_INV.
939 * FIXME: this function sets ins->type unconditionally in some cases, but
940 * it should set it to invalid for some types (a conv.x on an object)
943 type_from_op (MonoCompile *cfg, MonoInst *ins, MonoInst *src1, MonoInst *src2)
945 switch (ins->opcode) {
952 /* FIXME: check unverifiable args for STACK_MP */
953 ins->type = bin_num_table [src1->type] [src2->type];
954 ins->opcode += binops_op_map [ins->type];
961 ins->type = bin_int_table [src1->type] [src2->type];
962 ins->opcode += binops_op_map [ins->type];
967 ins->type = shift_table [src1->type] [src2->type];
968 ins->opcode += binops_op_map [ins->type];
973 ins->type = bin_comp_table [src1->type] [src2->type] ? STACK_I4: STACK_INV;
974 if ((src1->type == STACK_I8) || ((SIZEOF_VOID_P == 8) && ((src1->type == STACK_PTR) || (src1->type == STACK_OBJ) || (src1->type == STACK_MP))))
975 ins->opcode = OP_LCOMPARE;
976 else if (src1->type == STACK_R4)
977 ins->opcode = OP_RCOMPARE;
978 else if (src1->type == STACK_R8)
979 ins->opcode = OP_FCOMPARE;
981 ins->opcode = OP_ICOMPARE;
983 case OP_ICOMPARE_IMM:
984 ins->type = bin_comp_table [src1->type] [src1->type] ? STACK_I4 : STACK_INV;
985 if ((src1->type == STACK_I8) || ((SIZEOF_VOID_P == 8) && ((src1->type == STACK_PTR) || (src1->type == STACK_OBJ) || (src1->type == STACK_MP))))
986 ins->opcode = OP_LCOMPARE_IMM;
998 ins->opcode += beqops_op_map [src1->type];
1001 ins->type = bin_comp_table [src1->type] [src2->type] ? STACK_I4: STACK_INV;
1002 ins->opcode += ceqops_op_map [src1->type];
1008 ins->type = (bin_comp_table [src1->type] [src2->type] & 1) ? STACK_I4: STACK_INV;
1009 ins->opcode += ceqops_op_map [src1->type];
1013 ins->type = neg_table [src1->type];
1014 ins->opcode += unops_op_map [ins->type];
1017 if (src1->type >= STACK_I4 && src1->type <= STACK_PTR)
1018 ins->type = src1->type;
1020 ins->type = STACK_INV;
1021 ins->opcode += unops_op_map [ins->type];
1027 ins->type = STACK_I4;
1028 ins->opcode += unops_op_map [src1->type];
1031 ins->type = STACK_R8;
1032 switch (src1->type) {
1035 ins->opcode = OP_ICONV_TO_R_UN;
1038 ins->opcode = OP_LCONV_TO_R_UN;
1042 case CEE_CONV_OVF_I1:
1043 case CEE_CONV_OVF_U1:
1044 case CEE_CONV_OVF_I2:
1045 case CEE_CONV_OVF_U2:
1046 case CEE_CONV_OVF_I4:
1047 case CEE_CONV_OVF_U4:
1048 ins->type = STACK_I4;
1049 ins->opcode += ovf3ops_op_map [src1->type];
1051 case CEE_CONV_OVF_I_UN:
1052 case CEE_CONV_OVF_U_UN:
1053 ins->type = STACK_PTR;
1054 ins->opcode += ovf2ops_op_map [src1->type];
1056 case CEE_CONV_OVF_I1_UN:
1057 case CEE_CONV_OVF_I2_UN:
1058 case CEE_CONV_OVF_I4_UN:
1059 case CEE_CONV_OVF_U1_UN:
1060 case CEE_CONV_OVF_U2_UN:
1061 case CEE_CONV_OVF_U4_UN:
1062 ins->type = STACK_I4;
1063 ins->opcode += ovf2ops_op_map [src1->type];
1066 ins->type = STACK_PTR;
1067 switch (src1->type) {
1069 ins->opcode = OP_ICONV_TO_U;
1073 #if SIZEOF_VOID_P == 8
1074 ins->opcode = OP_LCONV_TO_U;
1076 ins->opcode = OP_MOVE;
1080 ins->opcode = OP_LCONV_TO_U;
1083 ins->opcode = OP_FCONV_TO_U;
1089 ins->type = STACK_I8;
1090 ins->opcode += unops_op_map [src1->type];
1092 case CEE_CONV_OVF_I8:
1093 case CEE_CONV_OVF_U8:
1094 ins->type = STACK_I8;
1095 ins->opcode += ovf3ops_op_map [src1->type];
1097 case CEE_CONV_OVF_U8_UN:
1098 case CEE_CONV_OVF_I8_UN:
1099 ins->type = STACK_I8;
1100 ins->opcode += ovf2ops_op_map [src1->type];
1103 ins->type = cfg->r4_stack_type;
1104 ins->opcode += unops_op_map [src1->type];
1107 ins->type = STACK_R8;
1108 ins->opcode += unops_op_map [src1->type];
1111 ins->type = STACK_R8;
1115 ins->type = STACK_I4;
1116 ins->opcode += ovfops_op_map [src1->type];
1119 case CEE_CONV_OVF_I:
1120 case CEE_CONV_OVF_U:
1121 ins->type = STACK_PTR;
1122 ins->opcode += ovfops_op_map [src1->type];
1125 case CEE_ADD_OVF_UN:
1127 case CEE_MUL_OVF_UN:
1129 case CEE_SUB_OVF_UN:
1130 ins->type = bin_num_table [src1->type] [src2->type];
1131 ins->opcode += ovfops_op_map [src1->type];
1132 if (ins->type == STACK_R8)
1133 ins->type = STACK_INV;
1135 case OP_LOAD_MEMBASE:
1136 ins->type = STACK_PTR;
1138 case OP_LOADI1_MEMBASE:
1139 case OP_LOADU1_MEMBASE:
1140 case OP_LOADI2_MEMBASE:
1141 case OP_LOADU2_MEMBASE:
1142 case OP_LOADI4_MEMBASE:
1143 case OP_LOADU4_MEMBASE:
1144 ins->type = STACK_PTR;
1146 case OP_LOADI8_MEMBASE:
1147 ins->type = STACK_I8;
1149 case OP_LOADR4_MEMBASE:
1150 ins->type = cfg->r4_stack_type;
1152 case OP_LOADR8_MEMBASE:
1153 ins->type = STACK_R8;
1156 g_error ("opcode 0x%04x not handled in type from op", ins->opcode);
1160 if (ins->type == STACK_MP)
1161 ins->klass = mono_defaults.object_class;
1165 ldind_to_type (int op)
1168 case CEE_LDIND_I1: return mono_defaults.sbyte_class;
1169 case CEE_LDIND_U1: return mono_defaults.byte_class;
1170 case CEE_LDIND_I2: return mono_defaults.int16_class;
1171 case CEE_LDIND_U2: return mono_defaults.uint16_class;
1172 case CEE_LDIND_I4: return mono_defaults.int32_class;
1173 case CEE_LDIND_U4: return mono_defaults.uint32_class;
1174 case CEE_LDIND_I8: return mono_defaults.int64_class;
1175 case CEE_LDIND_I: return mono_defaults.int_class;
1176 case CEE_LDIND_R4: return mono_defaults.single_class;
1177 case CEE_LDIND_R8: return mono_defaults.double_class;
1178 case CEE_LDIND_REF:return mono_defaults.object_class; //FIXME we should try to return a more specific type
1179 default: g_error ("Unknown ldind type %d", op);
1186 param_table [STACK_MAX] [STACK_MAX] = {
1191 check_values_to_signature (MonoInst *args, MonoType *this_ins, MonoMethodSignature *sig)
1196 switch (args->type) {
1206 for (i = 0; i < sig->param_count; ++i) {
1207 switch (args [i].type) {
1211 if (!sig->params [i]->byref)
1215 if (sig->params [i]->byref)
1217 switch (sig->params [i]->type) {
1218 case MONO_TYPE_CLASS:
1219 case MONO_TYPE_STRING:
1220 case MONO_TYPE_OBJECT:
1221 case MONO_TYPE_SZARRAY:
1222 case MONO_TYPE_ARRAY:
1229 if (sig->params [i]->byref)
1231 if (sig->params [i]->type != MONO_TYPE_R4 && sig->params [i]->type != MONO_TYPE_R8)
1240 /*if (!param_table [args [i].type] [sig->params [i]->type])
1248 * When we need a pointer to the current domain many times in a method, we
1249 * call mono_domain_get() once and we store the result in a local variable.
1250 * This function returns the variable that represents the MonoDomain*.
1252 inline static MonoInst *
1253 mono_get_domainvar (MonoCompile *cfg)
1255 if (!cfg->domainvar)
1256 cfg->domainvar = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
1257 return cfg->domainvar;
1261 * The got_var contains the address of the Global Offset Table when AOT
1265 mono_get_got_var (MonoCompile *cfg)
1267 if (!cfg->compile_aot || !cfg->backend->need_got_var)
1269 if (!cfg->got_var) {
1270 cfg->got_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
1272 return cfg->got_var;
1276 mono_create_rgctx_var (MonoCompile *cfg)
1278 if (!cfg->rgctx_var) {
1279 cfg->rgctx_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
1280 /* force the var to be stack allocated */
1281 cfg->rgctx_var->flags |= MONO_INST_VOLATILE;
1286 mono_get_vtable_var (MonoCompile *cfg)
1288 g_assert (cfg->gshared);
1290 mono_create_rgctx_var (cfg);
1292 return cfg->rgctx_var;
1296 type_from_stack_type (MonoInst *ins) {
1297 switch (ins->type) {
1298 case STACK_I4: return &mono_defaults.int32_class->byval_arg;
1299 case STACK_I8: return &mono_defaults.int64_class->byval_arg;
1300 case STACK_PTR: return &mono_defaults.int_class->byval_arg;
1301 case STACK_R4: return &mono_defaults.single_class->byval_arg;
1302 case STACK_R8: return &mono_defaults.double_class->byval_arg;
1304 return &ins->klass->this_arg;
1305 case STACK_OBJ: return &mono_defaults.object_class->byval_arg;
1306 case STACK_VTYPE: return &ins->klass->byval_arg;
1308 g_error ("stack type %d to monotype not handled\n", ins->type);
1313 static G_GNUC_UNUSED int
1314 type_to_stack_type (MonoCompile *cfg, MonoType *t)
1316 t = mono_type_get_underlying_type (t);
1328 case MONO_TYPE_FNPTR:
1330 case MONO_TYPE_CLASS:
1331 case MONO_TYPE_STRING:
1332 case MONO_TYPE_OBJECT:
1333 case MONO_TYPE_SZARRAY:
1334 case MONO_TYPE_ARRAY:
1340 return cfg->r4_stack_type;
1343 case MONO_TYPE_VALUETYPE:
1344 case MONO_TYPE_TYPEDBYREF:
1346 case MONO_TYPE_GENERICINST:
1347 if (mono_type_generic_inst_is_valuetype (t))
1353 g_assert_not_reached ();
1360 array_access_to_klass (int opcode)
1364 return mono_defaults.byte_class;
1366 return mono_defaults.uint16_class;
1369 return mono_defaults.int_class;
1372 return mono_defaults.sbyte_class;
1375 return mono_defaults.int16_class;
1378 return mono_defaults.int32_class;
1380 return mono_defaults.uint32_class;
1383 return mono_defaults.int64_class;
1386 return mono_defaults.single_class;
1389 return mono_defaults.double_class;
1390 case CEE_LDELEM_REF:
1391 case CEE_STELEM_REF:
1392 return mono_defaults.object_class;
1394 g_assert_not_reached ();
1400 * We try to share variables when possible
1403 mono_compile_get_interface_var (MonoCompile *cfg, int slot, MonoInst *ins)
1408 /* inlining can result in deeper stacks */
1409 if (slot >= cfg->header->max_stack)
1410 return mono_compile_create_var (cfg, type_from_stack_type (ins), OP_LOCAL);
1412 pos = ins->type - 1 + slot * STACK_MAX;
1414 switch (ins->type) {
1421 if ((vnum = cfg->intvars [pos]))
1422 return cfg->varinfo [vnum];
1423 res = mono_compile_create_var (cfg, type_from_stack_type (ins), OP_LOCAL);
1424 cfg->intvars [pos] = res->inst_c0;
1427 res = mono_compile_create_var (cfg, type_from_stack_type (ins), OP_LOCAL);
1433 mono_save_token_info (MonoCompile *cfg, MonoImage *image, guint32 token, gpointer key)
1436 * Don't use this if a generic_context is set, since that means AOT can't
1437 * look up the method using just the image+token.
1438 * table == 0 means this is a reference made from a wrapper.
1440 if (cfg->compile_aot && !cfg->generic_context && (mono_metadata_token_table (token) > 0)) {
1441 MonoJumpInfoToken *jump_info_token = (MonoJumpInfoToken *)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoJumpInfoToken));
1442 jump_info_token->image = image;
1443 jump_info_token->token = token;
1444 g_hash_table_insert (cfg->token_info_hash, key, jump_info_token);
1449 * This function is called to handle items that are left on the evaluation stack
1450 * at basic block boundaries. What happens is that we save the values to local variables
1451 * and we reload them later when first entering the target basic block (with the
1452 * handle_loaded_temps () function).
1453 * A single joint point will use the same variables (stored in the array bb->out_stack or
1454 * bb->in_stack, if the basic block is before or after the joint point).
1456 * This function needs to be called _before_ emitting the last instruction of
1457 * the bb (i.e. before emitting a branch).
1458 * If the stack merge fails at a join point, cfg->unverifiable is set.
1461 handle_stack_args (MonoCompile *cfg, MonoInst **sp, int count)
1464 MonoBasicBlock *bb = cfg->cbb;
1465 MonoBasicBlock *outb;
1466 MonoInst *inst, **locals;
1471 if (cfg->verbose_level > 3)
1472 printf ("%d item(s) on exit from B%d\n", count, bb->block_num);
1473 if (!bb->out_scount) {
1474 bb->out_scount = count;
1475 //printf ("bblock %d has out:", bb->block_num);
1477 for (i = 0; i < bb->out_count; ++i) {
1478 outb = bb->out_bb [i];
1479 /* exception handlers are linked, but they should not be considered for stack args */
1480 if (outb->flags & BB_EXCEPTION_HANDLER)
1482 //printf (" %d", outb->block_num);
1483 if (outb->in_stack) {
1485 bb->out_stack = outb->in_stack;
1491 bb->out_stack = (MonoInst **)mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*) * count);
1492 for (i = 0; i < count; ++i) {
1494 * try to reuse temps already allocated for this purpouse, if they occupy the same
1495 * stack slot and if they are of the same type.
1496 * This won't cause conflicts since if 'local' is used to
1497 * store one of the values in the in_stack of a bblock, then
1498 * the same variable will be used for the same outgoing stack
1500 * This doesn't work when inlining methods, since the bblocks
1501 * in the inlined methods do not inherit their in_stack from
1502 * the bblock they are inlined to. See bug #58863 for an
1505 if (cfg->inlined_method)
1506 bb->out_stack [i] = mono_compile_create_var (cfg, type_from_stack_type (sp [i]), OP_LOCAL);
1508 bb->out_stack [i] = mono_compile_get_interface_var (cfg, i, sp [i]);
1513 for (i = 0; i < bb->out_count; ++i) {
1514 outb = bb->out_bb [i];
1515 /* exception handlers are linked, but they should not be considered for stack args */
1516 if (outb->flags & BB_EXCEPTION_HANDLER)
1518 if (outb->in_scount) {
1519 if (outb->in_scount != bb->out_scount) {
1520 cfg->unverifiable = TRUE;
1523 continue; /* check they are the same locals */
1525 outb->in_scount = count;
1526 outb->in_stack = bb->out_stack;
1529 locals = bb->out_stack;
1531 for (i = 0; i < count; ++i) {
1532 EMIT_NEW_TEMPSTORE (cfg, inst, locals [i]->inst_c0, sp [i]);
1533 inst->cil_code = sp [i]->cil_code;
1534 sp [i] = locals [i];
1535 if (cfg->verbose_level > 3)
1536 printf ("storing %d to temp %d\n", i, (int)locals [i]->inst_c0);
1540 * It is possible that the out bblocks already have in_stack assigned, and
1541 * the in_stacks differ. In this case, we will store to all the different
1548 /* Find a bblock which has a different in_stack */
1550 while (bindex < bb->out_count) {
1551 outb = bb->out_bb [bindex];
1552 /* exception handlers are linked, but they should not be considered for stack args */
1553 if (outb->flags & BB_EXCEPTION_HANDLER) {
1557 if (outb->in_stack != locals) {
1558 for (i = 0; i < count; ++i) {
1559 EMIT_NEW_TEMPSTORE (cfg, inst, outb->in_stack [i]->inst_c0, sp [i]);
1560 inst->cil_code = sp [i]->cil_code;
1561 sp [i] = locals [i];
1562 if (cfg->verbose_level > 3)
1563 printf ("storing %d to temp %d\n", i, (int)outb->in_stack [i]->inst_c0);
1565 locals = outb->in_stack;
1575 mini_emit_runtime_constant (MonoCompile *cfg, MonoJumpInfoType patch_type, gpointer data)
1579 if (cfg->compile_aot) {
1580 EMIT_NEW_AOTCONST (cfg, ins, patch_type, data);
1586 ji.type = patch_type;
1587 ji.data.target = data;
1588 target = mono_resolve_patch_target (NULL, cfg->domain, NULL, &ji, FALSE, &error);
1589 mono_error_assert_ok (&error);
1591 EMIT_NEW_PCONST (cfg, ins, target);
1597 mono_create_fast_tls_getter (MonoCompile *cfg, MonoTlsKey key)
1599 int tls_offset = mono_tls_get_tls_offset (key);
1601 if (cfg->compile_aot)
1604 if (tls_offset != -1 && mono_arch_have_fast_tls ()) {
1606 MONO_INST_NEW (cfg, ins, OP_TLS_GET);
1607 ins->dreg = mono_alloc_preg (cfg);
1608 ins->inst_offset = tls_offset;
1615 mono_create_fast_tls_setter (MonoCompile *cfg, MonoInst* value, MonoTlsKey key)
1617 int tls_offset = mono_tls_get_tls_offset (key);
1619 if (cfg->compile_aot)
1622 if (tls_offset != -1 && mono_arch_have_fast_tls ()) {
1624 MONO_INST_NEW (cfg, ins, OP_TLS_SET);
1625 ins->sreg1 = value->dreg;
1626 ins->inst_offset = tls_offset;
1634 mono_create_tls_get (MonoCompile *cfg, MonoTlsKey key)
1636 MonoInst *fast_tls = NULL;
1638 if (!mini_get_debug_options ()->use_fallback_tls)
1639 fast_tls = mono_create_fast_tls_getter (cfg, key);
1642 MONO_ADD_INS (cfg->cbb, fast_tls);
1646 if (cfg->compile_aot) {
1649 * tls getters are critical pieces of code and we don't want to resolve them
1650 * through the standard plt/tramp mechanism since we might expose ourselves
1651 * to crashes and infinite recursions.
1653 EMIT_NEW_AOTCONST (cfg, addr, MONO_PATCH_INFO_GET_TLS_TRAMP, (void*)key);
1654 return mini_emit_calli (cfg, helper_sig_get_tls_tramp, NULL, addr, NULL, NULL);
1656 gpointer getter = mono_tls_get_tls_getter (key, FALSE);
1657 return mono_emit_jit_icall (cfg, getter, NULL);
1662 mono_create_tls_set (MonoCompile *cfg, MonoInst *value, MonoTlsKey key)
1664 MonoInst *fast_tls = NULL;
1666 if (!mini_get_debug_options ()->use_fallback_tls)
1667 fast_tls = mono_create_fast_tls_setter (cfg, value, key);
1670 MONO_ADD_INS (cfg->cbb, fast_tls);
1674 if (cfg->compile_aot) {
1676 EMIT_NEW_AOTCONST (cfg, addr, MONO_PATCH_INFO_SET_TLS_TRAMP, (void*)key);
1677 return mini_emit_calli (cfg, helper_sig_set_tls_tramp, &value, addr, NULL, NULL);
1679 gpointer setter = mono_tls_get_tls_setter (key, FALSE);
1680 return mono_emit_jit_icall (cfg, setter, &value);
1687 * Emit IR to push the current LMF onto the LMF stack.
1690 emit_push_lmf (MonoCompile *cfg)
1693 * Emit IR to push the LMF:
1694 * lmf_addr = <lmf_addr from tls>
1695 * lmf->lmf_addr = lmf_addr
1696 * lmf->prev_lmf = *lmf_addr
1699 MonoInst *ins, *lmf_ins;
1704 int lmf_reg, prev_lmf_reg;
1706 * Store lmf_addr in a variable, so it can be allocated to a global register.
1708 if (!cfg->lmf_addr_var)
1709 cfg->lmf_addr_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
1712 ins = mono_create_tls_get (cfg, TLS_KEY_JIT_TLS);
1714 int jit_tls_dreg = ins->dreg;
1716 lmf_reg = alloc_preg (cfg);
1717 EMIT_NEW_BIALU_IMM (cfg, lmf_ins, OP_PADD_IMM, lmf_reg, jit_tls_dreg, MONO_STRUCT_OFFSET (MonoJitTlsData, lmf));
1719 lmf_ins = mono_create_tls_get (cfg, TLS_KEY_LMF_ADDR);
1722 lmf_ins->dreg = cfg->lmf_addr_var->dreg;
1724 EMIT_NEW_VARLOADA (cfg, ins, cfg->lmf_var, NULL);
1725 lmf_reg = ins->dreg;
1727 prev_lmf_reg = alloc_preg (cfg);
1728 /* Save previous_lmf */
1729 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, prev_lmf_reg, cfg->lmf_addr_var->dreg, 0);
1730 EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, lmf_reg, MONO_STRUCT_OFFSET (MonoLMF, previous_lmf), prev_lmf_reg);
1732 EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, cfg->lmf_addr_var->dreg, 0, lmf_reg);
1738 * Emit IR to pop the current LMF from the LMF stack.
1741 emit_pop_lmf (MonoCompile *cfg)
1743 int lmf_reg, lmf_addr_reg;
1749 EMIT_NEW_VARLOADA (cfg, ins, cfg->lmf_var, NULL);
1750 lmf_reg = ins->dreg;
1754 * Emit IR to pop the LMF:
1755 * *(lmf->lmf_addr) = lmf->prev_lmf
1757 /* This could be called before emit_push_lmf () */
1758 if (!cfg->lmf_addr_var)
1759 cfg->lmf_addr_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
1760 lmf_addr_reg = cfg->lmf_addr_var->dreg;
1762 prev_lmf_reg = alloc_preg (cfg);
1763 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, prev_lmf_reg, lmf_reg, MONO_STRUCT_OFFSET (MonoLMF, previous_lmf));
1764 EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, lmf_addr_reg, 0, prev_lmf_reg);
1768 emit_instrumentation_call (MonoCompile *cfg, void *func)
1770 MonoInst *iargs [1];
1773 * Avoid instrumenting inlined methods since it can
1774 * distort profiling results.
1776 if (cfg->method != cfg->current_method)
1779 if (cfg->prof_options & MONO_PROFILE_ENTER_LEAVE) {
1780 EMIT_NEW_METHODCONST (cfg, iargs [0], cfg->method);
1781 mono_emit_jit_icall (cfg, func, iargs);
1786 ret_type_to_call_opcode (MonoCompile *cfg, MonoType *type, int calli, int virt)
1789 type = mini_get_underlying_type (type);
1790 switch (type->type) {
1791 case MONO_TYPE_VOID:
1792 return calli? OP_VOIDCALL_REG: virt? OP_VOIDCALL_MEMBASE: OP_VOIDCALL;
1799 return calli? OP_CALL_REG: virt? OP_CALL_MEMBASE: OP_CALL;
1803 case MONO_TYPE_FNPTR:
1804 return calli? OP_CALL_REG: virt? OP_CALL_MEMBASE: OP_CALL;
1805 case MONO_TYPE_CLASS:
1806 case MONO_TYPE_STRING:
1807 case MONO_TYPE_OBJECT:
1808 case MONO_TYPE_SZARRAY:
1809 case MONO_TYPE_ARRAY:
1810 return calli? OP_CALL_REG: virt? OP_CALL_MEMBASE: OP_CALL;
1813 return calli? OP_LCALL_REG: virt? OP_LCALL_MEMBASE: OP_LCALL;
1816 return calli? OP_RCALL_REG: virt? OP_RCALL_MEMBASE: OP_RCALL;
1818 return calli? OP_FCALL_REG: virt? OP_FCALL_MEMBASE: OP_FCALL;
1820 return calli? OP_FCALL_REG: virt? OP_FCALL_MEMBASE: OP_FCALL;
1821 case MONO_TYPE_VALUETYPE:
1822 if (type->data.klass->enumtype) {
1823 type = mono_class_enum_basetype (type->data.klass);
1826 return calli? OP_VCALL_REG: virt? OP_VCALL_MEMBASE: OP_VCALL;
1827 case MONO_TYPE_TYPEDBYREF:
1828 return calli? OP_VCALL_REG: virt? OP_VCALL_MEMBASE: OP_VCALL;
1829 case MONO_TYPE_GENERICINST:
1830 type = &type->data.generic_class->container_class->byval_arg;
1833 case MONO_TYPE_MVAR:
1835 return calli? OP_VCALL_REG: virt? OP_VCALL_MEMBASE: OP_VCALL;
1837 g_error ("unknown type 0x%02x in ret_type_to_call_opcode", type->type);
1842 //XXX this ignores if t is byref
1843 #define MONO_TYPE_IS_PRIMITIVE_SCALAR(t) ((((((t)->type >= MONO_TYPE_BOOLEAN && (t)->type <= MONO_TYPE_U8) || ((t)->type >= MONO_TYPE_I && (t)->type <= MONO_TYPE_U)))))
1846 * target_type_is_incompatible:
1847 * @cfg: MonoCompile context
1849 * Check that the item @arg on the evaluation stack can be stored
1850 * in the target type (can be a local, or field, etc).
1851 * The cfg arg can be used to check if we need verification or just
1854 * Returns: non-0 value if arg can't be stored on a target.
1857 target_type_is_incompatible (MonoCompile *cfg, MonoType *target, MonoInst *arg)
1859 MonoType *simple_type;
1862 if (target->byref) {
1863 /* FIXME: check that the pointed to types match */
1864 if (arg->type == STACK_MP) {
1865 /* This is needed to handle gshared types + ldaddr. We lower the types so we can handle enums and other typedef-like types. */
1866 MonoClass *target_class_lowered = mono_class_from_mono_type (mini_get_underlying_type (&mono_class_from_mono_type (target)->byval_arg));
1867 MonoClass *source_class_lowered = mono_class_from_mono_type (mini_get_underlying_type (&arg->klass->byval_arg));
1869 /* if the target is native int& or same type */
1870 if (target->type == MONO_TYPE_I || target_class_lowered == source_class_lowered)
1873 /* Both are primitive type byrefs and the source points to a larger type that the destination */
1874 if (MONO_TYPE_IS_PRIMITIVE_SCALAR (&target_class_lowered->byval_arg) && MONO_TYPE_IS_PRIMITIVE_SCALAR (&source_class_lowered->byval_arg) &&
1875 mono_class_instance_size (target_class_lowered) <= mono_class_instance_size (source_class_lowered))
1879 if (arg->type == STACK_PTR)
1884 simple_type = mini_get_underlying_type (target);
1885 switch (simple_type->type) {
1886 case MONO_TYPE_VOID:
1894 if (arg->type != STACK_I4 && arg->type != STACK_PTR)
1898 /* STACK_MP is needed when setting pinned locals */
1899 if (arg->type != STACK_I4 && arg->type != STACK_PTR && arg->type != STACK_MP)
1904 case MONO_TYPE_FNPTR:
1906 * Some opcodes like ldloca returns 'transient pointers' which can be stored in
1907 * in native int. (#688008).
1909 if (arg->type != STACK_I4 && arg->type != STACK_PTR && arg->type != STACK_MP)
1912 case MONO_TYPE_CLASS:
1913 case MONO_TYPE_STRING:
1914 case MONO_TYPE_OBJECT:
1915 case MONO_TYPE_SZARRAY:
1916 case MONO_TYPE_ARRAY:
1917 if (arg->type != STACK_OBJ)
1919 /* FIXME: check type compatibility */
1923 if (arg->type != STACK_I8)
1927 if (arg->type != cfg->r4_stack_type)
1931 if (arg->type != STACK_R8)
1934 case MONO_TYPE_VALUETYPE:
1935 if (arg->type != STACK_VTYPE)
1937 klass = mono_class_from_mono_type (simple_type);
1938 if (klass != arg->klass)
1941 case MONO_TYPE_TYPEDBYREF:
1942 if (arg->type != STACK_VTYPE)
1944 klass = mono_class_from_mono_type (simple_type);
1945 if (klass != arg->klass)
1948 case MONO_TYPE_GENERICINST:
1949 if (mono_type_generic_inst_is_valuetype (simple_type)) {
1950 MonoClass *target_class;
1951 if (arg->type != STACK_VTYPE)
1953 klass = mono_class_from_mono_type (simple_type);
1954 target_class = mono_class_from_mono_type (target);
1955 /* The second cases is needed when doing partial sharing */
1956 if (klass != arg->klass && target_class != arg->klass && target_class != mono_class_from_mono_type (mini_get_underlying_type (&arg->klass->byval_arg)))
1960 if (arg->type != STACK_OBJ)
1962 /* FIXME: check type compatibility */
1966 case MONO_TYPE_MVAR:
1967 g_assert (cfg->gshared);
1968 if (mini_type_var_is_vt (simple_type)) {
1969 if (arg->type != STACK_VTYPE)
1972 if (arg->type != STACK_OBJ)
1977 g_error ("unknown type 0x%02x in target_type_is_incompatible", simple_type->type);
1983 * Prepare arguments for passing to a function call.
1984 * Return a non-zero value if the arguments can't be passed to the given
1986 * The type checks are not yet complete and some conversions may need
1987 * casts on 32 or 64 bit architectures.
1989 * FIXME: implement this using target_type_is_incompatible ()
1992 check_call_signature (MonoCompile *cfg, MonoMethodSignature *sig, MonoInst **args)
1994 MonoType *simple_type;
1998 if (args [0]->type != STACK_OBJ && args [0]->type != STACK_MP && args [0]->type != STACK_PTR)
2002 for (i = 0; i < sig->param_count; ++i) {
2003 if (sig->params [i]->byref) {
2004 if (args [i]->type != STACK_MP && args [i]->type != STACK_PTR)
2008 simple_type = mini_get_underlying_type (sig->params [i]);
2010 switch (simple_type->type) {
2011 case MONO_TYPE_VOID:
2020 if (args [i]->type != STACK_I4 && args [i]->type != STACK_PTR)
2026 case MONO_TYPE_FNPTR:
2027 if (args [i]->type != STACK_I4 && args [i]->type != STACK_PTR && args [i]->type != STACK_MP && args [i]->type != STACK_OBJ)
2030 case MONO_TYPE_CLASS:
2031 case MONO_TYPE_STRING:
2032 case MONO_TYPE_OBJECT:
2033 case MONO_TYPE_SZARRAY:
2034 case MONO_TYPE_ARRAY:
2035 if (args [i]->type != STACK_OBJ)
2040 if (args [i]->type != STACK_I8)
2044 if (args [i]->type != cfg->r4_stack_type)
2048 if (args [i]->type != STACK_R8)
2051 case MONO_TYPE_VALUETYPE:
2052 if (simple_type->data.klass->enumtype) {
2053 simple_type = mono_class_enum_basetype (simple_type->data.klass);
2056 if (args [i]->type != STACK_VTYPE)
2059 case MONO_TYPE_TYPEDBYREF:
2060 if (args [i]->type != STACK_VTYPE)
2063 case MONO_TYPE_GENERICINST:
2064 simple_type = &simple_type->data.generic_class->container_class->byval_arg;
2067 case MONO_TYPE_MVAR:
2069 if (args [i]->type != STACK_VTYPE)
2073 g_error ("unknown type 0x%02x in check_call_signature",
2081 callvirt_to_call (int opcode)
2084 case OP_CALL_MEMBASE:
2086 case OP_VOIDCALL_MEMBASE:
2088 case OP_FCALL_MEMBASE:
2090 case OP_RCALL_MEMBASE:
2092 case OP_VCALL_MEMBASE:
2094 case OP_LCALL_MEMBASE:
2097 g_assert_not_reached ();
2104 callvirt_to_call_reg (int opcode)
2107 case OP_CALL_MEMBASE:
2109 case OP_VOIDCALL_MEMBASE:
2110 return OP_VOIDCALL_REG;
2111 case OP_FCALL_MEMBASE:
2112 return OP_FCALL_REG;
2113 case OP_RCALL_MEMBASE:
2114 return OP_RCALL_REG;
2115 case OP_VCALL_MEMBASE:
2116 return OP_VCALL_REG;
2117 case OP_LCALL_MEMBASE:
2118 return OP_LCALL_REG;
2120 g_assert_not_reached ();
2126 /* Either METHOD or IMT_ARG needs to be set */
2128 emit_imt_argument (MonoCompile *cfg, MonoCallInst *call, MonoMethod *method, MonoInst *imt_arg)
2132 if (COMPILE_LLVM (cfg)) {
2134 method_reg = alloc_preg (cfg);
2135 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, method_reg, imt_arg->dreg);
2137 MonoInst *ins = mini_emit_runtime_constant (cfg, MONO_PATCH_INFO_METHODCONST, method);
2138 method_reg = ins->dreg;
2142 call->imt_arg_reg = method_reg;
2144 mono_call_inst_add_outarg_reg (cfg, call, method_reg, MONO_ARCH_IMT_REG, FALSE);
2149 method_reg = alloc_preg (cfg);
2150 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, method_reg, imt_arg->dreg);
2152 MonoInst *ins = mini_emit_runtime_constant (cfg, MONO_PATCH_INFO_METHODCONST, method);
2153 method_reg = ins->dreg;
2156 mono_call_inst_add_outarg_reg (cfg, call, method_reg, MONO_ARCH_IMT_REG, FALSE);
2159 static MonoJumpInfo *
2160 mono_patch_info_new (MonoMemPool *mp, int ip, MonoJumpInfoType type, gconstpointer target)
2162 MonoJumpInfo *ji = (MonoJumpInfo *)mono_mempool_alloc (mp, sizeof (MonoJumpInfo));
2166 ji->data.target = target;
2172 mini_class_check_context_used (MonoCompile *cfg, MonoClass *klass)
2175 return mono_class_check_context_used (klass);
2181 mini_method_check_context_used (MonoCompile *cfg, MonoMethod *method)
2184 return mono_method_check_context_used (method);
2190 * check_method_sharing:
2192 * Check whenever the vtable or an mrgctx needs to be passed when calling CMETHOD.
2195 check_method_sharing (MonoCompile *cfg, MonoMethod *cmethod, gboolean *out_pass_vtable, gboolean *out_pass_mrgctx)
2197 gboolean pass_vtable = FALSE;
2198 gboolean pass_mrgctx = FALSE;
2200 if (((cmethod->flags & METHOD_ATTRIBUTE_STATIC) || cmethod->klass->valuetype) &&
2201 (mono_class_is_ginst (cmethod->klass) || mono_class_is_gtd (cmethod->klass))) {
2202 gboolean sharable = FALSE;
2204 if (mono_method_is_generic_sharable_full (cmethod, TRUE, TRUE, TRUE))
2208 * Pass vtable iff target method might
2209 * be shared, which means that sharing
2210 * is enabled for its class and its
2211 * context is sharable (and it's not a
2214 if (sharable && !(mini_method_get_context (cmethod) && mini_method_get_context (cmethod)->method_inst))
2218 if (mini_method_get_context (cmethod) &&
2219 mini_method_get_context (cmethod)->method_inst) {
2220 g_assert (!pass_vtable);
2222 if (mono_method_is_generic_sharable_full (cmethod, TRUE, TRUE, TRUE)) {
2225 if (cfg->gsharedvt && mini_is_gsharedvt_signature (mono_method_signature (cmethod)))
2230 if (out_pass_vtable)
2231 *out_pass_vtable = pass_vtable;
2232 if (out_pass_mrgctx)
2233 *out_pass_mrgctx = pass_mrgctx;
2236 inline static MonoCallInst *
2237 mono_emit_call_args (MonoCompile *cfg, MonoMethodSignature *sig,
2238 MonoInst **args, int calli, int virtual_, int tail, int rgctx, int unbox_trampoline)
2242 #ifdef MONO_ARCH_SOFT_FLOAT_FALLBACK
2250 emit_instrumentation_call (cfg, mono_profiler_method_leave);
2252 MONO_INST_NEW_CALL (cfg, call, OP_TAILCALL);
2254 MONO_INST_NEW_CALL (cfg, call, ret_type_to_call_opcode (cfg, sig->ret, calli, virtual_));
2257 call->signature = sig;
2258 call->rgctx_reg = rgctx;
2259 sig_ret = mini_get_underlying_type (sig->ret);
2261 type_to_eval_stack_type ((cfg), sig_ret, &call->inst);
2264 if (mini_type_is_vtype (sig_ret)) {
2265 call->vret_var = cfg->vret_addr;
2266 //g_assert_not_reached ();
2268 } else if (mini_type_is_vtype (sig_ret)) {
2269 MonoInst *temp = mono_compile_create_var (cfg, sig_ret, OP_LOCAL);
2272 temp->backend.is_pinvoke = sig->pinvoke;
2275 * We use a new opcode OP_OUTARG_VTRETADDR instead of LDADDR for emitting the
2276 * address of return value to increase optimization opportunities.
2277 * Before vtype decomposition, the dreg of the call ins itself represents the
2278 * fact the call modifies the return value. After decomposition, the call will
2279 * be transformed into one of the OP_VOIDCALL opcodes, and the VTRETADDR opcode
2280 * will be transformed into an LDADDR.
2282 MONO_INST_NEW (cfg, loada, OP_OUTARG_VTRETADDR);
2283 loada->dreg = alloc_preg (cfg);
2284 loada->inst_p0 = temp;
2285 /* We reference the call too since call->dreg could change during optimization */
2286 loada->inst_p1 = call;
2287 MONO_ADD_INS (cfg->cbb, loada);
2289 call->inst.dreg = temp->dreg;
2291 call->vret_var = loada;
2292 } else if (!MONO_TYPE_IS_VOID (sig_ret))
2293 call->inst.dreg = alloc_dreg (cfg, (MonoStackType)call->inst.type);
2295 #ifdef MONO_ARCH_SOFT_FLOAT_FALLBACK
2296 if (COMPILE_SOFT_FLOAT (cfg)) {
2298 * If the call has a float argument, we would need to do an r8->r4 conversion using
2299 * an icall, but that cannot be done during the call sequence since it would clobber
2300 * the call registers + the stack. So we do it before emitting the call.
2302 for (i = 0; i < sig->param_count + sig->hasthis; ++i) {
2304 MonoInst *in = call->args [i];
2306 if (i >= sig->hasthis)
2307 t = sig->params [i - sig->hasthis];
2309 t = &mono_defaults.int_class->byval_arg;
2310 t = mono_type_get_underlying_type (t);
2312 if (!t->byref && t->type == MONO_TYPE_R4) {
2313 MonoInst *iargs [1];
2317 conv = mono_emit_jit_icall (cfg, mono_fload_r4_arg, iargs);
2319 /* The result will be in an int vreg */
2320 call->args [i] = conv;
2326 call->need_unbox_trampoline = unbox_trampoline;
2329 if (COMPILE_LLVM (cfg))
2330 mono_llvm_emit_call (cfg, call);
2332 mono_arch_emit_call (cfg, call);
2334 mono_arch_emit_call (cfg, call);
2337 cfg->param_area = MAX (cfg->param_area, call->stack_usage);
2338 cfg->flags |= MONO_CFG_HAS_CALLS;
2344 set_rgctx_arg (MonoCompile *cfg, MonoCallInst *call, int rgctx_reg, MonoInst *rgctx_arg)
2346 mono_call_inst_add_outarg_reg (cfg, call, rgctx_reg, MONO_ARCH_RGCTX_REG, FALSE);
2347 cfg->uses_rgctx_reg = TRUE;
2348 call->rgctx_reg = TRUE;
2350 call->rgctx_arg_reg = rgctx_reg;
2355 mini_emit_calli (MonoCompile *cfg, MonoMethodSignature *sig, MonoInst **args, MonoInst *addr, MonoInst *imt_arg, MonoInst *rgctx_arg)
2360 gboolean check_sp = FALSE;
2362 if (cfg->check_pinvoke_callconv && cfg->method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE) {
2363 WrapperInfo *info = mono_marshal_get_wrapper_info (cfg->method);
2365 if (info && info->subtype == WRAPPER_SUBTYPE_PINVOKE)
2370 rgctx_reg = mono_alloc_preg (cfg);
2371 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, rgctx_reg, rgctx_arg->dreg);
2375 if (!cfg->stack_inbalance_var)
2376 cfg->stack_inbalance_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
2378 MONO_INST_NEW (cfg, ins, OP_GET_SP);
2379 ins->dreg = cfg->stack_inbalance_var->dreg;
2380 MONO_ADD_INS (cfg->cbb, ins);
2383 call = mono_emit_call_args (cfg, sig, args, TRUE, FALSE, FALSE, rgctx_arg ? TRUE : FALSE, FALSE);
2385 call->inst.sreg1 = addr->dreg;
2388 emit_imt_argument (cfg, call, NULL, imt_arg);
2390 MONO_ADD_INS (cfg->cbb, (MonoInst*)call);
2395 sp_reg = mono_alloc_preg (cfg);
2397 MONO_INST_NEW (cfg, ins, OP_GET_SP);
2399 MONO_ADD_INS (cfg->cbb, ins);
2401 /* Restore the stack so we don't crash when throwing the exception */
2402 MONO_INST_NEW (cfg, ins, OP_SET_SP);
2403 ins->sreg1 = cfg->stack_inbalance_var->dreg;
2404 MONO_ADD_INS (cfg->cbb, ins);
2406 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, cfg->stack_inbalance_var->dreg, sp_reg);
2407 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "ExecutionEngineException");
2411 set_rgctx_arg (cfg, call, rgctx_reg, rgctx_arg);
2413 return (MonoInst*)call;
2417 emit_get_rgctx_method (MonoCompile *cfg, int context_used, MonoMethod *cmethod, MonoRgctxInfoType rgctx_type);
2420 mono_emit_method_call_full (MonoCompile *cfg, MonoMethod *method, MonoMethodSignature *sig, gboolean tail,
2421 MonoInst **args, MonoInst *this_ins, MonoInst *imt_arg, MonoInst *rgctx_arg)
2423 #ifndef DISABLE_REMOTING
2424 gboolean might_be_remote = FALSE;
2426 gboolean virtual_ = this_ins != NULL;
2427 gboolean enable_for_aot = TRUE;
2430 MonoInst *call_target = NULL;
2432 gboolean need_unbox_trampoline;
2435 sig = mono_method_signature (method);
2437 if (cfg->llvm_only && (mono_class_is_interface (method->klass)))
2438 g_assert_not_reached ();
2441 rgctx_reg = mono_alloc_preg (cfg);
2442 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, rgctx_reg, rgctx_arg->dreg);
2445 if (method->string_ctor) {
2446 /* Create the real signature */
2447 /* FIXME: Cache these */
2448 MonoMethodSignature *ctor_sig = mono_metadata_signature_dup_mempool (cfg->mempool, sig);
2449 ctor_sig->ret = &mono_defaults.string_class->byval_arg;
2454 context_used = mini_method_check_context_used (cfg, method);
2456 #ifndef DISABLE_REMOTING
2457 might_be_remote = this_ins && sig->hasthis &&
2458 (mono_class_is_marshalbyref (method->klass) || method->klass == mono_defaults.object_class) &&
2459 !(method->flags & METHOD_ATTRIBUTE_VIRTUAL) && (!MONO_CHECK_THIS (this_ins) || context_used);
2461 if (might_be_remote && context_used) {
2464 g_assert (cfg->gshared);
2466 addr = emit_get_rgctx_method (cfg, context_used, method, MONO_RGCTX_INFO_REMOTING_INVOKE_WITH_CHECK);
2468 return mini_emit_calli (cfg, sig, args, addr, NULL, NULL);
2472 if (cfg->llvm_only && !call_target && virtual_ && (method->flags & METHOD_ATTRIBUTE_VIRTUAL))
2473 return emit_llvmonly_virtual_call (cfg, method, sig, 0, args);
2475 need_unbox_trampoline = method->klass == mono_defaults.object_class || mono_class_is_interface (method->klass);
2477 call = mono_emit_call_args (cfg, sig, args, FALSE, virtual_, tail, rgctx_arg ? TRUE : FALSE, need_unbox_trampoline);
2479 #ifndef DISABLE_REMOTING
2480 if (might_be_remote)
2481 call->method = mono_marshal_get_remoting_invoke_with_check (method);
2484 call->method = method;
2485 call->inst.flags |= MONO_INST_HAS_METHOD;
2486 call->inst.inst_left = this_ins;
2487 call->tail_call = tail;
2490 int vtable_reg, slot_reg, this_reg;
2493 this_reg = this_ins->dreg;
2495 if (!cfg->llvm_only && (method->klass->parent == mono_defaults.multicastdelegate_class) && !strcmp (method->name, "Invoke")) {
2496 MonoInst *dummy_use;
2498 MONO_EMIT_NULL_CHECK (cfg, this_reg);
2500 /* Make a call to delegate->invoke_impl */
2501 call->inst.inst_basereg = this_reg;
2502 call->inst.inst_offset = MONO_STRUCT_OFFSET (MonoDelegate, invoke_impl);
2503 MONO_ADD_INS (cfg->cbb, (MonoInst*)call);
2505 /* We must emit a dummy use here because the delegate trampoline will
2506 replace the 'this' argument with the delegate target making this activation
2507 no longer a root for the delegate.
2508 This is an issue for delegates that target collectible code such as dynamic
2509 methods of GC'able assemblies.
2511 For a test case look into #667921.
2513 FIXME: a dummy use is not the best way to do it as the local register allocator
2514 will put it on a caller save register and spil it around the call.
2515 Ideally, we would either put it on a callee save register or only do the store part.
2517 EMIT_NEW_DUMMY_USE (cfg, dummy_use, args [0]);
2519 return (MonoInst*)call;
2522 if ((!cfg->compile_aot || enable_for_aot) &&
2523 (!(method->flags & METHOD_ATTRIBUTE_VIRTUAL) ||
2524 (MONO_METHOD_IS_FINAL (method) &&
2525 method->wrapper_type != MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK)) &&
2526 !(mono_class_is_marshalbyref (method->klass) && context_used)) {
2528 * the method is not virtual, we just need to ensure this is not null
2529 * and then we can call the method directly.
2531 #ifndef DISABLE_REMOTING
2532 if (mono_class_is_marshalbyref (method->klass) || method->klass == mono_defaults.object_class) {
2534 * The check above ensures method is not gshared, this is needed since
2535 * gshared methods can't have wrappers.
2537 method = call->method = mono_marshal_get_remoting_invoke_with_check (method);
2541 if (!method->string_ctor)
2542 MONO_EMIT_NEW_CHECK_THIS (cfg, this_reg);
2544 call->inst.opcode = callvirt_to_call (call->inst.opcode);
2545 } else if ((method->flags & METHOD_ATTRIBUTE_VIRTUAL) && MONO_METHOD_IS_FINAL (method)) {
2547 * the method is virtual, but we can statically dispatch since either
2548 * it's class or the method itself are sealed.
2549 * But first we need to ensure it's not a null reference.
2551 MONO_EMIT_NEW_CHECK_THIS (cfg, this_reg);
2553 call->inst.opcode = callvirt_to_call (call->inst.opcode);
2554 } else if (call_target) {
2555 vtable_reg = alloc_preg (cfg);
2556 MONO_EMIT_NEW_LOAD_MEMBASE_FAULT (cfg, vtable_reg, this_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
2558 call->inst.opcode = callvirt_to_call_reg (call->inst.opcode);
2559 call->inst.sreg1 = call_target->dreg;
2560 call->inst.flags &= !MONO_INST_HAS_METHOD;
2562 vtable_reg = alloc_preg (cfg);
2563 MONO_EMIT_NEW_LOAD_MEMBASE_FAULT (cfg, vtable_reg, this_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
2564 if (mono_class_is_interface (method->klass)) {
2565 guint32 imt_slot = mono_method_get_imt_slot (method);
2566 emit_imt_argument (cfg, call, call->method, imt_arg);
2567 slot_reg = vtable_reg;
2568 offset = ((gint32)imt_slot - MONO_IMT_SIZE) * SIZEOF_VOID_P;
2570 slot_reg = vtable_reg;
2571 offset = MONO_STRUCT_OFFSET (MonoVTable, vtable) +
2572 ((mono_method_get_vtable_index (method)) * (SIZEOF_VOID_P));
2574 g_assert (mono_method_signature (method)->generic_param_count);
2575 emit_imt_argument (cfg, call, call->method, imt_arg);
2579 call->inst.sreg1 = slot_reg;
2580 call->inst.inst_offset = offset;
2581 call->is_virtual = TRUE;
2585 MONO_ADD_INS (cfg->cbb, (MonoInst*)call);
2588 set_rgctx_arg (cfg, call, rgctx_reg, rgctx_arg);
2590 return (MonoInst*)call;
2594 mono_emit_method_call (MonoCompile *cfg, MonoMethod *method, MonoInst **args, MonoInst *this_ins)
2596 return mono_emit_method_call_full (cfg, method, mono_method_signature (method), FALSE, args, this_ins, NULL, NULL);
2600 mono_emit_native_call (MonoCompile *cfg, gconstpointer func, MonoMethodSignature *sig,
2607 call = mono_emit_call_args (cfg, sig, args, FALSE, FALSE, FALSE, FALSE, FALSE);
2610 MONO_ADD_INS (cfg->cbb, (MonoInst*)call);
2612 return (MonoInst*)call;
2616 mono_emit_jit_icall (MonoCompile *cfg, gconstpointer func, MonoInst **args)
2618 MonoJitICallInfo *info = mono_find_jit_icall_by_addr (func);
2622 return mono_emit_native_call (cfg, mono_icall_get_wrapper (info), info->sig, args);
2626 * mono_emit_abs_call:
2628 * Emit a call to the runtime function described by PATCH_TYPE and DATA.
2630 inline static MonoInst*
2631 mono_emit_abs_call (MonoCompile *cfg, MonoJumpInfoType patch_type, gconstpointer data,
2632 MonoMethodSignature *sig, MonoInst **args)
2634 MonoJumpInfo *ji = mono_patch_info_new (cfg->mempool, 0, patch_type, data);
2638 * We pass ji as the call address, the PATCH_INFO_ABS resolving code will
2641 if (cfg->abs_patches == NULL)
2642 cfg->abs_patches = g_hash_table_new (NULL, NULL);
2643 g_hash_table_insert (cfg->abs_patches, ji, ji);
2644 ins = mono_emit_native_call (cfg, ji, sig, args);
2645 ((MonoCallInst*)ins)->fptr_is_patch = TRUE;
2649 static MonoMethodSignature*
2650 sig_to_rgctx_sig (MonoMethodSignature *sig)
2652 // FIXME: memory allocation
2653 MonoMethodSignature *res;
2656 res = (MonoMethodSignature *)g_malloc (MONO_SIZEOF_METHOD_SIGNATURE + (sig->param_count + 1) * sizeof (MonoType*));
2657 memcpy (res, sig, MONO_SIZEOF_METHOD_SIGNATURE);
2658 res->param_count = sig->param_count + 1;
2659 for (i = 0; i < sig->param_count; ++i)
2660 res->params [i] = sig->params [i];
2661 res->params [sig->param_count] = &mono_defaults.int_class->this_arg;
2665 /* Make an indirect call to FSIG passing an additional argument */
2667 emit_extra_arg_calli (MonoCompile *cfg, MonoMethodSignature *fsig, MonoInst **orig_args, int arg_reg, MonoInst *call_target)
2669 MonoMethodSignature *csig;
2670 MonoInst *args_buf [16];
2672 int i, pindex, tmp_reg;
2674 /* Make a call with an rgctx/extra arg */
2675 if (fsig->param_count + 2 < 16)
2678 args = (MonoInst **)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoInst*) * (fsig->param_count + 2));
2681 args [pindex ++] = orig_args [0];
2682 for (i = 0; i < fsig->param_count; ++i)
2683 args [pindex ++] = orig_args [fsig->hasthis + i];
2684 tmp_reg = alloc_preg (cfg);
2685 EMIT_NEW_UNALU (cfg, args [pindex], OP_MOVE, tmp_reg, arg_reg);
2686 csig = sig_to_rgctx_sig (fsig);
2687 return mini_emit_calli (cfg, csig, args, call_target, NULL, NULL);
2690 /* Emit an indirect call to the function descriptor ADDR */
2692 emit_llvmonly_calli (MonoCompile *cfg, MonoMethodSignature *fsig, MonoInst **args, MonoInst *addr)
2694 int addr_reg, arg_reg;
2695 MonoInst *call_target;
2697 g_assert (cfg->llvm_only);
2700 * addr points to a <addr, arg> pair, load both of them, and
2701 * make a call to addr, passing arg as an extra arg.
2703 addr_reg = alloc_preg (cfg);
2704 EMIT_NEW_LOAD_MEMBASE (cfg, call_target, OP_LOAD_MEMBASE, addr_reg, addr->dreg, 0);
2705 arg_reg = alloc_preg (cfg);
2706 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, arg_reg, addr->dreg, sizeof (gpointer));
2708 return emit_extra_arg_calli (cfg, fsig, args, arg_reg, call_target);
2712 direct_icalls_enabled (MonoCompile *cfg)
2716 /* LLVM on amd64 can't handle calls to non-32 bit addresses */
2718 if (cfg->compile_llvm && !cfg->llvm_only)
2721 if (cfg->gen_sdb_seq_points || cfg->disable_direct_icalls)
2727 mono_emit_jit_icall_by_info (MonoCompile *cfg, int il_offset, MonoJitICallInfo *info, MonoInst **args)
2730 * Call the jit icall without a wrapper if possible.
2731 * The wrapper is needed for the following reasons:
2732 * - to handle exceptions thrown using mono_raise_exceptions () from the
2733 * icall function. The EH code needs the lmf frame pushed by the
2734 * wrapper to be able to unwind back to managed code.
2735 * - to be able to do stack walks for asynchronously suspended
2736 * threads when debugging.
2738 if (info->no_raise && direct_icalls_enabled (cfg)) {
2742 if (!info->wrapper_method) {
2743 name = g_strdup_printf ("__icall_wrapper_%s", info->name);
2744 info->wrapper_method = mono_marshal_get_icall_wrapper (info->sig, name, info->func, TRUE);
2746 mono_memory_barrier ();
2750 * Inline the wrapper method, which is basically a call to the C icall, and
2751 * an exception check.
2753 costs = inline_method (cfg, info->wrapper_method, NULL,
2754 args, NULL, il_offset, TRUE);
2755 g_assert (costs > 0);
2756 g_assert (!MONO_TYPE_IS_VOID (info->sig->ret));
2760 return mono_emit_native_call (cfg, mono_icall_get_wrapper (info), info->sig, args);
2765 mono_emit_widen_call_res (MonoCompile *cfg, MonoInst *ins, MonoMethodSignature *fsig)
2767 if (!MONO_TYPE_IS_VOID (fsig->ret)) {
2768 if ((fsig->pinvoke || LLVM_ENABLED) && !fsig->ret->byref) {
2772 * Native code might return non register sized integers
2773 * without initializing the upper bits.
2775 switch (mono_type_to_load_membase (cfg, fsig->ret)) {
2776 case OP_LOADI1_MEMBASE:
2777 widen_op = OP_ICONV_TO_I1;
2779 case OP_LOADU1_MEMBASE:
2780 widen_op = OP_ICONV_TO_U1;
2782 case OP_LOADI2_MEMBASE:
2783 widen_op = OP_ICONV_TO_I2;
2785 case OP_LOADU2_MEMBASE:
2786 widen_op = OP_ICONV_TO_U2;
2792 if (widen_op != -1) {
2793 int dreg = alloc_preg (cfg);
2796 EMIT_NEW_UNALU (cfg, widen, widen_op, dreg, ins->dreg);
2797 widen->type = ins->type;
2808 emit_method_access_failure (MonoCompile *cfg, MonoMethod *caller, MonoMethod *callee)
2810 MonoInst *args [16];
2812 args [0] = emit_get_rgctx_method (cfg, mono_method_check_context_used (caller), caller, MONO_RGCTX_INFO_METHOD);
2813 args [1] = emit_get_rgctx_method (cfg, mono_method_check_context_used (callee), callee, MONO_RGCTX_INFO_METHOD);
2815 mono_emit_jit_icall (cfg, mono_throw_method_access, args);
2819 mini_get_memcpy_method (void)
2821 static MonoMethod *memcpy_method = NULL;
2822 if (!memcpy_method) {
2823 memcpy_method = mono_class_get_method_from_name (mono_defaults.string_class, "memcpy", 3);
2825 g_error ("Old corlib found. Install a new one");
2827 return memcpy_method;
2831 create_write_barrier_bitmap (MonoCompile *cfg, MonoClass *klass, unsigned *wb_bitmap, int offset)
2833 MonoClassField *field;
2834 gpointer iter = NULL;
2836 while ((field = mono_class_get_fields (klass, &iter))) {
2839 if (field->type->attrs & FIELD_ATTRIBUTE_STATIC)
2841 foffset = klass->valuetype ? field->offset - sizeof (MonoObject): field->offset;
2842 if (mini_type_is_reference (mono_field_get_type (field))) {
2843 g_assert ((foffset % SIZEOF_VOID_P) == 0);
2844 *wb_bitmap |= 1 << ((offset + foffset) / SIZEOF_VOID_P);
2846 MonoClass *field_class = mono_class_from_mono_type (field->type);
2847 if (field_class->has_references)
2848 create_write_barrier_bitmap (cfg, field_class, wb_bitmap, offset + foffset);
2854 mini_emit_write_barrier (MonoCompile *cfg, MonoInst *ptr, MonoInst *value)
2856 int card_table_shift_bits;
2857 gpointer card_table_mask;
2859 MonoInst *dummy_use;
2860 int nursery_shift_bits;
2861 size_t nursery_size;
2863 if (!cfg->gen_write_barriers)
2866 //method->wrapper_type != MONO_WRAPPER_WRITE_BARRIER && !MONO_INS_IS_PCONST_NULL (sp [1])
2868 card_table = mono_gc_get_card_table (&card_table_shift_bits, &card_table_mask);
2870 mono_gc_get_nursery (&nursery_shift_bits, &nursery_size);
2872 if (cfg->backend->have_card_table_wb && !cfg->compile_aot && card_table && nursery_shift_bits > 0 && !COMPILE_LLVM (cfg)) {
2875 MONO_INST_NEW (cfg, wbarrier, OP_CARD_TABLE_WBARRIER);
2876 wbarrier->sreg1 = ptr->dreg;
2877 wbarrier->sreg2 = value->dreg;
2878 MONO_ADD_INS (cfg->cbb, wbarrier);
2879 } else if (card_table) {
2880 int offset_reg = alloc_preg (cfg);
2885 * We emit a fast light weight write barrier. This always marks cards as in the concurrent
2886 * collector case, so, for the serial collector, it might slightly slow down nursery
2887 * collections. We also expect that the host system and the target system have the same card
2888 * table configuration, which is the case if they have the same pointer size.
2891 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SHR_UN_IMM, offset_reg, ptr->dreg, card_table_shift_bits);
2892 if (card_table_mask)
2893 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_PAND_IMM, offset_reg, offset_reg, card_table_mask);
2895 /*We can't use PADD_IMM since the cardtable might end up in high addresses and amd64 doesn't support
2896 * IMM's larger than 32bits.
2898 ins = mini_emit_runtime_constant (cfg, MONO_PATCH_INFO_GC_CARD_TABLE_ADDR, NULL);
2899 card_reg = ins->dreg;
2901 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, offset_reg, offset_reg, card_reg);
2902 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREI1_MEMBASE_IMM, offset_reg, 0, 1);
2904 MonoMethod *write_barrier = mono_gc_get_write_barrier ();
2905 mono_emit_method_call (cfg, write_barrier, &ptr, NULL);
2908 EMIT_NEW_DUMMY_USE (cfg, dummy_use, value);
2912 mini_emit_wb_aware_memcpy (MonoCompile *cfg, MonoClass *klass, MonoInst *iargs[4], int size, int align)
2914 int dest_ptr_reg, tmp_reg, destreg, srcreg, offset;
2915 unsigned need_wb = 0;
2920 /*types with references can't have alignment smaller than sizeof(void*) */
2921 if (align < SIZEOF_VOID_P)
2924 if (size > 5 * SIZEOF_VOID_P)
2927 create_write_barrier_bitmap (cfg, klass, &need_wb, 0);
2929 destreg = iargs [0]->dreg;
2930 srcreg = iargs [1]->dreg;
2933 dest_ptr_reg = alloc_preg (cfg);
2934 tmp_reg = alloc_preg (cfg);
2937 EMIT_NEW_UNALU (cfg, iargs [0], OP_MOVE, dest_ptr_reg, destreg);
2939 while (size >= SIZEOF_VOID_P) {
2940 MonoInst *load_inst;
2941 MONO_INST_NEW (cfg, load_inst, OP_LOAD_MEMBASE);
2942 load_inst->dreg = tmp_reg;
2943 load_inst->inst_basereg = srcreg;
2944 load_inst->inst_offset = offset;
2945 MONO_ADD_INS (cfg->cbb, load_inst);
2947 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREP_MEMBASE_REG, dest_ptr_reg, 0, tmp_reg);
2950 mini_emit_write_barrier (cfg, iargs [0], load_inst);
2952 offset += SIZEOF_VOID_P;
2953 size -= SIZEOF_VOID_P;
2956 /*tmp += sizeof (void*)*/
2957 if (size >= SIZEOF_VOID_P) {
2958 NEW_BIALU_IMM (cfg, iargs [0], OP_PADD_IMM, dest_ptr_reg, dest_ptr_reg, SIZEOF_VOID_P);
2959 MONO_ADD_INS (cfg->cbb, iargs [0]);
2963 /* Those cannot be references since size < sizeof (void*) */
2965 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI4_MEMBASE, tmp_reg, srcreg, offset);
2966 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI4_MEMBASE_REG, destreg, offset, tmp_reg);
2972 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI2_MEMBASE, tmp_reg, srcreg, offset);
2973 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI2_MEMBASE_REG, destreg, offset, tmp_reg);
2979 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI1_MEMBASE, tmp_reg, srcreg, offset);
2980 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI1_MEMBASE_REG, destreg, offset, tmp_reg);
2989 * Emit code to copy a valuetype of type @klass whose address is stored in
2990 * @src->dreg to memory whose address is stored at @dest->dreg.
2993 mini_emit_stobj (MonoCompile *cfg, MonoInst *dest, MonoInst *src, MonoClass *klass, gboolean native)
2995 MonoInst *iargs [4];
2998 MonoMethod *memcpy_method;
2999 MonoInst *size_ins = NULL;
3000 MonoInst *memcpy_ins = NULL;
3004 klass = mono_class_from_mono_type (mini_get_underlying_type (&klass->byval_arg));
3007 * This check breaks with spilled vars... need to handle it during verification anyway.
3008 * g_assert (klass && klass == src->klass && klass == dest->klass);
3011 if (mini_is_gsharedvt_klass (klass)) {
3013 size_ins = mini_emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_VALUE_SIZE);
3014 memcpy_ins = mini_emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_MEMCPY);
3018 n = mono_class_native_size (klass, &align);
3020 n = mono_class_value_size (klass, &align);
3023 align = SIZEOF_VOID_P;
3024 /* if native is true there should be no references in the struct */
3025 if (cfg->gen_write_barriers && (klass->has_references || size_ins) && !native) {
3026 /* Avoid barriers when storing to the stack */
3027 if (!((dest->opcode == OP_ADD_IMM && dest->sreg1 == cfg->frame_reg) ||
3028 (dest->opcode == OP_LDADDR))) {
3034 context_used = mini_class_check_context_used (cfg, klass);
3036 /* It's ok to intrinsify under gsharing since shared code types are layout stable. */
3037 if (!size_ins && (cfg->opt & MONO_OPT_INTRINS) && mini_emit_wb_aware_memcpy (cfg, klass, iargs, n, align)) {
3039 } else if (size_ins || align < SIZEOF_VOID_P) {
3041 iargs [2] = mini_emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_KLASS);
3043 iargs [2] = mini_emit_runtime_constant (cfg, MONO_PATCH_INFO_CLASS, klass);
3044 if (!cfg->compile_aot)
3045 mono_class_compute_gc_descriptor (klass);
3048 mono_emit_jit_icall (cfg, mono_gsharedvt_value_copy, iargs);
3050 mono_emit_jit_icall (cfg, mono_value_copy, iargs);
3052 /* We don't unroll more than 5 stores to avoid code bloat. */
3053 /*This is harmless and simplify mono_gc_get_range_copy_func */
3054 n += (SIZEOF_VOID_P - 1);
3055 n &= ~(SIZEOF_VOID_P - 1);
3057 EMIT_NEW_ICONST (cfg, iargs [2], n);
3058 mono_emit_jit_icall (cfg, mono_gc_get_range_copy_func (), iargs);
3063 if (!size_ins && (cfg->opt & MONO_OPT_INTRINS) && n <= sizeof (gpointer) * 8) {
3064 /* FIXME: Optimize the case when src/dest is OP_LDADDR */
3065 mini_emit_memcpy (cfg, dest->dreg, 0, src->dreg, 0, n, align);
3070 iargs [2] = size_ins;
3072 EMIT_NEW_ICONST (cfg, iargs [2], n);
3074 memcpy_method = mini_get_memcpy_method ();
3076 mini_emit_calli (cfg, mono_method_signature (memcpy_method), iargs, memcpy_ins, NULL, NULL);
3078 mono_emit_method_call (cfg, memcpy_method, iargs, NULL);
3083 mini_get_memset_method (void)
3085 static MonoMethod *memset_method = NULL;
3086 if (!memset_method) {
3087 memset_method = mono_class_get_method_from_name (mono_defaults.string_class, "memset", 3);
3089 g_error ("Old corlib found. Install a new one");
3091 return memset_method;
3095 mini_emit_initobj (MonoCompile *cfg, MonoInst *dest, const guchar *ip, MonoClass *klass)
3097 MonoInst *iargs [3];
3100 MonoMethod *memset_method;
3101 MonoInst *size_ins = NULL;
3102 MonoInst *bzero_ins = NULL;
3103 static MonoMethod *bzero_method;
3105 /* FIXME: Optimize this for the case when dest is an LDADDR */
3106 mono_class_init (klass);
3107 if (mini_is_gsharedvt_klass (klass)) {
3108 size_ins = mini_emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_VALUE_SIZE);
3109 bzero_ins = mini_emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_BZERO);
3111 bzero_method = mono_class_get_method_from_name (mono_defaults.string_class, "bzero_aligned_1", 2);
3112 g_assert (bzero_method);
3114 iargs [1] = size_ins;
3115 mini_emit_calli (cfg, mono_method_signature (bzero_method), iargs, bzero_ins, NULL, NULL);
3119 klass = mono_class_from_mono_type (mini_get_underlying_type (&klass->byval_arg));
3121 n = mono_class_value_size (klass, &align);
3123 if (n <= sizeof (gpointer) * 8) {
3124 mini_emit_memset (cfg, dest->dreg, 0, n, 0, align);
3127 memset_method = mini_get_memset_method ();
3129 EMIT_NEW_ICONST (cfg, iargs [1], 0);
3130 EMIT_NEW_ICONST (cfg, iargs [2], n);
3131 mono_emit_method_call (cfg, memset_method, iargs, NULL);
3138 * Emit IR to return either the this pointer for instance method,
3139 * or the mrgctx for static methods.
3142 emit_get_rgctx (MonoCompile *cfg, MonoMethod *method, int context_used)
3144 MonoInst *this_ins = NULL;
3146 g_assert (cfg->gshared);
3148 if (!(method->flags & METHOD_ATTRIBUTE_STATIC) &&
3149 !(context_used & MONO_GENERIC_CONTEXT_USED_METHOD) &&
3150 !method->klass->valuetype)
3151 EMIT_NEW_VARLOAD (cfg, this_ins, cfg->this_arg, &mono_defaults.object_class->byval_arg);
3153 if (context_used & MONO_GENERIC_CONTEXT_USED_METHOD) {
3154 MonoInst *mrgctx_loc, *mrgctx_var;
3156 g_assert (!this_ins);
3157 g_assert (method->is_inflated && mono_method_get_context (method)->method_inst);
3159 mrgctx_loc = mono_get_vtable_var (cfg);
3160 EMIT_NEW_TEMPLOAD (cfg, mrgctx_var, mrgctx_loc->inst_c0);
3163 } else if (MONO_CLASS_IS_INTERFACE (cfg->method->klass)) {
3164 MonoInst *mrgctx_loc, *mrgctx_var;
3166 /* Default interface methods need an mrgctx since the vtabke at runtime points at an implementing class */
3167 mrgctx_loc = mono_get_vtable_var (cfg);
3168 EMIT_NEW_TEMPLOAD (cfg, mrgctx_var, mrgctx_loc->inst_c0);
3170 g_assert (mono_method_needs_static_rgctx_invoke (cfg->method, TRUE));
3173 } else if (method->flags & METHOD_ATTRIBUTE_STATIC || method->klass->valuetype) {
3174 MonoInst *vtable_loc, *vtable_var;
3176 g_assert (!this_ins);
3178 vtable_loc = mono_get_vtable_var (cfg);
3179 EMIT_NEW_TEMPLOAD (cfg, vtable_var, vtable_loc->inst_c0);
3181 if (method->is_inflated && mono_method_get_context (method)->method_inst) {
3182 MonoInst *mrgctx_var = vtable_var;
3185 vtable_reg = alloc_preg (cfg);
3186 EMIT_NEW_LOAD_MEMBASE (cfg, vtable_var, OP_LOAD_MEMBASE, vtable_reg, mrgctx_var->dreg, MONO_STRUCT_OFFSET (MonoMethodRuntimeGenericContext, class_vtable));
3187 vtable_var->type = STACK_PTR;
3195 vtable_reg = alloc_preg (cfg);
3196 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, vtable_reg, this_ins->dreg, MONO_STRUCT_OFFSET (MonoObject, vtable));
3201 static MonoJumpInfoRgctxEntry *
3202 mono_patch_info_rgctx_entry_new (MonoMemPool *mp, MonoMethod *method, gboolean in_mrgctx, MonoJumpInfoType patch_type, gconstpointer patch_data, MonoRgctxInfoType info_type)
3204 MonoJumpInfoRgctxEntry *res = (MonoJumpInfoRgctxEntry *)mono_mempool_alloc0 (mp, sizeof (MonoJumpInfoRgctxEntry));
3205 res->method = method;
3206 res->in_mrgctx = in_mrgctx;
3207 res->data = (MonoJumpInfo *)mono_mempool_alloc0 (mp, sizeof (MonoJumpInfo));
3208 res->data->type = patch_type;
3209 res->data->data.target = patch_data;
3210 res->info_type = info_type;
3215 static inline MonoInst*
3216 emit_rgctx_fetch_inline (MonoCompile *cfg, MonoInst *rgctx, MonoJumpInfoRgctxEntry *entry)
3218 MonoInst *args [16];
3221 // FIXME: No fastpath since the slot is not a compile time constant
3223 EMIT_NEW_AOTCONST (cfg, args [1], MONO_PATCH_INFO_RGCTX_SLOT_INDEX, entry);
3224 if (entry->in_mrgctx)
3225 call = mono_emit_jit_icall (cfg, mono_fill_method_rgctx, args);
3227 call = mono_emit_jit_icall (cfg, mono_fill_class_rgctx, args);
3231 * FIXME: This can be called during decompose, which is a problem since it creates
3233 * Also, the fastpath doesn't work since the slot number is dynamically allocated.
3235 int i, slot, depth, index, rgctx_reg, val_reg, res_reg;
3237 MonoBasicBlock *is_null_bb, *end_bb;
3238 MonoInst *res, *ins, *call;
3241 slot = mini_get_rgctx_entry_slot (entry);
3243 mrgctx = MONO_RGCTX_SLOT_IS_MRGCTX (slot);
3244 index = MONO_RGCTX_SLOT_INDEX (slot);
3246 index += MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT / sizeof (gpointer);
3247 for (depth = 0; ; ++depth) {
3248 int size = mono_class_rgctx_get_array_size (depth, mrgctx);
3250 if (index < size - 1)
3255 NEW_BBLOCK (cfg, end_bb);
3256 NEW_BBLOCK (cfg, is_null_bb);
3259 rgctx_reg = rgctx->dreg;
3261 rgctx_reg = alloc_preg (cfg);
3263 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, rgctx_reg, rgctx->dreg, MONO_STRUCT_OFFSET (MonoVTable, runtime_generic_context));
3264 // FIXME: Avoid this check by allocating the table when the vtable is created etc.
3265 NEW_BBLOCK (cfg, is_null_bb);
3267 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, rgctx_reg, 0);
3268 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, is_null_bb);
3271 for (i = 0; i < depth; ++i) {
3272 int array_reg = alloc_preg (cfg);
3274 /* load ptr to next array */
3275 if (mrgctx && i == 0)
3276 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, array_reg, rgctx_reg, MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT);
3278 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, array_reg, rgctx_reg, 0);
3279 rgctx_reg = array_reg;
3280 /* is the ptr null? */
3281 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, rgctx_reg, 0);
3282 /* if yes, jump to actual trampoline */
3283 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, is_null_bb);
3287 val_reg = alloc_preg (cfg);
3288 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, val_reg, rgctx_reg, (index + 1) * sizeof (gpointer));
3289 /* is the slot null? */
3290 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, val_reg, 0);
3291 /* if yes, jump to actual trampoline */
3292 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, is_null_bb);
3295 res_reg = alloc_preg (cfg);
3296 MONO_INST_NEW (cfg, ins, OP_MOVE);
3297 ins->dreg = res_reg;
3298 ins->sreg1 = val_reg;
3299 MONO_ADD_INS (cfg->cbb, ins);
3301 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
3304 MONO_START_BB (cfg, is_null_bb);
3306 EMIT_NEW_ICONST (cfg, args [1], index);
3308 call = mono_emit_jit_icall (cfg, mono_fill_method_rgctx, args);
3310 call = mono_emit_jit_icall (cfg, mono_fill_class_rgctx, args);
3311 MONO_INST_NEW (cfg, ins, OP_MOVE);
3312 ins->dreg = res_reg;
3313 ins->sreg1 = call->dreg;
3314 MONO_ADD_INS (cfg->cbb, ins);
3315 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
3317 MONO_START_BB (cfg, end_bb);
3326 * Emit IR to load the value of the rgctx entry ENTRY from the rgctx
3329 static inline MonoInst*
3330 emit_rgctx_fetch (MonoCompile *cfg, MonoInst *rgctx, MonoJumpInfoRgctxEntry *entry)
3333 return emit_rgctx_fetch_inline (cfg, rgctx, entry);
3335 return mono_emit_abs_call (cfg, MONO_PATCH_INFO_RGCTX_FETCH, entry, helper_sig_rgctx_lazy_fetch_trampoline, &rgctx);
3339 mini_emit_get_rgctx_klass (MonoCompile *cfg, int context_used,
3340 MonoClass *klass, MonoRgctxInfoType rgctx_type)
3342 MonoJumpInfoRgctxEntry *entry = mono_patch_info_rgctx_entry_new (cfg->mempool, cfg->method, context_used & MONO_GENERIC_CONTEXT_USED_METHOD, MONO_PATCH_INFO_CLASS, klass, rgctx_type);
3343 MonoInst *rgctx = emit_get_rgctx (cfg, cfg->method, context_used);
3345 return emit_rgctx_fetch (cfg, rgctx, entry);
3349 emit_get_rgctx_sig (MonoCompile *cfg, int context_used,
3350 MonoMethodSignature *sig, MonoRgctxInfoType rgctx_type)
3352 MonoJumpInfoRgctxEntry *entry = mono_patch_info_rgctx_entry_new (cfg->mempool, cfg->method, context_used & MONO_GENERIC_CONTEXT_USED_METHOD, MONO_PATCH_INFO_SIGNATURE, sig, rgctx_type);
3353 MonoInst *rgctx = emit_get_rgctx (cfg, cfg->method, context_used);
3355 return emit_rgctx_fetch (cfg, rgctx, entry);
3359 emit_get_rgctx_gsharedvt_call (MonoCompile *cfg, int context_used,
3360 MonoMethodSignature *sig, MonoMethod *cmethod, MonoRgctxInfoType rgctx_type)
3362 MonoJumpInfoGSharedVtCall *call_info;
3363 MonoJumpInfoRgctxEntry *entry;
3366 call_info = (MonoJumpInfoGSharedVtCall *)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoJumpInfoGSharedVtCall));
3367 call_info->sig = sig;
3368 call_info->method = cmethod;
3370 entry = mono_patch_info_rgctx_entry_new (cfg->mempool, cfg->method, context_used & MONO_GENERIC_CONTEXT_USED_METHOD, MONO_PATCH_INFO_GSHAREDVT_CALL, call_info, rgctx_type);
3371 rgctx = emit_get_rgctx (cfg, cfg->method, context_used);
3373 return emit_rgctx_fetch (cfg, rgctx, entry);
3377 * emit_get_rgctx_virt_method:
3379 * Return data for method VIRT_METHOD for a receiver of type KLASS.
3382 emit_get_rgctx_virt_method (MonoCompile *cfg, int context_used,
3383 MonoClass *klass, MonoMethod *virt_method, MonoRgctxInfoType rgctx_type)
3385 MonoJumpInfoVirtMethod *info;
3386 MonoJumpInfoRgctxEntry *entry;
3389 info = (MonoJumpInfoVirtMethod *)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoJumpInfoVirtMethod));
3390 info->klass = klass;
3391 info->method = virt_method;
3393 entry = mono_patch_info_rgctx_entry_new (cfg->mempool, cfg->method, context_used & MONO_GENERIC_CONTEXT_USED_METHOD, MONO_PATCH_INFO_VIRT_METHOD, info, rgctx_type);
3394 rgctx = emit_get_rgctx (cfg, cfg->method, context_used);
3396 return emit_rgctx_fetch (cfg, rgctx, entry);
3400 emit_get_rgctx_gsharedvt_method (MonoCompile *cfg, int context_used,
3401 MonoMethod *cmethod, MonoGSharedVtMethodInfo *info)
3403 MonoJumpInfoRgctxEntry *entry;
3406 entry = mono_patch_info_rgctx_entry_new (cfg->mempool, cfg->method, context_used & MONO_GENERIC_CONTEXT_USED_METHOD, MONO_PATCH_INFO_GSHAREDVT_METHOD, info, MONO_RGCTX_INFO_METHOD_GSHAREDVT_INFO);
3407 rgctx = emit_get_rgctx (cfg, cfg->method, context_used);
3409 return emit_rgctx_fetch (cfg, rgctx, entry);
3413 * emit_get_rgctx_method:
3415 * Emit IR to load the property RGCTX_TYPE of CMETHOD. If context_used is 0, emit
3416 * normal constants, else emit a load from the rgctx.
3419 emit_get_rgctx_method (MonoCompile *cfg, int context_used,
3420 MonoMethod *cmethod, MonoRgctxInfoType rgctx_type)
3422 if (!context_used) {
3425 switch (rgctx_type) {
3426 case MONO_RGCTX_INFO_METHOD:
3427 EMIT_NEW_METHODCONST (cfg, ins, cmethod);
3429 case MONO_RGCTX_INFO_METHOD_RGCTX:
3430 EMIT_NEW_METHOD_RGCTX_CONST (cfg, ins, cmethod);
3433 g_assert_not_reached ();
3436 MonoJumpInfoRgctxEntry *entry = mono_patch_info_rgctx_entry_new (cfg->mempool, cfg->method, context_used & MONO_GENERIC_CONTEXT_USED_METHOD, MONO_PATCH_INFO_METHODCONST, cmethod, rgctx_type);
3437 MonoInst *rgctx = emit_get_rgctx (cfg, cfg->method, context_used);
3439 return emit_rgctx_fetch (cfg, rgctx, entry);
3444 emit_get_rgctx_field (MonoCompile *cfg, int context_used,
3445 MonoClassField *field, MonoRgctxInfoType rgctx_type)
3447 MonoJumpInfoRgctxEntry *entry = mono_patch_info_rgctx_entry_new (cfg->mempool, cfg->method, context_used & MONO_GENERIC_CONTEXT_USED_METHOD, MONO_PATCH_INFO_FIELD, field, rgctx_type);
3448 MonoInst *rgctx = emit_get_rgctx (cfg, cfg->method, context_used);
3450 return emit_rgctx_fetch (cfg, rgctx, entry);
3454 get_gsharedvt_info_slot (MonoCompile *cfg, gpointer data, MonoRgctxInfoType rgctx_type)
3456 MonoGSharedVtMethodInfo *info = cfg->gsharedvt_info;
3457 MonoRuntimeGenericContextInfoTemplate *template_;
3462 for (i = 0; i < info->num_entries; ++i) {
3463 MonoRuntimeGenericContextInfoTemplate *otemplate = &info->entries [i];
3465 if (otemplate->info_type == rgctx_type && otemplate->data == data && rgctx_type != MONO_RGCTX_INFO_LOCAL_OFFSET)
3469 if (info->num_entries == info->count_entries) {
3470 MonoRuntimeGenericContextInfoTemplate *new_entries;
3471 int new_count_entries = info->count_entries ? info->count_entries * 2 : 16;
3473 new_entries = (MonoRuntimeGenericContextInfoTemplate *)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoRuntimeGenericContextInfoTemplate) * new_count_entries);
3475 memcpy (new_entries, info->entries, sizeof (MonoRuntimeGenericContextInfoTemplate) * info->count_entries);
3476 info->entries = new_entries;
3477 info->count_entries = new_count_entries;
3480 idx = info->num_entries;
3481 template_ = &info->entries [idx];
3482 template_->info_type = rgctx_type;
3483 template_->data = data;
3485 info->num_entries ++;
3491 * emit_get_gsharedvt_info:
3493 * This is similar to emit_get_rgctx_.., but loads the data from the gsharedvt info var instead of calling an rgctx fetch trampoline.
3496 emit_get_gsharedvt_info (MonoCompile *cfg, gpointer data, MonoRgctxInfoType rgctx_type)
3501 idx = get_gsharedvt_info_slot (cfg, data, rgctx_type);
3502 /* Load info->entries [idx] */
3503 dreg = alloc_preg (cfg);
3504 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, dreg, cfg->gsharedvt_info_var->dreg, MONO_STRUCT_OFFSET (MonoGSharedVtMethodRuntimeInfo, entries) + (idx * sizeof (gpointer)));
3510 mini_emit_get_gsharedvt_info_klass (MonoCompile *cfg, MonoClass *klass, MonoRgctxInfoType rgctx_type)
3512 return emit_get_gsharedvt_info (cfg, &klass->byval_arg, rgctx_type);
3516 * On return the caller must check @klass for load errors.
3519 emit_class_init (MonoCompile *cfg, MonoClass *klass)
3521 MonoInst *vtable_arg;
3524 context_used = mini_class_check_context_used (cfg, klass);
3527 vtable_arg = mini_emit_get_rgctx_klass (cfg, context_used,
3528 klass, MONO_RGCTX_INFO_VTABLE);
3530 MonoVTable *vtable = mono_class_vtable (cfg->domain, klass);
3534 EMIT_NEW_VTABLECONST (cfg, vtable_arg, vtable);
3537 if (!COMPILE_LLVM (cfg) && cfg->backend->have_op_generic_class_init) {
3541 * Using an opcode instead of emitting IR here allows the hiding of the call inside the opcode,
3542 * so this doesn't have to clobber any regs and it doesn't break basic blocks.
3544 MONO_INST_NEW (cfg, ins, OP_GENERIC_CLASS_INIT);
3545 ins->sreg1 = vtable_arg->dreg;
3546 MONO_ADD_INS (cfg->cbb, ins);
3549 MonoBasicBlock *inited_bb;
3550 MonoInst *args [16];
3552 inited_reg = alloc_ireg (cfg);
3554 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU1_MEMBASE, inited_reg, vtable_arg->dreg, MONO_STRUCT_OFFSET (MonoVTable, initialized));
3556 NEW_BBLOCK (cfg, inited_bb);
3558 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, inited_reg, 0);
3559 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBNE_UN, inited_bb);
3561 args [0] = vtable_arg;
3562 mono_emit_jit_icall (cfg, mono_generic_class_init, args);
3564 MONO_START_BB (cfg, inited_bb);
3569 emit_seq_point (MonoCompile *cfg, MonoMethod *method, guint8* ip, gboolean intr_loc, gboolean nonempty_stack)
3573 if (cfg->gen_seq_points && cfg->method == method) {
3574 NEW_SEQ_POINT (cfg, ins, ip - cfg->header->code, intr_loc);
3576 ins->flags |= MONO_INST_NONEMPTY_STACK;
3577 MONO_ADD_INS (cfg->cbb, ins);
3582 mini_save_cast_details (MonoCompile *cfg, MonoClass *klass, int obj_reg, gboolean null_check)
3584 if (mini_get_debug_options ()->better_cast_details) {
3585 int vtable_reg = alloc_preg (cfg);
3586 int klass_reg = alloc_preg (cfg);
3587 MonoBasicBlock *is_null_bb = NULL;
3589 int to_klass_reg, context_used;
3592 NEW_BBLOCK (cfg, is_null_bb);
3594 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, obj_reg, 0);
3595 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, is_null_bb);
3598 tls_get = mono_create_tls_get (cfg, TLS_KEY_JIT_TLS);
3600 fprintf (stderr, "error: --debug=casts not supported on this platform.\n.");
3604 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, vtable_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
3605 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
3607 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, tls_get->dreg, MONO_STRUCT_OFFSET (MonoJitTlsData, class_cast_from), klass_reg);
3609 context_used = mini_class_check_context_used (cfg, klass);
3611 MonoInst *class_ins;
3613 class_ins = mini_emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_KLASS);
3614 to_klass_reg = class_ins->dreg;
3616 to_klass_reg = alloc_preg (cfg);
3617 MONO_EMIT_NEW_CLASSCONST (cfg, to_klass_reg, klass);
3619 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, tls_get->dreg, MONO_STRUCT_OFFSET (MonoJitTlsData, class_cast_to), to_klass_reg);
3622 MONO_START_BB (cfg, is_null_bb);
3627 mini_reset_cast_details (MonoCompile *cfg)
3629 /* Reset the variables holding the cast details */
3630 if (mini_get_debug_options ()->better_cast_details) {
3631 MonoInst *tls_get = mono_create_tls_get (cfg, TLS_KEY_JIT_TLS);
3632 /* It is enough to reset the from field */
3633 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STORE_MEMBASE_IMM, tls_get->dreg, MONO_STRUCT_OFFSET (MonoJitTlsData, class_cast_from), 0);
3638 * On return the caller must check @array_class for load errors
3641 mini_emit_check_array_type (MonoCompile *cfg, MonoInst *obj, MonoClass *array_class)
3643 int vtable_reg = alloc_preg (cfg);
3646 context_used = mini_class_check_context_used (cfg, array_class);
3648 mini_save_cast_details (cfg, array_class, obj->dreg, FALSE);
3650 MONO_EMIT_NEW_LOAD_MEMBASE_FAULT (cfg, vtable_reg, obj->dreg, MONO_STRUCT_OFFSET (MonoObject, vtable));
3652 if (cfg->opt & MONO_OPT_SHARED) {
3653 int class_reg = alloc_preg (cfg);
3656 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, class_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
3657 ins = mini_emit_runtime_constant (cfg, MONO_PATCH_INFO_CLASS, array_class);
3658 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, class_reg, ins->dreg);
3659 } else if (context_used) {
3660 MonoInst *vtable_ins;
3662 vtable_ins = mini_emit_get_rgctx_klass (cfg, context_used, array_class, MONO_RGCTX_INFO_VTABLE);
3663 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, vtable_reg, vtable_ins->dreg);
3665 if (cfg->compile_aot) {
3669 if (!(vtable = mono_class_vtable (cfg->domain, array_class)))
3671 vt_reg = alloc_preg (cfg);
3672 MONO_EMIT_NEW_VTABLECONST (cfg, vt_reg, vtable);
3673 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, vtable_reg, vt_reg);
3676 if (!(vtable = mono_class_vtable (cfg->domain, array_class)))
3678 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, vtable_reg, vtable);
3682 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "ArrayTypeMismatchException");
3684 mini_reset_cast_details (cfg);
3688 * Handles unbox of a Nullable<T>. If context_used is non zero, then shared
3689 * generic code is generated.
3692 handle_unbox_nullable (MonoCompile* cfg, MonoInst* val, MonoClass* klass, int context_used)
3694 MonoMethod* method = mono_class_get_method_from_name (klass, "Unbox", 1);
3697 MonoInst *rgctx, *addr;
3699 /* FIXME: What if the class is shared? We might not
3700 have to get the address of the method from the
3702 addr = emit_get_rgctx_method (cfg, context_used, method,
3703 MONO_RGCTX_INFO_GENERIC_METHOD_CODE);
3704 if (cfg->llvm_only) {
3705 cfg->signatures = g_slist_prepend_mempool (cfg->mempool, cfg->signatures, mono_method_signature (method));
3706 return emit_llvmonly_calli (cfg, mono_method_signature (method), &val, addr);
3708 rgctx = emit_get_rgctx (cfg, cfg->method, context_used);
3710 return mini_emit_calli (cfg, mono_method_signature (method), &val, addr, NULL, rgctx);
3713 gboolean pass_vtable, pass_mrgctx;
3714 MonoInst *rgctx_arg = NULL;
3716 check_method_sharing (cfg, method, &pass_vtable, &pass_mrgctx);
3717 g_assert (!pass_mrgctx);
3720 MonoVTable *vtable = mono_class_vtable (cfg->domain, method->klass);
3723 EMIT_NEW_VTABLECONST (cfg, rgctx_arg, vtable);
3726 return mono_emit_method_call_full (cfg, method, NULL, FALSE, &val, NULL, NULL, rgctx_arg);
3731 handle_unbox (MonoCompile *cfg, MonoClass *klass, MonoInst **sp, int context_used)
3735 int vtable_reg = alloc_dreg (cfg ,STACK_PTR);
3736 int klass_reg = alloc_dreg (cfg ,STACK_PTR);
3737 int eclass_reg = alloc_dreg (cfg ,STACK_PTR);
3738 int rank_reg = alloc_dreg (cfg ,STACK_I4);
3740 obj_reg = sp [0]->dreg;
3741 MONO_EMIT_NEW_LOAD_MEMBASE_FAULT (cfg, vtable_reg, obj_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
3742 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADU1_MEMBASE, rank_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, rank));
3744 /* FIXME: generics */
3745 g_assert (klass->rank == 0);
3748 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, rank_reg, 0);
3749 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException");
3751 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, klass));
3752 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, eclass_reg, klass_reg, MONO_STRUCT_OFFSET (MonoClass, element_class));
3755 MonoInst *element_class;
3757 /* This assertion is from the unboxcast insn */
3758 g_assert (klass->rank == 0);
3760 element_class = mini_emit_get_rgctx_klass (cfg, context_used,
3761 klass, MONO_RGCTX_INFO_ELEMENT_KLASS);
3763 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, eclass_reg, element_class->dreg);
3764 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException");
3766 mini_save_cast_details (cfg, klass->element_class, obj_reg, FALSE);
3767 mini_emit_class_check (cfg, eclass_reg, klass->element_class);
3768 mini_reset_cast_details (cfg);
3771 NEW_BIALU_IMM (cfg, add, OP_ADD_IMM, alloc_dreg (cfg, STACK_MP), obj_reg, sizeof (MonoObject));
3772 MONO_ADD_INS (cfg->cbb, add);
3773 add->type = STACK_MP;
3780 handle_unbox_gsharedvt (MonoCompile *cfg, MonoClass *klass, MonoInst *obj)
3782 MonoInst *addr, *klass_inst, *is_ref, *args[16];
3783 MonoBasicBlock *is_ref_bb, *is_nullable_bb, *end_bb;
3787 klass_inst = mini_emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_KLASS);
3793 args [1] = klass_inst;
3796 obj = mono_emit_jit_icall (cfg, mono_object_castclass_unbox, args);
3798 NEW_BBLOCK (cfg, is_ref_bb);
3799 NEW_BBLOCK (cfg, is_nullable_bb);
3800 NEW_BBLOCK (cfg, end_bb);
3801 is_ref = mini_emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_CLASS_BOX_TYPE);
3802 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, is_ref->dreg, MONO_GSHAREDVT_BOX_TYPE_REF);
3803 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, is_ref_bb);
3805 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, is_ref->dreg, MONO_GSHAREDVT_BOX_TYPE_NULLABLE);
3806 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, is_nullable_bb);
3808 /* This will contain either the address of the unboxed vtype, or an address of the temporary where the ref is stored */
3809 addr_reg = alloc_dreg (cfg, STACK_MP);
3813 NEW_BIALU_IMM (cfg, addr, OP_ADD_IMM, addr_reg, obj->dreg, sizeof (MonoObject));
3814 MONO_ADD_INS (cfg->cbb, addr);
3816 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
3819 MONO_START_BB (cfg, is_ref_bb);
3821 /* Save the ref to a temporary */
3822 dreg = alloc_ireg (cfg);
3823 EMIT_NEW_VARLOADA_VREG (cfg, addr, dreg, &klass->byval_arg);
3824 addr->dreg = addr_reg;
3825 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, addr->dreg, 0, obj->dreg);
3826 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
3829 MONO_START_BB (cfg, is_nullable_bb);
3832 MonoInst *addr = mini_emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_NULLABLE_CLASS_UNBOX);
3833 MonoInst *unbox_call;
3834 MonoMethodSignature *unbox_sig;
3836 unbox_sig = (MonoMethodSignature *)mono_mempool_alloc0 (cfg->mempool, MONO_SIZEOF_METHOD_SIGNATURE + (1 * sizeof (MonoType *)));
3837 unbox_sig->ret = &klass->byval_arg;
3838 unbox_sig->param_count = 1;
3839 unbox_sig->params [0] = &mono_defaults.object_class->byval_arg;
3842 unbox_call = emit_llvmonly_calli (cfg, unbox_sig, &obj, addr);
3844 unbox_call = mini_emit_calli (cfg, unbox_sig, &obj, addr, NULL, NULL);
3846 EMIT_NEW_VARLOADA_VREG (cfg, addr, unbox_call->dreg, &klass->byval_arg);
3847 addr->dreg = addr_reg;
3850 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
3853 MONO_START_BB (cfg, end_bb);
3856 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, addr_reg, 0);
3862 * Returns NULL and set the cfg exception on error.
3865 handle_alloc (MonoCompile *cfg, MonoClass *klass, gboolean for_box, int context_used)
3867 MonoInst *iargs [2];
3872 MonoRgctxInfoType rgctx_info;
3873 MonoInst *iargs [2];
3874 gboolean known_instance_size = !mini_is_gsharedvt_klass (klass);
3876 MonoMethod *managed_alloc = mono_gc_get_managed_allocator (klass, for_box, known_instance_size);
3878 if (cfg->opt & MONO_OPT_SHARED)
3879 rgctx_info = MONO_RGCTX_INFO_KLASS;
3881 rgctx_info = MONO_RGCTX_INFO_VTABLE;
3882 data = mini_emit_get_rgctx_klass (cfg, context_used, klass, rgctx_info);
3884 if (cfg->opt & MONO_OPT_SHARED) {
3885 EMIT_NEW_DOMAINCONST (cfg, iargs [0]);
3887 alloc_ftn = ves_icall_object_new;
3890 alloc_ftn = ves_icall_object_new_specific;
3893 if (managed_alloc && !(cfg->opt & MONO_OPT_SHARED)) {
3894 if (known_instance_size) {
3895 int size = mono_class_instance_size (klass);
3896 if (size < sizeof (MonoObject))
3897 g_error ("Invalid size %d for class %s", size, mono_type_get_full_name (klass));
3899 EMIT_NEW_ICONST (cfg, iargs [1], size);
3901 return mono_emit_method_call (cfg, managed_alloc, iargs, NULL);
3904 return mono_emit_jit_icall (cfg, alloc_ftn, iargs);
3907 if (cfg->opt & MONO_OPT_SHARED) {
3908 EMIT_NEW_DOMAINCONST (cfg, iargs [0]);
3909 EMIT_NEW_CLASSCONST (cfg, iargs [1], klass);
3911 alloc_ftn = ves_icall_object_new;
3912 } else if (cfg->compile_aot && cfg->cbb->out_of_line && klass->type_token && klass->image == mono_defaults.corlib && !mono_class_is_ginst (klass)) {
3913 /* This happens often in argument checking code, eg. throw new FooException... */
3914 /* Avoid relocations and save some space by calling a helper function specialized to mscorlib */
3915 EMIT_NEW_ICONST (cfg, iargs [0], mono_metadata_token_index (klass->type_token));
3916 return mono_emit_jit_icall (cfg, mono_helper_newobj_mscorlib, iargs);
3918 MonoVTable *vtable = mono_class_vtable (cfg->domain, klass);
3919 MonoMethod *managed_alloc = NULL;
3923 mono_cfg_set_exception (cfg, MONO_EXCEPTION_TYPE_LOAD);
3924 cfg->exception_ptr = klass;
3928 managed_alloc = mono_gc_get_managed_allocator (klass, for_box, TRUE);
3930 if (managed_alloc) {
3931 int size = mono_class_instance_size (klass);
3932 if (size < sizeof (MonoObject))
3933 g_error ("Invalid size %d for class %s", size, mono_type_get_full_name (klass));
3935 EMIT_NEW_VTABLECONST (cfg, iargs [0], vtable);
3936 EMIT_NEW_ICONST (cfg, iargs [1], size);
3937 return mono_emit_method_call (cfg, managed_alloc, iargs, NULL);
3939 alloc_ftn = mono_class_get_allocation_ftn (vtable, for_box, &pass_lw);
3941 guint32 lw = vtable->klass->instance_size;
3942 lw = ((lw + (sizeof (gpointer) - 1)) & ~(sizeof (gpointer) - 1)) / sizeof (gpointer);
3943 EMIT_NEW_ICONST (cfg, iargs [0], lw);
3944 EMIT_NEW_VTABLECONST (cfg, iargs [1], vtable);
3947 EMIT_NEW_VTABLECONST (cfg, iargs [0], vtable);
3951 return mono_emit_jit_icall (cfg, alloc_ftn, iargs);
3955 * Returns NULL and set the cfg exception on error.
3958 handle_box (MonoCompile *cfg, MonoInst *val, MonoClass *klass, int context_used)
3960 MonoInst *alloc, *ins;
3962 if (mono_class_is_nullable (klass)) {
3963 MonoMethod* method = mono_class_get_method_from_name (klass, "Box", 1);
3966 if (cfg->llvm_only && cfg->gsharedvt) {
3967 MonoInst *addr = emit_get_rgctx_method (cfg, context_used, method,
3968 MONO_RGCTX_INFO_GENERIC_METHOD_CODE);
3969 return emit_llvmonly_calli (cfg, mono_method_signature (method), &val, addr);
3971 /* FIXME: What if the class is shared? We might not
3972 have to get the method address from the RGCTX. */
3973 MonoInst *addr = emit_get_rgctx_method (cfg, context_used, method,
3974 MONO_RGCTX_INFO_GENERIC_METHOD_CODE);
3975 MonoInst *rgctx = emit_get_rgctx (cfg, cfg->method, context_used);
3977 return mini_emit_calli (cfg, mono_method_signature (method), &val, addr, NULL, rgctx);
3980 gboolean pass_vtable, pass_mrgctx;
3981 MonoInst *rgctx_arg = NULL;
3983 check_method_sharing (cfg, method, &pass_vtable, &pass_mrgctx);
3984 g_assert (!pass_mrgctx);
3987 MonoVTable *vtable = mono_class_vtable (cfg->domain, method->klass);
3990 EMIT_NEW_VTABLECONST (cfg, rgctx_arg, vtable);
3993 return mono_emit_method_call_full (cfg, method, NULL, FALSE, &val, NULL, NULL, rgctx_arg);
3997 if (mini_is_gsharedvt_klass (klass)) {
3998 MonoBasicBlock *is_ref_bb, *is_nullable_bb, *end_bb;
3999 MonoInst *res, *is_ref, *src_var, *addr;
4002 dreg = alloc_ireg (cfg);
4004 NEW_BBLOCK (cfg, is_ref_bb);
4005 NEW_BBLOCK (cfg, is_nullable_bb);
4006 NEW_BBLOCK (cfg, end_bb);
4007 is_ref = mini_emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_CLASS_BOX_TYPE);
4008 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, is_ref->dreg, MONO_GSHAREDVT_BOX_TYPE_REF);
4009 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, is_ref_bb);
4011 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, is_ref->dreg, MONO_GSHAREDVT_BOX_TYPE_NULLABLE);
4012 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, is_nullable_bb);
4015 alloc = handle_alloc (cfg, klass, TRUE, context_used);
4018 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, alloc->dreg, sizeof (MonoObject), val->dreg);
4019 ins->opcode = OP_STOREV_MEMBASE;
4021 EMIT_NEW_UNALU (cfg, res, OP_MOVE, dreg, alloc->dreg);
4022 res->type = STACK_OBJ;
4024 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4027 MONO_START_BB (cfg, is_ref_bb);
4029 /* val is a vtype, so has to load the value manually */
4030 src_var = get_vreg_to_inst (cfg, val->dreg);
4032 src_var = mono_compile_create_var_for_vreg (cfg, &klass->byval_arg, OP_LOCAL, val->dreg);
4033 EMIT_NEW_VARLOADA (cfg, addr, src_var, src_var->inst_vtype);
4034 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, dreg, addr->dreg, 0);
4035 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4038 MONO_START_BB (cfg, is_nullable_bb);
4041 MonoInst *addr = mini_emit_get_gsharedvt_info_klass (cfg, klass,
4042 MONO_RGCTX_INFO_NULLABLE_CLASS_BOX);
4044 MonoMethodSignature *box_sig;
4047 * klass is Nullable<T>, need to call Nullable<T>.Box () using a gsharedvt signature, but we cannot
4048 * construct that method at JIT time, so have to do things by hand.
4050 box_sig = (MonoMethodSignature *)mono_mempool_alloc0 (cfg->mempool, MONO_SIZEOF_METHOD_SIGNATURE + (1 * sizeof (MonoType *)));
4051 box_sig->ret = &mono_defaults.object_class->byval_arg;
4052 box_sig->param_count = 1;
4053 box_sig->params [0] = &klass->byval_arg;
4056 box_call = emit_llvmonly_calli (cfg, box_sig, &val, addr);
4058 box_call = mini_emit_calli (cfg, box_sig, &val, addr, NULL, NULL);
4059 EMIT_NEW_UNALU (cfg, res, OP_MOVE, dreg, box_call->dreg);
4060 res->type = STACK_OBJ;
4064 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
4066 MONO_START_BB (cfg, end_bb);
4070 alloc = handle_alloc (cfg, klass, TRUE, context_used);
4074 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, alloc->dreg, sizeof (MonoObject), val->dreg);
4079 static GHashTable* direct_icall_type_hash;
4082 icall_is_direct_callable (MonoCompile *cfg, MonoMethod *cmethod)
4084 /* LLVM on amd64 can't handle calls to non-32 bit addresses */
4085 if (!direct_icalls_enabled (cfg))
4089 * An icall is directly callable if it doesn't directly or indirectly call mono_raise_exception ().
4090 * Whitelist a few icalls for now.
4092 if (!direct_icall_type_hash) {
4093 GHashTable *h = g_hash_table_new (g_str_hash, g_str_equal);
4095 g_hash_table_insert (h, (char*)"Decimal", GUINT_TO_POINTER (1));
4096 g_hash_table_insert (h, (char*)"Number", GUINT_TO_POINTER (1));
4097 g_hash_table_insert (h, (char*)"Buffer", GUINT_TO_POINTER (1));
4098 g_hash_table_insert (h, (char*)"Monitor", GUINT_TO_POINTER (1));
4099 mono_memory_barrier ();
4100 direct_icall_type_hash = h;
4103 if (cmethod->klass == mono_defaults.math_class)
4105 /* No locking needed */
4106 if (cmethod->klass->image == mono_defaults.corlib && g_hash_table_lookup (direct_icall_type_hash, cmethod->klass->name))
4112 method_needs_stack_walk (MonoCompile *cfg, MonoMethod *cmethod)
4114 if (cmethod->klass == mono_defaults.systemtype_class) {
4115 if (!strcmp (cmethod->name, "GetType"))
4121 static G_GNUC_UNUSED MonoInst*
4122 handle_enum_has_flag (MonoCompile *cfg, MonoClass *klass, MonoInst *enum_this, MonoInst *enum_flag)
4124 MonoType *enum_type = mono_type_get_underlying_type (&klass->byval_arg);
4125 guint32 load_opc = mono_type_to_load_membase (cfg, enum_type);
4128 switch (enum_type->type) {
4131 #if SIZEOF_REGISTER == 8
4143 MonoInst *load, *and_, *cmp, *ceq;
4144 int enum_reg = is_i4 ? alloc_ireg (cfg) : alloc_lreg (cfg);
4145 int and_reg = is_i4 ? alloc_ireg (cfg) : alloc_lreg (cfg);
4146 int dest_reg = alloc_ireg (cfg);
4148 EMIT_NEW_LOAD_MEMBASE (cfg, load, load_opc, enum_reg, enum_this->dreg, 0);
4149 EMIT_NEW_BIALU (cfg, and_, is_i4 ? OP_IAND : OP_LAND, and_reg, enum_reg, enum_flag->dreg);
4150 EMIT_NEW_BIALU (cfg, cmp, is_i4 ? OP_ICOMPARE : OP_LCOMPARE, -1, and_reg, enum_flag->dreg);
4151 EMIT_NEW_UNALU (cfg, ceq, is_i4 ? OP_ICEQ : OP_LCEQ, dest_reg, -1);
4153 ceq->type = STACK_I4;
4156 load = mono_decompose_opcode (cfg, load);
4157 and_ = mono_decompose_opcode (cfg, and_);
4158 cmp = mono_decompose_opcode (cfg, cmp);
4159 ceq = mono_decompose_opcode (cfg, ceq);
4167 * Returns NULL and set the cfg exception on error.
4169 static G_GNUC_UNUSED MonoInst*
4170 handle_delegate_ctor (MonoCompile *cfg, MonoClass *klass, MonoInst *target, MonoMethod *method, int context_used, gboolean virtual_)
4174 gpointer trampoline;
4175 MonoInst *obj, *method_ins, *tramp_ins;
4179 if (virtual_ && !cfg->llvm_only) {
4180 MonoMethod *invoke = mono_get_delegate_invoke (klass);
4183 if (!mono_get_delegate_virtual_invoke_impl (mono_method_signature (invoke), context_used ? NULL : method))
4187 obj = handle_alloc (cfg, klass, FALSE, mono_class_check_context_used (klass));
4191 /* Inline the contents of mono_delegate_ctor */
4193 /* Set target field */
4194 /* Optimize away setting of NULL target */
4195 if (!MONO_INS_IS_PCONST_NULL (target)) {
4196 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, target), target->dreg);
4197 if (cfg->gen_write_barriers) {
4198 dreg = alloc_preg (cfg);
4199 EMIT_NEW_BIALU_IMM (cfg, ptr, OP_PADD_IMM, dreg, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, target));
4200 mini_emit_write_barrier (cfg, ptr, target);
4204 /* Set method field */
4205 method_ins = emit_get_rgctx_method (cfg, context_used, method, MONO_RGCTX_INFO_METHOD);
4206 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, method), method_ins->dreg);
4209 * To avoid looking up the compiled code belonging to the target method
4210 * in mono_delegate_trampoline (), we allocate a per-domain memory slot to
4211 * store it, and we fill it after the method has been compiled.
4213 if (!method->dynamic && !(cfg->opt & MONO_OPT_SHARED)) {
4214 MonoInst *code_slot_ins;
4217 code_slot_ins = emit_get_rgctx_method (cfg, context_used, method, MONO_RGCTX_INFO_METHOD_DELEGATE_CODE);
4219 domain = mono_domain_get ();
4220 mono_domain_lock (domain);
4221 if (!domain_jit_info (domain)->method_code_hash)
4222 domain_jit_info (domain)->method_code_hash = g_hash_table_new (NULL, NULL);
4223 code_slot = (guint8 **)g_hash_table_lookup (domain_jit_info (domain)->method_code_hash, method);
4225 code_slot = (guint8 **)mono_domain_alloc0 (domain, sizeof (gpointer));
4226 g_hash_table_insert (domain_jit_info (domain)->method_code_hash, method, code_slot);
4228 mono_domain_unlock (domain);
4230 code_slot_ins = mini_emit_runtime_constant (cfg, MONO_PATCH_INFO_METHOD_CODE_SLOT, method);
4232 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, method_code), code_slot_ins->dreg);
4235 if (cfg->llvm_only) {
4236 MonoInst *args [16];
4241 args [2] = emit_get_rgctx_method (cfg, context_used, method, MONO_RGCTX_INFO_METHOD);
4242 mono_emit_jit_icall (cfg, mono_llvmonly_init_delegate_virtual, args);
4245 mono_emit_jit_icall (cfg, mono_llvmonly_init_delegate, args);
4251 if (cfg->compile_aot) {
4252 MonoDelegateClassMethodPair *del_tramp;
4254 del_tramp = (MonoDelegateClassMethodPair *)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoDelegateClassMethodPair));
4255 del_tramp->klass = klass;
4256 del_tramp->method = context_used ? NULL : method;
4257 del_tramp->is_virtual = virtual_;
4258 EMIT_NEW_AOTCONST (cfg, tramp_ins, MONO_PATCH_INFO_DELEGATE_TRAMPOLINE, del_tramp);
4261 trampoline = mono_create_delegate_virtual_trampoline (cfg->domain, klass, context_used ? NULL : method);
4263 trampoline = mono_create_delegate_trampoline_info (cfg->domain, klass, context_used ? NULL : method);
4264 EMIT_NEW_PCONST (cfg, tramp_ins, trampoline);
4267 /* Set invoke_impl field */
4269 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, invoke_impl), tramp_ins->dreg);
4271 dreg = alloc_preg (cfg);
4272 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, dreg, tramp_ins->dreg, MONO_STRUCT_OFFSET (MonoDelegateTrampInfo, invoke_impl));
4273 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, invoke_impl), dreg);
4275 dreg = alloc_preg (cfg);
4276 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, dreg, tramp_ins->dreg, MONO_STRUCT_OFFSET (MonoDelegateTrampInfo, method_ptr));
4277 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, method_ptr), dreg);
4280 dreg = alloc_preg (cfg);
4281 MONO_EMIT_NEW_ICONST (cfg, dreg, virtual_ ? 1 : 0);
4282 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI1_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, method_is_virtual), dreg);
4284 /* All the checks which are in mono_delegate_ctor () are done by the delegate trampoline */
4290 handle_array_new (MonoCompile *cfg, int rank, MonoInst **sp, unsigned char *ip)
4292 MonoJitICallInfo *info;
4294 /* Need to register the icall so it gets an icall wrapper */
4295 info = mono_get_array_new_va_icall (rank);
4297 cfg->flags |= MONO_CFG_HAS_VARARGS;
4299 /* mono_array_new_va () needs a vararg calling convention */
4300 cfg->exception_message = g_strdup ("array-new");
4301 cfg->disable_llvm = TRUE;
4303 /* FIXME: This uses info->sig, but it should use the signature of the wrapper */
4304 return mono_emit_native_call (cfg, mono_icall_get_wrapper (info), info->sig, sp);
4308 * handle_constrained_gsharedvt_call:
4310 * Handle constrained calls where the receiver is a gsharedvt type.
4311 * Return the instruction representing the call. Set the cfg exception on failure.
4314 handle_constrained_gsharedvt_call (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **sp, MonoClass *constrained_class,
4315 gboolean *ref_emit_widen)
4317 MonoInst *ins = NULL;
4318 gboolean emit_widen = *ref_emit_widen;
4321 * Constrained calls need to behave differently at runtime dependending on whenever the receiver is instantiated as ref type or as a vtype.
4322 * This is hard to do with the current call code, since we would have to emit a branch and two different calls. So instead, we
4323 * pack the arguments into an array, and do the rest of the work in in an icall.
4325 if (((cmethod->klass == mono_defaults.object_class) || mono_class_is_interface (cmethod->klass) || (!cmethod->klass->valuetype && cmethod->klass->image != mono_defaults.corlib)) &&
4326 (MONO_TYPE_IS_VOID (fsig->ret) || MONO_TYPE_IS_PRIMITIVE (fsig->ret) || MONO_TYPE_IS_REFERENCE (fsig->ret) || MONO_TYPE_ISSTRUCT (fsig->ret) || mono_class_is_enum (mono_class_from_mono_type (fsig->ret)) || mini_is_gsharedvt_type (fsig->ret)) &&
4327 (fsig->param_count == 0 || (!fsig->hasthis && fsig->param_count == 1) || (fsig->param_count == 1 && (MONO_TYPE_IS_REFERENCE (fsig->params [0]) || fsig->params [0]->byref || mini_is_gsharedvt_type (fsig->params [0]))))) {
4328 MonoInst *args [16];
4331 * This case handles calls to
4332 * - object:ToString()/Equals()/GetHashCode(),
4333 * - System.IComparable<T>:CompareTo()
4334 * - System.IEquatable<T>:Equals ()
4335 * plus some simple interface calls enough to support AsyncTaskMethodBuilder.
4339 if (mono_method_check_context_used (cmethod))
4340 args [1] = emit_get_rgctx_method (cfg, mono_method_check_context_used (cmethod), cmethod, MONO_RGCTX_INFO_METHOD);
4342 EMIT_NEW_METHODCONST (cfg, args [1], cmethod);
4343 args [2] = mini_emit_get_rgctx_klass (cfg, mono_class_check_context_used (constrained_class), constrained_class, MONO_RGCTX_INFO_KLASS);
4345 /* !fsig->hasthis is for the wrapper for the Object.GetType () icall */
4346 if (fsig->hasthis && fsig->param_count) {
4347 /* Pass the arguments using a localloc-ed array using the format expected by runtime_invoke () */
4348 MONO_INST_NEW (cfg, ins, OP_LOCALLOC_IMM);
4349 ins->dreg = alloc_preg (cfg);
4350 ins->inst_imm = fsig->param_count * sizeof (mgreg_t);
4351 MONO_ADD_INS (cfg->cbb, ins);
4354 if (mini_is_gsharedvt_type (fsig->params [0])) {
4355 int addr_reg, deref_arg_reg;
4357 ins = mini_emit_get_gsharedvt_info_klass (cfg, mono_class_from_mono_type (fsig->params [0]), MONO_RGCTX_INFO_CLASS_BOX_TYPE);
4358 deref_arg_reg = alloc_preg (cfg);
4359 /* deref_arg = BOX_TYPE != MONO_GSHAREDVT_BOX_TYPE_VTYPE */
4360 EMIT_NEW_BIALU_IMM (cfg, args [3], OP_ISUB_IMM, deref_arg_reg, ins->dreg, 1);
4362 EMIT_NEW_VARLOADA_VREG (cfg, ins, sp [1]->dreg, fsig->params [0]);
4363 addr_reg = ins->dreg;
4364 EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, args [4]->dreg, 0, addr_reg);
4366 EMIT_NEW_ICONST (cfg, args [3], 0);
4367 EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, args [4]->dreg, 0, sp [1]->dreg);
4370 EMIT_NEW_ICONST (cfg, args [3], 0);
4371 EMIT_NEW_ICONST (cfg, args [4], 0);
4373 ins = mono_emit_jit_icall (cfg, mono_gsharedvt_constrained_call, args);
4376 if (mini_is_gsharedvt_type (fsig->ret)) {
4377 ins = handle_unbox_gsharedvt (cfg, mono_class_from_mono_type (fsig->ret), ins);
4378 } else if (MONO_TYPE_IS_PRIMITIVE (fsig->ret) || MONO_TYPE_ISSTRUCT (fsig->ret) || mono_class_is_enum (mono_class_from_mono_type (fsig->ret))) {
4382 NEW_BIALU_IMM (cfg, add, OP_ADD_IMM, alloc_dreg (cfg, STACK_MP), ins->dreg, sizeof (MonoObject));
4383 MONO_ADD_INS (cfg->cbb, add);
4385 NEW_LOAD_MEMBASE_TYPE (cfg, ins, fsig->ret, add->dreg, 0);
4386 MONO_ADD_INS (cfg->cbb, ins);
4387 /* ins represents the call result */
4390 GSHAREDVT_FAILURE (CEE_CALLVIRT);
4393 *ref_emit_widen = emit_widen;
4402 mono_emit_load_got_addr (MonoCompile *cfg)
4404 MonoInst *getaddr, *dummy_use;
4406 if (!cfg->got_var || cfg->got_var_allocated)
4409 MONO_INST_NEW (cfg, getaddr, OP_LOAD_GOTADDR);
4410 getaddr->cil_code = cfg->header->code;
4411 getaddr->dreg = cfg->got_var->dreg;
4413 /* Add it to the start of the first bblock */
4414 if (cfg->bb_entry->code) {
4415 getaddr->next = cfg->bb_entry->code;
4416 cfg->bb_entry->code = getaddr;
4419 MONO_ADD_INS (cfg->bb_entry, getaddr);
4421 cfg->got_var_allocated = TRUE;
4424 * Add a dummy use to keep the got_var alive, since real uses might
4425 * only be generated by the back ends.
4426 * Add it to end_bblock, so the variable's lifetime covers the whole
4428 * It would be better to make the usage of the got var explicit in all
4429 * cases when the backend needs it (i.e. calls, throw etc.), so this
4430 * wouldn't be needed.
4432 NEW_DUMMY_USE (cfg, dummy_use, cfg->got_var);
4433 MONO_ADD_INS (cfg->bb_exit, dummy_use);
4436 static int inline_limit;
4437 static gboolean inline_limit_inited;
4440 mono_method_check_inlining (MonoCompile *cfg, MonoMethod *method)
4442 MonoMethodHeaderSummary header;
4444 #ifdef MONO_ARCH_SOFT_FLOAT_FALLBACK
4445 MonoMethodSignature *sig = mono_method_signature (method);
4449 if (cfg->disable_inline)
4454 if (cfg->inline_depth > 10)
4457 if (!mono_method_get_header_summary (method, &header))
4460 /*runtime, icall and pinvoke are checked by summary call*/
4461 if ((method->iflags & METHOD_IMPL_ATTRIBUTE_NOINLINING) ||
4462 (method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED) ||
4463 (mono_class_is_marshalbyref (method->klass)) ||
4467 /* also consider num_locals? */
4468 /* Do the size check early to avoid creating vtables */
4469 if (!inline_limit_inited) {
4471 if ((inlinelimit = g_getenv ("MONO_INLINELIMIT"))) {
4472 inline_limit = atoi (inlinelimit);
4473 g_free (inlinelimit);
4475 inline_limit = INLINE_LENGTH_LIMIT;
4476 inline_limit_inited = TRUE;
4478 if (header.code_size >= inline_limit && !(method->iflags & METHOD_IMPL_ATTRIBUTE_AGGRESSIVE_INLINING))
4482 * if we can initialize the class of the method right away, we do,
4483 * otherwise we don't allow inlining if the class needs initialization,
4484 * since it would mean inserting a call to mono_runtime_class_init()
4485 * inside the inlined code
4487 if (cfg->gshared && method->klass->has_cctor && mini_class_check_context_used (cfg, method->klass))
4490 if (!(cfg->opt & MONO_OPT_SHARED)) {
4491 /* The AggressiveInlining hint is a good excuse to force that cctor to run. */
4492 if (method->iflags & METHOD_IMPL_ATTRIBUTE_AGGRESSIVE_INLINING) {
4493 if (method->klass->has_cctor) {
4494 vtable = mono_class_vtable (cfg->domain, method->klass);
4497 if (!cfg->compile_aot) {
4499 if (!mono_runtime_class_init_full (vtable, &error)) {
4500 mono_error_cleanup (&error);
4505 } else if (mono_class_is_before_field_init (method->klass)) {
4506 if (cfg->run_cctors && method->klass->has_cctor) {
4507 /*FIXME it would easier and lazier to just use mono_class_try_get_vtable */
4508 if (!method->klass->runtime_info)
4509 /* No vtable created yet */
4511 vtable = mono_class_vtable (cfg->domain, method->klass);
4514 /* This makes so that inline cannot trigger */
4515 /* .cctors: too many apps depend on them */
4516 /* running with a specific order... */
4517 if (! vtable->initialized)
4520 if (!mono_runtime_class_init_full (vtable, &error)) {
4521 mono_error_cleanup (&error);
4525 } else if (mono_class_needs_cctor_run (method->klass, NULL)) {
4526 if (!method->klass->runtime_info)
4527 /* No vtable created yet */
4529 vtable = mono_class_vtable (cfg->domain, method->klass);
4532 if (!vtable->initialized)
4537 * If we're compiling for shared code
4538 * the cctor will need to be run at aot method load time, for example,
4539 * or at the end of the compilation of the inlining method.
4541 if (mono_class_needs_cctor_run (method->klass, NULL) && !mono_class_is_before_field_init (method->klass))
4545 #ifdef MONO_ARCH_SOFT_FLOAT_FALLBACK
4546 if (mono_arch_is_soft_float ()) {
4548 if (sig->ret && sig->ret->type == MONO_TYPE_R4)
4550 for (i = 0; i < sig->param_count; ++i)
4551 if (!sig->params [i]->byref && sig->params [i]->type == MONO_TYPE_R4)
4556 if (g_list_find (cfg->dont_inline, method))
4563 mini_field_access_needs_cctor_run (MonoCompile *cfg, MonoMethod *method, MonoClass *klass, MonoVTable *vtable)
4565 if (!cfg->compile_aot) {
4567 if (vtable->initialized)
4571 if (mono_class_is_before_field_init (klass)) {
4572 if (cfg->method == method)
4576 if (!mono_class_needs_cctor_run (klass, method))
4579 if (! (method->flags & METHOD_ATTRIBUTE_STATIC) && (klass == method->klass))
4580 /* The initialization is already done before the method is called */
4587 mini_emit_ldelema_1_ins (MonoCompile *cfg, MonoClass *klass, MonoInst *arr, MonoInst *index, gboolean bcheck)
4591 int mult_reg, add_reg, array_reg, index_reg, index2_reg;
4594 if (mini_is_gsharedvt_variable_klass (klass)) {
4597 mono_class_init (klass);
4598 size = mono_class_array_element_size (klass);
4601 mult_reg = alloc_preg (cfg);
4602 array_reg = arr->dreg;
4603 index_reg = index->dreg;
4605 #if SIZEOF_REGISTER == 8
4606 /* The array reg is 64 bits but the index reg is only 32 */
4607 if (COMPILE_LLVM (cfg)) {
4609 * abcrem can't handle the OP_SEXT_I4, so add this after abcrem,
4610 * during OP_BOUNDS_CHECK decomposition, and in the implementation
4611 * of OP_X86_LEA for llvm.
4613 index2_reg = index_reg;
4615 index2_reg = alloc_preg (cfg);
4616 MONO_EMIT_NEW_UNALU (cfg, OP_SEXT_I4, index2_reg, index_reg);
4619 if (index->type == STACK_I8) {
4620 index2_reg = alloc_preg (cfg);
4621 MONO_EMIT_NEW_UNALU (cfg, OP_LCONV_TO_I4, index2_reg, index_reg);
4623 index2_reg = index_reg;
4628 MONO_EMIT_BOUNDS_CHECK (cfg, array_reg, MonoArray, max_length, index2_reg);
4630 #if defined(TARGET_X86) || defined(TARGET_AMD64)
4631 if (size == 1 || size == 2 || size == 4 || size == 8) {
4632 static const int fast_log2 [] = { 1, 0, 1, -1, 2, -1, -1, -1, 3 };
4634 EMIT_NEW_X86_LEA (cfg, ins, array_reg, index2_reg, fast_log2 [size], MONO_STRUCT_OFFSET (MonoArray, vector));
4635 ins->klass = mono_class_get_element_class (klass);
4636 ins->type = STACK_MP;
4642 add_reg = alloc_ireg_mp (cfg);
4645 MonoInst *rgctx_ins;
4648 g_assert (cfg->gshared);
4649 context_used = mini_class_check_context_used (cfg, klass);
4650 g_assert (context_used);
4651 rgctx_ins = mini_emit_get_gsharedvt_info_klass (cfg, klass, MONO_RGCTX_INFO_ARRAY_ELEMENT_SIZE);
4652 MONO_EMIT_NEW_BIALU (cfg, OP_IMUL, mult_reg, index2_reg, rgctx_ins->dreg);
4654 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_MUL_IMM, mult_reg, index2_reg, size);
4656 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, add_reg, array_reg, mult_reg);
4657 NEW_BIALU_IMM (cfg, ins, OP_PADD_IMM, add_reg, add_reg, MONO_STRUCT_OFFSET (MonoArray, vector));
4658 ins->klass = mono_class_get_element_class (klass);
4659 ins->type = STACK_MP;
4660 MONO_ADD_INS (cfg->cbb, ins);
4666 mini_emit_ldelema_2_ins (MonoCompile *cfg, MonoClass *klass, MonoInst *arr, MonoInst *index_ins1, MonoInst *index_ins2)
4668 int bounds_reg = alloc_preg (cfg);
4669 int add_reg = alloc_ireg_mp (cfg);
4670 int mult_reg = alloc_preg (cfg);
4671 int mult2_reg = alloc_preg (cfg);
4672 int low1_reg = alloc_preg (cfg);
4673 int low2_reg = alloc_preg (cfg);
4674 int high1_reg = alloc_preg (cfg);
4675 int high2_reg = alloc_preg (cfg);
4676 int realidx1_reg = alloc_preg (cfg);
4677 int realidx2_reg = alloc_preg (cfg);
4678 int sum_reg = alloc_preg (cfg);
4679 int index1, index2, tmpreg;
4683 mono_class_init (klass);
4684 size = mono_class_array_element_size (klass);
4686 index1 = index_ins1->dreg;
4687 index2 = index_ins2->dreg;
4689 #if SIZEOF_REGISTER == 8
4690 /* The array reg is 64 bits but the index reg is only 32 */
4691 if (COMPILE_LLVM (cfg)) {
4694 tmpreg = alloc_preg (cfg);
4695 MONO_EMIT_NEW_UNALU (cfg, OP_SEXT_I4, tmpreg, index1);
4697 tmpreg = alloc_preg (cfg);
4698 MONO_EMIT_NEW_UNALU (cfg, OP_SEXT_I4, tmpreg, index2);
4702 // FIXME: Do we need to do something here for i8 indexes, like in ldelema_1_ins ?
4706 /* range checking */
4707 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, bounds_reg,
4708 arr->dreg, MONO_STRUCT_OFFSET (MonoArray, bounds));
4710 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI4_MEMBASE, low1_reg,
4711 bounds_reg, MONO_STRUCT_OFFSET (MonoArrayBounds, lower_bound));
4712 MONO_EMIT_NEW_BIALU (cfg, OP_PSUB, realidx1_reg, index1, low1_reg);
4713 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI4_MEMBASE, high1_reg,
4714 bounds_reg, MONO_STRUCT_OFFSET (MonoArrayBounds, length));
4715 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, high1_reg, realidx1_reg);
4716 MONO_EMIT_NEW_COND_EXC (cfg, LE_UN, "IndexOutOfRangeException");
4718 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI4_MEMBASE, low2_reg,
4719 bounds_reg, sizeof (MonoArrayBounds) + MONO_STRUCT_OFFSET (MonoArrayBounds, lower_bound));
4720 MONO_EMIT_NEW_BIALU (cfg, OP_PSUB, realidx2_reg, index2, low2_reg);
4721 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI4_MEMBASE, high2_reg,
4722 bounds_reg, sizeof (MonoArrayBounds) + MONO_STRUCT_OFFSET (MonoArrayBounds, length));
4723 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, high2_reg, realidx2_reg);
4724 MONO_EMIT_NEW_COND_EXC (cfg, LE_UN, "IndexOutOfRangeException");
4726 MONO_EMIT_NEW_BIALU (cfg, OP_PMUL, mult_reg, high2_reg, realidx1_reg);
4727 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, sum_reg, mult_reg, realidx2_reg);
4728 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_PMUL_IMM, mult2_reg, sum_reg, size);
4729 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, add_reg, mult2_reg, arr->dreg);
4730 NEW_BIALU_IMM (cfg, ins, OP_PADD_IMM, add_reg, add_reg, MONO_STRUCT_OFFSET (MonoArray, vector));
4732 ins->type = STACK_MP;
4734 MONO_ADD_INS (cfg->cbb, ins);
4740 mini_emit_ldelema_ins (MonoCompile *cfg, MonoMethod *cmethod, MonoInst **sp, unsigned char *ip, gboolean is_set)
4744 MonoMethod *addr_method;
4746 MonoClass *eclass = cmethod->klass->element_class;
4748 rank = mono_method_signature (cmethod)->param_count - (is_set? 1: 0);
4751 return mini_emit_ldelema_1_ins (cfg, eclass, sp [0], sp [1], TRUE);
4753 /* emit_ldelema_2 depends on OP_LMUL */
4754 if (!cfg->backend->emulate_mul_div && rank == 2 && (cfg->opt & MONO_OPT_INTRINS) && !mini_is_gsharedvt_variable_klass (eclass)) {
4755 return mini_emit_ldelema_2_ins (cfg, eclass, sp [0], sp [1], sp [2]);
4758 if (mini_is_gsharedvt_variable_klass (eclass))
4761 element_size = mono_class_array_element_size (eclass);
4762 addr_method = mono_marshal_get_array_address (rank, element_size);
4763 addr = mono_emit_method_call (cfg, addr_method, sp, NULL);
4768 static MonoBreakPolicy
4769 always_insert_breakpoint (MonoMethod *method)
4771 return MONO_BREAK_POLICY_ALWAYS;
4774 static MonoBreakPolicyFunc break_policy_func = always_insert_breakpoint;
4777 * mono_set_break_policy:
4778 * \param policy_callback the new callback function
4780 * Allow embedders to decide wherther to actually obey breakpoint instructions
4781 * (both break IL instructions and \c Debugger.Break method calls), for example
4782 * to not allow an app to be aborted by a perfectly valid IL opcode when executing
4783 * untrusted or semi-trusted code.
4785 * \p policy_callback will be called every time a break point instruction needs to
4786 * be inserted with the method argument being the method that calls \c Debugger.Break
4787 * or has the IL \c break instruction. The callback should return \c MONO_BREAK_POLICY_NEVER
4788 * if it wants the breakpoint to not be effective in the given method.
4789 * \c MONO_BREAK_POLICY_ALWAYS is the default.
4792 mono_set_break_policy (MonoBreakPolicyFunc policy_callback)
4794 if (policy_callback)
4795 break_policy_func = policy_callback;
4797 break_policy_func = always_insert_breakpoint;
4801 should_insert_brekpoint (MonoMethod *method) {
4802 switch (break_policy_func (method)) {
4803 case MONO_BREAK_POLICY_ALWAYS:
4805 case MONO_BREAK_POLICY_NEVER:
4807 case MONO_BREAK_POLICY_ON_DBG:
4808 g_warning ("mdb no longer supported");
4811 g_warning ("Incorrect value returned from break policy callback");
4816 /* optimize the simple GetGenericValueImpl/SetGenericValueImpl generic icalls */
4818 emit_array_generic_access (MonoCompile *cfg, MonoMethodSignature *fsig, MonoInst **args, int is_set)
4820 MonoInst *addr, *store, *load;
4821 MonoClass *eklass = mono_class_from_mono_type (fsig->params [2]);
4823 /* the bounds check is already done by the callers */
4824 addr = mini_emit_ldelema_1_ins (cfg, eklass, args [0], args [1], FALSE);
4826 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, load, &eklass->byval_arg, args [2]->dreg, 0);
4827 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, store, &eklass->byval_arg, addr->dreg, 0, load->dreg);
4828 if (mini_type_is_reference (&eklass->byval_arg))
4829 mini_emit_write_barrier (cfg, addr, load);
4831 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, load, &eklass->byval_arg, addr->dreg, 0);
4832 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, store, &eklass->byval_arg, args [2]->dreg, 0, load->dreg);
4839 generic_class_is_reference_type (MonoCompile *cfg, MonoClass *klass)
4841 return mini_type_is_reference (&klass->byval_arg);
4845 emit_array_store (MonoCompile *cfg, MonoClass *klass, MonoInst **sp, gboolean safety_checks)
4847 if (safety_checks && generic_class_is_reference_type (cfg, klass) &&
4848 !(MONO_INS_IS_PCONST_NULL (sp [2]))) {
4849 MonoClass *obj_array = mono_array_class_get_cached (mono_defaults.object_class, 1);
4850 MonoMethod *helper = mono_marshal_get_virtual_stelemref (obj_array);
4851 MonoInst *iargs [3];
4854 mono_class_setup_vtable (obj_array);
4855 g_assert (helper->slot);
4857 if (sp [0]->type != STACK_OBJ)
4859 if (sp [2]->type != STACK_OBJ)
4866 return mono_emit_method_call (cfg, helper, iargs, sp [0]);
4870 if (mini_is_gsharedvt_variable_klass (klass)) {
4873 // FIXME-VT: OP_ICONST optimization
4874 addr = mini_emit_ldelema_1_ins (cfg, klass, sp [0], sp [1], TRUE);
4875 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, addr->dreg, 0, sp [2]->dreg);
4876 ins->opcode = OP_STOREV_MEMBASE;
4877 } else if (sp [1]->opcode == OP_ICONST) {
4878 int array_reg = sp [0]->dreg;
4879 int index_reg = sp [1]->dreg;
4880 int offset = (mono_class_array_element_size (klass) * sp [1]->inst_c0) + MONO_STRUCT_OFFSET (MonoArray, vector);
4882 if (SIZEOF_REGISTER == 8 && COMPILE_LLVM (cfg))
4883 MONO_EMIT_NEW_UNALU (cfg, OP_ZEXT_I4, index_reg, index_reg);
4886 MONO_EMIT_BOUNDS_CHECK (cfg, array_reg, MonoArray, max_length, index_reg);
4887 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, array_reg, offset, sp [2]->dreg);
4889 MonoInst *addr = mini_emit_ldelema_1_ins (cfg, klass, sp [0], sp [1], safety_checks);
4890 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, addr->dreg, 0, sp [2]->dreg);
4891 if (generic_class_is_reference_type (cfg, klass))
4892 mini_emit_write_barrier (cfg, addr, sp [2]);
4899 emit_array_unsafe_access (MonoCompile *cfg, MonoMethodSignature *fsig, MonoInst **args, int is_set)
4904 eklass = mono_class_from_mono_type (fsig->params [2]);
4906 eklass = mono_class_from_mono_type (fsig->ret);
4909 return emit_array_store (cfg, eklass, args, FALSE);
4911 MonoInst *ins, *addr = mini_emit_ldelema_1_ins (cfg, eklass, args [0], args [1], FALSE);
4912 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &eklass->byval_arg, addr->dreg, 0);
4918 is_unsafe_mov_compatible (MonoCompile *cfg, MonoClass *param_klass, MonoClass *return_klass)
4921 int param_size, return_size;
4923 param_klass = mono_class_from_mono_type (mini_get_underlying_type (¶m_klass->byval_arg));
4924 return_klass = mono_class_from_mono_type (mini_get_underlying_type (&return_klass->byval_arg));
4926 if (cfg->verbose_level > 3)
4927 printf ("[UNSAFE-MOV-INTRISIC] %s <- %s\n", return_klass->name, param_klass->name);
4929 //Don't allow mixing reference types with value types
4930 if (param_klass->valuetype != return_klass->valuetype) {
4931 if (cfg->verbose_level > 3)
4932 printf ("[UNSAFE-MOV-INTRISIC]\tone of the args is a valuetype and the other is not\n");
4936 if (!param_klass->valuetype) {
4937 if (cfg->verbose_level > 3)
4938 printf ("[UNSAFE-MOV-INTRISIC]\targs are reference types\n");
4943 if (param_klass->has_references || return_klass->has_references)
4946 /* Avoid mixing structs and primitive types/enums, they need to be handled differently in the JIT */
4947 if ((MONO_TYPE_ISSTRUCT (¶m_klass->byval_arg) && !MONO_TYPE_ISSTRUCT (&return_klass->byval_arg)) ||
4948 (!MONO_TYPE_ISSTRUCT (¶m_klass->byval_arg) && MONO_TYPE_ISSTRUCT (&return_klass->byval_arg))) {
4949 if (cfg->verbose_level > 3)
4950 printf ("[UNSAFE-MOV-INTRISIC]\tmixing structs and scalars\n");
4954 if (param_klass->byval_arg.type == MONO_TYPE_R4 || param_klass->byval_arg.type == MONO_TYPE_R8 ||
4955 return_klass->byval_arg.type == MONO_TYPE_R4 || return_klass->byval_arg.type == MONO_TYPE_R8) {
4956 if (cfg->verbose_level > 3)
4957 printf ("[UNSAFE-MOV-INTRISIC]\tfloat or double are not supported\n");
4961 param_size = mono_class_value_size (param_klass, &align);
4962 return_size = mono_class_value_size (return_klass, &align);
4964 //We can do it if sizes match
4965 if (param_size == return_size) {
4966 if (cfg->verbose_level > 3)
4967 printf ("[UNSAFE-MOV-INTRISIC]\tsame size\n");
4971 //No simple way to handle struct if sizes don't match
4972 if (MONO_TYPE_ISSTRUCT (¶m_klass->byval_arg)) {
4973 if (cfg->verbose_level > 3)
4974 printf ("[UNSAFE-MOV-INTRISIC]\tsize mismatch and type is a struct\n");
4979 * Same reg size category.
4980 * A quick note on why we don't require widening here.
4981 * The intrinsic is "R Array.UnsafeMov<S,R> (S s)".
4983 * Since the source value comes from a function argument, the JIT will already have
4984 * the value in a VREG and performed any widening needed before (say, when loading from a field).
4986 if (param_size <= 4 && return_size <= 4) {
4987 if (cfg->verbose_level > 3)
4988 printf ("[UNSAFE-MOV-INTRISIC]\tsize mismatch but both are of the same reg class\n");
4996 emit_array_unsafe_mov (MonoCompile *cfg, MonoMethodSignature *fsig, MonoInst **args)
4998 MonoClass *param_klass = mono_class_from_mono_type (fsig->params [0]);
4999 MonoClass *return_klass = mono_class_from_mono_type (fsig->ret);
5001 if (mini_is_gsharedvt_variable_type (fsig->ret))
5004 //Valuetypes that are semantically equivalent or numbers than can be widened to
5005 if (is_unsafe_mov_compatible (cfg, param_klass, return_klass))
5008 //Arrays of valuetypes that are semantically equivalent
5009 if (param_klass->rank == 1 && return_klass->rank == 1 && is_unsafe_mov_compatible (cfg, param_klass->element_class, return_klass->element_class))
5016 mini_emit_inst_for_ctor (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
5018 #ifdef MONO_ARCH_SIMD_INTRINSICS
5019 MonoInst *ins = NULL;
5021 if (cfg->opt & MONO_OPT_SIMD) {
5022 ins = mono_emit_simd_intrinsics (cfg, cmethod, fsig, args);
5028 return mono_emit_native_types_intrinsics (cfg, cmethod, fsig, args);
5032 mini_emit_memory_barrier (MonoCompile *cfg, int kind)
5034 MonoInst *ins = NULL;
5035 MONO_INST_NEW (cfg, ins, OP_MEMORY_BARRIER);
5036 MONO_ADD_INS (cfg->cbb, ins);
5037 ins->backend.memory_barrier_kind = kind;
5043 llvm_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
5045 MonoInst *ins = NULL;
5048 /* The LLVM backend supports these intrinsics */
5049 if (cmethod->klass == mono_defaults.math_class) {
5050 if (strcmp (cmethod->name, "Sin") == 0) {
5052 } else if (strcmp (cmethod->name, "Cos") == 0) {
5054 } else if (strcmp (cmethod->name, "Sqrt") == 0) {
5056 } else if (strcmp (cmethod->name, "Abs") == 0 && fsig->params [0]->type == MONO_TYPE_R8) {
5060 if (opcode && fsig->param_count == 1) {
5061 MONO_INST_NEW (cfg, ins, opcode);
5062 ins->type = STACK_R8;
5063 ins->dreg = mono_alloc_dreg (cfg, ins->type);
5064 ins->sreg1 = args [0]->dreg;
5065 MONO_ADD_INS (cfg->cbb, ins);
5069 if (cfg->opt & MONO_OPT_CMOV) {
5070 if (strcmp (cmethod->name, "Min") == 0) {
5071 if (fsig->params [0]->type == MONO_TYPE_I4)
5073 if (fsig->params [0]->type == MONO_TYPE_U4)
5074 opcode = OP_IMIN_UN;
5075 else if (fsig->params [0]->type == MONO_TYPE_I8)
5077 else if (fsig->params [0]->type == MONO_TYPE_U8)
5078 opcode = OP_LMIN_UN;
5079 } else if (strcmp (cmethod->name, "Max") == 0) {
5080 if (fsig->params [0]->type == MONO_TYPE_I4)
5082 if (fsig->params [0]->type == MONO_TYPE_U4)
5083 opcode = OP_IMAX_UN;
5084 else if (fsig->params [0]->type == MONO_TYPE_I8)
5086 else if (fsig->params [0]->type == MONO_TYPE_U8)
5087 opcode = OP_LMAX_UN;
5091 if (opcode && fsig->param_count == 2) {
5092 MONO_INST_NEW (cfg, ins, opcode);
5093 ins->type = fsig->params [0]->type == MONO_TYPE_I4 ? STACK_I4 : STACK_I8;
5094 ins->dreg = mono_alloc_dreg (cfg, ins->type);
5095 ins->sreg1 = args [0]->dreg;
5096 ins->sreg2 = args [1]->dreg;
5097 MONO_ADD_INS (cfg->cbb, ins);
5105 mini_emit_inst_for_sharable_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
5107 if (cmethod->klass == mono_defaults.array_class) {
5108 if (strcmp (cmethod->name, "UnsafeStore") == 0)
5109 return emit_array_unsafe_access (cfg, fsig, args, TRUE);
5110 else if (strcmp (cmethod->name, "UnsafeLoad") == 0)
5111 return emit_array_unsafe_access (cfg, fsig, args, FALSE);
5112 else if (strcmp (cmethod->name, "UnsafeMov") == 0)
5113 return emit_array_unsafe_mov (cfg, fsig, args);
5120 mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
5122 MonoInst *ins = NULL;
5123 MonoClass *runtime_helpers_class = mono_class_get_runtime_helpers_class ();
5125 if (cmethod->klass == mono_defaults.string_class) {
5126 if (strcmp (cmethod->name, "get_Chars") == 0 && fsig->param_count + fsig->hasthis == 2) {
5127 int dreg = alloc_ireg (cfg);
5128 int index_reg = alloc_preg (cfg);
5129 int add_reg = alloc_preg (cfg);
5131 #if SIZEOF_REGISTER == 8
5132 if (COMPILE_LLVM (cfg)) {
5133 MONO_EMIT_NEW_UNALU (cfg, OP_ZEXT_I4, index_reg, args [1]->dreg);
5135 /* The array reg is 64 bits but the index reg is only 32 */
5136 MONO_EMIT_NEW_UNALU (cfg, OP_SEXT_I4, index_reg, args [1]->dreg);
5139 index_reg = args [1]->dreg;
5141 MONO_EMIT_BOUNDS_CHECK (cfg, args [0]->dreg, MonoString, length, index_reg);
5143 #if defined(TARGET_X86) || defined(TARGET_AMD64)
5144 EMIT_NEW_X86_LEA (cfg, ins, args [0]->dreg, index_reg, 1, MONO_STRUCT_OFFSET (MonoString, chars));
5145 add_reg = ins->dreg;
5146 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADU2_MEMBASE, dreg,
5149 int mult_reg = alloc_preg (cfg);
5150 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SHL_IMM, mult_reg, index_reg, 1);
5151 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, add_reg, mult_reg, args [0]->dreg);
5152 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADU2_MEMBASE, dreg,
5153 add_reg, MONO_STRUCT_OFFSET (MonoString, chars));
5155 type_from_op (cfg, ins, NULL, NULL);
5157 } else if (strcmp (cmethod->name, "get_Length") == 0 && fsig->param_count + fsig->hasthis == 1) {
5158 int dreg = alloc_ireg (cfg);
5159 /* Decompose later to allow more optimizations */
5160 EMIT_NEW_UNALU (cfg, ins, OP_STRLEN, dreg, args [0]->dreg);
5161 ins->type = STACK_I4;
5162 ins->flags |= MONO_INST_FAULT;
5163 cfg->cbb->has_array_access = TRUE;
5164 cfg->flags |= MONO_CFG_HAS_ARRAY_ACCESS;
5169 } else if (cmethod->klass == mono_defaults.object_class) {
5170 if (strcmp (cmethod->name, "GetType") == 0 && fsig->param_count + fsig->hasthis == 1) {
5171 int dreg = alloc_ireg_ref (cfg);
5172 int vt_reg = alloc_preg (cfg);
5173 MONO_EMIT_NEW_LOAD_MEMBASE_FAULT (cfg, vt_reg, args [0]->dreg, MONO_STRUCT_OFFSET (MonoObject, vtable));
5174 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, dreg, vt_reg, MONO_STRUCT_OFFSET (MonoVTable, type));
5175 type_from_op (cfg, ins, NULL, NULL);
5178 } else if (!cfg->backend->emulate_mul_div && strcmp (cmethod->name, "InternalGetHashCode") == 0 && fsig->param_count == 1 && !mono_gc_is_moving ()) {
5179 int dreg = alloc_ireg (cfg);
5180 int t1 = alloc_ireg (cfg);
5182 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SHL_IMM, t1, args [0]->dreg, 3);
5183 EMIT_NEW_BIALU_IMM (cfg, ins, OP_MUL_IMM, dreg, t1, 2654435761u);
5184 ins->type = STACK_I4;
5187 } else if (strcmp (cmethod->name, ".ctor") == 0 && fsig->param_count == 0) {
5188 MONO_INST_NEW (cfg, ins, OP_NOP);
5189 MONO_ADD_INS (cfg->cbb, ins);
5193 } else if (cmethod->klass == mono_defaults.array_class) {
5194 if (strcmp (cmethod->name, "GetGenericValueImpl") == 0 && fsig->param_count + fsig->hasthis == 3 && !cfg->gsharedvt)
5195 return emit_array_generic_access (cfg, fsig, args, FALSE);
5196 else if (strcmp (cmethod->name, "SetGenericValueImpl") == 0 && fsig->param_count + fsig->hasthis == 3 && !cfg->gsharedvt)
5197 return emit_array_generic_access (cfg, fsig, args, TRUE);
5199 #ifndef MONO_BIG_ARRAYS
5201 * This is an inline version of GetLength/GetLowerBound(0) used frequently in
5204 else if (((strcmp (cmethod->name, "GetLength") == 0 && fsig->param_count + fsig->hasthis == 2) ||
5205 (strcmp (cmethod->name, "GetLowerBound") == 0 && fsig->param_count + fsig->hasthis == 2)) &&
5206 args [1]->opcode == OP_ICONST && args [1]->inst_c0 == 0) {
5207 int dreg = alloc_ireg (cfg);
5208 int bounds_reg = alloc_ireg_mp (cfg);
5209 MonoBasicBlock *end_bb, *szarray_bb;
5210 gboolean get_length = strcmp (cmethod->name, "GetLength") == 0;
5212 NEW_BBLOCK (cfg, end_bb);
5213 NEW_BBLOCK (cfg, szarray_bb);
5215 EMIT_NEW_LOAD_MEMBASE_FAULT (cfg, ins, OP_LOAD_MEMBASE, bounds_reg,
5216 args [0]->dreg, MONO_STRUCT_OFFSET (MonoArray, bounds));
5217 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, bounds_reg, 0);
5218 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, szarray_bb);
5219 /* Non-szarray case */
5221 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADI4_MEMBASE, dreg,
5222 bounds_reg, MONO_STRUCT_OFFSET (MonoArrayBounds, length));
5224 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADI4_MEMBASE, dreg,
5225 bounds_reg, MONO_STRUCT_OFFSET (MonoArrayBounds, lower_bound));
5226 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
5227 MONO_START_BB (cfg, szarray_bb);
5230 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADI4_MEMBASE, dreg,
5231 args [0]->dreg, MONO_STRUCT_OFFSET (MonoArray, max_length));
5233 MONO_EMIT_NEW_ICONST (cfg, dreg, 0);
5234 MONO_START_BB (cfg, end_bb);
5236 EMIT_NEW_UNALU (cfg, ins, OP_MOVE, dreg, dreg);
5237 ins->type = STACK_I4;
5243 if (cmethod->name [0] != 'g')
5246 if (strcmp (cmethod->name, "get_Rank") == 0 && fsig->param_count + fsig->hasthis == 1) {
5247 int dreg = alloc_ireg (cfg);
5248 int vtable_reg = alloc_preg (cfg);
5249 MONO_EMIT_NEW_LOAD_MEMBASE_OP_FAULT (cfg, OP_LOAD_MEMBASE, vtable_reg,
5250 args [0]->dreg, MONO_STRUCT_OFFSET (MonoObject, vtable));
5251 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADU1_MEMBASE, dreg,
5252 vtable_reg, MONO_STRUCT_OFFSET (MonoVTable, rank));
5253 type_from_op (cfg, ins, NULL, NULL);
5256 } else if (strcmp (cmethod->name, "get_Length") == 0 && fsig->param_count + fsig->hasthis == 1) {
5257 int dreg = alloc_ireg (cfg);
5259 EMIT_NEW_LOAD_MEMBASE_FAULT (cfg, ins, OP_LOADI4_MEMBASE, dreg,
5260 args [0]->dreg, MONO_STRUCT_OFFSET (MonoArray, max_length));
5261 type_from_op (cfg, ins, NULL, NULL);
5266 } else if (cmethod->klass == runtime_helpers_class) {
5267 if (strcmp (cmethod->name, "get_OffsetToStringData") == 0 && fsig->param_count == 0) {
5268 EMIT_NEW_ICONST (cfg, ins, MONO_STRUCT_OFFSET (MonoString, chars));
5270 } else if (strcmp (cmethod->name, "IsReferenceOrContainsReferences") == 0 && fsig->param_count == 0) {
5271 MonoGenericContext *ctx = mono_method_get_context (cmethod);
5273 g_assert (ctx->method_inst);
5274 g_assert (ctx->method_inst->type_argc == 1);
5275 MonoType *t = mini_get_underlying_type (ctx->method_inst->type_argv [0]);
5276 MonoClass *klass = mono_class_from_mono_type (t);
5280 mono_class_init (klass);
5281 if (MONO_TYPE_IS_REFERENCE (t))
5282 EMIT_NEW_ICONST (cfg, ins, 1);
5283 else if (MONO_TYPE_IS_PRIMITIVE (t))
5284 EMIT_NEW_ICONST (cfg, ins, 0);
5285 else if (cfg->gshared && (t->type == MONO_TYPE_VAR || t->type == MONO_TYPE_MVAR) && !mini_type_var_is_vt (t))
5286 EMIT_NEW_ICONST (cfg, ins, 1);
5287 else if (!cfg->gshared || !mini_class_check_context_used (cfg, klass))
5288 EMIT_NEW_ICONST (cfg, ins, klass->has_references ? 1 : 0);
5290 g_assert (cfg->gshared);
5292 int context_used = mini_class_check_context_used (cfg, klass);
5294 /* This returns 1 or 2 */
5295 MonoInst *info = mini_emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_CLASS_IS_REF_OR_CONTAINS_REFS);
5296 int dreg = alloc_ireg (cfg);
5297 EMIT_NEW_BIALU_IMM (cfg, ins, OP_ISUB_IMM, dreg, info->dreg, 1);
5303 } else if (cmethod->klass == mono_defaults.monitor_class) {
5304 gboolean is_enter = FALSE;
5305 gboolean is_v4 = FALSE;
5307 if (!strcmp (cmethod->name, "Enter") && fsig->param_count == 2 && fsig->params [1]->byref) {
5311 if (!strcmp (cmethod->name, "Enter") && fsig->param_count == 1)
5316 * To make async stack traces work, icalls which can block should have a wrapper.
5317 * For Monitor.Enter, emit two calls: a fastpath which doesn't have a wrapper, and a slowpath, which does.
5319 MonoBasicBlock *end_bb;
5321 NEW_BBLOCK (cfg, end_bb);
5323 ins = mono_emit_jit_icall (cfg, is_v4 ? (gpointer)mono_monitor_enter_v4_fast : (gpointer)mono_monitor_enter_fast, args);
5324 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ICOMPARE_IMM, -1, ins->dreg, 0);
5325 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBNE_UN, end_bb);
5326 ins = mono_emit_jit_icall (cfg, is_v4 ? (gpointer)mono_monitor_enter_v4_internal : (gpointer)mono_monitor_enter_internal, args);
5327 MONO_START_BB (cfg, end_bb);
5330 } else if (cmethod->klass == mono_defaults.thread_class) {
5331 if (strcmp (cmethod->name, "SpinWait_nop") == 0 && fsig->param_count == 0) {
5332 MONO_INST_NEW (cfg, ins, OP_RELAXED_NOP);
5333 MONO_ADD_INS (cfg->cbb, ins);
5335 } else if (strcmp (cmethod->name, "MemoryBarrier") == 0 && fsig->param_count == 0) {
5336 return mini_emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_SEQ);
5337 } else if (!strcmp (cmethod->name, "VolatileRead") && fsig->param_count == 1) {
5339 gboolean is_ref = mini_type_is_reference (fsig->params [0]);
5341 if (fsig->params [0]->type == MONO_TYPE_I1)
5342 opcode = OP_LOADI1_MEMBASE;
5343 else if (fsig->params [0]->type == MONO_TYPE_U1)
5344 opcode = OP_LOADU1_MEMBASE;
5345 else if (fsig->params [0]->type == MONO_TYPE_I2)
5346 opcode = OP_LOADI2_MEMBASE;
5347 else if (fsig->params [0]->type == MONO_TYPE_U2)
5348 opcode = OP_LOADU2_MEMBASE;
5349 else if (fsig->params [0]->type == MONO_TYPE_I4)
5350 opcode = OP_LOADI4_MEMBASE;
5351 else if (fsig->params [0]->type == MONO_TYPE_U4)
5352 opcode = OP_LOADU4_MEMBASE;
5353 else if (fsig->params [0]->type == MONO_TYPE_I8 || fsig->params [0]->type == MONO_TYPE_U8)
5354 opcode = OP_LOADI8_MEMBASE;
5355 else if (fsig->params [0]->type == MONO_TYPE_R4)
5356 opcode = OP_LOADR4_MEMBASE;
5357 else if (fsig->params [0]->type == MONO_TYPE_R8)
5358 opcode = OP_LOADR8_MEMBASE;
5359 else if (is_ref || fsig->params [0]->type == MONO_TYPE_I || fsig->params [0]->type == MONO_TYPE_U)
5360 opcode = OP_LOAD_MEMBASE;
5363 MONO_INST_NEW (cfg, ins, opcode);
5364 ins->inst_basereg = args [0]->dreg;
5365 ins->inst_offset = 0;
5366 MONO_ADD_INS (cfg->cbb, ins);
5368 switch (fsig->params [0]->type) {
5375 ins->dreg = mono_alloc_ireg (cfg);
5376 ins->type = STACK_I4;
5380 ins->dreg = mono_alloc_lreg (cfg);
5381 ins->type = STACK_I8;
5385 ins->dreg = mono_alloc_ireg (cfg);
5386 #if SIZEOF_REGISTER == 8
5387 ins->type = STACK_I8;
5389 ins->type = STACK_I4;
5394 ins->dreg = mono_alloc_freg (cfg);
5395 ins->type = STACK_R8;
5398 g_assert (mini_type_is_reference (fsig->params [0]));
5399 ins->dreg = mono_alloc_ireg_ref (cfg);
5400 ins->type = STACK_OBJ;
5404 if (opcode == OP_LOADI8_MEMBASE)
5405 ins = mono_decompose_opcode (cfg, ins);
5407 mini_emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_SEQ);
5411 } else if (!strcmp (cmethod->name, "VolatileWrite") && fsig->param_count == 2) {
5413 gboolean is_ref = mini_type_is_reference (fsig->params [0]);
5415 if (fsig->params [0]->type == MONO_TYPE_I1 || fsig->params [0]->type == MONO_TYPE_U1)
5416 opcode = OP_STOREI1_MEMBASE_REG;
5417 else if (fsig->params [0]->type == MONO_TYPE_I2 || fsig->params [0]->type == MONO_TYPE_U2)
5418 opcode = OP_STOREI2_MEMBASE_REG;
5419 else if (fsig->params [0]->type == MONO_TYPE_I4 || fsig->params [0]->type == MONO_TYPE_U4)
5420 opcode = OP_STOREI4_MEMBASE_REG;
5421 else if (fsig->params [0]->type == MONO_TYPE_I8 || fsig->params [0]->type == MONO_TYPE_U8)
5422 opcode = OP_STOREI8_MEMBASE_REG;
5423 else if (fsig->params [0]->type == MONO_TYPE_R4)
5424 opcode = OP_STORER4_MEMBASE_REG;
5425 else if (fsig->params [0]->type == MONO_TYPE_R8)
5426 opcode = OP_STORER8_MEMBASE_REG;
5427 else if (is_ref || fsig->params [0]->type == MONO_TYPE_I || fsig->params [0]->type == MONO_TYPE_U)
5428 opcode = OP_STORE_MEMBASE_REG;
5431 mini_emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_SEQ);
5433 MONO_INST_NEW (cfg, ins, opcode);
5434 ins->sreg1 = args [1]->dreg;
5435 ins->inst_destbasereg = args [0]->dreg;
5436 ins->inst_offset = 0;
5437 MONO_ADD_INS (cfg->cbb, ins);
5439 if (opcode == OP_STOREI8_MEMBASE_REG)
5440 ins = mono_decompose_opcode (cfg, ins);
5445 } else if (cmethod->klass->image == mono_defaults.corlib &&
5446 (strcmp (cmethod->klass->name_space, "System.Threading") == 0) &&
5447 (strcmp (cmethod->klass->name, "Interlocked") == 0)) {
5450 #if SIZEOF_REGISTER == 8
5451 if (!cfg->llvm_only && strcmp (cmethod->name, "Read") == 0 && fsig->param_count == 1 && (fsig->params [0]->type == MONO_TYPE_I8)) {
5452 if (!cfg->llvm_only && mono_arch_opcode_supported (OP_ATOMIC_LOAD_I8)) {
5453 MONO_INST_NEW (cfg, ins, OP_ATOMIC_LOAD_I8);
5454 ins->dreg = mono_alloc_preg (cfg);
5455 ins->sreg1 = args [0]->dreg;
5456 ins->type = STACK_I8;
5457 ins->backend.memory_barrier_kind = MONO_MEMORY_BARRIER_SEQ;
5458 MONO_ADD_INS (cfg->cbb, ins);
5462 mini_emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_SEQ);
5464 /* 64 bit reads are already atomic */
5465 MONO_INST_NEW (cfg, load_ins, OP_LOADI8_MEMBASE);
5466 load_ins->dreg = mono_alloc_preg (cfg);
5467 load_ins->inst_basereg = args [0]->dreg;
5468 load_ins->inst_offset = 0;
5469 load_ins->type = STACK_I8;
5470 MONO_ADD_INS (cfg->cbb, load_ins);
5472 mini_emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_SEQ);
5479 if (strcmp (cmethod->name, "Increment") == 0 && fsig->param_count == 1) {
5480 MonoInst *ins_iconst;
5483 if (fsig->params [0]->type == MONO_TYPE_I4) {
5484 opcode = OP_ATOMIC_ADD_I4;
5485 cfg->has_atomic_add_i4 = TRUE;
5487 #if SIZEOF_REGISTER == 8
5488 else if (fsig->params [0]->type == MONO_TYPE_I8)
5489 opcode = OP_ATOMIC_ADD_I8;
5492 if (!mono_arch_opcode_supported (opcode))
5494 MONO_INST_NEW (cfg, ins_iconst, OP_ICONST);
5495 ins_iconst->inst_c0 = 1;
5496 ins_iconst->dreg = mono_alloc_ireg (cfg);
5497 MONO_ADD_INS (cfg->cbb, ins_iconst);
5499 MONO_INST_NEW (cfg, ins, opcode);
5500 ins->dreg = mono_alloc_ireg (cfg);
5501 ins->inst_basereg = args [0]->dreg;
5502 ins->inst_offset = 0;
5503 ins->sreg2 = ins_iconst->dreg;
5504 ins->type = (opcode == OP_ATOMIC_ADD_I4) ? STACK_I4 : STACK_I8;
5505 MONO_ADD_INS (cfg->cbb, ins);
5507 } else if (strcmp (cmethod->name, "Decrement") == 0 && fsig->param_count == 1) {
5508 MonoInst *ins_iconst;
5511 if (fsig->params [0]->type == MONO_TYPE_I4) {
5512 opcode = OP_ATOMIC_ADD_I4;
5513 cfg->has_atomic_add_i4 = TRUE;
5515 #if SIZEOF_REGISTER == 8
5516 else if (fsig->params [0]->type == MONO_TYPE_I8)
5517 opcode = OP_ATOMIC_ADD_I8;
5520 if (!mono_arch_opcode_supported (opcode))
5522 MONO_INST_NEW (cfg, ins_iconst, OP_ICONST);
5523 ins_iconst->inst_c0 = -1;
5524 ins_iconst->dreg = mono_alloc_ireg (cfg);
5525 MONO_ADD_INS (cfg->cbb, ins_iconst);
5527 MONO_INST_NEW (cfg, ins, opcode);
5528 ins->dreg = mono_alloc_ireg (cfg);
5529 ins->inst_basereg = args [0]->dreg;
5530 ins->inst_offset = 0;
5531 ins->sreg2 = ins_iconst->dreg;
5532 ins->type = (opcode == OP_ATOMIC_ADD_I4) ? STACK_I4 : STACK_I8;
5533 MONO_ADD_INS (cfg->cbb, ins);
5535 } else if (strcmp (cmethod->name, "Add") == 0 && fsig->param_count == 2) {
5538 if (fsig->params [0]->type == MONO_TYPE_I4) {
5539 opcode = OP_ATOMIC_ADD_I4;
5540 cfg->has_atomic_add_i4 = TRUE;
5542 #if SIZEOF_REGISTER == 8
5543 else if (fsig->params [0]->type == MONO_TYPE_I8)
5544 opcode = OP_ATOMIC_ADD_I8;
5547 if (!mono_arch_opcode_supported (opcode))
5549 MONO_INST_NEW (cfg, ins, opcode);
5550 ins->dreg = mono_alloc_ireg (cfg);
5551 ins->inst_basereg = args [0]->dreg;
5552 ins->inst_offset = 0;
5553 ins->sreg2 = args [1]->dreg;
5554 ins->type = (opcode == OP_ATOMIC_ADD_I4) ? STACK_I4 : STACK_I8;
5555 MONO_ADD_INS (cfg->cbb, ins);
5558 else if (strcmp (cmethod->name, "Exchange") == 0 && fsig->param_count == 2) {
5559 MonoInst *f2i = NULL, *i2f;
5560 guint32 opcode, f2i_opcode, i2f_opcode;
5561 gboolean is_ref = mini_type_is_reference (fsig->params [0]);
5562 gboolean is_float = fsig->params [0]->type == MONO_TYPE_R4 || fsig->params [0]->type == MONO_TYPE_R8;
5564 if (fsig->params [0]->type == MONO_TYPE_I4 ||
5565 fsig->params [0]->type == MONO_TYPE_R4) {
5566 opcode = OP_ATOMIC_EXCHANGE_I4;
5567 f2i_opcode = OP_MOVE_F_TO_I4;
5568 i2f_opcode = OP_MOVE_I4_TO_F;
5569 cfg->has_atomic_exchange_i4 = TRUE;
5571 #if SIZEOF_REGISTER == 8
5573 fsig->params [0]->type == MONO_TYPE_I8 ||
5574 fsig->params [0]->type == MONO_TYPE_R8 ||
5575 fsig->params [0]->type == MONO_TYPE_I) {
5576 opcode = OP_ATOMIC_EXCHANGE_I8;
5577 f2i_opcode = OP_MOVE_F_TO_I8;
5578 i2f_opcode = OP_MOVE_I8_TO_F;
5581 else if (is_ref || fsig->params [0]->type == MONO_TYPE_I) {
5582 opcode = OP_ATOMIC_EXCHANGE_I4;
5583 cfg->has_atomic_exchange_i4 = TRUE;
5589 if (!mono_arch_opcode_supported (opcode))
5593 /* TODO: Decompose these opcodes instead of bailing here. */
5594 if (COMPILE_SOFT_FLOAT (cfg))
5597 MONO_INST_NEW (cfg, f2i, f2i_opcode);
5598 f2i->dreg = mono_alloc_ireg (cfg);
5599 f2i->sreg1 = args [1]->dreg;
5600 if (f2i_opcode == OP_MOVE_F_TO_I4)
5601 f2i->backend.spill_var = mini_get_int_to_float_spill_area (cfg);
5602 MONO_ADD_INS (cfg->cbb, f2i);
5605 MONO_INST_NEW (cfg, ins, opcode);
5606 ins->dreg = is_ref ? mono_alloc_ireg_ref (cfg) : mono_alloc_ireg (cfg);
5607 ins->inst_basereg = args [0]->dreg;
5608 ins->inst_offset = 0;
5609 ins->sreg2 = is_float ? f2i->dreg : args [1]->dreg;
5610 MONO_ADD_INS (cfg->cbb, ins);
5612 switch (fsig->params [0]->type) {
5614 ins->type = STACK_I4;
5617 ins->type = STACK_I8;
5620 #if SIZEOF_REGISTER == 8
5621 ins->type = STACK_I8;
5623 ins->type = STACK_I4;
5628 ins->type = STACK_R8;
5631 g_assert (mini_type_is_reference (fsig->params [0]));
5632 ins->type = STACK_OBJ;
5637 MONO_INST_NEW (cfg, i2f, i2f_opcode);
5638 i2f->dreg = mono_alloc_freg (cfg);
5639 i2f->sreg1 = ins->dreg;
5640 i2f->type = STACK_R8;
5641 if (i2f_opcode == OP_MOVE_I4_TO_F)
5642 i2f->backend.spill_var = mini_get_int_to_float_spill_area (cfg);
5643 MONO_ADD_INS (cfg->cbb, i2f);
5648 if (cfg->gen_write_barriers && is_ref)
5649 mini_emit_write_barrier (cfg, args [0], args [1]);
5651 else if ((strcmp (cmethod->name, "CompareExchange") == 0) && fsig->param_count == 3) {
5652 MonoInst *f2i_new = NULL, *f2i_cmp = NULL, *i2f;
5653 guint32 opcode, f2i_opcode, i2f_opcode;
5654 gboolean is_ref = mini_type_is_reference (fsig->params [1]);
5655 gboolean is_float = fsig->params [1]->type == MONO_TYPE_R4 || fsig->params [1]->type == MONO_TYPE_R8;
5657 if (fsig->params [1]->type == MONO_TYPE_I4 ||
5658 fsig->params [1]->type == MONO_TYPE_R4) {
5659 opcode = OP_ATOMIC_CAS_I4;
5660 f2i_opcode = OP_MOVE_F_TO_I4;
5661 i2f_opcode = OP_MOVE_I4_TO_F;
5662 cfg->has_atomic_cas_i4 = TRUE;
5664 #if SIZEOF_REGISTER == 8
5666 fsig->params [1]->type == MONO_TYPE_I8 ||
5667 fsig->params [1]->type == MONO_TYPE_R8 ||
5668 fsig->params [1]->type == MONO_TYPE_I) {
5669 opcode = OP_ATOMIC_CAS_I8;
5670 f2i_opcode = OP_MOVE_F_TO_I8;
5671 i2f_opcode = OP_MOVE_I8_TO_F;
5674 else if (is_ref || fsig->params [1]->type == MONO_TYPE_I) {
5675 opcode = OP_ATOMIC_CAS_I4;
5676 cfg->has_atomic_cas_i4 = TRUE;
5682 if (!mono_arch_opcode_supported (opcode))
5686 /* TODO: Decompose these opcodes instead of bailing here. */
5687 if (COMPILE_SOFT_FLOAT (cfg))
5690 MONO_INST_NEW (cfg, f2i_new, f2i_opcode);
5691 f2i_new->dreg = mono_alloc_ireg (cfg);
5692 f2i_new->sreg1 = args [1]->dreg;
5693 if (f2i_opcode == OP_MOVE_F_TO_I4)
5694 f2i_new->backend.spill_var = mini_get_int_to_float_spill_area (cfg);
5695 MONO_ADD_INS (cfg->cbb, f2i_new);
5697 MONO_INST_NEW (cfg, f2i_cmp, f2i_opcode);
5698 f2i_cmp->dreg = mono_alloc_ireg (cfg);
5699 f2i_cmp->sreg1 = args [2]->dreg;
5700 if (f2i_opcode == OP_MOVE_F_TO_I4)
5701 f2i_cmp->backend.spill_var = mini_get_int_to_float_spill_area (cfg);
5702 MONO_ADD_INS (cfg->cbb, f2i_cmp);
5705 MONO_INST_NEW (cfg, ins, opcode);
5706 ins->dreg = is_ref ? alloc_ireg_ref (cfg) : alloc_ireg (cfg);
5707 ins->sreg1 = args [0]->dreg;
5708 ins->sreg2 = is_float ? f2i_new->dreg : args [1]->dreg;
5709 ins->sreg3 = is_float ? f2i_cmp->dreg : args [2]->dreg;
5710 MONO_ADD_INS (cfg->cbb, ins);
5712 switch (fsig->params [1]->type) {
5714 ins->type = STACK_I4;
5717 ins->type = STACK_I8;
5720 #if SIZEOF_REGISTER == 8
5721 ins->type = STACK_I8;
5723 ins->type = STACK_I4;
5727 ins->type = cfg->r4_stack_type;
5730 ins->type = STACK_R8;
5733 g_assert (mini_type_is_reference (fsig->params [1]));
5734 ins->type = STACK_OBJ;
5739 MONO_INST_NEW (cfg, i2f, i2f_opcode);
5740 i2f->dreg = mono_alloc_freg (cfg);
5741 i2f->sreg1 = ins->dreg;
5742 i2f->type = STACK_R8;
5743 if (i2f_opcode == OP_MOVE_I4_TO_F)
5744 i2f->backend.spill_var = mini_get_int_to_float_spill_area (cfg);
5745 MONO_ADD_INS (cfg->cbb, i2f);
5750 if (cfg->gen_write_barriers && is_ref)
5751 mini_emit_write_barrier (cfg, args [0], args [1]);
5753 else if ((strcmp (cmethod->name, "CompareExchange") == 0) && fsig->param_count == 4 &&
5754 fsig->params [1]->type == MONO_TYPE_I4) {
5755 MonoInst *cmp, *ceq;
5757 if (!mono_arch_opcode_supported (OP_ATOMIC_CAS_I4))
5760 /* int32 r = CAS (location, value, comparand); */
5761 MONO_INST_NEW (cfg, ins, OP_ATOMIC_CAS_I4);
5762 ins->dreg = alloc_ireg (cfg);
5763 ins->sreg1 = args [0]->dreg;
5764 ins->sreg2 = args [1]->dreg;
5765 ins->sreg3 = args [2]->dreg;
5766 ins->type = STACK_I4;
5767 MONO_ADD_INS (cfg->cbb, ins);
5769 /* bool result = r == comparand; */
5770 MONO_INST_NEW (cfg, cmp, OP_ICOMPARE);
5771 cmp->sreg1 = ins->dreg;
5772 cmp->sreg2 = args [2]->dreg;
5773 cmp->type = STACK_I4;
5774 MONO_ADD_INS (cfg->cbb, cmp);
5776 MONO_INST_NEW (cfg, ceq, OP_ICEQ);
5777 ceq->dreg = alloc_ireg (cfg);
5778 ceq->type = STACK_I4;
5779 MONO_ADD_INS (cfg->cbb, ceq);
5781 /* *success = result; */
5782 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREI1_MEMBASE_REG, args [3]->dreg, 0, ceq->dreg);
5784 cfg->has_atomic_cas_i4 = TRUE;
5786 else if (strcmp (cmethod->name, "MemoryBarrier") == 0 && fsig->param_count == 0)
5787 ins = mini_emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_SEQ);
5791 } else if (cmethod->klass->image == mono_defaults.corlib &&
5792 (strcmp (cmethod->klass->name_space, "System.Threading") == 0) &&
5793 (strcmp (cmethod->klass->name, "Volatile") == 0)) {
5796 if (!cfg->llvm_only && !strcmp (cmethod->name, "Read") && fsig->param_count == 1) {
5798 MonoType *t = fsig->params [0];
5800 gboolean is_float = t->type == MONO_TYPE_R4 || t->type == MONO_TYPE_R8;
5802 g_assert (t->byref);
5803 /* t is a byref type, so the reference check is more complicated */
5804 is_ref = mini_type_is_reference (&mono_class_from_mono_type (t)->byval_arg);
5805 if (t->type == MONO_TYPE_I1)
5806 opcode = OP_ATOMIC_LOAD_I1;
5807 else if (t->type == MONO_TYPE_U1 || t->type == MONO_TYPE_BOOLEAN)
5808 opcode = OP_ATOMIC_LOAD_U1;
5809 else if (t->type == MONO_TYPE_I2)
5810 opcode = OP_ATOMIC_LOAD_I2;
5811 else if (t->type == MONO_TYPE_U2)
5812 opcode = OP_ATOMIC_LOAD_U2;
5813 else if (t->type == MONO_TYPE_I4)
5814 opcode = OP_ATOMIC_LOAD_I4;
5815 else if (t->type == MONO_TYPE_U4)
5816 opcode = OP_ATOMIC_LOAD_U4;
5817 else if (t->type == MONO_TYPE_R4)
5818 opcode = OP_ATOMIC_LOAD_R4;
5819 else if (t->type == MONO_TYPE_R8)
5820 opcode = OP_ATOMIC_LOAD_R8;
5821 #if SIZEOF_REGISTER == 8
5822 else if (t->type == MONO_TYPE_I8 || t->type == MONO_TYPE_I)
5823 opcode = OP_ATOMIC_LOAD_I8;
5824 else if (is_ref || t->type == MONO_TYPE_U8 || t->type == MONO_TYPE_U)
5825 opcode = OP_ATOMIC_LOAD_U8;
5827 else if (t->type == MONO_TYPE_I)
5828 opcode = OP_ATOMIC_LOAD_I4;
5829 else if (is_ref || t->type == MONO_TYPE_U)
5830 opcode = OP_ATOMIC_LOAD_U4;
5834 if (!mono_arch_opcode_supported (opcode))
5837 MONO_INST_NEW (cfg, ins, opcode);
5838 ins->dreg = is_ref ? mono_alloc_ireg_ref (cfg) : (is_float ? mono_alloc_freg (cfg) : mono_alloc_ireg (cfg));
5839 ins->sreg1 = args [0]->dreg;
5840 ins->backend.memory_barrier_kind = MONO_MEMORY_BARRIER_ACQ;
5841 MONO_ADD_INS (cfg->cbb, ins);
5844 case MONO_TYPE_BOOLEAN:
5851 ins->type = STACK_I4;
5855 ins->type = STACK_I8;
5859 #if SIZEOF_REGISTER == 8
5860 ins->type = STACK_I8;
5862 ins->type = STACK_I4;
5866 ins->type = cfg->r4_stack_type;
5869 ins->type = STACK_R8;
5873 ins->type = STACK_OBJ;
5879 if (!cfg->llvm_only && !strcmp (cmethod->name, "Write") && fsig->param_count == 2) {
5881 MonoType *t = fsig->params [0];
5884 g_assert (t->byref);
5885 is_ref = mini_type_is_reference (&mono_class_from_mono_type (t)->byval_arg);
5886 if (t->type == MONO_TYPE_I1)
5887 opcode = OP_ATOMIC_STORE_I1;
5888 else if (t->type == MONO_TYPE_U1 || t->type == MONO_TYPE_BOOLEAN)
5889 opcode = OP_ATOMIC_STORE_U1;
5890 else if (t->type == MONO_TYPE_I2)
5891 opcode = OP_ATOMIC_STORE_I2;
5892 else if (t->type == MONO_TYPE_U2)
5893 opcode = OP_ATOMIC_STORE_U2;
5894 else if (t->type == MONO_TYPE_I4)
5895 opcode = OP_ATOMIC_STORE_I4;
5896 else if (t->type == MONO_TYPE_U4)
5897 opcode = OP_ATOMIC_STORE_U4;
5898 else if (t->type == MONO_TYPE_R4)
5899 opcode = OP_ATOMIC_STORE_R4;
5900 else if (t->type == MONO_TYPE_R8)
5901 opcode = OP_ATOMIC_STORE_R8;
5902 #if SIZEOF_REGISTER == 8
5903 else if (t->type == MONO_TYPE_I8 || t->type == MONO_TYPE_I)
5904 opcode = OP_ATOMIC_STORE_I8;
5905 else if (is_ref || t->type == MONO_TYPE_U8 || t->type == MONO_TYPE_U)
5906 opcode = OP_ATOMIC_STORE_U8;
5908 else if (t->type == MONO_TYPE_I)
5909 opcode = OP_ATOMIC_STORE_I4;
5910 else if (is_ref || t->type == MONO_TYPE_U)
5911 opcode = OP_ATOMIC_STORE_U4;
5915 if (!mono_arch_opcode_supported (opcode))
5918 MONO_INST_NEW (cfg, ins, opcode);
5919 ins->dreg = args [0]->dreg;
5920 ins->sreg1 = args [1]->dreg;
5921 ins->backend.memory_barrier_kind = MONO_MEMORY_BARRIER_REL;
5922 MONO_ADD_INS (cfg->cbb, ins);
5924 if (cfg->gen_write_barriers && is_ref)
5925 mini_emit_write_barrier (cfg, args [0], args [1]);
5931 } else if (cmethod->klass->image == mono_defaults.corlib &&
5932 (strcmp (cmethod->klass->name_space, "System.Diagnostics") == 0) &&
5933 (strcmp (cmethod->klass->name, "Debugger") == 0)) {
5934 if (!strcmp (cmethod->name, "Break") && fsig->param_count == 0) {
5935 if (should_insert_brekpoint (cfg->method)) {
5936 ins = mono_emit_jit_icall (cfg, mono_debugger_agent_user_break, NULL);
5938 MONO_INST_NEW (cfg, ins, OP_NOP);
5939 MONO_ADD_INS (cfg->cbb, ins);
5943 } else if (cmethod->klass->image == mono_defaults.corlib &&
5944 (strcmp (cmethod->klass->name_space, "System") == 0) &&
5945 (strcmp (cmethod->klass->name, "Environment") == 0)) {
5946 if (!strcmp (cmethod->name, "get_IsRunningOnWindows") && fsig->param_count == 0) {
5948 EMIT_NEW_ICONST (cfg, ins, 1);
5950 EMIT_NEW_ICONST (cfg, ins, 0);
5953 } else if (cmethod->klass->image == mono_defaults.corlib &&
5954 (strcmp (cmethod->klass->name_space, "System.Reflection") == 0) &&
5955 (strcmp (cmethod->klass->name, "Assembly") == 0)) {
5956 if (cfg->llvm_only && !strcmp (cmethod->name, "GetExecutingAssembly")) {
5957 /* No stack walks are currently available, so implement this as an intrinsic */
5958 MonoInst *assembly_ins;
5960 EMIT_NEW_AOTCONST (cfg, assembly_ins, MONO_PATCH_INFO_IMAGE, cfg->method->klass->image);
5961 ins = mono_emit_jit_icall (cfg, mono_get_assembly_object, &assembly_ins);
5964 } else if (cmethod->klass->image == mono_defaults.corlib &&
5965 (strcmp (cmethod->klass->name_space, "System.Reflection") == 0) &&
5966 (strcmp (cmethod->klass->name, "MethodBase") == 0)) {
5967 if (cfg->llvm_only && !strcmp (cmethod->name, "GetCurrentMethod")) {
5968 /* No stack walks are currently available, so implement this as an intrinsic */
5969 MonoInst *method_ins;
5970 MonoMethod *declaring = cfg->method;
5972 /* This returns the declaring generic method */
5973 if (declaring->is_inflated)
5974 declaring = ((MonoMethodInflated*)cfg->method)->declaring;
5975 EMIT_NEW_AOTCONST (cfg, method_ins, MONO_PATCH_INFO_METHODCONST, declaring);
5976 ins = mono_emit_jit_icall (cfg, mono_get_method_object, &method_ins);
5977 cfg->no_inline = TRUE;
5978 if (cfg->method != cfg->current_method)
5979 inline_failure (cfg, "MethodBase:GetCurrentMethod ()");
5982 } else if (cmethod->klass == mono_defaults.math_class) {
5984 * There is general branchless code for Min/Max, but it does not work for
5986 * http://everything2.com/?node_id=1051618
5988 } else if (cmethod->klass == mono_defaults.systemtype_class && !strcmp (cmethod->name, "op_Equality")) {
5989 EMIT_NEW_BIALU (cfg, ins, OP_COMPARE, -1, args [0]->dreg, args [1]->dreg);
5990 MONO_INST_NEW (cfg, ins, OP_PCEQ);
5991 ins->dreg = alloc_preg (cfg);
5992 ins->type = STACK_I4;
5993 MONO_ADD_INS (cfg->cbb, ins);
5995 } else if (((!strcmp (cmethod->klass->image->assembly->aname.name, "MonoMac") ||
5996 !strcmp (cmethod->klass->image->assembly->aname.name, "monotouch")) &&
5997 !strcmp (cmethod->klass->name_space, "XamCore.ObjCRuntime") &&
5998 !strcmp (cmethod->klass->name, "Selector")) ||
5999 ((!strcmp (cmethod->klass->image->assembly->aname.name, "Xamarin.iOS") ||
6000 !strcmp (cmethod->klass->image->assembly->aname.name, "Xamarin.Mac")) &&
6001 !strcmp (cmethod->klass->name_space, "ObjCRuntime") &&
6002 !strcmp (cmethod->klass->name, "Selector"))
6004 if ((cfg->backend->have_objc_get_selector || cfg->compile_llvm) &&
6005 !strcmp (cmethod->name, "GetHandle") && fsig->param_count == 1 &&
6006 (args [0]->opcode == OP_GOT_ENTRY || args [0]->opcode == OP_AOTCONST) &&
6009 MonoJumpInfoToken *ji;
6012 if (args [0]->opcode == OP_GOT_ENTRY) {
6013 pi = (MonoInst *)args [0]->inst_p1;
6014 g_assert (pi->opcode == OP_PATCH_INFO);
6015 g_assert (GPOINTER_TO_INT (pi->inst_p1) == MONO_PATCH_INFO_LDSTR);
6016 ji = (MonoJumpInfoToken *)pi->inst_p0;
6018 g_assert (GPOINTER_TO_INT (args [0]->inst_p1) == MONO_PATCH_INFO_LDSTR);
6019 ji = (MonoJumpInfoToken *)args [0]->inst_p0;
6022 NULLIFY_INS (args [0]);
6024 s = mono_ldstr_utf8 (ji->image, mono_metadata_token_index (ji->token), &cfg->error);
6025 return_val_if_nok (&cfg->error, NULL);
6027 MONO_INST_NEW (cfg, ins, OP_OBJC_GET_SELECTOR);
6028 ins->dreg = mono_alloc_ireg (cfg);
6031 MONO_ADD_INS (cfg->cbb, ins);
6036 #ifdef MONO_ARCH_SIMD_INTRINSICS
6037 if (cfg->opt & MONO_OPT_SIMD) {
6038 ins = mono_emit_simd_intrinsics (cfg, cmethod, fsig, args);
6044 ins = mono_emit_native_types_intrinsics (cfg, cmethod, fsig, args);
6048 if (COMPILE_LLVM (cfg)) {
6049 ins = llvm_emit_inst_for_method (cfg, cmethod, fsig, args);
6054 return mono_arch_emit_inst_for_method (cfg, cmethod, fsig, args);
6058 * This entry point could be used later for arbitrary method
6061 inline static MonoInst*
6062 mini_redirect_call (MonoCompile *cfg, MonoMethod *method,
6063 MonoMethodSignature *signature, MonoInst **args, MonoInst *this_ins)
6065 if (method->klass == mono_defaults.string_class) {
6066 /* managed string allocation support */
6067 if (strcmp (method->name, "InternalAllocateStr") == 0 && !(mono_profiler_events & MONO_PROFILE_ALLOCATIONS) && !(cfg->opt & MONO_OPT_SHARED)) {
6068 MonoInst *iargs [2];
6069 MonoVTable *vtable = mono_class_vtable (cfg->domain, method->klass);
6070 MonoMethod *managed_alloc = NULL;
6072 g_assert (vtable); /*Should not fail since it System.String*/
6073 #ifndef MONO_CROSS_COMPILE
6074 managed_alloc = mono_gc_get_managed_allocator (method->klass, FALSE, FALSE);
6078 EMIT_NEW_VTABLECONST (cfg, iargs [0], vtable);
6079 iargs [1] = args [0];
6080 return mono_emit_method_call (cfg, managed_alloc, iargs, this_ins);
6087 mono_save_args (MonoCompile *cfg, MonoMethodSignature *sig, MonoInst **sp)
6089 MonoInst *store, *temp;
6092 for (i = 0; i < sig->param_count + sig->hasthis; ++i) {
6093 MonoType *argtype = (sig->hasthis && (i == 0)) ? type_from_stack_type (*sp) : sig->params [i - sig->hasthis];
6096 * FIXME: We should use *args++ = sp [0], but that would mean the arg
6097 * would be different than the MonoInst's used to represent arguments, and
6098 * the ldelema implementation can't deal with that.
6099 * Solution: When ldelema is used on an inline argument, create a var for
6100 * it, emit ldelema on that var, and emit the saving code below in
6101 * inline_method () if needed.
6103 temp = mono_compile_create_var (cfg, argtype, OP_LOCAL);
6104 cfg->args [i] = temp;
6105 /* This uses cfg->args [i] which is set by the preceeding line */
6106 EMIT_NEW_ARGSTORE (cfg, store, i, *sp);
6107 store->cil_code = sp [0]->cil_code;
6112 #define MONO_INLINE_CALLED_LIMITED_METHODS 1
6113 #define MONO_INLINE_CALLER_LIMITED_METHODS 1
6115 #if (MONO_INLINE_CALLED_LIMITED_METHODS)
6117 check_inline_called_method_name_limit (MonoMethod *called_method)
6120 static const char *limit = NULL;
6122 if (limit == NULL) {
6123 const char *limit_string = g_getenv ("MONO_INLINE_CALLED_METHOD_NAME_LIMIT");
6125 if (limit_string != NULL)
6126 limit = limit_string;
6131 if (limit [0] != '\0') {
6132 char *called_method_name = mono_method_full_name (called_method, TRUE);
6134 strncmp_result = strncmp (called_method_name, limit, strlen (limit));
6135 g_free (called_method_name);
6137 //return (strncmp_result <= 0);
6138 return (strncmp_result == 0);
6145 #if (MONO_INLINE_CALLER_LIMITED_METHODS)
6147 check_inline_caller_method_name_limit (MonoMethod *caller_method)
6150 static const char *limit = NULL;
6152 if (limit == NULL) {
6153 const char *limit_string = g_getenv ("MONO_INLINE_CALLER_METHOD_NAME_LIMIT");
6154 if (limit_string != NULL) {
6155 limit = limit_string;
6161 if (limit [0] != '\0') {
6162 char *caller_method_name = mono_method_full_name (caller_method, TRUE);
6164 strncmp_result = strncmp (caller_method_name, limit, strlen (limit));
6165 g_free (caller_method_name);
6167 //return (strncmp_result <= 0);
6168 return (strncmp_result == 0);
6176 emit_init_rvar (MonoCompile *cfg, int dreg, MonoType *rtype)
6178 static double r8_0 = 0.0;
6179 static float r4_0 = 0.0;
6183 rtype = mini_get_underlying_type (rtype);
6187 MONO_EMIT_NEW_PCONST (cfg, dreg, NULL);
6188 } else if (t >= MONO_TYPE_BOOLEAN && t <= MONO_TYPE_U4) {
6189 MONO_EMIT_NEW_ICONST (cfg, dreg, 0);
6190 } else if (t == MONO_TYPE_I8 || t == MONO_TYPE_U8) {
6191 MONO_EMIT_NEW_I8CONST (cfg, dreg, 0);
6192 } else if (cfg->r4fp && t == MONO_TYPE_R4) {
6193 MONO_INST_NEW (cfg, ins, OP_R4CONST);
6194 ins->type = STACK_R4;
6195 ins->inst_p0 = (void*)&r4_0;
6197 MONO_ADD_INS (cfg->cbb, ins);
6198 } else if (t == MONO_TYPE_R4 || t == MONO_TYPE_R8) {
6199 MONO_INST_NEW (cfg, ins, OP_R8CONST);
6200 ins->type = STACK_R8;
6201 ins->inst_p0 = (void*)&r8_0;
6203 MONO_ADD_INS (cfg->cbb, ins);
6204 } else if ((t == MONO_TYPE_VALUETYPE) || (t == MONO_TYPE_TYPEDBYREF) ||
6205 ((t == MONO_TYPE_GENERICINST) && mono_type_generic_inst_is_valuetype (rtype))) {
6206 MONO_EMIT_NEW_VZERO (cfg, dreg, mono_class_from_mono_type (rtype));
6207 } else if (((t == MONO_TYPE_VAR) || (t == MONO_TYPE_MVAR)) && mini_type_var_is_vt (rtype)) {
6208 MONO_EMIT_NEW_VZERO (cfg, dreg, mono_class_from_mono_type (rtype));
6210 MONO_EMIT_NEW_PCONST (cfg, dreg, NULL);
6215 emit_dummy_init_rvar (MonoCompile *cfg, int dreg, MonoType *rtype)
6219 rtype = mini_get_underlying_type (rtype);
6223 MONO_EMIT_NEW_DUMMY_INIT (cfg, dreg, OP_DUMMY_PCONST);
6224 } else if (t >= MONO_TYPE_BOOLEAN && t <= MONO_TYPE_U4) {
6225 MONO_EMIT_NEW_DUMMY_INIT (cfg, dreg, OP_DUMMY_ICONST);
6226 } else if (t == MONO_TYPE_I8 || t == MONO_TYPE_U8) {
6227 MONO_EMIT_NEW_DUMMY_INIT (cfg, dreg, OP_DUMMY_I8CONST);
6228 } else if (cfg->r4fp && t == MONO_TYPE_R4) {
6229 MONO_EMIT_NEW_DUMMY_INIT (cfg, dreg, OP_DUMMY_R4CONST);
6230 } else if (t == MONO_TYPE_R4 || t == MONO_TYPE_R8) {
6231 MONO_EMIT_NEW_DUMMY_INIT (cfg, dreg, OP_DUMMY_R8CONST);
6232 } else if ((t == MONO_TYPE_VALUETYPE) || (t == MONO_TYPE_TYPEDBYREF) ||
6233 ((t == MONO_TYPE_GENERICINST) && mono_type_generic_inst_is_valuetype (rtype))) {
6234 MONO_EMIT_NEW_DUMMY_INIT (cfg, dreg, OP_DUMMY_VZERO);
6235 } else if (((t == MONO_TYPE_VAR) || (t == MONO_TYPE_MVAR)) && mini_type_var_is_vt (rtype)) {
6236 MONO_EMIT_NEW_DUMMY_INIT (cfg, dreg, OP_DUMMY_VZERO);
6238 emit_init_rvar (cfg, dreg, rtype);
6242 /* If INIT is FALSE, emit dummy initialization statements to keep the IR valid */
6244 emit_init_local (MonoCompile *cfg, int local, MonoType *type, gboolean init)
6246 MonoInst *var = cfg->locals [local];
6247 if (COMPILE_SOFT_FLOAT (cfg)) {
6249 int reg = alloc_dreg (cfg, (MonoStackType)var->type);
6250 emit_init_rvar (cfg, reg, type);
6251 EMIT_NEW_LOCSTORE (cfg, store, local, cfg->cbb->last_ins);
6254 emit_init_rvar (cfg, var->dreg, type);
6256 emit_dummy_init_rvar (cfg, var->dreg, type);
6261 mini_inline_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **sp, guchar *ip, guint real_offset, gboolean inline_always)
6263 return inline_method (cfg, cmethod, fsig, sp, ip, real_offset, inline_always);
6269 * Return the cost of inlining CMETHOD, or zero if it should not be inlined.
6272 inline_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **sp,
6273 guchar *ip, guint real_offset, gboolean inline_always)
6276 MonoInst *ins, *rvar = NULL;
6277 MonoMethodHeader *cheader;
6278 MonoBasicBlock *ebblock, *sbblock;
6280 MonoMethod *prev_inlined_method;
6281 MonoInst **prev_locals, **prev_args;
6282 MonoType **prev_arg_types;
6283 guint prev_real_offset;
6284 GHashTable *prev_cbb_hash;
6285 MonoBasicBlock **prev_cil_offset_to_bb;
6286 MonoBasicBlock *prev_cbb;
6287 const unsigned char *prev_ip;
6288 unsigned char *prev_cil_start;
6289 guint32 prev_cil_offset_to_bb_len;
6290 MonoMethod *prev_current_method;
6291 MonoGenericContext *prev_generic_context;
6292 gboolean ret_var_set, prev_ret_var_set, prev_disable_inline, virtual_ = FALSE;
6294 g_assert (cfg->exception_type == MONO_EXCEPTION_NONE);
6296 #if (MONO_INLINE_CALLED_LIMITED_METHODS)
6297 if ((! inline_always) && ! check_inline_called_method_name_limit (cmethod))
6300 #if (MONO_INLINE_CALLER_LIMITED_METHODS)
6301 if ((! inline_always) && ! check_inline_caller_method_name_limit (cfg->method))
6306 fsig = mono_method_signature (cmethod);
6308 if (cfg->verbose_level > 2)
6309 printf ("INLINE START %p %s -> %s\n", cmethod, mono_method_full_name (cfg->method, TRUE), mono_method_full_name (cmethod, TRUE));
6311 if (!cmethod->inline_info) {
6312 cfg->stat_inlineable_methods++;
6313 cmethod->inline_info = 1;
6316 /* allocate local variables */
6317 cheader = mono_method_get_header_checked (cmethod, &error);
6319 if (inline_always) {
6320 mono_cfg_set_exception (cfg, MONO_EXCEPTION_MONO_ERROR);
6321 mono_error_move (&cfg->error, &error);
6323 mono_error_cleanup (&error);
6328 /*Must verify before creating locals as it can cause the JIT to assert.*/
6329 if (mono_compile_is_broken (cfg, cmethod, FALSE)) {
6330 mono_metadata_free_mh (cheader);
6334 /* allocate space to store the return value */
6335 if (!MONO_TYPE_IS_VOID (fsig->ret)) {
6336 rvar = mono_compile_create_var (cfg, fsig->ret, OP_LOCAL);
6339 prev_locals = cfg->locals;
6340 cfg->locals = (MonoInst **)mono_mempool_alloc0 (cfg->mempool, cheader->num_locals * sizeof (MonoInst*));
6341 for (i = 0; i < cheader->num_locals; ++i)
6342 cfg->locals [i] = mono_compile_create_var (cfg, cheader->locals [i], OP_LOCAL);
6344 /* allocate start and end blocks */
6345 /* This is needed so if the inline is aborted, we can clean up */
6346 NEW_BBLOCK (cfg, sbblock);
6347 sbblock->real_offset = real_offset;
6349 NEW_BBLOCK (cfg, ebblock);
6350 ebblock->block_num = cfg->num_bblocks++;
6351 ebblock->real_offset = real_offset;
6353 prev_args = cfg->args;
6354 prev_arg_types = cfg->arg_types;
6355 prev_inlined_method = cfg->inlined_method;
6356 cfg->inlined_method = cmethod;
6357 cfg->ret_var_set = FALSE;
6358 cfg->inline_depth ++;
6359 prev_real_offset = cfg->real_offset;
6360 prev_cbb_hash = cfg->cbb_hash;
6361 prev_cil_offset_to_bb = cfg->cil_offset_to_bb;
6362 prev_cil_offset_to_bb_len = cfg->cil_offset_to_bb_len;
6363 prev_cil_start = cfg->cil_start;
6365 prev_cbb = cfg->cbb;
6366 prev_current_method = cfg->current_method;
6367 prev_generic_context = cfg->generic_context;
6368 prev_ret_var_set = cfg->ret_var_set;
6369 prev_disable_inline = cfg->disable_inline;
6371 if (ip && *ip == CEE_CALLVIRT && !(cmethod->flags & METHOD_ATTRIBUTE_STATIC))
6374 costs = mono_method_to_ir (cfg, cmethod, sbblock, ebblock, rvar, sp, real_offset, virtual_);
6376 ret_var_set = cfg->ret_var_set;
6378 cfg->inlined_method = prev_inlined_method;
6379 cfg->real_offset = prev_real_offset;
6380 cfg->cbb_hash = prev_cbb_hash;
6381 cfg->cil_offset_to_bb = prev_cil_offset_to_bb;
6382 cfg->cil_offset_to_bb_len = prev_cil_offset_to_bb_len;
6383 cfg->cil_start = prev_cil_start;
6385 cfg->locals = prev_locals;
6386 cfg->args = prev_args;
6387 cfg->arg_types = prev_arg_types;
6388 cfg->current_method = prev_current_method;
6389 cfg->generic_context = prev_generic_context;
6390 cfg->ret_var_set = prev_ret_var_set;
6391 cfg->disable_inline = prev_disable_inline;
6392 cfg->inline_depth --;
6394 if ((costs >= 0 && costs < 60) || inline_always || (costs >= 0 && (cmethod->iflags & METHOD_IMPL_ATTRIBUTE_AGGRESSIVE_INLINING))) {
6395 if (cfg->verbose_level > 2)
6396 printf ("INLINE END %s -> %s\n", mono_method_full_name (cfg->method, TRUE), mono_method_full_name (cmethod, TRUE));
6398 cfg->stat_inlined_methods++;
6400 /* always add some code to avoid block split failures */
6401 MONO_INST_NEW (cfg, ins, OP_NOP);
6402 MONO_ADD_INS (prev_cbb, ins);
6404 prev_cbb->next_bb = sbblock;
6405 link_bblock (cfg, prev_cbb, sbblock);
6408 * Get rid of the begin and end bblocks if possible to aid local
6411 if (prev_cbb->out_count == 1)
6412 mono_merge_basic_blocks (cfg, prev_cbb, sbblock);
6414 if ((prev_cbb->out_count == 1) && (prev_cbb->out_bb [0]->in_count == 1) && (prev_cbb->out_bb [0] != ebblock))
6415 mono_merge_basic_blocks (cfg, prev_cbb, prev_cbb->out_bb [0]);
6417 if ((ebblock->in_count == 1) && ebblock->in_bb [0]->out_count == 1) {
6418 MonoBasicBlock *prev = ebblock->in_bb [0];
6420 if (prev->next_bb == ebblock) {
6421 mono_merge_basic_blocks (cfg, prev, ebblock);
6423 if ((prev_cbb->out_count == 1) && (prev_cbb->out_bb [0]->in_count == 1) && (prev_cbb->out_bb [0] == prev)) {
6424 mono_merge_basic_blocks (cfg, prev_cbb, prev);
6425 cfg->cbb = prev_cbb;
6428 /* There could be a bblock after 'prev', and making 'prev' the current bb could cause problems */
6433 * Its possible that the rvar is set in some prev bblock, but not in others.
6439 for (i = 0; i < ebblock->in_count; ++i) {
6440 bb = ebblock->in_bb [i];
6442 if (bb->last_ins && bb->last_ins->opcode == OP_NOT_REACHED) {
6445 emit_init_rvar (cfg, rvar->dreg, fsig->ret);
6455 * If the inlined method contains only a throw, then the ret var is not
6456 * set, so set it to a dummy value.
6459 emit_init_rvar (cfg, rvar->dreg, fsig->ret);
6461 EMIT_NEW_TEMPLOAD (cfg, ins, rvar->inst_c0);
6464 cfg->headers_to_free = g_slist_prepend_mempool (cfg->mempool, cfg->headers_to_free, cheader);
6467 if (cfg->verbose_level > 2)
6468 printf ("INLINE ABORTED %s (cost %d)\n", mono_method_full_name (cmethod, TRUE), costs);
6469 cfg->exception_type = MONO_EXCEPTION_NONE;
6471 /* This gets rid of the newly added bblocks */
6472 cfg->cbb = prev_cbb;
6474 cfg->headers_to_free = g_slist_prepend_mempool (cfg->mempool, cfg->headers_to_free, cheader);
6479 * Some of these comments may well be out-of-date.
6480 * Design decisions: we do a single pass over the IL code (and we do bblock
6481 * splitting/merging in the few cases when it's required: a back jump to an IL
6482 * address that was not already seen as bblock starting point).
6483 * Code is validated as we go (full verification is still better left to metadata/verify.c).
6484 * Complex operations are decomposed in simpler ones right away. We need to let the
6485 * arch-specific code peek and poke inside this process somehow (except when the
6486 * optimizations can take advantage of the full semantic info of coarse opcodes).
6487 * All the opcodes of the form opcode.s are 'normalized' to opcode.
6488 * MonoInst->opcode initially is the IL opcode or some simplification of that
6489 * (OP_LOAD, OP_STORE). The arch-specific code may rearrange it to an arch-specific
6490 * opcode with value bigger than OP_LAST.
6491 * At this point the IR can be handed over to an interpreter, a dumb code generator
6492 * or to the optimizing code generator that will translate it to SSA form.
6494 * Profiling directed optimizations.
6495 * We may compile by default with few or no optimizations and instrument the code
6496 * or the user may indicate what methods to optimize the most either in a config file
6497 * or through repeated runs where the compiler applies offline the optimizations to
6498 * each method and then decides if it was worth it.
6501 #define CHECK_TYPE(ins) if (!(ins)->type) UNVERIFIED
6502 #define CHECK_STACK(num) if ((sp - stack_start) < (num)) UNVERIFIED
6503 #define CHECK_STACK_OVF(num) if (((sp - stack_start) + (num)) > header->max_stack) UNVERIFIED
6504 #define CHECK_ARG(num) if ((unsigned)(num) >= (unsigned)num_args) UNVERIFIED
6505 #define CHECK_LOCAL(num) if ((unsigned)(num) >= (unsigned)header->num_locals) UNVERIFIED
6506 #define CHECK_OPSIZE(size) if (ip + size > end) UNVERIFIED
6507 #define CHECK_UNVERIFIABLE(cfg) if (cfg->unverifiable) UNVERIFIED
6508 #define CHECK_TYPELOAD(klass) if (!(klass) || mono_class_has_failure (klass)) TYPE_LOAD_ERROR ((klass))
6510 /* offset from br.s -> br like opcodes */
6511 #define BIG_BRANCH_OFFSET 13
6514 ip_in_bb (MonoCompile *cfg, MonoBasicBlock *bb, const guint8* ip)
6516 MonoBasicBlock *b = cfg->cil_offset_to_bb [ip - cfg->cil_start];
6518 return b == NULL || b == bb;
6522 get_basic_blocks (MonoCompile *cfg, MonoMethodHeader* header, guint real_offset, unsigned char *start, unsigned char *end, unsigned char **pos)
6524 unsigned char *ip = start;
6525 unsigned char *target;
6528 MonoBasicBlock *bblock;
6529 const MonoOpcode *opcode;
6532 cli_addr = ip - start;
6533 i = mono_opcode_value ((const guint8 **)&ip, end);
6536 opcode = &mono_opcodes [i];
6537 switch (opcode->argument) {
6538 case MonoInlineNone:
6541 case MonoInlineString:
6542 case MonoInlineType:
6543 case MonoInlineField:
6544 case MonoInlineMethod:
6547 case MonoShortInlineR:
6554 case MonoShortInlineVar:
6555 case MonoShortInlineI:
6558 case MonoShortInlineBrTarget:
6559 target = start + cli_addr + 2 + (signed char)ip [1];
6560 GET_BBLOCK (cfg, bblock, target);
6563 GET_BBLOCK (cfg, bblock, ip);
6565 case MonoInlineBrTarget:
6566 target = start + cli_addr + 5 + (gint32)read32 (ip + 1);
6567 GET_BBLOCK (cfg, bblock, target);
6570 GET_BBLOCK (cfg, bblock, ip);
6572 case MonoInlineSwitch: {
6573 guint32 n = read32 (ip + 1);
6576 cli_addr += 5 + 4 * n;
6577 target = start + cli_addr;
6578 GET_BBLOCK (cfg, bblock, target);
6580 for (j = 0; j < n; ++j) {
6581 target = start + cli_addr + (gint32)read32 (ip);
6582 GET_BBLOCK (cfg, bblock, target);
6592 g_assert_not_reached ();
6595 if (i == CEE_THROW) {
6596 unsigned char *bb_start = ip - 1;
6598 /* Find the start of the bblock containing the throw */
6600 while ((bb_start >= start) && !bblock) {
6601 bblock = cfg->cil_offset_to_bb [(bb_start) - start];
6605 bblock->out_of_line = 1;
6615 static inline MonoMethod *
6616 mini_get_method_allow_open (MonoMethod *m, guint32 token, MonoClass *klass, MonoGenericContext *context, MonoError *error)
6622 if (m->wrapper_type != MONO_WRAPPER_NONE) {
6623 method = (MonoMethod *)mono_method_get_wrapper_data (m, token);
6625 method = mono_class_inflate_generic_method_checked (method, context, error);
6628 method = mono_get_method_checked (m->klass->image, token, klass, context, error);
6634 static inline MonoMethod *
6635 mini_get_method (MonoCompile *cfg, MonoMethod *m, guint32 token, MonoClass *klass, MonoGenericContext *context)
6638 MonoMethod *method = mini_get_method_allow_open (m, token, klass, context, cfg ? &cfg->error : &error);
6640 if (method && cfg && !cfg->gshared && mono_class_is_open_constructed_type (&method->klass->byval_arg)) {
6641 mono_error_set_bad_image (&cfg->error, cfg->method->klass->image, "Method with open type while not compiling gshared");
6645 if (!method && !cfg)
6646 mono_error_cleanup (&error); /* FIXME don't swallow the error */
6652 mini_get_class (MonoMethod *method, guint32 token, MonoGenericContext *context)
6657 if (method->wrapper_type != MONO_WRAPPER_NONE) {
6658 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
6660 klass = mono_class_inflate_generic_class_checked (klass, context, &error);
6661 mono_error_cleanup (&error); /* FIXME don't swallow the error */
6664 klass = mono_class_get_and_inflate_typespec_checked (method->klass->image, token, context, &error);
6665 mono_error_cleanup (&error); /* FIXME don't swallow the error */
6668 mono_class_init (klass);
6672 static inline MonoMethodSignature*
6673 mini_get_signature (MonoMethod *method, guint32 token, MonoGenericContext *context, MonoError *error)
6675 MonoMethodSignature *fsig;
6678 if (method->wrapper_type != MONO_WRAPPER_NONE) {
6679 fsig = (MonoMethodSignature *)mono_method_get_wrapper_data (method, token);
6681 fsig = mono_metadata_parse_signature_checked (method->klass->image, token, error);
6682 return_val_if_nok (error, NULL);
6685 fsig = mono_inflate_generic_signature(fsig, context, error);
6691 throw_exception (void)
6693 static MonoMethod *method = NULL;
6696 MonoSecurityManager *secman = mono_security_manager_get_methods ();
6697 method = mono_class_get_method_from_name (secman->securitymanager, "ThrowException", 1);
6704 emit_throw_exception (MonoCompile *cfg, MonoException *ex)
6706 MonoMethod *thrower = throw_exception ();
6709 EMIT_NEW_PCONST (cfg, args [0], ex);
6710 mono_emit_method_call (cfg, thrower, args, NULL);
6714 * Return the original method is a wrapper is specified. We can only access
6715 * the custom attributes from the original method.
6718 get_original_method (MonoMethod *method)
6720 if (method->wrapper_type == MONO_WRAPPER_NONE)
6723 /* native code (which is like Critical) can call any managed method XXX FIXME XXX to validate all usages */
6724 if (method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED)
6727 /* in other cases we need to find the original method */
6728 return mono_marshal_method_from_wrapper (method);
6732 ensure_method_is_allowed_to_access_field (MonoCompile *cfg, MonoMethod *caller, MonoClassField *field)
6734 /* we can't get the coreclr security level on wrappers since they don't have the attributes */
6735 MonoException *ex = mono_security_core_clr_is_field_access_allowed (get_original_method (caller), field);
6737 emit_throw_exception (cfg, ex);
6741 ensure_method_is_allowed_to_call_method (MonoCompile *cfg, MonoMethod *caller, MonoMethod *callee)
6743 /* we can't get the coreclr security level on wrappers since they don't have the attributes */
6744 MonoException *ex = mono_security_core_clr_is_call_allowed (get_original_method (caller), callee);
6746 emit_throw_exception (cfg, ex);
6750 * Check that the IL instructions at ip are the array initialization
6751 * sequence and return the pointer to the data and the size.
6754 initialize_array_data (MonoMethod *method, gboolean aot, unsigned char *ip, MonoClass *klass, guint32 len, int *out_size, guint32 *out_field_token)
6757 * newarr[System.Int32]
6759 * ldtoken field valuetype ...
6760 * call void class [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(class [mscorlib]System.Array, valuetype [mscorlib]System.RuntimeFieldHandle)
6762 if (ip [0] == CEE_DUP && ip [1] == CEE_LDTOKEN && ip [5] == 0x4 && ip [6] == CEE_CALL) {
6764 guint32 token = read32 (ip + 7);
6765 guint32 field_token = read32 (ip + 2);
6766 guint32 field_index = field_token & 0xffffff;
6768 const char *data_ptr;
6770 MonoMethod *cmethod;
6771 MonoClass *dummy_class;
6772 MonoClassField *field = mono_field_from_token_checked (method->klass->image, field_token, &dummy_class, NULL, &error);
6776 mono_error_cleanup (&error); /* FIXME don't swallow the error */
6780 *out_field_token = field_token;
6782 cmethod = mini_get_method (NULL, method, token, NULL, NULL);
6785 if (strcmp (cmethod->name, "InitializeArray") || strcmp (cmethod->klass->name, "RuntimeHelpers") || cmethod->klass->image != mono_defaults.corlib)
6787 switch (mini_get_underlying_type (&klass->byval_arg)->type) {
6791 /* we need to swap on big endian, so punt. Should we handle R4 and R8 as well? */
6792 #if TARGET_BYTE_ORDER == G_LITTLE_ENDIAN
6809 if (size > mono_type_size (field->type, &dummy_align))
6812 /*g_print ("optimized in %s: size: %d, numelems: %d\n", method->name, size, newarr->inst_newa_len->inst_c0);*/
6813 if (!image_is_dynamic (method->klass->image)) {
6814 field_index = read32 (ip + 2) & 0xffffff;
6815 mono_metadata_field_info (method->klass->image, field_index - 1, NULL, &rva, NULL);
6816 data_ptr = mono_image_rva_map (method->klass->image, rva);
6817 /*g_print ("field: 0x%08x, rva: %d, rva_ptr: %p\n", read32 (ip + 2), rva, data_ptr);*/
6818 /* for aot code we do the lookup on load */
6819 if (aot && data_ptr)
6820 return (const char *)GUINT_TO_POINTER (rva);
6822 /*FIXME is it possible to AOT a SRE assembly not meant to be saved? */
6824 data_ptr = mono_field_get_data (field);
6832 set_exception_type_from_invalid_il (MonoCompile *cfg, MonoMethod *method, unsigned char *ip)
6835 char *method_fname = mono_method_full_name (method, TRUE);
6837 MonoMethodHeader *header = mono_method_get_header_checked (method, &error);
6840 method_code = g_strdup_printf ("could not parse method body due to %s", mono_error_get_message (&error));
6841 mono_error_cleanup (&error);
6842 } else if (header->code_size == 0)
6843 method_code = g_strdup ("method body is empty.");
6845 method_code = mono_disasm_code_one (NULL, method, ip, NULL);
6846 mono_cfg_set_exception_invalid_program (cfg, g_strdup_printf ("Invalid IL code in %s: %s\n", method_fname, method_code));
6847 g_free (method_fname);
6848 g_free (method_code);
6849 cfg->headers_to_free = g_slist_prepend_mempool (cfg->mempool, cfg->headers_to_free, header);
6853 emit_stloc_ir (MonoCompile *cfg, MonoInst **sp, MonoMethodHeader *header, int n)
6856 guint32 opcode = mono_type_to_regmove (cfg, header->locals [n]);
6857 if ((opcode == OP_MOVE) && cfg->cbb->last_ins == sp [0] &&
6858 ((sp [0]->opcode == OP_ICONST) || (sp [0]->opcode == OP_I8CONST))) {
6859 /* Optimize reg-reg moves away */
6861 * Can't optimize other opcodes, since sp[0] might point to
6862 * the last ins of a decomposed opcode.
6864 sp [0]->dreg = (cfg)->locals [n]->dreg;
6866 EMIT_NEW_LOCSTORE (cfg, ins, n, *sp);
6871 * ldloca inhibits many optimizations so try to get rid of it in common
6874 static inline unsigned char *
6875 emit_optimized_ldloca_ir (MonoCompile *cfg, unsigned char *ip, unsigned char *end, int size)
6885 local = read16 (ip + 2);
6889 if (ip + 6 < end && (ip [0] == CEE_PREFIX1) && (ip [1] == CEE_INITOBJ) && ip_in_bb (cfg, cfg->cbb, ip + 1)) {
6890 /* From the INITOBJ case */
6891 token = read32 (ip + 2);
6892 klass = mini_get_class (cfg->current_method, token, cfg->generic_context);
6893 CHECK_TYPELOAD (klass);
6894 type = mini_get_underlying_type (&klass->byval_arg);
6895 emit_init_local (cfg, local, type, TRUE);
6903 emit_llvmonly_virtual_call (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, int context_used, MonoInst **sp)
6905 MonoInst *icall_args [16];
6906 MonoInst *call_target, *ins, *vtable_ins;
6907 int arg_reg, this_reg, vtable_reg;
6908 gboolean is_iface = mono_class_is_interface (cmethod->klass);
6909 gboolean is_gsharedvt = cfg->gsharedvt && mini_is_gsharedvt_variable_signature (fsig);
6910 gboolean variant_iface = FALSE;
6913 gboolean special_array_interface = cmethod->klass->is_array_special_interface;
6916 * In llvm-only mode, vtables contain function descriptors instead of
6917 * method addresses/trampolines.
6919 MONO_EMIT_NULL_CHECK (cfg, sp [0]->dreg);
6922 slot = mono_method_get_imt_slot (cmethod);
6924 slot = mono_method_get_vtable_index (cmethod);
6926 this_reg = sp [0]->dreg;
6928 if (is_iface && mono_class_has_variant_generic_params (cmethod->klass))
6929 variant_iface = TRUE;
6931 if (!fsig->generic_param_count && !is_iface && !is_gsharedvt) {
6933 * The simplest case, a normal virtual call.
6935 int slot_reg = alloc_preg (cfg);
6936 int addr_reg = alloc_preg (cfg);
6937 int arg_reg = alloc_preg (cfg);
6938 MonoBasicBlock *non_null_bb;
6940 vtable_reg = alloc_preg (cfg);
6941 EMIT_NEW_LOAD_MEMBASE (cfg, vtable_ins, OP_LOAD_MEMBASE, vtable_reg, this_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
6942 offset = MONO_STRUCT_OFFSET (MonoVTable, vtable) + (slot * SIZEOF_VOID_P);
6944 /* Load the vtable slot, which contains a function descriptor. */
6945 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, slot_reg, vtable_reg, offset);
6947 NEW_BBLOCK (cfg, non_null_bb);
6949 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, slot_reg, 0);
6950 cfg->cbb->last_ins->flags |= MONO_INST_LIKELY;
6951 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, non_null_bb);
6954 // FIXME: Make the wrapper use the preserveall cconv
6955 // FIXME: Use one icall per slot for small slot numbers ?
6956 icall_args [0] = vtable_ins;
6957 EMIT_NEW_ICONST (cfg, icall_args [1], slot);
6958 /* Make the icall return the vtable slot value to save some code space */
6959 ins = mono_emit_jit_icall (cfg, mono_init_vtable_slot, icall_args);
6960 ins->dreg = slot_reg;
6961 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, non_null_bb);
6964 MONO_START_BB (cfg, non_null_bb);
6965 /* Load the address + arg from the vtable slot */
6966 EMIT_NEW_LOAD_MEMBASE (cfg, call_target, OP_LOAD_MEMBASE, addr_reg, slot_reg, 0);
6967 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, arg_reg, slot_reg, SIZEOF_VOID_P);
6969 return emit_extra_arg_calli (cfg, fsig, sp, arg_reg, call_target);
6972 if (!fsig->generic_param_count && is_iface && !variant_iface && !is_gsharedvt && !special_array_interface) {
6974 * A simple interface call
6976 * We make a call through an imt slot to obtain the function descriptor we need to call.
6977 * The imt slot contains a function descriptor for a runtime function + arg.
6979 int slot_reg = alloc_preg (cfg);
6980 int addr_reg = alloc_preg (cfg);
6981 int arg_reg = alloc_preg (cfg);
6982 MonoInst *thunk_addr_ins, *thunk_arg_ins, *ftndesc_ins;
6984 vtable_reg = alloc_preg (cfg);
6985 EMIT_NEW_LOAD_MEMBASE (cfg, vtable_ins, OP_LOAD_MEMBASE, vtable_reg, this_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
6986 offset = ((gint32)slot - MONO_IMT_SIZE) * SIZEOF_VOID_P;
6989 * The slot is already initialized when the vtable is created so there is no need
6993 /* Load the imt slot, which contains a function descriptor. */
6994 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, slot_reg, vtable_reg, offset);
6996 /* Load the address + arg of the imt thunk from the imt slot */
6997 EMIT_NEW_LOAD_MEMBASE (cfg, thunk_addr_ins, OP_LOAD_MEMBASE, addr_reg, slot_reg, 0);
6998 EMIT_NEW_LOAD_MEMBASE (cfg, thunk_arg_ins, OP_LOAD_MEMBASE, arg_reg, slot_reg, SIZEOF_VOID_P);
7000 * IMT thunks in llvm-only mode are C functions which take an info argument
7001 * plus the imt method and return the ftndesc to call.
7003 icall_args [0] = thunk_arg_ins;
7004 icall_args [1] = emit_get_rgctx_method (cfg, context_used,
7005 cmethod, MONO_RGCTX_INFO_METHOD);
7006 ftndesc_ins = mini_emit_calli (cfg, helper_sig_llvmonly_imt_trampoline, icall_args, thunk_addr_ins, NULL, NULL);
7008 return emit_llvmonly_calli (cfg, fsig, sp, ftndesc_ins);
7011 if ((fsig->generic_param_count || variant_iface || special_array_interface) && !is_gsharedvt) {
7013 * This is similar to the interface case, the vtable slot points to an imt thunk which is
7014 * dynamically extended as more instantiations are discovered.
7015 * This handles generic virtual methods both on classes and interfaces.
7017 int slot_reg = alloc_preg (cfg);
7018 int addr_reg = alloc_preg (cfg);
7019 int arg_reg = alloc_preg (cfg);
7020 int ftndesc_reg = alloc_preg (cfg);
7021 MonoInst *thunk_addr_ins, *thunk_arg_ins, *ftndesc_ins;
7022 MonoBasicBlock *slowpath_bb, *end_bb;
7024 NEW_BBLOCK (cfg, slowpath_bb);
7025 NEW_BBLOCK (cfg, end_bb);
7027 vtable_reg = alloc_preg (cfg);
7028 EMIT_NEW_LOAD_MEMBASE (cfg, vtable_ins, OP_LOAD_MEMBASE, vtable_reg, this_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
7030 offset = ((gint32)slot - MONO_IMT_SIZE) * SIZEOF_VOID_P;
7032 offset = MONO_STRUCT_OFFSET (MonoVTable, vtable) + (slot * SIZEOF_VOID_P);
7034 /* Load the slot, which contains a function descriptor. */
7035 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, slot_reg, vtable_reg, offset);
7037 /* These slots are not initialized, so fall back to the slow path until they are initialized */
7038 /* That happens when mono_method_add_generic_virtual_invocation () creates an IMT thunk */
7039 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, slot_reg, 0);
7040 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, slowpath_bb);
7043 /* Same as with iface calls */
7044 EMIT_NEW_LOAD_MEMBASE (cfg, thunk_addr_ins, OP_LOAD_MEMBASE, addr_reg, slot_reg, 0);
7045 EMIT_NEW_LOAD_MEMBASE (cfg, thunk_arg_ins, OP_LOAD_MEMBASE, arg_reg, slot_reg, SIZEOF_VOID_P);
7046 icall_args [0] = thunk_arg_ins;
7047 icall_args [1] = emit_get_rgctx_method (cfg, context_used,
7048 cmethod, MONO_RGCTX_INFO_METHOD);
7049 ftndesc_ins = mini_emit_calli (cfg, helper_sig_llvmonly_imt_trampoline, icall_args, thunk_addr_ins, NULL, NULL);
7050 ftndesc_ins->dreg = ftndesc_reg;
7052 * Unlike normal iface calls, these imt thunks can return NULL, i.e. when they are passed an instantiation
7053 * they don't know about yet. Fall back to the slowpath in that case.
7055 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, ftndesc_reg, 0);
7056 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, slowpath_bb);
7058 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
7061 MONO_START_BB (cfg, slowpath_bb);
7062 icall_args [0] = vtable_ins;
7063 EMIT_NEW_ICONST (cfg, icall_args [1], slot);
7064 icall_args [2] = emit_get_rgctx_method (cfg, context_used,
7065 cmethod, MONO_RGCTX_INFO_METHOD);
7067 ftndesc_ins = mono_emit_jit_icall (cfg, mono_resolve_generic_virtual_iface_call, icall_args);
7069 ftndesc_ins = mono_emit_jit_icall (cfg, mono_resolve_generic_virtual_call, icall_args);
7070 ftndesc_ins->dreg = ftndesc_reg;
7071 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
7074 MONO_START_BB (cfg, end_bb);
7075 return emit_llvmonly_calli (cfg, fsig, sp, ftndesc_ins);
7079 * Non-optimized cases
7081 icall_args [0] = sp [0];
7082 EMIT_NEW_ICONST (cfg, icall_args [1], slot);
7084 icall_args [2] = emit_get_rgctx_method (cfg, context_used,
7085 cmethod, MONO_RGCTX_INFO_METHOD);
7087 arg_reg = alloc_preg (cfg);
7088 MONO_EMIT_NEW_PCONST (cfg, arg_reg, NULL);
7089 EMIT_NEW_VARLOADA_VREG (cfg, icall_args [3], arg_reg, &mono_defaults.int_class->byval_arg);
7091 g_assert (is_gsharedvt);
7093 call_target = mono_emit_jit_icall (cfg, mono_resolve_iface_call_gsharedvt, icall_args);
7095 call_target = mono_emit_jit_icall (cfg, mono_resolve_vcall_gsharedvt, icall_args);
7098 * Pass the extra argument even if the callee doesn't receive it, most
7099 * calling conventions allow this.
7101 return emit_extra_arg_calli (cfg, fsig, sp, arg_reg, call_target);
7105 is_exception_class (MonoClass *klass)
7108 if (klass == mono_defaults.exception_class)
7110 klass = klass->parent;
7116 * is_jit_optimizer_disabled:
7118 * Determine whenever M's assembly has a DebuggableAttribute with the
7119 * IsJITOptimizerDisabled flag set.
7122 is_jit_optimizer_disabled (MonoMethod *m)
7125 MonoAssembly *ass = m->klass->image->assembly;
7126 MonoCustomAttrInfo* attrs;
7129 gboolean val = FALSE;
7132 if (ass->jit_optimizer_disabled_inited)
7133 return ass->jit_optimizer_disabled;
7135 klass = mono_class_try_get_debuggable_attribute_class ();
7139 ass->jit_optimizer_disabled = FALSE;
7140 mono_memory_barrier ();
7141 ass->jit_optimizer_disabled_inited = TRUE;
7145 attrs = mono_custom_attrs_from_assembly_checked (ass, FALSE, &error);
7146 mono_error_cleanup (&error); /* FIXME don't swallow the error */
7148 for (i = 0; i < attrs->num_attrs; ++i) {
7149 MonoCustomAttrEntry *attr = &attrs->attrs [i];
7151 MonoMethodSignature *sig;
7153 if (!attr->ctor || attr->ctor->klass != klass)
7155 /* Decode the attribute. See reflection.c */
7156 p = (const char*)attr->data;
7157 g_assert (read16 (p) == 0x0001);
7160 // FIXME: Support named parameters
7161 sig = mono_method_signature (attr->ctor);
7162 if (sig->param_count != 2 || sig->params [0]->type != MONO_TYPE_BOOLEAN || sig->params [1]->type != MONO_TYPE_BOOLEAN)
7164 /* Two boolean arguments */
7168 mono_custom_attrs_free (attrs);
7171 ass->jit_optimizer_disabled = val;
7172 mono_memory_barrier ();
7173 ass->jit_optimizer_disabled_inited = TRUE;
7179 is_supported_tail_call (MonoCompile *cfg, MonoMethod *method, MonoMethod *cmethod, MonoMethodSignature *fsig, int call_opcode)
7181 gboolean supported_tail_call;
7184 supported_tail_call = mono_arch_tail_call_supported (cfg, mono_method_signature (method), mono_method_signature (cmethod));
7186 for (i = 0; i < fsig->param_count; ++i) {
7187 if (fsig->params [i]->byref || fsig->params [i]->type == MONO_TYPE_PTR || fsig->params [i]->type == MONO_TYPE_FNPTR)
7188 /* These can point to the current method's stack */
7189 supported_tail_call = FALSE;
7191 if (fsig->hasthis && cmethod->klass->valuetype)
7192 /* this might point to the current method's stack */
7193 supported_tail_call = FALSE;
7194 if (cmethod->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)
7195 supported_tail_call = FALSE;
7196 if (cfg->method->save_lmf)
7197 supported_tail_call = FALSE;
7198 if (cmethod->wrapper_type && cmethod->wrapper_type != MONO_WRAPPER_DYNAMIC_METHOD)
7199 supported_tail_call = FALSE;
7200 if (call_opcode != CEE_CALL)
7201 supported_tail_call = FALSE;
7203 /* Debugging support */
7205 if (supported_tail_call) {
7206 if (!mono_debug_count ())
7207 supported_tail_call = FALSE;
7211 return supported_tail_call;
7217 * Handle calls made to ctors from NEWOBJ opcodes.
7220 handle_ctor_call (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, int context_used,
7221 MonoInst **sp, guint8 *ip, int *inline_costs)
7223 MonoInst *vtable_arg = NULL, *callvirt_this_arg = NULL, *ins;
7225 if (cmethod->klass->valuetype && mono_class_generic_sharing_enabled (cmethod->klass) &&
7226 mono_method_is_generic_sharable (cmethod, TRUE)) {
7227 if (cmethod->is_inflated && mono_method_get_context (cmethod)->method_inst) {
7228 mono_class_vtable (cfg->domain, cmethod->klass);
7229 CHECK_TYPELOAD (cmethod->klass);
7231 vtable_arg = emit_get_rgctx_method (cfg, context_used,
7232 cmethod, MONO_RGCTX_INFO_METHOD_RGCTX);
7235 vtable_arg = mini_emit_get_rgctx_klass (cfg, context_used,
7236 cmethod->klass, MONO_RGCTX_INFO_VTABLE);
7238 MonoVTable *vtable = mono_class_vtable (cfg->domain, cmethod->klass);
7240 CHECK_TYPELOAD (cmethod->klass);
7241 EMIT_NEW_VTABLECONST (cfg, vtable_arg, vtable);
7246 /* Avoid virtual calls to ctors if possible */
7247 if (mono_class_is_marshalbyref (cmethod->klass))
7248 callvirt_this_arg = sp [0];
7250 if (cmethod && (cfg->opt & MONO_OPT_INTRINS) && (ins = mini_emit_inst_for_ctor (cfg, cmethod, fsig, sp))) {
7251 g_assert (MONO_TYPE_IS_VOID (fsig->ret));
7252 CHECK_CFG_EXCEPTION;
7253 } else if ((cfg->opt & MONO_OPT_INLINE) && cmethod && !context_used && !vtable_arg &&
7254 mono_method_check_inlining (cfg, cmethod) &&
7255 !mono_class_is_subclass_of (cmethod->klass, mono_defaults.exception_class, FALSE)) {
7258 if ((costs = inline_method (cfg, cmethod, fsig, sp, ip, cfg->real_offset, FALSE))) {
7259 cfg->real_offset += 5;
7261 *inline_costs += costs - 5;
7263 INLINE_FAILURE ("inline failure");
7264 // FIXME-VT: Clean this up
7265 if (cfg->gsharedvt && mini_is_gsharedvt_signature (fsig))
7266 GSHAREDVT_FAILURE(*ip);
7267 mono_emit_method_call_full (cfg, cmethod, fsig, FALSE, sp, callvirt_this_arg, NULL, NULL);
7269 } else if (cfg->gsharedvt && mini_is_gsharedvt_signature (fsig)) {
7272 addr = emit_get_rgctx_gsharedvt_call (cfg, context_used, fsig, cmethod, MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE);
7274 if (cfg->llvm_only) {
7275 // FIXME: Avoid initializing vtable_arg
7276 emit_llvmonly_calli (cfg, fsig, sp, addr);
7278 mini_emit_calli (cfg, fsig, sp, addr, NULL, vtable_arg);
7280 } else if (context_used &&
7281 ((!mono_method_is_generic_sharable_full (cmethod, TRUE, FALSE, FALSE) ||
7282 !mono_class_generic_sharing_enabled (cmethod->klass)) || cfg->gsharedvt)) {
7283 MonoInst *cmethod_addr;
7285 /* Generic calls made out of gsharedvt methods cannot be patched, so use an indirect call */
7287 if (cfg->llvm_only) {
7288 MonoInst *addr = emit_get_rgctx_method (cfg, context_used, cmethod,
7289 MONO_RGCTX_INFO_GENERIC_METHOD_CODE);
7290 emit_llvmonly_calli (cfg, fsig, sp, addr);
7292 cmethod_addr = emit_get_rgctx_method (cfg, context_used,
7293 cmethod, MONO_RGCTX_INFO_GENERIC_METHOD_CODE);
7295 mini_emit_calli (cfg, fsig, sp, cmethod_addr, NULL, vtable_arg);
7298 INLINE_FAILURE ("ctor call");
7299 ins = mono_emit_method_call_full (cfg, cmethod, fsig, FALSE, sp,
7300 callvirt_this_arg, NULL, vtable_arg);
7307 emit_setret (MonoCompile *cfg, MonoInst *val)
7309 MonoType *ret_type = mini_get_underlying_type (mono_method_signature (cfg->method)->ret);
7312 if (mini_type_to_stind (cfg, ret_type) == CEE_STOBJ) {
7315 if (!cfg->vret_addr) {
7316 EMIT_NEW_VARSTORE (cfg, ins, cfg->ret, ret_type, val);
7318 EMIT_NEW_RETLOADA (cfg, ret_addr);
7320 EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STOREV_MEMBASE, ret_addr->dreg, 0, val->dreg);
7321 ins->klass = mono_class_from_mono_type (ret_type);
7324 #ifdef MONO_ARCH_SOFT_FLOAT_FALLBACK
7325 if (COMPILE_SOFT_FLOAT (cfg) && !ret_type->byref && ret_type->type == MONO_TYPE_R4) {
7326 MonoInst *iargs [1];
7330 conv = mono_emit_jit_icall (cfg, mono_fload_r4_arg, iargs);
7331 mono_arch_emit_setret (cfg, cfg->method, conv);
7333 mono_arch_emit_setret (cfg, cfg->method, val);
7336 mono_arch_emit_setret (cfg, cfg->method, val);
7342 * mono_method_to_ir:
7344 * Translate the .net IL into linear IR.
7346 * @start_bblock: if not NULL, the starting basic block, used during inlining.
7347 * @end_bblock: if not NULL, the ending basic block, used during inlining.
7348 * @return_var: if not NULL, the place where the return value is stored, used during inlining.
7349 * @inline_args: if not NULL, contains the arguments to the inline call
7350 * @inline_offset: if not zero, the real offset from the inline call, or zero otherwise.
7351 * @is_virtual_call: whether this method is being called as a result of a call to callvirt
7353 * This method is used to turn ECMA IL into Mono's internal Linear IR
7354 * reprensetation. It is used both for entire methods, as well as
7355 * inlining existing methods. In the former case, the @start_bblock,
7356 * @end_bblock, @return_var, @inline_args are all set to NULL, and the
7357 * inline_offset is set to zero.
7359 * Returns: the inline cost, or -1 if there was an error processing this method.
7362 mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_bblock, MonoBasicBlock *end_bblock,
7363 MonoInst *return_var, MonoInst **inline_args,
7364 guint inline_offset, gboolean is_virtual_call)
7367 MonoInst *ins, **sp, **stack_start;
7368 MonoBasicBlock *tblock = NULL, *init_localsbb = NULL;
7369 MonoSimpleBasicBlock *bb = NULL, *original_bb = NULL;
7370 MonoMethod *cmethod, *method_definition;
7371 MonoInst **arg_array;
7372 MonoMethodHeader *header;
7374 guint32 token, ins_flag;
7376 MonoClass *constrained_class = NULL;
7377 unsigned char *ip, *end, *target, *err_pos;
7378 MonoMethodSignature *sig;
7379 MonoGenericContext *generic_context = NULL;
7380 MonoGenericContainer *generic_container = NULL;
7381 MonoType **param_types;
7382 int i, n, start_new_bblock, dreg;
7383 int num_calls = 0, inline_costs = 0;
7384 int breakpoint_id = 0;
7386 GSList *class_inits = NULL;
7387 gboolean dont_verify, dont_verify_stloc, readonly = FALSE;
7389 gboolean init_locals, seq_points, skip_dead_blocks;
7390 gboolean sym_seq_points = FALSE;
7391 MonoDebugMethodInfo *minfo;
7392 MonoBitSet *seq_point_locs = NULL;
7393 MonoBitSet *seq_point_set_locs = NULL;
7395 cfg->disable_inline = is_jit_optimizer_disabled (method);
7397 /* serialization and xdomain stuff may need access to private fields and methods */
7398 dont_verify = method->klass->image->assembly->corlib_internal? TRUE: FALSE;
7399 dont_verify |= method->wrapper_type == MONO_WRAPPER_XDOMAIN_INVOKE;
7400 dont_verify |= method->wrapper_type == MONO_WRAPPER_XDOMAIN_DISPATCH;
7401 dont_verify |= method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE; /* bug #77896 */
7402 dont_verify |= method->wrapper_type == MONO_WRAPPER_COMINTEROP;
7403 dont_verify |= method->wrapper_type == MONO_WRAPPER_COMINTEROP_INVOKE;
7405 /* still some type unsafety issues in marshal wrappers... (unknown is PtrToStructure) */
7406 dont_verify_stloc = method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE;
7407 dont_verify_stloc |= method->wrapper_type == MONO_WRAPPER_UNKNOWN;
7408 dont_verify_stloc |= method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED;
7409 dont_verify_stloc |= method->wrapper_type == MONO_WRAPPER_STELEMREF;
7411 image = method->klass->image;
7412 header = mono_method_get_header_checked (method, &cfg->error);
7414 mono_cfg_set_exception (cfg, MONO_EXCEPTION_MONO_ERROR);
7415 goto exception_exit;
7417 cfg->headers_to_free = g_slist_prepend_mempool (cfg->mempool, cfg->headers_to_free, header);
7420 generic_container = mono_method_get_generic_container (method);
7421 sig = mono_method_signature (method);
7422 num_args = sig->hasthis + sig->param_count;
7423 ip = (unsigned char*)header->code;
7424 cfg->cil_start = ip;
7425 end = ip + header->code_size;
7426 cfg->stat_cil_code_size += header->code_size;
7428 seq_points = cfg->gen_seq_points && cfg->method == method;
7430 if (method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED) {
7431 /* We could hit a seq point before attaching to the JIT (#8338) */
7435 if (cfg->gen_sdb_seq_points && cfg->method == method) {
7436 minfo = mono_debug_lookup_method (method);
7438 MonoSymSeqPoint *sps;
7439 int i, n_il_offsets;
7441 mono_debug_get_seq_points (minfo, NULL, NULL, NULL, &sps, &n_il_offsets);
7442 seq_point_locs = mono_bitset_mem_new (mono_mempool_alloc0 (cfg->mempool, mono_bitset_alloc_size (header->code_size, 0)), header->code_size, 0);
7443 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);
7444 sym_seq_points = TRUE;
7445 for (i = 0; i < n_il_offsets; ++i) {
7446 if (sps [i].il_offset < header->code_size)
7447 mono_bitset_set_fast (seq_point_locs, sps [i].il_offset);
7451 MonoDebugMethodAsyncInfo* asyncMethod = mono_debug_lookup_method_async_debug_info (method);
7453 for (i = 0; asyncMethod != NULL && i < asyncMethod->num_awaits; i++)
7455 mono_bitset_set_fast (seq_point_locs, asyncMethod->resume_offsets[i]);
7456 mono_bitset_set_fast (seq_point_locs, asyncMethod->yield_offsets[i]);
7458 mono_debug_free_method_async_debug_info (asyncMethod);
7460 } else if (!method->wrapper_type && !method->dynamic && mono_debug_image_has_debug_info (method->klass->image)) {
7461 /* Methods without line number info like auto-generated property accessors */
7462 seq_point_locs = mono_bitset_mem_new (mono_mempool_alloc0 (cfg->mempool, mono_bitset_alloc_size (header->code_size, 0)), header->code_size, 0);
7463 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);
7464 sym_seq_points = TRUE;
7469 * Methods without init_locals set could cause asserts in various passes
7470 * (#497220). To work around this, we emit dummy initialization opcodes
7471 * (OP_DUMMY_ICONST etc.) which generate no code. These are only supported
7472 * on some platforms.
7474 if ((cfg->opt & MONO_OPT_UNSAFE) && cfg->backend->have_dummy_init)
7475 init_locals = header->init_locals;
7479 method_definition = method;
7480 while (method_definition->is_inflated) {
7481 MonoMethodInflated *imethod = (MonoMethodInflated *) method_definition;
7482 method_definition = imethod->declaring;
7485 /* SkipVerification is not allowed if core-clr is enabled */
7486 if (!dont_verify && mini_assembly_can_skip_verification (cfg->domain, method)) {
7488 dont_verify_stloc = TRUE;
7491 if (sig->is_inflated)
7492 generic_context = mono_method_get_context (method);
7493 else if (generic_container)
7494 generic_context = &generic_container->context;
7495 cfg->generic_context = generic_context;
7498 g_assert (!sig->has_type_parameters);
7500 if (sig->generic_param_count && method->wrapper_type == MONO_WRAPPER_NONE) {
7501 g_assert (method->is_inflated);
7502 g_assert (mono_method_get_context (method)->method_inst);
7504 if (method->is_inflated && mono_method_get_context (method)->method_inst)
7505 g_assert (sig->generic_param_count);
7507 if (cfg->method == method) {
7508 cfg->real_offset = 0;
7510 cfg->real_offset = inline_offset;
7513 cfg->cil_offset_to_bb = (MonoBasicBlock **)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoBasicBlock*) * header->code_size);
7514 cfg->cil_offset_to_bb_len = header->code_size;
7516 cfg->current_method = method;
7518 if (cfg->verbose_level > 2)
7519 printf ("method to IR %s\n", mono_method_full_name (method, TRUE));
7521 param_types = (MonoType **)mono_mempool_alloc (cfg->mempool, sizeof (MonoType*) * num_args);
7523 param_types [0] = method->klass->valuetype?&method->klass->this_arg:&method->klass->byval_arg;
7524 for (n = 0; n < sig->param_count; ++n)
7525 param_types [n + sig->hasthis] = sig->params [n];
7526 cfg->arg_types = param_types;
7528 cfg->dont_inline = g_list_prepend (cfg->dont_inline, method);
7529 if (cfg->method == method) {
7531 if (cfg->prof_options & MONO_PROFILE_INS_COVERAGE)
7532 cfg->coverage_info = mono_profiler_coverage_alloc (cfg->method, header->code_size);
7535 NEW_BBLOCK (cfg, start_bblock);
7536 cfg->bb_entry = start_bblock;
7537 start_bblock->cil_code = NULL;
7538 start_bblock->cil_length = 0;
7541 NEW_BBLOCK (cfg, end_bblock);
7542 cfg->bb_exit = end_bblock;
7543 end_bblock->cil_code = NULL;
7544 end_bblock->cil_length = 0;
7545 end_bblock->flags |= BB_INDIRECT_JUMP_TARGET;
7546 g_assert (cfg->num_bblocks == 2);
7548 arg_array = cfg->args;
7550 if (header->num_clauses) {
7551 cfg->spvars = g_hash_table_new (NULL, NULL);
7552 cfg->exvars = g_hash_table_new (NULL, NULL);
7554 /* handle exception clauses */
7555 for (i = 0; i < header->num_clauses; ++i) {
7556 MonoBasicBlock *try_bb;
7557 MonoExceptionClause *clause = &header->clauses [i];
7558 GET_BBLOCK (cfg, try_bb, ip + clause->try_offset);
7560 try_bb->real_offset = clause->try_offset;
7561 try_bb->try_start = TRUE;
7562 try_bb->region = ((i + 1) << 8) | clause->flags;
7563 GET_BBLOCK (cfg, tblock, ip + clause->handler_offset);
7564 tblock->real_offset = clause->handler_offset;
7565 tblock->flags |= BB_EXCEPTION_HANDLER;
7568 * Linking the try block with the EH block hinders inlining as we won't be able to
7569 * merge the bblocks from inlining and produce an artificial hole for no good reason.
7571 if (COMPILE_LLVM (cfg))
7572 link_bblock (cfg, try_bb, tblock);
7574 if (*(ip + clause->handler_offset) == CEE_POP)
7575 tblock->flags |= BB_EXCEPTION_DEAD_OBJ;
7577 if (clause->flags == MONO_EXCEPTION_CLAUSE_FINALLY ||
7578 clause->flags == MONO_EXCEPTION_CLAUSE_FILTER ||
7579 clause->flags == MONO_EXCEPTION_CLAUSE_FAULT) {
7580 MONO_INST_NEW (cfg, ins, OP_START_HANDLER);
7581 MONO_ADD_INS (tblock, ins);
7583 if (seq_points && clause->flags != MONO_EXCEPTION_CLAUSE_FINALLY && clause->flags != MONO_EXCEPTION_CLAUSE_FILTER) {
7584 /* finally clauses already have a seq point */
7585 /* seq points for filter clauses are emitted below */
7586 NEW_SEQ_POINT (cfg, ins, clause->handler_offset, TRUE);
7587 MONO_ADD_INS (tblock, ins);
7590 /* todo: is a fault block unsafe to optimize? */
7591 if (clause->flags == MONO_EXCEPTION_CLAUSE_FAULT)
7592 tblock->flags |= BB_EXCEPTION_UNSAFE;
7595 /*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);
7597 printf ("%s", mono_disasm_code_one (NULL, method, p, &p));
7599 /* catch and filter blocks get the exception object on the stack */
7600 if (clause->flags == MONO_EXCEPTION_CLAUSE_NONE ||
7601 clause->flags == MONO_EXCEPTION_CLAUSE_FILTER) {
7603 /* mostly like handle_stack_args (), but just sets the input args */
7604 /* printf ("handling clause at IL_%04x\n", clause->handler_offset); */
7605 tblock->in_scount = 1;
7606 tblock->in_stack = (MonoInst **)mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*));
7607 tblock->in_stack [0] = mono_create_exvar_for_offset (cfg, clause->handler_offset);
7611 #ifdef MONO_CONTEXT_SET_LLVM_EXC_REG
7612 /* The EH code passes in the exception in a register to both JITted and LLVM compiled code */
7613 if (!cfg->compile_llvm) {
7614 MONO_INST_NEW (cfg, ins, OP_GET_EX_OBJ);
7615 ins->dreg = tblock->in_stack [0]->dreg;
7616 MONO_ADD_INS (tblock, ins);
7619 MonoInst *dummy_use;
7622 * Add a dummy use for the exvar so its liveness info will be
7625 EMIT_NEW_DUMMY_USE (cfg, dummy_use, tblock->in_stack [0]);
7628 if (seq_points && clause->flags == MONO_EXCEPTION_CLAUSE_FILTER) {
7629 NEW_SEQ_POINT (cfg, ins, clause->handler_offset, TRUE);
7630 MONO_ADD_INS (tblock, ins);
7633 if (clause->flags == MONO_EXCEPTION_CLAUSE_FILTER) {
7634 GET_BBLOCK (cfg, tblock, ip + clause->data.filter_offset);
7635 tblock->flags |= BB_EXCEPTION_HANDLER;
7636 tblock->real_offset = clause->data.filter_offset;
7637 tblock->in_scount = 1;
7638 tblock->in_stack = (MonoInst **)mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*));
7639 /* The filter block shares the exvar with the handler block */
7640 tblock->in_stack [0] = mono_create_exvar_for_offset (cfg, clause->handler_offset);
7641 MONO_INST_NEW (cfg, ins, OP_START_HANDLER);
7642 MONO_ADD_INS (tblock, ins);
7646 if (clause->flags != MONO_EXCEPTION_CLAUSE_FILTER &&
7647 clause->data.catch_class &&
7649 mono_class_check_context_used (clause->data.catch_class)) {
7651 * In shared generic code with catch
7652 * clauses containing type variables
7653 * the exception handling code has to
7654 * be able to get to the rgctx.
7655 * Therefore we have to make sure that
7656 * the vtable/mrgctx argument (for
7657 * static or generic methods) or the
7658 * "this" argument (for non-static
7659 * methods) are live.
7661 if ((method->flags & METHOD_ATTRIBUTE_STATIC) ||
7662 mini_method_get_context (method)->method_inst ||
7663 method->klass->valuetype) {
7664 mono_get_vtable_var (cfg);
7666 MonoInst *dummy_use;
7668 EMIT_NEW_DUMMY_USE (cfg, dummy_use, arg_array [0]);
7673 arg_array = (MonoInst **) alloca (sizeof (MonoInst *) * num_args);
7674 cfg->cbb = start_bblock;
7675 cfg->args = arg_array;
7676 mono_save_args (cfg, sig, inline_args);
7679 /* FIRST CODE BLOCK */
7680 NEW_BBLOCK (cfg, tblock);
7681 tblock->cil_code = ip;
7685 ADD_BBLOCK (cfg, tblock);
7687 if (cfg->method == method) {
7688 breakpoint_id = mono_debugger_method_has_breakpoint (method);
7689 if (breakpoint_id) {
7690 MONO_INST_NEW (cfg, ins, OP_BREAK);
7691 MONO_ADD_INS (cfg->cbb, ins);
7695 /* we use a separate basic block for the initialization code */
7696 NEW_BBLOCK (cfg, init_localsbb);
7697 if (cfg->method == method)
7698 cfg->bb_init = init_localsbb;
7699 init_localsbb->real_offset = cfg->real_offset;
7700 start_bblock->next_bb = init_localsbb;
7701 init_localsbb->next_bb = cfg->cbb;
7702 link_bblock (cfg, start_bblock, init_localsbb);
7703 link_bblock (cfg, init_localsbb, cfg->cbb);
7705 cfg->cbb = init_localsbb;
7707 if (cfg->gsharedvt && cfg->method == method) {
7708 MonoGSharedVtMethodInfo *info;
7709 MonoInst *var, *locals_var;
7712 info = (MonoGSharedVtMethodInfo *)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoGSharedVtMethodInfo));
7713 info->method = cfg->method;
7714 info->count_entries = 16;
7715 info->entries = (MonoRuntimeGenericContextInfoTemplate *)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoRuntimeGenericContextInfoTemplate) * info->count_entries);
7716 cfg->gsharedvt_info = info;
7718 var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
7719 /* prevent it from being register allocated */
7720 //var->flags |= MONO_INST_VOLATILE;
7721 cfg->gsharedvt_info_var = var;
7723 ins = emit_get_rgctx_gsharedvt_method (cfg, mini_method_check_context_used (cfg, method), method, info);
7724 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, var->dreg, ins->dreg);
7726 /* Allocate locals */
7727 locals_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
7728 /* prevent it from being register allocated */
7729 //locals_var->flags |= MONO_INST_VOLATILE;
7730 cfg->gsharedvt_locals_var = locals_var;
7732 dreg = alloc_ireg (cfg);
7733 MONO_EMIT_NEW_LOAD_MEMBASE_OP (cfg, OP_LOADI4_MEMBASE, dreg, var->dreg, MONO_STRUCT_OFFSET (MonoGSharedVtMethodRuntimeInfo, locals_size));
7735 MONO_INST_NEW (cfg, ins, OP_LOCALLOC);
7736 ins->dreg = locals_var->dreg;
7738 MONO_ADD_INS (cfg->cbb, ins);
7739 cfg->gsharedvt_locals_var_ins = ins;
7741 cfg->flags |= MONO_CFG_HAS_ALLOCA;
7744 ins->flags |= MONO_INST_INIT;
7748 if (mono_security_core_clr_enabled ()) {
7749 /* check if this is native code, e.g. an icall or a p/invoke */
7750 if (method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE) {
7751 MonoMethod *wrapped = mono_marshal_method_from_wrapper (method);
7753 gboolean pinvk = (wrapped->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL);
7754 gboolean icall = (wrapped->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL);
7756 /* if this ia a native call then it can only be JITted from platform code */
7757 if ((icall || pinvk) && method->klass && method->klass->image) {
7758 if (!mono_security_core_clr_is_platform_image (method->klass->image)) {
7759 MonoException *ex = icall ? mono_get_exception_security () :
7760 mono_get_exception_method_access ();
7761 emit_throw_exception (cfg, ex);
7768 CHECK_CFG_EXCEPTION;
7770 if (header->code_size == 0)
7773 if (get_basic_blocks (cfg, header, cfg->real_offset, ip, end, &err_pos)) {
7778 if (cfg->method == method)
7779 mono_debug_init_method (cfg, cfg->cbb, breakpoint_id);
7781 for (n = 0; n < header->num_locals; ++n) {
7782 if (header->locals [n]->type == MONO_TYPE_VOID && !header->locals [n]->byref)
7787 /* We force the vtable variable here for all shared methods
7788 for the possibility that they might show up in a stack
7789 trace where their exact instantiation is needed. */
7790 if (cfg->gshared && method == cfg->method) {
7791 if ((method->flags & METHOD_ATTRIBUTE_STATIC) ||
7792 mini_method_get_context (method)->method_inst ||
7793 method->klass->valuetype) {
7794 mono_get_vtable_var (cfg);
7796 /* FIXME: Is there a better way to do this?
7797 We need the variable live for the duration
7798 of the whole method. */
7799 cfg->args [0]->flags |= MONO_INST_VOLATILE;
7803 /* add a check for this != NULL to inlined methods */
7804 if (is_virtual_call) {
7807 NEW_ARGLOAD (cfg, arg_ins, 0);
7808 MONO_ADD_INS (cfg->cbb, arg_ins);
7809 MONO_EMIT_NEW_CHECK_THIS (cfg, arg_ins->dreg);
7812 skip_dead_blocks = !dont_verify;
7813 if (skip_dead_blocks) {
7814 original_bb = bb = mono_basic_block_split (method, &cfg->error, header);
7819 /* we use a spare stack slot in SWITCH and NEWOBJ and others */
7820 stack_start = sp = (MonoInst **)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoInst*) * (header->max_stack + 1));
7823 start_new_bblock = 0;
7825 if (cfg->method == method)
7826 cfg->real_offset = ip - header->code;
7828 cfg->real_offset = inline_offset;
7833 if (start_new_bblock) {
7834 cfg->cbb->cil_length = ip - cfg->cbb->cil_code;
7835 if (start_new_bblock == 2) {
7836 g_assert (ip == tblock->cil_code);
7838 GET_BBLOCK (cfg, tblock, ip);
7840 cfg->cbb->next_bb = tblock;
7842 start_new_bblock = 0;
7843 for (i = 0; i < cfg->cbb->in_scount; ++i) {
7844 if (cfg->verbose_level > 3)
7845 printf ("loading %d from temp %d\n", i, (int)cfg->cbb->in_stack [i]->inst_c0);
7846 EMIT_NEW_TEMPLOAD (cfg, ins, cfg->cbb->in_stack [i]->inst_c0);
7850 g_slist_free (class_inits);
7853 if ((tblock = cfg->cil_offset_to_bb [ip - cfg->cil_start]) && (tblock != cfg->cbb)) {
7854 link_bblock (cfg, cfg->cbb, tblock);
7855 if (sp != stack_start) {
7856 handle_stack_args (cfg, stack_start, sp - stack_start);
7858 CHECK_UNVERIFIABLE (cfg);
7860 cfg->cbb->next_bb = tblock;
7862 for (i = 0; i < cfg->cbb->in_scount; ++i) {
7863 if (cfg->verbose_level > 3)
7864 printf ("loading %d from temp %d\n", i, (int)cfg->cbb->in_stack [i]->inst_c0);
7865 EMIT_NEW_TEMPLOAD (cfg, ins, cfg->cbb->in_stack [i]->inst_c0);
7868 g_slist_free (class_inits);
7873 if (skip_dead_blocks) {
7874 int ip_offset = ip - header->code;
7876 if (ip_offset == bb->end)
7880 int op_size = mono_opcode_size (ip, end);
7881 g_assert (op_size > 0); /*The BB formation pass must catch all bad ops*/
7883 if (cfg->verbose_level > 3) printf ("SKIPPING DEAD OP at %x\n", ip_offset);
7885 if (ip_offset + op_size == bb->end) {
7886 MONO_INST_NEW (cfg, ins, OP_NOP);
7887 MONO_ADD_INS (cfg->cbb, ins);
7888 start_new_bblock = 1;
7896 * Sequence points are points where the debugger can place a breakpoint.
7897 * Currently, we generate these automatically at points where the IL
7900 if (seq_points && ((!sym_seq_points && (sp == stack_start)) || (sym_seq_points && mono_bitset_test_fast (seq_point_locs, ip - header->code)))) {
7902 * Make methods interruptable at the beginning, and at the targets of
7903 * backward branches.
7904 * Also, do this at the start of every bblock in methods with clauses too,
7905 * to be able to handle instructions with inprecise control flow like
7907 * Backward branches are handled at the end of method-to-ir ().
7909 gboolean intr_loc = ip == header->code || (!cfg->cbb->last_ins && cfg->header->num_clauses);
7910 gboolean sym_seq_point = sym_seq_points && mono_bitset_test_fast (seq_point_locs, ip - header->code);
7912 /* Avoid sequence points on empty IL like .volatile */
7913 // FIXME: Enable this
7914 //if (!(cfg->cbb->last_ins && cfg->cbb->last_ins->opcode == OP_SEQ_POINT)) {
7915 NEW_SEQ_POINT (cfg, ins, ip - header->code, intr_loc);
7916 if ((sp != stack_start) && !sym_seq_point)
7917 ins->flags |= MONO_INST_NONEMPTY_STACK;
7918 MONO_ADD_INS (cfg->cbb, ins);
7921 mono_bitset_set_fast (seq_point_set_locs, ip - header->code);
7924 cfg->cbb->real_offset = cfg->real_offset;
7926 if ((cfg->method == method) && cfg->coverage_info) {
7927 guint32 cil_offset = ip - header->code;
7928 cfg->coverage_info->data [cil_offset].cil_code = ip;
7930 /* TODO: Use an increment here */
7931 #if defined(TARGET_X86)
7932 MONO_INST_NEW (cfg, ins, OP_STORE_MEM_IMM);
7933 ins->inst_p0 = &(cfg->coverage_info->data [cil_offset].count);
7935 MONO_ADD_INS (cfg->cbb, ins);
7937 EMIT_NEW_PCONST (cfg, ins, &(cfg->coverage_info->data [cil_offset].count));
7938 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STORE_MEMBASE_IMM, ins->dreg, 0, 1);
7942 if (cfg->verbose_level > 3)
7943 printf ("converting (in B%d: stack: %d) %s", cfg->cbb->block_num, (int)(sp - stack_start), mono_disasm_code_one (NULL, method, ip, NULL));
7947 if (seq_points && !sym_seq_points && sp != stack_start) {
7949 * The C# compiler uses these nops to notify the JIT that it should
7950 * insert seq points.
7952 NEW_SEQ_POINT (cfg, ins, ip - header->code, FALSE);
7953 MONO_ADD_INS (cfg->cbb, ins);
7955 if (cfg->keep_cil_nops)
7956 MONO_INST_NEW (cfg, ins, OP_HARD_NOP);
7958 MONO_INST_NEW (cfg, ins, OP_NOP);
7960 MONO_ADD_INS (cfg->cbb, ins);
7963 if (should_insert_brekpoint (cfg->method)) {
7964 ins = mono_emit_jit_icall (cfg, mono_debugger_agent_user_break, NULL);
7966 MONO_INST_NEW (cfg, ins, OP_NOP);
7969 MONO_ADD_INS (cfg->cbb, ins);
7975 CHECK_STACK_OVF (1);
7976 n = (*ip)-CEE_LDARG_0;
7978 EMIT_NEW_ARGLOAD (cfg, ins, n);
7986 CHECK_STACK_OVF (1);
7987 n = (*ip)-CEE_LDLOC_0;
7989 EMIT_NEW_LOCLOAD (cfg, ins, n);
7998 n = (*ip)-CEE_STLOC_0;
8001 if (!dont_verify_stloc && target_type_is_incompatible (cfg, header->locals [n], *sp))
8003 emit_stloc_ir (cfg, sp, header, n);
8010 CHECK_STACK_OVF (1);
8013 EMIT_NEW_ARGLOAD (cfg, ins, n);
8019 CHECK_STACK_OVF (1);
8022 NEW_ARGLOADA (cfg, ins, n);
8023 MONO_ADD_INS (cfg->cbb, ins);
8033 if (!dont_verify_stloc && target_type_is_incompatible (cfg, param_types [ip [1]], *sp))
8035 EMIT_NEW_ARGSTORE (cfg, ins, n, *sp);
8040 CHECK_STACK_OVF (1);
8043 EMIT_NEW_LOCLOAD (cfg, ins, n);
8047 case CEE_LDLOCA_S: {
8048 unsigned char *tmp_ip;
8050 CHECK_STACK_OVF (1);
8051 CHECK_LOCAL (ip [1]);
8053 if ((tmp_ip = emit_optimized_ldloca_ir (cfg, ip, end, 1))) {
8059 EMIT_NEW_LOCLOADA (cfg, ins, ip [1]);
8068 CHECK_LOCAL (ip [1]);
8069 if (!dont_verify_stloc && target_type_is_incompatible (cfg, header->locals [ip [1]], *sp))
8071 emit_stloc_ir (cfg, sp, header, ip [1]);
8076 CHECK_STACK_OVF (1);
8077 EMIT_NEW_PCONST (cfg, ins, NULL);
8078 ins->type = STACK_OBJ;
8083 CHECK_STACK_OVF (1);
8084 EMIT_NEW_ICONST (cfg, ins, -1);
8097 CHECK_STACK_OVF (1);
8098 EMIT_NEW_ICONST (cfg, ins, (*ip) - CEE_LDC_I4_0);
8104 CHECK_STACK_OVF (1);
8106 EMIT_NEW_ICONST (cfg, ins, *((signed char*)ip));
8112 CHECK_STACK_OVF (1);
8113 EMIT_NEW_ICONST (cfg, ins, (gint32)read32 (ip + 1));
8119 CHECK_STACK_OVF (1);
8120 MONO_INST_NEW (cfg, ins, OP_I8CONST);
8121 ins->type = STACK_I8;
8122 ins->dreg = alloc_dreg (cfg, STACK_I8);
8124 ins->inst_l = (gint64)read64 (ip);
8125 MONO_ADD_INS (cfg->cbb, ins);
8131 gboolean use_aotconst = FALSE;
8133 #ifdef TARGET_POWERPC
8134 /* FIXME: Clean this up */
8135 if (cfg->compile_aot)
8136 use_aotconst = TRUE;
8139 /* FIXME: we should really allocate this only late in the compilation process */
8140 f = (float *)mono_domain_alloc (cfg->domain, sizeof (float));
8142 CHECK_STACK_OVF (1);
8148 EMIT_NEW_AOTCONST (cfg, cons, MONO_PATCH_INFO_R4, f);
8150 dreg = alloc_freg (cfg);
8151 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADR4_MEMBASE, dreg, cons->dreg, 0);
8152 ins->type = cfg->r4_stack_type;
8154 MONO_INST_NEW (cfg, ins, OP_R4CONST);
8155 ins->type = cfg->r4_stack_type;
8156 ins->dreg = alloc_dreg (cfg, STACK_R8);
8158 MONO_ADD_INS (cfg->cbb, ins);
8168 gboolean use_aotconst = FALSE;
8170 #ifdef TARGET_POWERPC
8171 /* FIXME: Clean this up */
8172 if (cfg->compile_aot)
8173 use_aotconst = TRUE;
8176 /* FIXME: we should really allocate this only late in the compilation process */
8177 d = (double *)mono_domain_alloc (cfg->domain, sizeof (double));
8179 CHECK_STACK_OVF (1);
8185 EMIT_NEW_AOTCONST (cfg, cons, MONO_PATCH_INFO_R8, d);
8187 dreg = alloc_freg (cfg);
8188 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOADR8_MEMBASE, dreg, cons->dreg, 0);
8189 ins->type = STACK_R8;
8191 MONO_INST_NEW (cfg, ins, OP_R8CONST);
8192 ins->type = STACK_R8;
8193 ins->dreg = alloc_dreg (cfg, STACK_R8);
8195 MONO_ADD_INS (cfg->cbb, ins);
8204 MonoInst *temp, *store;
8206 CHECK_STACK_OVF (1);
8210 temp = mono_compile_create_var (cfg, type_from_stack_type (ins), OP_LOCAL);
8211 EMIT_NEW_TEMPSTORE (cfg, store, temp->inst_c0, ins);
8213 EMIT_NEW_TEMPLOAD (cfg, ins, temp->inst_c0);
8216 EMIT_NEW_TEMPLOAD (cfg, ins, temp->inst_c0);
8229 if (sp [0]->type == STACK_R8)
8230 /* we need to pop the value from the x86 FP stack */
8231 MONO_EMIT_NEW_UNALU (cfg, OP_X86_FPOP, -1, sp [0]->dreg);
8236 MonoMethodSignature *fsig;
8239 INLINE_FAILURE ("jmp");
8240 GSHAREDVT_FAILURE (*ip);
8243 if (stack_start != sp)
8245 token = read32 (ip + 1);
8246 /* FIXME: check the signature matches */
8247 cmethod = mini_get_method (cfg, method, token, NULL, generic_context);
8250 if (cfg->gshared && mono_method_check_context_used (cmethod))
8251 GENERIC_SHARING_FAILURE (CEE_JMP);
8253 emit_instrumentation_call (cfg, mono_profiler_method_leave);
8255 fsig = mono_method_signature (cmethod);
8256 n = fsig->param_count + fsig->hasthis;
8257 if (cfg->llvm_only) {
8260 args = (MonoInst **)mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*) * n);
8261 for (i = 0; i < n; ++i)
8262 EMIT_NEW_ARGLOAD (cfg, args [i], i);
8263 ins = mono_emit_method_call_full (cfg, cmethod, fsig, TRUE, args, NULL, NULL, NULL);
8265 * The code in mono-basic-block.c treats the rest of the code as dead, but we
8266 * have to emit a normal return since llvm expects it.
8269 emit_setret (cfg, ins);
8270 MONO_INST_NEW (cfg, ins, OP_BR);
8271 ins->inst_target_bb = end_bblock;
8272 MONO_ADD_INS (cfg->cbb, ins);
8273 link_bblock (cfg, cfg->cbb, end_bblock);
8276 } else if (cfg->backend->have_op_tail_call) {
8277 /* Handle tail calls similarly to calls */
8280 MONO_INST_NEW_CALL (cfg, call, OP_TAILCALL);
8281 call->method = cmethod;
8282 call->tail_call = TRUE;
8283 call->signature = mono_method_signature (cmethod);
8284 call->args = (MonoInst **)mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*) * n);
8285 call->inst.inst_p0 = cmethod;
8286 for (i = 0; i < n; ++i)
8287 EMIT_NEW_ARGLOAD (cfg, call->args [i], i);
8289 if (mini_type_is_vtype (mini_get_underlying_type (call->signature->ret)))
8290 call->vret_var = cfg->vret_addr;
8292 mono_arch_emit_call (cfg, call);
8293 cfg->param_area = MAX(cfg->param_area, call->stack_usage);
8294 MONO_ADD_INS (cfg->cbb, (MonoInst*)call);
8296 for (i = 0; i < num_args; ++i)
8297 /* Prevent arguments from being optimized away */
8298 arg_array [i]->flags |= MONO_INST_VOLATILE;
8300 MONO_INST_NEW_CALL (cfg, call, OP_JMP);
8301 ins = (MonoInst*)call;
8302 ins->inst_p0 = cmethod;
8303 MONO_ADD_INS (cfg->cbb, ins);
8307 start_new_bblock = 1;
8312 MonoMethodSignature *fsig;
8315 token = read32 (ip + 1);
8319 //GSHAREDVT_FAILURE (*ip);
8324 fsig = mini_get_signature (method, token, generic_context, &cfg->error);
8327 if (method->dynamic && fsig->pinvoke) {
8331 * This is a call through a function pointer using a pinvoke
8332 * signature. Have to create a wrapper and call that instead.
8333 * FIXME: This is very slow, need to create a wrapper at JIT time
8334 * instead based on the signature.
8336 EMIT_NEW_IMAGECONST (cfg, args [0], method->klass->image);
8337 EMIT_NEW_PCONST (cfg, args [1], fsig);
8339 addr = mono_emit_jit_icall (cfg, mono_get_native_calli_wrapper, args);
8342 n = fsig->param_count + fsig->hasthis;
8346 //g_assert (!virtual_ || fsig->hasthis);
8350 inline_costs += 10 * num_calls++;
8353 * Making generic calls out of gsharedvt methods.
8354 * This needs to be used for all generic calls, not just ones with a gsharedvt signature, to avoid
8355 * patching gshared method addresses into a gsharedvt method.
8357 if (cfg->gsharedvt && mini_is_gsharedvt_signature (fsig)) {
8359 * We pass the address to the gsharedvt trampoline in the rgctx reg
8361 MonoInst *callee = addr;
8363 if (method->wrapper_type != MONO_WRAPPER_DELEGATE_INVOKE)
8365 GSHAREDVT_FAILURE (*ip);
8369 GSHAREDVT_FAILURE (*ip);
8371 addr = emit_get_rgctx_sig (cfg, context_used,
8372 fsig, MONO_RGCTX_INFO_SIG_GSHAREDVT_OUT_TRAMPOLINE_CALLI);
8373 ins = (MonoInst*)mini_emit_calli (cfg, fsig, sp, addr, NULL, callee);
8377 /* Prevent inlining of methods with indirect calls */
8378 INLINE_FAILURE ("indirect call");
8380 if (addr->opcode == OP_PCONST || addr->opcode == OP_AOTCONST || addr->opcode == OP_GOT_ENTRY) {
8381 MonoJumpInfoType info_type;
8385 * Instead of emitting an indirect call, emit a direct call
8386 * with the contents of the aotconst as the patch info.
8388 if (addr->opcode == OP_PCONST || addr->opcode == OP_AOTCONST) {
8389 info_type = (MonoJumpInfoType)addr->inst_c1;
8390 info_data = addr->inst_p0;
8392 info_type = (MonoJumpInfoType)addr->inst_right->inst_c1;
8393 info_data = addr->inst_right->inst_left;
8396 if (info_type == MONO_PATCH_INFO_ICALL_ADDR) {
8397 ins = (MonoInst*)mono_emit_abs_call (cfg, MONO_PATCH_INFO_ICALL_ADDR_CALL, info_data, fsig, sp);
8400 } else if (info_type == MONO_PATCH_INFO_JIT_ICALL_ADDR) {
8401 ins = (MonoInst*)mono_emit_abs_call (cfg, info_type, info_data, fsig, sp);
8406 ins = (MonoInst*)mini_emit_calli (cfg, fsig, sp, addr, NULL, NULL);
8410 /* End of call, INS should contain the result of the call, if any */
8412 if (!MONO_TYPE_IS_VOID (fsig->ret)) {
8414 *sp++ = mono_emit_widen_call_res (cfg, ins, fsig);
8417 CHECK_CFG_EXCEPTION;
8421 constrained_class = NULL;
8425 case CEE_CALLVIRT: {
8426 MonoInst *addr = NULL;
8427 MonoMethodSignature *fsig = NULL;
8429 int virtual_ = *ip == CEE_CALLVIRT;
8430 gboolean pass_imt_from_rgctx = FALSE;
8431 MonoInst *imt_arg = NULL;
8432 MonoInst *keep_this_alive = NULL;
8433 gboolean pass_vtable = FALSE;
8434 gboolean pass_mrgctx = FALSE;
8435 MonoInst *vtable_arg = NULL;
8436 gboolean check_this = FALSE;
8437 gboolean supported_tail_call = FALSE;
8438 gboolean tail_call = FALSE;
8439 gboolean need_seq_point = FALSE;
8440 guint32 call_opcode = *ip;
8441 gboolean emit_widen = TRUE;
8442 gboolean push_res = TRUE;
8443 gboolean skip_ret = FALSE;
8444 gboolean delegate_invoke = FALSE;
8445 gboolean direct_icall = FALSE;
8446 gboolean constrained_partial_call = FALSE;
8447 MonoMethod *cil_method;
8450 token = read32 (ip + 1);
8454 cmethod = mini_get_method (cfg, method, token, NULL, generic_context);
8457 cil_method = cmethod;
8459 if (constrained_class) {
8460 if ((constrained_class->byval_arg.type == MONO_TYPE_VAR || constrained_class->byval_arg.type == MONO_TYPE_MVAR) && cfg->gshared) {
8461 if (!mini_is_gsharedvt_klass (constrained_class)) {
8462 g_assert (!cmethod->klass->valuetype);
8463 if (!mini_type_is_reference (&constrained_class->byval_arg))
8464 constrained_partial_call = TRUE;
8468 if (method->wrapper_type != MONO_WRAPPER_NONE) {
8469 if (cfg->verbose_level > 2)
8470 printf ("DM Constrained call to %s\n", mono_type_get_full_name (constrained_class));
8471 if (!((constrained_class->byval_arg.type == MONO_TYPE_VAR ||
8472 constrained_class->byval_arg.type == MONO_TYPE_MVAR) &&
8474 cmethod = mono_get_method_constrained_with_method (image, cil_method, constrained_class, generic_context, &cfg->error);
8478 if (cfg->verbose_level > 2)
8479 printf ("Constrained call to %s\n", mono_type_get_full_name (constrained_class));
8481 if ((constrained_class->byval_arg.type == MONO_TYPE_VAR || constrained_class->byval_arg.type == MONO_TYPE_MVAR) && cfg->gshared) {
8483 * This is needed since get_method_constrained can't find
8484 * the method in klass representing a type var.
8485 * The type var is guaranteed to be a reference type in this
8488 if (!mini_is_gsharedvt_klass (constrained_class))
8489 g_assert (!cmethod->klass->valuetype);
8491 cmethod = mono_get_method_constrained_checked (image, token, constrained_class, generic_context, &cil_method, &cfg->error);
8496 if (constrained_class->enumtype && !strcmp (cmethod->name, "GetHashCode")) {
8497 /* Use the corresponding method from the base type to avoid boxing */
8498 MonoType *base_type = mono_class_enum_basetype (constrained_class);
8499 g_assert (base_type);
8500 constrained_class = mono_class_from_mono_type (base_type);
8501 cmethod = mono_class_get_method_from_name (constrained_class, cmethod->name, 0);
8506 if (!dont_verify && !cfg->skip_visibility) {
8507 MonoMethod *target_method = cil_method;
8508 if (method->is_inflated) {
8509 target_method = mini_get_method_allow_open (method, token, NULL, &(mono_method_get_generic_container (method_definition)->context), &cfg->error);
8512 if (!mono_method_can_access_method (method_definition, target_method) &&
8513 !mono_method_can_access_method (method, cil_method))
8514 emit_method_access_failure (cfg, method, cil_method);
8517 if (mono_security_core_clr_enabled ())
8518 ensure_method_is_allowed_to_call_method (cfg, method, cil_method);
8520 if (!virtual_ && (cmethod->flags & METHOD_ATTRIBUTE_ABSTRACT))
8521 /* MS.NET seems to silently convert this to a callvirt */
8526 * MS.NET accepts non virtual calls to virtual final methods of transparent proxy classes and
8527 * converts to a callvirt.
8529 * tests/bug-515884.il is an example of this behavior
8531 const int test_flags = METHOD_ATTRIBUTE_VIRTUAL | METHOD_ATTRIBUTE_FINAL | METHOD_ATTRIBUTE_STATIC;
8532 const int expected_flags = METHOD_ATTRIBUTE_VIRTUAL | METHOD_ATTRIBUTE_FINAL;
8533 if (!virtual_ && mono_class_is_marshalbyref (cmethod->klass) && (cmethod->flags & test_flags) == expected_flags && cfg->method->wrapper_type == MONO_WRAPPER_NONE)
8537 if (!cmethod->klass->inited)
8538 if (!mono_class_init (cmethod->klass))
8539 TYPE_LOAD_ERROR (cmethod->klass);
8541 fsig = mono_method_signature (cmethod);
8544 if (cmethod->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL &&
8545 mini_class_is_system_array (cmethod->klass)) {
8546 array_rank = cmethod->klass->rank;
8547 } else if ((cmethod->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) && icall_is_direct_callable (cfg, cmethod)) {
8548 direct_icall = TRUE;
8549 } else if (fsig->pinvoke) {
8550 MonoMethod *wrapper = mono_marshal_get_native_wrapper (cmethod, TRUE, cfg->compile_aot);
8551 fsig = mono_method_signature (wrapper);
8552 } else if (constrained_class) {
8554 fsig = mono_method_get_signature_checked (cmethod, image, token, generic_context, &cfg->error);
8558 if (cfg->llvm_only && !cfg->method->wrapper_type && (!cmethod || cmethod->is_inflated))
8559 cfg->signatures = g_slist_prepend_mempool (cfg->mempool, cfg->signatures, fsig);
8561 /* See code below */
8562 if (cmethod->klass == mono_defaults.monitor_class && !strcmp (cmethod->name, "Enter") && mono_method_signature (cmethod)->param_count == 1) {
8563 MonoBasicBlock *tbb;
8565 GET_BBLOCK (cfg, tbb, ip + 5);
8566 if (tbb->try_start && MONO_REGION_FLAGS(tbb->region) == MONO_EXCEPTION_CLAUSE_FINALLY) {
8568 * We want to extend the try block to cover the call, but we can't do it if the
8569 * call is made directly since its followed by an exception check.
8571 direct_icall = FALSE;
8575 mono_save_token_info (cfg, image, token, cil_method);
8577 if (!(seq_point_locs && mono_bitset_test_fast (seq_point_locs, ip + 5 - header->code)))
8578 need_seq_point = TRUE;
8580 /* Don't support calls made using type arguments for now */
8582 if (cfg->gsharedvt) {
8583 if (mini_is_gsharedvt_signature (fsig))
8584 GSHAREDVT_FAILURE (*ip);
8588 if (cmethod->string_ctor && method->wrapper_type != MONO_WRAPPER_RUNTIME_INVOKE)
8589 g_assert_not_reached ();
8591 n = fsig->param_count + fsig->hasthis;
8593 if (!cfg->gshared && mono_class_is_gtd (cmethod->klass))
8597 g_assert (!mono_method_check_context_used (cmethod));
8601 //g_assert (!virtual_ || fsig->hasthis);
8605 if (cmethod && cmethod->klass->image == mono_defaults.corlib && !strcmp (cmethod->klass->name, "ThrowHelper"))
8606 cfg->cbb->out_of_line = TRUE;
8609 * We have the `constrained.' prefix opcode.
8611 if (constrained_class) {
8612 if (mini_is_gsharedvt_klass (constrained_class)) {
8613 if ((cmethod->klass != mono_defaults.object_class) && constrained_class->valuetype && cmethod->klass->valuetype) {
8614 /* The 'Own method' case below */
8615 } else if (cmethod->klass->image != mono_defaults.corlib && !mono_class_is_interface (cmethod->klass) && !cmethod->klass->valuetype) {
8616 /* 'The type parameter is instantiated as a reference type' case below. */
8618 ins = handle_constrained_gsharedvt_call (cfg, cmethod, fsig, sp, constrained_class, &emit_widen);
8619 CHECK_CFG_EXCEPTION;
8625 if (constrained_partial_call) {
8626 gboolean need_box = TRUE;
8629 * The receiver is a valuetype, but the exact type is not known at compile time. This means the
8630 * called method is not known at compile time either. The called method could end up being
8631 * one of the methods on the parent classes (object/valuetype/enum), in which case we need
8632 * to box the receiver.
8633 * A simple solution would be to box always and make a normal virtual call, but that would
8634 * be bad performance wise.
8636 if (mono_class_is_interface (cmethod->klass) && mono_class_is_ginst (cmethod->klass)) {
8638 * The parent classes implement no generic interfaces, so the called method will be a vtype method, so no boxing neccessary.
8643 if (!(cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL) && (cmethod->klass == mono_defaults.object_class || cmethod->klass == mono_defaults.enum_class->parent || cmethod->klass == mono_defaults.enum_class)) {
8644 /* The called method is not virtual, i.e. Object:GetType (), the receiver is a vtype, has to box */
8645 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &constrained_class->byval_arg, sp [0]->dreg, 0);
8646 ins->klass = constrained_class;
8647 sp [0] = handle_box (cfg, ins, constrained_class, mono_class_check_context_used (constrained_class));
8648 CHECK_CFG_EXCEPTION;
8649 } else if (need_box) {
8651 MonoBasicBlock *is_ref_bb, *end_bb;
8652 MonoInst *nonbox_call;
8655 * Determine at runtime whenever the called method is defined on object/valuetype/enum, and emit a boxing call
8657 * FIXME: It is possible to inline the called method in a lot of cases, i.e. for T_INT,
8658 * the no-box case goes to a method in Int32, while the box case goes to a method in Enum.
8660 addr = emit_get_rgctx_virt_method (cfg, mono_class_check_context_used (constrained_class), constrained_class, cmethod, MONO_RGCTX_INFO_VIRT_METHOD_CODE);
8662 NEW_BBLOCK (cfg, is_ref_bb);
8663 NEW_BBLOCK (cfg, end_bb);
8665 box_type = emit_get_rgctx_virt_method (cfg, mono_class_check_context_used (constrained_class), constrained_class, cmethod, MONO_RGCTX_INFO_VIRT_METHOD_BOX_TYPE);
8666 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, box_type->dreg, MONO_GSHAREDVT_BOX_TYPE_REF);
8667 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, is_ref_bb);
8670 nonbox_call = (MonoInst*)mini_emit_calli (cfg, fsig, sp, addr, NULL, NULL);
8672 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
8675 MONO_START_BB (cfg, is_ref_bb);
8676 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &constrained_class->byval_arg, sp [0]->dreg, 0);
8677 ins->klass = constrained_class;
8678 sp [0] = handle_box (cfg, ins, constrained_class, mono_class_check_context_used (constrained_class));
8679 ins = (MonoInst*)mini_emit_calli (cfg, fsig, sp, addr, NULL, NULL);
8681 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
8683 MONO_START_BB (cfg, end_bb);
8686 nonbox_call->dreg = ins->dreg;
8689 g_assert (mono_class_is_interface (cmethod->klass));
8690 addr = emit_get_rgctx_virt_method (cfg, mono_class_check_context_used (constrained_class), constrained_class, cmethod, MONO_RGCTX_INFO_VIRT_METHOD_CODE);
8691 ins = (MonoInst*)mini_emit_calli (cfg, fsig, sp, addr, NULL, NULL);
8694 } else if (constrained_class->valuetype && (cmethod->klass == mono_defaults.object_class || cmethod->klass == mono_defaults.enum_class->parent || cmethod->klass == mono_defaults.enum_class)) {
8696 * The type parameter is instantiated as a valuetype,
8697 * but that type doesn't override the method we're
8698 * calling, so we need to box `this'.
8700 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &constrained_class->byval_arg, sp [0]->dreg, 0);
8701 ins->klass = constrained_class;
8702 sp [0] = handle_box (cfg, ins, constrained_class, mono_class_check_context_used (constrained_class));
8703 CHECK_CFG_EXCEPTION;
8704 } else if (!constrained_class->valuetype) {
8705 int dreg = alloc_ireg_ref (cfg);
8708 * The type parameter is instantiated as a reference
8709 * type. We have a managed pointer on the stack, so
8710 * we need to dereference it here.
8712 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, dreg, sp [0]->dreg, 0);
8713 ins->type = STACK_OBJ;
8716 if (cmethod->klass->valuetype) {
8719 /* Interface method */
8722 mono_class_setup_vtable (constrained_class);
8723 CHECK_TYPELOAD (constrained_class);
8724 ioffset = mono_class_interface_offset (constrained_class, cmethod->klass);
8726 TYPE_LOAD_ERROR (constrained_class);
8727 slot = mono_method_get_vtable_slot (cmethod);
8729 TYPE_LOAD_ERROR (cmethod->klass);
8730 cmethod = constrained_class->vtable [ioffset + slot];
8732 if (cmethod->klass == mono_defaults.enum_class) {
8733 /* Enum implements some interfaces, so treat this as the first case */
8734 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &constrained_class->byval_arg, sp [0]->dreg, 0);
8735 ins->klass = constrained_class;
8736 sp [0] = handle_box (cfg, ins, constrained_class, mono_class_check_context_used (constrained_class));
8737 CHECK_CFG_EXCEPTION;
8742 constrained_class = NULL;
8745 if (check_call_signature (cfg, fsig, sp))
8748 if ((cmethod->klass->parent == mono_defaults.multicastdelegate_class) && !strcmp (cmethod->name, "Invoke"))
8749 delegate_invoke = TRUE;
8751 if ((cfg->opt & MONO_OPT_INTRINS) && (ins = mini_emit_inst_for_sharable_method (cfg, cmethod, fsig, sp))) {
8752 if (!MONO_TYPE_IS_VOID (fsig->ret)) {
8753 type_to_eval_stack_type ((cfg), fsig->ret, ins);
8761 * If the callee is a shared method, then its static cctor
8762 * might not get called after the call was patched.
8764 if (cfg->gshared && cmethod->klass != method->klass && mono_class_is_ginst (cmethod->klass) && mono_method_is_generic_sharable (cmethod, TRUE) && mono_class_needs_cctor_run (cmethod->klass, method)) {
8765 emit_class_init (cfg, cmethod->klass);
8766 CHECK_TYPELOAD (cmethod->klass);
8769 check_method_sharing (cfg, cmethod, &pass_vtable, &pass_mrgctx);
8772 MonoGenericContext *cmethod_context = mono_method_get_context (cmethod);
8774 context_used = mini_method_check_context_used (cfg, cmethod);
8776 if (context_used && mono_class_is_interface (cmethod->klass)) {
8777 /* Generic method interface
8778 calls are resolved via a
8779 helper function and don't
8781 if (!cmethod_context || !cmethod_context->method_inst)
8782 pass_imt_from_rgctx = TRUE;
8786 * If a shared method calls another
8787 * shared method then the caller must
8788 * have a generic sharing context
8789 * because the magic trampoline
8790 * requires it. FIXME: We shouldn't
8791 * have to force the vtable/mrgctx
8792 * variable here. Instead there
8793 * should be a flag in the cfg to
8794 * request a generic sharing context.
8797 ((cfg->method->flags & METHOD_ATTRIBUTE_STATIC) || cfg->method->klass->valuetype))
8798 mono_get_vtable_var (cfg);
8803 vtable_arg = mini_emit_get_rgctx_klass (cfg, context_used, cmethod->klass, MONO_RGCTX_INFO_VTABLE);
8805 MonoVTable *vtable = mono_class_vtable (cfg->domain, cmethod->klass);
8807 CHECK_TYPELOAD (cmethod->klass);
8808 EMIT_NEW_VTABLECONST (cfg, vtable_arg, vtable);
8813 g_assert (!vtable_arg);
8815 if (!cfg->compile_aot) {
8817 * emit_get_rgctx_method () calls mono_class_vtable () so check
8818 * for type load errors before.
8820 mono_class_setup_vtable (cmethod->klass);
8821 CHECK_TYPELOAD (cmethod->klass);
8824 vtable_arg = emit_get_rgctx_method (cfg, context_used, cmethod, MONO_RGCTX_INFO_METHOD_RGCTX);
8826 /* !marshalbyref is needed to properly handle generic methods + remoting */
8827 if ((!(cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL) ||
8828 MONO_METHOD_IS_FINAL (cmethod)) &&
8829 !mono_class_is_marshalbyref (cmethod->klass)) {
8836 if (pass_imt_from_rgctx) {
8837 g_assert (!pass_vtable);
8839 imt_arg = emit_get_rgctx_method (cfg, context_used,
8840 cmethod, MONO_RGCTX_INFO_METHOD);
8844 MONO_EMIT_NEW_CHECK_THIS (cfg, sp [0]->dreg);
8846 /* Calling virtual generic methods */
8847 if (virtual_ && (cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL) &&
8848 !(MONO_METHOD_IS_FINAL (cmethod) &&
8849 cmethod->wrapper_type != MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK) &&
8850 fsig->generic_param_count &&
8851 !(cfg->gsharedvt && mini_is_gsharedvt_signature (fsig)) &&
8853 MonoInst *this_temp, *this_arg_temp, *store;
8854 MonoInst *iargs [4];
8856 g_assert (fsig->is_inflated);
8858 /* Prevent inlining of methods that contain indirect calls */
8859 INLINE_FAILURE ("virtual generic call");
8861 if (cfg->gsharedvt && mini_is_gsharedvt_signature (fsig))
8862 GSHAREDVT_FAILURE (*ip);
8864 if (cfg->backend->have_generalized_imt_trampoline && cfg->backend->gshared_supported && cmethod->wrapper_type == MONO_WRAPPER_NONE) {
8865 g_assert (!imt_arg);
8867 g_assert (cmethod->is_inflated);
8868 imt_arg = emit_get_rgctx_method (cfg, context_used,
8869 cmethod, MONO_RGCTX_INFO_METHOD);
8870 ins = mono_emit_method_call_full (cfg, cmethod, fsig, FALSE, sp, sp [0], imt_arg, NULL);
8872 this_temp = mono_compile_create_var (cfg, type_from_stack_type (sp [0]), OP_LOCAL);
8873 NEW_TEMPSTORE (cfg, store, this_temp->inst_c0, sp [0]);
8874 MONO_ADD_INS (cfg->cbb, store);
8876 /* FIXME: This should be a managed pointer */
8877 this_arg_temp = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
8879 EMIT_NEW_TEMPLOAD (cfg, iargs [0], this_temp->inst_c0);
8880 iargs [1] = emit_get_rgctx_method (cfg, context_used,
8881 cmethod, MONO_RGCTX_INFO_METHOD);
8882 EMIT_NEW_TEMPLOADA (cfg, iargs [2], this_arg_temp->inst_c0);
8883 addr = mono_emit_jit_icall (cfg,
8884 mono_helper_compile_generic_method, iargs);
8886 EMIT_NEW_TEMPLOAD (cfg, sp [0], this_arg_temp->inst_c0);
8888 ins = (MonoInst*)mini_emit_calli (cfg, fsig, sp, addr, NULL, NULL);
8895 * Implement a workaround for the inherent races involved in locking:
8901 * If a thread abort happens between the call to Monitor.Enter () and the start of the
8902 * try block, the Exit () won't be executed, see:
8903 * http://www.bluebytesoftware.com/blog/2007/01/30/MonitorEnterThreadAbortsAndOrphanedLocks.aspx
8904 * To work around this, we extend such try blocks to include the last x bytes
8905 * of the Monitor.Enter () call.
8907 if (cmethod->klass == mono_defaults.monitor_class && !strcmp (cmethod->name, "Enter") && mono_method_signature (cmethod)->param_count == 1) {
8908 MonoBasicBlock *tbb;
8910 GET_BBLOCK (cfg, tbb, ip + 5);
8912 * Only extend try blocks with a finally, to avoid catching exceptions thrown
8913 * from Monitor.Enter like ArgumentNullException.
8915 if (tbb->try_start && MONO_REGION_FLAGS(tbb->region) == MONO_EXCEPTION_CLAUSE_FINALLY) {
8916 /* Mark this bblock as needing to be extended */
8917 tbb->extend_try_block = TRUE;
8921 /* Conversion to a JIT intrinsic */
8922 if ((cfg->opt & MONO_OPT_INTRINS) && (ins = mini_emit_inst_for_method (cfg, cmethod, fsig, sp))) {
8923 if (!MONO_TYPE_IS_VOID (fsig->ret)) {
8924 type_to_eval_stack_type ((cfg), fsig->ret, ins);
8932 if ((cfg->opt & MONO_OPT_INLINE) &&
8933 (!virtual_ || !(cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL) || MONO_METHOD_IS_FINAL (cmethod)) &&
8934 mono_method_check_inlining (cfg, cmethod)) {
8936 gboolean always = FALSE;
8938 if ((cmethod->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
8939 (cmethod->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)) {
8940 /* Prevent inlining of methods that call wrappers */
8941 INLINE_FAILURE ("wrapper call");
8942 cmethod = mono_marshal_get_native_wrapper (cmethod, TRUE, FALSE);
8946 costs = inline_method (cfg, cmethod, fsig, sp, ip, cfg->real_offset, always);
8948 cfg->real_offset += 5;
8950 if (!MONO_TYPE_IS_VOID (fsig->ret)) {
8951 /* *sp is already set by inline_method */
8956 inline_costs += costs;
8962 /* Tail recursion elimination */
8963 if ((cfg->opt & MONO_OPT_TAILC) && call_opcode == CEE_CALL && cmethod == method && ip [5] == CEE_RET && !vtable_arg) {
8964 gboolean has_vtargs = FALSE;
8967 /* Prevent inlining of methods with tail calls (the call stack would be altered) */
8968 INLINE_FAILURE ("tail call");
8970 /* keep it simple */
8971 for (i = fsig->param_count - 1; i >= 0; i--) {
8972 if (MONO_TYPE_ISSTRUCT (mono_method_signature (cmethod)->params [i]))
8977 if (need_seq_point) {
8978 emit_seq_point (cfg, method, ip, FALSE, TRUE);
8979 need_seq_point = FALSE;
8981 for (i = 0; i < n; ++i)
8982 EMIT_NEW_ARGSTORE (cfg, ins, i, sp [i]);
8983 MONO_INST_NEW (cfg, ins, OP_BR);
8984 MONO_ADD_INS (cfg->cbb, ins);
8985 tblock = start_bblock->out_bb [0];
8986 link_bblock (cfg, cfg->cbb, tblock);
8987 ins->inst_target_bb = tblock;
8988 start_new_bblock = 1;
8990 /* skip the CEE_RET, too */
8991 if (ip_in_bb (cfg, cfg->cbb, ip + 5))
8998 inline_costs += 10 * num_calls++;
9001 * Synchronized wrappers.
9002 * Its hard to determine where to replace a method with its synchronized
9003 * wrapper without causing an infinite recursion. The current solution is
9004 * to add the synchronized wrapper in the trampolines, and to
9005 * change the called method to a dummy wrapper, and resolve that wrapper
9006 * to the real method in mono_jit_compile_method ().
9008 if (cfg->method->wrapper_type == MONO_WRAPPER_SYNCHRONIZED) {
9009 MonoMethod *orig = mono_marshal_method_from_wrapper (cfg->method);
9010 if (cmethod == orig || (cmethod->is_inflated && mono_method_get_declaring_generic_method (cmethod) == orig))
9011 cmethod = mono_marshal_get_synchronized_inner_wrapper (cmethod);
9015 * Making generic calls out of gsharedvt methods.
9016 * This needs to be used for all generic calls, not just ones with a gsharedvt signature, to avoid
9017 * patching gshared method addresses into a gsharedvt method.
9019 if (cfg->gsharedvt && (mini_is_gsharedvt_signature (fsig) || cmethod->is_inflated || mono_class_is_ginst (cmethod->klass)) &&
9020 !(cmethod->klass->rank && cmethod->klass->byval_arg.type != MONO_TYPE_SZARRAY) &&
9021 (!(cfg->llvm_only && virtual_ && (cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL)))) {
9022 MonoRgctxInfoType info_type;
9025 //if (mono_class_is_interface (cmethod->klass))
9026 //GSHAREDVT_FAILURE (*ip);
9027 // disable for possible remoting calls
9028 if (fsig->hasthis && (mono_class_is_marshalbyref (method->klass) || method->klass == mono_defaults.object_class))
9029 GSHAREDVT_FAILURE (*ip);
9030 if (fsig->generic_param_count) {
9031 /* virtual generic call */
9032 g_assert (!imt_arg);
9033 /* Same as the virtual generic case above */
9034 imt_arg = emit_get_rgctx_method (cfg, context_used,
9035 cmethod, MONO_RGCTX_INFO_METHOD);
9036 /* This is not needed, as the trampoline code will pass one, and it might be passed in the same reg as the imt arg */
9038 } else if (mono_class_is_interface (cmethod->klass) && !imt_arg) {
9039 /* This can happen when we call a fully instantiated iface method */
9040 imt_arg = emit_get_rgctx_method (cfg, context_used,
9041 cmethod, MONO_RGCTX_INFO_METHOD);
9046 if ((cmethod->klass->parent == mono_defaults.multicastdelegate_class) && (!strcmp (cmethod->name, "Invoke")))
9047 keep_this_alive = sp [0];
9049 if (virtual_ && (cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL))
9050 info_type = MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE_VIRT;
9052 info_type = MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE;
9053 addr = emit_get_rgctx_gsharedvt_call (cfg, context_used, fsig, cmethod, info_type);
9055 if (cfg->llvm_only) {
9056 // FIXME: Avoid initializing vtable_arg
9057 ins = emit_llvmonly_calli (cfg, fsig, sp, addr);
9059 ins = (MonoInst*)mini_emit_calli (cfg, fsig, sp, addr, imt_arg, vtable_arg);
9064 /* Generic sharing */
9067 * Use this if the callee is gsharedvt sharable too, since
9068 * at runtime we might find an instantiation so the call cannot
9069 * be patched (the 'no_patch' code path in mini-trampolines.c).
9071 if (context_used && !imt_arg && !array_rank && !delegate_invoke &&
9072 (!mono_method_is_generic_sharable_full (cmethod, TRUE, FALSE, FALSE) ||
9073 !mono_class_generic_sharing_enabled (cmethod->klass)) &&
9074 (!virtual_ || MONO_METHOD_IS_FINAL (cmethod) ||
9075 !(cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL))) {
9076 INLINE_FAILURE ("gshared");
9078 g_assert (cfg->gshared && cmethod);
9082 * We are compiling a call to a
9083 * generic method from shared code,
9084 * which means that we have to look up
9085 * the method in the rgctx and do an
9089 MONO_EMIT_NEW_CHECK_THIS (cfg, sp [0]->dreg);
9091 if (cfg->llvm_only) {
9092 if (cfg->gsharedvt && mini_is_gsharedvt_variable_signature (fsig))
9093 addr = emit_get_rgctx_method (cfg, context_used, cmethod, MONO_RGCTX_INFO_GSHAREDVT_OUT_WRAPPER);
9095 addr = emit_get_rgctx_method (cfg, context_used, cmethod, MONO_RGCTX_INFO_GENERIC_METHOD_CODE);
9096 // FIXME: Avoid initializing imt_arg/vtable_arg
9097 ins = emit_llvmonly_calli (cfg, fsig, sp, addr);
9099 addr = emit_get_rgctx_method (cfg, context_used, cmethod, MONO_RGCTX_INFO_GENERIC_METHOD_CODE);
9100 ins = (MonoInst*)mini_emit_calli (cfg, fsig, sp, addr, imt_arg, vtable_arg);
9105 /* Direct calls to icalls */
9107 MonoMethod *wrapper;
9110 /* Inline the wrapper */
9111 wrapper = mono_marshal_get_native_wrapper (cmethod, TRUE, cfg->compile_aot);
9113 costs = inline_method (cfg, wrapper, fsig, sp, ip, cfg->real_offset, TRUE);
9114 g_assert (costs > 0);
9115 cfg->real_offset += 5;
9117 if (!MONO_TYPE_IS_VOID (fsig->ret)) {
9118 /* *sp is already set by inline_method */
9123 inline_costs += costs;
9132 if (strcmp (cmethod->name, "Set") == 0) { /* array Set */
9133 MonoInst *val = sp [fsig->param_count];
9135 if (val->type == STACK_OBJ) {
9136 MonoInst *iargs [2];
9141 mono_emit_jit_icall (cfg, mono_helper_stelem_ref_check, iargs);
9144 addr = mini_emit_ldelema_ins (cfg, cmethod, sp, ip, TRUE);
9145 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, fsig->params [fsig->param_count - 1], addr->dreg, 0, val->dreg);
9146 if (cfg->gen_write_barriers && val->type == STACK_OBJ && !MONO_INS_IS_PCONST_NULL (val))
9147 mini_emit_write_barrier (cfg, addr, val);
9148 if (cfg->gen_write_barriers && mini_is_gsharedvt_klass (cmethod->klass))
9149 GSHAREDVT_FAILURE (*ip);
9150 } else if (strcmp (cmethod->name, "Get") == 0) { /* array Get */
9151 addr = mini_emit_ldelema_ins (cfg, cmethod, sp, ip, FALSE);
9153 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, fsig->ret, addr->dreg, 0);
9154 } else if (strcmp (cmethod->name, "Address") == 0) { /* array Address */
9155 if (!cmethod->klass->element_class->valuetype && !readonly)
9156 mini_emit_check_array_type (cfg, sp [0], cmethod->klass);
9157 CHECK_TYPELOAD (cmethod->klass);
9160 addr = mini_emit_ldelema_ins (cfg, cmethod, sp, ip, FALSE);
9163 g_assert_not_reached ();
9170 ins = mini_redirect_call (cfg, cmethod, fsig, sp, virtual_ ? sp [0] : NULL);
9174 /* Tail prefix / tail call optimization */
9176 /* FIXME: Enabling TAILC breaks some inlining/stack trace/etc tests */
9177 /* FIXME: runtime generic context pointer for jumps? */
9178 /* FIXME: handle this for generic sharing eventually */
9179 if ((ins_flag & MONO_INST_TAILCALL) &&
9180 !vtable_arg && !cfg->gshared && is_supported_tail_call (cfg, method, cmethod, fsig, call_opcode))
9181 supported_tail_call = TRUE;
9183 if (supported_tail_call) {
9186 /* Prevent inlining of methods with tail calls (the call stack would be altered) */
9187 INLINE_FAILURE ("tail call");
9189 //printf ("HIT: %s -> %s\n", mono_method_full_name (cfg->method, TRUE), mono_method_full_name (cmethod, TRUE));
9191 if (cfg->backend->have_op_tail_call) {
9192 /* Handle tail calls similarly to normal calls */
9195 emit_instrumentation_call (cfg, mono_profiler_method_leave);
9197 MONO_INST_NEW_CALL (cfg, call, OP_JMP);
9198 call->tail_call = TRUE;
9199 call->method = cmethod;
9200 call->signature = mono_method_signature (cmethod);
9203 * We implement tail calls by storing the actual arguments into the
9204 * argument variables, then emitting a CEE_JMP.
9206 for (i = 0; i < n; ++i) {
9207 /* Prevent argument from being register allocated */
9208 arg_array [i]->flags |= MONO_INST_VOLATILE;
9209 EMIT_NEW_ARGSTORE (cfg, ins, i, sp [i]);
9211 ins = (MonoInst*)call;
9212 ins->inst_p0 = cmethod;
9213 ins->inst_p1 = arg_array [0];
9214 MONO_ADD_INS (cfg->cbb, ins);
9215 link_bblock (cfg, cfg->cbb, end_bblock);
9216 start_new_bblock = 1;
9218 // FIXME: Eliminate unreachable epilogs
9221 * OP_TAILCALL has no return value, so skip the CEE_RET if it is
9222 * only reachable from this call.
9224 GET_BBLOCK (cfg, tblock, ip + 5);
9225 if (tblock == cfg->cbb || tblock->in_count == 0)
9234 * Virtual calls in llvm-only mode.
9236 if (cfg->llvm_only && virtual_ && cmethod && (cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL)) {
9237 ins = emit_llvmonly_virtual_call (cfg, cmethod, fsig, context_used, sp);
9242 if (!(method->iflags & METHOD_IMPL_ATTRIBUTE_AGGRESSIVE_INLINING) && !(cmethod->iflags & METHOD_IMPL_ATTRIBUTE_AGGRESSIVE_INLINING))
9243 INLINE_FAILURE ("call");
9244 ins = mono_emit_method_call_full (cfg, cmethod, fsig, tail_call, sp, virtual_ ? sp [0] : NULL,
9245 imt_arg, vtable_arg);
9247 if (tail_call && !cfg->llvm_only) {
9248 link_bblock (cfg, cfg->cbb, end_bblock);
9249 start_new_bblock = 1;
9251 // FIXME: Eliminate unreachable epilogs
9254 * OP_TAILCALL has no return value, so skip the CEE_RET if it is
9255 * only reachable from this call.
9257 GET_BBLOCK (cfg, tblock, ip + 5);
9258 if (tblock == cfg->cbb || tblock->in_count == 0)
9265 /* End of call, INS should contain the result of the call, if any */
9267 if (push_res && !MONO_TYPE_IS_VOID (fsig->ret)) {
9270 *sp++ = mono_emit_widen_call_res (cfg, ins, fsig);
9275 if (keep_this_alive) {
9276 MonoInst *dummy_use;
9278 /* See mono_emit_method_call_full () */
9279 EMIT_NEW_DUMMY_USE (cfg, dummy_use, keep_this_alive);
9282 if (cfg->llvm_only && cmethod && method_needs_stack_walk (cfg, cmethod)) {
9284 * Clang can convert these calls to tail calls which screw up the stack
9285 * walk. This happens even when the -fno-optimize-sibling-calls
9286 * option is passed to clang.
9287 * Work around this by emitting a dummy call.
9289 mono_emit_jit_icall (cfg, mono_dummy_jit_icall, NULL);
9292 CHECK_CFG_EXCEPTION;
9296 g_assert (*ip == CEE_RET);
9300 constrained_class = NULL;
9302 emit_seq_point (cfg, method, ip, FALSE, TRUE);
9306 if (cfg->method != method) {
9307 /* return from inlined method */
9309 * If in_count == 0, that means the ret is unreachable due to
9310 * being preceeded by a throw. In that case, inline_method () will
9311 * handle setting the return value
9312 * (test case: test_0_inline_throw ()).
9314 if (return_var && cfg->cbb->in_count) {
9315 MonoType *ret_type = mono_method_signature (method)->ret;
9321 if ((method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD || method->wrapper_type == MONO_WRAPPER_NONE) && target_type_is_incompatible (cfg, ret_type, *sp))
9324 //g_assert (returnvar != -1);
9325 EMIT_NEW_TEMPSTORE (cfg, store, return_var->inst_c0, *sp);
9326 cfg->ret_var_set = TRUE;
9329 emit_instrumentation_call (cfg, mono_profiler_method_leave);
9331 if (cfg->lmf_var && cfg->cbb->in_count && !cfg->llvm_only)
9335 MonoType *ret_type = mini_get_underlying_type (mono_method_signature (method)->ret);
9337 if (seq_points && !sym_seq_points) {
9339 * Place a seq point here too even through the IL stack is not
9340 * empty, so a step over on
9343 * will work correctly.
9345 NEW_SEQ_POINT (cfg, ins, ip - header->code, TRUE);
9346 MONO_ADD_INS (cfg->cbb, ins);
9349 g_assert (!return_var);
9353 if ((method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD || method->wrapper_type == MONO_WRAPPER_NONE) && target_type_is_incompatible (cfg, ret_type, *sp))
9356 emit_setret (cfg, *sp);
9359 if (sp != stack_start)
9361 MONO_INST_NEW (cfg, ins, OP_BR);
9363 ins->inst_target_bb = end_bblock;
9364 MONO_ADD_INS (cfg->cbb, ins);
9365 link_bblock (cfg, cfg->cbb, end_bblock);
9366 start_new_bblock = 1;
9370 MONO_INST_NEW (cfg, ins, OP_BR);
9372 target = ip + 1 + (signed char)(*ip);
9374 GET_BBLOCK (cfg, tblock, target);
9375 link_bblock (cfg, cfg->cbb, tblock);
9376 ins->inst_target_bb = tblock;
9377 if (sp != stack_start) {
9378 handle_stack_args (cfg, stack_start, sp - stack_start);
9380 CHECK_UNVERIFIABLE (cfg);
9382 MONO_ADD_INS (cfg->cbb, ins);
9383 start_new_bblock = 1;
9384 inline_costs += BRANCH_COST;
9398 MONO_INST_NEW (cfg, ins, *ip + BIG_BRANCH_OFFSET);
9400 target = ip + 1 + *(signed char*)ip;
9406 inline_costs += BRANCH_COST;
9410 MONO_INST_NEW (cfg, ins, OP_BR);
9413 target = ip + 4 + (gint32)read32(ip);
9415 GET_BBLOCK (cfg, tblock, target);
9416 link_bblock (cfg, cfg->cbb, tblock);
9417 ins->inst_target_bb = tblock;
9418 if (sp != stack_start) {
9419 handle_stack_args (cfg, stack_start, sp - stack_start);
9421 CHECK_UNVERIFIABLE (cfg);
9424 MONO_ADD_INS (cfg->cbb, ins);
9426 start_new_bblock = 1;
9427 inline_costs += BRANCH_COST;
9434 gboolean is_short = ((*ip) == CEE_BRFALSE_S) || ((*ip) == CEE_BRTRUE_S);
9435 gboolean is_true = ((*ip) == CEE_BRTRUE_S) || ((*ip) == CEE_BRTRUE);
9436 guint32 opsize = is_short ? 1 : 4;
9438 CHECK_OPSIZE (opsize);
9440 if (sp [-1]->type == STACK_VTYPE || sp [-1]->type == STACK_R8)
9443 target = ip + opsize + (is_short ? *(signed char*)ip : (gint32)read32(ip));
9448 GET_BBLOCK (cfg, tblock, target);
9449 link_bblock (cfg, cfg->cbb, tblock);
9450 GET_BBLOCK (cfg, tblock, ip);
9451 link_bblock (cfg, cfg->cbb, tblock);
9453 if (sp != stack_start) {
9454 handle_stack_args (cfg, stack_start, sp - stack_start);
9455 CHECK_UNVERIFIABLE (cfg);
9458 MONO_INST_NEW(cfg, cmp, OP_ICOMPARE_IMM);
9459 cmp->sreg1 = sp [0]->dreg;
9460 type_from_op (cfg, cmp, sp [0], NULL);
9463 #if SIZEOF_REGISTER == 4
9464 if (cmp->opcode == OP_LCOMPARE_IMM) {
9465 /* Convert it to OP_LCOMPARE */
9466 MONO_INST_NEW (cfg, ins, OP_I8CONST);
9467 ins->type = STACK_I8;
9468 ins->dreg = alloc_dreg (cfg, STACK_I8);
9470 MONO_ADD_INS (cfg->cbb, ins);
9471 cmp->opcode = OP_LCOMPARE;
9472 cmp->sreg2 = ins->dreg;
9475 MONO_ADD_INS (cfg->cbb, cmp);
9477 MONO_INST_NEW (cfg, ins, is_true ? CEE_BNE_UN : CEE_BEQ);
9478 type_from_op (cfg, ins, sp [0], NULL);
9479 MONO_ADD_INS (cfg->cbb, ins);
9480 ins->inst_many_bb = (MonoBasicBlock **)mono_mempool_alloc (cfg->mempool, sizeof(gpointer)*2);
9481 GET_BBLOCK (cfg, tblock, target);
9482 ins->inst_true_bb = tblock;
9483 GET_BBLOCK (cfg, tblock, ip);
9484 ins->inst_false_bb = tblock;
9485 start_new_bblock = 2;
9488 inline_costs += BRANCH_COST;
9503 MONO_INST_NEW (cfg, ins, *ip);
9505 target = ip + 4 + (gint32)read32(ip);
9511 inline_costs += BRANCH_COST;
9515 MonoBasicBlock **targets;
9516 MonoBasicBlock *default_bblock;
9517 MonoJumpInfoBBTable *table;
9518 int offset_reg = alloc_preg (cfg);
9519 int target_reg = alloc_preg (cfg);
9520 int table_reg = alloc_preg (cfg);
9521 int sum_reg = alloc_preg (cfg);
9522 gboolean use_op_switch;
9526 n = read32 (ip + 1);
9529 if ((src1->type != STACK_I4) && (src1->type != STACK_PTR))
9533 CHECK_OPSIZE (n * sizeof (guint32));
9534 target = ip + n * sizeof (guint32);
9536 GET_BBLOCK (cfg, default_bblock, target);
9537 default_bblock->flags |= BB_INDIRECT_JUMP_TARGET;
9539 targets = (MonoBasicBlock **)mono_mempool_alloc (cfg->mempool, sizeof (MonoBasicBlock*) * n);
9540 for (i = 0; i < n; ++i) {
9541 GET_BBLOCK (cfg, tblock, target + (gint32)read32(ip));
9542 targets [i] = tblock;
9543 targets [i]->flags |= BB_INDIRECT_JUMP_TARGET;
9547 if (sp != stack_start) {
9549 * Link the current bb with the targets as well, so handle_stack_args
9550 * will set their in_stack correctly.
9552 link_bblock (cfg, cfg->cbb, default_bblock);
9553 for (i = 0; i < n; ++i)
9554 link_bblock (cfg, cfg->cbb, targets [i]);
9556 handle_stack_args (cfg, stack_start, sp - stack_start);
9558 CHECK_UNVERIFIABLE (cfg);
9560 /* Undo the links */
9561 mono_unlink_bblock (cfg, cfg->cbb, default_bblock);
9562 for (i = 0; i < n; ++i)
9563 mono_unlink_bblock (cfg, cfg->cbb, targets [i]);
9566 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ICOMPARE_IMM, -1, src1->dreg, n);
9567 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBGE_UN, default_bblock);
9569 for (i = 0; i < n; ++i)
9570 link_bblock (cfg, cfg->cbb, targets [i]);
9572 table = (MonoJumpInfoBBTable *)mono_mempool_alloc (cfg->mempool, sizeof (MonoJumpInfoBBTable));
9573 table->table = targets;
9574 table->table_size = n;
9576 use_op_switch = FALSE;
9578 /* ARM implements SWITCH statements differently */
9579 /* FIXME: Make it use the generic implementation */
9580 if (!cfg->compile_aot)
9581 use_op_switch = TRUE;
9584 if (COMPILE_LLVM (cfg))
9585 use_op_switch = TRUE;
9587 cfg->cbb->has_jump_table = 1;
9589 if (use_op_switch) {
9590 MONO_INST_NEW (cfg, ins, OP_SWITCH);
9591 ins->sreg1 = src1->dreg;
9592 ins->inst_p0 = table;
9593 ins->inst_many_bb = targets;
9594 ins->klass = (MonoClass *)GUINT_TO_POINTER (n);
9595 MONO_ADD_INS (cfg->cbb, ins);
9597 if (sizeof (gpointer) == 8)
9598 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SHL_IMM, offset_reg, src1->dreg, 3);
9600 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SHL_IMM, offset_reg, src1->dreg, 2);
9602 #if SIZEOF_REGISTER == 8
9603 /* The upper word might not be zero, and we add it to a 64 bit address later */
9604 MONO_EMIT_NEW_UNALU (cfg, OP_ZEXT_I4, offset_reg, offset_reg);
9607 if (cfg->compile_aot) {
9608 MONO_EMIT_NEW_AOTCONST (cfg, table_reg, table, MONO_PATCH_INFO_SWITCH);
9610 MONO_INST_NEW (cfg, ins, OP_JUMP_TABLE);
9611 ins->inst_c1 = MONO_PATCH_INFO_SWITCH;
9612 ins->inst_p0 = table;
9613 ins->dreg = table_reg;
9614 MONO_ADD_INS (cfg->cbb, ins);
9617 /* FIXME: Use load_memindex */
9618 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, sum_reg, table_reg, offset_reg);
9619 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, target_reg, sum_reg, 0);
9620 MONO_EMIT_NEW_UNALU (cfg, OP_BR_REG, -1, target_reg);
9622 start_new_bblock = 1;
9623 inline_costs += (BRANCH_COST * 2);
9640 ins = mini_emit_memory_load (cfg, &ldind_to_type (*ip)->byval_arg, sp [0], 0, ins_flag);
9656 if (ins_flag & MONO_INST_VOLATILE) {
9657 /* Volatile stores have release semantics, see 12.6.7 in Ecma 335 */
9658 mini_emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_REL);
9661 NEW_STORE_MEMBASE (cfg, ins, stind_to_store_membase (*ip), sp [0]->dreg, 0, sp [1]->dreg);
9662 ins->flags |= ins_flag;
9665 MONO_ADD_INS (cfg->cbb, ins);
9667 if (cfg->gen_write_barriers && *ip == CEE_STIND_REF && method->wrapper_type != MONO_WRAPPER_WRITE_BARRIER && !MONO_INS_IS_PCONST_NULL (sp [1]))
9668 mini_emit_write_barrier (cfg, sp [0], sp [1]);
9677 MONO_INST_NEW (cfg, ins, (*ip));
9679 ins->sreg1 = sp [0]->dreg;
9680 ins->sreg2 = sp [1]->dreg;
9681 type_from_op (cfg, ins, sp [0], sp [1]);
9683 ins->dreg = alloc_dreg ((cfg), (MonoStackType)(ins)->type);
9685 /* Use the immediate opcodes if possible */
9686 if ((sp [1]->opcode == OP_ICONST) && mono_arch_is_inst_imm (sp [1]->inst_c0)) {
9687 int imm_opcode = mono_op_to_op_imm_noemul (ins->opcode);
9688 if (imm_opcode != -1) {
9689 ins->opcode = imm_opcode;
9690 ins->inst_p1 = (gpointer)(gssize)(sp [1]->inst_c0);
9693 NULLIFY_INS (sp [1]);
9697 MONO_ADD_INS ((cfg)->cbb, (ins));
9699 *sp++ = mono_decompose_opcode (cfg, ins);
9716 MONO_INST_NEW (cfg, ins, (*ip));
9718 ins->sreg1 = sp [0]->dreg;
9719 ins->sreg2 = sp [1]->dreg;
9720 type_from_op (cfg, ins, sp [0], sp [1]);
9722 add_widen_op (cfg, ins, &sp [0], &sp [1]);
9723 ins->dreg = alloc_dreg ((cfg), (MonoStackType)(ins)->type);
9725 /* FIXME: Pass opcode to is_inst_imm */
9727 /* Use the immediate opcodes if possible */
9728 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)) {
9729 int imm_opcode = mono_op_to_op_imm_noemul (ins->opcode);
9730 if (imm_opcode != -1) {
9731 ins->opcode = imm_opcode;
9732 if (sp [1]->opcode == OP_I8CONST) {
9733 #if SIZEOF_REGISTER == 8
9734 ins->inst_imm = sp [1]->inst_l;
9736 ins->inst_ls_word = sp [1]->inst_ls_word;
9737 ins->inst_ms_word = sp [1]->inst_ms_word;
9741 ins->inst_imm = (gssize)(sp [1]->inst_c0);
9744 /* Might be followed by an instruction added by add_widen_op */
9745 if (sp [1]->next == NULL)
9746 NULLIFY_INS (sp [1]);
9749 MONO_ADD_INS ((cfg)->cbb, (ins));
9751 *sp++ = mono_decompose_opcode (cfg, ins);
9764 case CEE_CONV_OVF_I8:
9765 case CEE_CONV_OVF_U8:
9769 /* Special case this earlier so we have long constants in the IR */
9770 if ((((*ip) == CEE_CONV_I8) || ((*ip) == CEE_CONV_U8)) && (sp [-1]->opcode == OP_ICONST)) {
9771 int data = sp [-1]->inst_c0;
9772 sp [-1]->opcode = OP_I8CONST;
9773 sp [-1]->type = STACK_I8;
9774 #if SIZEOF_REGISTER == 8
9775 if ((*ip) == CEE_CONV_U8)
9776 sp [-1]->inst_c0 = (guint32)data;
9778 sp [-1]->inst_c0 = data;
9780 sp [-1]->inst_ls_word = data;
9781 if ((*ip) == CEE_CONV_U8)
9782 sp [-1]->inst_ms_word = 0;
9784 sp [-1]->inst_ms_word = (data < 0) ? -1 : 0;
9786 sp [-1]->dreg = alloc_dreg (cfg, STACK_I8);
9793 case CEE_CONV_OVF_I4:
9794 case CEE_CONV_OVF_I1:
9795 case CEE_CONV_OVF_I2:
9796 case CEE_CONV_OVF_I:
9797 case CEE_CONV_OVF_U:
9800 if (sp [-1]->type == STACK_R8 || sp [-1]->type == STACK_R4) {
9801 ADD_UNOP (CEE_CONV_OVF_I8);
9808 case CEE_CONV_OVF_U1:
9809 case CEE_CONV_OVF_U2:
9810 case CEE_CONV_OVF_U4:
9813 if (sp [-1]->type == STACK_R8 || sp [-1]->type == STACK_R4) {
9814 ADD_UNOP (CEE_CONV_OVF_U8);
9821 case CEE_CONV_OVF_I1_UN:
9822 case CEE_CONV_OVF_I2_UN:
9823 case CEE_CONV_OVF_I4_UN:
9824 case CEE_CONV_OVF_I8_UN:
9825 case CEE_CONV_OVF_U1_UN:
9826 case CEE_CONV_OVF_U2_UN:
9827 case CEE_CONV_OVF_U4_UN:
9828 case CEE_CONV_OVF_U8_UN:
9829 case CEE_CONV_OVF_I_UN:
9830 case CEE_CONV_OVF_U_UN:
9837 CHECK_CFG_EXCEPTION;
9841 case CEE_ADD_OVF_UN:
9843 case CEE_MUL_OVF_UN:
9845 case CEE_SUB_OVF_UN:
9851 GSHAREDVT_FAILURE (*ip);
9854 token = read32 (ip + 1);
9855 klass = mini_get_class (method, token, generic_context);
9856 CHECK_TYPELOAD (klass);
9858 mini_emit_memory_copy (cfg, sp [0], sp [1], klass, FALSE, ins_flag);
9869 token = read32 (ip + 1);
9870 klass = mini_get_class (method, token, generic_context);
9871 CHECK_TYPELOAD (klass);
9873 /* Optimize the common ldobj+stloc combination */
9883 loc_index = ip [5] - CEE_STLOC_0;
9890 if ((loc_index != -1) && ip_in_bb (cfg, cfg->cbb, ip + 5)) {
9891 CHECK_LOCAL (loc_index);
9893 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, sp [0]->dreg, 0);
9894 ins->dreg = cfg->locals [loc_index]->dreg;
9895 ins->flags |= ins_flag;
9898 if (ins_flag & MONO_INST_VOLATILE) {
9899 /* Volatile loads have acquire semantics, see 12.6.7 in Ecma 335 */
9900 mini_emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_ACQ);
9906 /* Optimize the ldobj+stobj combination */
9907 if (((ip [5] == CEE_STOBJ) && ip_in_bb (cfg, cfg->cbb, ip + 5) && read32 (ip + 6) == token)) {
9912 mini_emit_memory_copy (cfg, sp [0], sp [1], klass, FALSE, ins_flag);
9919 ins = mini_emit_memory_load (cfg, &klass->byval_arg, sp [0], 0, ins_flag);
9928 CHECK_STACK_OVF (1);
9930 n = read32 (ip + 1);
9932 if (method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD) {
9933 EMIT_NEW_PCONST (cfg, ins, mono_method_get_wrapper_data (method, n));
9934 ins->type = STACK_OBJ;
9937 else if (method->wrapper_type != MONO_WRAPPER_NONE) {
9938 MonoInst *iargs [1];
9939 char *str = (char *)mono_method_get_wrapper_data (method, n);
9941 if (cfg->compile_aot)
9942 EMIT_NEW_LDSTRLITCONST (cfg, iargs [0], str);
9944 EMIT_NEW_PCONST (cfg, iargs [0], str);
9945 *sp = mono_emit_jit_icall (cfg, mono_string_new_wrapper, iargs);
9947 if (cfg->opt & MONO_OPT_SHARED) {
9948 MonoInst *iargs [3];
9950 if (cfg->compile_aot) {
9951 cfg->ldstr_list = g_list_prepend (cfg->ldstr_list, GINT_TO_POINTER (n));
9953 EMIT_NEW_DOMAINCONST (cfg, iargs [0]);
9954 EMIT_NEW_IMAGECONST (cfg, iargs [1], image);
9955 EMIT_NEW_ICONST (cfg, iargs [2], mono_metadata_token_index (n));
9956 *sp = mono_emit_jit_icall (cfg, ves_icall_mono_ldstr, iargs);
9957 mono_ldstr_checked (cfg->domain, image, mono_metadata_token_index (n), &cfg->error);
9960 if (cfg->cbb->out_of_line) {
9961 MonoInst *iargs [2];
9963 if (image == mono_defaults.corlib) {
9965 * Avoid relocations in AOT and save some space by using a
9966 * version of helper_ldstr specialized to mscorlib.
9968 EMIT_NEW_ICONST (cfg, iargs [0], mono_metadata_token_index (n));
9969 *sp = mono_emit_jit_icall (cfg, mono_helper_ldstr_mscorlib, iargs);
9971 /* Avoid creating the string object */
9972 EMIT_NEW_IMAGECONST (cfg, iargs [0], image);
9973 EMIT_NEW_ICONST (cfg, iargs [1], mono_metadata_token_index (n));
9974 *sp = mono_emit_jit_icall (cfg, mono_helper_ldstr, iargs);
9978 if (cfg->compile_aot) {
9979 NEW_LDSTRCONST (cfg, ins, image, n);
9981 MONO_ADD_INS (cfg->cbb, ins);
9984 NEW_PCONST (cfg, ins, NULL);
9985 ins->type = STACK_OBJ;
9986 ins->inst_p0 = mono_ldstr_checked (cfg->domain, image, mono_metadata_token_index (n), &cfg->error);
9990 OUT_OF_MEMORY_FAILURE;
9993 MONO_ADD_INS (cfg->cbb, ins);
10002 MonoInst *iargs [2];
10003 MonoMethodSignature *fsig;
10006 MonoInst *vtable_arg = NULL;
10009 token = read32 (ip + 1);
10010 cmethod = mini_get_method (cfg, method, token, NULL, generic_context);
10013 fsig = mono_method_get_signature_checked (cmethod, image, token, generic_context, &cfg->error);
10016 mono_save_token_info (cfg, image, token, cmethod);
10018 if (!mono_class_init (cmethod->klass))
10019 TYPE_LOAD_ERROR (cmethod->klass);
10021 context_used = mini_method_check_context_used (cfg, cmethod);
10023 if (mono_security_core_clr_enabled ())
10024 ensure_method_is_allowed_to_call_method (cfg, method, cmethod);
10026 if (cfg->gshared && cmethod && cmethod->klass != method->klass && mono_class_is_ginst (cmethod->klass) && mono_method_is_generic_sharable (cmethod, TRUE) && mono_class_needs_cctor_run (cmethod->klass, method)) {
10027 emit_class_init (cfg, cmethod->klass);
10028 CHECK_TYPELOAD (cmethod->klass);
10032 if (cfg->gsharedvt) {
10033 if (mini_is_gsharedvt_variable_signature (sig))
10034 GSHAREDVT_FAILURE (*ip);
10038 n = fsig->param_count;
10042 * Generate smaller code for the common newobj <exception> instruction in
10043 * argument checking code.
10045 if (cfg->cbb->out_of_line && cmethod->klass->image == mono_defaults.corlib &&
10046 is_exception_class (cmethod->klass) && n <= 2 &&
10047 ((n < 1) || (!fsig->params [0]->byref && fsig->params [0]->type == MONO_TYPE_STRING)) &&
10048 ((n < 2) || (!fsig->params [1]->byref && fsig->params [1]->type == MONO_TYPE_STRING))) {
10049 MonoInst *iargs [3];
10053 EMIT_NEW_ICONST (cfg, iargs [0], cmethod->klass->type_token);
10056 *sp ++ = mono_emit_jit_icall (cfg, mono_create_corlib_exception_0, iargs);
10059 iargs [1] = sp [0];
10060 *sp ++ = mono_emit_jit_icall (cfg, mono_create_corlib_exception_1, iargs);
10063 iargs [1] = sp [0];
10064 iargs [2] = sp [1];
10065 *sp ++ = mono_emit_jit_icall (cfg, mono_create_corlib_exception_2, iargs);
10068 g_assert_not_reached ();
10076 /* move the args to allow room for 'this' in the first position */
10082 /* check_call_signature () requires sp[0] to be set */
10083 this_ins.type = STACK_OBJ;
10084 sp [0] = &this_ins;
10085 if (check_call_signature (cfg, fsig, sp))
10090 if (mini_class_is_system_array (cmethod->klass)) {
10091 *sp = emit_get_rgctx_method (cfg, context_used,
10092 cmethod, MONO_RGCTX_INFO_METHOD);
10094 /* Avoid varargs in the common case */
10095 if (fsig->param_count == 1)
10096 alloc = mono_emit_jit_icall (cfg, mono_array_new_1, sp);
10097 else if (fsig->param_count == 2)
10098 alloc = mono_emit_jit_icall (cfg, mono_array_new_2, sp);
10099 else if (fsig->param_count == 3)
10100 alloc = mono_emit_jit_icall (cfg, mono_array_new_3, sp);
10101 else if (fsig->param_count == 4)
10102 alloc = mono_emit_jit_icall (cfg, mono_array_new_4, sp);
10104 alloc = handle_array_new (cfg, fsig->param_count, sp, ip);
10105 } else if (cmethod->string_ctor) {
10106 g_assert (!context_used);
10107 g_assert (!vtable_arg);
10108 /* we simply pass a null pointer */
10109 EMIT_NEW_PCONST (cfg, *sp, NULL);
10110 /* now call the string ctor */
10111 alloc = mono_emit_method_call_full (cfg, cmethod, fsig, FALSE, sp, NULL, NULL, NULL);
10113 if (cmethod->klass->valuetype) {
10114 iargs [0] = mono_compile_create_var (cfg, &cmethod->klass->byval_arg, OP_LOCAL);
10115 emit_init_rvar (cfg, iargs [0]->dreg, &cmethod->klass->byval_arg);
10116 EMIT_NEW_TEMPLOADA (cfg, *sp, iargs [0]->inst_c0);
10121 * The code generated by mini_emit_virtual_call () expects
10122 * iargs [0] to be a boxed instance, but luckily the vcall
10123 * will be transformed into a normal call there.
10125 } else if (context_used) {
10126 alloc = handle_alloc (cfg, cmethod->klass, FALSE, context_used);
10129 MonoVTable *vtable = NULL;
10131 if (!cfg->compile_aot)
10132 vtable = mono_class_vtable (cfg->domain, cmethod->klass);
10133 CHECK_TYPELOAD (cmethod->klass);
10136 * TypeInitializationExceptions thrown from the mono_runtime_class_init
10137 * call in mono_jit_runtime_invoke () can abort the finalizer thread.
10138 * As a workaround, we call class cctors before allocating objects.
10140 if (mini_field_access_needs_cctor_run (cfg, method, cmethod->klass, vtable) && !(g_slist_find (class_inits, cmethod->klass))) {
10141 emit_class_init (cfg, cmethod->klass);
10142 if (cfg->verbose_level > 2)
10143 printf ("class %s.%s needs init call for ctor\n", cmethod->klass->name_space, cmethod->klass->name);
10144 class_inits = g_slist_prepend (class_inits, cmethod->klass);
10147 alloc = handle_alloc (cfg, cmethod->klass, FALSE, 0);
10150 CHECK_CFG_EXCEPTION; /*for handle_alloc*/
10153 MONO_EMIT_NEW_UNALU (cfg, OP_NOT_NULL, -1, alloc->dreg);
10155 /* Now call the actual ctor */
10156 handle_ctor_call (cfg, cmethod, fsig, context_used, sp, ip, &inline_costs);
10157 CHECK_CFG_EXCEPTION;
10160 if (alloc == NULL) {
10162 EMIT_NEW_TEMPLOAD (cfg, ins, iargs [0]->inst_c0);
10163 type_to_eval_stack_type (cfg, &ins->klass->byval_arg, ins);
10171 if (!(seq_point_locs && mono_bitset_test_fast (seq_point_locs, ip - header->code)))
10172 emit_seq_point (cfg, method, ip, FALSE, TRUE);
10175 case CEE_CASTCLASS:
10180 token = read32 (ip + 1);
10181 klass = mini_get_class (method, token, generic_context);
10182 CHECK_TYPELOAD (klass);
10183 if (sp [0]->type != STACK_OBJ)
10186 MONO_INST_NEW (cfg, ins, *ip == CEE_ISINST ? OP_ISINST : OP_CASTCLASS);
10187 ins->dreg = alloc_preg (cfg);
10188 ins->sreg1 = (*sp)->dreg;
10189 ins->klass = klass;
10190 ins->type = STACK_OBJ;
10191 MONO_ADD_INS (cfg->cbb, ins);
10193 CHECK_CFG_EXCEPTION;
10197 cfg->flags |= MONO_CFG_HAS_TYPE_CHECK;
10200 case CEE_UNBOX_ANY: {
10201 MonoInst *res, *addr;
10206 token = read32 (ip + 1);
10207 klass = mini_get_class (method, token, generic_context);
10208 CHECK_TYPELOAD (klass);
10210 mono_save_token_info (cfg, image, token, klass);
10212 context_used = mini_class_check_context_used (cfg, klass);
10214 if (mini_is_gsharedvt_klass (klass)) {
10215 res = handle_unbox_gsharedvt (cfg, klass, *sp);
10217 } else if (generic_class_is_reference_type (cfg, klass)) {
10218 if (MONO_INS_IS_PCONST_NULL (*sp)) {
10219 EMIT_NEW_PCONST (cfg, res, NULL);
10220 res->type = STACK_OBJ;
10222 MONO_INST_NEW (cfg, res, OP_CASTCLASS);
10223 res->dreg = alloc_preg (cfg);
10224 res->sreg1 = (*sp)->dreg;
10225 res->klass = klass;
10226 res->type = STACK_OBJ;
10227 MONO_ADD_INS (cfg->cbb, res);
10228 cfg->flags |= MONO_CFG_HAS_TYPE_CHECK;
10230 } else if (mono_class_is_nullable (klass)) {
10231 res = handle_unbox_nullable (cfg, *sp, klass, context_used);
10233 addr = handle_unbox (cfg, klass, sp, context_used);
10235 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, addr->dreg, 0);
10246 MonoClass *enum_class;
10247 MonoMethod *has_flag;
10253 token = read32 (ip + 1);
10254 klass = mini_get_class (method, token, generic_context);
10255 CHECK_TYPELOAD (klass);
10257 mono_save_token_info (cfg, image, token, klass);
10259 context_used = mini_class_check_context_used (cfg, klass);
10261 if (generic_class_is_reference_type (cfg, klass)) {
10267 if (klass == mono_defaults.void_class)
10269 if (target_type_is_incompatible (cfg, &klass->byval_arg, *sp))
10271 /* frequent check in generic code: box (struct), brtrue */
10276 * <push int/long ptr>
10279 * constrained. MyFlags
10280 * callvirt instace bool class [mscorlib] System.Enum::HasFlag (class [mscorlib] System.Enum)
10282 * If we find this sequence and the operand types on box and constrained
10283 * are equal, we can emit a specialized instruction sequence instead of
10284 * the very slow HasFlag () call.
10286 if ((cfg->opt & MONO_OPT_INTRINS) &&
10287 /* Cheap checks first. */
10288 ip + 5 + 6 + 5 < end &&
10289 ip [5] == CEE_PREFIX1 &&
10290 ip [6] == CEE_CONSTRAINED_ &&
10291 ip [11] == CEE_CALLVIRT &&
10292 ip_in_bb (cfg, cfg->cbb, ip + 5 + 6 + 5) &&
10293 mono_class_is_enum (klass) &&
10294 (enum_class = mini_get_class (method, read32 (ip + 7), generic_context)) &&
10295 (has_flag = mini_get_method (cfg, method, read32 (ip + 12), NULL, generic_context)) &&
10296 has_flag->klass == mono_defaults.enum_class &&
10297 !strcmp (has_flag->name, "HasFlag") &&
10298 has_flag->signature->hasthis &&
10299 has_flag->signature->param_count == 1) {
10300 CHECK_TYPELOAD (enum_class);
10302 if (enum_class == klass) {
10303 MonoInst *enum_this, *enum_flag;
10308 enum_this = sp [0];
10309 enum_flag = sp [1];
10311 *sp++ = handle_enum_has_flag (cfg, klass, enum_this, enum_flag);
10316 // FIXME: LLVM can't handle the inconsistent bb linking
10317 if (!mono_class_is_nullable (klass) &&
10318 !mini_is_gsharedvt_klass (klass) &&
10319 ip + 5 < end && ip_in_bb (cfg, cfg->cbb, ip + 5) &&
10320 (ip [5] == CEE_BRTRUE ||
10321 ip [5] == CEE_BRTRUE_S ||
10322 ip [5] == CEE_BRFALSE ||
10323 ip [5] == CEE_BRFALSE_S)) {
10324 gboolean is_true = ip [5] == CEE_BRTRUE || ip [5] == CEE_BRTRUE_S;
10326 MonoBasicBlock *true_bb, *false_bb;
10330 if (cfg->verbose_level > 3) {
10331 printf ("converting (in B%d: stack: %d) %s", cfg->cbb->block_num, (int)(sp - stack_start), mono_disasm_code_one (NULL, method, ip, NULL));
10332 printf ("<box+brtrue opt>\n");
10337 case CEE_BRFALSE_S:
10340 target = ip + 1 + (signed char)(*ip);
10347 target = ip + 4 + (gint)(read32 (ip));
10351 g_assert_not_reached ();
10355 * We need to link both bblocks, since it is needed for handling stack
10356 * arguments correctly (See test_0_box_brtrue_opt_regress_81102).
10357 * Branching to only one of them would lead to inconsistencies, so
10358 * generate an ICONST+BRTRUE, the branch opts will get rid of them.
10360 GET_BBLOCK (cfg, true_bb, target);
10361 GET_BBLOCK (cfg, false_bb, ip);
10363 mono_link_bblock (cfg, cfg->cbb, true_bb);
10364 mono_link_bblock (cfg, cfg->cbb, false_bb);
10366 if (sp != stack_start) {
10367 handle_stack_args (cfg, stack_start, sp - stack_start);
10369 CHECK_UNVERIFIABLE (cfg);
10372 if (COMPILE_LLVM (cfg)) {
10373 dreg = alloc_ireg (cfg);
10374 MONO_EMIT_NEW_ICONST (cfg, dreg, 0);
10375 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, dreg, is_true ? 0 : 1);
10377 MONO_EMIT_NEW_BRANCH_BLOCK2 (cfg, OP_IBEQ, true_bb, false_bb);
10379 /* The JIT can't eliminate the iconst+compare */
10380 MONO_INST_NEW (cfg, ins, OP_BR);
10381 ins->inst_target_bb = is_true ? true_bb : false_bb;
10382 MONO_ADD_INS (cfg->cbb, ins);
10385 start_new_bblock = 1;
10389 *sp++ = handle_box (cfg, val, klass, context_used);
10391 CHECK_CFG_EXCEPTION;
10400 token = read32 (ip + 1);
10401 klass = mini_get_class (method, token, generic_context);
10402 CHECK_TYPELOAD (klass);
10404 mono_save_token_info (cfg, image, token, klass);
10406 context_used = mini_class_check_context_used (cfg, klass);
10408 if (mono_class_is_nullable (klass)) {
10411 val = handle_unbox_nullable (cfg, *sp, klass, context_used);
10412 EMIT_NEW_VARLOADA (cfg, ins, get_vreg_to_inst (cfg, val->dreg), &val->klass->byval_arg);
10416 ins = handle_unbox (cfg, klass, sp, context_used);
10429 MonoClassField *field;
10430 #ifndef DISABLE_REMOTING
10434 gboolean is_instance;
10436 gpointer addr = NULL;
10437 gboolean is_special_static;
10439 MonoInst *store_val = NULL;
10440 MonoInst *thread_ins;
10443 is_instance = (op == CEE_LDFLD || op == CEE_LDFLDA || op == CEE_STFLD);
10445 if (op == CEE_STFLD) {
10448 store_val = sp [1];
10453 if (sp [0]->type == STACK_I4 || sp [0]->type == STACK_I8 || sp [0]->type == STACK_R8)
10455 if (*ip != CEE_LDFLD && sp [0]->type == STACK_VTYPE)
10458 if (op == CEE_STSFLD) {
10461 store_val = sp [0];
10466 token = read32 (ip + 1);
10467 if (method->wrapper_type != MONO_WRAPPER_NONE) {
10468 field = (MonoClassField *)mono_method_get_wrapper_data (method, token);
10469 klass = field->parent;
10472 field = mono_field_from_token_checked (image, token, &klass, generic_context, &cfg->error);
10475 if (!dont_verify && !cfg->skip_visibility && !mono_method_can_access_field (method, field))
10476 FIELD_ACCESS_FAILURE (method, field);
10477 mono_class_init (klass);
10479 /* if the class is Critical then transparent code cannot access it's fields */
10480 if (!is_instance && mono_security_core_clr_enabled ())
10481 ensure_method_is_allowed_to_access_field (cfg, method, field);
10483 /* XXX this is technically required but, so far (SL2), no [SecurityCritical] types (not many exists) have
10484 any visible *instance* field (in fact there's a single case for a static field in Marshal) XXX
10485 if (mono_security_core_clr_enabled ())
10486 ensure_method_is_allowed_to_access_field (cfg, method, field);
10489 ftype = mono_field_get_type (field);
10492 * LDFLD etc. is usable on static fields as well, so convert those cases to
10495 if (is_instance && ftype->attrs & FIELD_ATTRIBUTE_STATIC) {
10507 g_assert_not_reached ();
10509 is_instance = FALSE;
10512 context_used = mini_class_check_context_used (cfg, klass);
10514 /* INSTANCE CASE */
10516 foffset = klass->valuetype? field->offset - sizeof (MonoObject): field->offset;
10517 if (op == CEE_STFLD) {
10518 if (target_type_is_incompatible (cfg, field->type, sp [1]))
10520 #ifndef DISABLE_REMOTING
10521 if ((mono_class_is_marshalbyref (klass) && !MONO_CHECK_THIS (sp [0])) || mono_class_is_contextbound (klass) || klass == mono_defaults.marshalbyrefobject_class) {
10522 MonoMethod *stfld_wrapper = mono_marshal_get_stfld_wrapper (field->type);
10523 MonoInst *iargs [5];
10525 GSHAREDVT_FAILURE (op);
10527 iargs [0] = sp [0];
10528 EMIT_NEW_CLASSCONST (cfg, iargs [1], klass);
10529 EMIT_NEW_FIELDCONST (cfg, iargs [2], field);
10530 EMIT_NEW_ICONST (cfg, iargs [3], klass->valuetype ? field->offset - sizeof (MonoObject) :
10532 iargs [4] = sp [1];
10534 if (cfg->opt & MONO_OPT_INLINE || cfg->compile_aot) {
10535 costs = inline_method (cfg, stfld_wrapper, mono_method_signature (stfld_wrapper),
10536 iargs, ip, cfg->real_offset, TRUE);
10537 CHECK_CFG_EXCEPTION;
10538 g_assert (costs > 0);
10540 cfg->real_offset += 5;
10542 inline_costs += costs;
10544 mono_emit_method_call (cfg, stfld_wrapper, iargs, NULL);
10549 MonoInst *store, *wbarrier_ptr_ins = NULL;
10551 MONO_EMIT_NULL_CHECK (cfg, sp [0]->dreg);
10553 if (ins_flag & MONO_INST_VOLATILE) {
10554 /* Volatile stores have release semantics, see 12.6.7 in Ecma 335 */
10555 mini_emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_REL);
10558 if (mini_is_gsharedvt_klass (klass)) {
10559 MonoInst *offset_ins;
10561 context_used = mini_class_check_context_used (cfg, klass);
10563 offset_ins = emit_get_gsharedvt_info (cfg, field, MONO_RGCTX_INFO_FIELD_OFFSET);
10564 /* The value is offset by 1 */
10565 EMIT_NEW_BIALU_IMM (cfg, ins, OP_PSUB_IMM, offset_ins->dreg, offset_ins->dreg, 1);
10566 dreg = alloc_ireg_mp (cfg);
10567 EMIT_NEW_BIALU (cfg, ins, OP_PADD, dreg, sp [0]->dreg, offset_ins->dreg);
10568 wbarrier_ptr_ins = ins;
10569 /* The decomposition will call mini_emit_memory_copy () which will emit a wbarrier if needed */
10570 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, store, field->type, dreg, 0, sp [1]->dreg);
10572 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, store, field->type, sp [0]->dreg, foffset, sp [1]->dreg);
10574 if (sp [0]->opcode != OP_LDADDR)
10575 store->flags |= MONO_INST_FAULT;
10577 if (cfg->gen_write_barriers && mini_type_to_stind (cfg, field->type) == CEE_STIND_REF && !MONO_INS_IS_PCONST_NULL (sp [1])) {
10578 if (mini_is_gsharedvt_klass (klass)) {
10579 g_assert (wbarrier_ptr_ins);
10580 mini_emit_write_barrier (cfg, wbarrier_ptr_ins, sp [1]);
10582 /* insert call to write barrier */
10586 dreg = alloc_ireg_mp (cfg);
10587 EMIT_NEW_BIALU_IMM (cfg, ptr, OP_PADD_IMM, dreg, sp [0]->dreg, foffset);
10588 mini_emit_write_barrier (cfg, ptr, sp [1]);
10592 store->flags |= ins_flag;
10599 #ifndef DISABLE_REMOTING
10600 if (is_instance && ((mono_class_is_marshalbyref (klass) && !MONO_CHECK_THIS (sp [0])) || mono_class_is_contextbound (klass) || klass == mono_defaults.marshalbyrefobject_class)) {
10601 MonoMethod *wrapper = (op == CEE_LDFLDA) ? mono_marshal_get_ldflda_wrapper (field->type) : mono_marshal_get_ldfld_wrapper (field->type);
10602 MonoInst *iargs [4];
10604 GSHAREDVT_FAILURE (op);
10606 iargs [0] = sp [0];
10607 EMIT_NEW_CLASSCONST (cfg, iargs [1], klass);
10608 EMIT_NEW_FIELDCONST (cfg, iargs [2], field);
10609 EMIT_NEW_ICONST (cfg, iargs [3], klass->valuetype ? field->offset - sizeof (MonoObject) : field->offset);
10610 if (cfg->opt & MONO_OPT_INLINE || cfg->compile_aot) {
10611 costs = inline_method (cfg, wrapper, mono_method_signature (wrapper),
10612 iargs, ip, cfg->real_offset, TRUE);
10613 CHECK_CFG_EXCEPTION;
10614 g_assert (costs > 0);
10616 cfg->real_offset += 5;
10620 inline_costs += costs;
10622 ins = mono_emit_method_call (cfg, wrapper, iargs, NULL);
10628 if (sp [0]->type == STACK_VTYPE) {
10631 /* Have to compute the address of the variable */
10633 var = get_vreg_to_inst (cfg, sp [0]->dreg);
10635 var = mono_compile_create_var_for_vreg (cfg, &klass->byval_arg, OP_LOCAL, sp [0]->dreg);
10637 g_assert (var->klass == klass);
10639 EMIT_NEW_VARLOADA (cfg, ins, var, &var->klass->byval_arg);
10643 if (op == CEE_LDFLDA) {
10644 if (sp [0]->type == STACK_OBJ) {
10645 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, sp [0]->dreg, 0);
10646 MONO_EMIT_NEW_COND_EXC (cfg, EQ, "NullReferenceException");
10649 dreg = alloc_ireg_mp (cfg);
10651 if (mini_is_gsharedvt_klass (klass)) {
10652 MonoInst *offset_ins;
10654 offset_ins = emit_get_gsharedvt_info (cfg, field, MONO_RGCTX_INFO_FIELD_OFFSET);
10655 /* The value is offset by 1 */
10656 EMIT_NEW_BIALU_IMM (cfg, ins, OP_PSUB_IMM, offset_ins->dreg, offset_ins->dreg, 1);
10657 EMIT_NEW_BIALU (cfg, ins, OP_PADD, dreg, sp [0]->dreg, offset_ins->dreg);
10659 EMIT_NEW_BIALU_IMM (cfg, ins, OP_PADD_IMM, dreg, sp [0]->dreg, foffset);
10661 ins->klass = mono_class_from_mono_type (field->type);
10662 ins->type = STACK_MP;
10667 MONO_EMIT_NULL_CHECK (cfg, sp [0]->dreg);
10669 if (sp [0]->opcode == OP_LDADDR && klass->simd_type && cfg->opt & MONO_OPT_SIMD) {
10670 ins = mono_emit_simd_field_load (cfg, field, sp [0]);
10679 MonoInst *field_add_inst = sp [0];
10680 if (mini_is_gsharedvt_klass (klass)) {
10681 MonoInst *offset_ins;
10683 offset_ins = emit_get_gsharedvt_info (cfg, field, MONO_RGCTX_INFO_FIELD_OFFSET);
10684 /* The value is offset by 1 */
10685 EMIT_NEW_BIALU_IMM (cfg, ins, OP_PSUB_IMM, offset_ins->dreg, offset_ins->dreg, 1);
10686 EMIT_NEW_BIALU (cfg, field_add_inst, OP_PADD, alloc_ireg_mp (cfg), sp [0]->dreg, offset_ins->dreg);
10690 load = mini_emit_memory_load (cfg, field->type, field_add_inst, foffset, ins_flag);
10692 if (sp [0]->opcode != OP_LDADDR)
10693 load->flags |= MONO_INST_FAULT;
10705 context_used = mini_class_check_context_used (cfg, klass);
10707 if (ftype->attrs & FIELD_ATTRIBUTE_LITERAL) {
10708 mono_error_set_field_load (&cfg->error, field->parent, field->name, "Using static instructions with literal field");
10712 /* The special_static_fields field is init'd in mono_class_vtable, so it needs
10713 * to be called here.
10715 if (!context_used && !(cfg->opt & MONO_OPT_SHARED)) {
10716 mono_class_vtable (cfg->domain, klass);
10717 CHECK_TYPELOAD (klass);
10719 mono_domain_lock (cfg->domain);
10720 if (cfg->domain->special_static_fields)
10721 addr = g_hash_table_lookup (cfg->domain->special_static_fields, field);
10722 mono_domain_unlock (cfg->domain);
10724 is_special_static = mono_class_field_is_special_static (field);
10726 if (is_special_static && ((gsize)addr & 0x80000000) == 0)
10727 thread_ins = mono_create_tls_get (cfg, TLS_KEY_THREAD);
10731 /* Generate IR to compute the field address */
10732 if (is_special_static && ((gsize)addr & 0x80000000) == 0 && thread_ins && !(cfg->opt & MONO_OPT_SHARED) && !context_used) {
10734 * Fast access to TLS data
10735 * Inline version of get_thread_static_data () in
10739 int idx, static_data_reg, array_reg, dreg;
10741 if (context_used && cfg->gsharedvt && mini_is_gsharedvt_klass (klass))
10742 GSHAREDVT_FAILURE (op);
10744 static_data_reg = alloc_ireg (cfg);
10745 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, static_data_reg, thread_ins->dreg, MONO_STRUCT_OFFSET (MonoInternalThread, static_data));
10747 if (cfg->compile_aot) {
10748 int offset_reg, offset2_reg, idx_reg;
10750 /* For TLS variables, this will return the TLS offset */
10751 EMIT_NEW_SFLDACONST (cfg, ins, field);
10752 offset_reg = ins->dreg;
10753 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_IAND_IMM, offset_reg, offset_reg, 0x7fffffff);
10754 idx_reg = alloc_ireg (cfg);
10755 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_IAND_IMM, idx_reg, offset_reg, 0x3f);
10756 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ISHL_IMM, idx_reg, idx_reg, sizeof (gpointer) == 8 ? 3 : 2);
10757 MONO_EMIT_NEW_BIALU (cfg, OP_PADD, static_data_reg, static_data_reg, idx_reg);
10758 array_reg = alloc_ireg (cfg);
10759 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, array_reg, static_data_reg, 0);
10760 offset2_reg = alloc_ireg (cfg);
10761 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ISHR_UN_IMM, offset2_reg, offset_reg, 6);
10762 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_IAND_IMM, offset2_reg, offset2_reg, 0x1ffffff);
10763 dreg = alloc_ireg (cfg);
10764 EMIT_NEW_BIALU (cfg, ins, OP_PADD, dreg, array_reg, offset2_reg);
10766 offset = (gsize)addr & 0x7fffffff;
10767 idx = offset & 0x3f;
10769 array_reg = alloc_ireg (cfg);
10770 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, array_reg, static_data_reg, idx * sizeof (gpointer));
10771 dreg = alloc_ireg (cfg);
10772 EMIT_NEW_BIALU_IMM (cfg, ins, OP_ADD_IMM, dreg, array_reg, ((offset >> 6) & 0x1ffffff));
10774 } else if ((cfg->opt & MONO_OPT_SHARED) ||
10775 (cfg->compile_aot && is_special_static) ||
10776 (context_used && is_special_static)) {
10777 MonoInst *iargs [2];
10779 g_assert (field->parent);
10780 EMIT_NEW_DOMAINCONST (cfg, iargs [0]);
10781 if (context_used) {
10782 iargs [1] = emit_get_rgctx_field (cfg, context_used,
10783 field, MONO_RGCTX_INFO_CLASS_FIELD);
10785 EMIT_NEW_FIELDCONST (cfg, iargs [1], field);
10787 ins = mono_emit_jit_icall (cfg, mono_class_static_field_address, iargs);
10788 } else if (context_used) {
10789 MonoInst *static_data;
10792 g_print ("sharing static field access in %s.%s.%s - depth %d offset %d\n",
10793 method->klass->name_space, method->klass->name, method->name,
10794 depth, field->offset);
10797 if (mono_class_needs_cctor_run (klass, method))
10798 emit_class_init (cfg, klass);
10801 * The pointer we're computing here is
10803 * super_info.static_data + field->offset
10805 static_data = mini_emit_get_rgctx_klass (cfg, context_used,
10806 klass, MONO_RGCTX_INFO_STATIC_DATA);
10808 if (mini_is_gsharedvt_klass (klass)) {
10809 MonoInst *offset_ins;
10811 offset_ins = emit_get_rgctx_field (cfg, context_used, field, MONO_RGCTX_INFO_FIELD_OFFSET);
10812 /* The value is offset by 1 */
10813 EMIT_NEW_BIALU_IMM (cfg, ins, OP_PSUB_IMM, offset_ins->dreg, offset_ins->dreg, 1);
10814 dreg = alloc_ireg_mp (cfg);
10815 EMIT_NEW_BIALU (cfg, ins, OP_PADD, dreg, static_data->dreg, offset_ins->dreg);
10816 } else if (field->offset == 0) {
10819 int addr_reg = mono_alloc_preg (cfg);
10820 EMIT_NEW_BIALU_IMM (cfg, ins, OP_PADD_IMM, addr_reg, static_data->dreg, field->offset);
10822 } else if ((cfg->opt & MONO_OPT_SHARED) || (cfg->compile_aot && addr)) {
10823 MonoInst *iargs [2];
10825 g_assert (field->parent);
10826 EMIT_NEW_DOMAINCONST (cfg, iargs [0]);
10827 EMIT_NEW_FIELDCONST (cfg, iargs [1], field);
10828 ins = mono_emit_jit_icall (cfg, mono_class_static_field_address, iargs);
10830 MonoVTable *vtable = NULL;
10832 if (!cfg->compile_aot)
10833 vtable = mono_class_vtable (cfg->domain, klass);
10834 CHECK_TYPELOAD (klass);
10837 if (mini_field_access_needs_cctor_run (cfg, method, klass, vtable)) {
10838 if (!(g_slist_find (class_inits, klass))) {
10839 emit_class_init (cfg, klass);
10840 if (cfg->verbose_level > 2)
10841 printf ("class %s.%s needs init call for %s\n", klass->name_space, klass->name, mono_field_get_name (field));
10842 class_inits = g_slist_prepend (class_inits, klass);
10845 if (cfg->run_cctors) {
10846 /* This makes so that inline cannot trigger */
10847 /* .cctors: too many apps depend on them */
10848 /* running with a specific order... */
10850 if (! vtable->initialized)
10851 INLINE_FAILURE ("class init");
10852 if (!mono_runtime_class_init_full (vtable, &cfg->error)) {
10853 mono_cfg_set_exception (cfg, MONO_EXCEPTION_MONO_ERROR);
10854 goto exception_exit;
10858 if (cfg->compile_aot)
10859 EMIT_NEW_SFLDACONST (cfg, ins, field);
10862 addr = (char*)mono_vtable_get_static_field_data (vtable) + field->offset;
10864 EMIT_NEW_PCONST (cfg, ins, addr);
10867 MonoInst *iargs [1];
10868 EMIT_NEW_ICONST (cfg, iargs [0], GPOINTER_TO_UINT (addr));
10869 ins = mono_emit_jit_icall (cfg, mono_get_special_static_data, iargs);
10873 /* Generate IR to do the actual load/store operation */
10875 if ((op == CEE_STFLD || op == CEE_STSFLD) && (ins_flag & MONO_INST_VOLATILE)) {
10876 /* Volatile stores have release semantics, see 12.6.7 in Ecma 335 */
10877 mini_emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_REL);
10880 if (op == CEE_LDSFLDA) {
10881 ins->klass = mono_class_from_mono_type (ftype);
10882 ins->type = STACK_PTR;
10884 } else if (op == CEE_STSFLD) {
10887 EMIT_NEW_STORE_MEMBASE_TYPE (cfg, store, ftype, ins->dreg, 0, store_val->dreg);
10888 store->flags |= ins_flag;
10890 gboolean is_const = FALSE;
10891 MonoVTable *vtable = NULL;
10892 gpointer addr = NULL;
10894 if (!context_used) {
10895 vtable = mono_class_vtable (cfg->domain, klass);
10896 CHECK_TYPELOAD (klass);
10898 if ((ftype->attrs & FIELD_ATTRIBUTE_INIT_ONLY) && (((addr = mono_aot_readonly_field_override (field)) != NULL) ||
10899 (!context_used && !((cfg->opt & MONO_OPT_SHARED) || cfg->compile_aot) && vtable->initialized))) {
10900 int ro_type = ftype->type;
10902 addr = (char*)mono_vtable_get_static_field_data (vtable) + field->offset;
10903 if (ro_type == MONO_TYPE_VALUETYPE && ftype->data.klass->enumtype) {
10904 ro_type = mono_class_enum_basetype (ftype->data.klass)->type;
10907 GSHAREDVT_FAILURE (op);
10909 /* printf ("RO-FIELD %s.%s:%s\n", klass->name_space, klass->name, mono_field_get_name (field));*/
10912 case MONO_TYPE_BOOLEAN:
10914 EMIT_NEW_ICONST (cfg, *sp, *((guint8 *)addr));
10918 EMIT_NEW_ICONST (cfg, *sp, *((gint8 *)addr));
10921 case MONO_TYPE_CHAR:
10923 EMIT_NEW_ICONST (cfg, *sp, *((guint16 *)addr));
10927 EMIT_NEW_ICONST (cfg, *sp, *((gint16 *)addr));
10932 EMIT_NEW_ICONST (cfg, *sp, *((gint32 *)addr));
10936 EMIT_NEW_ICONST (cfg, *sp, *((guint32 *)addr));
10941 case MONO_TYPE_PTR:
10942 case MONO_TYPE_FNPTR:
10943 EMIT_NEW_PCONST (cfg, *sp, *((gpointer *)addr));
10944 type_to_eval_stack_type ((cfg), field->type, *sp);
10947 case MONO_TYPE_STRING:
10948 case MONO_TYPE_OBJECT:
10949 case MONO_TYPE_CLASS:
10950 case MONO_TYPE_SZARRAY:
10951 case MONO_TYPE_ARRAY:
10952 if (!mono_gc_is_moving ()) {
10953 EMIT_NEW_PCONST (cfg, *sp, *((gpointer *)addr));
10954 type_to_eval_stack_type ((cfg), field->type, *sp);
10962 EMIT_NEW_I8CONST (cfg, *sp, *((gint64 *)addr));
10967 case MONO_TYPE_VALUETYPE:
10977 CHECK_STACK_OVF (1);
10979 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, load, field->type, ins->dreg, 0);
10980 load->flags |= ins_flag;
10986 if ((op == CEE_LDFLD || op == CEE_LDSFLD) && (ins_flag & MONO_INST_VOLATILE)) {
10987 /* Volatile loads have acquire semantics, see 12.6.7 in Ecma 335 */
10988 mini_emit_memory_barrier (cfg, MONO_MEMORY_BARRIER_ACQ);
10999 token = read32 (ip + 1);
11000 klass = mini_get_class (method, token, generic_context);
11001 CHECK_TYPELOAD (klass);
11003 /* FIXME: should check item at sp [1] is compatible with the type of the store. */
11004 mini_emit_memory_store (cfg, &klass->byval_arg, sp [0], sp [1], ins_flag);
11015 const char *data_ptr;
11017 guint32 field_token;
11023 token = read32 (ip + 1);
11025 klass = mini_get_class (method, token, generic_context);
11026 CHECK_TYPELOAD (klass);
11027 if (klass->byval_arg.type == MONO_TYPE_VOID)
11030 context_used = mini_class_check_context_used (cfg, klass);
11032 if (sp [0]->type == STACK_I8 || (SIZEOF_VOID_P == 8 && sp [0]->type == STACK_PTR)) {
11033 MONO_INST_NEW (cfg, ins, OP_LCONV_TO_OVF_U4);
11034 ins->sreg1 = sp [0]->dreg;
11035 ins->type = STACK_I4;
11036 ins->dreg = alloc_ireg (cfg);
11037 MONO_ADD_INS (cfg->cbb, ins);
11038 *sp = mono_decompose_opcode (cfg, ins);
11041 if (context_used) {
11042 MonoInst *args [3];
11043 MonoClass *array_class = mono_array_class_get (klass, 1);
11044 MonoMethod *managed_alloc = mono_gc_get_managed_array_allocator (array_class);
11046 /* FIXME: Use OP_NEWARR and decompose later to help abcrem */
11049 args [0] = mini_emit_get_rgctx_klass (cfg, context_used,
11050 array_class, MONO_RGCTX_INFO_VTABLE);
11055 ins = mono_emit_method_call (cfg, managed_alloc, args, NULL);
11057 ins = mono_emit_jit_icall (cfg, ves_icall_array_new_specific, args);
11059 if (cfg->opt & MONO_OPT_SHARED) {
11060 /* Decompose now to avoid problems with references to the domainvar */
11061 MonoInst *iargs [3];
11063 EMIT_NEW_DOMAINCONST (cfg, iargs [0]);
11064 EMIT_NEW_CLASSCONST (cfg, iargs [1], klass);
11065 iargs [2] = sp [0];
11067 ins = mono_emit_jit_icall (cfg, ves_icall_array_new, iargs);
11069 /* Decompose later since it is needed by abcrem */
11070 MonoClass *array_type = mono_array_class_get (klass, 1);
11071 mono_class_vtable (cfg->domain, array_type);
11072 CHECK_TYPELOAD (array_type);
11074 MONO_INST_NEW (cfg, ins, OP_NEWARR);
11075 ins->dreg = alloc_ireg_ref (cfg);
11076 ins->sreg1 = sp [0]->dreg;
11077 ins->inst_newa_class = klass;
11078 ins->type = STACK_OBJ;
11079 ins->klass = array_type;
11080 MONO_ADD_INS (cfg->cbb, ins);
11081 cfg->flags |= MONO_CFG_HAS_ARRAY_ACCESS;
11082 cfg->cbb->has_array_access = TRUE;
11084 /* Needed so mono_emit_load_get_addr () gets called */
11085 mono_get_got_var (cfg);
11095 * we inline/optimize the initialization sequence if possible.
11096 * we should also allocate the array as not cleared, since we spend as much time clearing to 0 as initializing
11097 * for small sizes open code the memcpy
11098 * ensure the rva field is big enough
11100 if ((cfg->opt & MONO_OPT_INTRINS) && ip + 6 < end && ip_in_bb (cfg, cfg->cbb, ip + 6) && (len_ins->opcode == OP_ICONST) && (data_ptr = initialize_array_data (method, cfg->compile_aot, ip, klass, len_ins->inst_c0, &data_size, &field_token))) {
11101 MonoMethod *memcpy_method = mini_get_memcpy_method ();
11102 MonoInst *iargs [3];
11103 int add_reg = alloc_ireg_mp (cfg);
11105 EMIT_NEW_BIALU_IMM (cfg, iargs [0], OP_PADD_IMM, add_reg, ins->dreg, MONO_STRUCT_OFFSET (MonoArray, vector));
11106 if (cfg->compile_aot) {
11107 EMIT_NEW_AOTCONST_TOKEN (cfg, iargs [1], MONO_PATCH_INFO_RVA, method->klass->image, GPOINTER_TO_UINT(field_token), STACK_PTR, NULL);
11109 EMIT_NEW_PCONST (cfg, iargs [1], (char*)data_ptr);
11111 EMIT_NEW_ICONST (cfg, iargs [2], data_size);
11112 mono_emit_method_call (cfg, memcpy_method, iargs, NULL);
11121 if (sp [0]->type != STACK_OBJ)
11124 MONO_INST_NEW (cfg, ins, OP_LDLEN);
11125 ins->dreg = alloc_preg (cfg);
11126 ins->sreg1 = sp [0]->dreg;
11127 ins->type = STACK_I4;
11128 /* This flag will be inherited by the decomposition */
11129 ins->flags |= MONO_INST_FAULT;
11130 MONO_ADD_INS (cfg->cbb, ins);
11131 cfg->flags |= MONO_CFG_HAS_ARRAY_ACCESS;
11132 cfg->cbb->has_array_access = TRUE;
11140 if (sp [0]->type != STACK_OBJ)
11143 cfg->flags |= MONO_CFG_HAS_LDELEMA;
11145 klass = mini_get_class (method, read32 (ip + 1), generic_context);
11146 CHECK_TYPELOAD (klass);
11147 /* we need to make sure that this array is exactly the type it needs
11148 * to be for correctness. the wrappers are lax with their usage
11149 * so we need to ignore them here
11151 if (!klass->valuetype && method->wrapper_type == MONO_WRAPPER_NONE && !readonly) {
11152 MonoClass *array_class = mono_array_class_get (klass, 1);
11153 mini_emit_check_array_type (cfg, sp [0], array_class);
11154 CHECK_TYPELOAD (array_class);
11158 ins = mini_emit_ldelema_1_ins (cfg, klass, sp [0], sp [1], TRUE);
11163 case CEE_LDELEM_I1:
11164 case CEE_LDELEM_U1:
11165 case CEE_LDELEM_I2:
11166 case CEE_LDELEM_U2:
11167 case CEE_LDELEM_I4:
11168 case CEE_LDELEM_U4:
11169 case CEE_LDELEM_I8:
11171 case CEE_LDELEM_R4:
11172 case CEE_LDELEM_R8:
11173 case CEE_LDELEM_REF: {
11179 if (*ip == CEE_LDELEM) {
11181 token = read32 (ip + 1);
11182 klass = mini_get_class (method, token, generic_context);
11183 CHECK_TYPELOAD (klass);
11184 mono_class_init (klass);
11187 klass = array_access_to_klass (*ip);
11189 if (sp [0]->type != STACK_OBJ)
11192 cfg->flags |= MONO_CFG_HAS_LDELEMA;
11194 if (mini_is_gsharedvt_variable_klass (klass)) {
11195 // FIXME-VT: OP_ICONST optimization
11196 addr = mini_emit_ldelema_1_ins (cfg, klass, sp [0], sp [1], TRUE);
11197 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, addr->dreg, 0);
11198 ins->opcode = OP_LOADV_MEMBASE;
11199 } else if (sp [1]->opcode == OP_ICONST) {
11200 int array_reg = sp [0]->dreg;
11201 int index_reg = sp [1]->dreg;
11202 int offset = (mono_class_array_element_size (klass) * sp [1]->inst_c0) + MONO_STRUCT_OFFSET (MonoArray, vector);
11204 if (SIZEOF_REGISTER == 8 && COMPILE_LLVM (cfg))
11205 MONO_EMIT_NEW_UNALU (cfg, OP_ZEXT_I4, index_reg, index_reg);
11207 MONO_EMIT_BOUNDS_CHECK (cfg, array_reg, MonoArray, max_length, index_reg);
11208 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, array_reg, offset);
11210 addr = mini_emit_ldelema_1_ins (cfg, klass, sp [0], sp [1], TRUE);
11211 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, addr->dreg, 0);
11214 if (*ip == CEE_LDELEM)
11221 case CEE_STELEM_I1:
11222 case CEE_STELEM_I2:
11223 case CEE_STELEM_I4:
11224 case CEE_STELEM_I8:
11225 case CEE_STELEM_R4:
11226 case CEE_STELEM_R8:
11227 case CEE_STELEM_REF:
11232 cfg->flags |= MONO_CFG_HAS_LDELEMA;
11234 if (*ip == CEE_STELEM) {
11236 token = read32 (ip + 1);
11237 klass = mini_get_class (method, token, generic_context);
11238 CHECK_TYPELOAD (klass);
11239 mono_class_init (klass);
11242 klass = array_access_to_klass (*ip);
11244 if (sp [0]->type != STACK_OBJ)
11247 emit_array_store (cfg, klass, sp, TRUE);
11249 if (*ip == CEE_STELEM)
11256 case CEE_CKFINITE: {
11260 if (cfg->llvm_only) {
11261 MonoInst *iargs [1];
11263 iargs [0] = sp [0];
11264 *sp++ = mono_emit_jit_icall (cfg, mono_ckfinite, iargs);
11266 MONO_INST_NEW (cfg, ins, OP_CKFINITE);
11267 ins->sreg1 = sp [0]->dreg;
11268 ins->dreg = alloc_freg (cfg);
11269 ins->type = STACK_R8;
11270 MONO_ADD_INS (cfg->cbb, ins);
11272 *sp++ = mono_decompose_opcode (cfg, ins);
11278 case CEE_REFANYVAL: {
11279 MonoInst *src_var, *src;
11281 int klass_reg = alloc_preg (cfg);
11282 int dreg = alloc_preg (cfg);
11284 GSHAREDVT_FAILURE (*ip);
11287 MONO_INST_NEW (cfg, ins, *ip);
11290 klass = mini_get_class (method, read32 (ip + 1), generic_context);
11291 CHECK_TYPELOAD (klass);
11293 context_used = mini_class_check_context_used (cfg, klass);
11296 src_var = get_vreg_to_inst (cfg, sp [0]->dreg);
11298 src_var = mono_compile_create_var_for_vreg (cfg, &mono_defaults.typed_reference_class->byval_arg, OP_LOCAL, sp [0]->dreg);
11299 EMIT_NEW_VARLOADA (cfg, src, src_var, src_var->inst_vtype);
11300 MONO_EMIT_NEW_LOAD_MEMBASE (cfg, klass_reg, src->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, klass));
11302 if (context_used) {
11303 MonoInst *klass_ins;
11305 klass_ins = mini_emit_get_rgctx_klass (cfg, context_used,
11306 klass, MONO_RGCTX_INFO_KLASS);
11309 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, klass_reg, klass_ins->dreg);
11310 MONO_EMIT_NEW_COND_EXC (cfg, NE_UN, "InvalidCastException");
11312 mini_emit_class_check (cfg, klass_reg, klass);
11314 EMIT_NEW_LOAD_MEMBASE (cfg, ins, OP_LOAD_MEMBASE, dreg, src->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, value));
11315 ins->type = STACK_MP;
11316 ins->klass = klass;
11321 case CEE_MKREFANY: {
11322 MonoInst *loc, *addr;
11324 GSHAREDVT_FAILURE (*ip);
11327 MONO_INST_NEW (cfg, ins, *ip);
11330 klass = mini_get_class (method, read32 (ip + 1), generic_context);
11331 CHECK_TYPELOAD (klass);
11333 context_used = mini_class_check_context_used (cfg, klass);
11335 loc = mono_compile_create_var (cfg, &mono_defaults.typed_reference_class->byval_arg, OP_LOCAL);
11336 EMIT_NEW_TEMPLOADA (cfg, addr, loc->inst_c0);
11338 if (context_used) {
11339 MonoInst *const_ins;
11340 int type_reg = alloc_preg (cfg);
11342 const_ins = mini_emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_KLASS);
11343 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREP_MEMBASE_REG, addr->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, klass), const_ins->dreg);
11344 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ADD_IMM, type_reg, const_ins->dreg, MONO_STRUCT_OFFSET (MonoClass, byval_arg));
11345 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREP_MEMBASE_REG, addr->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, type), type_reg);
11347 int const_reg = alloc_preg (cfg);
11348 int type_reg = alloc_preg (cfg);
11350 MONO_EMIT_NEW_CLASSCONST (cfg, const_reg, klass);
11351 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREP_MEMBASE_REG, addr->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, klass), const_reg);
11352 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_ADD_IMM, type_reg, const_reg, MONO_STRUCT_OFFSET (MonoClass, byval_arg));
11353 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREP_MEMBASE_REG, addr->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, type), type_reg);
11355 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STOREP_MEMBASE_REG, addr->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, value), sp [0]->dreg);
11357 EMIT_NEW_TEMPLOAD (cfg, ins, loc->inst_c0);
11358 ins->type = STACK_VTYPE;
11359 ins->klass = mono_defaults.typed_reference_class;
11364 case CEE_LDTOKEN: {
11366 MonoClass *handle_class;
11368 CHECK_STACK_OVF (1);
11371 n = read32 (ip + 1);
11373 if (method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD ||
11374 method->wrapper_type == MONO_WRAPPER_SYNCHRONIZED) {
11375 handle = mono_method_get_wrapper_data (method, n);
11376 handle_class = (MonoClass *)mono_method_get_wrapper_data (method, n + 1);
11377 if (handle_class == mono_defaults.typehandle_class)
11378 handle = &((MonoClass*)handle)->byval_arg;
11381 handle = mono_ldtoken_checked (image, n, &handle_class, generic_context, &cfg->error);
11386 mono_class_init (handle_class);
11387 if (cfg->gshared) {
11388 if (mono_metadata_token_table (n) == MONO_TABLE_TYPEDEF ||
11389 mono_metadata_token_table (n) == MONO_TABLE_TYPEREF) {
11390 /* This case handles ldtoken
11391 of an open type, like for
11394 } else if (handle_class == mono_defaults.typehandle_class) {
11395 context_used = mini_class_check_context_used (cfg, mono_class_from_mono_type ((MonoType *)handle));
11396 } else if (handle_class == mono_defaults.fieldhandle_class)
11397 context_used = mini_class_check_context_used (cfg, ((MonoClassField*)handle)->parent);
11398 else if (handle_class == mono_defaults.methodhandle_class)
11399 context_used = mini_method_check_context_used (cfg, (MonoMethod *)handle);
11401 g_assert_not_reached ();
11404 if ((cfg->opt & MONO_OPT_SHARED) &&
11405 method->wrapper_type != MONO_WRAPPER_DYNAMIC_METHOD &&
11406 method->wrapper_type != MONO_WRAPPER_SYNCHRONIZED) {
11407 MonoInst *addr, *vtvar, *iargs [3];
11408 int method_context_used;
11410 method_context_used = mini_method_check_context_used (cfg, method);
11412 vtvar = mono_compile_create_var (cfg, &handle_class->byval_arg, OP_LOCAL);
11414 EMIT_NEW_IMAGECONST (cfg, iargs [0], image);
11415 EMIT_NEW_ICONST (cfg, iargs [1], n);
11416 if (method_context_used) {
11417 iargs [2] = emit_get_rgctx_method (cfg, method_context_used,
11418 method, MONO_RGCTX_INFO_METHOD);
11419 ins = mono_emit_jit_icall (cfg, mono_ldtoken_wrapper_generic_shared, iargs);
11421 EMIT_NEW_PCONST (cfg, iargs [2], generic_context);
11422 ins = mono_emit_jit_icall (cfg, mono_ldtoken_wrapper, iargs);
11424 EMIT_NEW_TEMPLOADA (cfg, addr, vtvar->inst_c0);
11426 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, addr->dreg, 0, ins->dreg);
11428 EMIT_NEW_TEMPLOAD (cfg, ins, vtvar->inst_c0);
11430 if ((ip + 5 < end) && ip_in_bb (cfg, cfg->cbb, ip + 5) &&
11431 ((ip [5] == CEE_CALL) || (ip [5] == CEE_CALLVIRT)) &&
11432 (cmethod = mini_get_method (cfg, method, read32 (ip + 6), NULL, generic_context)) &&
11433 (cmethod->klass == mono_defaults.systemtype_class) &&
11434 (strcmp (cmethod->name, "GetTypeFromHandle") == 0)) {
11435 MonoClass *tclass = mono_class_from_mono_type ((MonoType *)handle);
11437 mono_class_init (tclass);
11438 if (context_used) {
11439 ins = mini_emit_get_rgctx_klass (cfg, context_used,
11440 tclass, MONO_RGCTX_INFO_REFLECTION_TYPE);
11441 } else if (cfg->compile_aot) {
11442 if (method->wrapper_type) {
11443 error_init (&error); //got to do it since there are multiple conditionals below
11444 if (mono_class_get_checked (tclass->image, tclass->type_token, &error) == tclass && !generic_context) {
11445 /* Special case for static synchronized wrappers */
11446 EMIT_NEW_TYPE_FROM_HANDLE_CONST (cfg, ins, tclass->image, tclass->type_token, generic_context);
11448 mono_error_cleanup (&error); /* FIXME don't swallow the error */
11449 /* FIXME: n is not a normal token */
11451 EMIT_NEW_PCONST (cfg, ins, NULL);
11454 EMIT_NEW_TYPE_FROM_HANDLE_CONST (cfg, ins, image, n, generic_context);
11457 MonoReflectionType *rt = mono_type_get_object_checked (cfg->domain, (MonoType *)handle, &cfg->error);
11459 EMIT_NEW_PCONST (cfg, ins, rt);
11461 ins->type = STACK_OBJ;
11462 ins->klass = cmethod->klass;
11465 MonoInst *addr, *vtvar;
11467 vtvar = mono_compile_create_var (cfg, &handle_class->byval_arg, OP_LOCAL);
11469 if (context_used) {
11470 if (handle_class == mono_defaults.typehandle_class) {
11471 ins = mini_emit_get_rgctx_klass (cfg, context_used,
11472 mono_class_from_mono_type ((MonoType *)handle),
11473 MONO_RGCTX_INFO_TYPE);
11474 } else if (handle_class == mono_defaults.methodhandle_class) {
11475 ins = emit_get_rgctx_method (cfg, context_used,
11476 (MonoMethod *)handle, MONO_RGCTX_INFO_METHOD);
11477 } else if (handle_class == mono_defaults.fieldhandle_class) {
11478 ins = emit_get_rgctx_field (cfg, context_used,
11479 (MonoClassField *)handle, MONO_RGCTX_INFO_CLASS_FIELD);
11481 g_assert_not_reached ();
11483 } else if (cfg->compile_aot) {
11484 EMIT_NEW_LDTOKENCONST (cfg, ins, image, n, generic_context);
11486 EMIT_NEW_PCONST (cfg, ins, handle);
11488 EMIT_NEW_TEMPLOADA (cfg, addr, vtvar->inst_c0);
11489 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, addr->dreg, 0, ins->dreg);
11490 EMIT_NEW_TEMPLOAD (cfg, ins, vtvar->inst_c0);
11500 if (sp [-1]->type != STACK_OBJ)
11503 MONO_INST_NEW (cfg, ins, OP_THROW);
11505 ins->sreg1 = sp [0]->dreg;
11507 cfg->cbb->out_of_line = TRUE;
11508 MONO_ADD_INS (cfg->cbb, ins);
11509 MONO_INST_NEW (cfg, ins, OP_NOT_REACHED);
11510 MONO_ADD_INS (cfg->cbb, ins);
11513 link_bblock (cfg, cfg->cbb, end_bblock);
11514 start_new_bblock = 1;
11515 /* This can complicate code generation for llvm since the return value might not be defined */
11516 if (COMPILE_LLVM (cfg))
11517 INLINE_FAILURE ("throw");
11519 case CEE_ENDFINALLY:
11520 if (!ip_in_finally_clause (cfg, ip - header->code))
11522 /* mono_save_seq_point_info () depends on this */
11523 if (sp != stack_start)
11524 emit_seq_point (cfg, method, ip, FALSE, FALSE);
11525 MONO_INST_NEW (cfg, ins, OP_ENDFINALLY);
11526 MONO_ADD_INS (cfg->cbb, ins);
11528 start_new_bblock = 1;
11531 * Control will leave the method so empty the stack, otherwise
11532 * the next basic block will start with a nonempty stack.
11534 while (sp != stack_start) {
11539 case CEE_LEAVE_S: {
11542 if (*ip == CEE_LEAVE) {
11544 target = ip + 5 + (gint32)read32(ip + 1);
11547 target = ip + 2 + (signed char)(ip [1]);
11550 /* empty the stack */
11551 while (sp != stack_start) {
11556 * If this leave statement is in a catch block, check for a
11557 * pending exception, and rethrow it if necessary.
11558 * We avoid doing this in runtime invoke wrappers, since those are called
11559 * by native code which excepts the wrapper to catch all exceptions.
11561 for (i = 0; i < header->num_clauses; ++i) {
11562 MonoExceptionClause *clause = &header->clauses [i];
11565 * Use <= in the final comparison to handle clauses with multiple
11566 * leave statements, like in bug #78024.
11567 * The ordering of the exception clauses guarantees that we find the
11568 * innermost clause.
11570 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) {
11572 MonoBasicBlock *dont_throw;
11577 NEW_TEMPLOAD (cfg, load, mono_find_exvar_for_offset (cfg, clause->handler_offset)->inst_c0);
11580 exc_ins = mono_emit_jit_icall (cfg, mono_thread_get_undeniable_exception, NULL);
11582 NEW_BBLOCK (cfg, dont_throw);
11585 * Currently, we always rethrow the abort exception, despite the
11586 * fact that this is not correct. See thread6.cs for an example.
11587 * But propagating the abort exception is more important than
11588 * getting the sematics right.
11590 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, exc_ins->dreg, 0);
11591 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, dont_throw);
11592 MONO_EMIT_NEW_UNALU (cfg, OP_THROW, -1, exc_ins->dreg);
11594 MONO_START_BB (cfg, dont_throw);
11599 cfg->cbb->try_end = (intptr_t)(ip - header->code);
11602 if ((handlers = mono_find_final_block (cfg, ip, target, MONO_EXCEPTION_CLAUSE_FINALLY))) {
11604 MonoExceptionClause *clause;
11606 for (tmp = handlers; tmp; tmp = tmp->next) {
11607 clause = (MonoExceptionClause *)tmp->data;
11608 tblock = cfg->cil_offset_to_bb [clause->handler_offset];
11610 link_bblock (cfg, cfg->cbb, tblock);
11611 MONO_INST_NEW (cfg, ins, OP_CALL_HANDLER);
11612 ins->inst_target_bb = tblock;
11613 ins->inst_eh_block = clause;
11614 MONO_ADD_INS (cfg->cbb, ins);
11615 cfg->cbb->has_call_handler = 1;
11616 if (COMPILE_LLVM (cfg)) {
11617 MonoBasicBlock *target_bb;
11620 * Link the finally bblock with the target, since it will
11621 * conceptually branch there.
11623 GET_BBLOCK (cfg, tblock, cfg->cil_start + clause->handler_offset + clause->handler_len - 1);
11624 GET_BBLOCK (cfg, target_bb, target);
11625 link_bblock (cfg, tblock, target_bb);
11628 g_list_free (handlers);
11631 MONO_INST_NEW (cfg, ins, OP_BR);
11632 MONO_ADD_INS (cfg->cbb, ins);
11633 GET_BBLOCK (cfg, tblock, target);
11634 link_bblock (cfg, cfg->cbb, tblock);
11635 ins->inst_target_bb = tblock;
11637 start_new_bblock = 1;
11639 if (*ip == CEE_LEAVE)
11648 * Mono specific opcodes
11650 case MONO_CUSTOM_PREFIX: {
11652 g_assert (method->wrapper_type != MONO_WRAPPER_NONE);
11656 case CEE_MONO_ICALL: {
11658 MonoJitICallInfo *info;
11660 token = read32 (ip + 2);
11661 func = mono_method_get_wrapper_data (method, token);
11662 info = mono_find_jit_icall_by_addr (func);
11664 g_error ("Could not find icall address in wrapper %s", mono_method_full_name (method, 1));
11667 CHECK_STACK (info->sig->param_count);
11668 sp -= info->sig->param_count;
11670 ins = mono_emit_jit_icall (cfg, info->func, sp);
11671 if (!MONO_TYPE_IS_VOID (info->sig->ret))
11675 inline_costs += 10 * num_calls++;
11679 case CEE_MONO_LDPTR_CARD_TABLE:
11680 case CEE_MONO_LDPTR_NURSERY_START:
11681 case CEE_MONO_LDPTR_NURSERY_BITS:
11682 case CEE_MONO_LDPTR_INT_REQ_FLAG: {
11683 CHECK_STACK_OVF (1);
11686 case CEE_MONO_LDPTR_CARD_TABLE:
11687 ins = mini_emit_runtime_constant (cfg, MONO_PATCH_INFO_GC_CARD_TABLE_ADDR, NULL);
11689 case CEE_MONO_LDPTR_NURSERY_START:
11690 ins = mini_emit_runtime_constant (cfg, MONO_PATCH_INFO_GC_NURSERY_START, NULL);
11692 case CEE_MONO_LDPTR_NURSERY_BITS:
11693 ins = mini_emit_runtime_constant (cfg, MONO_PATCH_INFO_GC_NURSERY_BITS, NULL);
11695 case CEE_MONO_LDPTR_INT_REQ_FLAG:
11696 ins = mini_emit_runtime_constant (cfg, MONO_PATCH_INFO_INTERRUPTION_REQUEST_FLAG, NULL);
11702 inline_costs += 10 * num_calls++;
11705 case CEE_MONO_LDPTR: {
11708 CHECK_STACK_OVF (1);
11710 token = read32 (ip + 2);
11712 ptr = mono_method_get_wrapper_data (method, token);
11713 EMIT_NEW_PCONST (cfg, ins, ptr);
11716 inline_costs += 10 * num_calls++;
11717 /* Can't embed random pointers into AOT code */
11721 case CEE_MONO_JIT_ICALL_ADDR: {
11722 MonoJitICallInfo *callinfo;
11725 CHECK_STACK_OVF (1);
11727 token = read32 (ip + 2);
11729 ptr = mono_method_get_wrapper_data (method, token);
11730 callinfo = mono_find_jit_icall_by_addr (ptr);
11731 g_assert (callinfo);
11732 EMIT_NEW_JIT_ICALL_ADDRCONST (cfg, ins, (char*)callinfo->name);
11735 inline_costs += 10 * num_calls++;
11738 case CEE_MONO_ICALL_ADDR: {
11739 MonoMethod *cmethod;
11742 CHECK_STACK_OVF (1);
11744 token = read32 (ip + 2);
11746 cmethod = (MonoMethod *)mono_method_get_wrapper_data (method, token);
11748 if (cfg->compile_aot) {
11749 if (cfg->direct_pinvoke && ip + 6 < end && (ip [6] == CEE_POP)) {
11751 * This is generated by emit_native_wrapper () to resolve the pinvoke address
11752 * before the call, its not needed when using direct pinvoke.
11753 * This is not an optimization, but its used to avoid looking up pinvokes
11754 * on platforms which don't support dlopen ().
11756 EMIT_NEW_PCONST (cfg, ins, NULL);
11758 EMIT_NEW_AOTCONST (cfg, ins, MONO_PATCH_INFO_ICALL_ADDR, cmethod);
11761 ptr = mono_lookup_internal_call (cmethod);
11763 EMIT_NEW_PCONST (cfg, ins, ptr);
11769 case CEE_MONO_VTADDR: {
11770 MonoInst *src_var, *src;
11776 src_var = get_vreg_to_inst (cfg, sp [0]->dreg);
11777 EMIT_NEW_VARLOADA ((cfg), (src), src_var, src_var->inst_vtype);
11782 case CEE_MONO_NEWOBJ: {
11783 MonoInst *iargs [2];
11785 CHECK_STACK_OVF (1);
11787 token = read32 (ip + 2);
11788 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
11789 mono_class_init (klass);
11790 NEW_DOMAINCONST (cfg, iargs [0]);
11791 MONO_ADD_INS (cfg->cbb, iargs [0]);
11792 NEW_CLASSCONST (cfg, iargs [1], klass);
11793 MONO_ADD_INS (cfg->cbb, iargs [1]);
11794 *sp++ = mono_emit_jit_icall (cfg, ves_icall_object_new, iargs);
11796 inline_costs += 10 * num_calls++;
11799 case CEE_MONO_OBJADDR:
11802 MONO_INST_NEW (cfg, ins, OP_MOVE);
11803 ins->dreg = alloc_ireg_mp (cfg);
11804 ins->sreg1 = sp [0]->dreg;
11805 ins->type = STACK_MP;
11806 MONO_ADD_INS (cfg->cbb, ins);
11810 case CEE_MONO_LDNATIVEOBJ:
11812 * Similar to LDOBJ, but instead load the unmanaged
11813 * representation of the vtype to the stack.
11818 token = read32 (ip + 2);
11819 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
11820 g_assert (klass->valuetype);
11821 mono_class_init (klass);
11824 MonoInst *src, *dest, *temp;
11827 temp = mono_compile_create_var (cfg, &klass->byval_arg, OP_LOCAL);
11828 temp->backend.is_pinvoke = 1;
11829 EMIT_NEW_TEMPLOADA (cfg, dest, temp->inst_c0);
11830 mini_emit_memory_copy (cfg, dest, src, klass, TRUE, 0);
11832 EMIT_NEW_TEMPLOAD (cfg, dest, temp->inst_c0);
11833 dest->type = STACK_VTYPE;
11834 dest->klass = klass;
11840 case CEE_MONO_RETOBJ: {
11842 * Same as RET, but return the native representation of a vtype
11845 g_assert (cfg->ret);
11846 g_assert (mono_method_signature (method)->pinvoke);
11851 token = read32 (ip + 2);
11852 klass = (MonoClass *)mono_method_get_wrapper_data (method, token);
11854 if (!cfg->vret_addr) {
11855 g_assert (cfg->ret_var_is_local);
11857 EMIT_NEW_VARLOADA (cfg, ins, cfg->ret, cfg->ret->inst_vtype);
11859 EMIT_NEW_RETLOADA (cfg, ins);
11861 mini_emit_memory_copy (cfg, ins, sp [0], klass, TRUE, 0);
11863 if (sp != stack_start)
11866 MONO_INST_NEW (cfg, ins, OP_BR);
11867 ins->inst_target_bb = end_bblock;
11868 MONO_ADD_INS (cfg->cbb, ins);
11869 link_bblock (cfg, cfg->cbb, end_bblock);
11870 start_new_bblock = 1;
11874 case CEE_MONO_SAVE_LMF:
11875 case CEE_MONO_RESTORE_LMF:
11878 case CEE_MONO_CLASSCONST:
11879 CHECK_STACK_OVF (1);
11881 token = read32 (ip + 2);
11882 EMIT_NEW_CLASSCONST (cfg, ins, mono_method_get_wrapper_data (method, token));
11885 inline_costs += 10 * num_calls++;
11887 case CEE_MONO_NOT_TAKEN:
11888 cfg->cbb->out_of_line = TRUE;
11891 case CEE_MONO_TLS: {
11894 CHECK_STACK_OVF (1);
11896 key = (MonoTlsKey)read32 (ip + 2);
11897 g_assert (key < TLS_KEY_NUM);
11899 ins = mono_create_tls_get (cfg, key);
11901 ins->type = STACK_PTR;
11906 case CEE_MONO_DYN_CALL: {
11907 MonoCallInst *call;
11909 /* It would be easier to call a trampoline, but that would put an
11910 * extra frame on the stack, confusing exception handling. So
11911 * implement it inline using an opcode for now.
11914 if (!cfg->dyn_call_var) {
11915 cfg->dyn_call_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
11916 /* prevent it from being register allocated */
11917 cfg->dyn_call_var->flags |= MONO_INST_VOLATILE;
11920 /* Has to use a call inst since it local regalloc expects it */
11921 MONO_INST_NEW_CALL (cfg, call, OP_DYN_CALL);
11922 ins = (MonoInst*)call;
11924 ins->sreg1 = sp [0]->dreg;
11925 ins->sreg2 = sp [1]->dreg;
11926 MONO_ADD_INS (cfg->cbb, ins);
11928 cfg->param_area = MAX (cfg->param_area, cfg->backend->dyn_call_param_area);
11931 inline_costs += 10 * num_calls++;
11935 case CEE_MONO_MEMORY_BARRIER: {
11937 mini_emit_memory_barrier (cfg, (int)read32 (ip + 2));
11941 case CEE_MONO_ATOMIC_STORE_I4: {
11942 g_assert (mono_arch_opcode_supported (OP_ATOMIC_STORE_I4));
11948 MONO_INST_NEW (cfg, ins, OP_ATOMIC_STORE_I4);
11949 ins->dreg = sp [0]->dreg;
11950 ins->sreg1 = sp [1]->dreg;
11951 ins->backend.memory_barrier_kind = (int) read32 (ip + 2);
11952 MONO_ADD_INS (cfg->cbb, ins);
11957 case CEE_MONO_JIT_ATTACH: {
11958 MonoInst *args [16], *domain_ins;
11959 MonoInst *ad_ins, *jit_tls_ins;
11960 MonoBasicBlock *next_bb = NULL, *call_bb = NULL;
11962 g_assert (!mono_threads_is_coop_enabled ());
11964 cfg->orig_domain_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
11966 EMIT_NEW_PCONST (cfg, ins, NULL);
11967 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, cfg->orig_domain_var->dreg, ins->dreg);
11969 ad_ins = mono_create_tls_get (cfg, TLS_KEY_DOMAIN);
11970 jit_tls_ins = mono_create_tls_get (cfg, TLS_KEY_JIT_TLS);
11972 if (ad_ins && jit_tls_ins) {
11973 NEW_BBLOCK (cfg, next_bb);
11974 NEW_BBLOCK (cfg, call_bb);
11976 if (cfg->compile_aot) {
11977 /* AOT code is only used in the root domain */
11978 EMIT_NEW_PCONST (cfg, domain_ins, NULL);
11980 EMIT_NEW_PCONST (cfg, domain_ins, cfg->domain);
11982 MONO_EMIT_NEW_BIALU (cfg, OP_COMPARE, -1, ad_ins->dreg, domain_ins->dreg);
11983 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, call_bb);
11985 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, jit_tls_ins->dreg, 0);
11986 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, call_bb);
11988 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, next_bb);
11989 MONO_START_BB (cfg, call_bb);
11992 /* AOT code is only used in the root domain */
11993 EMIT_NEW_PCONST (cfg, args [0], cfg->compile_aot ? NULL : cfg->domain);
11994 if (cfg->compile_aot) {
11998 * This is called on unattached threads, so it cannot go through the trampoline
11999 * infrastructure. Use an indirect call through a got slot initialized at load time
12002 EMIT_NEW_AOTCONST (cfg, addr, MONO_PATCH_INFO_JIT_THREAD_ATTACH, NULL);
12003 ins = mini_emit_calli (cfg, helper_sig_jit_thread_attach, args, addr, NULL, NULL);
12005 ins = mono_emit_jit_icall (cfg, mono_jit_thread_attach, args);
12007 MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, cfg->orig_domain_var->dreg, ins->dreg);
12010 MONO_START_BB (cfg, next_bb);
12015 case CEE_MONO_JIT_DETACH: {
12016 MonoInst *args [16];
12018 /* Restore the original domain */
12019 dreg = alloc_ireg (cfg);
12020 EMIT_NEW_UNALU (cfg, args [0], OP_MOVE, dreg, cfg->orig_domain_var->dreg);
12021 mono_emit_jit_icall (cfg, mono_jit_set_domain, args);
12025 case CEE_MONO_CALLI_EXTRA_ARG: {
12027 MonoMethodSignature *fsig;
12031 * This is the same as CEE_CALLI, but passes an additional argument
12032 * to the called method in llvmonly mode.
12033 * This is only used by delegate invoke wrappers to call the
12034 * actual delegate method.
12036 g_assert (method->wrapper_type == MONO_WRAPPER_DELEGATE_INVOKE);
12039 token = read32 (ip + 2);
12047 fsig = mini_get_signature (method, token, generic_context, &cfg->error);
12050 if (cfg->llvm_only)
12051 cfg->signatures = g_slist_prepend_mempool (cfg->mempool, cfg->signatures, fsig);
12053 n = fsig->param_count + fsig->hasthis + 1;
12060 if (cfg->llvm_only) {
12062 * The lowest bit of 'arg' determines whenever the callee uses the gsharedvt
12063 * cconv. This is set by mono_init_delegate ().
12065 if (cfg->gsharedvt && mini_is_gsharedvt_variable_signature (fsig)) {
12066 MonoInst *callee = addr;
12067 MonoInst *call, *localloc_ins;
12068 MonoBasicBlock *is_gsharedvt_bb, *end_bb;
12069 int low_bit_reg = alloc_preg (cfg);
12071 NEW_BBLOCK (cfg, is_gsharedvt_bb);
12072 NEW_BBLOCK (cfg, end_bb);
12074 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_PAND_IMM, low_bit_reg, arg->dreg, 1);
12075 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, low_bit_reg, 0);
12076 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, is_gsharedvt_bb);
12078 /* Normal case: callee uses a normal cconv, have to add an out wrapper */
12079 addr = emit_get_rgctx_sig (cfg, context_used,
12080 fsig, MONO_RGCTX_INFO_SIG_GSHAREDVT_OUT_TRAMPOLINE_CALLI);
12082 * ADDR points to a gsharedvt-out wrapper, have to pass <callee, arg> as an extra arg.
12084 MONO_INST_NEW (cfg, ins, OP_LOCALLOC_IMM);
12085 ins->dreg = alloc_preg (cfg);
12086 ins->inst_imm = 2 * SIZEOF_VOID_P;
12087 MONO_ADD_INS (cfg->cbb, ins);
12088 localloc_ins = ins;
12089 cfg->flags |= MONO_CFG_HAS_ALLOCA;
12090 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, localloc_ins->dreg, 0, callee->dreg);
12091 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, localloc_ins->dreg, SIZEOF_VOID_P, arg->dreg);
12093 call = emit_extra_arg_calli (cfg, fsig, sp, localloc_ins->dreg, addr);
12094 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
12096 /* Gsharedvt case: callee uses a gsharedvt cconv, no conversion is needed */
12097 MONO_START_BB (cfg, is_gsharedvt_bb);
12098 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_PXOR_IMM, arg->dreg, arg->dreg, 1);
12099 ins = emit_extra_arg_calli (cfg, fsig, sp, arg->dreg, callee);
12100 ins->dreg = call->dreg;
12102 MONO_START_BB (cfg, end_bb);
12104 /* Caller uses a normal calling conv */
12106 MonoInst *callee = addr;
12107 MonoInst *call, *localloc_ins;
12108 MonoBasicBlock *is_gsharedvt_bb, *end_bb;
12109 int low_bit_reg = alloc_preg (cfg);
12111 NEW_BBLOCK (cfg, is_gsharedvt_bb);
12112 NEW_BBLOCK (cfg, end_bb);
12114 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_PAND_IMM, low_bit_reg, arg->dreg, 1);
12115 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, low_bit_reg, 0);
12116 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, is_gsharedvt_bb);
12118 /* Normal case: callee uses a normal cconv, no conversion is needed */
12119 call = emit_extra_arg_calli (cfg, fsig, sp, arg->dreg, callee);
12120 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
12121 /* Gsharedvt case: callee uses a gsharedvt cconv, have to add an in wrapper */
12122 MONO_START_BB (cfg, is_gsharedvt_bb);
12123 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_PXOR_IMM, arg->dreg, arg->dreg, 1);
12124 NEW_AOTCONST (cfg, addr, MONO_PATCH_INFO_GSHAREDVT_IN_WRAPPER, fsig);
12125 MONO_ADD_INS (cfg->cbb, addr);
12127 * ADDR points to a gsharedvt-in wrapper, have to pass <callee, arg> as an extra arg.
12129 MONO_INST_NEW (cfg, ins, OP_LOCALLOC_IMM);
12130 ins->dreg = alloc_preg (cfg);
12131 ins->inst_imm = 2 * SIZEOF_VOID_P;
12132 MONO_ADD_INS (cfg->cbb, ins);
12133 localloc_ins = ins;
12134 cfg->flags |= MONO_CFG_HAS_ALLOCA;
12135 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, localloc_ins->dreg, 0, callee->dreg);
12136 MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, localloc_ins->dreg, SIZEOF_VOID_P, arg->dreg);
12138 ins = emit_extra_arg_calli (cfg, fsig, sp, localloc_ins->dreg, addr);
12139 ins->dreg = call->dreg;
12140 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
12142 MONO_START_BB (cfg, end_bb);
12145 /* Same as CEE_CALLI */
12146 if (cfg->gsharedvt && mini_is_gsharedvt_signature (fsig)) {
12148 * We pass the address to the gsharedvt trampoline in the rgctx reg
12150 MonoInst *callee = addr;
12152 addr = emit_get_rgctx_sig (cfg, context_used,
12153 fsig, MONO_RGCTX_INFO_SIG_GSHAREDVT_OUT_TRAMPOLINE_CALLI);
12154 ins = (MonoInst*)mini_emit_calli (cfg, fsig, sp, addr, NULL, callee);
12156 ins = (MonoInst*)mini_emit_calli (cfg, fsig, sp, addr, NULL, NULL);
12160 if (!MONO_TYPE_IS_VOID (fsig->ret))
12161 *sp++ = mono_emit_widen_call_res (cfg, ins, fsig);
12163 CHECK_CFG_EXCEPTION;
12167 constrained_class = NULL;
12170 case CEE_MONO_LDDOMAIN:
12171 CHECK_STACK_OVF (1);
12172 EMIT_NEW_PCONST (cfg, ins, cfg->compile_aot ? NULL : cfg->domain);
12176 case CEE_MONO_GET_LAST_ERROR:
12178 CHECK_STACK_OVF (1);
12180 MONO_INST_NEW (cfg, ins, OP_GET_LAST_ERROR);
12181 ins->dreg = alloc_dreg (cfg, STACK_I4);
12182 ins->type = STACK_I4;
12183 MONO_ADD_INS (cfg->cbb, ins);
12188 case CEE_MONO_GET_RGCTX_ARG:
12190 CHECK_STACK_OVF (1);
12192 mono_create_rgctx_var (cfg);
12194 MONO_INST_NEW (cfg, ins, OP_MOVE);
12195 ins->dreg = alloc_dreg (cfg, STACK_PTR);
12196 ins->sreg1 = cfg->rgctx_var->dreg;
12197 ins->type = STACK_PTR;
12198 MONO_ADD_INS (cfg->cbb, ins);
12204 g_error ("opcode 0x%02x 0x%02x not handled", MONO_CUSTOM_PREFIX, ip [1]);
12210 case CEE_PREFIX1: {
12213 case CEE_ARGLIST: {
12214 /* somewhat similar to LDTOKEN */
12215 MonoInst *addr, *vtvar;
12216 CHECK_STACK_OVF (1);
12217 vtvar = mono_compile_create_var (cfg, &mono_defaults.argumenthandle_class->byval_arg, OP_LOCAL);
12219 EMIT_NEW_TEMPLOADA (cfg, addr, vtvar->inst_c0);
12220 EMIT_NEW_UNALU (cfg, ins, OP_ARGLIST, -1, addr->dreg);
12222 EMIT_NEW_TEMPLOAD (cfg, ins, vtvar->inst_c0);
12223 ins->type = STACK_VTYPE;
12224 ins->klass = mono_defaults.argumenthandle_class;
12234 MonoInst *cmp, *arg1, *arg2;
12242 * The following transforms:
12243 * CEE_CEQ into OP_CEQ
12244 * CEE_CGT into OP_CGT
12245 * CEE_CGT_UN into OP_CGT_UN
12246 * CEE_CLT into OP_CLT
12247 * CEE_CLT_UN into OP_CLT_UN
12249 MONO_INST_NEW (cfg, cmp, (OP_CEQ - CEE_CEQ) + ip [1]);
12251 MONO_INST_NEW (cfg, ins, cmp->opcode);
12252 cmp->sreg1 = arg1->dreg;
12253 cmp->sreg2 = arg2->dreg;
12254 type_from_op (cfg, cmp, arg1, arg2);
12256 add_widen_op (cfg, cmp, &arg1, &arg2);
12257 if ((arg1->type == STACK_I8) || ((SIZEOF_VOID_P == 8) && ((arg1->type == STACK_PTR) || (arg1->type == STACK_OBJ) || (arg1->type == STACK_MP))))
12258 cmp->opcode = OP_LCOMPARE;
12259 else if (arg1->type == STACK_R4)
12260 cmp->opcode = OP_RCOMPARE;
12261 else if (arg1->type == STACK_R8)
12262 cmp->opcode = OP_FCOMPARE;
12264 cmp->opcode = OP_ICOMPARE;
12265 MONO_ADD_INS (cfg->cbb, cmp);
12266 ins->type = STACK_I4;
12267 ins->dreg = alloc_dreg (cfg, (MonoStackType)ins->type);
12268 type_from_op (cfg, ins, arg1, arg2);
12270 if (cmp->opcode == OP_FCOMPARE || cmp->opcode == OP_RCOMPARE) {
12272 * The backends expect the fceq opcodes to do the
12275 ins->sreg1 = cmp->sreg1;
12276 ins->sreg2 = cmp->sreg2;
12279 MONO_ADD_INS (cfg->cbb, ins);
12285 MonoInst *argconst;
12286 MonoMethod *cil_method;
12288 CHECK_STACK_OVF (1);
12290 n = read32 (ip + 2);
12291 cmethod = mini_get_method (cfg, method, n, NULL, generic_context);
12294 mono_class_init (cmethod->klass);
12296 mono_save_token_info (cfg, image, n, cmethod);
12298 context_used = mini_method_check_context_used (cfg, cmethod);
12300 cil_method = cmethod;
12301 if (!dont_verify && !cfg->skip_visibility && !mono_method_can_access_method (method, cmethod))
12302 emit_method_access_failure (cfg, method, cil_method);
12304 if (mono_security_core_clr_enabled ())
12305 ensure_method_is_allowed_to_call_method (cfg, method, cmethod);
12308 * Optimize the common case of ldftn+delegate creation
12310 if ((sp > stack_start) && (ip + 6 + 5 < end) && ip_in_bb (cfg, cfg->cbb, ip + 6) && (ip [6] == CEE_NEWOBJ)) {
12311 MonoMethod *ctor_method = mini_get_method (cfg, method, read32 (ip + 7), NULL, generic_context);
12312 if (ctor_method && (ctor_method->klass->parent == mono_defaults.multicastdelegate_class)) {
12313 MonoInst *target_ins, *handle_ins;
12314 MonoMethod *invoke;
12315 int invoke_context_used;
12317 invoke = mono_get_delegate_invoke (ctor_method->klass);
12318 if (!invoke || !mono_method_signature (invoke))
12321 invoke_context_used = mini_method_check_context_used (cfg, invoke);
12323 target_ins = sp [-1];
12325 if (mono_security_core_clr_enabled ())
12326 ensure_method_is_allowed_to_call_method (cfg, method, ctor_method);
12328 if (!(cmethod->flags & METHOD_ATTRIBUTE_STATIC)) {
12329 /*LAME IMPL: We must not add a null check for virtual invoke delegates.*/
12330 if (mono_method_signature (invoke)->param_count == mono_method_signature (cmethod)->param_count) {
12331 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, target_ins->dreg, 0);
12332 MONO_EMIT_NEW_COND_EXC (cfg, EQ, "ArgumentException");
12336 /* FIXME: SGEN support */
12337 if (invoke_context_used == 0 || cfg->llvm_only) {
12339 if (cfg->verbose_level > 3)
12340 g_print ("converting (in B%d: stack: %d) %s", cfg->cbb->block_num, (int)(sp - stack_start), mono_disasm_code_one (NULL, method, ip, NULL));
12341 if ((handle_ins = handle_delegate_ctor (cfg, ctor_method->klass, target_ins, cmethod, context_used, FALSE))) {
12344 CHECK_CFG_EXCEPTION;
12354 argconst = emit_get_rgctx_method (cfg, context_used, cmethod, MONO_RGCTX_INFO_METHOD);
12355 ins = mono_emit_jit_icall (cfg, mono_ldftn, &argconst);
12359 inline_costs += 10 * num_calls++;
12362 case CEE_LDVIRTFTN: {
12363 MonoInst *args [2];
12367 n = read32 (ip + 2);
12368 cmethod = mini_get_method (cfg, method, n, NULL, generic_context);
12371 mono_class_init (cmethod->klass);
12373 context_used = mini_method_check_context_used (cfg, cmethod);
12375 if (mono_security_core_clr_enabled ())
12376 ensure_method_is_allowed_to_call_method (cfg, method, cmethod);
12379 * Optimize the common case of ldvirtftn+delegate creation
12381 if ((sp > stack_start) && (ip + 6 + 5 < end) && ip_in_bb (cfg, cfg->cbb, ip + 6) && (ip [6] == CEE_NEWOBJ) && (ip > header->code && ip [-1] == CEE_DUP)) {
12382 MonoMethod *ctor_method = mini_get_method (cfg, method, read32 (ip + 7), NULL, generic_context);
12383 if (ctor_method && (ctor_method->klass->parent == mono_defaults.multicastdelegate_class)) {
12384 MonoInst *target_ins, *handle_ins;
12385 MonoMethod *invoke;
12386 int invoke_context_used;
12387 gboolean is_virtual = cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL;
12389 invoke = mono_get_delegate_invoke (ctor_method->klass);
12390 if (!invoke || !mono_method_signature (invoke))
12393 invoke_context_used = mini_method_check_context_used (cfg, invoke);
12395 target_ins = sp [-1];
12397 if (mono_security_core_clr_enabled ())
12398 ensure_method_is_allowed_to_call_method (cfg, method, ctor_method);
12400 /* FIXME: SGEN support */
12401 if (invoke_context_used == 0 || cfg->llvm_only) {
12403 if (cfg->verbose_level > 3)
12404 g_print ("converting (in B%d: stack: %d) %s", cfg->cbb->block_num, (int)(sp - stack_start), mono_disasm_code_one (NULL, method, ip, NULL));
12405 if ((handle_ins = handle_delegate_ctor (cfg, ctor_method->klass, target_ins, cmethod, context_used, is_virtual))) {
12408 CHECK_CFG_EXCEPTION;
12421 args [1] = emit_get_rgctx_method (cfg, context_used,
12422 cmethod, MONO_RGCTX_INFO_METHOD);
12425 *sp++ = mono_emit_jit_icall (cfg, mono_ldvirtfn_gshared, args);
12427 *sp++ = mono_emit_jit_icall (cfg, mono_ldvirtfn, args);
12430 inline_costs += 10 * num_calls++;
12434 CHECK_STACK_OVF (1);
12436 n = read16 (ip + 2);
12438 EMIT_NEW_ARGLOAD (cfg, ins, n);
12443 CHECK_STACK_OVF (1);
12445 n = read16 (ip + 2);
12447 NEW_ARGLOADA (cfg, ins, n);
12448 MONO_ADD_INS (cfg->cbb, ins);
12456 n = read16 (ip + 2);
12458 if (!dont_verify_stloc && target_type_is_incompatible (cfg, param_types [n], *sp))
12460 EMIT_NEW_ARGSTORE (cfg, ins, n, *sp);
12464 CHECK_STACK_OVF (1);
12466 n = read16 (ip + 2);
12468 EMIT_NEW_LOCLOAD (cfg, ins, n);
12473 unsigned char *tmp_ip;
12474 CHECK_STACK_OVF (1);
12476 n = read16 (ip + 2);
12479 if ((tmp_ip = emit_optimized_ldloca_ir (cfg, ip, end, 2))) {
12485 EMIT_NEW_LOCLOADA (cfg, ins, n);
12494 n = read16 (ip + 2);
12496 if (!dont_verify_stloc && target_type_is_incompatible (cfg, header->locals [n], *sp))
12498 emit_stloc_ir (cfg, sp, header, n);
12502 case CEE_LOCALLOC: {
12504 MonoBasicBlock *non_zero_bb, *end_bb;
12505 int alloc_ptr = alloc_preg (cfg);
12507 if (sp != stack_start)
12509 if (cfg->method != method)
12511 * Inlining this into a loop in a parent could lead to
12512 * stack overflows which is different behavior than the
12513 * non-inlined case, thus disable inlining in this case.
12515 INLINE_FAILURE("localloc");
12517 NEW_BBLOCK (cfg, non_zero_bb);
12518 NEW_BBLOCK (cfg, end_bb);
12520 /* if size != zero */
12521 MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, sp [0]->dreg, 0);
12522 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, non_zero_bb);
12524 //size is zero, so result is NULL
12525 MONO_EMIT_NEW_PCONST (cfg, alloc_ptr, NULL);
12526 MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb);
12528 MONO_START_BB (cfg, non_zero_bb);
12529 MONO_INST_NEW (cfg, ins, OP_LOCALLOC);
12530 ins->dreg = alloc_ptr;
12531 ins->sreg1 = sp [0]->dreg;
12532 ins->type = STACK_PTR;
12533 MONO_ADD_INS (cfg->cbb, ins);
12535 cfg->flags |= MONO_CFG_HAS_ALLOCA;
12537 ins->flags |= MONO_INST_INIT;
12539 MONO_START_BB (cfg, end_bb);
12540 EMIT_NEW_UNALU (cfg, ins, OP_MOVE, alloc_preg (cfg), alloc_ptr);
12541 ins->type = STACK_PTR;
12547 case CEE_ENDFILTER: {
12548 MonoExceptionClause *clause, *nearest;
12553 if ((sp != stack_start) || (sp [0]->type != STACK_I4))
12555 MONO_INST_NEW (cfg, ins, OP_ENDFILTER);
12556 ins->sreg1 = (*sp)->dreg;
12557 MONO_ADD_INS (cfg->cbb, ins);
12558 start_new_bblock = 1;
12562 for (cc = 0; cc < header->num_clauses; ++cc) {
12563 clause = &header->clauses [cc];
12564 if ((clause->flags & MONO_EXCEPTION_CLAUSE_FILTER) &&
12565 ((ip - header->code) > clause->data.filter_offset && (ip - header->code) <= clause->handler_offset) &&
12566 (!nearest || (clause->data.filter_offset < nearest->data.filter_offset)))
12569 g_assert (nearest);
12570 if ((ip - header->code) != nearest->handler_offset)
12575 case CEE_UNALIGNED_:
12576 ins_flag |= MONO_INST_UNALIGNED;
12577 /* FIXME: record alignment? we can assume 1 for now */
12581 case CEE_VOLATILE_:
12582 ins_flag |= MONO_INST_VOLATILE;
12586 ins_flag |= MONO_INST_TAILCALL;
12587 cfg->flags |= MONO_CFG_HAS_TAIL;
12588 /* Can't inline tail calls at this time */
12589 inline_costs += 100000;
12596 token = read32 (ip + 2);
12597 klass = mini_get_class (method, token, generic_context);
12598 CHECK_TYPELOAD (klass);
12599 if (generic_class_is_reference_type (cfg, klass))
12600 MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STORE_MEMBASE_IMM, sp [0]->dreg, 0, 0);
12602 mini_emit_initobj (cfg, *sp, NULL, klass);
12606 case CEE_CONSTRAINED_:
12608 token = read32 (ip + 2);
12609 constrained_class = mini_get_class (method, token, generic_context);
12610 CHECK_TYPELOAD (constrained_class);
12616 mini_emit_memory_copy_bytes (cfg, sp [0], sp [1], sp [2], ins_flag);
12624 mini_emit_memory_init_bytes (cfg, sp [0], sp [1], sp [2], ins_flag);
12632 ins_flag |= MONO_INST_NOTYPECHECK;
12634 ins_flag |= MONO_INST_NORANGECHECK;
12635 /* we ignore the no-nullcheck for now since we
12636 * really do it explicitly only when doing callvirt->call
12640 case CEE_RETHROW: {
12642 int handler_offset = -1;
12644 for (i = 0; i < header->num_clauses; ++i) {
12645 MonoExceptionClause *clause = &header->clauses [i];
12646 if (MONO_OFFSET_IN_HANDLER (clause, ip - header->code) && !(clause->flags & MONO_EXCEPTION_CLAUSE_FINALLY)) {
12647 handler_offset = clause->handler_offset;
12652 cfg->cbb->flags |= BB_EXCEPTION_UNSAFE;
12654 if (handler_offset == -1)
12657 EMIT_NEW_TEMPLOAD (cfg, load, mono_find_exvar_for_offset (cfg, handler_offset)->inst_c0);
12658 MONO_INST_NEW (cfg, ins, OP_RETHROW);
12659 ins->sreg1 = load->dreg;
12660 MONO_ADD_INS (cfg->cbb, ins);
12662 MONO_INST_NEW (cfg, ins, OP_NOT_REACHED);
12663 MONO_ADD_INS (cfg->cbb, ins);
12666 link_bblock (cfg, cfg->cbb, end_bblock);
12667 start_new_bblock = 1;
12675 CHECK_STACK_OVF (1);
12677 token = read32 (ip + 2);
12678 if (mono_metadata_token_table (token) == MONO_TABLE_TYPESPEC && !image_is_dynamic (method->klass->image) && !generic_context) {
12679 MonoType *type = mono_type_create_from_typespec_checked (image, token, &cfg->error);
12682 val = mono_type_size (type, &ialign);
12684 MonoClass *klass = mini_get_class (method, token, generic_context);
12685 CHECK_TYPELOAD (klass);
12687 val = mono_type_size (&klass->byval_arg, &ialign);
12689 if (mini_is_gsharedvt_klass (klass))
12690 GSHAREDVT_FAILURE (*ip);
12692 EMIT_NEW_ICONST (cfg, ins, val);
12697 case CEE_REFANYTYPE: {
12698 MonoInst *src_var, *src;
12700 GSHAREDVT_FAILURE (*ip);
12706 src_var = get_vreg_to_inst (cfg, sp [0]->dreg);
12708 src_var = mono_compile_create_var_for_vreg (cfg, &mono_defaults.typed_reference_class->byval_arg, OP_LOCAL, sp [0]->dreg);
12709 EMIT_NEW_VARLOADA (cfg, src, src_var, src_var->inst_vtype);
12710 EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &mono_defaults.typehandle_class->byval_arg, src->dreg, MONO_STRUCT_OFFSET (MonoTypedRef, type));
12715 case CEE_READONLY_:
12728 g_warning ("opcode 0xfe 0x%02x not handled", ip [1]);
12738 g_warning ("opcode 0x%02x not handled", *ip);
12742 if (start_new_bblock != 1)
12745 cfg->cbb->cil_length = ip - cfg->cbb->cil_code;
12746 if (cfg->cbb->next_bb) {
12747 /* This could already be set because of inlining, #693905 */
12748 MonoBasicBlock *bb = cfg->cbb;
12750 while (bb->next_bb)
12752 bb->next_bb = end_bblock;
12754 cfg->cbb->next_bb = end_bblock;
12757 if (cfg->method == method && cfg->domainvar) {
12759 MonoInst *get_domain;
12761 cfg->cbb = init_localsbb;
12763 get_domain = mono_create_tls_get (cfg, TLS_KEY_DOMAIN);
12764 NEW_TEMPSTORE (cfg, store, cfg->domainvar->inst_c0, get_domain);
12765 MONO_ADD_INS (cfg->cbb, store);
12768 #if defined(TARGET_POWERPC) || defined(TARGET_X86)
12769 if (cfg->compile_aot)
12770 /* FIXME: The plt slots require a GOT var even if the method doesn't use it */
12771 mono_get_got_var (cfg);
12774 if (cfg->method == method && cfg->got_var)
12775 mono_emit_load_got_addr (cfg);
12777 if (init_localsbb) {
12778 cfg->cbb = init_localsbb;
12780 for (i = 0; i < header->num_locals; ++i) {
12781 emit_init_local (cfg, i, header->locals [i], init_locals);
12785 if (cfg->init_ref_vars && cfg->method == method) {
12786 /* Emit initialization for ref vars */
12787 // FIXME: Avoid duplication initialization for IL locals.
12788 for (i = 0; i < cfg->num_varinfo; ++i) {
12789 MonoInst *ins = cfg->varinfo [i];
12791 if (ins->opcode == OP_LOCAL && ins->type == STACK_OBJ)
12792 MONO_EMIT_NEW_PCONST (cfg, ins->dreg, NULL);
12796 if (cfg->lmf_var && cfg->method == method && !cfg->llvm_only) {
12797 cfg->cbb = init_localsbb;
12798 emit_push_lmf (cfg);
12801 cfg->cbb = init_localsbb;
12802 emit_instrumentation_call (cfg, mono_profiler_method_enter);
12805 MonoBasicBlock *bb;
12808 * Make seq points at backward branch targets interruptable.
12810 for (bb = cfg->bb_entry; bb; bb = bb->next_bb)
12811 if (bb->code && bb->in_count > 1 && bb->code->opcode == OP_SEQ_POINT)
12812 bb->code->flags |= MONO_INST_SINGLE_STEP_LOC;
12815 /* Add a sequence point for method entry/exit events */
12816 if (seq_points && cfg->gen_sdb_seq_points) {
12817 NEW_SEQ_POINT (cfg, ins, METHOD_ENTRY_IL_OFFSET, FALSE);
12818 MONO_ADD_INS (init_localsbb, ins);
12819 NEW_SEQ_POINT (cfg, ins, METHOD_EXIT_IL_OFFSET, FALSE);
12820 MONO_ADD_INS (cfg->bb_exit, ins);
12824 * Add seq points for IL offsets which have line number info, but wasn't generated a seq point during JITting because
12825 * the code they refer to was dead (#11880).
12827 if (sym_seq_points) {
12828 for (i = 0; i < header->code_size; ++i) {
12829 if (mono_bitset_test_fast (seq_point_locs, i) && !mono_bitset_test_fast (seq_point_set_locs, i)) {
12832 NEW_SEQ_POINT (cfg, ins, i, FALSE);
12833 mono_add_seq_point (cfg, NULL, ins, SEQ_POINT_NATIVE_OFFSET_DEAD_CODE);
12840 if (cfg->method == method) {
12841 MonoBasicBlock *bb;
12842 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
12843 if (bb == cfg->bb_init)
12846 bb->region = mono_find_block_region (cfg, bb->real_offset);
12848 mono_create_spvar_for_region (cfg, bb->region);
12849 if (cfg->verbose_level > 2)
12850 printf ("REGION BB%d IL_%04x ID_%08X\n", bb->block_num, bb->real_offset, bb->region);
12853 MonoBasicBlock *bb;
12854 /* get_most_deep_clause () in mini-llvm.c depends on this for inlined bblocks */
12855 for (bb = start_bblock; bb != end_bblock; bb = bb->next_bb) {
12856 bb->real_offset = inline_offset;
12860 if (inline_costs < 0) {
12863 /* Method is too large */
12864 mname = mono_method_full_name (method, TRUE);
12865 mono_cfg_set_exception_invalid_program (cfg, g_strdup_printf ("Method %s is too complex.", mname));
12869 if ((cfg->verbose_level > 2) && (cfg->method == method))
12870 mono_print_code (cfg, "AFTER METHOD-TO-IR");
12875 g_assert (!mono_error_ok (&cfg->error));
12879 g_assert (cfg->exception_type != MONO_EXCEPTION_NONE);
12883 set_exception_type_from_invalid_il (cfg, method, ip);
12887 g_slist_free (class_inits);
12888 mono_basic_block_free (original_bb);
12889 cfg->dont_inline = g_list_remove (cfg->dont_inline, method);
12890 if (cfg->exception_type)
12893 return inline_costs;
12897 store_membase_reg_to_store_membase_imm (int opcode)
12900 case OP_STORE_MEMBASE_REG:
12901 return OP_STORE_MEMBASE_IMM;
12902 case OP_STOREI1_MEMBASE_REG:
12903 return OP_STOREI1_MEMBASE_IMM;
12904 case OP_STOREI2_MEMBASE_REG:
12905 return OP_STOREI2_MEMBASE_IMM;
12906 case OP_STOREI4_MEMBASE_REG:
12907 return OP_STOREI4_MEMBASE_IMM;
12908 case OP_STOREI8_MEMBASE_REG:
12909 return OP_STOREI8_MEMBASE_IMM;
12911 g_assert_not_reached ();
12918 mono_op_to_op_imm (int opcode)
12922 return OP_IADD_IMM;
12924 return OP_ISUB_IMM;
12926 return OP_IDIV_IMM;
12928 return OP_IDIV_UN_IMM;
12930 return OP_IREM_IMM;
12932 return OP_IREM_UN_IMM;
12934 return OP_IMUL_IMM;
12936 return OP_IAND_IMM;
12940 return OP_IXOR_IMM;
12942 return OP_ISHL_IMM;
12944 return OP_ISHR_IMM;
12946 return OP_ISHR_UN_IMM;
12949 return OP_LADD_IMM;
12951 return OP_LSUB_IMM;
12953 return OP_LAND_IMM;
12957 return OP_LXOR_IMM;
12959 return OP_LSHL_IMM;
12961 return OP_LSHR_IMM;
12963 return OP_LSHR_UN_IMM;
12964 #if SIZEOF_REGISTER == 8
12966 return OP_LREM_IMM;
12970 return OP_COMPARE_IMM;
12972 return OP_ICOMPARE_IMM;
12974 return OP_LCOMPARE_IMM;
12976 case OP_STORE_MEMBASE_REG:
12977 return OP_STORE_MEMBASE_IMM;
12978 case OP_STOREI1_MEMBASE_REG:
12979 return OP_STOREI1_MEMBASE_IMM;
12980 case OP_STOREI2_MEMBASE_REG:
12981 return OP_STOREI2_MEMBASE_IMM;
12982 case OP_STOREI4_MEMBASE_REG:
12983 return OP_STOREI4_MEMBASE_IMM;
12985 #if defined(TARGET_X86) || defined (TARGET_AMD64)
12987 return OP_X86_PUSH_IMM;
12988 case OP_X86_COMPARE_MEMBASE_REG:
12989 return OP_X86_COMPARE_MEMBASE_IMM;
12991 #if defined(TARGET_AMD64)
12992 case OP_AMD64_ICOMPARE_MEMBASE_REG:
12993 return OP_AMD64_ICOMPARE_MEMBASE_IMM;
12995 case OP_VOIDCALL_REG:
12996 return OP_VOIDCALL;
13004 return OP_LOCALLOC_IMM;
13011 ldind_to_load_membase (int opcode)
13015 return OP_LOADI1_MEMBASE;
13017 return OP_LOADU1_MEMBASE;
13019 return OP_LOADI2_MEMBASE;
13021 return OP_LOADU2_MEMBASE;
13023 return OP_LOADI4_MEMBASE;
13025 return OP_LOADU4_MEMBASE;
13027 return OP_LOAD_MEMBASE;
13028 case CEE_LDIND_REF:
13029 return OP_LOAD_MEMBASE;
13031 return OP_LOADI8_MEMBASE;
13033 return OP_LOADR4_MEMBASE;
13035 return OP_LOADR8_MEMBASE;
13037 g_assert_not_reached ();
13044 stind_to_store_membase (int opcode)
13048 return OP_STOREI1_MEMBASE_REG;
13050 return OP_STOREI2_MEMBASE_REG;
13052 return OP_STOREI4_MEMBASE_REG;
13054 case CEE_STIND_REF:
13055 return OP_STORE_MEMBASE_REG;
13057 return OP_STOREI8_MEMBASE_REG;
13059 return OP_STORER4_MEMBASE_REG;
13061 return OP_STORER8_MEMBASE_REG;
13063 g_assert_not_reached ();
13070 mono_load_membase_to_load_mem (int opcode)
13072 // FIXME: Add a MONO_ARCH_HAVE_LOAD_MEM macro
13073 #if defined(TARGET_X86) || defined(TARGET_AMD64)
13075 case OP_LOAD_MEMBASE:
13076 return OP_LOAD_MEM;
13077 case OP_LOADU1_MEMBASE:
13078 return OP_LOADU1_MEM;
13079 case OP_LOADU2_MEMBASE:
13080 return OP_LOADU2_MEM;
13081 case OP_LOADI4_MEMBASE:
13082 return OP_LOADI4_MEM;
13083 case OP_LOADU4_MEMBASE:
13084 return OP_LOADU4_MEM;
13085 #if SIZEOF_REGISTER == 8
13086 case OP_LOADI8_MEMBASE:
13087 return OP_LOADI8_MEM;
13096 op_to_op_dest_membase (int store_opcode, int opcode)
13098 #if defined(TARGET_X86)
13099 if (!((store_opcode == OP_STORE_MEMBASE_REG) || (store_opcode == OP_STOREI4_MEMBASE_REG)))
13104 return OP_X86_ADD_MEMBASE_REG;
13106 return OP_X86_SUB_MEMBASE_REG;
13108 return OP_X86_AND_MEMBASE_REG;
13110 return OP_X86_OR_MEMBASE_REG;
13112 return OP_X86_XOR_MEMBASE_REG;
13115 return OP_X86_ADD_MEMBASE_IMM;
13118 return OP_X86_SUB_MEMBASE_IMM;
13121 return OP_X86_AND_MEMBASE_IMM;
13124 return OP_X86_OR_MEMBASE_IMM;
13127 return OP_X86_XOR_MEMBASE_IMM;
13133 #if defined(TARGET_AMD64)
13134 if (!((store_opcode == OP_STORE_MEMBASE_REG) || (store_opcode == OP_STOREI4_MEMBASE_REG) || (store_opcode == OP_STOREI8_MEMBASE_REG)))
13139 return OP_X86_ADD_MEMBASE_REG;
13141 return OP_X86_SUB_MEMBASE_REG;
13143 return OP_X86_AND_MEMBASE_REG;
13145 return OP_X86_OR_MEMBASE_REG;
13147 return OP_X86_XOR_MEMBASE_REG;
13149 return OP_X86_ADD_MEMBASE_IMM;
13151 return OP_X86_SUB_MEMBASE_IMM;
13153 return OP_X86_AND_MEMBASE_IMM;
13155 return OP_X86_OR_MEMBASE_IMM;
13157 return OP_X86_XOR_MEMBASE_IMM;
13159 return OP_AMD64_ADD_MEMBASE_REG;
13161 return OP_AMD64_SUB_MEMBASE_REG;
13163 return OP_AMD64_AND_MEMBASE_REG;
13165 return OP_AMD64_OR_MEMBASE_REG;
13167 return OP_AMD64_XOR_MEMBASE_REG;
13170 return OP_AMD64_ADD_MEMBASE_IMM;
13173 return OP_AMD64_SUB_MEMBASE_IMM;
13176 return OP_AMD64_AND_MEMBASE_IMM;
13179 return OP_AMD64_OR_MEMBASE_IMM;
13182 return OP_AMD64_XOR_MEMBASE_IMM;
13192 op_to_op_store_membase (int store_opcode, int opcode)
13194 #if defined(TARGET_X86) || defined(TARGET_AMD64)
13197 if (store_opcode == OP_STOREI1_MEMBASE_REG)
13198 return OP_X86_SETEQ_MEMBASE;
13200 if (store_opcode == OP_STOREI1_MEMBASE_REG)
13201 return OP_X86_SETNE_MEMBASE;
13209 op_to_op_src1_membase (MonoCompile *cfg, int load_opcode, int opcode)
13212 /* FIXME: This has sign extension issues */
13214 if ((opcode == OP_ICOMPARE_IMM) && (load_opcode == OP_LOADU1_MEMBASE))
13215 return OP_X86_COMPARE_MEMBASE8_IMM;
13218 if (!((load_opcode == OP_LOAD_MEMBASE) || (load_opcode == OP_LOADI4_MEMBASE) || (load_opcode == OP_LOADU4_MEMBASE)))
13223 return OP_X86_PUSH_MEMBASE;
13224 case OP_COMPARE_IMM:
13225 case OP_ICOMPARE_IMM:
13226 return OP_X86_COMPARE_MEMBASE_IMM;
13229 return OP_X86_COMPARE_MEMBASE_REG;
13233 #ifdef TARGET_AMD64
13234 /* FIXME: This has sign extension issues */
13236 if ((opcode == OP_ICOMPARE_IMM) && (load_opcode == OP_LOADU1_MEMBASE))
13237 return OP_X86_COMPARE_MEMBASE8_IMM;
13242 if ((load_opcode == OP_LOAD_MEMBASE && !cfg->backend->ilp32) || (load_opcode == OP_LOADI8_MEMBASE))
13243 return OP_X86_PUSH_MEMBASE;
13245 /* FIXME: This only works for 32 bit immediates
13246 case OP_COMPARE_IMM:
13247 case OP_LCOMPARE_IMM:
13248 if ((load_opcode == OP_LOAD_MEMBASE) || (load_opcode == OP_LOADI8_MEMBASE))
13249 return OP_AMD64_COMPARE_MEMBASE_IMM;
13251 case OP_ICOMPARE_IMM:
13252 if ((load_opcode == OP_LOADI4_MEMBASE) || (load_opcode == OP_LOADU4_MEMBASE))
13253 return OP_AMD64_ICOMPARE_MEMBASE_IMM;
13257 if (cfg->backend->ilp32 && load_opcode == OP_LOAD_MEMBASE)
13258 return OP_AMD64_ICOMPARE_MEMBASE_REG;
13259 if ((load_opcode == OP_LOAD_MEMBASE && !cfg->backend->ilp32) || (load_opcode == OP_LOADI8_MEMBASE))
13260 return OP_AMD64_COMPARE_MEMBASE_REG;
13263 if ((load_opcode == OP_LOADI4_MEMBASE) || (load_opcode == OP_LOADU4_MEMBASE))
13264 return OP_AMD64_ICOMPARE_MEMBASE_REG;
13273 op_to_op_src2_membase (MonoCompile *cfg, int load_opcode, int opcode)
13276 if (!((load_opcode == OP_LOAD_MEMBASE) || (load_opcode == OP_LOADI4_MEMBASE) || (load_opcode == OP_LOADU4_MEMBASE)))
13282 return OP_X86_COMPARE_REG_MEMBASE;
13284 return OP_X86_ADD_REG_MEMBASE;
13286 return OP_X86_SUB_REG_MEMBASE;
13288 return OP_X86_AND_REG_MEMBASE;
13290 return OP_X86_OR_REG_MEMBASE;
13292 return OP_X86_XOR_REG_MEMBASE;
13296 #ifdef TARGET_AMD64
13297 if ((load_opcode == OP_LOADI4_MEMBASE) || (load_opcode == OP_LOADU4_MEMBASE) || (load_opcode == OP_LOAD_MEMBASE && cfg->backend->ilp32)) {
13300 return OP_AMD64_ICOMPARE_REG_MEMBASE;
13302 return OP_X86_ADD_REG_MEMBASE;
13304 return OP_X86_SUB_REG_MEMBASE;
13306 return OP_X86_AND_REG_MEMBASE;
13308 return OP_X86_OR_REG_MEMBASE;
13310 return OP_X86_XOR_REG_MEMBASE;
13312 } else if ((load_opcode == OP_LOADI8_MEMBASE) || (load_opcode == OP_LOAD_MEMBASE && !cfg->backend->ilp32)) {
13316 return OP_AMD64_COMPARE_REG_MEMBASE;
13318 return OP_AMD64_ADD_REG_MEMBASE;
13320 return OP_AMD64_SUB_REG_MEMBASE;
13322 return OP_AMD64_AND_REG_MEMBASE;
13324 return OP_AMD64_OR_REG_MEMBASE;
13326 return OP_AMD64_XOR_REG_MEMBASE;
13335 mono_op_to_op_imm_noemul (int opcode)
13338 #if SIZEOF_REGISTER == 4 && !defined(MONO_ARCH_NO_EMULATE_LONG_SHIFT_OPS)
13344 #if defined(MONO_ARCH_EMULATE_MUL_DIV) || defined(MONO_ARCH_EMULATE_DIV)
13351 #if defined(MONO_ARCH_EMULATE_MUL_DIV)
13356 return mono_op_to_op_imm (opcode);
13361 * mono_handle_global_vregs:
13363 * Make vregs used in more than one bblock 'global', i.e. allocate a variable
13367 mono_handle_global_vregs (MonoCompile *cfg)
13369 gint32 *vreg_to_bb;
13370 MonoBasicBlock *bb;
13373 vreg_to_bb = (gint32 *)mono_mempool_alloc0 (cfg->mempool, sizeof (gint32*) * cfg->next_vreg + 1);
13375 #ifdef MONO_ARCH_SIMD_INTRINSICS
13376 if (cfg->uses_simd_intrinsics)
13377 mono_simd_simplify_indirection (cfg);
13380 /* Find local vregs used in more than one bb */
13381 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
13382 MonoInst *ins = bb->code;
13383 int block_num = bb->block_num;
13385 if (cfg->verbose_level > 2)
13386 printf ("\nHANDLE-GLOBAL-VREGS BLOCK %d:\n", bb->block_num);
13389 for (; ins; ins = ins->next) {
13390 const char *spec = INS_INFO (ins->opcode);
13391 int regtype = 0, regindex;
13394 if (G_UNLIKELY (cfg->verbose_level > 2))
13395 mono_print_ins (ins);
13397 g_assert (ins->opcode >= MONO_CEE_LAST);
13399 for (regindex = 0; regindex < 4; regindex ++) {
13402 if (regindex == 0) {
13403 regtype = spec [MONO_INST_DEST];
13404 if (regtype == ' ')
13407 } else if (regindex == 1) {
13408 regtype = spec [MONO_INST_SRC1];
13409 if (regtype == ' ')
13412 } else if (regindex == 2) {
13413 regtype = spec [MONO_INST_SRC2];
13414 if (regtype == ' ')
13417 } else if (regindex == 3) {
13418 regtype = spec [MONO_INST_SRC3];
13419 if (regtype == ' ')
13424 #if SIZEOF_REGISTER == 4
13425 /* In the LLVM case, the long opcodes are not decomposed */
13426 if (regtype == 'l' && !COMPILE_LLVM (cfg)) {
13428 * Since some instructions reference the original long vreg,
13429 * and some reference the two component vregs, it is quite hard
13430 * to determine when it needs to be global. So be conservative.
13432 if (!get_vreg_to_inst (cfg, vreg)) {
13433 mono_compile_create_var_for_vreg (cfg, &mono_defaults.int64_class->byval_arg, OP_LOCAL, vreg);
13435 if (cfg->verbose_level > 2)
13436 printf ("LONG VREG R%d made global.\n", vreg);
13440 * Make the component vregs volatile since the optimizations can
13441 * get confused otherwise.
13443 get_vreg_to_inst (cfg, MONO_LVREG_LS (vreg))->flags |= MONO_INST_VOLATILE;
13444 get_vreg_to_inst (cfg, MONO_LVREG_MS (vreg))->flags |= MONO_INST_VOLATILE;
13448 g_assert (vreg != -1);
13450 prev_bb = vreg_to_bb [vreg];
13451 if (prev_bb == 0) {
13452 /* 0 is a valid block num */
13453 vreg_to_bb [vreg] = block_num + 1;
13454 } else if ((prev_bb != block_num + 1) && (prev_bb != -1)) {
13455 if (((regtype == 'i' && (vreg < MONO_MAX_IREGS))) || (regtype == 'f' && (vreg < MONO_MAX_FREGS)))
13458 if (!get_vreg_to_inst (cfg, vreg)) {
13459 if (G_UNLIKELY (cfg->verbose_level > 2))
13460 printf ("VREG R%d used in BB%d and BB%d made global.\n", vreg, vreg_to_bb [vreg], block_num);
13464 if (vreg_is_ref (cfg, vreg))
13465 mono_compile_create_var_for_vreg (cfg, &mono_defaults.object_class->byval_arg, OP_LOCAL, vreg);
13467 mono_compile_create_var_for_vreg (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL, vreg);
13470 mono_compile_create_var_for_vreg (cfg, &mono_defaults.int64_class->byval_arg, OP_LOCAL, vreg);
13473 mono_compile_create_var_for_vreg (cfg, &mono_defaults.double_class->byval_arg, OP_LOCAL, vreg);
13477 mono_compile_create_var_for_vreg (cfg, &ins->klass->byval_arg, OP_LOCAL, vreg);
13480 g_assert_not_reached ();
13484 /* Flag as having been used in more than one bb */
13485 vreg_to_bb [vreg] = -1;
13491 /* If a variable is used in only one bblock, convert it into a local vreg */
13492 for (i = 0; i < cfg->num_varinfo; i++) {
13493 MonoInst *var = cfg->varinfo [i];
13494 MonoMethodVar *vmv = MONO_VARINFO (cfg, i);
13496 switch (var->type) {
13502 #if SIZEOF_REGISTER == 8
13505 #if !defined(TARGET_X86)
13506 /* Enabling this screws up the fp stack on x86 */
13509 if (mono_arch_is_soft_float ())
13513 if (var->type == STACK_VTYPE && cfg->gsharedvt && mini_is_gsharedvt_variable_type (var->inst_vtype))
13517 /* Arguments are implicitly global */
13518 /* Putting R4 vars into registers doesn't work currently */
13519 /* The gsharedvt vars are implicitly referenced by ldaddr opcodes, but those opcodes are only generated later */
13520 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) {
13522 * Make that the variable's liveness interval doesn't contain a call, since
13523 * that would cause the lvreg to be spilled, making the whole optimization
13526 /* This is too slow for JIT compilation */
13528 if (cfg->compile_aot && vreg_to_bb [var->dreg]) {
13530 int def_index, call_index, ins_index;
13531 gboolean spilled = FALSE;
13536 for (ins = vreg_to_bb [var->dreg]->code; ins; ins = ins->next) {
13537 const char *spec = INS_INFO (ins->opcode);
13539 if ((spec [MONO_INST_DEST] != ' ') && (ins->dreg == var->dreg))
13540 def_index = ins_index;
13542 if (((spec [MONO_INST_SRC1] != ' ') && (ins->sreg1 == var->dreg)) ||
13543 ((spec [MONO_INST_SRC1] != ' ') && (ins->sreg1 == var->dreg))) {
13544 if (call_index > def_index) {
13550 if (MONO_IS_CALL (ins))
13551 call_index = ins_index;
13561 if (G_UNLIKELY (cfg->verbose_level > 2))
13562 printf ("CONVERTED R%d(%d) TO VREG.\n", var->dreg, vmv->idx);
13563 var->flags |= MONO_INST_IS_DEAD;
13564 cfg->vreg_to_inst [var->dreg] = NULL;
13571 * Compress the varinfo and vars tables so the liveness computation is faster and
13572 * takes up less space.
13575 for (i = 0; i < cfg->num_varinfo; ++i) {
13576 MonoInst *var = cfg->varinfo [i];
13577 if (pos < i && cfg->locals_start == i)
13578 cfg->locals_start = pos;
13579 if (!(var->flags & MONO_INST_IS_DEAD)) {
13581 cfg->varinfo [pos] = cfg->varinfo [i];
13582 cfg->varinfo [pos]->inst_c0 = pos;
13583 memcpy (&cfg->vars [pos], &cfg->vars [i], sizeof (MonoMethodVar));
13584 cfg->vars [pos].idx = pos;
13585 #if SIZEOF_REGISTER == 4
13586 if (cfg->varinfo [pos]->type == STACK_I8) {
13587 /* Modify the two component vars too */
13590 var1 = get_vreg_to_inst (cfg, MONO_LVREG_LS (cfg->varinfo [pos]->dreg));
13591 var1->inst_c0 = pos;
13592 var1 = get_vreg_to_inst (cfg, MONO_LVREG_MS (cfg->varinfo [pos]->dreg));
13593 var1->inst_c0 = pos;
13600 cfg->num_varinfo = pos;
13601 if (cfg->locals_start > cfg->num_varinfo)
13602 cfg->locals_start = cfg->num_varinfo;
13606 * mono_allocate_gsharedvt_vars:
13608 * Allocate variables with gsharedvt types to entries in the MonoGSharedVtMethodRuntimeInfo.entries array.
13609 * Initialize cfg->gsharedvt_vreg_to_idx with the mapping between vregs and indexes.
13612 mono_allocate_gsharedvt_vars (MonoCompile *cfg)
13616 cfg->gsharedvt_vreg_to_idx = (int *)mono_mempool_alloc0 (cfg->mempool, sizeof (int) * cfg->next_vreg);
13618 for (i = 0; i < cfg->num_varinfo; ++i) {
13619 MonoInst *ins = cfg->varinfo [i];
13622 if (mini_is_gsharedvt_variable_type (ins->inst_vtype)) {
13623 if (i >= cfg->locals_start) {
13625 idx = get_gsharedvt_info_slot (cfg, ins->inst_vtype, MONO_RGCTX_INFO_LOCAL_OFFSET);
13626 cfg->gsharedvt_vreg_to_idx [ins->dreg] = idx + 1;
13627 ins->opcode = OP_GSHAREDVT_LOCAL;
13628 ins->inst_imm = idx;
13631 cfg->gsharedvt_vreg_to_idx [ins->dreg] = -1;
13632 ins->opcode = OP_GSHAREDVT_ARG_REGOFFSET;
13639 * mono_spill_global_vars:
13641 * Generate spill code for variables which are not allocated to registers,
13642 * and replace vregs with their allocated hregs. *need_local_opts is set to TRUE if
13643 * code is generated which could be optimized by the local optimization passes.
13646 mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local_opts)
13648 MonoBasicBlock *bb;
13650 int orig_next_vreg;
13651 guint32 *vreg_to_lvreg;
13653 guint32 i, lvregs_len, lvregs_size;
13654 gboolean dest_has_lvreg = FALSE;
13655 MonoStackType stacktypes [128];
13656 MonoInst **live_range_start, **live_range_end;
13657 MonoBasicBlock **live_range_start_bb, **live_range_end_bb;
13659 *need_local_opts = FALSE;
13661 memset (spec2, 0, sizeof (spec2));
13663 /* FIXME: Move this function to mini.c */
13664 stacktypes ['i'] = STACK_PTR;
13665 stacktypes ['l'] = STACK_I8;
13666 stacktypes ['f'] = STACK_R8;
13667 #ifdef MONO_ARCH_SIMD_INTRINSICS
13668 stacktypes ['x'] = STACK_VTYPE;
13671 #if SIZEOF_REGISTER == 4
13672 /* Create MonoInsts for longs */
13673 for (i = 0; i < cfg->num_varinfo; i++) {
13674 MonoInst *ins = cfg->varinfo [i];
13676 if ((ins->opcode != OP_REGVAR) && !(ins->flags & MONO_INST_IS_DEAD)) {
13677 switch (ins->type) {
13682 if (ins->type == STACK_R8 && !COMPILE_SOFT_FLOAT (cfg))
13685 g_assert (ins->opcode == OP_REGOFFSET);
13687 tree = get_vreg_to_inst (cfg, MONO_LVREG_LS (ins->dreg));
13689 tree->opcode = OP_REGOFFSET;
13690 tree->inst_basereg = ins->inst_basereg;
13691 tree->inst_offset = ins->inst_offset + MINI_LS_WORD_OFFSET;
13693 tree = get_vreg_to_inst (cfg, MONO_LVREG_MS (ins->dreg));
13695 tree->opcode = OP_REGOFFSET;
13696 tree->inst_basereg = ins->inst_basereg;
13697 tree->inst_offset = ins->inst_offset + MINI_MS_WORD_OFFSET;
13707 if (cfg->compute_gc_maps) {
13708 /* registers need liveness info even for !non refs */
13709 for (i = 0; i < cfg->num_varinfo; i++) {
13710 MonoInst *ins = cfg->varinfo [i];
13712 if (ins->opcode == OP_REGVAR)
13713 ins->flags |= MONO_INST_GC_TRACK;
13717 /* FIXME: widening and truncation */
13720 * As an optimization, when a variable allocated to the stack is first loaded into
13721 * an lvreg, we will remember the lvreg and use it the next time instead of loading
13722 * the variable again.
13724 orig_next_vreg = cfg->next_vreg;
13725 vreg_to_lvreg = (guint32 *)mono_mempool_alloc0 (cfg->mempool, sizeof (guint32) * cfg->next_vreg);
13726 lvregs_size = 1024;
13727 lvregs = (guint32 *)mono_mempool_alloc (cfg->mempool, sizeof (guint32) * lvregs_size);
13731 * These arrays contain the first and last instructions accessing a given
13733 * Since we emit bblocks in the same order we process them here, and we
13734 * don't split live ranges, these will precisely describe the live range of
13735 * the variable, i.e. the instruction range where a valid value can be found
13736 * in the variables location.
13737 * The live range is computed using the liveness info computed by the liveness pass.
13738 * We can't use vmv->range, since that is an abstract live range, and we need
13739 * one which is instruction precise.
13740 * FIXME: Variables used in out-of-line bblocks have a hole in their live range.
13742 /* FIXME: Only do this if debugging info is requested */
13743 live_range_start = g_new0 (MonoInst*, cfg->next_vreg);
13744 live_range_end = g_new0 (MonoInst*, cfg->next_vreg);
13745 live_range_start_bb = g_new (MonoBasicBlock*, cfg->next_vreg);
13746 live_range_end_bb = g_new (MonoBasicBlock*, cfg->next_vreg);
13748 /* Add spill loads/stores */
13749 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
13752 if (cfg->verbose_level > 2)
13753 printf ("\nSPILL BLOCK %d:\n", bb->block_num);
13755 /* Clear vreg_to_lvreg array */
13756 for (i = 0; i < lvregs_len; i++)
13757 vreg_to_lvreg [lvregs [i]] = 0;
13761 MONO_BB_FOR_EACH_INS (bb, ins) {
13762 const char *spec = INS_INFO (ins->opcode);
13763 int regtype, srcindex, sreg, tmp_reg, prev_dreg, num_sregs;
13764 gboolean store, no_lvreg;
13765 int sregs [MONO_MAX_SRC_REGS];
13767 if (G_UNLIKELY (cfg->verbose_level > 2))
13768 mono_print_ins (ins);
13770 if (ins->opcode == OP_NOP)
13774 * We handle LDADDR here as well, since it can only be decomposed
13775 * when variable addresses are known.
13777 if (ins->opcode == OP_LDADDR) {
13778 MonoInst *var = (MonoInst *)ins->inst_p0;
13780 if (var->opcode == OP_VTARG_ADDR) {
13781 /* Happens on SPARC/S390 where vtypes are passed by reference */
13782 MonoInst *vtaddr = var->inst_left;
13783 if (vtaddr->opcode == OP_REGVAR) {
13784 ins->opcode = OP_MOVE;
13785 ins->sreg1 = vtaddr->dreg;
13787 else if (var->inst_left->opcode == OP_REGOFFSET) {
13788 ins->opcode = OP_LOAD_MEMBASE;
13789 ins->inst_basereg = vtaddr->inst_basereg;
13790 ins->inst_offset = vtaddr->inst_offset;
13793 } else if (cfg->gsharedvt && cfg->gsharedvt_vreg_to_idx [var->dreg] < 0) {
13794 /* gsharedvt arg passed by ref */
13795 g_assert (var->opcode == OP_GSHAREDVT_ARG_REGOFFSET);
13797 ins->opcode = OP_LOAD_MEMBASE;
13798 ins->inst_basereg = var->inst_basereg;
13799 ins->inst_offset = var->inst_offset;
13800 } else if (cfg->gsharedvt && cfg->gsharedvt_vreg_to_idx [var->dreg]) {
13801 MonoInst *load, *load2, *load3;
13802 int idx = cfg->gsharedvt_vreg_to_idx [var->dreg] - 1;
13803 int reg1, reg2, reg3;
13804 MonoInst *info_var = cfg->gsharedvt_info_var;
13805 MonoInst *locals_var = cfg->gsharedvt_locals_var;
13809 * Compute the address of the local as gsharedvt_locals_var + gsharedvt_info_var->locals_offsets [idx].
13812 g_assert (var->opcode == OP_GSHAREDVT_LOCAL);
13814 g_assert (info_var);
13815 g_assert (locals_var);
13817 /* Mark the instruction used to compute the locals var as used */
13818 cfg->gsharedvt_locals_var_ins = NULL;
13820 /* Load the offset */
13821 if (info_var->opcode == OP_REGOFFSET) {
13822 reg1 = alloc_ireg (cfg);
13823 NEW_LOAD_MEMBASE (cfg, load, OP_LOAD_MEMBASE, reg1, info_var->inst_basereg, info_var->inst_offset);
13824 } else if (info_var->opcode == OP_REGVAR) {
13826 reg1 = info_var->dreg;
13828 g_assert_not_reached ();
13830 reg2 = alloc_ireg (cfg);
13831 NEW_LOAD_MEMBASE (cfg, load2, OP_LOADI4_MEMBASE, reg2, reg1, MONO_STRUCT_OFFSET (MonoGSharedVtMethodRuntimeInfo, entries) + (idx * sizeof (gpointer)));
13832 /* Load the locals area address */
13833 reg3 = alloc_ireg (cfg);
13834 if (locals_var->opcode == OP_REGOFFSET) {
13835 NEW_LOAD_MEMBASE (cfg, load3, OP_LOAD_MEMBASE, reg3, locals_var->inst_basereg, locals_var->inst_offset);
13836 } else if (locals_var->opcode == OP_REGVAR) {
13837 NEW_UNALU (cfg, load3, OP_MOVE, reg3, locals_var->dreg);
13839 g_assert_not_reached ();
13841 /* Compute the address */
13842 ins->opcode = OP_PADD;
13846 mono_bblock_insert_before_ins (bb, ins, load3);
13847 mono_bblock_insert_before_ins (bb, load3, load2);
13849 mono_bblock_insert_before_ins (bb, load2, load);
13851 g_assert (var->opcode == OP_REGOFFSET);
13853 ins->opcode = OP_ADD_IMM;
13854 ins->sreg1 = var->inst_basereg;
13855 ins->inst_imm = var->inst_offset;
13858 *need_local_opts = TRUE;
13859 spec = INS_INFO (ins->opcode);
13862 if (ins->opcode < MONO_CEE_LAST) {
13863 mono_print_ins (ins);
13864 g_assert_not_reached ();
13868 * Store opcodes have destbasereg in the dreg, but in reality, it is an
13872 if (MONO_IS_STORE_MEMBASE (ins)) {
13873 tmp_reg = ins->dreg;
13874 ins->dreg = ins->sreg2;
13875 ins->sreg2 = tmp_reg;
13878 spec2 [MONO_INST_DEST] = ' ';
13879 spec2 [MONO_INST_SRC1] = spec [MONO_INST_SRC1];
13880 spec2 [MONO_INST_SRC2] = spec [MONO_INST_DEST];
13881 spec2 [MONO_INST_SRC3] = ' ';
13883 } else if (MONO_IS_STORE_MEMINDEX (ins))
13884 g_assert_not_reached ();
13889 if (G_UNLIKELY (cfg->verbose_level > 2)) {
13890 printf ("\t %.3s %d", spec, ins->dreg);
13891 num_sregs = mono_inst_get_src_registers (ins, sregs);
13892 for (srcindex = 0; srcindex < num_sregs; ++srcindex)
13893 printf (" %d", sregs [srcindex]);
13900 regtype = spec [MONO_INST_DEST];
13901 g_assert (((ins->dreg == -1) && (regtype == ' ')) || ((ins->dreg != -1) && (regtype != ' ')));
13904 if ((ins->dreg != -1) && get_vreg_to_inst (cfg, ins->dreg)) {
13905 MonoInst *var = get_vreg_to_inst (cfg, ins->dreg);
13906 MonoInst *store_ins;
13908 MonoInst *def_ins = ins;
13909 int dreg = ins->dreg; /* The original vreg */
13911 store_opcode = mono_type_to_store_membase (cfg, var->inst_vtype);
13913 if (var->opcode == OP_REGVAR) {
13914 ins->dreg = var->dreg;
13915 } 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)) {
13917 * Instead of emitting a load+store, use a _membase opcode.
13919 g_assert (var->opcode == OP_REGOFFSET);
13920 if (ins->opcode == OP_MOVE) {
13924 ins->opcode = op_to_op_dest_membase (store_opcode, ins->opcode);
13925 ins->inst_basereg = var->inst_basereg;
13926 ins->inst_offset = var->inst_offset;
13929 spec = INS_INFO (ins->opcode);
13933 g_assert (var->opcode == OP_REGOFFSET);
13935 prev_dreg = ins->dreg;
13937 /* Invalidate any previous lvreg for this vreg */
13938 vreg_to_lvreg [ins->dreg] = 0;
13942 if (COMPILE_SOFT_FLOAT (cfg) && store_opcode == OP_STORER8_MEMBASE_REG) {
13944 store_opcode = OP_STOREI8_MEMBASE_REG;
13947 ins->dreg = alloc_dreg (cfg, stacktypes [regtype]);
13949 #if SIZEOF_REGISTER != 8
13950 if (regtype == 'l') {
13951 NEW_STORE_MEMBASE (cfg, store_ins, OP_STOREI4_MEMBASE_REG, var->inst_basereg, var->inst_offset + MINI_LS_WORD_OFFSET, MONO_LVREG_LS (ins->dreg));
13952 mono_bblock_insert_after_ins (bb, ins, store_ins);
13953 NEW_STORE_MEMBASE (cfg, store_ins, OP_STOREI4_MEMBASE_REG, var->inst_basereg, var->inst_offset + MINI_MS_WORD_OFFSET, MONO_LVREG_MS (ins->dreg));
13954 mono_bblock_insert_after_ins (bb, ins, store_ins);
13955 def_ins = store_ins;
13960 g_assert (store_opcode != OP_STOREV_MEMBASE);
13962 /* Try to fuse the store into the instruction itself */
13963 /* FIXME: Add more instructions */
13964 if (!lvreg && ((ins->opcode == OP_ICONST) || ((ins->opcode == OP_I8CONST) && (ins->inst_c0 == 0)))) {
13965 ins->opcode = store_membase_reg_to_store_membase_imm (store_opcode);
13966 ins->inst_imm = ins->inst_c0;
13967 ins->inst_destbasereg = var->inst_basereg;
13968 ins->inst_offset = var->inst_offset;
13969 spec = INS_INFO (ins->opcode);
13970 } else if (!lvreg && ((ins->opcode == OP_MOVE) || (ins->opcode == OP_FMOVE) || (ins->opcode == OP_LMOVE) || (ins->opcode == OP_RMOVE))) {
13971 ins->opcode = store_opcode;
13972 ins->inst_destbasereg = var->inst_basereg;
13973 ins->inst_offset = var->inst_offset;
13977 tmp_reg = ins->dreg;
13978 ins->dreg = ins->sreg2;
13979 ins->sreg2 = tmp_reg;
13982 spec2 [MONO_INST_DEST] = ' ';
13983 spec2 [MONO_INST_SRC1] = spec [MONO_INST_SRC1];
13984 spec2 [MONO_INST_SRC2] = spec [MONO_INST_DEST];
13985 spec2 [MONO_INST_SRC3] = ' ';
13987 } else if (!lvreg && (op_to_op_store_membase (store_opcode, ins->opcode) != -1)) {
13988 // FIXME: The backends expect the base reg to be in inst_basereg
13989 ins->opcode = op_to_op_store_membase (store_opcode, ins->opcode);
13991 ins->inst_basereg = var->inst_basereg;
13992 ins->inst_offset = var->inst_offset;
13993 spec = INS_INFO (ins->opcode);
13995 /* printf ("INS: "); mono_print_ins (ins); */
13996 /* Create a store instruction */
13997 NEW_STORE_MEMBASE (cfg, store_ins, store_opcode, var->inst_basereg, var->inst_offset, ins->dreg);
13999 /* Insert it after the instruction */
14000 mono_bblock_insert_after_ins (bb, ins, store_ins);
14002 def_ins = store_ins;
14005 * We can't assign ins->dreg to var->dreg here, since the
14006 * sregs could use it. So set a flag, and do it after
14009 if ((!cfg->backend->use_fpstack || ((store_opcode != OP_STORER8_MEMBASE_REG) && (store_opcode != OP_STORER4_MEMBASE_REG))) && !((var)->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT)))
14010 dest_has_lvreg = TRUE;
14015 if (def_ins && !live_range_start [dreg]) {
14016 live_range_start [dreg] = def_ins;
14017 live_range_start_bb [dreg] = bb;
14020 if (cfg->compute_gc_maps && def_ins && (var->flags & MONO_INST_GC_TRACK)) {
14023 MONO_INST_NEW (cfg, tmp, OP_GC_LIVENESS_DEF);
14024 tmp->inst_c1 = dreg;
14025 mono_bblock_insert_after_ins (bb, def_ins, tmp);
14032 num_sregs = mono_inst_get_src_registers (ins, sregs);
14033 for (srcindex = 0; srcindex < 3; ++srcindex) {
14034 regtype = spec [MONO_INST_SRC1 + srcindex];
14035 sreg = sregs [srcindex];
14037 g_assert (((sreg == -1) && (regtype == ' ')) || ((sreg != -1) && (regtype != ' ')));
14038 if ((sreg != -1) && get_vreg_to_inst (cfg, sreg)) {
14039 MonoInst *var = get_vreg_to_inst (cfg, sreg);
14040 MonoInst *use_ins = ins;
14041 MonoInst *load_ins;
14042 guint32 load_opcode;
14044 if (var->opcode == OP_REGVAR) {
14045 sregs [srcindex] = var->dreg;
14046 //mono_inst_set_src_registers (ins, sregs);
14047 live_range_end [sreg] = use_ins;
14048 live_range_end_bb [sreg] = bb;
14050 if (cfg->compute_gc_maps && var->dreg < orig_next_vreg && (var->flags & MONO_INST_GC_TRACK)) {
14053 MONO_INST_NEW (cfg, tmp, OP_GC_LIVENESS_USE);
14054 /* var->dreg is a hreg */
14055 tmp->inst_c1 = sreg;
14056 mono_bblock_insert_after_ins (bb, ins, tmp);
14062 g_assert (var->opcode == OP_REGOFFSET);
14064 load_opcode = mono_type_to_load_membase (cfg, var->inst_vtype);
14066 g_assert (load_opcode != OP_LOADV_MEMBASE);
14068 if (vreg_to_lvreg [sreg]) {
14069 g_assert (vreg_to_lvreg [sreg] != -1);
14071 /* The variable is already loaded to an lvreg */
14072 if (G_UNLIKELY (cfg->verbose_level > 2))
14073 printf ("\t\tUse lvreg R%d for R%d.\n", vreg_to_lvreg [sreg], sreg);
14074 sregs [srcindex] = vreg_to_lvreg [sreg];
14075 //mono_inst_set_src_registers (ins, sregs);
14079 /* Try to fuse the load into the instruction */
14080 if ((srcindex == 0) && (op_to_op_src1_membase (cfg, load_opcode, ins->opcode) != -1)) {
14081 ins->opcode = op_to_op_src1_membase (cfg, load_opcode, ins->opcode);
14082 sregs [0] = var->inst_basereg;
14083 //mono_inst_set_src_registers (ins, sregs);
14084 ins->inst_offset = var->inst_offset;
14085 } else if ((srcindex == 1) && (op_to_op_src2_membase (cfg, load_opcode, ins->opcode) != -1)) {
14086 ins->opcode = op_to_op_src2_membase (cfg, load_opcode, ins->opcode);
14087 sregs [1] = var->inst_basereg;
14088 //mono_inst_set_src_registers (ins, sregs);
14089 ins->inst_offset = var->inst_offset;
14091 if (MONO_IS_REAL_MOVE (ins)) {
14092 ins->opcode = OP_NOP;
14095 //printf ("%d ", srcindex); mono_print_ins (ins);
14097 sreg = alloc_dreg (cfg, stacktypes [regtype]);
14099 if ((!cfg->backend->use_fpstack || ((load_opcode != OP_LOADR8_MEMBASE) && (load_opcode != OP_LOADR4_MEMBASE))) && !((var)->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT)) && !no_lvreg) {
14100 if (var->dreg == prev_dreg) {
14102 * sreg refers to the value loaded by the load
14103 * emitted below, but we need to use ins->dreg
14104 * since it refers to the store emitted earlier.
14108 g_assert (sreg != -1);
14109 vreg_to_lvreg [var->dreg] = sreg;
14110 if (lvregs_len >= lvregs_size) {
14111 guint32 *new_lvregs = mono_mempool_alloc0 (cfg->mempool, sizeof (guint32) * lvregs_size * 2);
14112 memcpy (new_lvregs, lvregs, sizeof (guint32) * lvregs_size);
14113 lvregs = new_lvregs;
14116 lvregs [lvregs_len ++] = var->dreg;
14120 sregs [srcindex] = sreg;
14121 //mono_inst_set_src_registers (ins, sregs);
14123 #if SIZEOF_REGISTER != 8
14124 if (regtype == 'l') {
14125 NEW_LOAD_MEMBASE (cfg, load_ins, OP_LOADI4_MEMBASE, MONO_LVREG_MS (sreg), var->inst_basereg, var->inst_offset + MINI_MS_WORD_OFFSET);
14126 mono_bblock_insert_before_ins (bb, ins, load_ins);
14127 NEW_LOAD_MEMBASE (cfg, load_ins, OP_LOADI4_MEMBASE, MONO_LVREG_LS (sreg), var->inst_basereg, var->inst_offset + MINI_LS_WORD_OFFSET);
14128 mono_bblock_insert_before_ins (bb, ins, load_ins);
14129 use_ins = load_ins;
14134 #if SIZEOF_REGISTER == 4
14135 g_assert (load_opcode != OP_LOADI8_MEMBASE);
14137 NEW_LOAD_MEMBASE (cfg, load_ins, load_opcode, sreg, var->inst_basereg, var->inst_offset);
14138 mono_bblock_insert_before_ins (bb, ins, load_ins);
14139 use_ins = load_ins;
14143 if (var->dreg < orig_next_vreg) {
14144 live_range_end [var->dreg] = use_ins;
14145 live_range_end_bb [var->dreg] = bb;
14148 if (cfg->compute_gc_maps && var->dreg < orig_next_vreg && (var->flags & MONO_INST_GC_TRACK)) {
14151 MONO_INST_NEW (cfg, tmp, OP_GC_LIVENESS_USE);
14152 tmp->inst_c1 = var->dreg;
14153 mono_bblock_insert_after_ins (bb, ins, tmp);
14157 mono_inst_set_src_registers (ins, sregs);
14159 if (dest_has_lvreg) {
14160 g_assert (ins->dreg != -1);
14161 vreg_to_lvreg [prev_dreg] = ins->dreg;
14162 if (lvregs_len >= lvregs_size) {
14163 guint32 *new_lvregs = mono_mempool_alloc0 (cfg->mempool, sizeof (guint32) * lvregs_size * 2);
14164 memcpy (new_lvregs, lvregs, sizeof (guint32) * lvregs_size);
14165 lvregs = new_lvregs;
14168 lvregs [lvregs_len ++] = prev_dreg;
14169 dest_has_lvreg = FALSE;
14173 tmp_reg = ins->dreg;
14174 ins->dreg = ins->sreg2;
14175 ins->sreg2 = tmp_reg;
14178 if (MONO_IS_CALL (ins)) {
14179 /* Clear vreg_to_lvreg array */
14180 for (i = 0; i < lvregs_len; i++)
14181 vreg_to_lvreg [lvregs [i]] = 0;
14183 } else if (ins->opcode == OP_NOP) {
14185 MONO_INST_NULLIFY_SREGS (ins);
14188 if (cfg->verbose_level > 2)
14189 mono_print_ins_index (1, ins);
14192 /* Extend the live range based on the liveness info */
14193 if (cfg->compute_precise_live_ranges && bb->live_out_set && bb->code) {
14194 for (i = 0; i < cfg->num_varinfo; i ++) {
14195 MonoMethodVar *vi = MONO_VARINFO (cfg, i);
14197 if (vreg_is_volatile (cfg, vi->vreg))
14198 /* The liveness info is incomplete */
14201 if (mono_bitset_test_fast (bb->live_in_set, i) && !live_range_start [vi->vreg]) {
14202 /* Live from at least the first ins of this bb */
14203 live_range_start [vi->vreg] = bb->code;
14204 live_range_start_bb [vi->vreg] = bb;
14207 if (mono_bitset_test_fast (bb->live_out_set, i)) {
14208 /* Live at least until the last ins of this bb */
14209 live_range_end [vi->vreg] = bb->last_ins;
14210 live_range_end_bb [vi->vreg] = bb;
14217 * Emit LIVERANGE_START/LIVERANGE_END opcodes, the backend will implement them
14218 * by storing the current native offset into MonoMethodVar->live_range_start/end.
14220 if (cfg->backend->have_liverange_ops && cfg->compute_precise_live_ranges && cfg->comp_done & MONO_COMP_LIVENESS) {
14221 for (i = 0; i < cfg->num_varinfo; ++i) {
14222 int vreg = MONO_VARINFO (cfg, i)->vreg;
14225 if (live_range_start [vreg]) {
14226 MONO_INST_NEW (cfg, ins, OP_LIVERANGE_START);
14228 ins->inst_c1 = vreg;
14229 mono_bblock_insert_after_ins (live_range_start_bb [vreg], live_range_start [vreg], ins);
14231 if (live_range_end [vreg]) {
14232 MONO_INST_NEW (cfg, ins, OP_LIVERANGE_END);
14234 ins->inst_c1 = vreg;
14235 if (live_range_end [vreg] == live_range_end_bb [vreg]->last_ins)
14236 mono_add_ins_to_end (live_range_end_bb [vreg], ins);
14238 mono_bblock_insert_after_ins (live_range_end_bb [vreg], live_range_end [vreg], ins);
14243 if (cfg->gsharedvt_locals_var_ins) {
14244 /* Nullify if unused */
14245 cfg->gsharedvt_locals_var_ins->opcode = OP_PCONST;
14246 cfg->gsharedvt_locals_var_ins->inst_imm = 0;
14249 g_free (live_range_start);
14250 g_free (live_range_end);
14251 g_free (live_range_start_bb);
14252 g_free (live_range_end_bb);
14258 * - use 'iadd' instead of 'int_add'
14259 * - handling ovf opcodes: decompose in method_to_ir.
14260 * - unify iregs/fregs
14261 * -> partly done, the missing parts are:
14262 * - a more complete unification would involve unifying the hregs as well, so
14263 * code wouldn't need if (fp) all over the place. but that would mean the hregs
14264 * would no longer map to the machine hregs, so the code generators would need to
14265 * be modified. Also, on ia64 for example, niregs + nfregs > 256 -> bitmasks
14266 * wouldn't work any more. Duplicating the code in mono_local_regalloc () into
14267 * fp/non-fp branches speeds it up by about 15%.
14268 * - use sext/zext opcodes instead of shifts
14270 * - get rid of TEMPLOADs if possible and use vregs instead
14271 * - clean up usage of OP_P/OP_ opcodes
14272 * - cleanup usage of DUMMY_USE
14273 * - cleanup the setting of ins->type for MonoInst's which are pushed on the
14275 * - set the stack type and allocate a dreg in the EMIT_NEW macros
14276 * - get rid of all the <foo>2 stuff when the new JIT is ready.
14277 * - make sure handle_stack_args () is called before the branch is emitted
14278 * - when the new IR is done, get rid of all unused stuff
14279 * - COMPARE/BEQ as separate instructions or unify them ?
14280 * - keeping them separate allows specialized compare instructions like
14281 * compare_imm, compare_membase
14282 * - most back ends unify fp compare+branch, fp compare+ceq
14283 * - integrate mono_save_args into inline_method
14284 * - get rid of the empty bblocks created by MONO_EMIT_NEW_BRACH_BLOCK2
14285 * - handle long shift opts on 32 bit platforms somehow: they require
14286 * 3 sregs (2 for arg1 and 1 for arg2)
14287 * - make byref a 'normal' type.
14288 * - use vregs for bb->out_stacks if possible, handle_global_vreg will make them a
14289 * variable if needed.
14290 * - do not start a new IL level bblock when cfg->cbb is changed by a function call
14291 * like inline_method.
14292 * - remove inlining restrictions
14293 * - fix LNEG and enable cfold of INEG
14294 * - generalize x86 optimizations like ldelema as a peephole optimization
14295 * - add store_mem_imm for amd64
14296 * - optimize the loading of the interruption flag in the managed->native wrappers
14297 * - avoid special handling of OP_NOP in passes
14298 * - move code inserting instructions into one function/macro.
14299 * - try a coalescing phase after liveness analysis
14300 * - add float -> vreg conversion + local optimizations on !x86
14301 * - figure out how to handle decomposed branches during optimizations, ie.
14302 * compare+branch, op_jump_table+op_br etc.
14303 * - promote RuntimeXHandles to vregs
14304 * - vtype cleanups:
14305 * - add a NEW_VARLOADA_VREG macro
14306 * - the vtype optimizations are blocked by the LDADDR opcodes generated for
14307 * accessing vtype fields.
14308 * - get rid of I8CONST on 64 bit platforms
14309 * - dealing with the increase in code size due to branches created during opcode
14311 * - use extended basic blocks
14312 * - all parts of the JIT
14313 * - handle_global_vregs () && local regalloc
14314 * - avoid introducing global vregs during decomposition, like 'vtable' in isinst
14315 * - sources of increase in code size:
14318 * - isinst and castclass
14319 * - lvregs not allocated to global registers even if used multiple times
14320 * - call cctors outside the JIT, to make -v output more readable and JIT timings more
14322 * - check for fp stack leakage in other opcodes too. (-> 'exceptions' optimization)
14323 * - add all micro optimizations from the old JIT
14324 * - put tree optimizations into the deadce pass
14325 * - decompose op_start_handler/op_endfilter/op_endfinally earlier using an arch
14326 * specific function.
14327 * - unify the float comparison opcodes with the other comparison opcodes, i.e.
14328 * fcompare + branchCC.
14329 * - create a helper function for allocating a stack slot, taking into account
14330 * MONO_CFG_HAS_SPILLUP.
14332 * - optimize mono_regstate2_alloc_int/float.
14333 * - fix the pessimistic handling of variables accessed in exception handler blocks.
14334 * - need to write a tree optimization pass, but the creation of trees is difficult, i.e.
14335 * parts of the tree could be separated by other instructions, killing the tree
14336 * arguments, or stores killing loads etc. Also, should we fold loads into other
14337 * instructions if the result of the load is used multiple times ?
14338 * - make the REM_IMM optimization in mini-x86.c arch-independent.
14339 * - LAST MERGE: 108395.
14340 * - when returning vtypes in registers, generate IR and append it to the end of the
14341 * last bb instead of doing it in the epilog.
14342 * - change the store opcodes so they use sreg1 instead of dreg to store the base register.
14350 - When to decompose opcodes:
14351 - earlier: this makes some optimizations hard to implement, since the low level IR
14352 no longer contains the neccessary information. But it is easier to do.
14353 - later: harder to implement, enables more optimizations.
14354 - Branches inside bblocks:
14355 - created when decomposing complex opcodes.
14356 - branches to another bblock: harmless, but not tracked by the branch
14357 optimizations, so need to branch to a label at the start of the bblock.
14358 - branches to inside the same bblock: very problematic, trips up the local
14359 reg allocator. Can be fixed by spitting the current bblock, but that is a
14360 complex operation, since some local vregs can become global vregs etc.
14361 - Local/global vregs:
14362 - local vregs: temporary vregs used inside one bblock. Assigned to hregs by the
14363 local register allocator.
14364 - global vregs: used in more than one bblock. Have an associated MonoMethodVar
14365 structure, created by mono_create_var (). Assigned to hregs or the stack by
14366 the global register allocator.
14367 - When to do optimizations like alu->alu_imm:
14368 - earlier -> saves work later on since the IR will be smaller/simpler
14369 - later -> can work on more instructions
14370 - Handling of valuetypes:
14371 - When a vtype is pushed on the stack, a new temporary is created, an
14372 instruction computing its address (LDADDR) is emitted and pushed on
14373 the stack. Need to optimize cases when the vtype is used immediately as in
14374 argument passing, stloc etc.
14375 - Instead of the to_end stuff in the old JIT, simply call the function handling
14376 the values on the stack before emitting the last instruction of the bb.
14379 #else /* !DISABLE_JIT */
14382 mono_set_break_policy (MonoBreakPolicyFunc policy_callback)
14386 #endif /* !DISABLE_JIT */